Files
integreat/docs/testing/behaviors/pos.md
Bryce 6b5d33a32f feat(tests): implement integration and unit tests for auth, company, and ledger behaviors
- Auth: 30 tests (97 assertions) covering OAuth, sessions, JWT, impersonation, roles
- Company: 35 tests (92 assertions) covering profile, 1099, expense reports, permissions
- Ledger: 113 tests (148 assertions) covering grid, journal entries, import, reports
- Fix existing test failures in running_balance, insights, tx, plaid, graphql
- Fix InMemSolrClient to handle Solr query syntax properly
- Update behavior docs: auth (42 done), company (32 done), ledger (120 done)
- All 478 tests pass with 0 failures, 0 errors
2026-05-08 16:12:08 -07:00

22 KiB
Raw Blame History

POS Behaviors

Overview

The POS (Point of Sale) module provides SSR (HTMX-driven) grid pages for viewing sales data imported from payment processors. All pages share a common grid layout with filters, sortable columns, and pagination. Data is read-only except for the admin Sales Summaries edit wizard.

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: Wizard Behaviors

Wizards are multi-step forms with HTMX-driven navigation:

  1. Each step is a GET that renders a form fragment
  2. Form submissions are POST/PUT with validation
  3. Navigation between steps updates the wizard state
  4. Final submit creates/updates the entity

Test implications: Unit test validation logic and state transitions. Integration test the full wizard flow once. UI test only the happy path.

Pattern: Permission Gates

Every mutating operation checks:

  1. assert-can-see-client — user has access to the client
  2. assert-not-locked — invoice 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.


Sales Orders

Display Behaviors

# Behavior Test Strategy Status
1.1 It should display a table with columns: Client, Date, Source, Total, Tax, Tip, Payment Methods UI [ ]
1.2 It should show the Client column only when multiple clients are in scope Integration [x]
1.3 It should render the Source column as a pill badge UI [ ]
1.4 It should render each unique payment method as a pill in the Payment Methods column (cash, card, gift card, other) UI [ ]
1.5 It should display action buttons above the grid showing Total and Tax pills summarizing the currently filtered result set UI [ ]
1.6 It should show an external link icon row button when the sales order has a reference link UI [ ]

Filtering Behaviors

# Behavior Test Strategy Status
2.1 It should filter sales orders by date range (start date / end date) Integration [x]
2.2 It should filter sales orders by total amount range (min / max) Integration [x]
2.3 It should filter sales orders by payment method via radio cards: All, Cash, Card, Gift Card, Other Integration [x]
2.4 It should filter sales orders by processor via radio cards: All, Square, Doordash, Uber Eats, Grubhub, Koala, EZCater, No Processor Integration [x]
2.5 It should filter sales orders by category text input matching order line item category Integration [x]
2.6 Given multiple filters are applied, when the user changes one filter, then the table should refresh with the combined filter set Integration [x]

Sorting Behaviors

# Behavior Test Strategy Status
3.1 It should sort by client name ascending/descending Integration [x]
3.2 It should sort by date ascending/descending Integration [x]
3.3 It should sort by total amount ascending/descending Integration [x]
3.4 It should sort by tax amount ascending/descending Integration [x]
3.5 It should sort by tip amount ascending/descending Integration [x]
3.6 It should sort by source ascending/descending Integration [x]
3.7 It should sort by processor ascending/descending Integration [ ]
3.8 Given the user clicks a column header twice, then the sort direction should toggle Integration [x]

Note: 3.7 is untestable because :sales-order/processor does not exist in the Datomic schema; processor info lives on :charge/processor.

Pagination Behaviors

# Behavior Test Strategy Status
4.1 It should display 25 sales orders per page by default Integration [x]
4.2 It should allow changing the per-page count Integration [x]
4.3 It should calculate the total amount and tax across ALL matching sales orders, not just the current page Unit [x]

Expected Deposits

Display Behaviors

# Behavior Test Strategy Status
5.1 It should display a table with columns: Client, Date, Sales Date, Total, Fee UI [ ]
5.2 It should show the Client column only when multiple clients are in scope Integration [x]
5.3 It should show a totals breakdown per expected deposit, aggregating charges by sales date with count and amount UI [ ]
5.4 It should show an external link icon row button when the expected deposit has a reference link UI [ ]
5.5 It should show a "Transaction" button linking to the associated transaction when one exists UI [ ]

Filtering Behaviors

