Codeunit 99000774 Calculate Routing Line

App
Base Application
Namespace
Microsoft.Manufacturing.Routing
Versions
17-28

Procedures, 8Events, 83

Versions171819202122232425262728

Source29

Source in 29

src/Layers/W1/BaseApp/Manufacturing/Routing/CalculateRoutingLine.Codeunit.al2605 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.Routing;

using Microsoft.Foundation.Enums;
using Microsoft.Foundation.UOM;
using Microsoft.Inventory.Costing;
using Microsoft.Manufacturing.Capacity;
using Microsoft.Manufacturing.Document;
using Microsoft.Manufacturing.MachineCenter;
using Microsoft.Manufacturing.Setup;
using Microsoft.Manufacturing.WorkCenter;

codeunit 99000774 "Calculate Routing Line"
{
    Permissions = TableData "Production Order" = r,
                  TableData "Prod. Order Line" = r,
                  TableData "Prod. Order Routing Line" = rim,
                  TableData "Prod. Order Capacity Need" = rimd,
                  TableData "Work Center" = r,
                  TableData "Calendar Entry" = r,
                  TableData "Machine Center" = r,
                  TableData "Manufacturing Setup" = r,
                  TableData "Capacity Constrained Resource" = r;

    trigger OnRun()
    begin
    end;

    var
        MfgSetup: Record "Manufacturing Setup";
        Workcenter: Record "Work Center";
        Workcenter2: Record "Work Center";
        MachineCenter: Record "Machine Center";
        ProdOrder: Record "Production Order";
        ProdOrderLine: Record "Prod. Order Line";
        ProdOrderRoutingLine: Record "Prod. Order Routing Line";
        ProdOrderCapNeed: Record "Prod. Order Capacity Need";
        ProdOrderCapNeed2: Record "Prod. Order Capacity Need";
        CalendarEntry: Record "Calendar Entry";
        CalendarMgt: Codeunit "Shop Calendar Management";
        UOMMgt: Codeunit "Unit of Measure Management";
        RoutingTimeType: Enum "Routing Time Type";
        NextCapNeedLineNo: Integer;
        ProdStartingTime: Time;
        ProdEndingTime: Time;
        ProdStartingDate: Date;
        ProdEndingDate: Date;
        MaxLotSize: Decimal;
        TotalLotSize: Decimal;
        ProdOrderQty: Decimal;
        TotalScrap: Decimal;
        LotSize: Decimal;
        RemainNeedQty: Decimal;
        ConCurrCap: Decimal;
        RunStartingDateTime: DateTime;
        RunEndingDateTime: DateTime;
        FirstInBatch: Boolean;
        FirstEntry: Boolean;
        UpdateDates: Boolean;
        WaitTimeOnly: Boolean;
        CurrentWorkCenterNo: Code[20];
        CurrentTimeFactor: Decimal;
        CurrentRounding: Decimal;

#pragma warning disable AA0074
#pragma warning disable AA0470
        Text000: Label 'Error when calculating %1. Calendar is not available %2 %3 for %4 %5.';
#pragma warning restore AA0470
        Text001: Label 'backward';
        Text002: Label 'before';
        Text003: Label 'forward';
        Text004: Label 'after';
        Text005: Label 'The sum of setup, move and wait time exceeds the available time in the period.';
        Text006: Label 'fixed schedule';
        Text007: Label 'Starting time must be before ending time.';
#pragma warning restore AA0074

    local procedure TestForError(DirectionTxt: Text[30]; BefAfterTxt: Text[30]; Date: Date)
    begin
        if RemainNeedQty <> 0 then
            Error(
              Text000,
              DirectionTxt,
              BefAfterTxt,
              Date,
              ProdOrderRoutingLine.Type,
              ProdOrderRoutingLine."No.");
    end;

    local procedure CreateCapNeed(NeedDate: Date; StartingTime: Time; EndingTime: Time; NeedQty: Decimal; TimeType: Enum "Routing Time Type"; Direction: Option Forward,Backward)
    begin
        InitProdOrderCapNeed(ProdOrder, ProdOrderRoutingLine, ProdOrderCapNeed, TimeType, NeedDate, StartingTime, EndingTime, NeedQty);

        ProdOrderCapNeed."Time Type" := TimeType;
        if TimeType = TimeType::"Run Time" then
            ProdOrderCapNeed."Lot Size" := LotSize;

        if TimeType = TimeType::"Run Time" then
            if RemainNeedQty = 0 then begin
                if FirstInBatch then
                    ProdOrderCapNeed."Send-Ahead Type" := ProdOrderCapNeed."Send-Ahead Type"::Both
                else
                    case Direction of
                        Direction::Forward:
                            ProdOrderCapNeed."Send-Ahead Type" := ProdOrderCapNeed."Send-Ahead Type"::Output;
                        Direction::Backward:
                            ProdOrderCapNeed."Send-Ahead Type" := ProdOrderCapNeed."Send-Ahead Type"::Input;
                    end;
            end else
                if FirstInBatch then
                    case Direction of
                        Direction::Forward:
                            ProdOrderCapNeed."Send-Ahead Type" := ProdOrderCapNeed."Send-Ahead Type"::Input;
                        Direction::Backward:
                            ProdOrderCapNeed."Send-Ahead Type" := ProdOrderCapNeed."Send-Ahead Type"::Output;
                    end;

        ProdOrderCapNeed.UpdateDatetime();

        OnBeforeProdOrderCapNeedInsert(ProdOrderCapNeed, ProdOrderRoutingLine, ProdOrder, CalendarEntry);
        ProdOrderCapNeed.Insert();

        NextCapNeedLineNo := NextCapNeedLineNo + 1;

        OnAfterCreateCapNeeded(
            ProdOrderRoutingLine, NeedDate, NeedQty, RemainNeedQty, CalendarEntry, StartingTime, EndingTime, TimeType.AsInteger(),
            NextCapNeedLineNo, ConCurrCap, LotSize, FirstInBatch, Direction);
    end;

    local procedure InitProdOrderCapNeed(ProdOrder: Record "Production Order"; var ProdOrderRoutingLine: Record "Prod. Order Routing Line"; var ProdOrderCapNeed: Record "Prod. Order Capacity Need"; TimeType: Enum "Routing Time Type"; NeedDate: Date; StartingTime: Time; EndingTime: Time; NeedQty: Decimal)
    var
        ActuallyPostedTime: Decimal;
        DistributedCapNeed: Decimal;
        ShouldUpdateAllocatedTimeAndExpectedCapacityNeed: Boolean;
    begin
        OnBeforeInitProdOrderCapNeed(ProdOrder, ProdOrderRoutingLine, ProdOrderCapNeed, TimeType, NeedDate, StartingTime, EndingTime, NeedQty, LotSize);

        ProdOrderCapNeed.Init();
        ProdOrderCapNeed.Status := ProdOrder.Status;
        ProdOrderCapNeed."Prod. Order No." := ProdOrder."No.";
        ProdOrderCapNeed."Routing No." := ProdOrderRoutingLine."Routing No.";
        ProdOrderCapNeed."Routing Reference No." := ProdOrderRoutingLine."Routing Reference No.";
        ProdOrderCapNeed."Line No." := NextCapNeedLineNo;
        ProdOrderCapNeed.Type := ProdOrderRoutingLine.Type;
        ProdOrderCapNeed."No." := ProdOrderRoutingLine."No.";
        ProdOrderCapNeed."Work Center No." := ProdOrderRoutingLine."Work Center No.";
        ProdOrderCapNeed."Operation No." := ProdOrderRoutingLine."Operation No.";
        ProdOrderCapNeed."Work Center Group Code" := ProdOrderRoutingLine."Work Center Group Code";
        ProdOrderCapNeed.Date := NeedDate;
        ProdOrderCapNeed."Starting Time" := StartingTime;
        ProdOrderCapNeed."Ending Time" := EndingTime;
        ProdOrderCapNeed."Needed Time" := NeedQty;
        ProdOrderCapNeed."Needed Time (ms)" := NeedQty * CalendarMgt.TimeFactor(Workcenter."Unit of Measure Code");
        ProdOrderCapNeed."Concurrent Capacities" := ConCurrCap;
        ProdOrderCapNeed.Efficiency := CalendarEntry.Efficiency;
        ProdOrderCapNeed."Requested Only" := false;
        ProdOrderCapNeed.Active := true;
        ShouldUpdateAllocatedTimeAndExpectedCapacityNeed := ProdOrder.Status <> ProdOrder.Status::Simulated;
        OnInitProdOrderCapNeedOnBeforeUpdateAllocatedTimeAndExpectedCapacityNeed(ProdOrderCapNeed, ShouldUpdateAllocatedTimeAndExpectedCapacityNeed);
        if ShouldUpdateAllocatedTimeAndExpectedCapacityNeed then begin
            ActuallyPostedTime := CalcActuallyPostedCapacityTime(ProdOrderRoutingLine, TimeType);
            DistributedCapNeed := CalcDistributedCapacityNeedForOperation(ProdOrderRoutingLine, TimeType);
            ProdOrderCapNeed."Allocated Time" := NeedQty - ActuallyPostedTime + DistributedCapNeed;
            if ProdOrderCapNeed."Allocated Time" < 0 then
                ProdOrderCapNeed."Allocated Time" := 0;
            ProdOrderRoutingLine."Expected Capacity Need" :=
              ProdOrderRoutingLine."Expected Capacity Need" + ProdOrderCapNeed."Needed Time (ms)";
        end;

        OnAfterInitProdOrderCapNeed(ProdOrder, ProdOrderRoutingLine, ProdOrderCapNeed, NeedQty, TimeType, ActuallyPostedTime, DistributedCapNeed);
    end;

    local procedure CreateLoadBack(TimeType: Enum "Routing Time Type"; Write: Boolean)
    var
        OldCalendarEntry: Record "Calendar Entry";
        AvQtyBase: Decimal;
        RelevantEfficiency: Decimal;
        xConCurrCap: Decimal;
        RemainNeedQtyBase: Decimal;
        StartingTime: Time;
        StopLoop: Boolean;
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeCreateLoadBack(RemainNeedQty, CalendarEntry, TimeType, Write, FirstEntry, WaitTimeOnly, CurrentWorkCenterNo, ProdStartingDate, ProdStartingTime, ProdEndingDate, ProdEndingTime, IsHandled, RemainNeedQtyBase, CurrentTimeFactor);
        if IsHandled then
            exit;

        xConCurrCap := 1;
        if (RemainNeedQty = 0) and ((not FirstEntry) or (not Write) or WaitTimeOnly) then
            exit;
        if CalendarEntry.Find('+') then begin
            if (TimeType = TimeType::"Wait Time") and (CalendarEntry.Date < ProdEndingDate) then begin
                CalendarEntry.Date := ProdEndingDate;
                CreateCalendarEntry(CalendarEntry);
            end;
            GetCurrentWorkCenterTimeFactorAndRounding(CalendarEntry."Work Center No.");
            RemainNeedQtyBase := Round(RemainNeedQty * CurrentTimeFactor, CurrentRounding);
            repeat
                OldCalendarEntry := CalendarEntry;
                ConCurrCap := ProdOrderRoutingLine."Concurrent Capacities";
                if (ConCurrCap = 0) or (CalendarEntry.Capacity < ConCurrCap) then
                    ConCurrCap := CalendarEntry.Capacity;
                if TimeType <> TimeType::"Run Time" then
                    RemainNeedQtyBase := Round(RemainNeedQtyBase * ConCurrCap / xConCurrCap, CurrentRounding);
                xConCurrCap := ConCurrCap;
                AvQtyBase := CalcLoadBackAvailQtyBase(TimeType);

                if AvQtyBase > RemainNeedQtyBase then
                    AvQtyBase := RemainNeedQtyBase;
                if TimeType in [TimeType::"Setup Time", TimeType::"Run Time"] then
                    RelevantEfficiency := CalendarEntry.Efficiency
                else
                    RelevantEfficiency := 100;

                OnCreateLoadBackOnAfterCalcRelevantEfficiency(ProdOrderRoutingLine, TimeType, RelevantEfficiency);

                StartingTime :=
                  CalendarMgt.CalcTimeSubtract(
                    CalendarEntry."Ending Time",
                    Round(AvQtyBase * 100 / RelevantEfficiency / ConCurrCap, 1, '>'));
                RemainNeedQtyBase := RemainNeedQtyBase - AvQtyBase;
                OnCreateLoadBackOnBeforeCheckWrite(ProdOrderRoutingLine, TimeType, RelevantEfficiency, RemainNeedQtyBase, RemainNeedQty, CurrentRounding, Write, StartingTime, AvQtyBase, CalendarEntry);
                if Write then begin
                    RemainNeedQty := Round(RemainNeedQtyBase / CurrentTimeFactor, CurrentRounding);
                    CreateCapNeed(
                      CalendarEntry.Date, StartingTime, CalendarEntry."Ending Time",
                      Round(AvQtyBase / CurrentTimeFactor, CurrentRounding), TimeType, 1);
                    FirstInBatch := false;
                    FirstEntry := false;
                end;
                if UpdateDates and
                   ((CalendarEntry."Capacity (Effective)" <> 0) or (TimeType = TimeType::"Wait Time"))
                then begin
                    ProdOrderRoutingLine."Ending Time" := CalendarEntry."Ending Time";
                    ProdOrderRoutingLine."Ending Date" := CalendarEntry.Date;
                    UpdateDates := false;
                end;
                ProdEndingTime := StartingTime;
                ProdEndingDate := CalendarEntry.Date;
                ProdOrderRoutingLine."Starting Time" := StartingTime;
                ProdOrderRoutingLine."Starting Date" := CalendarEntry.Date;

                if (RemainNeedQtyBase = 0) and ((not FirstEntry) or (not Write)) then
                    StopLoop := true
                else
                    if TimeType = TimeType::"Wait Time" then begin
                        StopLoop := false;
                        ReturnNextCalendarEntry(CalendarEntry, OldCalendarEntry, 0);
                    end else begin
                        CalendarEntry := OldCalendarEntry;
                        StopLoop := CalendarEntry.Next(-1) = 0;
                    end;
                OnCreateLoadBackOnBeforeEndStopLoop(ProdOrderRoutingLine, TimeType, StopLoop);
            until StopLoop;
            RemainNeedQty := Round(RemainNeedQtyBase / CurrentTimeFactor, CurrentRounding);
        end;
    end;

    local procedure CalcLoadBackAvailQtyBase(TimeType: Enum "Routing Time Type") AvQtyBase: Decimal
    var
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeCalcLoadBackAvailQtyBase(ProdOrderRoutingLine, TimeType, CurrentTimeFactor, CurrentRounding, AvQtyBase, IsHandled);
        if IsHandled then
            exit(AvQtyBase);

        AvQtyBase :=
            CalcAvailQtyBase(
                CalendarEntry, ProdEndingDate, ProdEndingTime, TimeType, ConCurrCap, false, CurrentTimeFactor, CurrentRounding);
    end;

    local procedure CreateLoadForward(TimeType: Enum "Routing Time Type"; Write: Boolean; LoadFactor: Decimal)
    var
        OldCalendarEntry: Record "Calendar Entry";
        AvQtyBase: Decimal;
        RelevantEfficiency: Decimal;
        xConCurrCap: Decimal;
        RemainNeedQtyBase: Decimal;
        EndingTime: Time;
        StopLoop: Boolean;
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeCreateLoadForward(RemainNeedQty, CalendarEntry, TimeType, Write, LoadFactor, FirstEntry, WaitTimeOnly, CurrentWorkCenterNo, ProdStartingDate, ProdStartingTime, ProdEndingDate, ProdEndingTime, IsHandled, RemainNeedQtyBase, CurrentTimeFactor);
        if IsHandled then
            exit;

        xConCurrCap := 1;
        if (RemainNeedQty = 0) and ((not FirstEntry) or (not Write) or WaitTimeOnly) then
            exit;
        if CalendarEntry.Find('-') then begin
            if (TimeType = TimeType::"Wait Time") and (CalendarEntry.Date > ProdStartingDate) then begin
                CalendarEntry.Date := ProdStartingDate;
                CreateCalendarEntry(CalendarEntry);
            end;
            if CalendarEntry."Capacity (Effective)" = 0 then begin
                CalendarEntry."Starting Time" := ProdStartingTime;
                CalendarEntry.Date := ProdStartingDate;
            end;
            if CalendarEntry."Work Center No." = Workcenter."No." then
                GetCurrentWorkCenterTimeFactorAndRounding(Workcenter."No.")
            else
                GetCurrentWorkCenterTimeFactorAndRounding(CalendarEntry."Work Center No.");
            RemainNeedQtyBase := Round(RemainNeedQty * CurrentTimeFactor, CurrentRounding);
            repeat
                OldCalendarEntry := CalendarEntry;
                ConCurrCap := ProdOrderRoutingLine."Concurrent Capacities";
                if (ConCurrCap = 0) or (CalendarEntry.Capacity < ConCurrCap) then
                    ConCurrCap := CalendarEntry.Capacity;
                if TimeType <> TimeType::"Run Time" then
                    RemainNeedQtyBase := Round(RemainNeedQtyBase * ConCurrCap / xConCurrCap, CurrentRounding);
                xConCurrCap := ConCurrCap;
                AvQtyBase := CalcLoadForwardAvailQtyBase(TimeType);

                if AvQtyBase * LoadFactor > RemainNeedQtyBase then
                    AvQtyBase := Round(RemainNeedQtyBase / LoadFactor, CurrentRounding);

                if TimeType in [TimeType::"Setup Time", TimeType::"Run Time"] then
                    RelevantEfficiency := CalendarEntry.Efficiency
                else
                    RelevantEfficiency := 100;

                IsHandled := false;
                OnCreateLoadForwardOnBeforeCalcEndingTime(EndingTime, CalendarEntry, AvQtyBase, RelevantEfficiency, ConCurrCap, IsHandled, ProdOrderRoutingLine, TimeType);
                if not IsHandled then
                    EndingTime := CalendarEntry."Starting Time" + Round(AvQtyBase * 100 / RelevantEfficiency / ConCurrCap, 1, '>');

                if AvQtyBase * LoadFactor >= 0 then
                    RemainNeedQtyBase := RemainNeedQtyBase - AvQtyBase * LoadFactor;
                OnCreateLoadForwardOnBeforeCheckWrite(ProdOrderRoutingLine, TimeType, RelevantEfficiency, RemainNeedQtyBase, RemainNeedQty, CurrentRounding, Write);
                if Write then begin
                    RemainNeedQty := Round(RemainNeedQtyBase / CurrentTimeFactor, CurrentRounding);
                    CreateCapNeed(
                      CalendarEntry.Date, CalendarEntry."Starting Time", EndingTime,
                      Round(AvQtyBase * LoadFactor / CurrentTimeFactor, CurrentRounding), TimeType, 0);
                    FirstInBatch := false;
                    FirstEntry := false;
                end;
                if UpdateDates and
                   ((CalendarEntry."Capacity (Effective)" <> 0) or (TimeType = TimeType::"Wait Time"))
                then begin
                    ProdOrderRoutingLine."Starting Time" := CalendarEntry."Starting Time";
                    ProdOrderRoutingLine."Starting Date" := CalendarEntry.Date;
                    UpdateDates := false;
                end;
                if (EndingTime = 000000T) and (AvQtyBase <> 0) then
                    // Ending Time reached 24:00:00 so we need to move date as well
                    CalendarEntry.Date := CalendarEntry.Date + 1;
                ProdStartingTime := EndingTime;
                ProdStartingDate := CalendarEntry.Date;
                ProdOrderRoutingLine."Ending Time" := EndingTime;
                ProdOrderRoutingLine."Ending Date" := CalendarEntry.Date;

                if ProdOrderRoutingLine."Schedule Manually" then begin
                    if TimeType = TimeType::"Setup Time" then
                        RunStartingDateTime := CreateDateTime(ProdStartingDate, ProdStartingTime);
                    if (RemainNeedQtyBase < 0) or (Round(RemainNeedQtyBase, CurrentRounding) = 0) then
                        RemainNeedQtyBase := 0;
                end;
                OnCreateLoadForwardOnAfterScheduleProdOrderRoutingLineManually(ProdOrderRoutingLine, TimeType, RunStartingDateTime, ProdStartingDate, ProdStartingTime, RemainNeedQtyBase);

                if (RemainNeedQtyBase = 0) and ((not FirstEntry) or (not Write)) and (AvQtyBase * LoadFactor >= 0) then
                    StopLoop := true
                else
                    if TimeType = TimeType::"Wait Time" then begin
                        StopLoop := false;
                        ReturnNextCalendarEntry(CalendarEntry, OldCalendarEntry, 1);
                    end else begin
                        CalendarEntry := OldCalendarEntry;
                        StopLoop := CalendarEntry.Next() = 0;
                    end;
                OnCreateLoadForwardOnBeforeEndStopLoop(ProdOrderRoutingLine, TimeType, StopLoop, FirstEntry, WaitTimeOnly, ProdStartingDate, ProdStartingTime, ProdEndingDate, ProdEndingTime, RemainNeedQty, RemainNeedQtyBase, Write, LoadFactor, CurrentWorkCenterNo, CurrentTimeFactor, CurrentRounding, CalendarEntry);
            until StopLoop;
            RemainNeedQty := Round(RemainNeedQtyBase / CurrentTimeFactor, CurrentRounding);
        end;
    end;

    local procedure CalcLoadForwardAvailQtyBase(TimeType: Enum "Routing Time Type") AvQtyBase: Decimal
    var
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeCalcLoadForwardAvailQtyBase(ProdOrderRoutingLine, TimeType, CurrentTimeFactor, CurrentRounding, AvQtyBase, IsHandled);
        if IsHandled then
            exit(AvQtyBase);

        AvQtyBase :=
            CalcAvailQtyBase(
                CalendarEntry, ProdStartingDate, ProdStartingTime, TimeType, ConCurrCap, true, CurrentTimeFactor, CurrentRounding);
    end;

    local procedure AvailableCapacity(CapType: Enum "Capacity Type"; CapNo: Code[20]; StartingDateTime: DateTime; EndingDateTime: DateTime) AvQty: Decimal
    var
        CalendarEntry2: Record "Calendar Entry";
        ConCurrCapacity: Decimal;
        Overlap: Decimal;
        TotalDuration: Decimal;
    begin
        CalendarEntry2.SetCapacityFilters(CapType, CapNo);
        CalendarEntry2.SetFilter("Starting Date-Time", '<=%1', EndingDateTime);
        CalendarEntry2.SetFilter("Ending Date-Time", '>=%1', StartingDateTime);

        if CalendarEntry2.Find('-') then
            repeat
                ConCurrCapacity := ProdOrderRoutingLine."Concurrent Capacities";
                if (ConCurrCapacity = 0) or (CalendarEntry2.Capacity < ConCurrCapacity) then
                    ConCurrCapacity := CalendarEntry2.Capacity;

                Overlap := 0;
                if StartingDateTime > CalendarEntry2."Starting Date-Time" then
                    Overlap := CalcDuration(CalendarEntry2."Starting Date-Time", StartingDateTime);
                if EndingDateTime < CalendarEntry2."Ending Date-Time" then
                    Overlap := Overlap + CalcDuration(EndingDateTime, CalendarEntry2."Ending Date-Time");

                TotalDuration := CalcDuration(CalendarEntry2."Starting Date-Time", CalendarEntry2."Ending Date-Time");

                AvQty := AvQty +
                  Round(
                    ((TotalDuration - Overlap) / TotalDuration) *
                    CalendarEntry2."Capacity (Effective)" / CalendarEntry2.Capacity * ConCurrCapacity,
                    Workcenter."Calendar Rounding Precision");
            until CalendarEntry2.Next() = 0;
        exit(AvQty);
    end;

    local procedure LoadCapBack(CapType: Enum "Capacity Type"; CapNo: Code[20]; TimeType: Enum "Routing Time Type"; Write: Boolean)
    begin
        OnBeforeLoadCapBack(ProdOrderRoutingLine, TimeType, RemainNeedQty, ProdEndingDate, ProdEndingTime);

        ProdOrderRoutingLine."Starting Date" := ProdEndingDate;
        ProdOrderRoutingLine."Starting Time" := ProdEndingTime;

        CalendarEntry.SetCapacityFilters(CapType, CapNo);
        CalendarEntry.SetRange("Ending Date-Time", 0DT, CreateDateTime(ProdEndingDate + 1, ProdEndingTime));
        CalendarEntry.SetRange("Starting Date-Time", 0DT, CreateDateTime(ProdEndingDate, ProdEndingTime));
        OnLoadCapBackOnAfterSetCalendarEntryFilters(CalendarEntry);

        CreateLoadBack(TimeType, Write);

        OnLoadCapBackOnAfterCreateLoadBack(CapType, CapNo, TimeType, Write);

        if RemainNeedQty = 0 then
            exit;

        TestForError(Text001, Text002, ProdOrderRoutingLine."Starting Date");
    end;

    procedure LoadCapForward(CapType: Enum "Capacity Type"; CapNo: Code[20]; TimeType: Enum "Routing Time Type"; Write: Boolean)
    var
        TotalAvailCapacity: Decimal;
        LoadFactor: Decimal;
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeLoadCapForward(ProdOrderRoutingLine, CapType, CapNo, TimeType, ProdStartingDate, ProdStartingTime, IsHandled, RemainNeedQty);
        if IsHandled then
            exit;

        ProdOrderRoutingLine."Ending Date" := ProdStartingDate;
        ProdOrderRoutingLine."Ending Time" := ProdStartingTime;

        CalendarEntry.SetCapacityFilters(CapType, CapNo);
        CalendarEntry.SetFilter("Starting Date-Time", '>=%1', CreateDateTime(ProdStartingDate - 1, ProdStartingTime));
        if TimeType = TimeType::"Wait Time" then
            CalendarEntry.SetFilter("Ending Date-Time", '>=%1', CreateDateTime(ProdStartingDate, 000000T))
        else
            CalendarEntry.SetFilter("Ending Date-Time", '>=%1', CreateDateTime(ProdStartingDate, ProdStartingTime));

        if ProdOrderRoutingLine."Schedule Manually" and (TimeType = TimeType::"Run Time") then begin
            OnLoadCapForwardOnScheduleManuallyOnBeforeCheckDateTimes(ProdOrderRoutingLine, CapType, CapNo, TimeType, ProdStartingDate, ProdStartingTime, RemainNeedQty, RunStartingDateTime, RunEndingDateTime);
            if ProdOrderRoutingLine."Run Time" <> 0 then
                if (RunEndingDateTime < RunStartingDateTime) or
                    ((RunEndingDateTime = RunStartingDateTime) and (ProdOrderRoutingLine."Input Quantity" <> 0))
                then
                    Error(Text005);
            TotalAvailCapacity :=
              AvailableCapacity(CapType, CapNo, RunStartingDateTime, RunEndingDateTime);
            if TotalAvailCapacity = 0 then begin
                TestForError(Text006, Text002, DT2Date(RunEndingDateTime));
                LoadFactor := 0;
            end else
                LoadFactor := Round(RemainNeedQty / TotalAvailCapacity, Workcenter."Calendar Rounding Precision", '>');
        end else
            LoadFactor := 1;

        OnLoadCapForwardOnBeforeCreateLoadForward(RemainNeedQty, CapType, CapNo, TimeType, Write, LoadFactor);
        CreateLoadForward(TimeType, Write, LoadFactor);
        OnLoadCapForwardOnAfterCreateLoadForward(RemainNeedQty, CapType, CapNo, TimeType, Write, LoadFactor);

        if RemainNeedQty = 0 then
            exit;

        TestForError(Text003, Text004, ProdOrderRoutingLine."Ending Date");
    end;

    local procedure CalcMove()
    var
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeCalcMove(ProdOrderRoutingLine, WorkCenter, ProdEndingDate, ProdEndingTime, UpdateDates, IsHandled);
        if IsHandled then
            exit;

        RemainNeedQty :=
          Round(
            ProdOrderRoutingLine."Move Time" *
            CalendarMgt.TimeFactor(ProdOrderRoutingLine."Move Time Unit of Meas. Code") /
            CalendarMgt.TimeFactor(Workcenter."Unit of Measure Code"),
            Workcenter."Calendar Rounding Precision");

        LoadCapBack(ProdOrderRoutingLine.Type, ProdOrderRoutingLine."No.", RoutingTimeType::"Move Time", false);
    end;

    local procedure CalcWaitBack()
    var
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeCalcWaitBack(ProdOrderRoutingLine, WorkCenter, ProdEndingDate, ProdEndingTime, UpdateDates, IsHandled);
        if IsHandled then
            exit;

        RemainNeedQty :=
          Round(
            ProdOrderRoutingLine."Wait Time" *
            CalendarMgt.TimeFactor(ProdOrderRoutingLine."Wait Time Unit of Meas. Code") /
            CalendarMgt.TimeFactor(WorkCenter."Unit of Measure Code"),
            Workcenter."Calendar Rounding Precision");

        LoadCapBack(ProdOrderRoutingLine.Type, ProdOrderRoutingLine."No.", RoutingTimeType::"Wait Time", false);
    end;

    local procedure GetSendAheadStartingTime(ProdOrderRoutingLineNext: Record "Prod. Order Routing Line"; var SendAheadLotSize: Decimal): Boolean
    var
        xProdOrderRoutingLine: Record "Prod. Order Routing Line";
        RunTime: Decimal;
        WaitTime: Decimal;
        MoveTime: Decimal;
        SetupTime: Decimal;
        ResidualLotSize: Decimal;
        ResidualProdStartDateTime: DateTime;
    begin
        ProdStartingDate := ProdOrderRoutingLineNext."Starting Date";
        ProdStartingTime := ProdOrderRoutingLineNext."Starting Time";
        SendAheadLotSize := MaxLotSize;
        if TotalLotSize = MaxLotSize then
            exit(true);

        if (ProdOrderRoutingLine."Send-Ahead Quantity" = 0) or
           (ProdOrderRoutingLine."Send-Ahead Quantity" >= MaxLotSize)
        then begin
            TotalLotSize := SendAheadLotSize;
            exit(false);
        end;

        SendAheadLotSize := ProdOrderRoutingLine."Send-Ahead Quantity";
        if MaxLotSize < (TotalLotSize + SendAheadLotSize) then begin
            SendAheadLotSize := MaxLotSize - TotalLotSize;
            TotalLotSize := MaxLotSize;
        end else begin
            if TotalLotSize = 0 then begin
                ResidualLotSize := MaxLotSize mod SendAheadLotSize;
                if ResidualLotSize = 0 then
                    ResidualLotSize := SendAheadLotSize;
            end;
            TotalLotSize := TotalLotSize + SendAheadLotSize;
        end;

        ProdOrderCapNeed2.Reset();
        ProdOrderCapNeed2.SetCurrentKey(Status, "Prod. Order No.", "Routing Reference No.", "Operation No.", Date, "Starting Time");
        ProdOrderCapNeed2.SetRange(Status, ProdOrderRoutingLine.Status);
        ProdOrderCapNeed2.SetRange("Prod. Order No.", ProdOrderRoutingLine."Prod. Order No.");
        ProdOrderCapNeed2.SetRange("Requested Only", false);
        ProdOrderCapNeed2.SetRange("Routing No.", ProdOrderRoutingLine."Routing No.");
        ProdOrderCapNeed2.SetRange("Routing Reference No.", ProdOrderRoutingLine."Routing Reference No.");
        ProdOrderCapNeed2.SetRange("Operation No.", ProdOrderRoutingLine."Operation No.");
        if ProdOrderCapNeed2.FindFirst() then begin
            if TotalLotSize <> MaxLotSize then begin
                SendAheadLotSize := MaxLotSize - (TotalLotSize - SendAheadLotSize);
                TotalLotSize := MaxLotSize;
            end;
            exit(false);
        end;
        // calculate Starting Date/Time for the last lot of the next line by going back from Ending Date/Time of the line
        Workcenter2.Get(ProdOrderRoutingLineNext."Work Center No.");
        SetupTime :=
          Round(
            ProdOrderRoutingLineNext."Setup Time" *
            CalendarMgt.TimeFactor(ProdOrderRoutingLineNext."Setup Time Unit of Meas. Code") /
            CalendarMgt.TimeFactor(Workcenter2."Unit of Measure Code"),
            Workcenter2."Calendar Rounding Precision");
        RunTime :=
          Round(
            (ResidualLotSize * ProdOrderRoutingLineNext.RunTimePer()) *
            CalendarMgt.TimeFactor(ProdOrderRoutingLineNext."Run Time Unit of Meas. Code") /
            CalendarMgt.TimeFactor(Workcenter2."Unit of Measure Code"),
            Workcenter2."Calendar Rounding Precision");
        WaitTime :=
          Round(
            ProdOrderRoutingLineNext."Wait Time" *
            CalendarMgt.TimeFactor(ProdOrderRoutingLineNext."Wait Time Unit of Meas. Code") /
            CalendarMgt.TimeFactor(Workcenter2."Unit of Measure Code"),
            Workcenter2."Calendar Rounding Precision");
        MoveTime :=
          Round(
            ProdOrderRoutingLineNext."Move Time" *
            CalendarMgt.TimeFactor(ProdOrderRoutingLineNext."Move Time Unit of Meas. Code") /
            CalendarMgt.TimeFactor(Workcenter2."Unit of Measure Code"),
            Workcenter2."Calendar Rounding Precision");

        xProdOrderRoutingLine := ProdOrderRoutingLine;
        ProdOrderRoutingLine := ProdOrderRoutingLineNext;
        ProdEndingDate := ProdOrderRoutingLine."Ending Date";
        ProdEndingTime := ProdOrderRoutingLine."Ending Time";
        RemainNeedQty := SetupTime;
        LoadCapBack(ProdOrderRoutingLine.Type, ProdOrderRoutingLine."No.", RoutingTimeType::"Setup Time", false);
        RemainNeedQty := MoveTime;
        LoadCapBack(ProdOrderRoutingLine.Type, ProdOrderRoutingLine."No.", RoutingTimeType::"Move Time", false);
        RemainNeedQty := WaitTime;
        LoadCapBack(ProdOrderRoutingLine.Type, ProdOrderRoutingLine."No.", RoutingTimeType::"Wait Time", false);
        RemainNeedQty := RunTime;
        LoadCapBack(ProdOrderRoutingLine.Type, ProdOrderRoutingLine."No.", RoutingTimeType::"Run Time", false);
        ProdOrderRoutingLine := xProdOrderRoutingLine;

        ProdOrderRoutingLine."Starting Date" := ProdEndingDate;
        ProdOrderRoutingLine."Starting Time" := ProdEndingTime;
        ResidualProdStartDateTime := CreateDateTime(ProdEndingDate, ProdEndingTime);
        // calculate Ending Date/Time of current line by going forward from Starting Date/Time of the next line
        Workcenter.Get(ProdOrderRoutingLine."Work Center No.");
        RunTime :=
          Round(
            (MaxLotSize - SendAheadLotSize) * ProdOrderRoutingLine.RunTimePer() *
            CalendarMgt.TimeFactor(ProdOrderRoutingLine."Run Time Unit of Meas. Code") /
            CalendarMgt.TimeFactor(Workcenter."Unit of Measure Code"),
            Workcenter."Calendar Rounding Precision");
        WaitTime :=
          Round(
            ProdOrderRoutingLine."Wait Time" *
            CalendarMgt.TimeFactor(ProdOrderRoutingLine."Wait Time Unit of Meas. Code") /
            CalendarMgt.TimeFactor(Workcenter."Unit of Measure Code"),
            Workcenter."Calendar Rounding Precision");
        MoveTime :=
          Round(
            ProdOrderRoutingLine."Move Time" *
            CalendarMgt.TimeFactor(ProdOrderRoutingLine."Move Time Unit of Meas. Code") /
            CalendarMgt.TimeFactor(Workcenter."Unit of Measure Code"),
            Workcenter."Calendar Rounding Precision");

        RemainNeedQty := RunTime;
        LoadCapForward(ProdOrderRoutingLine.Type, ProdOrderRoutingLine."No.", RoutingTimeType::"Run Time", false);
        RemainNeedQty := WaitTime;
        LoadCapForward(ProdOrderRoutingLine.Type, ProdOrderRoutingLine."No.", RoutingTimeType::"Wait Time", false);
        RemainNeedQty := MoveTime;
        LoadCapForward(ProdOrderRoutingLine.Type, ProdOrderRoutingLine."No.", RoutingTimeType::"Move Time", false);
        // last lot must be finished by current Work Center, otherwise we recalculate Ending Date/Time of current line
        ProdOrderRoutingLine.UpdateDatetime();
        if ProdOrderRoutingLine."Ending Date-Time" > ResidualProdStartDateTime then begin
            ProdOrderRoutingLine."Ending Date" := DT2Date(ResidualProdStartDateTime);
            ProdOrderRoutingLine."Ending Time" := DT2Time(ResidualProdStartDateTime);
            FilterCalendarEntryBeforeOrOnDateTime(ProdOrderRoutingLine.Type, ProdOrderRoutingLine."No.", ProdOrderRoutingLine."Ending Date", ProdOrderRoutingLine."Ending Time");
            if CalendarEntry.FindLast() then
                if ProdOrderRoutingLine."Ending Time" > CalendarEntry."Ending Time" then
                    if (WaitTime = 0) or (MoveTime <> 0) then
                        // Wait Time can end outside of working hours, but only if Move Time = 0
                        ProdOrderRoutingLine."Ending Time" := CalendarEntry."Ending Time";
            ProdStartingDate := ProdOrderRoutingLine."Ending Date";
            ProdStartingTime := ProdOrderRoutingLine."Ending Time";
        end;
        exit(false);
    end;

    procedure CalcRoutingLineBack(CalculateEndDate: Boolean)
    var
        ProdOrderRoutingLine2: Record "Prod. Order Routing Line";
        ProdOrderRoutingLine3: Record "Prod. Order Routing Line";
        ConstrainedCapacity: Record "Capacity Constrained Resource";
        ParentWorkCenter: Record "Capacity Constrained Resource";
        TempProdOrderRoutingLine, TempProdOrderRoutingLine2 : Record "Prod. Order Routing Line" temporary;
        WorkCenterQueueTime: Record "Work Center";
        RoutingHeader: Record "Routing Header";
        RoutingVersion: Record "Routing Version";
        Qty, SendAheadLotSize : Decimal;
        ParentIsConstrained: Boolean;
        ResourceIsConstrained: Boolean;
        ShouldCalcNextOperation: Boolean;
        IsHandled, IsParallelRouting : Boolean;
    begin
        IsHandled := false;
        OnBeforeCalcRoutingLineBack(ProdOrderRoutingLine, CalculateEndDate, IsHandled);
        if IsHandled then
            exit;

        CalendarEntry.SetRange(Date, 0D, ProdOrderRoutingLine."Ending Date");

        ProdEndingTime := ProdOrderRoutingLine."Ending Time";
        ProdEndingDate := ProdOrderRoutingLine."Ending Date";
        ProdStartingTime := ProdOrderRoutingLine."Ending Time";
        ProdStartingDate := ProdOrderRoutingLine."Ending Date";

        FirstEntry := true;

        IsParallelRouting := false;
        RoutingVersion.SetLoadFields(Type);
        if RoutingVersion.Get(ProdOrderLine."Routing No.", ProdOrderLine."Routing Version Code") then
            IsParallelRouting := RoutingVersion.Type = RoutingVersion.Type::Parallel
        else begin
            RoutingHeader.SetLoadFields(Type);
            if RoutingHeader.Get(ProdOrderLine."Routing No.") then
                IsParallelRouting := RoutingHeader.Type = RoutingHeader.Type::Parallel;
        end;

        ShouldCalcNextOperation := (ProdOrderRoutingLine."Next Operation No." <> '') and CalculateEndDate;
        OnCalcRoutingLineBackOnAfterCalcShouldCalcNextOperation(ProdOrderRoutingLine, ShouldCalcNextOperation);
        if ShouldCalcNextOperation then begin
            Clear(ProdOrderRoutingLine3);

            TempProdOrderRoutingLine.Reset();
            TempProdOrderRoutingLine.DeleteAll();

            SetRoutingLineFilters(ProdOrderRoutingLine, ProdOrderRoutingLine2);
            ProdOrderRoutingLine2.SetFilter("Operation No.", ProdOrderRoutingLine."Next Operation No.");
            if ProdOrderRoutingLine2.Find('-') then
                repeat
                    TotalLotSize := 0;
                    GetSendAheadStartingTime(ProdOrderRoutingLine2, SendAheadLotSize);
                    TempProdOrderRoutingLine.Copy(ProdOrderRoutingLine2);
                    TempProdOrderRoutingLine.Insert(false, true);
                    SetMinDateTime(ProdEndingDate, ProdEndingTime, ProdStartingDate, ProdStartingTime);

                    if IsParallelRouting then
                        if TempProdOrderRoutingLine2.IsEmpty() then begin
                            WorkCenterQueueTime.Get(ProdOrderRoutingLine2."Work Center No.");
                            Qty := Round(
                                    WorkCenterQueueTime."Queue Time" *
                                    CalendarMgt.TimeFactor(WorkCenterQueueTime."Queue Time Unit of Meas. Code") /
                                    CalendarMgt.TimeFactor(WorkCenterQueueTime."Unit of Measure Code"),
                                    WorkCenterQueueTime."Calendar Rounding Precision");

                            TempProdOrderRoutingLine2.TransferFields(ProdOrderRoutingLine2);
                            TempProdOrderRoutingLine2."Input Quantity" := Qty;
                            TempProdOrderRoutingLine2.Insert(false, true);
                        end else begin
                            WorkCenterQueueTime.Get(ProdOrderRoutingLine2."Work Center No.");
                            Qty := Round(
                                    WorkCenterQueueTime."Queue Time" *
                                    CalendarMgt.TimeFactor(WorkCenterQueueTime."Queue Time Unit of Meas. Code") /
                                    CalendarMgt.TimeFactor(WorkCenterQueueTime."Unit of Measure Code"),
                                    WorkCenterQueueTime."Calendar Rounding Precision");
                            if TempProdOrderRoutingLine2."Work Center No." <> ProdOrderRoutingLine2."Work Center No." then begin
                                if TempProdOrderRoutingLine2."Input Quantity" < Qty then begin
                                    TempProdOrderRoutingLine2.DeleteAll();
                                    TempProdOrderRoutingLine2.TransferFields(ProdOrderRoutingLine2);
                                    TempProdOrderRoutingLine2."Input Quantity" := Qty;
                                    TempProdOrderRoutingLine2.Insert(false, true);
                                end;
                            end else
                                if ProdOrderRoutingLine2."Starting Date-Time" < TempProdOrderRoutingLine2."Starting Date-Time" then begin
                                    TempProdOrderRoutingLine2.DeleteAll();
                                    TempProdOrderRoutingLine2.TransferFields(ProdOrderRoutingLine2);
                                    TempProdOrderRoutingLine2."Input Quantity" := Qty;
                                    TempProdOrderRoutingLine2.Insert(false, true);
                                end;
                        end;
                    ProdOrderRoutingLine3 := ProdOrderRoutingLine2;
                until ProdOrderRoutingLine2.Next() = 0;

            if IsParallelRouting then
                if ProdOrderRoutingLine2.Get(
                    TempProdOrderRoutingLine2.Status,
                    TempProdOrderRoutingLine2."Prod. Order No.",
                    TempProdOrderRoutingLine2."Routing Reference No.",
                    TempProdOrderRoutingLine2."Routing No.",
                    TempProdOrderRoutingLine2."Operation No.")
                then begin
                    GetSendAheadStartingTime(ProdOrderRoutingLine2, SendAheadLotSize);
                    TempProdOrderRoutingLine.GetBySystemId(ProdOrderRoutingLine2.SystemId);
                    TempProdOrderRoutingLine.Copy(ProdOrderRoutingLine2);
                    TempProdOrderRoutingLine.Modify();
                    WorkCenterQueueTime.Get(ProdOrderRoutingLine2."Work Center No.");
                    if WorkCenterQueueTime."Queue Time" <> 0 then begin
                        ProdEndingDate := ProdStartingDate;
                        ProdEndingTime := ProdStartingTime;
                    end;
                    ProdOrderRoutingLine3 := ProdOrderRoutingLine2;
                end;

            OnCalcRoutingLineBackOnBeforeGetQueueTime(ProdOrderRoutingLine, ProdOrderRoutingLine2, ProdOrderRoutingLine3);

            if ProdOrderRoutingLine3."Prod. Order No." <> '' then begin
                Workcenter2.Get(ProdOrderRoutingLine3."Work Center No.");
                ProdOrderRoutingLine3."Critical Path" := true;
                ProdOrderRoutingLine3.UpdateDatetime();
                ProdOrderRoutingLine3.Modify();
                if ProdOrderRoutingLine3.Type = ProdOrderRoutingLine3.Type::"Machine Center" then begin
                    MachineCenter.Get(ProdOrderRoutingLine3."No.");
                    Workcenter2."Queue Time" := MachineCenter."Queue Time";
                    Workcenter2."Queue Time Unit of Meas. Code" :=
                      MachineCenter."Queue Time Unit of Meas. Code";
                end;
                UpdateDates := false;
                RemainNeedQty :=
                  Round(
                    Workcenter2."Queue Time" *
                    CalendarMgt.TimeFactor(Workcenter2."Queue Time Unit of Meas. Code") /
                    CalendarMgt.TimeFactor(Workcenter2."Unit of Measure Code"),
                    Workcenter2."Calendar Rounding Precision");

                LoadCapBack(ProdOrderRoutingLine2.Type, ProdOrderRoutingLine2."No.", RoutingTimeType::"Queue Time", false);
            end else
                ProdOrderRoutingLine3 := ProdOrderRoutingLine2;
        end else
            SetLotSizesToMax(SendAheadLotSize, TotalLotSize);

        // In case of Parallel Routing and the last operation is finished
        if ProdEndingDate = CalendarMgt.GetMaxDate() then begin
            ProdOrderRoutingLine."Ending Date" := ProdOrderLine."Ending Date";
            ProdOrderRoutingLine."Ending Time" := ProdOrderLine."Ending Time";

            ProdEndingTime := ProdOrderRoutingLine."Ending Time";
            ProdEndingDate := ProdOrderRoutingLine."Ending Date";
            ProdStartingTime := ProdOrderRoutingLine."Ending Time";
            ProdStartingDate := ProdOrderRoutingLine."Ending Date";

            TotalLotSize := MaxLotSize;
            SendAheadLotSize := MaxLotSize;
        end;

        UpdateDates := true;

        CalcMove();

        CalcWaitBack();

        if ProdOrderRoutingLine."Schedule Manually" then // Move and wait time has been calculated
            exit;

        repeat
            LotSize := SendAheadLotSize;
            RemainNeedQty := LotSize * ProdOrderRoutingLine.RunTimePer();
            OnCalculateRoutingLineBackOnAfterCalcRemainNeedQtyForLotSize(ProdOrderRoutingLine, RemainNeedQty);
            RemainNeedQty :=
              Round(
                RemainNeedQty *
                CalendarMgt.TimeFactor(ProdOrderRoutingLine."Run Time Unit of Meas. Code") /
                CalendarMgt.TimeFactor(Workcenter."Unit of Measure Code"),
                Workcenter."Calendar Rounding Precision");

            GetConstrainedSetup(ConstrainedCapacity, ResourceIsConstrained, ParentWorkCenter, ParentIsConstrained);
            if not ProdOrderRoutingLine."Schedule Manually" and
               (ResourceIsConstrained or ParentIsConstrained)
            then
                FinitelyLoadCapBack(RoutingTimeType::"Run Time", ConstrainedCapacity, ResourceIsConstrained, ParentWorkCenter, ParentIsConstrained)
            else
                LoadCapBack(ProdOrderRoutingLine.Type, ProdOrderRoutingLine."No.", RoutingTimeType::"Run Time", true);

            ProdEndingDate := ProdOrderRoutingLine."Starting Date";
            ProdEndingTime := ProdOrderRoutingLine."Starting Time";
        until FindSendAheadStartingTime(TempProdOrderRoutingLine, SendAheadLotSize);

        ProdEndingDate := ProdOrderRoutingLine."Starting Date";
        ProdEndingTime := ProdOrderRoutingLine."Starting Time";
        RemainNeedQty :=
          Round(
            ProdOrderRoutingLine."Setup Time" *
            CalendarMgt.TimeFactor(ProdOrderRoutingLine."Setup Time Unit of Meas. Code") /
            CalendarMgt.TimeFactor(Workcenter."Unit of Measure Code"),
            Workcenter."Calendar Rounding Precision");
        OnCalcRoutingLineBackOnAfterCalcRemainNeedQty(ProdOrderRoutingLine, Workcenter, RemainNeedQty);

        GetConstrainedSetup(ConstrainedCapacity, ResourceIsConstrained, ParentWorkCenter, ParentIsConstrained);
        if not ProdOrderRoutingLine."Schedule Manually" and
           (ResourceIsConstrained or ParentIsConstrained)
        then
            FinitelyLoadCapBack(RoutingTimeType::"Setup Time", ConstrainedCapacity, ResourceIsConstrained, ParentWorkCenter, ParentIsConstrained)
        else
            LoadCapBack(ProdOrderRoutingLine.Type, ProdOrderRoutingLine."No.", RoutingTimeType::"Setup Time", true);

        ProdOrderRoutingLine."Starting Date" := ProdEndingDate;
        ProdOrderRoutingLine."Starting Time" := ProdEndingTime;

        if ProdOrderRoutingLine."Ending Date" = CalendarMgt.GetMaxDate() then begin
            ProdOrderRoutingLine."Ending Date" := ProdOrderRoutingLine."Starting Date";
            ProdOrderRoutingLine."Ending Time" := ProdOrderRoutingLine."Starting Time";
        end;

        ProdOrderRoutingLine.UpdateDatetime();
        ProdOrderRoutingLine.Modify();

        OnAfterCalcRoutingLineBack(ProdOrderRoutingLine, ProdOrderLine);
    end;

    local procedure GetConstrainedSetup(var ConstrainedCapacity: Record "Capacity Constrained Resource"; var ResourceIsConstrained: Boolean; var ParentWorkCenter: Record "Capacity Constrained Resource"; var ParentIsConstrained: Boolean)
    var
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeGetConstrainedSetup(ProdOrderRoutingLine, ConstrainedCapacity, ResourceIsConstrained, ParentWorkCenter, ParentIsConstrained, IsHandled);
        if IsHandled then
            exit;

        ResourceIsConstrained := ConstrainedCapacity.Get(ProdOrderRoutingLine.Type, ProdOrderRoutingLine."No.");
        ParentIsConstrained := ParentWorkCenter.Get(ProdOrderRoutingLine.Type::"Work Center", ProdOrderRoutingLine."Work Center No.");
    end;

    local procedure GetSendAheadEndingTime(ProdOrderRoutingLinePrev: Record "Prod. Order Routing Line"; var SendAheadLotSize: Decimal): Boolean
    var
        xProdOrderRoutingLine: Record "Prod. Order Routing Line";
        SetupTime: Decimal;
        RunTime: Decimal;
        WaitTime: Decimal;
        MoveTime: Decimal;
        ResidualLotSize: Decimal;
        ResidualProdStartDateTime: DateTime;
    begin
        ProdEndingTime := ProdOrderRoutingLinePrev."Ending Time";
        ProdEndingDate := ProdOrderRoutingLinePrev."Ending Date";
        SendAheadLotSize := MaxLotSize;
        if TotalLotSize = MaxLotSize then
            exit(true);

        if (ProdOrderRoutingLinePrev."Send-Ahead Quantity" = 0) or
           (ProdOrderRoutingLinePrev."Send-Ahead Quantity" >= MaxLotSize)
        then begin
            TotalLotSize := SendAheadLotSize;
            exit(false);
        end;
        SendAheadLotSize := ProdOrderRoutingLinePrev."Send-Ahead Quantity";
        if MaxLotSize < (TotalLotSize + SendAheadLotSize) then begin
            SendAheadLotSize := MaxLotSize - TotalLotSize;
            TotalLotSize := MaxLotSize;
        end else begin
            if TotalLotSize = 0 then begin
                ResidualLotSize := MaxLotSize mod SendAheadLotSize;
                if ResidualLotSize = 0 then
                    ResidualLotSize := SendAheadLotSize;
            end;
            TotalLotSize += SendAheadLotSize;
        end;

        ProdOrderCapNeed2.Reset();
        ProdOrderCapNeed2.SetCurrentKey(Status, "Prod. Order No.", "Routing Reference No.", "Operation No.", Date, "Starting Time");
        ProdOrderCapNeed2.SetRange(Status, ProdOrderRoutingLine.Status);
        ProdOrderCapNeed2.SetRange("Prod. Order No.", ProdOrderRoutingLine."Prod. Order No.");
        ProdOrderCapNeed2.SetRange("Requested Only", false);
        ProdOrderCapNeed2.SetRange("Routing No.", ProdOrderRoutingLine."Routing No.");
        ProdOrderCapNeed2.SetRange("Routing Reference No.", ProdOrderRoutingLine."Routing Reference No.");
        ProdOrderCapNeed2.SetRange("Operation No.", ProdOrderRoutingLine."Operation No.");
        if ProdOrderCapNeed2.FindFirst() then begin
            if TotalLotSize <> MaxLotSize then begin
                SendAheadLotSize := MaxLotSize - (TotalLotSize - SendAheadLotSize);
                TotalLotSize := MaxLotSize;
            end;
            exit(false);
        end;
        // calculate Starting Date/Time of current line using Setup/Run/Wait/Move Time for the first send-ahead lot from previous line
        Workcenter2.Get(ProdOrderRoutingLinePrev."Work Center No.");
        SetupTime :=
          Round(
            ProdOrderRoutingLinePrev."Setup Time" *
            CalendarMgt.TimeFactor(ProdOrderRoutingLinePrev."Setup Time Unit of Meas. Code") /
            CalendarMgt.TimeFactor(Workcenter2."Unit of Measure Code"),
            Workcenter2."Calendar Rounding Precision");
        RunTime :=
          Round(
            SendAheadLotSize * ProdOrderRoutingLinePrev.RunTimePer() *
            CalendarMgt.TimeFactor(ProdOrderRoutingLinePrev."Run Time Unit of Meas. Code") /
            CalendarMgt.TimeFactor(Workcenter2."Unit of Measure Code"),
            Workcenter2."Calendar Rounding Precision");
        WaitTime :=
          Round(
            ProdOrderRoutingLinePrev."Wait Time" *
            CalendarMgt.TimeFactor(ProdOrderRoutingLinePrev."Wait Time Unit of Meas. Code") /
            CalendarMgt.TimeFactor(Workcenter2."Unit of Measure Code"),
            Workcenter2."Calendar Rounding Precision");
        MoveTime :=
          Round(
            ProdOrderRoutingLinePrev."Move Time" *
            CalendarMgt.TimeFactor(ProdOrderRoutingLinePrev."Move Time Unit of Meas. Code") /
            CalendarMgt.TimeFactor(Workcenter2."Unit of Measure Code"),
            Workcenter2."Calendar Rounding Precision");
        OnGetSendAheadEndingTimeOnAfterCalcTimes(ProdOrderRoutingLinePrev, Workcenter2, SetupTime, RunTime, WaitTime, MoveTime);

        xProdOrderRoutingLine := ProdOrderRoutingLine;
        ProdOrderRoutingLine := ProdOrderRoutingLinePrev;
        ProdStartingDate := ProdOrderRoutingLine."Starting Date";
        ProdStartingTime := ProdOrderRoutingLine."Starting Time";
        RemainNeedQty := SetupTime;
        LoadCapForward(ProdOrderRoutingLine.Type, ProdOrderRoutingLine."No.", RoutingTimeType::"Setup Time", false);
        RemainNeedQty := RunTime;
        LoadCapForward(ProdOrderRoutingLine.Type, ProdOrderRoutingLine."No.", RoutingTimeType::"Run Time", false);
        RemainNeedQty := WaitTime;
        LoadCapForward(ProdOrderRoutingLine.Type, ProdOrderRoutingLine."No.", RoutingTimeType::"Wait Time", false);
        RemainNeedQty := MoveTime;
        LoadCapForward(ProdOrderRoutingLine.Type, ProdOrderRoutingLine."No.", RoutingTimeType::"Move Time", false);
        ProdOrderRoutingLine := xProdOrderRoutingLine;

        ProdOrderRoutingLine."Starting Date" := ProdStartingDate;
        ProdOrderRoutingLine."Starting Time" := ProdStartingTime;
        // calculate Starting Date/Time for the last lot of current line by going forward from Starting Date/Time of the line
        Workcenter.Get(ProdOrderRoutingLine."Work Center No.");
        SetupTime :=
          Round(
            ProdOrderRoutingLine."Setup Time" *
            CalendarMgt.TimeFactor(ProdOrderRoutingLine."Run Time Unit of Meas. Code") /
            CalendarMgt.TimeFactor(Workcenter."Unit of Measure Code"),
            Workcenter."Calendar Rounding Precision");
        RunTime :=
          Round(
            (MaxLotSize - ResidualLotSize) * ProdOrderRoutingLine.RunTimePer() *
            CalendarMgt.TimeFactor(ProdOrderRoutingLine."Run Time Unit of Meas. Code") /
            CalendarMgt.TimeFactor(Workcenter."Unit of Measure Code"),
            Workcenter."Calendar Rounding Precision");
        OnGetSendAheadEndingTimeOnAfterCalcSetupAndRunTime(ProdOrderRoutingLine, Workcenter, SetupTime, RunTime);

        RemainNeedQty := SetupTime;
        LoadCapForward(ProdOrderRoutingLine.Type, ProdOrderRoutingLine."No.", RoutingTimeType::"Setup Time", false);
        RemainNeedQty := RunTime;
        LoadCapForward(ProdOrderRoutingLine.Type, ProdOrderRoutingLine."No.", RoutingTimeType::"Run Time", false);
        ResidualProdStartDateTime := CreateDateTime(ProdStartingDate, ProdStartingTime);
        // last lot must be finished by previous Work Center, otherwise we recalculate Starting Date/Time of current line
        ProdOrderRoutingLinePrev.UpdateDatetime();
        if ProdOrderRoutingLinePrev."Ending Date-Time" > ResidualProdStartDateTime then begin
            ProdEndingDate := ProdOrderRoutingLinePrev."Ending Date";
            ProdEndingTime := ProdOrderRoutingLinePrev."Ending Time";
            RemainNeedQty := RunTime;
            LoadCapBack(ProdOrderRoutingLine.Type, ProdOrderRoutingLine."No.", RoutingTimeType::"Run Time", false);
            RemainNeedQty := SetupTime;
            LoadCapBack(ProdOrderRoutingLine.Type, ProdOrderRoutingLine."No.", RoutingTimeType::"Setup Time", false);
        end;

        ProdStartingDate := ProdOrderRoutingLine."Starting Date";
        ProdStartingTime := ProdOrderRoutingLine."Starting Time";
        ProdEndingDate := ProdOrderRoutingLine."Starting Date";
        ProdEndingTime := ProdOrderRoutingLine."Starting Time";
        exit(false);
    end;

    procedure CalcRoutingLineForward(CalculateStartDate: Boolean)
    var
        ProdOrderRoutingLine2: Record "Prod. Order Routing Line";
        ProdOrderRoutingLine3: Record "Prod. Order Routing Line";
        ConstrainedCapacity: Record "Capacity Constrained Resource";
        ParentWorkCenter: Record "Capacity Constrained Resource";
        TempProdOrderRoutingLine: Record "Prod. Order Routing Line" temporary;
        SendAheadLotSize: Decimal;
        InputQtyDiffTime: Decimal;
        ParentIsConstrained: Boolean;
        ResourceIsConstrained: Boolean;
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeCalcRoutingLineForward(ProdOrderRoutingLine, CalculateStartDate, IsHandled, TempProdOrderRoutingLine, SendAheadLotSize, MaxLotSize, TotalLotSize, RemainNeedQty, UpdateDates);
        if IsHandled then
            exit;

        CalendarEntry.SetRange(Date, ProdOrderRoutingLine."Starting Date", DMY2Date(31, 12, 9999));

        ProdStartingTime := ProdOrderRoutingLine."Starting Time";
        ProdStartingDate := ProdOrderRoutingLine."Starting Date";
        ProdEndingTime := ProdOrderRoutingLine."Starting Time";
        ProdEndingDate := ProdOrderRoutingLine."Starting Date";

        InputQtyDiffTime := 0;

        FirstEntry := true;

        if (ProdOrderRoutingLine."Previous Operation No." <> '') and
           CalculateStartDate
        then begin
            Clear(ProdOrderRoutingLine3);

            TempProdOrderRoutingLine.Reset();
            TempProdOrderRoutingLine.DeleteAll();

            SetRoutingLineFilters(ProdOrderRoutingLine, ProdOrderRoutingLine2);
            ProdOrderRoutingLine2.SetFilter("Operation No.", ProdOrderRoutingLine."Previous Operation No.");
            if ProdOrderRoutingLine2.Find('-') then
                repeat
                    TotalLotSize := 0;
                    GetSendAheadEndingTime(ProdOrderRoutingLine2, SendAheadLotSize);

                    TempProdOrderRoutingLine.Copy(ProdOrderRoutingLine2);
                    TempProdOrderRoutingLine.Insert();

                    SetMaxDateTime(ProdStartingDate, ProdStartingTime, ProdEndingDate, ProdEndingTime);
                    ProdOrderRoutingLine3 := ProdOrderRoutingLine2;

                    if (ProdOrderRoutingLine2."Send-Ahead Quantity" > 0) and
                       (ProdOrderRoutingLine2."Input Quantity" > ProdOrderRoutingLine."Input Quantity")
                    then begin
                        Workcenter2.Get(ProdOrderRoutingLine2."Work Center No.");
                        InputQtyDiffTime :=
                          (ProdOrderRoutingLine2."Input Quantity" - ProdOrderRoutingLine."Input Quantity") *
                          ProdOrderRoutingLine2.RunTimePer();
                        InputQtyDiffTime :=
                          Round(
                            InputQtyDiffTime *
                            CalendarMgt.TimeFactor(ProdOrderRoutingLine2."Run Time Unit of Meas. Code") /
                            CalendarMgt.TimeFactor(Workcenter2."Unit of Measure Code"),
                            Workcenter2."Calendar Rounding Precision");
                    end;
                until ProdOrderRoutingLine2.Next() = 0
            else begin
                // parallel routing with finished first operation
                if ProdStartingDate = 0D then begin
                    ProdOrderRoutingLine2.Get(ProdOrderRoutingLine.Status,
                      ProdOrderRoutingLine."Prod. Order No.", ProdOrderRoutingLine."Routing Reference No.",
                      ProdOrderRoutingLine."Routing No.", ProdOrderRoutingLine."Operation No.");
                    ProdStartingDate := ProdOrderRoutingLine2."Starting Date";
                    ProdStartingTime := ProdOrderRoutingLine2."Starting Time";
                end;
                TotalLotSize := MaxLotSize;
                SendAheadLotSize := MaxLotSize;
            end;

            if ProdOrderRoutingLine3."Prod. Order No." <> '' then begin
                ProdOrderRoutingLine3."Critical Path" := true;
                ProdOrderRoutingLine3.UpdateDatetime();
                ProdOrderRoutingLine3.Modify();
            end;
        end else
            SetLotSizesToMax(SendAheadLotSize, TotalLotSize);

        RemainNeedQty :=
          Round(
            Workcenter."Queue Time" *
            CalendarMgt.TimeFactor(Workcenter."Queue Time Unit of Meas. Code") /
            CalendarMgt.TimeFactor(Workcenter."Unit of Measure Code"),
            Workcenter."Calendar Rounding Precision");
        RemainNeedQty += InputQtyDiffTime;
        OnCalcRoutingLineForwardOnBeforeLoadCapForward(ProdOrderRoutingLine, ProdOrderRoutingLine3);
        LoadCapForward(ProdOrderRoutingLine.Type, ProdOrderRoutingLine."No.", RoutingTimeType::"Queue Time", false);
        RemainNeedQty :=
          Round(
            ProdOrderRoutingLine."Setup Time" *
            CalendarMgt.TimeFactor(ProdOrderRoutingLine."Setup Time Unit of Meas. Code") /
            CalendarMgt.TimeFactor(Workcenter."Unit of Measure Code"),
            Workcenter."Calendar Rounding Precision");
        OnCalcRoutingLineForwardOnAfterCalcRemainNeedQty(ProdOrderRoutingLine, Workcenter, RemainNeedQty);
        UpdateDates := true;

        GetConstrainedSetup(ConstrainedCapacity, ResourceIsConstrained, ParentWorkCenter, ParentIsConstrained);
        if not ProdOrderRoutingLine."Schedule Manually" and
           (RemainNeedQty > 0) and (ResourceIsConstrained or ParentIsConstrained)
        then
            FinitelyLoadCapForward(RoutingTimeType::"Setup Time", ConstrainedCapacity, ResourceIsConstrained, ParentWorkCenter, ParentIsConstrained)
        else
            LoadCapForward(ProdOrderRoutingLine.Type, ProdOrderRoutingLine."No.", RoutingTimeType::"Setup Time", true);

        FirstInBatch := true;
        repeat
            if (InputQtyDiffTime > 0) and (TotalLotSize = MaxLotSize) then
                SetMaxDateTime(
                  ProdStartingDate, ProdStartingTime, ProdOrderRoutingLine2."Ending Date", ProdOrderRoutingLine2."Ending Time");

            if SendAheadLotSize > ProdOrderQty then
                LotSize := ProdOrderQty
            else
                LotSize := SendAheadLotSize;
            RemainNeedQty := LotSize * ProdOrderRoutingLine.RunTimePer();
            OnCalculateRoutingLineForwardOnAfterCalcRemainNeedQtyForLotSize(ProdOrderRoutingLine, RemainNeedQty);
            RemainNeedQty :=
              Round(
                RemainNeedQty *
                CalendarMgt.TimeFactor(ProdOrderRoutingLine."Run Time Unit of Meas. Code") /
                CalendarMgt.TimeFactor(Workcenter."Unit of Measure Code"),
                Workcenter."Calendar Rounding Precision");

            GetConstrainedSetup(ConstrainedCapacity, ResourceIsConstrained, ParentWorkCenter, ParentIsConstrained);
            if not ProdOrderRoutingLine."Schedule Manually" and
               (RemainNeedQty > 0) and (ResourceIsConstrained or ParentIsConstrained)
            then
                FinitelyLoadCapForward(RoutingTimeType::"Run Time", ConstrainedCapacity, ResourceIsConstrained, ParentWorkCenter, ParentIsConstrained)
            else
                LoadCapForward(ProdOrderRoutingLine.Type, ProdOrderRoutingLine."No.", RoutingTimeType::"Run Time", true);

            ProdStartingDate := ProdOrderRoutingLine."Ending Date";
            ProdStartingTime := ProdOrderRoutingLine."Ending Time";
        until FindSendAheadEndingTime(TempProdOrderRoutingLine, SendAheadLotSize);

        RemainNeedQty :=
          Round(
            ProdOrderRoutingLine."Wait Time" *
            CalendarMgt.TimeFactor(ProdOrderRoutingLine."Wait Time Unit of Meas. Code") /
            CalendarMgt.TimeFactor(Workcenter."Unit of Measure Code"),
            Workcenter."Calendar Rounding Precision");

        OnCalcRoutingLineForwardOnBeforeLoadCapForward2(ProdOrderRoutingLine, ProdOrderRoutingLine3);
        LoadCapForward(ProdOrderRoutingLine.Type, ProdOrderRoutingLine."No.", RoutingTimeType::"Wait Time", false);
        RemainNeedQty :=
          Round(
            ProdOrderRoutingLine."Move Time" *
            CalendarMgt.TimeFactor(ProdOrderRoutingLine."Move Time Unit of Meas. Code") /
            CalendarMgt.TimeFactor(Workcenter."Unit of Measure Code"),
            Workcenter."Calendar Rounding Precision");
        LoadCapForward(ProdOrderRoutingLine.Type, ProdOrderRoutingLine."No.", RoutingTimeType::"Move Time", false);

        if ProdOrderRoutingLine."Starting Date" = 0D then begin
            ProdOrderRoutingLine."Starting Date" := ProdOrderRoutingLine."Ending Date";
            ProdOrderRoutingLine."Starting Time" := ProdOrderRoutingLine."Ending Time";
        end;

        ProdOrderRoutingLine.UpdateDatetime();
        ProdOrderRoutingLine.Modify();

        OnAfterCalcRoutingLineForward(ProdOrderRoutingLine, ProdOrderLine);
    end;

    local procedure CalculateRoutingLineFixed()
    var
        FixedProdOrderRoutingLine: Record "Prod. Order Routing Line";
        CalcEndDate: Boolean;
        CalcStartDate: Boolean;
    begin
        FixedProdOrderRoutingLine := ProdOrderRoutingLine;
        if FixedProdOrderRoutingLine."Starting Date-Time" > FixedProdOrderRoutingLine."Ending Date-Time" then
            Error(Text007);

        // Calculate wait and move time, find end of runtime
        CalcEndDate := true;
        OnCalculateRoutingLineFixedOnBeforeCalcRoutingLineBack(ProdOrderRoutingLine, CalcEndDate);
        CalcRoutingLineBack(CalcEndDate);
        RunEndingDateTime :=
          CreateDateTime(ProdOrderRoutingLine."Starting Date", ProdOrderRoutingLine."Starting Time");

        // Find start of runtime
        ProdOrderRoutingLine := FixedProdOrderRoutingLine;
        CalcStartDate := true;
        OnCalculateRoutingLineFixedOnBeforeCalcRoutingLineForward(ProdOrderRoutingLine, CalcStartDate);
        CalcRoutingLineForward(CalcStartDate);

        ProdOrderRoutingLine."Starting Time" := FixedProdOrderRoutingLine."Starting Time";
        ProdOrderRoutingLine."Starting Date" := FixedProdOrderRoutingLine."Starting Date";
        ProdOrderRoutingLine."Ending Time" := FixedProdOrderRoutingLine."Ending Time";
        ProdOrderRoutingLine."Ending Date" := FixedProdOrderRoutingLine."Ending Date";
        ProdOrderRoutingLine.UpdateDatetime();
        ProdOrderRoutingLine.Modify();
    end;

    procedure CalculateRoutingLine(var ProdOrderRoutingLine2: Record "Prod. Order Routing Line"; Direction: Option Forward,Backward; CalcStartEndDate: Boolean)
    var
        ProdOrderCapNeed: Record "Prod. Order Capacity Need";
        MfgCostCalcMgt: Codeunit "Mfg. Cost Calculation Mgt.";
        ExpectedOperOutput: Decimal;
        ActualOperOutput: Decimal;
        TotalQtyPerOperation: Decimal;
        TotalCapacityPerOperation: Decimal;
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeCalculateRoutingLine(ProdOrderRoutingLine2, Direction, CalcStartEndDate, IsHandled);
        if IsHandled then
            exit;

        MfgSetup.Get();

        ProdOrderRoutingLine := ProdOrderRoutingLine2;

        WaitTimeOnly :=
          (ProdOrderRoutingLine."Setup Time" = 0) and (ProdOrderRoutingLine."Run Time" = 0) and
          (ProdOrderRoutingLine."Move Time" = 0);

        if ProdOrderRoutingLine."Ending Time" = 0T then
            ProdOrderRoutingLine."Ending Time" := 000000T;

        if ProdOrderRoutingLine."Starting Time" = 0T then
            ProdOrderRoutingLine."Starting Time" := 000000T;

        ProdOrderRoutingLine."Expected Operation Cost Amt." := 0;
        ProdOrderRoutingLine."Expected Capacity Ovhd. Cost" := 0;
        ProdOrderRoutingLine."Expected Capacity Need" := 0;

        OnCalculateRoutingLineOnBeforeProdOrderCapNeedReset(ProdOrderRoutingLine, ProdOrderRoutingLine2);

        ProdOrderCapNeed.Reset();
        ProdOrderCapNeed.SetLoadFields(Status, "Prod. Order No.", "Requested Only", "Routing No.", "Routing Reference No.", "Operation No.");
        ProdOrderCapNeed.SetRange(Status, ProdOrderRoutingLine.Status);
        ProdOrderCapNeed.SetRange("Prod. Order No.", ProdOrderRoutingLine."Prod. Order No.");
        ProdOrderCapNeed.SetRange("Requested Only", false);
        ProdOrderCapNeed.SetRange("Routing No.", ProdOrderRoutingLine."Routing No.");
        ProdOrderCapNeed.SetRange("Routing Reference No.", ProdOrderRoutingLine."Routing Reference No.");
        ProdOrderCapNeed.SetRange("Operation No.", ProdOrderRoutingLine."Operation No.");
        ProdOrderCapNeed.DeleteAll();

        NextCapNeedLineNo := 1;

        ProdOrderRoutingLine.TestField("Work Center No.");

        CurrentWorkCenterNo := '';
        Workcenter.Get(ProdOrderRoutingLine."Work Center No.");
        if ProdOrderRoutingLine.Type = ProdOrderRoutingLine.Type::"Machine Center" then begin
            MachineCenter.Get(ProdOrderRoutingLine."No.");
            Workcenter."Queue Time" := MachineCenter."Queue Time";
            Workcenter."Queue Time Unit of Meas. Code" := MachineCenter."Queue Time Unit of Meas. Code";
        end;
        if not CalcStartEndDate then
            Clear(Workcenter."Queue Time");
        ProdOrder.Get(ProdOrderRoutingLine.Status, ProdOrderRoutingLine."Prod. Order No.");

        ProdOrderQty := 0;
        TotalScrap := 0;
        TotalLotSize := 0;
        ProdOrderLine.SetRange(Status, ProdOrderRoutingLine.Status);
        ProdOrderLine.SetRange("Prod. Order No.", ProdOrderRoutingLine."Prod. Order No.");
        ProdOrderLine.SetRange("Routing Reference No.", ProdOrderRoutingLine."Routing Reference No.");
        ProdOrderLine.SetRange("Routing No.", ProdOrderRoutingLine."Routing No.");
        ProdOrderLine.SetLoadFields("Quantity (Base)", "Scrap %", "Prod. Order No.", "Line No.", Status, "Routing No.", "Routing Version Code", "Ending Date", "Ending Time");
        OnCalculateRoutingLineOnAfterProdOrderLineSetFilters(ProdOrderLine, ProdOrder, ProdOrderRoutingLine);
        if ProdOrderLine.Find('-') then begin
            ExpectedOperOutput := 0;
            repeat
                IsHandled := false;
                OnCalculateRoutingLineOnBeforeCalcExpectedOperOutput(ProdOrderLine, ExpectedOperOutput, IsHandled);
                if not IsHandled then
                    ExpectedOperOutput := ExpectedOperOutput + ProdOrderLine."Quantity (Base)";
                TotalScrap := TotalScrap + ProdOrderLine."Scrap %";
            until ProdOrderLine.Next() = 0;
            ActualOperOutput := MfgCostCalcMgt.CalcActOutputQtyBase(ProdOrderLine, ProdOrderRoutingLine);
            ProdOrderQty := ExpectedOperOutput - ActualOperOutput;
            if ProdOrderQty < 0 then
                ProdOrderQty := 0;
        end;

        MaxLotSize :=
          ExpectedOperOutput *
          (1 + ProdOrderRoutingLine."Scrap Factor % (Accumulated)") *
          (1 + TotalScrap / 100) +
          ProdOrderRoutingLine."Fixed Scrap Qty. (Accum.)";

        OnCalculateRoutingLineOnAfterCalcMaxLotSize(ProdOrderQty, ProdOrderRoutingLine, TotalScrap, MaxLotSize);

        ProdOrderRoutingLine."Input Quantity" := MaxLotSize;

        if ActualOperOutput > 0 then
            TotalQtyPerOperation :=
              ExpectedOperOutput *
              (1 + ProdOrderRoutingLine."Scrap Factor % (Accumulated)") *
              (1 + TotalScrap / 100) +
              ProdOrderRoutingLine."Fixed Scrap Qty. (Accum.)"
        else
            TotalQtyPerOperation := MaxLotSize;

        OnBeforeCalcExpectedCost(ProdOrderRoutingLine, MaxLotSize, TotalQtyPerOperation, ActualOperOutput, ExpectedOperOutput, TotalScrap);

        TotalCapacityPerOperation :=
          Round(
            TotalQtyPerOperation *
            ProdOrderRoutingLine.RunTimePer() *
            CalendarMgt.QtyperTimeUnitofMeasure(
              ProdOrderRoutingLine."Work Center No.", ProdOrderRoutingLine."Run Time Unit of Meas. Code"),
            UOMMgt.QtyRndPrecision());

        OnCalculateRoutingLineOnBeforeCalcCostInclSetup(ProdOrderRoutingLine, TotalCapacityPerOperation, TotalQtyPerOperation);
        if MfgSetup."Cost Incl. Setup" then
            CalcCostInclSetup(ProdOrderRoutingLine, TotalCapacityPerOperation);
        OnCalculateRoutingLineOnAfterCalcCostInclSetup(ProdOrderRoutingLine, TotalCapacityPerOperation, TotalQtyPerOperation);

        CalcExpectedCost(ProdOrderRoutingLine, TotalQtyPerOperation, TotalCapacityPerOperation);

        IsHandled := false;
        OnBeforeScheduleRoutingLine(ProdOrderRoutingLine, CalcStartEndDate, IsHandled, Direction);
        if not IsHandled then
            if ProdOrderRoutingLine."Schedule Manually" then
                CalculateRoutingLineFixed()
            else
                if Direction = Direction::Backward then
                    CalcRoutingLineBack(CalcStartEndDate)
                else
                    CalcRoutingLineForward(CalcStartEndDate);

        OnAfterCalculateRoutingLine(ProdOrderRoutingLine, Enum::"Transfer Direction".FromInteger(Direction));

        ProdOrderRoutingLine2 := ProdOrderRoutingLine;
    end;

    local procedure SetLotSizesToMax(var SendAheadLotSize: Decimal; var TotalLotSize: Decimal)
    var
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeSetLotSizesToMax(SendAheadLotSize, TotalLotSize, MaxLotSize, ProdOrderRoutingLine, IsHandled);
        if IsHandled then
            exit;

        TotalLotSize := MaxLotSize;
        SendAheadLotSize := MaxLotSize;
    end;

    local procedure FinitelyLoadCapBack(TimeType: Enum "Routing Time Type"; ConstrainedCapacity: Record "Capacity Constrained Resource"; ResourceIsConstrained: Boolean; ParentWorkCenter: Record "Capacity Constrained Resource"; ParentIsConstrained: Boolean)
    var
        LastProdOrderCapNeed: Record "Prod. Order Capacity Need";
        AvailTime: Decimal;
        ProdEndingDateTime: DateTime;
        ProdEndingDateTimeAddOneDay: DateTime;
        SetupTime: Decimal;
        TimetoProgram: Decimal;
        AvailCap: Decimal;
        DampTime: Decimal;
        xConCurrCap: Decimal;
        EndTime: Time;
        StartTime: Time;
        ShouldProcessLastProdOrderCapNeed: Boolean;
    begin
        if (RemainNeedQty = 0) and WaitTimeOnly then
            exit;
        EndTime := ProdEndingTime;
        ProdEndingDateTime := CreateDateTime(ProdEndingDate, ProdEndingTime);
        ProdEndingDateTimeAddOneDay := CreateDateTime(ProdEndingDate + 1, ProdEndingTime);
        ConCurrCap := ProdOrderRoutingLine."Concurrent Capacities";
        xConCurrCap := 1;

        LastProdOrderCapNeed.SetCapacityFilters(ProdOrderRoutingLine.Type, ProdOrderRoutingLine."No.");

        CalendarEntry.SetCapacityFilters(ProdOrderRoutingLine.Type, ProdOrderRoutingLine."No.");
        CalendarEntry.SetFilter("Starting Date-Time", '<= %1', ProdEndingDateTime);
        CalendarEntry.SetFilter("Ending Date-Time", '<= %1', ProdEndingDateTimeAddOneDay);
        OnFinitelyLoadCapBackOnAfterSetCalendarEntryFilters(CalendarEntry);
        if CalendarEntry.Find('+') then
            repeat
                if (EndTime > CalendarEntry."Ending Time") or (EndTime < CalendarEntry."Starting Time") or
                   (ProdEndingDate <> CalendarEntry.Date)
                then
                    EndTime := CalendarEntry."Ending Time";
                StartTime := EndTime;

                if (ConCurrCap = 0) or (CalendarEntry.Capacity < ConCurrCap) then
                    ConCurrCap := CalendarEntry.Capacity;
                if TimeType <> TimeType::"Run Time" then
                    RemainNeedQty := RemainNeedQty * ConCurrCap / xConCurrCap;
                xConCurrCap := ConCurrCap;

                CalculateDailyLoad(AvailCap, DampTime, ConstrainedCapacity, ResourceIsConstrained, ParentWorkCenter, ParentIsConstrained);
                SetupTime := 0;
                if TimeType = TimeType::"Run Time" then begin
                    SetupTime :=
                      Round(
                        ProdOrderRoutingLine."Setup Time" *
                        CalendarMgt.TimeFactor(ProdOrderRoutingLine."Setup Time Unit of Meas. Code") /
                        CalendarMgt.TimeFactor(Workcenter."Unit of Measure Code"),
                        Workcenter."Calendar Rounding Precision");
                    SetupTime := SetupTime * ConCurrCap;
                    OnFinitelyLoadCapBackOnAfterCalcSetupTime(ProdOrderRoutingLine, Workcenter, SetupTime);
                end;
                if RemainNeedQty + SetupTime <= AvailCap + DampTime then
                    AvailCap := AvailCap + DampTime;
                AvailCap :=
                  Round(AvailCap *
                    CalendarMgt.TimeFactor(Workcenter."Unit of Measure Code") *
                    100 / CalendarEntry.Efficiency / ConCurrCap, 1, '>');
                if CalendarEntry.Capacity = CalendarEntry."Absence Capacity" then
                    AvailCap := 0;

                ShouldProcessLastProdOrderCapNeed := AvailCap > 0;
                OnFinitelyLoadCapBackOnAfterCalcShouldProcessLastProdOrderCapNeed(
                    ProdOrderRoutingLine, AvailCap, CalendarEntry, ProdEndingTime, ProdEndingDate, TimeType, FirstInBatch, FirstEntry,
                    UpdateDates, RemainNeedQty, ProdOrderCapNeed, ProdOrder, NextCapNeedLineNo, Workcenter, LotSize, ShouldProcessLastProdOrderCapNeed);
                if ShouldProcessLastProdOrderCapNeed then begin
                    ProdEndingDateTime := CreateDateTime(CalendarEntry.Date, EndTime);
                    LastProdOrderCapNeed.SetFilter(
                      "Ending Date-Time", '>= %1 & < %2', CalendarEntry."Starting Date-Time", ProdEndingDateTimeAddOneDay);
                    LastProdOrderCapNeed.SetFilter(
                      "Starting Date-Time", '>= %1 & < %2', CalendarEntry."Starting Date-Time", ProdEndingDateTime);
                    if LastProdOrderCapNeed.Find('+') then
                        repeat
                            if LastProdOrderCapNeed."Ending Time" < EndTime then begin
                                AvailTime := Min(CalendarMgt.CalcTimeDelta(EndTime, LastProdOrderCapNeed."Ending Time"), AvailCap);
                                OnFinitelyLoadCapBackOnAfterCalcAvailTimeFromLastProdOrderCapNeed(AvailTime, EndTime, AvailCap, LastProdOrderCapNeed);
                                if AvailTime > 0 then begin
                                    UpdateTimesBack(AvailTime, AvailCap, TimetoProgram, StartTime, EndTime);
                                    CreateCapNeed(CalendarEntry.Date, StartTime, EndTime, TimetoProgram, TimeType, 1);
                                    if FirstInBatch and FirstEntry then begin
                                        FirstInBatch := false;
                                        FirstEntry := false
                                    end;
                                    if UpdateDates then begin
                                        ProdOrderRoutingLine."Ending Time" := EndTime;
                                        ProdOrderRoutingLine."Ending Date" := CalendarEntry.Date;
                                        UpdateDates := false
                                    end;
                                    EndTime := StartTime;
                                end;
                            end;
                            if LastProdOrderCapNeed."Starting Time" < EndTime then
                                EndTime := LastProdOrderCapNeed."Starting Time";
                        until (LastProdOrderCapNeed.Next(-1) = 0) or (RemainNeedQty = 0) or (AvailCap = 0);

                    if (AvailCap > 0) and (RemainNeedQty > 0) then begin
                        AvailTime := Min(CalendarMgt.CalcTimeDelta(EndTime, CalendarEntry."Starting Time"), AvailCap);
                        OnFinitelyLoadCapBackOnAfterCalcAvailTimeFromCalendarEntry(AvailTime, EndTime, AvailCap, CalendarEntry);
                        if AvailTime > 0 then begin
                            UpdateTimesBack(AvailTime, AvailCap, TimetoProgram, StartTime, EndTime);
                            AdjustStartingTime(CalendarEntry, StartTime);

                            if TimetoProgram <> 0 then
                                CreateCapNeed(CalendarEntry.Date, StartTime, EndTime, TimetoProgram, TimeType, 1);
                            if FirstInBatch and FirstEntry then begin
                                FirstInBatch := false;
                                FirstEntry := false
                            end;
                            if UpdateDates then begin
                                ProdOrderRoutingLine."Ending Time" := EndTime;
                                ProdOrderRoutingLine."Ending Date" := CalendarEntry.Date;
                                UpdateDates := false
                            end;
                            EndTime := StartTime;
                        end;
                    end;
                end;
                if RemainNeedQty > 0 then begin
                    if CalendarEntry.Next(-1) = 0 then begin
                        TestForError(Text001, Text002, CalendarEntry.Date);
                        exit;
                    end;
                    EndTime := CalendarEntry."Ending Time";
                end else begin
                    ProdEndingTime := StartTime;
                    ProdEndingDate := CalendarEntry.Date;
                    ProdOrderRoutingLine."Starting Time" := StartTime;
                    ProdOrderRoutingLine."Starting Date" := CalendarEntry.Date;
                    exit;
                end;
            until false;
    end;

    local procedure FinitelyLoadCapForward(TimeType: Enum "Routing Time Type"; ConstrainedCapacity: Record "Capacity Constrained Resource"; ResourceIsConstrained: Boolean; ParentWorkCenter: Record "Capacity Constrained Resource"; ParentIsConstrained: Boolean)
    var
        NextProdOrderCapNeed: Record "Prod. Order Capacity Need";
        AvailTime: Decimal;
        ProdStartingDateTime: DateTime;
        ProdStartingDateTimeSubOneDay: DateTime;
        RunTime: Decimal;
        TimetoProgram: Decimal;
        AvailCap: Decimal;
        DampTime: Decimal;
        xConCurrCap: Decimal;
        EndTime: Time;
        StartTime: Time;
    begin
        if (RemainNeedQty = 0) and WaitTimeOnly then
            exit;
        StartTime := ProdStartingTime;
        ProdStartingDateTime := CreateDateTime(ProdStartingDate, ProdStartingTime);
        ProdStartingDateTimeSubOneDay := CreateDateTime(ProdStartingDate - 1, ProdStartingTime);
        ConCurrCap := ProdOrderRoutingLine."Concurrent Capacities";
        xConCurrCap := 1;

        NextProdOrderCapNeed.SetCapacityFilters(ProdOrderRoutingLine.Type, ProdOrderRoutingLine."No.");

        CalendarEntry.SetCapacityFilters(ProdOrderRoutingLine.Type, ProdOrderRoutingLine."No.");
        CalendarEntry.SetFilter("Starting Date-Time", '>= %1', ProdStartingDateTimeSubOneDay);
        CalendarEntry.SetFilter("Ending Date-Time", '>= %1', ProdStartingDateTime);
        if CalendarEntry.Find('-') then
            repeat
                if (StartTime < CalendarEntry."Starting Time") or (StartTime > CalendarEntry."Ending Time") or
                   (ProdStartingDate <> CalendarEntry.Date)
                then
                    StartTime := CalendarEntry."Starting Time";
                EndTime := StartTime;

                if (ConCurrCap = 0) or (CalendarEntry.Capacity < ConCurrCap) then
                    ConCurrCap := CalendarEntry.Capacity;
                if TimeType <> TimeType::"Run Time" then
                    RemainNeedQty := RemainNeedQty * ConCurrCap / xConCurrCap;
                xConCurrCap := ConCurrCap;

                CalculateDailyLoad(AvailCap, DampTime, ConstrainedCapacity, ResourceIsConstrained, ParentWorkCenter, ParentIsConstrained);
                RunTime := 0;
                if TimeType = TimeType::"Setup Time" then begin
                    RunTime := LotSize * ProdOrderRoutingLine.RunTimePer();
                    RunTime :=
                      Round(RunTime *
                        CalendarMgt.TimeFactor(ProdOrderRoutingLine."Run Time Unit of Meas. Code") /
                        CalendarMgt.TimeFactor(Workcenter."Unit of Measure Code"),
                        Workcenter."Calendar Rounding Precision");
                end;
                if RemainNeedQty + RunTime <= AvailCap + DampTime then
                    AvailCap := AvailCap + DampTime;
                AvailCap :=
                  Round(AvailCap *
                    CalendarMgt.TimeFactor(Workcenter."Unit of Measure Code") *
                    100 / CalendarEntry.Efficiency / ConCurrCap, 1, '>');
                if CalendarEntry.Capacity = CalendarEntry."Absence Capacity" then
                    AvailCap := 0;

                if AvailCap > 0 then begin
                    ProdStartingDateTime := CreateDateTime(CalendarEntry.Date, StartTime);
                    NextProdOrderCapNeed.SetFilter(
                      "Ending Date-Time", '> %1 & <= %2', ProdStartingDateTime, CalendarEntry."Ending Date-Time");
                    NextProdOrderCapNeed.SetFilter(
                      "Starting Date-Time", '> %1 & <= %2', ProdStartingDateTimeSubOneDay, CalendarEntry."Ending Date-Time");
                    if NextProdOrderCapNeed.Find('-') then
                        repeat
                            if NextProdOrderCapNeed."Starting Time" > StartTime then begin
                                AvailTime := Min(CalendarMgt.CalcTimeDelta(NextProdOrderCapNeed."Starting Time", StartTime), AvailCap);
                                if AvailTime > 0 then begin
                                    UpdateTimesForward(AvailTime, AvailCap, TimetoProgram, StartTime, EndTime);
                                    CreateCapNeed(CalendarEntry.Date, StartTime, EndTime, TimetoProgram, TimeType, 0);
                                    if FirstInBatch and FirstEntry then begin
                                        FirstInBatch := false;
                                        FirstEntry := false
                                    end;
                                    if UpdateDates then begin
                                        ProdOrderRoutingLine."Starting Time" := StartTime;
                                        ProdOrderRoutingLine."Starting Date" := CalendarEntry.Date;
                                        UpdateDates := false
                                    end;
                                    StartTime := EndTime;
                                end;
                            end;
                            if NextProdOrderCapNeed."Ending Time" > StartTime then
                                StartTime := NextProdOrderCapNeed."Ending Time"
                        until (NextProdOrderCapNeed.Next() = 0) or (RemainNeedQty = 0) or (AvailCap = 0);

                    if (AvailCap > 0) and (RemainNeedQty > 0) then begin
                        AvailTime := Min(CalendarMgt.CalcTimeDelta(CalendarEntry."Ending Time", StartTime), AvailCap);
                        if AvailTime > 0 then begin
                            UpdateTimesForward(AvailTime, AvailCap, TimetoProgram, StartTime, EndTime);
                            AdjustEndingTime(CalendarEntry, EndTime);
                            if TimetoProgram <> 0 then
                                CreateCapNeed(CalendarEntry.Date, StartTime, EndTime, TimetoProgram, TimeType, 0);
                            if FirstInBatch and FirstEntry then begin
                                FirstInBatch := false;
                                FirstEntry := false
                            end;
                            if UpdateDates then begin
                                ProdOrderRoutingLine."Starting Time" := StartTime;
                                ProdOrderRoutingLine."Starting Date" := CalendarEntry.Date;
                                UpdateDates := false
                            end;
                            StartTime := EndTime;
                        end;
                    end;
                end;
                if RemainNeedQty > 0 then begin
                    if CalendarEntry.Next() = 0 then begin
                        TestForError(Text003, Text004, CalendarEntry.Date);
                        exit;
                    end;
                    StartTime := CalendarEntry."Starting Time";
                end else begin
                    ProdStartingTime := EndTime;
                    ProdStartingDate := CalendarEntry.Date;
                    ProdOrderRoutingLine."Ending Time" := EndTime;
                    ProdOrderRoutingLine."Ending Date" := CalendarEntry.Date;
                    exit;
                end;
            until false;
    end;

    local procedure AdjustEndingTime(CalendarEntry: Record "Calendar Entry"; var EndingTime: Time)
    var
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeAdjustEndingTime(IsHandled, CalendarEntry, EndingTime);
        if IsHandled then
            exit;

        if EndingTime > CalendarEntry."Ending Time" then
            EndingTime := CalendarEntry."Ending Time";
    end;

    local procedure AdjustStartingTime(CalendarEntry: Record "Calendar Entry"; var StartingTime: Time)
    var
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeAdjustStartingTime(IsHandled, CalendarEntry, StartingTime);
        if IsHandled then
            exit;

        if StartingTime < CalendarEntry."Starting Time" then
            StartingTime := CalendarEntry."Starting Time";
    end;

    local procedure CalculateDailyLoad(var AvailCap: Decimal; var DampTime: Decimal; ConstrainedCapacity: Record "Capacity Constrained Resource"; IsResourceConstrained: Boolean; ParentWorkCenter: Record "Capacity Constrained Resource"; IsParentConstrained: Boolean)
    var
        CurrentLoadBase: Decimal;
        AvailCapWorkCenter: Decimal;
        DampTimeWorkCenter: Decimal;
        CapEffectiveBase: Decimal;
    begin
        GetCurrentWorkCenterTimeFactorAndRounding(Workcenter."No.");
        if (CalendarEntry."Capacity Type" = CalendarEntry."Capacity Type"::"Work Center") or
           ((CalendarEntry."Capacity Type" = CalendarEntry."Capacity Type"::"Machine Center") and
            (IsResourceConstrained xor IsParentConstrained))
        then begin
            if IsParentConstrained then begin
                ConstrainedCapacity := ParentWorkCenter;
                CalcCapConResWorkCenterLoadBase(ConstrainedCapacity, CalendarEntry.Date, CapEffectiveBase, CurrentLoadBase)
            end else
                CalcCapConResProdOrderNeedBase(ConstrainedCapacity, CalendarEntry.Date, CapEffectiveBase, CurrentLoadBase);
            CalcAvailCapBaseAndDampTime(
              ConstrainedCapacity, AvailCap, DampTime, CapEffectiveBase, CurrentLoadBase, CurrentTimeFactor, CurrentRounding);
        end
        else begin
            CalcCapConResProdOrderNeedBase(ConstrainedCapacity, CalendarEntry.Date, CapEffectiveBase, CurrentLoadBase);
            CalcAvailCapBaseAndDampTime(
              ConstrainedCapacity, AvailCap, DampTime, CapEffectiveBase, CurrentLoadBase, CurrentTimeFactor, CurrentRounding);

            CalcCapConResWorkCenterLoadBase(ParentWorkCenter, CalendarEntry.Date, CapEffectiveBase, CurrentLoadBase);
            CalcAvailCapBaseAndDampTime(
              ParentWorkCenter, AvailCapWorkCenter, DampTimeWorkCenter, CapEffectiveBase, CurrentLoadBase, CurrentTimeFactor, CurrentRounding);

            if AvailCap + DampTime > AvailCapWorkCenter + DampTimeWorkCenter then
                DampTime := DampTimeWorkCenter
            else
                if AvailCap + DampTime = AvailCapWorkCenter + DampTimeWorkCenter then
                    DampTime := max(DampTime, DampTimeWorkCenter);
            AvailCap := Round(Min(AvailCap, AvailCapWorkCenter), 1);
        end;
    end;

    local procedure UpdateTimesBack(var AvailTime: Decimal; var AvailCap: Decimal; var TimetoProgram: Decimal; var StartTime: Time; EndTime: Time)
    var
        RoundedTimetoProgram: Decimal;
        IsHandled: Boolean;
        ShouldUpdateAvailCap: Boolean;
    begin
        IsHandled := false;
        OnBeforeUpdateTimesBack(CalendarEntry, ProdOrderRoutingLine, AvailTime, AvailCap, TimetoProgram, StartTime, EndTime, ConCurrCap, Workcenter, RemainNeedQty, IsHandled);
        if IsHandled then
            exit;

        AvailTime :=
          Round(AvailTime / CalendarMgt.TimeFactor(Workcenter."Unit of Measure Code") *
            CalendarEntry.Efficiency / 100 * ConCurrCap, Workcenter."Calendar Rounding Precision");
        TimetoProgram := Min(RemainNeedQty, AvailTime);
        RoundedTimetoProgram :=
          Round(TimetoProgram *
            CalendarMgt.TimeFactor(Workcenter."Unit of Measure Code") *
            100 / CalendarEntry.Efficiency / ConCurrCap, 1, '>');
        StartTime := CalendarMgt.CalcTimeSubtract(EndTime, RoundedTimetoProgram);
        RemainNeedQty := RemainNeedQty - TimetoProgram;
        ShouldUpdateAvailCap := ProdOrderRoutingLine.Status <> ProdOrderRoutingLine.Status::Simulated;
        OnUpdateTimesBackOnBeforeUpdateAvailCap(ProdOrderRoutingLine, ShouldUpdateAvailCap);
        if ShouldUpdateAvailCap then
            AvailCap := AvailCap - RoundedTimetoProgram;
    end;

    local procedure UpdateTimesForward(var AvailTime: Decimal; var AvailCap: Decimal; var TimetoProgram: Decimal; StartTime: Time; var EndTime: Time)
    var
        RoundedTimetoProgram: Decimal;
        IsHandled: Boolean;
        ShouldUpdateAvailCap: Boolean;
    begin
        IsHandled := false;
        OnBeforeUpdateTimesForward(CalendarEntry, ProdOrderRoutingLine, AvailTime, AvailCap, TimetoProgram, StartTime, EndTime, ConCurrCap, Workcenter, RemainNeedQty, IsHandled);
        if IsHandled then
            exit;

        AvailTime :=
          Round(AvailTime / CalendarMgt.TimeFactor(Workcenter."Unit of Measure Code") *
            CalendarEntry.Efficiency / 100 * ConCurrCap, Workcenter."Calendar Rounding Precision");
        TimetoProgram := Min(RemainNeedQty, AvailTime);
        RoundedTimetoProgram :=
          Round(TimetoProgram *
            CalendarMgt.TimeFactor(Workcenter."Unit of Measure Code") *
            100 / CalendarEntry.Efficiency / ConCurrCap, 1, '>');
        EndTime := StartTime + RoundedTimetoProgram;
        RemainNeedQty := RemainNeedQty - TimetoProgram;
        ShouldUpdateAvailCap := ProdOrderRoutingLine.Status <> ProdOrderRoutingLine.Status::Simulated;
        OnUpdateTimesForwardOnBeforeUpdateAvailCap(ProdOrderRoutingLine, ShouldUpdateAvailCap);
        if ShouldUpdateAvailCap then
            AvailCap := AvailCap - RoundedTimetoProgram;
    end;

    local procedure "Min"(Number1: Decimal; Number2: Decimal): Decimal
    begin
        if Number1 <= Number2 then
            exit(Number1);

        exit(Number2);
    end;

    local procedure "Max"(Number1: Decimal; Number2: Decimal): Decimal
    begin
        if Number1 >= Number2 then
            exit(Number1);

        exit(Number2);
    end;

    local procedure CalcExpectedCost(var ProdOrderRoutingLine: Record "Prod. Order Routing Line"; TotalQtyPerOperation: Decimal; TotalCapacityPerOperation: Decimal)
    begin
        if ProdOrderRoutingLine."Unit Cost Calculation" = ProdOrderRoutingLine."Unit Cost Calculation"::Time then begin
            ProdOrderRoutingLine."Expected Operation Cost Amt." :=
              TotalCapacityPerOperation * ProdOrderRoutingLine."Unit Cost per";
            ProdOrderRoutingLine."Expected Capacity Ovhd. Cost" :=
              TotalCapacityPerOperation *
              (ProdOrderRoutingLine."Direct Unit Cost" *
               ProdOrderRoutingLine."Indirect Cost %" / 100 + ProdOrderRoutingLine."Overhead Rate");
        end else begin
            ProdOrderRoutingLine."Expected Operation Cost Amt." :=
              TotalQtyPerOperation * ProdOrderRoutingLine."Unit Cost per";
            ProdOrderRoutingLine."Expected Capacity Ovhd. Cost" :=
              TotalQtyPerOperation *
              (ProdOrderRoutingLine."Direct Unit Cost" *
               ProdOrderRoutingLine."Indirect Cost %" / 100 + ProdOrderRoutingLine."Overhead Rate");
        end;
    end;

    local procedure CalcCostInclSetup(ProdOrderRoutingLine: Record "Prod. Order Routing Line"; var TotalCapacityPerOperation: Decimal)
    var
        ConcurrentCapacity: Decimal;
    begin
        OnBeforeCalcCostInclSetup(ProdOrderRoutingLine, TotalCapacityPerOperation);

        ConcurrentCapacity := ProdOrderRoutingLine."Concurrent Capacities";
        if ConcurrentCapacity = 0 then
            ConcurrentCapacity := 1;
        TotalCapacityPerOperation :=
          TotalCapacityPerOperation +
          Round(
            ConcurrentCapacity * ProdOrderRoutingLine."Setup Time" *
            CalendarMgt.QtyperTimeUnitofMeasure(
              ProdOrderRoutingLine."Work Center No.", ProdOrderRoutingLine."Setup Time Unit of Meas. Code"),
            UOMMgt.QtyRndPrecision());

        OnAfterCalcCostInclSetup(ProdOrderRoutingLine, TotalCapacityPerOperation);
    end;

    local procedure CalcDuration(DateTime1: DateTime; DateTime2: DateTime) TotalDuration: Decimal
    begin
        TotalDuration :=
          Round(
            (DT2Date(DateTime2) - DT2Date(DateTime1)) * (86400000 / CalendarMgt.TimeFactor(Workcenter."Unit of Measure Code")) +
            (DT2Time(DateTime2) - DT2Time(DateTime1)) / CalendarMgt.TimeFactor(Workcenter."Unit of Measure Code"),
            Workcenter."Calendar Rounding Precision");
        exit(TotalDuration);
    end;

    local procedure FindSendAheadEndingTime(var TempProdOrderRoutingLine: Record "Prod. Order Routing Line"; var SendAheadLotSize: Decimal): Boolean
    var
        Result: Boolean;
        xTotalLotSize: Decimal;
        xSendAheadLotSize: Decimal;
    begin
        xTotalLotSize := TotalLotSize;
        xSendAheadLotSize := SendAheadLotSize;
        if TempProdOrderRoutingLine.FindSet() then
            repeat
                TotalLotSize := xTotalLotSize;
                SendAheadLotSize := xSendAheadLotSize;

                Result := Result or GetSendAheadEndingTime(TempProdOrderRoutingLine, SendAheadLotSize);
            until TempProdOrderRoutingLine.Next() = 0
        else
            Result := GetSendAheadEndingTime(TempProdOrderRoutingLine, SendAheadLotSize);

        exit(Result);
    end;

    local procedure FindSendAheadStartingTime(var TempProdOrderRoutingLine: Record "Prod. Order Routing Line"; var SendAheadLotSize: Decimal): Boolean
    var
        Result: Boolean;
        xTotalLotSize: Decimal;
        xSendAheadLotSize: Decimal;
    begin
        xTotalLotSize := TotalLotSize;
        xSendAheadLotSize := SendAheadLotSize;
        if TempProdOrderRoutingLine.FindSet() then
            repeat
                TotalLotSize := xTotalLotSize;
                SendAheadLotSize := xSendAheadLotSize;
                Result := Result or GetSendAheadStartingTime(TempProdOrderRoutingLine, SendAheadLotSize);
            until TempProdOrderRoutingLine.Next() = 0
        else
            Result := GetSendAheadStartingTime(TempProdOrderRoutingLine, SendAheadLotSize);

        exit(Result);
    end;

    procedure ReturnNextCalendarEntry(var CalendarEntry2: Record "Calendar Entry"; OldCalendarEntry: Record "Calendar Entry"; Direction: Option Backward,Forward)
    begin
        CalendarEntry2 := OldCalendarEntry;
        CalendarEntry2.SetRange(Date, CalendarEntry2.Date);
        OnReturnNextCalendarEntryOnAfterSetCalendarEntryDateFilter(CalendarEntry);

        if Direction = Direction::Backward then begin
            CalendarEntry2.Find('-');           // rewind within the same day
            CalendarEntry2.SetRange(Date);
            if CalendarEntry2.Next(-1) = 0 then
                TestForError(Text001, Text002, CalendarEntry2.Date);

            if (CalendarEntry2.Date + 1) < OldCalendarEntry.Date then begin
                CalendarEntry2.Date := OldCalendarEntry.Date - 1;
                CreateCalendarEntry(CalendarEntry2);
            end;
        end else begin
            CalendarEntry2.Find('+');            // rewind within the same day
            CalendarEntry2.SetRange(Date);
            if CalendarEntry2.Next() = 0 then
                TestForError(Text003, Text004, CalendarEntry2.Date);

            if OldCalendarEntry.Date < (CalendarEntry2.Date - 1) then begin
                CalendarEntry2.Date := OldCalendarEntry.Date + 1;
                CreateCalendarEntry(CalendarEntry2);
            end;
        end;
    end;

    local procedure CreateCalendarEntry(var CalendarEntry2: Record "Calendar Entry")
    begin
        CalendarEntry2."Ending Time" := 000000T;
        CalendarEntry2."Starting Time" := 000000T;
        CalendarEntry2.Efficiency := 100;
        CalendarEntry2."Absence Capacity" := 0;
        CalendarEntry2."Capacity (Total)" := 0;
        CalendarEntry2."Capacity (Effective)" := CalendarEntry2."Capacity (Total)";
        CalendarEntry2."Starting Date-Time" := CreateDateTime(CalendarEntry2.Date, CalendarEntry2."Starting Time");
        CalendarEntry2."Ending Date-Time" := CalendarEntry2."Starting Date-Time" + 86400000;
        if not CalendarEntry2.Get(CalendarEntry2."Capacity Type", CalendarEntry2."No.", CalendarEntry2.Date, CalendarEntry2."Starting Time", CalendarEntry2."Ending Time", CalendarEntry2."Work Shift Code") then
            CalendarEntry2.Insert();
    end;

    local procedure GetCurrentWorkCenterTimeFactorAndRounding(WorkCenterNo: Code[20])
    var
        WorkCenter: Record "Work Center";
    begin
        if CurrentWorkCenterNo = WorkCenterNo then
            exit;

        WorkCenter.Get(WorkCenterNo);
        CurrentTimeFactor := CalendarMgt.TimeFactor(WorkCenter."Unit of Measure Code");
        CurrentRounding := WorkCenter."Calendar Rounding Precision";
    end;

    local procedure CalcCapConResWorkCenterLoadBase(CapacityConstrainedResource: Record "Capacity Constrained Resource"; DateFilter: Date; var CapEffectiveBase: Decimal; var LoadBase: Decimal)
    begin
        CapEffectiveBase := 0;
        LoadBase := 0;

        CapacityConstrainedResource.SetRange("Date Filter", DateFilter);
        CapacityConstrainedResource.CalcFields("Capacity (Effective)", "Work Center Load Qty.");
        if CapacityConstrainedResource."Capacity (Effective)" <> 0 then begin
            CapEffectiveBase := Round(CapacityConstrainedResource."Capacity (Effective)" * CurrentTimeFactor, CurrentRounding);
            LoadBase := Round(CapacityConstrainedResource."Work Center Load Qty." * CurrentTimeFactor, CurrentRounding);
        end;
    end;

    local procedure CalcCapConResProdOrderNeedBase(CapacityConstrainedResource: Record "Capacity Constrained Resource"; DateFilter: Date; var CapEffectiveBase: Decimal; var LoadBase: Decimal)
    begin
        CapEffectiveBase := 0;
        LoadBase := 0;

        CapacityConstrainedResource.SetRange("Date Filter", DateFilter);
        CapacityConstrainedResource.CalcFields("Capacity (Effective)", "Prod. Order Need Qty.");
        if CapacityConstrainedResource."Capacity (Effective)" <> 0 then begin
            CapEffectiveBase := Round(CapacityConstrainedResource."Capacity (Effective)" * CurrentTimeFactor, CurrentRounding);
            LoadBase := Round(CapacityConstrainedResource."Prod. Order Need Qty." * CurrentTimeFactor, CurrentRounding);
        end;
    end;

    procedure CalcAvailCapBaseAndDampTime(CapacityConstrainedResource: Record "Capacity Constrained Resource"; var AvailCap: Decimal; var DampTime: Decimal; CapEffectiveBase: Decimal; LoadBase: Decimal; TimeFactor: Decimal; Rounding: Decimal)
    var
        AvailCapBase: Decimal;
        AvailCapBaseMax: Decimal;
        LoadPct: Decimal;
        DampenerPct: Decimal;
        CriticalLoadPct: Decimal;
    begin
        AvailCap := 0;
        DampTime := 0;

        if CapEffectiveBase = 0 then
            exit;

        CriticalLoadPct := CapacityConstrainedResource."Critical Load %";
        AvailCapBaseMax := Round(CapEffectiveBase * CriticalLoadPct / 100, Rounding);
        AvailCapBase := max(0, AvailCapBaseMax - LoadBase);
        AvailCap := Round(AvailCapBase / TimeFactor, Rounding);

        LoadPct := Round(LoadBase / CapEffectiveBase * 100, Rounding);
        DampenerPct := CapacityConstrainedResource."Dampener (% of Total Capacity)";
        DampTime :=
          Round(CapEffectiveBase / TimeFactor * Min(DampenerPct, CriticalLoadPct + DampenerPct - LoadPct) / 100, Rounding);
        DampTime := Round(Max(0, DampTime), 1);
    end;

    procedure CalcAvailQtyBase(var CalendarEntry: Record "Calendar Entry"; ProdStartDate: Date; ProdStartTime: Time; TimeType: Enum "Routing Time Type"; ConCurrCap: Decimal; IsForward: Boolean; TimeFactor: Decimal; Rounding: Decimal) AvQtyBase: Decimal
    var
        CalendarStartTime: Time;
        CalendarEndTime: Time;
        CalcFactor: Integer;
        ModifyCalendar: Boolean;
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeCalcAvailQtyBase(CalendarEntry, TimeType, AvQtyBase, IsHandled);
        if IsHandled then
            exit(AvQtyBase);

        if IsForward then begin
            CalendarStartTime := CalendarEntry."Starting Time";
            CalendarEndTime := CalendarEntry."Ending Time";
            CalcFactor := -1
        end else begin
            CalendarStartTime := CalendarEntry."Ending Time";
            CalendarEndTime := CalendarEntry."Starting Time";
            CalcFactor := 1;
        end;
        ModifyCalendar := false;

        if (((CalendarStartTime < ProdStartTime) and IsForward) or
            ((CalendarStartTime > ProdStartTime) and not IsForward)) and
           (CalendarEntry.Date = ProdStartDate)
        then begin
            case TimeType of
                TimeType::"Setup Time",
              TimeType::"Run Time":
                    AvQtyBase :=
                      Round(
                        Abs(CalendarEndTime - ProdStartTime) *
                        CalendarEntry.Efficiency / 100 * ConCurrCap, Rounding);
                TimeType::"Move Time",
              TimeType::"Queue Time":
                    AvQtyBase :=
                      Round(
                        Abs(CalendarEndTime - ProdStartTime) *
                        ConCurrCap, Rounding);
                TimeType::"Wait Time":
                    begin
                        AvQtyBase := CalcAvailQtyBaseForWaitTime(ProdStartTime, ProdStartDate, CalendarEntry.Date, CalcFactor, IsForward);
                        AvQtyBase := Round(AvQtyBase * ConCurrCap, Rounding);
                    end;
            end;
            ModifyCalendar := true;
        end else
            if (CalendarEntry.Capacity = CalendarEntry."Absence Capacity") and
               (TimeType <> TimeType::"Wait Time")
            then
                AvQtyBase := 0
            else
                case TimeType of
                    TimeType::"Setup Time",
                  TimeType::"Run Time":
                        AvQtyBase :=
                          Round(
                            TimeFactor * CalendarEntry."Capacity (Effective)" /
                            (CalendarEntry.Capacity - CalendarEntry."Absence Capacity") * ConCurrCap,
                            Rounding);
                    TimeType::"Move Time",
                  TimeType::"Queue Time":
                        AvQtyBase :=
                          Round(
                            TimeFactor * CalendarEntry."Capacity (Total)" /
                            (CalendarEntry.Capacity - CalendarEntry."Absence Capacity") * ConCurrCap,
                            Rounding);
                    TimeType::"Wait Time":
                        begin
                            AvQtyBase := CalcAvailQtyBaseForWaitTime(ProdStartTime, ProdStartDate, CalendarEntry.Date, CalcFactor, IsForward);
                            AvQtyBase := Round(AvQtyBase * ConCurrCap, Rounding);
                            ModifyCalendar := true;
                        end;
                end;

        if ModifyCalendar then
            if IsForward then
                CalendarEntry."Starting Time" := ProdStartTime
            else
                CalendarEntry."Ending Time" := ProdStartTime;
    end;

    local procedure CalcAvailQtyBaseForWaitTime(ProdStartTime: Time; ProdStartDate: Date; CalendarEntryDate: Date; CalcFactor: Integer; IsForward: Boolean): Decimal
    begin
        if (ProdStartTime = 000000T) and ((CalendarEntryDate <> ProdStartDate) or IsForward) then
            exit(86400000);
        exit((86400000 + (ProdStartTime - 000000T) * CalcFactor) mod 86400000);
    end;

    local procedure CalcActuallyPostedCapacityTime(ProdOrderRoutingLine: Record "Prod. Order Routing Line"; TimeType: Enum "Routing Time Type"): Decimal
    var
        CapacityLedgerEntry: Record "Capacity Ledger Entry";
    begin
        if not (ProdOrderRoutingLine.Status in
                [ProdOrderRoutingLine.Status::Released, ProdOrderRoutingLine.Status::Finished])
        then
            exit(0);

        if TimeType in [TimeType::"Setup Time", TimeType::"Run Time"] then begin
            CapacityLedgerEntry.SetRange("Order Type", CapacityLedgerEntry."Order Type"::Production);
            CapacityLedgerEntry.SetRange("Order No.", ProdOrderRoutingLine."Prod. Order No.");
            CapacityLedgerEntry.SetRange("Routing No.", ProdOrderRoutingLine."Routing No.");
            CapacityLedgerEntry.SetRange("Routing Reference No.", ProdOrderRoutingLine."Routing Reference No.");
            CapacityLedgerEntry.SetRange("Operation No.", ProdOrderRoutingLine."Operation No.");
            if TimeType = TimeType::"Setup Time" then begin
                CapacityLedgerEntry.CalcSums("Setup Time");
                exit(CapacityLedgerEntry."Setup Time");
            end;
            if TimeType = TimeType::"Run Time" then begin
                CapacityLedgerEntry.CalcSums("Run Time");
                exit(CapacityLedgerEntry."Run Time");
            end;
        end;
        exit(0);
    end;

    local procedure CalcDistributedCapacityNeedForOperation(ProdOrderRoutingLine: Record "Prod. Order Routing Line"; TimeType: Enum "Routing Time Type"): Decimal
    var
        ProdOrderCapacityNeed: Record "Prod. Order Capacity Need";
    begin
        ProdOrderCapacityNeed.SetRange(Status, ProdOrderRoutingLine.Status);
        ProdOrderCapacityNeed.SetRange("Prod. Order No.", ProdOrderRoutingLine."Prod. Order No.");
        ProdOrderCapacityNeed.SetRange("Requested Only", false);
        ProdOrderCapacityNeed.SetRange("Routing No.", ProdOrderRoutingLine."Routing No.");
        ProdOrderCapacityNeed.SetRange("Routing Reference No.", ProdOrderRoutingLine."Routing Reference No.");
        ProdOrderCapacityNeed.SetRange("Operation No.", ProdOrderRoutingLine."Operation No.");
        ProdOrderCapacityNeed.SetRange("Time Type", TimeType);
        ProdOrderCapacityNeed.CalcSums("Needed Time", "Allocated Time");
        exit(ProdOrderCapacityNeed."Needed Time" - ProdOrderCapacityNeed."Allocated Time");
    end;

    local procedure SetMaxDateTime(var ResultingDate: Date; var ResultingTime: Time; DateToCompare: Date; TimeToCompare: Time)
    begin
        if ((ResultingDate = DateToCompare) and (ResultingTime >= TimeToCompare)) or
           (ResultingDate > DateToCompare)
        then
            exit;
        ResultingDate := DateToCompare;
        ResultingTime := TimeToCompare;
    end;

    local procedure SetMinDateTime(var ResultingDate: Date; var ResultingTime: Time; DateToCompare: Date; TimeToCompare: Time)
    begin
        if ((ResultingDate = DateToCompare) and (ResultingTime <= TimeToCompare)) or
           (ResultingDate < DateToCompare)
        then
            exit;
        ResultingDate := DateToCompare;
        ResultingTime := TimeToCompare
    end;

    local procedure SetRoutingLineFilters(ProdOrderRoutingLine: Record "Prod. Order Routing Line"; var ProdOrderRoutingLine2: Record "Prod. Order Routing Line")
    begin
        ProdOrderRoutingLine2.SetRange(Status, ProdOrderRoutingLine.Status);
        ProdOrderRoutingLine2.SetRange("Prod. Order No.", ProdOrderRoutingLine."Prod. Order No.");
        ProdOrderRoutingLine2.SetRange("Routing Reference No.", ProdOrderRoutingLine."Routing Reference No.");
        ProdOrderRoutingLine2.SetRange("Routing No.", ProdOrderRoutingLine."Routing No.");
        ProdOrderRoutingLine2.SetFilter("Routing Status", '<>%1', ProdOrderRoutingLine2."Routing Status"::Finished);
    end;

    local procedure FilterCalendarEntryBeforeOrOnDateTime(CapacityType: Enum "Capacity Type"; WorkMachineCenterNo: Code[20]; DateValue: Date; TimeValue: Time)
    begin
        CalendarEntry.SetCapacityFilters(CapacityType, WorkMachineCenterNo);
        CalendarEntry.SetRange("Starting Date-Time", 0DT, CreateDateTime(DateValue, TimeValue));
        CalendarEntry.SetRange("Ending Date-Time", 0DT, CreateDateTime(DateValue + 1, TimeValue));
    end;

    procedure ReplanRoutingLine(var ProdOrderRoutingLine2: Record "Prod. Order Routing Line"; Direction: Option Forward,Backward; CalcStartEndDate: Boolean)
    var
        ProdOrderCapacityNeed: Record "Prod. Order Capacity Need";
        MfgCostCalcMgt: Codeunit "Mfg. Cost Calculation Mgt.";
        ActualOperOutput: Decimal;
        TotalQtyPerOperation: Decimal;
        TotalCapacityPerOperation: Decimal;
    begin
        MfgSetup.Get();

        ProdOrderRoutingLine := ProdOrderRoutingLine2;

        WaitTimeOnly :=
          (ProdOrderRoutingLine."Setup Time" = 0) and (ProdOrderRoutingLine."Run Time" = 0) and
          (ProdOrderRoutingLine."Move Time" = 0);

        if ProdOrderRoutingLine."Ending Time" = 0T then
            ProdOrderRoutingLine."Ending Time" := 000000T;

        if ProdOrderRoutingLine."Starting Time" = 0T then
            ProdOrderRoutingLine."Starting Time" := 000000T;

        ProdOrderRoutingLine."Expected Operation Cost Amt." := 0;
        ProdOrderRoutingLine."Expected Capacity Ovhd. Cost" := 0;
        ProdOrderRoutingLine."Expected Capacity Need" := 0;

        ProdOrderCapacityNeed.Reset();
        ProdOrderCapacityNeed.SetLoadFields(Status, "Prod. Order No.", "Requested Only", "Routing No.", "Routing Reference No.", "Operation No.");
        ProdOrderCapacityNeed.SetRange(Status, ProdOrderRoutingLine.Status);
        ProdOrderCapacityNeed.SetRange("Prod. Order No.", ProdOrderRoutingLine."Prod. Order No.");
        ProdOrderCapacityNeed.SetRange("Requested Only", false);
        ProdOrderCapacityNeed.SetRange("Routing No.", ProdOrderRoutingLine."Routing No.");
        ProdOrderCapacityNeed.SetRange("Routing Reference No.", ProdOrderRoutingLine."Routing Reference No.");
        ProdOrderCapacityNeed.SetRange("Operation No.", ProdOrderRoutingLine."Operation No.");
        ProdOrderCapacityNeed.DeleteAll();

        NextCapNeedLineNo := 1;

        ProdOrderRoutingLine.TestField("Work Center No.");

        CurrentWorkCenterNo := '';
        Workcenter.Get(ProdOrderRoutingLine."Work Center No.");
        if ProdOrderRoutingLine.Type = ProdOrderRoutingLine.Type::"Machine Center" then begin
            MachineCenter.Get(ProdOrderRoutingLine."No.");
            Workcenter."Queue Time" := MachineCenter."Queue Time";
            Workcenter."Queue Time Unit of Meas. Code" := MachineCenter."Queue Time Unit of Meas. Code";
        end;
        if not CalcStartEndDate then
            Clear(Workcenter."Queue Time");
        ProdOrder.Get(ProdOrderRoutingLine.Status, ProdOrderRoutingLine."Prod. Order No.");

        ProdOrderQty := 0;
        TotalLotSize := 0;
        ProdOrderLine.SetRange(Status, ProdOrderRoutingLine.Status);
        ProdOrderLine.SetRange("Prod. Order No.", ProdOrderRoutingLine."Prod. Order No.");
        ProdOrderLine.SetRange("Routing Reference No.", ProdOrderRoutingLine."Routing Reference No.");
        ProdOrderLine.SetRange("Routing No.", ProdOrderRoutingLine."Routing No.");
        ProdOrderLine.SetLoadFields("Quantity (Base)", "Scrap %", "Prod. Order No.", "Line No.", Status, "Routing No.", "Routing Version Code", "Ending Date", "Ending Time");
        if ProdOrderLine.FindFirst() then
            ProdOrderLine.CalcSums("Quantity (Base)", "Scrap %");
        ActualOperOutput := MfgCostCalcMgt.CalcActOutputQtyBase(ProdOrderLine, ProdOrderRoutingLine);
        ProdOrderQty := ProdOrderLine."Quantity (Base)" - ActualOperOutput;
        if ProdOrderQty < 0 then
            ProdOrderQty := 0;

        MaxLotSize :=
          ProdOrderQty *
          (1 + ProdOrderRoutingLine."Scrap Factor % (Accumulated)") *
          (1 + ProdOrderLine."Scrap %" / 100) +
          ProdOrderRoutingLine."Fixed Scrap Qty. (Accum.)";

        ProdOrderRoutingLine."Input Quantity" := MaxLotSize;

        if ActualOperOutput > 0 then
            TotalQtyPerOperation :=
              ProdOrderLine."Quantity (Base)" *
              (1 + ProdOrderRoutingLine."Scrap Factor % (Accumulated)") *
              (1 + ProdOrderLine."Scrap %" / 100) +
              ProdOrderRoutingLine."Fixed Scrap Qty. (Accum.)"
        else
            TotalQtyPerOperation := MaxLotSize;

        TotalCapacityPerOperation :=
          Round(
            TotalQtyPerOperation *
            ProdOrderRoutingLine.RunTimePer() *
            CalendarMgt.QtyperTimeUnitofMeasure(
              ProdOrderRoutingLine."Work Center No.", ProdOrderRoutingLine."Run Time Unit of Meas. Code"),
            UOMMgt.QtyRndPrecision());

        if MfgSetup."Cost Incl. Setup" then
            CalcCostInclSetup(ProdOrderRoutingLine, TotalCapacityPerOperation);

        CalcExpectedCost(ProdOrderRoutingLine, TotalQtyPerOperation, TotalCapacityPerOperation);

        if ProdOrderRoutingLine."Schedule Manually" then
            CalculateRoutingLineFixed()
        else
            if Direction = Direction::Backward then
                CalcRoutingLineBack(CalcStartEndDate)
            else
                CalcRoutingLineForward(CalcStartEndDate);

        ProdOrderRoutingLine2 := ProdOrderRoutingLine;
    end;

    [IntegrationEvent(false, false)]
    local procedure OnAfterCalcCostInclSetup(ProdOrderRoutingLine: Record "Prod. Order Routing Line"; var TotalCapacityPerOperation: Decimal)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnAfterCalculateRoutingLine(var ProdOrderRoutingLine: Record "Prod. Order Routing Line"; Direction: Enum "Transfer Direction")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnAfterInitProdOrderCapNeed(ProdOrder: Record "Production Order"; var ProdOrderRoutingLine: Record "Prod. Order Routing Line"; var ProdOrderCapNeed: Record "Prod. Order Capacity Need"; var NeedQty: Decimal; TimeType: Enum "Routing Time Type"; var ActuallyPostedTime: Decimal; var DistributedCapNeed: Decimal)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeAdjustEndingTime(var IsHandled: Boolean; CalendarEntry: Record "Calendar Entry"; var EndingTime: Time)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeAdjustStartingTime(var IsHandled: Boolean; CalendarEntry: Record "Calendar Entry"; var StartingTime: Time)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeCalcCostInclSetup(ProdOrderRoutingLine: Record "Prod. Order Routing Line"; var TotalCapacityPerOperation: Decimal)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeCalcExpectedCost(var ProdOrderRoutingLine: Record "Prod. Order Routing Line"; var MaxLotSize: Decimal; var TotalQtyPerOperation: Decimal; var ActualOperOutput: Decimal; var ExpectedOperOutput: Decimal; var TotalScrap: Decimal)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeGetConstrainedSetup(ProdOrderRoutingLine: Record "Prod. Order Routing Line"; var ConstrainedCapacity: Record "Capacity Constrained Resource"; var ResourceIsConstrained: Boolean; var ParentWorkCenter: Record "Capacity Constrained Resource"; var ParentIsConstrained: Boolean; var IsHandled: Boolean)
    begin
    end;

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

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

    [IntegrationEvent(false, false)]
    local procedure OnAfterCreateCapNeeded(ProdOrderRoutingLine: Record "Prod. Order Routing Line"; NeedDate: Date; NeedQty: Decimal; RemainNeedQty: Decimal; CalendarEntry: Record "Calendar Entry"; StartingTime: Time; EndingTime: Time; TimeType: Integer; var NextCapNeedLineNo: Integer; ConCurrCap: Decimal; LotSize: Decimal; FirstInBatch: Boolean; Direction: Integer);
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeCalcLoadBackAvailQtyBase(var ProdOrderRoutingLine: Record "Prod. Order Routing Line"; TimeType: Enum "Routing Time Type"; CurrentTimeFactor: Decimal; CurrentRounding: Decimal; var AvQtyBase: Decimal; var IsHandled: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeCalcLoadForwardAvailQtyBase(var ProdOrderRoutingLine: Record "Prod. Order Routing Line"; TimeType: Enum "Routing Time Type"; CurrentTimeFactor: Decimal; CurrentRounding: Decimal; var AvQtyBase: Decimal; var IsHandled: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeCalcRoutingLineBack(var ProdOrderRoutingLine: Record "Prod. Order Routing Line"; var CalculateEndDate: Boolean; var IsHandled: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeLoadCapBack(var ProdOrderRoutingLine: Record "Prod. Order Routing Line"; TimeType: Enum "Routing Time Type"; RemainNeedQty: Decimal; var ProdEndingDate: Date; var ProdEndingTime: Time)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeLoadCapForward(var ProdOrderRoutingLine: Record "Prod. Order Routing Line"; CapType: Enum "Capacity Type"; CapNo: Code[20]; TimeType: Enum "Routing Time Type"; var ProdStartingDate: Date; var ProdStartingTime: Time; var IsHandled: Boolean; RemainNeedQty: Decimal)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeProdOrderCapNeedInsert(var ProdOrderCapNeed: Record "Prod. Order Capacity Need"; ProdOrderRoutingLine: Record "Prod. Order Routing Line"; ProdOrder: Record "Production Order"; var CalendarEntry: Record "Calendar Entry")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeSetLotSizesToMax(var SendAheadLotSize: Decimal; var TotalLotSize: Decimal; MaxLotSize: Decimal; ProdOrderRoutingLine: Record "Prod. Order Routing Line"; var IsHandled: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeScheduleRoutingLine(var ProdOrderRoutingLine: Record "Prod. Order Routing Line"; var CalcStartEndDate: Boolean; var IsHandled: Boolean; var Direction: Option Forward,Backward)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeCalcMove(var ProdOrderRoutingLine: Record "Prod. Order Routing Line"; WorkCenter: Record "Work Center"; var ProdEndingDate: Date; var ProdEndingTime: Time; var UpdateDates: Boolean; var IsHandled: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeCalcRoutingLineForward(var ProdOrderRoutingLine: Record "Prod. Order Routing Line"; var CalculateStartDate: Boolean; var IsHandled: Boolean; var TempProdOrderRoutingLine: Record "Prod. Order Routing Line" temporary; var SendAheadLotSize: Decimal; MaxLotSize: Decimal; var TotalLotSize: Decimal; var RemainNeedQty: Decimal; var UpdateDates: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeCalcWaitBack(var ProdOrderRoutingLine: Record "Prod. Order Routing Line"; WorkCenter: Record "Work Center"; var ProdEndingDate: Date; var ProdEndingTime: Time; var UpdateDates: Boolean; var IsHandled: Boolean)
    begin
    end;

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

    [IntegrationEvent(false, false)]
    local procedure OnCalculateRoutingLineOnBeforeCalcExpectedOperOutput(var ProdOrderLine: Record "Prod. Order Line"; var ExpectedOperOutput: Decimal; var IsHandled: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnCalculateRoutingLineOnBeforeProdOrderCapNeedReset(var ProdOrderRoutingLine: Record "Prod. Order Routing Line"; ProdOrderRoutingLine2: Record "Prod. Order Routing Line")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnCreateLoadBackOnAfterCalcRelevantEfficiency(ProdOrderRoutingLine: Record "Prod. Order Routing Line"; TimeType: Enum "Routing Time Type"; var RelevantEfficiency: Decimal)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnCreateLoadBackOnBeforeEndStopLoop(ProdOrderRoutingLine: Record "Prod. Order Routing Line"; TimeType: Enum "Routing Time Type"; var StopLoop: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnCreateLoadForwardOnBeforeCalcEndingTime(var EndingTime: Time; CalendarEntry: Record "Calendar Entry"; AvQtyBase: Decimal; RelevantEfficiency: Decimal; ConCurrCap: Decimal; var IsHandled: Boolean; ProdOrderRoutingLine: Record "Prod. Order Routing Line"; TimeType: Enum "Routing Time Type")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnCreateLoadForwardOnBeforeEndStopLoop(ProdOrderRoutingLine: Record "Prod. Order Routing Line"; var TimeType: Enum "Routing Time Type"; var StopLoop: Boolean; FirstEntry: Boolean; WaitTimeOnly: Boolean; ProdStartingDate: Date; ProdStartingTime: Time; ProdEndingDate: Date; ProdEndingTime: Time; var RemainNeedQty: Decimal; var RemainNeedQtyBase: Decimal; var Write: Boolean; var LoadFactor: Decimal; var CurrentWorkCenterNo: Code[20]; var CurrentTimeFactor: Decimal; var CurrentRounding: Decimal; var CalendarEntry: Record "Calendar Entry")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnCreateLoadForwardOnBeforeCheckWrite(ProdOrderRoutingLine: Record "Prod. Order Routing Line"; TimeType: Enum "Routing Time Type"; var RelevantEfficiency: Decimal; var RemainNeedQtyBase: Decimal; var RemainNeedQty: Decimal; CurrentRounding: Decimal; Write: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnCreateLoadBackOnBeforeCheckWrite(ProdOrderRoutingLine: Record "Prod. Order Routing Line"; TimeType: Enum "Routing Time Type"; var RelevantEfficiency: Decimal; var RemainNeedQtyBase: Decimal; var RemainNeedQty: Decimal; CurrentRounding: Decimal; Write: Boolean; var StartingTime: Time; AvQtyBase: Decimal; var CalendarEntry: Record "Calendar Entry")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnCalculateRoutingLineOnAfterCalcCostInclSetup(ProdOrderRoutingLine: Record "Prod. Order Routing Line"; var TotalCapacityPerOperation: Decimal; var TotalQtyPerOperation: Decimal)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnCalculateRoutingLineOnBeforeCalcCostInclSetup(ProdOrderRoutingLine: Record "Prod. Order Routing Line"; var TotalCapacityPerOperation: Decimal; var TotalQtyPerOperation: Decimal)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnCalcRoutingLineBackOnBeforeGetQueueTime(var ProdOrderRoutingLine: Record "Prod. Order Routing Line"; var ProdOrderRoutingLine2: Record "Prod. Order Routing Line"; var ProdOrderRoutingLine3: Record "Prod. Order Routing Line");
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnCalculateRoutingLineBackOnAfterCalcRemainNeedQtyForLotSize(ProdOrderRoutingLine: Record "Prod. Order Routing Line"; var RemainNeedQty: Decimal)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnCalcRoutingLineBackOnAfterCalcShouldCalcNextOperation(var ProdOrderRoutingLine: Record "Prod. Order Routing Line"; var ShouldCalcNextOperation: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnCalculateRoutingLineForwardOnAfterCalcRemainNeedQtyForLotSize(ProdOrderRoutingLine: Record "Prod. Order Routing Line"; var RemainNeedQty: Decimal)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnCalculateRoutingLineFixedOnBeforeCalcRoutingLineBack(ProdOrderRoutingLine: Record "Prod. Order Routing Line"; var CalcEndDate: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnCalculateRoutingLineFixedOnBeforeCalcRoutingLineForward(ProdOrderRoutingLine: Record "Prod. Order Routing Line"; var CalcStartDate: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnFinitelyLoadCapBackOnAfterCalcShouldProcessLastProdOrderCapNeed(var ProdOrderRoutingLine: Record "Prod. Order Routing Line"; AvailCap: Decimal; CalendarEntry: Record "Calendar Entry"; ProdEndingTime: Time; ProdEndingDate: Date; TimeType: Enum "Routing Time Type"; var FirstInBatch: Boolean; var FirstEntry: Boolean; var UpdateDates: Boolean; var RemainNeedQty: Decimal; var ProdOrderCapNeed: Record "Prod. Order Capacity Need"; ProdOrder: Record "Production Order"; var NextCapNeedLineNo: Integer; Workcenter: Record "Work Center"; LotSize: Decimal; var ShouldProcessLastProdOrderCapNeed: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnCreateLoadForwardOnAfterScheduleProdOrderRoutingLineManually(var ProdOrderRoutingLine: Record "Prod. Order Routing Line"; RoutingTimeType: Enum "Routing Time Type"; var RunStartingDateTime: DateTime; var ProdStartingDate: Date; var ProdStartingTime: Time; var RemainNeedQtyBase: Decimal)
    begin
    end;

    [IntegrationEvent(false, false)]
    procedure OnBeforeCalculateRoutingLine(var ProdOrderRoutingLine: Record "Prod. Order Routing Line"; Direction: Option Forward,Backward; CalcStartEndDate: Boolean; var IsHandled: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnLoadCapForwardOnScheduleManuallyOnBeforeCheckDateTimes(var ProdOrderRoutingLine: Record "Prod. Order Routing Line"; CapType: Enum "Capacity Type"; CapNo: Code[20]; TimeType: Enum "Routing Time Type"; var ProdStartingDate: Date; var ProdStartingTime: Time; RemainNeedQty: Decimal; var RunStartingDateTime: DateTime; var RunEndingDateTime: DateTime)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeUpdateTimesBack(CalendarEntry: Record "Calendar Entry"; ProdOrderRoutingLine: Record "Prod. Order Routing Line"; var AvailTime: Decimal; var AvailCap: Decimal; var TimetoProgram: Decimal; var StartTime: Time; EndTime: Time; ConCurrCap: Decimal; Workcenter: Record "Work Center"; var RemainNeedQty: Decimal; var IsHandled: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeUpdateTimesForward(CalendarEntry: Record "Calendar Entry"; ProdOrderRoutingLine: Record "Prod. Order Routing Line"; var AvailTime: Decimal; var AvailCap: Decimal; var TimetoProgram: Decimal; var StartTime: Time; EndTime: Time; ConCurrCap: Decimal; Workcenter: Record "Work Center"; var RemainNeedQty: Decimal; var IsHandled: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnReturnNextCalendarEntryOnAfterSetCalendarEntryDateFilter(var CalendarEntry: Record "Calendar Entry")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnFinitelyLoadCapBackOnAfterSetCalendarEntryFilters(var CalendarEntry: Record "Calendar Entry")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnLoadCapBackOnAfterSetCalendarEntryFilters(var CalendarEntry: Record "Calendar Entry")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnCalculateRoutingLineOnAfterCalcMaxLotSize(ProdOrderQty: Decimal; var ProdOrderRoutingLine: Record "Prod. Order Routing Line"; TotalScrap: Decimal; var MaxLotSize: Decimal)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeInitProdOrderCapNeed(ProductionOrder: Record "Production Order"; var ProdOrderRoutingLine: Record "Prod. Order Routing Line"; var ProdOrderCapacityNeed: Record "Prod. Order Capacity Need"; RoutingTimeType: Enum "Routing Time Type"; NeedDate: Date; StartingTime: Time; EndingTime: Time; var NeedQty: Decimal; LotSize: Decimal)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnLoadCapBackOnAfterCreateLoadBack(CapacityType: Enum "Capacity Type"; CapacityNo: Code[20]; RoutingTimeType: Enum "Routing Time Type"; Write: Boolean);
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnCalcRoutingLineForwardOnBeforeLoadCapForward(ProdOrderRoutingLine: Record "Prod. Order Routing Line"; ProdOrderRoutingLine3: Record "Prod. Order Routing Line");
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnCalcRoutingLineForwardOnBeforeLoadCapForward2(ProdOrderRoutingLine: Record "Prod. Order Routing Line"; ProdOrderRoutingLine3: Record "Prod. Order Routing Line");
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnLoadCapForwardOnBeforeCreateLoadForward(var RemainNeedQty: Decimal; CapacityType: Enum "Capacity Type"; CapacityNo: Code[20]; RoutingTimeType: Enum "Routing Time Type"; Write: Boolean; LoadFactor: Decimal);
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnLoadCapForwardOnAfterCreateLoadForward(var RemainNeedQty: Decimal; CapacityType: Enum "Capacity Type"; CapacityNo: Code[20]; RoutingTimeType: Enum "Routing Time Type"; Write: Boolean; LoadFactor: Decimal);
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnGetSendAheadEndingTimeOnAfterCalcTimes(ProdOrderRoutingLine: Record "Prod. Order Routing Line"; WorkCenter: Record "Work Center"; var SetupTime: Decimal; var RunTime: Decimal; var WaitTime: Decimal; var MoveTime: Decimal)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnGetSendAheadEndingTimeOnAfterCalcSetupAndRunTime(ProdOrderRoutingLine: Record "Prod. Order Routing Line"; WorkCenter: Record "Work Center"; var SetupTime: Decimal; var RunTime: Decimal)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnCalcRoutingLineBackOnAfterCalcRemainNeedQty(ProdOrderRoutingLine: Record "Prod. Order Routing Line"; WorkCenter: Record "Work Center"; var RemainNeedQty: Decimal)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnFinitelyLoadCapBackOnAfterCalcSetupTime(ProdOrderRoutingLine: Record "Prod. Order Routing Line"; WorkCenter: Record "Work Center"; var SetupTime: Decimal)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnFinitelyLoadCapBackOnAfterCalcAvailTimeFromLastProdOrderCapNeed(var AvailTime: Decimal; EndTime: Time; AvailCap: Decimal; LastProdOrderCapacityNeed: Record "Prod. Order Capacity Need")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnFinitelyLoadCapBackOnAfterCalcAvailTimeFromCalendarEntry(var AvailTime: Decimal; EndTime: Time; AvailCap: Decimal; CalendarEntry: Record "Calendar Entry")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnCalcRoutingLineForwardOnAfterCalcRemainNeedQty(ProdOrderRoutingLine: Record "Prod. Order Routing Line"; WorkCenter: Record "Work Center"; var RemainNeedQty: Decimal)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeCalcAvailQtyBase(CalendarEntry: Record "Calendar Entry"; RoutingTimeType: Enum "Routing Time Type"; var AvQtyBase: Decimal; var IsHandled: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeCreateLoadBack(var RemainNeedQty: Decimal; CalendarEntry: Record "Calendar Entry"; var TimeType: Enum "Routing Time Type"; var Write: Boolean; FirstEntry: Boolean; WaitTimeOnly: Boolean; var CurrentWorkCenterNo: Code[20]; ProdStartingDate: Date; ProdStartingTime: Time; ProdEndingDate: Date; ProdEndingTime: Time; var IsHandled: Boolean; var RemainNeedQtyBase: Decimal; var CurrentTimeFactor: Decimal)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeCreateLoadForward(var RemainNeedQty: Decimal; CalendarEntry: Record "Calendar Entry"; var TimeType: Enum "Routing Time Type"; var Write: Boolean; var LoadFactor: Decimal; FirstEntry: Boolean; WaitTimeOnly: Boolean; var CurrentWorkCenterNo: Code[20]; ProdStartingDate: Date; ProdStartingTime: Time; ProdEndingDate: Date; ProdEndingTime: Time; var IsHandled: Boolean; var RemainNeedQtyBase: Decimal; var CurrentTimeFactor: Decimal)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnInitProdOrderCapNeedOnBeforeUpdateAllocatedTimeAndExpectedCapacityNeed(var ProdOrderCapacityNeed: Record "Prod. Order Capacity Need"; var ShouldUpdateAllocatedTimeAndExpectedCapacityNeed: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnUpdateTimesBackOnBeforeUpdateAvailCap(var ProdOrderRoutingLine: Record "Prod. Order Routing Line"; var ShouldUpdateAvailCap: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnUpdateTimesForwardOnBeforeUpdateAvailCap(var ProdOrderRoutingLine: Record "Prod. Order Routing Line"; var ShouldUpdateAvailCap: Boolean)
    begin
    end;
}