mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-09 05:58:18 +00:00
CHANGES
This commit is contained in:
@@ -44,6 +44,7 @@ import { Booking } from './entities/booking.entity';
|
||||
import { BookingsService } from './bookings.service';
|
||||
import { BookingClearanceService } from '../contracts/booking-clearance.service';
|
||||
import { ClearanceMilestoneService } from '../contracts/clearance-milestone.service';
|
||||
import { splitMilestones } from '../contracts/clearance-milestone.catalog';
|
||||
import { ClearanceWorkflowService } from '../contracts/clearance-workflow.service';
|
||||
import { ContractDocPhase } from '@edr/types';
|
||||
|
||||
@@ -1206,7 +1207,14 @@ export class BookingTransitionService {
|
||||
*/
|
||||
async finalizeClearance(bookingId: string, userId?: string): Promise<Booking> {
|
||||
const booking = await this.bookingsService.findById(bookingId);
|
||||
if (this.isPhasedCustoms(booking)) {
|
||||
// A GL-cleared customs booking runs the phased ET/DJ workflow end to end,
|
||||
// and finalize would jump the desks' own steps — it stays refused there.
|
||||
// An agent-cleared booking is different: the clearing agent does its
|
||||
// paperwork with customs off the platform, so Operations may close the
|
||||
// clearance once every customer document is approved. The steps that were
|
||||
// never taken are SKIPPED below rather than left pending, or the timeline
|
||||
// would stall behind milestones nobody is going to complete.
|
||||
if (this.isPhasedCustoms(booking) && !booking.clearedByAgent) {
|
||||
throw new BadRequestException(
|
||||
'General customs bookings use phased clearance — complete milestones via the phased actions instead of finalize.',
|
||||
);
|
||||
@@ -1220,6 +1228,13 @@ export class BookingTransitionService {
|
||||
);
|
||||
}
|
||||
|
||||
// Agent-cleared: close out the pre-booking steps the agent did not take,
|
||||
// then hand the booking to the customer to complete. Everything below this
|
||||
// point is the non-phased path and does not apply.
|
||||
if (booking.clearedByAgent) {
|
||||
return this.finalizeAgentClearedBooking(booking, userId);
|
||||
}
|
||||
|
||||
const { outputCode } = clearanceCodesForBooking(booking);
|
||||
if (outputCode) {
|
||||
const setting =
|
||||
@@ -1271,6 +1286,59 @@ export class BookingTransitionService {
|
||||
return fresh;
|
||||
}
|
||||
|
||||
/**
|
||||
* Operations closes the clearance on a booking the customer handed to a
|
||||
* registered clearing agent.
|
||||
*
|
||||
* The agent files with customs off the platform, so the steps it never
|
||||
* recorded here (declaration, transit permit, pre-clearance handoff,
|
||||
* Delivery Order / Release Order) are marked SKIPPED — a PENDING milestone
|
||||
* would stall the phase derivation and leave the wizard mid-flow forever.
|
||||
* Post-booking milestones are untouched: payment, wagons and the rail leg
|
||||
* still happen, and the customer completes the booking from here.
|
||||
*/
|
||||
private async finalizeAgentClearedBooking(
|
||||
booking: Booking,
|
||||
userId?: string,
|
||||
): Promise<Booking> {
|
||||
const bookingId = booking.id;
|
||||
const tradeDirection = booking.tradeDirection ?? 'IMPORT';
|
||||
const milestones =
|
||||
await this.workflowService.listMilestonesForBooking(bookingId);
|
||||
// Only the pre-booking half: anything from the boundary onward belongs to
|
||||
// the shipment itself, not to clearance.
|
||||
const { preBooking } = splitMilestones(tradeDirection);
|
||||
const preBookingCodes = new Set(preBooking.map((d) => d.code));
|
||||
const pending = milestones.filter(
|
||||
(m) => preBookingCodes.has(m.milestoneCode) && m.status === 'PENDING',
|
||||
);
|
||||
if (pending.length > 0) {
|
||||
await this.workflowService.skipMilestonesForBooking(
|
||||
bookingId,
|
||||
pending.map((m) => m.milestoneCode),
|
||||
);
|
||||
}
|
||||
|
||||
await this.bookingsRepository.update(bookingId, {
|
||||
status: 'CLEARANCE_READY',
|
||||
clearanceCurrentPhase: ContractDocPhase.GlEtPostClearance,
|
||||
preClearanceFinalizedAt: booking.preClearanceFinalizedAt ?? new Date(),
|
||||
} as never);
|
||||
await this.clearanceEvents.record({
|
||||
bookingId,
|
||||
action: 'CLEARANCE_FINALIZED',
|
||||
label:
|
||||
pending.length > 0
|
||||
? `Finalized clearance — ${pending.length} clearing-agent step(s) closed as not applicable`
|
||||
: 'Finalized document approval — clearance ready',
|
||||
actorId: userId ?? null,
|
||||
metadata: { skipped: pending.map((m) => m.milestoneCode) },
|
||||
});
|
||||
const fresh = await this.bookingsService.findById(bookingId);
|
||||
this.notifier.clearanceReady(fresh);
|
||||
return fresh;
|
||||
}
|
||||
|
||||
/**
|
||||
* Customer proceeds to operation once clearance is ready. They pick the
|
||||
* schedule day (the train departure day) for the shipment; the request then
|
||||
|
||||
Reference in New Issue
Block a user