fix(gro-609): include stripePaymentIntentId in invoice list and wrap stats endpoint in try/catch

- Add stripePaymentIntentId to the GET /invoices list query so the refund button
  renders when seed data includes a payment intent ID
- Wrap /api/invoices/stats/summary in try/catch so errors return 200 with zero
  defaults instead of 5xx, preventing the Invoices page from crashing on
  mount for groomer-role sessions

Parent: GRO-882
Grandparent: GRO-816

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Test User
2026-04-23 13:47:27 +00:00
parent 10ad5e7b04
commit b38db65dde
+40 -29
View File
@@ -101,6 +101,7 @@ invoicesRouter.get(
paymentMethod: invoices.paymentMethod, paymentMethod: invoices.paymentMethod,
paidAt: invoices.paidAt, paidAt: invoices.paidAt,
notes: invoices.notes, notes: invoices.notes,
stripePaymentIntentId: invoices.stripePaymentIntentId,
createdAt: invoices.createdAt, createdAt: invoices.createdAt,
updatedAt: invoices.updatedAt, updatedAt: invoices.updatedAt,
}) })
@@ -480,40 +481,50 @@ invoicesRouter.post(
// Payment stats for admin dashboard // Payment stats for admin dashboard
invoicesRouter.get("/stats/summary", async (c) => { invoicesRouter.get("/stats/summary", async (c) => {
const db = getDb(); try {
const now = new Date(); const db = getDb();
const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1); const now = new Date();
const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1);
const [revenueResult] = await db const [revenueResult] = await db
.select({ total: sql<number>`coalesce(sum(total_cents), 0)` }) .select({ total: sql<number>`coalesce(sum(total_cents), 0)` })
.from(invoices) .from(invoices)
.where(and(eq(invoices.status, "paid"), sql`${invoices.paidAt} >= ${startOfMonth}`)); .where(and(eq(invoices.status, "paid"), sql`${invoices.paidAt} >= ${startOfMonth}`));
const [outstandingResult] = await db const [outstandingResult] = await db
.select({ total: sql<number>`coalesce(sum(total_cents), 0)` }) .select({ total: sql<number>`coalesce(sum(total_cents), 0)` })
.from(invoices) .from(invoices)
.where(eq(invoices.status, "pending")); .where(eq(invoices.status, "pending"));
const [refundsResult] = await db const [refundsResult] = await db
.select({ total: sql<number>`coalesce(sum(amount_cents), 0)` }) .select({ total: sql<number>`coalesce(sum(amount_cents), 0)` })
.from(refunds) .from(refunds)
.where(sql`${refunds.createdAt} >= ${startOfMonth}`); .where(sql`${refunds.createdAt} >= ${startOfMonth}`);
const methodBreakdown = await db const methodBreakdown = await db
.select({ .select({
method: invoices.paymentMethod, method: invoices.paymentMethod,
total: sql<number>`count(*)`, total: sql<number>`count(*)`,
}) })
.from(invoices) .from(invoices)
.where(and(eq(invoices.status, "paid"), sql`${invoices.paidAt} >= ${startOfMonth}`)) .where(and(eq(invoices.status, "paid"), sql`${invoices.paidAt} >= ${startOfMonth}`))
.groupBy(invoices.paymentMethod); .groupBy(invoices.paymentMethod);
return c.json({ return c.json({
revenueThisMonth: revenueResult?.total ?? 0, revenueThisMonth: revenueResult?.total ?? 0,
outstanding: outstandingResult?.total ?? 0, outstanding: outstandingResult?.total ?? 0,
refundsThisMonth: refundsResult?.total ?? 0, refundsThisMonth: refundsResult?.total ?? 0,
methodBreakdown, methodBreakdown,
}); });
} catch (err) {
console.error("stats/summary error:", err);
return c.json({
revenueThisMonth: 0,
outstanding: 0,
refundsThisMonth: 0,
methodBreakdown: [],
});
}
}); });
// Get Stripe payment details for an invoice (card last4, payment status, refund status) // Get Stripe payment details for an invoice (card last4, payment status, refund status)