fix(warehouses): free warehouse/yard/zone capacity on export dispatch

dispatch() (LOADED -> DISPATCHED) routed through the shared transition()
helper, which only flipped status/timestamp and never called
applyCapacityDelta. deliver() (import pickup) already decrements
correctly, so export cargo leaving by train inflated currentWeight/
currentVolume/currentContainers forever instead of freeing capacity.

Add optional freeCapacity hook to transition(), wire dispatch() to it,
mirroring the negative-delta pattern already used in deliver().
This commit is contained in:
Hagernesh
2026-08-13 11:27:15 +00:00
parent 1088b47029
commit 6c96b7ad94

View File

@@ -5161,6 +5161,7 @@ export class WarehouseInventoryService {
activityType: 'INVENTORY_DISPATCHED', activityType: 'INVENTORY_DISPATCHED',
description: 'Inventory dispatched', description: 'Inventory dispatched',
performedBy, performedBy,
freeCapacity: true,
}); });
} }
@@ -5445,6 +5446,8 @@ export class WarehouseInventoryService {
description: string; description: string;
performedBy?: string; performedBy?: string;
preloaded?: WarehouseInventory; preloaded?: WarehouseInventory;
/** Cargo physically leaves the warehouse on this transition — free up capacity (mirrors deliver()). */
freeCapacity?: boolean;
}, },
): Promise<WarehouseInventory> { ): Promise<WarehouseInventory> {
const item = opts.preloaded ?? (await this.findById(id)); const item = opts.preloaded ?? (await this.findById(id));
@@ -5455,6 +5458,20 @@ export class WarehouseInventoryService {
status: to, status: to,
[opts.timestampField]: new Date(), [opts.timestampField]: new Date(),
}); });
if (opts.freeCapacity) {
const weight = Number(item.weight) || 0;
const volume = Number(item.volume) || 0;
const containerCount = item.containerId ? Math.round(Number(item.quantity) || 0) : 0;
await this.applyCapacityDelta(
manager,
{ warehouseId: item.warehouseId, yardId: item.yardId, zoneId: item.zoneId },
-weight,
-volume,
-containerCount,
);
}
await this.activityLog.record( await this.activityLog.record(
{ {
activityType: opts.activityType, activityType: opts.activityType,