fix rate issue

This commit is contained in:
Marshal
2026-07-30 12:18:50 +00:00
parent acb6f286d4
commit 9208050cb5
2 changed files with 50 additions and 0 deletions

View File

@@ -71,6 +71,36 @@ describe('contract base freight is priced on the contract lane only', () => {
).rejects.toThrow(UnprocessableEntityException);
});
it('freezes the OVERWEIGHT_PER_TON surcharge on export contracts only', async () => {
const overweight = rate({
rateType: 'OVERWEIGHT_PER_TON',
trigger: 'OVERWEIGHT',
rateUnit: 'PER_TON',
rateValue: 25,
originYardId: null,
destinationYardId: null,
} as Partial<Rate>);
const exported = await service([
rate({ rateType: 'CONTAINER_EXPORT', containerTypeId: CT20, rateValue: 900 }),
overweight,
]).buildBreakdown(contract({ tradeDirection: 'EXPORT' }));
expect(exported.lineItems).toEqual(
expect.arrayContaining([
expect.objectContaining({ code: 'OVERWEIGHT_PER_TON', unitPrice: 25 }),
]),
);
// Import derives overweight from the route's base freight — never frozen.
const imported = await service([
rate({ containerTypeId: CT20, rateValue: 750 }),
overweight,
]).buildBreakdown(contract({}));
expect(
imported.lineItems.some((li) => li.code === 'OVERWEIGHT_PER_TON'),
).toBe(false);
});
it('blocks bulk contracts too instead of borrowing an arbitrary rate', async () => {
const bulk = contract({ freightType: 'BULK', cargoScope: [] });
await expect(

View File

@@ -219,6 +219,26 @@ export class ContractPricingService {
});
}
}
// Overweight surcharge — EXPORT contracts freeze the OVERWEIGHT_PER_TON
// rate so booking pricing bills the contract's price on excess tons
// (frozenRateByCode wins over the live rate). Always included, no toggle:
// overweight is system-detected at booking, never customer-opted. IMPORT
// never reads this snapshot — its overweight price derives from the
// route's base container freight (see RuleEngineService).
if (contract.tradeDirection === 'EXPORT') {
const overweight = liveRates.find(
(r) => r.trigger === 'OVERWEIGHT' && r.currency === 'USD',
);
if (overweight && Number(overweight.rateValue) > 0) {
lineItems.push({
code: 'OVERWEIGHT_PER_TON',
label: 'Overweight surcharge (per excess ton)',
unit: toContractUnit(overweight.rateUnit),
unitPrice: convert(Number(overweight.rateValue)),
conditionalOn: 'is_overweight',
});
}
}
// Lashing / cargo securing — BULK only, shown when the contract's commodity
// needs lashing (cargoType.hasLashing). The commodity-scoped rate for the
// contract's direction wins over the commodity-wide catch-all; billed at