Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ workflows:
only:
- develop
- pm-1127_1
- ps-466

# Production builds are exectuted only on tagged commits to the
# master branch.
Expand Down
95 changes: 95 additions & 0 deletions sql/reports/topcoder/member-payments.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
WITH base AS (
SELECT
DATE(p.created_at) AS payment_created_date,

-- Payment info
p.payment_id,
w.description AS payment_desc,

-- Challenge system id
COALESCE(
NULLIF(TRIM(w.external_id), ''),
NULLIF(TRIM(c."id"::text), '')
) AS challenge_system_id,

p.payment_status,
w.category::text AS payment_type_desc,

-- Member info
mem.handle AS member_handle,
NULL::text AS payment_method_desc,
mem."userId" AS member_user_id,

-- Billing / client info
ba."name" AS billing_account_name,
cl."name" AS customer_name,
cl."codeName" AS sfdc_account_name,
NULL::text AS parent_name,

-- Challenge dates
DATE(c."createdAt") AS challenge_created_date,

-- Amount
COALESCE(p.gross_amount, p.total_amount) AS gross_amount
FROM finance.payment p
JOIN finance.winnings w
ON w.winning_id = p.winnings_id
JOIN members.member mem
ON mem."userId"::text = w.winner_id

LEFT JOIN challenges."Challenge" c
ON c."id" = w.external_id

LEFT JOIN challenges."ChallengeBilling" cb
ON cb."challengeId" = c."id"

LEFT JOIN "billing-accounts"."BillingAccount" ba
ON ba."id" = COALESCE(
NULLIF(p.billing_account, '')::int,
NULLIF(cb."billingAccountId", '')::int
)

LEFT JOIN "billing-accounts"."Client" cl
ON cl."id" = ba."clientId"

LEFT JOIN projects.projects proj
ON proj.id = c."projectId"::bigint

WHERE DATE(p.created_at) >= $1::date
AND DATE(p.created_at) < $2::date
)
SELECT
payment_created_date AS "payment_create_date.date_date",
payment_id AS "payment.payment_id",
payment_desc AS "payment.payment_desc",
COALESCE(challenge_system_id, '-') AS "challenge.challenge_system_id",
payment_status AS "payment.payment_status_desc",
payment_type_desc AS "payment.payment_type_desc",
member_handle AS "payee.handle",
payment_method_desc AS "payee.payment_method_desc",
billing_account_name AS "billing_account_budgets.billing_account_name",
customer_name AS "billing_account_budgets.customer_name",
sfdc_account_name AS "sfdc_account.name",
member_user_id AS "member_profile_basic.user_id",
parent_name AS "sfdc_account.parent_name",
challenge_created_date AS "challenge.create_date",
COALESCE(SUM(gross_amount), 0) AS "user_payment.gross_amount"
FROM base
GROUP BY
payment_created_date,
payment_id,
payment_desc,
challenge_system_id,
payment_status,
payment_type_desc,
member_handle,
payment_method_desc,
billing_account_name,
customer_name,
sfdc_account_name,
member_user_id,
parent_name,
challenge_created_date
ORDER BY
payment_created_date DESC
LIMIT 5000;
12 changes: 12 additions & 0 deletions src/reports/topcoder/topcoder-reports.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,18 @@ export class TopcoderReportsController {
return this.reports.get30DayPayments();
}

@Get("/member-payments")
@ApiOperation({
summary:
"List of member payments (optional query params: startDate, endDate in YYYY-MM-DD)",
})
getMemberPayments(
@Query("startDate") startDate?: string,
@Query("endDate") endDate?: string,
) {
return this.reports.getMemberPayments(startDate, endDate);
}

@Get("/90-day-member-spend")
@ApiOperation({
summary: "Total gross amount paid to members in the last 90 days",
Expand Down
52 changes: 52 additions & 0 deletions src/reports/topcoder/topcoder-reports.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,58 @@ export class TopcoderReportsService {
}));
}

async getMemberPayments(startDate?: string, endDate?: string) {
const defaultEnd = new Date();
const defaultStart = new Date(
defaultEnd.getTime() - 30 * 24 * 60 * 60 * 1000,
);

const start = startDate ?? defaultStart.toISOString().slice(0, 10);
const end = endDate ?? defaultEnd.toISOString().slice(0, 10);

const query = this.sql.load("reports/topcoder/member-payments.sql");
type MemberPaymentRow = {
"payment_create_date.date_date": Date | string | null;
"payment.payment_id": string | number | null;
"payment.payment_desc": string | null;
"challenge.challenge_system_id": string | null;
"payment.payment_status_desc": string | null;
"payment.payment_type_desc": string | null;
"payee.handle": string | null;
"payee.payment_method_desc": string | null;
"billing_account_budgets.billing_account_name": string | null;
"billing_account_budgets.customer_name": string | null;
"sfdc_account.name": string | null;
"member_profile_basic.user_id": string | number | null;
"sfdc_account.parent_name": string | null;
"challenge.create_date": Date | string | null;
"user_payment.gross_amount": string | number | null;
};

const rows = await this.db.query<MemberPaymentRow>(query, [start, end]);

return rows.map((row) => ({
paymentCreateDate: this.normalizeDate(
row["payment_create_date.date_date"],
),
paymentId: row["payment.payment_id"] ?? null,
paymentDesc: row["payment.payment_desc"] ?? null,
challengeSystemId: row["challenge.challenge_system_id"] ?? null,
paymentStatus: row["payment.payment_status_desc"] ?? null,
paymentType: row["payment.payment_type_desc"] ?? null,
payeeHandle: row["payee.handle"] ?? null,
payeePaymentMethod: row["payee.payment_method_desc"] ?? null,
billingAccountName:
row["billing_account_budgets.billing_account_name"] ?? null,
customerName: row["billing_account_budgets.customer_name"] ?? null,
sfdcAccountName: row["sfdc_account.name"] ?? null,
memberUserId: row["member_profile_basic.user_id"] ?? null,
parentName: row["sfdc_account.parent_name"] ?? null,
challengeCreatedAt: this.normalizeDate(row["challenge.create_date"]),
grossAmount: this.toNullableNumber(row["user_payment.gross_amount"]),
}));
}

async getRegistrantCountries(challengeId: string) {
const query = this.sql.load("reports/topcoder/registrant-countries.sql");
const rows = await this.db.query<RegistrantCountriesRow>(query, [
Expand Down
Loading