mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-28 05:30:55 +00:00
Merge pull request #834 from Tria-plc/freight_feature/usermanagement
Freight feature/usermanagement
This commit is contained in:
@@ -157,13 +157,13 @@ export class BookingLifecycleNotifierService {
|
||||
});
|
||||
}
|
||||
|
||||
/** Clearance finalized → customer can proceed to request operation. */
|
||||
/** Document approval finalized → customer can proceed to request operation. */
|
||||
clearanceReady(b: Booking): void {
|
||||
const msg =
|
||||
`Clearance for booking ${b.reference} is complete. ` +
|
||||
`Document approval for booking ${b.reference} is finalized. ` +
|
||||
`You can now proceed to request operation from the portal.`;
|
||||
void this.notifyContact(b, msg, 'CLEARANCE READY');
|
||||
this.inApp(b, 'Clearance complete', msg, {
|
||||
void this.notifyContact(b, msg, 'DOCUMENT APPROVAL FINALIZED');
|
||||
this.inApp(b, 'Document approval finalized', msg, {
|
||||
type: NotificationType.CLEARANCE_DECISION,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -456,7 +456,7 @@ export class ContractClearanceService {
|
||||
const allowed = ['CLEARANCE_UNDER_REVIEW', 'AWAITING_CLEARANCE_DOCUMENTS'];
|
||||
if (!allowed.includes(contract.status)) {
|
||||
throw new ConflictException(
|
||||
`Cannot finalize clearance on status "${contract.status}".`,
|
||||
`Cannot finalize document approval on status "${contract.status}".`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -374,23 +374,18 @@ export class ContractsService {
|
||||
if (companyProfileId) {
|
||||
// Business-license files are FileRecords (resource "company_profiles");
|
||||
// carry the live ones by reference. Staged/pending uploads are excluded by
|
||||
// code. Codes are slugged from each document name so they group under
|
||||
// "Profile documents" on the contract detail page.
|
||||
// code. The `business_license` prefix is preserved so the portal groups
|
||||
// them under "Business license" instead of the clearance catch-all — the
|
||||
// index suffix keeps multiple licences distinct.
|
||||
const records = await this.filesService.findByResource(
|
||||
companyProfileId,
|
||||
'company_profiles',
|
||||
);
|
||||
const slug = (name: string) =>
|
||||
name
|
||||
.toLowerCase()
|
||||
.replace(/\.[a-z0-9]+$/, '')
|
||||
.replace(/[^a-z0-9]+/g, '_')
|
||||
.replace(/^_+|_+$/g, '') || 'profile_document';
|
||||
|
||||
records
|
||||
.filter((r) => r.code === 'business_license')
|
||||
.forEach((r, i) => {
|
||||
const code = `${slug(r.name)}_${i + 1}`;
|
||||
const code = `business_license_${i + 1}`;
|
||||
if (existingCodes.has(code)) return;
|
||||
docs.push({
|
||||
code,
|
||||
|
||||
@@ -3444,19 +3444,22 @@ export class BookingBatchService implements OnModuleInit {
|
||||
|
||||
/**
|
||||
* Physical wagons marshalled in the schedule's built train, or null when the
|
||||
* schedule has no built train (or the consist is still empty) and the legacy
|
||||
* locomotive-derived capacity must apply. This count is what caps a built
|
||||
* train's bookings: 50 wagons coupled → 50 wagon slots, no more.
|
||||
* schedule has NO built train and the legacy locomotive-derived capacity must
|
||||
* apply. This count is what caps a built train's bookings: 50 wagons coupled
|
||||
* → 50 wagon slots, no more.
|
||||
*
|
||||
* A built train with an EMPTY consist returns 0, NOT null: zero coupled
|
||||
* wagons means zero capacity. Folding that case into null used to hand an
|
||||
* un-consisted train the abstract locomotive budget, so an empty train
|
||||
* advertised its full maxWagons as free space and accepted bookings the
|
||||
* allocator could never place.
|
||||
*/
|
||||
private async builtTrainWagonCount(
|
||||
schedule: TrainSchedule,
|
||||
): Promise<number | null> {
|
||||
const trainId = schedule.trainSet?.train?.id;
|
||||
if (!trainId) return null;
|
||||
const count = await this.dataSource
|
||||
.getRepository(Wagon)
|
||||
.count({ where: { trainId } });
|
||||
return count > 0 ? count : null;
|
||||
return this.dataSource.getRepository(Wagon).count({ where: { trainId } });
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -7131,9 +7131,19 @@ export class TrainSchedulingService {
|
||||
|
||||
await this.dataSource.transaction(async (manager) => {
|
||||
await manager.getRepository(TrainSetWagon).delete(trainSetWagonId);
|
||||
// Recount from the slot rows rather than decrementing the cached counter.
|
||||
// A blind `wagonCount - 1` desyncs the moment two removals race or the
|
||||
// in-memory schedule graph is stale, and the counter is what the schedule
|
||||
// capacity math reads.
|
||||
const remaining = await manager.getRepository(TrainSetWagon).find({
|
||||
where: { trainSetId: schedule.trainSetId },
|
||||
select: { id: true, lengthMeters: true },
|
||||
});
|
||||
await manager.getRepository(TrainSet).update(schedule.trainSetId, {
|
||||
wagonCount: Math.max(0, (schedule.trainSet?.wagonCount ?? 0) - 1),
|
||||
totalLengthMeters: Math.max(0, (schedule.trainSet?.totalLengthMeters ?? 0) - (wagon.lengthMeters ?? 0)),
|
||||
wagonCount: remaining.length,
|
||||
totalLengthMeters: roundTons(
|
||||
remaining.reduce((sum, w) => sum + (Number(w.lengthMeters) || 0), 0),
|
||||
),
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user