mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 18:42:49 +00:00
fix: improve handling of container sizes in wagon cancellation logic
This commit is contained in:
@@ -54,6 +54,10 @@ import {
|
|||||||
* fee scales with the cancelled wagon count and differs by what was booked.
|
* fee scales with the cancelled wagon count and differs by what was booked.
|
||||||
*/
|
*/
|
||||||
export const WAGON_CANCELLATION_FEE_RATE_TYPE = 'CANCELLATION_FEE';
|
export const WAGON_CANCELLATION_FEE_RATE_TYPE = 'CANCELLATION_FEE';
|
||||||
|
|
||||||
|
/** `booking_container.container_size` is stored as "20ft"/"40ft" — `Number()` on it is NaN. */
|
||||||
|
const sizeFtOf = (size: string | number | null | undefined): number =>
|
||||||
|
parseInt(String(size ?? ''), 10);
|
||||||
/** invoices.type of the fee invoice — the settlement branch key in BookingInvoiceService. */
|
/** invoices.type of the fee invoice — the settlement branch key in BookingInvoiceService. */
|
||||||
export const WAGON_CANCEL_FEE_INVOICE_TYPE = 'WAGON_CANCEL_FEE';
|
export const WAGON_CANCEL_FEE_INVOICE_TYPE = 'WAGON_CANCEL_FEE';
|
||||||
|
|
||||||
@@ -603,11 +607,11 @@ export class BookingWagonCancellationService {
|
|||||||
const live = liveBySize.get(cut.containerSize) ?? 0;
|
const live = liveBySize.get(cut.containerSize) ?? 0;
|
||||||
if (cut.quantity > live) {
|
if (cut.quantity > live) {
|
||||||
throw new BadRequestException(
|
throw new BadRequestException(
|
||||||
`Cannot cancel ${cut.quantity} × ${cut.containerSize}ft — the booking only has ${live}.`,
|
`Cannot cancel ${cut.quantity} × ${sizeFtOf(cut.containerSize)}ft — the booking only has ${live}.`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
bySize[cut.containerSize] = cut.quantity;
|
bySize[cut.containerSize] = cut.quantity;
|
||||||
wagons += cut.quantity * wagonsPerUnitForSize(Number(cut.containerSize));
|
wagons += cut.quantity * wagonsPerUnitForSize(sizeFtOf(cut.containerSize));
|
||||||
}
|
}
|
||||||
wagons = round2(wagons);
|
wagons = round2(wagons);
|
||||||
if (wagons > totalWagons) {
|
if (wagons > totalWagons) {
|
||||||
@@ -849,7 +853,7 @@ export class BookingWagonCancellationService {
|
|||||||
// wagon-space each size's units occupy, so the total always equals
|
// wagon-space each size's units occupy, so the total always equals
|
||||||
// cut.wagons (whole wagons on an allocation cut, fractional on a quantity cut).
|
// cut.wagons (whole wagons on an allocation cut, fractional on a quantity cut).
|
||||||
const bySize = Object.entries(cut.quantities.bySize ?? {}).filter(([, qty]) => qty > 0);
|
const bySize = Object.entries(cut.quantities.bySize ?? {}).filter(([, qty]) => qty > 0);
|
||||||
const spaceOf = ([size, qty]: [string, number]) => qty * wagonsPerUnitForSize(Number(size));
|
const spaceOf = ([size, qty]: [string, number]) => qty * wagonsPerUnitForSize(sizeFtOf(size));
|
||||||
const totalSpace = bySize.reduce((s, e) => s + spaceOf(e), 0);
|
const totalSpace = bySize.reduce((s, e) => s + spaceOf(e), 0);
|
||||||
if (!bySize.length || totalSpace <= 0) throw missing('containers');
|
if (!bySize.length || totalSpace <= 0) throw missing('containers');
|
||||||
const containerTypes = await this.dataSource.getRepository(ContainerType).find();
|
const containerTypes = await this.dataSource.getRepository(ContainerType).find();
|
||||||
@@ -858,7 +862,7 @@ export class BookingWagonCancellationService {
|
|||||||
let currency = '';
|
let currency = '';
|
||||||
for (const entry of bySize) {
|
for (const entry of bySize) {
|
||||||
const [size] = entry;
|
const [size] = entry;
|
||||||
const sizeFt = Number(size);
|
const sizeFt = sizeFtOf(size);
|
||||||
const typeIds = new Set(
|
const typeIds = new Set(
|
||||||
containerTypes.filter((ct) => Number(ct.sizeFt) === sizeFt).map((ct) => ct.id),
|
containerTypes.filter((ct) => Number(ct.sizeFt) === sizeFt).map((ct) => ct.id),
|
||||||
);
|
);
|
||||||
@@ -892,7 +896,7 @@ export class BookingWagonCancellationService {
|
|||||||
const live = lines.reduce((s, l) => s + Number(l.quantity ?? 0), 0);
|
const live = lines.reduce((s, l) => s + Number(l.quantity ?? 0), 0);
|
||||||
if (live < toDrop) {
|
if (live < toDrop) {
|
||||||
throw new BadRequestException(
|
throw new BadRequestException(
|
||||||
`Booking changed since the request: only ${live} × ${size}ft left, cannot cancel ${toDrop}.`,
|
`Booking changed since the request: only ${live} × ${sizeFtOf(size)}ft left, cannot cancel ${toDrop}.`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
@@ -936,7 +940,7 @@ export class BookingWagonCancellationService {
|
|||||||
});
|
});
|
||||||
await manager.getRepository(BookingContainer).update(line.id, {
|
await manager.getRepository(BookingContainer).update(line.id, {
|
||||||
quantity: qty - drop,
|
quantity: qty - drop,
|
||||||
wagonsRequired: round2((qty - drop) * wagonsPerUnitForSize(Number(size))),
|
wagonsRequired: round2((qty - drop) * wagonsPerUnitForSize(sizeFtOf(size))),
|
||||||
totalVgmTons: round3(Number(line.totalVgmTons) - droppedVgm),
|
totalVgmTons: round3(Number(line.totalVgmTons) - droppedVgm),
|
||||||
hazardousQuantity: keptUnits.filter((u) => u.isHazardous).length,
|
hazardousQuantity: keptUnits.filter((u) => u.isHazardous).length,
|
||||||
reeferQuantity: keptUnits.filter((u) => u.isReefer).length,
|
reeferQuantity: keptUnits.filter((u) => u.isReefer).length,
|
||||||
@@ -1024,7 +1028,7 @@ export class BookingWagonCancellationService {
|
|||||||
const doomedVgm = round3(doomed.reduce((s, u) => s + Number(u.vgmTons || 0), 0));
|
const doomedVgm = round3(doomed.reduce((s, u) => s + Number(u.vgmTons || 0), 0));
|
||||||
await manager.getRepository(BookingContainer).update(line.id, {
|
await manager.getRepository(BookingContainer).update(line.id, {
|
||||||
quantity: kept.length,
|
quantity: kept.length,
|
||||||
wagonsRequired: round2(kept.length * wagonsPerUnitForSize(Number(size))),
|
wagonsRequired: round2(kept.length * wagonsPerUnitForSize(sizeFtOf(size))),
|
||||||
totalVgmTons: round3(Number(line.totalVgmTons) - doomedVgm),
|
totalVgmTons: round3(Number(line.totalVgmTons) - doomedVgm),
|
||||||
hazardousQuantity: kept.filter((u) => u.isHazardous).length,
|
hazardousQuantity: kept.filter((u) => u.isHazardous).length,
|
||||||
reeferQuantity: kept.filter((u) => u.isReefer).length,
|
reeferQuantity: kept.filter((u) => u.isReefer).length,
|
||||||
|
|||||||
Reference in New Issue
Block a user