mirror of
https://github.com/microsoft/BCQuality.git
synced 2026-10-05 14:46:55 +01:00
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>
This commit is contained in:
parent
b91443beec
commit
9ac62e3967
34 changed files with 1244 additions and 10 deletions
29
.github/scripts/Test-SkillIndex.ps1
vendored
29
.github/scripts/Test-SkillIndex.ps1
vendored
|
|
@ -93,14 +93,15 @@ try {
|
||||||
'microsoft/skills/review/al-query-review.md',
|
'microsoft/skills/review/al-query-review.md',
|
||||||
'microsoft/skills/review/al-reporting-review.md',
|
'microsoft/skills/review/al-reporting-review.md',
|
||||||
'microsoft/skills/review/al-appsource-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')
|
$review = @($skills | Where-Object id -eq 'al-code-review')
|
||||||
if ($review.Count -ne 1) {
|
if ($review.Count -ne 1) {
|
||||||
throw "Expected exactly one al-code-review record, found $($review.Count)."
|
throw "Expected exactly one al-code-review record, found $($review.Count)."
|
||||||
}
|
}
|
||||||
if ((@($review[0].subSkills) -join "`n") -cne ($expectedLeaves -join "`n")) {
|
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) {
|
foreach ($leafPath in $expectedLeaves) {
|
||||||
$leaf = @($skills | Where-Object path -ceq $leafPath)
|
$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 = @{
|
$minimalReport = @{
|
||||||
skill = @{ id = 'al-style-review'; version = 1 }
|
skill = @{ id = 'al-style-review'; version = 1 }
|
||||||
outcome = 'completed'
|
outcome = 'completed'
|
||||||
|
|
@ -238,4 +261,4 @@ finally {
|
||||||
Remove-Item -LiteralPath $tmp -Recurse -Force -ErrorAction SilentlyContinue
|
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."
|
||||||
|
|
|
||||||
11
README.md
11
README.md
|
|
@ -76,7 +76,8 @@ the review can still discover knowledge by reading the folders.
|
||||||
|
|
||||||
## Scope
|
## 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
|
the agent's judgment; it is not an exhaustive BC manual or a substitute for
|
||||||
compilation, analyzers, tests, or human review. See
|
compilation, analyzers, tests, or human review. See
|
||||||
[coverage and limits](docs/using-bcquality.md#coverage-and-limits) for the
|
[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
|
Mechanical issues already enforced by the AL compiler or standard analyzers are
|
||||||
intentionally left to those deterministic tools rather than duplicated here.
|
intentionally left to those deterministic tools rather than duplicated here.
|
||||||
|
|
||||||
Functional areas such as Finance, Supply Chain Management, Manufacturing, Jobs,
|
The [SCM domain](microsoft/knowledge/scm/) covers selected inventory, costing,
|
||||||
Warehousing, and Service, and technologies such as PowerShell, pipelines, and
|
reservation, tracking, and warehouse/posting workflows, not exhaustive supply
|
||||||
Power Platform, remain valid future scope, **not current coverage claims**.
|
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
|
## What's in this repo
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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. |
|
| 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. |
|
| 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. |
|
| 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. |
|
| 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,
|
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
|
## 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
|
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
|
a nested part of that coordinator. All current review leaves accept app
|
||||||
folders, files, and diffs; request an Agent SDK review explicitly when that
|
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.
|
depends on the task, target context, enabled layers, and source evidence.
|
||||||
A whole-folder review is a current-state snapshot: detecting a published API
|
A whole-folder review is a current-state snapshot: detecting a published API
|
||||||
removal or another comparison-only regression requires an actual baseline.
|
removal or another comparison-only regression requires an actual baseline.
|
||||||
The corpus is technical AL guidance, not exhaustive functional validation or
|
The corpus combines technical AL guidance with a focused SCM functional
|
||||||
AppSource certification.
|
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
|
BCQuality intentionally does not duplicate mechanical diagnostics already
|
||||||
enforced by the AL compiler or standard analyzers. Run the consuming app's
|
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/) |
|
| Reporting | [Reporting](../microsoft/knowledge/reporting/) |
|
||||||
| Security | [Security](../microsoft/knowledge/security/) |
|
| Security | [Security](../microsoft/knowledge/security/) |
|
||||||
| Style | [Style](../microsoft/knowledge/style/) |
|
| Style | [Style](../microsoft/knowledge/style/) |
|
||||||
|
| Supply Chain Management | [SCM](../microsoft/knowledge/scm/) |
|
||||||
| Telemetry | [Telemetry](../microsoft/knowledge/telemetry/) |
|
| Telemetry | [Telemetry](../microsoft/knowledge/telemetry/) |
|
||||||
| Testing | [Testing](../microsoft/knowledge/testing/) |
|
| Testing | [Testing](../microsoft/knowledge/testing/) |
|
||||||
| User interface | [UI](../microsoft/knowledge/ui/) |
|
| User interface | [UI](../microsoft/knowledge/ui/) |
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,16 @@ The evaluation is convention-driven. The harness discovers every `<layer>/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.
|
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
|
## Validate the corpus
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,19 @@
|
||||||
"stop-when-runrequestpage-returns-empty-parameters"
|
"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": {
|
"style": {
|
||||||
"article": "label-comment-explains-placeholders"
|
"article": "label-comment-explains-placeholders"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
|
@ -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.';
|
||||||
|
}
|
||||||
|
|
@ -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.';
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
|
@ -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.';
|
||||||
|
}
|
||||||
|
|
@ -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.';
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
|
@ -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.';
|
||||||
|
}
|
||||||
|
|
@ -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.';
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
|
@ -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.';
|
||||||
|
}
|
||||||
|
|
@ -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.';
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
|
@ -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.';
|
||||||
|
}
|
||||||
|
|
@ -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.';
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
|
@ -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.';
|
||||||
|
}
|
||||||
|
|
@ -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.';
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
|
@ -28,6 +28,7 @@ sub-skills:
|
||||||
- microsoft/skills/review/al-reporting-review.md
|
- microsoft/skills/review/al-reporting-review.md
|
||||||
- microsoft/skills/review/al-appsource-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
|
||||||
---
|
---
|
||||||
|
|
||||||
# AL code review
|
# AL code review
|
||||||
|
|
|
||||||
213
microsoft/skills/review/al-scm-review.md
Normal file
213
microsoft/skills/review/al-scm-review.md
Normal file
|
|
@ -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": []
|
||||||
|
}
|
||||||
|
```
|
||||||
Loading…
Add table
Add a link
Reference in a new issue