mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-29 21:08:12 +00:00
feat(operations-reporting): plan station targets per cargo category
The OCC report plans a station lane per cargo type — Nagad–Mojo container and Nagad–Mojo fertilizer are separate numbers — but a target's identity was period + metric + dimension + dimensionKey, so the two collided on one slot. `cargo_category` is now part of the row and of the uniqueness check; it stays null for cargo_category and container_class targets, whose dimensionKey already carries the category. The config grid showed raw codes (VOLUME_TONS, cargo_category, a yard code). The list read now sends readable twins alongside the stored codes, which stay exactly as they are because the reports join on them — the same shape YardDistancesService uses. Labels resolve per dimension rather than from one merged map: CONTAINER_EXPORT exists in both vocabularies and reads differently in each, and merging them gave every cargo-category row the container-class wording.
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
/**
|
||||
* A station's plan is per station AND per cargo type, not per station.
|
||||
*
|
||||
* The OCC monthly report plans "Nagad–Mojo multimodal container 122,010 t" and
|
||||
* "Nagad–Mojo fertilizer 18,000 t" as separate lines against the same station,
|
||||
* which the single `dimension_key` column cannot express: a station-keyed target
|
||||
* would apply the whole station's plan to each of its cargo types.
|
||||
*
|
||||
* `cargo_category` is nullable, so `cargo_category` and `container_class`
|
||||
* targets are unaffected — they leave it null and stay keyed on
|
||||
* `dimension_key` alone. The uniqueness index moves to include it, since
|
||||
* (station, category) is now the slot.
|
||||
*/
|
||||
export class OperationsTargetCargoCategory3590000000000 implements MigrationInterface {
|
||||
name = "OperationsTargetCargoCategory3590000000000";
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE freight.operations_targets
|
||||
ADD COLUMN IF NOT EXISTS cargo_category varchar(60);
|
||||
`);
|
||||
|
||||
await queryRunner.query(`DROP INDEX IF EXISTS freight.ux_operations_targets_slot;`);
|
||||
|
||||
// COALESCE rather than a plain column list: a partial unique index treats
|
||||
// NULLs as distinct, which would let the same category target be entered
|
||||
// twice over.
|
||||
await queryRunner.query(`
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS ux_operations_targets_slot
|
||||
ON freight.operations_targets (
|
||||
period_type, period_start, metric, dimension, dimension_key,
|
||||
COALESCE(cargo_category, '')
|
||||
)
|
||||
WHERE deleted_at IS NULL;
|
||||
`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`DROP INDEX IF EXISTS freight.ux_operations_targets_slot;`);
|
||||
await queryRunner.query(`
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS ux_operations_targets_slot
|
||||
ON freight.operations_targets (period_type, period_start, metric, dimension, dimension_key)
|
||||
WHERE deleted_at IS NULL;
|
||||
`);
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE freight.operations_targets DROP COLUMN IF EXISTS cargo_category;
|
||||
`);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user