import { MigrationInterface, QueryRunner } from 'typeorm'; export class AddTrainScheduling1749400000000 implements MigrationInterface { name = 'AddTrainScheduling1749400000000'; public async up(queryRunner: QueryRunner): Promise { await queryRunner.query(` CREATE TABLE IF NOT EXISTS freight.wagon_types ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), code VARCHAR(32) NOT NULL UNIQUE, name VARCHAR(100) NOT NULL, capacity_tons NUMERIC(10,3) NOT NULL, length_meters NUMERIC(10,3) NOT NULL, max_wagons_per_train INT NULL, supported_load_types TEXT[] NOT NULL DEFAULT '{}', is_active BOOLEAN NOT NULL DEFAULT true, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), deleted_at TIMESTAMPTZ NULL ); `); await queryRunner.query(` CREATE TABLE IF NOT EXISTS freight.locomotives ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), code VARCHAR(32) NOT NULL UNIQUE, name VARCHAR(100) NULL, max_pull_weight_tons NUMERIC(10,3) NOT NULL, status VARCHAR(20) NOT NULL DEFAULT 'AVAILABLE', available_from TIMESTAMPTZ NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), deleted_at TIMESTAMPTZ NULL ); `); await queryRunner.query(` CREATE TABLE IF NOT EXISTS freight.train_sets ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), locomotive_id UUID NOT NULL, total_weight_tons NUMERIC(10,3) NOT NULL, total_length_meters NUMERIC(10,3) NOT NULL, wagon_count INT NOT NULL, status VARCHAR(20) NOT NULL DEFAULT 'DRAFT', created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), deleted_at TIMESTAMPTZ NULL, CONSTRAINT fk_train_sets_locomotive FOREIGN KEY (locomotive_id) REFERENCES freight.locomotives(id) ); `); await queryRunner.query(` CREATE TABLE IF NOT EXISTS freight.train_set_wagons ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), train_set_id UUID NOT NULL, wagon_type_id UUID NOT NULL, sequence_no INT NOT NULL, capacity_tons NUMERIC(10,3) NOT NULL, length_meters NUMERIC(10,3) NOT NULL, assigned_weight_tons NUMERIC(10,3) NOT NULL DEFAULT 0, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), deleted_at TIMESTAMPTZ NULL, CONSTRAINT uq_train_set_wagons_sequence UNIQUE (train_set_id, sequence_no), CONSTRAINT fk_train_set_wagons_train_set FOREIGN KEY (train_set_id) REFERENCES freight.train_sets(id) ON DELETE CASCADE, CONSTRAINT fk_train_set_wagons_wagon_type FOREIGN KEY (wagon_type_id) REFERENCES freight.wagon_types(id) ); `); await queryRunner.query(` CREATE TABLE IF NOT EXISTS freight.train_schedules ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), train_set_id UUID NOT NULL UNIQUE, origin_station_id UUID NOT NULL, destination_station_id UUID NOT NULL, scheduled_departure_date TIMESTAMPTZ NOT NULL, scheduled_arrival_date TIMESTAMPTZ NULL, status VARCHAR(20) NOT NULL DEFAULT 'DRAFT', created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), deleted_at TIMESTAMPTZ NULL, CONSTRAINT fk_train_schedules_train_set FOREIGN KEY (train_set_id) REFERENCES freight.train_sets(id), CONSTRAINT fk_train_schedules_origin FOREIGN KEY (origin_station_id) REFERENCES freight.yards(id), CONSTRAINT fk_train_schedules_destination FOREIGN KEY (destination_station_id) REFERENCES freight.yards(id) ); `); await queryRunner.query(` CREATE TABLE IF NOT EXISTS freight.train_schedule_bookings ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), train_schedule_id UUID NOT NULL, booking_id UUID NOT NULL UNIQUE, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), deleted_at TIMESTAMPTZ NULL, CONSTRAINT uq_train_schedule_booking UNIQUE (train_schedule_id, booking_id), CONSTRAINT fk_train_schedule_bookings_schedule FOREIGN KEY (train_schedule_id) REFERENCES freight.train_schedules(id) ON DELETE CASCADE, CONSTRAINT fk_train_schedule_bookings_booking FOREIGN KEY (booking_id) REFERENCES freight.bookings(id) ); `); await queryRunner.query(` CREATE TABLE IF NOT EXISTS freight.wagon_booking_allocations ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), train_set_wagon_id UUID NOT NULL, booking_id UUID NOT NULL, allocated_weight_tons NUMERIC(10,3) NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), deleted_at TIMESTAMPTZ NULL, CONSTRAINT fk_wagon_booking_allocations_wagon FOREIGN KEY (train_set_wagon_id) REFERENCES freight.train_set_wagons(id) ON DELETE CASCADE, CONSTRAINT fk_wagon_booking_allocations_booking FOREIGN KEY (booking_id) REFERENCES freight.bookings(id) ); `); await queryRunner.query(` CREATE INDEX IF NOT EXISTS idx_locomotives_status ON freight.locomotives(status); `); await queryRunner.query(` CREATE INDEX IF NOT EXISTS idx_train_sets_status ON freight.train_sets(status); `); await queryRunner.query(` CREATE INDEX IF NOT EXISTS idx_train_schedules_departure_status ON freight.train_schedules(scheduled_departure_date, status); `); await queryRunner.query(` CREATE INDEX IF NOT EXISTS idx_wagon_booking_allocations_booking ON freight.wagon_booking_allocations(booking_id); `); } public async down(queryRunner: QueryRunner): Promise { await queryRunner.query(`DROP TABLE IF EXISTS freight.wagon_booking_allocations;`); await queryRunner.query(`DROP TABLE IF EXISTS freight.train_schedule_bookings;`); await queryRunner.query(`DROP TABLE IF EXISTS freight.train_schedules;`); await queryRunner.query(`DROP TABLE IF EXISTS freight.train_set_wagons;`); await queryRunner.query(`DROP TABLE IF EXISTS freight.train_sets;`); await queryRunner.query(`DROP TABLE IF EXISTS freight.locomotives;`); await queryRunner.query(`DROP TABLE IF EXISTS freight.wagon_types;`); } }