Task-kiosk-260 — Standardize Adjustment / Reversal / Refund Wording
| Field | Value |
|---|---|
| Task ID | Task-kiosk-260 |
| Target release / Fix version | V1.2.0 |
| Scope | Admin Portal (kioskgaming_admin) — list, badge, filter, export, Audit/Transaction Detail |
| Out of scope (v1) | DB enum change for UsdWalletTransaction.type; Shop/Agent/Super Agent portal |
| Related docs | Admin financial correction debit spec · Admin USD wallet transaction detail view · Backend code review (full) |
1. Background
The terms Adjustment, Reversal, and Refund have different business meanings but are currently used inconsistently on Admin Portal. Operators cannot easily tell whether an entry is:
- a manual balance fix (correction),
- a reversal of a posted transaction (reversal),
- or a return of money/credit to the user (refund).
Task goal: standardize display taxonomy (presentation layer) — map from ledger/API to clear UI labels, with audit metadata on Detail pages.
2. Business definitions (source of truth)
| Case | UI wording | Short description |
|---|---|---|
| Admin credits balance to fix an error | Balance Correction — Credit | Manual ledger fix; has ticket/reason/admin; not required to link to one source payment tx |
| Admin debits balance to fix an error | Balance Correction — Debit | Clawback / debit correction; has ticket/reason/admin |
| Reverse an existing transaction | Transaction Reversal | Ledger entry paired 1-1 with source transaction (reject withdrawal, chained USD movement…) |
| Return money or credit to user | Refund | User receives balance back due to failed flow or intentional admin refund |
2.1 Do not use "Adjustment" as a generic label
- Forbidden to show badge/filter/export label
Adjustmentfor all correction/reversal/refund. - DB/API may still keep
type: adjustment— only UI changes label.
2.2 Transaction Reversal — required Detail fields
| Field | Description |
|---|---|
| Original Transaction ID | ID of reversed transaction (business transactionId or ledger row id — prefer business id) |
| Reversal Transaction ID | ID of current reversal row |
| Reason | Reversal reason (admin note, rejection reason, system message…) |
| Initiated By | Admin actor or System if automatic (payout fail webhook) |
2.3 Balance Correction / Refund — required Detail fields
| Field | Description |
|---|---|
| Reason | Required — from metadata.reason, metadata.note, refund description… |
| Initiated By | Admin (metadata.adminId, adminRejection.rejectedByAdminId…) or System |
3. Required changes (product)
3.1 Wording table (Admin UI — English)
| Internal signal | UI label |
|---|---|
| Admin credit correction | Balance Correction — Credit |
| Admin debit correction | Balance Correction — Debit |
| Transaction reversal | Transaction Reversal |
| User refund | Refund |
3.2 Acceptance criteria
- AC-1: No "Adjustment" shown as generic name on list/badge/filter/export (Admin).
- AC-2: Credit correction and Debit correction clearly distinguished (label + money direction +/-).
- AC-3: Reversal has link or reference to source transaction (clickable when route exists).
- AC-4: Refund used only when balance is actually returned to user — do not label every
type=reversalas Refund. - AC-5: Detail page shows Reason and Initiated By (not only generic reason label like "Balance adjustment").
4. Current state (gap analysis)
4.1 USD Wallet Ledger — serious mismatch
File: kioskgaming_admin/src/components/pages/usdWallets/usdWalletLedgerUtils.ts
DB type | Current badge | Current reasonLabel | Issue |
|---|---|---|---|
reversal | Refund | Balance correction | Against spec: reversal ≠ refund |
adjustment | Adjustment | Balance adjustment | Generic label; no Credit/Debit split |
Detail: UsdWalletActivityView.tsx — "Source & reason" section uses generic ledgerDisplay.reasonLabel, does not surface metadata.reason, metadata.adminId, sourceWithdrawalTransactionId.
4.2 Backend baseline (no enum change in v1)
Model: UsdWalletTransaction.type = deposit | withdrawal | adjustment | reversal
| Flow | DB type | Service | Near new wording |
|---|---|---|---|
POST /api/admin/finance-corrections (debit) | adjustment | usdWalletService.recordAdjustment | Balance Correction — Debit |
| Reject pending USD withdrawal | reversal | usdWalletService.recordReversal | Transaction Reversal |
Shop CDN credit-adjust direction=credit|debit | wallet ledger | adminAgentManagementBizService.applyShopWalletAdjust | Balance Correction — Credit/Debit |
| Payout fail → credit wallet | usually credit + payoutRefund metadata | paymentUserController, webhooks | Refund (product rule) |
| Admin game wallet refund | metadata.source=admin_refund | paymentAdminController._refundSingleTransaction | Refund |
Reversal metadata (reject withdrawal) — already present:
{
"sourceWithdrawalId": "<usd_wallet_transaction.uuid>",
"sourceWithdrawalTransactionId": "<business transactionId>",
"note": "<admin rejection note>"
}
Finance correction metadata — already present:
{
"ticketId": "INC-12345",
"reason": "...",
"adminId": "...",
"idempotencyKey": "..."
}
4.3 Other Admin screens still using "Adjustment"
| File | Location |
|---|---|
WalletTransactionTable.tsx | Filter + badge adjustment |
GameWalletTransactionTable.tsx | Direction filter adjustment → "Adjustment" |
AgentWalletTransferPanel.tsx | Option adjustment |
agentCdnTxUtils.ts | Label map |
CreditWalletActivityView.tsx | admin_shop_adjustment → "Admin shop adjustment" |
referenceTypeDescriptions.ts | admin_shop_adjustment blurb |
Already correct direction: FinanceCorrectionDebitModal.tsx — "Balance correction (debit)".
5. Classification rules (implementation)
Create one shared helper (proposed: kioskgaming_admin/src/utils/ledgerCorrectionDisplay.ts or extend usdWalletLedgerUtils.ts).
5.1 Pseudocode
function resolveCorrectionDisplay(input):
meta = parse metadata
type = lower(input.type)
refType = lower(input.referenceType)
netDelta = balanceNetAfter - balanceNetBefore
// --- Refund (user receives balance back) ---
IF meta.source == 'admin_refund'
OR meta.payoutRefund?.refundedAt
OR refType matches payment refund flow
OR provider indicates automatic payout refund (document per case)
→ label: "Refund"
show: reason, initiatedBy (admin or System)
// --- Transaction Reversal ---
IF type == 'reversal'
AND (
refType == 'usd_wallet_transaction'
OR meta.sourceWithdrawalTransactionId
OR meta.originalTransactionId with explicit reversal pairing
)
→ label: "Transaction Reversal"
show: originalTxId, reversalTxId, reason, initiatedBy
link: original ledger row / withdrawal detail when route exists
// --- Balance Correction ---
IF type == 'adjustment'
OR refType IN (
admin_finance_correction,
admin_shop_adjustment,
admin_agent_adjustment,
admin_super_agent_adjustment
)
OR AdminActionLog action == finance_balance_correction
→ IF netDelta >= 0 OR direction == credit
label: "Balance Correction — Credit"
ELSE
label: "Balance Correction — Debit"
show: reason, ticketId, initiatedBy
// --- Legacy fallback (no "Adjustment") ---
IF type == 'adjustment' OR type == 'reversal' (unclassified)
→ infer Credit/Debit from netDelta; prefix "Balance Correction —" or "Transaction Reversal"
per heuristics; log unknown pattern for follow-up
5.2 Product decisions to finalize before ship
| # | Question | Default proposal |
|---|---|---|
| D-1 | Payout gateway fail credit wallet vs admin reject withdrawal — same recordReversal shape | Refund when provider in payout-fail allowlist; Transaction Reversal when admin reject (admin_withdrawal_reject_reversal). Details §12.2 |
| D-2 | Balance Correction — Credit for USD wallet | Currently recordAdjustment is debit-only; USD credit may go via deposit path — document separately if no unified API |
| D-3 | Export CSV / notifications | Same new wording as UI list |
6. Implementation scope (summary)
Reviewable task details: §13 Implementation plan.
| Phase | Goal | Task IDs |
|---|---|---|
| 0 | Gate & constants | K260-001 → K260-003 |
| 1 | Backend classifier + API | K260-010 → K260-018 |
| 2 | Admin USD UI | K260-020 → K260-028 |
| 3 | CDN / Game / Player UI | K260-030 → K260-036 |
| 4 | Payment detail & export | K260-040 → K260-043 |
| 5 | QC & release | K260-050 → K260-052 |
Task split principle: 1 PR ≈ 1 task · diff target < 300 LOC · single concern · own test or QC snapshot.
7. UI copy reference
7.1 Badge / list column
| Label | Badge tone (suggested) |
|---|---|
| Balance Correction — Credit | Slate / blue, + amount |
| Balance Correction — Debit | Slate / amber, - amount |
| Transaction Reversal | Indigo |
| Refund | Emerald / orange (distinct from deposit) |
7.2 Detail section titles
- Correction details — Reason, Ticket ID, Initiated By, Amount, Balance before/after
- Reversal trace — Original Transaction ID (link), Reversal Transaction ID, Reason, Initiated By
- Refund details — Reason, Initiated By, Related payment transaction (link)
7.3 Microcopy
| Key | Text |
|---|---|
| Empty reason | No reason recorded. |
| System actor | System (automatic) |
| Unknown admin | Admin (ID: …) with copy |
8. Test plan (QC)
8.1 Balance Correction — Debit
- Super admin apply finance correction debit shop USD (
POST /api/admin/finance-corrections). - Open USD wallet ledger → row type
adjustment. - Expect: badge
Balance Correction — Debit; Detail has ticket, reason, admin name/id.
8.2 Balance Correction — Credit
- Shop CDN credit-adjust
direction=credit(legacy API or future credit correction). - Expect:
Balance Correction — Credit; no "Adjustment" shown.
8.3 Transaction Reversal
- Reject pending USD withdrawal (manual, non-auto payout).
- Expect: reversal row label
Transaction Reversal(not Refund). - Detail: Original Transaction ID = withdrawal
transactionId; Reversal ID = reversal row; Reason = rejection note; Initiated By = rejecting admin.
8.4 Refund
- Admin refund failed game wallet transaction (
admin_refund). - Expect: label
Refund; do not use Reversal. - Payout fail auto-refund (BTCPay cancel): label
Refund; reason frompayoutRefund.refundReason.
8.5 Regression
- Export CSV ledger uses new labels.
- No breaking filter values sent to API (filter may still use
type=adjustmentinternally; UI label differs). -
usdWalletLedgerUtils.test.tsassertions updated.
9. Quick mapping — scenario → label
| Scenario | UI Label |
|---|---|
| Finance correction debit (USD/CDN) | Balance Correction — Debit |
| Shop credit-adjust credit | Balance Correction — Credit |
| Reject USD withdrawal → reversal row | Transaction Reversal |
BTCPay / gateway payout fail → wallet credited (recordReversal, provider payout-fail) | Refund (see §12.2 — same row shape as reject reversal; classify by provider) |
| Admin refund game wallet | Refund |
Legacy row adjustment without metadata | Balance Correction — Credit/Debit (by delta) |
10. Definition of done
- All AC (§3.2) pass on staging.
- No user-facing
"Adjustment"on in-scope screens (grepkioskgaming_admin). - Unit tests + QC checklist §8 signed off.
- Product sign-off on decisions D-1, D-2 (§5.2).
12. Solution Architect Review (2026-06-20)
Architecture review — feasibility, internal spec conflicts, and recommendations before V1.2.0 implementation.
12.1 Overall verdict
| Criterion | Assessment |
|---|---|
| Problem to solve | ✅ Correct — operator confusion is real pain; USD ledger gap accurately described |
| "Presentation layer first" approach | ⚠️ Acceptable for V1.2.0 if clear precedence rules and sufficient test matrix |
| Spec ready-to-implement | ❌ Not yet — Refund vs Reversal conflict remains (§12.2), missing cross-row metadata lookup (§12.3), AC-2 vs backend USD credit gap (§12.4) |
| Estimated effort | Phase 1 (USD): 3–5 dev-days · Full 3 phase + backend enrich: 8–12 dev-days |
Recommendation: Ship Phase 1 + minimal backend enrich in V1.2.0; Phase 2–3 can ship same release if using one shared classifier (§12.5), not copying logic per file.
12.2 Serious conflict: Refund vs Transaction Reversal
Spec is internally contradictory:
| Source in doc | Conclusion |
|---|---|
| §5.1 pseudocode | All type=reversal + sourceWithdrawalTransactionId → Transaction Reversal |
| §9 mapping | "BTCPay / gateway payout fail → wallet credited" → Refund |
| AC-4 | Refund not applied to every type=reversal |
Codebase reality: payout fail (portalUsdWithdrawalPayoutService) and admin reject withdrawal (usdWalletManagementController) share the same pattern:
usdWalletService.recordReversalreferenceType: usd_wallet_transactionmetadata.sourceWithdrawalId,metadata.sourceWithdrawalTransactionId
→ Same mechanical shape; cannot classify using reversal row metadata alone.
Mandatory architecture decision (D-1 revised):
| Scenario | UI Label | Classification signal (proposed) |
|---|---|---|
| Admin reject pending withdrawal | Transaction Reversal | provider = admin_withdrawal_reject_reversal or metadata.adminRejection on source withdrawal row |
| Auto payout fail / webhook fail → USD returned to wallet | Refund | provider IN (portal_withdrawal_payout_fail_reversal, …) or metadata.payoutPhase = failed on source withdrawal |
| Chained USD movement (generic reversal) | Transaction Reversal | referenceType = usd_wallet_transaction and not in refund provider list |
Action: Update §5.1, §9 and tests §8.3/8.4 per table above. Do not use type=reversal alone as discriminator.
12.3 Metadata not on same row — enrichment needed
Detail requires Reason + Initiated By, but data is distributed:
| UI Field | Usually located at |
|---|---|
| Rejection reason | metadata.note on reversal row; or metadata.adminRejection.note on source withdrawal row |
| Initiated By (reject) | metadata.adminRejection.rejectedByAdminId on source withdrawal, not reversal row |
| Finance correction reason/ticket | metadata.reason, metadata.ticketId, metadata.adminId on adjustment row ✅ |
| Payout refund reason | PaymentTransaction.syncMetadata.payoutRefund — not on UsdWalletTransaction |
Architecture recommendation (Phase 1, not optional):
GET /admin/usd-wallets/ledger/:id(or enrich list payload) returns additional block:
{
"displayCategory": "transaction_reversal | balance_correction_debit | balance_correction_credit | refund",
"displayLabel": "Transaction Reversal",
"audit": {
"reason": "...",
"initiatedBy": { "kind": "admin|system", "adminId": "...", "displayName": "..." },
"originalTransactionId": "...",
"reversalTransactionId": "..."
},
"links": [{ "kind": "usd_ledger|payment_transaction", "id": "...", "label": "..." }]
}
-
Admin UI only renders
displayCategory/audit— no duplicated heuristics in 6+ components. -
Classifier logic one backend module:
usdLedgerDisplayCategoryService.js(or extendusdLedgerEnrichment.js) + mirror unit test; frontend thin wrapper for CDN/game wallets until backend parity.
Reason: Avoid drift between list, detail, CSV export, Telegram notify, and QC automation.
12.4 AC-2 vs backend USD Credit Correction gap
| Fact | Impact |
|---|---|
recordAdjustment debit only | All current finance-correction USD → Debit only |
No symmetric recordAdjustment credit | AC-2 "Credit correction" not testable on USD wallet in V1.2.0 unless mapped via ad-hoc recordDeposit |
WalletTransaction.type = credit | debit only (no adjustment) | WalletTransactionTable filter adjustment may be legacy/API alias — verify before changing label |
Recommendation:
- Clarify in AC: "Balance Correction — Credit (USD)" out of scope V1.2.0 or open backend ticket
recordAdjustmentCredit/ use deposit withreferenceType: admin_finance_correction. - CDN credit-adjust (
direction=credit) in scope Phase 2.
12.5 Proposed module architecture
┌─────────────────────────────────────────────────────────┐
│ Backend (source of truth for display taxonomy) │
│ usdLedgerDisplayCategoryService │
│ ← UsdWalletTransaction + linked source row │
│ ← PaymentTransaction.syncMetadata (refund) │
│ ← Admin lookup (initiatedBy displayName) │
└───────────────────────┬─────────────────────────────────┘
│ displayCategory + audit DTO
┌───────────────┼───────────────┐
▼ ▼ ▼
Admin list Admin detail CSV export
badges Reversal trace (same labels)
Shared TS (optional v1.1): @kioskgaming/ledger-display package shared by admin + future super-agent read-only views.
Anti-pattern to avoid: Extending only usdWalletLedgerUtils.ts without backend enrich → Phase 3 rewrites logic for TransactionDetails.tsx.
12.6 Scope gaps in doc
| Surface | Notes |
|---|---|
usdLedgerEnrichment.js / CSV export backend | Export may still use raw type=adjustment |
transactionBellNotifyService | Notification copy may still say "adjustment" |
AdjustBalanceModal (flagged withdrawal) | Legacy adjust-balance vs finance-corrections — wording not aligned |
adminTransactionDetailBizService | Timeline leg verb: Reversal — not mapped to display taxonomy |
| Payment transaction detail | payoutRefund banner exists; needs link to wallet Refund row |
| Filter UX | AC-1 forbids label "Adjustment" but API query param may still be type=adjustment — document "internal type vs display label" |
12.7 Classification precedence (canonical — replaces §5.1)
Mandatory order (first match wins):
- Refund —
metadata.source = admin_refund·syncMetadata.payoutRefund.refundedAt· provider inREFUND_PROVIDER_ALLOWLIST· game deposit refund flow - Transaction Reversal —
type = reversaland (provider = admin_withdrawal_reject_reversalor chained reversal not in refund allowlist) - Balance Correction — Debit —
type = adjustmentorreferenceTypeadmin correction andnetDelta < 0/ledgerType = debit - Balance Correction — Credit — admin correction reference and
netDelta > 0/ledgerType = credit - Legacy fallback — infer Credit/Debit from delta; never emit label "Adjustment"; log
unknown_ledger_display_patternmetric
REFUND_PROVIDER_ALLOWLIST (draft): portal_withdrawal_payout_fail_reversal, … (maintain in code constant + doc appendix).
12.8 Additional test matrix (required before ship)
| # | Case | Expected label |
|---|---|---|
| T-1 | Admin reject manual withdrawal | Transaction Reversal |
| T-2 | Portal auto payout fail reversal | Refund |
| T-3 | Finance correction USD debit | Balance Correction — Debit |
| T-4 | Shop CDN credit-adjust credit | Balance Correction — Credit |
| T-5 | Reversal row without resolvable source row | Fallback + telemetry; Detail does not crash |
| T-6 | Initiated By = admin id only | Resolve displayName via API enrich |
| T-7 | CSV export row T-2 | Same label as UI (not raw reversal) |
12.9 Definition of Done — additions
- D-1 revised signed off (Refund vs Reversal by provider / source context, not
typealone) - Backend
displayCategory(+ audit DTO) on USD ledger detail or documented exception with SA approval - No conflict between §5.1, §9 and AC-4
- Metric/log for unclassified legacy rows
- Cross-reference QC doc
site-documents/docs/qc/finance/(createTask-kiosk-260.mdwhen implementing)
12.10 Changelog (review)
| Date | Author | Notes |
|---|---|---|
| 2026-06-20 | Solution Architect review | §12 added — precedence, backend enrich, D-1 fix, scope gaps |
| 2026-06-20 | Solution Architect | §13 added — reviewable task breakdown plan |
13. Implementation plan — reviewable tasks
Each task = 1 PR, narrow scope, reviewer reads one concern only.
Size: XS ≈ 2–4h · S ≈ 0.5 day · M ≈ 1 day (avoid M if splittable).
13.0 Dependency graph (high level)
flowchart LR
K001[K260-001 Gate] --> K010[K260-010 Tests]
K003[K260-003 Constants] --> K010
K010 --> K011[K260-011 Classifier]
K011 --> K012[K260-012 Source resolver]
K012 --> K013[K260-013 Admin resolver]
K013 --> K014[K260-014 List API]
K013 --> K015[K260-015 Detail API]
K014 --> K020[K260-020 TS types]
K015 --> K022[K260-022 Detail sections]
K020 --> K021[K260-021 List badges]
K021 --> K028[K260-028 Cleanup utils]
Phase 0 — Gate & shared contracts
K260-001 · Product sign-off D-1 + scope AC-2 USD Credit
| Repo | site-documents (comment on ticket) |
| Size | XS |
| Depends | — |
| Deliverable | Comment/ticket: D-1 revised approved; AC-2 states USD Credit correction out of scope V1.2.0 (CDN credit in scope Phase 3) |
| Review checklist | [ ] Reversal vs Refund provider table finalized · [ ] Does not block Phase 1 dev |
PR: docs(K260-001): sign off D-1 and USD credit scope
K260-002 · API contract — displayCategory + audit DTO
| Repo | site-documents |
| Size | XS |
| Depends | K260-001 |
| Deliverable | Appendix in this doc or file Task-kiosk-260-api-contract.md: JSON schema, enum values, links[] kinds |
| Review checklist | [ ] Enum matches §12.7 · [ ] Nullable fields documented · [ ] Backward compatible (additive fields) |
PR: docs(K260-002): displayCategory API contract
K260-003 · Backend constants — provider allowlists
| Repo | kioskgaming_backend |
| Size | XS |
| Depends | K260-001 |
| Files | src/constants/usdLedgerDisplayCategories.js (new) |
| Deliverable | DISPLAY_CATEGORY_*, REFUND_PROVIDER_ALLOWLIST, REVERSAL_PROVIDER_ALLOWLIST, ADMIN_CORRECTION_REFERENCE_TYPES |
| Review checklist | [ ] Constants match §12.2 table · [ ] JSDoc cites Task-kiosk-260 · [ ] No runtime logic yet |
PR: feat(K260-003): USD ledger display category constants
Phase 1 — Backend classifier (TDD first)
K260-010 · Classifier unit tests (fixtures only)
| Repo | kioskgaming_backend |
| Size | S |
| Depends | K260-003 |
| Files | tests/unit/usdLedger/usdLedgerDisplayCategoryService.test.js, tests/fixtures/usdLedgerDisplay/*.json |
| Deliverable | Tests T-1…T-5 (§12.8) red — cover reject reversal, payout-fail reversal, finance debit, legacy adjustment, missing source row |
| Review checklist | [ ] Fixtures mirror real metadata shapes · [ ] Precedence cases explicit · [ ] Tests fail until K260-011 |
PR: test(K260-010): USD display category fixtures and cases
K260-011 · Pure classifier — resolveDisplayCategory(row, context?)
| Repo | kioskgaming_backend |
| Size | S |
| Depends | K260-010 |
| Files | src/services/display/usdLedgerDisplayCategoryService.js |
| Deliverable | Function: input plain ledger row (+ optional preloaded source row) → { displayCategory, displayLabel }; no DB |
| Review checklist | [ ] K260-010 green · [ ] Precedence §12.7 · [ ] Never returns label "Adjustment" |
PR: feat(K260-011): USD ledger display category classifier
K260-012 · Source row resolver
| Repo | kioskgaming_backend |
| Size | S |
| Depends | K260-011 |
| Files | usdLedgerDisplayCategoryService.js (+ helper loadLinkedUsdLedgerContext.js) |
| Deliverable | Batch-load source UsdWalletTransaction when referenceType=usd_wallet_transaction; attach to classifier context |
| Review checklist | [ ] N+1 avoided (batch map) · [ ] Missing source → graceful fallback · [ ] Unit test with mocked repo |
PR: feat(K260-012): load linked USD ledger row for display
K260-013 · Audit builder — reason + initiatedBy + tx ids
| Repo | kioskgaming_backend |
| Size | S |
| Depends | K260-012 |
| Files | usdLedgerDisplayCategoryService.js |
| Deliverable | buildDisplayAudit(row, context) → { reason, initiatedBy, originalTransactionId, reversalTransactionId }; system vs admin |
| Review checklist | [ ] Reject: reads adminRejection from source row · [ ] Finance correction: metadata.reason/ticketId/adminId · [ ] Empty reason → null not fake string |
PR: feat(K260-013): USD ledger display audit DTO builder
K260-014 · Admin displayName lookup (batch)
| Repo | kioskgaming_backend |
| Size | S |
| Depends | K260-013 |
| Files | usdLedgerDisplayCategoryService.js or adminDisplayNameResolver.js |
| Deliverable | Resolve adminId → { displayName, email? } batched for list enrich |
| Review checklist | [ ] Unknown admin → { kind:'admin', adminId, displayName:null } · [ ] No extra query per row in loop |
PR: feat(K260-014): batch admin display name for ledger audit
K260-015 · Wire classifier into ledger list API
| Repo | kioskgaming_backend |
| Size | S |
| Depends | K260-014 |
| Files | usdLedgerEnrichment.js or presentUsdLedgerRows path, usdWalletManagementController.js |
| Deliverable | Each row in GET /admin/usd-wallets/ledger includes displayCategory, displayLabel, audit (summary), links (optional minimal) |
| Review checklist | [ ] Additive JSON only · [ ] List perf smoke (50 rows) · [ ] Integration test 1 case |
PR: feat(K260-015): enrich USD ledger list with displayCategory
K260-016 · Wire classifier into ledger detail API
| Repo | kioskgaming_backend |
| Size | XS |
| Depends | K260-015 |
| Files | getUsdWalletLedgerById in usdWalletManagementController.js |
| Deliverable | GET /admin/usd-wallets/ledger/:id returns full audit + links (source ledger, payment tx if derivable) |
| Review checklist | [ ] Same classifier as list · [ ] 404 unchanged · [ ] OpenAPI/doc comment updated |
PR: feat(K260-016): enrich USD ledger detail with full audit DTO
K260-017 · Links builder — deep link targets
| Repo | kioskgaming_backend |
| Size | XS |
| Depends | K260-016 |
| Files | usdLedgerDisplayCategoryService.js |
| Deliverable | links[] entries with kind (usd_ledger or payment_transaction), id, label for reversal/refund rows |
| Review checklist | [ ] Reversal → link source withdrawal uuid · [ ] Payment tx link only when resolvable · [ ] No broken ids |
PR: feat(K260-017): USD ledger display deep link targets
K260-018 · CSV export — displayLabel column
| Repo | kioskgaming_backend |
| Size | S |
| Depends | K260-015 |
| Files | export path in usdWalletManagementController.js |
| Deliverable | Export adds column Display type (= displayLabel); keep raw type column for ops |
| Review checklist | [ ] T-7 §12.8 · [ ] Column order documented · [ ] No regression on existing columns |
PR: feat(K260-018): USD ledger CSV display label column
Phase 2 — Admin USD UI (consume API only)
K260-020 · TypeScript types — UsdLedgerDisplay
| Repo | kioskgaming_admin |
| Size | XS |
| Depends | K260-002 |
| Files | src/types/usdLedgerDisplay.ts (new) |
| Deliverable | Types matching API contract; map displayCategory → badge class helper types only |
| Review checklist | [ ] Matches backend enum · [ ] Optional fields correct |
PR: types(K260-020): USD ledger display DTO types
K260-021 · List badges — use displayLabel from API
| Repo | kioskgaming_admin |
| Size | S |
| Depends | K260-015, K260-020 |
| Files | usdWalletLedgerUtils.ts (badge path only), USD wallets list component |
| Deliverable | Badge text = API displayLabel; fallback to legacy only if field missing (feature flag or version check) |
| Review checklist | [ ] No user-facing "Adjustment"/mislabeled "Refund" on list · [ ] Badge colors §7.1 · [ ] Fallback logged in dev |
PR: feat(K260-021): USD wallet list badges from displayLabel
K260-022 · Detail section — Balance Correction
| Repo | kioskgaming_admin |
| Size | S |
| Depends | K260-016, K260-020 |
| Files | UsdWalletActivityView.tsx |
| Deliverable | Conditional block when displayCategory is correction_*: Reason, Ticket ID, Initiated By |
| Review checklist | [ ] Renders only for correction categories · [ ] Empty states §7.3 · [ ] No duplicate generic reasonLabel |
PR: feat(K260-022): USD detail correction audit section
K260-023 · Detail section — Transaction Reversal trace
| Repo | kioskgaming_admin |
| Size | S |
| Depends | K260-016, K260-017 |
| Files | UsdWalletActivityView.tsx |
| Deliverable | Block: Original Transaction ID, Reversal Transaction ID, Reason, Initiated By + link from links[] |
| Review checklist | [ ] AC-3 · [ ] Copy buttons on ids · [ ] T-1 manual QC steps pass |
PR: feat(K260-023): USD detail reversal trace section
K260-024 · Detail section — Refund
| Repo | kioskgaming_admin |
| Size | XS |
| Depends | K260-016 |
| Files | UsdWalletActivityView.tsx |
| Deliverable | Block when displayCategory=refund: Reason, Initiated By, related payment link |
| Review checklist | [ ] AC-4 · [ ] T-2 scenario · [ ] Distinct from Reversal UI |
PR: feat(K260-024): USD detail refund section
K260-025 · Filter labels — rename "Adjustment" filter copy
| Repo | kioskgaming_admin |
| Size | XS |
| Depends | K260-021 |
| Files | USD wallets filter UI (if any type filter exposed) |
| Deliverable | UI label "Balance corrections" / group; query param still type=adjustment |
| Review checklist | [ ] AC-1 filter copy · [ ] API param unchanged · [ ] Tooltip explains internal type |
PR: fix(K260-025): USD ledger filter display labels
K260-026 · Remove incorrect legacy labels in usdWalletLedgerUtils
| Repo | kioskgaming_admin |
| Size | XS |
| Depends | K260-021 |
| Files | usdWalletLedgerUtils.ts |
| Deliverable | Delete getLedgerUxStatus paths that map reversal→Refund, adjustment→Adjustment; keep fallback for rows without API enrich |
| Review checklist | [ ] grep no 'Refund' for reversal type in utils · [ ] Tests updated |
PR: refactor(K260-026): remove legacy USD ledger mislabels
K260-027 · Update usdWalletLedgerUtils.test.ts
| Repo | kioskgaming_admin |
| Size | XS |
| Depends | K260-026 |
| Files | usdWalletLedgerUtils.test.ts |
| Deliverable | Tests aligned with API-driven labels or explicit fallbacks |
| Review checklist | [ ] CI green · [ ] No assertions on old wrong labels |
PR: test(K260-027): update USD ledger utils tests
K260-028 · Spike cleanup — reasonLabel generic swap
| Repo | kioskgaming_admin |
| Size | XS |
| Depends | K260-022–024 |
| Files | usdWalletLedgerUtils.ts (resolveBusinessLabels adjustment/reversal branches) |
| Deliverable | reasonLabel for correction/reversal/refund defers to audit.reason when present; generic "Balance adjustment" removed |
| Review checklist | [ ] AC-5 · [ ] Deposit/withdrawal labels unchanged |
PR: fix(K260-028): use audit reason instead of generic reasonLabel
Phase 3 — CDN / Game / Player (frontend-only v1)
Phase 3 does not yet have backend CDN classifier — use thin mapper in admin, reuse constants pattern from K260-003 (copy enum to TS or shared doc). Follow-up ticket: backend parity.
K260-030 · Player wallet — badge labels only
| Repo | kioskgaming_admin |
| Size | XS |
| Depends | K260-003 (TS copy of reference types) |
| Files | WalletTransactionTable.tsx |
| Deliverable | Map referenceType + credit/debit → correction/refund labels; verify if adjustment filter still valid |
| Review checklist | [ ] No "Adjustment" badge · [ ] Filter still works · [ ] Scope: labels only |
PR: feat(K260-030): player wallet transaction display labels
K260-031 · Game wallet — Refund vs Adjustment direction
| Repo | kioskgaming_admin |
| Size | XS |
| Depends | — |
| Files | GameWalletTransactionTable.tsx |
| Deliverable | Keep admin_refund → Refund; adjustment direction → Balance Correction — Credit/Debit by amount sign or metadata |
| Review checklist | [ ] Refund modal flows unchanged · [ ] Filter option labels updated |
PR: feat(K260-031): game wallet direction display labels
K260-032 · Credit wallet activity — reference type labels
| Repo | kioskgaming_admin |
| Size | XS |
| Depends | — |
| Files | CreditWalletActivityView.tsx, referenceTypeDescriptions.ts |
| Deliverable | admin_shop_adjustment → "Balance correction (shop)"; finance correction refs aligned |
| Review checklist | [ ] Copy consistent with §7 · [ ] Descriptions not misleading |
PR: docs(K260-032): credit wallet reference label copy
K260-033 · Agent CDN utils + transfer panel labels
| Repo | kioskgaming_admin |
| Size | XS |
| Depends | — |
| Files | agentCdnTxUtils.ts, AgentWalletTransferPanel.tsx |
| Deliverable | Replace "Adjustment" option label with "Balance correction" |
| Review checklist | [ ] API value unchanged · [ ] UI only |
PR: fix(K260-033): agent CDN adjustment label copy
K260-034 · Finance correction modal — align Credit/Debit copy
| Repo | kioskgaming_admin |
| Size | XS |
| Depends | — |
| Files | FinanceCorrectionDebitModal.tsx |
| Deliverable | Title/toast use exact strings §3.1 ("Balance Correction — Debit") |
| Review checklist | [ ] Consistent with list badges · [ ] No functional change |
PR: fix(K260-034): finance correction modal wording
K260-035 · Flagged withdrawal AdjustBalanceModal copy
| Repo | kioskgaming_admin |
| Size | XS |
| Depends | — |
| Files | AdjustBalanceModal.tsx |
| Deliverable | Placeholder/help text references "Balance correction" not generic "adjustment" |
| Review checklist | [ ] Legacy API unchanged · [ ] Copy only |
PR: fix(K260-035): flagged withdrawal modal wording
K260-036 · Grep gate — no user-facing "Adjustment"
| Repo | kioskgaming_admin |
| Size | XS |
| Depends | K260-021…035 |
| Deliverable | Script or CI note: rg 'Adjustment' src/ allowlist for internal enums/API keys only |
| Review checklist | [ ] AC-1 · [ ] Allowlist documented in PR |
PR: chore(K260-036): document Adjustment string allowlist
Phase 4 — Payment detail & cross-links
K260-040 · Payment detail — link to USD Refund ledger row
| Repo | kioskgaming_admin + optional backend |
| Size | S |
| Depends | K260-016 |
| Files | TransactionDetails.tsx, maybe API to find ledger by payment tx |
| Deliverable | When syncMetadata.payoutRefund.walletTransactionId or USD reversal exists → link "View wallet refund" |
| Review checklist | [ ] Link only when id exists · [ ] No new mislabel |
PR: feat(K260-040): payment detail link to USD refund ledger
K260-041 · Cash flow timeline — displayLabel for USD legs
| Repo | kioskgaming_backend + kioskgaming_admin |
| Size | S |
| Depends | K260-015 |
| Files | adminTransactionDetailBizService.js, timeline consumer |
| Deliverable | USD legs in payment detail use displayLabel not raw Reversal/Deposit where applicable |
| Review checklist | [ ] Non-USD legs unchanged · [ ] One payment tx QC case |
PR: feat(K260-041): payment timeline USD leg display labels
K260-042 · Export payment transactions — optional display column
| Repo | kioskgaming_admin |
| Size | XS |
| Depends | K260-041 |
| Files | transactionDisplayUtils.ts export helpers |
| Deliverable | If export mentions adjustment/refund, use new wording |
| Review checklist | [ ] D-3 · [ ] Scope limited to export strings |
PR: fix(K260-042): payment export display wording
K260-043 · Notification copy audit (optional V1.2.0)
| Repo | kioskgaming_backend |
| Size | XS |
| Depends | K260-015 |
| Files | transactionBellNotifyService.js |
| Deliverable | Bell messages for finance correction use "Balance correction" not "adjustment" |
| Review checklist | [ ] Message-only · [ ] i18n N/A |
PR: fix(K260-043): bell notification wording for corrections
Phase 5 — QC & release
K260-050 · QC checklist doc
| Repo | site-documents |
| Size | XS |
| Depends | Phase 1–2 done |
| Files | docs/qc/finance/task-kiosk-260.md (new) |
| Deliverable | Executable steps T-1…T-7 (§12.8) + screenshot placeholders |
| Review checklist | [ ] Maps to AC · [ ] Staging data prerequisites listed |
PR: docs(K260-050): QC checklist Task-kiosk-260
K260-051 · Staging verification sign-off
| Repo | ticket / PR comment |
| Size | XS |
| Depends | K260-050, all dev tasks |
| Deliverable | QA comment: AC-1…AC-5 checked |
| Review checklist | [ ] §10 DoD complete |
PR: N/A — QA ticket
K260-052 · Release note snippet V1.2.0
| Repo | site-documents or release doc |
| Size | XS |
| Depends | K260-051 |
| Deliverable | Operator-facing note: terminology change table §3.1 |
| Review checklist | [ ] CS/Ops informed · [ ] No promise of USD credit correction if out of scope |
PR: docs(K260-052): V1.2.0 admin ledger wording release note
13.1 Suggested sprint allocation
| Sprint | Tasks | Goal |
|---|---|---|
| S1 | K260-001…003, K260-010…014 | Classifier + tests green locally |
| S2 | K260-015…018, K260-020…021 | API enrich + list badges |
| S3 | K260-022…028 | USD detail complete — MVP shippable |
| S4 | K260-030…036, K260-040…043 | Remaining surfaces + polish |
| S5 | K260-050…052 | QC + release |
MVP cutoff (recommended): end of S3 — USD wallet list + detail + CSV with correct wording; Phase 3 can be V1.2.1 if scope needs trimming.
13.2 PR review rubric (every task)
Reviewer confirms:
- Single concern — PR title matches one K260-xxx id
- No new classifier logic in admin (Phase 2+) — UI consumes API
- Tests — backend unit or frontend test / manual QC step cited
- Strings — do not introduce user-facing "Adjustment"
- Backward compatible — API additive; filters unchanged
13.3 Task index (quick lookup)
| ID | Title | Phase |
|---|---|---|
| K260-001 | Product sign-off D-1 | 0 |
| K260-002 | API contract doc | 0 |
| K260-003 | Backend constants | 0 |
| K260-010 | Classifier tests (red) | 1 |
| K260-011 | Pure classifier | 1 |
| K260-012 | Source row resolver | 1 |
| K260-013 | Audit DTO builder | 1 |
| K260-014 | Admin displayName batch | 1 |
| K260-015 | List API enrich | 1 |
| K260-016 | Detail API enrich | 1 |
| K260-017 | Links builder | 1 |
| K260-018 | CSV display column | 1 |
| K260-020 | TS types | 2 |
| K260-021 | List badges | 2 |
| K260-022 | Detail correction section | 2 |
| K260-023 | Detail reversal trace | 2 |
| K260-024 | Detail refund section | 2 |
| K260-025 | Filter labels | 2 |
| K260-026 | Remove legacy mislabels | 2 |
| K260-027 | Utils unit tests | 2 |
| K260-028 | reasonLabel cleanup | 2 |
| K260-030 | Player wallet labels | 3 |
| K260-031 | Game wallet labels | 3 |
| K260-032 | Credit wallet labels | 3 |
| K260-033 | Agent CDN labels | 3 |
| K260-034 | Finance modal copy | 3 |
| K260-035 | Flagged withdrawal copy | 3 |
| K260-036 | Grep gate | 3 |
| K260-040 | Payment → USD link | 4 |
| K260-041 | Timeline USD labels | 4 |
| K260-042 | Payment export wording | 4 |
| K260-043 | Bell notifications | 4 |
| K260-050 | QC doc | 5 |
| K260-051 | Staging sign-off | 5 |
| K260-052 | Release note | 5 |
Total: 32 tasks · ~22 dev-days sequential · ~8–10 dev-days with 2 devs parallel on Phase 1/2