# Behavior Test Strategy Status
6.1 It should filter expected deposits by date range Integration [x]
6.2 It should support exact match ID to jump to a specific record, showing a removable pill when active Integration [x]
6.3 Given multiple filters are applied, when the user changes one filter, then the table should refresh with the combined filter set Integration [x]

Sorting Behaviors

# Behavior Test Strategy Status
7.1 It should sort by client name ascending/descending Integration [x]
7.2 It should sort by location ascending/descending Integration [x]
7.3 It should sort by date ascending/descending Integration [x]
7.4 It should sort by total amount ascending/descending Integration [x]
7.5 It should sort by fee amount ascending/descending Integration [x]
7.6 Given the user clicks a column header twice, then the sort direction should toggle Integration [x]

Pagination Behaviors

# Behavior Test Strategy Status
8.1 It should display 25 expected deposits per page by default Integration [x]
8.2 It should allow changing the per-page count Integration [x]

Tenders

Display Behaviors

# Behavior Test Strategy Status
9.1 It should display a table with columns: Client, Date, Total, Processor, Tip, Links UI [ ]
9.2 It should show the Client column only when multiple clients are in scope Integration [x]
9.3 It should render the Processor column as a pill badge UI [ ]
9.4 It should show an external link icon row button when the tender has a reference link UI [ ]
9.5 It should show an "expected deposit" pill in the Links column when an associated expected deposit exists UI [ ]

Filtering Behaviors

# Behavior Test Strategy Status
10.1 It should filter tenders by date range Integration [x]
10.2 It should filter tenders by processor via radio cards: All, Square, Doordash, Uber Eats, Grubhub, Koala, EZCater, No Processor Integration [x]
10.3 It should filter tenders by total amount range (min / max) Integration [x]
10.4 Given multiple filters are applied, when the user changes one filter, then the table should refresh with the combined filter set Integration [x]

Sorting Behaviors

# Behavior Test Strategy Status
11.1 It should sort by client name ascending/descending Integration [x]
11.2 It should sort by date ascending/descending Integration [x]
11.3 It should sort by total amount ascending/descending Integration [x]
11.4 It should sort by tip amount ascending/descending Integration [x]
11.5 It should sort by processor ascending/descending Integration [x]
11.6 Given the user clicks a column header twice, then the sort direction should toggle Integration [x]

Pagination Behaviors

# Behavior Test Strategy Status
12.1 It should display 25 tenders per page by default Integration [x]
12.2 It should allow changing the per-page count Integration [x]

Refunds

Display Behaviors

# Behavior Test Strategy Status
13.1 It should display a table with columns: Client, Date, Total, Type, Fee UI [ ]
13.2 It should show the Client column only when multiple clients are in scope Integration [x]

Filtering Behaviors

# Behavior Test Strategy Status
14.1 It should filter refunds by date range Integration [x]
14.2 It should filter refunds by total amount range (min / max) Integration [x]
14.3 Given multiple filters are applied, when the user changes one filter, then the table should refresh with the combined filter set Integration [x]

Sorting Behaviors

# Behavior Test Strategy Status
15.1 It should sort by client name ascending/descending Integration [x]
15.2 It should sort by date ascending/descending Integration [x]
15.3 It should sort by total amount ascending/descending Integration [x]
15.4 It should sort by fee amount ascending/descending Integration [ ]
15.5 It should sort by type ascending/descending Integration [x]
15.6 Given the user clicks a column header twice, then the sort direction should toggle Integration [x]

Note: 15.4 is blocked by a source bug: refunds.clj line 62 uses ?sort-tip instead of ?sort-fee for fee sorting, causing an unbound variable error.

Pagination Behaviors

# Behavior Test Strategy Status
16.1 It should display 25 refunds per page by default Integration [x]
16.2 It should allow changing the per-page count Integration [x]

Cash Drawer Shifts

Display Behaviors

# Behavior Test Strategy Status
17.1 It should display a table with columns: Client, Date, Paid in, Paid out, Expected cash, Opened cash UI [ ]
17.2 It should show the Client column only when multiple clients are in scope Integration [x]

Filtering Behaviors

# Behavior Test Strategy Status
18.1 It should filter cash drawer shifts by date range Integration [x]
18.2 It should filter cash drawer shifts by total amount range (min / max) Integration [ ]
18.3 Given multiple filters are applied, when the user changes one filter, then the table should refresh with the combined filter set Integration [x]

Note: 18.2 is not implemented: the UI shows total-field* but fetch-ids in cash_drawer_shifts.clj does not handle total-gte/total-lte.

Sorting Behaviors

