add fan number

This commit is contained in:
Tria
2026-06-05 15:59:12 +03:00
288 changed files with 21965 additions and 3600 deletions

View File

@@ -43,10 +43,10 @@ export class MoveCustomersToFreightSchema1748900000000 implements MigrationInter
CREATE INDEX IF NOT EXISTS "IDX_freight_customers_email"
ON freight.customers (email);
`);
await queryRunner.query(`
CREATE INDEX IF NOT EXISTS "IDX_freight_customers_user_id"
ON freight.customers (user_id);
`);
// await queryRunner.query(`
// CREATE INDEX IF NOT EXISTS "IDX_freight_customers_user_id"
// ON freight.customers (user_id);
//`);
// Copy rows from public.customers when that legacy table exists
await queryRunner.query(`

View File

@@ -0,0 +1,50 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class BookingFlowRefactor1749200000000 implements MigrationInterface {
name = 'BookingFlowRefactor1749200000000';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
CREATE TABLE IF NOT EXISTS freight.booking_review_note (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
booking_id UUID NOT NULL REFERENCES freight.bookings(id) ON DELETE CASCADE,
author_id UUID,
note TEXT NOT NULL,
type VARCHAR(30) NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
deleted_at TIMESTAMPTZ
);
CREATE INDEX IF NOT EXISTS idx_booking_review_note_booking_id
ON freight.booking_review_note(booking_id);
`);
await queryRunner.query(`
ALTER TABLE freight.bookings
ADD COLUMN IF NOT EXISTS marketing_approved_by_id UUID,
ADD COLUMN IF NOT EXISTS marketing_approved_at TIMESTAMPTZ,
ADD COLUMN IF NOT EXISTS contract_summary TEXT,
ADD COLUMN IF NOT EXISTS locked_at TIMESTAMPTZ;
`);
await queryRunner.query(`
UPDATE freight.bookings SET status = 'SUBMITTED'
WHERE status IN ('RFQ_SUBMITTED', 'QUOTATION_SENT', 'QUOTATION_APPROVED');
UPDATE freight.bookings SET status = 'REJECTED'
WHERE status = 'QUOTATION_REJECTED';
UPDATE freight.bookings SET status = 'CANCELLED'
WHERE status = 'CANCELLED';
`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
ALTER TABLE freight.bookings
DROP COLUMN IF EXISTS locked_at,
DROP COLUMN IF EXISTS contract_summary,
DROP COLUMN IF EXISTS marketing_approved_at,
DROP COLUMN IF EXISTS marketing_approved_by_id;
`);
await queryRunner.query(`DROP TABLE IF EXISTS freight.booking_review_note;`);
}
}

View File

@@ -0,0 +1,71 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddBookingFreightType1749300000000 implements MigrationInterface {
name = 'AddBookingFreightType1749300000000';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
ALTER TABLE freight.bookings
ADD COLUMN IF NOT EXISTS freight_type VARCHAR(20);
`);
await queryRunner.query(`
UPDATE freight.bookings b
SET freight_type = 'CONTAINER'
WHERE EXISTS (
SELECT 1 FROM freight.booking_container bc WHERE bc.booking_id = b.id
);
`);
await queryRunner.query(`
UPDATE freight.bookings b
SET freight_type = 'BULK'
WHERE freight_type IS NULL
AND b.cargo_type_id IS NOT NULL
AND EXISTS (
SELECT 1 FROM freight.cargo_types ct
WHERE ct.id = b.cargo_type_id AND ct.requires_director_approval = true
);
`);
await queryRunner.query(`
UPDATE freight.bookings
SET freight_type = 'CONTAINER'
WHERE freight_type IS NULL;
`);
await queryRunner.query(`
ALTER TABLE freight.bookings
ALTER COLUMN cargo_type_id DROP NOT NULL;
`);
await queryRunner.query(`
ALTER TABLE freight.bookings
ALTER COLUMN freight_type SET NOT NULL;
`);
await queryRunner.query(`
ALTER TABLE freight.bookings
ADD CONSTRAINT chk_bookings_freight_type
CHECK (freight_type IN ('CONTAINER', 'BULK'));
`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
ALTER TABLE freight.bookings DROP CONSTRAINT IF EXISTS chk_bookings_freight_type;
`);
await queryRunner.query(`
ALTER TABLE freight.bookings DROP COLUMN IF EXISTS freight_type;
`);
await queryRunner.query(`
UPDATE freight.bookings SET cargo_type_id = (
SELECT id FROM freight.cargo_types LIMIT 1
) WHERE cargo_type_id IS NULL;
`);
await queryRunner.query(`
ALTER TABLE freight.bookings
ALTER COLUMN cargo_type_id SET NOT NULL;
`);
}
}

View File

@@ -1,19 +1,20 @@
// import { MigrationInterface, QueryRunner } from 'typeorm';
import { MigrationInterface, QueryRunner } from 'typeorm';
// export class AddFanNumberToCompanies1749300000000 implements MigrationInterface {
// name = 'AddFanNumberToCompanies1749300000000';
export class AddFanNumberToCompanies1749300000000 implements MigrationInterface {
name = 'AddFanNumberToCompanies1749300000000';
// public async up(queryRunner: QueryRunner): Promise<void> {
// await queryRunner.query(`
// ALTER TABLE freight.companies
// ADD COLUMN fan_number varchar(16) NULL;
// `);
// }
public async up(queryRunner: QueryRunner): Promise<void> {
// fan_number may already exist when CreateCompaniesModule ran with the full schema
await queryRunner.query(`
ALTER TABLE freight.companies
ADD COLUMN IF NOT EXISTS fan_number varchar(16) NULL;
`);
}
// public async down(queryRunner: QueryRunner): Promise<void> {
// await queryRunner.query(`
// ALTER TABLE freight.companies
// DROP COLUMN fan_number;
// `);
// }
// }
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
ALTER TABLE freight.companies
DROP COLUMN IF EXISTS fan_number;
`);
}
}

View File

@@ -0,0 +1,45 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddContractSignatures1749400000000 implements MigrationInterface {
name = 'AddContractSignatures1749400000000';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
ALTER TABLE freight.bookings
ADD COLUMN IF NOT EXISTS contract_template_key VARCHAR(80),
ADD COLUMN IF NOT EXISTS contract_generated_at TIMESTAMPTZ,
ADD COLUMN IF NOT EXISTS pricing_breakdown JSONB;
`);
await queryRunner.query(`
CREATE TABLE IF NOT EXISTS freight.booking_contract_signatures (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
booking_id UUID NOT NULL REFERENCES freight.bookings(id) ON DELETE CASCADE,
signer_role VARCHAR(20) NOT NULL,
signer_user_id UUID,
signer_display_name VARCHAR(200) NOT NULL,
signed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
signature_file_id UUID REFERENCES freight.files(id) ON DELETE SET NULL,
consent_text TEXT,
ip_address VARCHAR(64),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
deleted_at TIMESTAMPTZ,
CONSTRAINT uq_booking_contract_signatures_role
UNIQUE (booking_id, signer_role)
);
CREATE INDEX IF NOT EXISTS idx_booking_contract_signatures_booking_id
ON freight.booking_contract_signatures(booking_id);
`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`DROP TABLE IF EXISTS freight.booking_contract_signatures;`);
await queryRunner.query(`
ALTER TABLE freight.bookings
DROP COLUMN IF EXISTS pricing_breakdown,
DROP COLUMN IF EXISTS contract_generated_at,
DROP COLUMN IF EXISTS contract_template_key;
`);
}
}

