Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions .github/scripts/Test-SkillIndex.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -93,14 +93,15 @@ try {
'microsoft/skills/review/al-query-review.md',
'microsoft/skills/review/al-reporting-review.md',
'microsoft/skills/review/al-appsource-review.md',
'microsoft/skills/review/al-telemetry-review.md'
'microsoft/skills/review/al-telemetry-review.md',
'microsoft/skills/review/al-scm-review.md'
)
$review = @($skills | Where-Object id -eq 'al-code-review')
if ($review.Count -ne 1) {
throw "Expected exactly one al-code-review record, found $($review.Count)."
}
if ((@($review[0].subSkills) -join "`n") -cne ($expectedLeaves -join "`n")) {
throw 'al-code-review subSkills did not preserve the declared 17-leaf order.'
throw "al-code-review subSkills did not preserve the declared $($expectedLeaves.Count)-leaf order."
}
foreach ($leafPath in $expectedLeaves) {
$leaf = @($skills | Where-Object path -ceq $leafPath)
Expand Down Expand Up @@ -238,4 +239,4 @@ finally {
Remove-Item -LiteralPath $tmp -Recurse -Force -ErrorAction SilentlyContinue
}

Write-Output 'Skill-index check PASSED: deterministic, schema-valid, and all 17 review leaves preserved in order.'
Write-Output "Skill-index check PASSED: deterministic, schema-valid, and all $($expectedLeaves.Count) review leaves preserved in order."
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,17 +76,20 @@ the review can still discover knowledge by reading the folders.

## Scope

Today's curated content focuses on **technical AL code review**. It augments
Today's curated content covers **technical AL code review** and a focused
**Supply Chain Management (SCM)** functional domain. It augments
the agent's judgment; it is not an exhaustive BC manual or a substitute for
compilation, analyzers, tests, or human review. See
[coverage and limits](docs/using-bcquality.md#coverage-and-limits) for the
available domains and the difference between a folder review and a comparison.
Mechanical issues already enforced by the AL compiler or standard analyzers are
intentionally left to those deterministic tools rather than duplicated here.

Functional areas such as Finance, Supply Chain Management, Manufacturing, Jobs,
Warehousing, and Service, and technologies such as PowerShell, pipelines, and
Power Platform, remain valid future scope, **not current coverage claims**.
The [SCM domain](microsoft/knowledge/scm/) covers selected inventory, costing,
reservation, tracking, and warehouse/posting workflows, not exhaustive supply
chain validation. Broader functional coverage such as Finance, Manufacturing,
Jobs, and Service, and technologies such as PowerShell, pipelines, and Power
Platform, remain valid future scope, **not current coverage claims**.

## What's in this repo

Expand Down
22 changes: 19 additions & 3 deletions docs/using-bcquality.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ branch names with ones in your project.
| Uncommitted changes | Use the installed al-code-review skill to review my staged and unstaged tracked changes against HEAD, without changing files. Identify any untracked AL files not included in that diff. |
| Branch changes | Use the installed al-code-review skill to review changes on this branch since its merge base with `origin/main`. Exclude uncommitted changes and do not edit files. |
| Focused review | Use the installed al-code-review skill to review performance in the app in this folder, without changing files. Return the complete performance findings report. |
| Supply chain code | Use the installed al-code-review skill to review SCM posting, inventory, reservations, item tracking, and warehouse workflows in this app folder, without changing files. Return the complete Supply Chain Management findings report. |
| Agent SDK code | Use the installed al-code-review skill to review Agent SDK implementation and usage in this app folder, without changing files. Return the complete Agents findings report. |

For Git comparisons, the named base ref must exist locally. If it is missing,
Expand Down Expand Up @@ -183,7 +184,7 @@ using your normal compilation, analyzer, test, and human-review workflow.

## Coverage and limits

The Microsoft broad review composes the 17 Microsoft domains listed below.
The Microsoft broad review composes the Microsoft domains listed below.
The Community Agents review is a separate skill selected by the request, not
a nested part of that coordinator. All current review leaves accept app
folders, files, and diffs; request an Agent SDK review explicitly when that
Expand All @@ -193,8 +194,22 @@ Available knowledge is **not** a promise that every rule will run. Selection
depends on the task, target context, enabled layers, and source evidence.
A whole-folder review is a current-state snapshot: detecting a published API
removal or another comparison-only regression requires an actual baseline.
The corpus is technical AL guidance, not exhaustive functional validation or
AppSource certification.
The corpus combines technical AL guidance with targeted functional-domain
invariants, not exhaustive functional validation or AppSource certification.

The SCM leaf owns selected inventory/value, application, reservation, tracking,
and warehouse/posting invariants. It prunes unrelated AL using the actual
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.

SCM owns Item/Value/Capacity/Warehouse and inventory-application posting
records. Pure G/L, customer/vendor/detailed/VAT and financial-only posting
mutations belong to Finance, even when that domain is not enabled. Equivalent
findings for one inventory-originated posting bypass have one SCM primary
owner; distinct independent financial defects remain separate.

BCQuality intentionally does not duplicate mechanical diagnostics already
enforced by the AL compiler or standard analyzers. Run the consuming app's
Expand Down Expand Up @@ -222,6 +237,7 @@ Each article describes one concern. Where samples exist, use its linked
| Reporting | [Reporting](../microsoft/knowledge/reporting/) |
| Security | [Security](../microsoft/knowledge/security/) |
| Style | [Style](../microsoft/knowledge/style/) |
| Supply Chain Management | [SCM](../microsoft/knowledge/scm/) |
| Telemetry | [Telemetry](../microsoft/knowledge/telemetry/) |
| Testing | [Testing](../microsoft/knowledge/testing/) |
| User interface | [UI](../microsoft/knowledge/ui/) |
Expand Down
10 changes: 10 additions & 0 deletions evaluation/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

The SCM `articles` override deliberately selects every rule in the initial
functional domain, producing nine positive cases and nine clean controls.
Business context is executable: document/status `TestField` guards,
calculated-revaluation fields, source-transfer base quantities, warehouse
reconciliation steps, and additional-demand promising parameters survive
neutralization. Do not move those preconditions into comments or generic
"posting" helper names; removing them can turn a real defect into a valid
alternative workflow. The clean pairs exercise the supported APIs selected by
the same routing cues, not merely unrelated code that contains no SCM tokens.

## Validate the corpus

```powershell
Expand Down
13 changes: 13 additions & 0 deletions evaluation/review-fixtures.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,19 @@
"stop-when-runrequestpage-returns-empty-parameters"
]
},
"scm": {
"articles": [
"post-item-ledger-changes-through-item-journals",
"post-revaluation-through-the-item-journal-batch",
"change-item-applications-through-posting-routines",
"cancel-reservations-through-reservation-management",
"transfer-item-tracking-through-source-reservation-codeunits",
"reconcile-warehouse-adjustments-with-the-item-ledger",
"post-transfers-through-shipment-and-receipt-codeunits",
"use-date-aware-availability-for-promising",
"carry-out-requisition-actions-through-the-standard-workflow"
]
},
"style": {
"article": "label-comment-explains-placeholders"
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
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;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
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;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
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

Explicit cancellation of an existing binding reservation uses `"Reservation Engine Mgt.".CancelReservation`. It checks the reservation status and `"Disallow Cancellation"`, handles the counterpart, and preserves or retracks the remaining source quantities as appropriate. Source-line quantity changes have their own source-specific reservation management path.

Not every Reservation Entry has a partner or identical lot/serial values on both sides: Surplus/Prospect entries and supported late-binding scenarios have different relationships. Temporary buffers, engine-owned updates, and supported publisher metadata are not independent cancellation. Cancelling a reservation is also different from intentionally removing an item-tracking assignment.

The samples retrieve the negative side of a persistent sales-line reservation and cancel only the binding. They do not delete the sales line or remove its tracking specifications.

See sample: [`cancel-reservations-through-reservation-management.good.al`](cancel-reservations-through-reservation-management.good.al).

## Anti Pattern

`Delete(true)`, `DeleteAll`, or a status/source rewrite on persistent `"Reservation Entry"` records does not perform binding-reservation cancellation. Even deleting both sides can discard tracking that should survive and omit retracking.

Normal processing of temporary Prospect/Surplus buffers is outside that cancellation workflow, and an unpaired row is not intrinsically an orphan.

See sample: [`cancel-reservations-through-reservation-management.bad.al`](cancel-reservations-through-reservation-management.bad.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)
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
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.';
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
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.';
}
Loading