mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-27 14:20:58 +00:00
gMerge branch 'dev' of github.com:Tria-plc/edr-platform into dev
This commit is contained in:
23
apps/edr-freight-api/src/scripts/seed-dropdown-settings.ts
Normal file
23
apps/edr-freight-api/src/scripts/seed-dropdown-settings.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { AppDataSource } from "../data-source";
|
||||
import { DropdownSettingsSeeder } from "../seed/dropdown-settings.seeder";
|
||||
|
||||
/**
|
||||
* Seed the dropdown settings catalog (codes/labels only, no options) into an
|
||||
* EMPTY dropdown_settings table; skips entirely if any rows exist. Run on
|
||||
* demand:
|
||||
* pnpm --filter @edr/freight-api seed:dropdown-settings
|
||||
*/
|
||||
async function run() {
|
||||
await AppDataSource.initialize();
|
||||
try {
|
||||
const seeder = new DropdownSettingsSeeder(AppDataSource);
|
||||
await seeder.run();
|
||||
} finally {
|
||||
await AppDataSource.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
run().catch((error) => {
|
||||
console.error("Failed to seed dropdown settings:", error);
|
||||
process.exit(1);
|
||||
});
|
||||
39
apps/edr-freight-api/src/scripts/seed-edr-trucks.ts
Normal file
39
apps/edr-freight-api/src/scripts/seed-edr-trucks.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { AppDataSource } from '../data-source';
|
||||
import { EdrTruckFleetSeeder } from '../seed/edr-truck-fleet.seeder';
|
||||
|
||||
/**
|
||||
* Seeds the 62-truck EDR fleet used by first-mile / last-mile.
|
||||
*
|
||||
* The seeder is idempotent (`ON CONFLICT (plate_number) DO NOTHING`), so a
|
||||
* re-run will NOT overwrite a truck whose rate was tuned by hand.
|
||||
*/
|
||||
async function seedEdrTrucks() {
|
||||
await AppDataSource.initialize();
|
||||
|
||||
try {
|
||||
await new EdrTruckFleetSeeder(AppDataSource).run();
|
||||
|
||||
const summary = await AppDataSource.query(`
|
||||
SELECT
|
||||
COUNT(*)::int AS trucks,
|
||||
COUNT(*) FILTER (WHERE status = 'ACTIVE')::int AS active,
|
||||
COUNT(*) FILTER (WHERE availability = 'FREE')::int AS free,
|
||||
COUNT(*) FILTER (WHERE price_per_km > 0)::int AS priced,
|
||||
COUNT(*) FILTER (WHERE price_per_km IS NULL OR price_per_km <= 0)::int AS unpriced,
|
||||
MIN(price_per_km)::text AS min_rate,
|
||||
MAX(price_per_km)::text AS max_rate
|
||||
FROM freight.vehicles
|
||||
WHERE vehicle_type = 'TRUCK';
|
||||
`);
|
||||
|
||||
console.table(summary);
|
||||
console.log('Seeded EDR truck fleet.');
|
||||
} finally {
|
||||
await AppDataSource.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
seedEdrTrucks().catch((error) => {
|
||||
console.error('Failed to seed EDR truck fleet:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -1,5 +1,9 @@
|
||||
import { AppDataSource } from '../data-source';
|
||||
import { SeedEdRWagonFleet1750400000000 } from '../migrations/1750400000000-SeedEdRWagonFleet';
|
||||
import { SeedEdrWagonFleetErNumbering2260000000000 } from '../migrations/2260000000000-SeedEdrWagonFleetErNumbering';
|
||||
import { AddWagonTrainNumbers2270000000000 } from '../migrations/2270000000000-AddWagonTrainNumbers';
|
||||
import { SeedWagonRunNumbers2280000000000 } from '../migrations/2280000000000-SeedWagonRunNumbers';
|
||||
import { WagonNumberPartialUnique2280000000000 } from '../migrations/2280000000000-WagonNumberPartialUnique';
|
||||
import { SeedWagonYardDoraleh2290000000000 } from '../migrations/2290000000000-SeedWagonYardDoraleh';
|
||||
|
||||
async function seedEdRWagons() {
|
||||
await AppDataSource.initialize();
|
||||
@@ -10,28 +14,76 @@ async function seedEdRWagons() {
|
||||
await queryRunner.connect();
|
||||
await queryRunner.startTransaction();
|
||||
|
||||
await new SeedEdRWagonFleet1750400000000().up(queryRunner);
|
||||
// Fleet first (recreates every wagon with NULL yard + NULL runs), then the
|
||||
// columns are ensured to exist, then the run roster and the yard are applied
|
||||
// on top. Same order the migrations run in, so the script and a fresh
|
||||
// migrate agree.
|
||||
await new SeedEdrWagonFleetErNumbering2260000000000().up(queryRunner);
|
||||
await new AddWagonTrainNumbers2270000000000().up(queryRunner);
|
||||
// Not a wagon seed, but it owns wagon_number uniqueness — included so this
|
||||
// script leaves the same schema a real `migration:run` would, rather than a
|
||||
// database missing the partial unique index.
|
||||
await new WagonNumberPartialUnique2280000000000().up(queryRunner);
|
||||
await new SeedWagonRunNumbers2280000000000().up(queryRunner);
|
||||
await new SeedWagonYardDoraleh2290000000000().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 no_yard,
|
||||
COUNT(*) FILTER (WHERE w.export_train_number IS NOT NULL)::int AS on_a_run
|
||||
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,
|
||||
COUNT(*) FILTER (WHERE export_train_number IS NOT NULL)::int AS on_a_run
|
||||
FROM freight.wagons;
|
||||
`);
|
||||
|
||||
const yards = await queryRunner.query(`
|
||||
SELECT
|
||||
COALESCE(y.label, '(no yard)') AS yard,
|
||||
COUNT(*)::int AS wagons
|
||||
FROM freight.wagons w
|
||||
LEFT JOIN freight.yards y ON y.id = w.current_yard_id
|
||||
GROUP BY y.label
|
||||
ORDER BY 2 DESC;
|
||||
`);
|
||||
|
||||
const runs = await queryRunner.query(`
|
||||
SELECT
|
||||
export_train_number AS export_run,
|
||||
import_train_number AS import_run,
|
||||
COUNT(*)::int AS wagons
|
||||
FROM freight.wagons
|
||||
WHERE export_train_number IS NOT NULL
|
||||
GROUP BY export_train_number, import_train_number
|
||||
ORDER BY export_train_number;
|
||||
`);
|
||||
|
||||
await queryRunner.commitTransaction();
|
||||
|
||||
console.log('Seeded EDR wagon fleet:', summary);
|
||||
console.log('\nFleet by wagon type:');
|
||||
console.table(summary);
|
||||
console.log('Run roster (export/import pairs):');
|
||||
console.table(runs);
|
||||
console.log('Fleet by yard:');
|
||||
console.table(yards);
|
||||
console.log(
|
||||
`Seeded EDR wagon fleet — ${totals.total} wagons total (expected 1100), ` +
|
||||
`${totals.on_a_run} on a run (expected 533).`,
|
||||
);
|
||||
} catch (error) {
|
||||
await queryRunner.rollbackTransaction();
|
||||
throw error;
|
||||
|
||||
@@ -209,7 +209,6 @@ async function ensureReferences(manager: any) {
|
||||
code: '40FT',
|
||||
label: '40FT',
|
||||
sizeFt: 40,
|
||||
wagonsPerUnit: 1,
|
||||
isReefer: false,
|
||||
isOpenTop: false,
|
||||
isActive: true,
|
||||
|
||||
@@ -118,7 +118,6 @@ async function main() {
|
||||
code: '40FT',
|
||||
label: '40FT',
|
||||
sizeFt: 40,
|
||||
wagonsPerUnit: 1,
|
||||
isReefer: false,
|
||||
isOpenTop: false,
|
||||
isActive: true,
|
||||
|
||||
@@ -12,6 +12,7 @@ import { Booking } from '../modules/bookings/entities/booking.entity';
|
||||
import { BookingContainer } from '../modules/bookings/entities/booking-container.entity';
|
||||
import { WarehouseInventory } from '../modules/warehouses/entities/warehouse-inventory.entity';
|
||||
import { CargoType } from '../modules/rule-engine/entities/cargo-type.entity';
|
||||
import { wagonsPerUnitForSize } from '../modules/rule-engine/container-type.util';
|
||||
import { ContainerType } from '../modules/rule-engine/entities/container-type.entity';
|
||||
import { ServiceType } from '../modules/rule-engine/entities/service-type.entity';
|
||||
import { Yard } from '../modules/rule-engine/entities/yard.entity';
|
||||
@@ -115,7 +116,7 @@ async function main() {
|
||||
reeferQuantity: 0,
|
||||
vgmPerUnitTons: Number((weightKg / containerQuantity / 1000).toFixed(3)),
|
||||
totalVgmTons: Number((weightKg / 1000).toFixed(3)),
|
||||
wagonsRequired: Math.max(1, containerQuantity * Number(containerType!.wagonsPerUnit ?? 1)),
|
||||
wagonsRequired: Math.max(1, containerQuantity * wagonsPerUnitForSize(containerType!.sizeFt)),
|
||||
isOverweight: false,
|
||||
}),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user