mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-29 16:28:12 +00:00
add items per wagon map to cargo types and sync bulk rate units
- Implemented in the to manage the physical item capacity for each wagon type. - Added a new migration to create the column in the table. - Introduced method in to update rate units when cargo type unit of measure changes. - Updated booking calculations to consider items per wagon for break-bulk cargo. - Refactored various components to utilize the new items fit logic and ensure consistent date formatting across the application. - Added tests for the new display timezone functionality to ensure consistent date/time representation across different user settings.
This commit is contained in:
@@ -120,6 +120,12 @@ export function bookingCargoTons(booking: {
|
||||
* 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.
|
||||
*
|
||||
* `itemsFit` is the wagon type's PHYSICAL item capacity (floor space — from
|
||||
* cargoType.itemsPerWagonMap). It binds independently of tonnage: a 70T wagon
|
||||
* that fits 4 cars takes 3 cars of 20T (weight binds) but only 4 cars of 10T
|
||||
* (floor binds, 30T of rated capacity ride empty). Absent/invalid fit falls
|
||||
* back to tonnage-only (legacy cargo types without a configured fit).
|
||||
*/
|
||||
export function bulkItemWagonsRequired(
|
||||
booking: {
|
||||
@@ -128,6 +134,7 @@ export function bulkItemWagonsRequired(
|
||||
bulkTotalWeightTons?: number | string | null;
|
||||
},
|
||||
capacityTons: number,
|
||||
itemsFit?: number | null,
|
||||
): number {
|
||||
if (booking.freightType !== 'BULK' || !(capacityTons > 0)) return 0;
|
||||
const quantity = num(booking.cargoTotalWeightVgm);
|
||||
@@ -136,10 +143,56 @@ export function bulkItemWagonsRequired(
|
||||
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));
|
||||
const byTonnage = Math.max(1, Math.floor(capacityTons / perItemTons));
|
||||
const byFloor = num(itemsFit) >= 1 ? Math.floor(num(itemsFit)) : Infinity;
|
||||
const itemsPerWagon = Math.min(byTonnage, byFloor);
|
||||
return Math.max(1, Math.ceil(quantity / itemsPerWagon));
|
||||
}
|
||||
|
||||
type ItemFitCargoType = {
|
||||
wagonTypes?: Array<{ id: string; capacityTons?: number | string | null }> | null;
|
||||
itemsPerWagonMap?: Record<string, number> | null;
|
||||
} | null;
|
||||
|
||||
/** Configured whole-items fit of one wagon type for a cargo type; null if unset. */
|
||||
export function bulkItemsFitFor(
|
||||
cargoType: ItemFitCargoType | undefined,
|
||||
wagonTypeId: string | null | undefined,
|
||||
): number | null {
|
||||
const fit = wagonTypeId ? Number(cargoType?.itemsPerWagonMap?.[wagonTypeId]) : NaN;
|
||||
return Number.isFinite(fit) && fit >= 1 ? fit : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Break-bulk wagon count when no single wagon type is fixed yet: the best
|
||||
* (fewest-wagon) count across the cargo type's allowed wagon types, each
|
||||
* respecting its own items-fit. With no fits configured this equals the old
|
||||
* max-capacity estimate; with no allowed types it degrades to
|
||||
* `fallbackCapacityTons` tonnage-only.
|
||||
*/
|
||||
export function bulkItemWagonsForAllowedTypes(
|
||||
booking: {
|
||||
freightType?: string | null;
|
||||
cargoTotalWeightVgm?: number | string | null;
|
||||
bulkTotalWeightTons?: number | string | null;
|
||||
},
|
||||
cargoType: ItemFitCargoType | undefined,
|
||||
fallbackCapacityTons: number,
|
||||
): number {
|
||||
const allowed = (cargoType?.wagonTypes ?? []).filter((wt) => num(wt.capacityTons) > 0);
|
||||
if (!allowed.length) return bulkItemWagonsRequired(booking, fallbackCapacityTons);
|
||||
let best = 0;
|
||||
for (const wagonType of allowed) {
|
||||
const wagons = bulkItemWagonsRequired(
|
||||
booking,
|
||||
num(wagonType.capacityTons),
|
||||
bulkItemsFitFor(cargoType, wagonType.id),
|
||||
);
|
||||
if (wagons > 0 && (best === 0 || wagons < best)) best = wagons;
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
/** 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);
|
||||
|
||||
Reference in New Issue
Block a user