mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-30 06:28:12 +00:00
115 lines
5.0 KiB
TypeScript
115 lines
5.0 KiB
TypeScript
import { MigrationInterface, QueryRunner } from "typeorm";
|
||
|
||
/**
|
||
* Reference data for the operations reporting suite (turnaround, delay,
|
||
* trainset, TEU, cargo volume).
|
||
*
|
||
* Two new tables and two new columns:
|
||
*
|
||
* - `operations_standards` — single-row settings table, same shape as
|
||
* `logo_settings` / `exchange_settings`. Holds the railway's standard times
|
||
* and charged-tonnage factors. Editable in the backoffice because the
|
||
* business calls the corridor standard "flexible".
|
||
* - `operations_targets` — the planned side of every "Plan / Operated /
|
||
* Implement Rate" table in the spec. One row per period × metric ×
|
||
* dimension value.
|
||
* - `yard_distances.standard_hours` — the per-corridor standard transit time
|
||
* (Negad→GMP 21h, →Adama 20h, →Modjo 20.5h, →Sebeta 22h). Null falls back to
|
||
* `operations_standards.default_leg_standard_hours`.
|
||
* - `cargo_types.full_trainset_wagons` — wagons in a full trainset of this
|
||
* cargo (37 for vehicles, 22 for sand). Null falls back to
|
||
* `operations_standards.default_full_trainset_wagons`.
|
||
*
|
||
* The seed row is inserted only when the table is empty, so re-running this
|
||
* never overwrites values an operator has since edited.
|
||
*/
|
||
export class OperationsReporting3580000000000 implements MigrationInterface {
|
||
name = "OperationsReporting3580000000000";
|
||
|
||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||
await queryRunner.query(`
|
||
CREATE TABLE IF NOT EXISTS freight.operations_standards (
|
||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
station_standard_hours_ethiopia numeric(6,2) NOT NULL DEFAULT 10,
|
||
station_standard_hours_djibouti numeric(6,2) NOT NULL DEFAULT 13,
|
||
cycle_standard_hours_container numeric(6,2) NOT NULL DEFAULT 65,
|
||
cycle_standard_hours_bulk_dmp numeric(6,2) NOT NULL DEFAULT 88,
|
||
cycle_standard_hours_bulk_nagad numeric(6,2) NOT NULL DEFAULT 96,
|
||
cycle_standard_hours_bulk_bcc numeric(6,2) NOT NULL DEFAULT 96,
|
||
default_leg_standard_hours numeric(6,2) NOT NULL DEFAULT 21,
|
||
delay_tolerance_minutes integer NOT NULL DEFAULT 30,
|
||
charged_tons_full_20ft numeric(8,2) NOT NULL DEFAULT 20,
|
||
charged_tons_full_40ft numeric(8,2) NOT NULL DEFAULT 40,
|
||
charged_tons_empty_20ft numeric(8,2) NOT NULL DEFAULT 2.24,
|
||
charged_tons_empty_40ft numeric(8,2) NOT NULL DEFAULT 3.88,
|
||
charged_tons_per_wagon_general numeric(8,2) NOT NULL DEFAULT 70,
|
||
charged_tons_per_wagon_perishable numeric(8,2) NOT NULL DEFAULT 38,
|
||
default_full_trainset_wagons integer NOT NULL DEFAULT 50,
|
||
updated_by_id uuid,
|
||
created_at timestamptz NOT NULL DEFAULT now(),
|
||
updated_at timestamptz NOT NULL DEFAULT now(),
|
||
deleted_at timestamptz
|
||
);
|
||
`);
|
||
|
||
// Column defaults carry every value — the seed only needs the row to exist.
|
||
await queryRunner.query(`
|
||
INSERT INTO freight.operations_standards (id)
|
||
SELECT gen_random_uuid()
|
||
WHERE NOT EXISTS (SELECT 1 FROM freight.operations_standards WHERE deleted_at IS NULL);
|
||
`);
|
||
|
||
await queryRunner.query(`
|
||
CREATE TABLE IF NOT EXISTS freight.operations_targets (
|
||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
period_type varchar(10) NOT NULL,
|
||
period_start date NOT NULL,
|
||
metric varchar(20) NOT NULL,
|
||
dimension varchar(20) NOT NULL,
|
||
dimension_key varchar(60) NOT NULL,
|
||
planned_value numeric(14,3) NOT NULL,
|
||
note text,
|
||
created_at timestamptz NOT NULL DEFAULT now(),
|
||
updated_at timestamptz NOT NULL DEFAULT now(),
|
||
deleted_at timestamptz
|
||
);
|
||
`);
|
||
|
||
// Partial unique index rather than a table constraint, so a soft-deleted
|
||
// target can be re-created — same choice as yard_distances.
|
||
await queryRunner.query(`
|
||
CREATE UNIQUE INDEX IF NOT EXISTS ux_operations_targets_slot
|
||
ON freight.operations_targets (period_type, period_start, metric, dimension, dimension_key)
|
||
WHERE deleted_at IS NULL;
|
||
`);
|
||
|
||
// The reports look targets up by period and metric, never by id.
|
||
await queryRunner.query(`
|
||
CREATE INDEX IF NOT EXISTS idx_operations_targets_lookup
|
||
ON freight.operations_targets (metric, period_type, period_start)
|
||
WHERE deleted_at IS NULL;
|
||
`);
|
||
|
||
await queryRunner.query(`
|
||
ALTER TABLE freight.yard_distances
|
||
ADD COLUMN IF NOT EXISTS standard_hours numeric(6,2);
|
||
`);
|
||
|
||
await queryRunner.query(`
|
||
ALTER TABLE freight.cargo_types
|
||
ADD COLUMN IF NOT EXISTS full_trainset_wagons integer;
|
||
`);
|
||
}
|
||
|
||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||
await queryRunner.query(
|
||
`ALTER TABLE freight.cargo_types DROP COLUMN IF EXISTS full_trainset_wagons;`,
|
||
);
|
||
await queryRunner.query(
|
||
`ALTER TABLE freight.yard_distances DROP COLUMN IF EXISTS standard_hours;`,
|
||
);
|
||
await queryRunner.query(`DROP TABLE IF EXISTS freight.operations_targets;`);
|
||
await queryRunner.query(`DROP TABLE IF EXISTS freight.operations_standards;`);
|
||
}
|
||
}
|