Codeunit 7314 Warehouse Availability Mgt.

App
Base Application
Namespace
Microsoft.Warehouse.Availability
Versions
17-28

Procedures, 28Events, 26Obsolete, 4

Versions171819202122232425262728

Source29

Source in 29

src/Layers/W1/BaseApp/Warehouse/Availability/WarehouseAvailabilityMgt.Codeunit.al1212 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.Warehouse.Availability;

using Microsoft.Assembly.Document;
using Microsoft.Inventory.Availability;
using Microsoft.Inventory.Item;
using Microsoft.Inventory.Ledger;
using Microsoft.Inventory.Location;
using Microsoft.Inventory.Tracking;
using Microsoft.Projects.Project.Job;
using Microsoft.Projects.Project.Planning;
using Microsoft.Warehouse.Activity;
using Microsoft.Warehouse.Document;
using Microsoft.Warehouse.History;
using Microsoft.Warehouse.Ledger;
using Microsoft.Warehouse.Structure;
using Microsoft.Warehouse.Tracking;
using Microsoft.Warehouse.Worksheet;
using System.Reflection;

codeunit 7314 "Warehouse Availability Mgt."
{

    trigger OnRun()
    begin
    end;

    procedure CalcLineReservedQtyOnInvt(SourceType: Integer; SourceSubType: Option; SourceNo: Code[20]; SourceLineNo: Integer; SourceSubLineNo: Integer; HandleResPickAndShipQty: Boolean; var WarehouseActivityLine: Record "Warehouse Activity Line"): Decimal
    var
        DummyItemTrackingSetup: Record "Item Tracking Setup";
    begin
        exit(
            CalcLineReservedQtyOnInvt(SourceType, SourceSubType, SourceNo, SourceLineNo, SourceSubLineNo, HandleResPickAndShipQty,
            DummyItemTrackingSetup, WarehouseActivityLine));
    end;

    procedure CalcLineReservedQtyOnInvt(SourceType: Integer; SourceSubType: Option; SourceNo: Code[20]; SourceLineNo: Integer; SourceSubLineNo: Integer; HandleResPickAndShipQty: Boolean; WhseItemTrackingSetup: Record "Item Tracking Setup"; var WarehouseActivityLine: Record "Warehouse Activity Line"): Decimal
    var
        ReservEntry: Record "Reservation Entry";
        ReservEntry2: Record "Reservation Entry";
        ReservQtyonInvt: Decimal;
        PickQty: Decimal;
        IsHandled: Boolean;
    begin
        // Returns the reserved quantity against ILE for the demand line
        case SourceType of
            Database::Job, Database::"Job Planning Line":
                begin
                    ReservEntry.SetSourceFilter(
                      Database::"Job Planning Line", "Job Planning Line Status"::Order.AsInteger(), SourceNo, SourceLineNo, true);
                    ReservEntry.SetSourceFilter('', 0);
                end;
            else begin
                IsHandled := false;
                OnCalcLineReservedQtyOnInvtOnSetSourceFilters(ReservEntry, SourceType, SourceSubType, SourceNo, SourceLineNo, SourceSubLineNo, IsHandled);
                if not IsHandled then
                    ReservEntry.SetSourceFilter(SourceType, SourceSubType, SourceNo, SourceLineNo, true);
            end;
        end;
        ReservEntry.SetRange("Reservation Status", ReservEntry."Reservation Status"::Reservation);
        if ReservEntry.Find('-') then
            repeat
                ReservEntry2.Reset();
                ReservEntry2.SetRange("Entry No.", ReservEntry."Entry No.");
                ReservEntry2.SetRange(Positive, true);
                ReservEntry2.SetRange("Source Type", Database::"Item Ledger Entry");
                ReservEntry2.SetRange("Reservation Status", ReservEntry2."Reservation Status"::Reservation);
                ReservEntry2.SetTrackingFilterFromItemTrackingSetupIfNotBlank(WhseItemTrackingSetup);
                ReservEntry2.CalcSums("Quantity (Base)");
                ReservQtyonInvt += ReservEntry2."Quantity (Base)";
            until ReservEntry.Next() = 0;

        if HandleResPickAndShipQty then begin
            PickQty := CalcRegisteredAndOutstandingPickQty(ReservEntry, WarehouseActivityLine);
            if ReservQtyonInvt > PickQty then
                ReservQtyonInvt -= PickQty
            else
                ReservQtyonInvt := 0;
        end;

        OnAfterCalcLineReservedQtyOnInvt(ReservEntry, ReservQtyonInvt);
        exit(ReservQtyonInvt);
    end;

    procedure CalcReservQtyOnPicksShips(LocationCode: Code[10]; ItemNo: Code[20]; VariantCode: Code[10]; var WarehouseActivityLine: Record "Warehouse Activity Line"): Decimal
    var
        TempTrackingSpecification: Record "Tracking Specification" temporary;
    begin
        exit(CalcReservQtyOnPicksShipsWithItemTracking(
            WarehouseActivityLine, TempTrackingSpecification, LocationCode, ItemNo, VariantCode));
    end;

    procedure CalcReservQtyOnPicksShipsWithItemTracking(var WarehouseActivityLine: Record "Warehouse Activity Line"; var TrackingSpecification: Record "Tracking Specification"; LocationCode: Code[10]; ItemNo: Code[20]; VariantCode: Code[10]) Result: Decimal
    var
        TempReservEntryBuffer: Record "Reservation Entry Buffer" temporary;
        CalcRsvQtyOnPicksShipsWithIT: Query CalcRsvQtyOnPicksShipsWithIT;
        ResPickShipQty: Decimal;
        QtyPicked: Decimal;
        QtyToPick: Decimal;
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeCalcReservQtyOnPicksShipsWithItemTracking(LocationCode, ItemNo, VariantCode, Result, IsHandled);
        if IsHandled then
            exit(Result);

        CalcRsvQtyOnPicksShipsWithIT.SetRange(Item_No_, ItemNo);
        CalcRsvQtyOnPicksShipsWithIT.SetRange(Variant_Code, VariantCode);
        CalcRsvQtyOnPicksShipsWithIT.SetRange(Location_Code, LocationCode);
        CalcRsvQtyOnPicksShipsWithIT.SetRange(Reservation_Status, "Reservation Status"::Reservation);
        CalcRsvQtyOnPicksShipsWithIT.SetRange(Positive, false);

        CalcRsvQtyOnPicksShipsWithIT.SetRange(Positive_2, true);
        CalcRsvQtyOnPicksShipsWithIT.SetRange(Source_Type_2, Database::"Item Ledger Entry");

        if TrackingSpecification."Serial No." <> '' then
            CalcRsvQtyOnPicksShipsWithIT.SetRange(Serial_No_, TrackingSpecification."Serial No.");
        if TrackingSpecification."Lot No." <> '' then
            CalcRsvQtyOnPicksShipsWithIT.SetRange(Lot_No_, TrackingSpecification."Lot No.");
        if TrackingSpecification."Package No." <> '' then
            CalcRsvQtyOnPicksShipsWithIT.SetRange(Package_No_, TrackingSpecification."Package No.");

        OnBeforeOpenCalcRsvQtyOnPickShipWithITQuery(CalcRsvQtyOnPicksShipsWithIT);
        CalcRsvQtyOnPicksShipsWithIT.Open();

        while CalcRsvQtyOnPicksShipsWithIT.Read() do begin
            TempReservEntryBuffer."Quantity (Base)" := CalcRsvQtyOnPicksShipsWithIT.Quantity__Base_;
            TempReservEntryBuffer."Source Batch Name" := CalcRsvQtyOnPicksShipsWithIT.Source_Batch_Name;
            TempReservEntryBuffer."Source Type" := CalcRsvQtyOnPicksShipsWithIT.Source_Type;
            TempReservEntryBuffer."Source Subtype" := CalcRsvQtyOnPicksShipsWithIT.Source_Subtype;
            TempReservEntryBuffer."Source ID" := CalcRsvQtyOnPicksShipsWithIT.Source_ID;
            TempReservEntryBuffer."Source Prod. Order Line" := CalcRsvQtyOnPicksShipsWithIT.Source_Prod__Order_Line;
            TempReservEntryBuffer."Source Ref. No." := CalcRsvQtyOnPicksShipsWithIT.Source_Ref__No_;

            if TempReservEntryBuffer.Find() then begin
                TempReservEntryBuffer."Quantity (Base)" += CalcRsvQtyOnPicksShipsWithIT.Quantity__Base_;
                TempReservEntryBuffer.Modify();
            end else
                TempReservEntryBuffer.Insert();
        end;

        if TempReservEntryBuffer.FindSet() then
            repeat
                QtyPicked :=
                  CalcQtyRegisteredPick(
                    LocationCode, TempReservEntryBuffer."Source Type", TempReservEntryBuffer."Source Subtype", TempReservEntryBuffer."Source ID", TempReservEntryBuffer."Source Ref. No.", TempReservEntryBuffer."Source Prod. Order Line");
                ValidateQtyPickedInShipmentBin(QtyPicked, LocationCode, ItemNo, VariantCode, TrackingSpecification, TempReservEntryBuffer."Source Type");
                QtyToPick :=
                  CalcQtyOutstandingPick(
                    TempReservEntryBuffer."Source Type", TempReservEntryBuffer."Source Subtype", TempReservEntryBuffer."Source ID", TempReservEntryBuffer."Source Ref. No.", TempReservEntryBuffer."Source Prod. Order Line", WarehouseActivityLine);
                if -TempReservEntryBuffer."Quantity (Base)" > QtyPicked + QtyToPick then
                    ResPickShipQty += (QtyPicked + QtyToPick)
                else
                    ResPickShipQty += -TempReservEntryBuffer."Quantity (Base)";
            until TempReservEntryBuffer.Next() = 0;
        exit(ResPickShipQty);
    end;

    procedure CalcLineReservQtyOnPicksShips(SourceType: Integer; SourceSubType: Option; SourceID: Code[20]; SourceRefNo: Integer; SourceProdOrderLine: Integer; ReservedQtyBase: Decimal; var WarehouseActivityLine: Record "Warehouse Activity Line"): Decimal
    var
        PickedNotYetShippedQty: Decimal;
        OutstandingQtyOnPickLines: Decimal;
    begin
        // Returns the reserved part of the sum of outstanding quantity on pick lines and
        // quantity on shipment lines picked but not yet shipped for a given demand line
        PickedNotYetShippedQty := CalcQtyPickedOnWhseShipmentLine(SourceType, SourceSubType, SourceID, SourceRefNo);
        OnCalcLineReservQtyOnPicksShipsOnAfterCalcPickedNotYetShippedQty(SourceType, SourceSubType, SourceID, SourceProdOrderLine, SourceRefNo, PickedNotYetShippedQty);

        OutstandingQtyOnPickLines :=
          CalcQtyOutstandingPick(SourceType, SourceSubType, SourceID, SourceRefNo, SourceProdOrderLine, WarehouseActivityLine);

        if -ReservedQtyBase > (PickedNotYetShippedQty + OutstandingQtyOnPickLines) then
            exit(PickedNotYetShippedQty + OutstandingQtyOnPickLines);

        exit(-ReservedQtyBase);
    end;

    procedure CalcInvtAvailQty(Item: Record Item; Location: Record Location; VariantCode: Code[10]; var WarehouseActivityLine: Record "Warehouse Activity Line") Result: Decimal
    var
        QtyReceivedNotAvail: Decimal;
        QtyAssgndtoPick: Decimal;
        QtyShipped: Decimal;
        QtyReservedOnPickShip: Decimal;
        QtyOnDedicatedBins: Decimal;
        ReservedQtyOnInventory: Decimal;
        SubTotal: Decimal;
        QtyPicked: Decimal;
        IsHandled: Boolean;
    begin
        // Returns the available quantity to pick for pick/ship/receipt/put-away
        // locations without directed put-away and pick
        IsHandled := false;
        OnBeforeCalcInvtAvailQty(Item, Location, VariantCode, WarehouseActivityLine, Result, IsHandled);
        if IsHandled then
            exit(Result);

        Item.SetRange("Location Filter", Location.Code);
        Item.SetRange("Variant Filter", VariantCode);
        if Location."Require Shipment" then
            Item.CalcFields(Inventory, "Reserved Qty. on Inventory", "Qty. Picked")
        else
            Item.CalcFields(Inventory, "Reserved Qty. on Inventory");

        if Location."Require Receive" and Location."Require Put-away" then
            QtyReceivedNotAvail := CalcQtyRcvdNotAvailable(Location.Code, Item."No.", VariantCode);

        QtyAssgndtoPick := CalcQtyAssgndtoPick(Location, Item."No.", VariantCode, '');

        if Location.RequireShipment(Location.Code) then
            QtyShipped := CalcQtyShipped(Location, Item."No.", VariantCode);
        QtyReservedOnPickShip := CalcReservQtyOnPicksShips(Location.Code, Item."No.", VariantCode, WarehouseActivityLine);
        // exclude quantity on dedicated bins
        QtyOnDedicatedBins := CalcQtyOnDedicatedBins(Location.Code, Item."No.", VariantCode);
        if (QtyOnDedicatedBins > 0) and Location."Require Receive" and Location."Require Put-away" then
            QtyReceivedNotAvail -= CalcQtyAssignedToPutAway(Location.Code, Item."No.", VariantCode, true);

        ReservedQtyOnInventory := Item."Reserved Qty. on Inventory";
        OnAfterCalcReservedQtyOnInventory(Item, ReservedQtyOnInventory, Location);

        QtyPicked := Item."Qty. Picked";
        OnAfterCalcQtyPicked(Item, QtyPicked, Location);

        // If all inventory is reserved, then only the qty on non-dedicated bins is available
        if (Item.Inventory - Abs(ReservedQtyOnInventory) = 0) and (QtyOnDedicatedBins > 0) then
            exit(Item.Inventory - QtyReceivedNotAvail - QtyAssgndtoPick - QtyPicked + QtyShipped - QtyOnDedicatedBins);
        // The reserved qty might exceed the qty available in warehouse and thereby
        // having reserved from the qty not yet put-away
        if (Item.Inventory - QtyReceivedNotAvail - QtyAssgndtoPick - QtyPicked + QtyShipped - QtyOnDedicatedBins) <
           (Abs(ReservedQtyOnInventory) - QtyReservedOnPickShip)
        then
            exit(0);

        SubTotal :=
          Item.Inventory - QtyReceivedNotAvail - QtyAssgndtoPick -
          Abs(ReservedQtyOnInventory) - QtyPicked + QtyShipped;

        exit(SubTotal);
    end;

    local procedure CalcQtyRcvdNotAvailable(LocationCode: Code[10]; ItemNo: Code[20]; VariantCode: Code[10]): Decimal
    var
        PostedWhseReceiptLine: Record "Posted Whse. Receipt Line";
        TempBin: Record Bin temporary;
        WarehouseEntry: Record "Warehouse Entry";
        DummyItemTrackingSetup: Record "Item Tracking Setup";
        QtyRcvdNotAvailable: Decimal;
        QtyAvailToPutAway: Decimal;
    begin
        // Returns the quantity received but not yet put-away for a given item
        // for pick/ship/receipt/put-away locations without directed put-away and pick
        // with consideration of actually available quantity to put-away in receive bins
        PostedWhseReceiptLine.SetCurrentKey("Item No.", "Location Code", "Variant Code");
        PostedWhseReceiptLine.SetRange("Item No.", ItemNo);
        PostedWhseReceiptLine.SetRange("Location Code", LocationCode);
        PostedWhseReceiptLine.SetRange("Variant Code", VariantCode);
        OnCalcQtyRcvdNotAvailableOnBeforeCalcSums(PostedWhseReceiptLine, LocationCode, ItemNo, VariantCode);
        PostedWhseReceiptLine.CalcSums("Qty. (Base)", "Qty. Put Away (Base)");
        QtyRcvdNotAvailable := PostedWhseReceiptLine."Qty. (Base)" - PostedWhseReceiptLine."Qty. Put Away (Base)";

        WarehouseEntry.SetRange("Location Code", LocationCode);
        if (QtyRcvdNotAvailable > 0) and not WarehouseEntry.IsEmpty() then begin
            PostedWhseReceiptLine.FindSet();
            repeat
                TempBin."Location Code" := PostedWhseReceiptLine."Location Code";
                TempBin.Code := PostedWhseReceiptLine."Bin Code";
                if TempBin.Insert() then;
            until PostedWhseReceiptLine.Next() = 0;

            if TempBin.FindSet() then
                repeat
                    QtyAvailToPutAway +=
                        CalcQtyOnBin(TempBin."Location Code", TempBin.Code, PostedWhseReceiptLine."Item No.", PostedWhseReceiptLine."Variant Code", DummyItemTrackingSetup);
                until TempBin.Next() = 0;

            if QtyAvailToPutAway < QtyRcvdNotAvailable then
                QtyRcvdNotAvailable := QtyAvailToPutAway;
        end;

        OnAfterCalcQtyRcvdNotAvailable(PostedWhseReceiptLine, LocationCode, ItemNo, VariantCode, QtyRcvdNotAvailable);
        exit(QtyRcvdNotAvailable);
    end;

    procedure CalcQtyAssgndtoPick(Location: Record Location; ItemNo: Code[20]; VariantCode: Code[10]; BinTypeFilter: Text[250]): Decimal
    var
        WarehouseActivityLine: Record "Warehouse Activity Line";
    begin
        // Returns the outstanding quantity on pick lines for a given item
        // for a pick location without directed put-away and pick
        WarehouseActivityLine.SetCurrentKey(
            "Item No.", "Location Code", "Activity Type", "Bin Type Code",
            "Unit of Measure Code", "Variant Code", "Breakbulk No.");
        WarehouseActivityLine.SetRange("Item No.", ItemNo);
        WarehouseActivityLine.SetRange("Location Code", Location.Code);
        WarehouseActivityLine.SetRange("Variant Code", VariantCode);
        WarehouseActivityLine.SetRange("Bin Type Code", BinTypeFilter);
        if Location."Bin Mandatory" then
            WarehouseActivityLine.SetRange("Action Type", WarehouseActivityLine."Action Type"::Take)
        else begin
            WarehouseActivityLine.SetRange("Action Type", WarehouseActivityLine."Action Type"::" ");
            WarehouseActivityLine.SetRange("Breakbulk No.", 0);
        end;
        if Location."Require Shipment" then
            WarehouseActivityLine.SetRange("Activity Type", WarehouseActivityLine."Activity Type"::Pick)
        else begin
            WarehouseActivityLine.SetRange("Activity Type", WarehouseActivityLine."Activity Type"::"Invt. Pick");
            WarehouseActivityLine.SetRange("Assemble to Order", false);
        end;
        OnCalcQtyAssgndtoPickOnAfterSetFilters(WarehouseActivityLine, Location, ItemNo, VariantCode, BinTypeFilter);
        WarehouseActivityLine.CalcSums("Qty. Outstanding (Base)");
        exit(WarehouseActivityLine."Qty. Outstanding (Base)");
    end;

    procedure CalcQtyAssignedToMove(WhseWorksheetLine: Record "Whse. Worksheet Line"; WhseItemTrackingLine: Record "Whse. Item Tracking Line"): Decimal
    var
        WarehouseActivityLine: Record "Warehouse Activity Line";
    begin
        WarehouseActivityLine.SetRange("Item No.", WhseWorksheetLine."Item No.");
        WarehouseActivityLine.SetRange("Location Code", WhseWorksheetLine."Location Code");
        WarehouseActivityLine.SetRange("Activity Type", "Warehouse Activity Type"::Movement);
        WarehouseActivityLine.SetRange("Variant Code", WhseWorksheetLine."Variant Code");
        WarehouseActivityLine.SetRange("Action Type", WarehouseActivityLine."Action Type"::Take);
        WarehouseActivityLine.SetRange("Bin Code", WhseWorksheetLine."From Bin Code");
        WarehouseActivityLine.SetTrackingFilterFromWhseItemTrackingLineIfNotBlank(WhseItemTrackingLine);
        WarehouseActivityLine.CalcSums("Qty. Outstanding (Base)");
        exit(WarehouseActivityLine."Qty. Outstanding (Base)");
    end;

    local procedure CalcQtyAssignedToPutAway(LocationCode: Code[10]; ItemNo: Code[20]; VariantCode: Code[10]; DedicatedOnly: Boolean): Decimal
    var
        WarehouseActivityLine: Record "Warehouse Activity Line";
    begin
        WarehouseActivityLine.SetRange("Item No.", ItemNo);
        WarehouseActivityLine.SetRange("Location Code", LocationCode);
        WarehouseActivityLine.SetRange("Variant Code", VariantCode);
        WarehouseActivityLine.SetRange("Activity Type", WarehouseActivityLine."Activity Type"::"Put-away");
        WarehouseActivityLine.SetRange("Whse. Document Type", WarehouseActivityLine."Whse. Document Type"::Receipt);
        WarehouseActivityLine.SetFilter("Action Type", '%1|%2', WarehouseActivityLine."Action Type"::Take, WarehouseActivityLine."Action Type"::" ");
        if DedicatedOnly then
            WarehouseActivityLine.SetRange(Dedicated, true);
        WarehouseActivityLine.CalcSums("Qty. Outstanding (Base)");
        exit(WarehouseActivityLine."Qty. Outstanding (Base)");
    end;

    procedure CalcQtyAssgndOnWksh(DefWhseWkshLine: Record "Whse. Worksheet Line"; RespectUOMCode: Boolean; ExcludeLine: Boolean): Decimal
    var
        WhseWorksheetLine: Record "Whse. Worksheet Line";
    begin
        WhseWorksheetLine.SetCurrentKey(
              "Item No.", "Location Code", "Worksheet Template Name", "Variant Code", "Unit of Measure Code");
        WhseWorksheetLine.SetRange("Item No.", DefWhseWkshLine."Item No.");
        WhseWorksheetLine.SetRange("Location Code", DefWhseWkshLine."Location Code");
        WhseWorksheetLine.SetRange("Worksheet Template Name", DefWhseWkshLine."Worksheet Template Name");
        WhseWorksheetLine.SetRange("Variant Code", DefWhseWkshLine."Variant Code");
        if RespectUOMCode then
            WhseWorksheetLine.SetRange("Unit of Measure Code", DefWhseWkshLine."Unit of Measure Code");
        WhseWorksheetLine.CalcSums("Qty. to Handle (Base)");
        if ExcludeLine and DefWhseWkshLine.Find() then
            WhseWorksheetLine."Qty. to Handle (Base)" := WhseWorksheetLine."Qty. to Handle (Base)" - DefWhseWkshLine."Qty. to Handle (Base)";
        exit(WhseWorksheetLine."Qty. to Handle (Base)");
    end;

    local procedure CalcQtyShipped(Location: Record Location; ItemNo: Code[20]; VariantCode: Code[10]): Decimal
    var
        WarehouseShipmentLine: Record "Warehouse Shipment Line";
    begin
        WarehouseShipmentLine.SetRange("Item No.", ItemNo);
        WarehouseShipmentLine.SetRange("Location Code", Location.Code);
        WarehouseShipmentLine.SetRange("Variant Code", VariantCode);
        WarehouseShipmentLine.CalcSums("Qty. Shipped (Base)");
        exit(WarehouseShipmentLine."Qty. Shipped (Base)");
    end;

    procedure CalcQtyOnDedicatedBins(LocationCode: Code[10]; ItemNo: Code[20]; VariantCode: Code[10]): Decimal
    var
        DummyItemTrackingSetup: Record "Item Tracking Setup";
    begin
        exit(CalcQtyOnDedicatedBins(LocationCode, ItemNo, VariantCode, DummyItemTrackingSetup));
    end;

    procedure CalcQtyOnDedicatedBins(LocationCode: Code[10]; ItemNo: Code[20]; VariantCode: Code[10]; WhseItemTrackingSetup: Record "Item Tracking Setup"): Decimal
    var
        WarehouseEntry: Record "Warehouse Entry";
        QtyOnDedicatedBin: Decimal;
    begin
        WarehouseEntry.ReadIsolation(IsolationLevel::ReadUnCommitted);
        WarehouseEntry.SetRange("Item No.", ItemNo);
        WarehouseEntry.SetRange("Location Code", LocationCode);
        WarehouseEntry.SetRange("Variant Code", VariantCode);
        WarehouseEntry.SetRange(Dedicated, true);
        WarehouseEntry.SetTrackingFilterFromItemTrackingSetupIfNotBlank(WhseItemTrackingSetup);
        WarehouseEntry.CalcSums("Qty. (Base)");
        QtyOnDedicatedBin := WarehouseEntry."Qty. (Base)";

        OnAfterCalcQtyOnDedicatedBins(LocationCode, ItemNo, VariantCode, WarehouseEntry, WhseItemTrackingSetup, QtyOnDedicatedBin);
        exit(QtyOnDedicatedBin);
    end;

    procedure CalcQtyOnBin(LocationCode: Code[10]; BinCode: Code[20]; ItemNo: Code[20]; VariantCode: Code[10]; WhseItemTrackingSetup: Record "Item Tracking Setup"): Decimal
    var
        WarehouseEntry: Record "Warehouse Entry";
    begin
        if BinCode = '' then
            exit(0);

        WarehouseEntry.SetLoadFields("Qty. (Base)");
        WarehouseEntry.SetRange("Item No.", ItemNo);
        WarehouseEntry.SetRange("Bin Code", BinCode);
        WarehouseEntry.SetRange("Location Code", LocationCode);
        WarehouseEntry.SetRange("Variant Code", VariantCode);
        WarehouseEntry.SetTrackingFilterFromItemTrackingSetupIfNotBlank(WhseItemTrackingSetup);
        WarehouseEntry.CalcSums("Qty. (Base)");
        exit(WarehouseEntry."Qty. (Base)");
    end;

    procedure CalcQtyOnBlockedITOrOnBlockedOutbndBins(LocationCode: Code[10]; ItemNo: Code[20]; VariantCode: Code[10]; WhseItemTrackingSetup: Record "Item Tracking Setup") QtyBlocked: Decimal
    var
        BinContent: Record "Bin Content";
    begin
        BinContent.SetCurrentKey("Location Code", "Item No.", "Variant Code");
        BinContent.SetRange("Location Code", LocationCode);
        BinContent.SetRange("Item No.", ItemNo);
        BinContent.SetRange("Variant Code", VariantCode);
        BinContent.SetTrackingFilterFromItemTrackingSetupifNotBlankIfRequired(WhseItemTrackingSetup);
        if BinContent.FindSet() then
            repeat
                if BinContent."Block Movement" in [BinContent."Block Movement"::All, BinContent."Block Movement"::Outbound] then begin
                    BinContent.CalcFields("Quantity (Base)");
                    QtyBlocked += BinContent."Quantity (Base)";
                end else
                    QtyBlocked += BinContent.CalcQtyWithBlockedItemTracking();
                OnCalcQtyOnBlockedITOrOnBlockedOutbndBinsOnBeforeNext(BinContent, WhseItemTrackingSetup, QtyBlocked);
            until BinContent.Next() = 0;

        OnAfterCalcQtyOnBlockedITOrOnBlockedOutbndBins(LocationCode, ItemNo, VariantCode, WhseItemTrackingSetup, QtyBlocked);
    end;

    procedure CalcQtyOnOutboundBins(LocationCode: Code[10]; ItemNo: Code[20]; VariantCode: Code[10]; WhseItemTrackingSetup: Record "Item Tracking Setup"; ExcludeDedicatedBinContent: Boolean) QtyOnOutboundBins: Decimal
    var
        WhseShptLine: Record "Warehouse Shipment Line";
        Location: Record Location;
        TempBinContentBuffer: Record "Bin Content Buffer" temporary;
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeCalcQtyOnOutboundBins(LocationCode, ItemNo, VariantCode, WhseItemTrackingSetup, ExcludeDedicatedBinContent, IsHandled, QtyOnOutboundBins);
        if IsHandled then
            exit(QtyOnOutboundBins);

        Location.Get(LocationCode);
        if not Location."Require Pick" then
            exit(0);

        if Location."Directed Put-away and Pick" then
            QtyOnOutboundBins :=
                CalcQtyOnOutboundBinsOnDirectedPutAwayPickLocation(
                  LocationCode, ItemNo, VariantCode, WhseItemTrackingSetup, ExcludeDedicatedBinContent)
        else
            if Location."Bin Mandatory" and WhseItemTrackingSetup.TrackingExists() then begin
                GetOutboundBinsOnBasicWarehouseLocation(
                  TempBinContentBuffer, LocationCode, ItemNo, VariantCode, WhseItemTrackingSetup);
                ExcludeDedicatedBinContentFromTempBinContentBuffer(TempBinContentBuffer, ExcludeDedicatedBinContent);
                TempBinContentBuffer.CalcSums("Qty. Outstanding (Base)");
                QtyOnOutboundBins := TempBinContentBuffer."Qty. Outstanding (Base)";
            end else begin
                WhseShptLine.SetRange("Item No.", ItemNo);
                WhseShptLine.SetRange("Location Code", LocationCode);
                WhseShptLine.SetRange("Variant Code", VariantCode);
                WhseShptLine.CalcSums("Qty. Picked (Base)", "Qty. Shipped (Base)");
                QtyOnOutboundBins := WhseShptLine."Qty. Picked (Base)" - WhseShptLine."Qty. Shipped (Base)";
            end;
    end;

    local procedure CalcQtyOnOutboundBinsOnDirectedPutAwayPickLocation(LocationCode: Code[10]; ItemNo: Code[20]; VariantCode: Code[10]; WhseItemTrackingSetup: Record "Item Tracking Setup"; ExcludeDedicatedBinContent: Boolean) QtyOnOutboundBins: Decimal
    var
        WhseEntry: Record "Warehouse Entry";
        Location: Record Location;
        CreatePick: Codeunit "Create Pick";
    begin
        Location.Get(LocationCode);
        if not Location."Directed Put-away and Pick" then
            exit(0);

        WhseEntry.ReadIsolation(IsolationLevel::ReadUnCommitted);
        WhseEntry.SetCalculationFilters(ItemNo, LocationCode, VariantCode, WhseItemTrackingSetup, ExcludeDedicatedBinContent);
        WhseEntry.SetFilter("Bin Type Code", CreatePick.GetBinTypeFilter(1)); // Shipping area
        WhseEntry.CalcSums("Qty. (Base)");
        QtyOnOutboundBins := WhseEntry."Qty. (Base)";
        if Location."Adjustment Bin Code" <> '' then begin
            WhseEntry.SetRange("Bin Type Code");
            WhseEntry.SetRange("Bin Code", Location."Adjustment Bin Code");
            WhseEntry.CalcSums("Qty. (Base)");
            QtyOnOutboundBins += WhseEntry."Qty. (Base)";
        end;
    end;

    procedure GetOutboundBinsOnBasicWarehouseLocation(var TempBinContentBuffer: Record "Bin Content Buffer" temporary; LocationCode: Code[10]; ItemNo: Code[20]; VariantCode: Code[10]; WhseItemTrackingSetup: Record "Item Tracking Setup")
    var
        Location: Record Location;
        WarehouseEntry: Record "Warehouse Entry";
        QtyInBin: Decimal;
    begin
        TempBinContentBuffer.Reset();
        TempBinContentBuffer.DeleteAll();

        Location.Get(LocationCode);
        if not Location."Bin Mandatory" then
            exit;
        if not Location."Require Pick" or Location."Directed Put-away and Pick" then
            exit;

        WhseItemTrackingSetup."Serial No. Required" := true;
        WhseItemTrackingSetup."Lot No. Required" := true;
        WhseItemTrackingSetup."Package No. Required" := true;

        WarehouseEntry.SetLoadFields("Bin Code");
        WarehouseEntry.SetCurrentKey("Bin Code");
        WarehouseEntry.SetCalculationFilters(ItemNo, LocationCode, VariantCode, WhseItemTrackingSetup, false);
        WarehouseEntry.SetFilter("Whse. Document Type", '%1|%2|%3',
            WarehouseEntry."Whse. Document Type"::Shipment,
            WarehouseEntry."Whse. Document Type"::Assembly,
            WarehouseEntry."Whse. Document Type"::Job);
        WarehouseEntry.SetRange("Reference Document", WarehouseEntry."Reference Document"::Pick);
        WarehouseEntry.SetFilter("Qty. (Base)", '>%1', 0);
        OnGetOutboundBinsOnBasicWarehouseLocationOnAfterSetWarehouseEntryFilters(WarehouseEntry);
        WarehouseEntry.SetLoadFields("Bin Code", Dedicated);
        if WarehouseEntry.FindSet() then
            repeat
                WarehouseEntry.SetRange("Bin Code", WarehouseEntry."Bin Code");
                QtyInBin := CalcQtyOnBin(LocationCode, WarehouseEntry."Bin Code", ItemNo, VariantCode, WhseItemTrackingSetup);
                if QtyInBin > 0 then begin
                    TempBinContentBuffer.Init();
                    TempBinContentBuffer."Location Code" := LocationCode;
                    TempBinContentBuffer."Bin Code" := WarehouseEntry."Bin Code";
                    TempBinContentBuffer.Dedicated := WarehouseEntry.Dedicated;
                    TempBinContentBuffer."Item No." := ItemNo;
                    TempBinContentBuffer."Variant Code" := VariantCode;
                    TempBinContentBuffer."Qty. Outstanding (Base)" := QtyInBin;
                    TempBinContentBuffer.Insert();
                end;

                WarehouseEntry.FindLast();
                WarehouseEntry.SetRange("Bin Code");
            until WarehouseEntry.Next() = 0;

        if Location."Shipment Bin Code" <> '' then begin
            TempBinContentBuffer.SetRange("Bin Code", Location."Shipment Bin Code");
            if TempBinContentBuffer.IsEmpty() then begin
                QtyInBin := CalcQtyOnBin(LocationCode, Location."Shipment Bin Code", ItemNo, VariantCode, WhseItemTrackingSetup);
                if QtyInBin > 0 then begin
                    TempBinContentBuffer.Init();
                    TempBinContentBuffer."Location Code" := LocationCode;
                    TempBinContentBuffer."Bin Code" := Location."Shipment Bin Code";
                    TempBinContentBuffer."Item No." := ItemNo;
                    TempBinContentBuffer."Variant Code" := VariantCode;
                    TempBinContentBuffer."Qty. Outstanding (Base)" := QtyInBin;
                    TempBinContentBuffer.Insert();
                end;
            end;
        end;

        TempBinContentBuffer.Reset();

        OnAfterGetOutboundBinsOnBasicWarehouseLocation(Location, TempBinContentBuffer, LocationCode, ItemNo, VariantCode, WhseItemTrackingSetup);
    end;

    procedure CalcQtyOnSpecialBinsOnLocation(LocationCode: Code[10]; ItemNo: Code[20]; VariantCode: Code[10]; WhseItemTrackingSetup: Record "Item Tracking Setup"; var TempBinContentBufferExcluded: Record "Bin Content Buffer" temporary) QtyOnSpecialBins: Decimal
    var
        SpecialBins: List of [Code[20]];
        SpecialBin: Code[20];
    begin
        GetSpecialBins(SpecialBins, LocationCode);
        foreach SpecialBin in SpecialBins do begin
            TempBinContentBufferExcluded.SetRange("Location Code", LocationCode);
            TempBinContentBufferExcluded.SetRange("Bin Code", SpecialBin);
            if TempBinContentBufferExcluded.IsEmpty() then
                QtyOnSpecialBins +=
                    CalcQtyOnBin(LocationCode, SpecialBin, ItemNo, VariantCode, WhseItemTrackingSetup);
        end;
    end;

    local procedure GetSpecialBins(var SpecialBins: List of [Code[20]]; LocationCode: Code[10])
    var
        Location: Record Location;
    begin
        Location.Get(LocationCode);
        if Location."To-Assembly Bin Code" <> '' then
            SpecialBins.Add(Location."To-Assembly Bin Code");
        if (Location."Open Shop Floor Bin Code" <> '') and not SpecialBins.Contains(Location."Open Shop Floor Bin Code") then
            SpecialBins.Add(Location."Open Shop Floor Bin Code");
        if (Location."To-Production Bin Code" <> '') and not SpecialBins.Contains(Location."To-Production Bin Code") then
            SpecialBins.Add(Location."To-Production Bin Code");
        if Location."To-Job Bin Code" <> '' then
            SpecialBins.Add(Location."To-Job Bin Code");

        OnAfterGetSpecialBins(Location, SpecialBins);
    end;

    procedure CalcResidualPickedQty(var WhseEntry: Record "Warehouse Entry") Result: Decimal
    var
        WhseEntry2: Record "Warehouse Entry";
    begin
        WhseEntry2.ReadIsolation(IsolationLevel::ReadUnCommitted);
        if WhseEntry.FindSet() then
            repeat
                WhseEntry.SetRange("Bin Code", WhseEntry."Bin Code");
                WhseEntry2.CopyFilters(WhseEntry);
                WhseEntry2.SetRange("Whse. Document Type");
                WhseEntry2.SetRange("Reference Document");
                WhseEntry2.SetRange("Qty. (Base)");
                WhseEntry2.CalcSums("Qty. (Base)");
                Result += WhseEntry2."Qty. (Base)";
                WhseEntry.FindLast();
                WhseEntry.SetRange("Bin Code");
            until WhseEntry.Next() = 0;
    end;

    procedure CalcQtyOnBlockedItemTracking(LocationCode: Code[10]; ItemNo: Code[20]; VariantCode: Code[10]): Decimal
    var
        ItemTrackingSetup: Record "Item Tracking Setup";
    begin
        exit(CalcQtyOnBlockedItemTracking(LocationCode, ItemNo, VariantCode, ItemTrackingSetup));
    end;

    procedure CalcQtyOnBlockedItemTracking(LocationCode: Code[10]; ItemNo: Code[20]; VariantCode: Code[10]; ItemTrackingSetup: Record "Item Tracking Setup"): Decimal
    var
        CalcQtyOnBlockedITOnSNQuery: Query "CalcQtyOnBlockedITOnSNQuery";
        CalcQtyOnBlockedITOnLNQuery: Query "CalcQtyOnBlockedITOnLNQuery";
        CalcQtyOnBlockedITOnPNQuery: Query "CalcQtyOnBlockedITOnPNQuery";
        LotsBlocked: List of [Code[50]];
        PackagesBlocked: List of [Code[50]];
        SNQtyBlocked: Decimal;
        LotQtyBlocked: Decimal;
        PackageQtyBlocked: Decimal;
        SNLotQtyBlocked: Decimal;
        SNPackageQtyBlocked: Decimal;
        LotPackageQtyBlocked: Decimal;
        QtyBlocked: Decimal;
        IsHandled: Boolean;
    begin
        OnBeforeCalcQtyOnBlockedItemTracking(LocationCode, ItemNo, VariantCode, QtyBlocked, IsHandled);
        if IsHandled then
            exit(QtyBlocked);

        //Calculating packages blocked
        CalcQtyOnBlockedITOnPNQuery.SetRange(Item_No_, ItemNo);
        CalcQtyOnBlockedITOnPNQuery.SetRange(Variant_Code, VariantCode);
        CalcQtyOnBlockedITOnPNQuery.SetRange(Blocked, true);
        CalcQtyOnBlockedITOnPNQuery.SetItemTrackingFilter(ItemTrackingSetup);

        CalcQtyOnBlockedITOnPNQuery.SetRange(ILE_Item_No_, ItemNo);
        CalcQtyOnBlockedITOnPNQuery.SetRange(ILE_Variant_Code, VariantCode);
        CalcQtyOnBlockedITOnPNQuery.SetRange(ILE_Location_Code, LocationCode);

        CalcQtyOnBlockedITOnPNQuery.Open();

        while CalcQtyOnBlockedITOnPNQuery.Read() do begin
            if (not PackagesBlocked.Contains(CalcQtyOnBlockedITOnPNQuery.Package_No_)) then
                PackagesBlocked.Add(CalcQtyOnBlockedITOnPNQuery.Package_No_);
            PackageQtyBlocked += CalcQtyOnBlockedITOnPNQuery.Quantity;
        end;

        //Calculating lots blocked
        CalcQtyOnBlockedITOnLNQuery.SetRange(Item_No_, ItemNo);
        CalcQtyOnBlockedITOnLNQuery.SetRange(Variant_Code, VariantCode);
        CalcQtyOnBlockedITOnLNQuery.SetRange(Blocked, true);
        CalcQtyOnBlockedITOnLNQuery.SetItemTrackingFilter(ItemTrackingSetup);

        CalcQtyOnBlockedITOnLNQuery.SetRange(ILE_Item_No_, ItemNo);
        CalcQtyOnBlockedITOnLNQuery.SetRange(ILE_Variant_Code, VariantCode);
        CalcQtyOnBlockedITOnLNQuery.SetRange(ILE_Location_Code, LocationCode);

        CalcQtyOnBlockedITOnLNQuery.Open();

        while CalcQtyOnBlockedITOnLNQuery.Read() do begin
            if (PackagesBlocked.Contains(CalcQtyOnBlockedITOnLNQuery.Package_No_)) then
                LotPackageQtyBlocked += CalcQtyOnBlockedITOnLNQuery.Quantity;

            if (not LotsBlocked.Contains(CalcQtyOnBlockedITOnLNQuery.Lot_No_)) then
                LotsBlocked.Add(CalcQtyOnBlockedITOnLNQuery.Lot_No_);
            LotQtyBlocked += CalcQtyOnBlockedITOnLNQuery.Quantity;
        end;

        //Calculating serial no blocked
        CalcQtyOnBlockedITOnSNQuery.SetRange(Item_No_, ItemNo);
        CalcQtyOnBlockedITOnSNQuery.SetRange(Variant_Code, VariantCode);
        CalcQtyOnBlockedITOnSNQuery.SetRange(Blocked, true);
        CalcQtyOnBlockedITOnSNQuery.SetItemTrackingFilter(ItemTrackingSetup);

        CalcQtyOnBlockedITOnSNQuery.SetRange(ILE_Item_No_, ItemNo);
        CalcQtyOnBlockedITOnSNQuery.SetRange(ILE_Variant_Code, VariantCode);
        CalcQtyOnBlockedITOnSNQuery.SetRange(ILE_Location_Code, LocationCode);

        CalcQtyOnBlockedITOnSNQuery.Open();

        while CalcQtyOnBlockedITOnSNQuery.Read() do begin
            if (LotsBlocked.Contains(CalcQtyOnBlockedITOnSNQuery.Lot_No_)) then
                SNLotQtyBlocked += CalcQtyOnBlockedITOnSNQuery.Quantity
            else
                if (PackagesBlocked.Contains(CalcQtyOnBlockedITOnSNQuery.Package_No_)) then
                    SNPackageQtyBlocked += CalcQtyOnBlockedITOnSNQuery.Quantity;

            SNQtyBlocked += CalcQtyOnBlockedITOnSNQuery.Quantity;
        end;

        QtyBlocked := SNQtyBlocked + LotQtyBlocked + PackageQtyBlocked - SNLotQtyBlocked - SNPackageQtyBlocked - LotPackageQtyBlocked;

        exit(QtyBlocked);
    end;

    local procedure CalcQtyPickedOnJobPlanningLine(SourceSubtype: Option; SourceID: Code[20]; SourceRefNo: Integer): Decimal
    var
        JobPlanningLine: Record "Job Planning Line";
    begin
        JobPlanningLine.SetRange(Status, SourceSubtype);
        JobPlanningLine.SetRange("Job No.", SourceID);
        JobPlanningLine.SetRange("Job Contract Entry No.", SourceRefNo);
        JobPlanningLine.SetLoadFields("Qty. Picked (Base)");
        if JobPlanningLine.FindFirst() then
            exit(JobPlanningLine."Qty. Picked (Base)");
    end;

    local procedure CalcQtyPickedOnAssemblyLine(SourceSubtype: Option; SourceID: Code[20]; SourceRefNo: Integer): Decimal
    var
        AssemblyLine: Record "Assembly Line";
    begin
        AssemblyLine.SetRange("Document Type", SourceSubtype);
        AssemblyLine.SetRange("Document No.", SourceID);
        AssemblyLine.SetRange("Line No.", SourceRefNo);
        AssemblyLine.SetLoadFields("Qty. Picked (Base)");
        if AssemblyLine.FindFirst() then
            exit(AssemblyLine."Qty. Picked (Base)");

        exit(0);
    end;

    local procedure CalcQtyPickedOnWhseShipmentLine(SourceType: Integer; SourceSubType: Option; SourceID: Code[20]; SourceRefNo: Integer): Decimal
    var
        WhseShipmentLine: Record "Warehouse Shipment Line";
    begin
        WhseShipmentLine.SetSourceFilter(SourceType, SourceSubType, SourceID, SourceRefNo, false);
        WhseShipmentLine.CalcSums("Qty. Picked (Base)", "Qty. Shipped (Base)");
        exit(WhseShipmentLine."Qty. Picked (Base)" - WhseShipmentLine."Qty. Shipped (Base)");
    end;

    local procedure CalcQtyPickedNotShipped(SourceType: Integer; SourceSubType: Option; SourceID: Code[20]; SourceRefNo: Integer): Decimal
    var
        WarehouseEntry: Record "Warehouse Entry";
        QtyPickedBase: Decimal;
        QtyShippedBase: Decimal;
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeCalcQtyPickedNotShipped(SourceType, SourceSubType, SourceID, SourceRefNo, QtyPickedBase, IsHandled);
        if IsHandled then
            exit(QtyPickedBase);

        WarehouseEntry.Reset();
        WarehouseEntry.ReadIsolation(IsolationLevel::ReadUnCommitted);
        WarehouseEntry.SetSourceFilter(SourceType, SourceSubType, SourceID, SourceRefNo, true);
        WarehouseEntry.SetRange("Entry Type", WarehouseEntry."Entry Type"::Movement);
        WarehouseEntry.SetRange("Reference Document", WarehouseEntry."Reference Document"::Pick);
        WarehouseEntry.SetFilter(Quantity, '>%1', 0);
        WarehouseEntry.CalcSums("Qty. (Base)");
        QtyPickedBase := WarehouseEntry."Qty. (Base)";

        WarehouseEntry.Reset();
        WarehouseEntry.ReadIsolation(IsolationLevel::ReadUnCommitted);
        WarehouseEntry.SetSourceFilter(SourceType, SourceSubType, SourceID, SourceRefNo, true);
        WarehouseEntry.SetRange("Entry Type", WarehouseEntry."Entry Type"::"Negative Adjmt.");
        WarehouseEntry.SetRange("Whse. Document Type", WarehouseEntry."Whse. Document Type"::Shipment);
        WarehouseEntry.CalcSums("Qty. (Base)");
        QtyShippedBase := -WarehouseEntry."Qty. (Base)";

        if QtyPickedBase > QtyShippedBase then
            exit(QtyPickedBase - QtyShippedBase);

        exit(0);
    end;

    procedure CalcRegisteredAndOutstandingPickQty(ReservationEntry: Record "Reservation Entry"; var WarehouseActivityLine: Record "Warehouse Activity Line"): Decimal
    begin
        exit(
            CalcQtyRegisteredPick(
                ReservationEntry."Location Code", ReservationEntry."Source Type", ReservationEntry."Source Subtype", ReservationEntry."Source ID", ReservationEntry."Source Ref. No.", ReservationEntry."Source Prod. Order Line") +
            CalcQtyOutstandingPick(
                ReservationEntry."Source Type", ReservationEntry."Source Subtype", ReservationEntry."Source ID", ReservationEntry."Source Ref. No.", ReservationEntry."Source Prod. Order Line", WarehouseActivityLine));
    end;

    local procedure CalcQtyRegisteredPick(LocationCode: Code[10]; SourceType: Integer; SourceSubType: Option; SourceID: Code[20]; SourceRefNo: Integer; SourceProdOrderLine: Integer): Decimal
    var
        Location: Record Location;
        Quantity: Decimal;
        IsHandled: Boolean;
    begin
        IsHandled := false;
        Quantity := 0;
        OnBeforeCalcQtyRegisteredPick(SourceType, SourceSubType, SourceID, SourceRefNo, SourceProdOrderLine, Quantity, IsHandled);
        if IsHandled then
            exit(Quantity);

        if SourceType = Database::"Assembly Line" then
            exit(CalcQtyPickedOnAssemblyLine(SourceSubType, SourceID, SourceRefNo));

        if SourceType = Database::"Job Planning Line" then
            exit(CalcQtyPickedOnJobPlanningLine(SourceSubType, SourceID, SourceRefNo));

        if Location.RequireShipment(LocationCode) then begin
            if Location.Get(LocationCode) and Location."Bin Mandatory" then
                exit(CalcQtyPickedNotShipped(SourceType, SourceSubType, SourceID, SourceRefNo));
            exit(CalcQtyPickedOnWhseShipmentLine(SourceType, SourceSubType, SourceID, SourceRefNo));
        end;

        exit(0);
    end;

    procedure CalcQtyRegisteredPick(ReservationEntry: Record "Reservation Entry"): Decimal
    begin
        exit(
            CalcQtyRegisteredPick(
                ReservationEntry."Location Code", ReservationEntry."Source Type", ReservationEntry."Source Subtype", ReservationEntry."Source ID", ReservationEntry."Source Ref. No.", ReservationEntry."Source Prod. Order Line"));
    end;

    local procedure CalcQtyOutstandingPick(SourceType: Integer; SourceSubType: Option; SourceID: Code[20]; SourceRefNo: Integer; SourceProdOrderLine: Integer; var WarehouseActivityLine: Record "Warehouse Activity Line"): Decimal
    var
        WarehouseActivityLineForCalc: Record "Warehouse Activity Line";
    begin
        if SourceType = 5407 then // Database::"Prod. Order Component"
            WarehouseActivityLineForCalc.SetSourceFilter(SourceType, SourceSubType, SourceID, SourceProdOrderLine, SourceRefNo, true)
        else
            WarehouseActivityLineForCalc.SetSourceFilter(SourceType, SourceSubType, SourceID, SourceRefNo, -1, true);
        WarehouseActivityLineForCalc.SetFilter("Action Type", '%1|%2', WarehouseActivityLineForCalc."Action Type"::Take, WarehouseActivityLineForCalc."Action Type"::" ");
        OnCalcQtyOutstandingPickOnAfterSetFilters(WarehouseActivityLineForCalc, SourceType, SourceSubType, SourceID, SourceRefNo, SourceProdOrderLine);

        WarehouseActivityLineForCalc.CalcSums("Qty. Outstanding (Base)");

        // For not yet committed warehouse activity lines
        WarehouseActivityLine.CopyFilters(WarehouseActivityLineForCalc);
        WarehouseActivityLine.CalcSums("Qty. Outstanding (Base)");
        OnCalcQtyOutstandingPickOnAfterCalcSums(WarehouseActivityLine, WarehouseActivityLineForCalc);

        exit(WarehouseActivityLineForCalc."Qty. Outstanding (Base)" + WarehouseActivityLine."Qty. Outstanding (Base)");
    end;

    procedure CalcQtyAvailToTakeOnWhseWorksheetLine(WhseWorksheetLine: Record "Whse. Worksheet Line") AvailQtyBase: Decimal
    var
        Location: Record Location;
        Item: Record Item;
        BinContent: Record "Bin Content";
        TempWhseActivLine: Record "Warehouse Activity Line" temporary;
        TypeHelper: Codeunit "Type Helper";
        InventoryAvailQtyBase: Decimal;
        QtyOnDedicatedBins: Decimal;
        QtyPickedNotShipped: Decimal;
        QtyReservedOnPickShip: Decimal;
        QtyReservedForCurrLine: Decimal;
        ExcludeShipmentBin: Boolean;
    begin
        AvailQtyBase := 0;

        Location.GetLocationSetup(WhseWorksheetLine."Location Code", Location);
        if Location."Directed Put-away and Pick" then
            exit(0);

        if Item.Get(WhseWorksheetLine."Item No.") then;

        if Location."Bin Mandatory" then begin
            BinContent.SetRange("Location Code", WhseWorksheetLine."Location Code");
            BinContent.SetRange("Item No.", WhseWorksheetLine."Item No.");
            BinContent.SetRange("Variant Code", WhseWorksheetLine."Variant Code");
            SetPickableBinFilter(BinContent, Location);
            if BinContent.FindSet() then
                repeat
                    AvailQtyBase += TypeHelper.Maximum(0, BinContent.CalcQtyAvailToPick(0));
                until BinContent.Next() = 0;

            Item.SetRange("Location Filter", WhseWorksheetLine."Location Code");
            Item.SetRange("Variant Filter", WhseWorksheetLine."Variant Code");
            Item.CalcFields("Reserved Qty. on Inventory");
            ExcludeShipmentBin := Location."Require Pick" and (Location."Shipment Bin Code" <> '');

            QtyPickedNotShipped := CalcQtyBasePickedNotShippedOnWarehouseShipmentLine(WhseWorksheetLine, Item, '');
            if ExcludeShipmentBin then
                QtyPickedNotShipped -=
                    CalcQtyBasePickedNotShippedOnWarehouseShipmentLine(WhseWorksheetLine, Item, Location."Shipment Bin Code");

            AvailQtyBase :=
                AvailQtyBase - Item."Reserved Qty. on Inventory" - TypeHelper.Maximum(0, QtyPickedNotShipped);
        end else
            AvailQtyBase := CalcInvtAvailQty(Item, Location, WhseWorksheetLine."Variant Code", TempWhseActivLine);

        if Location."Require Pick" or Location.IsProdConsumpWhseHandlingTypeWarehousePick() then
            QtyReservedOnPickShip := CalcReservQtyOnPicksShips(WhseWorksheetLine."Location Code", WhseWorksheetLine."Item No.", WhseWorksheetLine."Variant Code", TempWhseActivLine);

        QtyReservedForCurrLine :=
          Abs(
            CalcLineReservedQtyOnInvt(
              WhseWorksheetLine."Source Type", WhseWorksheetLine."Source Subtype", WhseWorksheetLine."Source No.", WhseWorksheetLine."Source Line No.", WhseWorksheetLine."Source Subline No.", true, TempWhseActivLine));

        AvailQtyBase := AvailQtyBase + QtyReservedOnPickShip + QtyReservedForCurrLine;

        if Location."Bin Mandatory" and Location."Require Receive" and Location."Require Put-away" then begin
            QtyOnDedicatedBins := CalcQtyOnDedicatedBins(Location.Code, Item."No.", WhseWorksheetLine."Variant Code");
            InventoryAvailQtyBase :=
                CalcInvtAvailQty(Item, Location, WhseWorksheetLine."Variant Code", TempWhseActivLine) +
                QtyReservedOnPickShip + QtyReservedForCurrLine - QtyOnDedicatedBins;
            InventoryAvailQtyBase := TypeHelper.Maximum(0, InventoryAvailQtyBase);
            AvailQtyBase := TypeHelper.Minimum(AvailQtyBase, InventoryAvailQtyBase);
        end;
    end;

    local procedure SetPickableBinFilter(var BinContent: Record "Bin Content"; Location: Record Location)
    var
        ExcludeShipmentBin: Boolean;
        ExcludeReceiptBin: Boolean;
    begin
        ExcludeShipmentBin := Location."Require Pick" and (Location."Shipment Bin Code" <> '');
        ExcludeReceiptBin := Location."Require Put-away" and (Location."Receipt Bin Code" <> '');

        case true of
            ExcludeShipmentBin and ExcludeReceiptBin:
                BinContent.SetFilter("Bin Code", '<>%1&<>%2', Location."Shipment Bin Code", Location."Receipt Bin Code");
            ExcludeShipmentBin:
                BinContent.SetFilter("Bin Code", '<>%1', Location."Shipment Bin Code");
            ExcludeReceiptBin:
                BinContent.SetFilter("Bin Code", '<>%1', Location."Receipt Bin Code");
        end;
    end;

    local procedure CalcQtyBasePickedNotShippedOnWarehouseShipmentLine(WhseWorksheetLine: Record "Whse. Worksheet Line"; Item: Record Item; BinCodeFilter: Code[20]): Decimal
    var
        WarehouseShipmentLine: Record "Warehouse Shipment Line";
    begin
        WarehouseShipmentLine.SetRange("Item No.", Item."No.");
        WarehouseShipmentLine.SetRange("Location Code", WhseWorksheetLine."Location Code");
        if WhseWorksheetLine."Variant Code" <> '' then
            WarehouseShipmentLine.SetRange("Variant Code", WhseWorksheetLine."Variant Code");
        if BinCodeFilter <> '' then
            WarehouseShipmentLine.SetRange("Bin Code", BinCodeFilter);
        WarehouseShipmentLine.CalcSums("Qty. Picked (Base)", "Qty. Shipped (Base)");
        exit(WarehouseShipmentLine."Qty. Picked (Base)" - WarehouseShipmentLine."Qty. Shipped (Base)")
    end;

    local procedure ValidateQtyPickedInShipmentBin(var QtyPicked: Decimal; LocationCode: Code[10]; ItemNo: Code[20]; VariantCode: Code[10]; TrackingSpecification: Record "Tracking Specification"; SourceType: Integer)
    var
        Location: Record Location;
        ItemTrackingSetup: Record "Item Tracking Setup";
        QtyOnShipmentBin: Decimal;
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeValidateQtyPickedInShipmentBin(QtyPicked, LocationCode, ItemNo, VariantCode, TrackingSpecification, SourceType, IsHandled);
        if IsHandled then
            exit;

        if not (Location.RequireShipment(LocationCode) and (QtyPicked <> 0)) then
            exit;

        if SourceType <> 37 then // Database::Sales Line
            exit;

        ItemTrackingSetup.CopyTrackingFromTrackingSpec(TrackingSpecification);
        if not (Location.Get(LocationCode) and Location."Bin Mandatory" and (Location."Shipment Bin Code" <> '')) then
            exit;

        QtyOnShipmentBin := CalcQtyOnShipmentBins(LocationCode, ItemNo, VariantCode, ItemTrackingSetup);
        if QtyOnShipmentBin = 0 then
            QtyPicked := 0
    end;

    local procedure CalcQtyOnShipmentBins(LocationCode: Code[10]; ItemNo: Code[20]; VariantCode: Code[10]; WhseItemTrackingSetup: Record "Item Tracking Setup"): Decimal
    var
        WarehouseEntry: Record "Warehouse Entry";
        BinType: Record "Bin Type";
        Location: Record Location;
        Bin: Record Bin;
        CreatePick: Codeunit "Create Pick";
        ShipBinTypeFilter: Text;
        QtyOnShipmentBins: Decimal;
        DefaultBinAlreadyCounted: Boolean;
    begin
        ShipBinTypeFilter := CreatePick.GetBinTypeFilter(1);

        if ShipBinTypeFilter <> '' then begin
            WarehouseEntry.ReadIsolation(IsolationLevel::ReadUnCommitted);
            WarehouseEntry.SetLoadFields("Qty. (Base)");
            WarehouseEntry.SetRange("Item No.", ItemNo);
            WarehouseEntry.SetRange("Location Code", LocationCode);
            WarehouseEntry.SetRange("Variant Code", VariantCode);
            WarehouseEntry.SetFilter("Bin Type Code", ShipBinTypeFilter);
            WarehouseEntry.SetTrackingFilterFromItemTrackingSetupIfNotBlank(WhseItemTrackingSetup);
            WarehouseEntry.CalcSums("Qty. (Base)");
            QtyOnShipmentBins := WarehouseEntry."Qty. (Base)";
        end;

        if Location.Get(LocationCode) and (Location."Shipment Bin Code" <> '') then
            if Bin.Get(LocationCode, Location."Shipment Bin Code") then begin
                if (ShipBinTypeFilter <> '') and (Bin."Bin Type Code" <> '') then
                    if BinType.Get(Bin."Bin Type Code") then
                        DefaultBinAlreadyCounted := BinType.Ship;
                if not DefaultBinAlreadyCounted then
                    QtyOnShipmentBins += CalcQtyOnBin(LocationCode, Location."Shipment Bin Code", ItemNo, VariantCode, WhseItemTrackingSetup);
            end;

        exit(QtyOnShipmentBins);
    end;

    [IntegrationEvent(false, false)]
    local procedure OnAfterCalcQtyPicked(var Item: Record Item; var QtyPicked: Decimal; Location: Record Location)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeValidateQtyPickedInShipmentBin(var QtyPicked: Decimal; LocationCode: Code[10]; ItemNo: Code[20]; VariantCode: Code[10]; TrackingSpecification: Record "Tracking Specification"; SourceType: Integer; var IsHandled: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnAfterCalcReservedQtyOnInventory(var Item: Record Item; var ReservedQtyOnInventory: Decimal; Location: Record Location)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnAfterCalcQtyRcvdNotAvailable(var PostedWhseReceiptLine: Record "Posted Whse. Receipt Line"; LocationCode: Code[10]; ItemNo: Code[20]; VariantCode: Code[10]; var QtyRcvdNotAvailable: Decimal)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnAfterCalcQtyOnBlockedITOrOnBlockedOutbndBins(LocationCode: Code[10]; ItemNo: Code[20]; VariantCode: Code[10]; WhseItemTrackingSetup: Record "Item Tracking Setup"; var QtyBlocked: Decimal)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeCalcReservQtyOnPicksShipsWithItemTracking(LocationCode: Code[10]; ItemNo: Code[20]; VariantCode: Code[10]; var Result: Decimal; var IsHandled: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnCalcQtyOutstandingPickOnAfterSetFilters(var WarehouseActivityLine: Record "Warehouse Activity Line"; SourceType: Integer; SourceSubType: Option; SourceID: Code[20]; SourceRefNo: Integer; SourceProdOrderLine: Integer)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeCalcInvtAvailQty(Item: Record Item; Location: Record Location; VariantCode: Code[10]; var WarehouseActivityLine: Record "Warehouse Activity Line"; var Result: Decimal; var IsHandled: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnCalcQtyAssgndtoPickOnAfterSetFilters(var WhseActivLine: Record "Warehouse Activity Line"; Location: Record Location; ItemNo: Code[20]; VariantCode: Code[10]; BinTypeFilter: Text[250])
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeCalcQtyOnBlockedItemTracking(LocationCode: Code[10]; ItemNo: Code[20]; VariantCode: Code[10]; var QtyBlocked: Decimal; var IsHandled: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnAfterCalcLineReservedQtyOnInvt(var ReservationEntry: Record "Reservation Entry"; var ReservQtyonInvt: Decimal)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeCalcQtyOnOutboundBins(LocationCode: Code[10]; ItemNo: Code[20]; VariantCode: Code[10]; WhseItemTrackingSetup: Record "Item Tracking Setup"; ExcludeDedicatedBinContent: Boolean; var IsHandled: Boolean; var QtyOnOutboundBins: Decimal)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnAfterGetOutboundBinsOnBasicWarehouseLocation(Location: Record Location; var TempBinContentBuffer: Record "Bin Content Buffer" temporary; LocationCode: Code[10]; ItemNo: Code[20]; VariantCode: Code[10]; WhseItemTrackingSetup: Record "Item Tracking Setup")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnAfterGetSpecialBins(Location: Record Location; var SpecialBins: List of [Code[20]])
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnCalcQtyOnBlockedITOrOnBlockedOutbndBinsOnBeforeNext(var BinContent: Record "Bin Content"; WhseItemTrackingSetup: Record "Item Tracking Setup"; var QtyBlocked: Decimal)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnAfterCalcQtyOnDedicatedBins(LocationCode: Code[10]; ItemNo: Code[20]; VariantCode: Code[10]; var WarehouseEntry: Record "Warehouse Entry"; TempWhseItemTrackingSetup: Record "Item Tracking Setup" temporary; var QtyOnDedicatedBin: Decimal)
    begin
    end;

    // Codeunit "Item Availability Forms Mgt"

    procedure ShowItemAvailFromWhseRcptLine(var WhseRcptLine: Record "Warehouse Receipt Line"; AvailabilityType: Enum "Item Availability Type")
    var
        Item: Record Item;
        ItemAvailabilityFormsMgt: Codeunit "Item Availability Forms Mgt";
        NewDate: Date;
        NewVariantCode: Code[10];
        NewLocationCode: Code[10];
        NewUnitOfMeasureCode: Code[10];
    begin
        WhseRcptLine.TestField("Item No.");
        Item.Reset();
        Item.Get(WhseRcptLine."Item No.");
        ItemAvailabilityFormsMgt.FilterItem(Item, WhseRcptLine."Location Code", WhseRcptLine."Variant Code", WhseRcptLine."Due Date");

        OnBeforeShowItemAvailFromWhseRcptLine(Item, WhseRcptLine, AvailabilityType);
        case AvailabilityType of
            AvailabilityType::Period:
                ItemAvailabilityFormsMgt.ShowItemAvailabilityByPeriod(Item, WhseRcptLine.FieldCaption(WhseRcptLine."Due Date"), WhseRcptLine."Due Date", NewDate);
            AvailabilityType::Variant:
                ItemAvailabilityFormsMgt.ShowItemAvailabilityByVariant(Item, WhseRcptLine.FieldCaption(WhseRcptLine."Variant Code"), WhseRcptLine."Variant Code", NewVariantCode);
            AvailabilityType::Location:
                ItemAvailabilityFormsMgt.ShowItemAvailabilityByLocation(Item, '', WhseRcptLine."Location Code", NewLocationCode);
            AvailabilityType::"Event":
                ItemAvailabilityFormsMgt.ShowItemAvailabilityByEvent(Item, WhseRcptLine.FieldCaption(WhseRcptLine."Due Date"), WhseRcptLine."Due Date", NewDate, false);
            AvailabilityType::BOM:
                ItemAvailabilityFormsMgt.ShowItemAvailabilityByBOMLevel(Item, WhseRcptLine.FieldCaption(WhseRcptLine."Due Date"), WhseRcptLine."Due Date", NewDate);
            AvailabilityType::UOM:
                ItemAvailabilityFormsMgt.ShowItemAvailabilityByUOM(Item, WhseRcptLine.FieldCaption(WhseRcptLine."Unit of Measure Code"), WhseRcptLine."Unit of Measure Code", NewUnitOfMeasureCode);
        end;
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeShowItemAvailFromWhseRcptLine(var Item: Record Item; var WhseRcptLine: Record Microsoft.Warehouse.Document."Warehouse Receipt Line"; AvailabilityType: Enum "Item Availability Type")
    begin
    end;

    procedure ShowItemAvailabilityFromWhseActivLine(var WhseActivLine: Record "Warehouse Activity Line"; AvailabilityType: Enum "Item Availability Type")
    var
        Item: Record Item;
        ItemAvailabilityFormsMgt: Codeunit "Item Availability Forms Mgt";
        NewDate: Date;
        NewVariantCode: Code[10];
        NewLocationCode: Code[10];
        NewUnitOfMeasureCode: Code[10];
    begin
        WhseActivLine.TestField("Item No.");
        Item.Reset();
        Item.Get(WhseActivLine."Item No.");
        ItemAvailabilityFormsMgt.FilterItem(Item, WhseActivLine."Location Code", WhseActivLine."Variant Code", WhseActivLine."Due Date");

        OnBeforeShowItemAvailabilityFromWhseActivLine(Item, WhseActivLine, AvailabilityType);
        case AvailabilityType of
            AvailabilityType::Period:
                ItemAvailabilityFormsMgt.ShowItemAvailabilityByPeriod(Item, WhseActivLine.FieldCaption(WhseActivLine."Due Date"), WhseActivLine."Due Date", NewDate);
            AvailabilityType::Variant:
                ItemAvailabilityFormsMgt.ShowItemAvailabilityByVariant(Item, WhseActivLine.FieldCaption(WhseActivLine."Variant Code"), WhseActivLine."Variant Code", NewVariantCode);
            AvailabilityType::Location:
                ItemAvailabilityFormsMgt.ShowItemAvailabilityByLocation(Item, '', WhseActivLine."Location Code", NewLocationCode);
            AvailabilityType::"Event":
                ItemAvailabilityFormsMgt.ShowItemAvailabilityByEvent(Item, WhseActivLine.FieldCaption(WhseActivLine."Due Date"), WhseActivLine."Due Date", NewDate, false);
            AvailabilityType::BOM:
                ItemAvailabilityFormsMgt.ShowItemAvailabilityByBOMLevel(Item, WhseActivLine.FieldCaption(WhseActivLine."Due Date"), WhseActivLine."Due Date", NewDate);
            AvailabilityType::UOM:
                ItemAvailabilityFormsMgt.ShowItemAvailabilityByUOM(Item, WhseActivLine.FieldCaption(WhseActivLine."Unit of Measure Code"), WhseActivLine."Unit of Measure Code", NewUnitOfMeasureCode);
        end;
    end;

    local procedure ExcludeDedicatedBinContentFromTempBinContentBuffer(var TempBinContentBuffer: Record "Bin Content Buffer" temporary; ExcludeDedicatedBinContent: Boolean)
    begin
        if not ExcludeDedicatedBinContent then
            exit;

        TempBinContentBuffer.SetRange(Dedicated, false);
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeShowItemAvailabilityFromWhseActivLine(var Item: Record Item; var WhseActivLine: Record Microsoft.Warehouse.Activity."Warehouse Activity Line"; AvailabilityType: Enum "Item Availability Type")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnCalcQtyRcvdNotAvailableOnBeforeCalcSums(var PostedWhseReceiptLine: Record "Posted Whse. Receipt Line"; LocationCode: Code[10]; ItemNo: Code[20]; VariantCode: Code[10])
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeCalcQtyPickedNotShipped(SourceType: Integer; SourceSubType: Option; SourceID: Code[20]; SourceRefNo: Integer; var QtyPickedNotShippedBase: Decimal; var IsHandled: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeCalcQtyRegisteredPick(SourceType: Integer; SourceSubType: Option; SourceID: Code[20]; SourceRefNo: Integer; SourceProdOrderLine: Integer; var Quantity: Decimal; var IsHandled: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnCalcLineReservQtyOnPicksShipsOnAfterCalcPickedNotYetShippedQty(SourceType: Integer; SourceSubType: Option; SourceID: Code[20]; SourceRefNo: Integer; SourceProdOrderLine: Integer; var PickedNotYetShippedQty: Decimal)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnCalcLineReservedQtyOnInvtOnSetSourceFilters(var ReservEntry: Record "Reservation Entry"; SourceType: Integer; SourceSubType: Option; SourceID: Code[20]; SourceLineNo: Integer; SourceSubLineNo: Integer; var IsHandled: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnGetOutboundBinsOnBasicWarehouseLocationOnAfterSetWarehouseEntryFilters(var WarehouseEntry: Record "Warehouse Entry")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeOpenCalcRsvQtyOnPickShipWithITQuery(var CalcRsvQtyOnPicksShipsWithIT: Query CalcRsvQtyOnPicksShipsWithIT)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnCalcQtyOutstandingPickOnAfterCalcSums(var WarehouseActivityLine: Record "Warehouse Activity Line"; var WarehouseActivityLineForCalc: Record "Warehouse Activity Line");
    begin
    end;
}