Flows
F 12 Tester Balance
Status: š¢ ready to test Owner: Adilet Last updated: 2026-05-08 Last verified on production: ā
Goal
A tester sees their earnings split into 4 financial buckets + a Reports counter, broken down per cycle and per individual issue. They can export their full issue history as a CSV for personal accounting.
Actors
- Primary ā Tester (any user with at least one filed issue)
Preconditions
- Tester is signed in.
- Has filed at least one issue across one or more cycles (F-08)
Trigger
Tester clicks "Balance" in the QA section sidebar, navigating to
/app/test-cycles/balance.
Happy path ā view balance
| # | Actor | UI surface | Action | API call | DB writes | Side effects | Suggested event |
|---|---|---|---|---|---|---|---|
| 1 | Tester | sidebar | Click "Balance" | GET /api/me/balance | ā | ā | balance.viewed |
| 2 | Frontend | /app/test-cycles/balance | renders 5-column header + by-cycle list + issue history table | ā | ā | ā | ā |
The 5 header columns are:
| Column | Source | Notes |
|---|---|---|
| Awaiting triage | issues with payout_status='pending' | Estimated using default rates per severity (forecast, not promise) |
| Pending verification | payout_status='approved' AND gate active AND status != 'verified' | Approved but waiting for fix re-test |
| Available | payout_status='approved' AND (gate inactive OR status='verified') | Will be paid in next batch |
| Total paid | payout_status='paid' | Lifetime |
| Reports | counts only ā issuesFiled, issuesApproved, issuesNoPayout, acceptanceRate | Counters, not money |
Happy path ā export CSV
| # | Actor | UI surface | Action | API call | DB writes | Side effects | Suggested event |
|---|---|---|---|---|---|---|---|
| 1 | Tester | balance page | Click "Export CSV" | GET /api/me/balance/export.csv | ā | downloads balance.csv | balance.csv_exported |
| 2 | Browser | (file save) | ā | ā | ā | ā | ā |
CSV columns: date, cycle, title, severity, status, payout_status, amount, currency, paid_at, payment_method, payment_reference.
Acceptance criteria
- AC-1 ā Given a tester with 5 filed issues across 2 cycles, when
they open
/app/test-cycles/balance, thenReports.issuesFiled = 5,byCycle.length = 2. - AC-2 ā Given the gate is active and one of their approved issues is not verified, when balance loads, then that issue's amount appears in Pending verification, not in Available.
- AC-3 ā Given the gate is inactive on a cycle, when balance loads, then approved issues from that cycle appear directly in Available.
- AC-4 ā Given an issue is paid, when balance loads, then it appears
in Total paid and the issue history row shows
paid_at+ payment method + reference (truncated link tooltip with reference). - AC-5 ā Given the tester has issues in 3 currencies (KZT, USD, EUR), when balance loads, then each header column shows separate lines per currency.
- AC-6 ā Given
acceptanceRateis computed asissuesApproved / (issuesApproved + issuesNoPayout), when the user has 4 approved + 1 rejected, thenacceptanceRate = 0.8(UI shows "80%"). - AC-7 ā Given the user clicks Export CSV, when the response
arrives, then a
text/csvblob downloads with column headers + one row per issue in their history. - AC-8 ā Given an admin in impersonation mode (F-03),
when they navigate to
/app/test-cycles/balance, then they see the target's balance, not their own.
Test data / fixtures
- A tester with at least:
- 1 pending issue
- 1 approved-but-unverified issue (gate active cycle)
- 1 verified-and-paid issue
- 1 rejected issue Across 2+ cycles, in 1+ currency.
Negative paths
| # | Scenario | Expected behavior |
|---|---|---|
| N-1 | Tester has filed zero issues | All 5 columns show "ā"; cycle list empty; history table empty |
| N-2 | Tester has filed only drafts | Same as N-1 (drafts are excluded) |
| N-3 | Unauthenticated GET | API 401 |
| N-4 | CSV download with 0 rows | CSV with header row only |
Manual QA checklist
- As a tester with mixed-state issues, open
/app/test-cycles/balance - Confirm 5 header tiles render
- Confirm "Pending verification" + "Available" sum to legacy "approvedUnpaid" (the deprecated combined bucket ā verify shape via DevTools network)
- Confirm by-cycle list shows N rows, each linking to that cycle
- Confirm issue history table shows all non-draft issues, with severity / status / payout-status badges, and paid-at + method where applicable
- Click an issue title ā navigates to issue detail
- Click "Export CSV" ā file downloads with N+1 lines (header + N rows)
- Open CSV in spreadsheet ā columns line up; payment method values
use friendly labels (
Banknotbank_transfer) ā verify - As an admin, impersonate a tester (F-03) ā balance shows target's data
- Sign out, navigate to
/app/test-cycles/balanceā redirects to sign-in
Automated test outline
test.describe("F-12 tester balance", () => {
test("buckets reflect gate state", async ({ request }) => { ⦠});
test("acceptance rate calc", async ({ request }) => { ⦠});
test("multi-currency totals", async ({ request }) => { ⦠});
test("csv export shape", async ({ request }) => { ⦠});
test("impersonation reflects target", async ({ page }) => { ⦠});
});Code references
- Pages:
src/app/app/test-cycles/balance/page.tsx - UI:
src/components/test-cycles/BalanceSummary.tsx - API:
src/app/api/me/balance/route.tssrc/app/api/me/balance/export.csv/route.ts
- Hooks:
src/hooks/test-cycles/balance.ts - Lib:
src/lib/issue-payout.tsārequiresVerification - Schema:
issues,issue_payouts,test_cycles,products
Events emitted (proposed)
balance.viewedā{ user_id, currencies: [...], total_paid_cents, total_available_cents }balance.csv_exportedā{ user_id, row_count }
Open questions / known gaps
- The deprecated
approvedUnpaidbucket still ships in the API response for backward compat (@deprecatedtag in the type). Plan a removal pass once the BalanceSummary UI is fully on the new split. - "Awaiting triage" is an estimate using default per-severity rates, not the per-cycle rates. Could refine later.
- No "request payout" flow ā payouts are admin-initiated only (F-11).
- No filter / search inside the issue history table ā gets long on prolific testers.