This commit is contained in:
Nathnael
2026-07-02 09:38:14 +00:00
parent 63937db32b
commit 5aafa436c1
6 changed files with 261 additions and 122 deletions

View File

@@ -1,7 +1,13 @@
import { forwardRef, Inject, Injectable, Logger } from "@nestjs/common";
import {
BadRequestException,
forwardRef,
Inject,
Injectable,
Logger,
} from "@nestjs/common";
import { OnEvent } from "@nestjs/event-emitter";
import { Freight } from "@edr/types";
import { DataSource } from "typeorm";
import { DataSource, EntityManager } from "typeorm";
import {
BillingService,
@@ -58,9 +64,9 @@ export class BookingInvoiceService {
* Ensure the booking has its invoice, generating one from the snapshotted
* pricing breakdown if absent. Called when a booking reaches a billable state.
* Idempotent — returns the existing open invoice instead of a duplicate.
* Returns `null` (and logs) when the booking is not billable: no company to
* bill (e.g. government bookings whose `companyId` is null, which the invoices
* FK requires), or no priced amount.
* Throws `BadRequestException` when the booking is not billable: no company
* to bill (e.g. government bookings whose `companyId` is null, which the
* invoices FK requires), or no priced amount.
*/
async ensureInvoiceForBooking(
booking: Booking,
@@ -74,8 +80,8 @@ export class BookingInvoiceService {
if (existing) return existing;
if (!booking.companyId) {
this.logger.warn(
`Skipping invoice for booking ${booking.reference} (${booking.id}): no company to bill.`,
throw new BadRequestException(
`Cannot generate invoice for booking ${booking.reference} (${booking.id}): no company to bill.`,
);
}
@@ -102,7 +108,13 @@ export class BookingInvoiceService {
}
}
updateStatus = this.billing.updateStatus;
updateStatus(
invoiceId: string,
status: Freight.InvoiceStatus,
manager?: EntityManager,
): Promise<void> {
return this.billing.updateStatus(invoiceId, status, manager);
}
/**
* Advance a booking once its prepaid invoice settles — the domain side-effect
@@ -165,7 +177,11 @@ export class BookingInvoiceService {
// Fall back to a single freight line when no breakdown was snapshotted.
if (lines.length === 0) {
const amount = Number(booking.totalAmount);
if (!Number.isFinite(amount) || amount <= 0) throw new Error("No price");
if (!Number.isFinite(amount) || amount <= 0) {
throw new BadRequestException(
`Cannot generate invoice for booking ${booking.reference} (${booking.id}): no priced amount.`,
);
}
lines.push({
chargeType: "FREIGHT",
description: "Rail freight",