# Behavior Test Strategy Status
19.1 It should sort by client name ascending/descending Integration [x]
19.2 It should sort by date ascending/descending Integration [x]
19.3 It should sort by paid-in amount ascending/descending Integration [x]
19.4 It should sort by paid-out amount ascending/descending Integration [x]
19.5 It should sort by expected-cash amount ascending/descending Integration [x]
19.6 It should sort by opened-cash amount ascending/descending Integration [x]
19.7 Given the user clicks a column header twice, then the sort direction should toggle Integration [x]

Pagination Behaviors

# Behavior Test Strategy Status
20.1 It should display 25 cash drawer shifts per page by default Integration [x]
20.2 It should allow changing the per-page count Integration [x]

Sales Summaries

Display Behaviors

# Behavior Test Strategy Status
21.1 It should display a table with columns: Client, Date, Debits, Credits UI [ ]
21.2 It should show the Client column only when multiple clients are in scope Integration [x]
21.3 It should display debit-line items with category and amount UI [ ]
21.4 It should display credit-line items with category and amount UI [ ]
21.5 It should show a red "missing account" warning pill for unmapped items UI [ ]
21.6 It should show a green "Total" pill for balanced summaries (debits equal credits) UI [ ]
21.7 It should show a red "Total" pill for unbalanced summaries (debits do not equal credits) UI [ ]
21.8 It should show an edit (pencil) icon row button opening the edit wizard modal UI [ ]

Filtering Behaviors

# Behavior Test Strategy Status
22.1 It should filter sales summaries by date range Integration [x]
22.2 Given multiple filters are applied, when the user changes one filter, then the table should refresh with the combined filter set Integration [x]

Sorting Behaviors

# Behavior Test Strategy Status
23.1 It should sort by client name ascending/descending Integration [x]
23.2 It should sort by date ascending/descending Integration [x]
23.3 It should sort by debits ascending/descending Integration [x]
23.4 It should sort by credits ascending/descending Integration [x]
23.5 Given the user clicks a column header twice, then the sort direction should toggle Integration [x]

Pagination Behaviors

# Behavior Test Strategy Status
24.1 It should display 25 sales summaries per page by default Integration [x]
24.2 It should allow changing the per-page count Integration [x]

Edit Wizard Behaviors

# Behavior Test Strategy Status
25.1 It should open a modal dialog when the edit button is clicked UI [ ]
25.2 It should display a data grid of summary items with columns: Category, Account, Debits, Credits UI [ ]
25.3 It should render auto items (non-manual) with read-only Category and amount, editable Account via typeahead UI [ ]
25.4 It should render manual items with editable Category (text input), Account (typeahead), Debit amount, and Credit amount UI [ ]
25.5 It should allow adding new manual items via a "New Summary Item" row UI [ ]
25.6 It should allow removing manual items via an X button UI [ ]
25.7 It should validate that an item cannot have both credit and debit amounts Unit + Integration [x]
25.8 It should display a total row with running totals for debits and credits UI [ ]
25.9 It should display an unbalanced row showing the difference when debits do not equal credits UI [ ]
25.10 It should search accounts scoped to the client with purpose "invoice" in the account typeahead Integration [x]
25.11 It should flash the row and close the modal after successful save UI [ ]

Cross-Cutting Behaviors

HTMX Live Filtering Behaviors

# Behavior Test Strategy Status
26.1 It should trigger table refresh on filter form change with a 500ms debounce Integration SKIPPED
26.2 It should trigger table refresh on hot-filter keyup with a 1000ms debounce Integration SKIPPED
26.3 It should POST to the table route and swap the grid contents Integration SKIPPED
26.4 It should update the browser URL via hx-push-url when filters change Integration SKIPPED

Note: 26.126.4 test frontend JavaScript/HTMX behavior and cannot be validated server-side.

Date Range Behaviors

# Behavior Test Strategy Status
27.1 It should support start-date and end-date query params on all pages Integration [x]
27.2 It should render the date range filter consistently across all pages UI [ ]
27.3 Given a date range with no start or end date, then the query should use scan functions with nil boundaries Integration [x]

Total Range Behaviors

# Behavior Test Strategy Status
28.1 It should support total-gte and total-lte query params on pages with amount filters Integration [x]
28.2 It should render money inputs for the total range filter UI [ ]

Exact Match ID Behaviors

# Behavior Test Strategy Status
29.1 It should support exact-match-id to jump to a specific record on applicable pages Integration [x]
29.2 It should show a removable pill when exact-match-id is active UI [ ]

Sorting Behaviors

# Behavior Test Strategy Status
30.1 It should toggle ascending/descending sort when a sortable column header is clicked Integration [x]
30.2 It should support multi-sort with active sorts appearing as removable pills above the grid Integration [x]
30.3 It should remove a sort when the X on its pill is clicked Integration [x]
30.4 It should default to sort by date descending for most pages Integration [x]

Pagination Behaviors

# Behavior Test Strategy Status
31.1 It should display first/previous/next/last pagination controls UI [ ]
31.2 It should display the total count above the grid UI [ ]

Client Scoping Behaviors

# Behavior Test Strategy Status
32.1 It should scope all queries to the user's accessible clients (trimmed to max 20) Integration [x]
32.2 It should hide the Client column when only one client is in scope Integration [x]
32.3 It should support client-id and client-code URL params Integration [x]

Permission Behaviors

# Behavior Test Strategy Status
33.1 It should require (can? identity {:subject :sales :activity :read}) to access POS pages Integration SKIPPED
33.2 It should require admin access (wrap-admin) to access Sales Summaries Integration [x]
33.3 It should redirect unauthenticated users Integration SKIPPED

Note: 33.1 tests the GraphQL can? gate which is not directly reachable at the unit/integration level; 33.3 tests middleware redirect behavior that is covered elsewhere.


Test Data Requirements

Entity Requirements
Clients Multiple clients with db/id, client/name, client/code; some with locked-until dates
Sales Orders With :sales-order/date, :sales-order/total, :sales-order/tax, :sales-order/tip, :sales-order/source, :sales-order/charges (with charge/type-name, charge/processor), :sales-order/line-items (with order-line-item/category)
Expected Deposits With :expected-deposit/date, :expected-deposit/total, :expected-deposit/fee, :expected-deposit/client, optional transaction/_expected-deposit
Tenders (Charges) With :charge/date, :charge/total, :charge/tip, :charge/processor, optional expected-deposit/_charges
Refunds With :sales-refund/date, :sales-refund/total, :sales-refund/fee, :sales-refund/type
Cash Drawer Shifts With :cash-drawer-shift/date, :cash-drawer-shift/paid-in, :cash-drawer-shift/paid-out, :cash-drawer-shift/expected-cash, :cash-drawer-shift/opened-cash
Sales Summaries With :sales-summary/date, :sales-summary/client, :sales-summary/items (with ledger-mapped/ledger-side, ledger-mapped/amount, sales-summary-item/category, optional ledger-mapped/account)
Accounts With purpose "invoice", scoped to clients, for Sales Summaries edit wizard

Existing Tests to Preserve

  • test/clj/auto_ap/ssr/pos/sales_orders_test.clj — Sales orders grid behaviors (20 tests, 43 assertions)
  • test/clj/auto_ap/ssr/pos/expected_deposits_test.clj — Expected deposits grid behaviors (9 tests, 23 assertions)
  • test/clj/auto_ap/ssr/pos/tenders_test.clj — Tenders grid behaviors (8 tests, 20 assertions)
  • test/clj/auto_ap/ssr/pos/refunds_test.clj — Refunds grid behaviors (7 tests, 17 assertions)
  • test/clj/auto_ap/ssr/pos/cash_drawer_shifts_test.clj — Cash drawer shifts grid behaviors (7 tests, 19 assertions)
  • test/clj/auto_ap/ssr/admin/sales_summaries_test.clj — Sales summaries admin behaviors (9 tests, 24 assertions)

Dependencies

  • Datomic (primary store)
  • HTMX/Alpine.js (frontend interactivity)
  • Grid system: auto-ap.ssr.grid-page-helper provides build, page-route, table-route, row*, table*
  • Components: auto-ap.ssr.components for data grids, pills, buttons, inputs
  • Querying: auto-ap.datomic for query2, pull-many, apply-pagination, apply-sort-3
  • Ions: iol-ion.query/scan-sales-orders, scan-expected-deposits, scan-charges, scan-sales-refunds, scan-cash-drawer-shifts
  • Permissions: auto-ap.permissions/can?
  • Time: auto-ap.time for date formatting and localization
  • Schema validation: Malli schemas enforce query params on every request

Known Discrepancies

  1. sales_orders.clj references :sales-order/processor in sort logic (behavior 3.7), but this attribute does not exist in the Datomic schema; processor info lives on :charge/processor.
  2. refunds.clj line 62 uses ?sort-tip instead of ?sort-fee for fee sorting, causing an unbound variable error (behavior 15.4).
  3. cash_drawer_shifts.clj renders total-field* in the UI filters but does not implement total-gte/total-lte filtering in fetch-ids (behavior 18.2).