import { MigrationInterface, QueryRunner } from 'typeorm'; /** * Configured rail distance between two yards (Configuration → Yard Distances). * Route creation resolves each segment's km from here (symmetric lookup: * one A↔B row serves both directions) instead of accepting free-text km, * and snapshots the value onto route_milestones.distance_km. * * Uniqueness is a partial index (deleted_at IS NULL) so a soft-deleted pair * can be re-created. */ export class CreateYardDistances2060000000000 implements MigrationInterface { name = 'CreateYardDistances2060000000000'; public async up(queryRunner: QueryRunner): Promise { await queryRunner.query(` CREATE TABLE IF NOT EXISTS freight.yard_distances ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), from_yard_id uuid NOT NULL REFERENCES freight.yards(id), to_yard_id uuid NOT NULL REFERENCES freight.yards(id), distance_km numeric(10,2) NOT NULL, 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_yard_distances_from_yard ON freight.yard_distances (from_yard_id); `); await queryRunner.query(` CREATE INDEX IF NOT EXISTS idx_yard_distances_to_yard ON freight.yard_distances (to_yard_id); `); await queryRunner.query(` CREATE UNIQUE INDEX IF NOT EXISTS uq_yard_distances_pair ON freight.yard_distances (from_yard_id, to_yard_id) WHERE deleted_at IS NULL; `); // Backfill from segments already stored on existing routes so editing them // does not immediately fail the "pair not configured" check. One row per // unordered pair; where routes disagree the longest segment wins. await queryRunner.query(` INSERT INTO freight.yard_distances (from_yard_id, to_yard_id, distance_km) SELECT DISTINCT ON (LEAST(prev_yard_id, yard_id), GREATEST(prev_yard_id, yard_id)) prev_yard_id, yard_id, distance_km FROM ( SELECT yard_id, distance_km, LAG(yard_id) OVER (PARTITION BY route_id ORDER BY sequence_no) AS prev_yard_id FROM freight.route_milestones WHERE deleted_at IS NULL ) segments WHERE prev_yard_id IS NOT NULL AND distance_km IS NOT NULL AND distance_km > 0 ORDER BY LEAST(prev_yard_id, yard_id), GREATEST(prev_yard_id, yard_id), distance_km DESC ON CONFLICT DO NOTHING; `); } public async down(queryRunner: QueryRunner): Promise { await queryRunner.query(`DROP TABLE IF EXISTS freight.yard_distances;`); } }