mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 18:42:49 +00:00
add seed
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
/**
|
||||
* Re-seed the EDR wagon fleet onto the official ER numbering.
|
||||
*
|
||||
* Supersedes SeedWagonsWithYardAssignment1784000000001, which seeded 500 wagons
|
||||
* on a `<CODE>-NNNN` scheme and wrote the status as 'Available' — mixed case
|
||||
* that never matches WagonStatus.Available ('AVAILABLE'), so status filters
|
||||
* silently returned nothing. This seed uses the enum value.
|
||||
*
|
||||
* Every wagon lands unassigned: current_yard_id NULL, status AVAILABLE. Wagon
|
||||
* specs (capacity/length/tare) stay owned by wagon_types and are not touched —
|
||||
* the types already exist and only the wagon↔type link is (re)established here.
|
||||
*/
|
||||
type FleetRow = {
|
||||
code: string;
|
||||
start: number;
|
||||
end: number;
|
||||
count: number;
|
||||
};
|
||||
|
||||
/** Official fleet: 1100 wagons, ER0001–ER1100, contiguous across 10 types. */
|
||||
const FLEET: FleetRow[] = [
|
||||
{ code: 'PW2', start: 1, end: 220, count: 220 },
|
||||
{ code: 'CW4', start: 221, end: 330, count: 110 },
|
||||
{ code: 'CW3', start: 331, end: 350, count: 20 },
|
||||
{ code: 'KW2', start: 351, end: 370, count: 20 },
|
||||
{ code: 'KW3', start: 371, end: 390, count: 20 },
|
||||
{ code: 'NW5', start: 391, end: 940, count: 550 },
|
||||
{ code: 'BW1', start: 941, end: 950, count: 10 },
|
||||
{ code: 'GW2', start: 951, end: 1060, count: 110 },
|
||||
{ code: 'NW6', start: 1061, end: 1080, count: 20 },
|
||||
{ code: 'NW7', start: 1081, end: 1100, count: 20 },
|
||||
];
|
||||
|
||||
const wagonNumber = (sequence: number) => `ER${String(sequence).padStart(4, '0')}`;
|
||||
|
||||
export class SeedEdrWagonFleetErNumbering2260000000000 implements MigrationInterface {
|
||||
name = 'SeedEdrWagonFleetErNumbering2260000000000';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
// Full replacement: the ER range is the fleet of record, so any wagon
|
||||
// outside it is stale seed data. Safe to hard-delete — containers and
|
||||
// train_set_wagons null their link, wagon_movements cascade.
|
||||
await queryRunner.query(`DELETE FROM freight.wagons;`);
|
||||
|
||||
for (const row of FLEET) {
|
||||
if (row.end - row.start + 1 !== row.count) {
|
||||
throw new Error(`wagon_range_mismatch:${row.code}`);
|
||||
}
|
||||
|
||||
const [typeRecord] = await queryRunner.query(
|
||||
`SELECT id FROM freight.wagon_types WHERE code = $1 AND deleted_at IS NULL LIMIT 1;`,
|
||||
[row.code],
|
||||
);
|
||||
|
||||
if (!typeRecord?.id) {
|
||||
throw new Error(`wagon_type_missing:${row.code}`);
|
||||
}
|
||||
|
||||
// generate_series builds the range server-side — one round trip per type
|
||||
// instead of 1100 individual INSERTs.
|
||||
await queryRunner.query(
|
||||
`
|
||||
INSERT INTO freight.wagons (
|
||||
wagon_number,
|
||||
wagon_type_id,
|
||||
status,
|
||||
current_yard_id,
|
||||
train_id,
|
||||
sequence_number,
|
||||
notes,
|
||||
train_set_wagon_id,
|
||||
current_train_schedule_id
|
||||
)
|
||||
SELECT
|
||||
'ER' || LPAD(seq::text, 4, '0'),
|
||||
$1::uuid,
|
||||
'AVAILABLE',
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
FROM generate_series($2::int, $3::int) AS seq
|
||||
ON CONFLICT (wagon_number) DO UPDATE SET
|
||||
wagon_type_id = EXCLUDED.wagon_type_id,
|
||||
status = EXCLUDED.status,
|
||||
current_yard_id = EXCLUDED.current_yard_id,
|
||||
updated_at = now();
|
||||
`,
|
||||
[typeRecord.id, row.start, row.end],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`DELETE FROM freight.wagons WHERE wagon_number BETWEEN $1 AND $2;`,
|
||||
[wagonNumber(FLEET[0].start), wagonNumber(FLEET[FLEET.length - 1].end)],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { AppDataSource } from '../data-source';
|
||||
import { SeedEdRWagonFleet1750400000000 } from '../migrations/1750400000000-SeedEdRWagonFleet';
|
||||
import { SeedEdrWagonFleetErNumbering2260000000000 } from '../migrations/2260000000000-SeedEdrWagonFleetErNumbering';
|
||||
|
||||
async function seedEdRWagons() {
|
||||
await AppDataSource.initialize();
|
||||
@@ -10,28 +10,32 @@ async function seedEdRWagons() {
|
||||
await queryRunner.connect();
|
||||
await queryRunner.startTransaction();
|
||||
|
||||
await new SeedEdRWagonFleet1750400000000().up(queryRunner);
|
||||
await new SeedEdrWagonFleetErNumbering2260000000000().up(queryRunner);
|
||||
|
||||
const [summary] = await queryRunner.query(`
|
||||
const summary = await queryRunner.query(`
|
||||
SELECT
|
||||
COUNT(*)::int AS total,
|
||||
COUNT(*) FILTER (WHERE wt.code = 'PW2')::int AS pw2,
|
||||
COUNT(*) FILTER (WHERE wt.code = 'CW4')::int AS cw4,
|
||||
COUNT(*) FILTER (WHERE wt.code = 'CW3')::int AS cw3,
|
||||
COUNT(*) FILTER (WHERE wt.code = 'KW2')::int AS kw2,
|
||||
COUNT(*) FILTER (WHERE wt.code = 'KW3')::int AS kw3,
|
||||
COUNT(*) FILTER (WHERE wt.code = 'NW5')::int AS nw5,
|
||||
COUNT(*) FILTER (WHERE wt.supported_load_types @> ARRAY['CONTAINER'])::int AS container_ready,
|
||||
COUNT(*) FILTER (WHERE wt.supported_load_types @> ARRAY['BULK'])::int AS bulk_ready,
|
||||
COUNT(*) FILTER (WHERE w.status = 'IMPORT_READY')::int AS import_ready
|
||||
wt.code,
|
||||
wt.name,
|
||||
COUNT(*)::int AS wagons,
|
||||
MIN(w.wagon_number) AS first_wagon,
|
||||
MAX(w.wagon_number) AS last_wagon,
|
||||
COUNT(*) FILTER (WHERE w.status = 'AVAILABLE')::int AS available,
|
||||
COUNT(*) FILTER (WHERE w.current_yard_id IS NULL)::int AS unassigned_yard
|
||||
FROM freight.wagons w
|
||||
JOIN freight.wagon_types wt ON wt.id = w.wagon_type_id
|
||||
WHERE w.wagon_number BETWEEN 'ER0001' AND 'ER0940';
|
||||
WHERE w.wagon_number BETWEEN 'ER0001' AND 'ER1100'
|
||||
GROUP BY wt.code, wt.name
|
||||
ORDER BY MIN(w.wagon_number);
|
||||
`);
|
||||
|
||||
const [totals] = await queryRunner.query(`
|
||||
SELECT COUNT(*)::int AS total FROM freight.wagons;
|
||||
`);
|
||||
|
||||
await queryRunner.commitTransaction();
|
||||
|
||||
console.log('Seeded EDR wagon fleet:', summary);
|
||||
console.table(summary);
|
||||
console.log(`Seeded EDR wagon fleet — ${totals.total} wagons total (expected 1100).`);
|
||||
} catch (error) {
|
||||
await queryRunner.rollbackTransaction();
|
||||
throw error;
|
||||
|
||||
Reference in New Issue
Block a user