From 08faf56c4f60f46919a7699ce8041879d0876692 Mon Sep 17 00:00:00 2001 From: Nathnael Date: Thu, 16 Jul 2026 06:43:12 +0000 Subject: [PATCH 1/2] fix: payment autopay on the payment init added for testing --- .../src/modules/billing/billing.service.ts | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/apps/edr-freight-api/src/modules/billing/billing.service.ts b/apps/edr-freight-api/src/modules/billing/billing.service.ts index 8c9461018..3b14d6f4c 100644 --- a/apps/edr-freight-api/src/modules/billing/billing.service.ts +++ b/apps/edr-freight-api/src/modules/billing/billing.service.ts @@ -125,7 +125,7 @@ export class BillingService { private readonly payment: PaymentService, private readonly companies: CompaniesService, private readonly invoiceDocuments: InvoiceDocumentService, - ) {} + ) { } // ── Reads ────────────────────────────────────────────────────────────────── @@ -432,7 +432,7 @@ export class BillingService { input.dueAt ?? new Date( Date.now() + - (input.dueInDays ?? DEFAULT_DUE_DAYS) * 24 * 60 * 60 * 1000, + (input.dueInDays ?? DEFAULT_DUE_DAYS) * 24 * 60 * 60 * 1000, ); const invoiceNumber = await this.nextInvoiceNumber(mg); @@ -991,26 +991,26 @@ export class BillingService { returnUrl: opts.returnUrl, failureUrl: opts.failureUrl, }); -// + // // Link the intent to the invoice BEFORE any settlement can correlate against it. await this.dataSource .getRepository(Invoice) .update({ id: invoice.id }, { paymentId: result.intentId }); - // DEMO: manually fire the gateway `payment.succeeded` callback here, without - // waiting for real gateway settlement. Runs AFTER the paymentId link above so - // `handlePaymentEvent → settleByPaymentId` can correlate the invoice. TODO: - // remove — real settlement flips this via the `${source}.invoice.paid` handler. - if (!result.immediateSuccess) { - await this.payment.handlePaymentEvent({ - eventType: "payment.succeeded", - eventId: `demo-${result.intentId}`, - referenceId: invoice.sourceId, - intentId: result.intentId, - providerTxnId: result.providerTxnId, - paidAt: (result.paidAt ?? new Date()).toISOString(), - }); - } + // // DEMO: manually fire the gateway `payment.succeeded` callback here, without + // // waiting for real gateway settlement. Runs AFTER the paymentId link above so + // // `handlePaymentEvent → settleByPaymentId` can correlate the invoice. TODO: + // // remove — real settlement flips this via the `${source}.invoice.paid` handler. + // if (!result.immediateSuccess) { + // await this.payment.handlePaymentEvent({ + // eventType: "payment.succeeded", + // eventId: `demo-${result.intentId}`, + // referenceId: invoice.sourceId, + // intentId: result.intentId, + // providerTxnId: result.providerTxnId, + // paidAt: (result.paidAt ?? new Date()).toISOString(), + // }); + // } if (result.immediateSuccess) { await this.settleByPaymentId( From 5f6d8f9294eea211fc52b036d8178daf8d826fef Mon Sep 17 00:00:00 2001 From: Nathnael Date: Thu, 16 Jul 2026 07:17:22 +0000 Subject: [PATCH 2/2] fix: cache invalidation --- .../pages/contracts/ContractClearanceDetailPage.tsx | 11 +++++++++-- .../src/pages/contracts/GlClearanceDetailPage.tsx | 9 ++++++++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/apps/edr-freight-web/backoffice/src/pages/contracts/ContractClearanceDetailPage.tsx b/apps/edr-freight-web/backoffice/src/pages/contracts/ContractClearanceDetailPage.tsx index 76666c320..619ce59ad 100644 --- a/apps/edr-freight-web/backoffice/src/pages/contracts/ContractClearanceDetailPage.tsx +++ b/apps/edr-freight-web/backoffice/src/pages/contracts/ContractClearanceDetailPage.tsx @@ -110,6 +110,13 @@ export default function ContractClearanceDetailPage() { const { data: bookingMilestones, refetch: refetchBookingMilestones } = useBookingMilestones(linkedBookingId); + // react-query's imperative refetch() ignores `enabled`, so calling it while + // linkedBookingId is still undefined (pre-booking clearance) would fire + // GET /contracts/bookings/undefined/milestones → 400 (uuid expected). Guard it. + const refetchBookingMilestonesIfLinked = () => { + if (linkedBookingId) void refetchBookingMilestones(); + }; + if (isLoading) { return ( @@ -261,7 +268,7 @@ export default function ContractClearanceDetailPage() { onChanged={() => { void refetch(); void refetchContract(); - void refetchBookingMilestones(); + refetchBookingMilestonesIfLinked(); }} /> @@ -279,7 +286,7 @@ export default function ContractClearanceDetailPage() { roleMode="ET" onChanged={() => { void refetch(); - void refetchBookingMilestones(); + refetchBookingMilestonesIfLinked(); }} onViewFile={view} onDownloadFile={(f) => void downloadBookingFile(f.id, f.name)} diff --git a/apps/edr-freight-web/backoffice/src/pages/contracts/GlClearanceDetailPage.tsx b/apps/edr-freight-web/backoffice/src/pages/contracts/GlClearanceDetailPage.tsx index 3444e85f1..daff48939 100644 --- a/apps/edr-freight-web/backoffice/src/pages/contracts/GlClearanceDetailPage.tsx +++ b/apps/edr-freight-web/backoffice/src/pages/contracts/GlClearanceDetailPage.tsx @@ -105,6 +105,13 @@ export default function GlClearanceDetailPage() { const { data: bookingMilestones, refetch: refetchBookingMilestones } = useBookingMilestones(linkedBookingId); + // react-query's imperative refetch() ignores `enabled`, so calling it while + // linkedBookingId is still undefined (pre-booking clearance) would fire + // GET /contracts/bookings/undefined/milestones → 400 (uuid expected). Guard it. + const refetchBookingMilestonesIfLinked = () => { + if (linkedBookingId) void refetchBookingMilestones(); + }; + if (isLoading) { return ( @@ -275,7 +282,7 @@ export default function GlClearanceDetailPage() { onUploadRoRequest={() => setUploadKind("ro")} onChanged={() => { void refetch(); - void refetchBookingMilestones(); + refetchBookingMilestonesIfLinked(); }} onViewFile={view} onDownloadFile={(f) => void downloadBookingFile(f.id, f.name)}