mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-08 21:48:18 +00:00
feat: Implement container hazardous-cargo surcharge logic
- Added support for container hazardous-cargo surcharge (HAZARDOUS billed PER_CONTAINER) in the rule engine. - Introduced new method in to calculate and apply container hazard charges based on booking details. - Updated to handle per-container hazard rates, ensuring they are scoped by trade direction and lane. - Enhanced tests to cover scenarios for container hazard rates, including validation for required fields and conflict checks. - Created a migration to update existing rates and enforce new constraints for container hazard rates in the database.
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
/**
|
||||
* The container hazardous-cargo surcharge (HAZARDOUS billed PER_CONTAINER) is
|
||||
* now sold per trade direction + origin → destination lane, optionally per
|
||||
* container type (20ft / 40ft) — the same shape as the empty-return service.
|
||||
* The per-ton (bulk) hazard rate keeps its global, unscoped shape.
|
||||
*
|
||||
* - CK_rates_yard_scope gains the per-container hazard rate in its
|
||||
* yard-carrying branch. Drop-and-recreate is the established shape for this
|
||||
* constraint — see 3890000000000-EmptyContainerRateScope.
|
||||
* - Existing lane-less per-container hazard rows cannot satisfy the new
|
||||
* branch and no longer match the way the engine prices container hazard
|
||||
* (per lane + size), so they are SUPERSEDED — kept for the audit trail, out
|
||||
* of the unique pattern index and out of pricing. The rates team re-enters
|
||||
* the surcharge per lane; until then a hazardous container booking on that
|
||||
* lane hard-blocks rather than shipping the service for free.
|
||||
*/
|
||||
export class ContainerHazardRateScope3960000000000 implements MigrationInterface {
|
||||
name = "ContainerHazardRateScope3960000000000";
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`
|
||||
UPDATE freight.rates
|
||||
SET status = 'SUPERSEDED'
|
||||
WHERE trigger = 'HAZARDOUS'
|
||||
AND rate_unit = 'PER_CONTAINER'
|
||||
AND (origin_yard_id IS NULL OR destination_yard_id IS NULL)
|
||||
AND deleted_at IS NULL
|
||||
AND status <> 'SUPERSEDED'
|
||||
`);
|
||||
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE freight.rates DROP CONSTRAINT IF EXISTS "CK_rates_yard_scope"`,
|
||||
);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE freight.rates ADD CONSTRAINT "CK_rates_yard_scope" CHECK (
|
||||
deleted_at IS NOT NULL OR status = 'SUPERSEDED' OR
|
||||
CASE
|
||||
WHEN (trigger = 'ALWAYS' AND applies_to IN ('BULK', 'CONTAINER', 'EMPTY_CONTAINER', 'INTERCITY'))
|
||||
OR trigger IN ('CUSTOMS_CLEARANCE', 'ETHIOPIAN_CUSTOMS_CLEARANCE', 'WITH_RETURN', 'FUEL')
|
||||
OR (trigger = 'HAZARDOUS' AND rate_unit = 'PER_CONTAINER')
|
||||
THEN origin_yard_id IS NOT NULL AND destination_yard_id IS NOT NULL
|
||||
ELSE origin_yard_id IS NULL AND destination_yard_id IS NULL
|
||||
END
|
||||
)
|
||||
`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
// Lane-scoped per-container hazard rows have no place under the old
|
||||
// constraint (surcharges carried no yards) — retire them the same way.
|
||||
await queryRunner.query(`
|
||||
UPDATE freight.rates
|
||||
SET status = 'SUPERSEDED'
|
||||
WHERE trigger = 'HAZARDOUS'
|
||||
AND rate_unit = 'PER_CONTAINER'
|
||||
AND (origin_yard_id IS NOT NULL OR destination_yard_id IS NOT NULL)
|
||||
AND deleted_at IS NULL
|
||||
AND status <> 'SUPERSEDED'
|
||||
`);
|
||||
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE freight.rates DROP CONSTRAINT IF EXISTS "CK_rates_yard_scope"`,
|
||||
);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE freight.rates ADD CONSTRAINT "CK_rates_yard_scope" CHECK (
|
||||
deleted_at IS NOT NULL OR status = 'SUPERSEDED' OR
|
||||
CASE
|
||||
WHEN (trigger = 'ALWAYS' AND applies_to IN ('BULK', 'CONTAINER', 'EMPTY_CONTAINER', 'INTERCITY'))
|
||||
OR trigger IN ('CUSTOMS_CLEARANCE', 'ETHIOPIAN_CUSTOMS_CLEARANCE', 'WITH_RETURN', 'FUEL')
|
||||
THEN origin_yard_id IS NOT NULL AND destination_yard_id IS NOT NULL
|
||||
ELSE origin_yard_id IS NULL AND destination_yard_id IS NULL
|
||||
END
|
||||
)
|
||||
`);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user