mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-08 03:05:42 +00:00
545 lines
24 KiB
TypeScript
545 lines
24 KiB
TypeScript
import {
|
|
MigrationInterface,
|
|
QueryRunner,
|
|
Table,
|
|
TableForeignKey,
|
|
TableIndex,
|
|
TableUnique,
|
|
} from 'typeorm';
|
|
|
|
export class ItmlsFullSchemaRewrite1748600000000 implements MigrationInterface {
|
|
name = 'ItmlsFullSchemaRewrite1748600000000';
|
|
|
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
// ── container_types ───────────────────────────────────────────────────
|
|
await queryRunner.query(`
|
|
DO $$ BEGIN
|
|
ALTER TABLE freight.container_types RENAME COLUMN size_code TO code;
|
|
EXCEPTION WHEN undefined_column THEN NULL;
|
|
END $$;
|
|
`);
|
|
await queryRunner.query(`
|
|
DO $$ BEGIN
|
|
ALTER TABLE freight.container_types RENAME COLUMN description TO label;
|
|
EXCEPTION WHEN undefined_column THEN NULL;
|
|
END $$;
|
|
`);
|
|
await queryRunner.query(`
|
|
ALTER TABLE freight.container_types
|
|
ADD COLUMN IF NOT EXISTS size_ft SMALLINT,
|
|
ADD COLUMN IF NOT EXISTS is_reefer BOOLEAN NOT NULL DEFAULT false,
|
|
ADD COLUMN IF NOT EXISTS is_open_top BOOLEAN NOT NULL DEFAULT false,
|
|
ADD COLUMN IF NOT EXISTS display_order INT NOT NULL DEFAULT 1;
|
|
`);
|
|
await queryRunner.query(`
|
|
ALTER TABLE freight.container_types
|
|
ADD COLUMN IF NOT EXISTS wagons_per_unit NUMERIC(4,2);
|
|
`);
|
|
await queryRunner.query(`
|
|
UPDATE freight.container_types
|
|
SET wagons_per_unit = CASE
|
|
WHEN containers_per_wagon > 0 THEN ROUND(1.0 / containers_per_wagon, 2)
|
|
ELSE 1.00
|
|
END
|
|
WHERE wagons_per_unit IS NULL;
|
|
`);
|
|
await queryRunner.query(`
|
|
UPDATE freight.container_types
|
|
SET size_ft = CASE WHEN code LIKE '40%' OR code LIKE '%40%' THEN 40 ELSE 20 END
|
|
WHERE size_ft IS NULL;
|
|
`);
|
|
await queryRunner.query(`
|
|
ALTER TABLE freight.container_types
|
|
ALTER COLUMN wagons_per_unit SET NOT NULL,
|
|
DROP COLUMN IF EXISTS containers_per_wagon;
|
|
`);
|
|
|
|
// ── weight_limit_rules ──────────────────────────────────────────────────
|
|
await queryRunner.query(`
|
|
DO $$ BEGIN
|
|
ALTER TABLE freight.weight_limit_rules RENAME COLUMN max_weight_tons TO max_vgm_tons;
|
|
EXCEPTION WHEN undefined_column THEN NULL;
|
|
END $$;
|
|
`);
|
|
await queryRunner.query(`
|
|
ALTER TABLE freight.weight_limit_rules
|
|
ALTER COLUMN max_vgm_tons TYPE NUMERIC(8,3);
|
|
`);
|
|
await queryRunner.query(`
|
|
ALTER TABLE freight.weight_limit_rules
|
|
ADD COLUMN IF NOT EXISTS effective_from DATE NOT NULL DEFAULT CURRENT_DATE,
|
|
ADD COLUMN IF NOT EXISTS effective_to DATE;
|
|
`);
|
|
await queryRunner.query(`
|
|
ALTER TABLE freight.weight_limit_rules
|
|
DROP COLUMN IF EXISTS warning_threshold_tons,
|
|
DROP COLUMN IF EXISTS exceeded_action,
|
|
DROP COLUMN IF EXISTS surcharge_id,
|
|
DROP COLUMN IF EXISTS is_active;
|
|
`);
|
|
|
|
// ── priority_rules ────────────────────────────────────────────────────
|
|
await queryRunner.query(`
|
|
ALTER TABLE freight.priority_rules
|
|
ADD COLUMN IF NOT EXISTS code VARCHAR(40),
|
|
ADD COLUMN IF NOT EXISTS label VARCHAR(100),
|
|
ADD COLUMN IF NOT EXISTS score INT NOT NULL DEFAULT 0,
|
|
ADD COLUMN IF NOT EXISTS condition_currency VARCHAR(5);
|
|
`);
|
|
await queryRunner.query(`
|
|
UPDATE freight.priority_rules
|
|
SET code = COALESCE(code, upper(priority_type::text)),
|
|
label = COALESCE(label, rule_name),
|
|
score = COALESCE(score, bonus_points)
|
|
WHERE code IS NULL OR label IS NULL;
|
|
`);
|
|
await queryRunner.query(`
|
|
ALTER TABLE freight.priority_rules
|
|
DROP COLUMN IF EXISTS priority_type,
|
|
DROP COLUMN IF EXISTS rule_name,
|
|
DROP COLUMN IF EXISTS bonus_points,
|
|
DROP COLUMN IF EXISTS activation_condition,
|
|
DROP COLUMN IF EXISTS description;
|
|
`);
|
|
await queryRunner.query(`
|
|
ALTER TABLE freight.priority_rules
|
|
ALTER COLUMN code SET NOT NULL,
|
|
ALTER COLUMN label SET NOT NULL;
|
|
`);
|
|
await queryRunner.createIndex(
|
|
'freight.priority_rules',
|
|
new TableIndex({ name: 'UQ_priority_rules_code', columnNames: ['code'], isUnique: true }),
|
|
);
|
|
|
|
// ── rates (before surcharge_types.rate_id) ────────────────────────────
|
|
await queryRunner.createTable(
|
|
new Table({
|
|
name: 'rates',
|
|
schema: 'freight',
|
|
columns: [
|
|
{ name: 'id', type: 'uuid', isPrimary: true, generationStrategy: 'uuid', default: 'uuid_generate_v4()' },
|
|
{ name: 'rate_type', type: 'varchar', length: '50' },
|
|
{ name: 'container_type_id', type: 'uuid', isNullable: true },
|
|
{ name: 'trade_direction', type: 'varchar', length: '10', isNullable: true },
|
|
{ name: 'currency', type: 'varchar', length: '5' },
|
|
{ name: 'rate_value', type: 'numeric', precision: 14, scale: 4 },
|
|
{ name: 'rate_unit', type: 'varchar', length: '30' },
|
|
{ name: 'status', type: 'varchar', length: '20', default: "'DRAFT'" },
|
|
{ name: 'proposed_by_staff_id', type: 'uuid' },
|
|
{ name: 'approved_by_ceo_id', type: 'uuid', isNullable: true },
|
|
{ name: 'approved_at', type: 'timestamptz', isNullable: true },
|
|
{ name: 'effective_from', type: 'date' },
|
|
{ name: 'effective_to', type: 'date', isNullable: true },
|
|
{ name: 'created_at', type: 'timestamptz', default: 'now()' },
|
|
{ name: 'updated_at', type: 'timestamptz', default: 'now()' },
|
|
{ name: 'deleted_at', type: 'timestamptz', isNullable: true },
|
|
],
|
|
}),
|
|
true,
|
|
);
|
|
|
|
// ── surcharge_types ───────────────────────────────────────────────────
|
|
await queryRunner.query(`
|
|
DO $$ BEGIN
|
|
ALTER TABLE freight.surcharge_types RENAME COLUMN name TO label;
|
|
EXCEPTION WHEN undefined_column THEN NULL;
|
|
END $$;
|
|
`);
|
|
await queryRunner.query(`
|
|
ALTER TABLE freight.surcharge_types
|
|
ADD COLUMN IF NOT EXISTS trigger_condition VARCHAR(50),
|
|
ADD COLUMN IF NOT EXISTS rate_id UUID;
|
|
`);
|
|
await queryRunner.query(`ALTER TABLE freight.surcharge_types DROP COLUMN IF EXISTS description`);
|
|
|
|
await queryRunner.query(`DROP TABLE IF EXISTS freight.surcharges CASCADE`);
|
|
|
|
// ── yards ─────────────────────────────────────────────────────────────
|
|
await queryRunner.createTable(
|
|
new Table({
|
|
name: 'yards',
|
|
schema: 'freight',
|
|
columns: [
|
|
{ name: 'id', type: 'uuid', isPrimary: true, generationStrategy: 'uuid', default: 'uuid_generate_v4()' },
|
|
{ name: 'code', type: 'varchar', length: '20' },
|
|
{ name: 'label', type: 'varchar', length: '100' },
|
|
{ name: 'country', type: 'varchar', length: '50' },
|
|
{ name: 'is_active', type: 'boolean', default: true },
|
|
{ name: 'display_order', type: 'int', default: 1 },
|
|
{ name: 'created_at', type: 'timestamptz', default: 'now()' },
|
|
{ name: 'updated_at', type: 'timestamptz', default: 'now()' },
|
|
{ name: 'deleted_at', type: 'timestamptz', isNullable: true },
|
|
],
|
|
}),
|
|
true,
|
|
);
|
|
await queryRunner.createIndex(
|
|
'freight.yards',
|
|
new TableIndex({ name: 'UQ_yards_code', columnNames: ['code'], isUnique: true }),
|
|
);
|
|
|
|
// ── shipping_lines ────────────────────────────────────────────────────
|
|
await queryRunner.createTable(
|
|
new Table({
|
|
name: 'shipping_lines',
|
|
schema: 'freight',
|
|
columns: [
|
|
{ name: 'id', type: 'uuid', isPrimary: true, generationStrategy: 'uuid', default: 'uuid_generate_v4()' },
|
|
{ name: 'code', type: 'varchar', length: '20' },
|
|
{ name: 'label', type: 'varchar', length: '100' },
|
|
{ name: 'mapped_to_code', type: 'varchar', length: '20', isNullable: true },
|
|
{ name: 'show_extra_fee_notice', type: 'boolean', default: false },
|
|
{ name: 'is_active', type: 'boolean', default: true },
|
|
{ name: 'created_at', type: 'timestamptz', default: 'now()' },
|
|
{ name: 'updated_at', type: 'timestamptz', default: 'now()' },
|
|
{ name: 'deleted_at', type: 'timestamptz', isNullable: true },
|
|
],
|
|
}),
|
|
true,
|
|
);
|
|
|
|
// ── approval_rules ────────────────────────────────────────────────────
|
|
await queryRunner.createTable(
|
|
new Table({
|
|
name: 'approval_rules',
|
|
schema: 'freight',
|
|
columns: [
|
|
{ name: 'id', type: 'uuid', isPrimary: true, generationStrategy: 'uuid', default: 'uuid_generate_v4()' },
|
|
{ name: 'requires_director_approval', type: 'boolean' },
|
|
{ name: 'step_order', type: 'smallint' },
|
|
{ name: 'required_role', type: 'varchar', length: '30' },
|
|
{ name: 'action_label', type: 'varchar', length: '50' },
|
|
{ name: 'blocks_role', type: 'varchar', length: '30', isNullable: true },
|
|
{ name: 'created_at', type: 'timestamptz', default: 'now()' },
|
|
{ name: 'updated_at', type: 'timestamptz', default: 'now()' },
|
|
{ name: 'deleted_at', type: 'timestamptz', isNullable: true },
|
|
],
|
|
}),
|
|
true,
|
|
);
|
|
await queryRunner.createUniqueConstraint(
|
|
'freight.approval_rules',
|
|
new TableUnique({
|
|
name: 'UQ_approval_rules_chain_step',
|
|
columnNames: ['requires_director_approval', 'step_order'],
|
|
}),
|
|
);
|
|
|
|
// ── bookings ────────────────────────────────────────────────────────
|
|
await queryRunner.query(`
|
|
ALTER TABLE freight.bookings
|
|
ADD COLUMN IF NOT EXISTS origin_yard_id UUID,
|
|
ADD COLUMN IF NOT EXISTS destination_yard_id UUID,
|
|
ADD COLUMN IF NOT EXISTS service_type_id UUID,
|
|
ADD COLUMN IF NOT EXISTS cargo_type_id UUID,
|
|
ADD COLUMN IF NOT EXISTS cargo_free_text VARCHAR(200),
|
|
ADD COLUMN IF NOT EXISTS shipping_line_id UUID,
|
|
ADD COLUMN IF NOT EXISTS pnr_code VARCHAR(50),
|
|
ADD COLUMN IF NOT EXISTS customer_signed_at TIMESTAMPTZ,
|
|
ADD COLUMN IF NOT EXISTS fully_executed_at TIMESTAMPTZ;
|
|
`);
|
|
|
|
await queryRunner.query(`
|
|
INSERT INTO freight.yards (id, code, label, country, is_active, display_order, created_at, updated_at)
|
|
VALUES
|
|
(uuid_generate_v4(), 'KALITY', 'Kality Rail Terminal', 'Ethiopia', true, 1, now(), now()),
|
|
(uuid_generate_v4(), 'MOJO', 'Mojo Dry Port', 'Ethiopia', true, 2, now(), now()),
|
|
(uuid_generate_v4(), 'DIRE_DAWA', 'Dire Dawa Yard', 'Ethiopia', true, 3, now(), now()),
|
|
(uuid_generate_v4(), 'DJIB_PORT', 'Djibouti Port Terminal', 'Djibouti', true, 4, now(), now()),
|
|
(uuid_generate_v4(), 'NAGAD', 'Nagad Terminal, Djibouti', 'Djibouti', true, 5, now(), now()),
|
|
(uuid_generate_v4(), 'LEGACY_ORIGIN', 'Legacy Origin', 'Ethiopia', true, 99, now(), now()),
|
|
(uuid_generate_v4(), 'LEGACY_DEST', 'Legacy Destination', 'Ethiopia', true, 100, now(), now())
|
|
ON CONFLICT (code) DO NOTHING;
|
|
`);
|
|
|
|
await queryRunner.query(`
|
|
INSERT INTO freight.service_types (id, code, service_name, can_be_booked_alone, includes_first_mile, includes_last_mile, includes_customs, priority_bonus_points, is_active, display_order, created_at, updated_at)
|
|
SELECT uuid_generate_v4(), 'RAIL', 'Rail Transport Only', true, false, false, false, 0, true, 1, now(), now()
|
|
WHERE NOT EXISTS (SELECT 1 FROM freight.service_types LIMIT 1);
|
|
`);
|
|
await queryRunner.query(`
|
|
INSERT INTO freight.cargo_types (id, code, cargo_type_name, requires_director_approval, show_free_text_box, is_active, display_order, created_at, updated_at)
|
|
SELECT uuid_generate_v4(), 'GENERAL', 'General Cargo', false, false, true, 1, now(), now()
|
|
WHERE NOT EXISTS (SELECT 1 FROM freight.cargo_types LIMIT 1);
|
|
`);
|
|
|
|
const hasServiceTypeCol = await queryRunner.query(`
|
|
SELECT 1 FROM information_schema.columns
|
|
WHERE table_schema = 'freight' AND table_name = 'bookings' AND column_name = 'service_type'
|
|
LIMIT 1;
|
|
`);
|
|
|
|
if (hasServiceTypeCol.length > 0) {
|
|
await queryRunner.query(`
|
|
UPDATE freight.bookings b
|
|
SET service_type_id = st.id
|
|
FROM freight.service_types st
|
|
WHERE b.service_type_id IS NULL
|
|
AND (
|
|
st.code = b.service_type
|
|
OR upper(replace(st.service_name, ' ', '_')) = upper(b.service_type)
|
|
OR st.code = upper(replace(b.service_type, ' ', '_'))
|
|
);
|
|
`);
|
|
await queryRunner.query(`
|
|
UPDATE freight.bookings b
|
|
SET cargo_type_id = ct.id
|
|
FROM freight.cargo_types ct
|
|
WHERE b.cargo_type_id IS NULL
|
|
AND (
|
|
ct.code = upper(b.freight_type)
|
|
OR ct.code = upper(concat(b.freight_type, '_', coalesce(b.freight_subtype, '')))
|
|
);
|
|
`);
|
|
await queryRunner.query(`
|
|
UPDATE freight.bookings b
|
|
SET cargo_free_text = b.freight_subtype
|
|
WHERE b.cargo_free_text IS NULL AND b.freight_subtype IS NOT NULL;
|
|
`);
|
|
await queryRunner.query(`
|
|
UPDATE freight.bookings b
|
|
SET origin_yard_id = y.id
|
|
FROM freight.yards y
|
|
WHERE b.origin_yard_id IS NULL
|
|
AND (y.label ILIKE b.origin_station OR y.code = upper(replace(b.origin_station, ' ', '_')));
|
|
`);
|
|
await queryRunner.query(`
|
|
UPDATE freight.bookings b
|
|
SET destination_yard_id = y.id
|
|
FROM freight.yards y
|
|
WHERE b.destination_yard_id IS NULL
|
|
AND (y.label ILIKE b.destination_station OR y.code = upper(replace(b.destination_station, ' ', '_')));
|
|
`);
|
|
}
|
|
|
|
const defaultServiceTypeId = await queryRunner.query(
|
|
`SELECT id FROM freight.service_types ORDER BY display_order LIMIT 1`,
|
|
);
|
|
const defaultCargoTypeId = await queryRunner.query(
|
|
`SELECT id FROM freight.cargo_types ORDER BY display_order LIMIT 1`,
|
|
);
|
|
const legacyOriginId = await queryRunner.query(
|
|
`SELECT id FROM freight.yards WHERE code = 'LEGACY_ORIGIN' LIMIT 1`,
|
|
);
|
|
const legacyDestId = await queryRunner.query(
|
|
`SELECT id FROM freight.yards WHERE code = 'LEGACY_DEST' LIMIT 1`,
|
|
);
|
|
|
|
if (defaultServiceTypeId[0]?.id) {
|
|
await queryRunner.query(
|
|
`UPDATE freight.bookings SET service_type_id = $1 WHERE service_type_id IS NULL`,
|
|
[defaultServiceTypeId[0].id],
|
|
);
|
|
}
|
|
if (defaultCargoTypeId[0]?.id) {
|
|
await queryRunner.query(
|
|
`UPDATE freight.bookings SET cargo_type_id = $1 WHERE cargo_type_id IS NULL`,
|
|
[defaultCargoTypeId[0].id],
|
|
);
|
|
}
|
|
if (legacyOriginId[0]?.id) {
|
|
await queryRunner.query(
|
|
`UPDATE freight.bookings SET origin_yard_id = $1 WHERE origin_yard_id IS NULL`,
|
|
[legacyOriginId[0].id],
|
|
);
|
|
}
|
|
if (legacyDestId[0]?.id) {
|
|
await queryRunner.query(
|
|
`UPDATE freight.bookings SET destination_yard_id = $1 WHERE destination_yard_id IS NULL`,
|
|
[legacyDestId[0].id],
|
|
);
|
|
}
|
|
|
|
const nullBookings = await queryRunner.query(
|
|
`SELECT COUNT(*)::int AS cnt FROM freight.bookings WHERE service_type_id IS NULL OR cargo_type_id IS NULL OR origin_yard_id IS NULL OR destination_yard_id IS NULL`,
|
|
);
|
|
if (nullBookings[0]?.cnt > 0) {
|
|
await queryRunner.query(`DELETE FROM freight.bookings WHERE service_type_id IS NULL OR cargo_type_id IS NULL OR origin_yard_id IS NULL OR destination_yard_id IS NULL`);
|
|
}
|
|
|
|
await queryRunner.query(`
|
|
ALTER TABLE freight.bookings
|
|
ALTER COLUMN service_type_id SET NOT NULL,
|
|
ALTER COLUMN cargo_type_id SET NOT NULL,
|
|
ALTER COLUMN origin_yard_id SET NOT NULL,
|
|
ALTER COLUMN destination_yard_id SET NOT NULL;
|
|
`);
|
|
|
|
await queryRunner.query(`
|
|
ALTER TABLE freight.bookings
|
|
DROP COLUMN IF EXISTS origin_station,
|
|
DROP COLUMN IF EXISTS destination_station,
|
|
DROP COLUMN IF EXISTS service_type,
|
|
DROP COLUMN IF EXISTS freight_type,
|
|
DROP COLUMN IF EXISTS freight_subtype,
|
|
DROP COLUMN IF EXISTS containers,
|
|
DROP COLUMN IF EXISTS first_mile_enabled,
|
|
DROP COLUMN IF EXISTS last_mile_enabled,
|
|
DROP COLUMN IF EXISTS is_refrigerated;
|
|
`);
|
|
|
|
// ── booking_container ─────────────────────────────────────────────────
|
|
await queryRunner.createTable(
|
|
new Table({
|
|
name: 'booking_container',
|
|
schema: 'freight',
|
|
columns: [
|
|
{ name: 'id', type: 'uuid', isPrimary: true, generationStrategy: 'uuid', default: 'uuid_generate_v4()' },
|
|
{ name: 'booking_id', type: 'uuid' },
|
|
{ name: 'container_type_id', type: 'uuid' },
|
|
{ name: 'quantity', type: 'smallint' },
|
|
{ name: 'vgm_per_unit_tons', type: 'numeric', precision: 10, scale: 3 },
|
|
{ name: 'total_vgm_tons', type: 'numeric', precision: 12, scale: 3 },
|
|
{ name: 'wagons_required', type: 'numeric', precision: 6, scale: 2 },
|
|
{ name: 'weight_limit_rule_id', type: 'uuid', isNullable: true },
|
|
{ name: 'is_overweight', type: 'boolean', default: false },
|
|
{ name: 'overweight_excess_tons', type: 'numeric', precision: 10, scale: 3, isNullable: true },
|
|
{ name: 'created_at', type: 'timestamptz', default: 'now()' },
|
|
{ name: 'updated_at', type: 'timestamptz', default: 'now()' },
|
|
{ name: 'deleted_at', type: 'timestamptz', isNullable: true },
|
|
],
|
|
}),
|
|
true,
|
|
);
|
|
|
|
await queryRunner.createTable(
|
|
new Table({
|
|
name: 'booking_rate_snapshot',
|
|
schema: 'freight',
|
|
columns: [
|
|
{ name: 'id', type: 'uuid', isPrimary: true, generationStrategy: 'uuid', default: 'uuid_generate_v4()' },
|
|
{ name: 'booking_id', type: 'uuid' },
|
|
{ name: 'rate_id', type: 'uuid' },
|
|
{ name: 'rate_type', type: 'varchar', length: '50' },
|
|
{ name: 'rate_value', type: 'numeric', precision: 14, scale: 4 },
|
|
{ name: 'rate_unit', type: 'varchar', length: '30' },
|
|
{ name: 'currency', type: 'varchar', length: '5' },
|
|
{ name: 'snapshotted_at', type: 'timestamptz' },
|
|
{ name: 'created_at', type: 'timestamptz', default: 'now()' },
|
|
{ name: 'updated_at', type: 'timestamptz', default: 'now()' },
|
|
{ name: 'deleted_at', type: 'timestamptz', isNullable: true },
|
|
],
|
|
}),
|
|
true,
|
|
);
|
|
|
|
await queryRunner.createTable(
|
|
new Table({
|
|
name: 'booking_cargo_modifier',
|
|
schema: 'freight',
|
|
columns: [
|
|
{ name: 'id', type: 'uuid', isPrimary: true, generationStrategy: 'uuid', default: 'uuid_generate_v4()' },
|
|
{ name: 'booking_id', type: 'uuid' },
|
|
{ name: 'surcharge_type_id', type: 'uuid' },
|
|
{ name: 'trigger_value', type: 'numeric', precision: 14, scale: 4, isNullable: true },
|
|
{ name: 'calculated_amount', type: 'numeric', precision: 14, scale: 2 },
|
|
{ name: 'rate_snapshot_id', type: 'uuid' },
|
|
{ name: 'created_at', type: 'timestamptz', default: 'now()' },
|
|
{ name: 'updated_at', type: 'timestamptz', default: 'now()' },
|
|
{ name: 'deleted_at', type: 'timestamptz', isNullable: true },
|
|
],
|
|
}),
|
|
true,
|
|
);
|
|
|
|
await queryRunner.createTable(
|
|
new Table({
|
|
name: 'booking_approval_step',
|
|
schema: 'freight',
|
|
columns: [
|
|
{ name: 'id', type: 'uuid', isPrimary: true, generationStrategy: 'uuid', default: 'uuid_generate_v4()' },
|
|
{ name: 'booking_id', type: 'uuid' },
|
|
{ name: 'approval_rule_id', type: 'uuid' },
|
|
{ name: 'step_order', type: 'smallint' },
|
|
{ name: 'required_role', type: 'varchar', length: '30' },
|
|
{ name: 'status', type: 'varchar', length: '20', default: "'PENDING'" },
|
|
{ name: 'actioned_by_staff_id', type: 'uuid', isNullable: true },
|
|
{ name: 'actioned_at', type: 'timestamptz', isNullable: true },
|
|
{ name: 'remarks', type: 'text', isNullable: true },
|
|
{ name: 'created_at', type: 'timestamptz', default: 'now()' },
|
|
{ name: 'updated_at', type: 'timestamptz', default: 'now()' },
|
|
{ name: 'deleted_at', type: 'timestamptz', isNullable: true },
|
|
],
|
|
}),
|
|
true,
|
|
);
|
|
|
|
// Foreign keys
|
|
await queryRunner.createForeignKey(
|
|
'freight.surcharge_types',
|
|
new TableForeignKey({
|
|
name: 'FK_surcharge_types_rate_id',
|
|
columnNames: ['rate_id'],
|
|
referencedTableName: 'rates',
|
|
referencedSchema: 'freight',
|
|
referencedColumnNames: ['id'],
|
|
}),
|
|
);
|
|
await queryRunner.createForeignKey(
|
|
'freight.booking_container',
|
|
new TableForeignKey({
|
|
columnNames: ['booking_id'],
|
|
referencedTableName: 'bookings',
|
|
referencedSchema: 'freight',
|
|
referencedColumnNames: ['id'],
|
|
onDelete: 'CASCADE',
|
|
}),
|
|
);
|
|
await queryRunner.createForeignKey(
|
|
'freight.bookings',
|
|
new TableForeignKey({
|
|
columnNames: ['origin_yard_id'],
|
|
referencedTableName: 'yards',
|
|
referencedSchema: 'freight',
|
|
referencedColumnNames: ['id'],
|
|
}),
|
|
);
|
|
await queryRunner.createForeignKey(
|
|
'freight.bookings',
|
|
new TableForeignKey({
|
|
columnNames: ['destination_yard_id'],
|
|
referencedTableName: 'yards',
|
|
referencedSchema: 'freight',
|
|
referencedColumnNames: ['id'],
|
|
}),
|
|
);
|
|
}
|
|
|
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
await queryRunner.dropTable('freight.booking_approval_step', true);
|
|
await queryRunner.dropTable('freight.booking_cargo_modifier', true);
|
|
await queryRunner.dropTable('freight.booking_rate_snapshot', true);
|
|
await queryRunner.dropTable('freight.booking_container', true);
|
|
|
|
await queryRunner.query(`
|
|
ALTER TABLE freight.bookings
|
|
ADD COLUMN IF NOT EXISTS origin_station VARCHAR(255),
|
|
ADD COLUMN IF NOT EXISTS destination_station VARCHAR(255),
|
|
ADD COLUMN IF NOT EXISTS service_type VARCHAR(30),
|
|
ADD COLUMN IF NOT EXISTS freight_type VARCHAR(20),
|
|
ADD COLUMN IF NOT EXISTS freight_subtype VARCHAR(100),
|
|
ADD COLUMN IF NOT EXISTS containers JSONB,
|
|
ADD COLUMN IF NOT EXISTS first_mile_enabled BOOLEAN DEFAULT false,
|
|
ADD COLUMN IF NOT EXISTS last_mile_enabled BOOLEAN DEFAULT false,
|
|
ADD COLUMN IF NOT EXISTS is_refrigerated BOOLEAN DEFAULT false;
|
|
`);
|
|
await queryRunner.query(`
|
|
ALTER TABLE freight.bookings
|
|
DROP COLUMN IF EXISTS origin_yard_id,
|
|
DROP COLUMN IF EXISTS destination_yard_id,
|
|
DROP COLUMN IF EXISTS service_type_id,
|
|
DROP COLUMN IF EXISTS cargo_type_id,
|
|
DROP COLUMN IF EXISTS cargo_free_text,
|
|
DROP COLUMN IF EXISTS shipping_line_id,
|
|
DROP COLUMN IF EXISTS pnr_code,
|
|
DROP COLUMN IF EXISTS customer_signed_at,
|
|
DROP COLUMN IF EXISTS fully_executed_at;
|
|
`);
|
|
|
|
await queryRunner.dropTable('freight.approval_rules', true);
|
|
await queryRunner.dropTable('freight.shipping_lines', true);
|
|
await queryRunner.dropTable('freight.yards', true);
|
|
await queryRunner.dropTable('freight.rates', true);
|
|
}
|
|
}
|