# Legacy SPA Behaviors

## Overview

These pages are rendered client-side via Reagent/Re-frame and use GraphQL for data fetching. They are being migrated to HTMX SSR. **No UI tests should be written for these pages until migrated.**

**Testing Philosophy**
- Prefer unit tests for pure business logic (calculations, validations, transformations)
- Use integration tests for GraphQL queries, mutations, and data flows
- Use UI tests only after migration to SSR; until then, mark as "UI (when migrated)"
- Every behavior must be user-visible; no tests for implementation details

---

## Testing Patterns

### Pattern: GraphQL Data Fetching
All data fetching uses re-frame `graphql` effect with JWT token from `:user` in app-db:
1. Queries use `owns-state` to track loading status per page
2. Results flow through `::data-page/received` event which stores data and syncs URL query params
3. Filter changes are debounced (800ms) via `dispatch-debounce`

**Test implications:** Integration test the GraphQL query resolution and response shape. Do not test the re-frame effect machinery.

### Pattern: Re-frame State Management
- **Data pages**: `data-page` namespace provides reusable pagination/filtering state
  - `::data-page/params` — merged filters + table params + query params
  - `::data-page/data` — GraphQL response data
  - `::data-page/checked` — selected rows for bulk operations
- **Forms**: `forms` namespace manages edit dialogs with `start-form`, `change-handler`, `save-succeeded`
- **Status**: `status` namespace tracks async operation states (`:loading`, `:complete`, `:error`)

**Test implications:** Test via integration tests that verify the correct data appears after state transitions. Do not test subscription internals.

### Pattern: Client-Side Routing
- Navbar uses Bidi `path-for` with `routes/routes` for legacy SPA links
- Some navbar items (Payments, POS, Invoices) now link to `ssr-routes/only-routes` instead
- Page components dispatch `::mounted` on mount and `::unmounted` on unmount

**Test implications:** After migration, integration test route redirects from old SPA routes to new SSR routes.

---

## Home

### Dashboard Display

| # | Behavior | Test Strategy | Status |
|---|----------|---------------|--------|
| 1.1 | It should display a dashboard with three chart sections: top expense categories, upcoming bills, and cash flow projection | UI (when migrated) | [ ] |
| 1.2 | It should load data for the currently selected client on page load | Integration | [ ] |
| 1.3 | It should display a note: "these reports are for [client]. Please choose a specific customer for their report." when the user has access to multiple clients and has not selected a specific one | UI (when migrated) | [ ] |
| 1.4 | It should display an interactive bar chart for cash flow projection | UI (when migrated) | [ ] |
| 1.5 | It should display a table below the cash flow chart showing invoices, upcoming debits, and upcoming credits with days-until due | UI (when migrated) | [ ] |
| 1.6 | It should allow switching the cash flow range between 7, 30, 60, 90, 120, 150, and 180 days | UI (when migrated) | [ ] |
| 1.7 | Given the user switches the cash flow range, then the chart and table should update to reflect the selected range | Integration | [ ] |
| 1.8 | Given the user clicks a cash flow bar, then it should redirect to the unpaid invoices page for that date | UI (when migrated) | [ ] |
| 1.9 | It should show empty charts gracefully when there is no data for the selected client | UI (when migrated) | [ ] |
| 1.10 | It should show a loading state while GraphQL data is being fetched | UI (when migrated) | [ ] |
| 1.11 | It should handle GraphQL errors by showing the loading state appropriately | UI (when migrated) | [ ] |

---

## Login

### Authentication

| # | Behavior | Test Strategy | Status |
|---|----------|---------------|--------|
| 2.1 | It should display a "Login with Google" button on the login page | UI (when migrated) | [ ] |
| 2.2 | It should link the login button to Google OAuth | UI (when migrated) | [ ] |
| 2.3 | It should preserve the `redirect-to` query parameter in the Google OAuth URL | Integration | [ ] |
| 2.4 | It should display a warning notification with the logout reason when the `logout-reason` query parameter is set | UI (when migrated) | [ ] |
| 2.5 | It should redirect an already authenticated user away from the login page | Integration | [ ] |

