Merge branch 'dev' of github.com:Tria-plc/edr-platform into freight/feature/user_management_UI

This commit is contained in:
natib21
2026-07-16 08:13:57 +00:00
3 changed files with 34 additions and 20 deletions

View File

@@ -125,7 +125,7 @@ export class BillingService {
private readonly payment: PaymentService, private readonly payment: PaymentService,
private readonly companies: CompaniesService, private readonly companies: CompaniesService,
private readonly invoiceDocuments: InvoiceDocumentService, private readonly invoiceDocuments: InvoiceDocumentService,
) {} ) { }
// ── Reads ────────────────────────────────────────────────────────────────── // ── Reads ──────────────────────────────────────────────────────────────────
@@ -432,7 +432,7 @@ export class BillingService {
input.dueAt ?? input.dueAt ??
new Date( new Date(
Date.now() + 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); const invoiceNumber = await this.nextInvoiceNumber(mg);
@@ -991,26 +991,26 @@ export class BillingService {
returnUrl: opts.returnUrl, returnUrl: opts.returnUrl,
failureUrl: opts.failureUrl, failureUrl: opts.failureUrl,
}); });
// //
// Link the intent to the invoice BEFORE any settlement can correlate against it. // Link the intent to the invoice BEFORE any settlement can correlate against it.
await this.dataSource await this.dataSource
.getRepository(Invoice) .getRepository(Invoice)
.update({ id: invoice.id }, { paymentId: result.intentId }); .update({ id: invoice.id }, { paymentId: result.intentId });
// DEMO: manually fire the gateway `payment.succeeded` callback here, without // // DEMO: manually fire the gateway `payment.succeeded` callback here, without
// waiting for real gateway settlement. Runs AFTER the paymentId link above so // // waiting for real gateway settlement. Runs AFTER the paymentId link above so
// `handlePaymentEvent → settleByPaymentId` can correlate the invoice. TODO: // // `handlePaymentEvent → settleByPaymentId` can correlate the invoice. TODO:
// remove — real settlement flips this via the `${source}.invoice.paid` handler. // // remove — real settlement flips this via the `${source}.invoice.paid` handler.
if (!result.immediateSuccess) { // if (!result.immediateSuccess) {
await this.payment.handlePaymentEvent({ // await this.payment.handlePaymentEvent({
eventType: "payment.succeeded", // eventType: "payment.succeeded",
eventId: `demo-${result.intentId}`, // eventId: `demo-${result.intentId}`,
referenceId: invoice.sourceId, // referenceId: invoice.sourceId,
intentId: result.intentId, // intentId: result.intentId,
providerTxnId: result.providerTxnId, // providerTxnId: result.providerTxnId,
paidAt: (result.paidAt ?? new Date()).toISOString(), // paidAt: (result.paidAt ?? new Date()).toISOString(),
}); // });
} // }
if (result.immediateSuccess) { if (result.immediateSuccess) {
await this.settleByPaymentId( await this.settleByPaymentId(

View File

@@ -110,6 +110,13 @@ export default function ContractClearanceDetailPage() {
const { data: bookingMilestones, refetch: refetchBookingMilestones } = const { data: bookingMilestones, refetch: refetchBookingMilestones } =
useBookingMilestones(linkedBookingId); 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) { if (isLoading) {
return ( return (
<PageContainer> <PageContainer>
@@ -261,7 +268,7 @@ export default function ContractClearanceDetailPage() {
onChanged={() => { onChanged={() => {
void refetch(); void refetch();
void refetchContract(); void refetchContract();
void refetchBookingMilestones(); refetchBookingMilestonesIfLinked();
}} }}
/> />
</Grid.Col> </Grid.Col>
@@ -279,7 +286,7 @@ export default function ContractClearanceDetailPage() {
roleMode="ET" roleMode="ET"
onChanged={() => { onChanged={() => {
void refetch(); void refetch();
void refetchBookingMilestones(); refetchBookingMilestonesIfLinked();
}} }}
onViewFile={view} onViewFile={view}
onDownloadFile={(f) => void downloadBookingFile(f.id, f.name)} onDownloadFile={(f) => void downloadBookingFile(f.id, f.name)}

View File

@@ -105,6 +105,13 @@ export default function GlClearanceDetailPage() {
const { data: bookingMilestones, refetch: refetchBookingMilestones } = const { data: bookingMilestones, refetch: refetchBookingMilestones } =
useBookingMilestones(linkedBookingId); 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) { if (isLoading) {
return ( return (
<PageContainer> <PageContainer>
@@ -275,7 +282,7 @@ export default function GlClearanceDetailPage() {
onUploadRoRequest={() => setUploadKind("ro")} onUploadRoRequest={() => setUploadKind("ro")}
onChanged={() => { onChanged={() => {
void refetch(); void refetch();
void refetchBookingMilestones(); refetchBookingMilestonesIfLinked();
}} }}
onViewFile={view} onViewFile={view}
onDownloadFile={(f) => void downloadBookingFile(f.id, f.name)} onDownloadFile={(f) => void downloadBookingFile(f.id, f.name)}