mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-29 22:18:12 +00:00
Merge branch 'freight/develop' of github.com:Tria-plc/edr-platform into freight_feature/priority
This commit is contained in:
139
apps/edr-freight-api/src/seed/batch5-test-data.seeder.ts
Normal file
139
apps/edr-freight-api/src/seed/batch5-test-data.seeder.ts
Normal file
@@ -0,0 +1,139 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { DataSource } from 'typeorm';
|
||||
|
||||
import { Booking } from '../modules/bookings/entities/booking.entity';
|
||||
import { ServiceType } from '../modules/rule-engine/entities/service-type.entity';
|
||||
import { Yard } from '../modules/rule-engine/entities/yard.entity';
|
||||
import { Warehouse } from '../modules/warehouses/entities/warehouse.entity';
|
||||
import { WarehouseInventory } from '../modules/warehouses/entities/warehouse-inventory.entity';
|
||||
import { WarehouseYard } from '../modules/warehouses/entities/warehouse-yard.entity';
|
||||
import { WarehouseZone } from '../modules/warehouses/entities/warehouse-zone.entity';
|
||||
|
||||
const SEED_REFS = ['SEED-B5-EXP-001', 'SEED-B5-EXP-002', 'SEED-B5-EXP-003'];
|
||||
|
||||
const SEEDS = [
|
||||
{ ref: 'SEED-B5-EXP-001', weight: 5000, notes: 'Electronics export cargo' },
|
||||
{ ref: 'SEED-B5-EXP-002', weight: 8500, notes: 'Textile export cargo' },
|
||||
{ ref: 'SEED-B5-EXP-003', weight: 3200, notes: 'Coffee export cargo' },
|
||||
];
|
||||
|
||||
/**
|
||||
* Seeds 3 EXPORT+PAID bookings with READY_FOR_LOADING + inspection PASSED inventory
|
||||
* so the Batch 5 "Ready To Load" tab has visible rows to test against.
|
||||
*
|
||||
* Origin: any Ethiopian yard (route-based direction = EXPORT when dest = Djibouti)
|
||||
* Destination: any Djiboutian yard
|
||||
* Uses the INDODE_OPEN warehouse created by IndodeFacilitySeeder.
|
||||
*/
|
||||
@Injectable()
|
||||
export class Batch5TestDataSeeder {
|
||||
private readonly logger = new Logger(Batch5TestDataSeeder.name);
|
||||
|
||||
constructor(private readonly dataSource: DataSource) {}
|
||||
|
||||
async run(): Promise<void> {
|
||||
const bookingRepo = this.dataSource.getRepository(Booking);
|
||||
|
||||
const existing = await bookingRepo.findOne({ where: { reference: SEED_REFS[0] } });
|
||||
if (existing) {
|
||||
this.logger.log('Batch 5 test data already seeded, skipping');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const yardRepo = this.dataSource.getRepository(Yard);
|
||||
const serviceTypeRepo = this.dataSource.getRepository(ServiceType);
|
||||
const warehouseRepo = this.dataSource.getRepository(Warehouse);
|
||||
const warehouseYardRepo = this.dataSource.getRepository(WarehouseYard);
|
||||
const warehouseZoneRepo = this.dataSource.getRepository(WarehouseZone);
|
||||
const inventoryRepo = this.dataSource.getRepository(WarehouseInventory);
|
||||
|
||||
// Find Ethiopian origin yard and Djiboutian destination yard.
|
||||
const originYard =
|
||||
(await yardRepo.findOne({ where: { code: 'ADDIS_ABABA' } })) ??
|
||||
(await yardRepo.findOne({ where: { country: 'Ethiopia' } }));
|
||||
const destYard =
|
||||
(await yardRepo.findOne({ where: { code: 'DJIBOUTI' } })) ??
|
||||
(await yardRepo.findOne({ where: { country: 'Djibouti' } }));
|
||||
|
||||
if (!originYard || !destYard) {
|
||||
this.logger.warn(
|
||||
`Required yards not found (origin=${originYard?.code ?? 'none'}, dest=${destYard?.code ?? 'none'}); skipping Batch 5 seed`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Find any active service type (bookings require one).
|
||||
const serviceType =
|
||||
(await serviceTypeRepo.findOne({ where: { code: 'RAIL_CONTAINER' } })) ??
|
||||
(await serviceTypeRepo.findOne({ where: { isActive: true } }));
|
||||
if (!serviceType) {
|
||||
this.logger.warn('No service type found; skipping Batch 5 seed');
|
||||
return;
|
||||
}
|
||||
|
||||
// Find INDODE warehouse.
|
||||
const warehouse = await warehouseRepo.findOne({ where: { code: 'INDODE_OPEN' } });
|
||||
if (!warehouse) {
|
||||
this.logger.warn('INDODE_OPEN warehouse not found; skipping Batch 5 seed');
|
||||
return;
|
||||
}
|
||||
|
||||
const warehouseYard = await warehouseYardRepo.findOne({ where: { warehouseId: warehouse.id } });
|
||||
if (!warehouseYard) {
|
||||
this.logger.warn('No warehouse yard found for INDODE_OPEN; skipping Batch 5 seed');
|
||||
return;
|
||||
}
|
||||
|
||||
const warehouseZone = await warehouseZoneRepo.findOne({ where: { yardId: warehouseYard.id } });
|
||||
if (!warehouseZone) {
|
||||
this.logger.warn('No warehouse zone found; skipping Batch 5 seed');
|
||||
return;
|
||||
}
|
||||
|
||||
const now = new Date();
|
||||
|
||||
for (const seed of SEEDS) {
|
||||
const booking = await bookingRepo.save(
|
||||
bookingRepo.create({
|
||||
reference: seed.ref,
|
||||
originYardId: originYard.id,
|
||||
destinationYardId: destYard.id,
|
||||
serviceTypeId: serviceType.id,
|
||||
status: 'PAID',
|
||||
paymentStatus: 'PAID',
|
||||
tradeDirection: 'EXPORT',
|
||||
freightType: 'BULK',
|
||||
cargoTotalWeightVgm: seed.weight,
|
||||
cargoFreeText: seed.notes,
|
||||
}),
|
||||
);
|
||||
|
||||
await inventoryRepo.save(
|
||||
inventoryRepo.create({
|
||||
bookingId: booking.id,
|
||||
warehouseId: warehouse.id,
|
||||
yardId: warehouseYard.id,
|
||||
zoneId: warehouseZone.id,
|
||||
status: 'READY_FOR_LOADING',
|
||||
inspectionStatus: 'PASSED',
|
||||
inspectedAt: new Date(now.getTime() - 3600 * 1000),
|
||||
quantity: 1,
|
||||
weight: seed.weight,
|
||||
arrivedAt: new Date(now.getTime() - 7200 * 1000),
|
||||
readyForLoadingAt: new Date(now.getTime() - 1800 * 1000),
|
||||
notes: `[SEED-B5] ${seed.notes}`,
|
||||
}),
|
||||
);
|
||||
|
||||
this.logger.log(`Seeded ${seed.ref} → READY_FOR_LOADING + PASSED`);
|
||||
}
|
||||
|
||||
this.logger.log('✅ Batch 5 Ready-To-Load test data seeded successfully');
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
`Batch5TestDataSeeder failed: ${error instanceof Error ? error.message : String(error)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
103
apps/edr-freight-api/src/seed/batch7-test-data.seeder.ts
Normal file
103
apps/edr-freight-api/src/seed/batch7-test-data.seeder.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { DataSource } from 'typeorm';
|
||||
|
||||
import { Booking } from '../modules/bookings/entities/booking.entity';
|
||||
import { Locomotive } from '../modules/locomotives/entities/locomotive.entity';
|
||||
import { TrainSchedule } from '../modules/train-schedules/entities/train-schedule.entity';
|
||||
import { TrainScheduleBooking } from '../modules/train-schedules/entities/train-schedule-booking.entity';
|
||||
import { TrainSet } from '../modules/train-sets/entities/train-set.entity';
|
||||
|
||||
/**
|
||||
* Seeds two ARRIVED train schedules so the Import Arrive Queue (Batch 7) is demonstrable:
|
||||
* - SEED-IMP-TRAIN-01: DJIB_PORT → MOJO (IMPORT) linked to booking SEED-IMP-001 → SHOWS
|
||||
* - SEED-EXP-TRAIN-01: MOJO → DJIB_PORT (EXPORT) linked to booking SEED-EXP-001 → must NOT show
|
||||
*
|
||||
* Read-only train-schedule SERVICE logic is untouched; this only inserts fixture rows.
|
||||
* Idempotent: guards on the import train number.
|
||||
*/
|
||||
@Injectable()
|
||||
export class Batch7TestDataSeeder {
|
||||
private readonly logger = new Logger(Batch7TestDataSeeder.name);
|
||||
|
||||
constructor(private readonly dataSource: DataSource) {}
|
||||
|
||||
async run(): Promise<void> {
|
||||
const scheduleRepo = this.dataSource.getRepository(TrainSchedule);
|
||||
|
||||
const existing = await scheduleRepo.findOne({ where: { trainNumber: 'SEED-IMP-TRAIN-01' } });
|
||||
if (existing) {
|
||||
this.logger.log('Batch 7 test data already seeded, skipping');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const bookingRepo = this.dataSource.getRepository(Booking);
|
||||
const locoRepo = this.dataSource.getRepository(Locomotive);
|
||||
const trainSetRepo = this.dataSource.getRepository(TrainSet);
|
||||
const scheduleBookingRepo = this.dataSource.getRepository(TrainScheduleBooking);
|
||||
|
||||
const importBooking = await bookingRepo.findOne({ where: { reference: 'SEED-IMP-001' } });
|
||||
const exportBooking = await bookingRepo.findOne({ where: { reference: 'SEED-EXP-001' } });
|
||||
if (!importBooking) {
|
||||
this.logger.warn('SEED-IMP-001 booking not found; skipping Batch 7 seed');
|
||||
return;
|
||||
}
|
||||
|
||||
// One shared locomotive is fine — train_set.locomotive_id is not unique.
|
||||
const loco =
|
||||
(await locoRepo.findOne({ where: { code: 'SEED-LOCO-01' } })) ??
|
||||
(await locoRepo.save(
|
||||
locoRepo.create({ code: 'SEED-LOCO-01', name: 'Seed Locomotive', maxPullWeightTons: 4000 }),
|
||||
));
|
||||
|
||||
const now = new Date();
|
||||
const arrival = new Date(now.getTime() - 3600 * 1000);
|
||||
const departure = new Date(now.getTime() - 6 * 3600 * 1000);
|
||||
|
||||
const makeArrivedTrain = async (
|
||||
trainNumber: string,
|
||||
booking: Booking,
|
||||
): Promise<void> => {
|
||||
const trainSet = await trainSetRepo.save(
|
||||
trainSetRepo.create({
|
||||
locomotiveId: loco.id,
|
||||
totalWeightTons: 500,
|
||||
totalLengthMeters: 300,
|
||||
wagonCount: 10,
|
||||
status: 'COMPLETED',
|
||||
}),
|
||||
);
|
||||
|
||||
const schedule = await scheduleRepo.save(
|
||||
scheduleRepo.create({
|
||||
trainSetId: trainSet.id,
|
||||
originStationId: booking.originYardId,
|
||||
destinationStationId: booking.destinationYardId,
|
||||
scheduledDepartureDate: departure,
|
||||
scheduledArrivalDate: arrival,
|
||||
actualArrivalAt: arrival,
|
||||
status: 'ARRIVED' as TrainSchedule['status'],
|
||||
trainNumber,
|
||||
}),
|
||||
);
|
||||
|
||||
await scheduleBookingRepo.save(
|
||||
scheduleBookingRepo.create({ trainScheduleId: schedule.id, bookingId: booking.id }),
|
||||
);
|
||||
|
||||
this.logger.log(`Seeded arrived train ${trainNumber} → booking ${booking.reference}`);
|
||||
};
|
||||
|
||||
await makeArrivedTrain('SEED-IMP-TRAIN-01', importBooking);
|
||||
if (exportBooking) {
|
||||
await makeArrivedTrain('SEED-EXP-TRAIN-01', exportBooking);
|
||||
}
|
||||
|
||||
this.logger.log('✅ Batch 7 arrive-queue test data seeded successfully');
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
`Batch7TestDataSeeder failed: ${error instanceof Error ? error.message : String(error)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
51
apps/edr-freight-api/src/seed/batch8-test-data.seeder.ts
Normal file
51
apps/edr-freight-api/src/seed/batch8-test-data.seeder.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { DataSource } from 'typeorm';
|
||||
|
||||
import { Booking } from '../modules/bookings/entities/booking.entity';
|
||||
import { TrainSchedule } from '../modules/train-schedules/entities/train-schedule.entity';
|
||||
import { TrainScheduleBooking } from '../modules/train-schedules/entities/train-schedule-booking.entity';
|
||||
|
||||
/**
|
||||
* Makes the Batch 7 seed import train demonstrable for Batch 8: a booking riding an ARRIVED
|
||||
* train is IN_TRANSIT until unloaded, so flip the seed import train's assigned bookings to
|
||||
* IN_TRANSIT (an unload-eligible status). Idempotent — re-applying IN_TRANSIT is a no-op.
|
||||
*/
|
||||
@Injectable()
|
||||
export class Batch8TestDataSeeder {
|
||||
private readonly logger = new Logger(Batch8TestDataSeeder.name);
|
||||
|
||||
constructor(private readonly dataSource: DataSource) {}
|
||||
|
||||
async run(): Promise<void> {
|
||||
try {
|
||||
const scheduleRepo = this.dataSource.getRepository(TrainSchedule);
|
||||
const scheduleBookingRepo = this.dataSource.getRepository(TrainScheduleBooking);
|
||||
const bookingRepo = this.dataSource.getRepository(Booking);
|
||||
|
||||
const train = await scheduleRepo.findOne({ where: { trainNumber: 'SEED-IMP-TRAIN-01' } });
|
||||
if (!train) {
|
||||
this.logger.log('SEED-IMP-TRAIN-01 not found; skipping Batch 8 seed');
|
||||
return;
|
||||
}
|
||||
|
||||
const links = await scheduleBookingRepo.find({ where: { trainScheduleId: train.id } });
|
||||
let updated = 0;
|
||||
for (const link of links) {
|
||||
const booking = await bookingRepo.findOne({ where: { id: link.bookingId } });
|
||||
if (!booking || booking.status === 'IN_TRANSIT') continue;
|
||||
await bookingRepo.update(booking.id, { status: 'IN_TRANSIT' });
|
||||
updated += 1;
|
||||
}
|
||||
|
||||
if (updated > 0) {
|
||||
this.logger.log(`✅ Batch 8: set ${updated} import train booking(s) to IN_TRANSIT (unload-eligible)`);
|
||||
} else {
|
||||
this.logger.log('Batch 8: import train bookings already IN_TRANSIT, skipping');
|
||||
}
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
`Batch8TestDataSeeder failed: ${error instanceof Error ? error.message : String(error)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
258
apps/edr-freight-api/src/seed/warehouse-demo.seeder.ts
Normal file
258
apps/edr-freight-api/src/seed/warehouse-demo.seeder.ts
Normal file
@@ -0,0 +1,258 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { DataSource } from 'typeorm';
|
||||
|
||||
import { Booking } from '../modules/bookings/entities/booking.entity';
|
||||
import { CargoType } from '../modules/rule-engine/entities/cargo-type.entity';
|
||||
import { Locomotive } from '../modules/locomotives/entities/locomotive.entity';
|
||||
import { ServiceType } from '../modules/rule-engine/entities/service-type.entity';
|
||||
import { Yard } from '../modules/rule-engine/entities/yard.entity';
|
||||
import { TrainSchedule } from '../modules/train-schedules/entities/train-schedule.entity';
|
||||
import { TrainScheduleBooking } from '../modules/train-schedules/entities/train-schedule-booking.entity';
|
||||
import { TrainSet } from '../modules/train-sets/entities/train-set.entity';
|
||||
import { Warehouse } from '../modules/warehouses/entities/warehouse.entity';
|
||||
import { WarehouseInventory } from '../modules/warehouses/entities/warehouse-inventory.entity';
|
||||
import { WarehouseYard } from '../modules/warehouses/entities/warehouse-yard.entity';
|
||||
import { WarehouseZone } from '../modules/warehouses/entities/warehouse-zone.entity';
|
||||
|
||||
/**
|
||||
* One coherent warehouse dataset so EVERY queue/tab shows representative data:
|
||||
* Export → Receive Queue : PAID export bookings, not yet received
|
||||
* Export → Ready To Load : EXPORT inventory READY_FOR_LOADING + inspection PASSED
|
||||
* Export → Loaded/Dispatch : EXPORT inventory LOADED
|
||||
* Import → Arrive Queue : an ARRIVED import train with IN_TRANSIT bookings (no inventory)
|
||||
* Import → Unloaded Queue : UNLOADED import inventory
|
||||
* Import → Dispatch Queue : READY_FOR_PICKUP import inventory (PASSED)
|
||||
*
|
||||
* Idempotent: guarded on a sentinel booking reference. Uses dedicated WH-DEMO-* references so it
|
||||
* never collides with other seeders. To repopulate after items are walked through their lifecycle,
|
||||
* delete the WH-DEMO-* bookings (cascades) and reboot.
|
||||
*/
|
||||
@Injectable()
|
||||
export class WarehouseDemoSeeder {
|
||||
private readonly logger = new Logger(WarehouseDemoSeeder.name);
|
||||
private readonly SENTINEL = 'WH-DEMO-RCV-1';
|
||||
|
||||
constructor(private readonly dataSource: DataSource) {}
|
||||
|
||||
async run(): Promise<void> {
|
||||
const bookingRepo = this.dataSource.getRepository(Booking);
|
||||
if (await bookingRepo.findOne({ where: { reference: this.SENTINEL } })) {
|
||||
this.logger.log('Warehouse demo data already seeded, skipping');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const yardRepo = this.dataSource.getRepository(Yard);
|
||||
const serviceTypeRepo = this.dataSource.getRepository(ServiceType);
|
||||
const cargoTypeRepo = this.dataSource.getRepository(CargoType);
|
||||
const warehouseRepo = this.dataSource.getRepository(Warehouse);
|
||||
const whYardRepo = this.dataSource.getRepository(WarehouseYard);
|
||||
const whZoneRepo = this.dataSource.getRepository(WarehouseZone);
|
||||
const invRepo = this.dataSource.getRepository(WarehouseInventory);
|
||||
|
||||
const djibYard =
|
||||
(await yardRepo.findOne({ where: { code: 'DJIB_PORT' } })) ??
|
||||
(await yardRepo.findOne({ where: { country: 'Djibouti' } }));
|
||||
const ethYard =
|
||||
(await yardRepo.findOne({ where: { code: 'MOJO' } })) ??
|
||||
(await yardRepo.findOne({ where: { country: 'Ethiopia' } }));
|
||||
const serviceType =
|
||||
(await serviceTypeRepo.findOne({ where: { code: 'RAIL_CONTAINER' } })) ??
|
||||
(await serviceTypeRepo.findOne({ where: { isActive: true } }));
|
||||
const cargoType = await cargoTypeRepo.findOne({ where: { isActive: true } });
|
||||
|
||||
if (!djibYard || !ethYard || !serviceType) {
|
||||
this.logger.warn(
|
||||
`Missing yards/service type (djib=${djibYard?.code}, eth=${ethYard?.code}, svc=${serviceType?.code}); skipping`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const warehouse = await warehouseRepo.findOne({ where: { code: 'INDODE_OPEN' } });
|
||||
const whYard = warehouse ? await whYardRepo.findOne({ where: { warehouseId: warehouse.id } }) : null;
|
||||
const whZone = whYard ? await whZoneRepo.findOne({ where: { yardId: whYard.id } }) : null;
|
||||
if (!warehouse || !whYard || !whZone) {
|
||||
this.logger.warn('INDODE_OPEN warehouse/yard/zone missing; skipping warehouse demo seed');
|
||||
return;
|
||||
}
|
||||
|
||||
const now = Date.now();
|
||||
const ago = (mins: number) => new Date(now - mins * 60_000);
|
||||
|
||||
// EXPORT booking = Ethiopia → Djibouti; IMPORT booking = Djibouti → Ethiopia.
|
||||
const makeBooking = async (
|
||||
reference: string,
|
||||
direction: 'EXPORT' | 'IMPORT',
|
||||
status: string,
|
||||
weight: number,
|
||||
idx: number,
|
||||
): Promise<Booking> =>
|
||||
bookingRepo.save(
|
||||
bookingRepo.create({
|
||||
reference,
|
||||
originYardId: direction === 'EXPORT' ? ethYard.id : djibYard.id,
|
||||
destinationYardId: direction === 'EXPORT' ? djibYard.id : ethYard.id,
|
||||
serviceTypeId: serviceType.id,
|
||||
status,
|
||||
paymentStatus: 'PAID',
|
||||
tradeDirection: direction,
|
||||
freightType: idx % 2 === 0 ? 'CONTAINER' : 'BULK',
|
||||
cargoTypeId: cargoType?.id ?? null,
|
||||
cargoFreeText: cargoType ? null : `${direction} demo cargo ${idx}`,
|
||||
cargoTotalWeightVgm: weight,
|
||||
}),
|
||||
);
|
||||
|
||||
const makeInventory = async (
|
||||
booking: Booking,
|
||||
status: string,
|
||||
weight: number,
|
||||
extra: Partial<WarehouseInventory>,
|
||||
): Promise<void> => {
|
||||
await invRepo.save(
|
||||
invRepo.create({
|
||||
warehouseId: warehouse.id,
|
||||
yardId: whYard.id,
|
||||
zoneId: whZone.id,
|
||||
bookingId: booking.id,
|
||||
quantity: 1,
|
||||
weight,
|
||||
status: status as WarehouseInventory['status'],
|
||||
notes: '[WH-DEMO]',
|
||||
...extra,
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
let created = 0;
|
||||
|
||||
// 1) Export Receive Queue — 3 PAID export bookings, NO inventory.
|
||||
for (let i = 1; i <= 3; i++) {
|
||||
await makeBooking(`WH-DEMO-RCV-${i}`, 'EXPORT', 'PAID', 4000 + i * 500, i);
|
||||
created++;
|
||||
}
|
||||
|
||||
// 2) Export Ready To Load — EXPORT inventory READY_FOR_LOADING + PASSED.
|
||||
for (let i = 1; i <= 3; i++) {
|
||||
const b = await makeBooking(`WH-DEMO-RTL-${i}`, 'EXPORT', 'PAID', 6000 + i * 500, i);
|
||||
await makeInventory(b, 'READY_FOR_LOADING', 6000 + i * 500, {
|
||||
inspectionStatus: 'PASSED',
|
||||
arrivedAt: ago(180),
|
||||
inspectedAt: ago(120),
|
||||
readyForLoadingAt: ago(60),
|
||||
});
|
||||
created++;
|
||||
}
|
||||
|
||||
// 3) Export Loaded / Dispatch Queue — EXPORT inventory LOADED.
|
||||
for (let i = 1; i <= 2; i++) {
|
||||
const b = await makeBooking(`WH-DEMO-LOAD-${i}`, 'EXPORT', 'PAID', 7000 + i * 500, i);
|
||||
await makeInventory(b, 'LOADED', 7000 + i * 500, {
|
||||
inspectionStatus: 'PASSED',
|
||||
arrivedAt: ago(240),
|
||||
inspectedAt: ago(180),
|
||||
readyForLoadingAt: ago(120),
|
||||
loadedAt: ago(30),
|
||||
});
|
||||
created++;
|
||||
}
|
||||
|
||||
// 4) Import Unloaded Queue — UNLOADED import inventory (not inspected, not stored).
|
||||
for (let i = 1; i <= 3; i++) {
|
||||
const b = await makeBooking(`WH-DEMO-UNL-${i}`, 'IMPORT', 'IN_TRANSIT', 5000 + i * 500, i);
|
||||
await makeInventory(b, 'UNLOADED', 5000 + i * 500, {
|
||||
arrivedAt: ago(90),
|
||||
unloadedAt: ago(45),
|
||||
});
|
||||
created++;
|
||||
}
|
||||
|
||||
// 5) Import Dispatch Queue — READY_FOR_PICKUP import inventory (inspection PASSED).
|
||||
for (let i = 1; i <= 3; i++) {
|
||||
const b = await makeBooking(`WH-DEMO-PKR-${i}`, 'IMPORT', 'IN_TRANSIT', 5500 + i * 500, i);
|
||||
await makeInventory(b, 'READY_FOR_PICKUP', 5500 + i * 500, {
|
||||
inspectionStatus: 'PASSED',
|
||||
arrivedAt: ago(200),
|
||||
unloadedAt: ago(160),
|
||||
inspectedAt: ago(120),
|
||||
readyForPickupAt: ago(60),
|
||||
});
|
||||
created++;
|
||||
}
|
||||
|
||||
// 6) Import Arrive Queue — an ARRIVED import train with IN_TRANSIT bookings, no inventory yet.
|
||||
await this.seedArrivedImportTrain(djibYard, ethYard, serviceType, cargoType, ago(60), ago(360));
|
||||
created += 1;
|
||||
|
||||
this.logger.log(`✅ Warehouse demo seeded: ${created} buckets populated across every queue`);
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
`WarehouseDemoSeeder failed: ${error instanceof Error ? error.message : String(error)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/** An ARRIVED Djibouti→Ethiopia train with 3 IN_TRANSIT bookings (no inventory) for the Arrive Queue. */
|
||||
private async seedArrivedImportTrain(
|
||||
djibYard: Yard,
|
||||
ethYard: Yard,
|
||||
serviceType: ServiceType,
|
||||
cargoType: CargoType | null,
|
||||
arrival: Date,
|
||||
departure: Date,
|
||||
): Promise<void> {
|
||||
const bookingRepo = this.dataSource.getRepository(Booking);
|
||||
const locoRepo = this.dataSource.getRepository(Locomotive);
|
||||
const trainSetRepo = this.dataSource.getRepository(TrainSet);
|
||||
const scheduleRepo = this.dataSource.getRepository(TrainSchedule);
|
||||
const scheduleBookingRepo = this.dataSource.getRepository(TrainScheduleBooking);
|
||||
|
||||
const loco =
|
||||
(await locoRepo.findOne({ where: { code: 'WH-DEMO-LOCO' } })) ??
|
||||
(await locoRepo.save(locoRepo.create({ code: 'WH-DEMO-LOCO', name: 'Demo Locomotive', maxPullWeightTons: 4000 })));
|
||||
|
||||
const trainSet = await trainSetRepo.save(
|
||||
trainSetRepo.create({
|
||||
locomotiveId: loco.id,
|
||||
totalWeightTons: 500,
|
||||
totalLengthMeters: 300,
|
||||
wagonCount: 10,
|
||||
status: 'COMPLETED',
|
||||
}),
|
||||
);
|
||||
|
||||
const schedule = await scheduleRepo.save(
|
||||
scheduleRepo.create({
|
||||
trainSetId: trainSet.id,
|
||||
originStationId: djibYard.id,
|
||||
destinationStationId: ethYard.id,
|
||||
scheduledDepartureDate: departure,
|
||||
scheduledArrivalDate: arrival,
|
||||
actualArrivalAt: arrival,
|
||||
status: 'ARRIVED' as TrainSchedule['status'],
|
||||
trainNumber: 'WH-DEMO-IMP-TRAIN',
|
||||
}),
|
||||
);
|
||||
|
||||
for (let i = 1; i <= 3; i++) {
|
||||
const b = await bookingRepo.save(
|
||||
bookingRepo.create({
|
||||
reference: `WH-DEMO-ARR-${i}`,
|
||||
originYardId: djibYard.id,
|
||||
destinationYardId: ethYard.id,
|
||||
serviceTypeId: serviceType.id,
|
||||
status: 'IN_TRANSIT',
|
||||
paymentStatus: 'PAID',
|
||||
tradeDirection: 'IMPORT',
|
||||
freightType: i % 2 === 0 ? 'CONTAINER' : 'BULK',
|
||||
cargoTypeId: cargoType?.id ?? null,
|
||||
cargoFreeText: cargoType ? null : `IMPORT arrive demo cargo ${i}`,
|
||||
cargoTotalWeightVgm: 5000 + i * 400,
|
||||
}),
|
||||
);
|
||||
await scheduleBookingRepo.save(
|
||||
scheduleBookingRepo.create({ trainScheduleId: schedule.id, bookingId: b.id }),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user