-
Notifications
You must be signed in to change notification settings - Fork 4
fix: admin payment revenue calc uses payments table with refund deduc… #66
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -545,7 +545,7 @@ const adminService = { | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| e.id AS event_id, e.name AS event_name, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| COUNT(t.id)::int AS tickets_generated, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| COUNT(t.check_in_at)::int AS tickets_checked_in, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| p.transaction_id, p.gateway, p.status AS gateway_status, p.paid_at | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| p.transaction_id, p.juspay_order_id, p.gateway, p.status AS gateway_status, p.paid_at | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| FROM bookings b | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| JOIN users u ON u.id = b.user_id | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| JOIN events e ON e.id = b.event_id | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -578,7 +578,7 @@ const adminService = { | |||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (conditions.length > 0) query += ` WHERE ${conditions.join(' AND ')}` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| query += ` GROUP BY b.id, u.id, u.name, u.email, u.phone, e.id, e.name, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| p.transaction_id, p.gateway, p.status, p.paid_at` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| p.transaction_id, p.juspay_order_id, p.gateway, p.status, p.paid_at` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| query += ` ORDER BY b.booked_at DESC LIMIT $${p++} OFFSET $${p++}` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| params.push(limit, offset) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -613,22 +613,23 @@ const adminService = { | |||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const whereClause = conditions.join(' AND ') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // For tickets subquery, we need to rebuild the conditions | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const ticketConditions = managerId | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // For tickets/payment subqueries, we need to rebuild the conditions | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const subqueryManagerCond = managerId | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? `bk.event_id IN (SELECT id FROM events WHERE manager_id = $${managerParamPosition})` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| : '1=1' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const ticketDateCond = [`bk.booked_at >= $${fromParamPosition}`] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (toParamPosition) ticketDateCond.push(`bk.booked_at <= $${toParamPosition}`) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const ticketWhere = [ticketConditions, ...ticketDateCond, 'bk.status = \'confirmed\''].filter(Boolean).join(' AND ') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const subqueryDateCond = [`bk.booked_at >= $${fromParamPosition}`] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (toParamPosition) subqueryDateCond.push(`bk.booked_at <= $${toParamPosition}`) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const ticketWhere = [subqueryManagerCond, ...subqueryDateCond, 'bk.status = \'confirmed\''].filter(Boolean).join(' AND ') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const paymentWhere = [subqueryManagerCond, ...subqueryDateCond, "p.status IN ('success', 'refunded')"].filter(Boolean).join(' AND ') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+620
to
+623
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't window refunded revenue by The new net-revenue formula still uses the booking timestamp for date filtering. That makes historical ranges unstable: a refund posted later changes revenue for the original booking window because Also applies to: 631-632 |
||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const row = await dbOneOrNone(` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| SELECT | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| COUNT(*)::int AS total_payments, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| COUNT(*) FILTER (WHERE b.status = 'confirmed')::int AS successful_payments, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| COUNT(*) FILTER (WHERE b.status = 'pending')::int AS pending_payments, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| COUNT(*) FILTER (WHERE b.status = 'cancelled')::int AS failed_payments, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| COALESCE(SUM(b.total_amount) FILTER (WHERE b.status = 'confirmed'), 0)::numeric AS total_revenue, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| COALESCE(AVG(b.total_amount) FILTER (WHERE b.status = 'confirmed'), 0)::numeric AS avg_order_value, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| COALESCE((SELECT SUM(p.amount - COALESCE(p.refund_amount, 0)) FROM payments p JOIN bookings bk ON bk.id = p.booking_id WHERE ${paymentWhere}), 0)::numeric AS total_revenue, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| COALESCE((SELECT AVG(p.amount - COALESCE(p.refund_amount, 0)) FROM payments p JOIN bookings bk ON bk.id = p.booking_id WHERE ${paymentWhere} AND (p.amount - COALESCE(p.refund_amount, 0)) > 0), 0)::numeric AS avg_order_value, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+631
to
+632
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| COALESCE((SELECT SUM(p.amount - COALESCE(p.refund_amount, 0)) FROM payments p JOIN bookings bk ON bk.id = p.booking_id WHERE ${paymentWhere}), 0)::numeric AS total_revenue, | |
| COALESCE((SELECT AVG(p.amount - COALESCE(p.refund_amount, 0)) FROM payments p JOIN bookings bk ON bk.id = p.booking_id WHERE ${paymentWhere} AND (p.amount - COALESCE(p.refund_amount, 0)) > 0), 0)::numeric AS avg_order_value, | |
| COALESCE(( | |
| SELECT SUM(p.amount - COALESCE(p.refund_amount, 0)) | |
| FROM payments p | |
| JOIN LATERAL jsonb_array_elements_text( | |
| COALESCE( | |
| (p.raw_payload->'bookingIds')::jsonb, | |
| to_jsonb(ARRAY[p.booking_id]::int[]) | |
| ) | |
| ) AS bid(booking_id_text) ON TRUE | |
| JOIN bookings bk ON bk.id = bid.booking_id_text::int | |
| WHERE ${paymentWhere} | |
| ), 0)::numeric AS total_revenue, | |
| COALESCE(( | |
| SELECT AVG(p.amount - COALESCE(p.refund_amount, 0)) | |
| FROM payments p | |
| JOIN LATERAL jsonb_array_elements_text( | |
| COALESCE( | |
| (p.raw_payload->'bookingIds')::jsonb, | |
| to_jsonb(ARRAY[p.booking_id]::int[]) | |
| ) | |
| ) AS bid(booking_id_text) ON TRUE | |
| JOIN bookings bk ON bk.id = bid.booking_id_text::int | |
| WHERE ${paymentWhere} AND (p.amount - COALESCE(p.refund_amount, 0)) > 0 | |
| ), 0)::numeric AS avg_order_value, |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -128,6 +128,34 @@ const PaymentAnalysisView = () => { | |
| const statusData = Object.entries(statusCounts).filter(([, v]) => v > 0) | ||
|
|
||
| const txnCols = [ | ||
| { | ||
| field: 'id', headerName: 'Order ID', width: 100, | ||
| renderCell: ({ row }) => ( | ||
| <Typography variant='body2' fontWeight={600} sx={{ fontFamily: 'monospace' }}> | ||
| #{row.id} | ||
| </Typography> | ||
|
Comment on lines
+131
to
+136
|
||
| ) | ||
| }, | ||
| { | ||
| field: 'juspay_order_id', headerName: 'Juspay ID', width: 180, | ||
| renderCell: ({ row }) => ( | ||
| <Tooltip title={row.juspay_order_id || '—'} arrow> | ||
| <Typography variant='body2' color='text.secondary' noWrap sx={{ fontFamily: 'monospace', fontSize: '0.75rem' }}> | ||
| {row.juspay_order_id || '—'} | ||
| </Typography> | ||
| </Tooltip> | ||
| ) | ||
| }, | ||
| { | ||
| field: 'transaction_id', headerName: 'Transaction ID', width: 180, | ||
| renderCell: ({ row }) => ( | ||
| <Tooltip title={row.transaction_id || '—'} arrow> | ||
| <Typography variant='body2' color='text.secondary' noWrap sx={{ fontFamily: 'monospace', fontSize: '0.75rem' }}> | ||
| {row.transaction_id || '—'} | ||
| </Typography> | ||
| </Tooltip> | ||
| ) | ||
| }, | ||
| { | ||
| field: 'user_name', headerName: 'User', flex: 1, minWidth: 220, | ||
| renderCell: ({ row }) => ( | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: NexEvent/Citronics_2k26
Length of output: 3748
Add a payment-scoped row ID to prevent DataGrid collisions.
The query groups by
p.juspay_order_idand other payment fields without grouping byp.id. This means a single booking with multiple payments will produce multiple rows in the result—all with the sameb.id. The frontend grid atsrc/views/admin/payments/PaymentAnalysisView.js, Line 516 usesgetRowId={row => row.id}(booking ID), causing duplicate row IDs in the DataGrid whenever a booking has more than one payment.Suggested change
Update the grid row key:
🤖 Prompt for AI Agents