### Needs Activation

| # | Behavior | Test Strategy | Status |
|---|----------|---------------|--------|
| 2.6 | It should display the message: "Sorry, your user is not activated yet. Please have Ben Skinner enable your account." when the user's account is inactive | UI (when migrated) | [ ] |
| 2.7 | It should provide a "here" link that clears user state and redirects to the login page | Integration | [ ] |

---

## Transactions

### List Display

| # | Behavior | Test Strategy | Status |
|---|----------|---------------|--------|
| 3.1 | It should display a table of transactions with columns for amount, memo, location, approval status, vendor, accounts, date, and description | UI (when migrated) | [ ] |
| 3.2 | It should load the transaction list with a default date filter of the last 1 month | Integration | [ ] |
| 3.3 | It should allow filtering by vendor, account, bank account, date range, amount range, location, import batch, description, and linked status via a sidebar | UI (when migrated) | [ ] |
| 3.4 | It should debounce sidebar filter changes by 800ms before refreshing data | Integration | [ ] |
| 3.5 | It should support pagination with start and per-page parameters | Integration | [ ] |
| 3.6 | It should display checkboxes for row selection when the user is an admin | UI (when migrated) | [ ] |
| 3.7 | It should not display checkboxes or bulk action buttons for non-admin users | UI (when migrated) | [ ] |

### Transaction Edit

| # | Behavior | Test Strategy | Status |
|---|----------|---------------|--------|
| 3.8 | It should open an edit sidebar when the user clicks a transaction row | UI (when migrated) | [ ] |
| 3.9 | It should allow updating the vendor, approval status, memo, and expense accounts | UI (when migrated) | [ ] |
| 3.10 | It should validate that the sum of expense account amounts equals the transaction amount | Unit + Integration | [ ] |
| 3.11 | It should validate that locations are valid for the selected accounts | Unit + Integration | [ ] |
| 3.12 | It should block editing locked transactions | Integration | [ ] |
| 3.13 | It should display validation errors inline in the edit form | UI (when migrated) | [ ] |

### Bulk Operations (Admin)

| # | Behavior | Test Strategy | Status |
|---|----------|---------------|--------|
| 3.14 | It should allow deleting selected transactions or all visible transactions | Integration | [ ] |
| 3.15 | It should allow suppressing selected transactions instead of deleting them | Integration | [ ] |
| 3.16 | It should allow bulk coding by applying vendor, account, approval status, and account rules to multiple transactions | Integration | [ ] |
| 3.17 | It should allow importing transactions via a manual Yodlee import dialog | Integration | [ ] |

### Route Variants

| # | Behavior | Test Strategy | Status |
|---|----------|---------------|--------|
| 3.18 | It should apply the correct default approval status filter for each route variant: all, unapproved, approved, requires-feedback, excluded | Integration | [ ] |

---

## Ledger

### General Ledger

| # | Behavior | Test Strategy | Status |
|---|----------|---------------|--------|
| 4.1 | It should display a table of journal entries with expandable line items | UI (when migrated) | [ ] |
| 4.2 | It should load the ledger with a default date filter of the last 1 month | Integration | [ ] |
| 4.3 | It should allow filtering by vendor, account, bank account, date range, amount range, and location via a sidebar | UI (when migrated) | [ ] |
| 4.4 | It should support virtual pagination controls | Integration | [ ] |
| 4.5 | It should allow admin users to export filtered results as a CSV download | Integration | [ ] |
| 4.6 | It should display "Not authorized" for users with the manager role | UI (when migrated) | [ ] |

### Profit and Loss

| # | Behavior | Test Strategy | Status |
|---|----------|---------------|--------|
| 4.7 | It should generate a profit and loss report for a single client with a default period | Integration | [ ] |
| 4.8 | It should allow selecting multiple companies via a multi-select typeahead and generating a combined report | UI (when migrated) | [ ] |
| 4.9 | It should provide period preset buttons: 13 periods, 12 months, last week, week-to-date, last month, month-to-date, year-to-date, last calendar year, and full year | UI (when migrated) | [ ] |
| 4.10 | It should populate the correct date ranges when a period preset is selected | Unit | [ ] |
| 4.11 | It should allow custom period selection via start and end date pickers in advanced mode | UI (when migrated) | [ ] |
| 4.12 | It should optionally include period-over-period deltas | UI (when migrated) | [ ] |
| 4.13 | It should optionally break out the report by location, which is mutually exclusive with including deltas | Integration | [ ] |
| 4.14 | It should allow admin users to generate and download a PDF export | Integration | [ ] |
| 4.15 | It should provide an email composition link for single-client PDF exports | UI (when migrated) | [ ] |
| 4.16 | It should open a ledger detail sidebar when the user clicks a report cell | UI (when migrated) | [ ] |
| 4.17 | It should display "Not authorized" for users with the manager role | UI (when migrated) | [ ] |

### Cash Flows

| # | Behavior | Test Strategy | Status |
|---|----------|---------------|--------|
| 4.18 | It should generate a cash flows statement report | Integration | [ ] |
| 4.19 | It should use the same company, period, delta, and location column controls as the profit and loss report | Integration | [ ] |
| 4.20 | It should allow admin users to export the report to PDF | Integration | [ ] |
| 4.21 | It should open a ledger detail sidebar when the user clicks a report cell | UI (when migrated) | [ ] |
| 4.22 | It should display "Not authorized" for users with the manager role | UI (when migrated) | [ ] |

### Profit and Loss Detail

| # | Behavior | Test Strategy | Status |
|---|----------|---------------|--------|
| 4.23 | It should generate a detailed journal entry report with a default 2-week date range | Integration | [ ] |
| 4.24 | It should group journal entries by category: sales, COGS, payroll, controllable, fixed overhead, and ownership controllable | Integration | [ ] |
| 4.25 | It should display Gross Profit, Overhead, and Net Profit summaries | UI (when migrated) | [ ] |
| 4.26 | It should filter journal entries by the selected start and end dates | Integration | [ ] |
| 4.27 | It should allow admin users to export the report to PDF with an email link for single client | Integration | [ ] |
| 4.28 | It should display "Not authorized" for users with the manager role | UI (when migrated) | [ ] |

### Balance Sheet

| # | Behavior | Test Strategy | Status |
|---|----------|---------------|--------|
| 4.29 | It should generate a balance sheet report as of a specific date | Integration | [ ] |
| 4.30 | It should allow selecting multiple companies and combining them into a single report | Integration | [ ] |
| 4.31 | It should optionally include a prior-year comparison with a side-by-side view | UI (when migrated) | [ ] |
| 4.32 | It should allow admin users to export the report to PDF with an email composition link for single client | Integration | [ ] |
| 4.33 | It should open ledger entries filtered by account and date range when the user clicks a cell | UI (when migrated) | [ ] |
| 4.34 | It should display "Not authorized" for users with the manager role | UI (when migrated) | [ ] |

### External Ledger

| # | Behavior | Test Strategy | Status |
|---|----------|---------------|--------|
| 4.35 | It should display only externally-imported journal entries with an external_id | Integration | [ ] |
| 4.36 | It should allow admin users to delete selected entries, with a maximum of 1000 at once | Integration | [ ] |
| 4.37 | It should allow admin users to export to CSV | Integration | [ ] |
| 4.38 | It should display "Not authorized" for non-admin users | UI (when migrated) | [ ] |

### External Import

| # | Behavior | Test Strategy | Status |
|---|----------|---------------|--------|
| 4.39 | It should allow admin users to paste tab-separated data into a textarea | UI (when migrated) | [ ] |
| 4.40 | It should provide a checkbox to indicate whether the first row is a header | UI (when migrated) | [ ] |
| 4.41 | It should parse the pasted data into a table with columns: Id, Client, Source, Vendor, Date, Account, Location, Debit, Credit, Note, and Cleared against | Integration | [ ] |
| 4.42 | It should validate that the client code exists | Unit + Integration | [ ] |
| 4.43 | It should validate that the vendor exists | Unit + Integration | [ ] |
| 4.44 | It should validate that the date is in MM/dd/yyyy format | Unit + Integration | [ ] |
| 4.45 | It should validate that total debits equal total credits | Unit + Integration | [ ] |
| 4.46 | It should validate that all amounts are greater than 0 | Unit + Integration | [ ] |
| 4.47 | It should validate that entries are dated after the client's locked-until date | Integration | [ ] |
| 4.48 | It should validate that the location belongs to the client or is "A" | Unit + Integration | [ ] |
| 4.49 | It should validate that the account exists and the location matches the account's required location | Unit + Integration | [ ] |
| 4.50 | It should display errors per row with a dropdown explanation | UI (when migrated) | [ ] |
| 4.51 | It should show status icons indicating success, ignored, or existing per row after import | UI (when migrated) | [ ] |
| 4.52 | It should display the total success count, ignored count, and error count after import | UI (when migrated) | [ ] |
| 4.53 | It should provide an "Only show errors" filter | UI (when migrated) | [ ] |
| 4.54 | It should display "Not authorized" for non-admin users | UI (when migrated) | [ ] |

---

## Payments

| # | Behavior | Test Strategy | Status |
|---|----------|---------------|--------|
| 5.1 | It should be fully migrated to SSR at `/payment/`; the legacy client route exists only for navbar highlighting | N/A | [x] |

---

## Reports

| # | Behavior | Test Strategy | Status |
|---|----------|---------------|--------|
| 6.1 | It should be fully migrated to SSR at `/company/reports`; the legacy client route exists only for navbar highlighting | N/A | [x] |

---

## Vendors

### Admin Vendor Management

| # | Behavior | Test Strategy | Status |
|---|----------|---------------|--------|
| 7.1 | It should be fully migrated to SSR at `/admin/vendor`; the legacy client route exists only for navbar highlighting | N/A | [x] |

### New Vendor Dialog

| # | Behavior | Test Strategy | Status |
|---|----------|---------------|--------|
| 7.2 | It should load the home dashboard with the vendor creation dialog pre-opened when navigating to `/vendor/new` | UI (when migrated) | [ ] |
| 7.3 | It should only open the vendor dialog if the user has `:vendor :create` permission | Integration | [ ] |
| 7.4 | It should allow creating a new vendor with name, terms, address, contacts, and default account | UI (when migrated) | [ ] |
| 7.5 | It should validate that only one terms override exists per client | Unit + Integration | [ ] |
| 7.6 | It should validate that only one schedule payment DOM override exists per client | Unit + Integration | [ ] |
| 7.7 | It should validate that only one account override exists per client | Unit + Integration | [ ] |

---

## Cross-Cutting Behaviors

### GraphQL Query Patterns

| # | Behavior | Test Strategy | Status |
|---|----------|---------------|--------|
| 8.1 | It should include the JWT token from the `:user` app-db state in all GraphQL requests | Integration | [ ] |
| 8.2 | It should track loading status per page using `owns-state` | Integration | [ ] |
| 8.3 | It should store GraphQL response data and sync URL query params via the `::data-page/received` event | Integration | [ ] |
| 8.4 | It should debounce filter changes by 800ms before dispatching GraphQL requests | Integration | [ ] |

### Re-frame State Management

| # | Behavior | Test Strategy | Status |
|---|----------|---------------|--------|
| 8.5 | It should merge filters, table params, and query params into `::data-page/params` | Integration | [ ] |
| 8.6 | It should store GraphQL response data in `::data-page/data` | Integration | [ ] |
| 8.7 | It should track selected rows for bulk operations in `::data-page/checked` | Integration | [ ] |
| 8.8 | It should manage edit dialog state with `start-form`, `change-handler`, and `save-succeeded` events | Integration | [ ] |
| 8.9 | It should track async operation states as `:loading`, `:complete`, or `:error` | Integration | [ ] |

### Navigation

| # | Behavior | Test Strategy | Status |
|---|----------|---------------|--------|
| 8.10 | It should redirect old SPA routes to new SSR routes after migration | Integration | [ ] |
| 8.11 | It should set up data subscriptions, forward event listeners, and parameter change watchers on page mount | Integration | [ ] |
| 8.12 | It should tear down data subscriptions, forward event listeners, and parameter change watchers on page unmount | Integration | [ ] |

---

## Test Data Requirements

| Entity | Requirements |
|--------|-------------|
| **Users** | Admin, power-user, manager, user, and read-only roles |
| **Clients** | Multiple clients with different locations; some with locked-until dates |
| **Vendors** | With/without default accounts, terms, and autopay settings |
| **Accounts** | Expense accounts with/without invoice allowance; different locations |
| **Bank Accounts** | Check, cash, and credit types |
| **Transactions** | Various approval statuses, dates, amounts, locked/unlocked states |
| **Journal Entries** | With/without external_id; various categories and accounts |
| **Invoices** | Various statuses and due dates for cash flow projections |

## Existing Tests to Preserve

- Test GraphQL query resolution for `HomeDashboard`, `TransactionPage`, `LedgerPage`, `ProfitAndLoss`, `CashFlows`, `JournalDetailReport`, `BalanceSheet`, and `ExternalLedger`
- Test re-frame event handlers for data page state transitions
- Test form validation logic for transaction editing and external ledger import

## Dependencies

- GraphQL API (data fetching)
- Re-frame/Reagent (client-side state and rendering)
- Bidi (client-side routing)
- Recharts (chart rendering on Home page)
- Server-side PDF generation (P&L, Cash Flows, Balance Sheet, P&L Detail)

## Migration Notes

### Already Migrated to SSR
- **Payments** (`/payments/`) - Fully migrated to `/payment/` SSR routes
- **Reports** (`/reports/`) - Fully migrated to `/company/reports` SSR routes
- **Admin Vendors** (`/admin/vendors`) - Fully migrated to `/admin/vendor` SSR routes

### Still Legacy SPA (prioritized by complexity)
1. **Transactions** - High complexity (filters, edit form, bulk operations, manual import)
2. **Home/Dashboard** - Medium complexity (charts, cash flow calculations)
3. **Ledger** - Medium complexity (filters, CSV export)
4. **External Ledger** - Low-medium complexity (subset of ledger + delete)
5. **External Import** - Medium complexity (TSV parsing, validation, batch import)
6. **Profit and Loss** - High complexity (multi-company, periods, PDF export)
7. **Cash Flows** - High complexity (shares P&L infrastructure)
8. **Profit and Loss Detail** - Medium complexity (category filtering, running balances)
9. **Balance Sheet** - Medium complexity (comparison mode, drill-down)
10. **Login** - Low complexity (static page)
11. **Needs Activation** - Low complexity (static page)
12. **New Vendor** - Low complexity (dialog on home page)

### Migration Risks
- **Chart libraries**: Home page uses Recharts (React). Replacement needed for SSR.
- **PDF generation**: P&L, Cash Flows, Balance Sheet, P&L Detail all support PDF export via server-side generation.
- **Bulk operations**: Transactions page has complex bulk coding/deletion with validation.
- **External import**: TSV parsing and validation logic lives entirely in SPA; server-side equivalent needed.
