add reference field to train schedules and implement unique sequence generation

This commit is contained in:
Marshal
2026-07-07 23:06:44 +00:00
parent 88b1e548a2
commit 9dd9ace313
9 changed files with 299 additions and 21 deletions

View File

@@ -0,0 +1,58 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
/**
* Adds train_schedules.reference: a human-facing unique schedule number
* S-YYYY-NNNNN (per-year sequence, like bookings' BK-YYYY-NNNNNN).
*
* - Adds the nullable column.
* - Backfills existing rows: within each created-at year, numbers rows by
* created_at ascending (oldest → S-<year>-00001). Deterministic order.
* - Adds a partial unique index (NULLs allowed so a future insert can stage
* the row before the app stamps its reference).
*/
export class AddTrainScheduleReference2030000000000
implements MigrationInterface
{
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
ALTER TABLE freight.train_schedules
ADD COLUMN IF NOT EXISTS reference VARCHAR(20);
`);
// Backfill per-year, ordered by created_at (oldest = 00001). Uses the row's
// own created-at year as the reference year so historical rows keep a
// sensible number.
await queryRunner.query(`
WITH numbered AS (
SELECT
id,
EXTRACT(YEAR FROM created_at)::int AS yr,
ROW_NUMBER() OVER (
PARTITION BY EXTRACT(YEAR FROM created_at)
ORDER BY created_at ASC, id ASC
) AS seq
FROM freight.train_schedules
WHERE reference IS NULL
)
UPDATE freight.train_schedules ts
SET reference = 'S-' || numbered.yr || '-' || LPAD(numbered.seq::text, 5, '0')
FROM numbered
WHERE ts.id = numbered.id;
`);
await queryRunner.query(`
CREATE UNIQUE INDEX IF NOT EXISTS ux_train_schedules_reference
ON freight.train_schedules (reference)
WHERE reference IS NOT NULL;
`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
DROP INDEX IF EXISTS freight.ux_train_schedules_reference;
`);
await queryRunner.query(`
ALTER TABLE freight.train_schedules DROP COLUMN IF EXISTS reference;
`);
}
}