Files
integreat/docs/testing/behaviors/ledger.md
Bryce d627e3c5d0 refactor(all): rewrite all behavior docs in table format with checkboxes
Rewrite all 11 remaining behavior documents to match the streamlined
invoice.md format:

- dashboard.md: 250 lines, 62 behaviors
- payment.md: 260 lines, behaviors for list, void, check printing, ACH
- transaction.md: 310 lines, list, import, admin insights
- ledger.md: 519 lines, entries, P&L, balance sheet, cash flows
- company.md: 320 lines, profile, 1099s, Plaid/Yodlee, reports
- admin.md: 494 lines, clients, accounts, vendors, rules, jobs, history
- pos.md: 405 lines, sales, deposits, tenders, refunds, shifts
- search-indicators.md: 167 lines, search modal, indicators
- auth.md: 184 lines, login, logout, impersonation, sessions
- outgoing-invoice.md: 192 lines, create, line items, PDF
- legacy-spa.md: 340 lines, all legacy pages (docs only)

All documents now use:
- Testing Patterns section with reusable abstractions
- Numbered tables: # | Behavior | Test Strategy | Status
- It should... behavior descriptions
- Checkboxes [ ]/[x] for tracking implementation
- Cross-Cutting Behaviors for permissions, lock dates, etc.
- Test Data Requirements tables
- Existing Tests to Preserve sections

Total: 3,844 lines of behavior documentation across 12 subsystem docs.
2026-05-04 13:48:51 -07:00

28 KiB

Ledger Behaviors

Overview

The Ledger module is the core accounting interface of Integreat. It provides server-side rendered (HTMX) pages for viewing journal entries, creating manual journal entries, and generating financial reports (Profit & Loss, Balance Sheet, Cash Flows). All ledger pages are permission-gated and client-scoped.

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: Modal Form Behaviors

Modal forms are HTMX-driven dialogs:

  1. Opened via GET request that renders a form fragment
  2. Form submissions are POST/PUT with validation
  3. On success, the modal closes and the table updates in place
  4. On validation failure, the modal shows error messages

Test implications: Unit test validation logic. Integration test the full modal flow once. UI test only the happy path.

Pattern: Report Behaviors

Financial reports follow a consistent pattern:

  1. Form with client multi-select, date/period selectors, and toggles
  2. Run button triggers HTMX request to generate report
  3. Report data is computed from account snapshots and running balances
  4. Export button generates PDF and returns a download modal

Test implications: Unit test calculation logic. Integration test report generation with various filter combinations. UI test only one report 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 — entry 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.


Ledger Entries List

Display Behaviors

# Behavior Test Strategy Status
1.1 It should display a paginated, sortable data grid of journal entries UI [ ]
1.2 It should show the Client column only when multiple clients OR multiple locations are selected Integration [ ]
1.3 It should display the Vendor column, falling back to alternate-description when no vendor is present Integration [ ]
1.4 It should hide the Source column on the internal ledger page UI [ ]
1.5 It should hide the External ID column on the internal ledger page UI [ ]
1.6 It should truncate the External ID column to a max-width when displayed UI [ ]
1.7 It should display the Date column with formatted dates UI [ ]
1.8 It should display the Amount column formatted as currency UI [ ]
1.9 It should display Debit lines with account, location, and amount per line item UI [ ]
1.10 It should display Credit lines with account, location, and amount per line item UI [ ]
1.11 It should display a Links dropdown with links to original invoice, source file, transaction, or memo UI [ ]
1.12 It should show the page title reflecting the status filter, e.g. "Unpaid Register" or "Paid Register" UI [ ]
1.13 It should show an "Add journal entry" button on the internal ledger page UI [ ]
1.14 It should hide the "Add journal entry" button on the external ledger page UI [ ]
1.15 It should show a client selection sidebar when the user has access to multiple clients UI [ ]

Filtering Behaviors

# Behavior Test Strategy Status
2.1 It should filter entries by vendor typeahead selection Integration [ ]
2.2 It should filter entries by account typeahead selection Integration [ ]
2.3 It should filter entries by bank account via radio filter Integration [ ]
2.4 It should refresh the bank account filter when the client selection changes Integration [ ]
2.5 It should filter entries by date range Integration [ ]
2.6 It should filter entries by invoice number text search Integration [ ]
2.7 It should filter entries by account code range (gte/lte inputs) Integration [ ]
2.8 It should filter entries by amount range (gte/lte inputs) Integration [ ]
2.9 It should filter to unbalanced entries when "Show unbalanced" is checked Integration [ ]
2.10 It should support exact-match navigation to a specific entry by ID, bypassing other filters Integration [ ]
2.11 It should clear the exact match ID pill when clicked UI [ ]
2.12 Given multiple filters are applied, when the user changes one filter, then the table should refresh with the combined filter set Integration [ ]

