diff --git a/src/Layers/BE/Tests/SCM-Manufacturing/SCMManufacturing70.Codeunit.al b/src/Layers/BE/Tests/SCM-Manufacturing/SCMManufacturing70.Codeunit.al index 3b60acbd22d..99bf77bc945 100644 --- a/src/Layers/BE/Tests/SCM-Manufacturing/SCMManufacturing70.Codeunit.al +++ b/src/Layers/BE/Tests/SCM-Manufacturing/SCMManufacturing70.Codeunit.al @@ -3632,7 +3632,7 @@ codeunit 137063 "SCM Manufacturing 7.0" end; [Test] - [HandlerFunctions('ChangeStatusOnProdOrderToFinished,ErrorMessageHandler')] + [HandlerFunctions('ChangeStatusOnProdOrderToFinished')] procedure ChangeProductionOrderStatusWithoutVariantCode() var ProductionOrder: array[4] of Record "Production Order"; @@ -3668,7 +3668,8 @@ codeunit 137063 "SCM Manufacturing 7.0" ProductionOrder[1]."Source Type"::Item, Item[1]."No.", LibraryRandom.RandInt(5)); // [GIVEN] Refresh Production Order "X". - LibraryManufacturing.RefreshProdOrder(ProductionOrder[1], false, true, true, true, false); + AssertError LibraryManufacturing.RefreshProdOrder(ProductionOrder[1], false, true, true, true, false); + Assert.ExpectedTestFieldError(ProductionOrder[1].FieldCaption("Variant Code"), ''); // [GIVEN] Create Production Order "Y". LibraryManufacturing.CreateProductionOrder( @@ -3798,6 +3799,110 @@ codeunit 137063 "SCM Manufacturing 7.0" Assert.AreEqual(SKURoutingHeader."No.", ProdOrderLine."Routing No.", ProductionRoutingErr); end; + [Test] + [Scope('OnPrem')] + procedure RefreshProdOrderWithoutCalcLinesSkipsHeaderVariantCheck() + var + Item: Record Item; + Item2: Record Item; + Item3: Record Item; + ItemVariant: Record "Item Variant"; + ProdOrderComponent: Record "Prod. Order Component"; + ProdOrderLine: Record "Prod. Order Line"; + ProductionBOMHeader: Record "Production BOM Header"; + ProductionOrder: Record "Production Order"; + RoutingHeader: Record "Routing Header"; + RoutingLine: Record "Routing Line"; + WorkCenter: Record "Work Center"; + begin + // [SCENARIO 633092] Refreshing a Released Production Order with Calc. Lines = No does not require a header + // Variant Code even when the source item has "Variant Mandatory if Exists" = Yes, because the existing lines + // carry their own variant codes (fashion/apparel pattern with a blank header variant and variant-specific lines). + Initialize(); + + // [GIVEN] Create Work Center. + CreateWorkCenter(WorkCenter); + + // [GIVEN] Create a manufactured Item and its component items. + CreateMultipleItems( + Item, Item2, Item3, Item."Replenishment System"::"Prod. Order", Item3."Replenishment System"::"Prod. Order", + Item."Reordering Policy"::Order, false); + UpdateItem(Item, Item.FieldNo("Manufacturing Policy"), Item."Manufacturing Policy"::"Make-to-Order"); + + // [GIVEN] Create Production BOM and Routing and assign them to the Item. + LibraryManufacturing.CreateCertifiedProductionBOM(ProductionBOMHeader, Item2."No.", 1); + CreateRoutingWithMultipleRoutingLine(RoutingHeader, WorkCenter."No.", RoutingHeader.Type::Serial, RoutingLine.Type::"Work Center"); + UpdateItem(Item, Item.FieldNo("Production BOM No."), ProductionBOMHeader."No."); + UpdateItem(Item, Item.FieldNo("Routing No."), RoutingHeader."No."); + + // [GIVEN] A variant exists for the Item and the variant is mandatory. + LibraryInventory.CreateItemVariant(ItemVariant, Item."No."); + UpdateItem(Item, Item.FieldNo("Variant Mandatory if Exists"), Item."Variant Mandatory if Exists"::Yes); + + // [GIVEN] Create a Released Production Order for the Item with a blank header Variant Code. + LibraryManufacturing.CreateProductionOrder( + ProductionOrder, ProductionOrder.Status::Released, ProductionOrder."Source Type"::Item, Item."No.", 100); + + // [GIVEN] Create a Prod. Order Line carrying the specific Variant Code is added manually. + LibraryManufacturing.CreateProdOrderLine( + ProdOrderLine, ProductionOrder.Status, ProductionOrder."No.", Item."No.", ItemVariant.Code, '', 100); + + // [WHEN] Refresh with Calc. Lines = No, Calc. Routings = Yes, Calc. Components = Yes. + LibraryManufacturing.RefreshProdOrder(ProductionOrder, false, false, true, true, false); + + // [THEN] Verify no error is raised and components are generated for the variant-specific line. + FindProdOrderComponent(ProdOrderComponent, ProductionOrder, Item2."No."); + Assert.RecordIsNotEmpty(ProdOrderComponent); + end; + + [Test] + procedure RefreshProdOrderWithCalcLinesRequiresHeaderVariantCheck() + var + Item: Record Item; + Item2: Record Item; + Item3: Record Item; + ItemVariant: Record "Item Variant"; + ProductionBOMHeader: Record "Production BOM Header"; + ProductionOrder: Record "Production Order"; + RoutingHeader: Record "Routing Header"; + RoutingLine: Record "Routing Line"; + WorkCenter: Record "Work Center"; + begin + // [SCENARIO 633092] Refreshing a Released Production Order with Calc. Lines = Yes still requires a header + // Variant Code when the source item has "Variant Mandatory if Exists" = Yes, because the lines are calculated + // from the header and would otherwise be created without a mandatory variant. + Initialize(); + + // [GIVEN] Create Work Center. + CreateWorkCenter(WorkCenter); + + // [GIVEN] Create a manufactured Item and its component items. + CreateMultipleItems( + Item, Item2, Item3, Item."Replenishment System"::"Prod. Order", Item3."Replenishment System"::"Prod. Order", + Item."Reordering Policy"::Order, false); + UpdateItem(Item, Item.FieldNo("Manufacturing Policy"), Item."Manufacturing Policy"::"Make-to-Order"); + + // [GIVEN] Create Production BOM and Routing and assign them to the Item. + LibraryManufacturing.CreateCertifiedProductionBOM(ProductionBOMHeader, Item2."No.", 1); + CreateRoutingWithMultipleRoutingLine(RoutingHeader, WorkCenter."No.", RoutingHeader.Type::Serial, RoutingLine.Type::"Work Center"); + UpdateItem(Item, Item.FieldNo("Production BOM No."), ProductionBOMHeader."No."); + UpdateItem(Item, Item.FieldNo("Routing No."), RoutingHeader."No."); + + // [GIVEN] A variant exists for the Item and the variant is mandatory. + LibraryInventory.CreateItemVariant(ItemVariant, Item."No."); + UpdateItem(Item, Item.FieldNo("Variant Mandatory if Exists"), Item."Variant Mandatory if Exists"::Yes); + + // [GIVEN] Create a Released Production Order for the Item with a blank header Variant Code. + LibraryManufacturing.CreateProductionOrder( + ProductionOrder, ProductionOrder.Status::Released, ProductionOrder."Source Type"::Item, Item."No.", 100); + + // [WHEN] Refresh with Calc. Lines = Yes, Calc. Routings = Yes, Calc. Components = Yes. + asserterror LibraryManufacturing.RefreshProdOrder(ProductionOrder, false, true, true, true, false); + + // [THEN] Verify an error is raised because the mandatory header Variant Code is blank. + Assert.ExpectedTestFieldError(ProductionOrder.FieldCaption("Variant Code"), ''); + end; + local procedure Initialize() var LibraryERMCountryData: Codeunit "Library - ERM Country Data"; diff --git a/src/Layers/IT/Tests/SCM-Manufacturing/SCMManufacturing70.Codeunit.al b/src/Layers/IT/Tests/SCM-Manufacturing/SCMManufacturing70.Codeunit.al index 41e931dd3f4..186024b65cb 100644 --- a/src/Layers/IT/Tests/SCM-Manufacturing/SCMManufacturing70.Codeunit.al +++ b/src/Layers/IT/Tests/SCM-Manufacturing/SCMManufacturing70.Codeunit.al @@ -3626,7 +3626,7 @@ codeunit 137063 "SCM Manufacturing 7.0" end; [Test] - [HandlerFunctions('ChangeStatusOnProdOrderToFinished,ErrorMessageHandler')] + [HandlerFunctions('ChangeStatusOnProdOrderToFinished')] procedure ChangeProductionOrderStatusWithoutVariantCode() var ProductionOrder: array[4] of Record "Production Order"; @@ -3662,7 +3662,8 @@ codeunit 137063 "SCM Manufacturing 7.0" ProductionOrder[1]."Source Type"::Item, Item[1]."No.", LibraryRandom.RandInt(5)); // [GIVEN] Refresh Production Order "X". - LibraryManufacturing.RefreshProdOrder(ProductionOrder[1], false, true, true, true, false); + AssertError LibraryManufacturing.RefreshProdOrder(ProductionOrder[1], false, true, true, true, false); + Assert.ExpectedTestFieldError(ProductionOrder[1].FieldCaption("Variant Code"), ''); // [GIVEN] Create Production Order "Y". LibraryManufacturing.CreateProductionOrder( @@ -3792,6 +3793,110 @@ codeunit 137063 "SCM Manufacturing 7.0" Assert.AreEqual(SKURoutingHeader."No.", ProdOrderLine."Routing No.", ProductionRoutingErr); end; + [Test] + [Scope('OnPrem')] + procedure RefreshProdOrderWithoutCalcLinesSkipsHeaderVariantCheck() + var + Item: Record Item; + Item2: Record Item; + Item3: Record Item; + ItemVariant: Record "Item Variant"; + ProdOrderComponent: Record "Prod. Order Component"; + ProdOrderLine: Record "Prod. Order Line"; + ProductionBOMHeader: Record "Production BOM Header"; + ProductionOrder: Record "Production Order"; + RoutingHeader: Record "Routing Header"; + RoutingLine: Record "Routing Line"; + WorkCenter: Record "Work Center"; + begin + // [SCENARIO 633092] Refreshing a Released Production Order with Calc. Lines = No does not require a header + // Variant Code even when the source item has "Variant Mandatory if Exists" = Yes, because the existing lines + // carry their own variant codes (fashion/apparel pattern with a blank header variant and variant-specific lines). + Initialize(); + + // [GIVEN] Create Work Center. + CreateWorkCenter(WorkCenter); + + // [GIVEN] Create a manufactured Item and its component items. + CreateMultipleItems( + Item, Item2, Item3, Item."Replenishment System"::"Prod. Order", Item3."Replenishment System"::"Prod. Order", + Item."Reordering Policy"::Order, false); + UpdateItem(Item, Item.FieldNo("Manufacturing Policy"), Item."Manufacturing Policy"::"Make-to-Order"); + + // [GIVEN] Create Production BOM and Routing and assign them to the Item. + LibraryManufacturing.CreateCertifiedProductionBOM(ProductionBOMHeader, Item2."No.", 1); + CreateRoutingWithMultipleRoutingLine(RoutingHeader, WorkCenter."No.", RoutingHeader.Type::Serial, RoutingLine.Type::"Work Center"); + UpdateItem(Item, Item.FieldNo("Production BOM No."), ProductionBOMHeader."No."); + UpdateItem(Item, Item.FieldNo("Routing No."), RoutingHeader."No."); + + // [GIVEN] A variant exists for the Item and the variant is mandatory. + LibraryInventory.CreateItemVariant(ItemVariant, Item."No."); + UpdateItem(Item, Item.FieldNo("Variant Mandatory if Exists"), Item."Variant Mandatory if Exists"::Yes); + + // [GIVEN] Create a Released Production Order for the Item with a blank header Variant Code. + LibraryManufacturing.CreateProductionOrder( + ProductionOrder, ProductionOrder.Status::Released, ProductionOrder."Source Type"::Item, Item."No.", 100); + + // [GIVEN] Create a Prod. Order Line carrying the specific Variant Code is added manually. + LibraryManufacturing.CreateProdOrderLine( + ProdOrderLine, ProductionOrder.Status, ProductionOrder."No.", Item."No.", ItemVariant.Code, '', 100); + + // [WHEN] Refresh with Calc. Lines = No, Calc. Routings = Yes, Calc. Components = Yes. + LibraryManufacturing.RefreshProdOrder(ProductionOrder, false, false, true, true, false); + + // [THEN] Verify no error is raised and components are generated for the variant-specific line. + FindProdOrderComponent(ProdOrderComponent, ProductionOrder, Item2."No."); + Assert.RecordIsNotEmpty(ProdOrderComponent); + end; + + [Test] + procedure RefreshProdOrderWithCalcLinesRequiresHeaderVariantCheck() + var + Item: Record Item; + Item2: Record Item; + Item3: Record Item; + ItemVariant: Record "Item Variant"; + ProductionBOMHeader: Record "Production BOM Header"; + ProductionOrder: Record "Production Order"; + RoutingHeader: Record "Routing Header"; + RoutingLine: Record "Routing Line"; + WorkCenter: Record "Work Center"; + begin + // [SCENARIO 633092] Refreshing a Released Production Order with Calc. Lines = Yes still requires a header + // Variant Code when the source item has "Variant Mandatory if Exists" = Yes, because the lines are calculated + // from the header and would otherwise be created without a mandatory variant. + Initialize(); + + // [GIVEN] Create Work Center. + CreateWorkCenter(WorkCenter); + + // [GIVEN] Create a manufactured Item and its component items. + CreateMultipleItems( + Item, Item2, Item3, Item."Replenishment System"::"Prod. Order", Item3."Replenishment System"::"Prod. Order", + Item."Reordering Policy"::Order, false); + UpdateItem(Item, Item.FieldNo("Manufacturing Policy"), Item."Manufacturing Policy"::"Make-to-Order"); + + // [GIVEN] Create Production BOM and Routing and assign them to the Item. + LibraryManufacturing.CreateCertifiedProductionBOM(ProductionBOMHeader, Item2."No.", 1); + CreateRoutingWithMultipleRoutingLine(RoutingHeader, WorkCenter."No.", RoutingHeader.Type::Serial, RoutingLine.Type::"Work Center"); + UpdateItem(Item, Item.FieldNo("Production BOM No."), ProductionBOMHeader."No."); + UpdateItem(Item, Item.FieldNo("Routing No."), RoutingHeader."No."); + + // [GIVEN] A variant exists for the Item and the variant is mandatory. + LibraryInventory.CreateItemVariant(ItemVariant, Item."No."); + UpdateItem(Item, Item.FieldNo("Variant Mandatory if Exists"), Item."Variant Mandatory if Exists"::Yes); + + // [GIVEN] Create a Released Production Order for the Item with a blank header Variant Code. + LibraryManufacturing.CreateProductionOrder( + ProductionOrder, ProductionOrder.Status::Released, ProductionOrder."Source Type"::Item, Item."No.", 100); + + // [WHEN] Refresh with Calc. Lines = Yes, Calc. Routings = Yes, Calc. Components = Yes. + asserterror LibraryManufacturing.RefreshProdOrder(ProductionOrder, false, true, true, true, false); + + // [THEN] Verify an error is raised because the mandatory header Variant Code is blank. + Assert.ExpectedTestFieldError(ProductionOrder.FieldCaption("Variant Code"), ''); + end; + local procedure Initialize() var LibraryERMCountryData: Codeunit "Library - ERM Country Data"; diff --git a/src/Layers/W1/BaseApp/Manufacturing/Document/RefreshProductionOrder.Report.al b/src/Layers/W1/BaseApp/Manufacturing/Document/RefreshProductionOrder.Report.al index eee4c5ff292..43d41738112 100644 --- a/src/Layers/W1/BaseApp/Manufacturing/Document/RefreshProductionOrder.Report.al +++ b/src/Layers/W1/BaseApp/Manufacturing/Document/RefreshProductionOrder.Report.al @@ -44,6 +44,7 @@ report 99001025 "Refresh Production Order" CurrReport.Skip(); TestField("Due Date"); + CheckVariantCodeMandatory("Production Order"); if CalcLines then if IsComponentPicked("Production Order") then begin @@ -400,6 +401,23 @@ report 99001025 "Refresh Production Order" exit(not ProdOrderComponent.IsEmpty()); end; + local procedure CheckVariantCodeMandatory(var ProductionOrder: Record "Production Order") + var + Item: Record Item; + IsHandled: Boolean; + begin + if not CalcLines then + exit; + if ProductionOrder."Source Type" <> ProductionOrder."Source Type"::Item then + exit; + if Item.IsVariantMandatory(true, ProductionOrder."Source No.") then begin + IsHandled := false; + OnBeforeCheckVariantCodeMandatory(ProductionOrder, IsHandled); + if not IsHandled then + ProductionOrder.TestField("Variant Code"); + end; + end; + local procedure GetRoutingNo(ProductionOrder: Record "Production Order") RoutingNo: Code[20] var Item: Record Item; @@ -480,6 +498,11 @@ report 99001025 "Refresh Production Order" begin end; + [IntegrationEvent(false, false)] + local procedure OnBeforeCheckVariantCodeMandatory(var ProductionOrder: Record "Production Order"; var IsHandled: Boolean) + begin + end; + [IntegrationEvent(false, false)] local procedure OnProductionOrderOnAfterGetRecordOnAfterCalcRoutingsOrComponents(var ProductionOrder: Record "Production Order"; CalcLines: Boolean; CalcRoutings: Boolean; CalcComponents: Boolean; var ErrorOccured: Boolean) begin diff --git a/src/Layers/W1/Tests/SCM-Manufacturing/SCMManufacturing70.Codeunit.al b/src/Layers/W1/Tests/SCM-Manufacturing/SCMManufacturing70.Codeunit.al index dd11535830e..48a1458e385 100644 --- a/src/Layers/W1/Tests/SCM-Manufacturing/SCMManufacturing70.Codeunit.al +++ b/src/Layers/W1/Tests/SCM-Manufacturing/SCMManufacturing70.Codeunit.al @@ -3626,7 +3626,7 @@ codeunit 137063 "SCM Manufacturing 7.0" end; [Test] - [HandlerFunctions('ChangeStatusOnProdOrderToFinished,ErrorMessageHandler')] + [HandlerFunctions('ChangeStatusOnProdOrderToFinished')] procedure ChangeProductionOrderStatusWithoutVariantCode() var ProductionOrder: array[4] of Record "Production Order"; @@ -3662,7 +3662,8 @@ codeunit 137063 "SCM Manufacturing 7.0" ProductionOrder[1]."Source Type"::Item, Item[1]."No.", LibraryRandom.RandInt(5)); // [GIVEN] Refresh Production Order "X". - LibraryManufacturing.RefreshProdOrder(ProductionOrder[1], false, true, true, true, false); + AssertError LibraryManufacturing.RefreshProdOrder(ProductionOrder[1], false, true, true, true, false); + Assert.ExpectedTestFieldError(ProductionOrder[1].FieldCaption("Variant Code"), ''); // [GIVEN] Create Production Order "Y". LibraryManufacturing.CreateProductionOrder(