mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-08 11:18:17 +00:00
feat(rule-engine): add fuel surcharge rate trigger and cargo flag
This commit is contained in:
@@ -422,3 +422,105 @@ describe('RuleEngineService — lashing (bulk-only, per direction + commodity)',
|
||||
expect(lashingMods(result)).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('RuleEngineService — fuel surcharge (per lane + cargo type)', () => {
|
||||
const fuelPerLiter: Rate = {
|
||||
id: 'rate-fuel-liter',
|
||||
rateType: 'FUEL_SURCHARGE',
|
||||
trigger: 'FUEL',
|
||||
rateValue: 2,
|
||||
rateUnit: 'PER_LITER',
|
||||
baseLiters: 100,
|
||||
currency: 'USD',
|
||||
status: 'LIVE',
|
||||
containerTypeId: null,
|
||||
cargoTypeId: 'cargo-steel',
|
||||
tradeDirection: 'IMPORT',
|
||||
originYardId: 'yard-nagad',
|
||||
destinationYardId: 'yard-mojo',
|
||||
} as Rate;
|
||||
|
||||
const buildService = (rates: Rate[], hasFuel = true): RuleEngineService =>
|
||||
new RuleEngineService(
|
||||
{
|
||||
findById: jest
|
||||
.fn()
|
||||
.mockResolvedValue({ hasFuel, hasLashing: false, requiresDirectorApproval: false }),
|
||||
} as never,
|
||||
{ findById: jest.fn().mockResolvedValue(null) } as never,
|
||||
{ findActiveByContainerTypeId: jest.fn().mockResolvedValue([]) } as never,
|
||||
{ findAllActive: jest.fn().mockResolvedValue([]) } as never,
|
||||
{ findLiveRates: jest.fn().mockResolvedValue(rates) } as never,
|
||||
{ findById: jest.fn().mockResolvedValue(null) } as never,
|
||||
{} as never,
|
||||
);
|
||||
|
||||
const fuelInput = (
|
||||
overrides: Partial<BookingEvaluationInput> = {},
|
||||
): BookingEvaluationInput => ({
|
||||
serviceTypeId: 'svc-1',
|
||||
paymentCurrency: 'USD',
|
||||
tradeDirection: 'IMPORT',
|
||||
isHazardous: false,
|
||||
cargoTypeId: 'cargo-steel',
|
||||
originYardId: 'yard-nagad',
|
||||
destinationYardId: 'yard-mojo',
|
||||
totalWagons: 0,
|
||||
bulkTons: 100,
|
||||
bulkWagons: 4,
|
||||
containers: [],
|
||||
...overrides,
|
||||
});
|
||||
|
||||
const fuelMods = (result: Awaited<ReturnType<RuleEngineService['evaluate']>>) =>
|
||||
result.appliedModifiers.filter((m) => m.surchargeCode === 'FUEL_SURCHARGE');
|
||||
|
||||
it('PER_LITER bills base liters × rate value once, regardless of wagons', async () => {
|
||||
const result = await buildService([fuelPerLiter]).evaluate(fuelInput());
|
||||
const mods = fuelMods(result);
|
||||
expect(mods).toHaveLength(1);
|
||||
expect(mods[0].triggerValue).toBe(100);
|
||||
expect(mods[0].calculatedAmount).toBe(200);
|
||||
expect(mods[0].billingUnit).toBe('PER_LITER');
|
||||
});
|
||||
|
||||
it('PER_WAGON bills the wagons the cargo occupies', async () => {
|
||||
const result = await buildService([
|
||||
{ ...fuelPerLiter, rateUnit: 'PER_WAGON', baseLiters: null, rateValue: 50 } as Rate,
|
||||
]).evaluate(fuelInput());
|
||||
const mods = fuelMods(result);
|
||||
expect(mods[0].triggerValue).toBe(4);
|
||||
expect(mods[0].calculatedAmount).toBe(200);
|
||||
});
|
||||
|
||||
it('a rate for another lane, direction or commodity never bills', async () => {
|
||||
for (const wrong of [
|
||||
{ tradeDirection: 'EXPORT' },
|
||||
{ originYardId: 'yard-other' },
|
||||
{ destinationYardId: 'yard-other' },
|
||||
{ cargoTypeId: 'cargo-wheat' },
|
||||
]) {
|
||||
const result = await buildService([{ ...fuelPerLiter, ...wrong } as Rate]).evaluate(
|
||||
fuelInput(),
|
||||
);
|
||||
expect(fuelMods(result)).toHaveLength(0);
|
||||
}
|
||||
});
|
||||
|
||||
it('a domestic booking bills the DOMESTIC fuel lane', async () => {
|
||||
const result = await buildService([
|
||||
{ ...fuelPerLiter, tradeDirection: 'DOMESTIC' } as Rate,
|
||||
]).evaluate(fuelInput({ tradeDirection: 'DOMESTIC' }));
|
||||
expect(fuelMods(result)).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('no fuel charge when the cargo type does not have hasFuel', async () => {
|
||||
const result = await buildService([fuelPerLiter], false).evaluate(fuelInput());
|
||||
expect(fuelMods(result)).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('no matching lane rate bills nothing (lenient, like lashing)', async () => {
|
||||
const result = await buildService([]).evaluate(fuelInput());
|
||||
expect(fuelMods(result)).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user