Skip to content

Commit 04a5086

Browse files
committed
Merge branch 'develop' of github.com:topcoder-platform/reports-api-v6 into PM-5653_leaderboard-calculations
2 parents ae1bea3 + d10824f commit 04a5086

4 files changed

Lines changed: 41 additions & 32 deletions

File tree

README.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,11 @@ Dashboard figures use these shared definitions:
3838
`PAYMENT` winning. It is grouped by the payment creation month so projected
3939
payments that are owed or on hold remain visible. Members are deduplicated
4040
within each payment bucket and month.
41-
- Member-payment values use the latest payment version and sum `gross_amount`,
42-
falling back to `total_amount`. The payment-by-customer dashboard ranks the
43-
top five billing-account clients across the selected range and groups all
44-
unnamed or remaining clients under `Other Customers`.
41+
- Member-payment values use the latest non-cancelled finance payment and group
42+
`gross_amount` by payment creation month, falling back to `total_amount`.
43+
The payment-by-customer dashboard ranks the top five billing-account clients
44+
across the selected range and groups all unnamed or remaining clients under
45+
`Other Customers`.
4546
- Challenge participation uses the latest actual phase completion month for
4647
Challenge, Marathon Match, and First2Finish cohorts. Registrants are
4748
Submitter resources, and submitters have a non-deleted submission for the

