From 4357b2884e86ae2078dfb46122b53beca4957aa8 Mon Sep 17 00:00:00 2001 From: Sebastian Pinkow Date: Fri, 19 Jun 2026 11:38:15 +0200 Subject: [PATCH 1/3] Create Subcontracting Order Lines only for machine centers (consistency with subcontracting worksheet) --- .../Pageextensions/Manufacturing/SubcProdOrderRtng.PageExt.al | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Apps/W1/Subcontracting/App/src/Process/Pageextensions/Manufacturing/SubcProdOrderRtng.PageExt.al b/src/Apps/W1/Subcontracting/App/src/Process/Pageextensions/Manufacturing/SubcProdOrderRtng.PageExt.al index d3416d2031..3f18c2a5e6 100644 --- a/src/Apps/W1/Subcontracting/App/src/Process/Pageextensions/Manufacturing/SubcProdOrderRtng.PageExt.al +++ b/src/Apps/W1/Subcontracting/App/src/Process/Pageextensions/Manufacturing/SubcProdOrderRtng.PageExt.al @@ -4,6 +4,7 @@ // ------------------------------------------------------------------------------------------------ namespace Microsoft.Manufacturing.Subcontracting; +using Microsoft.Manufacturing.Capacity; using Microsoft.Manufacturing.Document; using Microsoft.Purchases.Document; @@ -122,6 +123,7 @@ pageextension 99001503 "Subc. Prod. Order Rtng." extends "Prod. Order Routing" NoOfCreatedPurchOrder: Integer; begin CurrPage.SetSelectionFilter(ProdOrderRoutingLine); + ProdOrderRoutingLine.SetRange(Type, "Capacity Type"::"Machine Center"); SubcPurchaseOrderCreator.ShowExistingPurchaseOrdersForRoutingLines(ProdOrderRoutingLine); ProdOrderRoutingLine.FindSet(); repeat From eff4ad72946c981be894badb1f82b22192f10cdd Mon Sep 17 00:00:00 2001 From: Sebastian Pinkow Date: Fri, 19 Jun 2026 14:08:44 +0200 Subject: [PATCH 2/3] [Subcontracting] Implement filtering for Work Center routing lines in subcontracting order creation only --- .../SubcPurchaseOrderCreator.Codeunit.al | 14 +++ .../SubcProdOrderRtng.PageExt.al | 60 ++++++------- .../SubcManagementLibrary.Codeunit.al | 2 +- .../Tests/SubcPurchSubcontTest.Codeunit.al | 85 +++++++++++++++++++ 4 files changed, 130 insertions(+), 31 deletions(-) diff --git a/src/Apps/W1/Subcontracting/App/src/Process/Codeunits/SubcPurchaseOrderCreator.Codeunit.al b/src/Apps/W1/Subcontracting/App/src/Process/Codeunits/SubcPurchaseOrderCreator.Codeunit.al index 6a958ce4b4..485ebfa05a 100644 --- a/src/Apps/W1/Subcontracting/App/src/Process/Codeunits/SubcPurchaseOrderCreator.Codeunit.al +++ b/src/Apps/W1/Subcontracting/App/src/Process/Codeunits/SubcPurchaseOrderCreator.Codeunit.al @@ -13,6 +13,7 @@ using Microsoft.Inventory.Item; using Microsoft.Inventory.Item.Catalog; using Microsoft.Inventory.Requisition; using Microsoft.Inventory.Tracking; +using Microsoft.Manufacturing.Capacity; using Microsoft.Manufacturing.Document; using Microsoft.Manufacturing.Setup; using Microsoft.Manufacturing.WorkCenter; @@ -319,6 +320,19 @@ codeunit 99001557 "Subc. Purchase Order Creator" PageManagement.PageRun(PurchaseLine); end; + internal procedure CreateSubcontractingOrdersForRoutingLineSelection(var ProdOrderRoutingLine: Record "Prod. Order Routing Line"): Integer + var + NoOfCreatedPurchOrder: Integer; + begin + ProdOrderRoutingLine.SetRange(Type, "Capacity Type"::"Work Center"); + ShowExistingPurchaseOrdersForRoutingLines(ProdOrderRoutingLine); + if ProdOrderRoutingLine.FindSet() then + repeat + NoOfCreatedPurchOrder += CreateSubcontractingPurchaseOrderFromRoutingLine(ProdOrderRoutingLine); + until ProdOrderRoutingLine.Next() = 0; + exit(NoOfCreatedPurchOrder); + end; + local procedure CheckProdOrderComponentLines(ProdOrderRoutingLine: Record "Prod. Order Routing Line"): Boolean var ProdOrderComponent: Record "Prod. Order Component"; diff --git a/src/Apps/W1/Subcontracting/App/src/Process/Pageextensions/Manufacturing/SubcProdOrderRtng.PageExt.al b/src/Apps/W1/Subcontracting/App/src/Process/Pageextensions/Manufacturing/SubcProdOrderRtng.PageExt.al index 3f18c2a5e6..f16ad942fe 100644 --- a/src/Apps/W1/Subcontracting/App/src/Process/Pageextensions/Manufacturing/SubcProdOrderRtng.PageExt.al +++ b/src/Apps/W1/Subcontracting/App/src/Process/Pageextensions/Manufacturing/SubcProdOrderRtng.PageExt.al @@ -4,7 +4,6 @@ // ------------------------------------------------------------------------------------------------ namespace Microsoft.Manufacturing.Subcontracting; -using Microsoft.Manufacturing.Capacity; using Microsoft.Manufacturing.Document; using Microsoft.Purchases.Document; @@ -118,37 +117,9 @@ pageextension 99001503 "Subc. Prod. Order Rtng." extends "Prod. Order Routing" trigger OnAction() var ProdOrderRoutingLine: Record "Prod. Order Routing Line"; - PurchaseLine: Record "Purchase Line"; - SubcPurchaseOrderCreator: Codeunit "Subc. Purchase Order Creator"; - NoOfCreatedPurchOrder: Integer; begin CurrPage.SetSelectionFilter(ProdOrderRoutingLine); - ProdOrderRoutingLine.SetRange(Type, "Capacity Type"::"Machine Center"); - SubcPurchaseOrderCreator.ShowExistingPurchaseOrdersForRoutingLines(ProdOrderRoutingLine); - ProdOrderRoutingLine.FindSet(); - repeat - NoOfCreatedPurchOrder += SubcPurchaseOrderCreator.CreateSubcontractingPurchaseOrderFromRoutingLine(ProdOrderRoutingLine); - until ProdOrderRoutingLine.Next() = 0; - - if NoOfCreatedPurchOrder = 0 then begin - PurchaseLine.SetCurrentKey("Document Type", Type, "Prod. Order No."); - PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); - PurchaseLine.SetRange(Type, PurchaseLine.Type::Item); - PurchaseLine.SetRange("Prod. Order No.", Rec."Prod. Order No."); - PurchaseLine.SetRange("Routing No.", Rec."Routing No."); - PurchaseLine.SetRange("Routing Reference No.", Rec."Routing Reference No."); - PurchaseLine.SetRange("Operation No.", Rec."Operation No."); - if PurchaseLine.IsEmpty() then - Message(NoPurchOrderCreatedMsg, ProdOrderRoutingLine."Prod. Order No.") - end else begin - if NoOfCreatedPurchOrder = 1 then begin - SubcPurchaseOrderCreator.ClearOperationNoForCreatedPurchaseOrder(); - SubcPurchaseOrderCreator.SetOperationNoForCreatedPurchaseOrder(Rec."Operation No."); - SubcPurchaseOrderCreator.ClearRoutingReferenceNoForCreatedPurchaseOrder(); - SubcPurchaseOrderCreator.SetRoutingReferenceNoForCreatedPurchaseOrder(Rec."Routing Reference No."); - end; - SubcPurchaseOrderCreator.ShowCreatedPurchaseOrder(Rec."Prod. Order No.", NoOfCreatedPurchOrder); - end; + CreateSubcontractingOrders(ProdOrderRoutingLine); end; } action("WIP Adjustment") @@ -226,4 +197,33 @@ pageextension 99001503 "Subc. Prod. Order Rtng." extends "Prod. Order Routing" Rec.Calcfields(Subcontracting); TransferWIPItemEnabled := Rec.Subcontracting; end; + + internal procedure CreateSubcontractingOrders(var ProdOrderRoutingLine: Record "Prod. Order Routing Line") + var + PurchaseLine: Record "Purchase Line"; + SubcPurchaseOrderCreator: Codeunit "Subc. Purchase Order Creator"; + NoOfCreatedPurchOrder: Integer; + begin + NoOfCreatedPurchOrder := SubcPurchaseOrderCreator.CreateSubcontractingOrdersForRoutingLineSelection(ProdOrderRoutingLine); + + if NoOfCreatedPurchOrder = 0 then begin + PurchaseLine.SetCurrentKey("Document Type", Type, "Prod. Order No."); + PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); + PurchaseLine.SetRange(Type, PurchaseLine.Type::Item); + PurchaseLine.SetRange("Prod. Order No.", Rec."Prod. Order No."); + PurchaseLine.SetRange("Routing No.", Rec."Routing No."); + PurchaseLine.SetRange("Routing Reference No.", Rec."Routing Reference No."); + PurchaseLine.SetRange("Operation No.", Rec."Operation No."); + if PurchaseLine.IsEmpty() then + Message(NoPurchOrderCreatedMsg, ProdOrderRoutingLine."Prod. Order No.") + end else begin + if NoOfCreatedPurchOrder = 1 then begin + SubcPurchaseOrderCreator.ClearOperationNoForCreatedPurchaseOrder(); + SubcPurchaseOrderCreator.SetOperationNoForCreatedPurchaseOrder(Rec."Operation No."); + SubcPurchaseOrderCreator.ClearRoutingReferenceNoForCreatedPurchaseOrder(); + SubcPurchaseOrderCreator.SetRoutingReferenceNoForCreatedPurchaseOrder(Rec."Routing Reference No."); + end; + SubcPurchaseOrderCreator.ShowCreatedPurchaseOrder(Rec."Prod. Order No.", NoOfCreatedPurchOrder); + end; + end; } \ No newline at end of file diff --git a/src/Apps/W1/Subcontracting/Test/src/Codeunits/Libraries/SubcManagementLibrary.Codeunit.al b/src/Apps/W1/Subcontracting/Test/src/Codeunits/Libraries/SubcManagementLibrary.Codeunit.al index b942ba816a..6084e5c1f9 100644 --- a/src/Apps/W1/Subcontracting/Test/src/Codeunits/Libraries/SubcManagementLibrary.Codeunit.al +++ b/src/Apps/W1/Subcontracting/Test/src/Codeunits/Libraries/SubcManagementLibrary.Codeunit.al @@ -129,7 +129,7 @@ codeunit 139983 "Subc. Management Library" LibraryManufacturing.CreateAndRefreshProductionOrder(ProductionOrder, ProdOrderStatus, ProdOrderSourceType, SourceNo, Quantity); end; - procedure CreateAndRefreshProductionOrder(var ProductionOrder: Record "Production Order"; ProdOrderStatus: Enum "Production Order Status"; ProdOrderSourceType: Enum "Prod. Order Source Type"; SourceNo: Code[20]; Quantity: Decimal; LocationCode: Code[20]) + procedure CreateAndRefreshProductionOrder(var ProductionOrder: Record "Production Order"; ProdOrderStatus: Enum "Production Order Status"; ProdOrderSourceType: Enum "Prod. Order Source Type"; SourceNo: Code[20]; Quantity: Decimal; LocationCode: Code[10]) var LibraryManufacturing: Codeunit "Library - Manufacturing"; begin diff --git a/src/Apps/W1/Subcontracting/Test/src/Codeunits/Tests/SubcPurchSubcontTest.Codeunit.al b/src/Apps/W1/Subcontracting/Test/src/Codeunits/Tests/SubcPurchSubcontTest.Codeunit.al index 0f6b7901b3..a7c2a9990a 100644 --- a/src/Apps/W1/Subcontracting/Test/src/Codeunits/Tests/SubcPurchSubcontTest.Codeunit.al +++ b/src/Apps/W1/Subcontracting/Test/src/Codeunits/Tests/SubcPurchSubcontTest.Codeunit.al @@ -622,6 +622,91 @@ codeunit 139991 "Subc. Purch. Subcont. Test" Assert.ExpectedError('You cannot change this routing line because transfer orders exist'); end; + [Test] + procedure WorkCenterRoutingLinesExcludedFromMultiSelectionWhenMachineCenterPresent() + var + CapacityUnitOfMeasure: Record "Capacity Unit of Measure"; + Item, Item2 : Record Item; + MachineCenter: Record "Machine Center"; + ProductionOrder: Record "Production Order"; + ProdOrderRoutingLine: Record "Prod. Order Routing Line"; + PurchaseLine: Record "Purchase Line"; + RoutingHeader: Record "Routing Header"; + RoutingLine: Record "Routing Line"; + WorkCenter: Record "Work Center"; + SubcPurchaseOrderCreator: Codeunit "Subc. Purchase Order Creator"; + MachineCenterNo: Code[20]; + NoOfCreatedOrders: Integer; + begin + // [SCENARIO] When CreateSubcontractingOrdersForRoutingLineSelection is called with a mixed + // selection containing both Work Center and Machine Center routing lines, only the + // Machine Center lines result in a subcontracting purchase order. + // This verifies that the Machine Center type filter is applied correctly when simulating + // multi-record selection (CurrPage.SetSelectionFilter cannot be used in test framework). + Initialize(); + + // [GIVEN] A subcontracting Work Center with a vendor + CreateAndCalculateNeededWorkCenter(WorkCenter, true); + + // [GIVEN] A Machine Center belonging to the subcontracting Work Center + LibraryMfgManagement.CreateMachineCenter(MachineCenterNo, WorkCenter."No.", "Flushing Method"::"Pick + Manual".AsInteger()); + MachineCenter.Get(MachineCenterNo); + LibraryManufacturing.CalculateMachCenterCalendar(MachineCenter, CalcDate('<-CY-1Y>', WorkDate()), CalcDate('', WorkDate())); + + // [GIVEN] A routing with a Work Center line (Op 010) and a Machine Center line (Op 020), + // both referencing the same subcontracting Work Center + LibraryManufacturing.CreateCapacityUnitOfMeasure(CapacityUnitOfMeasure, "Capacity Unit of Measure"::Minutes); + LibraryManufacturing.CreateRoutingHeader(RoutingHeader, RoutingHeader.Type::Serial); + + LibraryManufacturing.CreateRoutingLineSetup(RoutingLine, RoutingHeader, WorkCenter."No.", '010', 1, 1); + RoutingLine.Validate("Run Time Unit of Meas. Code", CapacityUnitOfMeasure.Code); + RoutingLine.Validate("Setup Time Unit of Meas. Code", CapacityUnitOfMeasure.Code); + RoutingLine.Modify(true); + + RoutingLine.Type := RoutingLine.Type::"Machine Center"; + LibraryManufacturing.CreateRoutingLineSetup(RoutingLine, RoutingHeader, MachineCenter."No.", '020', 1, 1); + RoutingLine.Validate("Run Time Unit of Meas. Code", CapacityUnitOfMeasure.Code); + RoutingLine.Validate("Setup Time Unit of Meas. Code", CapacityUnitOfMeasure.Code); + RoutingLine.Modify(true); + + RoutingHeader.Validate(Status, RoutingHeader.Status::Certified); + RoutingHeader.Modify(true); + + LibraryInventory.CreateItem(Item2); + LibraryManufacturing.CreateProductionBOM(Item2, 2); + + // [GIVEN] An item with the routing and a released production order + LibraryManufacturing.CreateItemManufacturing( + Item, "Costing Method"::FIFO, LibraryRandom.RandInt(10), + "Reordering Policy"::"Lot-for-Lot", "Flushing Method"::"Pick + Manual", + RoutingHeader."No.", Item2."Production BOM No."); + + SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( + ProductionOrder, "Production Order Status"::Released, + ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 1); + + LibraryMfgManagement.CreateSubcontractingReqWkshTemplateAndNameAndUpdateSetup(); + + // [WHEN] All routing lines (Work Center Op 010 + Machine Center Op 020) are passed to + // CreateSubcontractingOrdersForRoutingLineSelection, simulating a multi-record selection + ProdOrderRoutingLine.SetRange(Status, "Production Order Status"::Released); + ProdOrderRoutingLine.SetRange("Prod. Order No.", ProductionOrder."No."); + NoOfCreatedOrders := SubcPurchaseOrderCreator.CreateSubcontractingOrdersForRoutingLineSelection(ProdOrderRoutingLine); + + // [THEN] Exactly one purchase order is created (Work Center line is filtered out) + Assert.AreEqual(1, NoOfCreatedOrders, 'Exactly one subcontracting purchase order must be created.'); + + // [THEN] The purchase order is linked to the Machine Center operation (Op 020) + PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); + PurchaseLine.SetRange("Prod. Order No.", ProductionOrder."No."); + PurchaseLine.SetRange("Operation No.", '010'); + Assert.RecordCount(PurchaseLine, 1); + + // [THEN] No purchase order is created for the Work Center operation (Op 010) + PurchaseLine.SetRange("Operation No.", '020'); + Assert.RecordIsEmpty(PurchaseLine); + end; + [ModalPageHandler] procedure ItemTrackingLinesSimpleHandler(var ItemTrackingLines: TestPage "Item Tracking Lines") begin From 5236ba9c2cc922acc360bc0f81aab6890f1950dc Mon Sep 17 00:00:00 2001 From: Sebastian Pinkow Date: Fri, 19 Jun 2026 14:44:07 +0200 Subject: [PATCH 3/3] Correct scenario description of test --- .../src/Codeunits/Tests/SubcPurchSubcontTest.Codeunit.al | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Apps/W1/Subcontracting/Test/src/Codeunits/Tests/SubcPurchSubcontTest.Codeunit.al b/src/Apps/W1/Subcontracting/Test/src/Codeunits/Tests/SubcPurchSubcontTest.Codeunit.al index a7c2a9990a..2335b62fe9 100644 --- a/src/Apps/W1/Subcontracting/Test/src/Codeunits/Tests/SubcPurchSubcontTest.Codeunit.al +++ b/src/Apps/W1/Subcontracting/Test/src/Codeunits/Tests/SubcPurchSubcontTest.Codeunit.al @@ -640,8 +640,8 @@ codeunit 139991 "Subc. Purch. Subcont. Test" begin // [SCENARIO] When CreateSubcontractingOrdersForRoutingLineSelection is called with a mixed // selection containing both Work Center and Machine Center routing lines, only the - // Machine Center lines result in a subcontracting purchase order. - // This verifies that the Machine Center type filter is applied correctly when simulating + // Work Center lines result in a subcontracting purchase order. + // This verifies that the Work Center type filter is applied correctly when simulating // multi-record selection (CurrPage.SetSelectionFilter cannot be used in test framework). Initialize(); @@ -696,13 +696,13 @@ codeunit 139991 "Subc. Purch. Subcont. Test" // [THEN] Exactly one purchase order is created (Work Center line is filtered out) Assert.AreEqual(1, NoOfCreatedOrders, 'Exactly one subcontracting purchase order must be created.'); - // [THEN] The purchase order is linked to the Machine Center operation (Op 020) + // [THEN] The purchase order is linked to the Work Center operation (Op 010) PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); PurchaseLine.SetRange("Prod. Order No.", ProductionOrder."No."); PurchaseLine.SetRange("Operation No.", '010'); Assert.RecordCount(PurchaseLine, 1); - // [THEN] No purchase order is created for the Work Center operation (Op 010) + // [THEN] No purchase order is created for the Machine Center operation (Op 020) PurchaseLine.SetRange("Operation No.", '020'); Assert.RecordIsEmpty(PurchaseLine); end;