From 001babbd2d522c01168fa4fa74ae77045562119b Mon Sep 17 00:00:00 2001 From: Hagernesh Date: Thu, 9 Jul 2026 13:40:50 +0000 Subject: [PATCH] fix(warehouses): repair missing warehouse_inventory.grn_number column AddGrnNumberToWarehouseInventory1828000000000 is recorded in the migrations table but the column is absent - it was added, then dropped out-of-band. Because TypeORM has the original recorded it will never re-run, so every GRN read/write fails with "column grn_number does not exist": - bulkReceive() INSERT names grn_number (receive to warehouse) - importQueueByStatuses() Unloaded + Dispatch queues - exportInventoryByStatus() Received / Ready-To-Load / Loaded tabs - grnDocument() GRN PDF Re-adds the column, backfills from the "GRN Number:" receive note, recreates the partial index. Idempotent, and down() is a deliberate no-op so reverting the repair cannot re-introduce the outage. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../2090000000000-RepairGrnNumberColumn.ts | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 apps/edr-freight-api/src/migrations/2090000000000-RepairGrnNumberColumn.ts diff --git a/apps/edr-freight-api/src/migrations/2090000000000-RepairGrnNumberColumn.ts b/apps/edr-freight-api/src/migrations/2090000000000-RepairGrnNumberColumn.ts new file mode 100644 index 000000000..aa2f998a2 --- /dev/null +++ b/apps/edr-freight-api/src/migrations/2090000000000-RepairGrnNumberColumn.ts @@ -0,0 +1,55 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +/** + * Repairs `freight.warehouse_inventory.grn_number`. + * + * AddGrnNumberToWarehouseInventory1828000000000 is recorded in `migrations` but + * the column is absent on at least one environment - it was added, then dropped + * out-of-band (a stray `synchronize: true`, same class of damage that + * RepairSynchronizeDrift1870000000000 already had to undo). Because TypeORM has + * the original recorded, it will never re-run it. + * + * Without the column, everything that reads or writes a GRN fails with + * `column ... grn_number does not exist`: + * - bulkReceive() -> INSERT names grn_number (receive to warehouse) + * - importQueueByStatuses() -> Unloaded + Dispatch queues + * - exportInventoryByStatus() -> Received / Ready-To-Load / Loaded tabs + * - grnDocument() -> GRN PDF + * + * Idempotent: a no-op on environments where the column survived. + */ +export class RepairGrnNumberColumn2090000000000 implements MigrationInterface { + name = 'RepairGrnNumberColumn2090000000000'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE freight.warehouse_inventory + ADD COLUMN IF NOT EXISTS grn_number VARCHAR(100) NULL + `); + + // Recover the GRN for rows received before the column existed: it was also + // written into the receive note as "GRN Number: ". + await queryRunner.query(` + UPDATE freight.warehouse_inventory + SET grn_number = substring(notes FROM 'GRN Number: ([^\\n\\r]+)') + WHERE grn_number IS NULL + AND notes IS NOT NULL + AND notes ~ 'GRN Number: ' + `); + + await queryRunner.query(` + CREATE INDEX IF NOT EXISTS idx_warehouse_inventory_grn_number + ON freight.warehouse_inventory(grn_number) + WHERE grn_number IS NOT NULL + `); + } + + /** + * Deliberately a no-op. Dropping the column is what broke these environments + * in the first place, and the original 1828 migration already owns its own + * down(). Reverting this repair must not re-introduce the outage. + */ + public async down(): Promise { + // intentionally empty + } +}