Codeunit 99000787 Create Prod. Order Lines, source in 29

Source29

src/Layers/W1/BaseApp/Manufacturing/Document/CreateProdOrderLines.Codeunit.al867 lines, Copyright (c) Microsoft Corporation. MIT

// ------------------------------------------------------------------------------------------------
// 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.Document;

using Microsoft.Inventory;
using Microsoft.Inventory.Item;
using Microsoft.Inventory.Location;
using Microsoft.Inventory.Requisition;
using Microsoft.Inventory.Tracking;
using Microsoft.Manufacturing.Family;
using Microsoft.Manufacturing.Routing;
using Microsoft.Manufacturing.Setup;
using Microsoft.Sales.Document;

codeunit 99000787 "Create Prod. Order Lines"
{
    Permissions = TableData Item = r,
                  TableData "Sales Header" = r,
                  TableData "Sales Line" = r,
                  TableData "Prod. Order Line" = rimd,
                  TableData "Prod. Order Component" = rimd,
                  TableData "Manufacturing Setup" = rim,
                  TableData "Family Line" = r,
                  TableData "Production Order" = r;

    trigger OnRun()
    begin
    end;

    var
        SalesLine: Record "Sales Line";
        MfgSetup: Record "Manufacturing Setup";
        ProdOrder: Record "Production Order";
        ProdOrderLine: Record "Prod. Order Line";
        ProdOrderComp: Record "Prod. Order Component";
        TempOldProdOrderComp: Record "Prod. Order Component" temporary;
        CalcProdOrder: Codeunit "Calculate Prod. Order";
        NextProdOrderLineNo: Integer;
        InsertNew: Boolean;
        SalesLineIsSet: Boolean;

    procedure CheckStructure(Status: Option; ProdOrderNo: Code[20]; Direction: Option Forward,Backward; MultiLevel: Boolean; LetDueDateDecrease: Boolean)
    begin
        ProdOrder.Get(Status, ProdOrderNo);
        ProdOrderLine.SetRange(Status, Status);
        ProdOrderLine.SetRange("Prod. Order No.", ProdOrderNo);
        if ProdOrderLine.Find('+') then
            NextProdOrderLineNo := ProdOrderLine."Line No." + 10000
        else
            NextProdOrderLineNo := 10000;

        CheckMultiLevelStructure(Direction, MultiLevel, LetDueDateDecrease);
    end;

    procedure Copy(ProdOrder2: Record "Production Order"; Direction: Option Forward,Backward; VariantCode: Code[10]; LetDueDateDecrease: Boolean): Boolean
    var
        ErrorOccured: Boolean;
        IsHandled: Boolean;
    begin
        OnBeforeCopy(ProdOrder2, Direction, VariantCode, LetDueDateDecrease, ErrorOccured, IsHandled);
        if IsHandled then
            exit(not ErrorOccured);

        MfgSetup.Get();

        ProdOrder2.TestField("Source No.");
        ProdOrder2.TestField("Starting Time");
        ProdOrder2.TestField("Starting Date");
        ProdOrder2.TestField("Ending Time");
        ProdOrder2.TestField("Ending Date");
        if Direction = Direction::Backward then
            ProdOrder2.TestField("Due Date");

        ProdOrder := ProdOrder2;

        CreateProdOrderLine(ProdOrder, VariantCode, ErrorOccured);

        if not ProcessProdOrderLines(Direction, LetDueDateDecrease) then
            ErrorOccured := true;

        CheckMultiLevelStructure(Direction, true, LetDueDateDecrease);

        OnAfterCopy(ProdOrder, ErrorOccured);

        exit(not ErrorOccured);
    end;

    local procedure CopyFromFamily(): Boolean
    var
        Family: Record Family;
        FamilyLine: Record "Family Line";
        RoutingHeader: Record "Routing Header";
        ErrorOccured: Boolean;
    begin
        Family.Get(ProdOrder."Source No.");
        FamilyLine.SetCurrentKey("Low-Level Code");
        FamilyLine.SetRange("Family No.", ProdOrder."Source No.");
        OnCopyFromFamilyOnAfterFamilyLineSetFilters(FamilyLine, ProdOrder);

        if FamilyLine.FindSet() then
            repeat
                if FamilyLine."Item No." <> '' then begin
                    InitProdOrderLine(FamilyLine."Item No.", '', ProdOrder."Location Code");
                    OnCopyFromFamilyOnAfterInitProdOrderLine(ProdOrder, FamilyLine, ProdOrderLine);
                    ProdOrderLine.Description := FamilyLine.Description;
                    ProdOrderLine."Description 2" := FamilyLine."Description 2";
                    ProdOrderLine.Validate("Unit of Measure Code", FamilyLine."Unit of Measure Code");
                    ProdOrderLine.Validate(Quantity, FamilyLine.Quantity * ProdOrder.Quantity);
                    ProdOrderLine."Routing No." := Family."Routing No.";
                    ProdOrderLine."Routing Reference No." := 0;
                    if Family."Routing No." <> '' then begin
                        RoutingHeader.Get(Family."Routing No.");
                        ProdOrderLine."Routing Type" := RoutingHeader.Type;
                    end;
                    ProdOrderLine.UpdateDatetime();
                    OnCopyFromFamilyOnBeforeInsertProdOrderLine(ProdOrderLine, FamilyLine);
                    InsertProdOrderLine();
                    if ProdOrderLine.HasErrorOccured() then
                        ErrorOccured := true;
                    OnCopyFromFamilyOnAfterInsertProdOrderLine(ProdOrderLine);
                end;
            until FamilyLine.Next() = 0;
        exit(not ErrorOccured);
    end;

    procedure CopyFromSalesOrder(SalesHeader: Record "Sales Header"): Boolean
    var
        TempSalesPlanningLine: Record "Sales Planning Line" temporary;
        TrackingSpecification: Record "Tracking Specification";
        Location: Record Location;
        LeadTimeMgt: Codeunit "Lead-Time Management";
        ItemTrackingMgt: Codeunit "Item Tracking Management";
        SalesLineReserve: Codeunit "Sales Line-Reserve";
        ErrorOccured: Boolean;
        ShouldCreateTempSalesPlanningLines: Boolean;
        QuantityBase: Decimal;
    begin
        OnBeforeCopyFromSalesOrder(SalesHeader, SalesLine, ProdOrder, NextProdOrderLineNo);

        SalesLine.SetRange("Document Type", SalesHeader."Document Type");
        SalesLine.SetRange("Document No.", SalesHeader."No.");
        if SalesLine.FindSet() then
            repeat
                SalesLine.CalcFields("Reserved Quantity");
                ShouldCreateTempSalesPlanningLines := (SalesLine.Type = SalesLine.Type::Item) and
                   (SalesLine."No." <> '') and
                   ((SalesLine."Outstanding Quantity" - SalesLine."Reserved Quantity") <> 0);
                OnCopyFromSalesOrderOnAfterCalcShouldCreateTempSalesPlanningLines(SalesLine, ShouldCreateTempSalesPlanningLines);
                if ShouldCreateTempSalesPlanningLines then
                    if IsReplSystemProdOrder(SalesLine."No.", SalesLine."Variant Code", SalesLine."Location Code") then begin
                        TempSalesPlanningLine.Init();
                        TempSalesPlanningLine."Sales Order No." := SalesLine."Document No.";
                        TempSalesPlanningLine."Sales Order Line No." := SalesLine."Line No.";
                        TempSalesPlanningLine.Validate("Item No.", SalesLine."No.");
                        OnCopyFromSalesOrderOnBeforeSalesPlanLineInsert(SalesLine, TempSalesPlanningLine);
                        TempSalesPlanningLine.Insert();
                    end;
            until SalesLine.Next() = 0;
        OnCopyFromSalesOrderOnAfterSalesPlanLinesInsert(SalesHeader, SalesLine);

        TempSalesPlanningLine.SetCurrentKey("Low-Level Code");
        if TempSalesPlanningLine.FindSet() then
            repeat
                SalesLine.Get(SalesHeader."Document Type", TempSalesPlanningLine."Sales Order No.", TempSalesPlanningLine."Sales Order Line No.");
                SalesLine.CalcFields("Reserved Quantity");

                InitProdOrderLine(SalesLine."No.", SalesLine."Variant Code", SalesLine."Location Code");
                ProdOrderLine.Description := SalesLine.Description;
                ProdOrderLine."Description 2" := SalesLine."Description 2";
                SalesLine.CalcFields("Reserved Qty. (Base)");
                QuantityBase := SalesLine."Outstanding Qty. (Base)" - SalesLine."Reserved Qty. (Base)";
                OnCopyFromSalesOrderOnAfterCalcQuantityBase(ProdOrderLine, SalesLineIsSet, SalesLine, QuantityBase);
                ProdOrderLine.Validate("Quantity (Base)", QuantityBase);

                if Location.Get(ProdOrderLine."Location Code") and not Location."Require Pick" and (SalesLine."Bin Code" <> '') then
                    ProdOrderLine."Bin Code" := SalesLine."Bin Code";

                ProdOrderLine."Due Date" := SalesLine."Shipment Date";
                ProdOrderLine."Ending Date" :=
                  LeadTimeMgt.GetPlannedEndingDate(
                    ProdOrderLine."Item No.", ProdOrderLine."Location Code", ProdOrderLine."Variant Code",
                    ProdOrderLine."Due Date", '', Enum::"Requisition Ref. Order Type"::"Prod. Order");
                ProdOrderLine.Validate("Ending Date");

                OnBeforeProdOrderLineInsert(ProdOrderLine, ProdOrder, true, SalesLine);
                InsertProdOrderLine();
                if ProdOrderLine.HasErrorOccured() then
                    ErrorOccured := true;
                ItemTrackingMgt.CopyItemTracking(SalesLine.RowID1(), ProdOrderLine.RowID1(), true, true);

                if SalesLine."Document Type" = SalesLine."Document Type"::Order then begin // Not simulated
                    ProdOrderLine.CalcFields("Reserved Quantity", "Reserved Qty. (Base)");
                    TrackingSpecification.InitTrackingSpecification(
                        Database::"Prod. Order Line", ProdOrderLine.Status.AsInteger(), ProdOrderLine."Prod. Order No.", '', ProdOrderLine."Line No.", 0,
                        ProdOrderLine."Variant Code", ProdOrderLine."Location Code", ProdOrderLine."Qty. per Unit of Measure");
                    SalesLineReserve.BindToTracking(
                        SalesLine, TrackingSpecification, ProdOrderLine.Description, ProdOrderLine."Ending Date",
                        ProdOrderLine."Remaining Quantity" - ProdOrderLine."Reserved Quantity",
                        ProdOrderLine."Remaining Qty. (Base)" - ProdOrderLine."Reserved Qty. (Base)");
                end;
                CopyDimFromSalesLine(SalesLine, ProdOrderLine);
                OnCopyFromSalesOrderOnBeforeProdOrderLineModify(ProdOrderLine, SalesLine, TempSalesPlanningLine, NextProdOrderLineNo);
                ProdOrderLine.Modify();
                OnCopyFromSalesOrderOnAfterProdOrderLineModify(ProdOrderLine, SalesLine);
            until (TempSalesPlanningLine.Next() = 0);
        exit(not ErrorOccured);
    end;

    local procedure CreateProdOrderLine(ProdOrder: Record "Production Order"; VariantCode: Code[10]; var ErrorOccured: Boolean)
    var
        SalesHeader: Record "Sales Header";
        IsHandled: Boolean;
    begin
        DeleteLinesForProductionOrder(ProdOrder);

        NextProdOrderLineNo := 10000;

        InsertNew := false;

        case ProdOrder."Source Type" of
            ProdOrder."Source Type"::Item:
                begin
                    IsHandled := false;
                    OnCreateProdOrderLineOnBeforeCreateProdOrderLineFromItem(IsHandled, ErrorOccured, ProdOrder, VariantCode);
                    if not IsHandled then
                        CreateProdOrderLineFromItem(ProdOrder, VariantCode, ErrorOccured);
                end;
            ProdOrder."Source Type"::Family:
                if not CopyFromFamily() then
                    ErrorOccured := true;
            ProdOrder."Source Type"::"Sales Header":
                begin
                    InsertNew := true;
                    if ProdOrder.Status <> ProdOrder.Status::Simulated then
                        SalesHeader.Get(SalesHeader."Document Type"::Order, ProdOrder."Source No.")
                    else
                        SalesHeader.Get(SalesHeader."Document Type"::Quote, ProdOrder."Source No.");
                    if not CopyFromSalesOrder(SalesHeader) then
                        ErrorOccured := true;
                end;
        end;

        OnAfterCreateProdOrderLine(ProdOrder, VariantCode, ErrorOccured);
    end;

    local procedure CreateProdOrderLineFromItem(var ProductionOrder: Record "Production Order"; VariantCode: Code[10]; var ErrorOccured: Boolean)
    begin
        OnCreateProdOrderLineOnBeforeInitProdOrderLine(InsertNew);
        InitProdOrderLine(ProductionOrder."Source No.", VariantCode, ProductionOrder."Location Code");
        ProdOrderLine.Description := ProductionOrder.Description;
        ProdOrderLine."Description 2" := ProductionOrder."Description 2";
        ProdOrderLine.Validate(Quantity, ProductionOrder.Quantity);
        ProdOrderLine.UpdateDatetime();
        if SalesLineIsSet then
            CopyDimFromSalesLine(SalesLine, ProdOrderLine);
        OnBeforeProdOrderLineInsert(ProdOrderLine, ProductionOrder, SalesLineIsSet, SalesLine);
        ProdOrderLine.Insert();
        if ProdOrderLine.HasErrorOccured() then
            ErrorOccured := true;

        OnAfterProdOrderLineInsert(ProductionOrder, ProdOrderLine, NextProdOrderLineNo);
    end;

    local procedure DeleteLinesForProductionOrder(ProductionOrder: Record "Production Order")
    var
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeDeleteLinesForProductionOrder(ProductionOrder, NextProdOrderLineNo, IsHandled);
        if IsHandled then
            exit;

        ProdOrderLine.LockTable();
        ProdOrderLine.SetRange(Status, ProductionOrder.Status);
        ProdOrderLine.SetRange("Prod. Order No.", ProductionOrder."No.");
        ProdOrderLine.DeleteAll(true);
    end;

    procedure InitProdOrderLine(ItemNo: Code[20]; VariantCode: Code[10]; LocationCode: Code[10])
    var
        Item: Record Item;
        ProdOrderWarehouseMgt: Codeunit "Prod. Order Warehouse Mgt.";
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeInitProdOrderLine(ProdOrder, SalesLine, ItemNo, VariantCode, LocationCode, IsHandled);
        if IsHandled then
            exit;

        ProdOrderLine.Init();
        ProdOrderLine.SetIgnoreErrors();
        ProdOrderLine.Status := ProdOrder.Status;
        ProdOrderLine."Prod. Order No." := ProdOrder."No.";
        ProdOrderLine."Line No." := NextProdOrderLineNo;
        ProdOrderLine."Routing Reference No." := ProdOrderLine."Line No.";
        OnInitProdOrderLineBeforeAssignItemNo(ProdOrderLine, ItemNo, VariantCode, LocationCode);
        ProdOrderLine.Validate("Item No.", ItemNo);
        ProdOrderLine."Location Code" := LocationCode;
        ProdOrderWarehouseMgt.CompareProdOrderWithProdOrderLinesForLocation(ProdOrder, ProdOrderLine);
        ProdOrderWarehouseMgt.ValidateWarehousePutAwayLocation(ProdOrderLine);
        ProdOrderLine.Validate("Variant Code", VariantCode);
        OnInitProdOrderLineAfterVariantCode(ProdOrderLine, VariantCode);
        if (LocationCode = ProdOrder."Location Code") and (ProdOrder."Bin Code" <> '') then
            ProdOrderLine.Validate("Bin Code", ProdOrder."Bin Code")
        else
            CalcProdOrder.SetProdOrderLineBinCodeFromRoute(ProdOrderLine, ProdOrderLine."Location Code", ProdOrderLine."Routing No.");

        Item.Get(ItemNo);
        ProdOrderLine."Scrap %" := Item."Scrap %";
        OnInitProdOrderLineAfterScrap(ProdOrderLine, ProdOrder);
        ProdOrderLine."Due Date" := ProdOrder."Due Date";
        ProdOrderLine."Starting Date" := ProdOrder."Starting Date";
        ProdOrderLine."Starting Time" := ProdOrder."Starting Time";
        ProdOrderLine."Ending Date" := ProdOrder."Ending Date";
        ProdOrderLine."Ending Time" := ProdOrder."Ending Time";
        ProdOrderLine."Planning Level Code" := 0;
        ProdOrderLine."Inventory Posting Group" := Item."Inventory Posting Group";
        ProdOrderLine.UpdateDatetime();
        ProdOrderLine.Validate("Unit Cost");

        OnAfterInitProdOrderLine(ProdOrderLine, ProdOrder, SalesLine);

        NextProdOrderLineNo := NextProdOrderLineNo + 10000;
    end;

    procedure InsertProdOrderLine(): Boolean
    var
        ProdOrderLine3: Record "Prod. Order Line";
        IsHandled: Boolean;
    begin
        ProdOrderLine3 := ProdOrderLine;
        ProdOrderLine3.SetRange(Status, ProdOrderLine.Status);
        ProdOrderLine3.SetRange("Prod. Order No.", ProdOrderLine."Prod. Order No.");
        ProdOrderLine3.SetRange("Item No.", ProdOrderLine."Item No.");
        ProdOrderLine3.SetRange("Unit of Measure Code", ProdOrderLine."Unit of Measure Code");
        ProdOrderLine3.SetRange("Variant Code", ProdOrderLine."Variant Code");
        ProdOrderLine3.SetRange("Location Code", ProdOrderLine."Location Code");
        ProdOrderLine3.SetRange("Planning Level Code", ProdOrderLine."Planning Level Code");
        IsHandled := false;
        OnBeforeInsertProdOrderLine(ProdOrderLine, ProdOrderLine3, InsertNew, IsHandled);
        if not IsHandled then
            if (not InsertNew) and ProdOrderLine3.FindFirst() then begin
                CopyProdOrderCompToTemp(ProdOrderLine3);
                ProdOrderLine3.Validate(Quantity, ProdOrderLine3.Quantity + ProdOrderLine.Quantity);
                AdjustDateAndTime(ProdOrderLine3, ProdOrderLine."Due Date", ProdOrderLine."Ending Date", ProdOrderLine."Ending Time");

                if ProdOrderLine3."Planning Level Code" < ProdOrderLine."Planning Level Code" then begin
                    ProdOrderLine3."Planning Level Code" := ProdOrderLine."Planning Level Code";
                    UpdateCompPlanningLevel(ProdOrderLine3);
                end;
                OnBeforeProdOrderLine3Modify(ProdOrderLine3, ProdOrderLine);
                ProdOrderLine3.Modify();
                ProdOrderLine := ProdOrderLine3;
                exit(false);
            end;
        ProdOrderLine.Insert();
        OnAfterProdOrderLineInsert(ProdOrder, ProdOrderLine, NextProdOrderLineNo);
        exit(true);
    end;

    local procedure ProcessProdOrderLines(Direction: Option Forward,Backward; LetDueDateDecrease: Boolean): Boolean
    var
        ErrorOccured: Boolean;
        IsHandled: Boolean;
    begin
        ProdOrderLine.SetRange(Status, ProdOrder.Status);
        ProdOrderLine.SetRange("Prod. Order No.", ProdOrder."No.");
        if ProdOrderLine.FindSet(true) then
            repeat
                IsHandled := false;
                OnBeforeProcessProdOrderLine(ProdOrderLine, ProdOrder, Direction, LetDueDateDecrease, ErrorOccured, IsHandled);
                if not IsHandled then begin
                    CalcProdOrder.SetParameter(true);
                    if not CalcProdOrder.Calculate(ProdOrderLine, Direction, true, true, true, LetDueDateDecrease) then
                        ErrorOccured := true;
                end;
                OnAfterProcessProdOrderLine(ProdOrderLine, Direction, LetDueDateDecrease);
            until ProdOrderLine.Next() = 0;
        OnProcessProdOrderLinesOnBeforeAdjustStartEndingDate(ProdOrder);
        ProdOrder.AdjustStartEndingDate();
        ProdOrder.Modify();

        exit(not ErrorOccured);
    end;

    local procedure CheckMultiLevelStructure(Direction: Option Forward,Backward; MultiLevel: Boolean; LetDueDateDecrease: Boolean)
    var
        MultiLevelStructureCreated: Boolean;
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeCheckMultiLevelStructure(ProdOrder, Direction, IsHandled);
        if IsHandled then
            exit;

        ProdOrderComp.SetCurrentKey(Status, "Prod. Order No.", "Prod. Order Line No.", "Item Low-Level Code");
        ProdOrderComp.SetRange(Status, ProdOrder.Status);
        ProdOrderComp.SetRange("Prod. Order No.", ProdOrder."No.");
        ProdOrderComp.SetFilter("Item No.", '<>%1', '');
        if ProdOrderComp.FindSet(true) then
            repeat
                if ProdOrderComp."Planning Level Code" = 0 then
                    if ShouldIncreasePlanningLevel(ProdOrderComp) then begin
                        ProdOrderComp."Planning Level Code" := 1;
                        ProdOrderComp.Modify(true);
                    end;
                if ProdOrderComp."Planning Level Code" > 0 then
                    MultiLevelStructureCreated :=
                      MultiLevelStructureCreated or
                      CheckMakeOrderLine(ProdOrderComp, ProdOrderLine, Direction, MultiLevel, LetDueDateDecrease);
            until ProdOrderComp.Next() = 0;
        if MultiLevelStructureCreated then
            ReserveMultiLevelStructure(ProdOrderComp);
    end;

    local procedure ShouldIncreasePlanningLevel(ProdOrderComp: Record "Prod. Order Component") IncreasePlanningLevel: Boolean
    var
        Item: Record Item;
        StockkeepingUnit: Record "Stockkeeping Unit";
    begin
        if StockkeepingUnit.Get(ProdOrderComp."Location Code", ProdOrderComp."Item No.", ProdOrderComp."Variant Code") then
            IncreasePlanningLevel :=
              (StockkeepingUnit."Manufacturing Policy" = StockkeepingUnit."Manufacturing Policy"::"Make-to-Order") and
              (StockkeepingUnit.IsMfgSKU())
        else begin
            Item.Get(ProdOrderComp."Item No.");
            IncreasePlanningLevel :=
              (Item."Manufacturing Policy" = Item."Manufacturing Policy"::"Make-to-Order") and Item.IsMfgItem();
        end;
        OnAfterShouldIncreasePlanningLevel(ProdOrderComp, StockkeepingUnit, IncreasePlanningLevel);
    end;

    procedure CheckMakeOrderLine(var ProdOrderComp: Record "Prod. Order Component"; var ProdOrderLine: Record "Prod. Order Line"; Direction: Option Forward,Backward; MultiLevel: Boolean; LetDueDateDecrease: Boolean): Boolean
    var
        Item: Record Item;
        ParentItem: Record Item;
        ParentSKU: Record "Stockkeeping Unit";
        SKU: Record "Stockkeeping Unit";
        ProdOrderLine2: Record "Prod. Order Line";
        MakeProdOrder: Boolean;
        Inserted: Boolean;
    begin
        ProdOrderLine2.Get(ProdOrderComp.Status, ProdOrderComp."Prod. Order No.", ProdOrderComp."Prod. Order Line No.");
        if ParentSKU.Get(ProdOrderLine2."Location Code", ProdOrderLine2."Item No.", ProdOrderLine2."Variant Code") then
            MakeProdOrder := ParentSKU."Manufacturing Policy" = ParentSKU."Manufacturing Policy"::"Make-to-Order"
        else begin
            ParentItem.Get(ProdOrderLine2."Item No.");
            MakeProdOrder := ParentItem."Manufacturing Policy" = ParentItem."Manufacturing Policy"::"Make-to-Order";
        end;

        OnCheckMakeOrderLineBeforeIf(ProdOrder, ProdOrderLine2, ProdOrderComp, MakeProdOrder);

        if not MakeProdOrder then
            exit(false);

        Item.Get(ProdOrderComp."Item No.");

        if SKU.Get(ProdOrderComp."Location Code", ProdOrderComp."Item No.", ProdOrderComp."Variant Code") then
            MakeProdOrder :=
              SKU.IsMfgSKU() and (SKU."Manufacturing Policy" = SKU."Manufacturing Policy"::"Make-to-Order")
        else
            MakeProdOrder :=
              Item.IsMfgItem() and (Item."Manufacturing Policy" = Item."Manufacturing Policy"::"Make-to-Order");

        OnCheckMakeOrderLineBeforeIf(ProdOrder, ProdOrderLine2, ProdOrderComp, MakeProdOrder);

        if not MakeProdOrder then
            exit(false);

        InitProdOrderLine(ProdOrderComp."Item No.", ProdOrderComp."Variant Code", ProdOrderComp."Location Code");
        ProdOrderLine.Validate("Unit of Measure Code", ProdOrderComp."Unit of Measure Code");
        ProdOrderLine."Qty. per Unit of Measure" := ProdOrderComp."Qty. per Unit of Measure";
        ProdOrderLine."Bin Code" := ProdOrderComp."Bin Code";
        ProdOrderLine.Description := ProdOrderComp.Description;
        ProdOrderLine."Description 2" := ProdOrderComp."Description 2";
        ProdOrderComp.CalcFields("Reserved Quantity");
        ProdOrderLine.Validate(Quantity, ProdOrderComp."Expected Quantity" - ProdOrderComp."Reserved Quantity");
        if ProdOrderLine."Quantity (Base)" = 0 then
            exit(false);
        ProdOrderLine."Planning Level Code" := ProdOrderComp."Planning Level Code";
        ProdOrderLine."Due Date" := ProdOrderComp."Due Date";
        ProdOrderLine."Ending Date" := ProdOrderComp."Due Date";
        ProdOrderLine."Ending Time" := ProdOrderComp."Due Time";
        ProdOrderLine.UpdateDatetime();
        // this InsertNew is responsible for controlling if same POLine is added up or new POLine is created
        InsertNew := InsertNew and (ProdOrderComp."Planning Level Code" > 1);

        OnCheckMakeOrderLineBeforeInsert(ProdOrderLine, ProdOrderComp, InsertNew);
        Inserted := InsertProdOrderLine();
        if MultiLevel then begin
            if Inserted then
                CalcProdOrder.Calculate(ProdOrderLine, Direction::Backward, true, true, true, LetDueDateDecrease)
            else begin
                CalcProdOrder.Recalculate(ProdOrderLine, Direction::Backward, LetDueDateDecrease);
                if ProdOrderLine."Line No." < ProdOrderComp."Prod. Order Line No." then
                    UpdateProdOrderLine(ProdOrderLine, Direction, LetDueDateDecrease);
            end;
            OnCheckMakeOrderLineOnAfterUpdateProdOrderLine(ProdOrderLine);
        end else
            exit(false);
        ProdOrderComp."Supplied-by Line No." := ProdOrderLine."Line No.";
        ProdOrderComp.Modify();
        exit(true);
    end;

    procedure ReserveMultiLevelStructure(var ProdOrderComp2: Record "Prod. Order Component")
    var
        ProdOrderComp3: Record "Prod. Order Component";
        ProdOrderLine3: Record "Prod. Order Line";
        TrackingSpecification: Record "Tracking Specification";
        ProdOrderCompReserve: Codeunit "Prod. Order Comp.-Reserve";
        IsHandled: Boolean;
    begin
        ProdOrderComp3.Copy(ProdOrderComp2);
        ProdOrderComp3.SetCurrentKey(Status, "Prod. Order No.", "Prod. Order Line No.", "Due Date");
        ProdOrderComp3.SetFilter("Supplied-by Line No.", '<>0');
        if ProdOrderComp3.Find('+') then
            repeat
                IsHandled := false;
                OnBeforeReserveMultiLevelStructureComp(ProdOrderComp3, ProdOrderLine3, IsHandled);
                if not IsHandled then
                    if ProdOrderLine3.Get(
                         ProdOrderComp3.Status, ProdOrderComp3."Prod. Order No.", ProdOrderComp3."Supplied-by Line No.")
                    then begin
                        ProdOrderComp3.CalcFields("Reserved Quantity", "Reserved Qty. (Base)");
                        TrackingSpecification.InitTrackingSpecification(
                            Database::"Prod. Order Line", ProdOrderLine3.Status.AsInteger(), ProdOrderLine3."Prod. Order No.", '', ProdOrderLine3."Line No.", 0,
                            ProdOrderLine3."Variant Code", ProdOrderLine3."Location Code", ProdOrderLine3."Qty. per Unit of Measure");
                        ProdOrderCompReserve.BindToTracking(
                            ProdOrderComp3, TrackingSpecification, ProdOrderLine3.Description, ProdOrderLine3."Ending Date",
                            ProdOrderComp3."Remaining Quantity" - ProdOrderComp3."Reserved Quantity",
                            ProdOrderComp3."Remaining Qty. (Base)" - ProdOrderComp3."Reserved Qty. (Base)");
                    end;
                OnAfterReserveMultiLevelStructureComp(ProdOrderLine3, ProdOrderComp3);
            until ProdOrderComp3.Next(-1) = 0;
    end;

    procedure CopyDimFromSalesLine(SalesLine: Record "Sales Line"; var ProdOrderLine: Record "Prod. Order Line")
    begin
        ProdOrderLine."Shortcut Dimension 1 Code" := SalesLine."Shortcut Dimension 1 Code";
        ProdOrderLine."Shortcut Dimension 2 Code" := SalesLine."Shortcut Dimension 2 Code";
        ProdOrderLine."Dimension Set ID" := SalesLine."Dimension Set ID";
    end;

    procedure SetSalesLine(SalesLine2: Record "Sales Line")
    begin
        SalesLine := SalesLine2;
        SalesLineIsSet := true;
    end;

    local procedure AdjustDateAndTime(var ProdOrderLine3: Record "Prod. Order Line"; DueDate: Date; EndingDate: Date; EndingTime: Time)
    begin
        if ProdOrderLine3."Due Date" > DueDate then
            ProdOrderLine3."Due Date" := DueDate;

        if ProdOrderLine3."Ending Date" > EndingDate then begin
            ProdOrderLine3."Ending Date" := EndingDate;
            ProdOrderLine3."Ending Time" := EndingTime;
        end else
            if (ProdOrderLine3."Ending Date" = EndingDate) and
               (ProdOrderLine3."Ending Time" > EndingTime)
            then
                ProdOrderLine3."Ending Time" := EndingTime;
        ProdOrderLine3.UpdateDatetime();
    end;

    local procedure UpdateCompPlanningLevel(ProdOrderLine3: Record "Prod. Order Line")
    var
        ProdOrderComp3: Record "Prod. Order Component";
    begin
        // update planning level code of component
        ProdOrderComp3.SetRange(Status, ProdOrderLine3.Status);
        ProdOrderComp3.SetRange("Prod. Order No.", ProdOrderLine3."Prod. Order No.");
        ProdOrderComp3.SetRange("Prod. Order Line No.", ProdOrderLine3."Line No.");
        ProdOrderComp3.SetFilter("Planning Level Code", '>0');
        if ProdOrderComp3.FindSet(true) then
            repeat
                ProdOrderComp3."Planning Level Code" := ProdOrderLine3."Planning Level Code" + 1;
                ProdOrderComp3.Modify();
            until ProdOrderComp3.Next() = 0;
    end;

    local procedure UpdateProdOrderLine(var ProdOrderLine: Record "Prod. Order Line"; Direction: Option Forward,Backward; LetDueDateDecrease: Boolean)
    var
        ProdOrderLine3: Record "Prod. Order Line";
        ProdOrderComp3: Record "Prod. Order Component";
    begin
        ProdOrderComp3.SetRange(Status, ProdOrderLine.Status);
        ProdOrderComp3.SetRange("Prod. Order No.", ProdOrderLine."Prod. Order No.");
        ProdOrderComp3.SetRange("Prod. Order Line No.", ProdOrderLine."Line No.");
        if ProdOrderComp3.FindSet() then
            repeat
                ProdOrderLine3.CopyFilters(ProdOrderLine);
                ProdOrderLine3.SetRange("Item No.", ProdOrderComp3."Item No.");
                ProdOrderLine3.SetRange("Variant Code", ProdOrderComp3."Variant Code");
                if ProdOrderLine3.FindFirst() then begin
                    ProdOrderComp3.CalcFields("Reserved Quantity");
                    TempOldProdOrderComp.Get(ProdOrderComp3.Status, ProdOrderComp3."Prod. Order No.",
                      ProdOrderComp3."Prod. Order Line No.", ProdOrderComp3."Line No.");
                    ProdOrderLine3.Validate(Quantity,
                      ProdOrderLine3.Quantity - TempOldProdOrderComp."Expected Quantity" +
                      ProdOrderComp3."Expected Quantity" - ProdOrderComp3."Reserved Quantity");
                    if ProdOrderLine3."Planning Level Code" < ProdOrderComp3."Planning Level Code" then
                        ProdOrderLine3."Planning Level Code" := ProdOrderComp3."Planning Level Code";
                    AdjustDateAndTime(ProdOrderLine3, ProdOrderComp3."Due Date", ProdOrderComp3."Due Date", ProdOrderComp3."Due Time");
                    OnUpdateProdOrderLineOnBeforeUpdateCompPlanningLevel(ProdOrderLine3);
                    UpdateCompPlanningLevel(ProdOrderLine3);
                    CalcProdOrder.Recalculate(ProdOrderLine3, Direction::Backward, LetDueDateDecrease);
                    ProdOrderLine3.Modify();
                end;
            until ProdOrderComp3.Next() = 0;
        TempOldProdOrderComp.DeleteAll();

        OnAfterUpdateProdOrderLine(ProdOrderLine, Direction, LetDueDateDecrease);
    end;

    local procedure CopyProdOrderCompToTemp(ProdOrderLine3: Record "Prod. Order Line")
    var
        ProdOrderComp2: Record "Prod. Order Component";
    begin
        TempOldProdOrderComp.DeleteAll();
        ProdOrderComp2.SetRange(Status, ProdOrderLine3.Status);
        ProdOrderComp2.SetRange("Prod. Order No.", ProdOrderLine3."Prod. Order No.");
        ProdOrderComp2.SetRange("Prod. Order Line No.", ProdOrderLine3."Line No.");
        if ProdOrderComp2.FindSet() then
            repeat
                TempOldProdOrderComp := ProdOrderComp2;
                TempOldProdOrderComp.Insert();
            until ProdOrderComp2.Next() = 0;
    end;

    local procedure IsReplSystemProdOrder(ItemNo: Code[20]; VariantCode: Code[10]; LocationCode: Code[10]): Boolean
    var
        IsHandled: Boolean;
        ReplanSystemProdOrder: Boolean;
    begin
        IsHandled := false;
        OnBeforeIsReplSystemProdOrder(SalesLine, ReplanSystemProdOrder, IsHandled);
        if IsHandled then
            exit(ReplanSystemProdOrder);

        exit(CheckReplenishmentSystemProdOrderAndNotProductionBlocked(ItemNo, VariantCode, LocationCode));
    end;

    internal procedure CheckReplenishmentSystemProdOrderAndNotProductionBlocked(ItemNo: Code[20]; VariantCode: Code[10]; LocationCode: Code[10]) ReplenishmentSystemProdOrderAndNotProductionBlocked: Boolean
    var
        StockkeepingUnit: Record "Stockkeeping Unit";
        Item: Record Item;
        ItemVariant: Record "Item Variant";
    begin
        StockkeepingUnit.SetLoadFields("Replenishment System");
        if StockkeepingUnit.Get(LocationCode, ItemNo, VariantCode) then
            ReplenishmentSystemProdOrderAndNotProductionBlocked := StockkeepingUnit."Replenishment System" = StockkeepingUnit."Replenishment System"::"Prod. Order";

        Item.SetLoadFields("Replenishment System", Blocked, "Production Blocked");
        Item.Get(ItemNo);
        if not ReplenishmentSystemProdOrderAndNotProductionBlocked then
            ReplenishmentSystemProdOrderAndNotProductionBlocked := Item."Replenishment System" = Item."Replenishment System"::"Prod. Order";

        if ReplenishmentSystemProdOrderAndNotProductionBlocked then
            ReplenishmentSystemProdOrderAndNotProductionBlocked := (not Item.Blocked) and (Item."Production Blocked" <> Item."Production Blocked"::Output);

        if ReplenishmentSystemProdOrderAndNotProductionBlocked and (VariantCode <> '') then begin
            ItemVariant.SetLoadFields(Blocked, "Production Blocked");
            ItemVariant.Get(ItemNo, VariantCode);
            ReplenishmentSystemProdOrderAndNotProductionBlocked := (not ItemVariant.Blocked) and (ItemVariant."Production Blocked" <> ItemVariant."Production Blocked"::Output);
        end;
    end;

    [IntegrationEvent(false, false)]
    local procedure OnAfterCopy(var ProdOrder: Record "Production Order"; var ErrorOccured: Boolean)
    begin
    end;

    [IntegrationEvent(true, false)]
    local procedure OnAfterCreateProdOrderLine(ProdOrder: Record "Production Order"; VariantCode: Code[10]; var ErrorOccured: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnAfterInitProdOrderLine(var ProdOrderLine: Record "Prod. Order Line"; ProdOrder: Record "Production Order"; SalesLine: Record "Sales Line")
    begin
    end;

    [IntegrationEvent(true, false)]
    local procedure OnAfterProdOrderLineInsert(var ProdOrder: Record "Production Order"; var ProdOrderLine: Record "Prod. Order Line"; var NextProdOrderLineNo: Integer)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnAfterProcessProdOrderLine(var ProdOrderLine: Record "Prod. Order Line"; Direction: Option Forward,Backward; LetDueDateDecrease: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnAfterReserveMultiLevelStructureComp(var ProdOrderLine: Record "Prod. Order Line"; var ProdOrderComponent: Record "Prod. Order Component")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnAfterShouldIncreasePlanningLevel(ProdOrderComp: Record "Prod. Order Component"; StockkeepingUnit: Record "Stockkeeping Unit"; var IncreasePlanningLevel: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnAfterUpdateProdOrderLine(var ProdOrderLine: Record "Prod. Order Line"; Direction: Option Forward,Backward; LetDueDateDecrease: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnUpdateProdOrderLineOnBeforeUpdateCompPlanningLevel(var ProdOrderLine: Record "Prod. Order Line")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeCheckMultiLevelStructure(ProductionOrder: Record "Production Order"; Direction: Option Forward,Backward; var IsHandled: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeCopy(var ProductionOrder: Record "Production Order"; Direction: Option Forward,Backward; VariantCode: Code[10]; LetDueDateDecrease: Boolean; var ErrorOccured: Boolean; var IsHandled: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeCopyFromSalesOrder(SalesHeader: Record "Sales Header"; var SalesLine: Record "Sales Line"; ProductionOrder: Record "Production Order"; var NextProdOrderLineNo: Integer)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeDeleteLinesForProductionOrder(ProductionOrder: Record "Production Order"; var NextProdOrderLineNo: Integer; var IsHandled: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeInsertProdOrderLine(var ProdOrderLine: Record "Prod. Order Line"; var ProdOrderLine3: Record "Prod. Order Line"; var InsertNew: Boolean; var IsHandled: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeInitProdOrderLine(ProductionOrder: Record "Production Order"; SalesLine: Record "Sales Line"; var ItemNo: Code[20]; var VariantCode: Code[10]; var LocationCode: Code[10]; var IsHandled: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeProcessProdOrderLine(var ProdOrderLine: Record "Prod. Order Line"; ProductionOrder: Record "Production Order"; Direction: Option Forward,Backward; LetDueDateDecrease: Boolean; var ErrorOccured: Boolean; var IsHandled: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeProdOrderLineInsert(var ProdOrderLine: Record "Prod. Order Line"; var ProductionOrder: Record "Production Order"; SalesLineIsSet: Boolean; var SalesLine: Record "Sales Line")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeProdOrderLine3Modify(var ProdOrderLine3: Record "Prod. Order Line"; ProdOrderLine: Record "Prod. Order Line")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeReserveMultiLevelStructureComp(var ProdOrderComponent: Record "Prod. Order Component"; var ProdOrderLine: Record "Prod. Order Line"; var IsHandled: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnCopyFromFamilyOnAfterInitProdOrderLine(ProductionOrder: Record "Production Order"; FamilyLine: Record "Family Line"; var ProdOrderLine: Record "Prod. Order Line")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnCopyFromFamilyOnAfterInsertProdOrderLine(var ProdOrderLine: Record "Prod. Order Line")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnCopyFromFamilyOnAfterFamilyLineSetFilters(var FamilyLine: Record "Family Line"; ProductionOrder: Record "Production Order")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnCopyFromFamilyOnBeforeInsertProdOrderLine(var ProdOrderLine: Record "Prod. Order Line"; FamilyLine: Record "Family Line")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnCopyFromSalesOrderOnBeforeSalesPlanLineInsert(var SalesLine: Record "Sales Line"; var SalesPlanningLine: Record "Sales Planning Line")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnCopyFromSalesOrderOnBeforeProdOrderLineModify(var ProdOrderLine: Record "Prod. Order Line"; SalesLine: Record "Sales Line"; SalesPlanningLine: Record "Sales Planning Line"; var NextProdOrderLineNo: Integer)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnCopyFromSalesOrderOnAfterCalcQuantityBase(var ProdOrderLine: Record "Prod. Order Line"; SalesLineIsSet: Boolean; var SalesLine: Record "Sales Line"; var QuantityBase: Decimal)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnCopyFromSalesOrderOnAfterCalcShouldCreateTempSalesPlanningLines(var SalesLine: Record "Sales Line"; var ShouldCreateTempSalesPlanningLines: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnCopyFromSalesOrderOnAfterSalesPlanLinesInsert(SalesHeader: Record "Sales Header"; var SalesLine: Record "Sales Line")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnInitProdOrderLineAfterVariantCode(var ProdOrderLine: Record "Prod. Order Line"; VariantCode: Code[10])
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnInitProdOrderLineAfterScrap(var ProdOrderLine: Record "Prod. Order Line"; var ProductionOrder: Record "Production Order")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnInitProdOrderLineBeforeAssignItemNo(var ProdOrderLine: Record "Prod. Order Line"; ItemNo: Code[20]; VariantCode: Code[10]; LocationCode: Code[10])
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnCheckMakeOrderLineBeforeIf(var ProductionOrder: Record "Production Order"; var ProdOrderLine: Record "Prod. Order Line"; var ProdOrderComponent: Record "Prod. Order Component"; var MakeProdOrder: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnCheckMakeOrderLineBeforeInsert(var ProdOrderLine: Record "Prod. Order Line"; var ProdOrderComponent: Record "Prod. Order Component"; var InsertNew: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnCheckMakeOrderLineOnAfterUpdateProdOrderLine(var ProdOrderLine: Record "Prod. Order Line")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeIsReplSystemProdOrder(SalesLine: Record "Sales Line"; var ReplanSystemProdOrder: Boolean; var IsHandled: Boolean);
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnCreateProdOrderLineOnBeforeCreateProdOrderLineFromItem(var IsHandled: Boolean; var ErrorOccured: Boolean; var ProductionOrder: Record "Production Order"; VariantCode: Code[10])
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnCreateProdOrderLineOnBeforeInitProdOrderLine(var InsertNew: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnProcessProdOrderLinesOnBeforeAdjustStartEndingDate(var ProductionOrder: Record "Production Order")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnCopyFromSalesOrderOnAfterProdOrderLineModify(var ProdOrderLine: Record "Prod. Order Line"; var SalesLine: Record "Sales Line")
    begin
    end;
}