Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2165,6 +2165,7 @@ codeunit 7312 "Create Pick"
CalcPickableQtyFromWhseEntry.SetRange(Location_Code, LocationCode);
CalcPickableQtyFromWhseEntry.SetRange(Item_No_, ItemNo);
CalcPickableQtyFromWhseEntry.SetRange(Variant_Code, VariantCode);
CalcPickableQtyFromWhseEntry.SetFilter(Bin_Type_Code, GetBinTypeFilter(3)); // Only pick bins - exclude quantity sitting in receive/ship bins
CalcPickableQtyFromWhseEntry.Open();
if CalcPickableQtyFromWhseEntry.Read() then
QtyInBinNotBlockedNotDedicatedWithoutItemTrackingFilter := CalcPickableQtyFromWhseEntry.TotalPickableQtyBase;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -923,11 +923,57 @@ codeunit 7314 "Warehouse Availability Mgt."
if not (Location.Get(LocationCode) and Location."Bin Mandatory" and (Location."Shipment Bin Code" <> '')) then
exit;

QtyOnShipmentBin := CalcQtyOnBin(LocationCode, Location."Shipment Bin Code", ItemNo, VariantCode, ItemTrackingSetup);
// The quantity picked but not yet shipped can sit in any Ship-type bin, not only the location's default Shipment Bin,
// because a shipment can be assigned a different ship bin. Sum across all Ship-type bins to avoid falsely zeroing QtyPicked.
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;
ShipBinTypeFilter: Text;
QtyOnShipmentBins: Decimal;
DefaultBinIsShipType: Boolean;
begin
BinType.SetRange(Ship, true);
if BinType.FindSet() then
repeat
if ShipBinTypeFilter = '' then
ShipBinTypeFilter := BinType.Code
else
ShipBinTypeFilter += '|' + BinType.Code;
until BinType.Next() = 0;

if ShipBinTypeFilter <> '' then begin
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;

// The location's configured Shipment Bin may not have a Ship-type bin type assigned, so it is not covered by the
// filter above. Add its quantity too, unless it was already counted as a Ship-type bin (to avoid double counting).
if Location.Get(LocationCode) and (Location."Shipment Bin Code" <> '') then
if Bin.Get(LocationCode, Location."Shipment Bin Code") then begin
if Bin."Bin Type Code" <> '' then
if BinType.Get(Bin."Bin Type Code") then
DefaultBinIsShipType := BinType.Ship;
if not DefaultBinIsShipType 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
Expand Down
108 changes: 108 additions & 0 deletions src/Layers/W1/Tests/SCM/SCMWarehousePick.Codeunit.al
Original file line number Diff line number Diff line change
Expand Up @@ -2462,6 +2462,66 @@ codeunit 137055 "SCM Warehouse Pick"
end;
end;

[Test]
[HandlerFunctions('ReservationPageHandler')]
[Scope('OnPrem')]
procedure CreatePickForRemainingQtyWhenPreviousPickPlacedInNonDefaultShipmentBin()
var
Location: Record Location;
WarehouseEmployee: Record "Warehouse Employee";
Item: Record Item;
ShipZone: Record Zone;
DefaultShipmentBin: Record Bin;
NonDefaultShipmentBin: Record Bin;
SalesHeaderFirst: Record "Sales Header";
SalesHeaderSecond: Record "Sales Header";
WarehouseShipmentHeader: Record "Warehouse Shipment Header";
WarehouseActivityHeader: Record "Warehouse Activity Header";
TotalQty: Decimal;
FirstShipmentQty: Decimal;
RemainingQty: Decimal;
begin
// [SCENARIO 642378] Create Pick for the remaining quantity succeeds when a previous registered pick placed the
// picked-but-not-shipped quantity into a Ship-type bin other than the location's default Shipment Bin.
// [SCENARIO 642378] Previously the picked quantity was only looked for in the location's default Shipment Bin, so
// when the shipment was directed to a different ship bin the picked quantity was treated as zero. That wrongly
// removed the reservation cover on the picks/shipments, making the remaining inventory look unavailable and
// raising "Nothing to handle. The quantity to be picked is in bin ..., which is not set up for picking.".
Initialize();

// [GIVEN] Directed put-away and pick location with two Ship-type bins in the SHIP zone.
LibraryWarehouse.CreateFullWMSLocation(Location, 2);
LibraryWarehouse.CreateWarehouseEmployee(WarehouseEmployee, Location.Code, false);
LibraryWarehouse.FindZone(ShipZone, Location.Code, LibraryWarehouse.SelectBinType(false, true, false, false), false);
LibraryWarehouse.FindBin(DefaultShipmentBin, Location.Code, ShipZone.Code, 1); // Location's default Shipment Bin.
LibraryWarehouse.FindBin(NonDefaultShipmentBin, Location.Code, ShipZone.Code, 2); // Alternative Ship-type bin.

// [GIVEN] A random quantity of an item is in a pick bin, split across two shipments.
TotalQty := LibraryRandom.RandIntInRange(100, 200);
FirstShipmentQty := LibraryRandom.RandIntInRange(1, TotalQty - 1);
RemainingQty := TotalQty - FirstShipmentQty;
LibraryInventory.CreateItem(Item);
UpdateInventoryInPickBin(Item, Location.Code, TotalQty);

