Files
integreat/docs/testing/behaviors/ledger.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

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

Routes & Pages

Route Method Handler Description
/ledger GET ::all-page Main ledger entries list (internal)
/ledger/external-new GET ::external-page External ledger entries list
/ledger/new GET ::new Modal form for new journal entry
/ledger/new POST ::new-submit Submit new journal entry
/ledger/new/location-select GET ::location-select Location dropdown for line item
/ledger/new/account-typeahead GET ::account-typeahead Account search for line item
/ledger/new/line-item GET ::new-line-item Add new line item row
/ledger/external-import-new GET ::external-import-page External TSV import page
/ledger/external-import-new/parse POST ::external-import-parse Parse clipboard TSV data
/ledger/external-import-new/import POST ::external-import-import Import validated entries
/ledger/investigate GET ::investigate Investigation modal for report drill-down
/ledger/investigate/results GET ::investigate-results Investigation results table
/ledger/table GET ::table HTMX table fragment for ledger entries
/ledger/csv GET ::csv CSV export of ledger entries
/ledger/bank-account-filter GET ::bank-account-filter Bank account filter widget
/ledger/reports/profit-and-loss GET ::profit-and-loss P&L report page
/ledger/reports/profit-and-loss/run PUT ::run-profit-and-loss Run P&L report
/ledger/reports/profit-and-loss/export POST ::export-profit-and-loss Export P&L to PDF
/ledger/reports/balance-sheet GET ::balance-sheet Balance sheet page
/ledger/reports/balance-sheet/run GET ::run-balance-sheet Run balance sheet
/ledger/reports/balance-sheet/export POST ::export-balance-sheet Export balance sheet to PDF
/ledger/reports/cash-flows GET ::cash-flows Cash flows page
/ledger/reports/cash-flows/run PUT ::run-cash-flows Run cash flows report
/ledger/reports/cash-flows/export POST ::export-cash-flows Export cash flows to PDF

Behaviors by Page

Ledger Entries List

Page load

  • Displays a paginated, sortable data grid of journal entries
  • Shows client selection sidebar if user has access to multiple clients
  • Title reflects status filter: e.g. "Unpaid Register" or "Paid Register"
  • Includes an "Add journal entry" button (hidden on external ledger page)

Columns displayed

  • Client (hidden if only 1 client with 1 location)
  • Vendor (falls back to alternate-description if no vendor)
  • Source (hidden on internal ledger)
  • External ID (hidden on internal ledger, truncated to max-width)
  • Date
  • Amount (formatted as currency)
  • Debit lines (account + location + amount)
  • Credit lines (account + location + amount)
  • Links dropdown (links to original invoice, source file, transaction, or memo)

Row actions

  • Void button for unpaid invoices (requires :delete :invoice permission)
  • Edit button for unpaid/paid invoices (requires :edit :invoice permission)
  • Unvoid button for voided invoices (requires :edit :invoice permission)
  • Trash icon with confirmation for delete operations

Sorting

  • Available sorts: Client, Vendor, Source, External ID, Date, Amount, Account
  • Default sort: Date ascending
  • Sorting by Vendor or Source groups rows with break headers

Filtering (left sidebar)

  • Vendor typeahead search
  • Account typeahead search
  • Bank account radio filter (refreshes on client change)
  • Date range picker
  • Invoice number text search
  • Account code range (gte/lte inputs)
  • Amount range (gte/lte inputs)
  • "Show unbalanced" checkbox (filters to entries where debits ≠ credits)
  • Exact match ID pill (clears on click)

Pagination

  • Default 25 entries per page
  • Configurable per-page
  • Pagination controls at bottom

CSV Export

  • Exports all matching entries with line-item-level rows
  • Columns: ID, Client, Vendor, Source, External ID, Date, Amount, Account, Debit, Credit

New Journal Entry

