Skip to content

Commit 4ff34f5

Browse files
authored
Merge pull request #11 from topcoder-platform/develop
Add handling of engagements to locked / consumed tabls
2 parents 3e800fb + 377b58f commit 4ff34f5

23 files changed

Lines changed: 3756 additions & 193 deletions

.env.example

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,16 @@ DATABASE_URL="postgresql://postgres:postgres@localhost:5432/topcoder-services?sc
44
# Example: postgresql://postgres:postgres@localhost:5432/topcoder-services?schema=members
55
MEMBER_DB_URL=""
66

7+
# Optional lookup stores used to resolve billing-account line-item names.
8+
# If unset, line items are still returned with externalName omitted.
9+
CHALLENGE_DB_URL="postgresql://postgres:postgres@localhost:5432/topcoder-services?schema=challenges"
10+
ENGAGEMENTS_DB_URL="postgresql://postgres:postgres@localhost:5432/engagements"
11+
12+
# Historical engagement-payment backfill sources.
13+
# FINANCE_DB_URL points to tc-finance-api, PROJECTS_DB_URL points to projects-api-v6.
14+
FINANCE_DB_URL="postgresql://postgres:postgres@localhost:5432/topcoder-services?schema=finance"
15+
PROJECTS_DB_URL="postgresql://postgres:postgres@localhost:5432/topcoder-services?schema=projects"
16+
717
# Auth (tc-core-library-js)
818
AUTH_SECRET=""
919
AUTH0_URL="" # e.g. https://topcoder-dev.auth0.com/ (optional)

README.md

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@
77
- Endpoints:
88
- `GET /billing-accounts`
99
- `POST /billing-accounts`
10-
- `GET /billing-accounts/:billingAccountId` (includes locked/consumed arrays + budget totals)
10+
- `GET /billing-accounts/:billingAccountId` (includes locked/consumed arrays + budget totals; line items expose `amount`, `date`, `externalId`, `externalType`, `externalName`, and `challengeId` only for challenge compatibility)
1111
- `GET /billing-accounts/users/:userId` (list billing accounts accessible by the given Topcoder user ID — resolved via Salesforce resource object)
1212
- `PATCH /billing-accounts/:billingAccountId`
13-
- `PATCH /billing-accounts/:billingAccountId/lock-amount` (0 amount = unlock)
14-
- `PATCH /billing-accounts/:billingAccountId/consume-amount` (deletes locks for challenge, then upserts consumed)
13+
- `PATCH /billing-accounts/:billingAccountId/lock-amount` (challenge-only typed reference; non-negative amount; 0 amount = unlock; rejects insufficient remaining funds)
14+
- `PATCH /billing-accounts/:billingAccountId/consume-amount` (positive amount; challenge typed references delete the matching lock and overwrite consumed; engagement typed references append a consumed row; rejects insufficient remaining funds)
1515
- `GET /billing-accounts/:billingAccountId/users` (list users with access)
1616
- `POST /billing-accounts/:billingAccountId/users` (grant access; accepts `{ param: { userId } }` or `{ userId }`)
1717
- `DELETE /billing-accounts/:billingAccountId/users/:userId` (revoke access)
@@ -74,6 +74,38 @@ pnpm run build && pnpm start
7474
- Lookup the matching `user_account` by `user_account_id`, then query the Members DB by `user_name` (handle)
7575
- Upsert `BillingAccountAccess` for the resolved `userId`
7676

