mirror of
https://github.com/microsoft/BCQuality.git
synced 2026-10-05 22:56:55 +01:00
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>
This commit is contained in:
parent
9ac62e3967
commit
525e84e183
30 changed files with 148 additions and 362 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
See sample: [`cancel-reservations-through-reservation-management.good.al`](cancel-reservations-through-reservation-management.good.al).
|
||||
|
||||
## 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.
|
||||
`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.
|
||||
|
||||
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.
|
||||
Normal processing of temporary Prospect/Surplus buffers is outside that cancellation workflow, and an unpaired row is not intrinsically 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)
|
||||
See sample: [`cancel-reservations-through-reservation-management.bad.al`](cancel-reservations-through-reservation-management.bad.al).
|
||||
|
||||
## References
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
See sample: [`carry-out-requisition-actions-through-the-standard-workflow.good.al`](carry-out-requisition-actions-through-the-standard-workflow.good.al).
|
||||
|
||||
## 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.
|
||||
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.
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
## 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)
|
||||
See sample: [`carry-out-requisition-actions-through-the-standard-workflow.bad.al`](carry-out-requisition-actions-through-the-standard-workflow.bad.al).
|
||||
|
||||
## References
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,3 @@
|
|||
namespace BCQuality.SCM.Samples;
|
||||
|
||||
using Microsoft.Inventory.Ledger;
|
||||
|
||||
codeunit 50104 "SCM Item Application Bad"
|
||||
{
|
||||
procedure ChangeSalesQuantityApplication(ApplicationEntryNo: Integer; NewInboundEntryNo: Integer)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
See sample: [`change-item-applications-through-posting-routines.good.al`](change-item-applications-through-posting-routines.good.al).
|
||||
|
||||
## 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.
|
||||
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.
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
## 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)
|
||||
See sample: [`change-item-applications-through-posting-routines.bad.al`](change-item-applications-through-posting-routines.bad.al).
|
||||
|
||||
## References
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
See sample: [`post-item-ledger-changes-through-item-journals.good.al`](post-item-ledger-changes-through-item-journals.good.al).
|
||||
|
||||
## 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.
|
||||
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.
|
||||
|
||||
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.
|
||||
A table declaration or custom annotation-field update does not change inventory quantities or costs and is outside this transaction-state concern.
|
||||
|
||||
## 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)
|
||||
See sample: [`post-item-ledger-changes-through-item-journals.bad.al`](post-item-ledger-changes-through-item-journals.bad.al).
|
||||
|
||||
## References
|
||||
|
||||
|
|
|
|||
|
|
@ -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])
|
||||
|
|
|
|||
|
|
@ -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])
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
See sample: [`post-revaluation-through-the-item-journal-batch.good.al`](post-revaluation-through-the-item-journal-batch.good.al).
|
||||
|
||||
## 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.
|
||||
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.
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
## 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)
|
||||
See sample: [`post-revaluation-through-the-item-journal-batch.bad.al`](post-revaluation-through-the-item-journal-batch.bad.al).
|
||||
|
||||
## References
|
||||
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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])
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
See sample: [`post-transfers-through-shipment-and-receipt-codeunits.good.al`](post-transfers-through-shipment-and-receipt-codeunits.good.al).
|
||||
|
||||
## 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.
|
||||
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.
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
## 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)
|
||||
See sample: [`post-transfers-through-shipment-and-receipt-codeunits.bad.al`](post-transfers-through-shipment-and-receipt-codeunits.bad.al).
|
||||
|
||||
## References
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
See sample: [`reconcile-warehouse-adjustments-with-the-item-ledger.good.al`](reconcile-warehouse-adjustments-with-the-item-ledger.good.al).
|
||||
|
||||
## 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.
|
||||
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.
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
## 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)
|
||||
See sample: [`reconcile-warehouse-adjustments-with-the-item-ledger.bad.al`](reconcile-warehouse-adjustments-with-the-item-ledger.bad.al).
|
||||
|
||||
## References
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
See sample: [`transfer-item-tracking-through-source-reservation-codeunits.good.al`](transfer-item-tracking-through-source-reservation-codeunits.good.al).
|
||||
|
||||
## 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.
|
||||
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.
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
## 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)
|
||||
See sample: [`transfer-item-tracking-through-source-reservation-codeunits.bad.al`](transfer-item-tracking-through-source-reservation-codeunits.bad.al).
|
||||
|
||||
## References
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
See sample: [`use-date-aware-availability-for-promising.good.al`](use-date-aware-availability-for-promising.good.al).
|
||||
|
||||
## 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.
|
||||
`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.
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
## 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)
|
||||
See sample: [`use-date-aware-availability-for-promising.bad.al`](use-date-aware-availability-for-promising.bad.al).
|
||||
|
||||
## References
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue