diff --git a/src/Apps/W1/Subcontracting/App/src/Transfer/SubcCreateTransfOrder.Report.al b/src/Apps/W1/Subcontracting/App/src/Transfer/SubcCreateTransfOrder.Report.al index edf1d1f7bdc..7db50e7edc9 100644 --- a/src/Apps/W1/Subcontracting/App/src/Transfer/SubcCreateTransfOrder.Report.al +++ b/src/Apps/W1/Subcontracting/App/src/Transfer/SubcCreateTransfOrder.Report.al @@ -7,6 +7,8 @@ namespace Microsoft.Manufacturing.Subcontracting; using Microsoft.Foundation.UOM; using Microsoft.Inventory.Costing; using Microsoft.Inventory.Item; +using Microsoft.Inventory.Location; +using Microsoft.Inventory.Setup; using Microsoft.Inventory.Transfer; using Microsoft.Manufacturing.Document; using Microsoft.Manufacturing.WorkCenter; @@ -83,6 +85,27 @@ report 20501 "Subc. Create Transf. Order" OrderNoDoesNotExistInProdOrderErr: Label 'Operation %1 in the subcontracting order %2 does not exist in the routing %3 of the production order %4.', Comment = '%1=Operation No., %2=Purchase Order No., %3=Routing No., %4=Production Order No.'; OrderNoIsNotSubcontractorErr: Label 'Order %1 is not a Subcontractor work.', Comment = '%1=Purchase Order No.'; WarningToSpecifyPurchOrderErr: Label 'Warning. Specify a Purchase Order No. for the Subcontractor work.'; + CannotCreateTransferErr: Label 'Cannot create a transfer from location %1 to location %2 because location %1 requires warehousing. Set up an in-transit transfer route between the locations, or set Direct Transfer Posting to Direct Transfer in Inventory Setup.', Comment = '%1=Transfer-from location code, %2=Transfer-to location code'; + + local procedure CheckDirectTransferAllowed(TransferFromLocation: Code[10]; TransferToLocation: Code[10]) + var + Location: Record Location; + begin + if IsOneStepDirectTransfer() then + exit; + + if Location.RequirePicking(TransferFromLocation) or Location.RequireShipment(TransferFromLocation) then + Error(CannotCreateTransferErr, TransferFromLocation, TransferToLocation); + end; + + local procedure IsOneStepDirectTransfer(): Boolean + var + InventorySetup: Record "Inventory Setup"; + begin + InventorySetup.SetLoadFields("Direct Transfer Posting"); + InventorySetup.GetRecordOnce(); + exit(InventorySetup."Direct Transfer Posting" = InventorySetup."Direct Transfer Posting"::"Direct Transfer"); + end; local procedure InsertTransferHeader(TransferFromLocation: Code[10]) var @@ -106,8 +129,10 @@ report 20501 "Subc. Create Transf. Order" TransferHeader.Insert(true); TransferHeader.Validate("Transfer-from Code", TransferFromLocation); TransferHeader.Validate("Transfer-to Code", TransferToLocationCode); - if not TransferRoute.Get(TransferFromLocation, TransferToLocationCode) or (TransferRoute."In-Transit Code" = '') then + if not TransferRoute.Get(TransferFromLocation, TransferToLocationCode) or (TransferRoute."In-Transit Code" = '') then begin + CheckDirectTransferAllowed(TransferFromLocation, TransferToLocationCode); TransferHeader.Validate("Direct Transfer", true); + end; TransferHeader."Subc. Source Type" := TransferHeader."Subc. Source Type"::Subcontracting; TransferHeader."Source ID" := "Purchase Header"."Buy-from Vendor No."; diff --git a/src/Apps/W1/Subcontracting/Test/Tests/SubcLocationHandlerTest.Codeunit.al b/src/Apps/W1/Subcontracting/Test/Tests/SubcLocationHandlerTest.Codeunit.al index 6f998101390..558d120b3d3 100644 --- a/src/Apps/W1/Subcontracting/Test/Tests/SubcLocationHandlerTest.Codeunit.al +++ b/src/Apps/W1/Subcontracting/Test/Tests/SubcLocationHandlerTest.Codeunit.al @@ -8,6 +8,7 @@ using Microsoft.Foundation.Company; using Microsoft.Inventory.Item; using Microsoft.Inventory.Journal; using Microsoft.Inventory.Location; +using Microsoft.Inventory.Setup; using Microsoft.Inventory.Transfer; using Microsoft.Manufacturing.Document; using Microsoft.Manufacturing.MachineCenter; @@ -62,6 +63,8 @@ codeunit 139981 "Subc. Location Handler Test" LibraryERMCountryData.CreateVATData(); SubSetupLibrary.InitialSetupForGenProdPostingGroup(); + LibrarySetupStorage.Save(Database::"Inventory Setup"); + IsInitialized := true; Commit(); @@ -197,6 +200,99 @@ codeunit 139981 "Subc. Location Handler Test" Assert.AreEqual(LocationSub.Code, TransferHeader."Transfer-to Code", 'Transfer-to Code should be Subcontractor Location'); end; + [Test] + procedure DirectTransferFromRequireShipmentLocationIsBlocked() + var + Item: Record Item; + LocationOrig: Record Location; + LocationSub: Record Location; + ProdOrder: Record "Production Order"; + ProdOrderComp: Record "Prod. Order Component"; + ProdOrderLine: Record "Prod. Order Line"; + ProdOrderRtngLine: Record "Prod. Order Routing Line"; + PurchaseHeader: Record "Purchase Header"; + PurchaseLine: Record "Purchase Line"; + Vendor: Record Vendor; + CreateSubCTransfOrder: Report "Subc. Create Transf. Order"; + begin + // [SCENARIO 640958] Creating a subcontracting transfer with no in-transit route from a location that + // requires a shipment is blocked with a guided error instead of a raw TestField error. + Initialize(); + + // [GIVEN] Subcontractor and Original locations; the Original location requires a shipment; no in-transit transfer route exists. + LibraryWarehouse.CreateLocation(LocationSub); + LibraryWarehouse.CreateLocation(LocationOrig); + LocationOrig."Require Shipment" := true; + LocationOrig.Modify(true); + + // [GIVEN] Inventory Setup posts direct transfers via Receipt and Shipment (so warehouse handling is enforced) + SetInventoryDirectTransferPosting(false); + + // [GIVEN] Subcontracting Scenario Setup (component at the subcontractor location, original at the require-shipment location) + CreateSubcontractingSetup( + PurchaseHeader, PurchaseLine, ProdOrder, ProdOrderLine, ProdOrderComp, ProdOrderRtngLine, Vendor, + LocationSub, Item, LibraryRandom.RandInt(10), LocationSub.Code, LocationOrig.Code); + + // [WHEN] Running the Create Subcontracting Transfer Order report + Commit(); // Report requires commit + PurchaseHeader.SetRecFilter(); + CreateSubCTransfOrder.SetTableView(PurchaseHeader); + CreateSubCTransfOrder.UseRequestPage(false); + + // [THEN] A guided error is raised instead of a raw TestField error on the location + asserterror CreateSubCTransfOrder.Run(); + Assert.ExpectedError('requires warehousing'); + end; + + [Test] + [HandlerFunctions('HandleTransferOrder')] + procedure DirectTransferFromRequireShipmentLocationAllowedWithDirectTransferPosting() + var + Item: Record Item; + LocationOrig: Record Location; + LocationSub: Record Location; + ProdOrder: Record "Production Order"; + ProdOrderComp: Record "Prod. Order Component"; + ProdOrderLine: Record "Prod. Order Line"; + ProdOrderRtngLine: Record "Prod. Order Routing Line"; + PurchaseHeader: Record "Purchase Header"; + PurchaseLine: Record "Purchase Line"; + TransferHeader: Record "Transfer Header"; + Vendor: Record Vendor; + CreateSubCTransfOrder: Report "Subc. Create Transf. Order"; + begin + // [SCENARIO 640958] When Inventory Setup posts direct transfers as Direct Transfer, the transfer is created + // from a require-shipment source location instead of being blocked, because the Direct Transfer + // posting type skips the outbound warehouse-handling check. + Initialize(); + + // [GIVEN] Subcontractor and Original locations; the Original location requires a shipment; no in-transit route exists. + LibraryWarehouse.CreateLocation(LocationSub); + LibraryWarehouse.CreateLocation(LocationOrig); + LocationOrig."Require Shipment" := true; + LocationOrig.Modify(true); + + // [GIVEN] Inventory Setup posts direct transfers via Direct Transfer + SetInventoryDirectTransferPosting(true); + + // [GIVEN] Subcontracting Scenario Setup + CreateSubcontractingSetup( + PurchaseHeader, PurchaseLine, ProdOrder, ProdOrderLine, ProdOrderComp, ProdOrderRtngLine, Vendor, + LocationSub, Item, LibraryRandom.RandInt(10), LocationSub.Code, LocationOrig.Code); + + // [WHEN] Running the Create Subcontracting Transfer Order report + Commit(); // Report requires commit + PurchaseHeader.SetRecFilter(); + CreateSubCTransfOrder.SetTableView(PurchaseHeader); + CreateSubCTransfOrder.UseRequestPage(false); + CreateSubCTransfOrder.Run(); + + // [THEN] A direct transfer order is created (not blocked) + TransferHeader.SetRange("Subcontr. Purch. Order No.", PurchaseHeader."No."); + Assert.IsTrue(TransferHeader.FindFirst(), 'A transfer order should be created when Inventory Setup uses Direct Transfer posting.'); + Assert.IsTrue(TransferHeader."Direct Transfer", 'The created transfer order should be a direct transfer.'); + end; + [Test] [HandlerFunctions('HandleTransferOrder')] procedure TestTransferOrderCreation_PostAndRecreate() @@ -624,4 +720,16 @@ codeunit 139981 "Subc. Location Handler Test" procedure HandleTransferOrder(var TransfOrderPage: TestPage "Transfer Order") begin end; + + local procedure SetInventoryDirectTransferPosting(UseDirectTransfer: Boolean) + var + InventorySetup: Record "Inventory Setup"; + begin + InventorySetup.Get(); + if UseDirectTransfer then + InventorySetup.Validate("Direct Transfer Posting", InventorySetup."Direct Transfer Posting"::"Direct Transfer") + else + InventorySetup.Validate("Direct Transfer Posting", InventorySetup."Direct Transfer Posting"::"Receipt and Shipment"); + InventorySetup.Modify(true); + end; } \ No newline at end of file