mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-29 06:00:55 +00:00
feat(bookings): add ad-hoc additional-charge module
New freight.additional_charge table, independent of BookingClearanceCharge (unbounded per booking, free-text reason). Draft -> send issues an invoice and notifies the customer in-app/SMS/email; settles via the standard .invoice.paid event. Adds the Additional charges row-menu entry next to Cancel booking, permission-gated. Not included: the Additional Payments tab UI, add-charge modal, portal pay flow.
This commit is contained in:
@@ -42,6 +42,8 @@ import type { Response } from "express";
|
||||
import { BookingClearanceChargeService } from './booking-clearance-charge.service';
|
||||
import { ClearanceEventService } from './clearance-event.service';
|
||||
import { BillClearanceChargeDto } from './dto/clearance-charge.dto';
|
||||
import { AdditionalChargeService } from './additional-charge.service';
|
||||
import { CancelAdditionalChargeDto, CreateAdditionalChargeDto } from './dto/additional-charge.dto';
|
||||
import { BookingContractService } from './booking-contract.service';
|
||||
import { BookingPricingService } from './booking-pricing.service';
|
||||
import { BookingTransitionService } from './booking-transition.service';
|
||||
@@ -176,6 +178,7 @@ export class BookingsController {
|
||||
private readonly consolidationApprovalService: ConsolidationApprovalService,
|
||||
private readonly clearanceChargeService: BookingClearanceChargeService,
|
||||
private readonly clearanceEventService: ClearanceEventService,
|
||||
private readonly additionalChargeService: AdditionalChargeService,
|
||||
) {}
|
||||
|
||||
@Post()
|
||||
@@ -1210,6 +1213,61 @@ export class BookingsController {
|
||||
);
|
||||
}
|
||||
|
||||
// ── Additional charges (ad-hoc finance billing) ────────────────────────────
|
||||
|
||||
@Get(":id/additional-charges")
|
||||
@MixedAudience(FREIGHT_PERMS.additionalCharges.view)
|
||||
@ApiOperation({ summary: "Ad-hoc extra charges finance has raised against this booking" })
|
||||
async getAdditionalCharges(
|
||||
@Param("id", ParseUUIDPipe) id: string,
|
||||
@CurrentUser() user: TCurrentUser,
|
||||
) {
|
||||
if (!hasFreightPermission(user, FREIGHT_PERMS.additionalCharges.view)) {
|
||||
const booking = await this.bookingsService.findById(id);
|
||||
await this.bookingsService.assertCustomerCanAccessBooking(user?.id, booking);
|
||||
}
|
||||
return this.additionalChargeService.list(id);
|
||||
}
|
||||
|
||||
@Post(":id/additional-charges")
|
||||
@BookingStaff(FREIGHT_PERMS.additionalCharges.create)
|
||||
@UseInterceptors(FileInterceptor("file"))
|
||||
@ApiConsumes("multipart/form-data")
|
||||
@ApiOperation({
|
||||
summary: "Finance raises a new additional charge — draft, or send to the customer immediately",
|
||||
})
|
||||
createAdditionalCharge(
|
||||
@Param("id", ParseUUIDPipe) id: string,
|
||||
@UploadedFile() file: Express.Multer.File | undefined,
|
||||
@Body() dto: CreateAdditionalChargeDto,
|
||||
@CurrentUser() user: TCurrentUser,
|
||||
) {
|
||||
return this.additionalChargeService.create(id, dto, resolveAuthUserId(user), file);
|
||||
}
|
||||
|
||||
@Post(":id/additional-charges/:chargeId/send")
|
||||
@BookingStaff(FREIGHT_PERMS.additionalCharges.send)
|
||||
@ApiOperation({ summary: "Issue the draft charge's payable invoice and notify the customer" })
|
||||
sendAdditionalCharge(
|
||||
@Param("id", ParseUUIDPipe) id: string,
|
||||
@Param("chargeId", ParseUUIDPipe) chargeId: string,
|
||||
@CurrentUser() user: TCurrentUser,
|
||||
) {
|
||||
return this.additionalChargeService.send(id, chargeId, resolveAuthUserId(user));
|
||||
}
|
||||
|
||||
@Post(":id/additional-charges/:chargeId/cancel")
|
||||
@BookingStaff(FREIGHT_PERMS.additionalCharges.cancel)
|
||||
@ApiOperation({ summary: "Withdraw a draft or unpaid additional charge" })
|
||||
cancelAdditionalCharge(
|
||||
@Param("id", ParseUUIDPipe) id: string,
|
||||
@Param("chargeId", ParseUUIDPipe) chargeId: string,
|
||||
@Body() dto: CancelAdditionalChargeDto,
|
||||
@CurrentUser() user: TCurrentUser,
|
||||
) {
|
||||
return this.additionalChargeService.cancel(id, chargeId, resolveAuthUserId(user), dto.reason);
|
||||
}
|
||||
|
||||
@Post(":id/clearance/output-documents")
|
||||
@BookingStaff(FREIGHT_PERMS.bookings.uploadClearanceOutput)
|
||||
@UseInterceptors(AnyFilesInterceptor())
|
||||
|
||||
Reference in New Issue
Block a user