Modal form

  • Opens as modal dialog (750px wide)
  • Client typeahead (pre-filled if client already selected on parent page)
  • Date input (default today, format MM/DD/YYYY)
  • Vendor typeahead (disabled if editing existing entry)
  • Total amount input (must be ≥ $0.01)
  • Memo text input (optional)
  • Line items grid with columns: Account, Location, Debit, Credit

Line item behaviors

  • Account typeahead searches accounts scoped to selected client
  • Location dropdown updates based on selected account's required location
  • If account has a fixed location, dropdown is locked to that location
  • If account has no location restriction, shows client's locations
  • New line item rows added via HTMX request
  • Line items can be removed with X button

Validation

  • Client is required
  • Date is required and must be valid
  • Vendor is required
  • Amount must be ≥ $0.01
  • Each line item must have an allowed account
  • Each line item must have a location belonging to the account
  • Debits must sum to total amount
  • Credits must sum to total amount
  • Debits and credits must each equal the journal entry amount

On save

  • Generates external ID: manual-<uuid>
  • Updates client's ledger-last-change timestamp
  • On POST: prepends new row to table, triggers modal close
  • On PUT: replaces existing row in table, triggers modal close

External Import

Clipboard paste

  • User clicks "Load from clipboard" button
  • Reads TSV data from browser clipboard
  • Parses tab-separated values with columns: Id, Client, Source, Vendor, Date, Account Code, Location, Debit, Credit

Parse validation

  • Validates all rows have required fields
  • Dates must be parseable
  • Account codes must be numeric or bank account strings
  • Locations must be 1-2 characters
  • Debits/Credits must be valid money amounts

Import validation

  • Client code must exist
  • Vendor must exist (creates hidden vendor if missing)
  • Client must not be locked for the entry date
  • Debits and credits must balance per entry
  • Entry cannot total $0.00 (warning)
  • Location must belong to client
  • Account code must exist
  • Bank account code must belong to client
  • Account location requirements must be satisfied

Import results

  • Successful entries are imported
  • Entries with warnings are ignored (removed if previously existed)
  • Entries with errors block import and show error counts
  • Retracts existing entries by external ID before importing
  • Indexes imported entries in Solr asynchronously

P&L Report

Form

  • Customer multi-select typeahead (max 20, defaults to first 5 if "all")
  • Periods dropdown (default: year-to-date)
  • "Column per location" toggle
  • "Include deltas" toggle
  • Run button (HTMX PUT)
  • Export PDF button

Report generation

  • Computes running balances before generating
  • Queries detailed account snapshots for each client/period end date
  • Amounts calculated as: debits - credits for assets/dividends/expenses, credits - debits for others
  • Groups data by client, location, and period

Report output

  • Summary table: Sales, COGS, Payroll, Gross Profits, Overhead, Net Income
  • Detail table: Account-level breakdown within each category
  • Percent of sales calculated for each row
  • Deltas shown between periods if enabled
  • Column-per-location mode shows each location as separate columns

Warnings

  • Warns if >20 clients selected (truncates to 20)
  • Warns about unresolved ledger entries (missing numeric codes)
  • Shows sample links to admin history for invalid entries (if user has :view :history permission)

PDF Export

  • Generates PDF with Calibri Light font, 6pt
  • Uploads to S3 at reports/profit-and-loss/<uuid>/<name>.pdf
  • Persists report record in Datomic
  • Returns modal with download link

Balance Sheet

Form

  • Customer multi-select typeahead (max 20, defaults to first 5 if "all")
  • Date dropdown (single date, default today)
  • "Include deltas" toggle
  • Run button (HTMX GET)
  • Export PDF button

Report generation

  • Computes running balances before generating
  • Queries account snapshot as of each selected date
  • Groups by account categories: Assets, Liabilities, Owner's Equity
  • Includes Retained Earnings (net income across all P&L categories)

Report output

  • Assets section with account detail and subtotal
  • Liabilities section with account detail and subtotal
  • Owner's Equity section with account detail and subtotal
  • Retained Earnings line
  • Delta columns between periods if enabled and multiple dates selected

Warnings

  • Warns if >20 clients selected
  • Warns about unresolved ledger entries

