mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-30 07:38:10 +00:00
feat(freight-api): enrich canonical request log line
Emit the request line as raw JSON on stdout (level/time/logger as fields) instead of through Nest's console logger, whose prefix made it unparsable. Collect data points via logCtx at the flow chokepoints: BaseRepository writes (status changes, creates, deletes), invoice transitions, payment intent lifecycle + outbound payment-service calls, booking/contract entry state, review-note reasons, signatures and OTP verify outcomes.
This commit is contained in:
@@ -8,6 +8,7 @@ import {
|
||||
NotFoundException,
|
||||
} from "@nestjs/common";
|
||||
import { EventEmitter2 } from "@nestjs/event-emitter";
|
||||
import { logCtx } from "@edr/api-common";
|
||||
import { DataSource, EntityManager, In } from "typeorm";
|
||||
|
||||
import { Booking } from "../bookings/entities/booking.entity";
|
||||
@@ -951,6 +952,24 @@ export class BillingService {
|
||||
|
||||
await mg.update(Invoice, { id: invoice.id }, { status, ...extra });
|
||||
|
||||
// Every invoice status move in the app funnels through here — money
|
||||
// changing state is the single most-asked question in support.
|
||||
logCtx(
|
||||
{
|
||||
invoiceId: invoice.id,
|
||||
invoiceNumber: invoice.invoiceNumber,
|
||||
source: invoice.source,
|
||||
sourceId: invoice.sourceId,
|
||||
from: invoice.status,
|
||||
to: status,
|
||||
event,
|
||||
amount: Number(invoice.totalAmount),
|
||||
currency: invoice.currency,
|
||||
paymentId: extra.paymentId ?? invoice.paymentId ?? undefined,
|
||||
},
|
||||
{ path: "invoiceTransitions", mode: "push" },
|
||||
);
|
||||
|
||||
const updated = { ...invoice, ...extra, status } as Invoice;
|
||||
return {
|
||||
result: updated,
|
||||
@@ -1320,6 +1339,21 @@ export class BillingService {
|
||||
throw new BadRequestException("Invoice has no outstanding balance.");
|
||||
}
|
||||
|
||||
logCtx(
|
||||
{
|
||||
invoiceId: invoice.id,
|
||||
invoiceNumber: invoice.invoiceNumber,
|
||||
source: invoice.source,
|
||||
sourceId: invoice.sourceId,
|
||||
companyId: invoice.companyId,
|
||||
amountDue,
|
||||
currency: invoice.currency,
|
||||
method: opts.method ?? "TELEBIRR",
|
||||
platform: opts.platform,
|
||||
},
|
||||
{ path: "payment.payInvoice" },
|
||||
);
|
||||
|
||||
// CAC Bank is an OTP debit — the bank SMSes the code to this number, so it is
|
||||
// required up front (the payment service rejects it otherwise, as a 502 here).
|
||||
if (
|
||||
@@ -1443,7 +1477,24 @@ export class BillingService {
|
||||
// first on DESC, which would hand back an unissued invoice.
|
||||
order: { issuedAt: { direction: "DESC", nulls: "LAST" } },
|
||||
});
|
||||
if (!invoice) return null;
|
||||
if (!invoice) {
|
||||
logCtx(
|
||||
{ paymentId, outcome: "no-invoice-for-payment" },
|
||||
{ path: "payment.settleInvoice" },
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
logCtx(
|
||||
{
|
||||
paymentId,
|
||||
providerTxnId,
|
||||
invoiceId: invoice.id,
|
||||
invoiceNumber: invoice.invoiceNumber,
|
||||
invoiceStatus: invoice.status,
|
||||
},
|
||||
{ path: "payment.settleInvoice" },
|
||||
);
|
||||
|
||||
const settleable: Freight.InvoiceStatus[] = [
|
||||
...OPEN_STATUSES,
|
||||
@@ -1453,6 +1504,12 @@ export class BillingService {
|
||||
// Already PAID is the ordinary idempotent no-op (redelivery, or settled
|
||||
// inline by payInvoice). Anything else means money was captured with
|
||||
// nowhere to land — that needs a person, so say so loudly.
|
||||
logCtx(
|
||||
invoice.status === Freight.InvoiceStatus.Paid
|
||||
? "already-paid"
|
||||
: "captured-with-nowhere-to-land",
|
||||
{ path: "payment.settleInvoice.outcome", mode: "set" },
|
||||
);
|
||||
if (invoice.status !== Freight.InvoiceStatus.Paid) {
|
||||
this.logger.error(
|
||||
`Payment ${paymentId} succeeded but invoice ${invoice.invoiceNumber} ` +
|
||||
|
||||
Reference in New Issue
Block a user