77+
- Engagement payment consumed backfill:
78+
- Required env:
79+
- `DATABASE_URL` for billing-accounts-api-v6.
80+
- `FINANCE_DB_URL` for tc-finance-api (`winnings` and `payment`).
81+
- `ENGAGEMENTS_DB_URL` for engagements-api-v6 (`EngagementAssignment` and `Engagement`).
82+
- `PROJECTS_DB_URL` for projects-api-v6 (`projects.billingAccountId`, matching the trusted project lookup used by engagements-api-v6).
83+
- Optional `TGBillingAccounts` for finance-compatible TopGear exemptions. If unset, the script uses finance's default exempt accounts `80000062,80002800`.
84+
- Dry run:
85+
- `pnpm run backfill:engagementPayments -- --dry-run`
86+
- Dry run is the default. It reads finance engagement payments, resolves assignment/project/billing account context, plans inserts/updates, and writes a JSON audit report under `scripts/output/`.
87+
- Apply:
88+
- `pnpm run backfill:engagementPayments -- --apply`
89+
- Optional filters: `--assignmentId=<id>[,<id>]`, `--winningId=<id>[,<id>]`, `--since=<iso-date>`, `--until=<iso-date>`, `--limit=<n>`, `--report=<path>`.
90+
- Behavior:
91+
- Uses `payment.challenge_markup` first as a markup rate and computes `total_amount + (total_amount * challenge_markup)`.
92+
- If `payment.challenge_markup` is absent and `payment.challenge_fee` is present, treats `challenge_fee` as an absolute fee amount, computes `total_amount + challenge_fee`, and records the row in the `absoluteFee` audit bucket.
93+
- If neither finance value is present, falls back to the current `BillingAccount.markup` and records that fallback in the audit report.
94+
- Skips automated consumed-row planning for TopGear-exempt billing accounts and records those payments in the `exemptBillingAccounts` audit bucket.
95+
- Apply mode runs billing mutations, post-apply total calculation, and report writing inside one billing database transaction. If a write, verification read, or report write fails before commit, the billing mutations are rolled back.
96+
- Reconciles one assignment-level aggregate row for historical idempotency. Existing correct rows are left alone; a single incorrect row is updated; multiple existing rows are only moved when their total already matches the legacy expected amount.
97+
- Exceptions such as missing assignments, missing project billing accounts, missing billing accounts, and ambiguous consumed duplicates are reported without blocking resolvable assignments.
98+
- Validation:
99+
- The dry-run/apply report includes expected legacy totals and current/projected billing totals.
100+
- `verify:engagementPayments` invokes `psql` directly, so export `DATABASE_URL` in the shell before running it.
101+
- If the source schemas are visible in one Postgres session, run:
102+
- `pnpm run verify:engagementPayments -- -v finance_schema=finance -v engagements_schema=public -v projects_schema=projects -v billing_schema=billing-accounts -v exempt_billing_account_ids=80000062,80002800`
103+
- The verification SQL lists mismatches between expected legacy engagement-payment consumes and `ConsumedAmount` rows with `externalType = ENGAGEMENT`. TopGear-exempt accounts are reported separately as `topgear_exempt_*` statuses.
104+
- Rollback:
105+
- Use the JSON report from the apply run. A successful apply only commits after that report is written. Delete rows listed with `action: "insert"` and `createdId`; restore rows listed with `action: "update_single"` from `previous`; restore rows listed with `action: "move_existing_rows"` from their `existingRows` billing account ids.
106+
- If apply mode exits with an error before writing the report, the transaction rolled back and there should be no partial billing-ledger changes from that run.
107+
- If apply mode exits with an error after a report file exists, run the verifier or a dry run before rollback; the billing transaction should have committed all planned actions or none.
108+
77109
## Downstream Usage
78110

79111
- This service is consumed (directly or indirectly) by multiple Topcoder apps. The pointers below help with debugging.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
"import:access": "ts-node scripts/import-ba-access.ts",
1717
"import:markup": "ts-node scripts/import-markup.ts",
1818
"backfill:clientIds": "ts-node scripts/backfill-client-ids.ts",
19+
"backfill:engagementPayments": "ts-node scripts/backfill-engagement-payments.ts",
20+
"verify:engagementPayments": "psql \"$DATABASE_URL\" -f scripts/verify-engagement-payment-backfill.sql",
1921
"generate:subcontractor-sql": "ts-node scripts/generate-subcontractor-sql.ts"
2022
},
2123
"dependencies": {

packages/ba-prisma-client/edge.js

Lines changed: 15 additions & 9 deletions
Large diffs are not rendered by default.

packages/ba-prisma-client/index-browser.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,8 @@ exports.Prisma.BillingAccountScalarFieldEnum = {
158158
exports.Prisma.LockedAmountScalarFieldEnum = {
159159
id: 'id',
160160
billingAccountId: 'billingAccountId',
161-
challengeId: 'challengeId',
161+
externalId: 'externalId',
162+
externalType: 'externalType',
162163
amount: 'amount',
163164
createdAt: 'createdAt',
164165
updatedAt: 'updatedAt'
@@ -167,7 +168,8 @@ exports.Prisma.LockedAmountScalarFieldEnum = {
167168
exports.Prisma.ConsumedAmountScalarFieldEnum = {
168169
id: 'id',
169170
billingAccountId: 'billingAccountId',
170-
challengeId: 'challengeId',
171+
externalId: 'externalId',
172+
externalType: 'externalType',
171173
amount: 'amount',
172174
createdAt: 'createdAt',
173175
updatedAt: 'updatedAt'
@@ -204,6 +206,11 @@ exports.BAStatus = exports.$Enums.BAStatus = {
204206
INACTIVE: 'INACTIVE'
205207
};
206208

209+
exports.BudgetEntryExternalType = exports.$Enums.BudgetEntryExternalType = {
210+
CHALLENGE: 'CHALLENGE',
211+
ENGAGEMENT: 'ENGAGEMENT'
212+
};
213+
207214
exports.Prisma.ModelName = {
208215
Client: 'Client',
209216
BillingAccount: 'BillingAccount',

0 commit comments

Comments
 (0)