Files
integreat/docs/testing/behaviors/payment.md
Bryce b499d460f3 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.
2026-05-04 12:15:20 -07:00

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-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

{: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