// [GIVEN] The first sales order, reserved against inventory, is picked into the non-default Ship-type bin, but not shipped.
CreateReleaseAndReserveSalesOrder(SalesHeaderFirst, Location.Code, Item."No.", FirstShipmentQty, true);
LibraryWarehouse.CreateWhseShipmentFromSO(SalesHeaderFirst);
FindWarehouseShipmentHeader(WarehouseShipmentHeader, SalesHeaderFirst."No.");
UpdateShipmentBinOnWhseShipment(WarehouseShipmentHeader, NonDefaultShipmentBin.Code);
LibraryWarehouse.CreatePick(WarehouseShipmentHeader);
FindWarehouseActivityHeader(WarehouseActivityHeader, WarehouseActivityHeader.Type::Pick, Location.Code, SalesHeaderFirst."No.");
LibraryWarehouse.RegisterWhseActivity(WarehouseActivityHeader);

// [WHEN] Create Pick for a second sales order for the remaining quantity.
CreateReleaseAndReserveSalesOrder(SalesHeaderSecond, Location.Code, Item."No.", RemainingQty, false);
LibraryWarehouse.CreateWhseShipmentFromSO(SalesHeaderSecond);
FindWarehouseShipmentHeader(WarehouseShipmentHeader, SalesHeaderSecond."No.");
LibraryWarehouse.CreatePick(WarehouseShipmentHeader);

// [THEN] The pick for the remaining quantity is created (before the fix it failed with "Nothing to handle").
VerifyPick(SalesHeaderSecond."No.", Item."No.", Location.Code, RemainingQty);
end;

local procedure Initialize()
var
WarehouseActivityLine: Record "Warehouse Activity Line";
Expand Down Expand Up @@ -3040,6 +3100,54 @@ codeunit 137055 "SCM Warehouse Pick"
LibraryInventory.PostItemJournalLine(ItemJournalBatch."Journal Template Name", ItemJournalBatch.Name);
end;

local procedure UpdateInventoryInPickBin(Item: Record Item; LocationCode: Code[10]; Quantity: Decimal)
var
Zone: Record Zone;
Bin: Record Bin;
WarehouseJournalLine: Record "Warehouse Journal Line";
begin
EnsureGeneralPostingSetupForItem(Item);
LibraryWarehouse.FindZone(Zone, LocationCode, LibraryWarehouse.SelectBinType(false, false, true, true), false);
LibraryWarehouse.FindBin(Bin, LocationCode, Zone.Code, 1);
LibraryWarehouse.WarehouseJournalSetup(LocationCode, WarehouseJournalTemplate, WarehouseJournalBatch);
LibraryInventory.ClearItemJournal(ItemJournalTemplate, ItemJournalBatch);
LibraryWarehouse.CreateWhseJournalLine(
WarehouseJournalLine, WarehouseJournalBatch."Journal Template Name", WarehouseJournalBatch.Name,
LocationCode, Zone.Code, Bin.Code,
WarehouseJournalLine."Entry Type"::"Positive Adjmt.", Item."No.", Quantity);
CalculateAndPostWhseAdjustment(Item, LocationCode);
end;

local procedure EnsureGeneralPostingSetupForItem(Item: Record Item)
var
GeneralPostingSetup: Record "General Posting Setup";
LibraryERM: Codeunit "Library - ERM";
begin
// The whse adjustment posts with a blank Gen. Bus. Posting Group, so the ('', Item Gen. Prod. Posting Group)
// combination must exist with inventory accounts even though the item picked a group from a "full" setup that
// used a non-blank Gen. Bus. Posting Group.
if not GeneralPostingSetup.Get('', Item."Gen. Prod. Posting Group") then begin
GeneralPostingSetup.Init();
GeneralPostingSetup.Validate("Gen. Bus. Posting Group", '');
GeneralPostingSetup.Validate("Gen. Prod. Posting Group", Item."Gen. Prod. Posting Group");
GeneralPostingSetup.Insert(true);
end;
LibraryERM.SetGeneralPostingSetupInvtAccounts(GeneralPostingSetup);
GeneralPostingSetup.Modify(true);
end;

local procedure UpdateShipmentBinOnWhseShipment(WarehouseShipmentHeader: Record "Warehouse Shipment Header"; BinCode: Code[20])
var
WarehouseShipmentLine: Record "Warehouse Shipment Line";
begin
WarehouseShipmentLine.SetRange("No.", WarehouseShipmentHeader."No.");
WarehouseShipmentLine.FindSet();
repeat
WarehouseShipmentLine.Validate("Bin Code", BinCode);
WarehouseShipmentLine.Modify(true);
until WarehouseShipmentLine.Next() = 0;
end;

local procedure UpdateBinOnActivityLine(var WarehouseActivityLine: Record "Warehouse Activity Line"; SourceNo: Code[20]; BinCode: Code[20]; ActionType: Enum "Warehouse Action Type")
begin
FindWhseActivityLine(WarehouseActivityLine, WarehouseActivityLine."Activity Type"::Pick, ActionType, LocationWhite.Code, SourceNo);
Expand Down
Loading