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:
Hagernesh
2026-08-20 12:35:19 +00:00
parent 8e2bab27cf
commit 0ceb595714
12 changed files with 622 additions and 1 deletions

View File

@@ -200,6 +200,11 @@ export enum InvoiceSource {
* per-booking link.
*/
ShippingLineCredit = "shipping_line_credit",
/**
* Ad-hoc extra charge finance raises against a booking (e.g. a fee not
* covered by an existing fee rule). `sourceId` is the charge id.
*/
AdditionalCharge = "additional_charge",
}
export enum SchedulingStatus {
@@ -865,6 +870,36 @@ export interface ClearanceDocRequest {
at: string;
}
// ── Additional charges (ad-hoc finance billing) ──────────────────────────────
/**
* DRAFT: staff is still editing, nothing sent. SENT: invoice issued to the
* customer (in-app + SMS + email). PAID: the invoice settled. CANCELLED:
* withdrawn before payment.
*/
export type AdditionalChargeStatus = "DRAFT" | "SENT" | "PAID" | "CANCELLED";
/** One ad-hoc extra charge finance raised against a booking. */
export interface AdditionalCharge {
id: string;
bookingId: string;
reason: string;
status: AdditionalChargeStatus;
amount: number;
currency: string;
file: { id: string; name: string; url: string } | null;
invoiceId: string | null;
invoiceNumber: string | null;
paymentReference: string | null;
createdByName: string | null;
createdAt: string;
sentByName: string | null;
sentAt: string | null;
paidAt: string | null;
cancelledAt: string | null;
cancelReason: string | null;
}
/** One entry of a clearance document's audit trail, oldest first. */
export interface ClearanceDocumentEvent {
type: "UPLOADED" | "RESUBMITTED" | "QUERIED" | "APPROVED";