chore: fix script and add seed company

This commit is contained in:
ghost2023
2026-07-01 15:46:25 +03:00
parent aef868bc40
commit 174df0b7aa
5 changed files with 239 additions and 236 deletions

View File

@@ -3,8 +3,10 @@ import type { CommandContext } from "./types";
import { registerSeedTestContracts } from "./seed-test-contracts.cmd"; import { registerSeedTestContracts } from "./seed-test-contracts.cmd";
import { registerSeedTestSchedules } from "./seed-test-schedules.cmd"; import { registerSeedTestSchedules } from "./seed-test-schedules.cmd";
import { registerSeedTestCompany } from "./seed-test-company.cmd";
export function registerCommands(vorpal: Vorpal, ctx: CommandContext): void { export function registerCommands(vorpal: Vorpal, ctx: CommandContext): void {
registerSeedTestContracts(vorpal, ctx); registerSeedTestContracts(vorpal, ctx);
registerSeedTestSchedules(vorpal, ctx); registerSeedTestSchedules(vorpal, ctx);
registerSeedTestCompany(vorpal, ctx);
} }

View File

@@ -0,0 +1,85 @@
import type Vorpal from "vorpal";
import { DataSource } from "typeorm";
import type { CommandContext } from "./types";
import { Company, CompanyType, CompanyKind, CompanyStatus, CompanyNationality } from "../../modules/companies/entities/company.entity";
import { CompanyProfile, ProfileStatus, ProfileType } from "../../modules/companies/entities/company-profile.entity";
import { ExternalProfile } from "../../modules/companies/entities/external-profile.entity";
export function registerSeedTestCompany(
vorpal: Vorpal,
ctx: CommandContext,
): void {
vorpal
.command("seed:test-company", "Generate a test company with approved importer/exporter profiles and an external user")
.option("--name <name>", "Company name (default: Test Company)")
.option("--email <email>", "Company email (default: company@test.com)")
.option("--tin <tin>", "Tax ID (default: auto-generated TSTxxxxx)")
.action(async function (this: any, args: any) {
const { app } = ctx;
const ds = app.get(DataSource);
const raw = await ds.query(
`SELECT "tin" FROM "freight"."companies" WHERE "tin" LIKE 'TST%' AND "deleted_at" IS NULL ORDER BY "tin" DESC LIMIT 1`,
);
let nextTinNum = 1;
if (raw.length > 0) {
const num = parseInt((raw[0] as any).tin.replace("TST", ""), 10);
if (!isNaN(num)) nextTinNum = num + 1;
}
const name = args.options?.name ?? "Test Company";
const email = args.options?.email ?? "company@test.com";
const tin = args.options?.tin ?? `TST${String(nextTinNum).padStart(6, "0")}`;
const userId = `ffffffff-0000-4000-8000-${String(nextTinNum).padStart(12, "0")}`;
const existing = await ds.getRepository(Company).findOne({ where: { tin } });
if (existing) {
this.log(`Company with TIN ${tin} already exists (${existing.name})`);
return;
}
const company = await ds.getRepository(Company).save(
ds.getRepository(Company).create({
name,
type: CompanyType.Customer,
kind: CompanyKind.Commercial,
status: CompanyStatus.Active,
tin,
country: "Ethiopia",
nationality: CompanyNationality.Ethiopian,
email,
phone: "+251911000000",
address: "Test Address",
}),
);
this.log(` Created company: ${company.name} (TIN: ${tin})`);
for (const type of [ProfileType.importer, ProfileType.exporter]) {
await ds.getRepository(CompanyProfile).save(
ds.getRepository(CompanyProfile).create({
companyId: company.id,
type,
reference: `TST-${type.toUpperCase()}-${String(nextTinNum).padStart(3, "0")}`,
status: ProfileStatus.Active,
}),
);
this.log(` Created ${type} profile (approved)`);
}
await ds.getRepository(ExternalProfile).save(
ds.getRepository(ExternalProfile).create({
userId,
companyId: company.id,
firstName: "Test",
lastName: "User",
isPrimaryContact: true,
activeProfileType: ProfileType.importer,
onboardingCompleted: true,
onboardingStep: "done",
}),
);
this.log(` Created external profile: Test User (userId: ${userId})`);
this.log(`\nDone — login with email "${email}" and password "password"`);
});
}

