mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-07 05:55:02 +00:00
revert back the clerance payment
This commit is contained in:
@@ -48,6 +48,12 @@ export interface BookingContainerEvalInput {
|
||||
hazardousQuantity?: number;
|
||||
reeferQuantity?: number;
|
||||
returnQuantity?: number;
|
||||
/**
|
||||
* Wagon fraction one container of this line occupies (40ft = 1, 20ft = 0.5).
|
||||
* Lets a PER_WAGON empty-return rate bill the wagons the returned empties
|
||||
* ride back on. Missing ⇒ one wagon per container.
|
||||
*/
|
||||
wagonsPerUnit?: number;
|
||||
}
|
||||
|
||||
export interface BookingEvaluationInput {
|
||||
@@ -86,6 +92,12 @@ export interface BookingEvaluationInput {
|
||||
* container freight, which is scaled by container count instead.
|
||||
*/
|
||||
bulkTons?: number;
|
||||
/**
|
||||
* Wagons a BULK booking occupies (ceil(tons ÷ wagon capacity)), resolved by
|
||||
* the pricing service. Scales PER_WAGON kind-scoped surcharges (lashing);
|
||||
* 0/undefined when unknown — those charges then bill nothing.
|
||||
*/
|
||||
bulkWagons?: number;
|
||||
containers: BookingContainerEvalInput[];
|
||||
}
|
||||
|
||||
@@ -312,6 +324,9 @@ export class RuleEngineService {
|
||||
// Empty-container return is sold per route + container type — billed by
|
||||
// the route-matched block below, never by this route-agnostic loop.
|
||||
if (rate.trigger === 'WITH_RETURN') continue;
|
||||
// Lashing is sold per cargo kind + container type — billed by the
|
||||
// kind-aware block below, never by this generic loop.
|
||||
if (rate.trigger === 'LASHING') continue;
|
||||
const triggered = this.matchesTrigger(rate.trigger, {
|
||||
isHazardous: input.isHazardous,
|
||||
hasReefer,
|
||||
@@ -419,6 +434,10 @@ export class RuleEngineService {
|
||||
appliedModifiers.push(...withReturn.modifiers);
|
||||
hardBlocked.push(...withReturn.blocked);
|
||||
|
||||
if (hasLashing) {
|
||||
appliedModifiers.push(...this.lashingCharges(input, liveRates));
|
||||
}
|
||||
|
||||
return {
|
||||
priorityScore,
|
||||
appliedModifiers,
|
||||
@@ -538,12 +557,18 @@ export class RuleEngineService {
|
||||
}
|
||||
|
||||
const rateValue = Number(rate.rateValue);
|
||||
const amount = rate.rateUnit === 'FLAT' ? rateValue : qty * rateValue;
|
||||
// PER_WAGON bills the wagons the returned empties occupy (two 20ft share
|
||||
// one wagon), PER_CONTAINER the boxes themselves, FLAT once per line.
|
||||
const billed =
|
||||
rate.rateUnit === 'PER_WAGON'
|
||||
? Math.ceil(qty * (container.wagonsPerUnit ?? 1))
|
||||
: qty;
|
||||
const amount = rate.rateUnit === 'FLAT' ? rateValue : billed * rateValue;
|
||||
if (!(amount > 0)) continue;
|
||||
modifiers.push({
|
||||
rateId: rate.id,
|
||||
surchargeCode: this.surchargeCode(rate),
|
||||
triggerValue: qty,
|
||||
triggerValue: rate.rateUnit === 'FLAT' ? qty : billed,
|
||||
calculatedAmount: amount,
|
||||
currency: rate.currency,
|
||||
unitPriceUsd: rateValue,
|
||||
@@ -555,6 +580,70 @@ export class RuleEngineService {
|
||||
return { modifiers, blocked: [...new Set(blocked)] };
|
||||
}
|
||||
|
||||
/**
|
||||
* Cargo securing / lashing — sold per cargo kind, like the customs clearance
|
||||
* fee. Container bookings bill each container line at its own container
|
||||
* type's lashing rate (PER_CONTAINER × boxes, or PER_WAGON × the wagons the
|
||||
* line occupies); bulk bookings bill the type-less rate (PER_TON × tonnage,
|
||||
* or PER_WAGON × the wagons the bulk occupies). An unconfigured rate simply
|
||||
* bills nothing — same leniency as hazard/reefer.
|
||||
*/
|
||||
private lashingCharges(
|
||||
input: BookingEvaluationInput,
|
||||
liveRates: Rate[],
|
||||
): AppliedCargoModifier[] {
|
||||
const modifiers: AppliedCargoModifier[] = [];
|
||||
const lashingRates = liveRates.filter(
|
||||
(r) => r.trigger === 'LASHING' && r.currency === 'USD',
|
||||
);
|
||||
if (lashingRates.length === 0) return modifiers;
|
||||
|
||||
const push = (rate: Rate, billedQty: number): void => {
|
||||
const rateValue = Number(rate.rateValue);
|
||||
const amount =
|
||||
rate.rateUnit === 'FLAT' ? rateValue : billedQty * rateValue;
|
||||
if (!(amount > 0)) return;
|
||||
modifiers.push({
|
||||
rateId: rate.id,
|
||||
surchargeCode: this.surchargeCode(rate),
|
||||
triggerValue: rate.rateUnit === 'FLAT' ? 1 : billedQty,
|
||||
calculatedAmount: amount,
|
||||
currency: rate.currency,
|
||||
unitPriceUsd: rateValue,
|
||||
billingUnit: rate.rateUnit,
|
||||
});
|
||||
};
|
||||
|
||||
if (input.containers.length > 0) {
|
||||
for (const container of input.containers) {
|
||||
const qty = Number(container.quantity || 0);
|
||||
if (!(qty > 0)) continue;
|
||||
const rate = lashingRates.find(
|
||||
(r) => r.containerTypeId === container.containerTypeId,
|
||||
);
|
||||
if (!rate) continue;
|
||||
const billedQty =
|
||||
rate.rateUnit === 'PER_WAGON'
|
||||
? Math.ceil(qty * (container.wagonsPerUnit ?? 1))
|
||||
: qty;
|
||||
push(rate, billedQty);
|
||||
}
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
// Bulk — the type-less rate covers the whole booking.
|
||||
const rate = lashingRates.find((r) => !r.containerTypeId);
|
||||
if (!rate) return modifiers;
|
||||
const billedQty =
|
||||
rate.rateUnit === 'PER_TON'
|
||||
? Math.max(0, Number(input.bulkTons ?? 0))
|
||||
: rate.rateUnit === 'PER_WAGON'
|
||||
? Math.max(0, Number(input.bulkWagons ?? 0))
|
||||
: 1;
|
||||
push(rate, billedQty);
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Messages for container lines whose total weight exceeds the hard capacity
|
||||
* ceiling (weight_limit_rules.max_capacity_tons). Non-empty ⇒ the booking
|
||||
|
||||
Reference in New Issue
Block a user