sql/reports/dashboard/member-payment-by-customer.sql

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
-- Monthly paid-member values split by the selected range's top five clients.
1+
-- Monthly member-payment values split by the selected range's top five clients.
22
--
33
-- Parameters:
44
-- $1 timestamptz - inclusive reporting range start
55
-- $2 timestamptz - exclusive reporting range end
66
--
7-
-- The same ranked customer series is used for every month. Payments for
8-
-- unranked or unnamed clients are grouped under Other Customers.
7+
-- The same ranked customer series is used for every month. The latest
8+
-- non-cancelled payment record is counted in its creation month so projected
9+
-- payments remain visible. Payments for unranked or unnamed clients are
10+
-- grouped under Other Customers.
911
-- Billing-account ids are normalized and compared as text so the historical
1012
-- zero sentinel falls back to challenge billing without unsafe integer casts.
1113
WITH bounds AS (
@@ -28,9 +30,9 @@ latest_payment_versions AS MATERIALIZED (
2830
FROM finance.payment p
2931
GROUP BY p.winnings_id
3032
),
31-
paid_events AS MATERIALIZED (
33+
payment_events AS MATERIALIZED (
3234
SELECT
33-
COALESCE(p.date_paid, p.created_at) AS paid_at,
35+
p.created_at AS activity_at,
3436
COALESCE(p.gross_amount, p.total_amount, 0) AS amount,
3537
NULLIF(TRIM(cl.id), '') AS customer_id,
3638
NULLIF(TRIM(cl.name), '') AS customer_label
@@ -56,18 +58,18 @@ paid_events AS MATERIALIZED (
5658
)
5759
LEFT JOIN "billing-accounts"."Client" cl
5860
ON cl.id = COALESCE(payment_ba."clientId", challenge_ba."clientId")
59-
WHERE p.payment_status = 'PAID'
61+
WHERE p.payment_status IS DISTINCT FROM 'CANCELLED'
6062
AND w.type = 'PAYMENT'
61-
AND COALESCE(p.date_paid, p.created_at) IS NOT NULL
63+
AND p.created_at IS NOT NULL
6264
AND NULLIF(TRIM(w.winner_id), '') IS NOT NULL
6365
AND w.category::text IS DISTINCT FROM 'TOPGEAR_PAYMENT'
6466
),
6567
selected_events AS (
6668
SELECT pe.*
67-
FROM paid_events pe
69+
FROM payment_events pe
6870
CROSS JOIN bounds b
69-
WHERE pe.paid_at >= b.start_at
70-
AND pe.paid_at < b.end_at
71+
WHERE pe.activity_at >= b.start_at
72+
AND pe.activity_at < b.end_at
7173
),
7274
customer_totals AS (
7375
SELECT
@@ -118,7 +120,7 @@ series AS (
118120
),
119121
monthly_amounts AS (
120122
SELECT
121-
DATE_TRUNC('month', se.paid_at) AS month_start,
123+
DATE_TRUNC('month', se.activity_at) AS month_start,
122124
COALESCE(
123125
'customer-' || tc.customer_id,
124126
'other-customers'
@@ -129,7 +131,7 @@ monthly_amounts AS (
129131
ON tc.customer_id = se.customer_id
130132
AND tc.customer_label = se.customer_label
131133
GROUP BY
132-
DATE_TRUNC('month', se.paid_at),
134+
DATE_TRUNC('month', se.activity_at),
133135
COALESCE('customer-' || tc.customer_id, 'other-customers')
134136
)
135137
SELECT

sql/reports/dashboard/member-payment-by-month.sql

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
-- Monthly paid-member values split by canonical payment bucket.
1+
-- Monthly member-payment values split by canonical payment bucket.
22
--
33
-- Parameters:
44
-- $1 timestamptz - inclusive reporting range start
55
-- $2 timestamptz - exclusive reporting range end
66
--
7-
-- Only the latest version of each payment is considered. Gross amount is the
8-
-- preferred member-payment value, with total amount used as a fallback.
7+
-- The latest non-cancelled payment record is counted in its creation month so
8+
-- projected payments remain visible. Gross amount is the preferred
9+
-- member-payment value, with total amount used as a fallback.
910
WITH bounds AS (
1011
SELECT
1112
$1::timestamptz AT TIME ZONE 'UTC' AS start_at,
@@ -26,9 +27,9 @@ latest_payment_versions AS MATERIALIZED (
2627
FROM finance.payment p
2728
GROUP BY p.winnings_id
2829
),
29-
paid_events AS MATERIALIZED (
30+
payment_events AS MATERIALIZED (
3031
SELECT
31-
COALESCE(p.date_paid, p.created_at) AS paid_at,
32+
p.created_at AS activity_at,
3233
COALESCE(p.gross_amount, p.total_amount, 0) AS amount,
3334
CASE
3435
WHEN w.category::text = 'TAAS_PAYMENT' THEN 'taas'
@@ -48,15 +49,15 @@ paid_events AS MATERIALIZED (
4849
AND lpv.max_version = p.version
4950
JOIN finance.winnings w
5051
ON w.winning_id = p.winnings_id
51-
WHERE p.payment_status = 'PAID'
52+
WHERE p.payment_status IS DISTINCT FROM 'CANCELLED'
5253
AND w.type = 'PAYMENT'
53-
AND COALESCE(p.date_paid, p.created_at) IS NOT NULL
54+
AND p.created_at IS NOT NULL
5455
AND NULLIF(TRIM(w.winner_id), '') IS NOT NULL
5556
AND w.category::text IS DISTINCT FROM 'TOPGEAR_PAYMENT'
5657
),
5758
selected_months AS (
5859
SELECT
59-
DATE_TRUNC('month', pe.paid_at) AS month_start,
60+
DATE_TRUNC('month', pe.activity_at) AS month_start,
6061
COALESCE(SUM(pe.amount) FILTER (
6162
WHERE pe.payment_type = 'taas'
6263
), 0) AS taas,
@@ -69,11 +70,11 @@ selected_months AS (
6970
COALESCE(SUM(pe.amount) FILTER (
7071
WHERE pe.payment_type = 'engagement'
7172
), 0) AS engagement
72-
FROM paid_events pe
73+
FROM payment_events pe
7374
CROSS JOIN bounds b
74-
WHERE pe.paid_at >= b.start_at
75-
AND pe.paid_at < b.end_at
76-
GROUP BY DATE_TRUNC('month', pe.paid_at)
75+
WHERE pe.activity_at >= b.start_at
76+
AND pe.activity_at < b.end_at
77+
GROUP BY DATE_TRUNC('month', pe.activity_at)
7778
)
7879
SELECT
7980
TO_CHAR(m.month_start, 'YYYY-MM-01') AS month,

src/reports/dashboard/dashboard-reports.sql.spec.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,14 +54,16 @@ describe("Dashboard report SQL", () => {
5454
expect(sql).toMatch(/COUNT\(DISTINCT pe\.member_id\) FILTER/g);
5555
});
5656

57-
it("sums latest paid-member values by canonical payment bucket", () => {
57+
it("sums projected member-payment values by canonical payment bucket", () => {
5858
const sql = sqlLoader.load("reports/dashboard/member-payment-by-month.sql");
5959

6060
expect(sql).toContain("MAX(p.version) AS max_version");
6161
expect(sql).toContain("lpv.max_version = p.version");
62-
expect(sql).toContain("p.payment_status = 'PAID'");
62+
expect(sql).toContain("p.payment_status IS DISTINCT FROM 'CANCELLED'");
6363
expect(sql).toContain("w.type = 'PAYMENT'");
64-
expect(sql).toContain("COALESCE(p.date_paid, p.created_at)");
64+
expect(sql).toContain("p.created_at AS activity_at");
65+
expect(sql).not.toContain("p.payment_status = 'PAID'");
66+
expect(sql).not.toContain("p.date_paid");
6567
expect(sql).toContain(
6668
"COALESCE(p.gross_amount, p.total_amount, 0) AS amount",
6769
);
@@ -80,7 +82,10 @@ describe("Dashboard report SQL", () => {
8082
);
8183

8284
expect(sql).toContain("MAX(p.version) AS max_version");
83-
expect(sql).toContain("p.payment_status = 'PAID'");
85+
expect(sql).toContain("p.payment_status IS DISTINCT FROM 'CANCELLED'");
86+
expect(sql).toContain("p.created_at AS activity_at");
87+
expect(sql).not.toContain("p.payment_status = 'PAID'");
88+
expect(sql).not.toContain("p.date_paid");
8489
expect(sql).toContain(
8590
"COALESCE(p.gross_amount, p.total_amount, 0) AS amount",
8691
);

0 commit comments

Comments
 (0)