mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 18:42:49 +00:00
143 lines
6.4 KiB
JavaScript
143 lines
6.4 KiB
JavaScript
/**
|
|
* Seeds the wagon-allocation test bookings into edr_dev (idempotent) and
|
|
* prints their ids. Rows are kept — they are the evidence for the report.
|
|
*
|
|
* References are prefixed WGT- (Wagon Gate Test) so they are easy to find:
|
|
* SELECT * FROM freight.bookings WHERE reference LIKE 'WGT-%';
|
|
*/
|
|
const { Client } = require('pg');
|
|
|
|
const YARD = {
|
|
DORALEH_FREEZONE: 'fc558b95-da28-4fc3-8348-311a290c34ae',
|
|
DIRE_DAWA: 'f7b1686f-d43e-42aa-bc6a-d3d5849296c9',
|
|
KALITY: '61ae1e66-c229-4dcd-9851-b2b9424f3a95',
|
|
DMP: '7b658678-ce2b-41aa-8dc5-b38ba4a76e9b',
|
|
};
|
|
const CARGO = {
|
|
PERISHABLE: 'a5991d3a-d690-4b7e-98fd-ea3333aa16e7', // NW5 30T / PW2 20T
|
|
STEEL_BILLET: '8291ccc3-0dd9-4d78-aa44-284d028a19ce', // NW5 40T / PW2 10T (inverted)
|
|
BEANS: '9afd8eb3-975b-4ea7-a7db-04eb2c20a04d', // PW2 only, no cap
|
|
SAND: '8942884d-9991-42bb-87f7-a70930f8c43c', // CW3/CW4 only, no cap
|
|
};
|
|
const CONTAINER = {
|
|
'20FT': '77cf24ec-e74b-4bdb-b1cc-6f336379cc58',
|
|
'40FT': '349072e7-8a90-4c03-b682-08976abfd7e8',
|
|
};
|
|
const COMPANY = '300d5510-e3a5-4858-bc28-6e3beda8ca80';
|
|
|
|
/** Test bookings: 4 corridors, bulk + container, contested and uncontested. */
|
|
const BOOKINGS = [
|
|
// --- Case A: DORALEH_FREEZONE -> KALITY (full leg), bulk vs container contest
|
|
{ ref: 'WGT-A1', dir: 'IMPORT', type: 'BULK', from: 'DORALEH_FREEZONE', to: 'KALITY',
|
|
cargo: 'PERISHABLE', tons: 695, note: 'A: 695T Perishable, contends with containers for NW5' },
|
|
{ ref: 'WGT-A2', dir: 'IMPORT', type: 'CONTAINER', from: 'DORALEH_FREEZONE', to: 'KALITY',
|
|
containers: [{ type: '40FT', qty: 12, vgm: 26 }], note: 'A: 12x40ft, needs 12 container wagons' },
|
|
{ ref: 'WGT-A3', dir: 'IMPORT', type: 'CONTAINER', from: 'DORALEH_FREEZONE', to: 'KALITY',
|
|
containers: [{ type: '20FT', qty: 16, vgm: 12 }], note: 'A: 16x20ft = 8 wagons (TEU paired)' },
|
|
|
|
// --- Case B: sub-corridor legs on the same 3-stop route (leg overlap)
|
|
{ ref: 'WGT-B1', dir: 'IMPORT', type: 'CONTAINER', from: 'DORALEH_FREEZONE', to: 'DIRE_DAWA',
|
|
containers: [{ type: '20FT', qty: 6, vgm: 14 }], note: 'B: leg 1 only (DCT->Dire), 3 wagons' },
|
|
{ ref: 'WGT-B2', dir: 'IMPORT', type: 'BULK', from: 'DIRE_DAWA', to: 'KALITY',
|
|
cargo: 'PERISHABLE', tons: 120, note: 'B: leg 2 only (Dire->Kality) - may reuse leg-1 wagons' },
|
|
|
|
// --- Case C: cap inversion - Steel Billet is CHEAPER on NW5 (40T) than PW2 (10T)
|
|
{ ref: 'WGT-C1', dir: 'IMPORT', type: 'BULK', from: 'DORALEH_FREEZONE', to: 'KALITY',
|
|
cargo: 'STEEL_BILLET', tons: 400, note: 'C: inverted caps - must NOT blindly take PW2' },
|
|
|
|
// --- Case D: exclusive-type cargo (Beans=PW2 only, Sand=CW3/CW4 only)
|
|
{ ref: 'WGT-D1', dir: 'IMPORT', type: 'BULK', from: 'DORALEH_FREEZONE', to: 'KALITY',
|
|
cargo: 'BEANS', tons: 200, note: 'D: PW2-only cargo, no alternative' },
|
|
{ ref: 'WGT-D2', dir: 'IMPORT', type: 'BULK', from: 'DORALEH_FREEZONE', to: 'KALITY',
|
|
cargo: 'SAND', tons: 300, note: 'D: CW3/CW4-only cargo' },
|
|
|
|
// --- Case E: overload one leg (way beyond any train)
|
|
{ ref: 'WGT-E1', dir: 'IMPORT', type: 'BULK', from: 'DORALEH_FREEZONE', to: 'KALITY',
|
|
cargo: 'PERISHABLE', tons: 3000, note: 'E: 3000T - must overflow the loco pull limit' },
|
|
{ ref: 'WGT-E2', dir: 'IMPORT', type: 'BULK', from: 'DORALEH_FREEZONE', to: 'KALITY',
|
|
cargo: 'PERISHABLE', tons: 45, note: 'E: small bulk - must NOT share a wagon with A1' },
|
|
];
|
|
|
|
async function main() {
|
|
const client = new Client({
|
|
host: process.env.DB_HOST || '10.18.7.207',
|
|
port: Number(process.env.DB_PORT || 5432),
|
|
database: process.env.DB_NAME || 'edr_dev',
|
|
user: process.env.DB_USER || 'postgres',
|
|
password: process.env.DB_PASSWORD || 'dcba@1234',
|
|
});
|
|
await client.connect();
|
|
|
|
const out = [];
|
|
for (const b of BOOKINGS) {
|
|
const isBulk = b.type === 'BULK';
|
|
const totalVgm = isBulk
|
|
? b.tons
|
|
: b.containers.reduce((s, l) => s + l.qty * l.vgm, 0);
|
|
|
|
const existing = await client.query(
|
|
`SELECT id FROM freight.bookings WHERE reference = $1`,
|
|
[b.ref],
|
|
);
|
|
let id;
|
|
if (existing.rows.length) {
|
|
id = existing.rows[0].id;
|
|
await client.query(
|
|
`UPDATE freight.bookings
|
|
SET cargo_total_weight_vgm = $2, cargo_type_id = $3,
|
|
origin_yard_id = $4, destination_yard_id = $5,
|
|
freight_type = $6, trade_direction = $7,
|
|
status = 'CLEARANCE_READY', payment_status = 'PENDING',
|
|
scheduling_status = 'ELIGIBLE', train_schedule_id = NULL,
|
|
cargo_free_text = $8, updated_at = now()
|
|
WHERE id = $1`,
|
|
[id, totalVgm, isBulk ? CARGO[b.cargo] : null,
|
|
YARD[b.from], YARD[b.to], b.type, b.dir, b.note],
|
|
);
|
|
} else {
|
|
const res = await client.query(
|
|
`INSERT INTO freight.bookings (
|
|
reference, status, total_amount, payment_status, contract_type,
|
|
trade_direction, equipment_return, cargo_total_weight_vgm,
|
|
is_hazardous, payment_currency, version_number, priority_score,
|
|
origin_yard_id, destination_yard_id, cargo_type_id, cargo_free_text,
|
|
freight_type, scheduling_status, is_government,
|
|
customs_clearing_enabled, is_reefer, booking_type, is_split,
|
|
company_id, created_at, updated_at
|
|
) VALUES ($1,'CLEARANCE_READY',0,'PENDING','NEW',$2,'WITHOUT_RETURN',$3,
|
|
false,'ETB',1,0,$4,$5,$6,$7,$8,'ELIGIBLE',false,
|
|
false,false,'ONE_TIME',false,$9, now(), now())
|
|
RETURNING id`,
|
|
[b.ref, b.dir, totalVgm, YARD[b.from], YARD[b.to],
|
|
isBulk ? CARGO[b.cargo] : null, b.note, b.type, COMPANY],
|
|
);
|
|
id = res.rows[0].id;
|
|
}
|
|
|
|
// Container lines
|
|
await client.query(`DELETE FROM freight.booking_container WHERE booking_id = $1`, [id]);
|
|
if (!isBulk) {
|
|
for (const line of b.containers) {
|
|
await client.query(
|
|
`INSERT INTO freight.booking_container
|
|
(booking_id, container_type_id, quantity, vgm_per_unit_tons,
|
|
total_vgm_tons, wagons_required, container_size, created_at, updated_at)
|
|
VALUES ($1,$2,$3,$4,$5,$6,$7, now(), now())`,
|
|
[id, CONTAINER[line.type], line.qty, line.vgm, line.qty * line.vgm,
|
|
line.type === '40FT' ? line.qty : Math.ceil(line.qty / 2),
|
|
line.type === '40FT' ? '40ft' : '20ft'],
|
|
);
|
|
}
|
|
}
|
|
out.push({ ref: b.ref, id, note: b.note });
|
|
}
|
|
|
|
console.table(out);
|
|
await client.end();
|
|
}
|
|
|
|
main().catch((err) => {
|
|
console.error(err.message);
|
|
process.exit(1);
|
|
});
|