Sorting Behaviors

# Behavior Test Strategy Status
3.1 It should sort by Client ascending/descending Integration [ ]
3.2 It should sort by Vendor ascending/descending Integration [ ]
3.3 It should sort by Source ascending/descending Integration [ ]
3.4 It should sort by External ID 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 Account ascending/descending Integration [ ]
3.8 It should default to Date ascending Integration [ ]
3.9 Given sorting by Vendor, then rows should be grouped with break headers Integration [ ]
3.10 Given sorting by Source, then rows should be grouped with break headers Integration [ ]
3.11 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 entries per page by default Integration [ ]
4.2 It should allow changing the per-page count Integration [ ]
4.3 It should show pagination controls at the bottom of the table UI [ ]

Row Action Behaviors

# Behavior Test Strategy Status
5.1 It should show a void button for unpaid invoices when the user has :delete :invoice permission UI [ ]
5.2 It should show an edit button for unpaid and paid invoices when the user has :edit :invoice permission UI [ ]
5.3 It should show an unvoid button for voided invoices when the user has :edit :invoice permission UI [ ]
5.4 It should show a trash icon with confirmation for delete operations UI [ ]

CSV Export Behaviors

# Behavior Test Strategy Status
6.1 It should export all matching entries with line-item-level rows Integration [ ]
6.2 It should include columns: ID, Client, Vendor, Source, External ID, Date, Amount, Account, Debit, Credit Integration [ ]

New Journal Entry

Modal Form Behaviors

# Behavior Test Strategy Status
7.1 It should open as a modal dialog 750px wide UI [ ]
7.2 It should show a client typeahead pre-filled if a client is already selected on the parent page UI [ ]
7.3 It should show a date input defaulting to today in MM/DD/YYYY format UI [ ]
7.4 It should show a vendor typeahead disabled when editing an existing entry UI [ ]
7.5 It should show a total amount input requiring a value of at least $0.01 Unit + Integration [ ]
7.6 It should show an optional memo text input UI [ ]
7.7 It should display a line items grid with Account, Location, Debit, and Credit columns UI [ ]

Line Item Behaviors

# Behavior Test Strategy Status
8.1 It should allow account typeahead search scoped to the selected client Integration [ ]
8.2 It should update the location dropdown based on the selected account's required location Integration [ ]
8.3 It should lock the location dropdown to a fixed location when the account requires it Integration [ ]
8.4 It should show all client locations when the account has no location restriction Integration [ ]
8.5 It should add new line item rows via HTMX request UI [ ]
8.6 It should allow removing line item rows with an X button UI [ ]

Validation Behaviors

# Behavior Test Strategy Status
9.1 It should require a client Unit + Integration [ ]
9.2 It should require a valid date Unit + Integration [ ]
9.3 It should require a vendor Unit + Integration [ ]
9.4 It should require an amount of at least $0.01 Unit + Integration [ ]
9.5 It should require each line item to have an allowed account Unit + Integration [ ]
9.6 It should require each line item to have a location belonging to the account Unit + Integration [ ]
9.7 It should validate that debits sum to the total amount Unit + Integration [ ]
9.8 It should validate that credits sum to the total amount Unit + Integration [ ]
9.9 It should validate that debits and credits each equal the journal entry amount Unit + Integration [ ]
9.10 It should block saving when the entry date is on or before the client's locked-until date Integration [ ]

Save Behaviors

# Behavior Test Strategy Status
10.1 It should generate an external ID in the format manual-<uuid> Unit [ ]
10.2 It should update the client's ledger-last-change timestamp Integration [ ]
10.3 Given a new entry is saved successfully, then it should prepend the new row to the table and close the modal UI [ ]
10.4 Given an existing entry is saved successfully, then it should replace the existing row in the table and close the modal UI [ ]

External Import

Clipboard Paste Behaviors

# Behavior Test Strategy Status
11.1 It should allow clicking a "Load from clipboard" button UI [ ]
11.2 It should read TSV data from the browser clipboard UI [ ]
11.3 It should parse tab-separated values with columns: Id, Client, Source, Vendor, Date, Account Code, Location, Debit, Credit Integration [ ]

