changes export flow

This commit is contained in:
Marshal
2026-07-30 11:30:34 +00:00
parent b671f07ce6
commit 2ca04b8f98
47 changed files with 1195 additions and 74 deletions

View File

@@ -91,14 +91,21 @@ function num(value: unknown, fallback = 0): number {
* its container lines (quantity × VGM per unit). The portal's container flow
* stores per-line VGM and leaves `cargoTotalWeightVgm` at 0 — reading the
* total alone made every such booking weigh only its tare.
*
* Break-bulk (PER_ITEM) bookings overload `cargoTotalWeightVgm` with the ITEM
* COUNT, so their real tonnage lives in `bulkTotalWeightTons` — prefer it, or
* a 400-item / 800T booking would "weigh" 400T against the pull limit.
*/
export function bookingCargoTons(booking: {
cargoTotalWeightVgm?: number | string | null;
bulkTotalWeightTons?: number | string | null;
bookingContainers?: Array<{
quantity?: number | null;
vgmPerUnitTons?: number | string | null;
}> | null;
}): number {
const itemTons = num(booking.bulkTotalWeightTons);
if (itemTons > 0) return itemTons;
const total = num(booking.cargoTotalWeightVgm);
if (total > 0) return total;
return (booking.bookingContainers ?? []).reduce(
@@ -107,6 +114,32 @@ export function bookingCargoTons(booking: {
);
}
/**
* Wagons a break-bulk (PER_ITEM) bulk booking needs. Items are indivisible, so
* floor how many whole items fit one wagon, then ceil the wagon count:
* 400 items / 800T on 69T wagons → 2T per item → 34 items per wagon → 12 wagons.
* Returns 0 when the booking is not item-counted (PER_TON bulk, containers) —
* callers then fall back to the pooled-tonnage math.
*/
export function bulkItemWagonsRequired(
booking: {
freightType?: string | null;
cargoTotalWeightVgm?: number | string | null;
bulkTotalWeightTons?: number | string | null;
},
capacityTons: number,
): number {
if (booking.freightType !== 'BULK' || !(capacityTons > 0)) return 0;
const quantity = num(booking.cargoTotalWeightVgm);
const totalWeightTons = num(booking.bulkTotalWeightTons);
if (!(quantity > 0) || !(totalWeightTons > 0)) return 0;
const perItemTons = totalWeightTons / quantity;
// ponytail: an item heavier than a whole wagon still charges 1 wagon per
// item; reject such bookings at creation time if the case turns real.
const itemsPerWagon = Math.max(1, Math.floor(capacityTons / perItemTons));
return Math.max(1, Math.ceil(quantity / itemsPerWagon));
}
/** Gross weight of one loaded wagon: it hauls itself plus its cargo. */
export function grossWagonWeightTons(slot: Pick<ConsistSlot, 'tareWeightTons' | 'cargoTons'>): number {
return num(slot.tareWeightTons) + num(slot.cargoTons);