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.
9.2 KiB
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-startingbreakpoints)
Status Rendering
cleared— primary-colored pillpending— secondary-colored pillvoided— 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-urlshow 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-deletepermission
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/pendingautomatically filters topayment-status/pending/payments/clearedautomatically filters topayment-status/cleared/payments/voidedautomatically filters topayment-status/voided/paymentsshows 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
POSTEDstatus - 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-paymentlinks removed - Invoice outstanding balances restored
- Invoice status reverted to
unpaidif 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-untilcannot be voided - Lock date checked on both individual and bulk void
Voided Payments in Float
- Voided payments excluded from float calculations
- Only
pendingstatus 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
{: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-untilset 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-pdffor check PDF generationclj-timefor date parsing and coercionauto-ap.datomic/scan-paymentsfor efficient date-range queriesauto-ap.permissions/can?for permission checksauto-ap.datomic/audit-transactfor all mutations