mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-28 03:10:54 +00:00
add customs clearing filter and enhance clearance documents page for non-customs contracts
This commit is contained in:
@@ -1211,6 +1211,12 @@ export class BookingsService {
|
||||
destinationYardId: filter.destinationYardId,
|
||||
isGovernment: filter.isGovernment,
|
||||
consolidationPaired: filter.consolidationPaired,
|
||||
// DTO carries 'true'/'false' strings (query params); the repo option is a
|
||||
// real boolean — convert, preserving "not filtered" when absent.
|
||||
customsClearingEnabled:
|
||||
filter.customsClearingEnabled === undefined
|
||||
? undefined
|
||||
: filter.customsClearingEnabled === 'true',
|
||||
search: filter.search,
|
||||
sortBy: filter.sortBy,
|
||||
sortOrder: filter.sortOrder,
|
||||
|
||||
@@ -106,6 +106,14 @@ export class FilterBookingDto {
|
||||
@IsIn(['true', 'false'])
|
||||
isGovernment?: 'true' | 'false';
|
||||
|
||||
@ApiPropertyOptional({
|
||||
enum: ['true', 'false'],
|
||||
description: 'Filter customs vs self-clearance (non-customs) bookings',
|
||||
})
|
||||
@IsOptional()
|
||||
@IsIn(['true', 'false'])
|
||||
customsClearingEnabled?: 'true' | 'false';
|
||||
|
||||
@ApiPropertyOptional({ enum: TRADE_DIRECTIONS })
|
||||
@IsOptional()
|
||||
@IsIn([...TRADE_DIRECTIONS])
|
||||
|
||||
@@ -872,6 +872,7 @@ export class ContractClearanceService {
|
||||
pageSize: filter.pageSize ?? 100,
|
||||
statuses: ['CLEARANCE_UNDER_REVIEW'],
|
||||
customsClearingEnabled: false,
|
||||
search: filter.search,
|
||||
sortBy: filter.sortBy,
|
||||
sortOrder: filter.sortOrder,
|
||||
});
|
||||
@@ -896,6 +897,7 @@ export class ContractClearanceService {
|
||||
pageSize: filter.pageSize ?? 50,
|
||||
statuses: ['CLEARANCE_READY_FOR_BOOKING', 'ACTIVE', 'CLOSED', 'CANCELLED'],
|
||||
customsClearingEnabled: false,
|
||||
search: filter.search,
|
||||
sortBy: filter.sortBy ?? 'createdAt',
|
||||
sortOrder: filter.sortOrder ?? 'DESC',
|
||||
});
|
||||
|
||||
@@ -53,6 +53,18 @@ export class TrainSchedulesRepository extends BaseRepository<TrainSchedule> {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Light fetch for human-facing labels (notifications): reference, train
|
||||
* number, departure and the two station names — none of the composition
|
||||
* graph {@link findByIdWithFullGraph} drags in.
|
||||
*/
|
||||
findByIdWithStations(id: string): Promise<TrainSchedule | null> {
|
||||
return this.repository.findOne({
|
||||
where: { id },
|
||||
relations: { originStation: true, destinationStation: true },
|
||||
});
|
||||
}
|
||||
|
||||
async updateStatus(
|
||||
id: string,
|
||||
status: TrainScheduleStatus,
|
||||
|
||||
@@ -2258,7 +2258,7 @@ export class BookingBatchService implements OnModuleInit {
|
||||
this.logger.log(
|
||||
`[BATCH] ALLOCATED ${booking.reference} (${reason}) to train on schedule ${scheduleId}`,
|
||||
);
|
||||
this.notifier.secured(booking, reason);
|
||||
this.notifier.secured(booking, reason, scheduleId);
|
||||
void this.triggerWagonAllocation(scheduleId);
|
||||
void this.markWagonAllocatedMilestone(booking.id);
|
||||
// Customer tracking: freight payment settled (commercial pay-window path).
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
import { Booking } from '../bookings/entities/booking.entity';
|
||||
import { NotificationsService } from '../notifications/notifications.service';
|
||||
import { NotificationInboxService } from '../notification-inbox/notification-inbox.service';
|
||||
import { TrainSchedulesRepository } from '../train-schedules/train-schedules.repository';
|
||||
import { BATCH_TIMEZONE } from './booking-batch.constants';
|
||||
|
||||
@Injectable()
|
||||
@@ -18,8 +19,37 @@ export class BookingNotifierService {
|
||||
constructor(
|
||||
private readonly notifications: NotificationsService,
|
||||
private readonly inbox: NotificationInboxService,
|
||||
private readonly trainSchedules: TrainSchedulesRepository,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Human-readable description of a train schedule for customer messages:
|
||||
* reference (or train number) + route + departure date. Never leaks a UUID —
|
||||
* falls back to a generic phrase when the schedule can't be loaded.
|
||||
*/
|
||||
private async scheduleLabel(scheduleId?: string | null): Promise<string> {
|
||||
const fallback = 'your selected train';
|
||||
if (!scheduleId) return fallback;
|
||||
try {
|
||||
const s = await this.trainSchedules.findByIdWithStations(scheduleId);
|
||||
if (!s) return fallback;
|
||||
const ref = s.reference ?? s.trainNumber ?? null;
|
||||
const route =
|
||||
s.originStation?.label && s.destinationStation?.label
|
||||
? ` (${s.originStation.label} → ${s.destinationStation.label})`
|
||||
: '';
|
||||
const departure = s.scheduledDepartureDate
|
||||
? `, departing ${new Date(s.scheduledDepartureDate).toLocaleDateString('en-GB', { timeZone: BATCH_TIMEZONE })}`
|
||||
: '';
|
||||
return ref ? `train ${ref}${route}${departure}` : `${fallback}${route}${departure}`;
|
||||
} catch (err) {
|
||||
this.logger.warn(
|
||||
`scheduleLabel(${scheduleId}) failed: ${(err as Error).message}`,
|
||||
);
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
|
||||
private ref(b: Booking): string {
|
||||
return `${b.reference}${b.isGovernment ? ' (gov)' : ''}`;
|
||||
}
|
||||
@@ -127,12 +157,15 @@ export class BookingNotifierService {
|
||||
});
|
||||
}
|
||||
|
||||
secured(b: Booking, reason: 'paid' | 'gov'): void {
|
||||
const msg = `Booking ${b.reference ?? b.id} allocated on train schedule ${b.trainScheduleId ?? ''}${
|
||||
reason === 'gov' ? ' (government)' : ''
|
||||
}.`;
|
||||
void this.notifyContact(b, msg, 'ALLOCATED');
|
||||
this.inApp(b, 'Wagon allocated', msg);
|
||||
secured(b: Booking, reason: 'paid' | 'gov', scheduleId?: string | null): void {
|
||||
void (async () => {
|
||||
const label = await this.scheduleLabel(scheduleId ?? b.trainScheduleId);
|
||||
const msg = `Booking ${b.reference ?? b.id} allocated on ${label}${
|
||||
reason === 'gov' ? ' (government)' : ''
|
||||
}.`;
|
||||
void this.notifyContact(b, msg, 'ALLOCATED');
|
||||
this.inApp(b, 'Wagon allocated', msg);
|
||||
})();
|
||||
}
|
||||
|
||||
expired(b: Booking): void {
|
||||
|
||||
Reference in New Issue
Block a user