Files
edr-platform/apps/edr-freight-api/src/migrations/3750000000000-EthiopianCustomsContractTemplates.ts
Marshal b926a3116e feat: enhance train scheduling and contract management features
- Added StationWorkControls to manage loading/unloading phases in TrainScheduleV2DetailPage.
- Implemented API endpoints for recording station work and managing wagon detach requests.
- Updated contract templates to include Ethiopian customs handling options.
- Enhanced shipment forms to collect customs clearing agent details for without-customs bookings.
- Introduced NUMBER_OF_WAGONS as a unit of measure for bulk cargo, allowing customers to specify wagon counts.
- Improved validation for customs clearing agent information in shipment forms.
- Updated various components and services to accommodate new features and ensure data integrity.
2026-08-25 21:44:21 +00:00

119 lines
4.4 KiB
TypeScript

import { MigrationInterface, QueryRunner } from 'typeorm';
import { CONTRACT_TEMPLATE_DEFAULTS } from '../seed/data/contract-template-defaults';
/**
* Third customs-clearing option on contract templates: Ethiopian-customs-only
* (the Service Provider clears the Ethiopian side only, Djibouti stays with
* the Client), matching service types with includes_ethiopian_customs_only.
*
* - ethiopian_customs_only column on contract_templates (bulk variant flag;
* the seeded container variants carry it in the code suffix instead, like
* the existing _CUSTOMS/_NO_CUSTOMS pair).
* - The bulk unique index and intercity check widen to the new flag.
* - Seeds the two new system container templates from the defaults pack.
*/
const SEEDED_CODES = [
'IMPORT_CONTAINER_ETHIOPIAN_CUSTOMS',
'EXPORT_CONTAINER_ETHIOPIAN_CUSTOMS',
] as const;
export class EthiopianCustomsContractTemplates3750000000000 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
ALTER TABLE freight.contract_templates
ADD COLUMN IF NOT EXISTS ethiopian_customs_only boolean
`);
await queryRunner.query(
`DROP INDEX IF EXISTS freight.uq_contract_templates_cargo_dir_customs`,
);
await queryRunner.query(`
CREATE UNIQUE INDEX IF NOT EXISTS uq_contract_templates_cargo_dir_customs
ON freight.contract_templates
(cargo_type_id, trade_direction,
COALESCE(with_customs, false), COALESCE(ethiopian_customs_only, false))
WHERE deleted_at IS NULL AND cargo_type_id IS NOT NULL
`);
await queryRunner.query(`
ALTER TABLE freight.contract_templates
DROP CONSTRAINT IF EXISTS ck_bulk_intercity_no_customs
`);
await queryRunner.query(`
ALTER TABLE freight.contract_templates
ADD CONSTRAINT ck_bulk_intercity_no_customs CHECK (
cargo_type_id IS NULL
OR (
trade_direction IN ('IMPORT', 'EXPORT', 'INTERCITY')
AND (trade_direction = 'INTERCITY') = (with_customs IS NULL)
AND (ethiopian_customs_only IS NOT TRUE OR with_customs IS TRUE)
)
)
`);
for (const code of SEEDED_CODES) {
const seed = CONTRACT_TEMPLATE_DEFAULTS.find((t) => t.code === code);
if (!seed) throw new Error(`Missing contract template default for ${code}`);
await queryRunner.query(
`INSERT INTO freight.contract_templates
(id, code, name, description, document_title, whereas_clauses, articles,
is_active, is_system, created_at, updated_at)
SELECT gen_random_uuid(), $1::varchar, $2, $3, $4, $5::jsonb, $6::jsonb,
true, true, now(), now()
WHERE NOT EXISTS (
SELECT 1 FROM freight.contract_templates
WHERE code = $1::varchar AND deleted_at IS NULL
)`,
[
seed.code,
seed.name,
seed.description,
seed.documentTitle,
JSON.stringify(seed.whereasClauses),
JSON.stringify(
seed.articles.map((article, index) => ({ ...article, order: index + 1 })),
),
],
);
}
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`DELETE FROM freight.contract_templates WHERE code = ANY($1) AND is_system = true`,
[[...SEEDED_CODES]],
);
await queryRunner.query(`
ALTER TABLE freight.contract_templates
DROP CONSTRAINT IF EXISTS ck_bulk_intercity_no_customs
`);
await queryRunner.query(`
ALTER TABLE freight.contract_templates
ADD CONSTRAINT ck_bulk_intercity_no_customs CHECK (
cargo_type_id IS NULL
OR (
trade_direction IN ('IMPORT', 'EXPORT', 'INTERCITY')
AND (trade_direction = 'INTERCITY') = (with_customs IS NULL)
)
)
`);
await queryRunner.query(
`DROP INDEX IF EXISTS freight.uq_contract_templates_cargo_dir_customs`,
);
await queryRunner.query(`
CREATE UNIQUE INDEX IF NOT EXISTS uq_contract_templates_cargo_dir_customs
ON freight.contract_templates
(cargo_type_id, trade_direction, COALESCE(with_customs, false))
WHERE deleted_at IS NULL AND cargo_type_id IS NOT NULL
`);
await queryRunner.query(`
ALTER TABLE freight.contract_templates
DROP COLUMN IF EXISTS ethiopian_customs_only
`);
}
}