Codeunit 5404 Lead-Time Management

App
Base Application
Namespace
Microsoft.Inventory
Versions
17-28

Procedures, 15Events, 17Obsolete, 10

Versions171819202122232425262728

Source29

Source in 29

src/Layers/W1/BaseApp/Inventory/LeadTimeManagement.Codeunit.al375 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.Inventory;

using Microsoft.Foundation.Calendar;
using Microsoft.Inventory.Item;
using Microsoft.Inventory.Item.Catalog;
using Microsoft.Inventory.Location;
using Microsoft.Inventory.Planning;
using Microsoft.Inventory.Requisition;
using Microsoft.Inventory.Setup;
using Microsoft.Inventory.Transfer;

codeunit 5404 "Lead-Time Management"
{

    trigger OnRun()
    begin
    end;

    var
        InvtSetup: Record "Inventory Setup";
        Location: Record Location;
        Item: Record Item;
        TempSKU: Record "Stockkeeping Unit" temporary;
        CalChange: Record "Customized Calendar Change";
        LeadTimeCalcNegativeErr: Label 'The amount of time to replenish the item must not be negative.';
        GetPlanningParameters: Codeunit "Planning-Get Parameters";
        CalendarMgmt: Codeunit "Calendar Management";
        ManualScheduling: Boolean;

    procedure PurchaseLeadTime(ItemNo: Code[20]; LocationCode: Code[10]; VariantCode: Code[10]; VendorNo: Code[20]) Result: Code[20]
    var
        ItemVend: Record "Item Vendor";
    begin
        // Returns the leadtime in a date formula

        GetItem(ItemNo);
        ItemVend."Vendor No." := VendorNo;
        ItemVend."Variant Code" := VariantCode;
        Item.FindItemVend(ItemVend, LocationCode);
        Result := Format(ItemVend."Lead Time Calculation");

        OnAfterPurchaseLeadTime(ItemVend, Result);
    end;

    procedure ManufacturingLeadTime(ItemNo: Code[20]; LocationCode: Code[10]; VariantCode: Code[10]) Result: Code[20]
    begin
        // Returns the leadtime in a date formula

        GetPlanningParameters.SetManualScheduling(ManualScheduling);
        GetPlanningParameters.AtSKU(TempSKU, ItemNo, VariantCode, LocationCode);
        Result := Format(TempSKU."Lead Time Calculation");
        OnAfterManufacturingLeadTime(TempSKU, Result);
    end;

    procedure SetManualScheduling(NewManualScheduling: Boolean)
    begin
        ManualScheduling := NewManualScheduling;
    end;

    procedure WhseOutBoundHandlingTime(LocationCode: Code[10]): Code[10]
    begin
        // Returns the outbound warehouse handling time in a date formula

        if LocationCode = '' then begin
            InvtSetup.Get();
            exit(Format(InvtSetup."Outbound Whse. Handling Time"));
        end;

        GetLocation(LocationCode);
        exit(Format(Location."Outbound Whse. Handling Time"));
    end;

    procedure WhseInBoundHandlingTime(LocationCode: Code[10]) Result: Code[10]
    var
        IsHandled: Boolean;
    begin
        // Returns the inbound warehouse handling time in a date formula
        IsHandled := false;
        OnBeforeWhseInBoundHandlingTime(LocationCode, Result, IsHandled);
        if IsHandled then
            exit(Result);

        if LocationCode = '' then begin
            InvtSetup.Get();
            exit(Format(InvtSetup."Inbound Whse. Handling Time"));
        end;

        GetLocation(LocationCode);
        exit(Format(Location."Inbound Whse. Handling Time"));
    end;

    procedure SafetyLeadTime(ItemNo: Code[20]; LocationCode: Code[10]; VariantCode: Code[10]) Result: Code[20]
    begin
        // Returns the safety lead time in a date formula

        GetPlanningParameters.SetManualScheduling(ManualScheduling);
        GetPlanningParameters.AtSKU(TempSKU, ItemNo, VariantCode, LocationCode);
        Result := Format(TempSKU."Safety Lead Time");
        OnAfterSafetyLeadTime(TempSKU, Result);
    end;


    procedure GetPlannedEndingDate(ItemNo: Code[20]; LocationCode: Code[10]; VariantCode: Code[10]; DueDate: Date; VendorNo: Code[20]; RefOrderType: Enum "Requisition Ref. Order Type") Result: Date
    var
        CustomCalendarChange: array[2] of Record "Customized Calendar Change";
        TransferRoute: Record "Transfer Route";
        DateFormula: DateFormula;
        PlannedReceiptDate: Date;
        OrgDateExpression: Text[30];
        CheckBothCalendars: Boolean;
        IsHandled: Boolean;
    begin
        // Returns Ending Date calculated backward from Due Date
        IsHandled := false;
        OnBeforeGetPlannedEndingDate(ItemNo, LocationCode, VariantCode, DueDate, VendorNo, RefOrderType, Result, IsHandled);
        if IsHandled then
            exit(Result);

        GetPlanningParameters.SetManualScheduling(ManualScheduling);
        GetPlanningParameters.AtSKU(TempSKU, ItemNo, VariantCode, LocationCode);

        if RefOrderType = RefOrderType::Transfer then begin
            Evaluate(DateFormula, WhseInBoundHandlingTime(LocationCode));
            TransferRoute.GetTransferRoute(
              TempSKU."Transfer-from Code", LocationCode, TransferRoute."In-Transit Code", TransferRoute."Shipping Agent Code", TransferRoute."Shipping Agent Service Code");
            TransferRoute.CalcPlanReceiptDateBackward(
              PlannedReceiptDate, DueDate, DateFormula, LocationCode, TransferRoute."Shipping Agent Code", TransferRoute."Shipping Agent Service Code");
            exit(PlannedReceiptDate);
        end;
        OnGetPlannedEndingDateOnBeforeFormatDateFormula(TempSKU, RefOrderType, ItemNo, DueDate);
        FormatDateFormula(TempSKU."Safety Lead Time");
        OrgDateExpression := InternalLeadTimeDays(WhseInBoundHandlingTime(LocationCode) + Format(TempSKU."Safety Lead Time"));
        CustomCalendarChange[1].SetSource(CalChange."Source Type"::Location, LocationCode, '', '');
        if (VendorNo <> '') and (RefOrderType = RefOrderType::Purchase) then begin
            CustomCalendarChange[2].SetSource(CalChange."Source Type"::Vendor, VendorNo, '', '');
            CheckBothCalendars := true;
        end else
            CustomCalendarChange[2].SetSource(CalChange."Source Type"::Location, LocationCode, '', '');
        exit(CalendarMgmt.CalcDateBOC2(OrgDateExpression, DueDate, CustomCalendarChange, CheckBothCalendars));
    end;


    procedure GetPlannedStartingDate(ItemNo: Code[20]; LocationCode: Code[10]; VariantCode: Code[10]; VendorNo: Code[20]; LeadTime: Code[20]; RefOrderType: Enum "Requisition Ref. Order Type"; EndingDate: Date) Result: Date
    var
        CustomCalendarChange: array[2] of Record "Customized Calendar Change";
        TransferRoute: Record "Transfer Route";
        ShippingTime: DateFormula;
        PlannedShipmentDate: Date;
        CheckBothCalendars: Boolean;
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeGetPlannedStartingDate(ItemNo, LocationCode, VariantCode, VendorNo, LeadTime, RefOrderType, EndingDate, Result, IsHandled);
        if not IsHandled then begin
            // Returns Starting Date calculated backward from Ending Date

            if RefOrderType = RefOrderType::Transfer then begin
                GetPlanningParameters.AtSKU(TempSKU, ItemNo, VariantCode, LocationCode);

                TransferRoute.GetTransferRoute(
                  TempSKU."Transfer-from Code", LocationCode, TransferRoute."In-Transit Code", TransferRoute."Shipping Agent Code", TransferRoute."Shipping Agent Service Code");
                TransferRoute.GetShippingTime(
                  TempSKU."Transfer-from Code", LocationCode, TransferRoute."Shipping Agent Code", TransferRoute."Shipping Agent Service Code", ShippingTime);
                TransferRoute.CalcPlanShipmentDateBackward(
                  PlannedShipmentDate, EndingDate, ShippingTime,
                  TempSKU."Transfer-from Code", TransferRoute."Shipping Agent Code", TransferRoute."Shipping Agent Service Code");
                exit(PlannedShipmentDate);
            end;
            if DateFormulaIsEmpty(LeadTime) then
                exit(EndingDate);

            if (VendorNo <> '') and (RefOrderType = RefOrderType::Purchase) then begin
                CustomCalendarChange[1].SetSource(CalChange."Source Type"::Vendor, VendorNo, '', '');
                CustomCalendarChange[2].SetSource(CalChange."Source Type"::Location, LocationCode, '', '');
                CheckBothCalendars := true;
            end else
                CustomCalendarChange[1].SetSource(CalChange."Source Type"::Location, LocationCode, '', '');
            Result := CalendarMgmt.CalcDateBOC2(InternalLeadTimeDays(LeadTime), EndingDate, CustomCalendarChange, CheckBothCalendars);
        end;
        OnAfterPlannedStartingDate(LeadTime, EndingDate, CustomCalendarChange, CheckBothCalendars, Result);
    end;


    procedure GetPlannedEndingDate(ItemNo: Code[20]; LocationCode: Code[10]; VariantCode: Code[10]; VendorNo: Code[20]; LeadTime: Code[20]; RefOrderType: Enum "Requisition Ref. Order Type"; StartingDate: Date) Result: Date
    var
        CustomCalendarChange: array[2] of Record "Customized Calendar Change";
        TransferRoute: Record "Transfer Route";
        ShippingTime: DateFormula;
        PlannedReceiptDate: Date;
        CheckBothCalendars: Boolean;
        IsHandled: Boolean;
    begin
        // Returns Ending Date calculated forward from Starting Date
        IsHandled := false;
        OnBeforeGetPlannedEndingDateCalculaterForwardFromStartingDate(ItemNo, LocationCode, VariantCode, VendorNo, LeadTime, RefOrderType, StartingDate, Result, IsHandled);
        if IsHandled then
            exit(Result);

        if RefOrderType = RefOrderType::Transfer then begin
            GetPlanningParameters.AtSKU(TempSKU, ItemNo, VariantCode, LocationCode);

            TransferRoute.GetTransferRoute(
              TempSKU."Transfer-from Code", LocationCode, TransferRoute."In-Transit Code", TransferRoute."Shipping Agent Code", TransferRoute."Shipping Agent Service Code");
            TransferRoute.GetShippingTime(
              TempSKU."Transfer-from Code", LocationCode, TransferRoute."Shipping Agent Code", TransferRoute."Shipping Agent Service Code", ShippingTime);
            TransferRoute.CalcPlannedReceiptDateForward(
              StartingDate, PlannedReceiptDate, ShippingTime, LocationCode, TransferRoute."Shipping Agent Code", TransferRoute."Shipping Agent Service Code");
            exit(PlannedReceiptDate);
        end;
        if DateFormulaIsEmpty(LeadTime) then
            exit(StartingDate);

        if (VendorNo <> '') and (RefOrderType = RefOrderType::Purchase) then begin
            CustomCalendarChange[1].SetSource(CalChange."Source Type"::Vendor, VendorNo, '', '');
            CustomCalendarChange[2].SetSource(CalChange."Source Type"::Location, LocationCode, '', '');
            CheckBothCalendars := true;
        end else
            CustomCalendarChange[1].SetSource(CalChange."Source Type"::Location, LocationCode, '', '');
        exit(CalendarMgmt.CalcDateBOC(LeadTime, StartingDate, CustomCalendarChange, CheckBothCalendars));
    end;


    procedure GetPlannedDueDate(ItemNo: Code[20]; LocationCode: Code[10]; VariantCode: Code[10]; EndingDate: Date; VendorNo: Code[20]; RefOrderType: Enum "Requisition Ref. Order Type") Result: Date
    var
        CustomCalendarChange: array[2] of Record "Customized Calendar Change";
        TransferRoute: Record "Transfer Route";
        DateFormula: DateFormula;
        ReceiptDate: Date;
        OrgDateExpression: Text[30];
        CheckBothCalendars: Boolean;
        IsHandled: Boolean;
    begin
        // Returns Due Date calculated forward from Ending Date
        IsHandled := false;
        OnBeforeGetPlannedDueDate(ItemNo, LocationCode, VariantCode, EndingDate, VendorNo, RefOrderType, Result, IsHandled);
        if IsHandled then
            exit(Result);

        GetPlanningParameters.SetManualScheduling(ManualScheduling);
        GetPlanningParameters.AtSKU(TempSKU, ItemNo, VariantCode, LocationCode);
        OnGetPlannedDueDateOnBeforeFormatDateFormula(TempSKU, RefOrderType, EndingDate, ItemNo, LocationCode);
        FormatDateFormula(TempSKU."Safety Lead Time");

        if RefOrderType = RefOrderType::Transfer then begin
            Evaluate(DateFormula, WhseInBoundHandlingTime(LocationCode));
            TransferRoute.CalcReceiptDateForward(EndingDate, ReceiptDate, DateFormula, LocationCode);
            exit(ReceiptDate);
        end;
        OrgDateExpression := WhseInBoundHandlingTime(LocationCode) + Format(TempSKU."Safety Lead Time");
        CustomCalendarChange[1].SetSource(CalChange."Source Type"::Location, LocationCode, '', '');
        if (VendorNo <> '') and (RefOrderType = RefOrderType::Purchase) then begin
            CustomCalendarChange[2].SetSource(CalChange."Source Type"::Vendor, VendorNo, '', '');
            CheckBothCalendars := true;
        end;
        exit(CalendarMgmt.CalcDateBOC(OrgDateExpression, EndingDate, CustomCalendarChange, CheckBothCalendars));
    end;

    local procedure FormatDateFormula(var DateFormula: DateFormula)
    var
        DateFormulaText: Text;
    begin
        if Format(DateFormula) = '' then
            Evaluate(DateFormula, '<+0D>')
        else
            if not (CopyStr(Format(DateFormula), 1, 1) in ['+', '-']) then begin
                DateFormulaText := '+' + Format(DateFormula); // DateFormula is formated to local language
                Evaluate(DateFormula, DateFormulaText);
            end;
    end;

    local procedure GetItem(ItemNo: Code[20])
    begin
        if ItemNo <> Item."No." then
            Item.Get(ItemNo);
    end;

    local procedure GetLocation(LocationCode: Code[10])
    begin
        if Location.Code <> LocationCode then
            Location.Get(LocationCode);
    end;

    local procedure InternalLeadTimeDays(DateFormulaText: Code[30]): Text[20]
    var
        TotalDays: DateFormula;
        DateFormulaLoc: DateFormula;
    begin
        Evaluate(DateFormulaLoc, DateFormulaText);
        Evaluate(TotalDays, '<' + Format(CalcDate(DateFormulaLoc, WorkDate()) - WorkDate()) + 'D>'); // DateFormulaText is formatet to local language
        exit(Format(TotalDays));
    end;

    local procedure DateFormulaIsEmpty(DateFormulaText: Code[30]): Boolean
    var
        DateFormula: DateFormula;
    begin
        if DateFormulaText = '' then
            exit(true);

        Evaluate(DateFormula, DateFormulaText);
        exit(CalcDate(DateFormula, WorkDate()) = WorkDate());
    end;

    procedure CheckLeadTimeIsNotNegative(LeadTimeDateFormula: DateFormula)
    begin
        if CalcDate(LeadTimeDateFormula, WorkDate()) < WorkDate() then
            Error(LeadTimeCalcNegativeErr);
    end;


    [IntegrationEvent(false, false)]
    local procedure OnBeforeGetPlannedDueDate(ItemNo: Code[20]; LocationCode: Code[10]; VariantCode: Code[10]; EndingDate: Date; VendorNo: Code[20]; RefOrderType: Enum "Requisition Ref. Order Type"; var Result: Date; var IsHandled: Boolean)
    begin
    end;


    [IntegrationEvent(false, false)]
    local procedure OnBeforeGetPlannedEndingDate(ItemNo: Code[20]; LocationCode: Code[10]; VariantCode: Code[10]; DueDate: Date; VendorNo: Code[20]; RefOrderType: Enum "Requisition Ref. Order Type"; var Result: Date; var IsHandled: Boolean)
    begin
    end;


    [IntegrationEvent(false, false)]
    local procedure OnBeforeGetPlannedStartingDate(ItemNo: Code[20]; LocationCode: Code[10]; VariantCode: Code[10]; VendorNo: Code[20]; var LeadTime: Code[20]; RefOrderType: Enum "Requisition Ref. Order Type"; EndingDate: Date; var Result: Date; var IsHandled: Boolean)
    begin
    end;


    [IntegrationEvent(false, false)]
    local procedure OnGetPlannedDueDateOnBeforeFormatDateFormula(var SKU: Record "Stockkeeping Unit"; RefOrderType: Enum "Requisition Ref. Order Type"; EndingDate: Date; ItemNo: Code[20]; LocationCode: Code[10]);
    begin
    end;


    [IntegrationEvent(false, false)]
    local procedure OnGetPlannedEndingDateOnBeforeFormatDateFormula(var SKU: Record "Stockkeeping Unit"; RefOrderType: Enum "Requisition Ref. Order Type"; ItemNo: code[20]; DueDate: Date);
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnAfterManufacturingLeadTime(TempStockkeepingUnit: Record "Stockkeeping Unit" temporary; var Result: Code[20])
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnAfterPurchaseLeadTime(ItemVend: Record "Item Vendor"; var Result: Code[20])
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnAfterPlannedStartingDate(LeadTime: Code[20]; EndingDate: Date; CustomCalendarChange: array[2] of Record "Customized Calendar Change"; CheckBothCalendars: Boolean; var Result: Date)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnAfterSafetyLeadTime(TempStockkeepingUnit: Record "Stockkeeping Unit" temporary; var Result: Code[20])
    begin
    end;

    [IntegrationEvent(true, false)]
    local procedure OnBeforeWhseInBoundHandlingTime(LocationCode: Code[10]; var InboundWhseHandlingTime: Code[10]; var IsHandled: Boolean)
    begin
    end;


    [IntegrationEvent(false, false)]
    local procedure OnBeforeGetPlannedEndingDateCalculaterForwardFromStartingDate(ItemNo: Code[20]; LocationCode: Code[10]; VariantCode: Code[10]; VendorNo: Code[20]; var LeadTime: Code[20]; RefOrderType: Enum "Requisition Ref. Order Type"; var StartingDate: Date; var Result: Date; var IsHandled: Boolean)
    begin
    end;
}