Parse Validation Behaviors

# Behavior Test Strategy Status
12.1 It should validate that all rows have required fields Integration [ ]
12.2 It should validate that dates are parseable Unit + Integration [ ]
12.3 It should validate that account codes are numeric or bank account strings Unit + Integration [ ]
12.4 It should validate that locations are 1-2 characters Unit + Integration [ ]
12.5 It should validate that debits and credits are valid money amounts Unit + Integration [ ]

Import Validation Behaviors

# Behavior Test Strategy Status
13.1 It should validate that the client code exists Integration [ ]
13.2 It should validate that the vendor exists, creating a hidden vendor if missing Integration [ ]
13.3 It should block entries for dates when the client is locked Integration [ ]
13.4 It should validate that debits and credits balance per entry Unit + Integration [ ]
13.5 It should warn when an entry totals $0.00 Unit + Integration [ ]
13.6 It should validate that the location belongs to the client Integration [ ]
13.7 It should validate that the account code exists Integration [ ]
13.8 It should validate that bank account codes belong to the client Integration [ ]
13.9 It should validate that account location requirements are satisfied Integration [ ]

Import Result Behaviors

# Behavior Test Strategy Status
14.1 It should import successful entries Integration [ ]
14.2 It should ignore entries with warnings, removing them if they previously existed Integration [ ]
14.3 It should block import and show error counts when entries have errors Integration [ ]
14.4 It should retract existing entries by external ID before importing Integration [ ]
14.5 It should index imported entries in Solr asynchronously Integration [ ]

P&L Report

Form Behaviors

# Behavior Test Strategy Status
15.1 It should show a customer multi-select typeahead with a max of 20 selections UI [ ]
15.2 It should default to the first 5 customers when "all" is selected Integration [ ]
15.3 It should show a periods dropdown defaulting to year-to-date UI [ ]
15.4 It should show a "Column per location" toggle UI [ ]
15.5 It should show an "Include deltas" toggle UI [ ]
15.6 It should trigger report generation via HTMX PUT on the Run button UI [ ]
15.7 It should show an Export PDF button UI [ ]

Report Generation Behaviors

# Behavior Test Strategy Status
16.1 It should compute running balances before generating the report Integration [ ]
16.2 It should query detailed account snapshots for each client and period end date Integration [ ]
16.3 It should calculate amounts as debits minus credits for assets, dividends, and expenses Unit [ ]
16.4 It should calculate amounts as credits minus debits for liabilities, equity, and revenue Unit [ ]
16.5 It should group data by client, location, and period Integration [ ]

Report Output Behaviors

# Behavior Test Strategy Status
17.1 It should display a summary table with Sales, COGS, Payroll, Gross Profits, Overhead, and Net Income UI [ ]
17.2 It should display a detail table with account-level breakdown within each category UI [ ]
17.3 It should calculate percent of sales for each row Unit [ ]
17.4 It should show deltas between periods when enabled UI [ ]
17.5 It should show each location as separate columns when column-per-location mode is enabled UI [ ]

Warning Behaviors

# Behavior Test Strategy Status
18.1 It should warn when more than 20 clients are selected and truncate to 20 Integration [ ]
18.2 It should warn about unresolved ledger entries with missing numeric codes Integration [ ]
18.3 It should show sample links to admin history for invalid entries when the user has :view :history permission Integration [ ]

PDF Export Behaviors

# Behavior Test Strategy Status
19.1 It should generate a PDF with Calibri Light font at 6pt Integration [ ]
19.2 It should upload the PDF to S3 at reports/profit-and-loss/<uuid>/<name>.pdf Integration [ ]
19.3 It should persist a report record in Datomic Integration [ ]
19.4 It should return a modal with a download link UI [ ]

Balance Sheet

Form Behaviors

# Behavior Test Strategy Status
20.1 It should show a customer multi-select typeahead with a max of 20 selections UI [ ]
20.2 It should default to the first 5 customers when "all" is selected Integration [ ]
20.3 It should show a date dropdown defaulting to today UI [ ]
20.4 It should show an "Include deltas" toggle UI [ ]
20.5 It should trigger report generation via HTMX GET on the Run button UI [ ]
20.6 It should show an Export PDF button UI [ ]

Report Generation Behaviors

# Behavior Test Strategy Status
21.1 It should compute running balances before generating the report Integration [ ]
21.2 It should query account snapshots as of each selected date Integration [ ]
21.3 It should group accounts into Assets, Liabilities, and Owner's Equity Integration [ ]
21.4 It should include Retained Earnings as net income across all P&L categories Unit [ ]

