# Payment Behaviors ## Overview Payments represent disbursements to vendors for invoices. The payment system supports multiple payment types (check, cash, debit, 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. **Testing Philosophy** - Prefer unit tests for pure business logic (calculations, validations, transformations) - Use integration tests for database interactions and cross-system flows - Use UI tests only for end-to-end happy paths that touch multiple pages - Every behavior must be user-visible; no tests for implementation details --- ## Testing Patterns ### Pattern: Grid Page Behaviors Most list pages in Integreat follow the same pattern: 1. Fetch IDs via Datomic query with filters 2. Hydrate results via `pull-many` 3. Render table with sortable columns 4. Support selection (individual / all / all-filtered) 5. Action buttons appear conditionally based on permissions and selection state **Test implications:** Validate the query generation, not the rendering. UI tests only need to verify one filter and one sort work end-to-end. ### Pattern: Permission Gates Every mutating operation checks: 1. `assert-can-see-client` — user has access to the client 2. `assert-not-locked` — payment date >= client locked-until 3. `can?` — user has the specific permission for the activity **Test implications:** Integration test each gate independently. UI tests only verify the happy path with a permitted user. ### Pattern: Check Generation Behaviors Check printing involves: 1. Validation of invoices and bank account 2. Sequential check number assignment 3. PDF generation with MICR encoding 4. S3 upload and storage 5. Transaction creation (for cash payments) **Test implications:** Integration test the full flow once. Unit test validation logic and PDF content generation. --- ## Payment List Page ### Display Behaviors | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 1.1 | It should display a table with columns: Client, Vendor, Bank Account, Check #, Status, Date, Amount, Links | UI | [ ] | | 1.2 | It should show the Client column only when viewing payments for multiple clients | Integration | [ ] | | 1.3 | It should hide the Bank Account and Date columns on smaller viewports | UI | [ ] | | 1.4 | It should show "Cleared" status as a primary-colored pill | UI | [ ] | | 1.5 | It should show "Pending" status as a secondary-colored pill | UI | [ ] | | 1.6 | It should show "Voided" status as a red-colored pill | UI | [ ] | | 1.7 | It should render check numbers as links to S3 PDFs when an s3-url exists | UI | [ ] | | 1.8 | It should show plain check number text for payments without an s3-url | UI | [ ] | | 1.9 | It should display a links dropdown showing associated invoices and transactions | UI | [ ] | | 1.10 | It should display checkboxes for bulk selection on each row | UI | [ ] | | 1.11 | It should show no check number for non-check payment types | UI | [ ] | ### Filtering Behaviors | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 2.1 | It should filter payments by vendor typeahead selection | Integration | [ ] | | 2.2 | It should filter payments by date range | Integration | [ ] | | 2.3 | It should filter payments by check number (exact match or partial text) | Integration | [ ] | | 2.4 | It should filter payments by invoice number (exact match) | Integration | [ ] | | 2.5 | It should filter payments by amount range (min/max) | Integration | [ ] | | 2.6 | It should filter payments by payment type via radio cards (All, Cash, Check, Debit) | Integration | [ ] | | 2.7 | It should support exact-match navigation to a specific payment by ID, bypassing other filters | Integration | [ ] | | 2.8 | It should filter payments by status via route (`/payments/pending`, `/payments/cleared`, `/payments/voided`) | Integration | [ ] | | 2.9 | It should apply all filters via HTMX with debounced triggers | Integration | [ ] | | 2.10 | It should combine all filters with AND logic | Integration | [ ] | | 2.11 | It should use efficient time-bounded queries for date range filtering | Integration | [ ] | | 2.12 | It should parse check number search as Long when possible, falling back to exact string match | Unit + Integration | [ ] | | 2.13 | Given multiple filters are applied, when the user changes one filter, then the table should refresh with the combined filter set | Integration | [ ] | | 2.14 | It should bypass all other filters when exact-match ID is provided | Integration | [ ] | ### Sorting Behaviors | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 3.1 | It should sort by client name ascending/descending | Integration | [ ] | | 3.2 | It should sort by vendor name ascending/descending | Integration | [ ] | | 3.3 | It should sort by bank account ascending/descending | Integration | [ ] | | 3.4 | It should sort by check number ascending/descending | Integration | [ ] | | 3.5 | It should sort by date ascending/descending | Integration | [ ] | | 3.6 | It should sort by amount ascending/descending | Integration | [ ] | | 3.7 | It should sort by status ascending/descending | Integration | [ ] | | 3.8 | Given the user clicks a column header twice, then the sort direction should toggle | Integration | [ ] | ### Pagination Behaviors | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 4.1 | It should display 25 payments per page by default | Integration | [ ] | | 4.2 | It should allow changing the per-page count | Integration | [ ] | | 4.3 | It should calculate the total visible float and total float across all matching payments, not just the current page | Unit | [ ] | ### Selection Behaviors | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 5.1 | It should allow selecting individual payments via checkboxes | UI | [ ] | | 5.2 | It should allow selecting all visible payments via a header checkbox | UI | [ ] | | 5.3 | It should allow selecting all filtered payments (up to 250) for bulk operations | Integration | [ ] | | 5.4 | Given payments are selected, when the user applies a filter, then the selection should be cleared | Integration | [ ] | ### Row Action Behaviors | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 6.1 | It should show a trash icon on each row unless the payment status is already voided | UI | [ ] | | 6.2 | It should prompt for confirmation when clicking the trash icon ("Are you sure you want to void this payment?") | UI | [ ] | | 6.3 | Given confirmation, when voiding a payment, then the row should be removed from the table with animation | UI | [ ] | | 6.4 | It should block voiding cleared check payments | Integration | [ ] | ### Float Display Behaviors | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 7.1 | It should display a "Visible in float" pill showing the sum of pending payment amounts in the current filter view | Unit | [ ] | | 7.2 | It should display a "Total in float" pill showing the sum of all pending payments for the selected client(s) | Unit | [ ] | | 7.3 | It should exclude voided payments from float calculations | Unit | [ ] | | 7.4 | It should include only pending status payments in float calculations | Unit | [ ] | --- ## Bulk Void | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 8.1 | It should show a confirmation modal with warning icon and count of payments to be voided | UI | [ ] | | 8.2 | It should support "Selected only" mode to void only checkboxed payments | UI | [ ] | | 8.3 | It should support "All selected" mode to void all payments matching current filters (up to 250) | Integration | [ ] | | 8.4 | It should require admin permission for bulk void operations | Integration | [ ] | | 8.5 | Given confirmation, when voiding, then the modal should close and a notification should show "Successfully voided X of Y payments" | Integration | [ ] | | 8.6 | It should skip payments that already have transactions and skip already-voided payments | Integration | [ ] | --- ## Check Printing | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 9.1 | It should generate physical check PDFs with MICR encoding at the bottom | Integration | [ ] | | 9.2 | It should include payee, amount in numbers and words, date, memo, bank info, and client signature image | Integration | [ ] | | 9.3 | It should generate voucher copies with full invoice details below the check | Integration | [ ] | | 9.4 | It should store check PDFs in S3 under `checks/{uuid}.pdf` | Integration | [ ] | | 9.5 | It should assign check numbers sequentially from the bank account's check number | Integration | [ ] | | 9.6 | It should increment the bank account's check number by the number of vendors paid | Integration | [ ] | | 9.7 | It should validate that the bank account has a starting check number | Integration | [ ] | | 9.8 | It should merge multiple checks into a single PDF at `merged-checks/{uuid}.pdf` | Integration | [ ] | | 9.9 | It should group invoices by vendor, creating one check per vendor per batch | Integration | [ ] | | 9.10 | It should validate that all invoices belong to the same client and the selected bank account belongs to the same client | Integration | [ ] | | 9.11 | It should reject check creation if the total amount is <= $0.00 | Integration | [ ] | --- ## ACH Payments | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 10.1 | It should create pending payments with `payment-type/debit` | Integration | [ ] | | 10.2 | It should not generate check PDFs for ACH payments | Integration | [ ] | | 10.3 | It should not create transactions for ACH payments | Integration | [ ] | --- ## Balance Credit Payments | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 11.1 | It should allow paying invoices from existing vendor credit with `payment-type/balance-credit` | Integration | [ ] | | 11.2 | It should block balance credit payments when multiple vendors are selected | Integration | [ ] | | 11.3 | It should offset positive-balance invoices against negative-balance invoices | Integration | [ ] | | 11.4 | It should create a single cleared payment for the net amount, consuming credit invoices first-in | Integration | [ ] | --- ## Cash Payments | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 12.1 | It should create payments with `payment-type/cash` automatically marked as cleared | Integration | [ ] | | 12.2 | It should create an associated transaction with POSTED status | Integration | [ ] | | 12.3 | It should use the account with numeric code 21000 for cash payment transactions | Integration | [ ] | | 12.4 | It should set the payment date to the latest invoice date | Integration | [ ] | --- ## Cross-Cutting Behaviors ### Voiding Behaviors | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 13.1 | It should allow voiding pending payments | Integration | [ ] | | 13.2 | It should allow voiding cash, debit, and balance-credit payments even when cleared | Integration | [ ] | | 13.3 | It should block voiding cleared check payments | Integration | [ ] | | 13.4 | It should set the payment amount to 0.0 when voided | Integration | [ ] | | 13.5 | It should set the payment status to voided | Integration | [ ] | | 13.6 | It should remove all invoice-payment links when voiding | Integration | [ ] | | 13.7 | It should restore invoice outstanding balances by adding back the invoice-payment amount | Integration | [ ] | | 13.8 | It should revert invoice status to unpaid when restored balance becomes non-zero | Integration | [ ] | | 13.9 | It should unlink associated transactions when voiding | Integration | [ ] | ### Permission Behaviors | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 14.1 | It should require client visibility for viewing payments | Integration | [ ] | | 14.2 | It should require client visibility for voiding individual payments | Integration | [ ] | | 14.3 | It should require admin permission for bulk voiding payments | Integration | [ ] | | 14.4 | It should allow viewing S3 check PDFs to all users who can see the payment | Integration | [ ] | ### Lock Date Behaviors | # | Behavior | Test Strategy | Status | |---|----------|---------------|--------| | 15.1 | It should block voiding payments dated before the client's locked-until date | Integration | [ ] | | 15.2 | It should check lock dates on individual void operations | Integration | [ ] | | 15.3 | It should check lock dates on bulk void operations | Integration | [ ] | | 15.4 | It should exclude locked payments from bulk void results | Integration | [ ] | | 15.5 | It should show a warning when some selected payments are locked | UI | [ ] | --- ## Test Data Requirements | Entity | Requirements | |--------|-------------| | **Clients** | Multiple clients with different locations; some with locked-until dates | | **Vendors** | With/without default accounts; with/without terms and autopay settings | | **Bank Accounts** | Check, cash, and credit types; with/without starting check numbers | | **Invoices** | Various statuses, dates, amounts; positive and negative balances | | **Payments** | Check, cash, debit, and balance-credit types; pending, cleared, and voided statuses | | **Transactions** | Linked to cash payments with POSTED status | | **Files** | Check PDFs stored in S3 | ## Existing Tests to Preserve - `test/clj/auto_ap/integration/graphql/checks.clj` — Check/payment GraphQL operations ## 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