docs: add comprehensive test behavior documentation for all pages

Add behavior documentation covering all SSR and legacy SPA pages:
- Testing strategy and type definitions (unit/integration/UI)
- Dashboard, Invoice, Payment, Transaction, Ledger pages
- Company/Settings, POS, Admin, Search, Auth pages
- Legacy SPA behavior docs (no UI tests until migrated)
- Edge cases, test data requirements, and dependencies per subsystem

Total: 3,600+ lines of behavior documentation to guide test authorship.
This commit is contained in:
2026-05-04 12:15:20 -07:00
parent 2993da5c82
commit b499d460f3
13 changed files with 3785 additions and 0 deletions

View File

@@ -0,0 +1,236 @@
# Payment Behaviors
## Overview
Payments represent disbursements to vendors for invoices. The payment system supports multiple payment types (check, cash, debit, credit, balance credit) and tracks payments through statuses (pending, cleared, voided). Payments are created through the invoice payment flow and managed through a searchable grid interface.
## Routes & Pages
| Route | Handler | Purpose |
|-------|---------|---------|
| `GET /payments` | `::all-page` | List all payments |
| `GET /payments/pending` | `::pending-page` | List pending payments only |
| `GET /payments/cleared` | `::cleared-page` | List cleared payments only |
| `GET /payments/voided` | `::voided-page` | List voided payments only |
| `GET /payments/table` | `::table` | HTMX table fragment (sort/filter/paginate) |
| `DELETE /payments/:id` | `::delete` | Void a single payment |
| `GET /payments/bulk-delete` | `::bulk-delete` | Show bulk void confirmation modal |
| `DELETE /payments/bulk-delete` | `::bulk-delete-confirm` | Execute bulk void |
## Behaviors by Page
### Payment List
**Grid Display**
- Displays payments in a sortable, filterable table with checkboxes for bulk selection
- Columns: Client (code), Vendor (name), Bank Account (with icon), Check #, Status, Date, Amount, Links
- Client column hidden when viewing a single client's payments
- Bank account and date columns hidden on smaller viewports (`show-starting` breakpoints)
**Status Rendering**
- `cleared` — primary-colored pill
- `pending` — secondary-colored pill
- `voided` — red-colored pill
**Check Number Links**
- When a payment has an `s3-url`, the check number renders as a link opening the PDF in a new tab
- Payments without `s3-url` show plain check number text
**Links Column**
- Shows dropdown with links to associated invoices ("Inv. #{number}")
- Shows link to associated transaction if one exists ("Transaction")
**Action Buttons**
- "Visible in float" pill — sum of pending payment amounts in current filter view
- "Total in float" pill — sum of all pending payments for the selected client(s)
- "Void selected" button (red) — only visible to users with `:payment :bulk-delete` permission
**Row Actions**
- Trash icon appears on each row unless payment status is already `voided`
- Clicking trash prompts for confirmation ("Are you sure you want to void this payment?")
- After void, row is removed from the table with animation
### Payment Detail
Individual payments are viewed through the invoice or transaction detail pages. The payment list does not have a dedicated detail page — payments are managed inline from the grid.
### Bulk Delete (Void)
**Dialog**
- Triggered by clicking "Void selected" with one or more payments checked
- Shows warning icon with count of payments to be voided
- Supports two selection modes:
- **Selected only**: voids only checkboxed payments
- **All selected**: voids all payments matching current filters (up to 250)
**Confirmation**
- Clicking "Void payments" in modal executes the bulk void
- Modal closes and notification shows: "Successfully voided X of Y payments."
## Cross-Cutting Behaviors
### Filtering, Sorting, Pagination
**Filters (all apply via HTMX with debounced triggers)**
- **Vendor**: Typeahead search by vendor name
- **Date Range**: Start and end dates
- **Check #**: Exact match or partial text search (parsed as Long if possible)
- **Invoice #**: Exact match on invoice number
- **Amount Range**: Greater-than-or-equal and less-than-or-equal inputs
- **Payment Type**: Radio cards — All, Cash, Check, Debit
- **Exact Match ID**: Filter to a specific payment by Datomic entity ID
**Sortable Columns**
- Client, Vendor, Bank account, Check number, Date, Amount, Status
**Pagination**
- Default 25 per page
- Standard start/per-page query parameters
**Route-Based Status Filtering**
- `/payments/pending` automatically filters to `payment-status/pending`
- `/payments/cleared` automatically filters to `payment-status/cleared`
- `/payments/voided` automatically filters to `payment-status/voided`
- `/payments` shows all statuses
### Check Printing
**PDF Generation**
- Generates physical check PDFs with MICR encoding at bottom
- Includes: payee, amount in numbers and words, date, memo, bank info, client signature image if available
- Also generates voucher copies with full invoice details below the check
- PDFs stored in S3 under `checks/{uuid}.pdf`
**Check Numbering**
- Check numbers assigned sequentially from `bank-account/check-number`
- Bank account's check number incremented by number of vendors paid
- Validated: bank account must have a starting check number
**Batch Processing**
- Multiple checks can be merged into single PDF at `merged-checks/{uuid}.pdf`
- Invoices grouped by vendor — one check per vendor per batch
**Validation**
- All invoices must belong to same client
- Selected bank account must belong to same client
- Total amount must be greater than $0.00
### ACH Payments
ACH (debit) payments are created with `payment-type/debit`. They create pending payments without generating PDFs or transactions.
### Balance Credits
Balance credit payments (`payment-type/balance-credit`) allow paying invoices from existing vendor credit:
- Only one vendor at a time
- Positive-balance invoices (credits) offset negative-balance invoices (debts)
- Creates a single cleared payment for the net amount
- Credit invoices are consumed first-in based on outstanding balance
### Cash Payments
Cash payments (`payment-type/cash`):
- Automatically marked as `cleared`
- Creates an associated transaction with `POSTED` status
- Transaction uses account with numeric code 21000 (default set)
- Payment date set to latest invoice date
### Voiding Payments
**Individual Void Rules**
- Pending payments can always be voided
- Cash, debit, and balance-credit payments can be voided even when cleared
- Cleared checks cannot be voided
- Cannot void if client is locked (payment date before `client/locked-until`)
**Void Side Effects**
- Payment amount set to `0.0`
- Payment status set to `voided`
- All `invoice-payment` links removed
- Invoice outstanding balances restored
- Invoice status reverted to `unpaid` if balance becomes non-zero
- Associated transaction unlinked (if present)
**Bulk Void Rules**
- Admin permission required (`:payment :bulk-delete`)
- Skips payments that already have transactions (cannot void checks with transactions)
- Skips already-voided payments
- Respects client lock dates (only voids payments with date >= `locked-until`)
- Returns count actually voided vs. requested
### Permissions
- **View payments**: User must have visibility to the payment's client
- **Void individual payment**: User must be able to see the client
- **Bulk void payments**: Admin permission required
- **View S3 check PDFs**: Available to all users who can see the payment
## Edge Cases
**Zero-Amount Payments**
- Check/debit/cash/balance-credit creation rejects if total amount <= $0.00
- Void always sets amount to 0.0
**Locked Clients**
- Payments dated before `client/locked-until` cannot be voided
- Lock date checked on both individual and bulk void
**Voided Payments in Float**
- Voided payments excluded from float calculations
- Only `pending` status payments count toward "Visible in float" and "Total in float"
**Check Numbers**
- Non-check payment types have no check number
- Check number search attempts Long parsing; falls back to exact string match
**Invoice-Payment Restoration**
- When voiding, invoice balance is restored by adding back the `invoice-payment/amount`
- If restored balance is zero, invoice status remains as-is; otherwise reverts to `unpaid`
**Filter Combinations**
- All filters are AND-combined
- Date range uses Datomic scan-payments function for efficient time-bounded queries
- Exact-match-id bypasses all other filters
## Test Data Requirements
**Basic Payment**
```clojure
{:db/id "check-id"
:payment/check-number 1000
:payment/bank-account "bank-id"
:payment/client "client-id"
:payment/type :payment-type/check
:payment/amount 123.50
:payment/paid-to "Someone"
:payment/status :payment-status/pending
:payment/date #inst "2022-01-01"}
```
**Required Entities for Check Printing**
- Client with code and at least one bank account
- Bank account with `bank-account/check-number` (for checks) or without (for cash/debit)
- Vendor with name and default account
- Invoice with outstanding balance, vendor, client, and expense accounts
- Account with numeric code 21000 (for cash payment transactions)
**Locked Client Scenario**
- Client with `client/locked-until` set to future date
- Payment with date before lock date should be unvoidable
**Multi-Vendor Invoice Set**
- Multiple invoices from different vendors
- Should group by vendor for check printing
- Should reject for balance-credit (single vendor only)
## Dependencies
- Datomic for payment/invoice/transaction persistence
- S3 for check PDF storage and retrieval
- Solr for index updates after payment mutations
- HTMX + Alpine.js for interactive grid behavior
- `clj-pdf` for check PDF generation
- `clj-time` for date parsing and coercion
- `auto-ap.datomic/scan-payments` for efficient date-range queries
- `auto-ap.permissions/can?` for permission checks
- `auto-ap.datomic/audit-transact` for all mutations