Report Output Behaviors

# Behavior Test Strategy Status
22.1 It should display an Assets section with account detail and subtotal UI [ ]
22.2 It should display a Liabilities section with account detail and subtotal UI [ ]
22.3 It should display an Owner's Equity section with account detail and subtotal UI [ ]
22.4 It should display a Retained Earnings line UI [ ]
22.5 It should show delta columns between periods when enabled and multiple dates are selected UI [ ]

Warning Behaviors

# Behavior Test Strategy Status
23.1 It should warn when more than 20 clients are selected Integration [ ]
23.2 It should warn about unresolved ledger entries Integration [ ]

PDF Export Behaviors

# Behavior Test Strategy Status
24.1 It should generate a PDF and upload to S3 at reports/balance-sheet/<uuid>/<name>.pdf Integration [ ]
24.2 It should persist a report record in Datomic Integration [ ]
24.3 It should return a modal with a download link UI [ ]

Cash Flows

Form Behaviors

# Behavior Test Strategy Status
25.1 It should show a customer multi-select typeahead with a max of 20 selections UI [ ]
25.2 It should default to the first 5 customers when "all" is selected Integration [ ]
25.3 It should show a periods dropdown defaulting to year-to-date UI [ ]
25.4 It should trigger report generation via HTMX PUT on the Run button UI [ ]
25.5 It should show an Export PDF button UI [ ]

Report Generation Behaviors

# Behavior Test Strategy Status
26.1 It should query account snapshots as of period end plus one day Integration [ ]
26.2 It should group accounts into Operating Activities, Investment Activities, Financing Activities, and Cash Integration [ ]
26.3 It should calculate cash flow effect by adding or subtracting based on account code ranges Unit [ ]

Report Output Behaviors

# Behavior Test Strategy Status
27.1 It should display Net Income as the starting point UI [ ]
27.2 It should display Operating Activities detail with increases, decreases, and cash impact UI [ ]
27.3 It should display Investment Activities detail UI [ ]
27.4 It should display Financing Activities detail UI [ ]
27.5 It should display Change in Cash and Cash Equivalents total UI [ ]
27.6 It should display Bank Accounts / Cash detail UI [ ]

Warning Behaviors

# Behavior Test Strategy Status
28.1 It should warn when more than 20 clients are selected Integration [ ]
28.2 It should warn about unresolved ledger entries Integration [ ]

PDF Export Behaviors

# Behavior Test Strategy Status
29.1 It should generate a PDF and upload to S3 at reports/cash-flows/<uuid>/<name>.pdf Integration [ ]
29.2 It should persist a report record in Datomic Integration [ ]
29.3 It should return a modal with a download link UI [ ]

Investigation

Modal Behaviors

# Behavior Test Strategy Status
30.1 It should open as a modal dialog from report table cell clicks UI [ ]
30.2 It should filter ledger entries by the clicked cell's filters: account code range, client, location, and date range Integration [ ]
30.3 It should display a raw table without checkboxes UI [ ]
30.4 It should constrain the modal to a max height of 600px with scrollable content UI [ ]

Table Behaviors

# Behavior Test Strategy Status
31.1 It should use the same query schema as the main ledger list Integration [ ]
31.2 It should support sorting and pagination Integration [ ]
31.3 It should not push URL state on filter or sort changes Integration [ ]

Cross-Cutting Behaviors

Report Generation Behaviors

# Behavior Test Strategy Status
32.1 It should call upsert-running-balance before querying to ensure cached balances are current Integration [ ]
32.2 It should use detailed-account-snapshot Datomic query for raw report data Integration [ ]
32.3 It should build account lookups per-client via build-account-lookup Integration [ ]
32.4 It should skip entries without numeric codes and warn about unresolved entries Integration [ ]

Export Behaviors

# Behavior Test Strategy Status
33.1 It should support PDF export via clj-pdf for all three reports Integration [ ]
33.2 It should use Calibri Light 6pt font on letter size for all PDF exports Integration [ ]
33.3 It should upload PDFs to the S3 data bucket with a UUID-based key Integration [ ]
33.4 It should persist report metadata to Datomic with name, client, key, URL, creator, and created timestamp Integration [ ]
33.5 It should return a modal with an S3 download link after export UI [ ]

Filtering and Sorting Behaviors

