# Step 20 — Finance / Ledger verification checklist

## Before testing
- Use a separate test database for the NEW ResellBD project; back it up first.
- Apply migrations 001–011 and seed, then run 012 exactly once.
- Confirm MySQL supports InnoDB, CHECK constraints and triggers; verify the DB account has CREATE TRIGGER permission.
- Do not use production money or real payout details during tests.

## Ledger invariants
- Every `finance_transactions.transaction_key` has exactly two ledger rows for this implementation.
- Sum of debit paisa equals sum of credit paisa for each transaction key.
- Amounts are positive integer paisa; no float is used for stored values.
- `ledger_entries` UPDATE and DELETE are rejected by DB triggers.
- A repeated idempotency key must not create a second transaction.
- Reversal posts opposite accounts in a new transaction; original entries remain untouched.

## Commission lifecycle
- Commission can only be created for a reseller customer order with status `fulfilled`.
- Admin must enter the amount and eligibility date; no rate or return window is assumed by code.
- Before `eligible_after`, release is rejected and no ledger entry is written.
- After `eligible_after`, release creates debit `commission_expense` / credit `reseller_payable` and status `eligible`.
- A repeated release does not create duplicate ledger postings.
- Reversal of an eligible commission creates debit `reseller_payable` / credit `commission_expense` and status `reversed`.
- Pending commissions are not available for payout.

## Payout lifecycle
- Reseller can request only up to posted reseller payable balance minus active payout reservations.
- Two simultaneous payout requests cannot reserve more than available balance (test concurrent sessions).
- Admin can review/approve/reject requested payouts; only approved payouts can be marked paid.
- `Mark paid` requires an external payment reference and creates debit `reseller_payable` / credit `cash_or_bank`.
- A second `Mark paid` attempt must not create another journal.
- Rejected payouts release the reservation; paid payouts do not remain reserved.
- Test a payout after a commission reversal; balance must never become negative.

## Access / privacy
- Reseller can only see their own balance, payout and ledger rows.
- Non-admin cannot open `admin-finance.php`; non-reseller cannot open `reseller-finance.php`.
- CSRF failures are rejected; user-controlled text is escaped.
- Confirm destination numbers are masked in listings and no API transfer is falsely reported as sent.

## Known scope limits
- Commission amount is entered manually because an approved rate formula/fee schedule has not been provided.
- The app treats `fulfilled` on a reseller customer order as the manual delivery-complete marker. Delivery proof and return-window policy require operational rules.
- Payment transfers are manual; the app records the reference only after the real transfer is completed.
- Do not claim a finance audit or production verification until these tests pass on the target hosting/database.