PDF Export

  • Generates PDF, uploads to S3 at reports/balance-sheet/<uuid>/<name>.pdf
  • Persists report record in Datomic

Cash Flows

Form

  • Customer multi-select typeahead (max 20, defaults to first 5 if "all")
  • Periods dropdown (default: year-to-date)
  • Run button (HTMX PUT)
  • Export PDF button

Report generation

  • Queries account snapshot as of period end + 1 day
  • Groups accounts into: Operating Activities, Investment Activities, Financing Activities, Cash
  • Calculates cash flow effect: add or subtract based on account code ranges

Report output

  • Net Income starting point
  • Operating Activities detail (increases, decreases, +/- in cash)
  • Investment Activities detail
  • Financing Activities detail
  • Change in Cash and Cash Equivalents total
  • Bank Accounts / Cash detail

Warnings

  • Warns if >20 clients selected
  • Warns about unresolved ledger entries

PDF Export

  • Generates PDF, uploads to S3 at reports/cash-flows/<uuid>/<name>.pdf
  • Persists report record in Datomic

Investigation

Modal behavior

  • Opens as modal dialog from report table cell clicks
  • Shows ledger entries filtered by the clicked cell's filters (account code range, client, location, date range)
  • Displays raw table without checkboxes
  • Max height 600px with scrollable content

Table behavior

  • Uses same query schema as main ledger list
  • Supports sorting and pagination
  • Does not push URL state

Cross-Cutting Behaviors

Report generation

  • All reports call upsert-running-balance before querying to ensure cached balances are current
  • Reports use detailed-account-snapshot Datomic query for raw data
  • Account lookups built per-client via build-account-lookup
  • Reports skip entries without numeric codes (unassigned accounts) and warn

Export

  • All three reports support PDF export via clj-pdf
  • PDFs use Calibri Light 6pt font on letter size
  • Uploaded to S3 data bucket with UUID-based key
  • Report metadata persisted to Datomic (:report/name, :report/client, :report/key, :report/url, :report/creator, :report/created)
  • Export returns modal with S3 download link

Filtering and sorting

  • Ledger list filters applied via HTMX on change (500ms debounce) or keyup (1000ms debounce for hot filters)
  • Bank account filter refreshes when client selection changes
  • Sorting supports multiple sort keys with ascending/descending
  • Default sort is date ascending
  • Exact match ID filter bypasses all other filters

Permissions

  • All ledger pages require authenticated user
  • Main ledger: :read :ledger
  • New/Edit journal entry: :edit :ledger
  • External import: :import :ledger + admin assertion
  • P&L report: :read :profit-and-loss
  • Balance sheet: :read :balance-sheet
  • Cash flows: :read :cash-flows
  • Users can only see clients they have permission for (assert-can-see-client)
  • Invoice void/edit/unvoid actions require respective invoice permissions

Edge Cases

Empty states

  • No entries: empty table with pagination showing 0 results
  • No matching filters: empty table, filter pills remain
  • Report with no data: empty report form, no table rendered

Data locking

  • Journal entries cannot be created for dates on or before client's locked-until date
  • External import rejects entries for locked dates

Unbalanced entries

  • "Show unbalanced" filter computes debit/credit sums per entry and filters to mismatches
  • Unbalanced entries are still displayed in normal view

Account location mismatches

  • Account with fixed location rejects other locations
  • Account without location restriction rejects "A" (all) location
  • Validation occurs on both frontend (location select) and backend (schema)

Multi-client reports

  • Capped at 20 clients for performance
  • "All" clients defaults to first 5 for reports
  • Report names include all selected client names

Running balance cache

  • refresh-running-balance-cache recomputes balances for dirty line items
  • Changing a ledger entry marks its line items and subsequent entries as dirty
  • Non-dirty entries are not recomputed

Test Data Requirements

Clients

  • At least 2 clients with different locations
  • Client with 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

  • Entries with debits = credits (balanced)
  • Entries with debits ≠ credits (unbalanced)
  • 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

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