# Behavior Test Strategy Status
34.1 It should apply ledger list filters via HTMX on change with a 500ms debounce Integration [ ]
34.2 It should apply hot filters via HTMX on keyup with a 1000ms debounce Integration [ ]
34.3 It should refresh the bank account filter when client selection changes Integration [ ]
34.4 It should support multiple sort keys with ascending and descending direction Integration [ ]
34.5 It should default to date ascending sort Integration [ ]
34.6 It should bypass all other filters when an exact match ID filter is active Integration [ ]

Permission Behaviors

# Behavior Test Strategy Status
35.1 It should require an authenticated user for all ledger pages Integration [ ]
35.2 It should require :read :ledger permission for the main ledger page Integration [ ]
35.3 It should require :edit :ledger permission for new and edit journal entry Integration [ ]
35.4 It should require :import :ledger permission plus admin assertion for external import Integration [ ]
35.5 It should require :read :profit-and-loss permission for the P&L report Integration [ ]
35.6 It should require :read :balance-sheet permission for the balance sheet Integration [ ]
35.7 It should require :read :cash-flows permission for the cash flows report Integration [ ]
35.8 It should restrict users to clients they have permission for via assert-can-see-client Integration [ ]
35.9 It should require :delete :invoice permission for invoice void actions Integration [ ]
35.10 It should require :edit :invoice permission for invoice edit and unvoid actions Integration [ ]

Empty State Behaviors

# Behavior Test Strategy Status
36.1 It should show an empty table with pagination showing 0 results when no entries exist UI [ ]
36.2 It should show an empty table with filter pills remaining when filters match nothing UI [ ]
36.3 It should show an empty report form with no table rendered when report data is empty UI [ ]

Data Locking Behaviors

# Behavior Test Strategy Status
37.1 It should block creating journal entries for dates on or before the client's locked-until date Integration [ ]
37.2 It should reject external import entries for locked dates Integration [ ]

Unbalanced Entry Behaviors

# Behavior Test Strategy Status
38.1 It should compute debit and credit sums per entry for the "Show unbalanced" filter Unit [ ]
38.2 It should display unbalanced entries in the normal view without filtering UI [ ]

Account Location Behaviors

# Behavior Test Strategy Status
39.1 It should reject locations other than the fixed location for accounts with fixed locations Integration [ ]
39.2 It should reject "A" (all) location for accounts without location restrictions Integration [ ]
39.3 It should validate account location requirements on both the frontend location select and backend schema Integration [ ]

Running Balance Cache Behaviors

# Behavior Test Strategy Status
40.1 It should recompute balances for dirty line items via refresh-running-balance-cache Integration [ ]
40.2 It should mark a changed entry's line items and subsequent entries as dirty Integration [ ]
40.3 It should skip recomputation for non-dirty entries Integration [ ]

Test Data Requirements

Entity Requirements
Clients At least 2 clients with different locations; at least 1 client with a locked-until date in the past
Accounts Asset accounts (11000-11999); Liability accounts (20000-28999); Equity accounts (30000-39999); Revenue accounts (40000-49999); Expense accounts (50000-98999); accounts with fixed locations; accounts without location restrictions
Vendors Existing vendors; hidden vendors (auto-created on import)
Journal Entries Balanced entries (debits = credits); unbalanced entries (debits ≠ credits); entries with multiple line items; entries linked to invoices; entries with external IDs; entries across multiple dates and locations
Bank Accounts Bank accounts linked to clients

Existing Tests to Preserve

  • test/clj/auto_ap/ssr/ledger/ledger_test.clj — Ledger page rendering and grid behaviors
  • test/clj/auto_ap/integration/routes/ledger_test.clj — Ledger routes and mutations
  • test/clj/auto_ap/ledger/reports_test.clj — Report generation and calculation logic

Dependencies

  • auto-ap.ssr.ledger.common — Shared grid page config, query schema, filtering
  • auto-ap.ledger.reports — Report aggregation and formatting logic
  • auto-ap.ledger — Running balance cache, account lookups
  • auto-ap.datomic.accounts — Account querying and clientization
  • auto-ap.permissions — Permission checks and middleware
  • auto-ap.ssr.grid-page-helper — Generic data grid behaviors
  • auto-ap.ssr.components — UI components (typeahead, date inputs, buttons)
  • auto-ap.ssr.form-cursor — Form state management
  • clj-pdf — PDF generation
  • amazonica.aws.s3 — S3 upload for exports
  • datomic.api — Database queries and transactions