chore: type updates on the billing service

This commit is contained in:
Nathnael
2026-06-29 08:28:12 +00:00
parent 1da5af94ab
commit 36711eace4
2 changed files with 76 additions and 30 deletions

View File

@@ -308,42 +308,47 @@ export class BillingService {
* against it rather than recomputing from the source's own total, so
* discounts/penalties/adjustments carried on the invoice are honored.
*
* `type` selects which invoice when a source carries several (e.g. a booking's
* up-front vs final charge). Returns the most recent open (unpaid,
* non-cancelled) invoice of that type.
* Pass `type` to select a specific invoice when a source carries several (e.g.
* a booking's up-front vs final charge); omit it to settle whichever single
* invoice is currently open. Returns the most recent matching open (unpaid,
* non-cancelled) invoice.
*/
findPayable(
source: Freight.InvoiceSource,
sourceId: string,
type: string,
type?: string,
): Promise<Invoice | null> {
return this.dataSource.getRepository(Invoice).findOne({
where: { source, sourceId, type, status: In(OPEN_STATUSES) },
where: {
source,
sourceId,
status: In(OPEN_STATUSES),
...(type ? { type } : {}),
},
order: { issuedAt: "DESC" },
});
}
/**
* Settle a source's open invoice of `type` as paid and link the gateway
* Settle a source's currently-open invoice as paid and link the gateway
* payment, then emit `${source}.invoice.paid`. Resolves the open invoice then
* delegates to {@link markInvoiceAsPaid}. Full-payment only — no partial
* settlement. No-op (returns null) when the source has no open invoice of that
* type.
* settlement. No-op (returns null) when the source has no open invoice.
*
* `type` selects which invoice when a source carries several (e.g. a booking's
* up-front vs final charge). Pass the caller's transaction `manager` (e.g. from
* Type-blind by design: the payment process settles whichever invoice is due;
* any per-type reaction belongs in the `${source}.invoice.paid` handler, which
* reads `invoice.type`. Pass the caller's transaction `manager` (e.g. from
* `payment.service.finalizePaymentSuccess`) to enlist in its DB transaction.
*/
async settlePayable(
source: Freight.InvoiceSource,
sourceId: string,
type: string,
paymentId: string | null,
manager?: EntityManager,
): Promise<Invoice | null> {
const mg = manager ?? this.dataSource.manager;
const invoice = await mg.findOne(Invoice, {
where: { source, sourceId, type, status: In(OPEN_STATUSES) },
where: { source, sourceId, status: In(OPEN_STATUSES) },
order: { issuedAt: "DESC" },
});
if (!invoice) return null;
@@ -352,10 +357,9 @@ export class BillingService {
}
/**
* Refund a source's paid invoice of `type`, then emit
* `${source}.invoice.refunded`. Resolves the paid invoice then delegates to
* {@link markInvoiceAsRefunded}. No-op (returns null) when the source has no
* paid invoice of that type.
* Refund a source's paid invoice, then emit `${source}.invoice.refunded`.
* Resolves the paid invoice then delegates to {@link markInvoiceAsRefunded}.
* No-op (returns null) when the source has no paid invoice.
*
* Pass the caller's transaction `manager` (e.g. from `payment.service.refund`)
* to enlist in its DB transaction.
@@ -363,12 +367,11 @@ export class BillingService {
async refundPayable(
source: Freight.InvoiceSource,
sourceId: string,
type: string,
manager?: EntityManager,
): Promise<Invoice | null> {
const mg = manager ?? this.dataSource.manager;
const invoice = await mg.findOne(Invoice, {
where: { source, sourceId, type, status: Freight.InvoiceStatus.Paid },
where: { source, sourceId, status: Freight.InvoiceStatus.Paid },
order: { issuedAt: "DESC" },
});
if (!invoice) return null;