View File

@@ -0,0 +1,153 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddTrainScheduling1749400000000 implements MigrationInterface {
name = 'AddTrainScheduling1749400000000';
public async up(queryRunner: QueryRunner): Promise<void> {
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<void> {
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;`);
}
}

View File

@@ -0,0 +1,59 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddCompanyIdToBookings1749500000000 implements MigrationInterface {
name = 'AddCompanyIdToBookings1749500000000';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
ALTER TABLE freight.bookings
ALTER COLUMN customer_id DROP NOT NULL;
`);
await queryRunner.query(`
ALTER TABLE freight.bookings
ADD COLUMN IF NOT EXISTS company_id UUID;
`);
await queryRunner.query(`
CREATE INDEX IF NOT EXISTS idx_bookings_company_id
ON freight.bookings(company_id);
`);
await queryRunner.query(`
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_constraint WHERE conname = 'FK_bookings_company_id'
) THEN
ALTER TABLE freight.bookings
ADD CONSTRAINT "FK_bookings_company_id"
FOREIGN KEY (company_id)
REFERENCES freight.companies(id);
END IF;
END $$;
`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
ALTER TABLE freight.bookings
DROP CONSTRAINT IF EXISTS "FK_bookings_company_id";
`);
await queryRunner.query(`
UPDATE freight.bookings SET customer_id = company_id WHERE customer_id IS NULL AND company_id IS NOT NULL;
`);
await queryRunner.query(`
ALTER TABLE freight.bookings
ALTER COLUMN customer_id SET NOT NULL;
`);
await queryRunner.query(`
DROP INDEX IF EXISTS freight.idx_bookings_company_id;
`);
await queryRunner.query(`
ALTER TABLE freight.bookings
DROP COLUMN IF EXISTS company_id;
`);
}
}

View File

@@ -0,0 +1,19 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddBlocksRoleToApprovalStep1749600000000 implements MigrationInterface {
name = 'AddBlocksRoleToApprovalStep1749600000000';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
ALTER TABLE freight.booking_approval_step
ADD COLUMN IF NOT EXISTS blocks_role VARCHAR(30) NULL;
`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
ALTER TABLE freight.booking_approval_step
DROP COLUMN IF EXISTS blocks_role;
`);
}
}

View File

@@ -0,0 +1,48 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
/**
* Seed ITMLS US-06 approval chains if missing (standard + bulk).
*/
export class SeedDefaultApprovalRules1749700000000 implements MigrationInterface {
name = 'SeedDefaultApprovalRules1749700000000';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
INSERT INTO freight.approval_rules
(id, requires_director_approval, step_order, required_role, action_label, blocks_role, created_at, updated_at)
SELECT uuid_generate_v4(), false, 1, 'LINE_STAFF', 'Review & Approve', NULL, now(), now()
WHERE NOT EXISTS (
SELECT 1 FROM freight.approval_rules
WHERE requires_director_approval = false AND step_order = 1 AND deleted_at IS NULL
);
INSERT INTO freight.approval_rules
(id, requires_director_approval, step_order, required_role, action_label, blocks_role, created_at, updated_at)
SELECT uuid_generate_v4(), false, 2, 'DIRECTOR', 'Final Signature', 'LINE_STAFF', now(), now()
WHERE NOT EXISTS (
SELECT 1 FROM freight.approval_rules
WHERE requires_director_approval = false AND step_order = 2 AND deleted_at IS NULL
);
INSERT INTO freight.approval_rules
(id, requires_director_approval, step_order, required_role, action_label, blocks_role, created_at, updated_at)
SELECT uuid_generate_v4(), true, 1, 'DIRECTOR', 'Review & Approve', 'LINE_STAFF', now(), now()
WHERE NOT EXISTS (
SELECT 1 FROM freight.approval_rules
WHERE requires_director_approval = true AND step_order = 1 AND deleted_at IS NULL
);
INSERT INTO freight.approval_rules
(id, requires_director_approval, step_order, required_role, action_label, blocks_role, created_at, updated_at)
SELECT uuid_generate_v4(), true, 2, 'CEO', 'Final Signature', NULL, now(), now()
WHERE NOT EXISTS (
SELECT 1 FROM freight.approval_rules
WHERE requires_director_approval = true AND step_order = 2 AND deleted_at IS NULL
);
`);
}
public async down(_queryRunner: QueryRunner): Promise<void> {
// Keep seeded rules on rollback to avoid breaking in-flight bookings.
}
}

