diff --git a/src/Apps/W1/Subcontracting/Test/src/Codeunits/Tests/SubcPurchaseOrderTest.Codeunit.al b/src/Apps/W1/Subcontracting/Test/src/Codeunits/Tests/SubcPurchaseOrderTest.Codeunit.al new file mode 100644 index 0000000000..c4f2e7908d --- /dev/null +++ b/src/Apps/W1/Subcontracting/Test/src/Codeunits/Tests/SubcPurchaseOrderTest.Codeunit.al @@ -0,0 +1,1244 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ +namespace Microsoft.Manufacturing.Subcontracting.Test; + +using Microsoft.Finance.GeneralLedger.Setup; +using Microsoft.Finance.VAT.Setup; +using Microsoft.Foundation.Enums; +using Microsoft.Foundation.NoSeries; +using Microsoft.Inventory.Item; +using Microsoft.Inventory.Journal; +using Microsoft.Inventory.Ledger; +using Microsoft.Inventory.Location; +using Microsoft.Inventory.Planning; +using Microsoft.Inventory.Requisition; +using Microsoft.Inventory.Tracking; +using Microsoft.Inventory.Transfer; +using Microsoft.Manufacturing.Capacity; +using Microsoft.Manufacturing.Document; +using Microsoft.Manufacturing.MachineCenter; +using Microsoft.Manufacturing.Planning; +using Microsoft.Manufacturing.ProductionBOM; +using Microsoft.Manufacturing.Routing; +using Microsoft.Manufacturing.Setup; +using Microsoft.Manufacturing.Subcontracting; +using Microsoft.Manufacturing.WorkCenter; +using Microsoft.Purchases.Archive; +using Microsoft.Purchases.Comment; +using Microsoft.Purchases.Document; +using Microsoft.Purchases.History; +using Microsoft.Purchases.Vendor; +using Microsoft.Sales.Customer; +using Microsoft.Sales.Document; +using Microsoft.Utilities; +using Microsoft.Warehouse.Document; +using Microsoft.Warehouse.Structure; +using System.TestLibraries.Utilities; + +codeunit 139991 "Subc. Purchase Order Test" +{ + // [FEATURE] Subcontracting Management + Subtype = Test; + TestPermissions = Disabled; + TestType = IntegrationTest; + + trigger OnRun() + begin + IsInitialized := false; + end; + + [Test] + [HandlerFunctions('DoNotConfirmShowCreatedPurchOrderForSubcontracting')] + procedure TestCreationOfPurchOrderFromRtngLineWithSubcontractor() + var + Item: Record Item; + MachineCenter: array[2] of Record "Machine Center"; + ProductionOrder: Record "Production Order"; + PurchaseLine: Record "Purchase Line"; + WorkCenter: array[2] of Record "Work Center"; + begin + // [SCENARIO] Create Subcontracting Purchase Order directly from Prod. Order Routing Line + + // [GIVEN] Complete Setup of Manufacturing, include Work- and Machine Centers, Item + Initialize(); + + // [GIVEN] Some Parameters for Creation + Subcontracting := true; + UnitCostCalculation := UnitCostCalculation::Units; + + // [GIVEN] + CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); + + // [GIVEN] Create Item for Production include Routing and Prod. BOM + CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); + + SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( + ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); + + UpdateSubMgmtSetupWithReqWkshTemplate(); + + // [WHEN] Create Subcontracting Purchase Order from Prod. Order Routing + SubcontractingMgmtLibrary.CreateSubcontractingOrderFromProdOrderRtngPage(Item."Routing No.", WorkCenter[2]."No."); + + // [THEN] Check if Purchase Line exists + PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); + PurchaseLine.SetRange("Prod. Order No.", ProductionOrder."No."); + PurchaseLine.SetRange("Work Center No.", WorkCenter[2]."No."); + Assert.AreEqual(false, PurchaseLine.IsEmpty(), ''); + end; + + [Test] + procedure CreateSubcOrderFromRtngLineEmptyDefVATProdPostGrp() + var + GenProductPostingGroup: Record "Gen. Product Posting Group"; + Item: Record Item; + MachineCenter: array[2] of Record "Machine Center"; + ProductionOrder: Record "Production Order"; + ProdOrderRoutingLine: Record "Prod. Order Routing Line"; + PurchaseLine: Record "Purchase Line"; + VATPostingSetup: Record "VAT Posting Setup"; + Vendor: Record Vendor; + WorkCenter: array[2] of Record "Work Center"; + SubcPurchaseOrderCreator: Codeunit "Subc. Purchase Order Creator"; + OriginalDefVATProdPostGrp: Code[20]; + begin + // [SCENARIO 618715] Creating a Subcontracting Purchase Order from Prod. Order Routing Line + // should succeed even when "Def. VAT Prod. Posting Group" is empty on the Gen. Product Posting Group + // (US/Sales Tax localization). + + // [GIVEN] Complete Setup of Manufacturing, include Work- and Machine Centers, Item + Initialize(); + + // [GIVEN] Some Parameters for Creation + Subcontracting := true; + UnitCostCalculation := UnitCostCalculation::Units; + + // [GIVEN] Create subcontracting Work Center (sets Def. VAT Prod. Posting Group during creation) + CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); + + // [GIVEN] Create Item for Production include Routing and Prod. BOM + CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); + + SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( + ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); + + UpdateSubMgmtSetupWithReqWkshTemplate(); + + // [GIVEN] Clear "Def. VAT Prod. Posting Group" on the Work Center's Gen. Product Posting Group + // to simulate US/Sales Tax localization where this field is intentionally empty. + // Done after all other setup to avoid committing the change during item/production order creation. + GenProductPostingGroup.Get(WorkCenter[2]."Gen. Prod. Posting Group"); + OriginalDefVATProdPostGrp := GenProductPostingGroup."Def. VAT Prod. Posting Group"; + GenProductPostingGroup."Def. VAT Prod. Posting Group" := ''; + GenProductPostingGroup.Modify(); + + // [GIVEN] Create a VAT Posting Setup for the empty VAT Prod. Posting Group + // so the downstream purchase line validation can find a matching setup + Vendor.Get(WorkCenter[2]."Subcontractor No."); + if not VATPostingSetup.Get(Vendor."VAT Bus. Posting Group", '') then begin + VATPostingSetup.Init(); + VATPostingSetup."VAT Bus. Posting Group" := Vendor."VAT Bus. Posting Group"; + VATPostingSetup."VAT Prod. Posting Group" := ''; + VATPostingSetup."VAT Calculation Type" := VATPostingSetup."VAT Calculation Type"::"Normal VAT"; + VATPostingSetup."VAT %" := 0; + VATPostingSetup.Insert(); + end; + + // [WHEN] Create Subcontracting Purchase Order from Prod. Order Routing Line + ProdOrderRoutingLine.SetRange("Routing No.", Item."Routing No."); + ProdOrderRoutingLine.SetRange("Work Center No.", WorkCenter[2]."No."); + ProdOrderRoutingLine.FindFirst(); + SubcPurchaseOrderCreator.CreateSubcontractingPurchaseOrderFromRoutingLine(ProdOrderRoutingLine); + + // [THEN] Purchase Line is created successfully + PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); + PurchaseLine.SetRange("Prod. Order No.", ProductionOrder."No."); + PurchaseLine.SetRange("Work Center No.", WorkCenter[2]."No."); + Assert.AreEqual(false, PurchaseLine.IsEmpty(), 'Purchase Line should be created even when Def. VAT Prod. Posting Group is empty.'); + + // [TEARDOWN] Restore original Def. VAT Prod. Posting Group to prevent contaminating other tests + GenProductPostingGroup.Get(WorkCenter[2]."Gen. Prod. Posting Group"); + GenProductPostingGroup."Def. VAT Prod. Posting Group" := OriginalDefVATProdPostGrp; + GenProductPostingGroup.Modify(); + end; + + [Test] + [HandlerFunctions('DoNotConfirmShowCreatedPurchOrderForSubcontracting')] + procedure TestCreationOfPurchOrderFromRtngLineWithSubcontractorWithAddLine() + var + Item: Record Item; + MachineCenter: array[2] of Record "Machine Center"; + ProductionBOMLine: Record "Production BOM Line"; + ProductionOrder: Record "Production Order"; + PurchaseLine: Record "Purchase Line"; + WorkCenter: array[2] of Record "Work Center"; + begin + // [SCENARIO] Create Subcontracting Purchase Order directly from Prod. Order Routing Line + // [SCENARIO] and Transfer additional Line with marked Component; + + // [GIVEN] Complete Setup of Manufacturing, include Work- and Machine Centers, Item + Initialize(); + + // [GIVEN] Some Parameters for Creation + Subcontracting := true; + UnitCostCalculation := UnitCostCalculation::Units; + + // [GIVEN] + CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); + + // [GIVEN] Create Item for Production include Routing and Prod. BOM + CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); + + UpdateProdBomAndRoutingWithRoutingLink(Item, WorkCenter[2]."No."); + + SubcontractingMgmtLibrary.UpdateProdBomWithSubcontractingType(Item, "Subcontracting Type"::Purchase); + + SubcontractingMgmtLibrary.UpdateVendorWithSubcontractingLocationCode(WorkCenter[2]); + + SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( + ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); + + UpdateSubMgmtSetupWithReqWkshTemplate(); + + // [WHEN] Create Subcontracting Purchase Order from Prod. Order Routing + SubcontractingMgmtLibrary.CreateSubcontractingOrderFromProdOrderRtngPage(Item."Routing No.", WorkCenter[2]."No."); + + // [THEN] Check if Purchase Line with additional Component for Subcontracting Type exists + ProductionBOMLine.SetRange("Production BOM No.", Item."Production BOM No."); +#pragma warning disable AA0210 + ProductionBOMLine.SetRange("Subcontracting Type", ProductionBOMLine."Subcontracting Type"::Purchase); +#pragma warning restore AA0210 + ProductionBOMLine.FindFirst(); + + PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); + PurchaseLine.SetRange(Type, PurchaseLine.Type::Item); + PurchaseLine.SetRange("No.", ProductionBOMLine."No."); + Assert.AreEqual(false, PurchaseLine.IsEmpty(), ''); + end; + + [Test] + [HandlerFunctions('DoNotConfirmShowCreatedPurchOrderForSubcontracting')] + procedure TestCreationOfSubcontractingPurchOrderFromRtngLineWithAddInfoLine() + var + Item: Record Item; + MachineCenter: array[2] of Record "Machine Center"; + ProdOrderLine: Record "Prod. Order Line"; + ProdOrderRtngLine: Record "Prod. Order Routing Line"; + ProductionOrder: Record "Production Order"; + PurchaseLine: Record "Purchase Line"; + WorkCenter: array[2] of Record "Work Center"; + begin + // [SCENARIO] Create Subcontracting Purchase Order directly from Prod. Order Routing Line + // [SCENARIO] and Transfer additional Information Line; + + // [GIVEN] Complete Setup of Manufacturing, include Work- and Machine Centers, Item + Initialize(); + + // [GIVEN] Some Parameters for Creation + Subcontracting := true; + UnitCostCalculation := UnitCostCalculation::Units; + + // [GIVEN] + CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); + + // [GIVEN] Create Item for Production include Routing and Prod. BOM + CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); + + SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( + ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); + + UpdateSubMgmtSetupWithReqWkshTemplate(); + UpdateSubMgmtSetupTransferInfoLine(true); + + // [WHEN] Create Subcontracting Purchase Order from Prod. Order Routing + SubcontractingMgmtLibrary.CreateSubcontractingOrderFromProdOrderRtngPage(Item."Routing No.", WorkCenter[2]."No."); + + // [THEN] Check if Purchase Line with Additional Information Exists + PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); + PurchaseLine.SetRange("Prod. Order No.", ProductionOrder."No."); + PurchaseLine.FindFirst(); + PurchaseLine.SetRange("Document No.", PurchaseLine."Document No."); + PurchaseLine.SetRange("Prod. Order No."); + PurchaseLine.SetRange(Type, PurchaseLine.Type::" "); + PurchaseLine.FindFirst(); + + ProdOrderRtngLine.SetRange("Routing No.", Item."Routing No."); + ProdOrderRtngLine.SetRange("Work Center No.", WorkCenter[2]."No."); + ProdOrderRtngLine.FindFirst(); + ProdOrderLine.Get(ProdOrderLine.Status::Released, ProdOrderRtngLine."Prod. Order No.", ProdOrderRtngLine."Routing Reference No."); + + Assert.AreEqual(ProdOrderLine.Description, PurchaseLine.Description, ''); + + // [TEARDOWN] + UpdateSubMgmtSetupTransferInfoLine(false); + end; + + [Test] + [HandlerFunctions('DoConfirmCreateProdOrderForSubcontractingProcess')] + + procedure CheckGenPostGroupInSubContWorksheetAndSubConRoutingLine() + var + Item: Record Item; + Location: Record Location; + MachineCenter: array[2] of Record "Machine Center"; + ProdOrderRoutingLine: Record "Prod. Order Routing Line"; + ProductionOrder: Record "Production Order"; + PurchaseHeader: Record "Purchase Header"; + PurchaseLine: Record "Purchase Line"; + ReqWkshTemplate: Record "Req. Wksh. Template"; + RequisitionLine: Record "Requisition Line"; + RequisitionWkshName: Record "Requisition Wksh. Name"; + ManufacturingSetup: Record "Manufacturing Setup"; + Vendor: Record Vendor; + WorkCenter: array[2] of Record "Work Center"; + SubcCalculateSubContract: Report "Subc. Calculate Subcontracts"; + CarryOutActionMsgReq: Report "Carry Out Action Msg. - Req."; + GenBusPostingGroup1, GenBusPostingGroup2 : Code[20]; + ProdPostingGroup1, ProdPostingGroup2 : Code[20]; + VATBusPostingGroup1, VATBusPostingGroup2 : Code[20]; + VATProdPostingGroup1, VATProdPostingGroup2 : Code[20]; + ProdOrderRtng: TestPage "Prod. Order Routing"; + begin + // [SCENARIO] Check Gen. Prod. Posting Group value for Subcontracting Purchase Order + + // [GIVEN] Complete Setup of Manufacturing, include Work- and Machine Centers, Item + Initialize(); + + // [GIVEN] Some Parameters for Creation + Subcontracting := true; + UnitCostCalculation := UnitCostCalculation::Units; + UpdateSubMgmtSetupWithReqWkshTemplate(); + // [GIVEN] + CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); + + // [GIVEN] Create Item for Production include Routing and Prod. BOM + CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); + + UpdateProdBomAndRoutingWithRoutingLink(Item, WorkCenter[2]."No."); + + LibraryWarehouse.CreateLocationWithInventoryPostingSetup(Location); + Vendor.Get(WorkCenter[2]."Subcontractor No."); + WorkCenter2 := WorkCenter[2]; + WorkCenter2."Subcontractor No." := Vendor."No."; + Vendor."Subc. Location Code" := Location.Code; + LibraryWarehouse.CreateLocationWithInventoryPostingSetup(Location); + Vendor."Location Code" := Location.Code; + Vendor.Modify(); + + //[GIVEN] Create Production Order + SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( + ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); + + //[GIVEN] Create requisition worksheet template + SubcontractingMgmtLibrary.CreateReqWkshTemplateAndName(ReqWkshTemplate, RequisitionWkshName); + + //[GIVEN] create Purchase Order from Subcontracting Worksheet + RequisitionLine."Worksheet Template Name" := RequisitionWkshName."Worksheet Template Name"; + RequisitionLine."Journal Batch Name" := RequisitionWkshName.Name; + + SubcCalculateSubContract.SetWkShLine(RequisitionLine); + SubcCalculateSubContract.UseRequestPage(false); + SubcCalculateSubContract.RunModal(); + + RequisitionLine.SetRange("Worksheet Template Name", RequisitionWkshName."Worksheet Template Name"); + RequisitionLine.SetRange("Journal Batch Name", RequisitionWkshName.Name); +#pragma warning disable AA0210 + RequisitionLine.SetRange("Prod. Order No.", ProductionOrder."No."); +#pragma warning restore AA0210 + RequisitionLine.FindFirst(); + + Assert.AreEqual(ProductionOrder."No.", RequisitionLine."Prod. Order No.", 'Prod. Order No. has not found'); + + CarryOutActionMsgReq.SetReqWkshLine(RequisitionLine); + CarryOutActionMsgReq.UseRequestPage(false); + CarryOutActionMsgReq.RunModal(); + + PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); + PurchaseLine.SetRange("No.", ProductionOrder."Source No."); + PurchaseLine.SetRange(Type, "Purchase Line Type"::Item); + PurchaseLine.SetRange("Prod. Order No.", ProductionOrder."No."); + PurchaseLine.FindFirst(); + + //[GIVEN] Keep Posting Groups values for later Check + ProdPostingGroup1 := PurchaseLine."Gen. Prod. Posting Group"; + GenBusPostingGroup1 := PurchaseLine."Gen. Bus. Posting Group"; + VATBusPostingGroup1 := PurchaseLine."VAT Bus. Posting Group"; + VATProdPostingGroup1 := PurchaseLine."VAT Prod. Posting Group"; + + PurchaseHeader.Get(PurchaseLine."Document Type", PurchaseLine."Document No."); + + //[GIVEN] Delete Purchase Order + PurchaseHeader.Delete(true); + Commit(); + + ManufacturingSetup.Get(); + ManufacturingSetup."Subcontracting Template Name" := RequisitionLine."Worksheet Template Name"; + ManufacturingSetup."Subcontracting Batch Name" := RequisitionLine."Journal Batch Name"; + ManufacturingSetup.Modify(); + + // [GIVEN] Create Subcontracting Purchase Order from Prod. Order Routing + WorkCenter2 := WorkCenter[2]; + WorkCenter2."Subcontractor No." := Vendor."No."; + WorkCenter2.Modify(); + ProdOrderRoutingLine.SetRange("Prod. Order No.", ProductionOrder."No."); + ProdOrderRoutingLine.SetRange("Work Center No.", WorkCenter[2]."No."); + ProdOrderRoutingLine.FindFirst(); + ProdOrderRtng.OpenView(); + ProdOrderRtng.GoToRecord(ProdOrderRoutingLine); + ProdOrderRtng.CreateSubcontracting.Invoke(); + + PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); + PurchaseLine.SetRange("No.", ProductionOrder."Source No."); + PurchaseLine.SetRange(Type, "Purchase Line Type"::Item); + PurchaseLine.SetRange("Prod. Order No.", ProductionOrder."No."); + PurchaseLine.FindFirst(); + + //[GIVEN] Keep Posting Groups values for later Check + ProdPostingGroup2 := PurchaseLine."Gen. Prod. Posting Group"; + GenBusPostingGroup2 := PurchaseLine."Gen. Bus. Posting Group"; + VATBusPostingGroup2 := PurchaseLine."VAT Bus. Posting Group"; + VATProdPostingGroup2 := PurchaseLine."VAT Prod. Posting Group"; + + //[THEN] Check if Posting Groups values is the same as Standard + Assert.AreEqual(ProdPostingGroup1, ProdPostingGroup2, 'Gen. Prod. Posting Group is not Expected'); + Assert.AreEqual(GenBusPostingGroup1, GenBusPostingGroup2, 'Gen. Bus. Posting Group is not Expected'); + Assert.AreEqual(VATBusPostingGroup1, VATBusPostingGroup2, 'VAT Bus. Posting Group is not Expected'); + Assert.AreEqual(VATProdPostingGroup1, VATProdPostingGroup2, 'VAT Prod. Posting Group'); + end; + + [Test] + [HandlerFunctions('DoConfirmCreateProdOrderForSubcontractingProcess')] + procedure CheckGenPostGroupInSubContWorksheetAndSubConInPurchLineFunktion() + var + Item: Record Item; + Location: Record Location; + MachineCenter: array[2] of Record "Machine Center"; + ProductionOrder: Record "Production Order"; + PurchaseHeader: Record "Purchase Header"; + PurchaseLine: Record "Purchase Line"; + ReqWkshTemplate: Record "Req. Wksh. Template"; + RequisitionLine: Record "Requisition Line"; + RequisitionWkshName: Record "Requisition Wksh. Name"; + RoutingLink: Record "Routing Link"; + Vendor: Record Vendor; + WorkCenter: array[2] of Record "Work Center"; + SubcCalculateSubContract: Report "Subc. Calculate Subcontracts"; + CarryOutActionMsgReq: Report "Carry Out Action Msg. - Req."; + LibraryUtility: Codeunit "Library - Utility"; + GenBusPostingGroup1, GenBusPostingGroup2 : Code[20]; + ItemNoOriginPurchLine: Code[20]; + ProdPostingGroup1, ProdPostingGroup2 : Code[20]; + VATBusPostingGroup1, VATBusPostingGroup2 : Code[20]; + VATProdPostingGroup1, VATProdPostingGroup2 : Code[20]; + PurchOrder: TestPage "Purchase Order"; + begin + // [SCENARIO] Check change Location Code by change Subcontracting Type in Prod Order Component + + // [GIVEN] Complete Setup of Manufacturing, include Work- and Machine Centers, Item + Initialize(); + + // [GIVEN] Some Parameters for Creation + Subcontracting := true; + UnitCostCalculation := UnitCostCalculation::Units; + UpdateSubMgmtSetupWithReqWkshTemplate(); + // [GIVEN] + CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); + + // [GIVEN] Create Item for Production include Routing and Prod. BOM + CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); + + UpdateProdBomAndRoutingWithRoutingLink(Item, WorkCenter[2]."No."); + + LibraryWarehouse.CreateLocationWithInventoryPostingSetup(Location); + Vendor.Get(WorkCenter[2]."Subcontractor No."); + WorkCenter2 := WorkCenter[2]; + WorkCenter2."Subcontractor No." := Vendor."No."; + Vendor."Subc. Location Code" := Location.Code; + LibraryWarehouse.CreateLocationWithInventoryPostingSetup(Location); + Vendor."Location Code" := Location.Code; + Vendor.Modify(); + + //[GIVEN] Create Production Order + SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( + ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); + + //[GIVEN] Create requisition worksheet template + ReqWkshTemplate.DeleteAll(true); + ReqWkshTemplate.Name := SelectRequisitionTemplateName(); + RequisitionWkshName.Init(); + RequisitionWkshName.Validate("Worksheet Template Name", ReqWkshTemplate.Name); + RequisitionWkshName.Validate( + Name, + CopyStr( + LibraryUtility.GenerateRandomCode(RequisitionWkshName.FieldNo(Name), Database::"Requisition Wksh. Name"), + 1, LibraryUtility.GetFieldLength(Database::"Requisition Wksh. Name", RequisitionWkshName.FieldNo(Name)))); + RequisitionWkshName.Insert(true); + + //[GIVEN] create Purchase Order from Subcontracting Worksheet + RequisitionLine."Worksheet Template Name" := RequisitionWkshName."Worksheet Template Name"; + RequisitionLine."Journal Batch Name" := RequisitionWkshName.Name; + + SubcCalculateSubContract.SetWkShLine(RequisitionLine); + SubcCalculateSubContract.UseRequestPage(false); + SubcCalculateSubContract.RunModal(); + + RequisitionLine.SetRange("Worksheet Template Name", RequisitionWkshName."Worksheet Template Name"); + RequisitionLine.SetRange("Journal Batch Name", RequisitionWkshName.Name); +#pragma warning disable AA0210 + RequisitionLine.SetRange("Prod. Order No.", ProductionOrder."No."); +#pragma warning restore AA0210 + RequisitionLine.FindFirst(); + + Assert.AreEqual(ProductionOrder."No.", RequisitionLine."Prod. Order No.", 'Prod. Order No. has not found'); + + CarryOutActionMsgReq.SetReqWkshLine(RequisitionLine); + CarryOutActionMsgReq.UseRequestPage(false); + CarryOutActionMsgReq.RunModal(); + + PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); + PurchaseLine.SetRange("No.", ProductionOrder."Source No."); + PurchaseLine.SetRange(Type, "Purchase Line Type"::Item); + PurchaseLine.SetRange("Prod. Order No.", ProductionOrder."No."); + PurchaseLine.FindFirst(); + + //[GIVEN] Keep Gen. Prod. Posting Group value for later Check + ProdPostingGroup1 := PurchaseLine."Gen. Prod. Posting Group"; + GenBusPostingGroup1 := PurchaseLine."Gen. Bus. Posting Group"; + VATBusPostingGroup1 := PurchaseLine."VAT Bus. Posting Group"; + VATProdPostingGroup1 := PurchaseLine."VAT Prod. Posting Group"; + + PurchaseHeader.Get(PurchaseLine."Document Type", PurchaseLine."Document No."); + + //[GIVEN] Delete Purchase Order + PurchaseHeader.Delete(true); + Commit(); + + LibraryManufacturing.CreateRoutingLink(RoutingLink); + UpdateSubMgmtRoutingLink(RoutingLink.Code); + WorkCenter2 := WorkCenter[2]; + UpdateSubMgmtCommonWorkCenter(WorkCenter2."No."); + + //[GIVEN] Create Subcontracting Purchase Order from Purch + LibraryPurchase.CreatePurchaseOrderWithLocation(PurchaseHeader, Vendor."No.", Location.Code); + LibraryPurchase.CreatePurchaseLine(PurchaseLine, PurchaseHeader, PurchaseLine.Type::Item, LibraryInventory.CreateItemNo(), LibraryRandom.RandInt(100)); + PurchaseLine.Validate("Direct Unit Cost", LibraryRandom.RandDecInRange(1, 100, 2)); + ItemNoOriginPurchLine := PurchaseLine."No."; + PurchaseLine.Modify(true); + Commit(); + PurchOrder.OpenEdit(); + PurchOrder.GoToRecord(PurchaseHeader); + PurchOrder.PurchLines.CreateProdOrder.Invoke(); + + PurchaseLine.Reset(); + PurchaseLine.SetRange("Document Type", PurchaseHeader."Document Type"); + PurchaseLine.SetRange("Document No.", PurchaseHeader."No."); + PurchaseLine.SetRange(Type, "Purchase Line Type"::Item); + PurchaseLine.SetRange("No.", ItemNoOriginPurchLine); + PurchaseLine.FindFirst(); + + ProdPostingGroup2 := PurchaseLine."Gen. Prod. Posting Group"; + GenBusPostingGroup2 := PurchaseLine."Gen. Bus. Posting Group"; + VATBusPostingGroup2 := PurchaseLine."VAT Bus. Posting Group"; + VATProdPostingGroup2 := PurchaseLine."VAT Prod. Posting Group"; + + //[THEN] Check if Gen. Prod. Posting Group is the same as Standard + Assert.AreEqual(ProdPostingGroup1, ProdPostingGroup2, 'Gen. Prod. Posting Group is not Expected'); + Assert.AreEqual(GenBusPostingGroup1, GenBusPostingGroup2, 'Gen. Bus. Posting Group is not Expected'); + Assert.AreEqual(VATBusPostingGroup1, VATBusPostingGroup2, 'VAT Bus. Posting Group is not Expected'); + Assert.AreEqual(VATProdPostingGroup1, VATProdPostingGroup2, 'VAT Prod. Posting Group'); + end; + + [Test] + [HandlerFunctions('DoNotConfirmShowCreatedPurchOrderForSubcontracting')] + procedure TestTransferProdOrderRtngCommentByCreationOfSubcontrPurchOrder() + var + Item: Record Item; + MachineCenter: array[2] of Record "Machine Center"; + ProdOrderRtngLine: Record "Prod. Order Routing Line"; + ProductionOrder: Record "Production Order"; + PurchCommentLine: Record "Purch. Comment Line"; + PurchaseLine: Record "Purchase Line"; + WorkCenter: array[2] of Record "Work Center"; + ReleasedProdOrderRtng: TestPage "Prod. Order Routing"; + begin + // [SCENARIO] Create Subcontracting Purchase Order + // [SCENARIO] and test Transfer of Prod Order Rtng. Comment to PurchLine HTML Text; + + // [GIVEN] Complete Setup of Manufacturing, include Work- and Machine Centers, Item + Initialize(); + + // [GIVEN] Some Parameters for Creation + Subcontracting := true; + UnitCostCalculation := UnitCostCalculation::Units; + // [GIVEN] + CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); + + // [GIVEN] Create Item for Production include Routing and Prod. BOM + CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); + + UpdateProdBomAndRoutingWithRoutingLink(Item, WorkCenter[2]."No."); + + SubcontractingMgmtLibrary.UpdateProdBomWithSubcontractingType(Item, "Subcontracting Type"::Transfer); + + SubcontractingMgmtLibrary.UpdateVendorWithSubcontractingLocationCode(WorkCenter[2]); + + SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( + ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); + + UpdateSubMgmtSetupWithReqWkshTemplate(); + + SubcontractingMgmtLibrary.UpdateProdOrderCompWithLocationCode(ProductionOrder."No."); + + // Create Comment Line + ProdOrderRtngLine.SetRange(Status, ProdOrderRtngLine.Status::Released); + ProdOrderRtngLine.SetRange("Prod. Order No.", ProductionOrder."No."); + ProdOrderRtngLine.SetRange("Routing No.", Item."Routing No."); + ProdOrderRtngLine.SetRange("Work Center No.", WorkCenter[2]."No."); + ProdOrderRtngLine.FindFirst(); + LibraryMfgManagement.CreateProdOrderRtngCommentLine(ProdOrderRtngLine.Status, ProdOrderRtngLine."Prod. Order No.", ProdOrderRtngLine."Routing Reference No.", ProdOrderRtngLine."Routing No.", ProdOrderRtngLine."Operation No."); + + // [WHEN] Create Subcontracting Purchase Order from Prod. Order Routing + ReleasedProdOrderRtng.OpenView(); + ReleasedProdOrderRtng.GoToRecord(ProdOrderRtngLine); + ReleasedProdOrderRtng.CreateSubcontracting.Invoke(); + + // [THEN] Get transferred Rtng Comment Text + PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); + PurchaseLine.SetRange("Operation No.", ProdOrderRtngLine."Operation No."); + PurchaseLine.SetRange("Prod. Order No.", ProdOrderRtngLine."Prod. Order No."); + PurchaseLine.SetRange("Prod. Order Line No.", ProdOrderRtngLine."Routing Reference No."); + PurchaseLine.FindLast(); + + PurchCommentLine.SetRange("Document Type", PurchaseLine."Document Type"); + PurchCommentLine.SetRange("No.", PurchaseLine."Document No."); + PurchCommentLine.SetRange("Document Line No.", PurchaseLine."Line No."); + + Assert.IsFalse(PurchaseLine.IsEmpty(), 'Purchase Comment Line must be filled'); + + // [TEARDOWN] + end; + + [Test] + [HandlerFunctions('DoNotConfirmShowCreatedPurchOrderForSubcontracting')] + procedure Description2CopiedFromProdOrderComponentToPurchaseLine() + var + Item: Record Item; + MachineCenter: array[2] of Record "Machine Center"; + ProdOrderComp: Record "Prod. Order Component"; + ProductionOrder: Record "Production Order"; + PurchaseLine: Record "Purchase Line"; + WorkCenter: array[2] of Record "Work Center"; + ExpectedDescription2: Text[50]; + begin + // [SCENARIO] Description 2 from Prod. Order Component is propagated to Purchase Line + // [FEATURE] Bug 620556 - Subcontracting Description 2 alignment + + // [GIVEN] Complete Setup of Manufacturing + Initialize(); + Subcontracting := true; + UnitCostCalculation := UnitCostCalculation::Units; + + CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); + CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); + UpdateProdBomAndRoutingWithRoutingLink(Item, WorkCenter[2]."No."); + SubcontractingMgmtLibrary.UpdateProdBomWithSubcontractingType(Item, "Subcontracting Type"::Purchase); + SubcontractingMgmtLibrary.UpdateVendorWithSubcontractingLocationCode(WorkCenter[2]); + + SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( + ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); + + UpdateSubMgmtSetupWithReqWkshTemplate(); + + // [GIVEN] A Description 2 value is set on the Prod. Order Component with Subcontracting Type = Purchase + ProdOrderComp.SetRange(Status, ProdOrderComp.Status::Released); + ProdOrderComp.SetRange("Prod. Order No.", ProductionOrder."No."); +#pragma warning disable AA0210 + ProdOrderComp.SetRange("Subcontracting Type", ProdOrderComp."Subcontracting Type"::Purchase); +#pragma warning restore AA0210 + ProdOrderComp.FindFirst(); + ExpectedDescription2 := 'TestDescription2_Comp'; + ProdOrderComp."Description 2" := ExpectedDescription2; + ProdOrderComp.Modify(); + + // [WHEN] Create Subcontracting Purchase Order from Prod. Order Routing + SubcontractingMgmtLibrary.CreateSubcontractingOrderFromProdOrderRtngPage(Item."Routing No.", WorkCenter[2]."No."); + + // [THEN] Description 2 from Prod. Order Component is propagated to the component Purchase Line + PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); + PurchaseLine.SetRange(Type, PurchaseLine.Type::Item); + PurchaseLine.SetRange("No.", ProdOrderComp."Item No."); +#pragma warning disable AA0210 + PurchaseLine.SetRange("Subc. Prod. Order No.", ProductionOrder."No."); +#pragma warning restore AA0210 + PurchaseLine.FindFirst(); + Assert.AreEqual( + ExpectedDescription2, PurchaseLine."Description 2", + 'Description 2 must be propagated from Prod. Order Component to Purchase Line'); + end; + + [Test] + procedure Description2PopulatedOnRequisitionLineFromCalculateSubcontracts() + var + Item: Record Item; + MachineCenter: array[2] of Record "Machine Center"; + ProductionOrder: Record "Production Order"; + ReqWkshTemplate: Record "Req. Wksh. Template"; + RequisitionLine: Record "Requisition Line"; + RequisitionWkshName: Record "Requisition Wksh. Name"; + WorkCenter: array[2] of Record "Work Center"; + SubcCalculateSubContract: Report "Subc. Calculate Subcontracts"; + LibraryUtility: Codeunit "Library - Utility"; + ExpectedDescription2: Text[50]; + begin + // [SCENARIO] Description 2 from Prod. Order Routing Line is populated on Requisition Line + // via Calculate Subcontracts report + // [FEATURE] Bug 620556 - Subcontracting Description 2 alignment + + // [GIVEN] Complete Setup of Manufacturing + Initialize(); + Subcontracting := true; + UnitCostCalculation := UnitCostCalculation::Units; + UpdateSubMgmtSetupWithReqWkshTemplate(); + + CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); + CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); + UpdateProdBomAndRoutingWithRoutingLink(Item, WorkCenter[2]."No."); + SubcontractingMgmtLibrary.UpdateVendorWithSubcontractingLocationCode(WorkCenter[2]); + + SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( + ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); + + // [GIVEN] Description 2 is set on the subcontracting Work Center Name 2 + // (SubcCalcSubcontractsExt copies WorkCenter."Name 2" → RequisitionLine."Description 2") + ExpectedDescription2 := 'TestDesc2_WC'; + WorkCenter[2].Get(WorkCenter[2]."No."); + WorkCenter[2].Validate("Name 2", ExpectedDescription2); + WorkCenter[2].Modify(true); + + // [GIVEN] Create requisition worksheet + ReqWkshTemplate.DeleteAll(true); + ReqWkshTemplate.Name := SelectRequisitionTemplateName(); + RequisitionWkshName.Init(); + RequisitionWkshName.Validate("Worksheet Template Name", ReqWkshTemplate.Name); + RequisitionWkshName.Validate( + Name, + CopyStr( + LibraryUtility.GenerateRandomCode(RequisitionWkshName.FieldNo(Name), Database::"Requisition Wksh. Name"), + 1, LibraryUtility.GetFieldLength(Database::"Requisition Wksh. Name", RequisitionWkshName.FieldNo(Name)))); + RequisitionWkshName.Insert(true); + + RequisitionLine."Worksheet Template Name" := RequisitionWkshName."Worksheet Template Name"; + RequisitionLine."Journal Batch Name" := RequisitionWkshName.Name; + + // [WHEN] Calculate Subcontracts + SubcCalculateSubContract.SetWkShLine(RequisitionLine); + SubcCalculateSubContract.UseRequestPage(false); + SubcCalculateSubContract.RunModal(); + + // [THEN] Description 2 on the Requisition Line is populated + RequisitionLine.SetRange("Worksheet Template Name", RequisitionWkshName."Worksheet Template Name"); + RequisitionLine.SetRange("Journal Batch Name", RequisitionWkshName.Name); +#pragma warning disable AA0210 + RequisitionLine.SetRange("Prod. Order No.", ProductionOrder."No."); +#pragma warning restore AA0210 + RequisitionLine.FindFirst(); + Assert.AreEqual( + ExpectedDescription2, RequisitionLine."Description 2", + 'Description 2 must be populated on the Requisition Line from the subcontracting Work Center'); + end; + + [Test] + [HandlerFunctions('DoNotConfirmShowCreatedPurchOrderForSubcontracting')] + procedure CreateSubcontractingPOForEachProdOrderLineWhenLinesShareRoutingAndOperation() + var + Item: Record Item; + ProductionLocation: array[2] of Record Location; + MachineCenter: array[2] of Record "Machine Center"; + ProdOrderLine: Record "Prod. Order Line"; + ProdOrderRoutingLine: Record "Prod. Order Routing Line"; + ProductionOrder: Record "Production Order"; + WorkCenter: array[2] of Record "Work Center"; + ProdOrderRtng: TestPage "Prod. Order Routing"; + I: Integer; + ProdOrderLineNo: array[2] of Integer; + begin + // [SCENARIO 634238] When a Released Production Order has multiple Prod. Order lines sharing the same + // Routing/Operation, creating a Subcontracting Order for the second line must not raise the false + // "Purchase order(s) have already been created" warning, and must create/show its own Purchase Order. + + // [GIVEN] Subcontracting setup with direct transfer (no in-transit route) + Initialize(); + SubcontractingMgmtLibrary.UpdateManufacturingSetupWithSubcontractingLocation(); + Subcontracting := true; + UnitCostCalculation := UnitCostCalculation::Units; + CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); + UpdateVendorWithSubcontractingLocationCode(WorkCenter[2]); + CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); + UpdateProdBomAndRoutingWithRoutingLink(Item, WorkCenter[2]."No."); + + // [GIVEN] One released production order created directly from item + LibraryManufacturing.CreateProductionOrder(ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", 0); + + // [GIVEN] No production order lines exist yet for this order + ProdOrderLine.SetRange(Status, ProdOrderLine.Status::Released); + ProdOrderLine.SetRange("Prod. Order No.", ProductionOrder."No."); + Assert.AreEqual(0, ProdOrderLine.Count(), 'Expected no production order lines to exist before manually creating them'); + + // [GIVEN] Two production order lines on the same production order, on different locations + LibraryWarehouse.CreateLocationWithInventoryPostingSetup(ProductionLocation[1]); + LibraryWarehouse.CreateLocationWithInventoryPostingSetup(ProductionLocation[2]); + 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."; + Assert.AreNotEqual(ProdOrderLineNo[1], ProdOrderLineNo[2], 'Expected two distinct production order lines'); + + // [GIVEN] Refresh the production order to update the routing and component lines + LibraryManufacturing.RefreshProdOrder(ProductionOrder, false, false, true, true, false); + + // [GIVEN] The two production-order lines have transfer components on different locations + for I := 1 to 2 do begin + ProdOrderRoutingLine.Reset(); + ProdOrderRoutingLine.SetRange(Status, ProdOrderRoutingLine.Status::Released); + ProdOrderRoutingLine.SetRange("Prod. Order No.", ProductionOrder."No."); + ProdOrderRoutingLine.SetRange("Routing Reference No.", ProdOrderLineNo[I]); + ProdOrderRoutingLine.SetRange("Work Center No.", WorkCenter[2]."No."); + Assert.RecordCount(ProdOrderRoutingLine, 1); + + ProdOrderRoutingLine.FindFirst(); + + ProdOrderRtng.OpenView(); + ProdOrderRtng.GoToRecord(ProdOrderRoutingLine); + 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'); + LibraryVariableStorage.AssertEmpty(); + end; + end; + + [Test] + [HandlerFunctions('ConfirmYesShowSubcontractingPurchOrders,CapturePurchaseOrderPageNo')] + procedure CreateSubcontractingPONavigatesToOwnPOWhenLinesShareRoutingAndOperation() + var + Item: Record Item; + ProductionLocation: array[2] of Record Location; + MachineCenter: array[2] of Record "Machine Center"; + ProdOrderLine: Record "Prod. Order Line"; + ProdOrderRoutingLine: Record "Prod. Order Routing Line"; + ProductionOrder: Record "Production Order"; + PurchaseLine: Record "Purchase Line"; + WorkCenter: array[2] of Record "Work Center"; + ProdOrderRtng: TestPage "Prod. Order Routing"; + I: Integer; + ProdOrderLineNo: array[2] of Integer; + OpenedPurchaseOrderNo: Code[20]; + begin + // [SCENARIO 634238] When a Released Production Order has multiple Prod. Order lines sharing routing/operation, + // confirming "view them" on the just-created Subcontracting Order must open the PO tied to the invoked + // routing line, not the unrelated PO of a sibling line. + + // [GIVEN] Subcontracting setup with two prod order lines sharing routing/operation but different Routing Reference No. + Initialize(); + SubcontractingMgmtLibrary.UpdateManufacturingSetupWithSubcontractingLocation(); + Subcontracting := true; + UnitCostCalculation := UnitCostCalculation::Units; + CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); + UpdateVendorWithSubcontractingLocationCode(WorkCenter[2]); + CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); + UpdateProdBomAndRoutingWithRoutingLink(Item, WorkCenter[2]."No."); + + LibraryManufacturing.CreateProductionOrder(ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", 0); + + LibraryWarehouse.CreateLocationWithInventoryPostingSetup(ProductionLocation[1]); + LibraryWarehouse.CreateLocationWithInventoryPostingSetup(ProductionLocation[2]); + 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."; + + LibraryManufacturing.RefreshProdOrder(ProductionOrder, false, false, true, true, false); + + // [WHEN] Creating a Subcontracting Order from each routing line and confirming "view them" + for I := 1 to 2 do begin + ProdOrderRoutingLine.Reset(); + ProdOrderRoutingLine.SetRange(Status, ProdOrderRoutingLine.Status::Released); + ProdOrderRoutingLine.SetRange("Prod. Order No.", ProductionOrder."No."); + ProdOrderRoutingLine.SetRange("Routing Reference No.", ProdOrderLineNo[I]); + ProdOrderRoutingLine.SetRange("Work Center No.", WorkCenter[2]."No."); + ProdOrderRoutingLine.FindFirst(); + + ProdOrderRtng.OpenView(); + ProdOrderRtng.GoToRecord(ProdOrderRoutingLine); + ProdOrderRtng.CreateSubcontracting.Invoke(); + ProdOrderRtng.Close(); + + // [THEN] The page handler opens the Purchase Order whose line carries this routing line's Routing Reference No. + OpenedPurchaseOrderNo := CopyStr(LibraryVariableStorage.DequeueText(), 1, MaxStrLen(OpenedPurchaseOrderNo)); + PurchaseLine.Reset(); + PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); + PurchaseLine.SetRange("Document No.", OpenedPurchaseOrderNo); + PurchaseLine.SetRange(Type, PurchaseLine.Type::Item); + PurchaseLine.SetRange("Prod. Order No.", ProductionOrder."No."); + PurchaseLine.SetRange("Routing Reference No.", ProdOrderLineNo[I]); + Assert.IsFalse(PurchaseLine.IsEmpty(), StrSubstNo(PurchOrderRoutingErr, OpenedPurchaseOrderNo, ProdOrderLineNo[I])); + LibraryVariableStorage.AssertEmpty(); + end; + end; + + [Test] + [HandlerFunctions('ConfirmYesShowSubcontractingPurchOrders,HandlePurchaseOrderPage,HandlePurchaseLinesPage')] + procedure ShowExistingPurchOrdersOpensListWhenAlreadyCreated() + var + Item: Record Item; + MachineCenter: array[2] of Record "Machine Center"; + ProductionOrder: Record "Production Order"; + PurchaseLine: Record "Purchase Line"; + WorkCenter: array[2] of Record "Work Center"; + begin + // [SCENARIO 633224] First Create Subcontracting Order opens the Purchase Order; running the action again on the same routing line opens the Purchase Lines list. + + // [GIVEN] Manufacturing setup with subcontracting work center, item with routing/BOM, released production order + Initialize(); + Subcontracting := true; + UnitCostCalculation := UnitCostCalculation::Units; + CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); + CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); + SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( + ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); + UpdateSubMgmtSetupWithReqWkshTemplate(); + + // [WHEN] Create Subcontracting Order from the routing line for the first time + PurchaseOrderPageOpened := false; + PurchaseLinesPageOpened := false; + SubcontractingMgmtLibrary.CreateSubcontractingOrderFromProdOrderRtngPage(Item."Routing No.", WorkCenter[2]."No."); + + // [THEN] The Purchase Order card is shown + Assert.IsTrue(PurchaseOrderPageOpened, 'Purchase Order should open after first creation.'); + Assert.IsFalse(PurchaseLinesPageOpened, 'Purchase Lines list should not open on first creation.'); + + PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); + PurchaseLine.SetRange("Prod. Order No.", ProductionOrder."No."); + PurchaseLine.SetRange("Work Center No.", WorkCenter[2]."No."); + Assert.IsFalse(PurchaseLine.IsEmpty(), 'Purchase line should exist for the production order.'); + + // [WHEN] Create Subcontracting Order from the same routing line again + PurchaseOrderPageOpened := false; + PurchaseLinesPageOpened := false; + SubcontractingMgmtLibrary.CreateSubcontractingOrderFromProdOrderRtngPage(Item."Routing No.", WorkCenter[2]."No."); + + // [THEN] The Purchase Lines list is shown instead of individual Purchase Order cards + Assert.IsTrue(PurchaseLinesPageOpened, 'Purchase Lines list should open when purchase orders already exist.'); + Assert.IsFalse(PurchaseOrderPageOpened, 'Purchase Order card should not open when purchase orders already exist.'); + end; + + [ConfirmHandler] + procedure ConfirmYesShowSubcontractingPurchOrders(Question: Text[1024]; var Reply: Boolean) + begin + Reply := true; + end; + + [PageHandler] + procedure HandlePurchaseOrderPage(var PurchaseOrderPage: TestPage "Purchase Order") + begin + PurchaseOrderPageOpened := true; + PurchaseOrderPage.Close(); + end; + + [PageHandler] + procedure CapturePurchaseOrderPageNo(var PurchaseOrderPage: TestPage "Purchase Order") + begin + LibraryVariableStorage.Enqueue(PurchaseOrderPage."No.".Value); + PurchaseOrderPage.Close(); + end; + + [PageHandler] + procedure HandlePurchaseLinesPage(var PurchaseLinesPage: TestPage "Purchase Lines") + begin + PurchaseLinesPageOpened := true; + PurchaseLinesPage.Close(); + end; + + [ConfirmHandler] + procedure DoNotConfirmShowCreatedPurchOrderForSubcontracting(Question: Text[1024]; var Reply: Boolean) + begin + LibraryVariableStorage.Enqueue(Question); + Reply := false; + end; + + [ConfirmHandler] + procedure DoConfirmCreateProdOrderForSubcontractingProcess(Question: Text[1024]; var Reply: Boolean) + begin + case true of + Question.Contains('Do you want to create a production order from'): + Reply := true; + else + Reply := false; + end; + end; + + local procedure CreateAndCalculateNeededWorkAndMachineCenter(var WorkCenter: array[2] of Record "Work Center"; var MachineCenter: array[2] of Record "Machine Center") + var + CapacityUnitOfMeasure: Record "Capacity Unit of Measure"; + ShopCalendarCode: Code[10]; + MachineCenterNo: Code[20]; + MachineCenterNo2: Code[20]; + WorkCenterNo: Code[20]; + WorkCenterNo2: Code[20]; + begin + LibraryManufacturing.CreateCapacityUnitOfMeasure(CapacityUnitOfMeasure, "Capacity Unit of Measure"::Minutes); + ShopCalendarCode := LibraryManufacturing.UpdateShopCalendarWorkingDays(); + + // [GIVEN] Create and Calculate needed Work and Machine Center + CreateWorkCenter(WorkCenterNo, ShopCalendarCode, "Flushing Method"::"Pick + Manual", not Subcontracting, UnitCostCalculation, ''); + WorkCenter[1].Get(WorkCenterNo); + LibraryManufacturing.CalculateWorkCenterCalendar(WorkCenter[1], CalcDate('<-CY-1Y>', WorkDate()), CalcDate('', WorkDate())); + + LibraryMfgManagement.CreateMachineCenter(MachineCenterNo, WorkCenterNo, "Flushing Method"::"Pick + Manual".AsInteger()); + MachineCenter[1].Get(MachineCenterNo); + LibraryManufacturing.CalculateMachCenterCalendar(MachineCenter[1], CalcDate('<-CY-1Y>', WorkDate()), CalcDate('', WorkDate())); + + LibraryMfgManagement.CreateMachineCenter(MachineCenterNo2, WorkCenterNo, "Flushing Method"::"Pick + Manual".AsInteger()); + MachineCenter[2].Get(MachineCenterNo2); + LibraryManufacturing.CalculateMachCenterCalendar(MachineCenter[2], CalcDate('<-CY-1Y>', WorkDate()), CalcDate('', WorkDate())); + + if Subcontracting then + CreateWorkCenter(WorkCenterNo2, ShopCalendarCode, "Flushing Method"::"Pick + Manual", Subcontracting, UnitCostCalculation, '') + else + CreateWorkCenter(WorkCenterNo2, ShopCalendarCode, "Flushing Method"::"Pick + Manual", not Subcontracting, UnitCostCalculation, ''); + WorkCenter[2].Get(WorkCenterNo2); + LibraryManufacturing.CalculateWorkCenterCalendar(WorkCenter[2], CalcDate('<-CY-1Y>', WorkDate()), CalcDate('', WorkDate())); + end; + + local procedure CreateItemForProductionIncludeRoutingAndProdBOM(var Item: Record Item; var WorkCenter: array[2] of Record "Work Center"; var MachineCenter: array[2] of Record "Machine Center") + var + ManufacturingSetup: Record "Manufacturing Setup"; + ProductionBOMHeader: Record "Production BOM Header"; + NoSeries: Codeunit "No. Series"; + ItemNo: Code[20]; + ItemNo2: Code[20]; + ProductionBOMNo: Code[20]; + RoutingNo: Code[20]; + begin + ManufacturingSetup.SetLoadFields("Routing Nos."); + ManufacturingSetup.Get(); + RoutingNo := NoSeries.GetNextNo(ManufacturingSetup."Routing Nos.", WorkDate(), true); + + LibraryMfgManagement.CreateRouting(RoutingNo, MachineCenter[1]."No.", MachineCenter[2]."No.", WorkCenter[1]."No.", WorkCenter[2]."No."); + + // Create Items with Flushing method - Manual with the Parent Item containing Routing No. and Production BOM No. + + CreateItem(Item, "Costing Method"::FIFO, "Reordering Policy"::"Lot-for-Lot", "Flushing Method"::"Pick + Manual", '', ''); + ItemNo := Item."No."; + Clear(Item); + CreateItem(Item, "Costing Method"::FIFO, "Reordering Policy"::"Lot-for-Lot", "Flushing Method"::"Pick + Manual", '', ''); + ItemNo2 := Item."No."; + Clear(Item); + + ProductionBOMNo := LibraryManufacturing.CreateCertifProdBOMWithTwoComp(ProductionBOMHeader, ItemNo, ItemNo2, 1); // value important. + + CreateItem(Item, "Costing Method"::FIFO, "Reordering Policy"::"Lot-for-Lot", "Flushing Method"::"Pick + Manual", RoutingNo, ProductionBOMNo); + end; + + local procedure UpdateProdBomAndRoutingWithRoutingLink(Item: Record Item; WorkCenterNo: Code[20]) + var + ProductionBOMHeader: Record "Production BOM Header"; + ProductionBOMLine: Record "Production BOM Line"; + RoutingHeader: Record "Routing Header"; + RoutingLine: Record "Routing Line"; + RoutingLink: Record "Routing Link"; + begin + RoutingLink.Init(); + RoutingLink.Validate(Code, CopyStr(Item."Production BOM No.", 1, 10)); + RoutingLink.Insert(true); + + RoutingHeader.Get(Item."Routing No."); + RoutingHeader.Validate(Status, RoutingHeader.Status::New); + RoutingHeader.Modify(true); + + RoutingLine.SetRange("Routing No.", RoutingHeader."No."); + RoutingLine.SetRange(Type, RoutingLine.Type::"Work Center"); + RoutingLine.SetRange("No.", WorkCenterNo); + RoutingLine.FindFirst(); + RoutingLine.Validate("Routing Link Code", RoutingLink.Code); + RoutingLine.Modify(true); + + RoutingHeader.Validate(Status, RoutingHeader.Status::Certified); + RoutingHeader.Modify(true); + + ProductionBOMHeader.Get(Item."Production BOM No."); + ProductionBOMHeader.Validate(Status, ProductionBOMHeader.Status::New); + ProductionBOMHeader.Modify(true); + + ProductionBOMLine.SetRange("Production BOM No.", ProductionBOMHeader."No."); + ProductionBOMLine.FindLast(); + ProductionBOMLine.Validate("Routing Link Code", RoutingLink.Code); + ProductionBOMLine.Modify(true); + + ProductionBOMHeader.Validate(Status, ProductionBOMHeader.Status::Certified); + ProductionBOMHeader.Modify(true); + end; + + local procedure UpdateVendorWithSubcontractingLocationCode(WorkCenter: Record "Work Center") + var + Location: Record Location; + Vendor: Record Vendor; + begin + LibraryWarehouse.CreateLocationWithInventoryPostingSetup(Location); + Vendor.Get(WorkCenter."Subcontractor No."); + Vendor."Subc. Location Code" := Location.Code; + LibraryWarehouse.CreateLocationWithInventoryPostingSetup(Location); + Vendor."Location Code" := Location.Code; + Vendor.Modify(); + end; + + procedure CreateWorkCenter(var WorkCenterNo: Code[20]; ShopCalendarCode: Code[10]; FlushingMethod: Enum "Flushing Method"; Subcontract: Boolean; + UnitCostCalc: Option; + CurrencyCode: Code[10]) + var + GenProductPostingGroup: Record "Gen. Product Posting Group"; + VATPostingSetup: Record "VAT Posting Setup"; + WorkCenter: Record "Work Center"; + begin + // Create Work Center with required fields where random is used, values not important for test. + LibraryMfgManagement.CreateWorkCenterWithFixedCost(WorkCenter, ShopCalendarCode, 0); + + WorkCenter.Validate("Flushing Method", FlushingMethod); + WorkCenter.Validate("Direct Unit Cost", LibraryRandom.RandDec(10, 2)); + WorkCenter.Validate("Indirect Cost %", LibraryRandom.RandDec(5, 1)); + WorkCenter.Validate("Overhead Rate", LibraryRandom.RandDec(5, 1)); + WorkCenter.Validate("Unit Cost Calculation", UnitCostCalc); + + if Subcontract then begin + LibraryERM.FindVATPostingSetup(VATPostingSetup, VATPostingSetup."VAT Calculation Type"::"Normal VAT"); + GenProductPostingGroup.FindFirst(); + GenProductPostingGroup.Validate("Def. VAT Prod. Posting Group", VATPostingSetup."VAT Prod. Posting Group"); + GenProductPostingGroup.Modify(true); + WorkCenter.Validate("Subcontractor No.", LibraryMfgManagement.CreateSubcontractorWithCurrency(CurrencyCode)); + end; + WorkCenter.Modify(true); + WorkCenterNo := WorkCenter."No."; + end; + + local procedure CreateItem(var Item: Record Item; ItemCostingMethod: Enum "Costing Method"; ItemReorderPolicy: Enum "Reordering Policy"; + FlushingMethod: Enum "Flushing Method"; + RoutingNo: Code[20]; + ProductionBOMNo: Code[20]) + begin + // Create Item with required fields where random values not important for test. + LibraryManufacturing.CreateItemManufacturing( + Item, ItemCostingMethod, LibraryRandom.RandInt(10), ItemReorderPolicy, FlushingMethod, RoutingNo, ProductionBOMNo); + Item.Validate("Overhead Rate", LibraryRandom.RandDec(5, 2)); + Item.Validate("Indirect Cost %", LibraryRandom.RandDec(5, 2)); + Item.Modify(true); + end; + + local procedure Initialize() + begin + LibraryTestInitialize.OnTestInitialize(Codeunit::"Subc. Purchase Order Test"); + LibrarySetupStorage.Restore(); + + SubcontractingMgmtLibrary.Initialize(); + SubcontractingMgmtLibrary.UpdateSubMgmtSetup_ComponentAtLocation("Components at Location"::Purchase); + UpdateSubMgmtSetupWithReqWkshTemplate(); + LibraryVariableStorage.Clear(); + + LibraryMfgManagement.Initialize(); + + if IsInitialized then + exit; + LibraryTestInitialize.OnBeforeTestSuiteInitialize(Codeunit::"Subc. Purchase Order Test"); + + SubSetupLibrary.InitSetupFields(); + SubSetupLibrary.ConfigureSubManagementForNothingPresentScenario("Subc. Show/Edit Type"::Hide, "Subc. Show/Edit Type"::Hide); + SubSetupLibrary.ConfigureSubManagementForBothPresentScenario("Subc. Show/Edit Type"::Hide, "Subc. Show/Edit Type"::Hide); + LibraryERMCountryData.CreateVATData(); + SubSetupLibrary.InitialSetupForGenProdPostingGroup(); + + IsInitialized := true; + Commit(); + + LibraryTestInitialize.OnAfterTestSuiteInitialize(Codeunit::"Subc. Purchase Order Test"); + end; + + local procedure UpdateSubMgmtSetupWithReqWkshTemplate() + begin + LibraryMfgManagement.CreateSubcontractingReqWkshTemplateAndNameAndUpdateSetup(); + end; + + local procedure UpdateSubMgmtSetupTransferInfoLine(Update: Boolean) + var + ManufacturingSetup: Record "Manufacturing Setup"; + begin + ManufacturingSetup.Get(); + ManufacturingSetup."Create Prod. Order Info Line" := Update; + ManufacturingSetup.Modify(); + end; + + local procedure UpdateSubMgmtRoutingLink(RtngLink: Code[10]) + var + ManufacturingSetup: Record "Manufacturing Setup"; + begin + ManufacturingSetup.Get(); + ManufacturingSetup."Rtng. Link Code Purch. Prov." := RtngLink; + ManufacturingSetup.Modify(); + end; + + local procedure UpdateSubMgmtCommonWorkCenter(WorkCenterNo: Code[20]) + var + SubManagementSetup: Record "Subc. Management Setup"; + begin + SubManagementSetup.Get(); + SubManagementSetup."Common Work Center No." := WorkCenterNo; + SubManagementSetup.Modify(); + end; + + procedure SelectRequisitionTemplateName(): Code[10] + var + ReqWkshTemplate: Record "Req. Wksh. Template"; + LibraryUtility: Codeunit "Library - Utility"; + begin + ReqWkshTemplate.SetRange(Type, ReqWkshTemplate.Type::Subcontracting); + ReqWkshTemplate.SetRange(Recurring, false); + if not ReqWkshTemplate.FindFirst() then begin + ReqWkshTemplate.Init(); + ReqWkshTemplate.Validate( + Name, LibraryUtility.GenerateRandomCode(ReqWkshTemplate.FieldNo(Name), Database::"Req. Wksh. Template")); + ReqWkshTemplate.Insert(true); + ReqWkshTemplate.Validate(Type, ReqWkshTemplate.Type::Subcontracting); + ReqWkshTemplate."Page ID" := Page::"Subc. Subcontracting Worksheet"; + ReqWkshTemplate.Modify(true); + end; + exit(ReqWkshTemplate.Name); + end; + var + WorkCenter2: Record "Work Center"; + Assert: Codeunit Assert; + LibraryERM: Codeunit "Library - ERM"; + LibraryERMCountryData: Codeunit "Library - ERM Country Data"; + LibraryInventory: Codeunit "Library - Inventory"; + LibraryManufacturing: Codeunit "Library - Manufacturing"; + LibraryPlanning: Codeunit "Library - Planning"; + LibraryPurchase: Codeunit "Library - Purchase"; + LibraryRandom: Codeunit "Library - Random"; + LibrarySales: Codeunit "Library - Sales"; + LibrarySetupStorage: Codeunit "Library - Setup Storage"; + LibraryTestInitialize: Codeunit "Library - Test Initialize"; + LibraryWarehouse: Codeunit "Library - Warehouse"; + LibraryMfgManagement: Codeunit "Subc. Library Mfg. Management"; + SubcontractingMgmtLibrary: Codeunit "Subc. Management Library"; + LibraryVariableStorage: Codeunit "Library - Variable Storage"; + SubSetupLibrary: Codeunit "Subc. Setup Library"; + IsInitialized: Boolean; + Subcontracting: Boolean; + OpenedTransferOrderNo: Code[20]; + PurchaseOrderPageOpened: Boolean; + PurchaseLinesPageOpened: Boolean; + UnitCostCalculation: Option Time,Units; + ConfirmDialogCalledCount: Integer; + AlreadySpecifiedErr: Label 'You cannot open Tracking Specification because this component is already specified in Transfer Order %1.', Comment = '|%1 = Transfer Order No.'; + PurchOrderRoutingErr: Label 'Purchase Order %1 should contain a line tied to Routing Reference No. %2', Comment = '%1 = Purchase Order No., %2 = Routing Reference No.'; + +} \ No newline at end of file diff --git a/src/Apps/W1/Subcontracting/Test/src/Codeunits/Tests/SubcSubcontractingTest.Codeunit.al b/src/Apps/W1/Subcontracting/Test/src/Codeunits/Tests/SubcSubcontractingTest.Codeunit.al index 68c7fff2af..1ea9aba15b 100644 --- a/src/Apps/W1/Subcontracting/Test/src/Codeunits/Tests/SubcSubcontractingTest.Codeunit.al +++ b/src/Apps/W1/Subcontracting/Test/src/Codeunits/Tests/SubcSubcontractingTest.Codeunit.al @@ -48,188 +48,126 @@ codeunit 139989 "Subc. Subcontracting Test" end; [Test] - [HandlerFunctions('ConfirmHandler,HandleTransferOrder')] - procedure CreateTransferOrderFromSecondSubcontractingOrderOpensReusedTransferOrder() + procedure InsertRecordOnOpenPageSubcontractingManagementSetup() + var + SubcontractingMgmtSetupPage: TestPage "Subc. Management Setup"; + begin + // [SCENARIO] OnOpenPage should create a record in Subcontracting Setup + + // [GIVEN] No Record exists in Subcontracting Setup + Initialize(); + RemoveSubcontractingManagementSetupRecord(); + CheckNoSubcontractingManagementSetupRecordExist(); + + // [WHEN] Open Subcontracting Setup Page + SubcontractingMgmtSetupPage.OpenView(); + SubcontractingMgmtSetupPage.Close(); + + // [THEN] A Record should exist in Subcontracting Setup + CheckSubcontractingManagementSetupRecordExist(); + + // [TEARDOWN] + Clear(IsInitialized); + end; + + [Test] + procedure TestTransferOfSubcontractingTypeProdBOMLineToProdOrderComp() var Item: Record Item; MachineCenter: array[2] of Record "Machine Center"; - ProdOrderRoutingLine: Record "Prod. Order Routing Line"; - ProductionOrder1: Record "Production Order"; - ProductionOrder2: Record "Production Order"; - PurchaseHeader1: Record "Purchase Header"; - PurchaseHeader2: Record "Purchase Header"; - PurchaseLine: Record "Purchase Line"; - TransferHeader: Record "Transfer Header"; - TransferLine: Record "Transfer Line"; + ProductionBOMLine: Record "Production BOM Line"; + ProductionOrder: Record "Production Order"; WorkCenter: array[2] of Record "Work Center"; - ProductionLocation: Record Location; - FirstTransferOrderNo: Code[20]; - SecondTransferOrderNo: Code[20]; - ReleasedProdOrderRtng: TestPage "Prod. Order Routing"; - PurchaseHeaderPage: TestPage "Purchase Order"; begin - // [SCENARIO 634237] Creating transfer order for a second subcontracting PO should create and open a different transfer order. + // [SCENARIO] Check Transfer of Subcontracting Type from Production BOM Line to Prod Order Component - // [GIVEN] Subcontracting setup with transfer components and an initial subcontracting order with transfer order already created + // [GIVEN] Complete Setup of Manufacturing, include Work- and Machine Centers, Item Initialize(); - SubcontractingMgmtLibrary.UpdateManufacturingSetupWithSubcontractingLocation(); - SubcontractingMgmtLibrary.SetupInventorySetup(); + // [GIVEN] Some Parameters for Creation Subcontracting := true; UnitCostCalculation := UnitCostCalculation::Units; - CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); - CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); - UpdateProdBomAndRoutingWithRoutingLink(Item, WorkCenter[2]."No."); - SubcontractingMgmtLibrary.UpdateProdBomWithSubcontractingType(Item, "Subcontracting Type"::Transfer); - UpdateVendorWithSubcontractingLocationCode(WorkCenter[2]); - - LibraryWarehouse.CreateLocationWithInventoryPostingSetup(ProductionLocation); - SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( - ProductionOrder1, "Production Order Status"::Released, ProductionOrder1."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); - UpdateSubMgmtSetupWithReqWkshTemplate(); - SetAllProdOrderTransferComponentLocations(ProductionOrder1."No.", ProductionLocation.Code); - SubcontractingMgmtLibrary.CreateTransferRoute(WorkCenter[2], ProductionOrder1); + // [GIVEN] + CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); - ProdOrderRoutingLine.SetRange("Prod. Order No.", ProductionOrder1."No."); - ProdOrderRoutingLine.SetRange("Work Center No.", WorkCenter[2]."No."); - ProdOrderRoutingLine.FindFirst(); - ReleasedProdOrderRtng.OpenView(); - ReleasedProdOrderRtng.GoToRecord(ProdOrderRoutingLine); - ReleasedProdOrderRtng.CreateSubcontracting.Invoke(); - ReleasedProdOrderRtng.Close(); + // [GIVEN] Create Item for Production include Routing and Prod. BOM + CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); - PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); - PurchaseLine.SetRange("Prod. Order No.", ProductionOrder1."No."); - PurchaseLine.FindFirst(); - PurchaseHeader1.Get(PurchaseLine."Document Type", PurchaseLine."Document No."); + UpdateProdBomAndRoutingWithRoutingLink(Item, WorkCenter[2]."No."); - PurchaseHeaderPage.OpenView(); - PurchaseHeaderPage.GoToRecord(PurchaseHeader1); - PurchaseHeaderPage.CreateTransfOrdToSubcontractor.Invoke(); - PurchaseHeaderPage.Close(); + SubcontractingMgmtLibrary.UpdateProdBomWithSubcontractingType(Item, "Subcontracting Type"::Purchase); - TransferHeader.SetRange("Subcontr. Purch. Order No.", PurchaseHeader1."No."); - Assert.IsTrue(TransferHeader.FindFirst(), 'Expected transfer order for the first subcontracting purchase order.'); - FirstTransferOrderNo := TransferHeader."No."; - Assert.AreEqual(PurchaseHeader1."No.", TransferHeader."Subcontr. Purch. Order No.", 'First transfer order must be linked to the first subcontracting purchase order.'); + SubcontractingMgmtLibrary.UpdateVendorWithSubcontractingLocationCode(WorkCenter[2]); - // [GIVEN] A second released production order for the same subcontracting setup + // [WHEN] Creating Production Order to Transfer Information SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( - ProductionOrder2, "Production Order Status"::Released, ProductionOrder2."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); - SetAllProdOrderTransferComponentLocations(ProductionOrder2."No.", ProductionLocation.Code); - - ProdOrderRoutingLine.Reset(); - ProdOrderRoutingLine.SetRange("Prod. Order No.", ProductionOrder2."No."); - ProdOrderRoutingLine.SetRange("Work Center No.", WorkCenter[2]."No."); - ProdOrderRoutingLine.FindFirst(); - ReleasedProdOrderRtng.OpenView(); - ReleasedProdOrderRtng.GoToRecord(ProdOrderRoutingLine); - ReleasedProdOrderRtng.CreateSubcontracting.Invoke(); - ReleasedProdOrderRtng.Close(); - - PurchaseLine.Reset(); - PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); - PurchaseLine.SetRange("Prod. Order No.", ProductionOrder2."No."); - PurchaseLine.FindFirst(); - PurchaseHeader2.Get(PurchaseLine."Document Type", PurchaseLine."Document No."); - Assert.AreNotEqual(PurchaseHeader1."No.", PurchaseHeader2."No.", 'Second production order should create another subcontracting purchase order.'); - - // [WHEN] Creating transfer order from the second subcontracting purchase order - OpenedTransferOrderNo := ''; - PurchaseHeaderPage.OpenView(); - PurchaseHeaderPage.GoToRecord(PurchaseHeader2); - PurchaseHeaderPage.CreateTransfOrdToSubcontractor.Invoke(); - PurchaseHeaderPage.Close(); + ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); - TransferHeader.Reset(); - TransferHeader.SetRange("Subcontr. Purch. Order No.", PurchaseHeader2."No."); - Assert.IsTrue(TransferHeader.FindFirst(), 'Expected transfer order for the second subcontracting purchase order.'); - SecondTransferOrderNo := TransferHeader."No."; - Assert.AreEqual(PurchaseHeader2."No.", TransferHeader."Subcontr. Purch. Order No.", 'Second transfer order must be linked to the second subcontracting purchase order.'); - - // [THEN] A new transfer order is opened for the second subcontracting purchase order and contains lines for the second production order - Assert.AreNotEqual(FirstTransferOrderNo, SecondTransferOrderNo, 'A different subcontracting purchase order must create a new transfer order.'); - Assert.AreEqual(SecondTransferOrderNo, OpenedTransferOrderNo, 'The transfer order opened from the second subcontracting PO must belong to that purchase order.'); - TransferHeader.Get(FirstTransferOrderNo); - Assert.AreEqual(PurchaseHeader1."No.", TransferHeader."Subcontr. Purch. Order No.", 'First transfer order must remain linked to the first subcontracting purchase order.'); - TransferHeader.Get(SecondTransferOrderNo); - Assert.AreEqual(PurchaseHeader2."No.", TransferHeader."Subcontr. Purch. Order No.", 'Second transfer order must remain linked to the second subcontracting purchase order.'); - - TransferLine.Reset(); - TransferLine.SetRange("Document No.", SecondTransferOrderNo); - TransferLine.SetRange("Subc. Prod. Order No.", ProductionOrder2."No."); - Assert.RecordIsNotEmpty(TransferLine); + // [THEN] Check if Production BOM Line with additional Component for Subcontracting Type exists + ProductionBOMLine.SetRange("Production BOM No.", Item."Production BOM No."); + ProductionBOMLine.SetRange("Subcontracting Type", ProductionBOMLine."Subcontracting Type"::Purchase); + Assert.RecordIsNotEmpty(ProductionBOMLine); end; [Test] - [HandlerFunctions('ConfirmHandler,HandleTransferOrder')] - procedure CannotDeleteSubcontractingOrderWithAssociatedTransferOrder() + procedure TestChangeLocationOnProdOrderCompWithSubcontractingTypePurchase() var Item: Record Item; MachineCenter: array[2] of Record "Machine Center"; + ProdOrderComp: Record "Prod. Order Component"; ProductionOrder: Record "Production Order"; - PurchaseHeader: Record "Purchase Header"; - PurchaseLine: Record "Purchase Line"; - TransferHeader: Record "Transfer Header"; WorkCenter: array[2] of Record "Work Center"; - PurchaseHeaderPage: TestPage "Purchase Order"; - PurchaseOrderNo: Code[20]; + ActualLocationCode: Code[10]; begin - // [SCENARIO 630806] Deleting a Subcontracting Order is blocked when an associated Transfer Order exists + // [SCENARIO] Check change Location Code by change Subcontracting Type in Prod Order Component + + // [GIVEN] Complete Setup of Manufacturing, include Work- and Machine Centers, Item Initialize(); - SubcontractingMgmtLibrary.UpdateManufacturingSetupWithSubcontractingLocation(); // [GIVEN] Some Parameters for Creation Subcontracting := true; UnitCostCalculation := UnitCostCalculation::Units; - // [GIVEN] Work and Machine Centers, an Item with Routing and Prod. BOM configured for Transfer subcontracting + // [GIVEN] CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); + + // [GIVEN] Create Item for Production include Routing and Prod. BOM CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); - UpdateProdBomAndRoutingWithRoutingLink(Item, WorkCenter[2]."No."); - SubcontractingMgmtLibrary.UpdateProdBomWithSubcontractingType(Item, "Subcontracting Type"::Transfer); - UpdateVendorWithSubcontractingLocationCode(WorkCenter[2]); - // [GIVEN] A Released Production Order (not created from a Purchase Order) - SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( - ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); - UpdateSubMgmtSetupWithReqWkshTemplate(); - SubcontractingMgmtLibrary.UpdateProdOrderCompWithLocationCode(ProductionOrder."No."); - SubcontractingMgmtLibrary.CreateTransferRoute(WorkCenter[2], ProductionOrder); + UpdateProdBomAndRoutingWithRoutingLink(Item, WorkCenter[2]."No."); - // [GIVEN] A Subcontracting Order created from the Production Order Routing - SubcontractingMgmtLibrary.CreateSubcontractingOrderFromProdOrderRtngPage(Item."Routing No.", WorkCenter[2]."No."); - PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); - PurchaseLine.SetRange("Prod. Order No.", ProductionOrder."No."); - PurchaseLine.FindFirst(); - PurchaseHeader.Get(PurchaseLine."Document Type", PurchaseLine."Document No."); - PurchaseOrderNo := PurchaseHeader."No."; + SubcontractingMgmtLibrary.UpdateVendorWithSubcontractingLocationCode(WorkCenter[2]); - // [GIVEN] A Transfer Order created from the Subcontracting Order - PurchaseHeaderPage.OpenView(); - PurchaseHeaderPage.GoToRecord(PurchaseHeader); - PurchaseHeaderPage.CreateTransfOrdToSubcontractor.Invoke(); + SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( + ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); - // [WHEN] The Subcontracting Order is attempted to be deleted - asserterror PurchaseHeader.Delete(true); + // [WHEN] Get actual Location Code and Change Subcontracting Type + ProdOrderComp.SetRange("Prod. Order No.", ProductionOrder."No."); + ProdOrderComp.SetFilter("Routing Link Code", '<>%1', ''); + ProdOrderComp.FindFirst(); + ActualLocationCode := ProdOrderComp."Location Code"; + ProdOrderComp.Validate("Subcontracting Type", ProdOrderComp."Subcontracting Type"::Purchase); + ProdOrderComp.Modify(); - // [THEN] An error is raised and the Transfer Order still exists - TransferHeader.SetRange("Subcontr. Purch. Order No.", PurchaseOrderNo); - Assert.RecordIsNotEmpty(TransferHeader); + // [THEN] Check if Component Location differs from Origin Location Code ==> Vendor Subcontracting Location Code + Assert.AreNotEqual(ActualLocationCode, ProdOrderComp."Location Code", ''); end; [Test] [HandlerFunctions('ConfirmHandler')] - procedure TestCreationOfPurchOrderFromRtngLineWithSubcontractor() + procedure TestCheckSubcontractorPriceInFactbox() var Item: Record Item; MachineCenter: array[2] of Record "Machine Center"; ProductionOrder: Record "Production Order"; PurchaseLine: Record "Purchase Line"; + SubcontractorPrice: Record "Subcontractor Price"; WorkCenter: array[2] of Record "Work Center"; + SubPurchaseLineFactbox: TestPage "Subc. Purchase Line Factbox"; begin // [SCENARIO] Create Subcontracting Purchase Order directly from Prod. Order Routing Line + // Check if No of SubcontractorPrices is displayed // [GIVEN] Complete Setup of Manufacturing, include Work- and Machine Centers, Item Initialize(); @@ -244,6 +182,8 @@ codeunit 139989 "Subc. Subcontracting Test" // [GIVEN] Create Item for Production include Routing and Prod. BOM CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); + SubcontractingMgmtLibrary.CreateSubcontractorPrice(Item, WorkCenter[2]."No.", SubcontractorPrice); + SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); @@ -255,101 +195,92 @@ codeunit 139989 "Subc. Subcontracting Test" // [THEN] Check if Purchase Line exists PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); PurchaseLine.SetRange("Prod. Order No.", ProductionOrder."No."); +#pragma warning disable AA0210 PurchaseLine.SetRange("Work Center No.", WorkCenter[2]."No."); - Assert.AreEqual(false, PurchaseLine.IsEmpty(), ''); +#pragma warning restore AA0210 + PurchaseLine.FindFirst(); + + SubPurchaseLineFactbox.OpenView(); + SubPurchaseLineFactbox.GoToRecord(PurchaseLine); + Assert.AreEqual(SubPurchaseLineFactbox.SubcontractingPrices.Value, Format(SubcontractorPrice.Count()), ''); end; [Test] - procedure CreateSubcOrderFromRtngLineEmptyDefVATProdPostGrp() + [Scope('OnPrem')] + procedure DeleteWorkCenterWithPricesDeletesRelatedPrices() var - GenProductPostingGroup: Record "Gen. Product Posting Group"; Item: Record Item; - MachineCenter: array[2] of Record "Machine Center"; - ProductionOrder: Record "Production Order"; - ProdOrderRoutingLine: Record "Prod. Order Routing Line"; - PurchaseLine: Record "Purchase Line"; - VATPostingSetup: Record "VAT Posting Setup"; - Vendor: Record Vendor; - WorkCenter: array[2] of Record "Work Center"; - SubcPurchaseOrderCreator: Codeunit "Subc. Purchase Order Creator"; - OriginalDefVATProdPostGrp: Code[20]; + SubcontractorPrice: Record "Subcontractor Price"; + WorkCenter: Record "Work Center"; + WorkCenterNo: Code[20]; begin - // [SCENARIO 618715] Creating a Subcontracting Purchase Order from Prod. Order Routing Line - // should succeed even when "Def. VAT Prod. Posting Group" is empty on the Gen. Product Posting Group - // (US/Sales Tax localization). + // [SCENARIO 620643] Deleting a Work Center deletes all associated Subcontractor Prices - // [GIVEN] Complete Setup of Manufacturing, include Work- and Machine Centers, Item + // [GIVEN] A work center with a subcontractor and multiple Subcontractor Prices Initialize(); + LibraryMfgManagement.CreateWorkCenterWithCalendar(WorkCenter, 0); + WorkCenter.Validate("Subcontractor No.", LibraryMfgManagement.CreateSubcontractorWithCurrency('')); + WorkCenter.Modify(true); + LibraryInventory.CreateItem(Item); + WorkCenterNo := WorkCenter."No."; + SubcontractingMgmtLibrary.CreateSubContractingPrice(SubcontractorPrice, WorkCenterNo, WorkCenter."Subcontractor No.", Item."No.", '', '', WorkDate(), '', 0, ''); + SubcontractingMgmtLibrary.CreateSubContractingPrice(SubcontractorPrice, WorkCenterNo, WorkCenter."Subcontractor No.", Item."No.", '', '', WorkDate(), '', 10, ''); - // [GIVEN] Some Parameters for Creation - Subcontracting := true; - UnitCostCalculation := UnitCostCalculation::Units; - - // [GIVEN] Create subcontracting Work Center (sets Def. VAT Prod. Posting Group during creation) - CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); - - // [GIVEN] Create Item for Production include Routing and Prod. BOM - CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); - - SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( - ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); - - UpdateSubMgmtSetupWithReqWkshTemplate(); + // [WHEN] The work center is deleted + WorkCenter.Delete(true); - // [GIVEN] Clear "Def. VAT Prod. Posting Group" on the Work Center's Gen. Product Posting Group - // to simulate US/Sales Tax localization where this field is intentionally empty. - // Done after all other setup to avoid committing the change during item/production order creation. - GenProductPostingGroup.Get(WorkCenter[2]."Gen. Prod. Posting Group"); - OriginalDefVATProdPostGrp := GenProductPostingGroup."Def. VAT Prod. Posting Group"; - GenProductPostingGroup."Def. VAT Prod. Posting Group" := ''; - GenProductPostingGroup.Modify(); + // [THEN] All Subcontractor Prices for the work center are deleted + SubcontractorPrice.SetRange("Work Center No.", WorkCenterNo); + Assert.IsTrue(SubcontractorPrice.IsEmpty(), 'Subcontractor prices must be deleted when work center is deleted'); + end; - // [GIVEN] Create a VAT Posting Setup for the empty VAT Prod. Posting Group - // so the downstream purchase line validation can find a matching setup - Vendor.Get(WorkCenter[2]."Subcontractor No."); - if not VATPostingSetup.Get(Vendor."VAT Bus. Posting Group", '') then begin - VATPostingSetup.Init(); - VATPostingSetup."VAT Bus. Posting Group" := Vendor."VAT Bus. Posting Group"; - VATPostingSetup."VAT Prod. Posting Group" := ''; - VATPostingSetup."VAT Calculation Type" := VATPostingSetup."VAT Calculation Type"::"Normal VAT"; - VATPostingSetup."VAT %" := 0; - VATPostingSetup.Insert(); - end; + [Test] + [Scope('OnPrem')] + procedure DeleteItemWithPricesDeletesRelatedPrices() + var + Item: Record Item; + SubcontractorPrice: Record "Subcontractor Price"; + WorkCenter: Record "Work Center"; + ItemNo: Code[20]; + begin + // [SCENARIO 620643] Deleting an Item deletes all associated Subcontractor Prices - // [WHEN] Create Subcontracting Purchase Order from Prod. Order Routing Line - ProdOrderRoutingLine.SetRange("Routing No.", Item."Routing No."); - ProdOrderRoutingLine.SetRange("Work Center No.", WorkCenter[2]."No."); - ProdOrderRoutingLine.FindFirst(); - SubcPurchaseOrderCreator.CreateSubcontractingPurchaseOrderFromRoutingLine(ProdOrderRoutingLine); + // [GIVEN] An item with multiple Subcontractor Prices + Initialize(); + LibraryMfgManagement.CreateWorkCenterWithCalendar(WorkCenter, 0); + WorkCenter.Validate("Subcontractor No.", LibraryMfgManagement.CreateSubcontractorWithCurrency('')); + WorkCenter.Modify(true); + LibraryInventory.CreateItem(Item); + ItemNo := Item."No."; + SubcontractingMgmtLibrary.CreateSubContractingPrice(SubcontractorPrice, WorkCenter."No.", WorkCenter."Subcontractor No.", ItemNo, '', '', WorkDate(), '', 0, ''); + SubcontractingMgmtLibrary.CreateSubContractingPrice(SubcontractorPrice, WorkCenter."No.", WorkCenter."Subcontractor No.", ItemNo, '', '', WorkDate(), '', 10, ''); - // [THEN] Purchase Line is created successfully - PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); - PurchaseLine.SetRange("Prod. Order No.", ProductionOrder."No."); - PurchaseLine.SetRange("Work Center No.", WorkCenter[2]."No."); - Assert.AreEqual(false, PurchaseLine.IsEmpty(), 'Purchase Line should be created even when Def. VAT Prod. Posting Group is empty.'); + // [WHEN] The item is deleted + Item.Delete(true); - // [TEARDOWN] Restore original Def. VAT Prod. Posting Group to prevent contaminating other tests - GenProductPostingGroup.Get(WorkCenter[2]."Gen. Prod. Posting Group"); - GenProductPostingGroup."Def. VAT Prod. Posting Group" := OriginalDefVATProdPostGrp; - GenProductPostingGroup.Modify(); + // [THEN] All Subcontractor Prices for the item are deleted + SubcontractorPrice.SetRange("Item No.", ItemNo); + Assert.IsTrue(SubcontractorPrice.IsEmpty(), 'Subcontractor prices must be deleted when item is deleted'); end; [Test] - [HandlerFunctions('ConfirmHandler')] - procedure TestCreationOfPurchOrderFromRtngLineWithSubcontractorWithAddLine() + [HandlerFunctions('ConfirmHandler,SubcontrDispatchingListDefaultRequestPageHandler')] + procedure TestSubcontrDispatchingList() var Item: Record Item; MachineCenter: array[2] of Record "Machine Center"; - ProductionBOMLine: Record "Production BOM Line"; ProductionOrder: Record "Production Order"; + PurchaseHeader: Record "Purchase Header"; PurchaseLine: Record "Purchase Line"; WorkCenter: array[2] of Record "Work Center"; + LibraryReportDataset: Codeunit "Library - Report Dataset"; + XmlParameters: Text; begin - // [SCENARIO] Create Subcontracting Purchase Order directly from Prod. Order Routing Line - // [SCENARIO] and Transfer additional Line with marked Component; + // [SCENARIO] Create Subcontracting and check Subcontr Dispatching List // [GIVEN] Complete Setup of Manufacturing, include Work- and Machine Centers, Item Initialize(); + SubcontractingMgmtLibrary.SetupInventorySetup(); // [GIVEN] Some Parameters for Creation Subcontracting := true; @@ -363,7 +294,7 @@ codeunit 139989 "Subc. Subcontracting Test" UpdateProdBomAndRoutingWithRoutingLink(Item, WorkCenter[2]."No."); - SubcontractingMgmtLibrary.UpdateProdBomWithSubcontractingType(Item, "Subcontracting Type"::Purchase); + SubcontractingMgmtLibrary.UpdateProdBomWithSubcontractingType(Item, "Subcontracting Type"::Transfer); SubcontractingMgmtLibrary.UpdateVendorWithSubcontractingLocationCode(WorkCenter[2]); @@ -372,39 +303,50 @@ codeunit 139989 "Subc. Subcontracting Test" UpdateSubMgmtSetupWithReqWkshTemplate(); + SubcontractingMgmtLibrary.UpdateProdOrderCompWithLocationCode(ProductionOrder."No."); + // [WHEN] Create Subcontracting Purchase Order from Prod. Order Routing SubcontractingMgmtLibrary.CreateSubcontractingOrderFromProdOrderRtngPage(Item."Routing No.", WorkCenter[2]."No."); - // [THEN] Check if Purchase Line with additional Component for Subcontracting Type exists - ProductionBOMLine.SetRange("Production BOM No.", Item."Production BOM No."); -#pragma warning disable AA0210 - ProductionBOMLine.SetRange("Subcontracting Type", ProductionBOMLine."Subcontracting Type"::Purchase); -#pragma warning restore AA0210 - ProductionBOMLine.FindFirst(); - + // [WHEN] Create Transfer Order PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); - PurchaseLine.SetRange(Type, PurchaseLine.Type::Item); - PurchaseLine.SetRange("No.", ProductionBOMLine."No."); - Assert.AreEqual(false, PurchaseLine.IsEmpty(), ''); + PurchaseLine.SetRange("Prod. Order No.", ProductionOrder."No."); + PurchaseLine.FindFirst(); + + PurchaseHeader.SetRange("Document Type", PurchaseLine."Document Type"::Order); + PurchaseHeader.SetRange("No.", PurchaseLine."Document No."); + PurchaseHeader.FindFirst(); + + // [THEN] Print Subcontr Dispatching List + PurchaseHeader.SetRecFilter(); + XmlParameters := Report.RunRequestPage(Report::"Subc. Dispatching List"); + LibraryReportDataset.RunReportAndLoad(Report::"Subc. Dispatching List", PurchaseHeader, XmlParameters); + // [THEN] the company address line is blank + LibraryReportDataset.AssertElementWithValueExists('SubcAddrInfoLine', ''); + // [THEN] an exemplary footer element is blank + LibraryReportDataset.AssertElementWithValueExists('SubcCompanyAddress1', ''); + LibraryReportDataset.AssertElementWithValueExists('Prod__Order_Routing_Line__Prod__Order_No__', ProductionOrder."No."); end; [Test] - [HandlerFunctions('ConfirmHandler')] - procedure TestCreationOfSubcontractingPurchOrderFromRtngLineWithAddInfoLine() + procedure TestTransferSubcontractingTypeAndVendorLocationIntoPlanningComponent() var + Customer: Record Customer; Item: Record Item; + Location: Record Location; MachineCenter: array[2] of Record "Machine Center"; - ProdOrderLine: Record "Prod. Order Line"; - ProdOrderRtngLine: Record "Prod. Order Routing Line"; - ProductionOrder: Record "Production Order"; - PurchaseLine: Record "Purchase Line"; + PlanningComponent: Record "Planning Component"; + ProductionBOMLine: Record "Production BOM Line"; + SalesHeader: Record "Sales Header"; + SalesLine: Record "Sales Line"; + Vendor: Record Vendor; WorkCenter: array[2] of Record "Work Center"; begin - // [SCENARIO] Create Subcontracting Purchase Order directly from Prod. Order Routing Line - // [SCENARIO] and Transfer additional Information Line; + // [SCENARIO] Create Sales Order and test Planning Component // [GIVEN] Complete Setup of Manufacturing, include Work- and Machine Centers, Item Initialize(); + SubcontractingMgmtLibrary.SetupInventorySetup(); // [GIVEN] Some Parameters for Creation Subcontracting := true; @@ -415,2283 +357,440 @@ codeunit 139989 "Subc. Subcontracting Test" // [GIVEN] Create Item for Production include Routing and Prod. BOM CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); + Item."Reordering Policy" := "Reordering Policy"::Order; + Item.Modify(); - SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( - ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); + UpdateProdBomAndRoutingWithRoutingLink(Item, WorkCenter[2]."No."); - UpdateSubMgmtSetupWithReqWkshTemplate(); - UpdateSubMgmtSetupTransferInfoLine(true); + SubcontractingMgmtLibrary.UpdateProdBomWithSubcontractingType(Item, "Subcontracting Type"::InventoryByVendor); - // [WHEN] Create Subcontracting Purchase Order from Prod. Order Routing - SubcontractingMgmtLibrary.CreateSubcontractingOrderFromProdOrderRtngPage(Item."Routing No.", WorkCenter[2]."No."); + SubcontractingMgmtLibrary.UpdateVendorWithSubcontractingLocationCode(WorkCenter[2]); - // [THEN] Check if Purchase Line with Additional Information Exists - PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); - PurchaseLine.SetRange("Prod. Order No.", ProductionOrder."No."); - PurchaseLine.FindFirst(); - PurchaseLine.SetRange("Document No.", PurchaseLine."Document No."); - PurchaseLine.SetRange("Prod. Order No."); - PurchaseLine.SetRange(Type, PurchaseLine.Type::" "); - PurchaseLine.FindFirst(); + LibrarySales.CreateCustomer(Customer); + LibraryWarehouse.CreateLocationWithInventoryPostingSetup(Location); + LibrarySales.CreateSalesDocumentWithItem(SalesHeader, SalesLine, "Sales Document Type"::Order, Customer."No.", Item."No.", 5, Location.Code, WorkDate()); - ProdOrderRtngLine.SetRange("Routing No.", Item."Routing No."); - ProdOrderRtngLine.SetRange("Work Center No.", WorkCenter[2]."No."); - ProdOrderRtngLine.FindFirst(); - ProdOrderLine.Get(ProdOrderLine.Status::Released, ProdOrderRtngLine."Prod. Order No.", ProdOrderRtngLine."Routing Reference No."); + // [WHEN] + LibraryPlanning.CalcRegenPlanForPlanWksh(Item, CalcDate('<-1M>', WorkDate()), CalcDate('<+1M>', WorkDate())); - Assert.AreEqual(ProdOrderLine.Description, PurchaseLine.Description, ''); + ProductionBOMLine.SetRange("Production BOM No.", Item."Production BOM No."); + ProductionBOMLine.FindLast(); + PlanningComponent.SetRange("Item No.", ProductionBOMLine."No."); + PlanningComponent.FindFirst(); - // [TEARDOWN] - UpdateSubMgmtSetupTransferInfoLine(false); + // [THEN] + Assert.Equal(ProductionBOMLine."Subcontracting Type", PlanningComponent."Subcontracting Type"); + Vendor.Get(WorkCenter[2]."Subcontractor No."); + Assert.Equal(Vendor."Subc. Location Code", PlanningComponent."Location Code"); end; [Test] - procedure TestTransferOfSubcontractingTypeProdBOMLineToProdOrderComp() + procedure PurchaseSubcTypeProdOrderCompExcludedFromPlanning() var + ComponentItem: Record Item; Item: Record Item; + Location: Record Location; MachineCenter: array[2] of Record "Machine Center"; + ProdOrderComp: Record "Prod. Order Component"; ProductionBOMLine: Record "Production BOM Line"; ProductionOrder: Record "Production Order"; + RequisitionLine: Record "Requisition Line"; WorkCenter: array[2] of Record "Work Center"; begin - // [SCENARIO] Check Transfer of Subcontracting Type from Production BOM Line to Prod Order Component + // [SCENARIO 630597] Prod. Order Components with Subcontracting Type "Purchase" should be + // excluded from planning engines because they will be purchased later via the subcontracting + // purchase order. // [GIVEN] Complete Setup of Manufacturing, include Work- and Machine Centers, Item Initialize(); + SubcontractingMgmtLibrary.SetupInventorySetup(); // [GIVEN] Some Parameters for Creation Subcontracting := true; UnitCostCalculation := UnitCostCalculation::Units; - // [GIVEN] + // [GIVEN] Create subcontracting Work/Machine Centers CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); - // [GIVEN] Create Item for Production include Routing and Prod. BOM + // [GIVEN] Create Item for Production include Routing and Prod. BOM (2 component items) CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); + // [GIVEN] Assign Routing Link Code between subcontracting routing line and last BOM line UpdateProdBomAndRoutingWithRoutingLink(Item, WorkCenter[2]."No."); + // [GIVEN] Set Subcontracting Type = Purchase on the linked BOM line SubcontractingMgmtLibrary.UpdateProdBomWithSubcontractingType(Item, "Subcontracting Type"::Purchase); + // [GIVEN] Set up vendor with subcontracting location SubcontractingMgmtLibrary.UpdateVendorWithSubcontractingLocationCode(WorkCenter[2]); - // [WHEN] Creating Production Order to Transfer Information - SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( - ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); - - // [THEN] Check if Production BOM Line with additional Component for Subcontracting Type exists + // [GIVEN] Set component item reordering policy to Lot-for-Lot (already done during creation) + // [GIVEN] Create inventory for the component item so planning considers it ProductionBOMLine.SetRange("Production BOM No.", Item."Production BOM No."); - ProductionBOMLine.SetRange("Subcontracting Type", ProductionBOMLine."Subcontracting Type"::Purchase); - Assert.RecordIsNotEmpty(ProductionBOMLine); - end; - - [Test] - [HandlerFunctions('ConfirmHandler,HandleTransferOrder')] - procedure TestCreationOfSubcontrTransferOrderFromSubcontrPurchOrder() - var - Item: Record Item; - MachineCenter: array[2] of Record "Machine Center"; - ProdOrderComp: Record "Prod. Order Component"; - ProductionOrder: Record "Production Order"; - PurchaseHeader: Record "Purchase Header"; - PurchaseLine: Record "Purchase Line"; - TransferLine: Record "Transfer Line"; - WorkCenter: array[2] of Record "Work Center"; - PurchaseHeaderPage: TestPage "Purchase Order"; - begin - // [SCENARIO] Create Subcontracting Transfer Order directly from Subcontracting Purchase Order - // [SCENARIO] and Transfer additional Line with marked Component ; - - // [GIVEN] Complete Setup of Manufacturing, include Work- and Machine Centers, Item - Initialize(); - SubcontractingMgmtLibrary.UpdateManufacturingSetupWithSubcontractingLocation(); - SubcontractingMgmtLibrary.SetupInventorySetup(); - - // [GIVEN] Some Parameters for Creation - Subcontracting := true; - UnitCostCalculation := UnitCostCalculation::Units; - - // [GIVEN] - CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); - - // [GIVEN] Create Item for Production include Routing and Prod. BOM - CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); - - UpdateProdBomAndRoutingWithRoutingLink(Item, WorkCenter[2]."No."); - - SubcontractingMgmtLibrary.UpdateProdBomWithSubcontractingType(Item, "Subcontracting Type"::Transfer); - - SubcontractingMgmtLibrary.UpdateVendorWithSubcontractingLocationCode(WorkCenter[2]); + ProductionBOMLine.FindLast(); + ComponentItem.Get(ProductionBOMLine."No."); + LibraryWarehouse.CreateLocationWithInventoryPostingSetup(Location); + // [GIVEN] Create and refresh Released Production Order SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( - ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); - - UpdateSubMgmtSetupWithReqWkshTemplate(); - - SubcontractingMgmtLibrary.UpdateProdOrderCompWithLocationCode(ProductionOrder."No."); - SubcontractingMgmtLibrary.CreateTransferRoute(WorkCenter[2], ProductionOrder); - - // [WHEN] Create Subcontracting Purchase Order from Prod. Order Routing - SubcontractingMgmtLibrary.CreateSubcontractingOrderFromProdOrderRtngPage(Item."Routing No.", WorkCenter[2]."No."); + ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); - // [WHEN] Create Transfer Order - PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); - PurchaseLine.SetRange("Prod. Order No.", ProductionOrder."No."); - PurchaseLine.FindFirst(); + // [GIVEN] Verify prod. order component with Purchase subcontracting type exists + ProdOrderComp.SetRange("Prod. Order No.", ProductionOrder."No."); + ProdOrderComp.SetRange("Item No.", ComponentItem."No."); + ProdOrderComp.SetRange("Subcontracting Type", "Subcontracting Type"::Purchase); + Assert.RecordIsNotEmpty(ProdOrderComp); - PurchaseHeader.SetRange("Document Type", PurchaseLine."Document Type"::Order); - PurchaseHeader.SetRange("No.", PurchaseLine."Document No."); - PurchaseHeader.FindFirst(); - PurchaseHeaderPage.OpenView(); - PurchaseHeaderPage.GoToRecord(PurchaseHeader); - PurchaseHeaderPage.CreateTransfOrdToSubcontractor.Invoke(); + // [WHEN] Run Regenerative Plan for the component item + ComponentItem.SetRecFilter(); + LibraryPlanning.CalcRegenPlanForPlanWksh(ComponentItem, CalcDate('<-1M>', WorkDate()), CalcDate('<+1M>', WorkDate())); - // [THEN] Check if Purchase Line with additional Component for Subcontracting Type exists - ProdOrderComp.SetRange("Prod. Order No.", ProductionOrder."No."); -#pragma warning disable AA0210 - ProdOrderComp.SetRange("Subcontracting Type", ProdOrderComp."Subcontracting Type"::Transfer); -#pragma warning restore AA0210 - ProdOrderComp.FindFirst(); + // [THEN] No requisition line is suggested for the component with Purchase subcontracting type + RequisitionLine.SetRange("No.", ComponentItem."No."); + Assert.RecordIsEmpty(RequisitionLine); - TransferLine.SetRange("Subc. Prod. Order No.", ProductionOrder."No."); - TransferLine.SetRange("Item No.", ProdOrderComp."Item No."); + // [WHEN] Changing the Subcontracting Type to None and run planning again + UpdateProdOrderComponentWithSubcontractingType(ProductionOrder, "Subcontracting Type"::Empty); + LibraryPlanning.CalcRegenPlanForPlanWksh(ComponentItem, CalcDate('<-1M>', WorkDate()), CalcDate('<+1M>', WorkDate())); - Assert.RecordIsNotEmpty(TransferLine); + // [THEN] Requisition line is suggested for the component with None subcontracting type + RequisitionLine.SetRange("No.", ComponentItem."No."); + Assert.RecordIsNotEmpty(RequisitionLine); end; [Test] - [HandlerFunctions('ConfirmHandler,HandleTransferOrder')] - procedure TestLocationInSubContractorTransferOrderAndComponentLine() + [HandlerFunctions('ConfirmHandler')] + procedure SubcontractingFieldsPopulatedOnIleAfterSubcontractingPurchaseReceipt() var Item: Record Item; - MachineCenter: array[2] of Record "Machine Center"; - ProdOrderComp: Record "Prod. Order Component"; + ItemLedgerEntry: Record "Item Ledger Entry"; ProductionOrder: Record "Production Order"; PurchaseHeader: Record "Purchase Header"; PurchaseLine: Record "Purchase Line"; - TransferHeader: Record "Transfer Header"; - TransferLine: Record "Transfer Line"; - WorkCenter: array[2] of Record "Work Center"; - CompLocation, TransferFrom : Code[10]; - PurchaseHeaderPage: TestPage "Purchase Order"; + SubcWorkCenter: Record "Work Center"; begin - // [SCENARIO] Create Subcontracting Transfer Order directly from Subcontracting Purchase Order - // [SCENARIO] check if Component Line Location Code and Transfer Form Code are equal + // [SCENARIO] Bug 633292 - Output Item Ledger Entry created from posting a subcontracting purchase receipt should have the Subcontracting extension fields populated, so that the Production actions on the Item Ledger Entries page can resolve the linked production order, routing, and components. - // [GIVEN] Complete Setup of Manufacturing, include Work- and Machine Centers, Item + // [GIVEN] Subcontracting setup Initialize(); - SubcontractingMgmtLibrary.UpdateManufacturingSetupWithSubcontractingLocation(); - SubcontractingMgmtLibrary.SetupInventorySetup(); - - // [GIVEN] Some Parameters for Creation Subcontracting := true; UnitCostCalculation := UnitCostCalculation::Units; - // [GIVEN] - CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); - - // [GIVEN] Create Item for Production include Routing and Prod. BOM - CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); - - UpdateProdBomAndRoutingWithRoutingLink(Item, WorkCenter[2]."No."); - - SubcontractingMgmtLibrary.UpdateProdBomWithSubcontractingType(Item, "Subcontracting Type"::Transfer); - - SubcontractingMgmtLibrary.UpdateVendorWithSubcontractingLocationCode(WorkCenter[2]); - + // [GIVEN] Released Production Order whose only routing operation is a subcontracting one (so receiving the subcontracting PO posts the Output ILE) + CreateItemWithSingleSubcontractingOperation(Item, SubcWorkCenter); + SubcontractingMgmtLibrary.UpdateVendorWithSubcontractingLocationCode(SubcWorkCenter); SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( - ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); - + ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); UpdateSubMgmtSetupWithReqWkshTemplate(); - SubcontractingMgmtLibrary.UpdateProdOrderCompWithLocationCode(ProductionOrder."No."); - SubcontractingMgmtLibrary.CreateTransferRoute(WorkCenter[2], ProductionOrder); - - // [WHEN] Create Subcontracting Purchase Order from Prod. Order Routing - SubcontractingMgmtLibrary.CreateSubcontractingOrderFromProdOrderRtngPage(Item."Routing No.", WorkCenter[2]."No."); - - ProdOrderComp.SetRange("Prod. Order No.", ProductionOrder."No."); + // [GIVEN] Subcontracting Purchase Order created from the Prod. Order Routing line + SubcontractingMgmtLibrary.CreateSubcontractingOrderFromProdOrderRtngPage(Item."Routing No.", SubcWorkCenter."No."); + PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); + PurchaseLine.SetRange("Prod. Order No.", ProductionOrder."No."); #pragma warning disable AA0210 - ProdOrderComp.SetRange("Subcontracting Type", "Subcontracting Type"::Transfer); + PurchaseLine.SetRange("Work Center No.", SubcWorkCenter."No."); #pragma warning restore AA0210 - ProdOrderComp.FindFirst(); - - //[GIVEN] Keep Location Code for later Check - CompLocation := ProdOrderComp."Location Code"; - - // [WHEN] Create Transfer Order - PurchaseLine.SetRange("Document Type", "Purchase Document Type"::Order); - PurchaseLine.SetRange("Prod. Order No.", ProductionOrder."No."); PurchaseLine.FindFirst(); + PurchaseHeader.Get(PurchaseLine."Document Type", PurchaseLine."Document No."); + EnsureGeneralPostingSetupIsValid(PurchaseLine."Gen. Bus. Posting Group", PurchaseLine."Gen. Prod. Posting Group"); - PurchaseHeader.SetRange("Document Type", PurchaseLine."Document Type"::Order); - PurchaseHeader.SetRange("No.", PurchaseLine."Document No."); - PurchaseHeader.FindFirst(); - PurchaseHeaderPage.OpenView(); - PurchaseHeaderPage.GoToRecord(PurchaseHeader); - PurchaseHeaderPage.CreateTransfOrdToSubcontractor.Invoke(); - - TransferLine.SetRange("Subc. Prod. Order No.", ProductionOrder."No."); - TransferLine.SetRange("Item No.", ProdOrderComp."Item No."); - TransferLine.SetRange("Subc. Prod. Ord. Comp Line No.", ProdOrderComp."Line No."); - TransferLine.FindFirst(); - - TransferHeader.Get(TransferLine."Document No."); - //[GIVEN] Keep Location Code for later Check - TransferFrom := TransferHeader."Transfer-from Code"; - - // [THEN] Check if Component Location Code and Transfer Form Code are equal - Assert.AreEqual(CompLocation, TransferFrom, 'Transfer-from Code is not expected'); - end; - - [Test] - [HandlerFunctions('ConfirmHandler,HandleTransferOrder')] - procedure TestLocationInSubContractorTransferOrderAndComponentLineWithChangeCompLineLocation() - var - Item: Record Item; - Location: Record Location; - MachineCenter: array[2] of Record "Machine Center"; - ProdOrderComp: Record "Prod. Order Component"; - ProductionOrder: Record "Production Order"; - PurchaseHeader: Record "Purchase Header"; - PurchaseLine: Record "Purchase Line"; - TransferHeader: Record "Transfer Header"; - TransferLine: Record "Transfer Line"; - WorkCenter: array[2] of Record "Work Center"; - CompLocation, TransferFrom : Code[10]; - PurchaseHeaderPage: TestPage "Purchase Order"; - begin - // [SCENARIO] Create Subcontracting Transfer Order directly from Subcontracting Purchase Order - // [SCENARIO] Change Component Location Code and check if Component Line Location Code and Transfer Form Code are equal - - // [GIVEN] Complete Setup of Manufacturing, include Work- and Machine Centers, Item - Initialize(); - SubcontractingMgmtLibrary.UpdateManufacturingSetupWithSubcontractingLocation(); - SubcontractingMgmtLibrary.SetupInventorySetup(); - - // [GIVEN] Some Parameters for Creation - Subcontracting := true; - UnitCostCalculation := UnitCostCalculation::Units; - - // [GIVEN] - CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); - - // [GIVEN] Create Item for Production include Routing and Prod. BOM - CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); - - UpdateProdBomAndRoutingWithRoutingLink(Item, WorkCenter[2]."No."); - - SubcontractingMgmtLibrary.UpdateProdBomWithSubcontractingType(Item, "Subcontracting Type"::Transfer); - - SubcontractingMgmtLibrary.UpdateVendorWithSubcontractingLocationCode(WorkCenter[2]); - - SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( - ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); - - UpdateSubMgmtSetupWithReqWkshTemplate(); - - SubcontractingMgmtLibrary.UpdateProdOrderCompWithLocationCode(ProductionOrder."No."); - SubcontractingMgmtLibrary.CreateTransferRoute(WorkCenter[2], ProductionOrder); - - // [WHEN] Create Subcontracting Purchase Order from Prod. Order Routing - SubcontractingMgmtLibrary.CreateSubcontractingOrderFromProdOrderRtngPage(Item."Routing No.", WorkCenter[2]."No."); + // [WHEN] Receive the subcontracting purchase order + LibraryPurchase.PostPurchaseDocument(PurchaseHeader, true, false); - LibraryWarehouse.CreateLocationWithInventoryPostingSetup(Location); - ProdOrderComp.SetRange("Prod. Order No.", ProductionOrder."No."); -#pragma warning disable AA0210 - ProdOrderComp.SetRange("Subcontracting Type", "Subcontracting Type"::Transfer); -#pragma warning restore AA0210 - ProdOrderComp.FindFirst(); - ProdOrderComp."Location Code" := Location.Code; - ProdOrderComp.Modify(); + // [THEN] An Output Item Ledger Entry exists with Subcontracting extension fields populated from the source purchase line + ItemLedgerEntry.SetRange("Item No.", Item."No."); + ItemLedgerEntry.SetRange("Entry Type", ItemLedgerEntry."Entry Type"::Output); + ItemLedgerEntry.SetRange("Order Type", ItemLedgerEntry."Order Type"::Production); + ItemLedgerEntry.SetRange("Order No.", ProductionOrder."No."); + ItemLedgerEntry.FindFirst(); - //[GIVEN] Keep Location Code for later Check - CompLocation := ProdOrderComp."Location Code"; - - // [WHEN] Create Transfer Order - PurchaseLine.SetRange("Document Type", "Purchase Document Type"::Order); - PurchaseLine.SetRange("Prod. Order No.", ProductionOrder."No."); - PurchaseLine.FindFirst(); - - PurchaseHeader.SetRange("Document Type", PurchaseLine."Document Type"::Order); - PurchaseHeader.SetRange("No.", PurchaseLine."Document No."); - PurchaseHeader.FindFirst(); - PurchaseHeaderPage.OpenView(); - PurchaseHeaderPage.GoToRecord(PurchaseHeader); - PurchaseHeaderPage.CreateTransfOrdToSubcontractor.Invoke(); - - TransferLine.SetRange("Subc. Prod. Order No.", ProductionOrder."No."); - TransferLine.SetRange("Item No.", ProdOrderComp."Item No."); - TransferLine.SetRange("Subc. Prod. Ord. Comp Line No.", ProdOrderComp."Line No."); - TransferLine.FindFirst(); - - TransferHeader.Get(TransferLine."Document No."); - - //[GIVEN] Keep Location Code for later Check - TransferFrom := TransferHeader."Transfer-from Code"; - - // [THEN] Check if Component Location Code and Transfer Form Code are equal - Assert.AreEqual(CompLocation, TransferFrom, 'Transfer-from Code is not expected'); - end; - - [Test] - [HandlerFunctions('ConfirmHandler,HandleTransferOrder,HandleCreateTransferOrderMsg')] - procedure CheckTransferOrderFromSubcontrAndReturnTransferOrderFromSubcontractorPurchOrder() - var - Bin: Record Bin; - Item: Record Item; - Location: Record Location; - MachineCenter: array[2] of Record "Machine Center"; - ProdOrderComp: Record "Prod. Order Component"; - ProductionOrder: Record "Production Order"; - PurchaseHeader: Record "Purchase Header"; - PurchaseLine: Record "Purchase Line"; - TransferHeader: Record "Transfer Header"; - TransferLine: Record "Transfer Line"; - WorkCenter: array[2] of Record "Work Center"; - TransferFrom1, TransferFrom2, TransferTo1, TransferTo2 : Code[10]; - PurchaseHeaderPage: TestPage "Purchase Order"; - TransferOrder: TestPage "Transfer Order"; - begin - // [SCENARIO] Create Subcontracting Transfer Order directly from Subcontracting Purchase Order - // [SCENARIO] Post transfer Order and Create Return Transfer Order - // [SCENARIO] check if Transfer-from and Transfer-to Locations are reversed - - // [GIVEN] Complete Setup of Manufacturing, include Work- and Machine Centers, Item - Initialize(); - SubcontractingMgmtLibrary.UpdateManufacturingSetupWithSubcontractingLocation(); - SubcontractingMgmtLibrary.SetupInventorySetup(); - - // [GIVEN] Some Parameters for Creation - Subcontracting := true; - UnitCostCalculation := UnitCostCalculation::Units; - - // [GIVEN] - CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); - - // [GIVEN] Create Item for Production include Routing and Prod. BOM - CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); - - UpdateProdBomAndRoutingWithRoutingLink(Item, WorkCenter[2]."No."); - - SubcontractingMgmtLibrary.UpdateProdBomWithSubcontractingType(Item, "Subcontracting Type"::Transfer); - - SubcontractingMgmtLibrary.UpdateVendorWithSubcontractingLocationCode(WorkCenter[2]); - - SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( - ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); - - UpdateSubMgmtSetupWithReqWkshTemplate(); - - SubcontractingMgmtLibrary.UpdateProdOrderCompWithLocationCode(ProductionOrder."No."); - SubcontractingMgmtLibrary.CreateTransferRoute(WorkCenter[2], ProductionOrder); - - // [WHEN] Create Subcontracting Purchase Order from Prod. Order Routing - SubcontractingMgmtLibrary.CreateSubcontractingOrderFromProdOrderRtngPage(Item."Routing No.", WorkCenter[2]."No."); - - // [WHEN] Create Transfer Order - PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); - PurchaseLine.SetRange("No.", ProductionOrder."Source No."); - PurchaseLine.SetRange(Type, "Purchase Line Type"::Item); - PurchaseLine.SetRange("Prod. Order No.", ProductionOrder."No."); - PurchaseLine.FindFirst(); - - PurchaseHeader.SetRange("Document Type", PurchaseLine."Document Type"::Order); - PurchaseHeader.SetRange("No.", PurchaseLine."Document No."); - PurchaseHeader.FindFirst(); - PurchaseHeaderPage.OpenView(); - PurchaseHeaderPage.GoToRecord(PurchaseHeader); - PurchaseHeaderPage.CreateTransfOrdToSubcontractor.Invoke(); - - // [THEN] Check if Purchase Line with additional Component for Subcontracting Type exists - ProdOrderComp.SetRange("Prod. Order No.", ProductionOrder."No."); -#pragma warning disable AA0210 - ProdOrderComp.SetRange("Subcontracting Type", ProdOrderComp."Subcontracting Type"::Transfer); -#pragma warning restore AA0210 - ProdOrderComp.FindFirst(); - - TransferLine.SetRange("Subc. Prod. Order No.", ProductionOrder."No."); - TransferLine.SetRange("Item No.", ProdOrderComp."Item No."); - TransferLine.SetRange("Subc. Prod. Ord. Comp Line No.", ProdOrderComp."Line No."); - TransferLine.FindFirst(); - - TransferHeader.SetRange("No.", TransferLine."Document No."); - TransferHeader.FindFirst(); - - //[GIVEN]create Inventory for Transfer - Item.Get(ProdOrderComp."Item No."); - Location.Get(TransferHeader."Transfer-from Code"); - CreateInventory(Item, Location, Bin, ProdOrderComp."Expected Qty. (Base)"); - - //[GIVEN] Keep Transfer Locations values for later Check - TransferFrom1 := TransferHeader."Transfer-from Code"; - TransferTo1 := TransferHeader."Transfer-to Code"; - - //[GIVEN] Enable direct transfer for posting - TransferHeader.Validate("Direct Transfer", true); - TransferHeader.Modify(true); - - //[WHEN] Post Transfer Order - TransferOrder.OpenView(); - TransferOrder.GoToRecord(TransferHeader); - TransferOrder.Post.Invoke(); - - //[WHEN] Create Return Transfer Order - PurchaseHeaderPage.GoToRecord(PurchaseHeader); - PurchaseHeaderPage.CreateReturnFromSubcontractor.Invoke(); - - ProdOrderComp.SetRange("Prod. Order No.", ProductionOrder."No."); -#pragma warning disable AA0210 - ProdOrderComp.SetRange("Subcontracting Type", ProdOrderComp."Subcontracting Type"::Transfer); -#pragma warning restore AA0210 - ProdOrderComp.FindFirst(); - - TransferLine.SetRange("Subc. Prod. Order No.", ProductionOrder."No."); - TransferLine.SetRange("Item No.", ProdOrderComp."Item No."); - TransferLine.SetRange("Subc. Prod. Ord. Comp Line No.", ProdOrderComp."Line No."); - TransferLine.FindFirst(); - - TransferHeader.SetRange("No.", TransferLine."Document No."); - TransferHeader.FindFirst(); - - //[GIVEN] Keep Transfer Locations values for later Check - TransferFrom2 := TransferHeader."Transfer-from Code"; - TransferTo2 := TransferHeader."Transfer-to Code"; - - //[THEN] Check if Transfer-from and Transfer-to Locations are reversed - Assert.AreEqual(TransferFrom1, TransferTo2, 'Transfer-from and Transfer-to Locations are reversed'); - Assert.AreEqual(TransferTo1, TransferFrom2, 'Transfer-from and Transfer-to Locations are reversed'); - end; - - [Test] - procedure TestChangeLocationOnProdOrderCompWithSubcontractingTypePurchase() - var - Item: Record Item; - MachineCenter: array[2] of Record "Machine Center"; - ProdOrderComp: Record "Prod. Order Component"; - ProductionOrder: Record "Production Order"; - WorkCenter: array[2] of Record "Work Center"; - ActualLocationCode: Code[10]; - begin - // [SCENARIO] Check change Location Code by change Subcontracting Type in Prod Order Component - - // [GIVEN] Complete Setup of Manufacturing, include Work- and Machine Centers, Item - Initialize(); - - // [GIVEN] Some Parameters for Creation - Subcontracting := true; - UnitCostCalculation := UnitCostCalculation::Units; - - // [GIVEN] - CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); - - // [GIVEN] Create Item for Production include Routing and Prod. BOM - CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); - - UpdateProdBomAndRoutingWithRoutingLink(Item, WorkCenter[2]."No."); - - SubcontractingMgmtLibrary.UpdateVendorWithSubcontractingLocationCode(WorkCenter[2]); - - SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( - ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); - - // [WHEN] Get actual Location Code and Change Subcontracting Type - ProdOrderComp.SetRange("Prod. Order No.", ProductionOrder."No."); - ProdOrderComp.SetFilter("Routing Link Code", '<>%1', ''); - ProdOrderComp.FindFirst(); - ActualLocationCode := ProdOrderComp."Location Code"; - ProdOrderComp.Validate("Subcontracting Type", ProdOrderComp."Subcontracting Type"::Purchase); - ProdOrderComp.Modify(); - - // [THEN] Check if Component Location differs from Origin Location Code ==> Vendor Subcontracting Location Code - Assert.AreNotEqual(ActualLocationCode, ProdOrderComp."Location Code", ''); - end; - - [Test] - [HandlerFunctions('ConfirmHandler')] - procedure CheckGenPostGroupInSubContWorksheetAndSubConRoutingLine() - var - Item: Record Item; - Location: Record Location; - MachineCenter: array[2] of Record "Machine Center"; - ProdOrderRoutingLine: Record "Prod. Order Routing Line"; - ProductionOrder: Record "Production Order"; - PurchaseHeader: Record "Purchase Header"; - PurchaseLine: Record "Purchase Line"; - ReqWkshTemplate: Record "Req. Wksh. Template"; - RequisitionLine: Record "Requisition Line"; - RequisitionWkshName: Record "Requisition Wksh. Name"; - ManufacturingSetup: Record "Manufacturing Setup"; - Vendor: Record Vendor; - WorkCenter: array[2] of Record "Work Center"; - SubcCalculateSubContract: Report "Subc. Calculate Subcontracts"; - CarryOutActionMsgReq: Report "Carry Out Action Msg. - Req."; - GenBusPostingGroup1, GenBusPostingGroup2 : Code[20]; - ProdPostingGroup1, ProdPostingGroup2 : Code[20]; - VATBusPostingGroup1, VATBusPostingGroup2 : Code[20]; - VATProdPostingGroup1, VATProdPostingGroup2 : Code[20]; - ProdOrderRtng: TestPage "Prod. Order Routing"; - begin - // [SCENARIO] Check Gen. Prod. Posting Group value for Subcontracting Purchase Order - - // [GIVEN] Complete Setup of Manufacturing, include Work- and Machine Centers, Item - Initialize(); - - // [GIVEN] Some Parameters for Creation - Subcontracting := true; - UnitCostCalculation := UnitCostCalculation::Units; - UpdateSubMgmtSetupWithReqWkshTemplate(); - // [GIVEN] - CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); - - // [GIVEN] Create Item for Production include Routing and Prod. BOM - CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); - - UpdateProdBomAndRoutingWithRoutingLink(Item, WorkCenter[2]."No."); - - LibraryWarehouse.CreateLocationWithInventoryPostingSetup(Location); - Vendor.Get(WorkCenter[2]."Subcontractor No."); - WorkCenter2 := WorkCenter[2]; - WorkCenter2."Subcontractor No." := Vendor."No."; - Vendor."Subc. Location Code" := Location.Code; - LibraryWarehouse.CreateLocationWithInventoryPostingSetup(Location); - Vendor."Location Code" := Location.Code; - Vendor.Modify(); - - //[GIVEN] Create Production Order - SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( - ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); - - //[GIVEN] Create requisition worksheet template - SubcontractingMgmtLibrary.CreateReqWkshTemplateAndName(ReqWkshTemplate, RequisitionWkshName); - - //[GIVEN] create Purchase Order from Subcontracting Worksheet - RequisitionLine."Worksheet Template Name" := RequisitionWkshName."Worksheet Template Name"; - RequisitionLine."Journal Batch Name" := RequisitionWkshName.Name; - - SubcCalculateSubContract.SetWkShLine(RequisitionLine); - SubcCalculateSubContract.UseRequestPage(false); - SubcCalculateSubContract.RunModal(); - - RequisitionLine.SetRange("Worksheet Template Name", RequisitionWkshName."Worksheet Template Name"); - RequisitionLine.SetRange("Journal Batch Name", RequisitionWkshName.Name); -#pragma warning disable AA0210 - RequisitionLine.SetRange("Prod. Order No.", ProductionOrder."No."); -#pragma warning restore AA0210 - RequisitionLine.FindFirst(); - - Assert.AreEqual(ProductionOrder."No.", RequisitionLine."Prod. Order No.", 'Prod. Order No. has not found'); - - CarryOutActionMsgReq.SetReqWkshLine(RequisitionLine); - CarryOutActionMsgReq.UseRequestPage(false); - CarryOutActionMsgReq.RunModal(); - - PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); - PurchaseLine.SetRange("No.", ProductionOrder."Source No."); - PurchaseLine.SetRange(Type, "Purchase Line Type"::Item); - PurchaseLine.SetRange("Prod. Order No.", ProductionOrder."No."); - PurchaseLine.FindFirst(); - - //[GIVEN] Keep Posting Groups values for later Check - ProdPostingGroup1 := PurchaseLine."Gen. Prod. Posting Group"; - GenBusPostingGroup1 := PurchaseLine."Gen. Bus. Posting Group"; - VATBusPostingGroup1 := PurchaseLine."VAT Bus. Posting Group"; - VATProdPostingGroup1 := PurchaseLine."VAT Prod. Posting Group"; - - PurchaseHeader.Get(PurchaseLine."Document Type", PurchaseLine."Document No."); - - //[GIVEN] Delete Purchase Order - PurchaseHeader.Delete(true); - Commit(); - - ManufacturingSetup.Get(); - ManufacturingSetup."Subcontracting Template Name" := RequisitionLine."Worksheet Template Name"; - ManufacturingSetup."Subcontracting Batch Name" := RequisitionLine."Journal Batch Name"; - ManufacturingSetup.Modify(); - - // [GIVEN] Create Subcontracting Purchase Order from Prod. Order Routing - WorkCenter2 := WorkCenter[2]; - WorkCenter2."Subcontractor No." := Vendor."No."; - WorkCenter2.Modify(); - ProdOrderRoutingLine.SetRange("Prod. Order No.", ProductionOrder."No."); - ProdOrderRoutingLine.SetRange("Work Center No.", WorkCenter[2]."No."); - ProdOrderRoutingLine.FindFirst(); - ProdOrderRtng.OpenView(); - ProdOrderRtng.GoToRecord(ProdOrderRoutingLine); - ProdOrderRtng.CreateSubcontracting.Invoke(); - - PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); - PurchaseLine.SetRange("No.", ProductionOrder."Source No."); - PurchaseLine.SetRange(Type, "Purchase Line Type"::Item); - PurchaseLine.SetRange("Prod. Order No.", ProductionOrder."No."); - PurchaseLine.FindFirst(); - - //[GIVEN] Keep Posting Groups values for later Check - ProdPostingGroup2 := PurchaseLine."Gen. Prod. Posting Group"; - GenBusPostingGroup2 := PurchaseLine."Gen. Bus. Posting Group"; - VATBusPostingGroup2 := PurchaseLine."VAT Bus. Posting Group"; - VATProdPostingGroup2 := PurchaseLine."VAT Prod. Posting Group"; - - //[THEN] Check if Posting Groups values is the same as Standard - Assert.AreEqual(ProdPostingGroup1, ProdPostingGroup2, 'Gen. Prod. Posting Group is not Expected'); - Assert.AreEqual(GenBusPostingGroup1, GenBusPostingGroup2, 'Gen. Bus. Posting Group is not Expected'); - Assert.AreEqual(VATBusPostingGroup1, VATBusPostingGroup2, 'VAT Bus. Posting Group is not Expected'); - Assert.AreEqual(VATProdPostingGroup1, VATProdPostingGroup2, 'VAT Prod. Posting Group'); - end; - - [Test] - [HandlerFunctions('ConfirmHandler')] - procedure TestTransferProdOrderRtngCommentByCreationOfSubcontrPurchOrder() - var - Item: Record Item; - MachineCenter: array[2] of Record "Machine Center"; - ProdOrderRtngLine: Record "Prod. Order Routing Line"; - ProductionOrder: Record "Production Order"; - PurchCommentLine: Record "Purch. Comment Line"; - PurchaseLine: Record "Purchase Line"; - WorkCenter: array[2] of Record "Work Center"; - ReleasedProdOrderRtng: TestPage "Prod. Order Routing"; - begin - // [SCENARIO] Create Subcontracting Purchase Order - // [SCENARIO] and test Transfer of Prod Order Rtng. Comment to PurchLine HTML Text; - - // [GIVEN] Complete Setup of Manufacturing, include Work- and Machine Centers, Item - Initialize(); - - // [GIVEN] Some Parameters for Creation - Subcontracting := true; - UnitCostCalculation := UnitCostCalculation::Units; - // [GIVEN] - CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); - - // [GIVEN] Create Item for Production include Routing and Prod. BOM - CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); - - UpdateProdBomAndRoutingWithRoutingLink(Item, WorkCenter[2]."No."); - - SubcontractingMgmtLibrary.UpdateProdBomWithSubcontractingType(Item, "Subcontracting Type"::Transfer); - - SubcontractingMgmtLibrary.UpdateVendorWithSubcontractingLocationCode(WorkCenter[2]); - - SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( - ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); - - UpdateSubMgmtSetupWithReqWkshTemplate(); - - SubcontractingMgmtLibrary.UpdateProdOrderCompWithLocationCode(ProductionOrder."No."); - - // Create Comment Line - ProdOrderRtngLine.SetRange(Status, ProdOrderRtngLine.Status::Released); - ProdOrderRtngLine.SetRange("Prod. Order No.", ProductionOrder."No."); - ProdOrderRtngLine.SetRange("Routing No.", Item."Routing No."); - ProdOrderRtngLine.SetRange("Work Center No.", WorkCenter[2]."No."); - ProdOrderRtngLine.FindFirst(); - LibraryMfgManagement.CreateProdOrderRtngCommentLine(ProdOrderRtngLine.Status, ProdOrderRtngLine."Prod. Order No.", ProdOrderRtngLine."Routing Reference No.", ProdOrderRtngLine."Routing No.", ProdOrderRtngLine."Operation No."); - - // [WHEN] Create Subcontracting Purchase Order from Prod. Order Routing - ReleasedProdOrderRtng.OpenView(); - ReleasedProdOrderRtng.GoToRecord(ProdOrderRtngLine); - ReleasedProdOrderRtng.CreateSubcontracting.Invoke(); - - // [THEN] Get transferred Rtng Comment Text - PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); - PurchaseLine.SetRange("Operation No.", ProdOrderRtngLine."Operation No."); - PurchaseLine.SetRange("Prod. Order No.", ProdOrderRtngLine."Prod. Order No."); - PurchaseLine.SetRange("Prod. Order Line No.", ProdOrderRtngLine."Routing Reference No."); - PurchaseLine.FindLast(); - - PurchCommentLine.SetRange("Document Type", PurchaseLine."Document Type"); - PurchCommentLine.SetRange("No.", PurchaseLine."Document No."); - PurchCommentLine.SetRange("Document Line No.", PurchaseLine."Line No."); - - Assert.IsFalse(PurchaseLine.IsEmpty(), 'Purchase Comment Line must be filled'); - - // [TEARDOWN] - end; - - [Test] - [HandlerFunctions('ConfirmHandler,HandleTransferOrder')] - procedure TestExpectedErrorOnChangingLocationCodeInProdOrderCompWithTransferOrderFromSubcontrPurchOrder() - var - Item: Record Item; - Location: Record Location; - MachineCenter: array[2] of Record "Machine Center"; - ProdOrderComp: Record "Prod. Order Component"; - ProductionOrder: Record "Production Order"; - PurchaseHeader: Record "Purchase Header"; - PurchaseLine: Record "Purchase Line"; - TransferLine: Record "Transfer Line"; - WorkCenter: array[2] of Record "Work Center"; - ProdOrderCompPage: TestPage "Prod. Order Components"; - PurchaseHeaderPage: TestPage "Purchase Order"; - begin - // [SCENARIO] Create Subcontracting Transfer Order directly from Subcontracting Purchase Order - // [SCENARIO] and Transfer additional Line with marked Component ; - // [SCENARIO] Expected Error on changing Location Code in Prod. Order Component - - // [GIVEN] Complete Setup of Manufacturing, include Work- and Machine Centers, Item - Initialize(); - SubcontractingMgmtLibrary.UpdateManufacturingSetupWithSubcontractingLocation(); - // [GIVEN] Some Parameters for Creation - Subcontracting := true; - UnitCostCalculation := UnitCostCalculation::Units; - - // [GIVEN] - CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); - LibraryWarehouse.CreateLocation(Location); - - // [GIVEN] Create Item for Production include Routing and Prod. BOM - CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); - - UpdateProdBomAndRoutingWithRoutingLink(Item, WorkCenter[2]."No."); - - SubcontractingMgmtLibrary.UpdateProdBomWithSubcontractingType(Item, "Subcontracting Type"::Transfer); - - SubcontractingMgmtLibrary.UpdateVendorWithSubcontractingLocationCode(WorkCenter[2]); - - SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( - ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); - - UpdateSubMgmtSetupWithReqWkshTemplate(); - - SubcontractingMgmtLibrary.UpdateProdOrderCompWithLocationCode(ProductionOrder."No."); - - SubcontractingMgmtLibrary.CreateTransferRoute(WorkCenter[2], ProductionOrder); - - // [WHEN] Create Subcontracting Purchase Order from Prod. Order Routing - SubcontractingMgmtLibrary.CreateSubcontractingOrderFromProdOrderRtngPage(Item."Routing No.", WorkCenter[2]."No."); - - // [WHEN] Create Transfer Order - PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); - PurchaseLine.SetRange("Prod. Order No.", ProductionOrder."No."); - PurchaseLine.FindFirst(); - - PurchaseHeader.SetRange("Document Type", PurchaseLine."Document Type"::Order); - PurchaseHeader.SetRange("No.", PurchaseLine."Document No."); - PurchaseHeader.FindFirst(); - PurchaseHeaderPage.OpenView(); - PurchaseHeaderPage.GoToRecord(PurchaseHeader); - PurchaseHeaderPage.CreateTransfOrdToSubcontractor.Invoke(); - - // [THEN] Check if Purchase Line with additional Component for Subcontracting Type exists - ProdOrderComp.SetRange("Prod. Order No.", ProductionOrder."No."); -#pragma warning disable AA0210 - ProdOrderComp.SetRange("Subcontracting Type", ProdOrderComp."Subcontracting Type"::Transfer); -#pragma warning restore AA0210 - ProdOrderComp.FindFirst(); - - TransferLine.SetRange("Subc. Prod. Order No.", ProductionOrder."No."); - TransferLine.SetRange("Item No.", ProdOrderComp."Item No."); - - Assert.RecordIsNotEmpty(TransferLine); - - ProdOrderCompPage.OpenEdit(); - ProdOrderCompPage.GoToRecord(ProdOrderComp); - asserterror ProdOrderCompPage."Location Code".SetValue(Location.Code); - Assert.ExpectedError('The component has already been assigned to the subcontracting transfer order'); - end; - - [Test] - [HandlerFunctions('ConfirmHandler,HandleTransferOrder')] - procedure TestReceiptDateFromTransferOrderLineFromSubcontrPurchOrderIsEquallyToProdOrderCompDueDate() - var - Item: Record Item; - Location: Record Location; - MachineCenter: array[2] of Record "Machine Center"; - ProdOrderComp: Record "Prod. Order Component"; - ProductionOrder: Record "Production Order"; - PurchaseHeader: Record "Purchase Header"; - PurchaseLine: Record "Purchase Line"; - TransferLine: Record "Transfer Line"; - WorkCenter: array[2] of Record "Work Center"; - ManufacturingSetup: Record "Manufacturing Setup"; - ExpectedDate: Date; - PurchaseHeaderPage: TestPage "Purchase Order"; - begin - // [SCENARIO] Create Subcontracting Transfer Order directly from Subcontracting Purchase Order - // [SCENARIO] and Transfer additional Line with marked Component ; - // [SCENARIO] Expected Error on changing Location Code in Prod. Order Component - - // [GIVEN] Complete Setup of Manufacturing, include Work- and Machine Centers, Item - Initialize(); - UpdateSubWhseHandlingTimeInSubManagementSetup(); - SubcontractingMgmtLibrary.UpdateManufacturingSetupWithSubcontractingLocation(); - - // [GIVEN] Some Parameters for Creation - Subcontracting := true; - UnitCostCalculation := UnitCostCalculation::Units; - - // [GIVEN] - CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); - LibraryWarehouse.CreateLocation(Location); - - // [GIVEN] Create Item for Production include Routing and Prod. BOM - CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); - - UpdateProdBomAndRoutingWithRoutingLink(Item, WorkCenter[2]."No."); - - SubcontractingMgmtLibrary.UpdateProdBomWithSubcontractingType(Item, "Subcontracting Type"::Transfer); - - SubcontractingMgmtLibrary.UpdateVendorWithSubcontractingLocationCode(WorkCenter[2]); - - SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( - ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); - - UpdateSubMgmtSetupWithReqWkshTemplate(); - - SubcontractingMgmtLibrary.UpdateProdOrderCompWithLocationCode(ProductionOrder."No."); - - UpdateProdOrderCompDueDate(ProductionOrder."No."); - - SubcontractingMgmtLibrary.CreateTransferRoute(WorkCenter[2], ProductionOrder); - - // [WHEN] Create Subcontracting Purchase Order from Prod. Order Routing - SubcontractingMgmtLibrary.CreateSubcontractingOrderFromProdOrderRtngPage(Item."Routing No.", WorkCenter[2]."No."); - - // [WHEN] Create Transfer Order - PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); - PurchaseLine.SetRange("Prod. Order No.", ProductionOrder."No."); - PurchaseLine.FindFirst(); - - PurchaseHeader.SetRange("Document Type", PurchaseLine."Document Type"::Order); - PurchaseHeader.SetRange("No.", PurchaseLine."Document No."); - PurchaseHeader.FindFirst(); - PurchaseHeaderPage.OpenView(); - PurchaseHeaderPage.GoToRecord(PurchaseHeader); - PurchaseHeaderPage.CreateTransfOrdToSubcontractor.Invoke(); - - // [THEN] Compare Due Date from Prod Order Comp with Receipt Date from Subc. Transfer Line - ProdOrderComp.SetRange("Prod. Order No.", ProductionOrder."No."); -#pragma warning disable AA0210 - ProdOrderComp.SetRange("Subcontracting Type", ProdOrderComp."Subcontracting Type"::Transfer); -#pragma warning restore AA0210 - ProdOrderComp.FindFirst(); - - TransferLine.SetRange("Subc. Prod. Order No.", ProductionOrder."No."); - TransferLine.SetRange("Item No.", ProdOrderComp."Item No."); - TransferLine.FindFirst(); - - ManufacturingSetup.Get(); - - ExpectedDate := CalcDate(ManufacturingSetup."Subc. Comp. Transfer Lead Time", TransferLine."Receipt Date"); - - Assert.AreEqual(ExpectedDate, ProdOrderComp."Due Date", ''); - - end; - - [Test] - [HandlerFunctions('ConfirmHandler,HandleTransferOrder')] - procedure TestLocationAndBinCodeIsSetFromOriginBinCodeAfterDeletingTransferOrder() - var - Item: Record Item; - Location: Record Location; - MachineCenter: array[2] of Record "Machine Center"; - ProdOrderComp: Record "Prod. Order Component"; - ProductionOrder: Record "Production Order"; - PurchaseHeader: Record "Purchase Header"; - PurchaseLine: Record "Purchase Line"; - TransferHeader: Record "Transfer Header"; - TransferLine: Record "Transfer Line"; - WorkCenter: array[2] of Record "Work Center"; - LocationCode: Code[10]; - BinCode: Code[20]; - PurchaseHeaderPage: TestPage "Purchase Order"; - begin - // [SCENARIO] Create Subcontracting Transfer Order directly from Subcontracting Purchase Order - // [SCENARIO] and Transfer additional Line with marked Component ; - // [SCENARIO] Expected Bin Code is filled with Original Bin Code - - // [GIVEN] Complete Setup of Manufacturing, include Work- and Machine Centers, Item - Initialize(); - SubcontractingMgmtLibrary.UpdateManufacturingSetupWithSubcontractingLocation(); - - // [GIVEN] Some Parameters for Creation - Subcontracting := true; - UnitCostCalculation := UnitCostCalculation::Units; - - // [GIVEN] - CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); - LibraryWarehouse.CreateLocation(Location); - - // [GIVEN] Create Item for Production include Routing and Prod. BOM - CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); - - UpdateProdBomAndRoutingWithRoutingLink(Item, WorkCenter[2]."No."); - - SubcontractingMgmtLibrary.UpdateProdBomWithSubcontractingType(Item, "Subcontracting Type"::Transfer); - - SubcontractingMgmtLibrary.UpdateVendorWithSubcontractingLocationCode(WorkCenter[2]); - - SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( - ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); - - UpdateSubMgmtSetupWithReqWkshTemplate(); - - UpdateProdOrderCompWithLocationAndBinCode(ProductionOrder."No.", LocationCode, BinCode); - - SubcontractingMgmtLibrary.CreateTransferRoute(WorkCenter[2], ProductionOrder); - - // [WHEN] Create Subcontracting Purchase Order from Prod. Order Routing - SubcontractingMgmtLibrary.CreateSubcontractingOrderFromProdOrderRtngPage(Item."Routing No.", WorkCenter[2]."No."); - - // [WHEN] Create Transfer Order - PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); - PurchaseLine.SetRange("Prod. Order No.", ProductionOrder."No."); - PurchaseLine.FindFirst(); - - PurchaseHeader.SetRange("Document Type", PurchaseLine."Document Type"::Order); - PurchaseHeader.SetRange("No.", PurchaseLine."Document No."); - PurchaseHeader.FindFirst(); - PurchaseHeaderPage.OpenView(); - PurchaseHeaderPage.GoToRecord(PurchaseHeader); - PurchaseHeaderPage.CreateTransfOrdToSubcontractor.Invoke(); - - // [THEN] Check if Purchase Line with additional Component for Subcontracting Type exists - ProdOrderComp.SetRange("Prod. Order No.", ProductionOrder."No."); -#pragma warning disable AA0210 - ProdOrderComp.SetRange("Subcontracting Type", ProdOrderComp."Subcontracting Type"::Transfer); -#pragma warning restore AA0210 - ProdOrderComp.FindFirst(); - - TransferLine.SetRange("Subc. Prod. Order No.", ProductionOrder."No."); - TransferLine.SetRange("Item No.", ProdOrderComp."Item No."); - - TransferLine.FindFirst(); - - TransferHeader.Get(TransferLine."Document No."); - TransferHeader.Delete(true); - - ProdOrderComp.SetRange("Prod. Order No.", ProductionOrder."No."); -#pragma warning disable AA0210 - ProdOrderComp.SetRange("Subcontracting Type", ProdOrderComp."Subcontracting Type"::Transfer); -#pragma warning restore AA0210 - ProdOrderComp.FindFirst(); - - Assert.AreEqual(ProdOrderComp."Location Code", LocationCode, ''); - Assert.AreEqual(ProdOrderComp."Bin Code", BinCode, ''); - end; - - [Test] - [HandlerFunctions('ConfirmHandler,HandleTransferOrder')] - procedure CheckBtnTrackingSpecificationOnProdOrderCompOnExistingReserveInTransferLine() - var - Item: Record Item; - Location: Record Location; - MachineCenter: array[2] of Record "Machine Center"; - ProdOrderComp: Record "Prod. Order Component"; - ProductionOrder: Record "Production Order"; - PurchaseHeader: Record "Purchase Header"; - PurchaseLine: Record "Purchase Line"; - TransferLine: Record "Transfer Line"; - WorkCenter: array[2] of Record "Work Center"; - ProdOrderCompPage: TestPage "Prod. Order Components"; - PurchaseHeaderPage: TestPage "Purchase Order"; - ExpectedErrorMsg: Text; - begin - // [SCENARIO] Create Subcontracting Transfer Order directly from Subcontracting Purchase Order - // [SCENARIO] and Transfer additional Line with marked Component ; - // [SCENARIO] Expected Error on open Item Tracking Lines in Prod. Order Component - - // [GIVEN] Complete Setup of Manufacturing, include Work- and Machine Centers, Item - Initialize(); - SubcontractingMgmtLibrary.UpdateManufacturingSetupWithSubcontractingLocation(); - - // [GIVEN] Some Parameters for Creation - Subcontracting := true; - UnitCostCalculation := UnitCostCalculation::Units; - - // [GIVEN] - CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); - LibraryWarehouse.CreateLocation(Location); - - // [GIVEN] Create Item for Production include Routing and Prod. BOM - CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); - - UpdateProdBomAndRoutingWithRoutingLink(Item, WorkCenter[2]."No."); - - SubcontractingMgmtLibrary.UpdateProdBomWithSubcontractingType(Item, "Subcontracting Type"::Transfer); - - SubcontractingMgmtLibrary.UpdateVendorWithSubcontractingLocationCode(WorkCenter[2]); - - SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( - ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); - - UpdateSubMgmtSetupWithReqWkshTemplate(); - - SubcontractingMgmtLibrary.UpdateProdOrderCompWithLocationCode(ProductionOrder."No."); - - SubcontractingMgmtLibrary.CreateTransferRoute(WorkCenter[2], ProductionOrder); - - // [WHEN] Create Subcontracting Purchase Order from Prod. Order Routing - SubcontractingMgmtLibrary.CreateSubcontractingOrderFromProdOrderRtngPage(Item."Routing No.", WorkCenter[2]."No."); - - // [WHEN] Create Transfer Order - PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); - PurchaseLine.SetRange("Prod. Order No.", ProductionOrder."No."); - PurchaseLine.FindFirst(); - - PurchaseHeader.SetRange("Document Type", PurchaseLine."Document Type"::Order); - PurchaseHeader.SetRange("No.", PurchaseLine."Document No."); - PurchaseHeader.FindFirst(); - PurchaseHeaderPage.OpenView(); - PurchaseHeaderPage.GoToRecord(PurchaseHeader); - PurchaseHeaderPage.CreateTransfOrdToSubcontractor.Invoke(); - - // [THEN] Check if Purchase Line with additional Component for Subcontracting Type exists, Mock Reservation Entries on TransferLine and try to open Item Tracking Lines from Prod order Comp. Page - ProdOrderComp.SetRange("Prod. Order No.", ProductionOrder."No."); -#pragma warning disable AA0210 - ProdOrderComp.SetRange("Subcontracting Type", ProdOrderComp."Subcontracting Type"::Transfer); -#pragma warning restore AA0210 - ProdOrderComp.FindFirst(); - - TransferLine.SetRange("Subc. Prod. Order No.", ProductionOrder."No."); - TransferLine.SetRange("Item No.", ProdOrderComp."Item No."); - - Assert.RecordIsNotEmpty(TransferLine); - TransferLine.FindFirst(); - - MockReservationEntryOnTransferLine(TransferLine, ProdOrderComp); - - ProdOrderCompPage.OpenEdit(); - ProdOrderCompPage.GoToRecord(ProdOrderComp); - asserterror ProdOrderCompPage.ItemTrackingLines.Invoke(); - ExpectedErrorMsg := StrSubstNo(AlreadySpecifiedErr, TransferLine."Document No."); - Assert.ExpectedError(ExpectedErrorMsg); - end; - - [Test] - [HandlerFunctions('ConfirmHandler')] - procedure TestCheckSubcontractorPriceInFactbox() - var - Item: Record Item; - MachineCenter: array[2] of Record "Machine Center"; - ProductionOrder: Record "Production Order"; - PurchaseLine: Record "Purchase Line"; - SubcontractorPrice: Record "Subcontractor Price"; - WorkCenter: array[2] of Record "Work Center"; - SubPurchaseLineFactbox: TestPage "Subc. Purchase Line Factbox"; - begin - // [SCENARIO] Create Subcontracting Purchase Order directly from Prod. Order Routing Line - // Check if No of SubcontractorPrices is displayed - - // [GIVEN] Complete Setup of Manufacturing, include Work- and Machine Centers, Item - Initialize(); - - // [GIVEN] Some Parameters for Creation - Subcontracting := true; - UnitCostCalculation := UnitCostCalculation::Units; - - // [GIVEN] - CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); - - // [GIVEN] Create Item for Production include Routing and Prod. BOM - CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); - - SubcontractingMgmtLibrary.CreateSubcontractorPrice(Item, WorkCenter[2]."No.", SubcontractorPrice); - - SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( - ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); - - UpdateSubMgmtSetupWithReqWkshTemplate(); - - // [WHEN] Create Subcontracting Purchase Order from Prod. Order Routing - SubcontractingMgmtLibrary.CreateSubcontractingOrderFromProdOrderRtngPage(Item."Routing No.", WorkCenter[2]."No."); - - // [THEN] Check if Purchase Line exists - PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); - PurchaseLine.SetRange("Prod. Order No.", ProductionOrder."No."); -#pragma warning disable AA0210 - PurchaseLine.SetRange("Work Center No.", WorkCenter[2]."No."); -#pragma warning restore AA0210 - PurchaseLine.FindFirst(); - - SubPurchaseLineFactbox.OpenView(); - SubPurchaseLineFactbox.GoToRecord(PurchaseLine); - Assert.AreEqual(SubPurchaseLineFactbox.SubcontractingPrices.Value, Format(SubcontractorPrice.Count()), ''); - end; - - [Test] - [Scope('OnPrem')] - procedure DeleteWorkCenterWithPricesDeletesRelatedPrices() - var - Item: Record Item; - SubcontractorPrice: Record "Subcontractor Price"; - WorkCenter: Record "Work Center"; - WorkCenterNo: Code[20]; - begin - // [SCENARIO 620643] Deleting a Work Center deletes all associated Subcontractor Prices - - // [GIVEN] A work center with a subcontractor and multiple Subcontractor Prices - Initialize(); - LibraryMfgManagement.CreateWorkCenterWithCalendar(WorkCenter, 0); - WorkCenter.Validate("Subcontractor No.", LibraryMfgManagement.CreateSubcontractorWithCurrency('')); - WorkCenter.Modify(true); - LibraryInventory.CreateItem(Item); - WorkCenterNo := WorkCenter."No."; - SubcontractingMgmtLibrary.CreateSubContractingPrice(SubcontractorPrice, WorkCenterNo, WorkCenter."Subcontractor No.", Item."No.", '', '', WorkDate(), '', 0, ''); - SubcontractingMgmtLibrary.CreateSubContractingPrice(SubcontractorPrice, WorkCenterNo, WorkCenter."Subcontractor No.", Item."No.", '', '', WorkDate(), '', 10, ''); - - // [WHEN] The work center is deleted - WorkCenter.Delete(true); - - // [THEN] All Subcontractor Prices for the work center are deleted - SubcontractorPrice.SetRange("Work Center No.", WorkCenterNo); - Assert.IsTrue(SubcontractorPrice.IsEmpty(), 'Subcontractor prices must be deleted when work center is deleted'); - end; - - [Test] - [Scope('OnPrem')] - procedure DeleteItemWithPricesDeletesRelatedPrices() - var - Item: Record Item; - SubcontractorPrice: Record "Subcontractor Price"; - WorkCenter: Record "Work Center"; - ItemNo: Code[20]; - begin - // [SCENARIO 620643] Deleting an Item deletes all associated Subcontractor Prices - - // [GIVEN] An item with multiple Subcontractor Prices - Initialize(); - LibraryMfgManagement.CreateWorkCenterWithCalendar(WorkCenter, 0); - WorkCenter.Validate("Subcontractor No.", LibraryMfgManagement.CreateSubcontractorWithCurrency('')); - WorkCenter.Modify(true); - LibraryInventory.CreateItem(Item); - ItemNo := Item."No."; - SubcontractingMgmtLibrary.CreateSubContractingPrice(SubcontractorPrice, WorkCenter."No.", WorkCenter."Subcontractor No.", ItemNo, '', '', WorkDate(), '', 0, ''); - SubcontractingMgmtLibrary.CreateSubContractingPrice(SubcontractorPrice, WorkCenter."No.", WorkCenter."Subcontractor No.", ItemNo, '', '', WorkDate(), '', 10, ''); - - // [WHEN] The item is deleted - Item.Delete(true); - - // [THEN] All Subcontractor Prices for the item are deleted - SubcontractorPrice.SetRange("Item No.", ItemNo); - Assert.IsTrue(SubcontractorPrice.IsEmpty(), 'Subcontractor prices must be deleted when item is deleted'); - end; - - [Test] - [HandlerFunctions('ConfirmHandler,SubcontrDispatchingListDefaultRequestPageHandler')] - procedure TestSubcontrDispatchingList() - var - Item: Record Item; - MachineCenter: array[2] of Record "Machine Center"; - ProductionOrder: Record "Production Order"; - PurchaseHeader: Record "Purchase Header"; - PurchaseLine: Record "Purchase Line"; - WorkCenter: array[2] of Record "Work Center"; - LibraryReportDataset: Codeunit "Library - Report Dataset"; - XmlParameters: Text; - begin - // [SCENARIO] Create Subcontracting and check Subcontr Dispatching List - - // [GIVEN] Complete Setup of Manufacturing, include Work- and Machine Centers, Item - Initialize(); - SubcontractingMgmtLibrary.SetupInventorySetup(); - - // [GIVEN] Some Parameters for Creation - Subcontracting := true; - UnitCostCalculation := UnitCostCalculation::Units; - - // [GIVEN] - CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); - - // [GIVEN] Create Item for Production include Routing and Prod. BOM - CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); - - UpdateProdBomAndRoutingWithRoutingLink(Item, WorkCenter[2]."No."); - - SubcontractingMgmtLibrary.UpdateProdBomWithSubcontractingType(Item, "Subcontracting Type"::Transfer); - - SubcontractingMgmtLibrary.UpdateVendorWithSubcontractingLocationCode(WorkCenter[2]); - - SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( - ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); - - UpdateSubMgmtSetupWithReqWkshTemplate(); - - SubcontractingMgmtLibrary.UpdateProdOrderCompWithLocationCode(ProductionOrder."No."); - - // [WHEN] Create Subcontracting Purchase Order from Prod. Order Routing - SubcontractingMgmtLibrary.CreateSubcontractingOrderFromProdOrderRtngPage(Item."Routing No.", WorkCenter[2]."No."); - - // [WHEN] Create Transfer Order - PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); - PurchaseLine.SetRange("Prod. Order No.", ProductionOrder."No."); - PurchaseLine.FindFirst(); - - PurchaseHeader.SetRange("Document Type", PurchaseLine."Document Type"::Order); - PurchaseHeader.SetRange("No.", PurchaseLine."Document No."); - PurchaseHeader.FindFirst(); - - // [THEN] Print Subcontr Dispatching List - PurchaseHeader.SetRecFilter(); - XmlParameters := Report.RunRequestPage(Report::"Subc. Dispatching List"); - LibraryReportDataset.RunReportAndLoad(Report::"Subc. Dispatching List", PurchaseHeader, XmlParameters); - // [THEN] the company address line is blank - LibraryReportDataset.AssertElementWithValueExists('SubcAddrInfoLine', ''); - // [THEN] an exemplary footer element is blank - LibraryReportDataset.AssertElementWithValueExists('SubcCompanyAddress1', ''); - LibraryReportDataset.AssertElementWithValueExists('Prod__Order_Routing_Line__Prod__Order_No__', ProductionOrder."No."); - end; - - [Test] - procedure TestTransferSubcontractingTypeAndVendorLocationIntoPlanningComponent() - var - Customer: Record Customer; - Item: Record Item; - Location: Record Location; - MachineCenter: array[2] of Record "Machine Center"; - PlanningComponent: Record "Planning Component"; - ProductionBOMLine: Record "Production BOM Line"; - SalesHeader: Record "Sales Header"; - SalesLine: Record "Sales Line"; - Vendor: Record Vendor; - WorkCenter: array[2] of Record "Work Center"; - begin - // [SCENARIO] Create Sales Order and test Planning Component - - // [GIVEN] Complete Setup of Manufacturing, include Work- and Machine Centers, Item - Initialize(); - SubcontractingMgmtLibrary.SetupInventorySetup(); - - // [GIVEN] Some Parameters for Creation - Subcontracting := true; - UnitCostCalculation := UnitCostCalculation::Units; - - // [GIVEN] - CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); - - // [GIVEN] Create Item for Production include Routing and Prod. BOM - CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); - Item."Reordering Policy" := "Reordering Policy"::Order; - Item.Modify(); - - UpdateProdBomAndRoutingWithRoutingLink(Item, WorkCenter[2]."No."); - - SubcontractingMgmtLibrary.UpdateProdBomWithSubcontractingType(Item, "Subcontracting Type"::InventoryByVendor); - - SubcontractingMgmtLibrary.UpdateVendorWithSubcontractingLocationCode(WorkCenter[2]); - - LibrarySales.CreateCustomer(Customer); - LibraryWarehouse.CreateLocationWithInventoryPostingSetup(Location); - LibrarySales.CreateSalesDocumentWithItem(SalesHeader, SalesLine, "Sales Document Type"::Order, Customer."No.", Item."No.", 5, Location.Code, WorkDate()); - - // [WHEN] - LibraryPlanning.CalcRegenPlanForPlanWksh(Item, CalcDate('<-1M>', WorkDate()), CalcDate('<+1M>', WorkDate())); - - ProductionBOMLine.SetRange("Production BOM No.", Item."Production BOM No."); - ProductionBOMLine.FindLast(); - PlanningComponent.SetRange("Item No.", ProductionBOMLine."No."); - PlanningComponent.FindFirst(); - - // [THEN] - Assert.Equal(ProductionBOMLine."Subcontracting Type", PlanningComponent."Subcontracting Type"); - Vendor.Get(WorkCenter[2]."Subcontractor No."); - Assert.Equal(Vendor."Subc. Location Code", PlanningComponent."Location Code"); - end; - - - [Test] - procedure PurchaseSubcTypeProdOrderCompExcludedFromPlanning() - var - ComponentItem: Record Item; - Item: Record Item; - Location: Record Location; - MachineCenter: array[2] of Record "Machine Center"; - ProdOrderComp: Record "Prod. Order Component"; - ProductionBOMLine: Record "Production BOM Line"; - ProductionOrder: Record "Production Order"; - RequisitionLine: Record "Requisition Line"; - WorkCenter: array[2] of Record "Work Center"; - begin - // [SCENARIO 630597] Prod. Order Components with Subcontracting Type "Purchase" should be - // excluded from planning engines because they will be purchased later via the subcontracting - // purchase order. - - // [GIVEN] Complete Setup of Manufacturing, include Work- and Machine Centers, Item - Initialize(); - SubcontractingMgmtLibrary.SetupInventorySetup(); - - // [GIVEN] Some Parameters for Creation - Subcontracting := true; - UnitCostCalculation := UnitCostCalculation::Units; - - // [GIVEN] Create subcontracting Work/Machine Centers - CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); - - // [GIVEN] Create Item for Production include Routing and Prod. BOM (2 component items) - CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); - - // [GIVEN] Assign Routing Link Code between subcontracting routing line and last BOM line - UpdateProdBomAndRoutingWithRoutingLink(Item, WorkCenter[2]."No."); - - // [GIVEN] Set Subcontracting Type = Purchase on the linked BOM line - SubcontractingMgmtLibrary.UpdateProdBomWithSubcontractingType(Item, "Subcontracting Type"::Purchase); - - // [GIVEN] Set up vendor with subcontracting location - SubcontractingMgmtLibrary.UpdateVendorWithSubcontractingLocationCode(WorkCenter[2]); - - // [GIVEN] Set component item reordering policy to Lot-for-Lot (already done during creation) - // [GIVEN] Create inventory for the component item so planning considers it - ProductionBOMLine.SetRange("Production BOM No.", Item."Production BOM No."); - ProductionBOMLine.FindLast(); - ComponentItem.Get(ProductionBOMLine."No."); - LibraryWarehouse.CreateLocationWithInventoryPostingSetup(Location); - - // [GIVEN] Create and refresh Released Production Order - SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( - ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); - - // [GIVEN] Verify prod. order component with Purchase subcontracting type exists - ProdOrderComp.SetRange("Prod. Order No.", ProductionOrder."No."); - ProdOrderComp.SetRange("Item No.", ComponentItem."No."); - ProdOrderComp.SetRange("Subcontracting Type", "Subcontracting Type"::Purchase); - Assert.RecordIsNotEmpty(ProdOrderComp); - - // [WHEN] Run Regenerative Plan for the component item - ComponentItem.SetRecFilter(); - LibraryPlanning.CalcRegenPlanForPlanWksh(ComponentItem, CalcDate('<-1M>', WorkDate()), CalcDate('<+1M>', WorkDate())); - - // [THEN] No requisition line is suggested for the component with Purchase subcontracting type - RequisitionLine.SetRange("No.", ComponentItem."No."); - Assert.RecordIsEmpty(RequisitionLine); - - // [WHEN] Changing the Subcontracting Type to None and run planning again - UpdateProdOrderComponentWithSubcontractingType(ProductionOrder, "Subcontracting Type"::Empty); - LibraryPlanning.CalcRegenPlanForPlanWksh(ComponentItem, CalcDate('<-1M>', WorkDate()), CalcDate('<+1M>', WorkDate())); - - // [THEN] Requisition line is suggested for the component with None subcontracting type - RequisitionLine.SetRange("No.", ComponentItem."No."); - Assert.RecordIsNotEmpty(RequisitionLine); - end; - - [Test] - [HandlerFunctions('ConfirmHandler')] - procedure SubcontractingFieldsPopulatedOnIleAfterSubcontractingPurchaseReceipt() - var - Item: Record Item; - ItemLedgerEntry: Record "Item Ledger Entry"; - ProductionOrder: Record "Production Order"; - PurchaseHeader: Record "Purchase Header"; - PurchaseLine: Record "Purchase Line"; - SubcWorkCenter: Record "Work Center"; - begin - // [SCENARIO] Bug 633292 - Output Item Ledger Entry created from posting a subcontracting purchase receipt should have the Subcontracting extension fields populated, so that the Production actions on the Item Ledger Entries page can resolve the linked production order, routing, and components. - - // [GIVEN] Subcontracting setup - Initialize(); - Subcontracting := true; - UnitCostCalculation := UnitCostCalculation::Units; - - // [GIVEN] Released Production Order whose only routing operation is a subcontracting one (so receiving the subcontracting PO posts the Output ILE) - CreateItemWithSingleSubcontractingOperation(Item, SubcWorkCenter); - SubcontractingMgmtLibrary.UpdateVendorWithSubcontractingLocationCode(SubcWorkCenter); - SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( - ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); - UpdateSubMgmtSetupWithReqWkshTemplate(); - - // [GIVEN] Subcontracting Purchase Order created from the Prod. Order Routing line - SubcontractingMgmtLibrary.CreateSubcontractingOrderFromProdOrderRtngPage(Item."Routing No.", SubcWorkCenter."No."); - PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); - PurchaseLine.SetRange("Prod. Order No.", ProductionOrder."No."); -#pragma warning disable AA0210 - PurchaseLine.SetRange("Work Center No.", SubcWorkCenter."No."); -#pragma warning restore AA0210 - PurchaseLine.FindFirst(); - PurchaseHeader.Get(PurchaseLine."Document Type", PurchaseLine."Document No."); - EnsureGeneralPostingSetupIsValid(PurchaseLine."Gen. Bus. Posting Group", PurchaseLine."Gen. Prod. Posting Group"); - - // [WHEN] Receive the subcontracting purchase order - LibraryPurchase.PostPurchaseDocument(PurchaseHeader, true, false); - - // [THEN] An Output Item Ledger Entry exists with Subcontracting extension fields populated from the source purchase line - ItemLedgerEntry.SetRange("Item No.", Item."No."); - ItemLedgerEntry.SetRange("Entry Type", ItemLedgerEntry."Entry Type"::Output); - ItemLedgerEntry.SetRange("Order Type", ItemLedgerEntry."Order Type"::Production); - ItemLedgerEntry.SetRange("Order No.", ProductionOrder."No."); - ItemLedgerEntry.FindFirst(); - - Assert.AreEqual( - PurchaseHeader."No.", ItemLedgerEntry."Subc. Purch. Order No.", - 'Item Ledger Entry "Subcontr. Purch. Order No." should equal the originating subcontracting purchase order.'); - Assert.AreEqual( - PurchaseLine."Line No.", ItemLedgerEntry."Subc. Purch. Order Line No.", - 'Item Ledger Entry "Subcontr. PO Line No." should equal the originating subcontracting purchase line.'); - Assert.AreEqual( - PurchaseLine."Operation No.", ItemLedgerEntry."Subc. Operation No.", - 'Item Ledger Entry "Operation No." (Subc) should equal the originating purchase line operation.'); - end; - - [Test] - [HandlerFunctions('ConfirmHandler')] - procedure ProdOFactboxMgmtResolvesProductionOrderForIleFromSubcontractingPurchaseReceipt() - var - Item: Record Item; - ItemLedgerEntry: Record "Item Ledger Entry"; - ProductionOrder: Record "Production Order"; - PurchaseHeader: Record "Purchase Header"; - PurchaseLine: Record "Purchase Line"; - SubcWorkCenter: Record "Work Center"; - SubcProdOFactboxMgmt: Codeunit "Subc. ProdO. Factbox Mgmt."; - begin - // [SCENARIO] Bug 633292 - Subc. ProdO. Factbox Mgmt. helpers should resolve a positive number of production order routings and components when given an Item Ledger Entry that originated from a subcontracting purchase receipt. Before the fix, the codeunit had no Item Ledger Entry branch in SetProdOrderInformationByVariant and returned 0 for any ILE variant. - - // [GIVEN] Subcontracting setup - Initialize(); - Subcontracting := true; - UnitCostCalculation := UnitCostCalculation::Units; - - // [GIVEN] Released Production Order whose only routing operation is a subcontracting one (so receiving the subcontracting PO posts the Output ILE) - CreateItemWithSingleSubcontractingOperation(Item, SubcWorkCenter); - SubcontractingMgmtLibrary.UpdateVendorWithSubcontractingLocationCode(SubcWorkCenter); - SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( - ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); - UpdateSubMgmtSetupWithReqWkshTemplate(); - - // [GIVEN] Subcontracting Purchase Order created from the Prod. Order Routing line and posted as received - SubcontractingMgmtLibrary.CreateSubcontractingOrderFromProdOrderRtngPage(Item."Routing No.", SubcWorkCenter."No."); - PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); - PurchaseLine.SetRange("Prod. Order No.", ProductionOrder."No."); -#pragma warning disable AA0210 - PurchaseLine.SetRange("Work Center No.", SubcWorkCenter."No."); -#pragma warning restore AA0210 - PurchaseLine.FindFirst(); - PurchaseHeader.Get(PurchaseLine."Document Type", PurchaseLine."Document No."); - EnsureGeneralPostingSetupIsValid(PurchaseLine."Gen. Bus. Posting Group", PurchaseLine."Gen. Prod. Posting Group"); - LibraryPurchase.PostPurchaseDocument(PurchaseHeader, true, false); - - ItemLedgerEntry.SetRange("Item No.", Item."No."); - ItemLedgerEntry.SetRange("Entry Type", ItemLedgerEntry."Entry Type"::Output); - ItemLedgerEntry.SetRange("Order Type", ItemLedgerEntry."Order Type"::Production); - ItemLedgerEntry.SetRange("Order No.", ProductionOrder."No."); - ItemLedgerEntry.FindFirst(); - - // [WHEN] CalcNoOfProductionOrderRoutings / CalcNoOfProductionOrderComponents are called with the ILE variant - // [THEN] Both return a positive count, confirming the production order linkage is resolved - Assert.IsTrue( - SubcProdOFactboxMgmt.CalcNoOfProductionOrderRoutings(ItemLedgerEntry) > 0, - 'CalcNoOfProductionOrderRoutings should return a positive count for an Item Ledger Entry from a subcontracting receipt.'); - Assert.IsTrue( - SubcProdOFactboxMgmt.CalcNoOfProductionOrderComponents(ItemLedgerEntry) > 0, - 'CalcNoOfProductionOrderComponents should return a positive count for an Item Ledger Entry from a subcontracting receipt.'); - end; - - [Test] - [HandlerFunctions('ConfirmHandler')] - procedure RoutingFactboxMgmtFiltersPurchOrderQtyByRoutingReferenceNo() - var - Item: Record Item; - ProdOrderRoutingLine: Record "Prod. Order Routing Line"; - ProductionOrder: Record "Production Order"; - PurchaseLine: Record "Purchase Line"; - SubcWorkCenter: Record "Work Center"; - SubcRoutingFactboxMgmt: Codeunit "Subc. Routing Factbox Mgmt."; - ExpectedPurchOrderQty: Decimal; - begin - // [SCENARIO] Regression test for Subc. Routing Factbox Mgmt. - // [SCENARIO] GetPurchOrderQtyFromRoutingLine must filter by "Routing Reference No." and not by "Prod. Order Line No.". - - // [GIVEN] A released production order with a subcontracting routing operation and a created subcontracting purchase order - Initialize(); - Subcontracting := true; - UnitCostCalculation := UnitCostCalculation::Units; - - CreateItemWithSingleSubcontractingOperation(Item, SubcWorkCenter); - SubcontractingMgmtLibrary.UpdateVendorWithSubcontractingLocationCode(SubcWorkCenter); - SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( - ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); - UpdateSubMgmtSetupWithReqWkshTemplate(); - - SubcontractingMgmtLibrary.CreateSubcontractingOrderFromProdOrderRtngPage(Item."Routing No.", SubcWorkCenter."No."); - - ProdOrderRoutingLine.SetRange(Status, ProdOrderRoutingLine.Status::Released); - ProdOrderRoutingLine.SetRange("Prod. Order No.", ProductionOrder."No."); - ProdOrderRoutingLine.SetRange("Routing No.", Item."Routing No."); - ProdOrderRoutingLine.SetRange("Work Center No.", SubcWorkCenter."No."); - ProdOrderRoutingLine.FindFirst(); - - PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); - PurchaseLine.SetRange("Prod. Order No.", ProductionOrder."No."); - PurchaseLine.SetRange("Prod. Order Line No.", ProdOrderRoutingLine."Routing Reference No."); - PurchaseLine.SetRange("Operation No.", ProdOrderRoutingLine."Operation No."); - PurchaseLine.FindFirst(); - - PurchaseLine.Validate(Quantity, LibraryRandom.RandIntInRange(7, 17)); - // Force a mismatch to prove the codeunit does not rely on Prod. Order Line No. - PurchaseLine."Prod. Order Line No." := ProdOrderRoutingLine."Routing Reference No." + 1; - PurchaseLine.Modify(true); - - Assert.AreNotEqual( - ProdOrderRoutingLine."Routing Reference No.", PurchaseLine."Prod. Order Line No.", - 'Test setup failed: Prod. Order Line No. must differ from Routing Reference No.'); - - // [WHEN] The factbox helper calculates purchase order quantity from the routing line - // [THEN] Quantity is returned for the line matched by Routing Reference No. - ExpectedPurchOrderQty := PurchaseLine.Quantity; - Assert.AreEqual( - ExpectedPurchOrderQty, - SubcRoutingFactboxMgmt.GetPurchOrderQtyFromRoutingLine(ProdOrderRoutingLine), - 'GetPurchOrderQtyFromRoutingLine must filter by Routing Reference No., not by Prod. Order Line No.'); - end; - - [Test] - [HandlerFunctions('ConfirmHandler,HandleTransferOrderReopen')] - procedure FactboxDrilldownTransferOrderReopenPersists() - var - Item: Record Item; - MachineCenter: array[2] of Record "Machine Center"; - ProdOrderRoutingLine: Record "Prod. Order Routing Line"; - ProductionOrder: Record "Production Order"; - PurchaseHeader: Record "Purchase Header"; - PurchaseLine: Record "Purchase Line"; - TransferHeader: Record "Transfer Header"; - WorkCenter: array[2] of Record "Work Center"; - ProductionLocation: Record Location; - SubcPurchFactboxMgmt: Codeunit "Subc. Purch. Factbox Mgmt."; - ReleaseTransferDocument: Codeunit "Release Transfer Document"; - PurchaseHeaderPage: TestPage "Purchase Order"; - ReleasedProdOrderRtng: TestPage "Prod. Order Routing"; - begin - // [SCENARIO] Bug 634267 - Reopen Transfer Order does not persist when opened from Subcontracting Details Factbox. - // ShowTransferOrdersAndReturnOrder must open the page on a real database record, so actions like - // Reopen that modify Rec directly persist after the page closes. - - // [GIVEN] Subcontracting setup with transfer components and a released transfer order - Initialize(); - SubcontractingMgmtLibrary.UpdateManufacturingSetupWithSubcontractingLocation(); - Subcontracting := true; - UnitCostCalculation := UnitCostCalculation::Units; - - CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); - CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); - UpdateProdBomAndRoutingWithRoutingLink(Item, WorkCenter[2]."No."); - SubcontractingMgmtLibrary.UpdateProdBomWithSubcontractingType(Item, "Subcontracting Type"::Transfer); - UpdateVendorWithSubcontractingLocationCode(WorkCenter[2]); - - LibraryWarehouse.CreateLocationWithInventoryPostingSetup(ProductionLocation); - SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( - ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); - UpdateSubMgmtSetupWithReqWkshTemplate(); - SetAllProdOrderTransferComponentLocations(ProductionOrder."No.", ProductionLocation.Code); - SubcontractingMgmtLibrary.CreateTransferRoute(WorkCenter[2], ProductionOrder); - - ProdOrderRoutingLine.SetRange("Prod. Order No.", ProductionOrder."No."); - ProdOrderRoutingLine.SetRange("Work Center No.", WorkCenter[2]."No."); - ProdOrderRoutingLine.FindFirst(); - ReleasedProdOrderRtng.OpenView(); - ReleasedProdOrderRtng.GoToRecord(ProdOrderRoutingLine); - ReleasedProdOrderRtng.CreateSubcontracting.Invoke(); - ReleasedProdOrderRtng.Close(); - - PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); - PurchaseLine.SetRange("Prod. Order No.", ProductionOrder."No."); - PurchaseLine.FindFirst(); - PurchaseHeader.Get(PurchaseLine."Document Type", PurchaseLine."Document No."); - - PurchaseHeaderPage.OpenView(); - PurchaseHeaderPage.GoToRecord(PurchaseHeader); - PurchaseHeaderPage.CreateTransfOrdToSubcontractor.Invoke(); - PurchaseHeaderPage.Close(); - - TransferHeader.SetRange("Subcontr. Purch. Order No.", PurchaseHeader."No."); - TransferHeader.FindFirst(); - ReleaseTransferDocument.Release(TransferHeader); - Assert.AreEqual(TransferHeader.Status::Released, TransferHeader.Status, 'Transfer order should be Released before the test.'); - - // [WHEN] Opening the transfer order from the factbox drill-down and performing Reopen - // The page handler HandleTransferOrderReopen will reopen the transfer order - PurchaseLine.FindFirst(); - SubcPurchFactboxMgmt.ShowTransferOrdersAndReturnOrder(PurchaseLine, true, false); - - // [THEN] The transfer order status must be Open after closing the page - TransferHeader.Get(TransferHeader."No."); - Assert.AreEqual(TransferHeader.Status::Open, TransferHeader.Status, - 'Transfer order status should be Open after Reopen from factbox drill-down. Before the fix, the Reopen modified a marked record and the change was lost.'); - end; - - [Test] - [HandlerFunctions('ConfirmHandler')] - procedure Description2CopiedFromProdOrderComponentToPurchaseLine() - var - Item: Record Item; - MachineCenter: array[2] of Record "Machine Center"; - ProdOrderComp: Record "Prod. Order Component"; - ProductionOrder: Record "Production Order"; - PurchaseLine: Record "Purchase Line"; - WorkCenter: array[2] of Record "Work Center"; - ExpectedDescription2: Text[50]; - begin - // [SCENARIO] Description 2 from Prod. Order Component is propagated to Purchase Line - // [FEATURE] Bug 620556 - Subcontracting Description 2 alignment - - // [GIVEN] Complete Setup of Manufacturing - Initialize(); - Subcontracting := true; - UnitCostCalculation := UnitCostCalculation::Units; - - CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); - CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); - UpdateProdBomAndRoutingWithRoutingLink(Item, WorkCenter[2]."No."); - SubcontractingMgmtLibrary.UpdateProdBomWithSubcontractingType(Item, "Subcontracting Type"::Purchase); - SubcontractingMgmtLibrary.UpdateVendorWithSubcontractingLocationCode(WorkCenter[2]); - - SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( - ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); - - UpdateSubMgmtSetupWithReqWkshTemplate(); - - // [GIVEN] A Description 2 value is set on the Prod. Order Component with Subcontracting Type = Purchase - ProdOrderComp.SetRange(Status, ProdOrderComp.Status::Released); - ProdOrderComp.SetRange("Prod. Order No.", ProductionOrder."No."); -#pragma warning disable AA0210 - ProdOrderComp.SetRange("Subcontracting Type", ProdOrderComp."Subcontracting Type"::Purchase); -#pragma warning restore AA0210 - ProdOrderComp.FindFirst(); - ExpectedDescription2 := 'TestDescription2_Comp'; - ProdOrderComp."Description 2" := ExpectedDescription2; - ProdOrderComp.Modify(); - - // [WHEN] Create Subcontracting Purchase Order from Prod. Order Routing - SubcontractingMgmtLibrary.CreateSubcontractingOrderFromProdOrderRtngPage(Item."Routing No.", WorkCenter[2]."No."); - - // [THEN] Description 2 from Prod. Order Component is propagated to the component Purchase Line - PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); - PurchaseLine.SetRange(Type, PurchaseLine.Type::Item); - PurchaseLine.SetRange("No.", ProdOrderComp."Item No."); -#pragma warning disable AA0210 - PurchaseLine.SetRange("Subc. Prod. Order No.", ProductionOrder."No."); -#pragma warning restore AA0210 - PurchaseLine.FindFirst(); - Assert.AreEqual( - ExpectedDescription2, PurchaseLine."Description 2", - 'Description 2 must be propagated from Prod. Order Component to Purchase Line'); - end; - - [Test] - procedure Description2PopulatedOnRequisitionLineFromCalculateSubcontracts() - var - Item: Record Item; - MachineCenter: array[2] of Record "Machine Center"; - ProductionOrder: Record "Production Order"; - ReqWkshTemplate: Record "Req. Wksh. Template"; - RequisitionLine: Record "Requisition Line"; - RequisitionWkshName: Record "Requisition Wksh. Name"; - WorkCenter: array[2] of Record "Work Center"; - SubcCalculateSubContract: Report "Subc. Calculate Subcontracts"; - LibraryUtility: Codeunit "Library - Utility"; - ExpectedDescription2: Text[50]; - begin - // [SCENARIO] Description 2 from Prod. Order Routing Line is populated on Requisition Line - // via Calculate Subcontracts report - // [FEATURE] Bug 620556 - Subcontracting Description 2 alignment - - // [GIVEN] Complete Setup of Manufacturing - Initialize(); - Subcontracting := true; - UnitCostCalculation := UnitCostCalculation::Units; - UpdateSubMgmtSetupWithReqWkshTemplate(); - - CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); - CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); - UpdateProdBomAndRoutingWithRoutingLink(Item, WorkCenter[2]."No."); - SubcontractingMgmtLibrary.UpdateVendorWithSubcontractingLocationCode(WorkCenter[2]); - - SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( - ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); - - // [GIVEN] Description 2 is set on the subcontracting Work Center Name 2 - // (SubcCalcSubcontractsExt copies WorkCenter."Name 2" → RequisitionLine."Description 2") - ExpectedDescription2 := 'TestDesc2_WC'; - WorkCenter[2].Get(WorkCenter[2]."No."); - WorkCenter[2].Validate("Name 2", ExpectedDescription2); - WorkCenter[2].Modify(true); - - // [GIVEN] Create requisition worksheet - ReqWkshTemplate.DeleteAll(true); - ReqWkshTemplate.Name := SelectRequisitionTemplateName(); - RequisitionWkshName.Init(); - RequisitionWkshName.Validate("Worksheet Template Name", ReqWkshTemplate.Name); - RequisitionWkshName.Validate( - Name, - CopyStr( - LibraryUtility.GenerateRandomCode(RequisitionWkshName.FieldNo(Name), Database::"Requisition Wksh. Name"), - 1, LibraryUtility.GetFieldLength(Database::"Requisition Wksh. Name", RequisitionWkshName.FieldNo(Name)))); - RequisitionWkshName.Insert(true); - - RequisitionLine."Worksheet Template Name" := RequisitionWkshName."Worksheet Template Name"; - RequisitionLine."Journal Batch Name" := RequisitionWkshName.Name; - - // [WHEN] Calculate Subcontracts - SubcCalculateSubContract.SetWkShLine(RequisitionLine); - SubcCalculateSubContract.UseRequestPage(false); - SubcCalculateSubContract.RunModal(); - - // [THEN] Description 2 on the Requisition Line is populated - RequisitionLine.SetRange("Worksheet Template Name", RequisitionWkshName."Worksheet Template Name"); - RequisitionLine.SetRange("Journal Batch Name", RequisitionWkshName.Name); -#pragma warning disable AA0210 - RequisitionLine.SetRange("Prod. Order No.", ProductionOrder."No."); -#pragma warning restore AA0210 - RequisitionLine.FindFirst(); - Assert.AreEqual( - ExpectedDescription2, RequisitionLine."Description 2", - 'Description 2 must be populated on the Requisition Line from the subcontracting Work Center'); - end; - - [Test] - [HandlerFunctions('RoutingLinkCodeDuplicateConfirmHandler')] - procedure ValidateRoutingLinkCodeOnProdOrderRtngLineShowsConfirmOnce() - var - Item: Record Item; - MachineCenter: array[2] of Record "Machine Center"; - ProdOrderRoutingLine: Record "Prod. Order Routing Line"; - ProductionOrder: Record "Production Order"; - WorkCenter: array[2] of Record "Work Center"; - begin - // [SCENARIO 617395] Validating Routing Link Code on a Prod. Order Routing Line shows the - // duplicate-use confirmation dialog exactly once. The BaseApp OnValidate already performs - // this check; the Subcontracting extension must not duplicate it. - Initialize(); - Subcontracting := true; - UnitCostCalculation := UnitCostCalculation::Units; - - // [GIVEN] Work centers, item with routing and BOM, with a routing link code assigned to the subcontracting routing line - CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); - CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); - UpdateProdBomAndRoutingWithRoutingLink(Item, WorkCenter[2]."No."); - UpdateVendorWithSubcontractingLocationCode(WorkCenter[2]); - - // [GIVEN] A released production order whose routing lines inherit the routing link code - SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( - ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); - - // [GIVEN] The prod. order routing line for the subcontracting work center (has a routing link code) - ProdOrderRoutingLine.SetRange("Routing No.", Item."Routing No."); - ProdOrderRoutingLine.SetRange("Work Center No.", WorkCenter[2]."No."); - ProdOrderRoutingLine.FindFirst(); - - // [WHEN] The routing link code is validated (re-validates the existing code, which triggers - // the BaseApp duplicate-use check) - ConfirmDialogCalledCount := 0; - ProdOrderRoutingLine.Validate("Routing Link Code", ProdOrderRoutingLine."Routing Link Code"); - - // [THEN] The confirmation dialog is shown exactly once — from the BaseApp — not twice Assert.AreEqual( - 1, ConfirmDialogCalledCount, - 'Routing Link Code duplicate confirmation must be shown exactly once, not twice'); - end; - - [Test] - [HandlerFunctions('ConfirmHandler')] - procedure CalcNoOfProductionOrderRoutingsReturnsOneForSubcontractingPurchaseLine() - var - Item: Record Item; - MachineCenter: array[2] of Record "Machine Center"; - ProdOrderRoutingLine: Record "Prod. Order Routing Line"; - ProductionOrder: Record "Production Order"; - PurchaseLine: Record "Purchase Line"; - WorkCenter: array[2] of Record "Work Center"; - SubcProdOFactboxMgmt: Codeunit "Subc. ProdO. Factbox Mgmt."; - begin - // [SCENARIO 634720] CalcNoOfProductionOrderRoutings must filter by Routing No. and Operation No. so the factbox count matches the drill-down (which is always a single routing line for a subcontracting purchase line). - - // [GIVEN] Manufacturing setup with a routing of multiple operations where only the second work center is subcontracting - Initialize(); - Subcontracting := true; - UnitCostCalculation := UnitCostCalculation::Units; - - CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); - CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); - - // [GIVEN] A Released Production Order whose routing has more than one operation - SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( - ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); - - ProdOrderRoutingLine.SetRange(Status, ProdOrderRoutingLine.Status::Released); - ProdOrderRoutingLine.SetRange("Prod. Order No.", ProductionOrder."No."); - Assert.IsTrue(ProdOrderRoutingLine.Count() > 1, 'Test precondition: routing must have more than one operation to detect the bug.'); - - UpdateSubMgmtSetupWithReqWkshTemplate(); - - // [GIVEN] A Subcontracting Purchase Order created from the routing line of the subcontracting work center - SubcontractingMgmtLibrary.CreateSubcontractingOrderFromProdOrderRtngPage(Item."Routing No.", WorkCenter[2]."No."); - PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); - PurchaseLine.SetRange("Prod. Order No.", ProductionOrder."No."); -#pragma warning disable AA0210 - PurchaseLine.SetRange("Work Center No.", WorkCenter[2]."No."); -#pragma warning restore AA0210 - PurchaseLine.FindFirst(); - - // [WHEN] CalcNoOfProductionOrderRoutings is called with the purchase line - // [THEN] It returns 1, matching the single routing line shown by the drill-down (not the total operations of the prod order line) + PurchaseHeader."No.", ItemLedgerEntry."Subc. Purch. Order No.", + 'Item Ledger Entry "Subcontr. Purch. Order No." should equal the originating subcontracting purchase order.'); Assert.AreEqual( - 1, SubcProdOFactboxMgmt.CalcNoOfProductionOrderRoutings(PurchaseLine), - 'CalcNoOfProductionOrderRoutings must equal the number of routing lines opened by the drill-down (exactly one for a subcontracting purchase line).'); + PurchaseLine."Line No.", ItemLedgerEntry."Subc. Purch. Order Line No.", + 'Item Ledger Entry "Subcontr. PO Line No." should equal the originating subcontracting purchase line.'); + Assert.AreEqual( + PurchaseLine."Operation No.", ItemLedgerEntry."Subc. Operation No.", + 'Item Ledger Entry "Operation No." (Subc) should equal the originating purchase line operation.'); end; [Test] - [HandlerFunctions('ConfirmHandler,HandleTransferOrder,HandleCreateTransferOrderMsg')] - procedure PostingDirectSubcontractingTransferSetsSourceFieldsOnDirectTransHeader() + [HandlerFunctions('ConfirmHandler')] + procedure ProdOFactboxMgmtResolvesProductionOrderForIleFromSubcontractingPurchaseReceipt() var - Bin: Record Bin; - DirectTransHeader: Record "Direct Trans. Header"; Item: Record Item; - Location: Record Location; - MachineCenter: array[2] of Record "Machine Center"; - ProdOrderComp: Record "Prod. Order Component"; + ItemLedgerEntry: Record "Item Ledger Entry"; ProductionOrder: Record "Production Order"; PurchaseHeader: Record "Purchase Header"; PurchaseLine: Record "Purchase Line"; - TransferHeader: Record "Transfer Header"; - TransferLine: Record "Transfer Line"; - Vendor: Record Vendor; - WorkCenter: array[2] of Record "Work Center"; - PurchaseHeaderPage: TestPage "Purchase Order"; + SubcWorkCenter: Record "Work Center"; + SubcProdOFactboxMgmt: Codeunit "Subc. ProdO. Factbox Mgmt."; begin - // [SCENARIO 617373] Posting a direct subcontracting transfer correctly propagates Source Type and Source ID to the Direct Trans. Header + // [SCENARIO] Bug 633292 - Subc. ProdO. Factbox Mgmt. helpers should resolve a positive number of production order routings and components when given an Item Ledger Entry that originated from a subcontracting purchase receipt. Before the fix, the codeunit had no Item Ledger Entry branch in SetProdOrderInformationByVariant and returned 0 for any ILE variant. - // [GIVEN] Complete manufacturing setup (no in-transit transfer route, so the report creates a Direct Transfer) + // [GIVEN] Subcontracting setup Initialize(); - SubcontractingMgmtLibrary.UpdateManufacturingSetupWithSubcontractingLocation(); Subcontracting := true; UnitCostCalculation := UnitCostCalculation::Units; - CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); - CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); - UpdateProdBomAndRoutingWithRoutingLink(Item, WorkCenter[2]."No."); - SubcontractingMgmtLibrary.UpdateProdBomWithSubcontractingType(Item, "Subcontracting Type"::Transfer); - UpdateVendorWithSubcontractingLocationCode(WorkCenter[2]); - + // [GIVEN] Released Production Order whose only routing operation is a subcontracting one (so receiving the subcontracting PO posts the Output ILE) + CreateItemWithSingleSubcontractingOperation(Item, SubcWorkCenter); + SubcontractingMgmtLibrary.UpdateVendorWithSubcontractingLocationCode(SubcWorkCenter); SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); - UpdateSubMgmtSetupWithReqWkshTemplate(); - SubcontractingMgmtLibrary.UpdateProdOrderCompWithLocationCode(ProductionOrder."No."); - - // [GIVEN] Subcontracting purchase order and transfer order to vendor - SubcontractingMgmtLibrary.CreateSubcontractingOrderFromProdOrderRtngPage(Item."Routing No.", WorkCenter[2]."No."); + // [GIVEN] Subcontracting Purchase Order created from the Prod. Order Routing line and posted as received + SubcontractingMgmtLibrary.CreateSubcontractingOrderFromProdOrderRtngPage(Item."Routing No.", SubcWorkCenter."No."); PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); PurchaseLine.SetRange("Prod. Order No.", ProductionOrder."No."); - PurchaseLine.FindFirst(); - - PurchaseHeader.Get(PurchaseLine."Document Type", PurchaseLine."Document No."); - PurchaseHeaderPage.OpenView(); - PurchaseHeaderPage.GoToRecord(PurchaseHeader); - PurchaseHeaderPage.CreateTransfOrdToSubcontractor.Invoke(); - - ProdOrderComp.SetRange("Prod. Order No.", ProductionOrder."No."); #pragma warning disable AA0210 - ProdOrderComp.SetRange("Subcontracting Type", ProdOrderComp."Subcontracting Type"::Transfer); + PurchaseLine.SetRange("Work Center No.", SubcWorkCenter."No."); #pragma warning restore AA0210 - ProdOrderComp.FindFirst(); - - TransferLine.SetRange("Subc. Prod. Order No.", ProductionOrder."No."); - TransferLine.SetRange("Item No.", ProdOrderComp."Item No."); - TransferLine.FindFirst(); - TransferHeader.Get(TransferLine."Document No."); - - Item.Get(ProdOrderComp."Item No."); - Location.Get(TransferHeader."Transfer-from Code"); - CreateInventory(Item, Location, Bin, ProdOrderComp."Expected Qty. (Base)"); - Vendor.Get(WorkCenter[2]."Subcontractor No."); + PurchaseLine.FindFirst(); + PurchaseHeader.Get(PurchaseLine."Document Type", PurchaseLine."Document No."); + EnsureGeneralPostingSetupIsValid(PurchaseLine."Gen. Bus. Posting Group", PurchaseLine."Gen. Prod. Posting Group"); + LibraryPurchase.PostPurchaseDocument(PurchaseHeader, true, false); - // [WHEN] Post the direct transfer - Codeunit.Run(Codeunit::"TransferOrder-Post Transfer", TransferHeader); + ItemLedgerEntry.SetRange("Item No.", Item."No."); + ItemLedgerEntry.SetRange("Entry Type", ItemLedgerEntry."Entry Type"::Output); + ItemLedgerEntry.SetRange("Order Type", ItemLedgerEntry."Order Type"::Production); + ItemLedgerEntry.SetRange("Order No.", ProductionOrder."No."); + ItemLedgerEntry.FindFirst(); - // [THEN] Direct Trans. Header has Source Type = Subcontracting and Source ID = Vendor No. - DirectTransHeader.SetRange("Subcontr. Purch. Order No.", PurchaseHeader."No."); - Assert.RecordIsNotEmpty(DirectTransHeader); - DirectTransHeader.FindFirst(); - Assert.AreEqual( - "Transfer Source Type"::Subcontracting, DirectTransHeader."Source Type", - 'Source Type must be Subcontracting on Direct Trans. Header'); - Assert.AreEqual( - Vendor."No.", DirectTransHeader."Source ID", - 'Source ID must be the Vendor No. on Direct Trans. Header'); + // [WHEN] CalcNoOfProductionOrderRoutings / CalcNoOfProductionOrderComponents are called with the ILE variant + // [THEN] Both return a positive count, confirming the production order linkage is resolved + Assert.IsTrue( + SubcProdOFactboxMgmt.CalcNoOfProductionOrderRoutings(ItemLedgerEntry) > 0, + 'CalcNoOfProductionOrderRoutings should return a positive count for an Item Ledger Entry from a subcontracting receipt.'); + Assert.IsTrue( + SubcProdOFactboxMgmt.CalcNoOfProductionOrderComponents(ItemLedgerEntry) > 0, + 'CalcNoOfProductionOrderComponents should return a positive count for an Item Ledger Entry from a subcontracting receipt.'); end; [Test] - [HandlerFunctions('ConfirmHandler,HandleTransferOrder')] - procedure CreateReturnTransferOrderAfterPartialShipOfOutbound() + [HandlerFunctions('ConfirmHandler')] + procedure RoutingFactboxMgmtFiltersPurchOrderQtyByRoutingReferenceNo() var - Bin: Record Bin; Item: Record Item; - Location: Record Location; - MachineCenter: array[2] of Record "Machine Center"; - ProdOrderComp: Record "Prod. Order Component"; + ProdOrderRoutingLine: Record "Prod. Order Routing Line"; ProductionOrder: Record "Production Order"; PurchaseLine: Record "Purchase Line"; - ReturnTransferHeader: Record "Transfer Header"; - TransferHeader: Record "Transfer Header"; - TransferLine: Record "Transfer Line"; - WorkCenter: array[2] of Record "Work Center"; - PurchaseHeaderPage: TestPage "Purchase Order"; - OutboundFromCode, OutboundToCode : Code[10]; - QtyPartialShip: Decimal; + SubcWorkCenter: Record "Work Center"; + SubcRoutingFactboxMgmt: Codeunit "Subc. Routing Factbox Mgmt."; + ExpectedPurchOrderQty: Decimal; begin - // [SCENARIO] Non-direct (in-transit) transfer: Return from Subcontractor must succeed when the outbound Transfer Order is only partially shipped, and a second Return attempt must be blocked with the existing error. - // [SCENARIO] Pre-fix the report errored 'Return from Subcontractor has already been created' on the first call because CheckTransferLineExists matched the unrelated outbound line. + // [SCENARIO] Regression test for Subc. Routing Factbox Mgmt. + // [SCENARIO] GetPurchOrderQtyFromRoutingLine must filter by "Routing Reference No." and not by "Prod. Order Line No.". - // [GIVEN] Standard subcontracting setup with an in-transit transfer route (non-direct) + // [GIVEN] A released production order with a subcontracting routing operation and a created subcontracting purchase order Initialize(); - SubcontractingMgmtLibrary.UpdateManufacturingSetupWithSubcontractingLocation(); Subcontracting := true; UnitCostCalculation := UnitCostCalculation::Units; - CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); - CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); - UpdateProdBomAndRoutingWithRoutingLink(Item, WorkCenter[2]."No."); - SubcontractingMgmtLibrary.UpdateProdBomWithSubcontractingType(Item, "Subcontracting Type"::Transfer); - UpdateVendorWithSubcontractingLocationCode(WorkCenter[2]); + + CreateItemWithSingleSubcontractingOperation(Item, SubcWorkCenter); + SubcontractingMgmtLibrary.UpdateVendorWithSubcontractingLocationCode(SubcWorkCenter); SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); UpdateSubMgmtSetupWithReqWkshTemplate(); - SubcontractingMgmtLibrary.UpdateProdOrderCompWithLocationCode(ProductionOrder."No."); - SubcontractingMgmtLibrary.CreateTransferRoute(WorkCenter[2], ProductionOrder); - SubcontractingMgmtLibrary.CreateSubcontractingOrderFromProdOrderRtngPage(Item."Routing No.", WorkCenter[2]."No."); + SubcontractingMgmtLibrary.CreateSubcontractingOrderFromProdOrderRtngPage(Item."Routing No.", SubcWorkCenter."No."); + + ProdOrderRoutingLine.SetRange(Status, ProdOrderRoutingLine.Status::Released); + ProdOrderRoutingLine.SetRange("Prod. Order No.", ProductionOrder."No."); + ProdOrderRoutingLine.SetRange("Routing No.", Item."Routing No."); + ProdOrderRoutingLine.SetRange("Work Center No.", SubcWorkCenter."No."); + ProdOrderRoutingLine.FindFirst(); PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); - PurchaseLine.SetRange("No.", ProductionOrder."Source No."); - PurchaseLine.SetRange(Type, "Purchase Line Type"::Item); PurchaseLine.SetRange("Prod. Order No.", ProductionOrder."No."); + PurchaseLine.SetRange("Prod. Order Line No.", ProdOrderRoutingLine."Routing Reference No."); + PurchaseLine.SetRange("Operation No.", ProdOrderRoutingLine."Operation No."); PurchaseLine.FindFirst(); - // [GIVEN] Outbound Subcontracting Transfer Order created from the Purchase Order - PurchaseHeaderPage.OpenView(); - PurchaseHeaderPage.GotoKey("Purchase Document Type"::Order, PurchaseLine."Document No."); - PurchaseHeaderPage.CreateTransfOrdToSubcontractor.Invoke(); + PurchaseLine.Validate(Quantity, LibraryRandom.RandIntInRange(7, 17)); + // Force a mismatch to prove the codeunit does not rely on Prod. Order Line No. + PurchaseLine."Prod. Order Line No." := ProdOrderRoutingLine."Routing Reference No." + 1; + PurchaseLine.Modify(true); - ProdOrderComp.SetRange("Prod. Order No.", ProductionOrder."No."); -#pragma warning disable AA0210 - ProdOrderComp.SetRange("Subcontracting Type", ProdOrderComp."Subcontracting Type"::Transfer); -#pragma warning restore AA0210 - ProdOrderComp.FindFirst(); + Assert.AreNotEqual( + ProdOrderRoutingLine."Routing Reference No.", PurchaseLine."Prod. Order Line No.", + 'Test setup failed: Prod. Order Line No. must differ from Routing Reference No.'); - TransferLine.SetRange("Subc. Prod. Order No.", ProductionOrder."No."); - TransferLine.SetRange("Item No.", ProdOrderComp."Item No."); - TransferLine.SetRange("Subc. Prod. Ord. Comp Line No.", ProdOrderComp."Line No."); - TransferLine.FindFirst(); - - TransferHeader.Get(TransferLine."Document No."); - OutboundFromCode := TransferHeader."Transfer-from Code"; - OutboundToCode := TransferHeader."Transfer-to Code"; - - // [GIVEN] Inventory at the source location and the outbound TO is partially shipped via Qty. to Ship (Ship only — items move to in-transit, line stays open with positive Outstanding) - Location.Get(OutboundFromCode); - Item.Get(ProdOrderComp."Item No."); - CreateInventory(Item, Location, Bin, ProdOrderComp."Expected Qty. (Base)"); - - QtyPartialShip := Round(TransferLine.Quantity / 2, 1, '<'); - TransferLine.Validate("Qty. to Ship", QtyPartialShip); - TransferLine.Modify(true); - LibraryWarehouse.PostTransferOrder(TransferHeader, true, false); - - // [WHEN] Creating a Return Transfer Order while the outbound TO line is still present (partially shipped) - PurchaseHeaderPage.GotoKey("Purchase Document Type"::Order, PurchaseLine."Document No."); - PurchaseHeaderPage.CreateReturnFromSubcontractor.Invoke(); - - // [THEN] A Return Transfer Order is created with reversed Transfer-from / Transfer-to and quantity capped by the partially-shipped qty - ReturnTransferHeader.SetRange("Subcontr. Purch. Order No.", PurchaseLine."Document No."); - ReturnTransferHeader.SetRange("Subc. Return Order", true); - Assert.IsTrue(ReturnTransferHeader.FindFirst(), 'Return Transfer Order should be created after partial ship of outbound'); - Assert.AreEqual(OutboundToCode, ReturnTransferHeader."Transfer-from Code", 'Return Transfer-from must be the subcontractor location'); - Assert.AreEqual(OutboundFromCode, ReturnTransferHeader."Transfer-to Code", 'Return Transfer-to must be the original component location'); - - TransferLine.Reset(); - TransferLine.SetRange("Document No.", ReturnTransferHeader."No."); - Assert.IsTrue(TransferLine.FindFirst(), 'Return Transfer Line should exist'); - Assert.AreEqual(QtyPartialShip, TransferLine.Quantity, 'Return quantity should equal the in-transit qty from the partial outbound shipment'); - - // [WHEN] Trying to create the Return Transfer Order again, with a Return TO already in place - PurchaseHeaderPage.GotoKey("Purchase Document Type"::Order, PurchaseLine."Document No."); - asserterror PurchaseHeaderPage.CreateReturnFromSubcontractor.Invoke(); - - // [THEN] The duplicate is blocked with the existing error - Assert.ExpectedError('Nothing to create. No components or WIP items to return for the specified subcontracting order'); + // [WHEN] The factbox helper calculates purchase order quantity from the routing line + // [THEN] Quantity is returned for the line matched by Routing Reference No. + ExpectedPurchOrderQty := PurchaseLine.Quantity; + Assert.AreEqual( + ExpectedPurchOrderQty, + SubcRoutingFactboxMgmt.GetPurchOrderQtyFromRoutingLine(ProdOrderRoutingLine), + 'GetPurchOrderQtyFromRoutingLine must filter by Routing Reference No., not by Prod. Order Line No.'); end; [Test] - [HandlerFunctions('ConfirmHandler,HandleTransferOrder')] - procedure CreateReturnTransferOrderAfterPartialShipOfOutboundDirectTransfer() + [HandlerFunctions('ConfirmHandler,HandleTransferOrderReopen')] + procedure FactboxDrilldownTransferOrderReopenPersists() var - Bin: Record Bin; Item: Record Item; - Location: Record Location; MachineCenter: array[2] of Record "Machine Center"; - ProdOrderComp: Record "Prod. Order Component"; + ProdOrderRoutingLine: Record "Prod. Order Routing Line"; ProductionOrder: Record "Production Order"; + PurchaseHeader: Record "Purchase Header"; PurchaseLine: Record "Purchase Line"; - ReturnTransferHeader: Record "Transfer Header"; TransferHeader: Record "Transfer Header"; - TransferLine: Record "Transfer Line"; WorkCenter: array[2] of Record "Work Center"; + ProductionLocation: Record Location; + SubcPurchFactboxMgmt: Codeunit "Subc. Purch. Factbox Mgmt."; + ReleaseTransferDocument: Codeunit "Release Transfer Document"; PurchaseHeaderPage: TestPage "Purchase Order"; - OutboundFromCode, OutboundToCode : Code[10]; - QtyPartialShip: Decimal; + ReleasedProdOrderRtng: TestPage "Prod. Order Routing"; begin - // [SCENARIO] Direct transfer (no in-transit route): Return from Subcontractor must succeed when the outbound Transfer Order has been partially direct-transferred, and a second Return attempt must be blocked with the existing error. - // [SCENARIO] In direct-transfer mode Qty. to Ship is forced to equal Quantity, so a partial transfer is achieved by reducing the line Quantity before posting. After the post the items already sit at the subcontractor and the outbound line is consumed. + // [SCENARIO] Bug 634267 - Reopen Transfer Order does not persist when opened from Subcontracting Details Factbox. + // ShowTransferOrdersAndReturnOrder must open the page on a real database record, so actions like + // Reopen that modify Rec directly persist after the page closes. - // [GIVEN] Standard subcontracting setup WITHOUT an in-transit transfer route — the outbound TO will be Direct Transfer + // [GIVEN] Subcontracting setup with transfer components and a released transfer order Initialize(); SubcontractingMgmtLibrary.UpdateManufacturingSetupWithSubcontractingLocation(); Subcontracting := true; UnitCostCalculation := UnitCostCalculation::Units; + CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); UpdateProdBomAndRoutingWithRoutingLink(Item, WorkCenter[2]."No."); SubcontractingMgmtLibrary.UpdateProdBomWithSubcontractingType(Item, "Subcontracting Type"::Transfer); UpdateVendorWithSubcontractingLocationCode(WorkCenter[2]); + + LibraryWarehouse.CreateLocationWithInventoryPostingSetup(ProductionLocation); SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); UpdateSubMgmtSetupWithReqWkshTemplate(); - SubcontractingMgmtLibrary.UpdateProdOrderCompWithLocationCode(ProductionOrder."No."); + SetAllProdOrderTransferComponentLocations(ProductionOrder."No.", ProductionLocation.Code); + SubcontractingMgmtLibrary.CreateTransferRoute(WorkCenter[2], ProductionOrder); - SubcontractingMgmtLibrary.CreateSubcontractingOrderFromProdOrderRtngPage(Item."Routing No.", WorkCenter[2]."No."); + ProdOrderRoutingLine.SetRange("Prod. Order No.", ProductionOrder."No."); + ProdOrderRoutingLine.SetRange("Work Center No.", WorkCenter[2]."No."); + ProdOrderRoutingLine.FindFirst(); + ReleasedProdOrderRtng.OpenView(); + ReleasedProdOrderRtng.GoToRecord(ProdOrderRoutingLine); + ReleasedProdOrderRtng.CreateSubcontracting.Invoke(); + ReleasedProdOrderRtng.Close(); PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); - PurchaseLine.SetRange("No.", ProductionOrder."Source No."); - PurchaseLine.SetRange(Type, "Purchase Line Type"::Item); PurchaseLine.SetRange("Prod. Order No.", ProductionOrder."No."); PurchaseLine.FindFirst(); + PurchaseHeader.Get(PurchaseLine."Document Type", PurchaseLine."Document No."); - // [GIVEN] Outbound Subcontracting Transfer Order created from the Purchase Order (Direct Transfer because no in-transit route) PurchaseHeaderPage.OpenView(); - PurchaseHeaderPage.GotoKey("Purchase Document Type"::Order, PurchaseLine."Document No."); + PurchaseHeaderPage.GoToRecord(PurchaseHeader); PurchaseHeaderPage.CreateTransfOrdToSubcontractor.Invoke(); + PurchaseHeaderPage.Close(); - ProdOrderComp.SetRange("Prod. Order No.", ProductionOrder."No."); -#pragma warning disable AA0210 - ProdOrderComp.SetRange("Subcontracting Type", ProdOrderComp."Subcontracting Type"::Transfer); -#pragma warning restore AA0210 - ProdOrderComp.FindFirst(); + TransferHeader.SetRange("Subcontr. Purch. Order No.", PurchaseHeader."No."); + TransferHeader.FindFirst(); + ReleaseTransferDocument.Release(TransferHeader); + Assert.AreEqual(TransferHeader.Status::Released, TransferHeader.Status, 'Transfer order should be Released before the test.'); + + // [WHEN] Opening the transfer order from the factbox drill-down and performing Reopen + // The page handler HandleTransferOrderReopen will reopen the transfer order + PurchaseLine.FindFirst(); + SubcPurchFactboxMgmt.ShowTransferOrdersAndReturnOrder(PurchaseLine, true, false); - TransferLine.SetRange("Subc. Prod. Order No.", ProductionOrder."No."); - TransferLine.SetRange("Item No.", ProdOrderComp."Item No."); - TransferLine.SetRange("Subc. Prod. Ord. Comp Line No.", ProdOrderComp."Line No."); - TransferLine.FindFirst(); - - TransferHeader.Get(TransferLine."Document No."); - OutboundFromCode := TransferHeader."Transfer-from Code"; - OutboundToCode := TransferHeader."Transfer-to Code"; - - // [GIVEN] Inventory at the source location and the outbound Quantity is reduced to a partial value, then direct-transferred (Ship+Receive in one operation — items land at the subcontractor location) - Location.Get(OutboundFromCode); - Item.Get(ProdOrderComp."Item No."); - CreateInventory(Item, Location, Bin, ProdOrderComp."Expected Qty. (Base)"); - - QtyPartialShip := Round(TransferLine.Quantity / 2, 1, '<'); - TransferLine.Validate(Quantity, QtyPartialShip); - TransferLine.Modify(true); - LibraryWarehouse.PostTransferOrder(TransferHeader, true, true); - - // [WHEN] Creating a Return Transfer Order while items are now at the subcontractor - PurchaseHeaderPage.GotoKey("Purchase Document Type"::Order, PurchaseLine."Document No."); - PurchaseHeaderPage.CreateReturnFromSubcontractor.Invoke(); - - // [THEN] A Return Transfer Order is created with reversed Transfer-from / Transfer-to and quantity capped by the qty that actually arrived at the subcontractor - ReturnTransferHeader.SetRange("Subcontr. Purch. Order No.", PurchaseLine."Document No."); - ReturnTransferHeader.SetRange("Subc. Return Order", true); - Assert.IsTrue(ReturnTransferHeader.FindFirst(), 'Return Transfer Order should be created after partial direct transfer'); - Assert.AreEqual(OutboundToCode, ReturnTransferHeader."Transfer-from Code", 'Return Transfer-from must be the subcontractor location'); - Assert.AreEqual(OutboundFromCode, ReturnTransferHeader."Transfer-to Code", 'Return Transfer-to must be the original component location'); - - TransferLine.Reset(); - TransferLine.SetRange("Document No.", ReturnTransferHeader."No."); - Assert.IsTrue(TransferLine.FindFirst(), 'Return Transfer Line should exist'); - Assert.AreEqual(QtyPartialShip, TransferLine.Quantity, 'Return quantity should equal the qty already at the subcontractor'); - - // [WHEN] Trying to create the Return Transfer Order again, with a Return TO already in place - PurchaseHeaderPage.GotoKey("Purchase Document Type"::Order, PurchaseLine."Document No."); - asserterror PurchaseHeaderPage.CreateReturnFromSubcontractor.Invoke(); - - // [THEN] The duplicate is blocked with the existing error - Assert.ExpectedError('Nothing to create. No components or WIP items to return for the specified subcontracting order'); + // [THEN] The transfer order status must be Open after closing the page + TransferHeader.Get(TransferHeader."No."); + Assert.AreEqual(TransferHeader.Status::Open, TransferHeader.Status, + 'Transfer order status should be Open after Reopen from factbox drill-down. Before the fix, the Reopen modified a marked record and the change was lost.'); end; [Test] - [HandlerFunctions('ConfirmHandler')] - procedure CreateSubcontractingPOForEachProdOrderLineWhenLinesShareRoutingAndOperation() + [HandlerFunctions('RoutingLinkCodeDuplicateConfirmHandler')] + procedure ValidateRoutingLinkCodeOnProdOrderRtngLineShowsConfirmOnce() var Item: Record Item; - ProductionLocation: array[2] of Record Location; MachineCenter: array[2] of Record "Machine Center"; - ProdOrderLine: Record "Prod. Order Line"; ProdOrderRoutingLine: Record "Prod. Order Routing Line"; ProductionOrder: Record "Production Order"; WorkCenter: array[2] of Record "Work Center"; - ProdOrderRtng: TestPage "Prod. Order Routing"; - I: Integer; - ProdOrderLineNo: array[2] of Integer; begin - // [SCENARIO 634238] When a Released Production Order has multiple Prod. Order lines sharing the same - // Routing/Operation, creating a Subcontracting Order for the second line must not raise the false - // "Purchase order(s) have already been created" warning, and must create/show its own Purchase Order. - - // [GIVEN] Subcontracting setup with direct transfer (no in-transit route) + // [SCENARIO 617395] Validating Routing Link Code on a Prod. Order Routing Line shows the + // duplicate-use confirmation dialog exactly once. The BaseApp OnValidate already performs + // this check; the Subcontracting extension must not duplicate it. Initialize(); - SubcontractingMgmtLibrary.UpdateManufacturingSetupWithSubcontractingLocation(); Subcontracting := true; UnitCostCalculation := UnitCostCalculation::Units; + + // [GIVEN] Work centers, item with routing and BOM, with a routing link code assigned to the subcontracting routing line CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); - UpdateVendorWithSubcontractingLocationCode(WorkCenter[2]); CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); UpdateProdBomAndRoutingWithRoutingLink(Item, WorkCenter[2]."No."); + UpdateVendorWithSubcontractingLocationCode(WorkCenter[2]); - // [GIVEN] One released production order created directly from item - LibraryManufacturing.CreateProductionOrder(ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", 0); - - // [GIVEN] No production order lines exist yet for this order - ProdOrderLine.SetRange(Status, ProdOrderLine.Status::Released); - ProdOrderLine.SetRange("Prod. Order No.", ProductionOrder."No."); - Assert.AreEqual(0, ProdOrderLine.Count(), 'Expected no production order lines to exist before manually creating them'); - - // [GIVEN] Two production order lines on the same production order, on different locations - LibraryWarehouse.CreateLocationWithInventoryPostingSetup(ProductionLocation[1]); - LibraryWarehouse.CreateLocationWithInventoryPostingSetup(ProductionLocation[2]); - 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."; - Assert.AreNotEqual(ProdOrderLineNo[1], ProdOrderLineNo[2], 'Expected two distinct production order lines'); - - // [GIVEN] Refresh the production order to update the routing and component lines - LibraryManufacturing.RefreshProdOrder(ProductionOrder, false, false, true, true, false); - - // [GIVEN] The two production-order lines have transfer components on different locations - for I := 1 to 2 do begin - ProdOrderRoutingLine.Reset(); - ProdOrderRoutingLine.SetRange(Status, ProdOrderRoutingLine.Status::Released); - ProdOrderRoutingLine.SetRange("Prod. Order No.", ProductionOrder."No."); - ProdOrderRoutingLine.SetRange("Routing Reference No.", ProdOrderLineNo[I]); - ProdOrderRoutingLine.SetRange("Work Center No.", WorkCenter[2]."No."); - Assert.RecordCount(ProdOrderRoutingLine, 1); - - ProdOrderRoutingLine.FindFirst(); - - ProdOrderRtng.OpenView(); - ProdOrderRtng.GoToRecord(ProdOrderRoutingLine); - 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'); - LibraryVariableStorage.AssertEmpty(); - end; - end; + // [GIVEN] A released production order whose routing lines inherit the routing link code + SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( + ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); - [Test] - [HandlerFunctions('ConfirmYesShowSubcontractingPurchOrders,CapturePurchaseOrderPageNo')] - procedure CreateSubcontractingPONavigatesToOwnPOWhenLinesShareRoutingAndOperation() - var - Item: Record Item; - ProductionLocation: array[2] of Record Location; - MachineCenter: array[2] of Record "Machine Center"; - ProdOrderLine: Record "Prod. Order Line"; - ProdOrderRoutingLine: Record "Prod. Order Routing Line"; - ProductionOrder: Record "Production Order"; - PurchaseLine: Record "Purchase Line"; - WorkCenter: array[2] of Record "Work Center"; - ProdOrderRtng: TestPage "Prod. Order Routing"; - I: Integer; - ProdOrderLineNo: array[2] of Integer; - OpenedPurchaseOrderNo: Code[20]; - begin - // [SCENARIO 634238] When a Released Production Order has multiple Prod. Order lines sharing routing/operation, - // confirming "view them" on the just-created Subcontracting Order must open the PO tied to the invoked - // routing line, not the unrelated PO of a sibling line. + // [GIVEN] The prod. order routing line for the subcontracting work center (has a routing link code) + ProdOrderRoutingLine.SetRange("Routing No.", Item."Routing No."); + ProdOrderRoutingLine.SetRange("Work Center No.", WorkCenter[2]."No."); + ProdOrderRoutingLine.FindFirst(); - // [GIVEN] Subcontracting setup with two prod order lines sharing routing/operation but different Routing Reference No. - Initialize(); - SubcontractingMgmtLibrary.UpdateManufacturingSetupWithSubcontractingLocation(); - Subcontracting := true; - UnitCostCalculation := UnitCostCalculation::Units; - CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); - UpdateVendorWithSubcontractingLocationCode(WorkCenter[2]); - CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); - UpdateProdBomAndRoutingWithRoutingLink(Item, WorkCenter[2]."No."); + // [WHEN] The routing link code is validated (re-validates the existing code, which triggers + // the BaseApp duplicate-use check) + ConfirmDialogCalledCount := 0; + ProdOrderRoutingLine.Validate("Routing Link Code", ProdOrderRoutingLine."Routing Link Code"); - LibraryManufacturing.CreateProductionOrder(ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", 0); - - LibraryWarehouse.CreateLocationWithInventoryPostingSetup(ProductionLocation[1]); - LibraryWarehouse.CreateLocationWithInventoryPostingSetup(ProductionLocation[2]); - 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."; - - LibraryManufacturing.RefreshProdOrder(ProductionOrder, false, false, true, true, false); - - // [WHEN] Creating a Subcontracting Order from each routing line and confirming "view them" - for I := 1 to 2 do begin - ProdOrderRoutingLine.Reset(); - ProdOrderRoutingLine.SetRange(Status, ProdOrderRoutingLine.Status::Released); - ProdOrderRoutingLine.SetRange("Prod. Order No.", ProductionOrder."No."); - ProdOrderRoutingLine.SetRange("Routing Reference No.", ProdOrderLineNo[I]); - ProdOrderRoutingLine.SetRange("Work Center No.", WorkCenter[2]."No."); - ProdOrderRoutingLine.FindFirst(); - - ProdOrderRtng.OpenView(); - ProdOrderRtng.GoToRecord(ProdOrderRoutingLine); - ProdOrderRtng.CreateSubcontracting.Invoke(); - ProdOrderRtng.Close(); - - // [THEN] The page handler opens the Purchase Order whose line carries this routing line's Routing Reference No. - OpenedPurchaseOrderNo := CopyStr(LibraryVariableStorage.DequeueText(), 1, MaxStrLen(OpenedPurchaseOrderNo)); - PurchaseLine.Reset(); - PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); - PurchaseLine.SetRange("Document No.", OpenedPurchaseOrderNo); - PurchaseLine.SetRange(Type, PurchaseLine.Type::Item); - PurchaseLine.SetRange("Prod. Order No.", ProductionOrder."No."); - PurchaseLine.SetRange("Routing Reference No.", ProdOrderLineNo[I]); - Assert.IsFalse(PurchaseLine.IsEmpty(), StrSubstNo(PurchOrderRoutingErr, OpenedPurchaseOrderNo, ProdOrderLineNo[I])); - LibraryVariableStorage.AssertEmpty(); - end; + // [THEN] The confirmation dialog is shown exactly once — from the BaseApp — not twice + Assert.AreEqual( + 1, ConfirmDialogCalledCount, + 'Routing Link Code duplicate confirmation must be shown exactly once, not twice'); end; [Test] - [HandlerFunctions('ConfirmYesShowSubcontractingPurchOrders,HandlePurchaseOrderPage,HandlePurchaseLinesPage')] - procedure ShowExistingPurchOrdersOpensListWhenAlreadyCreated() + [HandlerFunctions('ConfirmHandler')] + procedure CalcNoOfProductionOrderRoutingsReturnsOneForSubcontractingPurchaseLine() var Item: Record Item; MachineCenter: array[2] of Record "Machine Center"; + ProdOrderRoutingLine: Record "Prod. Order Routing Line"; ProductionOrder: Record "Production Order"; PurchaseLine: Record "Purchase Line"; WorkCenter: array[2] of Record "Work Center"; + SubcProdOFactboxMgmt: Codeunit "Subc. ProdO. Factbox Mgmt."; begin - // [SCENARIO 633224] First Create Subcontracting Order opens the Purchase Order; running the action again on the same routing line opens the Purchase Lines list. + // [SCENARIO 634720] CalcNoOfProductionOrderRoutings must filter by Routing No. and Operation No. so the factbox count matches the drill-down (which is always a single routing line for a subcontracting purchase line). - // [GIVEN] Manufacturing setup with subcontracting work center, item with routing/BOM, released production order + // [GIVEN] Manufacturing setup with a routing of multiple operations where only the second work center is subcontracting Initialize(); Subcontracting := true; UnitCostCalculation := UnitCostCalculation::Units; + CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); + + // [GIVEN] A Released Production Order whose routing has more than one operation SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( - ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); - UpdateSubMgmtSetupWithReqWkshTemplate(); + ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); - // [WHEN] Create Subcontracting Order from the routing line for the first time - PurchaseOrderPageOpened := false; - PurchaseLinesPageOpened := false; - SubcontractingMgmtLibrary.CreateSubcontractingOrderFromProdOrderRtngPage(Item."Routing No.", WorkCenter[2]."No."); + ProdOrderRoutingLine.SetRange(Status, ProdOrderRoutingLine.Status::Released); + ProdOrderRoutingLine.SetRange("Prod. Order No.", ProductionOrder."No."); + Assert.IsTrue(ProdOrderRoutingLine.Count() > 1, 'Test precondition: routing must have more than one operation to detect the bug.'); - // [THEN] The Purchase Order card is shown - Assert.IsTrue(PurchaseOrderPageOpened, 'Purchase Order should open after first creation.'); - Assert.IsFalse(PurchaseLinesPageOpened, 'Purchase Lines list should not open on first creation.'); + UpdateSubMgmtSetupWithReqWkshTemplate(); + // [GIVEN] A Subcontracting Purchase Order created from the routing line of the subcontracting work center + SubcontractingMgmtLibrary.CreateSubcontractingOrderFromProdOrderRtngPage(Item."Routing No.", WorkCenter[2]."No."); PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); PurchaseLine.SetRange("Prod. Order No.", ProductionOrder."No."); +#pragma warning disable AA0210 PurchaseLine.SetRange("Work Center No.", WorkCenter[2]."No."); - Assert.IsFalse(PurchaseLine.IsEmpty(), 'Purchase line should exist for the production order.'); - - // [WHEN] Create Subcontracting Order from the same routing line again - PurchaseOrderPageOpened := false; - PurchaseLinesPageOpened := false; - SubcontractingMgmtLibrary.CreateSubcontractingOrderFromProdOrderRtngPage(Item."Routing No.", WorkCenter[2]."No."); +#pragma warning restore AA0210 + PurchaseLine.FindFirst(); - // [THEN] The Purchase Lines list is shown instead of individual Purchase Order cards - Assert.IsTrue(PurchaseLinesPageOpened, 'Purchase Lines list should open when purchase orders already exist.'); - Assert.IsFalse(PurchaseOrderPageOpened, 'Purchase Order card should not open when purchase orders already exist.'); + // [WHEN] CalcNoOfProductionOrderRoutings is called with the purchase line + // [THEN] It returns 1, matching the single routing line shown by the drill-down (not the total operations of the prod order line) + Assert.AreEqual( + 1, SubcProdOFactboxMgmt.CalcNoOfProductionOrderRoutings(PurchaseLine), + 'CalcNoOfProductionOrderRoutings must equal the number of routing lines opened by the drill-down (exactly one for a subcontracting purchase line).'); end; [Test] @@ -2718,7 +817,7 @@ codeunit 139989 "Subc. Subcontracting Test" PriceWithStdTask: Decimal; SecondOperationNo: Code[10]; begin - // [SCENARIO 633226] Standard Task Code propagates from Routing → Prod. Order Routing → Subcontracting Worksheet, + // [SCENARIO 633226] Standard Task Code propagates from Routing → Prod. Order Routing → Subcontracting Worksheet, // is editable on the worksheet, and drives Subcontractor Price lookup. Editing or clearing it on a worksheet // line re-applies the matching subcontractor price; carrying out creates Purchase Lines with the correct unit costs. @@ -3390,7 +1489,7 @@ codeunit 139989 "Subc. Subcontracting Test" SubcPriceManagement.GetSubcPriceForReqLine(RequisitionLine, ''); // [THEN] Direct Unit Cost = price-list cost * Qty-per-UoM (1000 * 10 = 10000), - // not price-list cost * total base quantity (1000 * 30 = 30000 — the pre-fix behavior). + // not price-list cost * total base quantity (1000 * 30 = 30000 — the pre-fix behavior). Assert.AreEqual( PriceListUnitCost * QtyPerSet, RequisitionLine."Direct Unit Cost", 'Direct Unit Cost on the Subcontracting Worksheet must be derived from Qty. per Unit of Measure, not from total base quantity.'); @@ -3510,7 +1609,6 @@ codeunit 139989 "Subc. Subcontracting Test" ProdOrderComp.SetRange("Prod. Order No.", ProductionOrder."No."); ProdOrderComp.ModifyAll("Subcontracting Type", SubcontractingType); end; - var WorkCenter2: Record "Work Center"; Assert: Codeunit Assert; diff --git a/src/Apps/W1/Subcontracting/Test/src/Codeunits/Tests/SubcTransferOrderTest.Codeunit.al b/src/Apps/W1/Subcontracting/Test/src/Codeunits/Tests/SubcTransferOrderTest.Codeunit.al new file mode 100644 index 0000000000..555c850000 --- /dev/null +++ b/src/Apps/W1/Subcontracting/Test/src/Codeunits/Tests/SubcTransferOrderTest.Codeunit.al @@ -0,0 +1,1553 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ +namespace Microsoft.Manufacturing.Subcontracting.Test; + +using Microsoft.Finance.GeneralLedger.Setup; +using Microsoft.Finance.VAT.Setup; +using Microsoft.Foundation.Enums; +using Microsoft.Foundation.NoSeries; +using Microsoft.Inventory.Item; +using Microsoft.Inventory.Journal; +using Microsoft.Inventory.Ledger; +using Microsoft.Inventory.Location; +using Microsoft.Inventory.Planning; +using Microsoft.Inventory.Requisition; +using Microsoft.Inventory.Tracking; +using Microsoft.Inventory.Transfer; +using Microsoft.Manufacturing.Capacity; +using Microsoft.Manufacturing.Document; +using Microsoft.Manufacturing.MachineCenter; +using Microsoft.Manufacturing.Planning; +using Microsoft.Manufacturing.ProductionBOM; +using Microsoft.Manufacturing.Routing; +using Microsoft.Manufacturing.Setup; +using Microsoft.Manufacturing.Subcontracting; +using Microsoft.Manufacturing.WorkCenter; +using Microsoft.Purchases.Archive; +using Microsoft.Purchases.Comment; +using Microsoft.Purchases.Document; +using Microsoft.Purchases.History; +using Microsoft.Purchases.Vendor; +using Microsoft.Sales.Customer; +using Microsoft.Sales.Document; +using Microsoft.Utilities; +using Microsoft.Warehouse.Document; +using Microsoft.Warehouse.Structure; +using System.TestLibraries.Utilities; + +codeunit 139990 "Subc. Transfer Order Test" +{ + // [FEATURE] Subcontracting Management + Subtype = Test; + TestPermissions = Disabled; + TestType = IntegrationTest; + + trigger OnRun() + begin + IsInitialized := false; + end; + + [Test] + [HandlerFunctions('DoNotConfirmShowCreatedPurchOrderForSubcontracting,HandleTransferOrder')] + procedure CreateTransferOrderFromSecondSubcontractingOrderOpensReusedTransferOrder() + var + Item: Record Item; + MachineCenter: array[2] of Record "Machine Center"; + ProdOrderRoutingLine: Record "Prod. Order Routing Line"; + ProductionOrder1: Record "Production Order"; + ProductionOrder2: Record "Production Order"; + PurchaseHeader1: Record "Purchase Header"; + PurchaseHeader2: Record "Purchase Header"; + PurchaseLine: Record "Purchase Line"; + TransferHeader: Record "Transfer Header"; + TransferLine: Record "Transfer Line"; + WorkCenter: array[2] of Record "Work Center"; + ProductionLocation: Record Location; + FirstTransferOrderNo: Code[20]; + SecondTransferOrderNo: Code[20]; + ReleasedProdOrderRtng: TestPage "Prod. Order Routing"; + PurchaseHeaderPage: TestPage "Purchase Order"; + begin + // [SCENARIO 634237] Creating transfer order for a second subcontracting PO should create and open a different transfer order. + + // [GIVEN] Subcontracting setup with transfer components and an initial subcontracting order with transfer order already created + Initialize(); + SubcontractingMgmtLibrary.UpdateManufacturingSetupWithSubcontractingLocation(); + SubcontractingMgmtLibrary.SetupInventorySetup(); + + Subcontracting := true; + UnitCostCalculation := UnitCostCalculation::Units; + CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); + CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); + UpdateProdBomAndRoutingWithRoutingLink(Item, WorkCenter[2]."No."); + SubcontractingMgmtLibrary.UpdateProdBomWithSubcontractingType(Item, "Subcontracting Type"::Transfer); + UpdateVendorWithSubcontractingLocationCode(WorkCenter[2]); + + LibraryWarehouse.CreateLocationWithInventoryPostingSetup(ProductionLocation); + SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( + ProductionOrder1, "Production Order Status"::Released, ProductionOrder1."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); + UpdateSubMgmtSetupWithReqWkshTemplate(); + SetAllProdOrderTransferComponentLocations(ProductionOrder1."No.", ProductionLocation.Code); + + SubcontractingMgmtLibrary.CreateTransferRoute(WorkCenter[2], ProductionOrder1); + + ProdOrderRoutingLine.SetRange("Prod. Order No.", ProductionOrder1."No."); + ProdOrderRoutingLine.SetRange("Work Center No.", WorkCenter[2]."No."); + ProdOrderRoutingLine.FindFirst(); + ReleasedProdOrderRtng.OpenView(); + ReleasedProdOrderRtng.GoToRecord(ProdOrderRoutingLine); + ReleasedProdOrderRtng.CreateSubcontracting.Invoke(); + ReleasedProdOrderRtng.Close(); + + PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); + PurchaseLine.SetRange("Prod. Order No.", ProductionOrder1."No."); + PurchaseLine.FindFirst(); + PurchaseHeader1.Get(PurchaseLine."Document Type", PurchaseLine."Document No."); + + PurchaseHeaderPage.OpenView(); + PurchaseHeaderPage.GoToRecord(PurchaseHeader1); + PurchaseHeaderPage.CreateTransfOrdToSubcontractor.Invoke(); + PurchaseHeaderPage.Close(); + + TransferHeader.SetRange("Subcontr. Purch. Order No.", PurchaseHeader1."No."); + Assert.IsTrue(TransferHeader.FindFirst(), 'Expected transfer order for the first subcontracting purchase order.'); + FirstTransferOrderNo := TransferHeader."No."; + Assert.AreEqual(PurchaseHeader1."No.", TransferHeader."Subcontr. Purch. Order No.", 'First transfer order must be linked to the first subcontracting purchase order.'); + + // [GIVEN] A second released production order for the same subcontracting setup + SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( + ProductionOrder2, "Production Order Status"::Released, ProductionOrder2."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); + SetAllProdOrderTransferComponentLocations(ProductionOrder2."No.", ProductionLocation.Code); + + ProdOrderRoutingLine.Reset(); + ProdOrderRoutingLine.SetRange("Prod. Order No.", ProductionOrder2."No."); + ProdOrderRoutingLine.SetRange("Work Center No.", WorkCenter[2]."No."); + ProdOrderRoutingLine.FindFirst(); + ReleasedProdOrderRtng.OpenView(); + ReleasedProdOrderRtng.GoToRecord(ProdOrderRoutingLine); + ReleasedProdOrderRtng.CreateSubcontracting.Invoke(); + ReleasedProdOrderRtng.Close(); + + PurchaseLine.Reset(); + PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); + PurchaseLine.SetRange("Prod. Order No.", ProductionOrder2."No."); + PurchaseLine.FindFirst(); + PurchaseHeader2.Get(PurchaseLine."Document Type", PurchaseLine."Document No."); + Assert.AreNotEqual(PurchaseHeader1."No.", PurchaseHeader2."No.", 'Second production order should create another subcontracting purchase order.'); + + // [WHEN] Creating transfer order from the second subcontracting purchase order + OpenedTransferOrderNo := ''; + PurchaseHeaderPage.OpenView(); + PurchaseHeaderPage.GoToRecord(PurchaseHeader2); + PurchaseHeaderPage.CreateTransfOrdToSubcontractor.Invoke(); + PurchaseHeaderPage.Close(); + + TransferHeader.Reset(); + TransferHeader.SetRange("Subcontr. Purch. Order No.", PurchaseHeader2."No."); + Assert.IsTrue(TransferHeader.FindFirst(), 'Expected transfer order for the second subcontracting purchase order.'); + SecondTransferOrderNo := TransferHeader."No."; + Assert.AreEqual(PurchaseHeader2."No.", TransferHeader."Subcontr. Purch. Order No.", 'Second transfer order must be linked to the second subcontracting purchase order.'); + + // [THEN] A new transfer order is opened for the second subcontracting purchase order and contains lines for the second production order + Assert.AreNotEqual(FirstTransferOrderNo, SecondTransferOrderNo, 'A different subcontracting purchase order must create a new transfer order.'); + Assert.AreEqual(SecondTransferOrderNo, OpenedTransferOrderNo, 'The transfer order opened from the second subcontracting PO must belong to that purchase order.'); + TransferHeader.Get(FirstTransferOrderNo); + Assert.AreEqual(PurchaseHeader1."No.", TransferHeader."Subcontr. Purch. Order No.", 'First transfer order must remain linked to the first subcontracting purchase order.'); + TransferHeader.Get(SecondTransferOrderNo); + Assert.AreEqual(PurchaseHeader2."No.", TransferHeader."Subcontr. Purch. Order No.", 'Second transfer order must remain linked to the second subcontracting purchase order.'); + + TransferLine.Reset(); + TransferLine.SetRange("Document No.", SecondTransferOrderNo); + TransferLine.SetRange("Subc. Prod. Order No.", ProductionOrder2."No."); + Assert.RecordIsNotEmpty(TransferLine); + end; + + [Test] + [HandlerFunctions('DoNotConfirmShowCreatedPurchOrderForSubcontracting,HandleTransferOrder')] + procedure CannotDeleteSubcontractingOrderWithAssociatedTransferOrder() + var + Item: Record Item; + MachineCenter: array[2] of Record "Machine Center"; + ProductionOrder: Record "Production Order"; + PurchaseHeader: Record "Purchase Header"; + PurchaseLine: Record "Purchase Line"; + TransferHeader: Record "Transfer Header"; + WorkCenter: array[2] of Record "Work Center"; + PurchaseHeaderPage: TestPage "Purchase Order"; + PurchaseOrderNo: Code[20]; + begin + // [SCENARIO 630806] Deleting a Subcontracting Order is blocked when an associated Transfer Order exists + Initialize(); + SubcontractingMgmtLibrary.UpdateManufacturingSetupWithSubcontractingLocation(); + + // [GIVEN] Some Parameters for Creation + Subcontracting := true; + UnitCostCalculation := UnitCostCalculation::Units; + + // [GIVEN] Work and Machine Centers, an Item with Routing and Prod. BOM configured for Transfer subcontracting + CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); + CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); + UpdateProdBomAndRoutingWithRoutingLink(Item, WorkCenter[2]."No."); + SubcontractingMgmtLibrary.UpdateProdBomWithSubcontractingType(Item, "Subcontracting Type"::Transfer); + UpdateVendorWithSubcontractingLocationCode(WorkCenter[2]); + + // [GIVEN] A Released Production Order (not created from a Purchase Order) + SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( + ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); + UpdateSubMgmtSetupWithReqWkshTemplate(); + SubcontractingMgmtLibrary.UpdateProdOrderCompWithLocationCode(ProductionOrder."No."); + SubcontractingMgmtLibrary.CreateTransferRoute(WorkCenter[2], ProductionOrder); + + // [GIVEN] A Subcontracting Order created from the Production Order Routing + SubcontractingMgmtLibrary.CreateSubcontractingOrderFromProdOrderRtngPage(Item."Routing No.", WorkCenter[2]."No."); + PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); + PurchaseLine.SetRange("Prod. Order No.", ProductionOrder."No."); + PurchaseLine.FindFirst(); + PurchaseHeader.Get(PurchaseLine."Document Type", PurchaseLine."Document No."); + PurchaseOrderNo := PurchaseHeader."No."; + + // [GIVEN] A Transfer Order created from the Subcontracting Order + PurchaseHeaderPage.OpenView(); + PurchaseHeaderPage.GoToRecord(PurchaseHeader); + PurchaseHeaderPage.CreateTransfOrdToSubcontractor.Invoke(); + + // [WHEN] The Subcontracting Order is attempted to be deleted + asserterror PurchaseHeader.Delete(true); + + // [THEN] An error is raised and the Transfer Order still exists + TransferHeader.SetRange("Subcontr. Purch. Order No.", PurchaseOrderNo); + Assert.RecordIsNotEmpty(TransferHeader); + end; + + [Test] + [HandlerFunctions('DoNotConfirmShowCreatedPurchOrderForSubcontracting,HandleTransferOrder')] + procedure TestCreationOfSubcontrTransferOrderFromSubcontrPurchOrder() + var + Item: Record Item; + MachineCenter: array[2] of Record "Machine Center"; + ProdOrderComp: Record "Prod. Order Component"; + ProductionOrder: Record "Production Order"; + PurchaseHeader: Record "Purchase Header"; + PurchaseLine: Record "Purchase Line"; + TransferLine: Record "Transfer Line"; + WorkCenter: array[2] of Record "Work Center"; + PurchaseHeaderPage: TestPage "Purchase Order"; + begin + // [SCENARIO] Create Subcontracting Transfer Order directly from Subcontracting Purchase Order + // [SCENARIO] and Transfer additional Line with marked Component ; + + // [GIVEN] Complete Setup of Manufacturing, include Work- and Machine Centers, Item + Initialize(); + SubcontractingMgmtLibrary.UpdateManufacturingSetupWithSubcontractingLocation(); + SubcontractingMgmtLibrary.SetupInventorySetup(); + + // [GIVEN] Some Parameters for Creation + Subcontracting := true; + UnitCostCalculation := UnitCostCalculation::Units; + + // [GIVEN] + CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); + + // [GIVEN] Create Item for Production include Routing and Prod. BOM + CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); + + UpdateProdBomAndRoutingWithRoutingLink(Item, WorkCenter[2]."No."); + + SubcontractingMgmtLibrary.UpdateProdBomWithSubcontractingType(Item, "Subcontracting Type"::Transfer); + + SubcontractingMgmtLibrary.UpdateVendorWithSubcontractingLocationCode(WorkCenter[2]); + + SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( + ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); + + UpdateSubMgmtSetupWithReqWkshTemplate(); + + SubcontractingMgmtLibrary.UpdateProdOrderCompWithLocationCode(ProductionOrder."No."); + SubcontractingMgmtLibrary.CreateTransferRoute(WorkCenter[2], ProductionOrder); + + // [WHEN] Create Subcontracting Purchase Order from Prod. Order Routing + SubcontractingMgmtLibrary.CreateSubcontractingOrderFromProdOrderRtngPage(Item."Routing No.", WorkCenter[2]."No."); + + // [WHEN] Create Transfer Order + PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); + PurchaseLine.SetRange("Prod. Order No.", ProductionOrder."No."); + PurchaseLine.FindFirst(); + + PurchaseHeader.SetRange("Document Type", PurchaseLine."Document Type"::Order); + PurchaseHeader.SetRange("No.", PurchaseLine."Document No."); + PurchaseHeader.FindFirst(); + PurchaseHeaderPage.OpenView(); + PurchaseHeaderPage.GoToRecord(PurchaseHeader); + PurchaseHeaderPage.CreateTransfOrdToSubcontractor.Invoke(); + + // [THEN] Check if Purchase Line with additional Component for Subcontracting Type exists + ProdOrderComp.SetRange("Prod. Order No.", ProductionOrder."No."); +#pragma warning disable AA0210 + ProdOrderComp.SetRange("Subcontracting Type", ProdOrderComp."Subcontracting Type"::Transfer); +#pragma warning restore AA0210 + ProdOrderComp.FindFirst(); + + TransferLine.SetRange("Subc. Prod. Order No.", ProductionOrder."No."); + TransferLine.SetRange("Item No.", ProdOrderComp."Item No."); + + Assert.RecordIsNotEmpty(TransferLine); + end; + + [Test] + [HandlerFunctions('DoNotConfirmShowCreatedPurchOrderForSubcontracting,HandleTransferOrder')] + procedure TestLocationInSubContractorTransferOrderAndComponentLine() + var + Item: Record Item; + MachineCenter: array[2] of Record "Machine Center"; + ProdOrderComp: Record "Prod. Order Component"; + ProductionOrder: Record "Production Order"; + PurchaseHeader: Record "Purchase Header"; + PurchaseLine: Record "Purchase Line"; + TransferHeader: Record "Transfer Header"; + TransferLine: Record "Transfer Line"; + WorkCenter: array[2] of Record "Work Center"; + CompLocation, TransferFrom : Code[10]; + PurchaseHeaderPage: TestPage "Purchase Order"; + begin + // [SCENARIO] Create Subcontracting Transfer Order directly from Subcontracting Purchase Order + // [SCENARIO] check if Component Line Location Code and Transfer Form Code are equal + + // [GIVEN] Complete Setup of Manufacturing, include Work- and Machine Centers, Item + Initialize(); + SubcontractingMgmtLibrary.UpdateManufacturingSetupWithSubcontractingLocation(); + SubcontractingMgmtLibrary.SetupInventorySetup(); + + // [GIVEN] Some Parameters for Creation + Subcontracting := true; + UnitCostCalculation := UnitCostCalculation::Units; + + // [GIVEN] + CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); + + // [GIVEN] Create Item for Production include Routing and Prod. BOM + CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); + + UpdateProdBomAndRoutingWithRoutingLink(Item, WorkCenter[2]."No."); + + SubcontractingMgmtLibrary.UpdateProdBomWithSubcontractingType(Item, "Subcontracting Type"::Transfer); + + SubcontractingMgmtLibrary.UpdateVendorWithSubcontractingLocationCode(WorkCenter[2]); + + SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( + ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); + + UpdateSubMgmtSetupWithReqWkshTemplate(); + + SubcontractingMgmtLibrary.UpdateProdOrderCompWithLocationCode(ProductionOrder."No."); + SubcontractingMgmtLibrary.CreateTransferRoute(WorkCenter[2], ProductionOrder); + + // [WHEN] Create Subcontracting Purchase Order from Prod. Order Routing + SubcontractingMgmtLibrary.CreateSubcontractingOrderFromProdOrderRtngPage(Item."Routing No.", WorkCenter[2]."No."); + + ProdOrderComp.SetRange("Prod. Order No.", ProductionOrder."No."); +#pragma warning disable AA0210 + ProdOrderComp.SetRange("Subcontracting Type", "Subcontracting Type"::Transfer); +#pragma warning restore AA0210 + ProdOrderComp.FindFirst(); + + //[GIVEN] Keep Location Code for later Check + CompLocation := ProdOrderComp."Location Code"; + + // [WHEN] Create Transfer Order + PurchaseLine.SetRange("Document Type", "Purchase Document Type"::Order); + PurchaseLine.SetRange("Prod. Order No.", ProductionOrder."No."); + PurchaseLine.FindFirst(); + + PurchaseHeader.SetRange("Document Type", PurchaseLine."Document Type"::Order); + PurchaseHeader.SetRange("No.", PurchaseLine."Document No."); + PurchaseHeader.FindFirst(); + PurchaseHeaderPage.OpenView(); + PurchaseHeaderPage.GoToRecord(PurchaseHeader); + PurchaseHeaderPage.CreateTransfOrdToSubcontractor.Invoke(); + + TransferLine.SetRange("Subc. Prod. Order No.", ProductionOrder."No."); + TransferLine.SetRange("Item No.", ProdOrderComp."Item No."); + TransferLine.SetRange("Subc. Prod. Ord. Comp Line No.", ProdOrderComp."Line No."); + TransferLine.FindFirst(); + + TransferHeader.Get(TransferLine."Document No."); + //[GIVEN] Keep Location Code for later Check + TransferFrom := TransferHeader."Transfer-from Code"; + + // [THEN] Check if Component Location Code and Transfer Form Code are equal + Assert.AreEqual(CompLocation, TransferFrom, 'Transfer-from Code is not expected'); + end; + + [Test] + [HandlerFunctions('DoNotConfirmShowCreatedPurchOrderForSubcontracting,HandleTransferOrder')] + procedure TestLocationInSubContractorTransferOrderAndComponentLineWithChangeCompLineLocation() + var + Item: Record Item; + Location: Record Location; + MachineCenter: array[2] of Record "Machine Center"; + ProdOrderComp: Record "Prod. Order Component"; + ProductionOrder: Record "Production Order"; + PurchaseHeader: Record "Purchase Header"; + PurchaseLine: Record "Purchase Line"; + TransferHeader: Record "Transfer Header"; + TransferLine: Record "Transfer Line"; + WorkCenter: array[2] of Record "Work Center"; + CompLocation, TransferFrom : Code[10]; + PurchaseHeaderPage: TestPage "Purchase Order"; + begin + // [SCENARIO] Create Subcontracting Transfer Order directly from Subcontracting Purchase Order + // [SCENARIO] Change Component Location Code and check if Component Line Location Code and Transfer Form Code are equal + + // [GIVEN] Complete Setup of Manufacturing, include Work- and Machine Centers, Item + Initialize(); + SubcontractingMgmtLibrary.UpdateManufacturingSetupWithSubcontractingLocation(); + SubcontractingMgmtLibrary.SetupInventorySetup(); + + // [GIVEN] Some Parameters for Creation + Subcontracting := true; + UnitCostCalculation := UnitCostCalculation::Units; + + // [GIVEN] + CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); + + // [GIVEN] Create Item for Production include Routing and Prod. BOM + CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); + + UpdateProdBomAndRoutingWithRoutingLink(Item, WorkCenter[2]."No."); + + SubcontractingMgmtLibrary.UpdateProdBomWithSubcontractingType(Item, "Subcontracting Type"::Transfer); + + SubcontractingMgmtLibrary.UpdateVendorWithSubcontractingLocationCode(WorkCenter[2]); + + SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( + ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); + + UpdateSubMgmtSetupWithReqWkshTemplate(); + + SubcontractingMgmtLibrary.UpdateProdOrderCompWithLocationCode(ProductionOrder."No."); + SubcontractingMgmtLibrary.CreateTransferRoute(WorkCenter[2], ProductionOrder); + + // [WHEN] Create Subcontracting Purchase Order from Prod. Order Routing + SubcontractingMgmtLibrary.CreateSubcontractingOrderFromProdOrderRtngPage(Item."Routing No.", WorkCenter[2]."No."); + + LibraryWarehouse.CreateLocationWithInventoryPostingSetup(Location); + ProdOrderComp.SetRange("Prod. Order No.", ProductionOrder."No."); +#pragma warning disable AA0210 + ProdOrderComp.SetRange("Subcontracting Type", "Subcontracting Type"::Transfer); +#pragma warning restore AA0210 + ProdOrderComp.FindFirst(); + ProdOrderComp."Location Code" := Location.Code; + ProdOrderComp.Modify(); + + //[GIVEN] Keep Location Code for later Check + CompLocation := ProdOrderComp."Location Code"; + + // [WHEN] Create Transfer Order + PurchaseLine.SetRange("Document Type", "Purchase Document Type"::Order); + PurchaseLine.SetRange("Prod. Order No.", ProductionOrder."No."); + PurchaseLine.FindFirst(); + + PurchaseHeader.SetRange("Document Type", PurchaseLine."Document Type"::Order); + PurchaseHeader.SetRange("No.", PurchaseLine."Document No."); + PurchaseHeader.FindFirst(); + PurchaseHeaderPage.OpenView(); + PurchaseHeaderPage.GoToRecord(PurchaseHeader); + PurchaseHeaderPage.CreateTransfOrdToSubcontractor.Invoke(); + + TransferLine.SetRange("Subc. Prod. Order No.", ProductionOrder."No."); + TransferLine.SetRange("Item No.", ProdOrderComp."Item No."); + TransferLine.SetRange("Subc. Prod. Ord. Comp Line No.", ProdOrderComp."Line No."); + TransferLine.FindFirst(); + + TransferHeader.Get(TransferLine."Document No."); + + //[GIVEN] Keep Location Code for later Check + TransferFrom := TransferHeader."Transfer-from Code"; + + // [THEN] Check if Component Location Code and Transfer Form Code are equal + Assert.AreEqual(CompLocation, TransferFrom, 'Transfer-from Code is not expected'); + end; + + [Test] + [HandlerFunctions('DoNotConfirmShowCreatedPurchOrderForSubcontracting,HandleTransferOrder,HandleCreateTransferOrderMsg')] + procedure CheckTransferOrderFromSubcontrAndReturnTransferOrderFromSubcontractorPurchOrder() + var + Bin: Record Bin; + Item: Record Item; + Location: Record Location; + MachineCenter: array[2] of Record "Machine Center"; + ProdOrderComp: Record "Prod. Order Component"; + ProductionOrder: Record "Production Order"; + PurchaseHeader: Record "Purchase Header"; + PurchaseLine: Record "Purchase Line"; + TransferHeader: Record "Transfer Header"; + TransferLine: Record "Transfer Line"; + WorkCenter: array[2] of Record "Work Center"; + TransferFrom1, TransferFrom2, TransferTo1, TransferTo2 : Code[10]; + PurchaseHeaderPage: TestPage "Purchase Order"; + TransferOrder: TestPage "Transfer Order"; + begin + // [SCENARIO] Create Subcontracting Transfer Order directly from Subcontracting Purchase Order + // [SCENARIO] Post transfer Order and Create Return Transfer Order + // [SCENARIO] check if Transfer-from and Transfer-to Locations are reversed + + // [GIVEN] Complete Setup of Manufacturing, include Work- and Machine Centers, Item + Initialize(); + SubcontractingMgmtLibrary.UpdateManufacturingSetupWithSubcontractingLocation(); + SubcontractingMgmtLibrary.SetupInventorySetup(); + + // [GIVEN] Some Parameters for Creation + Subcontracting := true; + UnitCostCalculation := UnitCostCalculation::Units; + + // [GIVEN] + CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); + + // [GIVEN] Create Item for Production include Routing and Prod. BOM + CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); + + UpdateProdBomAndRoutingWithRoutingLink(Item, WorkCenter[2]."No."); + + SubcontractingMgmtLibrary.UpdateProdBomWithSubcontractingType(Item, "Subcontracting Type"::Transfer); + + SubcontractingMgmtLibrary.UpdateVendorWithSubcontractingLocationCode(WorkCenter[2]); + + SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( + ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); + + UpdateSubMgmtSetupWithReqWkshTemplate(); + + SubcontractingMgmtLibrary.UpdateProdOrderCompWithLocationCode(ProductionOrder."No."); + SubcontractingMgmtLibrary.CreateTransferRoute(WorkCenter[2], ProductionOrder); + + // [WHEN] Create Subcontracting Purchase Order from Prod. Order Routing + SubcontractingMgmtLibrary.CreateSubcontractingOrderFromProdOrderRtngPage(Item."Routing No.", WorkCenter[2]."No."); + + // [WHEN] Create Transfer Order + PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); + PurchaseLine.SetRange("No.", ProductionOrder."Source No."); + PurchaseLine.SetRange(Type, "Purchase Line Type"::Item); + PurchaseLine.SetRange("Prod. Order No.", ProductionOrder."No."); + PurchaseLine.FindFirst(); + + PurchaseHeader.SetRange("Document Type", PurchaseLine."Document Type"::Order); + PurchaseHeader.SetRange("No.", PurchaseLine."Document No."); + PurchaseHeader.FindFirst(); + PurchaseHeaderPage.OpenView(); + PurchaseHeaderPage.GoToRecord(PurchaseHeader); + PurchaseHeaderPage.CreateTransfOrdToSubcontractor.Invoke(); + + // [THEN] Check if Purchase Line with additional Component for Subcontracting Type exists + ProdOrderComp.SetRange("Prod. Order No.", ProductionOrder."No."); +#pragma warning disable AA0210 + ProdOrderComp.SetRange("Subcontracting Type", ProdOrderComp."Subcontracting Type"::Transfer); +#pragma warning restore AA0210 + ProdOrderComp.FindFirst(); + + TransferLine.SetRange("Subc. Prod. Order No.", ProductionOrder."No."); + TransferLine.SetRange("Item No.", ProdOrderComp."Item No."); + TransferLine.SetRange("Subc. Prod. Ord. Comp Line No.", ProdOrderComp."Line No."); + TransferLine.FindFirst(); + + TransferHeader.SetRange("No.", TransferLine."Document No."); + TransferHeader.FindFirst(); + + //[GIVEN]create Inventory for Transfer + Item.Get(ProdOrderComp."Item No."); + Location.Get(TransferHeader."Transfer-from Code"); + CreateInventory(Item, Location, Bin, ProdOrderComp."Expected Qty. (Base)"); + + //[GIVEN] Keep Transfer Locations values for later Check + TransferFrom1 := TransferHeader."Transfer-from Code"; + TransferTo1 := TransferHeader."Transfer-to Code"; + + //[GIVEN] Enable direct transfer for posting + TransferHeader.Validate("Direct Transfer", true); + TransferHeader.Modify(true); + + //[WHEN] Post Transfer Order + TransferOrder.OpenView(); + TransferOrder.GoToRecord(TransferHeader); + TransferOrder.Post.Invoke(); + + //[WHEN] Create Return Transfer Order + PurchaseHeaderPage.GoToRecord(PurchaseHeader); + PurchaseHeaderPage.CreateReturnFromSubcontractor.Invoke(); + + ProdOrderComp.SetRange("Prod. Order No.", ProductionOrder."No."); +#pragma warning disable AA0210 + ProdOrderComp.SetRange("Subcontracting Type", ProdOrderComp."Subcontracting Type"::Transfer); +#pragma warning restore AA0210 + ProdOrderComp.FindFirst(); + + TransferLine.SetRange("Subc. Prod. Order No.", ProductionOrder."No."); + TransferLine.SetRange("Item No.", ProdOrderComp."Item No."); + TransferLine.SetRange("Subc. Prod. Ord. Comp Line No.", ProdOrderComp."Line No."); + TransferLine.FindFirst(); + + TransferHeader.SetRange("No.", TransferLine."Document No."); + TransferHeader.FindFirst(); + + //[GIVEN] Keep Transfer Locations values for later Check + TransferFrom2 := TransferHeader."Transfer-from Code"; + TransferTo2 := TransferHeader."Transfer-to Code"; + + //[THEN] Check if Transfer-from and Transfer-to Locations are reversed + Assert.AreEqual(TransferFrom1, TransferTo2, 'Transfer-from and Transfer-to Locations are reversed'); + Assert.AreEqual(TransferTo1, TransferFrom2, 'Transfer-from and Transfer-to Locations are reversed'); + end; + + [Test] + [HandlerFunctions('DoNotConfirmShowCreatedPurchOrderForSubcontracting,HandleTransferOrder')] + procedure TestExpectedErrorOnChangingLocationCodeInProdOrderCompWithTransferOrderFromSubcontrPurchOrder() + var + Item: Record Item; + Location: Record Location; + MachineCenter: array[2] of Record "Machine Center"; + ProdOrderComp: Record "Prod. Order Component"; + ProductionOrder: Record "Production Order"; + PurchaseHeader: Record "Purchase Header"; + PurchaseLine: Record "Purchase Line"; + TransferLine: Record "Transfer Line"; + WorkCenter: array[2] of Record "Work Center"; + ProdOrderCompPage: TestPage "Prod. Order Components"; + PurchaseHeaderPage: TestPage "Purchase Order"; + begin + // [SCENARIO] Create Subcontracting Transfer Order directly from Subcontracting Purchase Order + // [SCENARIO] and Transfer additional Line with marked Component ; + // [SCENARIO] Expected Error on changing Location Code in Prod. Order Component + + // [GIVEN] Complete Setup of Manufacturing, include Work- and Machine Centers, Item + Initialize(); + SubcontractingMgmtLibrary.UpdateManufacturingSetupWithSubcontractingLocation(); + // [GIVEN] Some Parameters for Creation + Subcontracting := true; + UnitCostCalculation := UnitCostCalculation::Units; + + // [GIVEN] + CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); + LibraryWarehouse.CreateLocation(Location); + + // [GIVEN] Create Item for Production include Routing and Prod. BOM + CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); + + UpdateProdBomAndRoutingWithRoutingLink(Item, WorkCenter[2]."No."); + + SubcontractingMgmtLibrary.UpdateProdBomWithSubcontractingType(Item, "Subcontracting Type"::Transfer); + + SubcontractingMgmtLibrary.UpdateVendorWithSubcontractingLocationCode(WorkCenter[2]); + + SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( + ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); + + UpdateSubMgmtSetupWithReqWkshTemplate(); + + SubcontractingMgmtLibrary.UpdateProdOrderCompWithLocationCode(ProductionOrder."No."); + + SubcontractingMgmtLibrary.CreateTransferRoute(WorkCenter[2], ProductionOrder); + + // [WHEN] Create Subcontracting Purchase Order from Prod. Order Routing + SubcontractingMgmtLibrary.CreateSubcontractingOrderFromProdOrderRtngPage(Item."Routing No.", WorkCenter[2]."No."); + + // [WHEN] Create Transfer Order + PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); + PurchaseLine.SetRange("Prod. Order No.", ProductionOrder."No."); + PurchaseLine.FindFirst(); + + PurchaseHeader.SetRange("Document Type", PurchaseLine."Document Type"::Order); + PurchaseHeader.SetRange("No.", PurchaseLine."Document No."); + PurchaseHeader.FindFirst(); + PurchaseHeaderPage.OpenView(); + PurchaseHeaderPage.GoToRecord(PurchaseHeader); + PurchaseHeaderPage.CreateTransfOrdToSubcontractor.Invoke(); + + // [THEN] Check if Purchase Line with additional Component for Subcontracting Type exists + ProdOrderComp.SetRange("Prod. Order No.", ProductionOrder."No."); +#pragma warning disable AA0210 + ProdOrderComp.SetRange("Subcontracting Type", ProdOrderComp."Subcontracting Type"::Transfer); +#pragma warning restore AA0210 + ProdOrderComp.FindFirst(); + + TransferLine.SetRange("Subc. Prod. Order No.", ProductionOrder."No."); + TransferLine.SetRange("Item No.", ProdOrderComp."Item No."); + + Assert.RecordIsNotEmpty(TransferLine); + + ProdOrderCompPage.OpenEdit(); + ProdOrderCompPage.GoToRecord(ProdOrderComp); + asserterror ProdOrderCompPage."Location Code".SetValue(Location.Code); + Assert.ExpectedError('The component has already been assigned to the subcontracting transfer order'); + end; + + [Test] + [HandlerFunctions('DoNotConfirmShowCreatedPurchOrderForSubcontracting,HandleTransferOrder')] + procedure TestReceiptDateFromTransferOrderLineFromSubcontrPurchOrderIsEquallyToProdOrderCompDueDate() + var + Item: Record Item; + Location: Record Location; + MachineCenter: array[2] of Record "Machine Center"; + ProdOrderComp: Record "Prod. Order Component"; + ProductionOrder: Record "Production Order"; + PurchaseHeader: Record "Purchase Header"; + PurchaseLine: Record "Purchase Line"; + TransferLine: Record "Transfer Line"; + WorkCenter: array[2] of Record "Work Center"; + ManufacturingSetup: Record "Manufacturing Setup"; + ExpectedDate: Date; + PurchaseHeaderPage: TestPage "Purchase Order"; + begin + // [SCENARIO] Create Subcontracting Transfer Order directly from Subcontracting Purchase Order + // [SCENARIO] and Transfer additional Line with marked Component ; + // [SCENARIO] Expected Error on changing Location Code in Prod. Order Component + + // [GIVEN] Complete Setup of Manufacturing, include Work- and Machine Centers, Item + Initialize(); + UpdateSubWhseHandlingTimeInSubManagementSetup(); + SubcontractingMgmtLibrary.UpdateManufacturingSetupWithSubcontractingLocation(); + + // [GIVEN] Some Parameters for Creation + Subcontracting := true; + UnitCostCalculation := UnitCostCalculation::Units; + + // [GIVEN] + CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); + LibraryWarehouse.CreateLocation(Location); + + // [GIVEN] Create Item for Production include Routing and Prod. BOM + CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); + + UpdateProdBomAndRoutingWithRoutingLink(Item, WorkCenter[2]."No."); + + SubcontractingMgmtLibrary.UpdateProdBomWithSubcontractingType(Item, "Subcontracting Type"::Transfer); + + SubcontractingMgmtLibrary.UpdateVendorWithSubcontractingLocationCode(WorkCenter[2]); + + SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( + ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); + + UpdateSubMgmtSetupWithReqWkshTemplate(); + + SubcontractingMgmtLibrary.UpdateProdOrderCompWithLocationCode(ProductionOrder."No."); + + UpdateProdOrderCompDueDate(ProductionOrder."No."); + + SubcontractingMgmtLibrary.CreateTransferRoute(WorkCenter[2], ProductionOrder); + + // [WHEN] Create Subcontracting Purchase Order from Prod. Order Routing + SubcontractingMgmtLibrary.CreateSubcontractingOrderFromProdOrderRtngPage(Item."Routing No.", WorkCenter[2]."No."); + + // [WHEN] Create Transfer Order + PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); + PurchaseLine.SetRange("Prod. Order No.", ProductionOrder."No."); + PurchaseLine.FindFirst(); + + PurchaseHeader.SetRange("Document Type", PurchaseLine."Document Type"::Order); + PurchaseHeader.SetRange("No.", PurchaseLine."Document No."); + PurchaseHeader.FindFirst(); + PurchaseHeaderPage.OpenView(); + PurchaseHeaderPage.GoToRecord(PurchaseHeader); + PurchaseHeaderPage.CreateTransfOrdToSubcontractor.Invoke(); + + // [THEN] Compare Due Date from Prod Order Comp with Receipt Date from Subc. Transfer Line + ProdOrderComp.SetRange("Prod. Order No.", ProductionOrder."No."); +#pragma warning disable AA0210 + ProdOrderComp.SetRange("Subcontracting Type", ProdOrderComp."Subcontracting Type"::Transfer); +#pragma warning restore AA0210 + ProdOrderComp.FindFirst(); + + TransferLine.SetRange("Subc. Prod. Order No.", ProductionOrder."No."); + TransferLine.SetRange("Item No.", ProdOrderComp."Item No."); + TransferLine.FindFirst(); + + ManufacturingSetup.Get(); + + ExpectedDate := CalcDate(ManufacturingSetup."Subc. Inb. Whse. Handling Time", TransferLine."Receipt Date"); + + Assert.AreEqual(ExpectedDate, ProdOrderComp."Due Date", ''); + + end; + + [Test] + [HandlerFunctions('DoNotConfirmShowCreatedPurchOrderForSubcontracting,HandleTransferOrder')] + procedure TestLocationAndBinCodeIsSetFromOriginBinCodeAfterDeletingTransferOrder() + var + Item: Record Item; + Location: Record Location; + MachineCenter: array[2] of Record "Machine Center"; + ProdOrderComp: Record "Prod. Order Component"; + ProductionOrder: Record "Production Order"; + PurchaseHeader: Record "Purchase Header"; + PurchaseLine: Record "Purchase Line"; + TransferHeader: Record "Transfer Header"; + TransferLine: Record "Transfer Line"; + WorkCenter: array[2] of Record "Work Center"; + LocationCode: Code[10]; + BinCode: Code[20]; + PurchaseHeaderPage: TestPage "Purchase Order"; + begin + // [SCENARIO] Create Subcontracting Transfer Order directly from Subcontracting Purchase Order + // [SCENARIO] and Transfer additional Line with marked Component ; + // [SCENARIO] Expected Bin Code is filled with Original Bin Code + + // [GIVEN] Complete Setup of Manufacturing, include Work- and Machine Centers, Item + Initialize(); + SubcontractingMgmtLibrary.UpdateManufacturingSetupWithSubcontractingLocation(); + + // [GIVEN] Some Parameters for Creation + Subcontracting := true; + UnitCostCalculation := UnitCostCalculation::Units; + + // [GIVEN] + CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); + LibraryWarehouse.CreateLocation(Location); + + // [GIVEN] Create Item for Production include Routing and Prod. BOM + CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); + + UpdateProdBomAndRoutingWithRoutingLink(Item, WorkCenter[2]."No."); + + SubcontractingMgmtLibrary.UpdateProdBomWithSubcontractingType(Item, "Subcontracting Type"::Transfer); + + SubcontractingMgmtLibrary.UpdateVendorWithSubcontractingLocationCode(WorkCenter[2]); + + SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( + ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); + + UpdateSubMgmtSetupWithReqWkshTemplate(); + + UpdateProdOrderCompWithLocationAndBinCode(ProductionOrder."No.", LocationCode, BinCode); + + SubcontractingMgmtLibrary.CreateTransferRoute(WorkCenter[2], ProductionOrder); + + // [WHEN] Create Subcontracting Purchase Order from Prod. Order Routing + SubcontractingMgmtLibrary.CreateSubcontractingOrderFromProdOrderRtngPage(Item."Routing No.", WorkCenter[2]."No."); + + // [WHEN] Create Transfer Order + PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); + PurchaseLine.SetRange("Prod. Order No.", ProductionOrder."No."); + PurchaseLine.FindFirst(); + + PurchaseHeader.SetRange("Document Type", PurchaseLine."Document Type"::Order); + PurchaseHeader.SetRange("No.", PurchaseLine."Document No."); + PurchaseHeader.FindFirst(); + PurchaseHeaderPage.OpenView(); + PurchaseHeaderPage.GoToRecord(PurchaseHeader); + PurchaseHeaderPage.CreateTransfOrdToSubcontractor.Invoke(); + + // [THEN] Check if Purchase Line with additional Component for Subcontracting Type exists + ProdOrderComp.SetRange("Prod. Order No.", ProductionOrder."No."); +#pragma warning disable AA0210 + ProdOrderComp.SetRange("Subcontracting Type", ProdOrderComp."Subcontracting Type"::Transfer); +#pragma warning restore AA0210 + ProdOrderComp.FindFirst(); + + TransferLine.SetRange("Subc. Prod. Order No.", ProductionOrder."No."); + TransferLine.SetRange("Item No.", ProdOrderComp."Item No."); + + TransferLine.FindFirst(); + + TransferHeader.Get(TransferLine."Document No."); + TransferHeader.Delete(true); + + ProdOrderComp.SetRange("Prod. Order No.", ProductionOrder."No."); +#pragma warning disable AA0210 + ProdOrderComp.SetRange("Subcontracting Type", ProdOrderComp."Subcontracting Type"::Transfer); +#pragma warning restore AA0210 + ProdOrderComp.FindFirst(); + + Assert.AreEqual(ProdOrderComp."Location Code", LocationCode, ''); + Assert.AreEqual(ProdOrderComp."Bin Code", BinCode, ''); + end; + + [Test] + [HandlerFunctions('DoNotConfirmShowCreatedPurchOrderForSubcontracting,HandleTransferOrder')] + procedure CheckBtnTrackingSpecificationOnProdOrderCompOnExistingReserveInTransferLine() + var + Item: Record Item; + Location: Record Location; + MachineCenter: array[2] of Record "Machine Center"; + ProdOrderComp: Record "Prod. Order Component"; + ProductionOrder: Record "Production Order"; + PurchaseHeader: Record "Purchase Header"; + PurchaseLine: Record "Purchase Line"; + TransferLine: Record "Transfer Line"; + WorkCenter: array[2] of Record "Work Center"; + ProdOrderCompPage: TestPage "Prod. Order Components"; + PurchaseHeaderPage: TestPage "Purchase Order"; + ExpectedErrorMsg: Text; + begin + // [SCENARIO] Create Subcontracting Transfer Order directly from Subcontracting Purchase Order + // [SCENARIO] and Transfer additional Line with marked Component ; + // [SCENARIO] Expected Error on open Item Tracking Lines in Prod. Order Component + + // [GIVEN] Complete Setup of Manufacturing, include Work- and Machine Centers, Item + Initialize(); + SubcontractingMgmtLibrary.UpdateManufacturingSetupWithSubcontractingLocation(); + + // [GIVEN] Some Parameters for Creation + Subcontracting := true; + UnitCostCalculation := UnitCostCalculation::Units; + + // [GIVEN] + CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); + LibraryWarehouse.CreateLocation(Location); + + // [GIVEN] Create Item for Production include Routing and Prod. BOM + CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); + + UpdateProdBomAndRoutingWithRoutingLink(Item, WorkCenter[2]."No."); + + SubcontractingMgmtLibrary.UpdateProdBomWithSubcontractingType(Item, "Subcontracting Type"::Transfer); + + SubcontractingMgmtLibrary.UpdateVendorWithSubcontractingLocationCode(WorkCenter[2]); + + SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( + ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); + + UpdateSubMgmtSetupWithReqWkshTemplate(); + + SubcontractingMgmtLibrary.UpdateProdOrderCompWithLocationCode(ProductionOrder."No."); + + SubcontractingMgmtLibrary.CreateTransferRoute(WorkCenter[2], ProductionOrder); + + // [WHEN] Create Subcontracting Purchase Order from Prod. Order Routing + SubcontractingMgmtLibrary.CreateSubcontractingOrderFromProdOrderRtngPage(Item."Routing No.", WorkCenter[2]."No."); + + // [WHEN] Create Transfer Order + PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); + PurchaseLine.SetRange("Prod. Order No.", ProductionOrder."No."); + PurchaseLine.FindFirst(); + + PurchaseHeader.SetRange("Document Type", PurchaseLine."Document Type"::Order); + PurchaseHeader.SetRange("No.", PurchaseLine."Document No."); + PurchaseHeader.FindFirst(); + PurchaseHeaderPage.OpenView(); + PurchaseHeaderPage.GoToRecord(PurchaseHeader); + PurchaseHeaderPage.CreateTransfOrdToSubcontractor.Invoke(); + + // [THEN] Check if Purchase Line with additional Component for Subcontracting Type exists, Mock Reservation Entries on TransferLine and try to open Item Tracking Lines from Prod order Comp. Page + ProdOrderComp.SetRange("Prod. Order No.", ProductionOrder."No."); +#pragma warning disable AA0210 + ProdOrderComp.SetRange("Subcontracting Type", ProdOrderComp."Subcontracting Type"::Transfer); +#pragma warning restore AA0210 + ProdOrderComp.FindFirst(); + + TransferLine.SetRange("Subc. Prod. Order No.", ProductionOrder."No."); + TransferLine.SetRange("Item No.", ProdOrderComp."Item No."); + + Assert.RecordIsNotEmpty(TransferLine); + TransferLine.FindFirst(); + + MockReservationEntryOnTransferLine(TransferLine, ProdOrderComp); + + ProdOrderCompPage.OpenEdit(); + ProdOrderCompPage.GoToRecord(ProdOrderComp); + asserterror ProdOrderCompPage.ItemTrackingLines.Invoke(); + ExpectedErrorMsg := StrSubstNo(AlreadySpecifiedErr, TransferLine."Document No."); + Assert.ExpectedError(ExpectedErrorMsg); + end; + + [Test] + [HandlerFunctions('DoNotConfirmShowCreatedPurchOrderForSubcontracting,HandleTransferOrder,HandleCreateTransferOrderMsg')] + procedure PostingDirectSubcontractingTransferSetsSourceFieldsOnDirectTransHeader() + var + Bin: Record Bin; + DirectTransHeader: Record "Direct Trans. Header"; + Item: Record Item; + Location: Record Location; + MachineCenter: array[2] of Record "Machine Center"; + ProdOrderComp: Record "Prod. Order Component"; + ProductionOrder: Record "Production Order"; + PurchaseHeader: Record "Purchase Header"; + PurchaseLine: Record "Purchase Line"; + TransferHeader: Record "Transfer Header"; + TransferLine: Record "Transfer Line"; + Vendor: Record Vendor; + WorkCenter: array[2] of Record "Work Center"; + PurchaseHeaderPage: TestPage "Purchase Order"; + begin + // [SCENARIO 617373] Posting a direct subcontracting transfer correctly propagates Source Type and Source ID to the Direct Trans. Header + + // [GIVEN] Complete manufacturing setup (no in-transit transfer route, so the report creates a Direct Transfer) + Initialize(); + SubcontractingMgmtLibrary.UpdateManufacturingSetupWithSubcontractingLocation(); + Subcontracting := true; + UnitCostCalculation := UnitCostCalculation::Units; + + CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); + CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); + UpdateProdBomAndRoutingWithRoutingLink(Item, WorkCenter[2]."No."); + SubcontractingMgmtLibrary.UpdateProdBomWithSubcontractingType(Item, "Subcontracting Type"::Transfer); + UpdateVendorWithSubcontractingLocationCode(WorkCenter[2]); + + SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( + ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); + + UpdateSubMgmtSetupWithReqWkshTemplate(); + SubcontractingMgmtLibrary.UpdateProdOrderCompWithLocationCode(ProductionOrder."No."); + + // [GIVEN] Subcontracting purchase order and transfer order to vendor + SubcontractingMgmtLibrary.CreateSubcontractingOrderFromProdOrderRtngPage(Item."Routing No.", WorkCenter[2]."No."); + + PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); + PurchaseLine.SetRange("Prod. Order No.", ProductionOrder."No."); + PurchaseLine.FindFirst(); + + PurchaseHeader.Get(PurchaseLine."Document Type", PurchaseLine."Document No."); + PurchaseHeaderPage.OpenView(); + PurchaseHeaderPage.GoToRecord(PurchaseHeader); + PurchaseHeaderPage.CreateTransfOrdToSubcontractor.Invoke(); + + ProdOrderComp.SetRange("Prod. Order No.", ProductionOrder."No."); +#pragma warning disable AA0210 + ProdOrderComp.SetRange("Subcontracting Type", ProdOrderComp."Subcontracting Type"::Transfer); +#pragma warning restore AA0210 + ProdOrderComp.FindFirst(); + + TransferLine.SetRange("Subc. Prod. Order No.", ProductionOrder."No."); + TransferLine.SetRange("Item No.", ProdOrderComp."Item No."); + TransferLine.FindFirst(); + TransferHeader.Get(TransferLine."Document No."); + + Item.Get(ProdOrderComp."Item No."); + Location.Get(TransferHeader."Transfer-from Code"); + CreateInventory(Item, Location, Bin, ProdOrderComp."Expected Qty. (Base)"); + Vendor.Get(WorkCenter[2]."Subcontractor No."); + + // [WHEN] Post the direct transfer + Codeunit.Run(Codeunit::"TransferOrder-Post Transfer", TransferHeader); + + // [THEN] Direct Trans. Header has Source Type = Subcontracting and Source ID = Vendor No. + DirectTransHeader.SetRange("Subcontr. Purch. Order No.", PurchaseHeader."No."); + Assert.RecordIsNotEmpty(DirectTransHeader); + DirectTransHeader.FindFirst(); + Assert.AreEqual( + "Transfer Source Type"::Subcontracting, DirectTransHeader."Source Type", + 'Source Type must be Subcontracting on Direct Trans. Header'); + Assert.AreEqual( + Vendor."No.", DirectTransHeader."Source ID", + 'Source ID must be the Vendor No. on Direct Trans. Header'); + end; + + [Test] + [HandlerFunctions('DoNotConfirmShowCreatedPurchOrderForSubcontracting,HandleTransferOrder')] + procedure CreateReturnTransferOrderAfterPartialShipOfOutbound() + var + Bin: Record Bin; + Item: Record Item; + Location: Record Location; + MachineCenter: array[2] of Record "Machine Center"; + ProdOrderComp: Record "Prod. Order Component"; + ProductionOrder: Record "Production Order"; + PurchaseLine: Record "Purchase Line"; + ReturnTransferHeader: Record "Transfer Header"; + TransferHeader: Record "Transfer Header"; + TransferLine: Record "Transfer Line"; + WorkCenter: array[2] of Record "Work Center"; + PurchaseHeaderPage: TestPage "Purchase Order"; + OutboundFromCode, OutboundToCode : Code[10]; + QtyPartialShip: Decimal; + begin + // [SCENARIO] Non-direct (in-transit) transfer: Return from Subcontractor must succeed when the outbound Transfer Order is only partially shipped, and a second Return attempt must be blocked with the existing error. + // [SCENARIO] Pre-fix the report errored 'Return from Subcontractor has already been created' on the first call because CheckTransferLineExists matched the unrelated outbound line. + + // [GIVEN] Standard subcontracting setup with an in-transit transfer route (non-direct) + Initialize(); + SubcontractingMgmtLibrary.UpdateManufacturingSetupWithSubcontractingLocation(); + Subcontracting := true; + UnitCostCalculation := UnitCostCalculation::Units; + CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); + CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); + UpdateProdBomAndRoutingWithRoutingLink(Item, WorkCenter[2]."No."); + SubcontractingMgmtLibrary.UpdateProdBomWithSubcontractingType(Item, "Subcontracting Type"::Transfer); + UpdateVendorWithSubcontractingLocationCode(WorkCenter[2]); + SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( + ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); + UpdateSubMgmtSetupWithReqWkshTemplate(); + SubcontractingMgmtLibrary.UpdateProdOrderCompWithLocationCode(ProductionOrder."No."); + SubcontractingMgmtLibrary.CreateTransferRoute(WorkCenter[2], ProductionOrder); + + SubcontractingMgmtLibrary.CreateSubcontractingOrderFromProdOrderRtngPage(Item."Routing No.", WorkCenter[2]."No."); + + PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); + PurchaseLine.SetRange("No.", ProductionOrder."Source No."); + PurchaseLine.SetRange(Type, "Purchase Line Type"::Item); + PurchaseLine.SetRange("Prod. Order No.", ProductionOrder."No."); + PurchaseLine.FindFirst(); + + // [GIVEN] Outbound Subcontracting Transfer Order created from the Purchase Order + PurchaseHeaderPage.OpenView(); + PurchaseHeaderPage.GotoKey("Purchase Document Type"::Order, PurchaseLine."Document No."); + PurchaseHeaderPage.CreateTransfOrdToSubcontractor.Invoke(); + + ProdOrderComp.SetRange("Prod. Order No.", ProductionOrder."No."); +#pragma warning disable AA0210 + ProdOrderComp.SetRange("Subcontracting Type", ProdOrderComp."Subcontracting Type"::Transfer); +#pragma warning restore AA0210 + ProdOrderComp.FindFirst(); + + TransferLine.SetRange("Subc. Prod. Order No.", ProductionOrder."No."); + TransferLine.SetRange("Item No.", ProdOrderComp."Item No."); + TransferLine.SetRange("Subc. Prod. Ord. Comp Line No.", ProdOrderComp."Line No."); + TransferLine.FindFirst(); + + TransferHeader.Get(TransferLine."Document No."); + OutboundFromCode := TransferHeader."Transfer-from Code"; + OutboundToCode := TransferHeader."Transfer-to Code"; + + // [GIVEN] Inventory at the source location and the outbound TO is partially shipped via Qty. to Ship (Ship only — items move to in-transit, line stays open with positive Outstanding) + Location.Get(OutboundFromCode); + Item.Get(ProdOrderComp."Item No."); + CreateInventory(Item, Location, Bin, ProdOrderComp."Expected Qty. (Base)"); + + QtyPartialShip := Round(TransferLine.Quantity / 2, 1, '<'); + TransferLine.Validate("Qty. to Ship", QtyPartialShip); + TransferLine.Modify(true); + LibraryWarehouse.PostTransferOrder(TransferHeader, true, false); + + // [WHEN] Creating a Return Transfer Order while the outbound TO line is still present (partially shipped) + PurchaseHeaderPage.GotoKey("Purchase Document Type"::Order, PurchaseLine."Document No."); + PurchaseHeaderPage.CreateReturnFromSubcontractor.Invoke(); + + // [THEN] A Return Transfer Order is created with reversed Transfer-from / Transfer-to and quantity capped by the partially-shipped qty + ReturnTransferHeader.SetRange("Subcontr. Purch. Order No.", PurchaseLine."Document No."); + ReturnTransferHeader.SetRange("Subc. Return Order", true); + Assert.IsTrue(ReturnTransferHeader.FindFirst(), 'Return Transfer Order should be created after partial ship of outbound'); + Assert.AreEqual(OutboundToCode, ReturnTransferHeader."Transfer-from Code", 'Return Transfer-from must be the subcontractor location'); + Assert.AreEqual(OutboundFromCode, ReturnTransferHeader."Transfer-to Code", 'Return Transfer-to must be the original component location'); + + TransferLine.Reset(); + TransferLine.SetRange("Document No.", ReturnTransferHeader."No."); + Assert.IsTrue(TransferLine.FindFirst(), 'Return Transfer Line should exist'); + Assert.AreEqual(QtyPartialShip, TransferLine.Quantity, 'Return quantity should equal the in-transit qty from the partial outbound shipment'); + + // [WHEN] Trying to create the Return Transfer Order again, with a Return TO already in place + PurchaseHeaderPage.GotoKey("Purchase Document Type"::Order, PurchaseLine."Document No."); + asserterror PurchaseHeaderPage.CreateReturnFromSubcontractor.Invoke(); + + // [THEN] The duplicate is blocked with the existing error + Assert.ExpectedError('Nothing to create. No components or WIP items to return for the specified subcontracting order'); + end; + + [Test] + [HandlerFunctions('DoNotConfirmShowCreatedPurchOrderForSubcontracting,HandleTransferOrder')] + procedure CreateReturnTransferOrderAfterPartialShipOfOutboundDirectTransfer() + var + Bin: Record Bin; + Item: Record Item; + Location: Record Location; + MachineCenter: array[2] of Record "Machine Center"; + ProdOrderComp: Record "Prod. Order Component"; + ProductionOrder: Record "Production Order"; + PurchaseLine: Record "Purchase Line"; + ReturnTransferHeader: Record "Transfer Header"; + TransferHeader: Record "Transfer Header"; + TransferLine: Record "Transfer Line"; + WorkCenter: array[2] of Record "Work Center"; + PurchaseHeaderPage: TestPage "Purchase Order"; + OutboundFromCode, OutboundToCode : Code[10]; + QtyPartialShip: Decimal; + begin + // [SCENARIO] Direct transfer (no in-transit route): Return from Subcontractor must succeed when the outbound Transfer Order has been partially direct-transferred, and a second Return attempt must be blocked with the existing error. + // [SCENARIO] In direct-transfer mode Qty. to Ship is forced to equal Quantity, so a partial transfer is achieved by reducing the line Quantity before posting. After the post the items already sit at the subcontractor and the outbound line is consumed. + + // [GIVEN] Standard subcontracting setup WITHOUT an in-transit transfer route — the outbound TO will be Direct Transfer + Initialize(); + SubcontractingMgmtLibrary.UpdateManufacturingSetupWithSubcontractingLocation(); + Subcontracting := true; + UnitCostCalculation := UnitCostCalculation::Units; + CreateAndCalculateNeededWorkAndMachineCenter(WorkCenter, MachineCenter); + CreateItemForProductionIncludeRoutingAndProdBOM(Item, WorkCenter, MachineCenter); + UpdateProdBomAndRoutingWithRoutingLink(Item, WorkCenter[2]."No."); + SubcontractingMgmtLibrary.UpdateProdBomWithSubcontractingType(Item, "Subcontracting Type"::Transfer); + UpdateVendorWithSubcontractingLocationCode(WorkCenter[2]); + SubcontractingMgmtLibrary.CreateAndRefreshProductionOrder( + ProductionOrder, "Production Order Status"::Released, ProductionOrder."Source Type"::Item, Item."No.", LibraryRandom.RandInt(10) + 5); + UpdateSubMgmtSetupWithReqWkshTemplate(); + SubcontractingMgmtLibrary.UpdateProdOrderCompWithLocationCode(ProductionOrder."No."); + + SubcontractingMgmtLibrary.CreateSubcontractingOrderFromProdOrderRtngPage(Item."Routing No.", WorkCenter[2]."No."); + + PurchaseLine.SetRange("Document Type", PurchaseLine."Document Type"::Order); + PurchaseLine.SetRange("No.", ProductionOrder."Source No."); + PurchaseLine.SetRange(Type, "Purchase Line Type"::Item); + PurchaseLine.SetRange("Prod. Order No.", ProductionOrder."No."); + PurchaseLine.FindFirst(); + + // [GIVEN] Outbound Subcontracting Transfer Order created from the Purchase Order (Direct Transfer because no in-transit route) + PurchaseHeaderPage.OpenView(); + PurchaseHeaderPage.GotoKey("Purchase Document Type"::Order, PurchaseLine."Document No."); + PurchaseHeaderPage.CreateTransfOrdToSubcontractor.Invoke(); + + ProdOrderComp.SetRange("Prod. Order No.", ProductionOrder."No."); +#pragma warning disable AA0210 + ProdOrderComp.SetRange("Subcontracting Type", ProdOrderComp."Subcontracting Type"::Transfer); +#pragma warning restore AA0210 + ProdOrderComp.FindFirst(); + + TransferLine.SetRange("Subc. Prod. Order No.", ProductionOrder."No."); + TransferLine.SetRange("Item No.", ProdOrderComp."Item No."); + TransferLine.SetRange("Subc. Prod. Ord. Comp Line No.", ProdOrderComp."Line No."); + TransferLine.FindFirst(); + + TransferHeader.Get(TransferLine."Document No."); + OutboundFromCode := TransferHeader."Transfer-from Code"; + OutboundToCode := TransferHeader."Transfer-to Code"; + + // [GIVEN] Inventory at the source location and the outbound Quantity is reduced to a partial value, then direct-transferred (Ship+Receive in one operation — items land at the subcontractor location) + Location.Get(OutboundFromCode); + Item.Get(ProdOrderComp."Item No."); + CreateInventory(Item, Location, Bin, ProdOrderComp."Expected Qty. (Base)"); + + QtyPartialShip := Round(TransferLine.Quantity / 2, 1, '<'); + TransferLine.Validate(Quantity, QtyPartialShip); + TransferLine.Modify(true); + LibraryWarehouse.PostTransferOrder(TransferHeader, true, true); + + // [WHEN] Creating a Return Transfer Order while items are now at the subcontractor + PurchaseHeaderPage.GotoKey("Purchase Document Type"::Order, PurchaseLine."Document No."); + PurchaseHeaderPage.CreateReturnFromSubcontractor.Invoke(); + + // [THEN] A Return Transfer Order is created with reversed Transfer-from / Transfer-to and quantity capped by the qty that actually arrived at the subcontractor + ReturnTransferHeader.SetRange("Subcontr. Purch. Order No.", PurchaseLine."Document No."); + ReturnTransferHeader.SetRange("Subc. Return Order", true); + Assert.IsTrue(ReturnTransferHeader.FindFirst(), 'Return Transfer Order should be created after partial direct transfer'); + Assert.AreEqual(OutboundToCode, ReturnTransferHeader."Transfer-from Code", 'Return Transfer-from must be the subcontractor location'); + Assert.AreEqual(OutboundFromCode, ReturnTransferHeader."Transfer-to Code", 'Return Transfer-to must be the original component location'); + + TransferLine.Reset(); + TransferLine.SetRange("Document No.", ReturnTransferHeader."No."); + Assert.IsTrue(TransferLine.FindFirst(), 'Return Transfer Line should exist'); + Assert.AreEqual(QtyPartialShip, TransferLine.Quantity, 'Return quantity should equal the qty already at the subcontractor'); + + // [WHEN] Trying to create the Return Transfer Order again, with a Return TO already in place + PurchaseHeaderPage.GotoKey("Purchase Document Type"::Order, PurchaseLine."Document No."); + asserterror PurchaseHeaderPage.CreateReturnFromSubcontractor.Invoke(); + + // [THEN] The duplicate is blocked with the existing error + Assert.ExpectedError('Nothing to create. No components or WIP items to return for the specified subcontracting order'); + end; + + [PageHandler] + procedure HandleTransferOrder(var TransfOrderPage: TestPage "Transfer Order") + begin + OpenedTransferOrderNo := CopyStr(TransfOrderPage."No.".Value(), 1, MaxStrLen(OpenedTransferOrderNo)); + TransfOrderPage.OK().Invoke(); + end; + + [MessageHandler] + procedure HandleCreateTransferOrderMsg(Message: Text[1024]) + begin + end; + + [ConfirmHandler] + procedure DoNotConfirmShowCreatedPurchOrderForSubcontracting(Question: Text[1024]; var Reply: Boolean) + begin + LibraryVariableStorage.Enqueue(Question); + Reply := false; + end; + + local procedure CreateAndCalculateNeededWorkAndMachineCenter(var WorkCenter: array[2] of Record "Work Center"; var MachineCenter: array[2] of Record "Machine Center") + var + CapacityUnitOfMeasure: Record "Capacity Unit of Measure"; + ShopCalendarCode: Code[10]; + MachineCenterNo: Code[20]; + MachineCenterNo2: Code[20]; + WorkCenterNo: Code[20]; + WorkCenterNo2: Code[20]; + begin + LibraryManufacturing.CreateCapacityUnitOfMeasure(CapacityUnitOfMeasure, "Capacity Unit of Measure"::Minutes); + ShopCalendarCode := LibraryManufacturing.UpdateShopCalendarWorkingDays(); + + // [GIVEN] Create and Calculate needed Work and Machine Center + CreateWorkCenter(WorkCenterNo, ShopCalendarCode, "Flushing Method"::"Pick + Manual", not Subcontracting, UnitCostCalculation, ''); + WorkCenter[1].Get(WorkCenterNo); + LibraryManufacturing.CalculateWorkCenterCalendar(WorkCenter[1], CalcDate('<-CY-1Y>', WorkDate()), CalcDate('', WorkDate())); + + LibraryMfgManagement.CreateMachineCenter(MachineCenterNo, WorkCenterNo, "Flushing Method"::"Pick + Manual".AsInteger()); + MachineCenter[1].Get(MachineCenterNo); + LibraryManufacturing.CalculateMachCenterCalendar(MachineCenter[1], CalcDate('<-CY-1Y>', WorkDate()), CalcDate('', WorkDate())); + + LibraryMfgManagement.CreateMachineCenter(MachineCenterNo2, WorkCenterNo, "Flushing Method"::"Pick + Manual".AsInteger()); + MachineCenter[2].Get(MachineCenterNo2); + LibraryManufacturing.CalculateMachCenterCalendar(MachineCenter[2], CalcDate('<-CY-1Y>', WorkDate()), CalcDate('', WorkDate())); + + if Subcontracting then + CreateWorkCenter(WorkCenterNo2, ShopCalendarCode, "Flushing Method"::"Pick + Manual", Subcontracting, UnitCostCalculation, '') + else + CreateWorkCenter(WorkCenterNo2, ShopCalendarCode, "Flushing Method"::"Pick + Manual", not Subcontracting, UnitCostCalculation, ''); + WorkCenter[2].Get(WorkCenterNo2); + LibraryManufacturing.CalculateWorkCenterCalendar(WorkCenter[2], CalcDate('<-CY-1Y>', WorkDate()), CalcDate('', WorkDate())); + end; + + local procedure CreateItemForProductionIncludeRoutingAndProdBOM(var Item: Record Item; var WorkCenter: array[2] of Record "Work Center"; var MachineCenter: array[2] of Record "Machine Center") + var + ManufacturingSetup: Record "Manufacturing Setup"; + ProductionBOMHeader: Record "Production BOM Header"; + NoSeries: Codeunit "No. Series"; + ItemNo: Code[20]; + ItemNo2: Code[20]; + ProductionBOMNo: Code[20]; + RoutingNo: Code[20]; + begin + ManufacturingSetup.SetLoadFields("Routing Nos."); + ManufacturingSetup.Get(); + RoutingNo := NoSeries.GetNextNo(ManufacturingSetup."Routing Nos.", WorkDate(), true); + + LibraryMfgManagement.CreateRouting(RoutingNo, MachineCenter[1]."No.", MachineCenter[2]."No.", WorkCenter[1]."No.", WorkCenter[2]."No."); + + // Create Items with Flushing method - Manual with the Parent Item containing Routing No. and Production BOM No. + + CreateItem(Item, "Costing Method"::FIFO, "Reordering Policy"::"Lot-for-Lot", "Flushing Method"::"Pick + Manual", '', ''); + ItemNo := Item."No."; + Clear(Item); + CreateItem(Item, "Costing Method"::FIFO, "Reordering Policy"::"Lot-for-Lot", "Flushing Method"::"Pick + Manual", '', ''); + ItemNo2 := Item."No."; + Clear(Item); + + ProductionBOMNo := LibraryManufacturing.CreateCertifProdBOMWithTwoComp(ProductionBOMHeader, ItemNo, ItemNo2, 1); // value important. + + CreateItem(Item, "Costing Method"::FIFO, "Reordering Policy"::"Lot-for-Lot", "Flushing Method"::"Pick + Manual", RoutingNo, ProductionBOMNo); + end; + + local procedure SetAllProdOrderTransferComponentLocations(ProdOrderNo: Code[20]; LocationCode: Code[10]) + var + ProdOrderComp: Record "Prod. Order Component"; + begin + ProdOrderComp.SetRange("Prod. Order No.", ProdOrderNo); +#pragma warning disable AA0210 + ProdOrderComp.SetRange("Subcontracting Type", ProdOrderComp."Subcontracting Type"::Transfer); +#pragma warning restore AA0210 + if ProdOrderComp.FindSet() then + repeat + ProdOrderComp.Validate("Location Code", LocationCode); + ProdOrderComp.Modify(true); + until ProdOrderComp.Next() = 0; + end; + + local procedure UpdateProdBomAndRoutingWithRoutingLink(Item: Record Item; WorkCenterNo: Code[20]) + var + ProductionBOMHeader: Record "Production BOM Header"; + ProductionBOMLine: Record "Production BOM Line"; + RoutingHeader: Record "Routing Header"; + RoutingLine: Record "Routing Line"; + RoutingLink: Record "Routing Link"; + begin + RoutingLink.Init(); + RoutingLink.Validate(Code, CopyStr(Item."Production BOM No.", 1, 10)); + RoutingLink.Insert(true); + + RoutingHeader.Get(Item."Routing No."); + RoutingHeader.Validate(Status, RoutingHeader.Status::New); + RoutingHeader.Modify(true); + + RoutingLine.SetRange("Routing No.", RoutingHeader."No."); + RoutingLine.SetRange(Type, RoutingLine.Type::"Work Center"); + RoutingLine.SetRange("No.", WorkCenterNo); + RoutingLine.FindFirst(); + RoutingLine.Validate("Routing Link Code", RoutingLink.Code); + RoutingLine.Modify(true); + + RoutingHeader.Validate(Status, RoutingHeader.Status::Certified); + RoutingHeader.Modify(true); + + ProductionBOMHeader.Get(Item."Production BOM No."); + ProductionBOMHeader.Validate(Status, ProductionBOMHeader.Status::New); + ProductionBOMHeader.Modify(true); + + ProductionBOMLine.SetRange("Production BOM No.", ProductionBOMHeader."No."); + ProductionBOMLine.FindLast(); + ProductionBOMLine.Validate("Routing Link Code", RoutingLink.Code); + ProductionBOMLine.Modify(true); + + ProductionBOMHeader.Validate(Status, ProductionBOMHeader.Status::Certified); + ProductionBOMHeader.Modify(true); + end; + + local procedure UpdateProdOrderCompWithLocationAndBinCode(ProdOrderNo: Code[20]; var LocationCode: Code[10]; var BinCode: Code[20]) + var + Bin: Record Bin; + Location: Record Location; + ProdOrderComp: Record "Prod. Order Component"; + begin + ProdOrderComp.SetRange("Prod. Order No.", ProdOrderNo); +#pragma warning disable AA0210 + ProdOrderComp.SetRange("Subcontracting Type", ProdOrderComp."Subcontracting Type"::Transfer); +#pragma warning restore AA0210 + ProdOrderComp.FindFirst(); + LibraryWarehouse.CreateLocationWithInventoryPostingSetup(Location); + Location."Bin Mandatory" := true; + Location.Modify(); + LibraryWarehouse.CreateBin(Bin, Location.Code, '', '', ''); + ProdOrderComp."Location Code" := Location.Code; + ProdOrderComp."Bin Code" := Bin.Code; + ProdOrderComp.Modify(); + + LocationCode := Location.Code; + BinCode := Bin.Code; + end; + + local procedure UpdateProdOrderCompDueDate(ProdOrderNo: Code[20]) + var + ProdOrderComp: Record "Prod. Order Component"; + begin + ProdOrderComp.SetRange("Prod. Order No.", ProdOrderNo); +#pragma warning disable AA0210 + ProdOrderComp.SetRange("Subcontracting Type", ProdOrderComp."Subcontracting Type"::Transfer); +#pragma warning restore AA0210 + ProdOrderComp.FindFirst(); + ProdOrderComp."Due Date" := CalcDate('<+10D>', WorkDate()); + ProdOrderComp.Modify(); + end; + + local procedure UpdateVendorWithSubcontractingLocationCode(WorkCenter: Record "Work Center") + var + Location: Record Location; + Vendor: Record Vendor; + begin + LibraryWarehouse.CreateLocationWithInventoryPostingSetup(Location); + Vendor.Get(WorkCenter."Subcontractor No."); + Vendor."Subc. Location Code" := Location.Code; + LibraryWarehouse.CreateLocationWithInventoryPostingSetup(Location); + Vendor."Location Code" := Location.Code; + Vendor.Modify(); + end; + + procedure CreateWorkCenter(var WorkCenterNo: Code[20]; ShopCalendarCode: Code[10]; FlushingMethod: Enum "Flushing Method"; Subcontract: Boolean; + UnitCostCalc: Option; + CurrencyCode: Code[10]) + var + GenProductPostingGroup: Record "Gen. Product Posting Group"; + VATPostingSetup: Record "VAT Posting Setup"; + WorkCenter: Record "Work Center"; + begin + // Create Work Center with required fields where random is used, values not important for test. + LibraryMfgManagement.CreateWorkCenterWithFixedCost(WorkCenter, ShopCalendarCode, 0); + + WorkCenter.Validate("Flushing Method", FlushingMethod); + WorkCenter.Validate("Direct Unit Cost", LibraryRandom.RandDec(10, 2)); + WorkCenter.Validate("Indirect Cost %", LibraryRandom.RandDec(5, 1)); + WorkCenter.Validate("Overhead Rate", LibraryRandom.RandDec(5, 1)); + WorkCenter.Validate("Unit Cost Calculation", UnitCostCalc); + + if Subcontract then begin + LibraryERM.FindVATPostingSetup(VATPostingSetup, VATPostingSetup."VAT Calculation Type"::"Normal VAT"); + GenProductPostingGroup.FindFirst(); + GenProductPostingGroup.Validate("Def. VAT Prod. Posting Group", VATPostingSetup."VAT Prod. Posting Group"); + GenProductPostingGroup.Modify(true); + WorkCenter.Validate("Subcontractor No.", LibraryMfgManagement.CreateSubcontractorWithCurrency(CurrencyCode)); + end; + WorkCenter.Modify(true); + WorkCenterNo := WorkCenter."No."; + end; + + local procedure CreateItem(var Item: Record Item; ItemCostingMethod: Enum "Costing Method"; ItemReorderPolicy: Enum "Reordering Policy"; + FlushingMethod: Enum "Flushing Method"; + RoutingNo: Code[20]; + ProductionBOMNo: Code[20]) + begin + // Create Item with required fields where random values not important for test. + LibraryManufacturing.CreateItemManufacturing( + Item, ItemCostingMethod, LibraryRandom.RandInt(10), ItemReorderPolicy, FlushingMethod, RoutingNo, ProductionBOMNo); + Item.Validate("Overhead Rate", LibraryRandom.RandDec(5, 2)); + Item.Validate("Indirect Cost %", LibraryRandom.RandDec(5, 2)); + Item.Modify(true); + end; + + local procedure Initialize() + begin + LibraryTestInitialize.OnTestInitialize(Codeunit::"Subc. Transfer Order Test"); + LibrarySetupStorage.Restore(); + + SubcontractingMgmtLibrary.Initialize(); + SubcontractingMgmtLibrary.UpdateSubMgmtSetup_ComponentAtLocation("Components at Location"::Purchase); + UpdateSubMgmtSetupWithReqWkshTemplate(); + LibraryVariableStorage.Clear(); + + LibraryMfgManagement.Initialize(); + + if IsInitialized then + exit; + LibraryTestInitialize.OnBeforeTestSuiteInitialize(Codeunit::"Subc. Transfer Order Test"); + + SubSetupLibrary.InitSetupFields(); + SubSetupLibrary.ConfigureSubManagementForNothingPresentScenario("Subc. Show/Edit Type"::Hide, "Subc. Show/Edit Type"::Hide); + SubSetupLibrary.ConfigureSubManagementForBothPresentScenario("Subc. Show/Edit Type"::Hide, "Subc. Show/Edit Type"::Hide); + LibraryERMCountryData.CreateVATData(); + SubSetupLibrary.InitialSetupForGenProdPostingGroup(); + + IsInitialized := true; + Commit(); + + LibraryTestInitialize.OnAfterTestSuiteInitialize(Codeunit::"Subc. Transfer Order Test"); + end; + + local procedure UpdateSubMgmtSetupWithReqWkshTemplate() + begin + LibraryMfgManagement.CreateSubcontractingReqWkshTemplateAndNameAndUpdateSetup(); + end; + + local procedure UpdateSubWhseHandlingTimeInSubManagementSetup() + var + ManufacturingSetup: Record "Manufacturing Setup"; + begin + if not ManufacturingSetup.Get() then + exit; + + Evaluate(ManufacturingSetup."Subc. Inb. Whse. Handling Time", '<1D>'); + ManufacturingSetup.Modify(); + end; + + local procedure MockReservationEntryOnTransferLine(TransferLine: Record "Transfer Line"; ProdOrderComponent: Record "Prod. Order Component") + var + ReservationEntry: Record "Reservation Entry"; + begin + ReservationEntry.DeleteAll(); + ReservationEntry.Init(); + ReservationEntry."Source Type" := Database::"Transfer Line"; + ReservationEntry."Source ID" := TransferLine."Document No."; + ReservationEntry."Source Ref. No." := TransferLine."Line No."; + ReservationEntry."Item No." := ProdOrderComponent."Item No."; + ReservationEntry."Variant Code" := ProdOrderComponent."Variant Code"; + ReservationEntry.Insert(); + end; + + procedure CreateInventory(Item: Record Item; Location: Record Location; Bin: Record Bin; Quantity: Decimal) + var + ItemJournalLine: Record "Item Journal Line"; + begin + LibraryInventory.CreateItemJournalLineInItemTemplate( + ItemJournalLine, Item."No.", Location.Code, Bin.Code, Quantity); + LibraryInventory.PostItemJournalLine(ItemJournalLine."Journal Template Name", ItemJournalLine."Journal Batch Name"); + end; + var + WorkCenter2: Record "Work Center"; + Assert: Codeunit Assert; + LibraryERM: Codeunit "Library - ERM"; + LibraryERMCountryData: Codeunit "Library - ERM Country Data"; + LibraryInventory: Codeunit "Library - Inventory"; + LibraryManufacturing: Codeunit "Library - Manufacturing"; + LibraryPlanning: Codeunit "Library - Planning"; + LibraryPurchase: Codeunit "Library - Purchase"; + LibraryRandom: Codeunit "Library - Random"; + LibrarySales: Codeunit "Library - Sales"; + LibrarySetupStorage: Codeunit "Library - Setup Storage"; + LibraryTestInitialize: Codeunit "Library - Test Initialize"; + LibraryWarehouse: Codeunit "Library - Warehouse"; + LibraryMfgManagement: Codeunit "Subc. Library Mfg. Management"; + SubcontractingMgmtLibrary: Codeunit "Subc. Management Library"; + LibraryVariableStorage: Codeunit "Library - Variable Storage"; + SubSetupLibrary: Codeunit "Subc. Setup Library"; + IsInitialized: Boolean; + Subcontracting: Boolean; + OpenedTransferOrderNo: Code[20]; + PurchaseOrderPageOpened: Boolean; + PurchaseLinesPageOpened: Boolean; + UnitCostCalculation: Option Time,Units; + ConfirmDialogCalledCount: Integer; + AlreadySpecifiedErr: Label 'You cannot open Tracking Specification because this component is already specified in Transfer Order %1.', Comment = '|%1 = Transfer Order No.'; + PurchOrderRoutingErr: Label 'Purchase Order %1 should contain a line tied to Routing Reference No. %2', Comment = '%1 = Purchase Order No., %2 = Routing Reference No.'; + +} \ No newline at end of file