mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-28 12:30:58 +00:00
358 lines
21 KiB
TypeScript
358 lines
21 KiB
TypeScript
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||
|
||
/**
|
||
* Contract–Booking separation (additive phase). Introduces a first-class
|
||
* `freight.contracts` aggregate that owns the legal/commercial agreement (scope
|
||
* + unit rates, no quantities) and spawns shipment `bookings` via `contract_id`.
|
||
*
|
||
* Purely additive: no legacy columns are dropped here. The data backfill and
|
||
* legacy-column removal happen in a later cutover migration.
|
||
*
|
||
* See docs/new-doc.md §5.
|
||
*/
|
||
export class CreateContracts1822000000000 implements MigrationInterface {
|
||
name = 'CreateContracts1822000000000';
|
||
|
||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||
// ── contracts ───────────────────────────────────────────────────────────
|
||
await queryRunner.query(`
|
||
CREATE TABLE IF NOT EXISTS freight.contracts (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
reference VARCHAR(64) NOT NULL UNIQUE,
|
||
|
||
company_id UUID,
|
||
company_profile_id UUID,
|
||
is_government BOOLEAN NOT NULL DEFAULT FALSE,
|
||
government_institution VARCHAR(255),
|
||
|
||
contract_kind VARCHAR(20) NOT NULL,
|
||
renewal_of_id UUID REFERENCES freight.contracts(id),
|
||
trade_direction VARCHAR(10) NOT NULL,
|
||
freight_type VARCHAR(20) NOT NULL,
|
||
|
||
service_type_id UUID NOT NULL,
|
||
payment_currency VARCHAR(5) NOT NULL,
|
||
customs_clearing_enabled BOOLEAN NOT NULL DEFAULT FALSE,
|
||
customs_clearing_agent VARCHAR(200),
|
||
equipment_return VARCHAR(20),
|
||
|
||
first_mile_pickup_address TEXT,
|
||
first_mile_pickup_lat NUMERIC(10,7),
|
||
first_mile_pickup_lng NUMERIC(10,7),
|
||
last_mile_delivery_address TEXT,
|
||
last_mile_delivery_lat NUMERIC(10,7),
|
||
last_mile_delivery_lng NUMERIC(10,7),
|
||
|
||
is_hazardous BOOLEAN NOT NULL DEFAULT FALSE,
|
||
is_reefer BOOLEAN NOT NULL DEFAULT FALSE,
|
||
|
||
estimated_shipment_date TIMESTAMPTZ,
|
||
contract_validity_days INT,
|
||
contract_valid_from TIMESTAMPTZ,
|
||
contract_valid_until TIMESTAMPTZ,
|
||
expires_at TIMESTAMPTZ,
|
||
|
||
status VARCHAR(40) NOT NULL DEFAULT 'DRAFT',
|
||
clearance_status VARCHAR(40) NOT NULL DEFAULT 'NOT_APPLICABLE',
|
||
clearance_cycle_number INT NOT NULL DEFAULT 0,
|
||
|
||
pricing_breakdown JSONB,
|
||
pricing_display_mode VARCHAR(20) DEFAULT 'UNIT_RATES',
|
||
|
||
contract_type VARCHAR(20),
|
||
contract_template_key VARCHAR(128),
|
||
contract_generated_at TIMESTAMPTZ,
|
||
contract_summary TEXT,
|
||
version_number INT NOT NULL DEFAULT 1,
|
||
financial_terms JSONB,
|
||
|
||
approved_by_staff_id UUID,
|
||
approved_by_staff_at TIMESTAMPTZ,
|
||
signed_by_director_id UUID,
|
||
signed_by_director_at TIMESTAMPTZ,
|
||
signed_by_ceo_id UUID,
|
||
signed_by_ceo_at TIMESTAMPTZ,
|
||
customer_signed_at TIMESTAMPTZ,
|
||
fully_executed_at TIMESTAMPTZ,
|
||
locked_at TIMESTAMPTZ,
|
||
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||
deleted_at TIMESTAMPTZ
|
||
);
|
||
`);
|
||
await queryRunner.query(`CREATE INDEX IF NOT EXISTS idx_contracts_company ON freight.contracts(company_id);`);
|
||
await queryRunner.query(`CREATE INDEX IF NOT EXISTS idx_contracts_status ON freight.contracts(status);`);
|
||
await queryRunner.query(`CREATE INDEX IF NOT EXISTS idx_contracts_kind ON freight.contracts(contract_kind);`);
|
||
await queryRunner.query(`CREATE INDEX IF NOT EXISTS idx_contracts_valid_until ON freight.contracts(contract_valid_until);`);
|
||
|
||
// ── contract_routes ──────────────────────────────────────────────────────
|
||
await queryRunner.query(`
|
||
CREATE TABLE IF NOT EXISTS freight.contract_routes (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
contract_id UUID NOT NULL REFERENCES freight.contracts(id) ON DELETE CASCADE,
|
||
origin_yard_id UUID NOT NULL,
|
||
destination_yard_id UUID NOT NULL,
|
||
km NUMERIC(10,2),
|
||
sort_order SMALLINT NOT NULL DEFAULT 0,
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||
deleted_at TIMESTAMPTZ,
|
||
CONSTRAINT uq_contract_route UNIQUE (contract_id, origin_yard_id, destination_yard_id)
|
||
);
|
||
`);
|
||
await queryRunner.query(`CREATE INDEX IF NOT EXISTS idx_contract_routes_contract ON freight.contract_routes(contract_id);`);
|
||
|
||
// ── contract_cargo_scope ─────────────────────────────────────────────────
|
||
await queryRunner.query(`
|
||
CREATE TABLE IF NOT EXISTS freight.contract_cargo_scope (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
contract_id UUID NOT NULL REFERENCES freight.contracts(id) ON DELETE CASCADE,
|
||
container_size VARCHAR(10),
|
||
cargo_type_id UUID,
|
||
cargo_free_text VARCHAR(200),
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||
deleted_at TIMESTAMPTZ,
|
||
CONSTRAINT uq_contract_container_size UNIQUE NULLS NOT DISTINCT (contract_id, container_size)
|
||
);
|
||
`);
|
||
await queryRunner.query(`CREATE INDEX IF NOT EXISTS idx_contract_cargo_scope_contract ON freight.contract_cargo_scope(contract_id);`);
|
||
|
||
// ── contract_signatures ──────────────────────────────────────────────────
|
||
await queryRunner.query(`
|
||
CREATE TABLE IF NOT EXISTS freight.contract_signatures (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
contract_id UUID NOT NULL REFERENCES freight.contracts(id) ON DELETE CASCADE,
|
||
role VARCHAR(20) NOT NULL,
|
||
signer_display_name VARCHAR(255) NOT NULL,
|
||
signature_file_id UUID,
|
||
consent_text TEXT,
|
||
signed_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||
deleted_at TIMESTAMPTZ
|
||
);
|
||
`);
|
||
await queryRunner.query(`CREATE INDEX IF NOT EXISTS idx_contract_signatures_contract ON freight.contract_signatures(contract_id);`);
|
||
|
||
// ── contract_approval_steps ──────────────────────────────────────────────
|
||
await queryRunner.query(`
|
||
CREATE TABLE IF NOT EXISTS freight.contract_approval_steps (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
contract_id UUID NOT NULL REFERENCES freight.contracts(id) ON DELETE CASCADE,
|
||
step_order SMALLINT NOT NULL DEFAULT 0,
|
||
required_role VARCHAR(40) NOT NULL,
|
||
blocks_role VARCHAR(40),
|
||
status VARCHAR(20) NOT NULL DEFAULT 'PENDING',
|
||
acted_by_staff_id UUID,
|
||
acted_at TIMESTAMPTZ,
|
||
note TEXT,
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||
deleted_at TIMESTAMPTZ
|
||
);
|
||
`);
|
||
await queryRunner.query(`CREATE INDEX IF NOT EXISTS idx_contract_approval_steps_contract ON freight.contract_approval_steps(contract_id);`);
|
||
|
||
// ── contract_rate_snapshots ──────────────────────────────────────────────
|
||
await queryRunner.query(`
|
||
CREATE TABLE IF NOT EXISTS freight.contract_rate_snapshots (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
contract_id UUID NOT NULL REFERENCES freight.contracts(id) ON DELETE CASCADE,
|
||
rate_id UUID,
|
||
rate_code VARCHAR(64) NOT NULL,
|
||
description VARCHAR(255),
|
||
unit_price NUMERIC(14,2) NOT NULL,
|
||
unit_of_measure VARCHAR(32) NOT NULL,
|
||
currency VARCHAR(5) NOT NULL,
|
||
container_size VARCHAR(10),
|
||
is_surcharge BOOLEAN DEFAULT FALSE,
|
||
conditional_on VARCHAR(32),
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||
deleted_at TIMESTAMPTZ
|
||
);
|
||
`);
|
||
await queryRunner.query(`CREATE INDEX IF NOT EXISTS idx_contract_rate_snapshots_contract ON freight.contract_rate_snapshots(contract_id);`);
|
||
|
||
// ── contract_review_notes ────────────────────────────────────────────────
|
||
await queryRunner.query(`
|
||
CREATE TABLE IF NOT EXISTS freight.contract_review_notes (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
contract_id UUID NOT NULL REFERENCES freight.contracts(id) ON DELETE CASCADE,
|
||
note_type VARCHAR(40) NOT NULL,
|
||
body TEXT NOT NULL,
|
||
author_role VARCHAR(20),
|
||
author_user_id UUID,
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||
deleted_at TIMESTAMPTZ
|
||
);
|
||
`);
|
||
await queryRunner.query(`CREATE INDEX IF NOT EXISTS idx_contract_review_notes_contract ON freight.contract_review_notes(contract_id);`);
|
||
|
||
// ── contract_clearance_cycles ────────────────────────────────────────────
|
||
await queryRunner.query(`
|
||
CREATE TABLE IF NOT EXISTS freight.contract_clearance_cycles (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
contract_id UUID NOT NULL REFERENCES freight.contracts(id) ON DELETE CASCADE,
|
||
cycle_number INT NOT NULL,
|
||
status VARCHAR(40) NOT NULL DEFAULT 'AWAITING_DOCUMENTS',
|
||
booking_id UUID,
|
||
started_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||
clearance_ready_at TIMESTAMPTZ,
|
||
completed_at TIMESTAMPTZ,
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||
deleted_at TIMESTAMPTZ,
|
||
CONSTRAINT uq_contract_clearance_cycle UNIQUE (contract_id, cycle_number)
|
||
);
|
||
`);
|
||
await queryRunner.query(`CREATE INDEX IF NOT EXISTS idx_contract_clearance_cycles_contract ON freight.contract_clearance_cycles(contract_id);`);
|
||
|
||
// ── contract_document_review (pre-booking clearance, Path B) ─────────────
|
||
await queryRunner.query(`
|
||
CREATE TABLE IF NOT EXISTS freight.contract_document_review (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
contract_id UUID NOT NULL REFERENCES freight.contracts(id) ON DELETE CASCADE,
|
||
clearance_cycle_id UUID REFERENCES freight.contract_clearance_cycles(id),
|
||
setting_code VARCHAR(128) NOT NULL,
|
||
file_key VARCHAR(128) NOT NULL,
|
||
file_record_id UUID,
|
||
status VARCHAR(20) NOT NULL DEFAULT 'PENDING',
|
||
note TEXT,
|
||
uploaded_by_role VARCHAR(20) NOT NULL DEFAULT 'CUSTOMER',
|
||
reviewed_by_staff_id UUID,
|
||
reviewed_at TIMESTAMPTZ,
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||
deleted_at TIMESTAMPTZ,
|
||
CONSTRAINT uq_contract_document_review_doc
|
||
UNIQUE NULLS NOT DISTINCT (contract_id, clearance_cycle_id, setting_code, file_key)
|
||
);
|
||
`);
|
||
await queryRunner.query(`CREATE INDEX IF NOT EXISTS idx_contract_doc_review_contract ON freight.contract_document_review(contract_id);`);
|
||
await queryRunner.query(`CREATE INDEX IF NOT EXISTS idx_contract_doc_review_status ON freight.contract_document_review(status);`);
|
||
|
||
// ── clearance_milestones (GL tracking) ───────────────────────────────────
|
||
await queryRunner.query(`
|
||
CREATE TABLE IF NOT EXISTS freight.clearance_milestones (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
booking_id UUID REFERENCES freight.bookings(id) ON DELETE CASCADE,
|
||
contract_id UUID REFERENCES freight.contracts(id) ON DELETE CASCADE,
|
||
clearance_cycle_id UUID REFERENCES freight.contract_clearance_cycles(id),
|
||
milestone_code VARCHAR(64) NOT NULL,
|
||
milestone_label VARCHAR(255) NOT NULL,
|
||
status VARCHAR(20) NOT NULL DEFAULT 'PENDING',
|
||
owner_region VARCHAR(5),
|
||
triggered_by_doc BOOLEAN DEFAULT FALSE,
|
||
triggered_at TIMESTAMPTZ,
|
||
triggered_by_user_id UUID,
|
||
note TEXT,
|
||
sort_order SMALLINT NOT NULL DEFAULT 0,
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||
deleted_at TIMESTAMPTZ
|
||
);
|
||
`);
|
||
await queryRunner.query(`CREATE INDEX IF NOT EXISTS idx_clearance_milestones_booking ON freight.clearance_milestones(booking_id);`);
|
||
await queryRunner.query(`CREATE INDEX IF NOT EXISTS idx_clearance_milestones_contract ON freight.clearance_milestones(contract_id);`);
|
||
await queryRunner.query(`CREATE INDEX IF NOT EXISTS idx_clearance_milestones_region ON freight.clearance_milestones(owner_region, status);`);
|
||
// booking-scoped and contract-cycle-scoped uniqueness for milestone codes
|
||
await queryRunner.query(`
|
||
CREATE UNIQUE INDEX IF NOT EXISTS uq_clearance_milestone_booking
|
||
ON freight.clearance_milestones(booking_id, milestone_code) WHERE booking_id IS NOT NULL;
|
||
`);
|
||
await queryRunner.query(`
|
||
CREATE UNIQUE INDEX IF NOT EXISTS uq_clearance_milestone_cycle
|
||
ON freight.clearance_milestones(clearance_cycle_id, milestone_code) WHERE clearance_cycle_id IS NOT NULL;
|
||
`);
|
||
|
||
// ── booking_container_units (per-unit container detail) ──────────────────
|
||
await queryRunner.query(`
|
||
CREATE TABLE IF NOT EXISTS freight.booking_container_units (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
booking_container_id UUID NOT NULL REFERENCES freight.booking_container(id) ON DELETE CASCADE,
|
||
container_number VARCHAR(64) NOT NULL,
|
||
seal_number VARCHAR(64),
|
||
vgm_tons NUMERIC(10,3) NOT NULL,
|
||
is_hazardous BOOLEAN DEFAULT FALSE,
|
||
is_reefer BOOLEAN DEFAULT FALSE,
|
||
sort_order SMALLINT NOT NULL DEFAULT 0,
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||
deleted_at TIMESTAMPTZ,
|
||
CONSTRAINT uq_booking_container_unit_number UNIQUE (booking_container_id, container_number)
|
||
);
|
||
`);
|
||
|
||
// ── ALTER bookings ───────────────────────────────────────────────────────
|
||
await queryRunner.query(`ALTER TABLE freight.bookings ADD COLUMN IF NOT EXISTS contract_id UUID REFERENCES freight.contracts(id);`);
|
||
await queryRunner.query(`ALTER TABLE freight.bookings ADD COLUMN IF NOT EXISTS contract_route_id UUID REFERENCES freight.contract_routes(id);`);
|
||
await queryRunner.query(`ALTER TABLE freight.bookings ADD COLUMN IF NOT EXISTS created_by_role VARCHAR(20) DEFAULT 'CUSTOMER';`);
|
||
await queryRunner.query(`ALTER TABLE freight.bookings ADD COLUMN IF NOT EXISTS created_by_user_id UUID;`);
|
||
await queryRunner.query(`CREATE INDEX IF NOT EXISTS idx_bookings_contract ON freight.bookings(contract_id);`);
|
||
// One active booking per ONE_TIME contract. Postgres forbids a subquery in an
|
||
// index predicate, so we denormalize the contract kind onto the booking and
|
||
// predicate on that. The column is stamped at booking creation from the
|
||
// contract; the app layer (ContractBookingService) is the primary guard and
|
||
// this index is the backstop.
|
||
await queryRunner.query(`ALTER TABLE freight.bookings ADD COLUMN IF NOT EXISTS contract_kind VARCHAR(20);`);
|
||
await queryRunner.query(`
|
||
CREATE UNIQUE INDEX IF NOT EXISTS uq_one_active_booking_per_one_time_contract
|
||
ON freight.bookings (contract_id)
|
||
WHERE status NOT IN ('EXPIRED', 'CANCELLED', 'COMPLETED', 'REJECTED')
|
||
AND contract_id IS NOT NULL
|
||
AND contract_kind = 'ONE_TIME';
|
||
`);
|
||
|
||
// ── ALTER booking_container ──────────────────────────────────────────────
|
||
await queryRunner.query(`ALTER TABLE freight.booking_container ADD COLUMN IF NOT EXISTS container_size VARCHAR(10);`);
|
||
await queryRunner.query(`ALTER TABLE freight.booking_container ADD COLUMN IF NOT EXISTS hazardous_quantity SMALLINT DEFAULT 0;`);
|
||
await queryRunner.query(`ALTER TABLE freight.booking_container ADD COLUMN IF NOT EXISTS reefer_quantity SMALLINT DEFAULT 0;`);
|
||
|
||
// ── ALTER booking_document_review (denormalized contract link) ───────────
|
||
await queryRunner.query(`ALTER TABLE freight.booking_document_review ADD COLUMN IF NOT EXISTS contract_id UUID REFERENCES freight.contracts(id);`);
|
||
|
||
// ── Extend file_upload_fields with phased GL metadata ────────────────────
|
||
await queryRunner.query(`ALTER TABLE freight.file_upload_fields ADD COLUMN IF NOT EXISTS phase VARCHAR(40);`);
|
||
await queryRunner.query(`ALTER TABLE freight.file_upload_fields ADD COLUMN IF NOT EXISTS owner_region VARCHAR(5);`);
|
||
await queryRunner.query(`ALTER TABLE freight.file_upload_fields ADD COLUMN IF NOT EXISTS trade_direction VARCHAR(10);`);
|
||
await queryRunner.query(`ALTER TABLE freight.file_upload_fields ADD COLUMN IF NOT EXISTS triggers_milestone_code VARCHAR(64);`);
|
||
}
|
||
|
||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||
await queryRunner.query(`ALTER TABLE freight.file_upload_fields DROP COLUMN IF EXISTS triggers_milestone_code;`);
|
||
await queryRunner.query(`ALTER TABLE freight.file_upload_fields DROP COLUMN IF EXISTS trade_direction;`);
|
||
await queryRunner.query(`ALTER TABLE freight.file_upload_fields DROP COLUMN IF EXISTS owner_region;`);
|
||
await queryRunner.query(`ALTER TABLE freight.file_upload_fields DROP COLUMN IF EXISTS phase;`);
|
||
|
||
await queryRunner.query(`ALTER TABLE freight.booking_document_review DROP COLUMN IF EXISTS contract_id;`);
|
||
|
||
await queryRunner.query(`ALTER TABLE freight.booking_container DROP COLUMN IF EXISTS reefer_quantity;`);
|
||
await queryRunner.query(`ALTER TABLE freight.booking_container DROP COLUMN IF EXISTS hazardous_quantity;`);
|
||
await queryRunner.query(`ALTER TABLE freight.booking_container DROP COLUMN IF EXISTS container_size;`);
|
||
|
||
await queryRunner.query(`DROP INDEX IF EXISTS freight.uq_one_active_booking_per_one_time_contract;`);
|
||
await queryRunner.query(`DROP INDEX IF EXISTS freight.idx_bookings_contract;`);
|
||
await queryRunner.query(`ALTER TABLE freight.bookings DROP COLUMN IF EXISTS contract_kind;`);
|
||
await queryRunner.query(`ALTER TABLE freight.bookings DROP COLUMN IF EXISTS created_by_user_id;`);
|
||
await queryRunner.query(`ALTER TABLE freight.bookings DROP COLUMN IF EXISTS created_by_role;`);
|
||
await queryRunner.query(`ALTER TABLE freight.bookings DROP COLUMN IF EXISTS contract_route_id;`);
|
||
await queryRunner.query(`ALTER TABLE freight.bookings DROP COLUMN IF EXISTS contract_id;`);
|
||
|
||
await queryRunner.query(`DROP TABLE IF EXISTS freight.booking_container_units;`);
|
||
await queryRunner.query(`DROP TABLE IF EXISTS freight.clearance_milestones;`);
|
||
await queryRunner.query(`DROP TABLE IF EXISTS freight.contract_document_review;`);
|
||
await queryRunner.query(`DROP TABLE IF EXISTS freight.contract_clearance_cycles;`);
|
||
await queryRunner.query(`DROP TABLE IF EXISTS freight.contract_review_notes;`);
|
||
await queryRunner.query(`DROP TABLE IF EXISTS freight.contract_rate_snapshots;`);
|
||
await queryRunner.query(`DROP TABLE IF EXISTS freight.contract_approval_steps;`);
|
||
await queryRunner.query(`DROP TABLE IF EXISTS freight.contract_signatures;`);
|
||
await queryRunner.query(`DROP TABLE IF EXISTS freight.contract_cargo_scope;`);
|
||
await queryRunner.query(`DROP TABLE IF EXISTS freight.contract_routes;`);
|
||
await queryRunner.query(`DROP TABLE IF EXISTS freight.contracts;`);
|
||
}
|
||
}
|