Codeunit 99000891 Mfg. Item Tracking Mgt.

App
Base Application
Namespace
Microsoft.Inventory.Tracking
Versions
26-28

Procedures, 3Events, 9

Versions171819202122232425262728

Source29

Source in 29

src/Layers/W1/BaseApp/Manufacturing/Inventory/Tracking/MfgItemTrackingMgt.Codeunit.al780 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.Tracking;

using Microsoft.Foundation.NoSeries;
using Microsoft.Foundation.UOM;
using Microsoft.Inventory.Item;
using Microsoft.Inventory.Journal;
using Microsoft.Inventory.Ledger;
using Microsoft.Inventory.Setup;
using Microsoft.Manufacturing.Document;
using Microsoft.Warehouse.Activity.History;
using Microsoft.Warehouse.Tracking;
using Microsoft.Warehouse.Worksheet;

codeunit 99000891 "Mfg. Item Tracking Mgt."
{
    var
        ItemTrackingManagement: Codeunit "Item Tracking Management";
        UOMMgt: Codeunit "Unit of Measure Management";
        ItemTrackingLines: Page "Item Tracking Lines";
        CountingRecordsMsg: Label 'Counting records...';

    [EventSubscriber(ObjectType::Codeunit, Codeunit::"Item Tracking Management", 'OnGetItemTrackingSetupOnSetSerialNoRequired', '', true, false)]
    local procedure OnGetItemTrackingSetupOnSetSerialNoRequired(var ItemTrackingSetup: Record "Item Tracking Setup"; ItemTrackingCode: Record "Item Tracking Code"; EntryType: Enum "Item Ledger Entry Type"; Inbound: Boolean)
    begin
        case EntryType of
            EntryType::Consumption, EntryType::Output:
                if Inbound then
                    ItemTrackingSetup."Serial No. Required" := ItemTrackingCode."SN Manuf. Inbound Tracking"
                else
                    ItemTrackingSetup."Serial No. Required" := ItemTrackingCode."SN Manuf. Outbound Tracking";
        end;
    end;

    [EventSubscriber(ObjectType::Codeunit, Codeunit::"Item Tracking Management", 'OnGetItemTrackingSetupOnSetLotNoRequired', '', true, false)]
    local procedure OnGetItemTrackingSetupOnSetLotNoRequired(var ItemTrackingSetup: Record "Item Tracking Setup"; ItemTrackingCode: Record "Item Tracking Code"; EntryType: Enum "Item Ledger Entry Type"; Inbound: Boolean)
    begin
        case EntryType of
            EntryType::Consumption, EntryType::Output:
                if Inbound then
                    ItemTrackingSetup."Lot No. Required" := ItemTrackingCode."Lot Manuf. Inbound Tracking"
                else
                    ItemTrackingSetup."Lot No. Required" := ItemTrackingCode."Lot Manuf. Outbound Tracking";
        end;
    end;

    [EventSubscriber(ObjectType::Codeunit, Codeunit::"Item Tracking Management", 'OnGetItemTrackingSetupOnSetPackageNoRequired', '', true, false)]
    local procedure OnGetItemTrackingSetupOnSetPackageNoRequired(var ItemTrackingSetup: Record "Item Tracking Setup"; ItemTrackingCode: Record "Item Tracking Code"; EntryType: Enum "Item Ledger Entry Type"; Inbound: Boolean)
    begin
        case EntryType of
            EntryType::Consumption, EntryType::Output:
                if Inbound then
                    ItemTrackingSetup."Package No. Required" := ItemTrackingCode."Package Manuf. Inb. Tracking"
                else
                    ItemTrackingSetup."Package No. Required" := ItemTrackingCode."Package Manuf. Outb. Tracking";
        end;
    end;

    [EventSubscriber(ObjectType::Codeunit, Codeunit::"Item Tracking Management", 'OnAfterInitWhseWorksheetLine', '', true, false)]
    local procedure OnAfterInitWhseWorksheetLine(var WhseWorksheetLine: Record "Whse. Worksheet Line"; WhseDocType: Enum "Warehouse Worksheet Document Type"; SourceSubtype: Integer; SourceNo: Code[20]; SourceLineNo: Integer; SourceSublineNo: Integer)
    var
        ProdOrderComponent: Record "Prod. Order Component";
        ProdOrderLine: Record "Prod. Order Line";
    begin
        if WhseDocType <> Enum::"Warehouse Worksheet Document Type"::Production then
            exit;

        case WhseWorksheetLine."Source Type" of
            Database::"Prod. Order Line":
                begin
                    ProdOrderLine.SetLoadFields("Qty. Put Away (Base)");
                    ProdOrderLine.Get(SourceSubtype, SourceNo, SourceLineNo);
                    WhseWorksheetLine."Qty. Handled (Base)" := ProdOrderLine."Qty. Put Away (Base)";
                end;
            else begin
                ProdOrderComponent.SetLoadFields("Qty. Picked (Base)");
                ProdOrderComponent.Get(SourceSubtype, SourceNo, SourceLineNo, SourceSublineNo);
                WhseWorksheetLine."Qty. Handled (Base)" := ProdOrderComponent."Qty. Picked (Base)";
            end;
        end;
    end;

    [EventSubscriber(ObjectType::Codeunit, Codeunit::"Item Tracking Management", 'OnBeforeRetrieveItemTrackingFromReservEntry', '', true, false)]
    local procedure OnBeforeRetrieveItemTrackingFromReservEntry(ItemJnlLine: Record "Item Journal Line"; var ReservEntry: Record "Reservation Entry"; var Result: Boolean; var IsHandled: Boolean; var TempTrackingSpec: Record "Tracking Specification" temporary)
    begin
        if ItemJnlLine.Subcontracting then begin
            Result := RetrieveSubcontrItemTracking(ItemJnlLine, TempTrackingSpec);
            IsHandled := true;
        end;
    end;

    procedure RetrieveConsumpItemTracking(ItemJnlLine: Record "Item Journal Line"; var TempHandlingSpecification: Record "Tracking Specification" temporary): Boolean
    var
        ReservEntry: Record "Reservation Entry";
    begin
        ItemJnlLine.TestField("Order Type", ItemJnlLine."Order Type"::Production);
        ReservEntry.SetSourceFilter(
          Database::"Prod. Order Component", 3, ItemJnlLine."Order No.", ItemJnlLine."Prod. Order Comp. Line No.", true);
        ReservEntry.SetSourceFilter('', ItemJnlLine."Order Line No.");
        ReservEntry.SetFilter("Qty. to Handle (Base)", '<>0');
        ReservEntry.SetTrackingFilterFromItemJnlLine(ItemJnlLine);
        OnRetrieveConsumpItemTrackingOnAfterSetFilters(ReservEntry, ItemJnlLine);

        // Sum up in a temporary table per component line:
        exit(ItemTrackingManagement.SumUpItemTracking(ReservEntry, TempHandlingSpecification, true, true));
    end;

    local procedure RetrieveSubcontrItemTracking(ItemJnlLine: Record "Item Journal Line"; var TempHandlingSpecification: Record "Tracking Specification" temporary) Result: Boolean
    var
        ReservEntry: Record "Reservation Entry";
        ProdOrderRoutingLine: Record "Prod. Order Routing Line";
        IsLastOperation: Boolean;
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeRetrieveSubcontrItemTracking(ItemJnlLine, TempHandlingSpecification, Result, IsHandled);
        if IsHandled then
            exit(Result);

        if not ItemJnlLine.Subcontracting then
            exit(false);

        if ItemJnlLine."Operation No." = '' then
            exit(false);

        ItemJnlLine.TestField("Routing No.");
        ItemJnlLine.TestField("Order Type", ItemJnlLine."Order Type"::Production);
        if not ProdOrderRoutingLine.Get(
             ProdOrderRoutingLine.Status::Released, ItemJnlLine."Order No.",
             ItemJnlLine."Routing Reference No.", ItemJnlLine."Routing No.", ItemJnlLine."Operation No.")
        then
            exit(false);

        IsLastOperation := ProdOrderRoutingLine."Next Operation No." = '';
        OnRetrieveSubcontrItemTrackingOnBeforeCheckLastOperation(ProdOrderRoutingLine, IsLastOperation);
        if not IsLastOperation then
            exit(false);

        ReservEntry.SetSourceFilter(Database::"Prod. Order Line", 3, ItemJnlLine."Order No.", 0, true);
        ReservEntry.SetSourceFilter('', ItemJnlLine."Order Line No.");
        ReservEntry.SetFilter("Qty. to Handle (Base)", '<>0');
        if ItemTrackingManagement.SumUpItemTracking(ReservEntry, TempHandlingSpecification, false, true) then begin
            ReservEntry.SetRange("Reservation Status", ReservEntry."Reservation Status"::Prospect);
            if not ReservEntry.IsEmpty() then
                ReservEntry.DeleteAll();
            OnRetrieveSubcontrItemTrackingOnAfterDeleteReservEntries(TempHandlingSpecification, ReservEntry);
            exit(true);
        end;
        exit(false);
    end;

    [EventSubscriber(ObjectType::Codeunit, Codeunit::"Item Tracking Management", 'OnInitTrackingSpecificationByDocumentType', '', true, false)]
    local procedure OnInitTrackingSpecificationByDocumentType(var WhseWorksheetLine: Record "Whse. Worksheet Line"; SourceType: Integer)
    var
        ProdOrderLine: Record "Prod. Order Line";
    begin
        case WhseWorksheetLine."Whse. Document Type" of
            "Warehouse Worksheet Document Type"::Production:
                if SourceType = Database::"Prod. Order Line" then begin
                    ProdOrderLine.SetLoadFields(Status, ProdOrderLine."Prod. Order No.", ProdOrderLine."Line No.");
                    ProdOrderLine.SetFilter(Status, '%1|%2', ProdOrderLine.Status::Released, ProdOrderLine.Status::Finished);
                    ProdOrderLine.SetRange("Prod. Order No.", WhseWorksheetLine."Source No.");
                    ProdOrderLine.SetRange("Line No.", WhseWorksheetLine."Source Line No.");
                    if ProdOrderLine.FindFirst() then
                        InsertWhseItemTrkgLinesForProdOrderLine(ProdOrderLine, SourceType);
                end;
        end;
    end;

    local procedure InsertWhseItemTrkgLinesForProdOrderLine(ProdOrderLine: Record "Prod. Order Line"; SourceType: Integer)
    var
        WhseItemTrackingLine: Record "Whse. Item Tracking Line";
        ItemLedgerEntry: Record "Item Ledger Entry";
        SequenceNoMgt: Codeunit "Sequence No. Mgt.";
        QtyHandledBase: Decimal;
        RemQtyHandledBase: Decimal;
    begin
        QtyHandledBase := 0;

        ItemLedgerEntry.SetSourceFilterForProdOutputPutAway(ProdOrderLine);
        if ItemLedgerEntry.FindSet() then begin
            SequenceNoMgt.AllocateSeqNoBuffer(Database::"Whse. Item Tracking Line", ItemLedgerEntry.Count());
            WhseItemTrackingLine.SetSourceFilter(SourceType, ProdOrderLine.Status.AsInteger(), ProdOrderLine."Prod. Order No.", ProdOrderLine."Line No.", false);
            WhseItemTrackingLine.DeleteAll();
            WhseItemTrackingLine.Init();
            WhseItemTrackingLine.SetTrackingKey();
            repeat
                WhseItemTrackingLine.SetTrackingFilterFromItemLedgerEntry(ItemLedgerEntry);
                if not WhseItemTrackingLine.HasSameTrackingWithItemLedgerEntry(ItemLedgerEntry) then
                    RemQtyHandledBase := RegisteredPutAwayQtyBase(ProdOrderLine, ItemLedgerEntry)
                else
                    RemQtyHandledBase -= QtyHandledBase;
                QtyHandledBase := RemQtyHandledBase;
                if QtyHandledBase > ItemLedgerEntry.Quantity then
                    QtyHandledBase := ItemLedgerEntry.Quantity;

                if not WhseItemTrackingLine.FindFirst() then begin
                    WhseItemTrackingLine.Init();
                    WhseItemTrackingLine."Entry No." := WhseItemTrackingLine.GetNextEntryNo();

                    WhseItemTrackingLine."Item No." := ItemLedgerEntry."Item No.";
                    WhseItemTrackingLine."Location Code" := ItemLedgerEntry."Location Code";
                    WhseItemTrackingLine.Description := ItemLedgerEntry.Description;
                    WhseItemTrackingLine.SetSource(
                      Database::"Prod. Order Line", ProdOrderLine.Status.AsInteger(), ItemLedgerEntry."Order No.",
                      ItemLedgerEntry."Order Line No.", '', ItemLedgerEntry."Order Line No.");
                    WhseItemTrackingLine.CopyTrackingFromItemLedgEntry(ItemLedgerEntry);
                    WhseItemTrackingLine."Warranty Date" := ItemLedgerEntry."Warranty Date";
                    WhseItemTrackingLine."Expiration Date" := ItemLedgerEntry."Expiration Date";
                    WhseItemTrackingLine."Qty. per Unit of Measure" := ItemLedgerEntry."Qty. per Unit of Measure";
                    WhseItemTrackingLine."Quantity Handled (Base)" := QtyHandledBase;
                    WhseItemTrackingLine."Qty. Registered (Base)" := QtyHandledBase;
                    WhseItemTrackingLine.Validate("Quantity (Base)", ItemLedgerEntry.Quantity);
                    WhseItemTrackingLine.Insert();
                end else begin
                    WhseItemTrackingLine."Quantity Handled (Base)" += QtyHandledBase;
                    WhseItemTrackingLine."Qty. Registered (Base)" += QtyHandledBase;
                    WhseItemTrackingLine.Validate("Quantity (Base)", WhseItemTrackingLine."Quantity (Base)" + ItemLedgerEntry.Quantity);
                    WhseItemTrackingLine.Modify();
                end;
            until ItemLedgerEntry.Next() = 0;
        end;
    end;

    local procedure RegisteredPutAwayQtyBase(ProdOrderLine: Record "Prod. Order Line"; ItemLedgerEntry: Record "Item Ledger Entry"): Decimal
    var
        RegisteredWhseActivityLine: Record "Registered Whse. Activity Line";
    begin
        RegisteredWhseActivityLine.SetSourceFilter(Database::"Prod. Order Line", ProdOrderLine.Status.AsInteger(), ProdOrderLine."Prod. Order No.", ProdOrderLine."Line No.", -1, true);
        RegisteredWhseActivityLine.SetTrackingFilterFromItemLedgerEntry(ItemLedgerEntry);
        RegisteredWhseActivityLine.SetRange("Whse. Document No.", ProdOrderLine."Prod. Order No.");
        RegisteredWhseActivityLine.SetRange("Action Type", RegisteredWhseActivityLine."Action Type"::Take);
        RegisteredWhseActivityLine.CalcSums("Qty. (Base)");

        exit(RegisteredWhseActivityLine."Qty. (Base)");
    end;

    [EventSubscriber(ObjectType::Codeunit, Codeunit::"Item Tracking Management", 'OnInitTrackingSpecificationOnCreateNew', '', true, false)]
    local procedure OnInitTrackingSpecificationOnCreateNew(var WhseWorksheetLine: Record "Whse. Worksheet Line"; SourceType: Integer)
    var
        ProdOrderLine: Record "Prod. Order Line";
    begin
        case SourceType of
            Database::"Prod. Order Line":
                begin
                    ProdOrderLine.SetLoadFields(Status, ProdOrderLine."Prod. Order No.", ProdOrderLine."Line No.");
                    ProdOrderLine.SetFilter(Status, '%1|%2', ProdOrderLine.Status::Released, ProdOrderLine.Status::Finished);
                    ProdOrderLine.SetRange("Prod. Order No.", WhseWorksheetLine."Source No.");
                    ProdOrderLine.SetRange("Line No.", WhseWorksheetLine."Source Line No.");
                    if ProdOrderLine.FindFirst() then
                        CreateWhseItemTrackingForProdOrderLine(WhseWorksheetLine, ProdOrderLine);
                end;
        end;
    end;

    local procedure CreateWhseItemTrackingForProdOrderLine(WhseWkshLine: Record "Whse. Worksheet Line"; ProdOrderLine: Record "Prod. Order Line")
    var
        ItemLedgerEntry: Record "Item Ledger Entry";
        WhseItemTrackingLine: Record "Whse. Item Tracking Line";
        SequenceNoMgt: Codeunit "Sequence No. Mgt.";
        QtyToApply: Decimal;
    begin
        WhseItemTrackingLine.Reset();

        ItemLedgerEntry.SetSourceFilterForProdOutputPutAway(WhseWkshLine);
        if ItemLedgerEntry.FindSet() then begin
            SequenceNoMgt.AllocateSeqNoBuffer(Database::"Whse. Item Tracking Line", ItemLedgerEntry.Count());
            repeat
                WhseItemTrackingLine.Init();
                WhseItemTrackingLine."Entry No." := WhseItemTrackingLine.GetNextEntryNo();
                WhseItemTrackingLine."Item No." := WhseWkshLine."Item No.";
                WhseItemTrackingLine."Variant Code" := WhseWkshLine."Variant Code";
                WhseItemTrackingLine."Location Code" := WhseWkshLine."Location Code";
                WhseItemTrackingLine.Description := WhseWkshLine.Description;
                WhseItemTrackingLine."Qty. per Unit of Measure" := WhseWkshLine."Qty. per From Unit of Measure";
                WhseItemTrackingLine.SetSource(
                  Database::"Prod. Order Line", ProdOrderLine.Status.AsInteger(), WhseWkshLine."Whse. Document No.", WhseWkshLine."Whse. Document Line No.", '', 0);
                WhseItemTrackingLine.CopyTrackingFromItemLedgEntry(ItemLedgerEntry);
                QtyToApply := ItemLedgerEntry.Quantity - ProdOrderLine.GetUsedPutAwayQtyPerItemTracking(ItemLedgerEntry."Lot No.", ItemLedgerEntry."Serial No.", ItemLedgerEntry."Package No.");
                if QtyToApply <> 0 then begin
                    WhseItemTrackingLine."Quantity (Base)" := ItemLedgerEntry.Quantity - ProdOrderLine.GetUsedPutAwayQtyPerItemTracking(ItemLedgerEntry."Lot No.", ItemLedgerEntry."Serial No.", ItemLedgerEntry."Package No.");
                    if WhseWkshLine."Qty. (Base)" = WhseWkshLine."Qty. to Handle (Base)" then
                        WhseItemTrackingLine."Qty. to Handle (Base)" := WhseItemTrackingLine."Quantity (Base)";
                    WhseItemTrackingLine."Qty. to Handle" :=
                      Round(
                        WhseItemTrackingLine."Qty. to Handle (Base)" / WhseItemTrackingLine."Qty. per Unit of Measure",
                        UOMMgt.QtyRndPrecision());
                    WhseItemTrackingLine.Insert();
                end;
            until ItemLedgerEntry.Next() = 0;
        end;
    end;

    internal procedure SplitProdOrderLineForOutputPutAway(ProdOrderLine: Record "Prod. Order Line"; var TempProdOrdLineTrackingBuff: Record "Prod. Ord. Line Tracking Buff." temporary; SplitUpToQtyBase: Decimal)
    var
        ItemLedgerEntry: Record "Item Ledger Entry";
        ItemTrackingSetup: Record "Item Tracking Setup";
        BufferEntryNo: Integer;
        RemainingHandledQtyBase: Decimal;
        QtyBaseAvailableToPutAway: Decimal;
        ExitLoop: Boolean;
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeSplitProdOrderLineForOutputPutAway(ProdOrderLine, TempProdOrdLineTrackingBuff, SplitUpToQtyBase, IsHandled);
        if IsHandled then
            exit;

        TempProdOrdLineTrackingBuff.Reset();
        TempProdOrdLineTrackingBuff.DeleteAll();
        RemainingHandledQtyBase := SplitUpToQtyBase;

        if not ItemTrackingManagement.GetWhseItemTrkgSetup(ProdOrderLine."Item No.", ItemTrackingSetup) then begin
            CopyProdOrderLineFieldsToTempProdOrdLineTrackingBuff(ProdOrderLine, TempProdOrdLineTrackingBuff);
            TempProdOrdLineTrackingBuff."Buffer Entry No." := 1;
            TempProdOrdLineTrackingBuff.Insert();
            OnAfterSplitProdOrderLineForOutputPutAway(ProdOrderLine, TempProdOrdLineTrackingBuff);
            exit;
        end;

        ItemLedgerEntry.SetLoadFields("Order Type", "Order No.", "Order No.", "Serial No.", "Lot No.", "Package No.", "Warranty Date", "Expiration Date", Quantity);
        ItemLedgerEntry.SetSourceFilterForProdOutputPutAway(ProdOrderLine);
        if ItemLedgerEntry.FindSet() then
            repeat
                TempProdOrdLineTrackingBuff.SetTrackingFilterFromItemLedgerEntry(ItemLedgerEntry);
                TempProdOrdLineTrackingBuff.SetRange("Warranty Date", ItemLedgerEntry."Warranty Date");
                TempProdOrdLineTrackingBuff.SetRange("Expiration Date", ItemLedgerEntry."Expiration Date");
                if TempProdOrdLineTrackingBuff.FindFirst() then begin
                    UpdateQtySplitForPutAwayOnProdOrdLineTrackingBuffer(TempProdOrdLineTrackingBuff, ProdOrderLine, ItemLedgerEntry, QtyBaseAvailableToPutAway, RemainingHandledQtyBase);
                    if QtyBaseAvailableToPutAway > 0 then
                        TempProdOrdLineTrackingBuff.Modify();
                end else begin
                    BufferEntryNo += 1;
                    TempProdOrdLineTrackingBuff.Reset();
                    CopyProdOrderLineFieldsToTempProdOrdLineTrackingBuff(ProdOrderLine, TempProdOrdLineTrackingBuff);
                    TempProdOrdLineTrackingBuff."Buffer Entry No." := BufferEntryNo;
                    TempProdOrdLineTrackingBuff.CopyTrackingFromItemLedgerEntry(ItemLedgerEntry);
                    TempProdOrdLineTrackingBuff."Warranty Date" := ItemLedgerEntry."Warranty Date";
                    TempProdOrdLineTrackingBuff."Expiration Date" := ItemLedgerEntry."Expiration Date";
                    TempProdOrdLineTrackingBuff."Qty. split for Put Away (Base)" := 0;

                    UpdateQtySplitForPutAwayOnProdOrdLineTrackingBuffer(TempProdOrdLineTrackingBuff, ProdOrderLine, ItemLedgerEntry, QtyBaseAvailableToPutAway, RemainingHandledQtyBase);
                    if QtyBaseAvailableToPutAway > 0 then
                        TempProdOrdLineTrackingBuff.Insert();
                end;

                if SplitUpToQtyBase <> 0 then
                    ExitLoop := RemainingHandledQtyBase = 0;
            until (ItemLedgerEntry.Next() = 0) or ExitLoop
        else begin
            CopyProdOrderLineFieldsToTempProdOrdLineTrackingBuff(ProdOrderLine, TempProdOrdLineTrackingBuff);
            TempProdOrdLineTrackingBuff."Buffer Entry No." := 1;
            TempProdOrdLineTrackingBuff.Insert();
        end;
        OnAfterSplitProdOrderLineForOutputPutAway(ProdOrderLine, TempProdOrdLineTrackingBuff);
    end;

    local procedure CopyProdOrderLineFieldsToTempProdOrdLineTrackingBuff(ProdOrderLine: Record "Prod. Order Line"; var TempProdOrdLineTrackingBuff: Record "Prod. Ord. Line Tracking Buff.")
    begin
        Clear(TempProdOrdLineTrackingBuff);
        TempProdOrdLineTrackingBuff."Prod. Order Status" := ProdOrderLine."Status";
        TempProdOrdLineTrackingBuff."Prod. Order No." := ProdOrderLine."Prod. Order No.";
        TempProdOrdLineTrackingBuff."Prod. Order Line No." := ProdOrderLine."Line No.";
        TempProdOrdLineTrackingBuff."Item No." := ProdOrderLine."Item No.";
        TempProdOrdLineTrackingBuff."Variant Code" := ProdOrderLine."Variant Code";
        TempProdOrdLineTrackingBuff."Qty. Rounding Precision" := ProdOrderLine."Qty. Rounding Precision";
        TempProdOrdLineTrackingBuff."Qty. Rounding Precision (Base)" := ProdOrderLine."Qty. Rounding Precision (Base)";
        TempProdOrdLineTrackingBuff."Unit of Measure Code" := ProdOrderLine."Unit of Measure Code";
        TempProdOrdLineTrackingBuff."Qty. per Unit of Measure" := ProdOrderLine."Qty. per Unit of Measure";
        TempProdOrdLineTrackingBuff."Qty. split for Put Away (Base)" := ProdOrderLine."Finished Qty. (Base)";
        TempProdOrdLineTrackingBuff."Qty. split for Put Away" :=
          Round(
            TempProdOrdLineTrackingBuff."Qty. split for Put Away (Base)" / TempProdOrdLineTrackingBuff."Qty. per Unit of Measure",
            UOMMgt.QtyRndPrecision());

        OnAfterCopyProdOrderLineFieldsToTempProdOrdLineTrackingBuff(ProdOrderLine, TempProdOrdLineTrackingBuff);
    end;

    local procedure UpdateQtySplitForPutAwayOnProdOrdLineTrackingBuffer(var TempProdOrdLineTrackingBuff: Record "Prod. Ord. Line Tracking Buff." temporary; ProdOrderLine: Record "Prod. Order Line"; ItemLedgerEntry: Record "Item Ledger Entry"; var QtyBaseAvailableToPutAway: Decimal; var RemainingHandledQtyBase: Decimal)
    begin
        QtyBaseAvailableToPutAway := CalcTotalOutputQtyBaseForTracking(ProdOrderLine, ItemLedgerEntry)
       - ProdOrderLine.GetUsedPutAwayQtyPerItemTracking(ItemLedgerEntry."Lot No.", ItemLedgerEntry."Serial No.", ItemLedgerEntry."Package No.")
       - TempProdOrdLineTrackingBuff."Qty. split for Put Away (Base)";

        if QtyBaseAvailableToPutAway < 0 then
            exit;

        TempProdOrdLineTrackingBuff."Qty. split for Put Away (Base)" += CalcQtyBaseToPutAway(RemainingHandledQtyBase, QtyBaseAvailableToPutAway);
        TempProdOrdLineTrackingBuff."Qty. split for Put Away" :=
          Round(
            TempProdOrdLineTrackingBuff."Qty. split for Put Away (Base)" / TempProdOrdLineTrackingBuff."Qty. per Unit of Measure",
            UOMMgt.QtyRndPrecision());

        OnAfterUpdateQtySplitForPutAwayOnProdOrdLineTrackingBuffer(TempProdOrdLineTrackingBuff, ProdOrderLine, ItemLedgerEntry, QtyBaseAvailableToPutAway, RemainingHandledQtyBase);
    end;

    local procedure CalcTotalOutputQtyBaseForTracking(ProdOrderLine: Record "Prod. Order Line"; ItemLedgerEntry: Record "Item Ledger Entry"): Decimal
    var
        TotalItemLedgerEntry: Record "Item Ledger Entry";
    begin
        TotalItemLedgerEntry.SetSourceFilterForProdOutputPutAway(ProdOrderLine);
        TotalItemLedgerEntry.SetCurrentKey("Item No.", Open, "Variant Code", Positive, "Lot No.", "Serial No.", "Package No.");
        TotalItemLedgerEntry.SetRange("Lot No.", ItemLedgerEntry."Lot No.");
        TotalItemLedgerEntry.SetRange("Serial No.", ItemLedgerEntry."Serial No.");
        TotalItemLedgerEntry.SetRange("Package No.", ItemLedgerEntry."Package No.");
        TotalItemLedgerEntry.SetLoadFields(Quantity);
        TotalItemLedgerEntry.CalcSums(Quantity);
        exit(TotalItemLedgerEntry.Quantity);
    end;

    local procedure CalcQtyBaseToPutAway(var RemainingHandledQtyBase: Decimal; QtyBaseAvailableToPutAway: Decimal) Qty: Decimal
    begin
        if RemainingHandledQtyBase >= QtyBaseAvailableToPutAway then begin
            RemainingHandledQtyBase := RemainingHandledQtyBase - QtyBaseAvailableToPutAway;
            Qty := QtyBaseAvailableToPutAway;
        end else begin
            if RemainingHandledQtyBase <> 0 then
                Qty := RemainingHandledQtyBase
            else
                Qty := QtyBaseAvailableToPutAway;
            RemainingHandledQtyBase := 0;
        end;
    end;

    [EventSubscriber(ObjectType::Codeunit, Codeunit::"Item Tracking Management", 'OnCalcWhseItemTrkgLineOnSetSourceTypeFilter', '', true, false)]
    local procedure OnCalcWhseItemTrkgLineOnSetSourceTypeFilter(var WhseItemTrackingLine: Record "Whse. Item Tracking Line")
    begin
        case WhseItemTrackingLine."Source Type" of
            Database::"Prod. Order Line",
            Database::"Prod. Order Component":
                WhseItemTrackingLine."Source Type Filter" := WhseItemTrackingLine."Source Type Filter"::Production;
        end;
    end;

    [IntegrationEvent(false, false)]
    local procedure OnRetrieveConsumpItemTrackingOnAfterSetFilters(var ReservationEntry: Record "Reservation Entry"; ItemJournalLine: Record "Item Journal Line")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeRetrieveSubcontrItemTracking(ItemJnlLine: Record "Item Journal Line"; var TempHandlingSpecification: Record "Tracking Specification" temporary; var Result: Boolean; var IsHandled: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnRetrieveSubcontrItemTrackingOnBeforeCheckLastOperation(ProdOrderRoutingLine: Record Microsoft.Manufacturing.Document."Prod. Order Routing Line"; var IsLastOperation: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnRetrieveSubcontrItemTrackingOnAfterDeleteReservEntries(var TempHandlingSpecification: Record "Tracking Specification" temporary; var ReservationEntry: Record "Reservation Entry")
    begin
    end;

    // Item Tracking Code

    [EventSubscriber(ObjectType::Table, Database::"Item Tracking Code", 'OnValidateSNSpecificTrackingOnAfterSet', '', true, false)]
    local procedure OnValidateSNSpecificTrackingOnAfterSet(var ItemTrackingCode: Record "Item Tracking Code")
    begin
        ItemTrackingCode."SN Manuf. Inbound Tracking" := true;
        ItemTrackingCode."SN Manuf. Outbound Tracking" := true;
    end;

    [EventSubscriber(ObjectType::Table, Database::"Item Tracking Code", 'OnValidateLotSpecificTrackingOnAfterSet', '', true, false)]
    local procedure OnValidateLotSpecificTrackingOnAfterSet(var ItemTrackingCode: Record "Item Tracking Code")
    begin
        ItemTrackingCode."Lot Manuf. Inbound Tracking" := true;
        ItemTrackingCode."Lot Manuf. Outbound Tracking" := true;
    end;

    [EventSubscriber(ObjectType::Table, Database::"Item Tracking Code", 'OnValidatePackageSpecificTrackingOnAfterSet', '', true, false)]
    local procedure OnValidatePackageSpecificTrackingOnAfterSet(var ItemTrackingCode: Record "Item Tracking Code")
    begin
        ItemTrackingCode."Package Manuf. Inb. Tracking" := true;
        ItemTrackingCode."Package Manuf. Outb. Tracking" := true;
    end;

    // Report Carry Out Reservation
    [EventSubscriber(ObjectType::Report, Report::"Carry Out Reservation", 'OnCarryOutReservationOtherDemandType', '', true, false)]
    local procedure OnCarryOutReservationOtherDemandType(var ReservationWkshLine: Record "Reservation Wksh. Line"; DemandType: Enum "Reservation Demand Type")
    begin
        case DemandType of
            DemandType::"Production Components":
                ReservationWkshLine.SetRange("Source Type", Database::"Prod. Order Component");
        end;
    end;

    [EventSubscriber(ObjectType::Table, Database::"Action Message Entry", 'OnAfterSumUp', '', true, false)]
    local procedure OnAfterSumUp(var ActionMessageEntry: Record "Action Message Entry"; var ComponentBinding: Boolean; var FirstDate: Date; var FirstTime: Time)
    var
        ReservEntry: Record "Reservation Entry";
        ReservEntry2: Record "Reservation Entry";
        ProdOrderComp: Record "Prod. Order Component";
    begin
        ComponentBinding := false;
        if ActionMessageEntry."Source Type" = Database::"Prod. Order Line" then begin
            FirstDate := DMY2Date(31, 12, 9999);
            ActionMessageEntry.FilterToReservEntry(ReservEntry);
            ReservEntry.SetRange(Binding, ReservEntry.Binding::"Order-to-Order");
            if ReservEntry.FindSet() then
                repeat
                    if ReservEntry2.Get(ReservEntry."Entry No.", false) then
                        if (ReservEntry2."Source Type" = Database::"Prod. Order Component") and
                           (ReservEntry2."Source Subtype" = ReservEntry."Source Subtype") and
                           (ReservEntry2."Source ID" = ReservEntry."Source ID")
                        then
                            if ProdOrderComp.Get(
                                 ReservEntry2."Source Subtype", ReservEntry2."Source ID",
                                 ReservEntry2."Source Prod. Order Line", ReservEntry2."Source Ref. No.")
                            then begin
                                ComponentBinding := true;
                                if ProdOrderComp."Due Date" < FirstDate then begin
                                    FirstDate := ProdOrderComp."Due Date";
                                    FirstTime := ProdOrderComp."Due Time";
                                end;
                            end;
                until ReservEntry.Next() = 0;
        end;
    end;

    // Codeunit "Item Tracing Mgt."

    [EventSubscriber(ObjectType::Codeunit, Codeunit::"Item Tracing Mgt.", 'OnInsertRecordOnBeforeSetDescription', '', true, false)]
    local procedure OnInsertRecordOnBeforeSetDescription(var TempTrackEntry: Record "Item Tracing Buffer"; var RecRef: RecordRef; var Description2: Text[100])
    var
        ProductionOrder: Record "Production Order";
    begin
        if RecRef.Get(TempTrackEntry."Record Identifier") then
            case RecRef.Number of
                Database::"Production Order":
                    begin
                        RecRef.SetTable(ProductionOrder);
                        Description2 :=
                            StrSubstNo('%1 %2 %3 %4', ProductionOrder.Status, RecRef.Caption, TempTrackEntry."Entry Type", TempTrackEntry."Document No.");
                    end;
            end;
    end;

    [EventSubscriber(ObjectType::Codeunit, Codeunit::"Item Tracing Mgt.", 'OnAfterSetRecordID', '', true, false)]
    local procedure OnAfterSetRecordID(var TrackingEntry: Record "Item Tracing Buffer"; RecRef: RecordRef)
    var
        ProductionOrder: Record "Production Order";
    begin
        case TrackingEntry."Entry Type" of
            TrackingEntry."Entry Type"::Consumption,
            TrackingEntry."Entry Type"::Output:
                begin
                    ProductionOrder.SetFilter(Status, '>=%1', ProductionOrder.Status::Released);
                    ProductionOrder.SetRange("No.", TrackingEntry."Document No.");
                    if ProductionOrder.FindFirst() then begin
                        RecRef.GetTable(ProductionOrder);
                        TrackingEntry."Record Identifier" := RecRef.RecordId;
                    end;
                end;
        end;
    end;

    [EventSubscriber(ObjectType::Codeunit, Codeunit::"Item Tracing Mgt.", 'OnShowDocument', '', true, false)]
    local procedure OnShowDocument(RecRef: RecordRef; RecID: RecordId)
    var
        ProductionOrder: Record "Production Order";
    begin
        case RecID.TableNo of
            Database::"Production Order":
                begin
                    RecRef.SetTable(ProductionOrder);
                    if ProductionOrder.Status = ProductionOrder.Status::Released then
                        PAGE.RunModal(PAGE::"Released Production Order", ProductionOrder)
                    else
                        if ProductionOrder.Status = ProductionOrder.Status::Finished then
                            PAGE.RunModal(PAGE::"Finished Production Order", ProductionOrder);
                end;
        end;
    end;

    // Codeunit Reservation Engine Mgt.

    [EventSubscriber(ObjectType::Codeunit, Codeunit::"Reservation Engine Mgt.", 'OnModifyActionMessageDatingOnGetDampenerPeriod', '', true, false)]
    local procedure OnModifyActionMessageDatingOnGetDampenerPeriod(ReservEntry: Record "Reservation Entry"; var DampenerPeriod: Dateformula)
    var
        InventorySetup: Record "Inventory Setup";
    begin
        InventorySetup.Get();
        if (Format(InventorySetup."Default Dampener Period") = '') or
           ((ReservEntry.Binding = ReservEntry.Binding::"Order-to-Order") and
            (ReservEntry."Reservation Status" = ReservEntry."Reservation Status"::Reservation))
        then
            Evaluate(InventorySetup."Default Dampener Period", '<0D>');
        DampenerPeriod := InventorySetup."Default Dampener Period";
    end;

    [EventSubscriber(ObjectType::Codeunit, Codeunit::"Reservation Engine Mgt.", 'OnAfterShouldModifyActionMessageDating', '', true, false)]
    local procedure OnAfterShouldModifyActionMessageDating(ReservationEntry: Record "Reservation Entry"; var Result: Boolean)
    begin
        Result := Result or (ReservationEntry."Source Type" = Database::"Prod. Order Line");
    end;

    // Page Item Tracking Lines

    [EventSubscriber(ObjectType::Page, Page::"Item Tracking Lines", 'OnSetSourceSpecOnCollectTrackingData', '', true, false)]
    local procedure OnSetSourceSpecOnCollectTrackingData(var TrackingSpecification: Record "Tracking Specification"; var TempTrackingSpecification: Record "Tracking Specification" temporary; ExcludePostedEntries: Boolean; CurrentSignFactor: Integer; var SourceQuantity: Decimal)
    begin
        if TrackingSpecification."Source Type" = Database::"Prod. Order Line" then
            if TrackingSpecification."Source Subtype" = 3 then
                CollectPostedOutputEntries(TrackingSpecification, TempTrackingSpecification, SourceQuantity);
    end;

    local procedure CollectPostedOutputEntries(TrackingSpecification: Record "Tracking Specification"; var TempTrackingSpecification: Record "Tracking Specification" temporary; var SourceQuantity: Decimal)
    var
        ItemLedgerEntry: Record "Item Ledger Entry";
    begin
        // Used for collecting information about posted prod. order output from the created Item Ledger Entries.
        if TrackingSpecification."Source Type" <> Database::"Prod. Order Line" then
            exit;

        ItemLedgerEntry.SetCurrentKey("Order Type", "Order No.", "Order Line No.", "Entry Type");
        ItemLedgerEntry.SetRange("Order Type", ItemLedgerEntry."Order Type"::Production);
        ItemLedgerEntry.SetRange("Order No.", TrackingSpecification."Source ID");
        ItemLedgerEntry.SetRange("Order Line No.", TrackingSpecification."Source Prod. Order Line");
        ItemLedgerEntry.SetRange("Entry Type", ItemLedgerEntry."Entry Type"::Output);

        if ItemLedgerEntry.Find('-') then begin
            repeat
                TempTrackingSpecification := TrackingSpecification;
                TempTrackingSpecification."Entry No." := ItemLedgerEntry."Entry No.";
                TempTrackingSpecification."Item No." := ItemLedgerEntry."Item No.";
                TempTrackingSpecification.CopyTrackingFromItemLedgEntry(ItemLedgerEntry);
                TempTrackingSpecification."Quantity (Base)" := ItemLedgerEntry.Quantity;
                TempTrackingSpecification."Quantity Handled (Base)" := ItemLedgerEntry.Quantity;
                TempTrackingSpecification."Quantity Invoiced (Base)" := ItemLedgerEntry.Quantity;
                TempTrackingSpecification."Qty. per Unit of Measure" := ItemLedgerEntry."Qty. per Unit of Measure";
                TempTrackingSpecification.InitQtyToShip();
                ItemTrackingLines.RunOnBeforeCollectTempTrackingSpecificationInsert(TempTrackingSpecification, ItemLedgerEntry, TrackingSpecification);
                TempTrackingSpecification.Insert();
            until ItemLedgerEntry.Next() = 0;

            ItemLedgerEntry.CalcSums(Quantity);
            if ItemLedgerEntry.Quantity > SourceQuantity then
                SourceQuantity := ItemLedgerEntry.Quantity;
        end;

        OnAfterCollectPostedOutputEntries(ItemLedgerEntry, TempTrackingSpecification);
    end;

    [EventSubscriber(ObjectType::Page, Page::"Item Tracking Lines", 'OnCheckItemTrackingLineIsBoundForBarcodeScanning', '', true, false)]
    local procedure OnCheckItemTrackingLineIsBoundForBarcodeScanning(var TrackingSpecification: Record "Tracking Specification"; var Result: Boolean; IsHandled: Boolean)
    begin
        if TrackingSpecification."Source Type" = Database::"Prod. Order Line" then begin
            Result := not (TrackingSpecification."Qty. to Handle (Base)" < 0);
            IsHandled := true;
        end;
    end;

    [IntegrationEvent(false, false)]
    local procedure OnAfterCollectPostedOutputEntries(ItemLedgerEntry: Record "Item Ledger Entry"; var TempTrackingSpecification: Record "Tracking Specification" temporary)
    begin
    end;

    procedure ShowItemTrackingForProdOrderComp(Type: Integer; ID: Code[20]; ProdOrderLine: Integer; RefNo: Integer): Boolean
    var
        ItemLedgEntry: Record "Item Ledger Entry";
        TempItemLedgEntry: Record "Item Ledger Entry" temporary;
        Window: Dialog;
    begin
        Window.Open(CountingRecordsMsg);
        ItemLedgEntry.SetCurrentKey("Order Type", "Order No.", "Order Line No.",
          "Entry Type", "Prod. Order Comp. Line No.");
        ItemLedgEntry.SetRange("Order Type", ItemLedgEntry."Order Type"::Production);
        ItemLedgEntry.SetRange("Order No.", ID);
        ItemLedgEntry.SetRange("Order Line No.", ProdOrderLine);
        case Type of
            Database::"Prod. Order Line":
                begin
                    ItemLedgEntry.SetRange("Entry Type", ItemLedgEntry."Entry Type"::Output);
                    ItemLedgEntry.SetRange("Prod. Order Comp. Line No.", 0);
                end;
            Database::"Prod. Order Component":
                begin
                    ItemLedgEntry.SetRange("Entry Type", ItemLedgEntry."Entry Type"::Consumption);
                    ItemLedgEntry.SetRange("Prod. Order Comp. Line No.", RefNo);
                end;
            else
                exit(false);
        end;
        if ItemLedgEntry.FindSet() then
            repeat
                if ItemLedgEntry.TrackingExists() then begin
                    TempItemLedgEntry := ItemLedgEntry;
                    TempItemLedgEntry.Insert();
                end
            until ItemLedgEntry.Next() = 0;
        Window.Close();
        if TempItemLedgEntry.IsEmpty() then
            exit(false);

        PAGE.RunModal(PAGE::"Posted Item Tracking Lines", TempItemLedgEntry);
        exit(true);
    end;

    [EventSubscriber(ObjectType::Codeunit, Codeunit::"Item Tracking Doc. Management", 'OnAfterTableSignFactor', '', true, false)]
    local procedure OnAfterTableSignFactor(TableNo: Integer; var Sign: Integer);
    begin
        if TableNo = Database::"Prod. Order Component" then
            Sign := -1;
    end;

    [EventSubscriber(ObjectType::Codeunit, Codeunit::"Inventory Profile Offsetting", OnCheckIsSNSpecificTracking, '', true, false)]
    local procedure OnCheckIsSNSpecificTracking(ItemTrackingCode: Record "Item Tracking Code"; var SNSepecificTracking: Boolean)
    begin
        if SNSepecificTracking then
            exit;

        SNSepecificTracking := ItemTrackingCode."SN Manuf. Inbound Tracking" or ItemTrackingCode."SN Manuf. Outbound Tracking";
    end;

    [EventSubscriber(ObjectType::Codeunit, Codeunit::"Inventory Profile Offsetting", OnCheckIsLotSpecificTracking, '', true, false)]
    local procedure OnCheckIsLotSpecificTracking(ItemTrackingCode: Record "Item Tracking Code"; var LotSepecificTracking: Boolean)
    begin
        if LotSepecificTracking then
            exit;

        LotSepecificTracking := ItemTrackingCode."Lot Manuf. Inbound Tracking" or ItemTrackingCode."Lot Manuf. Outbound Tracking";
    end;

    [EventSubscriber(ObjectType::Codeunit, Codeunit::"Item Tracking Management", OnRegisterNewItemTrackingLinesOnBeforeCannotMatchItemTrackingError, '', true, false)]
    local procedure OnRegisterNewItemTrackingLinesOnBeforeCannotMatchItemTrackingError(var TempTrackingSpecification: Record "Tracking Specification" temporary; var QtyToHandleToNewRegister: Decimal; var QtyToHandleInItemTracking: Decimal; var QtyToHandleOnSourceDocLine: Decimal; var IsHandled: Boolean; var AllowWhseOverpick: Boolean)
    var
        Item: Record Item;
    begin
        if (TempTrackingSpecification."Source Type" <> Database::"Prod. Order Component") or (TempTrackingSpecification."Source Subtype" <> TempTrackingSpecification."Source Subtype"::"3") then
            exit;

        Item.SetLoadFields("Allow Whse. Overpick");
        Item.Get(TempTrackingSpecification."Item No.");
        AllowWhseOverpick := Item."Allow Whse. Overpick";
    end;

    [EventSubscriber(ObjectType::Codeunit, Codeunit::"Item Tracking Data Collection", OnFindRelatedParentTrkgSpecOnSetSourceFilters, '', true, false)]
    local procedure OnFindRelatedParentTrkgSpecOnSetSourceFilters(ItemJnlLine: Record "Item Journal Line"; var TempTrackingSpecification: Record "Tracking Specification" temporary)
    begin
        case ItemJnlLine."Entry Type" of
            ItemJnlLine."Entry Type"::Consumption:
                begin
                    if ItemJnlLine."Prod. Order Comp. Line No." = 0 then
                        exit;
                    TempTrackingSpecification.SetSourceFilter(Database::"Prod. Order Component", 3, ItemJnlLine."Order No.", ItemJnlLine."Prod. Order Comp. Line No.", false);
                    TempTrackingSpecification.SetSourceFilter('', ItemJnlLine."Order Line No.");
                end;
            ItemJnlLine."Entry Type"::Output:
                begin
                    TempTrackingSpecification.SetSourceFilter(Database::"Prod. Order Line", 3, ItemJnlLine."Order No.", -1, false);
                    TempTrackingSpecification.SetSourceFilter('', ItemJnlLine."Order Line No.");
                end;
        end;
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeSplitProdOrderLineForOutputPutAway(ProdOrderLine: Record "Prod. Order Line"; var TempProdOrdLineTrackingBuff: Record "Prod. Ord. Line Tracking Buff." temporary; SplitUpToQtyBase: Decimal; var IsHandled: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnAfterCopyProdOrderLineFieldsToTempProdOrdLineTrackingBuff(ProdOrderLine: Record "Prod. Order Line"; var TempProdOrdLineTrackingBuff: Record "Prod. Ord. Line Tracking Buff.")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnAfterUpdateQtySplitForPutAwayOnProdOrdLineTrackingBuffer(var TempProdOrdLineTrackingBuff: Record "Prod. Ord. Line Tracking Buff." temporary; ProdOrderLine: Record "Prod. Order Line"; ItemLedgerEntry: Record "Item Ledger Entry"; var QtyBaseAvailableToPutAway: Decimal; var RemainingHandledQtyBase: Decimal)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnAfterSplitProdOrderLineForOutputPutAway(ProdOrderLine: Record "Prod. Order Line"; var TempProdOrdLineTrackingBuff: Record "Prod. Ord. Line Tracking Buff." temporary)
    begin
    end;
}