Release Order plus Storage fee

This commit is contained in:
hagiye
2026-06-24 15:23:50 +03:00
parent 8ee31789c6
commit c69dd3c238
15 changed files with 178 additions and 85 deletions

View File

@@ -13,6 +13,8 @@ interface ItemAttributes {
tradeDirection: string | null;
cargoTypeCode: string | null;
containerTypeCode: string | null;
inventoryQuantity: number;
bookingContainerCount: number;
facilityId: string | null;
warehouseId: string | null;
yardId: string | null;
@@ -31,6 +33,8 @@ export interface FeePreview {
endIsOpen: boolean; // true when still accruing (no release/gate-clear yet)
elapsedDays: number;
chargeableDays: number;
containerCount: number;
billableUnits: number;
amount: number;
}
@@ -67,6 +71,7 @@ export class WarehouseFeeService {
`SELECT inv.arrived_at AS "arrivedAt",
inv.gate_cleared_at AS "gateClearedAt",
inv.release_date AS "releaseDate",
inv.quantity AS "inventoryQuantity",
inv.warehouse_id AS "warehouseId",
inv.yard_id AS "yardId",
inv.zone_id AS "zoneId",
@@ -74,7 +79,8 @@ export class WarehouseFeeService {
b.freight_type AS "freightType",
b.trade_direction AS "tradeDirection",
cgt.code AS "cargoTypeCode",
ctt.code AS "containerTypeCode"
ctt.code AS "containerTypeCode",
COALESCE(container_lines.container_count, 0) AS "bookingContainerCount"
FROM freight.warehouse_inventory inv
LEFT JOIN freight.warehouses w ON w.id = inv.warehouse_id
LEFT JOIN freight.bookings b ON b.id = inv.booking_id
@@ -82,6 +88,12 @@ export class WarehouseFeeService {
LEFT JOIN freight.cargo_types cgt ON cgt.id = cg.cargo_type_id
LEFT JOIN freight.containers ct ON ct.id = inv.container_id
LEFT JOIN freight.container_types ctt ON ctt.id = ct.container_type_id
LEFT JOIN LATERAL (
SELECT COALESCE(SUM(bc.quantity), 0)::int AS container_count
FROM freight.booking_container bc
WHERE bc.booking_id = inv.booking_id
AND bc.deleted_at IS NULL
) container_lines ON true
WHERE inv.id = $1 AND inv.deleted_at IS NULL`,
[inventoryId],
);
@@ -131,12 +143,18 @@ export class WarehouseFeeService {
const endIsOpen = !item.gateClearedAt && !item.releaseDate;
const freeDays = rule?.freeDays ?? 0;
const ratePerDay = Number(rule?.ratePerDay ?? 0);
const isContainer = (item.freightType ?? '').toUpperCase() === 'CONTAINER';
const inventoryQuantity = Math.max(1, Math.round(Number(item.inventoryQuantity) || 1));
const containerCount = isContainer
? Math.max(1, Math.round(Number(item.bookingContainerCount) || inventoryQuantity))
: 1;
const elapsedDays = start
? Math.max(0, Math.ceil((new Date(endDate).getTime() - start.getTime()) / MS_PER_DAY))
: 0;
const chargeableDays = Math.max(0, elapsedDays - freeDays);
const amount = Math.round(chargeableDays * ratePerDay * 100) / 100;
const billableUnits = chargeableDays * containerCount;
const amount = Math.round(billableUnits * ratePerDay * 100) / 100;
return {
ruleType,
@@ -150,6 +168,8 @@ export class WarehouseFeeService {
endIsOpen,
elapsedDays,
chargeableDays,
containerCount,
billableUnits,
amount,
};
}

View File

@@ -75,9 +75,9 @@ export class WarehouseInvoiceService {
feeType,
description:
p.ruleType === 'STORAGE_FEE'
? `Storage fee ${p.chargeableDays} chargeable day(s) after ${p.freeDays} free`
: `${isContainer ? 'Container' : 'Bulk'} demurrage ${p.chargeableDays} chargeable day(s) after ${p.freeDays} free`,
quantity: p.chargeableDays,
? `Storage fee - ${p.chargeableDays} chargeable day(s) x ${p.containerCount} container(s) after ${p.freeDays} free`
: `${isContainer ? 'Container' : 'Bulk'} demurrage - ${p.chargeableDays} chargeable day(s) x ${p.containerCount} container(s) after ${p.freeDays} free`,
quantity: p.billableUnits,
unitRate: p.ratePerDay,
amount: p.amount,
currency: p.currency,