mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-08 00:45:41 +00:00
- Add ShippingLineBookingsPage for listing and managing shipping line bookings. - Create ShippingLineDocumentsModal for document uploads related to bookings. - Introduce ShippingLineInitiateModal for initiating new shipping line bookings. - Implement booking document state management with booking-doc-state utility. - Add shipping line bookings service for API interactions. - Update index to export new components and services. - Enhance types for freight to include shipping line credits.
128 lines
5.7 KiB
TypeScript
128 lines
5.7 KiB
TypeScript
import { MigrationInterface, QueryRunner } from 'typeorm';
|
|
|
|
/**
|
|
* Per-shipping-line rates.
|
|
*
|
|
* A shipping line books rail capacity directly (see BookingShippingLine3450000000000)
|
|
* and negotiates its own prices, so the rate table gains an owner column:
|
|
* `shipping_line_company_id` NULL = the standard rate every customer pays,
|
|
* NOT NULL = a rate that only that line's bookings resolve.
|
|
*
|
|
* Points at `freight.shipping_line_companies` (the portal account that owns the
|
|
* booking), NOT `freight.shipping_lines` — the latter is carrier reference data
|
|
* naming who physically moves the goods, and the existing SHIPPING_LINE trigger
|
|
* already keys off it. Both stay independent.
|
|
*
|
|
* Line rates OVERRIDE rather than stack: a booking owned by a line prices off
|
|
* that line's rate for the lane, and is hard-blocked when none exists (the
|
|
* standard rate is deliberately not a fallback — see RuleEngineService).
|
|
*
|
|
* Every existing row keeps a NULL owner, so nothing needs backfilling and the
|
|
* standard-rate lookups behave exactly as before.
|
|
*/
|
|
export class ShippingLineRates3470000000000 implements MigrationInterface {
|
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
await queryRunner.query(`
|
|
ALTER TABLE freight.rates
|
|
ADD COLUMN IF NOT EXISTS shipping_line_company_id uuid
|
|
`);
|
|
|
|
await queryRunner.query(`
|
|
ALTER TABLE freight.rates
|
|
DROP CONSTRAINT IF EXISTS "FK_rates_shipping_line_company"
|
|
`);
|
|
await queryRunner.query(`
|
|
ALTER TABLE freight.rates
|
|
ADD CONSTRAINT "FK_rates_shipping_line_company"
|
|
FOREIGN KEY (shipping_line_company_id)
|
|
REFERENCES freight.shipping_line_companies (id)
|
|
ON DELETE RESTRICT
|
|
`);
|
|
|
|
// Rate resolution always filters by owner, so the lookups this column
|
|
// participates in are (owner, lane) — indexed together with rate_type,
|
|
// which every lookup also pins.
|
|
await queryRunner.query(`
|
|
CREATE INDEX IF NOT EXISTS idx_rates_shipping_line_company_id
|
|
ON freight.rates (shipping_line_company_id)
|
|
`);
|
|
await queryRunner.query(`
|
|
CREATE INDEX IF NOT EXISTS idx_rates_shipping_line_lane
|
|
ON freight.rates (shipping_line_company_id, rate_type, origin_yard_id, destination_yard_id)
|
|
WHERE shipping_line_company_id IS NOT NULL
|
|
`);
|
|
|
|
// A shipping line sells import freight only — the export leg is contracted
|
|
// through the customer, not the carrier. Enforced here so a line rate can
|
|
// never be filed against an export lane regardless of which API path wrote
|
|
// it. Surcharges carry no direction and are unaffected.
|
|
await queryRunner.query(`
|
|
ALTER TABLE freight.rates
|
|
DROP CONSTRAINT IF EXISTS "CK_rates_shipping_line_import_only"
|
|
`);
|
|
await queryRunner.query(`
|
|
ALTER TABLE freight.rates
|
|
ADD CONSTRAINT "CK_rates_shipping_line_import_only" CHECK (
|
|
deleted_at IS NOT NULL OR status = 'SUPERSEDED' OR
|
|
shipping_line_company_id IS NULL OR
|
|
trade_direction IS NULL OR trade_direction = 'IMPORT'
|
|
)
|
|
`);
|
|
|
|
// The owner joins the rate's identity. Without it MSC's 20ft Djibouti→Modjo
|
|
// rate collides with the standard rate for the same lane — same rate_type,
|
|
// same scope, same unit — and the insert fails on UQ_rates_pattern. NULL
|
|
// (the standard rate) collapses to the zero uuid like every other nullable
|
|
// scope column, so existing rows keep their current uniqueness exactly.
|
|
await queryRunner.query(`DROP INDEX IF EXISTS freight."UQ_rates_pattern"`);
|
|
await queryRunner.query(`
|
|
CREATE UNIQUE INDEX IF NOT EXISTS "UQ_rates_pattern" ON freight.rates USING btree (
|
|
rate_type,
|
|
COALESCE(shipping_line_company_id, '00000000-0000-0000-0000-000000000000'::uuid),
|
|
COALESCE(container_type_id, '00000000-0000-0000-0000-000000000000'::uuid),
|
|
COALESCE(cargo_type_id, '00000000-0000-0000-0000-000000000000'::uuid),
|
|
COALESCE(trade_direction, ''::character varying),
|
|
COALESCE(origin_yard_id, '00000000-0000-0000-0000-000000000000'::uuid),
|
|
COALESCE(destination_yard_id, '00000000-0000-0000-0000-000000000000'::uuid),
|
|
rate_unit,
|
|
COALESCE(min_km, '-1'::numeric)
|
|
) WHERE ((deleted_at IS NULL) AND ((status)::text <> 'SUPERSEDED'::text))
|
|
`);
|
|
}
|
|
|
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
// Restore the pre-owner pattern index (as left by LastMileRateBands).
|
|
await queryRunner.query(`DROP INDEX IF EXISTS freight."UQ_rates_pattern"`);
|
|
await queryRunner.query(`
|
|
CREATE UNIQUE INDEX IF NOT EXISTS "UQ_rates_pattern" ON freight.rates USING btree (
|
|
rate_type,
|
|
COALESCE(container_type_id, '00000000-0000-0000-0000-000000000000'::uuid),
|
|
COALESCE(cargo_type_id, '00000000-0000-0000-0000-000000000000'::uuid),
|
|
COALESCE(trade_direction, ''::character varying),
|
|
COALESCE(origin_yard_id, '00000000-0000-0000-0000-000000000000'::uuid),
|
|
COALESCE(destination_yard_id, '00000000-0000-0000-0000-000000000000'::uuid),
|
|
rate_unit,
|
|
COALESCE(min_km, '-1'::numeric)
|
|
) WHERE ((deleted_at IS NULL) AND ((status)::text <> 'SUPERSEDED'::text))
|
|
`);
|
|
|
|
await queryRunner.query(`
|
|
ALTER TABLE freight.rates
|
|
DROP CONSTRAINT IF EXISTS "CK_rates_shipping_line_import_only"
|
|
`);
|
|
await queryRunner.query(
|
|
`DROP INDEX IF EXISTS freight.idx_rates_shipping_line_lane`,
|
|
);
|
|
await queryRunner.query(
|
|
`DROP INDEX IF EXISTS freight.idx_rates_shipping_line_company_id`,
|
|
);
|
|
await queryRunner.query(`
|
|
ALTER TABLE freight.rates
|
|
DROP CONSTRAINT IF EXISTS "FK_rates_shipping_line_company"
|
|
`);
|
|
await queryRunner.query(`
|
|
ALTER TABLE freight.rates DROP COLUMN IF EXISTS shipping_line_company_id
|
|
`);
|
|
}
|
|
}
|