Speed up payment/refund processing on the supervisor dashboard

Reconciling a Yoco payment and sending payment links blocked the HTTP
response on ticket-PDF generation and email/WhatsApp sends; they now
run in the background like the other payment flows already did.
Registration/payment option loops (pricing, stock checks, ticket
generation) now resolve concurrently instead of sequentially. The
Payments page dropped a per-registration N+1 fetch and now refreshes
its lists in parallel after each action. Added missing indexes for
dashboard stats and donation-leg lookups, and made GET
/api/registrations optionally paginated.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-08 00:00:29 +02:00
co-authored by Claude Sonnet 5
parent 6759e9c2d3
commit 0a5b08020f
9 changed files with 145 additions and 123 deletions
+9 -8
View File
@@ -161,18 +161,18 @@ async function refreshPricingForRegistration(registrationId) {
});
if (!registration) return { changed: false };
let anyChanged = false;
for (const ro of registration.registrationOptions) {
// Each registrationOption is independent, so resolve/update them concurrently
// instead of one at a time — this loop sits directly in the payment-capture path.
const results = await Promise.all(registration.registrationOptions.map(async (ro) => {
// Only refresh options that were priced via a tier
if (!ro.appliedTierId) continue;
if (!ro.appliedTierId) return false;
// Find the currently applied tier
const currentTier = (ro.eventOption.earlyBirdTiers || []).find(t => t.id === ro.appliedTierId);
if (currentTier && new Date() < new Date(currentTier.deadline)) {
// The tier's deadline is still in the future — honor the locked price.
continue;
return false;
}
// Deadline has passed (or tier record missing) — resolve the next applicable tier
@@ -191,11 +191,12 @@ async function refreshPricingForRegistration(registrationId) {
appliedTierId: resolved.tierId
}
});
anyChanged = true;
return true;
}
}
return false;
}));
return { changed: anyChanged };
return { changed: results.some(Boolean) };
}
/**