[Bug 634238] Subcontracting: false 'already created' warning when prod order has multiple lines sharing routing/operation#8121
Conversation
…s sharing routing/operation (#634238) When a Released Production Order had multiple Prod. Order lines using the same Routing/Operation but different Routing Reference No., creating a Subcontracting Order from the routing of a second line incorrectly raised the 'Purchase order(s) have already been created' warning and, when confirmed, opened the unrelated PO of the first line. Root cause: the existence check in ShowExistingPurchaseOrdersForRoutingLines and the navigation in ShowCreatedPurchaseOrder filtered Purchase Lines by Prod. Order No. + Routing No. + Operation No. but not by Routing Reference No., which is the field that identifies the specific Prod. Order line. Fix: add Routing Reference No. to the existence-check filter, and propagate the current routing line's Routing Reference No. to the single-PO navigation branch via a new RoutingReferenceNo global (mirroring the existing OperationNo pattern). Test: SubcSubcontractingTest.CreateSubcontractingPOForEachProdOrderLineWhenLinesShareRoutingAndOperation
There was a problem hiding this comment.
AL Documentation Audit
Documentation gaps were detected in the following apps:
- Subcontracting-Test: 9% documentation coverage
- Subcontracting: 0% documentation coverage
To generate documentation, run /al-docs init or /al-docs update using GitHub Copilot CLI or Claude Code.
This review is for awareness to help keep documentation in sync with code changes. It is okay to dismiss this request.
Shared handler now enqueues data for 20+ unaware tests
Recommendation:
Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why |
…tractingTest.Codeunit.al Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…tractingTest.Codeunit.al Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Shared Initialize modified; may break existing tests
Recommendation:
Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why |
…tractingTest.Codeunit.al Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
RoutingReferenceNo not cleared in internal cleanupThe existing cleanup code at line 213 clears OperationNo but does not clear the newly added RoutingReferenceNo. If this procedure is called independently (e.g. in a path that doesn't go through the page extension's explicit Clear+Set calls), RoutingReferenceNo will retain its previous value and incorrectly filter the purchase line lookup. Recommendation:
Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why |
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Chethan Thopaiah <41570277+ChethanT@users.noreply.github.com>
- SubcProdOrderRtng.PageExt.al: add "Routing Reference No." filter to the NoOfCreatedPurchOrder = 0 existence-check branch — same root cause as the codeunit fix; without it the "No subcontracting order was created" message could be wrongly suppressed when a sibling Prod. Order line already has an unrelated PO. - SubcSubcontractingTest.Codeunit.al: revert garbled bot suggestions in CreateSubcontractingPOForEachProdOrderLineWhenLinesShareRoutingAndOperation (broken indentation, duplicated WHEN block with a placeholder comment in place of filters) and add a second test CreateSubcontractingPONavigatesToOwnPOWhenLinesShareRoutingAndOperation that confirms "view them" and asserts the opened PO is the one tied to the invoked routing line's Routing Reference No. — exercising the single-PO navigation branch that consumes the new RoutingReferenceNo global. Drops the now-redundant UpdateSubMgmtSetupWithReqWkshTemplate call (already in Initialize). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
…Subcontracting/634238-WrongMessagePOWith2Lines
ClearOperationNo doesn't reset RoutingReferenceNoThe existing Recommendation:
Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why |
| ProdOrderRtng.CreateSubcontracting.Invoke(); | ||
| ProdOrderRtng.Close(); | ||
|
|
||
| Assert.AreEqual('1 Purchase Order(s) created.\\Do you want to view them?', LibraryVariableStorage.DequeueText(), 'Expected "created" confirmation for each prod order line, not the false "already created" warning'); |
There was a problem hiding this comment.
Hardcoded label text in test assertion
The assertion compares LibraryVariableStorage.DequeueText() against the literal string '1 Purchase Order(s) created.\\Do you want to view them?', duplicating the PurchOrderCreatedTxt label from the production codeunit. Any change to that label text — including punctuation — will silently break this test.
Recommendation:
- Expose
PurchOrderCreatedTxtas a testable getter, or assert only the key distinguishing substring (e.g., that it does NOT contain'already been created') rather than duplicating the full formatted label.
| Assert.AreEqual('1 Purchase Order(s) created.\\Do you want to view them?', LibraryVariableStorage.DequeueText(), 'Expected "created" confirmation for each prod order line, not the false "already created" warning'); | |
| Assert.IsTrue( | |
| LibraryVariableStorage.DequeueText().Contains('Purchase Order(s) created'), | |
| 'Expected "created" confirmation, not the false "already created" warning'); |
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why
| LibraryManufacturing.CreateProdOrderLine(ProdOrderLine, ProductionOrder.Status, ProductionOrder."No.", Item."No.", '', ProductionLocation[1].Code, LibraryRandom.RandInt(10) + 2); | ||
| ProdOrderLineNo[1] := ProdOrderLine."Line No."; | ||
| LibraryManufacturing.CreateProdOrderLine(ProdOrderLine, ProductionOrder.Status, ProductionOrder."No.", Item."No.", '', ProductionLocation[2].Code, LibraryRandom.RandInt(10) + 2); | ||
| ProdOrderLineNo[2] := ProdOrderLine."Line No."; |
There was a problem hiding this comment.
Missing distinct-line guard in second scenario test
The test CreateSubcontractingPONavigatesToOwnPOWhenLinesShareRoutingAndOperation does not assert that ProdOrderLineNo[1] <> ProdOrderLineNo[2], unlike its sibling test. If both lines unexpectedly share the same line number — and thus the same Routing Reference No. — the test would still pass without verifying the multi-line navigation scenario.
Recommendation:
- Add the same guard assertion present in the sibling test immediately after both line numbers are captured.
| ProdOrderLineNo[2] := ProdOrderLine."Line No."; | |
| ProdOrderLineNo[2] := ProdOrderLine."Line No."; | |
| Assert.AreNotEqual(ProdOrderLineNo[1], ProdOrderLineNo[2], 'Expected two distinct production order lines with different Routing Reference Nos.'); |
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why
…Subcontracting/634238-WrongMessagePOWith2Lines
RoutingReferenceNo not cleared after ShowCreatedPurchaseOrderThe existing Recommendation:
Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why |
| [ConfirmHandler] | ||
| procedure DoNotConfirmShowCreatedPurchOrderForSubcontracting(Question: Text[1024]; var Reply: Boolean) | ||
| begin | ||
| LibraryVariableStorage.Enqueue(Question); |
There was a problem hiding this comment.
Shared confirm handler now enqueues—breaks existing tests
DoNotConfirmShowCreatedPurchOrderForSubcontracting is a shared [ConfirmHandler] that pre-existing tests already reference. Adding LibraryVariableStorage.Enqueue(Question) means every invocation from tests that do not subsequently call LibraryVariableStorage.DequeueText() will leave variable storage dirty, causing those tests to fail at any subsequent LibraryVariableStorage.AssertEmpty() call or polluting the next test's dequeue order.
Recommendation:
- Instead of modifying the shared handler, introduce a new dedicated confirm handler (e.g.,
CaptureAndRejectShowCreatedPurchOrderForSubcontracting) for the new tests that need to inspect the question text, and keep the original handler unchanged.
| LibraryVariableStorage.Enqueue(Question); | |
| [ConfirmHandler] | |
| procedure CaptureAndRejectShowCreatedPurchOrderForSubcontracting(Question: Text[1024]; var Reply: Boolean) | |
| begin | |
| LibraryVariableStorage.Enqueue(Question); | |
| Reply := false; | |
| end; | |
| [ConfirmHandler] | |
| procedure DoNotConfirmShowCreatedPurchOrderForSubcontracting(Question: Text[1024]; var Reply: Boolean) | |
| begin | |
| Reply := false; | |
| end; |
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why
|
|
||
| SubcontractingMgmtLibrary.Initialize(); | ||
| SubcontractingMgmtLibrary.UpdateSubMgmtSetup_ComponentAtLocation("Components at Location"::Purchase); | ||
| UpdateSubMgmtSetupWithReqWkshTemplate(); |
There was a problem hiding this comment.
UpdateSubMgmtSetupWithReqWkshTemplate runs on every test init
UpdateSubMgmtSetupWithReqWkshTemplate() is added before the if IsInitialized then exit guard, so it runs unconditionally on every test's Initialize() call rather than only once per test session. If any other test reconfigures the Requisition Worksheet template to a different value after Initialize, that configuration will be overwritten the next time Initialize is called.
Recommendation:
- Move the call inside the
if not IsInitialized thenblock (or equivalent one-time init guard) so that it only runs during first-time setup, consistent with how the rest of the one-time setup is structured.
| UpdateSubMgmtSetupWithReqWkshTemplate(); | |
| if IsInitialized then | |
| exit; | |
| UpdateSubMgmtSetupWithReqWkshTemplate(); | |
| // remaining one-time init ... |
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why
| PurchaseLine.SetLoadFields(SystemId); | ||
| if (NoOfCreatedPurchOrder = 1) and (OperationNo <> '') then | ||
| PurchaseLine.SetRange("Operation No.", OperationNo); | ||
| if (NoOfCreatedPurchOrder = 1) and (RoutingReferenceNo <> 0) then |
There was a problem hiding this comment.
RoutingReferenceNo = 0 silently skips the filter
The guard (RoutingReferenceNo <> 0) means that if a prod-order routing line genuinely has Routing Reference No. = 0 (e.g. a line created outside the normal production order refresh flow), the filter is not applied and FindFirst() can return a purchase line from the wrong routing context, reopening the original bug for those records.
Recommendation:
- Document that
Routing Reference No. = 0is intentionally excluded, or — if routing lines with value 0 are possible — add a dedicated boolean flag (similar to howOperationNouses an empty-string check which is unambiguous forCodefields) to explicitly indicate whether the caller has set the filter.
| if (NoOfCreatedPurchOrder = 1) and (RoutingReferenceNo <> 0) then | |
| // Option: use a separate flag to distinguish "not set" from "value is 0" | |
| if (NoOfCreatedPurchOrder = 1) and RoutingReferenceNoIsSet then | |
| PurchaseLine.SetRange("Routing Reference No.", RoutingReferenceNo); |
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why
Paired Clear/Set API is fragile; consider a single setterExposing Recommendation:
Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why |
UpdateSubMgmtSetupWithReqWkshTemplate runs on every test
Recommendation:
Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why |
Bug
AB#634238 — When a Released Production Order has multiple Prod. Order lines that share the same Routing/Operation but differ by
Routing Reference No., creating a Subcontracting Order from the routing of a second line:Root cause
Three filter sites identified Prod. Order lines by
Prod. Order No.+Routing No.+Operation No.only, missingRouting Reference No.— the field that disambiguates Prod. Order lines sharing the same routing/operation:Subc. Purchase Order Creator(codeunit99001557)ShowExistingPurchaseOrdersForRoutingLines— drives the "already created" prompt.ShowCreatedPurchaseOrder(single-PO navigation branch) — drives which PO opens.SubcProdOrderRtng.PageExt.al— the Create Subcontracting Order action'sif NoOfCreatedPurchOrder = 0fallback that decides whether to show the "No subcontracting order was created" message.The static
RunPageLinkon the Subcontracting Purchase Order Lines action inSubcProdOrderRtng.PageExt.alalready includesRouting Reference No.as a filter — confirming this is the canonical filter pattern the programmatic checks should follow.Fix
SubcPurchaseOrderCreator.Codeunit.alShowExistingPurchaseOrdersForRoutingLines: addPurchaseLine.SetRange("Routing Reference No.", ProdOrderRoutingLine."Routing Reference No.")to the existence check.ShowCreatedPurchaseOrder: add aRoutingReferenceNoglobal withSet/Clearaccessors (mirroring the existingOperationNopattern) and apply it in the single-PO navigation branch.SubcProdOrderRtng.PageExt.alClearRoutingReferenceNoForCreatedPurchaseOrder()+SetRoutingReferenceNoForCreatedPurchaseOrder(Rec."Routing Reference No.")before invokingShowCreatedPurchaseOrder.if NoOfCreatedPurchOrder = 0fallback also filters by"Routing Reference No." = Rec."Routing Reference No.", so the "No subcontracting order was created" message is no longer wrongly suppressed when a sibling Prod. Order line already has an unrelated PO.The change is minimal and targeted — no broader refactoring.
Tests
Two tests added to codeunit
139989 "Subc. Subcontracting Test". Both build a single Released Production Order with two Prod. Order lines for the same item on different locations (so they share routing/operation but have differentRouting Reference No.) and invoke Create Subcontracting Order on each line's routing entry.CreateSubcontractingPOForEachProdOrderLineWhenLinesShareRoutingAndOperation— covers Symptom 1. Captures the confirm-handler question text and asserts each invocation ends with the "1 Purchase Order(s) created" confirmation — i.e. neither raises the false "already created" warning.CreateSubcontractingPONavigatesToOwnPOWhenLinesShareRoutingAndOperation— covers Symptom 2. Confirms "view them" withYesand captures the opened Purchase Order'sNo.via a page handler, then asserts the opened PO contains aPurchase Linecarrying the invoked routing line'sRouting Reference No.— proving the single-PO navigation branch routes to the correct PO.Both tests fail on the unfixed code and pass after the fix.
The test app gains a dependency on
Library - Variable Storageso the confirm/page handlers can enqueue captured values for assertion.Work item
AB#634238