From 9ac62e39671bf69afa5de42a7af5301ad6908fb7 Mon Sep 17 00:00:00 2001 From: Jesper Schulz-Wedde Date: Thu, 17 Sep 2026 15:54:23 +0200 Subject: [PATCH 1/2] Add SCM functional knowledge domain Introduce nine source-backed rules with original AL sample pairs, bounded SCM review routing, and complete positive/clean evaluation coverage. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .github/scripts/Test-SkillIndex.ps1 | 29 ++- README.md | 11 +- docs/using-bcquality.md | 16 +- evaluation/README.md | 10 + evaluation/review-fixtures.json | 13 ++ ...ions-through-reservation-management.bad.al | 18 ++ ...ons-through-reservation-management.good.al | 19 ++ ...rvations-through-reservation-management.md | 39 ++++ ...tions-through-the-standard-workflow.bad.al | 53 +++++ ...ions-through-the-standard-workflow.good.al | 37 +++ ...n-actions-through-the-standard-workflow.md | 44 ++++ ...plications-through-posting-routines.bad.al | 37 +++ ...lications-through-posting-routines.good.al | 45 ++++ ...m-applications-through-posting-routines.md | 40 ++++ ...edger-changes-through-item-journals.bad.al | 37 +++ ...dger-changes-through-item-journals.good.al | 25 ++ ...em-ledger-changes-through-item-journals.md | 43 ++++ ...tion-through-the-item-journal-batch.bad.al | 22 ++ ...ion-through-the-item-journal-batch.good.al | 21 ++ ...aluation-through-the-item-journal-batch.md | 39 ++++ ...ough-shipment-and-receipt-codeunits.bad.al | 29 +++ ...ugh-shipment-and-receipt-codeunits.good.al | 37 +++ ...-through-shipment-and-receipt-codeunits.md | 42 ++++ ...se-adjustments-with-the-item-ledger.bad.al | 44 ++++ ...e-adjustments-with-the-item-ledger.good.al | 50 ++++ ...ehouse-adjustments-with-the-item-ledger.md | 41 ++++ ...hrough-source-reservation-codeunits.bad.al | 35 +++ ...rough-source-reservation-codeunits.good.al | 24 ++ ...ng-through-source-reservation-codeunits.md | 42 ++++ ...te-aware-availability-for-promising.bad.al | 25 ++ ...e-aware-availability-for-promising.good.al | 33 +++ ...e-date-aware-availability-for-promising.md | 40 ++++ microsoft/skills/review/al-code-review.md | 1 + microsoft/skills/review/al-scm-review.md | 213 ++++++++++++++++++ 34 files changed, 1244 insertions(+), 10 deletions(-) create mode 100644 microsoft/knowledge/scm/cancel-reservations-through-reservation-management.bad.al create mode 100644 microsoft/knowledge/scm/cancel-reservations-through-reservation-management.good.al create mode 100644 microsoft/knowledge/scm/cancel-reservations-through-reservation-management.md create mode 100644 microsoft/knowledge/scm/carry-out-requisition-actions-through-the-standard-workflow.bad.al create mode 100644 microsoft/knowledge/scm/carry-out-requisition-actions-through-the-standard-workflow.good.al create mode 100644 microsoft/knowledge/scm/carry-out-requisition-actions-through-the-standard-workflow.md create mode 100644 microsoft/knowledge/scm/change-item-applications-through-posting-routines.bad.al create mode 100644 microsoft/knowledge/scm/change-item-applications-through-posting-routines.good.al create mode 100644 microsoft/knowledge/scm/change-item-applications-through-posting-routines.md create mode 100644 microsoft/knowledge/scm/post-item-ledger-changes-through-item-journals.bad.al create mode 100644 microsoft/knowledge/scm/post-item-ledger-changes-through-item-journals.good.al create mode 100644 microsoft/knowledge/scm/post-item-ledger-changes-through-item-journals.md create mode 100644 microsoft/knowledge/scm/post-revaluation-through-the-item-journal-batch.bad.al create mode 100644 microsoft/knowledge/scm/post-revaluation-through-the-item-journal-batch.good.al create mode 100644 microsoft/knowledge/scm/post-revaluation-through-the-item-journal-batch.md create mode 100644 microsoft/knowledge/scm/post-transfers-through-shipment-and-receipt-codeunits.bad.al create mode 100644 microsoft/knowledge/scm/post-transfers-through-shipment-and-receipt-codeunits.good.al create mode 100644 microsoft/knowledge/scm/post-transfers-through-shipment-and-receipt-codeunits.md create mode 100644 microsoft/knowledge/scm/reconcile-warehouse-adjustments-with-the-item-ledger.bad.al create mode 100644 microsoft/knowledge/scm/reconcile-warehouse-adjustments-with-the-item-ledger.good.al create mode 100644 microsoft/knowledge/scm/reconcile-warehouse-adjustments-with-the-item-ledger.md create mode 100644 microsoft/knowledge/scm/transfer-item-tracking-through-source-reservation-codeunits.bad.al create mode 100644 microsoft/knowledge/scm/transfer-item-tracking-through-source-reservation-codeunits.good.al create mode 100644 microsoft/knowledge/scm/transfer-item-tracking-through-source-reservation-codeunits.md create mode 100644 microsoft/knowledge/scm/use-date-aware-availability-for-promising.bad.al create mode 100644 microsoft/knowledge/scm/use-date-aware-availability-for-promising.good.al create mode 100644 microsoft/knowledge/scm/use-date-aware-availability-for-promising.md create mode 100644 microsoft/skills/review/al-scm-review.md diff --git a/.github/scripts/Test-SkillIndex.ps1 b/.github/scripts/Test-SkillIndex.ps1 index ed07204d..dadcabfc 100644 --- a/.github/scripts/Test-SkillIndex.ps1 +++ b/.github/scripts/Test-SkillIndex.ps1 @@ -93,14 +93,15 @@ try { 'microsoft/skills/review/al-query-review.md', 'microsoft/skills/review/al-reporting-review.md', 'microsoft/skills/review/al-appsource-review.md', - 'microsoft/skills/review/al-telemetry-review.md' + 'microsoft/skills/review/al-telemetry-review.md', + 'microsoft/skills/review/al-scm-review.md' ) $review = @($skills | Where-Object id -eq 'al-code-review') if ($review.Count -ne 1) { throw "Expected exactly one al-code-review record, found $($review.Count)." } if ((@($review[0].subSkills) -join "`n") -cne ($expectedLeaves -join "`n")) { - throw 'al-code-review subSkills did not preserve the declared 17-leaf order.' + throw "al-code-review subSkills did not preserve the declared $($expectedLeaves.Count)-leaf order." } foreach ($leafPath in $expectedLeaves) { $leaf = @($skills | Where-Object path -ceq $leafPath) @@ -109,6 +110,28 @@ try { } } + $scm = @($skills | Where-Object id -eq 'al-scm-review') + if ($scm.Count -ne 1 -or + (@($scm[0].inputs) -join ',') -cne 'pr-diff,file-path,folder-path' -or + (@($scm[0].filters.technologies) -join ',') -cne 'al') { + throw 'SCM must be discoverable as an AL leaf accepting diffs, files, and complete folders.' + } + $scmText = Get-Content -LiteralPath (Join-Path $Root $scm[0].path) -Raw + $scmExamples = [regex]::Matches($scmText, '(?s)```json\s*(\{.*?\})\s*```') + if ($scmExamples.Count -ne 2) { + throw "Expected two SCM findings-report examples, found $($scmExamples.Count)." + } + foreach ($example in $scmExamples) { + if (-not ($example.Groups[1].Value | Test-Json -SchemaFile $reportSchema -ErrorAction Stop)) { + throw 'An SCM output example does not satisfy schemas/findings-report.schema.json.' + } + $report = $example.Groups[1].Value | ConvertFrom-Json + if ($report.skill.id -cne 'al-scm-review' -or + @($report.findings | Where-Object domain -cne 'Supply Chain Management').Count) { + throw 'SCM output examples must retain the leaf id and complete display domain.' + } + } + $minimalReport = @{ skill = @{ id = 'al-style-review'; version = 1 } outcome = 'completed' @@ -238,4 +261,4 @@ finally { Remove-Item -LiteralPath $tmp -Recurse -Force -ErrorAction SilentlyContinue } -Write-Output 'Skill-index check PASSED: deterministic, schema-valid, and all 17 review leaves preserved in order.' +Write-Output "Skill-index check PASSED: deterministic, schema-valid, and all $($expectedLeaves.Count) review leaves preserved in order." diff --git a/README.md b/README.md index daea4050..f29d7144 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,8 @@ the review can still discover knowledge by reading the folders. ## Scope -Today's curated content focuses on **technical AL code review**. It augments +Today's curated content covers **technical AL code review** and a focused +**Supply Chain Management (SCM)** functional domain. It augments the agent's judgment; it is not an exhaustive BC manual or a substitute for compilation, analyzers, tests, or human review. See [coverage and limits](docs/using-bcquality.md#coverage-and-limits) for the @@ -84,9 +85,11 @@ available domains and the difference between a folder review and a comparison. Mechanical issues already enforced by the AL compiler or standard analyzers are intentionally left to those deterministic tools rather than duplicated here. -Functional areas such as Finance, Supply Chain Management, Manufacturing, Jobs, -Warehousing, and Service, and technologies such as PowerShell, pipelines, and -Power Platform, remain valid future scope, **not current coverage claims**. +The [SCM domain](microsoft/knowledge/scm/) covers selected inventory, costing, +reservation, tracking, and warehouse/posting workflows, not exhaustive supply +chain validation. Broader functional coverage such as Finance, Manufacturing, +Jobs, and Service, and technologies such as PowerShell, pipelines, and Power +Platform, remain valid future scope, **not current coverage claims**. ## What's in this repo diff --git a/docs/using-bcquality.md b/docs/using-bcquality.md index e55751f9..60526d7c 100644 --- a/docs/using-bcquality.md +++ b/docs/using-bcquality.md @@ -85,6 +85,7 @@ branch names with ones in your project. | Uncommitted changes | Use the installed al-code-review skill to review my staged and unstaged tracked changes against HEAD, without changing files. Identify any untracked AL files not included in that diff. | | Branch changes | Use the installed al-code-review skill to review changes on this branch since its merge base with `origin/main`. Exclude uncommitted changes and do not edit files. | | Focused review | Use the installed al-code-review skill to review performance in the app in this folder, without changing files. Return the complete performance findings report. | +| Supply chain code | Use the installed al-code-review skill to review SCM posting, inventory, reservations, item tracking, and warehouse workflows in this app folder, without changing files. Return the complete Supply Chain Management findings report. | | Agent SDK code | Use the installed al-code-review skill to review Agent SDK implementation and usage in this app folder, without changing files. Return the complete Agents findings report. | For Git comparisons, the named base ref must exist locally. If it is missing, @@ -183,7 +184,7 @@ using your normal compilation, analyzer, test, and human-review workflow. ## Coverage and limits -The Microsoft broad review composes the 17 Microsoft domains listed below. +The Microsoft broad review composes the Microsoft domains listed below. The Community Agents review is a separate skill selected by the request, not a nested part of that coordinator. All current review leaves accept app folders, files, and diffs; request an Agent SDK review explicitly when that @@ -193,8 +194,16 @@ Available knowledge is **not** a promise that every rule will run. Selection depends on the task, target context, enabled layers, and source evidence. A whole-folder review is a current-state snapshot: detecting a published API removal or another comparison-only regression requires an actual baseline. -The corpus is technical AL guidance, not exhaustive functional validation or -AppSource certification. +The corpus combines technical AL guidance with a focused SCM functional +domain, not exhaustive functional validation or AppSource certification. + +The SCM leaf owns selected inventory/value, application, reservation, tracking, +and warehouse/posting invariants. It prunes unrelated AL using the actual +tables, codeunits, fields, and operations in scope; an item caption or a broad +`ApplicationArea` alone is not an SCM review signal. Missing workflow context +must not be replaced with an assumed posting defect. Manufacturing, assembly, +planning, and other supply-chain areas are covered only where an article +explicitly names the shared interface or invariant. BCQuality intentionally does not duplicate mechanical diagnostics already enforced by the AL compiler or standard analyzers. Run the consuming app's @@ -222,6 +231,7 @@ Each article describes one concern. Where samples exist, use its linked | Reporting | [Reporting](../microsoft/knowledge/reporting/) | | Security | [Security](../microsoft/knowledge/security/) | | Style | [Style](../microsoft/knowledge/style/) | +| Supply Chain Management | [SCM](../microsoft/knowledge/scm/) | | Telemetry | [Telemetry](../microsoft/knowledge/telemetry/) | | Testing | [Testing](../microsoft/knowledge/testing/) | | User interface | [UI](../microsoft/knowledge/ui/) | diff --git a/evaluation/README.md b/evaluation/README.md index 2125ce39..05b5e525 100644 --- a/evaluation/README.md +++ b/evaluation/README.md @@ -6,6 +6,16 @@ The evaluation is convention-driven. The harness discovers every `/skills Model-facing preparation hashes case IDs, neutralizes `Good`/`Bad` object-name tokens, and removes full-line sample comments so neither the article slug, domain, nor expected outcome reveals the answer. +The SCM `articles` override deliberately selects every rule in the initial +functional domain, producing nine positive cases and nine clean controls. +Business context is executable: document/status `TestField` guards, +calculated-revaluation fields, source-transfer base quantities, warehouse +reconciliation steps, and additional-demand promising parameters survive +neutralization. Do not move those preconditions into comments or generic +"posting" helper names; removing them can turn a real defect into a valid +alternative workflow. The clean pairs exercise the supported APIs selected by +the same routing cues, not merely unrelated code that contains no SCM tokens. + ## Validate the corpus ```powershell diff --git a/evaluation/review-fixtures.json b/evaluation/review-fixtures.json index 90c42c62..b4a17780 100644 --- a/evaluation/review-fixtures.json +++ b/evaluation/review-fixtures.json @@ -51,6 +51,19 @@ "stop-when-runrequestpage-returns-empty-parameters" ] }, + "scm": { + "articles": [ + "post-item-ledger-changes-through-item-journals", + "post-revaluation-through-the-item-journal-batch", + "change-item-applications-through-posting-routines", + "cancel-reservations-through-reservation-management", + "transfer-item-tracking-through-source-reservation-codeunits", + "reconcile-warehouse-adjustments-with-the-item-ledger", + "post-transfers-through-shipment-and-receipt-codeunits", + "use-date-aware-availability-for-promising", + "carry-out-requisition-actions-through-the-standard-workflow" + ] + }, "style": { "article": "label-comment-explains-placeholders" }, diff --git a/microsoft/knowledge/scm/cancel-reservations-through-reservation-management.bad.al b/microsoft/knowledge/scm/cancel-reservations-through-reservation-management.bad.al new file mode 100644 index 00000000..8d1e498a --- /dev/null +++ b/microsoft/knowledge/scm/cancel-reservations-through-reservation-management.bad.al @@ -0,0 +1,18 @@ +namespace BCQuality.SCM.Samples; + +using Microsoft.Inventory.Tracking; +using Microsoft.Sales.Document; + +codeunit 50106 "SCM Cancel Reservation Bad" +{ + procedure CancelSalesReservation(ReservationEntryNo: Integer) + var + ReservationEntry: Record "Reservation Entry"; + begin + ReservationEntry.Get(ReservationEntryNo, false); + ReservationEntry.TestField("Source Type", Database::"Sales Line"); + ReservationEntry.TestField("Reservation Status", ReservationEntry."Reservation Status"::Reservation); + + ReservationEntry.Delete(true); + end; +} diff --git a/microsoft/knowledge/scm/cancel-reservations-through-reservation-management.good.al b/microsoft/knowledge/scm/cancel-reservations-through-reservation-management.good.al new file mode 100644 index 00000000..91b39d75 --- /dev/null +++ b/microsoft/knowledge/scm/cancel-reservations-through-reservation-management.good.al @@ -0,0 +1,19 @@ +namespace BCQuality.SCM.Samples; + +using Microsoft.Inventory.Tracking; +using Microsoft.Sales.Document; + +codeunit 50107 "SCM Cancel Reservation Good" +{ + procedure CancelSalesReservation(ReservationEntryNo: Integer) + var + ReservationEntry: Record "Reservation Entry"; + ReservationEngineMgt: Codeunit "Reservation Engine Mgt."; + begin + ReservationEntry.Get(ReservationEntryNo, false); + ReservationEntry.TestField("Source Type", Database::"Sales Line"); + ReservationEntry.TestField("Reservation Status", ReservationEntry."Reservation Status"::Reservation); + + ReservationEngineMgt.CancelReservation(ReservationEntry); + end; +} diff --git a/microsoft/knowledge/scm/cancel-reservations-through-reservation-management.md b/microsoft/knowledge/scm/cancel-reservations-through-reservation-management.md new file mode 100644 index 00000000..76857018 --- /dev/null +++ b/microsoft/knowledge/scm/cancel-reservations-through-reservation-management.md @@ -0,0 +1,39 @@ +--- +bc-version: [all] +domain: scm +keywords: [reservation-entry, cancelreservation, reservation-engine-mgt, reservation-status, order-tracking, disallow-cancellation] +technologies: [al] +countries: [w1] +application-area: [all] +--- + +# Cancel reservations through reservation management + +## Description + +Persistent `"Reservation Entry"` rows are not disposable allocation markers. Reservation and Tracking links use an entry-number pair with opposite `Positive` values, while Surplus and Prospect entries can legitimately be unpaired. Cancelling a binding reservation must handle its counterpart and any remaining item tracking or order tracking, not just remove a row. + +## Best Practice + +For explicit cancellation of an existing binding reservation, use `"Reservation Engine Mgt.".CancelReservation`. It checks the reservation status and `"Disallow Cancellation"`, handles the counterpart, and preserves or retracks the remaining source quantities as appropriate. For source-line quantity changes, use the source-specific reservation management path rather than deleting its reservation rows yourself. + +Do not require every Reservation Entry to have a partner or identical lot/serial values on both sides: Surplus/Prospect entries and supported late-binding scenarios need different treatment. Temporary buffers, engine-owned updates, and supported publisher metadata are not independent cancellation. Cancelling a reservation is also different from intentionally removing an item-tracking assignment. + +The samples retrieve the negative side of a persistent sales-line reservation and cancel only the binding. They do not delete the sales line or remove its tracking specifications. + +## Anti Pattern + +Report `Delete(true)`, `DeleteAll`, or a status/source rewrite on persistent `"Reservation Entry"` records used as a replacement for cancelling a reservation. Deleting both sides is not a sufficient repair: it can still discard tracking that should survive and omit retracking. + +Require cancellation intent and a binding reservation in the visible context. Do not flag normal processing of temporary Prospect/Surplus buffers or diagnose every single row as an orphan. + +## Samples + +- [`cancel-reservations-through-reservation-management.bad.al`](cancel-reservations-through-reservation-management.bad.al) +- [`cancel-reservations-through-reservation-management.good.al`](cancel-reservations-through-reservation-management.good.al) + +## References + +- [Reservation, order tracking, and action messaging](https://learn.microsoft.com/en-us/dynamics365/business-central/design-details-reservation-order-tracking-and-action-messaging) +- [Item tracking and reservations](https://learn.microsoft.com/en-us/dynamics365/business-central/design-details-item-tracking-and-reservations) +- [BaseApp reservation cancellation](https://github.com/microsoft/BCApps/blob/8f7a04cb0db8aa96cb97e055c45c61aead49e280/src/Layers/W1/BaseApp/Inventory/Tracking/ReservationEngineMgt.Codeunit.al#L51-L90) diff --git a/microsoft/knowledge/scm/carry-out-requisition-actions-through-the-standard-workflow.bad.al b/microsoft/knowledge/scm/carry-out-requisition-actions-through-the-standard-workflow.bad.al new file mode 100644 index 00000000..be35a154 --- /dev/null +++ b/microsoft/knowledge/scm/carry-out-requisition-actions-through-the-standard-workflow.bad.al @@ -0,0 +1,53 @@ +namespace BCQuality.SCM.Samples; + +using Microsoft.Inventory.Requisition; +using Microsoft.Purchases.Document; +using Microsoft.Sales.Document; + +codeunit 50116 "SCM Requisition Action Bad" +{ + procedure CarryOutAcceptedNewPurchase(TemplateName: Code[10]; BatchName: Code[10]; LineNo: Integer; OrderDate: Date; PostingDate: Date; ReceiptDate: Date; CutoffDate: Date) + var + RequisitionLine: Record "Requisition Line"; + PurchaseHeader: Record "Purchase Header"; + PurchaseLine: Record "Purchase Line"; + begin + if (OrderDate = 0D) or (PostingDate = 0D) or (ReceiptDate = 0D) or (CutoffDate = 0D) then + Error(PlanningDatesErr); + RequisitionLine.Get(TemplateName, BatchName, LineNo); + RequisitionLine.TestField(Type, RequisitionLine.Type::Item); + RequisitionLine.TestField("Replenishment System", RequisitionLine."Replenishment System"::Purchase); + RequisitionLine.TestField("Action Message", RequisitionLine."Action Message"::New); + RequisitionLine.TestField("Accept Action Message", true); + RequisitionLine.TestField("Demand Type", Database::"Sales Line"); + RequisitionLine.TestField("Demand Order No."); + RequisitionLine.TestField("Vendor No."); + RequisitionLine.SetRecFilter(); + + PurchaseHeader.Init(); + PurchaseHeader."Document Type" := PurchaseHeader."Document Type"::Order; + PurchaseHeader.Insert(true); + PurchaseHeader.Validate("Buy-from Vendor No.", RequisitionLine."Vendor No."); + PurchaseHeader.Validate("Order Date", OrderDate); + PurchaseHeader.Validate("Posting Date", PostingDate); + PurchaseHeader.Validate("Expected Receipt Date", ReceiptDate); + PurchaseHeader.Modify(true); + + PurchaseLine.Init(); + PurchaseLine."Document Type" := PurchaseHeader."Document Type"; + PurchaseLine."Document No." := PurchaseHeader."No."; + PurchaseLine."Line No." := 10000; + PurchaseLine.Validate(Type, PurchaseLine.Type::Item); + PurchaseLine.Validate("No.", RequisitionLine."No."); + PurchaseLine.Validate("Location Code", RequisitionLine."Location Code"); + PurchaseLine.Validate("Variant Code", RequisitionLine."Variant Code"); + PurchaseLine.Validate("Unit of Measure Code", RequisitionLine."Unit of Measure Code"); + PurchaseLine.Validate(Quantity, RequisitionLine.Quantity); + PurchaseLine.Insert(true); + + RequisitionLine.Delete(true); + end; + + var + PlanningDatesErr: Label 'Supply explicit order, posting, receipt, and cutoff dates.'; +} diff --git a/microsoft/knowledge/scm/carry-out-requisition-actions-through-the-standard-workflow.good.al b/microsoft/knowledge/scm/carry-out-requisition-actions-through-the-standard-workflow.good.al new file mode 100644 index 00000000..fd2e7743 --- /dev/null +++ b/microsoft/knowledge/scm/carry-out-requisition-actions-through-the-standard-workflow.good.al @@ -0,0 +1,37 @@ +namespace BCQuality.SCM.Samples; + +using Microsoft.Inventory.Requisition; +using Microsoft.Purchases.Document; +using Microsoft.Sales.Document; + +codeunit 50117 "SCM Requisition Action Good" +{ + procedure CarryOutAcceptedNewPurchase(TemplateName: Code[10]; BatchName: Code[10]; LineNo: Integer; OrderDate: Date; PostingDate: Date; ReceiptDate: Date; CutoffDate: Date) + var + RequisitionLine: Record "Requisition Line"; + PurchaseHeaderDefaults: Record "Purchase Header"; + ReqWkshMakeOrder: Codeunit "Req. Wksh.-Make Order"; + begin + if (OrderDate = 0D) or (PostingDate = 0D) or (ReceiptDate = 0D) or (CutoffDate = 0D) then + Error(PlanningDatesErr); + RequisitionLine.Get(TemplateName, BatchName, LineNo); + RequisitionLine.TestField(Type, RequisitionLine.Type::Item); + RequisitionLine.TestField("Replenishment System", RequisitionLine."Replenishment System"::Purchase); + RequisitionLine.TestField("Action Message", RequisitionLine."Action Message"::New); + RequisitionLine.TestField("Accept Action Message", true); + RequisitionLine.TestField("Demand Type", Database::"Sales Line"); + RequisitionLine.TestField("Demand Order No."); + RequisitionLine.TestField("Vendor No."); + RequisitionLine.SetRecFilter(); + + PurchaseHeaderDefaults."Order Date" := OrderDate; + PurchaseHeaderDefaults."Posting Date" := PostingDate; + PurchaseHeaderDefaults."Expected Receipt Date" := ReceiptDate; + ReqWkshMakeOrder.Set(PurchaseHeaderDefaults, CutoffDate, false); + ReqWkshMakeOrder.SetSuppressCommit(true); + ReqWkshMakeOrder.CarryOutBatchAction(RequisitionLine); + end; + + var + PlanningDatesErr: Label 'Supply explicit order, posting, receipt, and cutoff dates.'; +} diff --git a/microsoft/knowledge/scm/carry-out-requisition-actions-through-the-standard-workflow.md b/microsoft/knowledge/scm/carry-out-requisition-actions-through-the-standard-workflow.md new file mode 100644 index 00000000..bd3c38ea --- /dev/null +++ b/microsoft/knowledge/scm/carry-out-requisition-actions-through-the-standard-workflow.md @@ -0,0 +1,44 @@ +--- +bc-version: [all] +domain: scm +keywords: [requisition-line, action-message, accept-action-message, req-wksh-make-order, carryoutbatchaction, demand-order-no, planning-flexibility] +technologies: [al] +countries: [w1] +application-area: [all] +--- + +# Carry out requisition actions through the standard workflow + +## Description + +A requisition/planning line is a pending change to a supply/demand network, not just a template for a purchase line. Carry-out interprets New, change-quantity, reschedule, and cancel actions, preserves referenced supply and planning flexibility, and moves reservation/tracking ownership before finalizing the proposal. Creating a plausible purchase order and deleting the requisition line can leave new supply unrelated to the demand that caused it. + +## Best Practice + +For requisition batch carry-out, initialize `"Req. Wksh.-Make Order"` with `Set` and invoke `CarryOutBatchAction` on the intended accepted lines. Supply the order/posting/receipt defaults separately from the ending-order-date cutoff, and preserve the selected worksheet/batch/line filters. A plain `Run` or a single order-line insertion helper is not a replacement for this batch initialization and finalization. + +Use the standard `"Carry Out Action"` dispatch for broader planning output and its configured purchase, transfer, assembly, or manufacturing choices. Do not turn every action into a new purchase order, bypass source-specific reservation transfer, or delete proposals before the owning workflow has completed their supply change. + +Ordinary manual purchase creation that does not consume planning output is outside this rule. Users may reject or delete unwanted proposals without creating supply; temporary planning simulations, pre-carry-out enrichment, and engine-owned cleanup are also legitimate. `Delete(true)` on a requisition line is not intrinsically a defect. + +The samples select an existing accepted New/Purchase item proposal with sales-demand context. Dates are explicit, the source selection remains bounded, and the clean sample leaves order creation and reservation handoff to the standard workflow; it is not a complete planning-run generator. + +## Anti Pattern + +Report code that consumes accepted persistent `"Requisition Line"` action messages, manually creates or changes supply from a subset of fields, and then deletes or marks the proposal handled without the standard carry-out/source-reservation handoff. Running purchase-field validation and the requisition delete trigger does not first move the proposal's demand links to the new purchase line. + +Require both proposal-consumption intent and a visible supply conversion. Do not flag an isolated deletion of an unwanted suggestion, an ordinary purchase-order API, or the standard carry-out engine's own insert/delete sequence. + +## Samples + +- [`carry-out-requisition-actions-through-the-standard-workflow.bad.al`](carry-out-requisition-actions-through-the-standard-workflow.bad.al) +- [`carry-out-requisition-actions-through-the-standard-workflow.good.al`](carry-out-requisition-actions-through-the-standard-workflow.good.al) + +## References + +- [Perform planning action messages](https://learn.microsoft.com/en-us/dynamics365/business-central/production-how-to-run-mps-and-mrp#to-perform-action-messages) +- [Planning functionality](https://learn.microsoft.com/en-us/dynamics365/business-central/production-about-planning-functionality) +- [Reservation, order tracking, and action messaging](https://learn.microsoft.com/en-us/dynamics365/business-central/design-details-reservation-order-tracking-and-action-messaging) +- [BaseApp carry-out caller and date defaults](https://github.com/microsoft/BCApps/blob/8f7a04cb0db8aa96cb97e055c45c61aead49e280/src/Layers/W1/BaseApp/Inventory/Requisition/CarryOutActionMsgReq.Report.al#L116-L133) +- [Batch initialization and selection](https://github.com/microsoft/BCApps/blob/8f7a04cb0db8aa96cb97e055c45c61aead49e280/src/Layers/W1/BaseApp/Inventory/Requisition/ReqWkshMakeOrder.Codeunit.al#L116-L215) +- [Reservation handoff before supply finalization](https://github.com/microsoft/BCApps/blob/8f7a04cb0db8aa96cb97e055c45c61aead49e280/src/Layers/W1/BaseApp/Inventory/Requisition/ReqWkshMakeOrder.Codeunit.al#L663-L743) diff --git a/microsoft/knowledge/scm/change-item-applications-through-posting-routines.bad.al b/microsoft/knowledge/scm/change-item-applications-through-posting-routines.bad.al new file mode 100644 index 00000000..e5ec9cbd --- /dev/null +++ b/microsoft/knowledge/scm/change-item-applications-through-posting-routines.bad.al @@ -0,0 +1,37 @@ +namespace BCQuality.SCM.Samples; + +using Microsoft.Inventory.Ledger; + +codeunit 50104 "SCM Item Application Bad" +{ + procedure ChangeSalesQuantityApplication(ApplicationEntryNo: Integer; NewInboundEntryNo: Integer) + var + ItemApplicationEntry: Record "Item Application Entry"; + OutboundItemLedgerEntry: Record "Item Ledger Entry"; + InboundItemLedgerEntry: Record "Item Ledger Entry"; + begin + ItemApplicationEntry.Get(ApplicationEntryNo); + ItemApplicationEntry.TestField(Quantity); + ItemApplicationEntry.TestField("Inbound Item Entry No."); + ItemApplicationEntry.TestField("Outbound Item Entry No."); + ItemApplicationEntry.TestField("Transferred-from Entry No.", 0); + if ItemApplicationEntry.CostApplication() then + Error(QuantityApplicationErr); + OutboundItemLedgerEntry.Get(ItemApplicationEntry."Outbound Item Entry No."); + OutboundItemLedgerEntry.TestField("Entry Type", OutboundItemLedgerEntry."Entry Type"::Sale); + OutboundItemLedgerEntry.TestField(Positive, false); + OutboundItemLedgerEntry.TestField("Drop Shipment", false); + OutboundItemLedgerEntry.TestField(Correction, false); + InboundItemLedgerEntry.Get(NewInboundEntryNo); + InboundItemLedgerEntry.TestField(Positive, true); + InboundItemLedgerEntry.TestField("Item No.", OutboundItemLedgerEntry."Item No."); + InboundItemLedgerEntry.TestField("Variant Code", OutboundItemLedgerEntry."Variant Code"); + InboundItemLedgerEntry.TestField("Location Code", OutboundItemLedgerEntry."Location Code"); + + ItemApplicationEntry."Inbound Item Entry No." := NewInboundEntryNo; + ItemApplicationEntry.Modify(true); + end; + + var + QuantityApplicationErr: Label 'Select an ordinary quantity application, not a cost application.'; +} diff --git a/microsoft/knowledge/scm/change-item-applications-through-posting-routines.good.al b/microsoft/knowledge/scm/change-item-applications-through-posting-routines.good.al new file mode 100644 index 00000000..bca8ea63 --- /dev/null +++ b/microsoft/knowledge/scm/change-item-applications-through-posting-routines.good.al @@ -0,0 +1,45 @@ +namespace BCQuality.SCM.Samples; + +using Microsoft.Inventory.Ledger; +using Microsoft.Inventory.Posting; + +codeunit 50105 "SCM Item Application Good" +{ + procedure ChangeSalesQuantityApplication(ApplicationEntryNo: Integer; NewInboundEntryNo: Integer) + var + ItemApplicationEntry: Record "Item Application Entry"; + OutboundItemLedgerEntry: Record "Item Ledger Entry"; + InboundItemLedgerEntry: Record "Item Ledger Entry"; + ItemJnlPostLine: Codeunit "Item Jnl.-Post Line"; + OutboundEntryNo: Integer; + begin + ItemApplicationEntry.Get(ApplicationEntryNo); + ItemApplicationEntry.TestField(Quantity); + ItemApplicationEntry.TestField("Inbound Item Entry No."); + ItemApplicationEntry.TestField("Outbound Item Entry No."); + ItemApplicationEntry.TestField("Transferred-from Entry No.", 0); + if ItemApplicationEntry.CostApplication() then + Error(QuantityApplicationErr); + OutboundEntryNo := ItemApplicationEntry."Outbound Item Entry No."; + OutboundItemLedgerEntry.Get(OutboundEntryNo); + OutboundItemLedgerEntry.TestField("Entry Type", OutboundItemLedgerEntry."Entry Type"::Sale); + OutboundItemLedgerEntry.TestField(Positive, false); + OutboundItemLedgerEntry.TestField("Drop Shipment", false); + OutboundItemLedgerEntry.TestField(Correction, false); + InboundItemLedgerEntry.Get(NewInboundEntryNo); + InboundItemLedgerEntry.TestField(Positive, true); + InboundItemLedgerEntry.TestField("Item No.", OutboundItemLedgerEntry."Item No."); + InboundItemLedgerEntry.TestField("Variant Code", OutboundItemLedgerEntry."Variant Code"); + InboundItemLedgerEntry.TestField("Location Code", OutboundItemLedgerEntry."Location Code"); + + ItemJnlPostLine.UnApply(ItemApplicationEntry); + OutboundItemLedgerEntry.Get(OutboundEntryNo); + ItemJnlPostLine.ReApply(OutboundItemLedgerEntry, NewInboundEntryNo); + ItemJnlPostLine.RedoApplications(); + ItemJnlPostLine.CostAdjust(); + ItemJnlPostLine.ClearApplicationLog(); + end; + + var + QuantityApplicationErr: Label 'Select an ordinary quantity application, not a cost application.'; +} diff --git a/microsoft/knowledge/scm/change-item-applications-through-posting-routines.md b/microsoft/knowledge/scm/change-item-applications-through-posting-routines.md new file mode 100644 index 00000000..69a1aed8 --- /dev/null +++ b/microsoft/knowledge/scm/change-item-applications-through-posting-routines.md @@ -0,0 +1,40 @@ +--- +bc-version: [all] +domain: scm +keywords: [item-application-entry, inbound-item-entry-no, unapply, reapply, redoapplications, costadjust, application-worksheet] +technologies: [al] +countries: [w1] +application-area: [all] +--- + +# Change item applications through posting routines + +## Description + +An `"Item Application Entry"` connects quantity application to cost flow; changing its inbound entry number is not merely fixing a foreign key. Unapplication/reapplication also affects ledger remaining quantities, open states, valuation, and entries needing cost adjustment. Direct edits can leave a plausible application row attached to inconsistent inventory and costs. + +## Best Practice + +Prefer the Application Worksheet for interactive corrections. For a narrowly controlled programmatic correction of an ordinary quantity application, use the same `"Item Jnl.-Post Line"` instance for `UnApply`, reload the affected outbound item entry, then `ReApply` it to the compatible inbound entry. Complete the application's `RedoApplications`, `CostAdjust`, and `ClearApplicationLog` lifecycle; do not commit a half-completed replacement. + +Respect the posting routines' inventory-period, correction, transfer, and drop-shipment restrictions rather than bypassing them. `"Transferred-from Entry No."`, outbound transfers, and special application types are not permission to reuse the ordinary-sales sample unchecked. Do not enable application-check bypasses or borrow the worksheet's multi-step recovery flags for a standalone transaction. + +`CostAdjust` honors automatic-cost-adjustment setup; calling it does not promise that all costs are settled when adjustment is disabled or deferred. Retain the required scheduled/manual adjustment process. Temporary application projections, extension metadata, and source-document reservation/order-tracking changes are not edits to the persistent item-application graph. + +## Anti Pattern + +Report independent `Modify`, `Delete`, or replacement `Insert` operations on persistent `"Item Application Entry"` rows used to repoint a receipt/shipment application, including a change to `"Inbound Item Entry No."` that leaves remaining quantities and cost propagation untouched. Valid item numbers, matching quantities, or running table triggers do not complete reapplication. + +Also report a visibly incomplete custom unapply/reapply transaction that omits finalization or commits between the two operations. Do not flag code merely because the standard posting/application workflow internally writes these tables. + +## Samples + +- [`change-item-applications-through-posting-routines.bad.al`](change-item-applications-through-posting-routines.bad.al) +- [`change-item-applications-through-posting-routines.good.al`](change-item-applications-through-posting-routines.good.al) + +## References + +- [Item application design](https://learn.microsoft.com/en-us/dynamics365/business-central/design-details-item-application) +- [Cost adjustment design](https://learn.microsoft.com/en-us/dynamics365/business-central/design-details-cost-adjustment) +- [BaseApp application finalization sequence](https://github.com/microsoft/BCApps/blob/8f7a04cb0db8aa96cb97e055c45c61aead49e280/src/Layers/W1/BaseApp/Inventory/Ledger/ApplicationWorksheet.Page.al#L495-L503) +- [BaseApp reapplication and cost-adjustment lifecycle](https://github.com/microsoft/BCApps/blob/8f7a04cb0db8aa96cb97e055c45c61aead49e280/src/Layers/W1/BaseApp/Inventory/Posting/ItemJnlPostLine.Codeunit.al#L5464-L5538) diff --git a/microsoft/knowledge/scm/post-item-ledger-changes-through-item-journals.bad.al b/microsoft/knowledge/scm/post-item-ledger-changes-through-item-journals.bad.al new file mode 100644 index 00000000..3088a43c --- /dev/null +++ b/microsoft/knowledge/scm/post-item-ledger-changes-through-item-journals.bad.al @@ -0,0 +1,37 @@ +namespace BCQuality.SCM.Samples; + +using Microsoft.Inventory.Journal; +using Microsoft.Inventory.Ledger; + +codeunit 50100 "SCM Stock Adjustment Bad" +{ + procedure PostPreparedPositiveAdjustment(ItemJournalLine: Record "Item Journal Line"; NewEntryNo: Integer) + var + ItemLedgerEntry: Record "Item Ledger Entry"; + begin + ItemJournalLine.TestField("Entry Type", ItemJournalLine."Entry Type"::"Positive Adjmt."); + ItemJournalLine.TestField("Value Entry Type", ItemJournalLine."Value Entry Type"::"Direct Cost"); + ItemJournalLine.TestField("Item No."); + ItemJournalLine.TestField("Posting Date"); + ItemJournalLine.TestField("Quantity (Base)"); + if ItemJournalLine."Quantity (Base)" < 0 then + Error(PositiveQuantityErr); + + ItemLedgerEntry.Init(); + ItemLedgerEntry."Entry No." := NewEntryNo; + ItemLedgerEntry."Item No." := ItemJournalLine."Item No."; + ItemLedgerEntry."Entry Type" := ItemJournalLine."Entry Type"; + ItemLedgerEntry."Posting Date" := ItemJournalLine."Posting Date"; + ItemLedgerEntry."Document No." := ItemJournalLine."Document No."; + ItemLedgerEntry."Location Code" := ItemJournalLine."Location Code"; + ItemLedgerEntry."Variant Code" := ItemJournalLine."Variant Code"; + ItemLedgerEntry.Quantity := ItemJournalLine."Quantity (Base)"; + ItemLedgerEntry."Remaining Quantity" := ItemLedgerEntry.Quantity; + ItemLedgerEntry.Positive := true; + ItemLedgerEntry.Open := true; + ItemLedgerEntry.Insert(true); + end; + + var + PositiveQuantityErr: Label 'The prepared adjustment must increase inventory.'; +} diff --git a/microsoft/knowledge/scm/post-item-ledger-changes-through-item-journals.good.al b/microsoft/knowledge/scm/post-item-ledger-changes-through-item-journals.good.al new file mode 100644 index 00000000..b7c4345f --- /dev/null +++ b/microsoft/knowledge/scm/post-item-ledger-changes-through-item-journals.good.al @@ -0,0 +1,25 @@ +namespace BCQuality.SCM.Samples; + +using Microsoft.Inventory.Journal; +using Microsoft.Inventory.Posting; + +codeunit 50101 "SCM Stock Adjustment Good" +{ + procedure PostPreparedPositiveAdjustment(var ItemJournalLine: Record "Item Journal Line") + var + ItemJnlPostLine: Codeunit "Item Jnl.-Post Line"; + begin + ItemJournalLine.TestField("Entry Type", ItemJournalLine."Entry Type"::"Positive Adjmt."); + ItemJournalLine.TestField("Value Entry Type", ItemJournalLine."Value Entry Type"::"Direct Cost"); + ItemJournalLine.TestField("Item No."); + ItemJournalLine.TestField("Posting Date"); + ItemJournalLine.TestField("Quantity (Base)"); + if ItemJournalLine."Quantity (Base)" < 0 then + Error(PositiveQuantityErr); + + ItemJnlPostLine.RunWithCheck(ItemJournalLine); + end; + + var + PositiveQuantityErr: Label 'The prepared adjustment must increase inventory.'; +} diff --git a/microsoft/knowledge/scm/post-item-ledger-changes-through-item-journals.md b/microsoft/knowledge/scm/post-item-ledger-changes-through-item-journals.md new file mode 100644 index 00000000..234b91e1 --- /dev/null +++ b/microsoft/knowledge/scm/post-item-ledger-changes-through-item-journals.md @@ -0,0 +1,43 @@ +--- +bc-version: [all] +domain: scm +keywords: [item-ledger-entry, value-entry, item-journal-line, item-jnl-post-line, runwithcheck, inventory-posting] +technologies: [al] +countries: [w1] +application-area: [all] +--- + +# Post item ledger changes through item journals + +## Description + +An item ledger entry is not an independently insertable stock balance. Posting connects its quantity to item applications, reservations, tracking, and one or more value entries; expected cost, invoicing, revaluation, and later cost adjustment can produce different value entries for the same item entry. Running a table's insert trigger does not run this posting workflow or its configured inventory-to-G/L integration. + +## Best Practice + +For a prepared standalone item-journal movement, enter through codeunit `"Item Jnl.-Post Line".RunWithCheck`. For a persisted journal batch, use `"Item Jnl.-Post Batch"`; for a sales, purchase, transfer, assembly, or production transaction, retain that workflow's owning document/posting orchestration rather than replacing it with a naked journal call. Let the posting engine create the ledger, value, and application records and perform its checks. + +Extend supported posting events and pass validated journal/source data into the owning workflow. Read-only ledger queries, temporary previews, extension-owned metadata fields, and supported publisher parameters consumed by the poster are not independent ledger posting and must not be flagged merely because they assign record fields. A publisher's `var` parameter or `IsHandled` flag is not blanket authorization to recreate quantity/cost state. A change inside the posting engine itself requires tracing that engine's surrounding invariants, not a ban on its own inserts. + +Do not require every value entry to point to an item ledger entry: capacity and production WIP have their own supported posting relationships. Assembly and manufacturing posting retain order/component/routing and capacity context; one bare output/consumption call is not full order completion. + +The clean sample takes an already prepared positive-adjustment journal line. It is not a substitute for journal preparation, batch revaluation, warehouse reconciliation, or source-document posting. + +## Anti Pattern + +Report extension code that independently inserts/deletes persistent `"Item Ledger Entry"` or `"Value Entry"` transaction rows, or overwrites posted quantity, remaining quantity, application identity, or cost amounts to implement a receipt, shipment, adjustment, or cost correction. `Insert(true)`, `Modify(true)`, and balanced-looking quantities do not supply the missing posting orchestration. + +Require evidence of a persistent transaction mutation and its business purpose; a table declaration or a write to a custom annotation field is insufficient. For a more specific revaluation or application defect, prefer the corresponding SCM article rather than reporting the same correction twice. + +## Samples + +- [`post-item-ledger-changes-through-item-journals.bad.al`](post-item-ledger-changes-through-item-journals.bad.al) +- [`post-item-ledger-changes-through-item-journals.good.al`](post-item-ledger-changes-through-item-journals.good.al) + +## References + +- [Inventory posting design](https://learn.microsoft.com/en-us/dynamics365/business-central/design-details-inventory-posting) +- [Item application design](https://learn.microsoft.com/en-us/dynamics365/business-central/design-details-item-application) +- [BaseApp item-journal posting entry point](https://github.com/microsoft/BCApps/blob/8f7a04cb0db8aa96cb97e055c45c61aead49e280/src/Layers/W1/BaseApp/Inventory/Posting/ItemJnlPostLine.Codeunit.al#L162-L179) +- [Assembly-order posting context](https://learn.microsoft.com/en-us/dynamics365/business-central/design-details-assembly-order-posting) +- [Production-order posting context](https://learn.microsoft.com/en-us/dynamics365/business-central/design-details-production-order-posting) diff --git a/microsoft/knowledge/scm/post-revaluation-through-the-item-journal-batch.bad.al b/microsoft/knowledge/scm/post-revaluation-through-the-item-journal-batch.bad.al new file mode 100644 index 00000000..d646d0b1 --- /dev/null +++ b/microsoft/knowledge/scm/post-revaluation-through-the-item-journal-batch.bad.al @@ -0,0 +1,22 @@ +namespace BCQuality.SCM.Samples; + +using Microsoft.Inventory.Journal; +using Microsoft.Inventory.Posting; + +codeunit 50102 "SCM Revaluation Batch Bad" +{ + procedure PostCalculatedRevaluationBatch(TemplateName: Code[10]; BatchName: Code[10]) + var + ItemJournalLine: Record "Item Journal Line"; + ItemJnlPostLine: Codeunit "Item Jnl.-Post Line"; + begin + ItemJournalLine.SetRange("Journal Template Name", TemplateName); + ItemJournalLine.SetRange("Journal Batch Name", BatchName); + ItemJournalLine.FindSet(); + repeat + ItemJournalLine.TestField("Value Entry Type", ItemJournalLine."Value Entry Type"::Revaluation); + ItemJournalLine.TestField("Inventory Value Per"); + ItemJnlPostLine.RunWithCheck(ItemJournalLine); + until ItemJournalLine.Next() = 0; + end; +} diff --git a/microsoft/knowledge/scm/post-revaluation-through-the-item-journal-batch.good.al b/microsoft/knowledge/scm/post-revaluation-through-the-item-journal-batch.good.al new file mode 100644 index 00000000..0dec6563 --- /dev/null +++ b/microsoft/knowledge/scm/post-revaluation-through-the-item-journal-batch.good.al @@ -0,0 +1,21 @@ +namespace BCQuality.SCM.Samples; + +using Microsoft.Inventory.Journal; +using Microsoft.Inventory.Posting; + +codeunit 50103 "SCM Revaluation Batch Good" +{ + procedure PostCalculatedRevaluationBatch(TemplateName: Code[10]; BatchName: Code[10]) + var + ItemJournalLine: Record "Item Journal Line"; + ItemJnlPostBatch: Codeunit "Item Jnl.-Post Batch"; + begin + ItemJournalLine.SetRange("Journal Template Name", TemplateName); + ItemJournalLine.SetRange("Journal Batch Name", BatchName); + ItemJournalLine.FindFirst(); + ItemJournalLine.TestField("Value Entry Type", ItemJournalLine."Value Entry Type"::Revaluation); + ItemJournalLine.TestField("Inventory Value Per"); + + ItemJnlPostBatch.Run(ItemJournalLine); + end; +} diff --git a/microsoft/knowledge/scm/post-revaluation-through-the-item-journal-batch.md b/microsoft/knowledge/scm/post-revaluation-through-the-item-journal-batch.md new file mode 100644 index 00000000..67c425f5 --- /dev/null +++ b/microsoft/knowledge/scm/post-revaluation-through-the-item-journal-batch.md @@ -0,0 +1,39 @@ +--- +bc-version: [all] +domain: scm +keywords: [revaluation, inventory-value-per, partial-revaluation, item-jnl-post-batch, item-journal-line, runwithcheck, standard-cost] +technologies: [al] +countries: [w1] +application-area: [all] +--- + +# Post calculated revaluation through the item journal batch + +## Description + +A calculated revaluation line with nonblank `"Inventory Value Per"` represents an aggregate, not a finalized posting against one item ledger entry. Codeunit `"Item Jnl.-Post Batch"` distributes that value over eligible entries, handles rounding, and coordinates Item/SKU standard-cost updates. Calling the line poster directly skips that batch work even though the input is a valid `"Item Journal Line"`. + +## Best Practice + +Post a prepared revaluation batch through `"Item Jnl.-Post Batch"`. Keep the calculated line's valuation date, aggregation scope, location/variant filters, and revaluation fields intact. The batch expands summarized values into per-entry postings and checks that the eligible inventory has not changed; for partial revaluation it also rechecks remaining quantity before posting. + +Do not treat the public `"Item Jnl.-Post Line".RunWithCheck` API as a replacement for that orchestration. It remains legitimate for finalized individual-entry revaluation lines within a workflow that already supplies the necessary checks; the batch itself uses the line poster. A call to that API without evidence of summarized or partial revaluation is not this defect. + +The samples explicitly require `"Value Entry Type" = Revaluation` and a nonblank `"Inventory Value Per"` in an existing calculated journal batch. They demonstrate posting, not how to calculate a new valuation or choose a standard cost. + +## Anti Pattern + +Report a loop that sends calculated aggregate revaluation lines straight to `"Item Jnl.-Post Line"`, or a custom partial-revaluation workflow that bypasses the remaining-quantity recheck visible in the standard batch. A loop over the journal is not equivalent to distributing the aggregate over its underlying item entries. + +Do not recommend directly editing existing `"Value Entry"` cost amounts or the Item's unit cost to repair the result. Use the revaluation/cost-adjustment workflow appropriate to the correction. + +## Samples + +- [`post-revaluation-through-the-item-journal-batch.bad.al`](post-revaluation-through-the-item-journal-batch.bad.al) +- [`post-revaluation-through-the-item-journal-batch.good.al`](post-revaluation-through-the-item-journal-batch.good.al) + +## References + +- [Revaluation design](https://learn.microsoft.com/en-us/dynamics365/business-central/design-details-revaluation) +- [Inventory posting design](https://learn.microsoft.com/en-us/dynamics365/business-central/design-details-inventory-posting) +- [BaseApp summarized revaluation and remaining-quantity checks](https://github.com/microsoft/BCApps/blob/8f7a04cb0db8aa96cb97e055c45c61aead49e280/src/Layers/W1/BaseApp/Inventory/Posting/ItemJnlPostBatch.Codeunit.al#L510-L714) diff --git a/microsoft/knowledge/scm/post-transfers-through-shipment-and-receipt-codeunits.bad.al b/microsoft/knowledge/scm/post-transfers-through-shipment-and-receipt-codeunits.bad.al new file mode 100644 index 00000000..04806618 --- /dev/null +++ b/microsoft/knowledge/scm/post-transfers-through-shipment-and-receipt-codeunits.bad.al @@ -0,0 +1,29 @@ +namespace BCQuality.SCM.Samples; + +using Microsoft.Inventory.Journal; +using Microsoft.Inventory.Location; +using Microsoft.Inventory.Posting; +using Microsoft.Inventory.Transfer; + +codeunit 50112 "SCM Transfer Posting Bad" +{ + procedure ShipTransferOrder(TransferOrderNo: Code[20]; var ItemJournalLine: Record "Item Journal Line") + var + TransferHeader: Record "Transfer Header"; + Location: Record Location; + ItemJnlPostLine: Codeunit "Item Jnl.-Post Line"; + begin + TransferHeader.Get(TransferOrderNo); + TransferHeader.TestField("Direct Transfer", false); + TransferHeader.TestField("In-Transit Code"); + Location.Get(TransferHeader."Transfer-from Code"); + Location.TestField("Require Shipment", false); + ItemJournalLine.TestField("Entry Type", ItemJournalLine."Entry Type"::Transfer); + ItemJournalLine.TestField("Location Code", TransferHeader."Transfer-from Code"); + ItemJournalLine.TestField("New Location Code", TransferHeader."In-Transit Code"); + + ItemJnlPostLine.RunWithCheck(ItemJournalLine); + TransferHeader."Last Shipment No." := ItemJournalLine."Document No."; + TransferHeader.Modify(true); + end; +} diff --git a/microsoft/knowledge/scm/post-transfers-through-shipment-and-receipt-codeunits.good.al b/microsoft/knowledge/scm/post-transfers-through-shipment-and-receipt-codeunits.good.al new file mode 100644 index 00000000..c6eb1008 --- /dev/null +++ b/microsoft/knowledge/scm/post-transfers-through-shipment-and-receipt-codeunits.good.al @@ -0,0 +1,37 @@ +namespace BCQuality.SCM.Samples; + +using Microsoft.Inventory.Location; +using Microsoft.Inventory.Transfer; + +codeunit 50113 "SCM Transfer Posting Good" +{ + procedure ShipTransferOrder(TransferOrderNo: Code[20]) + var + TransferHeader: Record "Transfer Header"; + Location: Record Location; + TransferOrderPostShipment: Codeunit "TransferOrder-Post Shipment"; + begin + TransferHeader.Get(TransferOrderNo); + TransferHeader.TestField("Direct Transfer", false); + TransferHeader.TestField("In-Transit Code"); + Location.Get(TransferHeader."Transfer-from Code"); + Location.TestField("Require Shipment", false); + + TransferOrderPostShipment.Run(TransferHeader); + end; + + procedure ReceiveTransferOrder(TransferOrderNo: Code[20]) + var + TransferHeader: Record "Transfer Header"; + Location: Record Location; + TransferOrderPostReceipt: Codeunit "TransferOrder-Post Receipt"; + begin + TransferHeader.Get(TransferOrderNo); + TransferHeader.TestField("Direct Transfer", false); + TransferHeader.TestField("In-Transit Code"); + Location.Get(TransferHeader."Transfer-to Code"); + Location.TestField("Require Receive", false); + + TransferOrderPostReceipt.Run(TransferHeader); + end; +} diff --git a/microsoft/knowledge/scm/post-transfers-through-shipment-and-receipt-codeunits.md b/microsoft/knowledge/scm/post-transfers-through-shipment-and-receipt-codeunits.md new file mode 100644 index 00000000..c7e4b8a4 --- /dev/null +++ b/microsoft/knowledge/scm/post-transfers-through-shipment-and-receipt-codeunits.md @@ -0,0 +1,42 @@ +--- +bc-version: [all] +domain: scm +keywords: [transfer-header, transfer-line, transferorder-post-shipment, transferorder-post-receipt, in-transit-code, last-shipment-no, item-application-entry] +technologies: [al] +countries: [w1] +application-area: [all] +--- + +# Post transfers through shipment and receipt codeunits + +## Description + +A two-step transfer order preserves a continuous quantity, reservation, and cost/application lineage from the source through in-transit to the destination. Its shipment and receipt workflows also own posted documents and derived lines for partial receipt. Posting an item-journal movement and manually marking a transfer header or line as shipped is not equivalent, even if the total quantities balance. + +## Best Practice + +For a prepared non-direct transfer order without required warehouse documents, use `"TransferOrder-Post Shipment".Run` at shipment and `"TransferOrder-Post Receipt".Run` at receipt, passing the actual `"Transfer Header"`. Validate the intended quantities to ship/receive through the source document; do not assign posted quantity counters as preparation. + +When warehouse shipment or receipt is required, use the warehouse document posting workflow that invokes the transfer poster with its real source context. Retain the configured standard direct-transfer workflow for direct transfers; the two-step sample's in-transit guard is not a universal requirement. + +Standalone item reclassification journals and bin movements are legitimate separate operations. Do not demand a fixed number of item ledger entries, or a nonzero `"Transferred-from Entry No."` on every transfer application: tracking/application splits and average-cost transfer handling differ. Require evidence that code is replacing completion of an existing transfer order, not merely moving stock through another supported process. + +## Anti Pattern + +Report ad-hoc item postings, independent positive/negative adjustments, manually created posted-transfer rows, or changes to source shipment/receipt counters used to stand in for transfer-order posting. Updating `"Last Shipment No."` after a bare item-journal call does not create the posted shipment, source-line progress, or transfer application lineage. + +Do not repair this by changing an existing item ledger entry's location or inventing application links. Route the source transaction through its owning shipment/receipt or configured direct-transfer workflow. Metadata enrichment inside that workflow is not itself a posting bypass. + +## Samples + +- [`post-transfers-through-shipment-and-receipt-codeunits.bad.al`](post-transfers-through-shipment-and-receipt-codeunits.bad.al) +- [`post-transfers-through-shipment-and-receipt-codeunits.good.al`](post-transfers-through-shipment-and-receipt-codeunits.good.al) + +## References + +- [Transfer inventory between locations](https://learn.microsoft.com/en-us/dynamics365/business-central/inventory-how-transfer-between-locations) +- [Item application design](https://learn.microsoft.com/en-us/dynamics365/business-central/design-details-item-application) +- [Cost adjustment design](https://learn.microsoft.com/en-us/dynamics365/business-central/design-details-cost-adjustment) +- [BaseApp shipment journal/source linkage](https://github.com/microsoft/BCApps/blob/8f7a04cb0db8aa96cb97e055c45c61aead49e280/src/Layers/W1/BaseApp/Inventory/Transfer/TransferOrderPostShipment.Codeunit.al#L292-L336) +- [Partial-receipt derived-line handling](https://github.com/microsoft/BCApps/blob/8f7a04cb0db8aa96cb97e055c45c61aead49e280/src/Layers/W1/BaseApp/Inventory/Transfer/TransferOrderPostReceipt.Codeunit.al#L457-L529) +- [Transfer application and average-cost branches](https://github.com/microsoft/BCApps/blob/8f7a04cb0db8aa96cb97e055c45c61aead49e280/src/Layers/W1/BaseApp/Inventory/Posting/ItemJnlPostLine.Codeunit.al#L1911-L1976) diff --git a/microsoft/knowledge/scm/reconcile-warehouse-adjustments-with-the-item-ledger.bad.al b/microsoft/knowledge/scm/reconcile-warehouse-adjustments-with-the-item-ledger.bad.al new file mode 100644 index 00000000..769dbd19 --- /dev/null +++ b/microsoft/knowledge/scm/reconcile-warehouse-adjustments-with-the-item-ledger.bad.al @@ -0,0 +1,44 @@ +namespace BCQuality.SCM.Samples; + +using Microsoft.Inventory.Item; +using Microsoft.Inventory.Journal; +using Microsoft.Inventory.Location; +using Microsoft.Warehouse.Journal; + +codeunit 50110 "SCM Warehouse Adjustment Bad" +{ + procedure ReconcileRegisteredWarehouseAdjustment(ItemNo: Code[20]; LocationCode: Code[10]; TemplateName: Code[10]; BatchName: Code[10]; PostingDate: Date; DocumentNo: Code[20]): Boolean + var + Item: Record Item; + ItemJournalBatch: Record "Item Journal Batch"; + ItemJournalLine: Record "Item Journal Line"; + Location: Record Location; + CalculateWhseAdjustment: Report "Calculate Whse. Adjustment"; + begin + Location.Get(LocationCode); + Location.TestField("Directed Put-away and Pick", true); + Location.TestField("Adjustment Bin Code"); + ItemJournalBatch.Get(TemplateName, BatchName); + ItemJournalLine.SetRange("Journal Template Name", TemplateName); + ItemJournalLine.SetRange("Journal Batch Name", BatchName); + if not ItemJournalLine.IsEmpty() then + Error(EmptyBatchErr); + + Item.Get(ItemNo); + Item.SetRecFilter(); + Item.SetRange("Location Filter", LocationCode); + ItemJournalLine."Journal Template Name" := TemplateName; + ItemJournalLine."Journal Batch Name" := BatchName; + CalculateWhseAdjustment.SetItemJnlLine(ItemJournalLine); + CalculateWhseAdjustment.SetTableView(Item); + CalculateWhseAdjustment.InitializeRequest(PostingDate, DocumentNo); + CalculateWhseAdjustment.SetHideValidationDialog(true); + CalculateWhseAdjustment.UseRequestPage(false); + CalculateWhseAdjustment.RunModal(); + + exit(true); + end; + + var + EmptyBatchErr: Label 'Use an empty, dedicated item journal batch for warehouse reconciliation.'; +} diff --git a/microsoft/knowledge/scm/reconcile-warehouse-adjustments-with-the-item-ledger.good.al b/microsoft/knowledge/scm/reconcile-warehouse-adjustments-with-the-item-ledger.good.al new file mode 100644 index 00000000..087f9562 --- /dev/null +++ b/microsoft/knowledge/scm/reconcile-warehouse-adjustments-with-the-item-ledger.good.al @@ -0,0 +1,50 @@ +namespace BCQuality.SCM.Samples; + +using Microsoft.Inventory.Item; +using Microsoft.Inventory.Journal; +using Microsoft.Inventory.Location; +using Microsoft.Inventory.Posting; +using Microsoft.Warehouse.Journal; + +codeunit 50111 "SCM Warehouse Adjustment Good" +{ + procedure ReconcileRegisteredWarehouseAdjustment(ItemNo: Code[20]; LocationCode: Code[10]; TemplateName: Code[10]; BatchName: Code[10]; PostingDate: Date; DocumentNo: Code[20]): Boolean + var + Item: Record Item; + ItemJournalBatch: Record "Item Journal Batch"; + ItemJournalLine: Record "Item Journal Line"; + Location: Record Location; + CalculateWhseAdjustment: Report "Calculate Whse. Adjustment"; + ItemJnlPostBatch: Codeunit "Item Jnl.-Post Batch"; + begin + Location.Get(LocationCode); + Location.TestField("Directed Put-away and Pick", true); + Location.TestField("Adjustment Bin Code"); + ItemJournalBatch.Get(TemplateName, BatchName); + ItemJournalLine.SetRange("Journal Template Name", TemplateName); + ItemJournalLine.SetRange("Journal Batch Name", BatchName); + if not ItemJournalLine.IsEmpty() then + Error(EmptyBatchErr); + + Item.Get(ItemNo); + Item.SetRecFilter(); + Item.SetRange("Location Filter", LocationCode); + ItemJournalLine."Journal Template Name" := TemplateName; + ItemJournalLine."Journal Batch Name" := BatchName; + CalculateWhseAdjustment.SetItemJnlLine(ItemJournalLine); + CalculateWhseAdjustment.SetTableView(Item); + CalculateWhseAdjustment.InitializeRequest(PostingDate, DocumentNo); + CalculateWhseAdjustment.SetHideValidationDialog(true); + CalculateWhseAdjustment.UseRequestPage(false); + CalculateWhseAdjustment.RunModal(); + + if ItemJournalLine.FindFirst() then begin + ItemJournalLine.TestField("Warehouse Adjustment", true); + ItemJnlPostBatch.Run(ItemJournalLine); + end; + exit(true); + end; + + var + EmptyBatchErr: Label 'Use an empty, dedicated item journal batch for warehouse reconciliation.'; +} diff --git a/microsoft/knowledge/scm/reconcile-warehouse-adjustments-with-the-item-ledger.md b/microsoft/knowledge/scm/reconcile-warehouse-adjustments-with-the-item-ledger.md new file mode 100644 index 00000000..9297cdcf --- /dev/null +++ b/microsoft/knowledge/scm/reconcile-warehouse-adjustments-with-the-item-ledger.md @@ -0,0 +1,41 @@ +--- +bc-version: [all] +domain: scm +keywords: [warehouse-adjustment, calculate-whse-adjustment, adjustment-bin-code, directed-put-away-and-pick, item-journal-line, warehouse-entry] +technologies: [al] +countries: [w1] +application-area: [all] +--- + +# Reconcile warehouse adjustments with the item ledger + +## Description + +At a Directed Put-away and Pick location, registering an ordinary warehouse quantity or physical-inventory adjustment and synchronizing it to inventory are distinct steps. The warehouse registration balances quantity through the adjustment bin. An additional ordinary item-journal increase/decrease is not the same as consuming that pending warehouse adjustment. + +## Best Practice + +After the warehouse adjustment has been registered, run `"Calculate Whse. Adjustment"` for the intended item/location and prepared item-journal batch, then post the generated lines through `"Item Jnl.-Post Batch"`. The calculation derives the reconciliation by location, variant, units of measure, and tracking, marks the lines `"Warehouse Adjustment"`, and accounts for already prepared unposted adjustments. + +Keep reconciliation separate from source-document posting: warehouse receipts/shipments use their document workflows. Intentional warehouse-only staging is valid when a separately owned reconciliation step completes the process; do not flag the registration call just because that later job is outside the diff. + +Do not generalize this rule to every warehouse operation. Bin movements need not change total inventory, and warehouse tracking/expiration reclassification has a standard batch path that can also post item-journal entries. Standard reclassification and basic-location item adjustments are not this ordinary advanced-warehouse quantity-adjustment case. + +The samples start after warehouse quantity registration and report whether inventory reconciliation completed. The clean sample calculates and posts into an empty dedicated batch; it is not a complete warehouse physical-count workflow. + +## Anti Pattern + +Report a workflow that claims to reconcile a registered advanced-warehouse quantity adjustment by posting a manually mirrored ordinary item-journal line, or that marks reconciliation complete after only warehouse registration or adjustment calculation. Calculation prepares journal lines; it does not post those lines. Require explicit synchronization intent and location/workflow evidence. + +Do not repair the defect by inventing positive/negative quantities or flipping `"Warehouse Adjustment"` on an arbitrary line. Use the calculation step so the adjustment-bin balance and the actual tracked quantities drive inventory reconciliation. + +## Samples + +- [`reconcile-warehouse-adjustments-with-the-item-ledger.bad.al`](reconcile-warehouse-adjustments-with-the-item-ledger.bad.al) +- [`reconcile-warehouse-adjustments-with-the-item-ledger.good.al`](reconcile-warehouse-adjustments-with-the-item-ledger.good.al) + +## References + +- [Synchronize adjusted warehouse entries with item ledger entries](https://learn.microsoft.com/en-us/dynamics365/business-central/inventory-how-count-adjust-reclassify#to-synchronize-the-adjusted-warehouse-entries-with-the-related-item-ledger-entries) +- [BaseApp warehouse-adjustment calculation](https://github.com/microsoft/BCApps/blob/8f7a04cb0db8aa96cb97e055c45c61aead49e280/src/Layers/W1/BaseApp/Warehouse/Journal/CalculateWhseAdjustment.Report.al#L298-L384) +- [Warehouse reclassification exception](https://github.com/microsoft/BCApps/blob/8f7a04cb0db8aa96cb97e055c45c61aead49e280/src/Layers/W1/BaseApp/Warehouse/Journal/WhseJnlRegisterBatch.Codeunit.al#L196-L210) diff --git a/microsoft/knowledge/scm/transfer-item-tracking-through-source-reservation-codeunits.bad.al b/microsoft/knowledge/scm/transfer-item-tracking-through-source-reservation-codeunits.bad.al new file mode 100644 index 00000000..ef12396d --- /dev/null +++ b/microsoft/knowledge/scm/transfer-item-tracking-through-source-reservation-codeunits.bad.al @@ -0,0 +1,35 @@ +namespace BCQuality.SCM.Samples; + +using Microsoft.Inventory.Tracking; +using Microsoft.Sales.Document; + +codeunit 50108 "SCM Tracking Transfer Bad" +{ + procedure TransferBlanketOrderTracking(var SourceBlanketOrderLine: Record "Sales Line"; var DestinationSalesOrderLine: Record "Sales Line"; QuantityBaseToTransfer: Decimal) + var + ReservationEntry: Record "Reservation Entry"; + begin + SourceBlanketOrderLine.TestField("Document Type", SourceBlanketOrderLine."Document Type"::"Blanket Order"); + SourceBlanketOrderLine.TestField(Type, SourceBlanketOrderLine.Type::Item); + DestinationSalesOrderLine.TestField("Document Type", DestinationSalesOrderLine."Document Type"::Order); + DestinationSalesOrderLine.TestField(Type, DestinationSalesOrderLine.Type::Item); + DestinationSalesOrderLine.TestField("No.", SourceBlanketOrderLine."No."); + if QuantityBaseToTransfer <= 0 then + Error(PositiveQuantityErr); + + ReservationEntry.SetRange("Source Type", Database::"Sales Line"); + ReservationEntry.SetRange("Source Subtype", SourceBlanketOrderLine."Document Type".AsInteger()); + ReservationEntry.SetRange("Source ID", SourceBlanketOrderLine."Document No."); + ReservationEntry.SetRange("Source Ref. No.", SourceBlanketOrderLine."Line No."); + ReservationEntry.SetRange(Positive, false); + ReservationEntry.FindFirst(); + ReservationEntry."Source Subtype" := DestinationSalesOrderLine."Document Type".AsInteger(); + ReservationEntry."Source ID" := DestinationSalesOrderLine."Document No."; + ReservationEntry."Source Ref. No." := DestinationSalesOrderLine."Line No."; + ReservationEntry.Validate("Quantity (Base)", -QuantityBaseToTransfer); + ReservationEntry.Modify(true); + end; + + var + PositiveQuantityErr: Label 'The base quantity to transfer must be positive.'; +} diff --git a/microsoft/knowledge/scm/transfer-item-tracking-through-source-reservation-codeunits.good.al b/microsoft/knowledge/scm/transfer-item-tracking-through-source-reservation-codeunits.good.al new file mode 100644 index 00000000..de58f691 --- /dev/null +++ b/microsoft/knowledge/scm/transfer-item-tracking-through-source-reservation-codeunits.good.al @@ -0,0 +1,24 @@ +namespace BCQuality.SCM.Samples; + +using Microsoft.Sales.Document; + +codeunit 50109 "SCM Tracking Transfer Good" +{ + procedure TransferBlanketOrderTracking(var SourceBlanketOrderLine: Record "Sales Line"; var DestinationSalesOrderLine: Record "Sales Line"; QuantityBaseToTransfer: Decimal) + var + SalesLineReserve: Codeunit "Sales Line-Reserve"; + begin + SourceBlanketOrderLine.TestField("Document Type", SourceBlanketOrderLine."Document Type"::"Blanket Order"); + SourceBlanketOrderLine.TestField(Type, SourceBlanketOrderLine.Type::Item); + DestinationSalesOrderLine.TestField("Document Type", DestinationSalesOrderLine."Document Type"::Order); + DestinationSalesOrderLine.TestField(Type, DestinationSalesOrderLine.Type::Item); + DestinationSalesOrderLine.TestField("No.", SourceBlanketOrderLine."No."); + if QuantityBaseToTransfer <= 0 then + Error(PositiveQuantityErr); + + SalesLineReserve.TransferSaleLineToSalesLine(SourceBlanketOrderLine, DestinationSalesOrderLine, QuantityBaseToTransfer); + end; + + var + PositiveQuantityErr: Label 'The base quantity to transfer must be positive.'; +} diff --git a/microsoft/knowledge/scm/transfer-item-tracking-through-source-reservation-codeunits.md b/microsoft/knowledge/scm/transfer-item-tracking-through-source-reservation-codeunits.md new file mode 100644 index 00000000..915ec4a5 --- /dev/null +++ b/microsoft/knowledge/scm/transfer-item-tracking-through-source-reservation-codeunits.md @@ -0,0 +1,42 @@ +--- +bc-version: [all] +domain: scm +keywords: [reservation-entry, tracking-specification, sales-line-reserve, transfersalelinetosalesline, transferreserventry, copyitemtracking, quantity-base] +technologies: [al] +countries: [w1] +application-area: [all] +--- + +# Transfer item tracking through source reservation codeunits + +## Description + +Moving lot/serial tracking between document lines is a source-ownership operation, not a copy of visible tracking fields. Partial movement must retain the old source's remainder, destination units of measure, quantities to handle/invoice, status, sign, and any reservation counterpart. Repointing a `"Reservation Entry"` loses this coordination; inserting a `"Tracking Specification"` row alone does not book the destination's source tracking. + +## Best Practice + +Use the reservation codeunit for the source workflow. For the tracking portion of blanket-sales-order or quote conversion to a sales order, `"Sales Line-Reserve".TransferSaleLineToSalesLine` takes the existing source line, prepared destination line, and quantity to transfer in **base units**. It delegates the source/status and quantity movement to `"Create Reserv. Entry".TransferReservEntry`. + +The caller still owns document conversion and destination-line preparation; this method does not create a sales order. Keep item, variant, location, and source identity consistent, and use other source-specific wrappers for purchases, transfers, assembly, or production instead of reusing a sales wrapper indiscriminately. + +`"Item Tracking Management".CopyItemTracking` serves a different purpose: it creates Prospect copies, not a transfer of reservation ownership. That is valid for its intended copy workflow. Temporary Tracking Specification processing is also normal, and persisted historical tracking specifications are not forbidden; distinguish the working/historic representation from the current source booking. + +## Anti Pattern + +Report direct rewrites of persistent `"Reservation Entry"` source type/subtype, ID, reference number, or quantities to implement source-line conversion or a partial tracking transfer. Changing only `"Quantity (Base)"` and source keys can drop the remainder or leave the other tracking/reservation quantities attached to the wrong source. + +Also report use of a tracking copy as a replacement for moving an existing binding reservation when that intent is explicit. Do not flag a legitimate Prospect copy, temporary tracking buffer, historical tracking read, or source-specific engine call merely because it uses these tables. + +## Samples + +- [`transfer-item-tracking-through-source-reservation-codeunits.bad.al`](transfer-item-tracking-through-source-reservation-codeunits.bad.al) +- [`transfer-item-tracking-through-source-reservation-codeunits.good.al`](transfer-item-tracking-through-source-reservation-codeunits.good.al) + +## References + +- [Item Tracking Lines window design](https://learn.microsoft.com/en-us/dynamics365/business-central/design-details-item-tracking-lines-window) +- [Active versus historic item-tracking entries](https://learn.microsoft.com/en-us/dynamics365/business-central/design-details-active-versus-historic-item-tracking-entries) +- [Item tracking and reservations](https://learn.microsoft.com/en-us/dynamics365/business-central/design-details-item-tracking-and-reservations) +- [BaseApp sales tracking transfer](https://github.com/microsoft/BCApps/blob/8f7a04cb0db8aa96cb97e055c45c61aead49e280/src/Layers/W1/BaseApp/Sales/Document/SalesLineReserve.Codeunit.al#L532-L578) +- [Base-unit conversion caller](https://github.com/microsoft/BCApps/blob/8f7a04cb0db8aa96cb97e055c45c61aead49e280/src/Layers/W1/BaseApp/Sales/Document/BlanketSalesOrdertoOrder.Codeunit.al#L195-L198) +- [Prospect-copy API](https://github.com/microsoft/BCApps/blob/8f7a04cb0db8aa96cb97e055c45c61aead49e280/src/Layers/W1/BaseApp/Inventory/Tracking/ItemTrackingManagement.Codeunit.al#L575-L657) diff --git a/microsoft/knowledge/scm/use-date-aware-availability-for-promising.bad.al b/microsoft/knowledge/scm/use-date-aware-availability-for-promising.bad.al new file mode 100644 index 00000000..d4069d87 --- /dev/null +++ b/microsoft/knowledge/scm/use-date-aware-availability-for-promising.bad.al @@ -0,0 +1,25 @@ +namespace BCQuality.SCM.Samples; + +using Microsoft.Inventory.Item; + +codeunit 50114 "SCM Additional Promise Bad" +{ + procedure CanPromiseAdditionalDemand(ItemNo: Code[20]; LocationCode: Code[10]; VariantCode: Code[10]; ShipmentDate: Date; RequestedAdditionalQuantityBase: Decimal; LookaheadDateFormula: DateFormula): Boolean + var + Item: Record Item; + begin + if (ShipmentDate = 0D) or (RequestedAdditionalQuantityBase <= 0) then + Error(DemandInputErr); + Item.Get(ItemNo); + Item.TestField(Type, Item.Type::Inventory); + Item.SetRange("Location Filter", LocationCode); + Item.SetRange("Variant Filter", VariantCode); + Item.SetRange("Date Filter", 0D, ShipmentDate); + + Item.CalcFields(Inventory); + exit(Item.Inventory >= RequestedAdditionalQuantityBase); + end; + + var + DemandInputErr: Label 'Enter a shipment date and a positive additional base quantity.'; +} diff --git a/microsoft/knowledge/scm/use-date-aware-availability-for-promising.good.al b/microsoft/knowledge/scm/use-date-aware-availability-for-promising.good.al new file mode 100644 index 00000000..c36a46b7 --- /dev/null +++ b/microsoft/knowledge/scm/use-date-aware-availability-for-promising.good.al @@ -0,0 +1,33 @@ +namespace BCQuality.SCM.Samples; + +using Microsoft.Foundation.Enums; +using Microsoft.Inventory.Availability; +using Microsoft.Inventory.Item; + +codeunit 50115 "SCM Additional Promise Good" +{ + procedure CanPromiseAdditionalDemand(ItemNo: Code[20]; LocationCode: Code[10]; VariantCode: Code[10]; ShipmentDate: Date; RequestedAdditionalQuantityBase: Decimal; LookaheadDateFormula: DateFormula): Boolean + var + Item: Record Item; + AvailableToPromise: Codeunit "Available to Promise"; + GrossRequirement: Decimal; + ScheduledReceipt: Decimal; + PromisableQuantityBase: Decimal; + begin + if (ShipmentDate = 0D) or (RequestedAdditionalQuantityBase <= 0) then + Error(DemandInputErr); + Item.Get(ItemNo); + Item.TestField(Type, Item.Type::Inventory); + Item.SetRange("Location Filter", LocationCode); + Item.SetRange("Variant Filter", VariantCode); + Item.SetRange("Date Filter", 0D, ShipmentDate); + + PromisableQuantityBase := AvailableToPromise.CalcQtyAvailableToPromise( + Item, GrossRequirement, ScheduledReceipt, ShipmentDate, + Enum::"Analysis Period Type"::Day, LookaheadDateFormula); + exit(PromisableQuantityBase >= RequestedAdditionalQuantityBase); + end; + + var + DemandInputErr: Label 'Enter a shipment date and a positive additional base quantity.'; +} diff --git a/microsoft/knowledge/scm/use-date-aware-availability-for-promising.md b/microsoft/knowledge/scm/use-date-aware-availability-for-promising.md new file mode 100644 index 00000000..17723b98 --- /dev/null +++ b/microsoft/knowledge/scm/use-date-aware-availability-for-promising.md @@ -0,0 +1,40 @@ +--- +bc-version: [all] +domain: scm +keywords: [available-to-promise, calcqtyavailabletopromise, inventory, shipment-date, gross-requirement, scheduled-receipt, location-filter, variant-filter] +technologies: [al] +countries: [w1] +application-area: [all] +--- + +# Use date-aware availability for promising + +## Description + +`Item.Inventory` is an on-hand quantity, not an available-to-promise answer. Promising additional demand must account for the requested date, location and variant, reservations, scheduled receipts, existing requirements, and demand within the configured lookahead. An on-hand comparison can promise inventory already committed elsewhere and miss incoming supply. + +## Best Practice + +For an additional demand not already recorded on a source line, use `"Available to Promise".CalcQtyAvailableToPromise` with the Item's location/variant filters, date range ending on the shipment date, and the configured period/lookahead horizon. Compare in base units. Use a fresh calculation context or the codeunit's recalculation support rather than carrying cached quantities between unrelated items or requests. + +For an existing sales-line change, retain the source-aware order-promising/availability workflow, which accounts for the line's own quantity or delta; blindly applying an additional-demand calculation can double-count that line. Assembly and production requirements/supply likewise need the standard availability context, not just a sales-only stock subtraction. + +An ATP result is not a reservation or a guarantee of warehouse pickability. Lot/serial constraints, bins, warehouse activity, and later concurrent changes still need their own checks. Conversely, an on-hand display, valuation report, or deliberately immediate-stock-only check is allowed to use `Item.Inventory`; do not replace its distinct business question with ATP. + +## Anti Pattern + +Report `CalcFields(Inventory)` or an equivalent sum of item ledger quantities used as the complete decision for a dated additional-demand promise, including code that applies location/variant filters but ignores other demand and supply. Require explicit promising intent; an Inventory FlowField read alone is not a finding. + +Also report a visible loss of location, variant, date, or source-line context in that calculation. Do not invent missing demand in an unseen caller or require this exact API when a visible supported workflow already supplies the correct availability semantics. + +## Samples + +- [`use-date-aware-availability-for-promising.bad.al`](use-date-aware-availability-for-promising.bad.al) +- [`use-date-aware-availability-for-promising.good.al`](use-date-aware-availability-for-promising.good.al) + +## References + +- [Calculate order-promising dates](https://learn.microsoft.com/en-us/dynamics365/business-central/sales-how-to-calculate-order-promising-dates) +- [Availability in the warehouse](https://learn.microsoft.com/en-us/dynamics365/business-central/design-details-availability-in-the-warehouse) +- [BaseApp ATP calculation](https://github.com/microsoft/BCApps/blob/8f7a04cb0db8aa96cb97e055c45c61aead49e280/src/Layers/W1/BaseApp/Inventory/Availability/AvailabletoPromise.Codeunit.al#L52-L184) +- [Forward-demand lookahead](https://github.com/microsoft/BCApps/blob/8f7a04cb0db8aa96cb97e055c45c61aead49e280/src/Layers/W1/BaseApp/Inventory/Availability/AvailabletoPromise.Codeunit.al#L295-L356) diff --git a/microsoft/skills/review/al-code-review.md b/microsoft/skills/review/al-code-review.md index dc1f1db1..7cfe07b5 100644 --- a/microsoft/skills/review/al-code-review.md +++ b/microsoft/skills/review/al-code-review.md @@ -28,6 +28,7 @@ sub-skills: - microsoft/skills/review/al-reporting-review.md - microsoft/skills/review/al-appsource-review.md - microsoft/skills/review/al-telemetry-review.md + - microsoft/skills/review/al-scm-review.md --- # AL code review diff --git a/microsoft/skills/review/al-scm-review.md b/microsoft/skills/review/al-scm-review.md new file mode 100644 index 00000000..819defd6 --- /dev/null +++ b/microsoft/skills/review/al-scm-review.md @@ -0,0 +1,213 @@ +--- +kind: action-skill +id: al-scm-review +version: 1 +title: AL Supply Chain Management review +description: Reviews SCM inventory costing, item application, reservations, order tracking, item tracking, warehouse, transfer, and planning workflows in AL. +inputs: [pr-diff, file-path, folder-path] +outputs: [findings-report] +bc-version: [all] +technologies: [al] +countries: [w1] +application-area: [all] +--- + +# AL Supply Chain Management review + +Reviews AL source against the `scm` knowledge domain. This leaf invokes no +sub-skills and is composed by `al-code-review`. It accepts diffs, individual +files, and complete app folders; for a folder, inspect every relevant AL file, +not a representative sample. A folder supplies no historical baseline. + +## Source + +Apply the source-surface gate in Relevance before retrieving knowledge. If the +gate passes, use READ's **Bounded retrieval for review skills** workflow with +`-Domain scm` and `-Technologies @('al')`. Consume every catalog page across +enabled layers, preserving exact paths, complete keywords, applicability, and +unknown dimensions. Select articles using catalog metadata only; never use an +index row as the basis for a finding. + +Entry owns index preparation. Do not rebuild the index in this leaf. When a +helper or prepared index is unavailable or invalid, follow READ's explicit +path-discovery and bounded native-read fallback through EOF. A retrieval +failure is not an empty or clean review. + +## Relevance + +First inspect the supplied scope for an SCM source surface: a changed +procedure, trigger, subscriber, or bound action that writes or posts inventory, +cost, application, reservation, tracking, warehouse, transfer, or planning +state, or calculates availability for a supply/demand decision. Resolve record +and codeunit declarations, source tables, event publishers, and nearby calls +within the supplied scope. Do not infer object identity from a variable name. + +An `Item` reference, a field caption, an unrelated ledger read, an object name +containing "warehouse", or a broad `ApplicationArea` alone does not pass this +gate. Comments and display strings are not execution evidence. When no source +surface passes, return `not-applicable` with zero coverage and no article-body +reads. In a mixed diff, worklist only the relevant procedures and their visible +supporting context, not every AL file in the app. Unknown application areas do +not by themselves exclude codeunits or subscribers. + +For candidates, apply READ's frontmatter matching semantics: + +- `bc-version`: the target BC major version from application dependency or + host context, not the extension's own version; otherwise unknown. +- `technologies`: AL. +- `countries`: the known target localization or host context; otherwise + unknown, not a guess based on the developer's language. +- `application-area`: the actual known task/object areas, not a substituted + `[all]`. Use explicit inventory, warehousing, assembly, manufacturing, or + supply-chain context to narrow the relevant source, not as proof of a defect. + +Discard nonmatching articles. Retain conditionally applicable articles only +when configuration permits; cap their findings at `medium` confidence and +name every unknown dimension in the message. + +## Worklist + +Extract deterministic tokens from the gated source: resolved object/type +names, quoted field names, methods, enum members, and called publishers. +Lowercase invariantly, replace punctuation and whitespace runs with one hyphen, +and trim leading/trailing hyphens. Thus `"Item Ledger Entry"` becomes +`item-ledger-entry`, `"Qty. (Base)"` becomes `qty-base`, and `RunWithCheck` +becomes `runwithcheck`. Apply the same normalization to catalog keywords. +Match whole normalized tokens/phrases, not substrings such as `item` in an +unrelated identifier. Do not manufacture synonyms that are not supported by +the changed source or the targeted cues. + +Select a catalog row only when a keyword intersects these tokens, or its +path/title/description identifies the same gated source surface **and +operation**. Object declarations establish context; field assignments, calls, +and decision logic establish the operation to evaluate. A shared table name +does not select every rule using that table. + +Use these targeted candidate-selection cues, resolving each slug to its +actual enabled catalog paths. They select articles to read, not findings to +emit; all platform reasoning and exceptions remain in those articles. + +| Changed source surface and operation | Article slug | +| --- | --- | +| `"Item Ledger Entry"`/`"Value Entry"` transaction writes, or a standalone item-journal quantity/value posting entry point | `post-item-ledger-changes-through-item-journals` | +| Revaluation `"Item Journal Line"` with `"Inventory Value Per"` or `"Partial Revaluation"`, and its line/batch posting calls | `post-revaluation-through-the-item-journal-batch` | +| `"Item Application Entry"` relationship/quantity mutation, or `UnApply`, `ReApply`, `RedoApplications`, `CostAdjust` in an application-correction flow | `change-item-applications-through-posting-routines` | +| Binding-reservation cancellation: `"Reservation Entry"` status, delete/quantity/source edits, `CancelReservation`, or source reservation-lifecycle calls | `cancel-reservations-through-reservation-management` | +| Tracking source conversion/partial movement: `"Sales Line-Reserve"`, `TransferSaleLineToSalesLine`, `TransferReservEntry`, `CopyItemTracking`, or `"Reservation Entry"`/`"Tracking Specification"` source/quantity writes | `transfer-item-tracking-through-source-reservation-codeunits` | +| Registered warehouse quantity/physical-adjustment synchronization, `"Directed Put-away and Pick"`, `"Adjustment Bin Code"`, `"Warehouse Adjustment"`, or `"Calculate Whse. Adjustment"` and the resulting item-journal posting | `reconcile-warehouse-adjustments-with-the-item-ledger` | +| `"Transfer Header"`/`"Transfer Line"` shipment/receipt completion, transfer posting publishers, in-transit/document-link changes, or item-journal posting presented as transfer-order completion | `post-transfers-through-shipment-and-receipt-codeunits` | +| `Inventory`, `CalcQtyAvailableToPromise`, or stock sums used in a dated supply/demand promise, including changed location/variant/date filters and source-demand context | `use-date-aware-availability-for-promising` | +| `"Requisition Line"` action-message execution, accepted planning suggestions, `"Req. Wksh.-Make Order"`, `CarryOutBatchAction`, or linked supply creation/change plus requisition-line deletion | `carry-out-requisition-actions-through-the-standard-workflow` | + +Do not select a cue solely from a caption, comment, or unrelated declaration. +Use the same gates for clean supported calls so their article exclusions are +evaluated, not just suspicious writes. Applicability is never an anti-pattern. + +Resolve normative conflicts per READ after reading the selected complete +articles. Keep enabled-layer candidates additive unless guidance actually +contradicts; do not deduplicate merely by filename. Record losing candidates +in `suppressed` with `layer-precedence`, and configuration-hidden candidates +with `configuration`. Noncandidates are not suppressions. + +Order exact worklisted paths ordinally and retrieve complete bodies in stable +chunks of at most eight, following every continuation within each chunk. +Never turn the chunk size into a top-eight cutoff. Read samples only when +needed, via their exact READ links and bounded sample retrieval. + +## Action + +Evaluate the visible source against each opened article's normative Best +Practice and Anti Pattern, including its scope and exclusions. Establish the +record's persistence, caller contract, document type/state, and affected +operation from evidence before reporting. Consult the article for treatment +of temporary buffers, supported publisher parameters, managed posting paths, +and legitimate read-only calculations; the skill itself defines no BC rule. +Do not infer missing work in an unseen caller or report every use of a routed +API. A supported alternative is not a defect. + +Emit only a concrete violation with its business consequence and supported +remediation. Use `major` for a demonstrated material SCM defect, `minor` for +a narrower best-practice conflict, and `blocker` only if the opened article +establishes a violated platform-level guarantee. Relevance alone produces no +finding. Deduplicate overlapping findings that prescribe the same correction; +prefer the article that owns the specific operation and retain any other +applicable article as a supporting reference. + +Copy `findings[].id` verbatim from the primary article's exact catalog path; +it must equal `references[0].path`. Cite only complete articles actually read. +Use `high` confidence only for unambiguous source evidence with known +applicability, `medium` for justified inference or conditional applicability. +Never label a guessed API signature or missing workflow context high confidence. + +Agent findings are optional and strictly SCM-scoped. Follow DO's precision +bar: concrete, material defects only, with `references: []`, an `agent:` id, +severity at most `minor`, and confidence at most `medium`. Omit generic AL, +style, performance, privacy, and unrelated technical findings owned by other +leaves. Do not invent a finding to compensate for an empty worklist. + +For an unambiguous local fix, supply literal replacement AL in +`suggested-code`, with a location range covering exactly those lines. Do not +replace an entire business workflow with a sample call that omits the +caller's setup, filters, source identity, or validations. When a mechanical- +looking fix cannot be expressed safely, give `suggested-code-omission-reason`. + +Outcomes follow DO: `completed` after evaluating the complete worklist, +including a clean result; `not-applicable` when the source gate fails; +`no-knowledge` when no applicable corpus survives filtering/configuration; +`partial` when only part of the worklist was evaluated; `failed` when no +reliable result can be produced. A source match with no matching article is +`completed` with an empty worklist, not a claimed evaluation of every SCM +concern. Explain partial/failed results and report accurate coverage. + +## Output + +Return one strict JSON findings-report per DO and +`schemas/findings-report.schema.json`, with no surrounding prose. Every +finding, including an agent finding, must have +`domain: "Supply Chain Management"`. Do not set `from-sub-skill` in a leaf +report; the coordinator adds it. All locations must identify existing lines +in the supplied source, and any range must start at `location.line`. + +A knowledge-backed finding with an exact article id: + +```json +{ + "skill": { "id": "al-scm-review", "version": 1 }, + "outcome": "completed", + "summary": { + "counts": { "blocker": 0, "major": 1, "minor": 0, "info": 0 }, + "coverage": { "worklist-size": 1, "items-evaluated": 1 } + }, + "findings": [ + { + "id": "microsoft/knowledge/scm/cancel-reservations-through-reservation-management.md", + "severity": "major", + "message": "This cancellation deletes only the negative reservation row. Use Reservation Engine Mgt. cancellation so counterpart and surviving tracking are handled by the owning workflow.", + "location": { "file": "src/CancelReservation.Codeunit.al", "line": 16 }, + "references": [ + { "path": "microsoft/knowledge/scm/cancel-reservations-through-reservation-management.md" } + ], + "confidence": "high", + "domain": "Supply Chain Management", + "suggested-code-omission-reason": "The replacement also requires a codeunit declaration outside the reported line." + } + ], + "suppressed": [] +} +``` + +An unrelated AL change, excluded before article retrieval: + +```json +{ + "skill": { "id": "al-scm-review", "version": 1 }, + "outcome": "not-applicable", + "outcome-reason": "The supplied AL changes contain no SCM posting, state mutation, or supply/demand availability surface.", + "summary": { + "counts": { "blocker": 0, "major": 0, "minor": 0, "info": 0 }, + "coverage": { "worklist-size": 0, "items-evaluated": 0 } + }, + "findings": [], + "suppressed": [] +} +``` From 525e84e183ee533e03fdca5bf94051faaf1cc982 Mon Sep 17 00:00:00 2001 From: Jesper Schulz-Wedde Date: Thu, 17 Sep 2026 18:59:22 +0200 Subject: [PATCH 2/2] Normalize SCM knowledge and review ownership Align article and AL sample conventions, keep BC facts separate from review mechanics, and clarify reciprocal Finance ownership without bespoke shared test assertions. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .github/scripts/Test-SkillIndex.ps1 | 22 -- docs/using-bcquality.md | 10 +- ...ions-through-reservation-management.bad.al | 5 - ...ons-through-reservation-management.good.al | 5 - ...rvations-through-reservation-management.md | 15 +- ...tions-through-the-standard-workflow.bad.al | 6 - ...ions-through-the-standard-workflow.good.al | 6 - ...n-actions-through-the-standard-workflow.md | 15 +- ...plications-through-posting-routines.bad.al | 4 - ...lications-through-posting-routines.good.al | 5 - ...m-applications-through-posting-routines.md | 17 +- ...edger-changes-through-item-journals.bad.al | 5 - ...dger-changes-through-item-journals.good.al | 5 - ...em-ledger-changes-through-item-journals.md | 17 +- ...tion-through-the-item-journal-batch.bad.al | 5 - ...ion-through-the-item-journal-batch.good.al | 5 - ...aluation-through-the-item-journal-batch.md | 15 +- ...ough-shipment-and-receipt-codeunits.bad.al | 7 - ...ugh-shipment-and-receipt-codeunits.good.al | 5 - ...-through-shipment-and-receipt-codeunits.md | 17 +- ...se-adjustments-with-the-item-ledger.bad.al | 7 - ...e-adjustments-with-the-item-ledger.good.al | 8 - ...ehouse-adjustments-with-the-item-ledger.md | 17 +- ...hrough-source-reservation-codeunits.bad.al | 5 - ...rough-source-reservation-codeunits.good.al | 4 - ...ng-through-source-reservation-codeunits.md | 17 +- ...te-aware-availability-for-promising.bad.al | 4 - ...e-aware-availability-for-promising.good.al | 6 - ...e-date-aware-availability-for-promising.md | 17 +- microsoft/skills/review/al-scm-review.md | 244 ++++++------------ 30 files changed, 153 insertions(+), 367 deletions(-) diff --git a/.github/scripts/Test-SkillIndex.ps1 b/.github/scripts/Test-SkillIndex.ps1 index dadcabfc..5354370b 100644 --- a/.github/scripts/Test-SkillIndex.ps1 +++ b/.github/scripts/Test-SkillIndex.ps1 @@ -110,28 +110,6 @@ try { } } - $scm = @($skills | Where-Object id -eq 'al-scm-review') - if ($scm.Count -ne 1 -or - (@($scm[0].inputs) -join ',') -cne 'pr-diff,file-path,folder-path' -or - (@($scm[0].filters.technologies) -join ',') -cne 'al') { - throw 'SCM must be discoverable as an AL leaf accepting diffs, files, and complete folders.' - } - $scmText = Get-Content -LiteralPath (Join-Path $Root $scm[0].path) -Raw - $scmExamples = [regex]::Matches($scmText, '(?s)```json\s*(\{.*?\})\s*```') - if ($scmExamples.Count -ne 2) { - throw "Expected two SCM findings-report examples, found $($scmExamples.Count)." - } - foreach ($example in $scmExamples) { - if (-not ($example.Groups[1].Value | Test-Json -SchemaFile $reportSchema -ErrorAction Stop)) { - throw 'An SCM output example does not satisfy schemas/findings-report.schema.json.' - } - $report = $example.Groups[1].Value | ConvertFrom-Json - if ($report.skill.id -cne 'al-scm-review' -or - @($report.findings | Where-Object domain -cne 'Supply Chain Management').Count) { - throw 'SCM output examples must retain the leaf id and complete display domain.' - } - } - $minimalReport = @{ skill = @{ id = 'al-style-review'; version = 1 } outcome = 'completed' diff --git a/docs/using-bcquality.md b/docs/using-bcquality.md index 60526d7c..2af08be9 100644 --- a/docs/using-bcquality.md +++ b/docs/using-bcquality.md @@ -194,8 +194,8 @@ Available knowledge is **not** a promise that every rule will run. Selection depends on the task, target context, enabled layers, and source evidence. A whole-folder review is a current-state snapshot: detecting a published API removal or another comparison-only regression requires an actual baseline. -The corpus combines technical AL guidance with a focused SCM functional -domain, not exhaustive functional validation or AppSource certification. +The corpus combines technical AL guidance with targeted functional-domain +invariants, not exhaustive functional validation or AppSource certification. The SCM leaf owns selected inventory/value, application, reservation, tracking, and warehouse/posting invariants. It prunes unrelated AL using the actual @@ -205,6 +205,12 @@ must not be replaced with an assumed posting defect. Manufacturing, assembly, planning, and other supply-chain areas are covered only where an article explicitly names the shared interface or invariant. +SCM owns Item/Value/Capacity/Warehouse and inventory-application posting +records. Pure G/L, customer/vendor/detailed/VAT and financial-only posting +mutations belong to Finance, even when that domain is not enabled. Equivalent +findings for one inventory-originated posting bypass have one SCM primary +owner; distinct independent financial defects remain separate. + BCQuality intentionally does not duplicate mechanical diagnostics already enforced by the AL compiler or standard analyzers. Run the consuming app's normal compiler and analyzer pipeline alongside review and authoring. Knowledge diff --git a/microsoft/knowledge/scm/cancel-reservations-through-reservation-management.bad.al b/microsoft/knowledge/scm/cancel-reservations-through-reservation-management.bad.al index 8d1e498a..248f3a40 100644 --- a/microsoft/knowledge/scm/cancel-reservations-through-reservation-management.bad.al +++ b/microsoft/knowledge/scm/cancel-reservations-through-reservation-management.bad.al @@ -1,8 +1,3 @@ -namespace BCQuality.SCM.Samples; - -using Microsoft.Inventory.Tracking; -using Microsoft.Sales.Document; - codeunit 50106 "SCM Cancel Reservation Bad" { procedure CancelSalesReservation(ReservationEntryNo: Integer) diff --git a/microsoft/knowledge/scm/cancel-reservations-through-reservation-management.good.al b/microsoft/knowledge/scm/cancel-reservations-through-reservation-management.good.al index 91b39d75..3d5849dd 100644 --- a/microsoft/knowledge/scm/cancel-reservations-through-reservation-management.good.al +++ b/microsoft/knowledge/scm/cancel-reservations-through-reservation-management.good.al @@ -1,8 +1,3 @@ -namespace BCQuality.SCM.Samples; - -using Microsoft.Inventory.Tracking; -using Microsoft.Sales.Document; - codeunit 50107 "SCM Cancel Reservation Good" { procedure CancelSalesReservation(ReservationEntryNo: Integer) diff --git a/microsoft/knowledge/scm/cancel-reservations-through-reservation-management.md b/microsoft/knowledge/scm/cancel-reservations-through-reservation-management.md index 76857018..1df3b125 100644 --- a/microsoft/knowledge/scm/cancel-reservations-through-reservation-management.md +++ b/microsoft/knowledge/scm/cancel-reservations-through-reservation-management.md @@ -15,22 +15,21 @@ Persistent `"Reservation Entry"` rows are not disposable allocation markers. Res ## Best Practice -For explicit cancellation of an existing binding reservation, use `"Reservation Engine Mgt.".CancelReservation`. It checks the reservation status and `"Disallow Cancellation"`, handles the counterpart, and preserves or retracks the remaining source quantities as appropriate. For source-line quantity changes, use the source-specific reservation management path rather than deleting its reservation rows yourself. +Explicit cancellation of an existing binding reservation uses `"Reservation Engine Mgt.".CancelReservation`. It checks the reservation status and `"Disallow Cancellation"`, handles the counterpart, and preserves or retracks the remaining source quantities as appropriate. Source-line quantity changes have their own source-specific reservation management path. -Do not require every Reservation Entry to have a partner or identical lot/serial values on both sides: Surplus/Prospect entries and supported late-binding scenarios need different treatment. Temporary buffers, engine-owned updates, and supported publisher metadata are not independent cancellation. Cancelling a reservation is also different from intentionally removing an item-tracking assignment. +Not every Reservation Entry has a partner or identical lot/serial values on both sides: Surplus/Prospect entries and supported late-binding scenarios have different relationships. Temporary buffers, engine-owned updates, and supported publisher metadata are not independent cancellation. Cancelling a reservation is also different from intentionally removing an item-tracking assignment. The samples retrieve the negative side of a persistent sales-line reservation and cancel only the binding. They do not delete the sales line or remove its tracking specifications. -## Anti Pattern +See sample: [`cancel-reservations-through-reservation-management.good.al`](cancel-reservations-through-reservation-management.good.al). -Report `Delete(true)`, `DeleteAll`, or a status/source rewrite on persistent `"Reservation Entry"` records used as a replacement for cancelling a reservation. Deleting both sides is not a sufficient repair: it can still discard tracking that should survive and omit retracking. +## Anti Pattern -Require cancellation intent and a binding reservation in the visible context. Do not flag normal processing of temporary Prospect/Surplus buffers or diagnose every single row as an orphan. +`Delete(true)`, `DeleteAll`, or a status/source rewrite on persistent `"Reservation Entry"` records does not perform binding-reservation cancellation. Even deleting both sides can discard tracking that should survive and omit retracking. -## Samples +Normal processing of temporary Prospect/Surplus buffers is outside that cancellation workflow, and an unpaired row is not intrinsically an orphan. -- [`cancel-reservations-through-reservation-management.bad.al`](cancel-reservations-through-reservation-management.bad.al) -- [`cancel-reservations-through-reservation-management.good.al`](cancel-reservations-through-reservation-management.good.al) +See sample: [`cancel-reservations-through-reservation-management.bad.al`](cancel-reservations-through-reservation-management.bad.al). ## References diff --git a/microsoft/knowledge/scm/carry-out-requisition-actions-through-the-standard-workflow.bad.al b/microsoft/knowledge/scm/carry-out-requisition-actions-through-the-standard-workflow.bad.al index be35a154..7bc4df82 100644 --- a/microsoft/knowledge/scm/carry-out-requisition-actions-through-the-standard-workflow.bad.al +++ b/microsoft/knowledge/scm/carry-out-requisition-actions-through-the-standard-workflow.bad.al @@ -1,9 +1,3 @@ -namespace BCQuality.SCM.Samples; - -using Microsoft.Inventory.Requisition; -using Microsoft.Purchases.Document; -using Microsoft.Sales.Document; - codeunit 50116 "SCM Requisition Action Bad" { procedure CarryOutAcceptedNewPurchase(TemplateName: Code[10]; BatchName: Code[10]; LineNo: Integer; OrderDate: Date; PostingDate: Date; ReceiptDate: Date; CutoffDate: Date) diff --git a/microsoft/knowledge/scm/carry-out-requisition-actions-through-the-standard-workflow.good.al b/microsoft/knowledge/scm/carry-out-requisition-actions-through-the-standard-workflow.good.al index fd2e7743..c09ea65a 100644 --- a/microsoft/knowledge/scm/carry-out-requisition-actions-through-the-standard-workflow.good.al +++ b/microsoft/knowledge/scm/carry-out-requisition-actions-through-the-standard-workflow.good.al @@ -1,9 +1,3 @@ -namespace BCQuality.SCM.Samples; - -using Microsoft.Inventory.Requisition; -using Microsoft.Purchases.Document; -using Microsoft.Sales.Document; - codeunit 50117 "SCM Requisition Action Good" { procedure CarryOutAcceptedNewPurchase(TemplateName: Code[10]; BatchName: Code[10]; LineNo: Integer; OrderDate: Date; PostingDate: Date; ReceiptDate: Date; CutoffDate: Date) diff --git a/microsoft/knowledge/scm/carry-out-requisition-actions-through-the-standard-workflow.md b/microsoft/knowledge/scm/carry-out-requisition-actions-through-the-standard-workflow.md index bd3c38ea..88f68bdf 100644 --- a/microsoft/knowledge/scm/carry-out-requisition-actions-through-the-standard-workflow.md +++ b/microsoft/knowledge/scm/carry-out-requisition-actions-through-the-standard-workflow.md @@ -15,24 +15,23 @@ A requisition/planning line is a pending change to a supply/demand network, not ## Best Practice -For requisition batch carry-out, initialize `"Req. Wksh.-Make Order"` with `Set` and invoke `CarryOutBatchAction` on the intended accepted lines. Supply the order/posting/receipt defaults separately from the ending-order-date cutoff, and preserve the selected worksheet/batch/line filters. A plain `Run` or a single order-line insertion helper is not a replacement for this batch initialization and finalization. +Requisition batch carry-out initializes `"Req. Wksh.-Make Order"` with `Set` and invokes `CarryOutBatchAction` on the intended accepted lines. Order/posting/receipt defaults are separate from the ending-order-date cutoff, and worksheet/batch/line filters define the selection. A plain `Run` or a single order-line insertion helper is not a replacement for this batch initialization and finalization. -Use the standard `"Carry Out Action"` dispatch for broader planning output and its configured purchase, transfer, assembly, or manufacturing choices. Do not turn every action into a new purchase order, bypass source-specific reservation transfer, or delete proposals before the owning workflow has completed their supply change. +The standard `"Carry Out Action"` dispatch handles broader planning output and its configured purchase, transfer, assembly, or manufacturing choices. Each action's supply change and source-specific reservation transfer precede proposal finalization; not every action creates a new purchase order. Ordinary manual purchase creation that does not consume planning output is outside this rule. Users may reject or delete unwanted proposals without creating supply; temporary planning simulations, pre-carry-out enrichment, and engine-owned cleanup are also legitimate. `Delete(true)` on a requisition line is not intrinsically a defect. The samples select an existing accepted New/Purchase item proposal with sales-demand context. Dates are explicit, the source selection remains bounded, and the clean sample leaves order creation and reservation handoff to the standard workflow; it is not a complete planning-run generator. -## Anti Pattern +See sample: [`carry-out-requisition-actions-through-the-standard-workflow.good.al`](carry-out-requisition-actions-through-the-standard-workflow.good.al). -Report code that consumes accepted persistent `"Requisition Line"` action messages, manually creates or changes supply from a subset of fields, and then deletes or marks the proposal handled without the standard carry-out/source-reservation handoff. Running purchase-field validation and the requisition delete trigger does not first move the proposal's demand links to the new purchase line. +## Anti Pattern -Require both proposal-consumption intent and a visible supply conversion. Do not flag an isolated deletion of an unwanted suggestion, an ordinary purchase-order API, or the standard carry-out engine's own insert/delete sequence. +Manually creating or changing supply from a subset of an accepted persistent `"Requisition Line"`, then deleting or marking that proposal handled, skips the standard carry-out/source-reservation handoff. Purchase-field validation and the requisition delete trigger do not first move the proposal's demand links to the new purchase line. -## Samples +Deleting an unwanted suggestion or creating an ordinary purchase order without consuming planning output is a separate operation. The standard carry-out engine's own insert/delete sequence participates in the source handoff rather than replacing it. -- [`carry-out-requisition-actions-through-the-standard-workflow.bad.al`](carry-out-requisition-actions-through-the-standard-workflow.bad.al) -- [`carry-out-requisition-actions-through-the-standard-workflow.good.al`](carry-out-requisition-actions-through-the-standard-workflow.good.al) +See sample: [`carry-out-requisition-actions-through-the-standard-workflow.bad.al`](carry-out-requisition-actions-through-the-standard-workflow.bad.al). ## References diff --git a/microsoft/knowledge/scm/change-item-applications-through-posting-routines.bad.al b/microsoft/knowledge/scm/change-item-applications-through-posting-routines.bad.al index e5ec9cbd..f0239dba 100644 --- a/microsoft/knowledge/scm/change-item-applications-through-posting-routines.bad.al +++ b/microsoft/knowledge/scm/change-item-applications-through-posting-routines.bad.al @@ -1,7 +1,3 @@ -namespace BCQuality.SCM.Samples; - -using Microsoft.Inventory.Ledger; - codeunit 50104 "SCM Item Application Bad" { procedure ChangeSalesQuantityApplication(ApplicationEntryNo: Integer; NewInboundEntryNo: Integer) diff --git a/microsoft/knowledge/scm/change-item-applications-through-posting-routines.good.al b/microsoft/knowledge/scm/change-item-applications-through-posting-routines.good.al index bca8ea63..e176aadd 100644 --- a/microsoft/knowledge/scm/change-item-applications-through-posting-routines.good.al +++ b/microsoft/knowledge/scm/change-item-applications-through-posting-routines.good.al @@ -1,8 +1,3 @@ -namespace BCQuality.SCM.Samples; - -using Microsoft.Inventory.Ledger; -using Microsoft.Inventory.Posting; - codeunit 50105 "SCM Item Application Good" { procedure ChangeSalesQuantityApplication(ApplicationEntryNo: Integer; NewInboundEntryNo: Integer) diff --git a/microsoft/knowledge/scm/change-item-applications-through-posting-routines.md b/microsoft/knowledge/scm/change-item-applications-through-posting-routines.md index 69a1aed8..5b5443e5 100644 --- a/microsoft/knowledge/scm/change-item-applications-through-posting-routines.md +++ b/microsoft/knowledge/scm/change-item-applications-through-posting-routines.md @@ -15,22 +15,21 @@ An `"Item Application Entry"` connects quantity application to cost flow; changi ## Best Practice -Prefer the Application Worksheet for interactive corrections. For a narrowly controlled programmatic correction of an ordinary quantity application, use the same `"Item Jnl.-Post Line"` instance for `UnApply`, reload the affected outbound item entry, then `ReApply` it to the compatible inbound entry. Complete the application's `RedoApplications`, `CostAdjust`, and `ClearApplicationLog` lifecycle; do not commit a half-completed replacement. +The Application Worksheet provides the interactive correction workflow. A narrowly controlled programmatic correction of an ordinary quantity application uses the same `"Item Jnl.-Post Line"` instance for `UnApply`, a reload of the affected outbound item entry, and `ReApply` to the compatible inbound entry. Its finalization lifecycle includes `RedoApplications`, `CostAdjust`, and `ClearApplicationLog`. -Respect the posting routines' inventory-period, correction, transfer, and drop-shipment restrictions rather than bypassing them. `"Transferred-from Entry No."`, outbound transfers, and special application types are not permission to reuse the ordinary-sales sample unchecked. Do not enable application-check bypasses or borrow the worksheet's multi-step recovery flags for a standalone transaction. +The posting routines enforce inventory-period, correction, transfer, and drop-shipment restrictions. Entries with `"Transferred-from Entry No."`, outbound transfers, and special application types are outside the ordinary-sales sample's scope. Application-check bypasses remove those protections, and the worksheet's multi-step recovery flags belong to its UI lifecycle rather than a standalone transaction. -`CostAdjust` honors automatic-cost-adjustment setup; calling it does not promise that all costs are settled when adjustment is disabled or deferred. Retain the required scheduled/manual adjustment process. Temporary application projections, extension metadata, and source-document reservation/order-tracking changes are not edits to the persistent item-application graph. +`CostAdjust` honors automatic-cost-adjustment setup; calling it does not mean all costs are settled when adjustment is disabled or deferred. Scheduled/manual adjustment remains necessary in those configurations. Temporary application projections, extension metadata, and source-document reservation/order-tracking changes are not edits to the persistent item-application graph. -## Anti Pattern +See sample: [`change-item-applications-through-posting-routines.good.al`](change-item-applications-through-posting-routines.good.al). -Report independent `Modify`, `Delete`, or replacement `Insert` operations on persistent `"Item Application Entry"` rows used to repoint a receipt/shipment application, including a change to `"Inbound Item Entry No."` that leaves remaining quantities and cost propagation untouched. Valid item numbers, matching quantities, or running table triggers do not complete reapplication. +## Anti Pattern -Also report a visibly incomplete custom unapply/reapply transaction that omits finalization or commits between the two operations. Do not flag code merely because the standard posting/application workflow internally writes these tables. +Independent `Modify`, `Delete`, or replacement `Insert` operations on persistent `"Item Application Entry"` rows can repoint a receipt/shipment application without updating remaining quantities or cost propagation. Valid item numbers, matching quantities, and running table triggers do not complete reapplication. -## Samples +Omitting finalization or committing between unapply and reapply exposes an incomplete replacement. The standard posting/application workflow's internal table writes differ because they participate in that lifecycle. -- [`change-item-applications-through-posting-routines.bad.al`](change-item-applications-through-posting-routines.bad.al) -- [`change-item-applications-through-posting-routines.good.al`](change-item-applications-through-posting-routines.good.al) +See sample: [`change-item-applications-through-posting-routines.bad.al`](change-item-applications-through-posting-routines.bad.al). ## References diff --git a/microsoft/knowledge/scm/post-item-ledger-changes-through-item-journals.bad.al b/microsoft/knowledge/scm/post-item-ledger-changes-through-item-journals.bad.al index 3088a43c..1187f96d 100644 --- a/microsoft/knowledge/scm/post-item-ledger-changes-through-item-journals.bad.al +++ b/microsoft/knowledge/scm/post-item-ledger-changes-through-item-journals.bad.al @@ -1,8 +1,3 @@ -namespace BCQuality.SCM.Samples; - -using Microsoft.Inventory.Journal; -using Microsoft.Inventory.Ledger; - codeunit 50100 "SCM Stock Adjustment Bad" { procedure PostPreparedPositiveAdjustment(ItemJournalLine: Record "Item Journal Line"; NewEntryNo: Integer) diff --git a/microsoft/knowledge/scm/post-item-ledger-changes-through-item-journals.good.al b/microsoft/knowledge/scm/post-item-ledger-changes-through-item-journals.good.al index b7c4345f..3e7866a1 100644 --- a/microsoft/knowledge/scm/post-item-ledger-changes-through-item-journals.good.al +++ b/microsoft/knowledge/scm/post-item-ledger-changes-through-item-journals.good.al @@ -1,8 +1,3 @@ -namespace BCQuality.SCM.Samples; - -using Microsoft.Inventory.Journal; -using Microsoft.Inventory.Posting; - codeunit 50101 "SCM Stock Adjustment Good" { procedure PostPreparedPositiveAdjustment(var ItemJournalLine: Record "Item Journal Line") diff --git a/microsoft/knowledge/scm/post-item-ledger-changes-through-item-journals.md b/microsoft/knowledge/scm/post-item-ledger-changes-through-item-journals.md index 234b91e1..0df6dac2 100644 --- a/microsoft/knowledge/scm/post-item-ledger-changes-through-item-journals.md +++ b/microsoft/knowledge/scm/post-item-ledger-changes-through-item-journals.md @@ -15,24 +15,23 @@ An item ledger entry is not an independently insertable stock balance. Posting c ## Best Practice -For a prepared standalone item-journal movement, enter through codeunit `"Item Jnl.-Post Line".RunWithCheck`. For a persisted journal batch, use `"Item Jnl.-Post Batch"`; for a sales, purchase, transfer, assembly, or production transaction, retain that workflow's owning document/posting orchestration rather than replacing it with a naked journal call. Let the posting engine create the ledger, value, and application records and perform its checks. +The posting entry point for a prepared standalone item-journal movement is `"Item Jnl.-Post Line".RunWithCheck`. Persisted journal batches use `"Item Jnl.-Post Batch"`; sales, purchase, transfer, assembly, and production transactions retain their owning document/posting orchestration. Those workflows create the ledger, value, and application records together with their required checks. -Extend supported posting events and pass validated journal/source data into the owning workflow. Read-only ledger queries, temporary previews, extension-owned metadata fields, and supported publisher parameters consumed by the poster are not independent ledger posting and must not be flagged merely because they assign record fields. A publisher's `var` parameter or `IsHandled` flag is not blanket authorization to recreate quantity/cost state. A change inside the posting engine itself requires tracing that engine's surrounding invariants, not a ban on its own inserts. +Supported posting events enrich validated journal/source data within the owning workflow. Read-only ledger queries, temporary ledger previews, extension-owned metadata fields, and publisher parameters consumed by the poster are not independent ledger posting. A temporary item-journal buffer can still produce persistent entries when passed to a posting codeunit. A publisher's `var` parameter or `IsHandled` flag alone does not supply the missing quantity/cost coordination; the normal engine's own inserts operate within that coordination. -Do not require every value entry to point to an item ledger entry: capacity and production WIP have their own supported posting relationships. Assembly and manufacturing posting retain order/component/routing and capacity context; one bare output/consumption call is not full order completion. +Not every value entry points to an item ledger entry: capacity and production WIP have their own supported posting relationships. Assembly and manufacturing posting retain order/component/routing and capacity context; one bare output/consumption call is not full order completion. The clean sample takes an already prepared positive-adjustment journal line. It is not a substitute for journal preparation, batch revaluation, warehouse reconciliation, or source-document posting. -## Anti Pattern +See sample: [`post-item-ledger-changes-through-item-journals.good.al`](post-item-ledger-changes-through-item-journals.good.al). -Report extension code that independently inserts/deletes persistent `"Item Ledger Entry"` or `"Value Entry"` transaction rows, or overwrites posted quantity, remaining quantity, application identity, or cost amounts to implement a receipt, shipment, adjustment, or cost correction. `Insert(true)`, `Modify(true)`, and balanced-looking quantities do not supply the missing posting orchestration. +## Anti Pattern -Require evidence of a persistent transaction mutation and its business purpose; a table declaration or a write to a custom annotation field is insufficient. For a more specific revaluation or application defect, prefer the corresponding SCM article rather than reporting the same correction twice. +Independent inserts/deletes of persistent `"Item Ledger Entry"` or `"Value Entry"` transaction rows, or overwrites of posted quantity, remaining quantity, application identity, or cost amounts, bypass the coordinated receipt, shipment, adjustment, or cost-correction workflow. `Insert(true)`, `Modify(true)`, and balanced-looking quantities do not supply that orchestration: stock can change without the corresponding application/value graph, or downstream cost flow can remain stale. -## Samples +A table declaration or custom annotation-field update does not change inventory quantities or costs and is outside this transaction-state concern. -- [`post-item-ledger-changes-through-item-journals.bad.al`](post-item-ledger-changes-through-item-journals.bad.al) -- [`post-item-ledger-changes-through-item-journals.good.al`](post-item-ledger-changes-through-item-journals.good.al) +See sample: [`post-item-ledger-changes-through-item-journals.bad.al`](post-item-ledger-changes-through-item-journals.bad.al). ## References diff --git a/microsoft/knowledge/scm/post-revaluation-through-the-item-journal-batch.bad.al b/microsoft/knowledge/scm/post-revaluation-through-the-item-journal-batch.bad.al index d646d0b1..df450cc8 100644 --- a/microsoft/knowledge/scm/post-revaluation-through-the-item-journal-batch.bad.al +++ b/microsoft/knowledge/scm/post-revaluation-through-the-item-journal-batch.bad.al @@ -1,8 +1,3 @@ -namespace BCQuality.SCM.Samples; - -using Microsoft.Inventory.Journal; -using Microsoft.Inventory.Posting; - codeunit 50102 "SCM Revaluation Batch Bad" { procedure PostCalculatedRevaluationBatch(TemplateName: Code[10]; BatchName: Code[10]) diff --git a/microsoft/knowledge/scm/post-revaluation-through-the-item-journal-batch.good.al b/microsoft/knowledge/scm/post-revaluation-through-the-item-journal-batch.good.al index 0dec6563..810fad5d 100644 --- a/microsoft/knowledge/scm/post-revaluation-through-the-item-journal-batch.good.al +++ b/microsoft/knowledge/scm/post-revaluation-through-the-item-journal-batch.good.al @@ -1,8 +1,3 @@ -namespace BCQuality.SCM.Samples; - -using Microsoft.Inventory.Journal; -using Microsoft.Inventory.Posting; - codeunit 50103 "SCM Revaluation Batch Good" { procedure PostCalculatedRevaluationBatch(TemplateName: Code[10]; BatchName: Code[10]) diff --git a/microsoft/knowledge/scm/post-revaluation-through-the-item-journal-batch.md b/microsoft/knowledge/scm/post-revaluation-through-the-item-journal-batch.md index 67c425f5..9730c2dc 100644 --- a/microsoft/knowledge/scm/post-revaluation-through-the-item-journal-batch.md +++ b/microsoft/knowledge/scm/post-revaluation-through-the-item-journal-batch.md @@ -15,22 +15,21 @@ A calculated revaluation line with nonblank `"Inventory Value Per"` represents a ## Best Practice -Post a prepared revaluation batch through `"Item Jnl.-Post Batch"`. Keep the calculated line's valuation date, aggregation scope, location/variant filters, and revaluation fields intact. The batch expands summarized values into per-entry postings and checks that the eligible inventory has not changed; for partial revaluation it also rechecks remaining quantity before posting. +Posting a prepared revaluation batch through `"Item Jnl.-Post Batch"` preserves the calculated line's valuation date, aggregation scope, location/variant filters, and revaluation fields. The batch expands summarized values into per-entry postings and checks that the eligible inventory has not changed; for partial revaluation it also rechecks remaining quantity before posting. -Do not treat the public `"Item Jnl.-Post Line".RunWithCheck` API as a replacement for that orchestration. It remains legitimate for finalized individual-entry revaluation lines within a workflow that already supplies the necessary checks; the batch itself uses the line poster. A call to that API without evidence of summarized or partial revaluation is not this defect. +The public `"Item Jnl.-Post Line".RunWithCheck` API does not replace that orchestration. It remains legitimate for finalized individual-entry revaluation lines within a workflow that already supplies the necessary checks; the batch itself uses the line poster. Ordinary quantity journals and finalized per-entry revaluations are distinct from this summarized/partial-revaluation case. The samples explicitly require `"Value Entry Type" = Revaluation` and a nonblank `"Inventory Value Per"` in an existing calculated journal batch. They demonstrate posting, not how to calculate a new valuation or choose a standard cost. -## Anti Pattern +See sample: [`post-revaluation-through-the-item-journal-batch.good.al`](post-revaluation-through-the-item-journal-batch.good.al). -Report a loop that sends calculated aggregate revaluation lines straight to `"Item Jnl.-Post Line"`, or a custom partial-revaluation workflow that bypasses the remaining-quantity recheck visible in the standard batch. A loop over the journal is not equivalent to distributing the aggregate over its underlying item entries. +## Anti Pattern -Do not recommend directly editing existing `"Value Entry"` cost amounts or the Item's unit cost to repair the result. Use the revaluation/cost-adjustment workflow appropriate to the correction. +A loop that sends calculated aggregate revaluation lines straight to `"Item Jnl.-Post Line"` skips distribution over the underlying item entries. A partial-revaluation workflow without the remaining-quantity recheck can post a valuation against inventory that no longer matches the calculation. -## Samples +Directly editing existing `"Value Entry"` cost amounts or the Item's unit cost does not repair those allocation and adjustment relationships. Their correction belongs to the revaluation/cost-adjustment workflow. -- [`post-revaluation-through-the-item-journal-batch.bad.al`](post-revaluation-through-the-item-journal-batch.bad.al) -- [`post-revaluation-through-the-item-journal-batch.good.al`](post-revaluation-through-the-item-journal-batch.good.al) +See sample: [`post-revaluation-through-the-item-journal-batch.bad.al`](post-revaluation-through-the-item-journal-batch.bad.al). ## References diff --git a/microsoft/knowledge/scm/post-transfers-through-shipment-and-receipt-codeunits.bad.al b/microsoft/knowledge/scm/post-transfers-through-shipment-and-receipt-codeunits.bad.al index 04806618..2e149200 100644 --- a/microsoft/knowledge/scm/post-transfers-through-shipment-and-receipt-codeunits.bad.al +++ b/microsoft/knowledge/scm/post-transfers-through-shipment-and-receipt-codeunits.bad.al @@ -1,10 +1,3 @@ -namespace BCQuality.SCM.Samples; - -using Microsoft.Inventory.Journal; -using Microsoft.Inventory.Location; -using Microsoft.Inventory.Posting; -using Microsoft.Inventory.Transfer; - codeunit 50112 "SCM Transfer Posting Bad" { procedure ShipTransferOrder(TransferOrderNo: Code[20]; var ItemJournalLine: Record "Item Journal Line") diff --git a/microsoft/knowledge/scm/post-transfers-through-shipment-and-receipt-codeunits.good.al b/microsoft/knowledge/scm/post-transfers-through-shipment-and-receipt-codeunits.good.al index c6eb1008..8507c99b 100644 --- a/microsoft/knowledge/scm/post-transfers-through-shipment-and-receipt-codeunits.good.al +++ b/microsoft/knowledge/scm/post-transfers-through-shipment-and-receipt-codeunits.good.al @@ -1,8 +1,3 @@ -namespace BCQuality.SCM.Samples; - -using Microsoft.Inventory.Location; -using Microsoft.Inventory.Transfer; - codeunit 50113 "SCM Transfer Posting Good" { procedure ShipTransferOrder(TransferOrderNo: Code[20]) diff --git a/microsoft/knowledge/scm/post-transfers-through-shipment-and-receipt-codeunits.md b/microsoft/knowledge/scm/post-transfers-through-shipment-and-receipt-codeunits.md index c7e4b8a4..b541dbab 100644 --- a/microsoft/knowledge/scm/post-transfers-through-shipment-and-receipt-codeunits.md +++ b/microsoft/knowledge/scm/post-transfers-through-shipment-and-receipt-codeunits.md @@ -15,22 +15,21 @@ A two-step transfer order preserves a continuous quantity, reservation, and cost ## Best Practice -For a prepared non-direct transfer order without required warehouse documents, use `"TransferOrder-Post Shipment".Run` at shipment and `"TransferOrder-Post Receipt".Run` at receipt, passing the actual `"Transfer Header"`. Validate the intended quantities to ship/receive through the source document; do not assign posted quantity counters as preparation. +A prepared non-direct transfer order without required warehouse documents uses `"TransferOrder-Post Shipment".Run` at shipment and `"TransferOrder-Post Receipt".Run` at receipt, with the actual `"Transfer Header"`. Validated source quantities to ship/receive prepare the operation; posted quantity counters are results of posting. -When warehouse shipment or receipt is required, use the warehouse document posting workflow that invokes the transfer poster with its real source context. Retain the configured standard direct-transfer workflow for direct transfers; the two-step sample's in-transit guard is not a universal requirement. +Required warehouse shipment or receipt enters the warehouse document posting workflow, which invokes the transfer poster with its real source context. Direct transfers have their configured standard workflow; the two-step sample's in-transit guard is not a universal requirement. -Standalone item reclassification journals and bin movements are legitimate separate operations. Do not demand a fixed number of item ledger entries, or a nonzero `"Transferred-from Entry No."` on every transfer application: tracking/application splits and average-cost transfer handling differ. Require evidence that code is replacing completion of an existing transfer order, not merely moving stock through another supported process. +Standalone item reclassification journals and bin movements are legitimate separate operations, not completion of an existing transfer order. Tracking/application splits and average-cost handling mean transfers do not have one fixed item-entry count or a nonzero `"Transferred-from Entry No."` on every application. -## Anti Pattern +See sample: [`post-transfers-through-shipment-and-receipt-codeunits.good.al`](post-transfers-through-shipment-and-receipt-codeunits.good.al). -Report ad-hoc item postings, independent positive/negative adjustments, manually created posted-transfer rows, or changes to source shipment/receipt counters used to stand in for transfer-order posting. Updating `"Last Shipment No."` after a bare item-journal call does not create the posted shipment, source-line progress, or transfer application lineage. +## Anti Pattern -Do not repair this by changing an existing item ledger entry's location or inventing application links. Route the source transaction through its owning shipment/receipt or configured direct-transfer workflow. Metadata enrichment inside that workflow is not itself a posting bypass. +Ad-hoc item postings, independent positive/negative adjustments, manually created posted-transfer rows, and direct shipment/receipt-counter changes cannot substitute for transfer-order posting. Updating `"Last Shipment No."` after a bare item-journal call does not create the posted shipment, source-line progress, or transfer application lineage. -## Samples +Changing an existing item ledger entry's location or inventing application links does not repair that missing workflow. Metadata enrichment within the normal shipment/receipt or direct-transfer workflow is distinct from replacing the posting operation. -- [`post-transfers-through-shipment-and-receipt-codeunits.bad.al`](post-transfers-through-shipment-and-receipt-codeunits.bad.al) -- [`post-transfers-through-shipment-and-receipt-codeunits.good.al`](post-transfers-through-shipment-and-receipt-codeunits.good.al) +See sample: [`post-transfers-through-shipment-and-receipt-codeunits.bad.al`](post-transfers-through-shipment-and-receipt-codeunits.bad.al). ## References diff --git a/microsoft/knowledge/scm/reconcile-warehouse-adjustments-with-the-item-ledger.bad.al b/microsoft/knowledge/scm/reconcile-warehouse-adjustments-with-the-item-ledger.bad.al index 769dbd19..97c7cb4a 100644 --- a/microsoft/knowledge/scm/reconcile-warehouse-adjustments-with-the-item-ledger.bad.al +++ b/microsoft/knowledge/scm/reconcile-warehouse-adjustments-with-the-item-ledger.bad.al @@ -1,10 +1,3 @@ -namespace BCQuality.SCM.Samples; - -using Microsoft.Inventory.Item; -using Microsoft.Inventory.Journal; -using Microsoft.Inventory.Location; -using Microsoft.Warehouse.Journal; - codeunit 50110 "SCM Warehouse Adjustment Bad" { procedure ReconcileRegisteredWarehouseAdjustment(ItemNo: Code[20]; LocationCode: Code[10]; TemplateName: Code[10]; BatchName: Code[10]; PostingDate: Date; DocumentNo: Code[20]): Boolean diff --git a/microsoft/knowledge/scm/reconcile-warehouse-adjustments-with-the-item-ledger.good.al b/microsoft/knowledge/scm/reconcile-warehouse-adjustments-with-the-item-ledger.good.al index 087f9562..469ed2d1 100644 --- a/microsoft/knowledge/scm/reconcile-warehouse-adjustments-with-the-item-ledger.good.al +++ b/microsoft/knowledge/scm/reconcile-warehouse-adjustments-with-the-item-ledger.good.al @@ -1,11 +1,3 @@ -namespace BCQuality.SCM.Samples; - -using Microsoft.Inventory.Item; -using Microsoft.Inventory.Journal; -using Microsoft.Inventory.Location; -using Microsoft.Inventory.Posting; -using Microsoft.Warehouse.Journal; - codeunit 50111 "SCM Warehouse Adjustment Good" { procedure ReconcileRegisteredWarehouseAdjustment(ItemNo: Code[20]; LocationCode: Code[10]; TemplateName: Code[10]; BatchName: Code[10]; PostingDate: Date; DocumentNo: Code[20]): Boolean diff --git a/microsoft/knowledge/scm/reconcile-warehouse-adjustments-with-the-item-ledger.md b/microsoft/knowledge/scm/reconcile-warehouse-adjustments-with-the-item-ledger.md index 9297cdcf..06230e75 100644 --- a/microsoft/knowledge/scm/reconcile-warehouse-adjustments-with-the-item-ledger.md +++ b/microsoft/knowledge/scm/reconcile-warehouse-adjustments-with-the-item-ledger.md @@ -15,24 +15,23 @@ At a Directed Put-away and Pick location, registering an ordinary warehouse quan ## Best Practice -After the warehouse adjustment has been registered, run `"Calculate Whse. Adjustment"` for the intended item/location and prepared item-journal batch, then post the generated lines through `"Item Jnl.-Post Batch"`. The calculation derives the reconciliation by location, variant, units of measure, and tracking, marks the lines `"Warehouse Adjustment"`, and accounts for already prepared unposted adjustments. +After warehouse registration, `"Calculate Whse. Adjustment"` prepares item-journal lines for the intended item/location and batch; `"Item Jnl.-Post Batch"` posts those lines. The calculation derives the reconciliation by location, variant, units of measure, and tracking, marks the lines `"Warehouse Adjustment"`, and accounts for already prepared unposted adjustments. -Keep reconciliation separate from source-document posting: warehouse receipts/shipments use their document workflows. Intentional warehouse-only staging is valid when a separately owned reconciliation step completes the process; do not flag the registration call just because that later job is outside the diff. +Reconciliation is separate from source-document posting: warehouse receipts/shipments use their document workflows. Intentional warehouse-only staging is valid when a separately owned reconciliation step completes the process; registration need not perform both phases in one call. -Do not generalize this rule to every warehouse operation. Bin movements need not change total inventory, and warehouse tracking/expiration reclassification has a standard batch path that can also post item-journal entries. Standard reclassification and basic-location item adjustments are not this ordinary advanced-warehouse quantity-adjustment case. +Bin movements need not change total inventory, and warehouse tracking/expiration reclassification has a standard batch path that can also post item-journal entries. Standard reclassification and basic-location item adjustments are not this ordinary advanced-warehouse quantity-adjustment case. The samples start after warehouse quantity registration and report whether inventory reconciliation completed. The clean sample calculates and posts into an empty dedicated batch; it is not a complete warehouse physical-count workflow. -## Anti Pattern +See sample: [`reconcile-warehouse-adjustments-with-the-item-ledger.good.al`](reconcile-warehouse-adjustments-with-the-item-ledger.good.al). -Report a workflow that claims to reconcile a registered advanced-warehouse quantity adjustment by posting a manually mirrored ordinary item-journal line, or that marks reconciliation complete after only warehouse registration or adjustment calculation. Calculation prepares journal lines; it does not post those lines. Require explicit synchronization intent and location/workflow evidence. +## Anti Pattern -Do not repair the defect by inventing positive/negative quantities or flipping `"Warehouse Adjustment"` on an arbitrary line. Use the calculation step so the adjustment-bin balance and the actual tracked quantities drive inventory reconciliation. +A manually mirrored ordinary item-journal line does not reconcile a registered advanced-warehouse quantity adjustment. Likewise, a reconciliation function that returns completion after only registration or adjustment calculation leaves any generated adjustment lines unposted. Calculation prepares journal lines; it does not post them. -## Samples +Invented positive/negative quantities or flipping `"Warehouse Adjustment"` on an arbitrary line does not establish the required relationship to the adjustment-bin balance and tracked quantities. That relationship comes from the calculation step. -- [`reconcile-warehouse-adjustments-with-the-item-ledger.bad.al`](reconcile-warehouse-adjustments-with-the-item-ledger.bad.al) -- [`reconcile-warehouse-adjustments-with-the-item-ledger.good.al`](reconcile-warehouse-adjustments-with-the-item-ledger.good.al) +See sample: [`reconcile-warehouse-adjustments-with-the-item-ledger.bad.al`](reconcile-warehouse-adjustments-with-the-item-ledger.bad.al). ## References diff --git a/microsoft/knowledge/scm/transfer-item-tracking-through-source-reservation-codeunits.bad.al b/microsoft/knowledge/scm/transfer-item-tracking-through-source-reservation-codeunits.bad.al index ef12396d..89368cfc 100644 --- a/microsoft/knowledge/scm/transfer-item-tracking-through-source-reservation-codeunits.bad.al +++ b/microsoft/knowledge/scm/transfer-item-tracking-through-source-reservation-codeunits.bad.al @@ -1,8 +1,3 @@ -namespace BCQuality.SCM.Samples; - -using Microsoft.Inventory.Tracking; -using Microsoft.Sales.Document; - codeunit 50108 "SCM Tracking Transfer Bad" { procedure TransferBlanketOrderTracking(var SourceBlanketOrderLine: Record "Sales Line"; var DestinationSalesOrderLine: Record "Sales Line"; QuantityBaseToTransfer: Decimal) diff --git a/microsoft/knowledge/scm/transfer-item-tracking-through-source-reservation-codeunits.good.al b/microsoft/knowledge/scm/transfer-item-tracking-through-source-reservation-codeunits.good.al index de58f691..a655b349 100644 --- a/microsoft/knowledge/scm/transfer-item-tracking-through-source-reservation-codeunits.good.al +++ b/microsoft/knowledge/scm/transfer-item-tracking-through-source-reservation-codeunits.good.al @@ -1,7 +1,3 @@ -namespace BCQuality.SCM.Samples; - -using Microsoft.Sales.Document; - codeunit 50109 "SCM Tracking Transfer Good" { procedure TransferBlanketOrderTracking(var SourceBlanketOrderLine: Record "Sales Line"; var DestinationSalesOrderLine: Record "Sales Line"; QuantityBaseToTransfer: Decimal) diff --git a/microsoft/knowledge/scm/transfer-item-tracking-through-source-reservation-codeunits.md b/microsoft/knowledge/scm/transfer-item-tracking-through-source-reservation-codeunits.md index 915ec4a5..8bc42ccd 100644 --- a/microsoft/knowledge/scm/transfer-item-tracking-through-source-reservation-codeunits.md +++ b/microsoft/knowledge/scm/transfer-item-tracking-through-source-reservation-codeunits.md @@ -15,22 +15,21 @@ Moving lot/serial tracking between document lines is a source-ownership operatio ## Best Practice -Use the reservation codeunit for the source workflow. For the tracking portion of blanket-sales-order or quote conversion to a sales order, `"Sales Line-Reserve".TransferSaleLineToSalesLine` takes the existing source line, prepared destination line, and quantity to transfer in **base units**. It delegates the source/status and quantity movement to `"Create Reserv. Entry".TransferReservEntry`. +Reservation codeunits provide source-specific tracking workflows. For the tracking portion of blanket-sales-order or quote conversion to a sales order, `"Sales Line-Reserve".TransferSaleLineToSalesLine` takes the existing source line, prepared destination line, and quantity to transfer in **base units**. It delegates the source/status and quantity movement to `"Create Reserv. Entry".TransferReservEntry`. -The caller still owns document conversion and destination-line preparation; this method does not create a sales order. Keep item, variant, location, and source identity consistent, and use other source-specific wrappers for purchases, transfers, assembly, or production instead of reusing a sales wrapper indiscriminately. +The caller still owns document conversion and destination-line preparation; this method does not create a sales order. The source and destination must have compatible item, variant, location, and source identity. Purchases, transfers, assembly, and production have their own source-specific wrappers rather than sharing the sales conversion contract. -`"Item Tracking Management".CopyItemTracking` serves a different purpose: it creates Prospect copies, not a transfer of reservation ownership. That is valid for its intended copy workflow. Temporary Tracking Specification processing is also normal, and persisted historical tracking specifications are not forbidden; distinguish the working/historic representation from the current source booking. +`"Item Tracking Management".CopyItemTracking` serves a different purpose: it creates Prospect copies, not a transfer of reservation ownership. That is valid for its intended copy workflow. Temporary Tracking Specification processing and persisted historical tracking specifications are also normal; the working/historic representation differs from the current source booking. -## Anti Pattern +See sample: [`transfer-item-tracking-through-source-reservation-codeunits.good.al`](transfer-item-tracking-through-source-reservation-codeunits.good.al). -Report direct rewrites of persistent `"Reservation Entry"` source type/subtype, ID, reference number, or quantities to implement source-line conversion or a partial tracking transfer. Changing only `"Quantity (Base)"` and source keys can drop the remainder or leave the other tracking/reservation quantities attached to the wrong source. +## Anti Pattern -Also report use of a tracking copy as a replacement for moving an existing binding reservation when that intent is explicit. Do not flag a legitimate Prospect copy, temporary tracking buffer, historical tracking read, or source-specific engine call merely because it uses these tables. +Direct rewrites of persistent `"Reservation Entry"` source type/subtype, ID, reference number, or quantities do not perform the source-line conversion or partial tracking-transfer workflow. Changing only `"Quantity (Base)"` and source keys can drop the remainder or leave the other tracking/reservation quantities attached to the wrong source. -## Samples +A tracking copy cannot replace movement of an existing binding reservation. Legitimate Prospect copying, temporary tracking buffers, historical tracking reads, and source-specific engine calls serve distinct purposes and are not independent reservation transfers. -- [`transfer-item-tracking-through-source-reservation-codeunits.bad.al`](transfer-item-tracking-through-source-reservation-codeunits.bad.al) -- [`transfer-item-tracking-through-source-reservation-codeunits.good.al`](transfer-item-tracking-through-source-reservation-codeunits.good.al) +See sample: [`transfer-item-tracking-through-source-reservation-codeunits.bad.al`](transfer-item-tracking-through-source-reservation-codeunits.bad.al). ## References diff --git a/microsoft/knowledge/scm/use-date-aware-availability-for-promising.bad.al b/microsoft/knowledge/scm/use-date-aware-availability-for-promising.bad.al index d4069d87..b90f9321 100644 --- a/microsoft/knowledge/scm/use-date-aware-availability-for-promising.bad.al +++ b/microsoft/knowledge/scm/use-date-aware-availability-for-promising.bad.al @@ -1,7 +1,3 @@ -namespace BCQuality.SCM.Samples; - -using Microsoft.Inventory.Item; - codeunit 50114 "SCM Additional Promise Bad" { procedure CanPromiseAdditionalDemand(ItemNo: Code[20]; LocationCode: Code[10]; VariantCode: Code[10]; ShipmentDate: Date; RequestedAdditionalQuantityBase: Decimal; LookaheadDateFormula: DateFormula): Boolean diff --git a/microsoft/knowledge/scm/use-date-aware-availability-for-promising.good.al b/microsoft/knowledge/scm/use-date-aware-availability-for-promising.good.al index c36a46b7..b33ee79e 100644 --- a/microsoft/knowledge/scm/use-date-aware-availability-for-promising.good.al +++ b/microsoft/knowledge/scm/use-date-aware-availability-for-promising.good.al @@ -1,9 +1,3 @@ -namespace BCQuality.SCM.Samples; - -using Microsoft.Foundation.Enums; -using Microsoft.Inventory.Availability; -using Microsoft.Inventory.Item; - codeunit 50115 "SCM Additional Promise Good" { procedure CanPromiseAdditionalDemand(ItemNo: Code[20]; LocationCode: Code[10]; VariantCode: Code[10]; ShipmentDate: Date; RequestedAdditionalQuantityBase: Decimal; LookaheadDateFormula: DateFormula): Boolean diff --git a/microsoft/knowledge/scm/use-date-aware-availability-for-promising.md b/microsoft/knowledge/scm/use-date-aware-availability-for-promising.md index 17723b98..a182a94c 100644 --- a/microsoft/knowledge/scm/use-date-aware-availability-for-promising.md +++ b/microsoft/knowledge/scm/use-date-aware-availability-for-promising.md @@ -15,22 +15,21 @@ application-area: [all] ## Best Practice -For an additional demand not already recorded on a source line, use `"Available to Promise".CalcQtyAvailableToPromise` with the Item's location/variant filters, date range ending on the shipment date, and the configured period/lookahead horizon. Compare in base units. Use a fresh calculation context or the codeunit's recalculation support rather than carrying cached quantities between unrelated items or requests. +For additional demand not already recorded on a source line, `"Available to Promise".CalcQtyAvailableToPromise` uses the Item's location/variant filters, date range ending on the shipment date, and configured period/lookahead horizon. Its result and the additional quantity are compared in base units. A fresh calculation context or the codeunit's recalculation support avoids carrying cached quantities between unrelated items or requests. -For an existing sales-line change, retain the source-aware order-promising/availability workflow, which accounts for the line's own quantity or delta; blindly applying an additional-demand calculation can double-count that line. Assembly and production requirements/supply likewise need the standard availability context, not just a sales-only stock subtraction. +The source-aware order-promising/availability workflow accounts for an existing sales line's own quantity or delta; applying an additional-demand calculation to that line can double-count it. Assembly and production requirements/supply likewise participate in the standard availability context, not just a sales-only stock subtraction. -An ATP result is not a reservation or a guarantee of warehouse pickability. Lot/serial constraints, bins, warehouse activity, and later concurrent changes still need their own checks. Conversely, an on-hand display, valuation report, or deliberately immediate-stock-only check is allowed to use `Item.Inventory`; do not replace its distinct business question with ATP. +An ATP result is not a reservation or a guarantee of warehouse pickability. Lot/serial constraints, bins, warehouse activity, and later concurrent changes still need their own checks. Conversely, an on-hand display, valuation report, or deliberately immediate-stock-only check can use `Item.Inventory`: it answers a different business question from ATP. -## Anti Pattern +See sample: [`use-date-aware-availability-for-promising.good.al`](use-date-aware-availability-for-promising.good.al). -Report `CalcFields(Inventory)` or an equivalent sum of item ledger quantities used as the complete decision for a dated additional-demand promise, including code that applies location/variant filters but ignores other demand and supply. Require explicit promising intent; an Inventory FlowField read alone is not a finding. +## Anti Pattern -Also report a visible loss of location, variant, date, or source-line context in that calculation. Do not invent missing demand in an unseen caller or require this exact API when a visible supported workflow already supplies the correct availability semantics. +`CalcFields(Inventory)` or an equivalent item-ledger quantity sum used as the complete decision for a dated additional-demand promise ignores existing demand and incoming supply, even with location/variant filters. An Inventory FlowField read for an on-hand display has no such promising contract. -## Samples +Losing location, variant, date, or source-line context changes the calculation's business meaning. Another supported workflow that preserves the same availability semantics does not have to call this exact API. -- [`use-date-aware-availability-for-promising.bad.al`](use-date-aware-availability-for-promising.bad.al) -- [`use-date-aware-availability-for-promising.good.al`](use-date-aware-availability-for-promising.good.al) +See sample: [`use-date-aware-availability-for-promising.bad.al`](use-date-aware-availability-for-promising.bad.al). ## References diff --git a/microsoft/skills/review/al-scm-review.md b/microsoft/skills/review/al-scm-review.md index 819defd6..a4345292 100644 --- a/microsoft/skills/review/al-scm-review.md +++ b/microsoft/skills/review/al-scm-review.md @@ -15,77 +15,57 @@ application-area: [all] # AL Supply Chain Management review Reviews AL source against the `scm` knowledge domain. This leaf invokes no -sub-skills and is composed by `al-code-review`. It accepts diffs, individual -files, and complete app folders; for a folder, inspect every relevant AL file, -not a representative sample. A folder supplies no historical baseline. +sub-skills and is composed by `al-code-review`. For a folder, inspect every +relevant AL file; a folder supplies no historical baseline. ## Source -Apply the source-surface gate in Relevance before retrieving knowledge. If the -gate passes, use READ's **Bounded retrieval for review skills** workflow with -`-Domain scm` and `-Technologies @('al')`. Consume every catalog page across -enabled layers, preserving exact paths, complete keywords, applicability, and -unknown dimensions. Select articles using catalog metadata only; never use an -index row as the basis for a finding. - -Entry owns index preparation. Do not rebuild the index in this leaf. When a -helper or prepared index is unavailable or invalid, follow READ's explicit -path-discovery and bounded native-read fallback through EOF. A retrieval -failure is not an empty or clean review. +Apply Relevance's source gate before retrieval. For a relevant scope, use READ's +**Bounded retrieval for review skills** with `-Domain scm` and +`-Technologies @('al')`. Consume every catalog page across enabled layers, +preserving exact paths and applicability. Select from metadata, then read only +worklisted complete articles. Entry owns index preparation; the leaf does not +rebuild. Unavailable/invalid helpers or indexes use READ's bounded native-read +fallback, never a success-shaped empty result. ## Relevance -First inspect the supplied scope for an SCM source surface: a changed -procedure, trigger, subscriber, or bound action that writes or posts inventory, -cost, application, reservation, tracking, warehouse, transfer, or planning -state, or calculates availability for a supply/demand decision. Resolve record -and codeunit declarations, source tables, event publishers, and nearby calls -within the supplied scope. Do not infer object identity from a variable name. - -An `Item` reference, a field caption, an unrelated ledger read, an object name -containing "warehouse", or a broad `ApplicationArea` alone does not pass this -gate. Comments and display strings are not execution evidence. When no source -surface passes, return `not-applicable` with zero coverage and no article-body -reads. In a mixed diff, worklist only the relevant procedures and their visible -supporting context, not every AL file in the app. Unknown application areas do -not by themselves exclude codeunits or subscribers. - -For candidates, apply READ's frontmatter matching semantics: - -- `bc-version`: the target BC major version from application dependency or - host context, not the extension's own version; otherwise unknown. -- `technologies`: AL. -- `countries`: the known target localization or host context; otherwise - unknown, not a guess based on the developer's language. -- `application-area`: the actual known task/object areas, not a substituted - `[all]`. Use explicit inventory, warehousing, assembly, manufacturing, or - supply-chain context to narrow the relevant source, not as proof of a defect. - -Discard nonmatching articles. Retain conditionally applicable articles only -when configuration permits; cap their findings at `medium` confidence and -name every unknown dimension in the message. +Resolve changed record/codeunit types, source tables, publishers and calls. +Gate on code that mutates or posts inventory, application, reservation, +tracking, warehouse, transfer or planning state, or makes a supply/demand +availability decision. Stock displays and other read-only queries without +that decision do not pass the gate. Names, comments, captions, an `Item` +reference or a broad `ApplicationArea` alone are not signals. +If no SCM surface remains, return `not-applicable` with zero coverage +and no article-body retrieval; in mixed diffs, retain only relevant procedures +and their visible supporting context. + +SCM owns `"Item Ledger Entry"`, `"Value Entry"`, `"Capacity Ledger Entry"`, +`"Warehouse Entry"` and inventory posting/application records. Pure `"G/L Entry"`, +`"Cust. Ledger Entry"`, `"Vendor Ledger Entry"`, `"Detailed Cust. Ledg. Entry"`, +`"Detailed Vendor Ledg. Entry"`, `"VAT Entry"` and financial-only posting +mutations belong to Finance. They remain outside SCM even if Finance is absent +or disabled; do not reclaim them as SCM agent findings. Ownership is not a +claim that every owned surface already has a dedicated article. + +Apply READ's frontmatter filters using the target BC major version from +application dependency/host context (not the extension version), AL, known +localization and actual task/object application areas. Omitted context stays +unknown, not `[all]`; unknown areas alone do not exclude codeunits/subscribers. +Retain conditional articles only when configured, cap their findings at +`medium`, and name every unknown dimension. ## Worklist -Extract deterministic tokens from the gated source: resolved object/type -names, quoted field names, methods, enum members, and called publishers. -Lowercase invariantly, replace punctuation and whitespace runs with one hyphen, -and trim leading/trailing hyphens. Thus `"Item Ledger Entry"` becomes -`item-ledger-entry`, `"Qty. (Base)"` becomes `qty-base`, and `RunWithCheck` -becomes `runwithcheck`. Apply the same normalization to catalog keywords. -Match whole normalized tokens/phrases, not substrings such as `item` in an -unrelated identifier. Do not manufacture synonyms that are not supported by -the changed source or the targeted cues. - -Select a catalog row only when a keyword intersects these tokens, or its -path/title/description identifies the same gated source surface **and -operation**. Object declarations establish context; field assignments, calls, -and decision logic establish the operation to evaluate. A shared table name -does not select every rule using that table. - -Use these targeted candidate-selection cues, resolving each slug to its -actual enabled catalog paths. They select articles to read, not findings to -emit; all platform reasoning and exceptions remain in those articles. +Extract resolved object/type names, quoted fields, methods, enum members and +publishers. Normalize these and catalog keywords by lowercasing invariantly, +replacing punctuation/whitespace runs with one hyphen and trimming hyphens: +`"Item Ledger Entry"` becomes `item-ledger-entry`; `RunWithCheck` becomes +`runwithcheck`. Match whole tokens/phrases, not identifier substrings. + +Select matching keywords or catalog topics only for the same source surface +**and operation**. The following cues resolve slugs to actual enabled catalog +paths; they select articles, not findings. Facts and exceptions stay in articles. | Changed source surface and operation | Article slug | | --- | --- | @@ -99,115 +79,43 @@ emit; all platform reasoning and exceptions remain in those articles. | `Inventory`, `CalcQtyAvailableToPromise`, or stock sums used in a dated supply/demand promise, including changed location/variant/date filters and source-demand context | `use-date-aware-availability-for-promising` | | `"Requisition Line"` action-message execution, accepted planning suggestions, `"Req. Wksh.-Make Order"`, `CarryOutBatchAction`, or linked supply creation/change plus requisition-line deletion | `carry-out-requisition-actions-through-the-standard-workflow` | -Do not select a cue solely from a caption, comment, or unrelated declaration. -Use the same gates for clean supported calls so their article exclusions are -evaluated, not just suspicious writes. Applicability is never an anti-pattern. - -Resolve normative conflicts per READ after reading the selected complete -articles. Keep enabled-layer candidates additive unless guidance actually -contradicts; do not deduplicate merely by filename. Record losing candidates -in `suppressed` with `layer-precedence`, and configuration-hidden candidates -with `configuration`. Noncandidates are not suppressions. - -Order exact worklisted paths ordinally and retrieve complete bodies in stable -chunks of at most eight, following every continuation within each chunk. -Never turn the chunk size into a top-eight cutoff. Read samples only when -needed, via their exact READ links and bounded sample retrieval. +Route clean supported calls through the same cues, not just suspicious writes. +Resolve actual normative conflicts per READ, preserving additive layers and +recording `layer-precedence`/`configuration` suppressions, not noncandidates. +Retrieve exact paths in ordinal chunks of at most eight, consume every +continuation, and never impose a top-eight cutoff. Samples use exact READ links. ## Action -Evaluate the visible source against each opened article's normative Best -Practice and Anti Pattern, including its scope and exclusions. Establish the -record's persistence, caller contract, document type/state, and affected -operation from evidence before reporting. Consult the article for treatment -of temporary buffers, supported publisher parameters, managed posting paths, -and legitimate read-only calculations; the skill itself defines no BC rule. -Do not infer missing work in an unseen caller or report every use of a routed -API. A supported alternative is not a defect. - -Emit only a concrete violation with its business consequence and supported -remediation. Use `major` for a demonstrated material SCM defect, `minor` for -a narrower best-practice conflict, and `blocker` only if the opened article -establishes a violated platform-level guarantee. Relevance alone produces no -finding. Deduplicate overlapping findings that prescribe the same correction; -prefer the article that owns the specific operation and retain any other -applicable article as a supporting reference. - -Copy `findings[].id` verbatim from the primary article's exact catalog path; -it must equal `references[0].path`. Cite only complete articles actually read. -Use `high` confidence only for unambiguous source evidence with known -applicability, `medium` for justified inference or conditional applicability. -Never label a guessed API signature or missing workflow context high confidence. - -Agent findings are optional and strictly SCM-scoped. Follow DO's precision -bar: concrete, material defects only, with `references: []`, an `agent:` id, -severity at most `minor`, and confidence at most `medium`. Omit generic AL, -style, performance, privacy, and unrelated technical findings owned by other -leaves. Do not invent a finding to compensate for an empty worklist. - -For an unambiguous local fix, supply literal replacement AL in -`suggested-code`, with a location range covering exactly those lines. Do not -replace an entire business workflow with a sample call that omits the -caller's setup, filters, source identity, or validations. When a mechanical- -looking fix cannot be expressed safely, give `suggested-code-omission-reason`. - -Outcomes follow DO: `completed` after evaluating the complete worklist, -including a clean result; `not-applicable` when the source gate fails; -`no-knowledge` when no applicable corpus survives filtering/configuration; -`partial` when only part of the worklist was evaluated; `failed` when no -reliable result can be produced. A source match with no matching article is -`completed` with an empty worklist, not a claimed evaluation of every SCM -concern. Explain partial/failed results and report accurate coverage. +Evaluate every opened article's normative facts, scope and exclusions against +visible persistence, caller contract, document state and operation. Emit only +concrete violations with business consequences and supported remediation; a +declaration, valid alternative or unseen caller is not evidence of a defect. + +- Use `major` for material SCM defects, `minor` for narrower best-practice + conflicts, and `blocker` only for an article-established platform guarantee. + Applicability alone produces no finding. High confidence requires unambiguous + evidence and known applicability; inference/conditional applicability caps it + at `medium`. +- Apply DO's single-owner deduplication. Equivalent findings for the same + inventory-originated posting bypass and correction have one SCM primary + owner, even when financial records are downstream. Prefer the most specific + SCM article and retain other applicable references as supporting evidence. + Distinct independent financial defects remain Finance; do not duplicate them. +- Agent findings stay strictly SCM-scoped under DO's precision bar, with + `references: []`, an `agent:` id and `minor`/`medium` ceilings. Generic AL and + other domains' concerns remain outside this leaf. +- Supply literal `suggested-code` only for a complete, local, unambiguous fix, + not a sample call that omits workflow setup/source identity. Explain omitted + mechanical-looking fixes with `suggested-code-omission-reason`. + +Outcome selection follows DO, including accurate coverage and reasons for +`partial`/`failed`. No surviving applicable corpus is `no-knowledge`; an existing +corpus with no matching operation is `completed` with an empty worklist. ## Output -Return one strict JSON findings-report per DO and -`schemas/findings-report.schema.json`, with no surrounding prose. Every -finding, including an agent finding, must have -`domain: "Supply Chain Management"`. Do not set `from-sub-skill` in a leaf -report; the coordinator adds it. All locations must identify existing lines -in the supplied source, and any range must start at `location.line`. - -A knowledge-backed finding with an exact article id: - -```json -{ - "skill": { "id": "al-scm-review", "version": 1 }, - "outcome": "completed", - "summary": { - "counts": { "blocker": 0, "major": 1, "minor": 0, "info": 0 }, - "coverage": { "worklist-size": 1, "items-evaluated": 1 } - }, - "findings": [ - { - "id": "microsoft/knowledge/scm/cancel-reservations-through-reservation-management.md", - "severity": "major", - "message": "This cancellation deletes only the negative reservation row. Use Reservation Engine Mgt. cancellation so counterpart and surviving tracking are handled by the owning workflow.", - "location": { "file": "src/CancelReservation.Codeunit.al", "line": 16 }, - "references": [ - { "path": "microsoft/knowledge/scm/cancel-reservations-through-reservation-management.md" } - ], - "confidence": "high", - "domain": "Supply Chain Management", - "suggested-code-omission-reason": "The replacement also requires a codeunit declaration outside the reported line." - } - ], - "suppressed": [] -} -``` - -An unrelated AL change, excluded before article retrieval: - -```json -{ - "skill": { "id": "al-scm-review", "version": 1 }, - "outcome": "not-applicable", - "outcome-reason": "The supplied AL changes contain no SCM posting, state mutation, or supply/demand availability surface.", - "summary": { - "counts": { "blocker": 0, "major": 0, "minor": 0, "info": 0 }, - "coverage": { "worklist-size": 0, "items-evaluated": 0 } - }, - "findings": [], - "suppressed": [] -} -``` +Output conforms to the DO findings-report contract and shared schema. Every +finding MUST set `domain` to `"Supply Chain Management"`. Knowledge-backed ids +equal the primary opened article's exact catalog path. The coordinator, not +this leaf, sets `from-sub-skill`.