View File

@@ -0,0 +1,28 @@
import { MigrationInterface, QueryRunner, TableIndex } from 'typeorm';
/**
* shipping_lines was created without a unique index on code; seeder upserts require it.
*/
export class AddShippingLinesCodeUniqueIndex1749800000000 implements MigrationInterface {
name = 'AddShippingLinesCodeUniqueIndex1749800000000';
public async up(queryRunner: QueryRunner): Promise<void> {
const existing = await queryRunner.query(
`SELECT 1 FROM pg_indexes WHERE schemaname = 'freight' AND tablename = 'shipping_lines' AND indexdef ILIKE '%UNIQUE%code%' LIMIT 1`,
);
if (existing.length === 0) {
await queryRunner.createIndex(
'freight.shipping_lines',
new TableIndex({
name: 'UQ_shipping_lines_code',
columnNames: ['code'],
isUnique: true,
}),
);
}
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.dropIndex('freight.shipping_lines', 'UQ_shipping_lines_code');
}
}

View File

@@ -0,0 +1,63 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
/**
* file_upload_settings / file_upload_fields entities had no migration; seeder requires both tables.
*/
export class CreateFileUploadSettingsTables1749900000000 implements MigrationInterface {
name = 'CreateFileUploadSettingsTables1749900000000';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
CREATE TABLE IF NOT EXISTS freight.file_upload_settings (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
code VARCHAR(128) NOT NULL,
label VARCHAR(256) NOT NULL,
description TEXT,
entity VARCHAR(32) NOT NULL DEFAULT 'other',
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
deleted_at TIMESTAMPTZ
);
`);
await queryRunner.query(`
CREATE UNIQUE INDEX IF NOT EXISTS "UQ_file_upload_settings_code"
ON freight.file_upload_settings (code);
`);
await queryRunner.query(`
CREATE TABLE IF NOT EXISTS freight.file_upload_fields (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
setting_id UUID NOT NULL,
file_key VARCHAR(128) NOT NULL,
file_label VARCHAR(256) NOT NULL,
help_text TEXT,
is_required BOOLEAN NOT NULL DEFAULT false,
is_multiple BOOLEAN NOT NULL DEFAULT false,
max_files INTEGER NOT NULL DEFAULT 1,
allowed_extensions TEXT[] NOT NULL DEFAULT '{}'::text[],
max_size_mb INTEGER NOT NULL DEFAULT 10,
display_order INTEGER NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
deleted_at TIMESTAMPTZ,
CONSTRAINT "CHK_file_upload_fields_max_files" CHECK (max_files > 0),
CONSTRAINT "CHK_file_upload_fields_max_size_mb" CHECK (max_size_mb > 0),
CONSTRAINT "FK_file_upload_fields_setting"
FOREIGN KEY (setting_id)
REFERENCES freight.file_upload_settings(id)
ON DELETE CASCADE
);
`);
await queryRunner.query(`
CREATE UNIQUE INDEX IF NOT EXISTS "UQ_file_upload_fields_setting_file_key"
ON freight.file_upload_fields (setting_id, file_key);
`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`DROP TABLE IF EXISTS freight.file_upload_fields CASCADE`);
await queryRunner.query(`DROP TABLE IF EXISTS freight.file_upload_settings CASCADE`);
}
}

View File

@@ -0,0 +1,45 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
/**
* Train entity gained extended fields; baseline trains table only had code/capacity/status/notes.
*/
export class AddTrainExtendedColumns1750000000000 implements MigrationInterface {
name = 'AddTrainExtendedColumns1750000000000';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
ALTER TABLE freight.trains
ADD COLUMN IF NOT EXISTS train_number VARCHAR(20),
ADD COLUMN IF NOT EXISTS train_name VARCHAR(100),
ADD COLUMN IF NOT EXISTS route_id UUID,
ADD COLUMN IF NOT EXISTS origin_station_id UUID,
ADD COLUMN IF NOT EXISTS destination_station_id UUID,
ADD COLUMN IF NOT EXISTS departure_time TIMESTAMPTZ,
ADD COLUMN IF NOT EXISTS arrival_time TIMESTAMPTZ,
ADD COLUMN IF NOT EXISTS locomotive_number VARCHAR(50),
ADD COLUMN IF NOT EXISTS remarks TEXT;
`);
await queryRunner.query(`
CREATE UNIQUE INDEX IF NOT EXISTS "UQ_trains_train_number"
ON freight.trains (train_number)
WHERE train_number IS NOT NULL;
`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`DROP INDEX IF EXISTS freight."UQ_trains_train_number"`);
await queryRunner.query(`
ALTER TABLE freight.trains
DROP COLUMN IF EXISTS remarks,
DROP COLUMN IF EXISTS locomotive_number,
DROP COLUMN IF EXISTS arrival_time,
DROP COLUMN IF EXISTS departure_time,
DROP COLUMN IF EXISTS destination_station_id,
DROP COLUMN IF EXISTS origin_station_id,
DROP COLUMN IF EXISTS route_id,
DROP COLUMN IF EXISTS train_name,
DROP COLUMN IF EXISTS train_number;
`);
}
}