add draft declaration

This commit is contained in:
Marshal
2026-08-23 19:02:33 +00:00
parent 58f88b74f8
commit 31fa313c66
10 changed files with 201 additions and 35 deletions

View File

@@ -1633,6 +1633,23 @@ export class BookingsController {
return this.transitionService.enrichBookingResponse(booking);
}
@Post(":id/clearance/draft-declaration/skip")
@BookingStaff(FREIGHT_PERMS.contracts.clearanceEtActions)
@ApiOperation({
summary:
"GL ET skips the draft-declaration round: no estimate is sent to the customer, the real declaration is filed directly and duty & tax passes by default",
})
async skipBookingDraftDeclaration(
@Param("id", ParseUUIDPipe) id: string,
@CurrentUser() user: TCurrentUser,
) {
const booking = await this.bookingClearanceService.skipDraftDeclaration(
id,
resolveAuthUserId(user),
);
return this.transitionService.enrichBookingResponse(booking);
}
@Post(":id/clearance/draft-declaration/accept")
@PortalCustomer()
@ApiOperation({

View File

@@ -785,6 +785,50 @@ export class BookingClearanceService {
return updated;
}
/**
* GL Ethiopia skips the draft-declaration round entirely: the customer is
* not sent an estimate, staff file the real customs declaration directly.
* Duty & tax passes with it by default — there is no draft price to advise
* from. Advising duty later still works and overrides the skip (a skipped
* milestone is completed normally by adviseDuty).
*/
async skipDraftDeclaration(bookingId: string, userId?: string): Promise<Booking> {
const booking = await this.loadBooking(bookingId);
if (booking.tradeDirection !== 'IMPORT') {
throw new BadRequestException('Draft declaration applies only to import bookings.');
}
await this.milestoneService.ensureBookingMilestones(bookingId, 'IMPORT');
const milestones = await this.workflowService.listMilestonesForBooking(bookingId);
const uploaded = milestones.find((m) => m.milestoneCode === 'DRAFT_DECLARATION_UPLOADED');
if (uploaded?.status === 'COMPLETED') {
throw new BadRequestException(
'A draft declaration was already sent to the customer — it can no longer be skipped.',
);
}
await this.workflowService.assertPriorCompleteForBooking(
bookingId,
'IMPORT',
'DRAFT_DECLARATION_UPLOADED',
);
await this.workflowService.skipMilestonesForBooking(bookingId, [
'DRAFT_DECLARATION_UPLOADED',
'DRAFT_DECLARATION_ACCEPTED',
]);
await this.workflowService.onDutySkippedForBooking(bookingId);
await this.bookingsRepository.update(bookingId, {
dutyRequired: false,
clearanceCurrentPhase: ContractDocPhase.GlEtOutput,
} as never);
await this.clearanceEvents.record({
bookingId,
action: 'DRAFT_DECLARATION_SKIPPED',
label:
'Skipped the draft declaration — filing the customs declaration directly (duty & tax passed by default)',
actorId: userId ?? null,
});
return this.bookingsService.findById(bookingId);
}
/**
* The customer accepts the draft declaration — GL Ethiopia may now file the
* real customs declaration.