View File

@@ -13,35 +13,6 @@ import { ContractRoute } from "../../modules/contracts/entities/contract-route.e
import { ContractCargoScope } from "../../modules/contracts/entities/contract-cargo-scope.entity"; import { ContractCargoScope } from "../../modules/contracts/entities/contract-cargo-scope.entity";
import { ContractRateSnapshot } from "../../modules/contracts/entities/contract-rate-snapshot.entity"; import { ContractRateSnapshot } from "../../modules/contracts/entities/contract-rate-snapshot.entity";
const COMPANY_IDS = [
"a0000001-0000-4000-8000-000000000001",
"a0000001-0000-4000-8000-000000000002",
"a0000001-0000-4000-8000-000000000003",
"a0000001-0000-4000-8000-000000000004",
];
const PROFILE_IDS = [
"b0000001-0000-4000-8000-000000000001",
"b0000001-0000-4000-8000-000000000002",
"b0000001-0000-4000-8000-000000000003",
"b0000001-0000-4000-8000-000000000004",
"b0000001-0000-4000-8000-000000000005",
"b0000001-0000-4000-8000-000000000006",
"b0000001-0000-4000-8000-000000000007",
"b0000001-0000-4000-8000-000000000008",
];
const EXTERNAL_PROFILE_IDS = [
"c0000001-0000-4000-8000-000000000001",
"c0000001-0000-4000-8000-000000000002",
"c0000001-0000-4000-8000-000000000003",
"c0000001-0000-4000-8000-000000000004",
];
function generateRef(index: number): string {
return `TST-CTR-${String(index).padStart(5, "0")}`;
}
export function registerSeedTestContracts( export function registerSeedTestContracts(
vorpal: Vorpal, vorpal: Vorpal,
ctx: CommandContext, ctx: CommandContext,
@@ -124,7 +95,15 @@ export function registerSeedTestContracts(
} }
const contractRepo = ds.getRepository(Contract); const contractRepo = ds.getRepository(Contract);
let contractCount = 0;
const maxRaw = await ds.query(
`SELECT "reference" FROM "freight"."contracts" WHERE "reference" LIKE 'TST-CTR-%' AND "deleted_at" IS NULL ORDER BY "reference" DESC LIMIT 1`,
);
let nextRef = 1;
if (maxRaw.length > 0) {
const num = parseInt(maxRaw[0].reference.replace("TST-CTR-", ""), 10);
if (!isNaN(num)) nextRef = num + 1;
}
for (let i = 0; i < count; i++) { for (let i = 0; i < count; i++) {
const statusIdx = i % statusList.length; const statusIdx = i % statusList.length;
@@ -145,13 +124,7 @@ export function registerSeedTestContracts(
}); });
if (!profile) continue; if (!profile) continue;
const ref = generateRef(i + 1); const ref = `TST-CTR-${String(nextRef + i).padStart(5, "0")}`;
const exists = await contractRepo.findOne({ where: { reference: ref } });
if (exists) {
this.log(` Contract ${ref} already exists — skipping`);
contractCount++;
continue;
}
const serviceTypeId = const serviceTypeId =
freightType === "BULK" && railBulk freightType === "BULK" && railBulk
@@ -239,54 +212,52 @@ export function registerSeedTestContracts(
); );
} }
contractCount++;
this.log(` Created ${status} ${freightType} ${direction} contract: ${ref} (${company.name})`); this.log(` Created ${status} ${freightType} ${direction} contract: ${ref} (${company.name})`);
} }
this.log(`Done — ${contractCount} contracts ensured`); this.log(`Done — ${count} new contracts created`);
}); });
} }
interface CompanySeed { interface CompanySeed {
id: string;
name: string; name: string;
tin: string; tin: string;
profiles: Array<{ id: string; type: ProfileType; reference: string }>; profiles: Array<{ type: ProfileType; reference: string }>;
externalProfile: { id: string; userId: string; firstName: string; lastName: string }; externalProfile: { userId: string; firstName: string; lastName: string };
} }
const TEST_COMPANIES: CompanySeed[] = [ const TEST_COMPANIES: CompanySeed[] = [
{ {
id: COMPANY_IDS[0], name: "Test Importer Co.", tin: "TST000001", name: "Test Importer Co.", tin: "TST000001",
profiles: [ profiles: [
{ id: PROFILE_IDS[0], type: ProfileType.importer, reference: "TST-IM-001" }, { type: ProfileType.importer, reference: "TST-IM-001" },
{ id: PROFILE_IDS[1], type: ProfileType.exporter, reference: "TST-EX-001" }, { type: ProfileType.exporter, reference: "TST-EX-001" },
], ],
externalProfile: { id: EXTERNAL_PROFILE_IDS[0], userId: "00000000-0000-0000-0000-000000000001", firstName: "Abebe", lastName: "Kebede" }, externalProfile: { userId: "00000000-0000-0000-0000-000000000001", firstName: "Abebe", lastName: "Kebede" },
}, },
{ {
id: COMPANY_IDS[1], name: "Test Exporter Ltd.", tin: "TST000002", name: "Test Exporter Ltd.", tin: "TST000002",
profiles: [ profiles: [
{ id: PROFILE_IDS[2], type: ProfileType.importer, reference: "TST-IM-002" }, { type: ProfileType.importer, reference: "TST-IM-002" },
{ id: PROFILE_IDS[3], type: ProfileType.exporter, reference: "TST-EX-002" }, { type: ProfileType.exporter, reference: "TST-EX-002" },
], ],
externalProfile: { id: EXTERNAL_PROFILE_IDS[1], userId: "00000000-0000-0000-0000-000000000002", firstName: "Bekele", lastName: "Alemu" }, externalProfile: { userId: "00000000-0000-0000-0000-000000000002", firstName: "Bekele", lastName: "Alemu" },
}, },
{ {
id: COMPANY_IDS[2], name: "Bulk Commodities PLC", tin: "TST000003", name: "Bulk Commodities PLC", tin: "TST000003",
profiles: [ profiles: [
{ id: PROFILE_IDS[4], type: ProfileType.importer, reference: "TST-IM-003" }, { type: ProfileType.importer, reference: "TST-IM-003" },
{ id: PROFILE_IDS[5], type: ProfileType.exporter, reference: "TST-EX-003" }, { type: ProfileType.exporter, reference: "TST-EX-003" },
], ],
externalProfile: { id: EXTERNAL_PROFILE_IDS[2], userId: "00000000-0000-0000-0000-000000000003", firstName: "Chala", lastName: "Tesfaye" }, externalProfile: { userId: "00000000-0000-0000-0000-000000000003", firstName: "Chala", lastName: "Tesfaye" },
}, },
{ {
id: COMPANY_IDS[3], name: "Hazardous Logistics Inc.", tin: "TST000004", name: "Hazardous Logistics Inc.", tin: "TST000004",
profiles: [ profiles: [
{ id: PROFILE_IDS[6], type: ProfileType.importer, reference: "TST-IM-004" }, { type: ProfileType.importer, reference: "TST-IM-004" },
{ id: PROFILE_IDS[7], type: ProfileType.exporter, reference: "TST-EX-004" }, { type: ProfileType.exporter, reference: "TST-EX-004" },
], ],
externalProfile: { id: EXTERNAL_PROFILE_IDS[3], userId: "00000000-0000-0000-0000-000000000004", firstName: "Desta", lastName: "Hailu" }, externalProfile: { userId: "00000000-0000-0000-0000-000000000004", firstName: "Desta", lastName: "Hailu" },
}, },
]; ];
@@ -297,11 +268,10 @@ async function seedTestCompanies(ds: DataSource, log: (msg: string) => void): Pr
const result: Company[] = []; const result: Company[] = [];
for (const seed of TEST_COMPANIES) { for (const seed of TEST_COMPANIES) {
let company = await companyRepo.findOne({ where: { id: seed.id } }); let company = await companyRepo.findOne({ where: { tin: seed.tin } });
if (!company) { if (!company) {
company = await companyRepo.save( company = await companyRepo.save(
companyRepo.create({ companyRepo.create({
id: seed.id,
name: seed.name, name: seed.name,
type: CompanyType.Customer, type: CompanyType.Customer,
kind: CompanyKind.Commercial, kind: CompanyKind.Commercial,
@@ -319,12 +289,13 @@ async function seedTestCompanies(ds: DataSource, log: (msg: string) => void): Pr
} }
for (const p of seed.profiles) { for (const p of seed.profiles) {
const existing = await profileRepo.findOne({ where: { id: p.id } }); const existing = await profileRepo.findOne({
where: { companyId: company.id, type: p.type },
});
if (!existing) { if (!existing) {
await profileRepo.save( await profileRepo.save(
profileRepo.create({ profileRepo.create({
id: p.id, companyId: company.id,
companyId: seed.id,
type: p.type, type: p.type,
reference: p.reference, reference: p.reference,
status: ProfileStatus.Active, status: ProfileStatus.Active,
@@ -335,13 +306,14 @@ async function seedTestCompanies(ds: DataSource, log: (msg: string) => void): Pr
} }
const ext = seed.externalProfile; const ext = seed.externalProfile;
const existingExt = await extProfileRepo.findOne({ where: { id: ext.id } }); const existingExt = await extProfileRepo.findOne({
where: { companyId: company.id, userId: ext.userId },
});
if (!existingExt) { if (!existingExt) {
await extProfileRepo.save( await extProfileRepo.save(
extProfileRepo.create({ extProfileRepo.create({
id: ext.id,
userId: ext.userId, userId: ext.userId,
companyId: seed.id, companyId: company.id,
firstName: ext.firstName, firstName: ext.firstName,
lastName: ext.lastName, lastName: ext.lastName,
isPrimaryContact: true, isPrimaryContact: true,

View File

@@ -12,8 +12,38 @@ import { TrainSet } from "../../modules/train-sets/entities/train-set.entity";
import { TrainSetLocomotive } from "../../modules/train-sets/entities/train-set-locomotive.entity"; import { TrainSetLocomotive } from "../../modules/train-sets/entities/train-set-locomotive.entity";
import { TrainSetWagon } from "../../modules/train-sets/entities/train-set-wagon.entity"; import { TrainSetWagon } from "../../modules/train-sets/entities/train-set-wagon.entity";
import { TrainSchedule } from "../../modules/train-schedules/entities/train-schedule.entity"; import { TrainSchedule } from "../../modules/train-schedules/entities/train-schedule.entity";
import { TrainScheduleBooking } from "../../modules/train-schedules/entities/train-schedule-booking.entity";
import { Booking } from "../../modules/bookings/entities/booking.entity"; async function nextSequence(ds: DataSource, pattern: string): Promise<number> {
const like = pattern.replace(/\*/g, "%");
const raw = await ds.query(
`SELECT "train_number" FROM "freight"."train_schedules" WHERE "train_number" LIKE $1 AND "deleted_at" IS NULL ORDER BY "train_number" DESC LIMIT 1`,
[like.replace(/%/g, "") + "%"],
);
if (raw.length === 0) return 1;
const ref: string = raw[0].train_number;
const num = parseInt(ref.replace(pattern.split("*")[0], ""), 10);
return isNaN(num) ? 1 : num + 1;
}
async function nextRouteSeq(ds: DataSource, prefix: string): Promise<number> {
const raw = await ds.query(
`SELECT "name" FROM "freight"."routes" WHERE "name" LIKE $1 AND "deleted_at" IS NULL ORDER BY "name" DESC LIMIT 1`,
[prefix + "%"],
);
if (raw.length === 0) return 1;
const num = parseInt(raw[0].name.replace(prefix, ""), 10);
return isNaN(num) ? 1 : num + 1;
}
async function nextWagonSeq(ds: DataSource, prefix: string): Promise<number> {
const raw = await ds.query(
`SELECT "wagon_number" FROM "freight"."wagons" WHERE "wagon_number" LIKE $1 AND "deleted_at" IS NULL ORDER BY "wagon_number" DESC LIMIT 1`,
[prefix + "%"],
);
if (raw.length === 0) return 1;
const num = parseInt(raw[0].wagon_number.replace(prefix, ""), 10);
return isNaN(num) ? 1 : num + 1;
}
export function registerSeedTestSchedules( export function registerSeedTestSchedules(
vorpal: Vorpal, vorpal: Vorpal,
@@ -43,8 +73,6 @@ export function registerSeedTestSchedules(
return; return;
} }
this.log(`Seeding ${count} schedules (direction=${directionList.join(",")}, status=${statusList.join(",")}, days_ahead=${daysAhead})...`);
const yards = await ds.getRepository(Yard).find({ where: { isActive: true } }); const yards = await ds.getRepository(Yard).find({ where: { isActive: true } });
const yardByCode = new Map(yards.map((y) => [y.code.toUpperCase(), y])); const yardByCode = new Map(yards.map((y) => [y.code.toUpperCase(), y]));
const djibouti = yardByCode.get("DJIBOUTI") ?? yards.find((y) => y.country === "Djibouti"); const djibouti = yardByCode.get("DJIBOUTI") ?? yards.find((y) => y.country === "Djibouti");
@@ -61,50 +89,90 @@ export function registerSeedTestSchedules(
return; return;
} }
const route = await ensureTestRoute(ds, yards, djibouti, addis, (msg) => this.log(msg)); const wagonType = wagonTypes[0];
const wagonCapacity = Number(wagonType.capacityTons) || 70;
const wagonLength = Number(wagonType.lengthMeters) || 14;
const tareWeight = Number(wagonType.tareWeightTons) || 14;
const locomotiveRepo = ds.getRepository(Locomotive);
const scheduleRepo = ds.getRepository(TrainSchedule); const scheduleRepo = ds.getRepository(TrainSchedule);
const trainSetRepo = ds.getRepository(TrainSet); const trainSetRepo = ds.getRepository(TrainSet);
let seeded = 0; const wagonRepo = ds.getRepository(Wagon);
const routeRepo = ds.getRepository(Route);
const milestoneRepo = ds.getRepository(RouteMilestone);
let nextTrainNum = await nextSequence(ds, "TST-SCH-*");
const routePrefix = "TST-RTE-";
let nextRouteNum = await nextRouteSeq(ds, routePrefix);
const now = new Date();
const travelHours = 11;
const intermediateYards = yards.filter(
(y) => y.id !== djibouti.id && y.id !== addis.id,
);
let loco = await locomotiveRepo.findOne({ where: { code: "TST-LOCO-01" } });
if (!loco) {
loco = await locomotiveRepo.save(
locomotiveRepo.create({
code: "TST-LOCO-01",
name: "Test Locomotive",
locomotiveType: "DIESEL",
maxPullWeightTons: 4200,
maxTrainLengthMeters: 760,
status: "AVAILABLE",
currentYardId: djibouti.id,
}),
);
}
for (let i = 0; i < count; i++) { for (let i = 0; i < count; i++) {
const seq = nextTrainNum + i;
const trainNumber = `TST-SCH-${String(seq).padStart(5, "0")}`;
const dir = directionList[i % directionList.length]; const dir = directionList[i % directionList.length];
const status = statusList[i % statusList.length]; const status = statusList[i % statusList.length];
const trainNumber = `TST-SCH-${String(i + 1).padStart(3, "0")}`; const isDispatched = status === "DISPATCHED";
const existing = await scheduleRepo.findOne({ where: { trainNumber } });
if (existing) {
this.log(` Schedule ${trainNumber} already exists — skipping`);
seeded++;
continue;
}
const originYard = dir === "IMPORT" ? djibouti : addis; const originYard = dir === "IMPORT" ? djibouti : addis;
const destYard = dir === "IMPORT" ? addis : djibouti; const destYard = dir === "IMPORT" ? addis : djibouti;
const isDispatched = status === "DISPATCHED"; const routeName = `${routePrefix}${String(nextRouteNum + i).padStart(3, "0")}`;
const now = new Date();
const departure = new Date(now); const departure = new Date(now);
departure.setDate(departure.getDate() + daysAhead); departure.setDate(departure.getDate() + daysAhead + i);
departure.setHours(7, 0, 0, 0); departure.setHours(7, 0, 0, 0);
const travelHours = dir === "IMPORT" ? 12 : 10;
const arrival = new Date(departure.getTime() + travelHours * 60 * 60 * 1000); const arrival = new Date(departure.getTime() + travelHours * 60 * 60 * 1000);
const locomotive = await ensureTestLocomotive(ds, originYard.id, (msg) => this.log(msg)); const route = await routeRepo.save(
const wagonCount = 4; routeRepo.create({
const wagonType = wagonTypes[0]; name: routeName,
const wagonCapacity = Number(wagonType.capacityTons) || 70; originYardId: originYard.id,
const wagonLength = Number(wagonType.lengthMeters) || 14; destinationYardId: destYard.id,
const tareWeight = Number(wagonType.tareWeightTons) || 14; isActive: true,
const totalWagonWeight = wagonCount * (tareWeight + 20); }),
);
await milestoneRepo.save(
milestoneRepo.create({ routeId: route.id, yardId: originYard.id, sequenceNo: 1 }),
);
for (const [mi, y] of intermediateYards.entries()) {
await milestoneRepo.save(
milestoneRepo.create({ routeId: route.id, yardId: y.id, sequenceNo: (mi + 1) * 2 }),
);
}
await milestoneRepo.save(
milestoneRepo.create({
routeId: route.id,
yardId: destYard.id,
sequenceNo: (intermediateYards.length + 1) * 2,
}),
);
const totalWagonWeight = 4 * (tareWeight + 20);
const trainSet = await trainSetRepo.save( const trainSet = await trainSetRepo.save(
trainSetRepo.create({ trainSetRepo.create({
locomotiveId: locomotive.id, locomotiveId: loco.id,
totalWeightTons: totalWagonWeight, totalWeightTons: totalWagonWeight,
totalLengthMeters: wagonLength * wagonCount, totalLengthMeters: wagonLength * 4,
wagonCount, wagonCount: 4,
status: isDispatched ? "DISPATCHED" : status === "DRAFT" ? "DRAFT" : "ASSIGNED", status: isDispatched ? "DISPATCHED" : status === "DRAFT" ? "DRAFT" : "ASSIGNED",
}), }),
); );
@@ -112,7 +180,7 @@ export function registerSeedTestSchedules(
await ds.getRepository(TrainSetLocomotive).save( await ds.getRepository(TrainSetLocomotive).save(
ds.getRepository(TrainSetLocomotive).create({ ds.getRepository(TrainSetLocomotive).create({
trainSetId: trainSet.id, trainSetId: trainSet.id,
locomotiveId: locomotive.id, locomotiveId: loco.id,
sequenceNo: 0, sequenceNo: 0,
}), }),
); );
@@ -134,26 +202,31 @@ export function registerSeedTestSchedules(
}), }),
); );
for (let w = 0; w < wagonCount; w++) { const wagonPrefix = `${trainNumber}-W`;
const seq = w + 1; let nextWagon = await nextWagonSeq(ds, wagonPrefix);
const wagonNumber = `${trainNumber}-W${String(seq).padStart(2, "0")}`; for (let w = 0; w < 4; w++) {
const ws = nextWagon + w;
const wagonNumber = `${wagonPrefix}${String(ws).padStart(2, "0")}`;
const physicalWagon = await ensureTestWagon(ds, { const wagon = wagonRepo.create({
wagonNumber, wagonNumber,
wagonTypeId: wagonType.id, wagonTypeId: wagonType.id,
yardId: originYard.id, currentYardId: originYard.id,
trainScheduleId: schedule.id, currentTrainScheduleId: schedule.id,
tareWeight, tareWeight,
capacityTons: wagonCapacity, maxPayloadWeight: wagonCapacity,
dispatched: isDispatched, status: isDispatched ? WagonStatus.Assigned : WagonStatus.Available,
notes: "Test seed wagon",
}); });
const saved = await wagonRepo.save(wagon as any);
const physicalWagon = Array.isArray(saved) ? saved[0] : saved;
await ds.getRepository(TrainSetWagon).save( await ds.getRepository(TrainSetWagon).save(
ds.getRepository(TrainSetWagon).create({ ds.getRepository(TrainSetWagon).create({
trainSetId: trainSet.id, trainSetId: trainSet.id,
wagonTypeId: wagonType.id, wagonTypeId: wagonType.id,
physicalWagonId: physicalWagon.id, physicalWagonId: physicalWagon.id,
sequenceNo: seq, sequenceNo: w + 1,
capacityTons: wagonCapacity, capacityTons: wagonCapacity,
lengthMeters: wagonLength, lengthMeters: wagonLength,
assignedWeightTons: 20, assignedWeightTons: 20,
@@ -162,137 +235,9 @@ export function registerSeedTestSchedules(
); );
} }
const scheduleBookings = await ds.getRepository(TrainScheduleBooking).find({
where: { trainScheduleId: schedule.id },
});
if (scheduleBookings.length === 0) {
const booking = await ds.getRepository(Booking).findOne({ where: {}, order: { createdAt: "DESC" } });
if (booking) {
await ds.getRepository(TrainScheduleBooking).save(
ds.getRepository(TrainScheduleBooking).create({
trainScheduleId: schedule.id,
bookingId: booking.id,
}),
);
}
}
seeded++;
this.log(` Created ${status} ${dir} schedule: ${trainNumber} (${originYard.label}${destYard.label})`); this.log(` Created ${status} ${dir} schedule: ${trainNumber} (${originYard.label}${destYard.label})`);
} }
this.log(`Done — ${seeded} train schedules ensured`); this.log(`Done — ${count} new train schedules created`);
}); });
} }
async function ensureTestRoute(
ds: DataSource,
yards: Yard[],
originYard: Yard,
destYard: Yard,
log: (msg: string) => void,
): Promise<Route> {
const repo = ds.getRepository(Route);
const name = `TST-RTE-${originYard.code}_${destYard.code}`;
let route = await repo.findOne({ where: { name } });
if (route) return route;
route = await repo.save(
repo.create({
name,
originYardId: originYard.id,
destinationYardId: destYard.id,
isActive: true,
}),
);
log(` Created route: ${name}`);
const milestoneRepo = ds.getRepository(RouteMilestone);
const intermediateYards = yards.filter(
(y) => y.id !== originYard.id && y.id !== destYard.id,
);
await milestoneRepo.save(
milestoneRepo.create({
routeId: route.id,
yardId: originYard.id,
sequenceNo: 0,
}),
);
for (const [idx, y] of intermediateYards.entries()) {
await milestoneRepo.save(
milestoneRepo.create({
routeId: route.id,
yardId: y.id,
sequenceNo: (idx + 1) * 2,
}),
);
}
await milestoneRepo.save(
milestoneRepo.create({
routeId: route.id,
yardId: destYard.id,
sequenceNo: (intermediateYards.length + 1) * 2,
}),
);
log(` Added ${intermediateYards.length + 2} milestones`);
return route;
}
async function ensureTestLocomotive(
ds: DataSource,
yardId: string,
log: (msg: string) => void,
): Promise<Locomotive> {
const repo = ds.getRepository(Locomotive);
const code = "TST-LOCO-01";
let loco = await repo.findOne({ where: { code } });
if (loco) return loco;
loco = await repo.save(
repo.create({
code,
name: "Test Locomotive",
locomotiveType: "DIESEL",
maxPullWeightTons: 4200,
maxTrainLengthMeters: 760,
status: "AVAILABLE",
currentYardId: yardId,
}),
);
log(` Created locomotive: ${code}`);
return loco;
}
async function ensureTestWagon(
ds: DataSource,
input: {
wagonNumber: string;
wagonTypeId: string;
yardId: string;
trainScheduleId: string;
tareWeight: number;
capacityTons: number;
dispatched: boolean;
},
): Promise<Wagon> {
const repo = ds.getRepository(Wagon);
const existing = await repo.findOne({ where: { wagonNumber: input.wagonNumber } });
if (existing) return existing;
const wagon = repo.create({
wagonNumber: input.wagonNumber,
wagonTypeId: input.wagonTypeId,
currentYardId: input.yardId,
currentTrainScheduleId: input.trainScheduleId,
tareWeight: input.tareWeight,
maxPayloadWeight: input.capacityTons,
status: input.dispatched ? WagonStatus.Assigned : WagonStatus.Available,
notes: "Test seed wagon",
});
const saved = await repo.save(wagon as any);
return Array.isArray(saved) ? saved[0] : saved;
}

View File

@@ -1,4 +1,3 @@
import type Vorpal from "vorpal";
import type { INestApplicationContext } from "@nestjs/common"; import type { INestApplicationContext } from "@nestjs/common";
export type CommandContext = { export type CommandContext = {