feat(warehouse): Batch 5 seed — 3 EXPORT READY_FOR_LOADING+PASSED inventory items

Creates SEED-B5-EXP-001/002/003 bookings (ADDIS_ABABA→DJIBOUTI, PAID, BULK)
with warehouse inventory at READY_FOR_LOADING + inspectionStatus PASSED so the
Ready To Load tab has visible rows. Idempotent on guard reference check.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Hagernesh
2026-06-20 19:45:07 +00:00
parent 806fc4a8ca
commit 43ba3c790b
2 changed files with 143 additions and 0 deletions

View File

@@ -49,6 +49,7 @@ import { PricingDataSeeder } from "./seed/pricing-data.seeder";
import { FileUploadSettingsSeeder } from "./seed/file-upload-settings.seeder";
import { IndodeFacilitySeeder } from "./seed/indode-facility.seeder";
import { Batch14TestDataSeeder } from "./seed/batch1-4-test-data.seeder";
import { Batch5TestDataSeeder } from "./seed/batch5-test-data.seeder";
import { FreightPermissionKeyMigrationSeeder } from "./seed/freight-permission-key-migration.seeder";
import { DemoFreightDataSeeder } from "./seed/demo-freight-data.seeder";
//New Trains, Wagons, Container and Cargo management modules
@@ -131,6 +132,7 @@ import { OverviewModule } from './modules/overview/overview.module';
DemoFreightDataSeeder,
IndodeFacilitySeeder,
Batch14TestDataSeeder,
Batch5TestDataSeeder,
],
})
export class AppModule implements OnApplicationBootstrap {
@@ -144,6 +146,7 @@ export class AppModule implements OnApplicationBootstrap {
private readonly fileUploadSettingsSeeder: FileUploadSettingsSeeder,
private readonly indodeFacilitySeeder: IndodeFacilitySeeder,
private readonly batch14TestDataSeeder: Batch14TestDataSeeder,
private readonly batch5TestDataSeeder: Batch5TestDataSeeder,
private readonly freightPermissionKeyMigrationSeeder: FreightPermissionKeyMigrationSeeder,
private readonly demoFreightDataSeeder: DemoFreightDataSeeder,
) { }
@@ -159,6 +162,7 @@ export class AppModule implements OnApplicationBootstrap {
await this.fileUploadSettingsSeeder.run();
await this.indodeFacilitySeeder.run();
await this.batch14TestDataSeeder.run();
await this.batch5TestDataSeeder.run();
// Idempotent demo data: ≥100 wagons/type, approval chains, 4 staff users.
// Each block self-guards on an empty-table check, so this is safe every boot.
await this.demoFreightDataSeeder.run();

View 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)}`,
);
}
}
}