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) <noreply@anthropic.com>
This commit is contained in:
Hagernesh
2026-07-09 13:40:50 +00:00
parent c53c186896
commit 001babbd2d

View File

@@ -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<void> {
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: <value>".
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<void> {
// intentionally empty
}
}