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
This commit is contained in:
@@ -59,19 +59,19 @@ The JWT token contains user identity and permissions:
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 1.2 | It should redirect to Google OAuth when the user clicks "Sign in with Google" | UI | [ ] |
|
||||
| 1.3 | It should exchange the authorization code for an access token on callback | Integration | [ ] |
|
||||
| 1.4 | It should fetch the user's Google profile using the access token | Integration | [ ] |
|
||||
| 1.5 | It should create a new user account when the user logs in for the first time | Integration | [ ] |
|
||||
| 1.6 | It should find the existing user account on subsequent logins | Integration | [ ] |
|
||||
| 1.7 | It should redirect to the original page after successful OAuth | Integration | [ ] |
|
||||
| 1.8 | It should redirect to the root page when no return URL is provided | Integration | [ ] |
|
||||
| 1.9 | It should establish a server-side session with user identity and version | Integration | [ ] |
|
||||
| 1.10 | It should pass the JWT token in the query string after successful OAuth | Integration | [ ] |
|
||||
| 1.3 | It should exchange the authorization code for an access token on callback | Integration | [x] |
|
||||
| 1.4 | It should fetch the user's Google profile using the access token | Integration | [x] |
|
||||
| 1.5 | It should create a new user account when the user logs in for the first time | Integration | [x] |
|
||||
| 1.6 | It should find the existing user account on subsequent logins | Integration | [x] |
|
||||
| 1.7 | It should redirect to the original page after successful OAuth | Integration | [x] |
|
||||
| 1.8 | It should redirect to the root page when no return URL is provided | Integration | [x] |
|
||||
| 1.9 | It should establish a server-side session with user identity and version | Integration | [x] |
|
||||
| 1.10 | It should pass the JWT token in the query string after successful OAuth | Integration | [x] |
|
||||
| 1.11 | It should display the user's clients and data after successful login | UI | [ ] |
|
||||
| 1.12 | It should handle users without email via Google provider ID | Integration | [ ] |
|
||||
| 1.13 | It should return 401 with error message when the OAuth code is missing | Integration | [ ] |
|
||||
| 1.14 | It should return 401 when the OAuth code is invalid or expired | Integration | [ ] |
|
||||
| 1.15 | It should return 401 and log a warning when the Google network request fails | Integration | [ ] |
|
||||
| 1.12 | It should handle users without email via Google provider ID | Integration | [x] |
|
||||
| 1.13 | It should return 401 with error message when the OAuth code is missing | Integration | [x] |
|
||||
| 1.14 | It should return 401 when the OAuth code is invalid or expired | Integration | SKIPPED |
|
||||
| 1.15 | It should return 401 and log a warning when the Google network request fails | Integration | SKIPPED |
|
||||
|
||||
---
|
||||
|
||||
@@ -79,9 +79,9 @@ The JWT token contains user identity and permissions:
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 2.1 | It should clear the session when the user navigates to `/logout` | Integration | [ ] |
|
||||
| 2.2 | It should redirect to the login page after logout | Integration | [ ] |
|
||||
| 2.3 | It should remain idempotent when logging out without an active session | Integration | [ ] |
|
||||
| 2.1 | It should clear the session when the user navigates to `/logout` | Integration | [x] |
|
||||
| 2.2 | It should redirect to the login page after logout | Integration | [x] |
|
||||
| 2.3 | It should remain idempotent when logging out without an active session | Integration | [x] |
|
||||
|
||||
---
|
||||
|
||||
@@ -89,12 +89,12 @@ The JWT token contains user identity and permissions:
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 3.1 | It should allow admin users to assume another user's identity via signed JWT | Integration | [ ] |
|
||||
| 3.2 | It should validate the impersonation JWT signature with `:jwt-secret` and `:hs512` | Integration | [ ] |
|
||||
| 3.3 | It should reject expired impersonation JWTs | Integration | [ ] |
|
||||
| 3.4 | It should block non-admin users from accessing `/impersonate` | Integration | [ ] |
|
||||
| 3.5 | It should block unauthenticated users from accessing `/impersonate` | Integration | [ ] |
|
||||
| 3.6 | It should replace the admin's session with the impersonated user's session | Integration | [ ] |
|
||||
| 3.1 | It should allow admin users to assume another user's identity via signed JWT | Integration | [x] |
|
||||
| 3.2 | It should validate the impersonation JWT signature with `:jwt-secret` and `:hs512` | Integration | [x] |
|
||||
| 3.3 | It should reject expired impersonation JWTs | Integration | [x] |
|
||||
| 3.4 | It should block non-admin users from accessing `/impersonate` | Integration | [x] |
|
||||
| 3.5 | It should block unauthenticated users from accessing `/impersonate` | Integration | [x] |
|
||||
| 3.6 | It should replace the admin's session with the impersonated user's session | Integration | [x] |
|
||||
|
||||
---
|
||||
|
||||
@@ -104,26 +104,26 @@ The JWT token contains user identity and permissions:
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 4.1 | It should allow authenticated requests to proceed to protected routes | Integration | [ ] |
|
||||
| 4.2 | It should redirect unauthenticated users to `/login` with a `redirect-to` parameter | Integration | [ ] |
|
||||
| 4.3 | It should return `hx-redirect: /login` for unauthenticated HTMX requests | Integration | [ ] |
|
||||
| 4.1 | It should allow authenticated requests to proceed to protected routes | Integration | [x] |
|
||||
| 4.2 | It should redirect unauthenticated users to `/login` with a `redirect-to` parameter | Integration | [x] |
|
||||
| 4.3 | It should return `hx-redirect: /login` for unauthenticated HTMX requests | Integration | [x] |
|
||||
|
||||
### Admin Gate Behaviors
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 5.1 | It should allow admin requests to proceed to admin-only routes | Integration | [ ] |
|
||||
| 5.2 | It should redirect non-admin users to `/login` when accessing admin routes | Integration | [ ] |
|
||||
| 5.1 | It should allow admin requests to proceed to admin-only routes | Integration | [x] |
|
||||
| 5.2 | It should redirect non-admin users to `/login` when accessing admin routes | Integration | [x] |
|
||||
|
||||
### Session Version Behaviors
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 6.1 | It should invalidate sessions with outdated version numbers | Integration | [ ] |
|
||||
| 6.2 | It should redirect to `/login` when an outdated session accesses normal routes | Integration | [ ] |
|
||||
| 6.3 | It should return `hx-redirect: /login` for outdated sessions on HTMX routes | Integration | [ ] |
|
||||
| 6.4 | It should return 401 for outdated sessions on GraphQL routes | Integration | [ ] |
|
||||
| 6.5 | It should treat sessions without a version as outdated | Integration | [ ] |
|
||||
| 6.1 | It should invalidate sessions with outdated version numbers | Integration | [x] |
|
||||
| 6.2 | It should redirect to `/login` when an outdated session accesses normal routes | Integration | [x] |
|
||||
| 6.3 | It should return `hx-redirect: /login` for outdated sessions on HTMX routes | Integration | [x] |
|
||||
| 6.4 | It should return 401 for outdated sessions on GraphQL routes | Integration | [x] |
|
||||
| 6.5 | It should treat sessions without a version as outdated | Integration | [x] |
|
||||
|
||||
---
|
||||
|
||||
@@ -133,34 +133,34 @@ The JWT token contains user identity and permissions:
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 7.1 | It should generate a JWT containing the user's role and client access on login | Unit | [ ] |
|
||||
| 7.2 | It should compress the client list for admin users to fit in the JWT | Unit | [ ] |
|
||||
| 7.3 | It should compress the client list for read-only users to fit in the JWT | Unit | [ ] |
|
||||
| 7.4 | It should include a plain client list for regular users in the JWT | Unit | [ ] |
|
||||
| 7.5 | It should create API tokens with admin role and 1000-day expiration | Unit | [ ] |
|
||||
| 7.1 | It should generate a JWT containing the user's role and client access on login | Unit | [x] |
|
||||
| 7.2 | It should compress the client list for admin users to fit in the JWT | Unit | [x] |
|
||||
| 7.3 | It should compress the client list for read-only users to fit in the JWT | Unit | [x] |
|
||||
| 7.4 | It should include a plain client list for regular users in the JWT | Unit | [x] |
|
||||
| 7.5 | It should create API tokens with admin role and 1000-day expiration | Unit | [x] |
|
||||
|
||||
### Middleware Behaviors
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 8.1 | It should convert 401 responses to HTMX redirects for unauthenticated users | Integration | [ ] |
|
||||
| 8.1 | It should convert 401 responses to HTMX redirects for unauthenticated users | Integration | [x] |
|
||||
|
||||
### Role-Based Behaviors
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 9.1 | It should allow admin users to access all clients | Integration | [ ] |
|
||||
| 9.2 | It should allow regular users to access only their assigned clients | Integration | [ ] |
|
||||
| 9.3 | It should allow read-only users to access all clients with view-only permissions | Integration | [ ] |
|
||||
| 9.4 | It should handle admin users with no clients by providing an empty compressed list | Unit | [ ] |
|
||||
| 9.5 | It should handle regular users with no clients by providing an empty client vector | Unit | [ ] |
|
||||
| 9.1 | It should allow admin users to access all clients | Integration | [x] |
|
||||
| 9.2 | It should allow regular users to access only their assigned clients | Integration | [x] |
|
||||
| 9.3 | It should allow read-only users to access all clients with view-only permissions | Integration | [x] |
|
||||
| 9.4 | It should handle admin users with no clients by providing an empty compressed list | Unit | [x] |
|
||||
| 9.5 | It should handle regular users with no clients by providing an empty client vector | Unit | [x] |
|
||||
|
||||
### Security Behaviors
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 10.1 | It should reject tampered JWTs during impersonation | Integration | [ ] |
|
||||
| 10.2 | It should treat sessions with nil identity as unauthenticated | Integration | [ ] |
|
||||
| 10.1 | It should reject tampered JWTs during impersonation | Integration | [x] |
|
||||
| 10.2 | It should treat sessions with nil identity as unauthenticated | Integration | [x] |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -50,21 +50,21 @@ All company pages listen for `clientSelected from:body` event and refresh `#app-
|
||||
| 1.3 | It should display the company address (street, city, state, zip) when address data exists | UI | [ ] |
|
||||
| 1.4 | It should omit missing address fields without showing error placeholders | UI | [ ] |
|
||||
| 1.5 | It should show a "Download vendor list" button | UI | [ ] |
|
||||
| 1.6 | It should download a CSV/Excel export when the download button is clicked | Integration | [ ] |
|
||||
| 1.6 | It should download a CSV/Excel export when the download button is clicked | Integration | [x] |
|
||||
|
||||
### Signature Behaviors
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 2.1 | It should show the signature section only when the user has signature edit permission | Integration | [ ] |
|
||||
| 2.1 | It should show the signature section only when the user has signature edit permission | Integration | [x] |
|
||||
| 2.2 | It should display the saved signature image when one exists | UI | [ ] |
|
||||
| 2.3 | It should show a "New signature" button that enables drawing mode on a canvas | UI | [ ] |
|
||||
| 2.4 | It should show a "Clear" button that clears the canvas while in drawing mode | UI | [ ] |
|
||||
| 2.5 | It should show an "Accept" button that submits the drawn signature | UI | [ ] |
|
||||
| 2.6 | It should reject invalid signature image data with a validation error | Unit + Integration | [ ] |
|
||||
| 2.6 | It should reject invalid signature image data with a validation error | Unit + Integration | [x] |
|
||||
| 2.7 | It should provide a drag-and-drop zone for uploading JPEG signature files | UI | [ ] |
|
||||
| 2.8 | It should change the drop zone background color on hover | UI | [ ] |
|
||||
| 2.9 | It should refresh the signature section with the uploaded image on successful upload | Integration | [ ] |
|
||||
| 2.9 | It should refresh the signature section with the uploaded image on successful upload | Integration | [x] |
|
||||
|
||||
---
|
||||
|
||||
@@ -74,7 +74,7 @@ All company pages listen for `clientSelected from:body` event and refresh `#app-
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 3.1 | It should display vendors who received $600 or more in check payments during the current tax year | Integration | [ ] |
|
||||
| 3.1 | It should display vendors who received $600 or more in check payments during the current tax year | Integration | [x] |
|
||||
| 3.2 | It should show grid columns: Client, Vendor Name, TIN, Expense Account, Address, Paid | UI | [ ] |
|
||||
| 3.3 | It should display the vendor's legal entity name as a subtitle under the vendor name | UI | [ ] |
|
||||
| 3.4 | It should show a 1099 type pill badge when a 1099 type is set | UI | [ ] |
|
||||
@@ -82,15 +82,15 @@ All company pages listen for `clientSelected from:body` event and refresh `#app-
|
||||
| 3.6 | It should show "No address" placeholder when the vendor has no address | UI | [ ] |
|
||||
| 3.7 | It should display the total paid amount as a pill badge rounded to the nearest dollar | UI | [ ] |
|
||||
| 3.8 | It should show an edit icon button on each row | UI | [ ] |
|
||||
| 3.9 | It should show vendors shared across multiple clients in each client's context | Integration | [ ] |
|
||||
| 3.9 | It should show vendors shared across multiple clients in each client's context | Integration | [x] |
|
||||
| 3.10 | It should show an empty grid when no vendors received $600+ in checks during the tax year | UI | [ ] |
|
||||
|
||||
### Filtering & Sorting Behaviors
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 4.1 | It should support standard grid query params (sort, pagination, search) | Integration | [ ] |
|
||||
| 4.2 | It should default sort by client code then amount | Integration | [ ] |
|
||||
| 4.1 | It should support standard grid query params (sort, pagination, search) | Integration | [x] |
|
||||
| 4.2 | It should default sort by client code then amount | Integration | [x] |
|
||||
|
||||
### Edit Behaviors
|
||||
|
||||
@@ -98,12 +98,12 @@ All company pages listen for `clientSelected from:body` event and refresh `#app-
|
||||
|---|----------|---------------|--------|
|
||||
| 5.1 | It should open a vendor edit dialog in a modal when the edit icon is clicked | UI | [ ] |
|
||||
| 5.2 | It should display address fields (Street 1, Street 2, City, State, ZIP) in the dialog | UI | [ ] |
|
||||
| 5.3 | It should validate the ZIP code as 5 digits or empty | Unit + Integration | [ ] |
|
||||
| 5.3 | It should validate the ZIP code as 5 digits or empty | Unit + Integration | [x] |
|
||||
| 5.4 | It should allow entering either a legal entity name or first/middle/last name | UI | [ ] |
|
||||
| 5.5 | It should allow entering a TIN and selecting TIN type (EIN or SSN) | UI | [ ] |
|
||||
| 5.6 | It should allow selecting a 1099 type from a dropdown | UI | [ ] |
|
||||
| 5.7 | It should close the modal and refresh the row with a flash highlight on successful save | Integration | [ ] |
|
||||
| 5.8 | It should null the address if all address fields are empty and no existing address | Integration | [ ] |
|
||||
| 5.7 | It should close the modal and refresh the row with a flash highlight on successful save | Integration | [x] |
|
||||
| 5.8 | It should null the address if all address fields are empty and no existing address | Integration | [x] |
|
||||
|
||||
---
|
||||
|
||||
@@ -115,10 +115,10 @@ All company pages listen for `clientSelected from:body` event and refresh `#app-
|
||||
|---|----------|---------------|--------|
|
||||
| 6.1 | It should display a bar chart of expenses grouped by top 20 expense accounts over the last 8 weeks | UI | [ ] |
|
||||
| 6.2 | It should show week ranges (Monday-Sunday) formatted as dates on the X-axis | UI | [ ] |
|
||||
| 6.3 | It should provide a vendor typeahead to filter expenses to a specific vendor | Integration | [ ] |
|
||||
| 6.4 | It should provide an expense account typeahead to filter to a specific account | Integration | [ ] |
|
||||
| 6.5 | It should refresh the chart when filters change | Integration | [ ] |
|
||||
| 6.6 | It should default to last 65 days of data but display last 8 weeks | Integration | [ ] |
|
||||
| 6.3 | It should provide a vendor typeahead to filter expenses to a specific vendor | Integration | [x] |
|
||||
| 6.4 | It should provide an expense account typeahead to filter to a specific account | Integration | [x] |
|
||||
| 6.5 | It should refresh the chart when filters change | Integration | [x] |
|
||||
| 6.6 | It should default to last 65 days of data but display last 8 weeks | Integration | [x] |
|
||||
|
||||
### Invoice Totals Behaviors
|
||||
|
||||
@@ -126,10 +126,10 @@ All company pages listen for `clientSelected from:body` event and refresh `#app-
|
||||
|---|----------|---------------|--------|
|
||||
| 7.1 | It should display a grid of total invoice amounts per vendor per company | UI | [ ] |
|
||||
| 7.2 | It should provide start and end date range filters | UI | [ ] |
|
||||
| 7.3 | It should default the date range to the last 30 days | Integration | [ ] |
|
||||
| 7.3 | It should default the date range to the last 30 days | Integration | [x] |
|
||||
| 7.4 | It should show the vendor name in a sticky left column | UI | [ ] |
|
||||
| 7.5 | It should show "-" for zero amounts | UI | [ ] |
|
||||
| 7.6 | It should push filter changes to browser history | Integration | [ ] |
|
||||
| 7.6 | It should push filter changes to browser history | Integration | [x] |
|
||||
|
||||
---
|
||||
|
||||
@@ -139,7 +139,7 @@ All company pages listen for `clientSelected from:body` event and refresh `#app-
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 8.1 | It should show the reconciliation navigation link only when the user has reconciliation report permission | Integration | [ ] |
|
||||
| 8.1 | It should show the reconciliation navigation link only when the user has reconciliation report permission | Integration | [x] |
|
||||
| 8.2 | It should require start and end dates to be submitted via a "Run" button | UI | [ ] |
|
||||
| 8.3 | It should show a "Please choose a time range to run the report" message when no dates are selected | UI | [ ] |
|
||||
|
||||
@@ -166,7 +166,7 @@ All company pages listen for `clientSelected from:body` event and refresh `#app-
|
||||
| 9.2 | It should show a red pill with error message tooltip when any linked bank account has failed or unauthorized status | UI | [ ] |
|
||||
| 9.3 | It should show a green "Success" pill when all accounts are healthy | UI | [ ] |
|
||||
| 9.4 | It should display linked accounts with name, masked number, last synced date, and identicon | UI | [ ] |
|
||||
| 9.5 | It should support sorting by external ID and Plaid bank status | Integration | [ ] |
|
||||
| 9.5 | It should support sorting by external ID and Plaid bank status | Integration | [x] |
|
||||
| 9.6 | It should show an empty grid when no bank accounts are linked | UI | [ ] |
|
||||
|
||||
### Link Behaviors
|
||||
@@ -176,8 +176,8 @@ All company pages listen for `clientSelected from:body` event and refresh `#app-
|
||||
| 10.1 | It should show a "Link account" button when a client is selected | UI | [ ] |
|
||||
| 10.2 | It should hide the link button when no client is selected | UI | [ ] |
|
||||
| 10.3 | It should open a Plaid Link modal when the link button is clicked | UI | [ ] |
|
||||
| 10.4 | It should create the Plaid item and accounts in the system after successful linking | Integration | [ ] |
|
||||
| 10.5 | It should redirect back to the Plaid page after successful account linking | Integration | [ ] |
|
||||
| 10.4 | It should create the Plaid item and accounts in the system after successful linking | Integration | SKIPPED |
|
||||
| 10.5 | It should redirect back to the Plaid page after successful account linking | Integration | SKIPPED |
|
||||
|
||||
### Re-authenticate Behaviors
|
||||
|
||||
@@ -185,7 +185,7 @@ All company pages listen for `clientSelected from:body` event and refresh `#app-
|
||||
|---|----------|---------------|--------|
|
||||
| 11.1 | It should show a "Reauthenticate" button on each row | UI | [ ] |
|
||||
| 11.2 | It should open Plaid Link in update mode when reauthenticate is clicked | UI | [ ] |
|
||||
| 11.3 | It should refresh the row after successful reauthentication | Integration | [ ] |
|
||||
| 11.3 | It should refresh the row after successful reauthentication | Integration | SKIPPED |
|
||||
|
||||
---
|
||||
|
||||
@@ -199,7 +199,7 @@ All company pages listen for `clientSelected from:body` event and refresh `#app-
|
||||
| 12.2 | It should hide the Client column when the user has only one client | UI | [ ] |
|
||||
| 12.3 | It should show a green pill for success status and a yellow pill for other statuses | UI | [ ] |
|
||||
| 12.4 | It should display linked accounts with name and number | UI | [ ] |
|
||||
| 12.5 | It should support sorting by status, client, provider account, and last updated | Integration | [ ] |
|
||||
| 12.5 | It should support sorting by status, client, provider account, and last updated | Integration | [x] |
|
||||
| 12.6 | It should show an empty grid when no bank accounts are linked | UI | [ ] |
|
||||
|
||||
### Link Behaviors
|
||||
@@ -209,7 +209,7 @@ All company pages listen for `clientSelected from:body` event and refresh `#app-
|
||||
| 13.1 | It should show a "Link new account" button | UI | [ ] |
|
||||
| 13.2 | It should disable the link button and show helper text when no client is selected | UI | [ ] |
|
||||
| 13.3 | It should open a Yodlee Fastlink modal when the link button is clicked | UI | [ ] |
|
||||
| 13.4 | It should display an error notification and close the modal after 3 seconds when Yodlee returns an error | Integration | [ ] |
|
||||
| 13.4 | It should display an error notification and close the modal after 3 seconds when Yodlee returns an error | Integration | SKIPPED |
|
||||
|
||||
### Re-authenticate Behaviors
|
||||
|
||||
@@ -217,15 +217,15 @@ All company pages listen for `clientSelected from:body` event and refresh `#app-
|
||||
|---|----------|---------------|--------|
|
||||
| 14.1 | It should show a "Reauthenticate" button per row | UI | [ ] |
|
||||
| 14.2 | It should open Fastlink in edit mode when reauthenticate is clicked | UI | [ ] |
|
||||
| 14.3 | It should refresh the row after successful reauthentication | Integration | [ ] |
|
||||
| 14.3 | It should refresh the row after successful reauthentication | Integration | SKIPPED |
|
||||
|
||||
### Admin Behaviors
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 15.1 | It should show a refresh button on each row for admin users | Integration | [ ] |
|
||||
| 15.2 | It should trigger a Yodlee account refresh when the refresh button is clicked | Integration | [ ] |
|
||||
| 15.3 | It should refresh the row after successful Yodlee refresh | Integration | [ ] |
|
||||
| 15.1 | It should show a refresh button on each row for admin users | Integration | SKIPPED |
|
||||
| 15.2 | It should trigger a Yodlee account refresh when the refresh button is clicked | Integration | SKIPPED |
|
||||
| 15.3 | It should refresh the row after successful Yodlee refresh | Integration | SKIPPED |
|
||||
|
||||
---
|
||||
|
||||
@@ -244,15 +244,15 @@ All company pages listen for `clientSelected from:body` event and refresh `#app-
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 17.1 | It should provide a download link to the report file on each row | UI | [ ] |
|
||||
| 17.2 | It should show a delete button on each row for admin users | Integration | [ ] |
|
||||
| 17.3 | It should delete the report and its file when the delete button is clicked | Integration | [ ] |
|
||||
| 17.2 | It should show a delete button on each row for admin users | Integration | [x] |
|
||||
| 17.3 | It should delete the report and its file when the delete button is clicked | Integration | [x] |
|
||||
|
||||
### Filtering & Sorting Behaviors
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 18.1 | It should support filtering by date range and client | Integration | [ ] |
|
||||
| 18.2 | It should support sorting by client, created date, creator, and name | Integration | [ ] |
|
||||
| 18.1 | It should support filtering by date range and client | Integration | [x] |
|
||||
| 18.2 | It should support sorting by client, created date, creator, and name | Integration | [x] |
|
||||
|
||||
---
|
||||
|
||||
@@ -262,26 +262,26 @@ All company pages listen for `clientSelected from:body` event and refresh `#app-
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 19.1 | It should refresh page content with a 300ms swap animation when the user switches clients | Integration | [ ] |
|
||||
| 19.1 | It should refresh page content with a 300ms swap animation when the user switches clients | Integration | [x] |
|
||||
| 19.2 | It should show appropriate placeholder states when no client is selected on pages that require one | UI | [ ] |
|
||||
| 19.3 | It should operate 1099 and reports grids across all visible clients when no single client is selected | Integration | [ ] |
|
||||
| 19.3 | It should operate 1099 and reports grids across all visible clients when no single client is selected | Integration | [x] |
|
||||
|
||||
### Permission Behaviors
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 20.1 | It should block access to company pages for unauthenticated users | Integration | [ ] |
|
||||
| 20.2 | It should block access to company pages for users without client access | Integration | [ ] |
|
||||
| 20.3 | It should hide the signature section from users without signature edit permission | Integration | [ ] |
|
||||
| 20.4 | It should hide the reconciliation report navigation link from users without reconciliation report permission | Integration | [ ] |
|
||||
| 20.5 | It should hide the delete report button from non-admin users | Integration | [ ] |
|
||||
| 20.6 | It should hide the Yodlee refresh button from non-admin users | Integration | [ ] |
|
||||
| 20.1 | It should block access to company pages for unauthenticated users | Integration | [x] |
|
||||
| 20.2 | It should block access to company pages for users without client access | Integration | [x] |
|
||||
| 20.3 | It should hide the signature section from users without signature edit permission | Integration | [x] |
|
||||
| 20.4 | It should hide the reconciliation report navigation link from users without reconciliation report permission | Integration | [x] |
|
||||
| 20.5 | It should hide the delete report button from non-admin users | Integration | [x] |
|
||||
| 20.6 | It should hide the Yodlee refresh button from non-admin users | Integration | SKIPPED |
|
||||
|
||||
### Bank Account Search Behaviors
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 21.1 | It should provide a bank account typeahead for searching accounts belonging to a specific client | Integration | [ ] |
|
||||
| 21.1 | It should provide a bank account typeahead for searching accounts belonging to a specific client | Integration | [x] |
|
||||
| 21.2 | It should show "Please select a client" message when no client is selected in the bank account typeahead | UI | [ ] |
|
||||
|
||||
---
|
||||
|
||||
@@ -47,10 +47,10 @@ The dashboard is restricted to admin users:
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 1.1 | It should render the main dashboard page with navigation, client selector, and "Dashboard" breadcrumb for admin users | UI | [ ] |
|
||||
| 1.2 | It should display six stub cards with loading spinners for progressive rendering | UI | [ ] |
|
||||
| 1.3 | It should trigger independent HTMX requests to load each card's content on page load | Integration | [ ] |
|
||||
| 1.4 | It should progressively replace stub cards with actual data as responses arrive | UI | [ ] |
|
||||
| 1.1 | It should render the main dashboard page with navigation, client selector, and "Dashboard" breadcrumb for admin users | UI | SKIPPED |
|
||||
| 1.2 | It should display six stub cards with loading spinners for progressive rendering | UI | SKIPPED |
|
||||
| 1.3 | It should trigger independent HTMX requests to load each card's content on page load | Integration | SKIPPED |
|
||||
| 1.4 | It should progressively replace stub cards with actual data as responses arrive | UI | SKIPPED |
|
||||
|
||||
---
|
||||
|
||||
@@ -60,14 +60,14 @@ The dashboard is restricted to admin users:
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 2.1 | It should display each client's name, account name, ledger balance, and last sync time | UI | [ ] |
|
||||
| 2.2 | It should exclude bank accounts with cash type from the display | Integration | [ ] |
|
||||
| 2.3 | It should format ledger balances as currency ($X,XXX.XX) | Unit + UI | [ ] |
|
||||
| 2.4 | It should display the last sync timestamp in standard time format when present | Unit + UI | [ ] |
|
||||
| 2.5 | It should display Intuit balance and sync time for Intuit-linked accounts | UI | [ ] |
|
||||
| 2.6 | It should display Yodlee available balance, sync time, and pending balance for Yodlee-linked accounts | UI | [ ] |
|
||||
| 2.7 | It should display Plaid balance and sync time for Plaid-linked accounts | UI | [ ] |
|
||||
| 2.8 | It should display $0.00 for missing or null balances | Unit + UI | [ ] |
|
||||
| 2.1 | It should display each client's name, account name, ledger balance, and last sync time | UI | SKIPPED |
|
||||
| 2.2 | It should exclude bank accounts with cash type from the display | Integration | [x] |
|
||||
| 2.3 | It should format ledger balances as currency ($X,XXX.XX) | Unit + UI | SKIPPED |
|
||||
| 2.4 | It should display the last sync timestamp in standard time format when present | Unit + UI | SKIPPED |
|
||||
| 2.5 | It should display Intuit balance and sync time for Intuit-linked accounts | UI | SKIPPED |
|
||||
| 2.6 | It should display Yodlee available balance, sync time, and pending balance for Yodlee-linked accounts | UI | SKIPPED |
|
||||
| 2.7 | It should display Plaid balance and sync time for Plaid-linked accounts | UI | SKIPPED |
|
||||
| 2.8 | It should display $0.00 for missing or null balances | Unit + UI | SKIPPED |
|
||||
|
||||
---
|
||||
|
||||
@@ -77,14 +77,14 @@ The dashboard is restricted to admin users:
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 3.1 | It should display a bar chart of gross sales for the last 14 days | UI | [ ] |
|
||||
| 3.2 | It should render an empty bar chart when no sales orders exist in the date range | UI | [ ] |
|
||||
| 3.1 | It should display a bar chart of gross sales for the last 14 days | UI | SKIPPED |
|
||||
| 3.2 | It should render an empty bar chart when no sales orders exist in the date range | UI | SKIPPED |
|
||||
|
||||
### Data Behaviors
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 3.3 | It should query and sum sales order totals by date for the selected clients | Integration | [ ] |
|
||||
| 3.3 | It should query and sum sales order totals by date for the selected clients | Integration | [x] |
|
||||
|
||||
---
|
||||
|
||||
@@ -94,14 +94,14 @@ The dashboard is restricted to admin users:
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 4.1 | It should display a pie chart of the top 5 expense accounts for the last month | UI | [ ] |
|
||||
| 4.2 | It should render an empty pie chart when no invoices with expense accounts exist in the date range | UI | [ ] |
|
||||
| 4.1 | It should display a pie chart of the top 5 expense accounts for the last month | UI | SKIPPED |
|
||||
| 4.2 | It should render an empty pie chart when no invoices with expense accounts exist in the date range | UI | SKIPPED |
|
||||
|
||||
### Data Behaviors
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 4.3 | It should sum expense amounts by account name for the selected clients | Integration | [ ] |
|
||||
| 4.3 | It should sum expense amounts by account name for the selected clients | Integration | [x] |
|
||||
|
||||
---
|
||||
|
||||
@@ -111,14 +111,14 @@ The dashboard is restricted to admin users:
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 5.1 | It should display income and expenses aggregated by category (sales, COGS, payroll, controllable, fixed overhead, ownership controllable) | UI | [ ] |
|
||||
| 5.2 | It should show $0.00 for both income and expenses when no data exists for the period | UI | [ ] |
|
||||
| 5.1 | It should display income and expenses aggregated by category (sales, COGS, payroll, controllable, fixed overhead, ownership controllable) | UI | SKIPPED |
|
||||
| 5.2 | It should show $0.00 for both income and expenses when no data exists for the period | UI | SKIPPED |
|
||||
|
||||
### Data Behaviors
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 5.3 | It should query P&L data via GraphQL for the selected clients and last month | Integration | [ ] |
|
||||
| 5.3 | It should query P&L data via GraphQL for the selected clients and last month | Integration | [x] |
|
||||
|
||||
---
|
||||
|
||||
@@ -128,18 +128,18 @@ The dashboard is restricted to admin users:
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 6.1 | It should display the count of unpaid invoices when the count is non-zero | UI | [ ] |
|
||||
| 6.2 | It should display the count of uncategorized transactions requiring feedback when the count is non-zero | UI | [ ] |
|
||||
| 6.3 | It should provide a "Pay now" link for unpaid invoices linking to the unpaid invoices page with year date range | UI | [ ] |
|
||||
| 6.4 | It should provide a "Review now" link for uncategorized transactions linking to the requires-feedback page | UI | [ ] |
|
||||
| 6.5 | It should hide task sections entirely when their respective counts are zero | Integration | [ ] |
|
||||
| 6.1 | It should display the count of unpaid invoices when the count is non-zero | UI | SKIPPED |
|
||||
| 6.2 | It should display the count of uncategorized transactions requiring feedback when the count is non-zero | UI | SKIPPED |
|
||||
| 6.3 | It should provide a "Pay now" link for unpaid invoices linking to the unpaid invoices page with year date range | UI | SKIPPED |
|
||||
| 6.4 | It should provide a "Review now" link for uncategorized transactions linking to the requires-feedback page | UI | SKIPPED |
|
||||
| 6.5 | It should hide task sections entirely when their respective counts are zero | Integration | [x] |
|
||||
|
||||
### Data Behaviors
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 6.6 | It should query Datomic for invoices with unpaid status for the selected clients | Integration | [ ] |
|
||||
| 6.7 | It should query Datomic for transactions with requires-feedback approval status for the selected clients | Integration | [ ] |
|
||||
| 6.6 | It should query Datomic for invoices with unpaid status for the selected clients | Integration | [x] |
|
||||
| 6.7 | It should query Datomic for transactions with requires-feedback approval status for the selected clients | Integration | [x] |
|
||||
|
||||
---
|
||||
|
||||
@@ -149,17 +149,17 @@ The dashboard is restricted to admin users:
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 7.1 | It should display a bar chart breaking down expenses by account | UI | [ ] |
|
||||
| 7.2 | It should render an empty chart when no expense data exists | UI | [ ] |
|
||||
| 7.3 | It should provide Vendor and Account typeahead filters | UI | [ ] |
|
||||
| 7.1 | It should display a bar chart breaking down expenses by account | UI | SKIPPED |
|
||||
| 7.2 | It should render an empty chart when no expense data exists | UI | SKIPPED |
|
||||
| 7.3 | It should provide Vendor and Account typeahead filters | UI | SKIPPED |
|
||||
|
||||
### Data Behaviors
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 7.4 | It should reload the chart with filtered data when filter selections change | Integration | [ ] |
|
||||
| 7.5 | It should update the URL with filter query parameters via hx-push-url | Integration | [ ] |
|
||||
| 7.6 | It should exclude voided invoices from the breakdown | Integration | [ ] |
|
||||
| 7.4 | It should reload the chart with filtered data when filter selections change | Integration | SKIPPED |
|
||||
| 7.5 | It should update the URL with filter query parameters via hx-push-url | Integration | SKIPPED |
|
||||
| 7.6 | It should exclude voided invoices from the breakdown | Integration | [x] |
|
||||
|
||||
---
|
||||
|
||||
@@ -169,9 +169,9 @@ The dashboard is restricted to admin users:
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 8.1 | It should filter the expense breakdown chart by vendor selection | Integration | [ ] |
|
||||
| 8.2 | It should filter the expense breakdown chart by expense account selection | Integration | [ ] |
|
||||
| 8.3 | It should trigger an HTMX request to reload the chart when any filter changes | Integration | [ ] |
|
||||
| 8.1 | It should filter the expense breakdown chart by vendor selection | Integration | SKIPPED |
|
||||
| 8.2 | It should filter the expense breakdown chart by expense account selection | Integration | SKIPPED |
|
||||
| 8.3 | It should trigger an HTMX request to reload the chart when any filter changes | Integration | SKIPPED |
|
||||
|
||||
---
|
||||
|
||||
@@ -179,14 +179,14 @@ The dashboard is restricted to admin users:
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 9.1 | It should update the dashboard content when the user selects different clients from the dropdown | UI | [ ] |
|
||||
| 9.2 | It should trigger a clientSelected event on the body when client selection changes | Integration | [ ] |
|
||||
| 9.3 | It should swap the dashboard content area with fresh content for the newly selected clients | Integration | [ ] |
|
||||
| 9.4 | It should re-fetch all card data with the new client context | Integration | [ ] |
|
||||
| 9.5 | It should limit reports to the first 20 selected clients from the valid set | Unit + Integration | [ ] |
|
||||
| 9.6 | It should display a yellow warning banner when more than 20 clients are selected | UI | [ ] |
|
||||
| 9.7 | It should persist the warning banner across client selection changes until fewer than 21 clients are selected | UI | [ ] |
|
||||
| 9.8 | It should trim the client set before executing any card data queries | Integration | [ ] |
|
||||
| 9.1 | It should update the dashboard content when the user selects different clients from the dropdown | UI | SKIPPED |
|
||||
| 9.2 | It should trigger a clientSelected event on the body when client selection changes | Integration | SKIPPED |
|
||||
| 9.3 | It should swap the dashboard content area with fresh content for the newly selected clients | Integration | SKIPPED |
|
||||
| 9.4 | It should re-fetch all card data with the new client context | Integration | SKIPPED |
|
||||
| 9.5 | It should limit reports to the first 20 selected clients from the valid set | Unit + Integration | [x] |
|
||||
| 9.6 | It should display a yellow warning banner when more than 20 clients are selected | UI | SKIPPED |
|
||||
| 9.7 | It should persist the warning banner across client selection changes until fewer than 21 clients are selected | UI | SKIPPED |
|
||||
| 9.8 | It should trim the client set before executing any card data queries | Integration | [x] |
|
||||
|
||||
---
|
||||
|
||||
@@ -194,10 +194,10 @@ The dashboard is restricted to admin users:
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 10.1 | It should load each card independently via separate HTMX requests | Integration | [ ] |
|
||||
| 10.2 | It should not prevent other cards from loading when one card endpoint fails | Integration | [ ] |
|
||||
| 10.3 | It should display a loading spinner on stub cards until data loads or a timeout occurs | UI | [ ] |
|
||||
| 10.4 | It should return appropriate HTTP status codes for card endpoint errors without breaking the page layout | Integration | [ ] |
|
||||
| 10.1 | It should load each card independently via separate HTMX requests | Integration | [x] |
|
||||
| 10.2 | It should not prevent other cards from loading when one card endpoint fails | Integration | [x] |
|
||||
| 10.3 | It should display a loading spinner on stub cards until data loads or a timeout occurs | UI | SKIPPED |
|
||||
| 10.4 | It should return appropriate HTTP status codes for card endpoint errors without breaking the page layout | Integration | [x] |
|
||||
|
||||
---
|
||||
|
||||
@@ -207,21 +207,21 @@ The dashboard is restricted to admin users:
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 11.1 | It should allow only admin users to access the dashboard page and card endpoints | Integration | [ ] |
|
||||
| 11.2 | It should redirect non-admin authenticated users to /login with a 302 status | Integration | [ ] |
|
||||
| 11.3 | It should redirect unauthenticated users to /login with a redirect-to parameter | Integration | [ ] |
|
||||
| 11.4 | It should verify admin role via middleware before executing any data queries | Integration | [ ] |
|
||||
| 11.1 | It should allow only admin users to access the dashboard page and card endpoints | Integration | [x] |
|
||||
| 11.2 | It should redirect non-admin authenticated users to /login with a 302 status | Integration | [x] |
|
||||
| 11.3 | It should redirect unauthenticated users to /login with a redirect-to parameter | Integration | [x] |
|
||||
| 11.4 | It should verify admin role via middleware before executing any data queries | Integration | [x] |
|
||||
|
||||
### Empty State Behaviors
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 12.1 | It should render the dashboard page when no clients are selected, with all cards showing empty states | UI | [ ] |
|
||||
| 12.2 | It should display an empty bank accounts list when no clients are selected | UI | [ ] |
|
||||
| 12.3 | It should display an empty sales chart when no clients are selected | UI | [ ] |
|
||||
| 12.4 | It should display an empty expense pie chart when no clients are selected | UI | [ ] |
|
||||
| 12.5 | It should show $0.00 income and expenses in the P&L card when no clients are selected | UI | [ ] |
|
||||
| 12.6 | It should hide all task sections when no clients are selected | UI | [ ] |
|
||||
| 12.1 | It should render the dashboard page when no clients are selected, with all cards showing empty states | UI | SKIPPED |
|
||||
| 12.2 | It should display an empty bank accounts list when no clients are selected | UI | SKIPPED |
|
||||
| 12.3 | It should display an empty sales chart when no clients are selected | UI | SKIPPED |
|
||||
| 12.4 | It should display an empty expense pie chart when no clients are selected | UI | SKIPPED |
|
||||
| 12.5 | It should show $0.00 income and expenses in the P&L card when no clients are selected | UI | SKIPPED |
|
||||
| 12.6 | It should hide all task sections when no clients are selected | UI | SKIPPED |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -205,7 +205,7 @@ Every mutating operation checks:
|
||||
| 13.4 | It should validate that custom payment amounts do not exceed the outstanding balance | Unit + Integration | [x] |
|
||||
| 13.5 | It should require a check number for handwritten checks | Integration | [x] |
|
||||
| 13.6 | It should block payment if the invoice date is before the client's locked-until date | Integration | [x] |
|
||||
| 13.7 | Given the user submits a check payment, when successful, then a PDF download link should be provided | Integration | [ ] |
|
||||
| 13.7 | Given the user submits a check payment, when successful, then a PDF download link should be provided | Integration | SKIPPED |
|
||||
|
||||
### Credit Payment
|
||||
|
||||
@@ -285,9 +285,9 @@ Every mutating operation checks:
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 20.1 | It should allow uploading CSV and PDF files via drag-and-drop | UI | [ ] |
|
||||
| 20.2 | It should parse CSV files directly | Integration | [ ] |
|
||||
| 20.3 | It should send PDF files to AWS Textract for OCR parsing when enabled | Integration | [ ] |
|
||||
| 20.4 | It should create invoices with pending import status | Integration | [ ] |
|
||||
| 20.2 | It should parse CSV files directly | Integration | [x] |
|
||||
| 20.3 | It should send PDF files to AWS Textract for OCR parsing when enabled | Integration | SKIPPED |
|
||||
| 20.4 | It should create invoices with pending import status | Integration | [x] |
|
||||
| 20.5 | It should display results with success/failure per file | UI | [ ] |
|
||||
| 20.6 | It should allow force-overriding client, vendor, location, and ChatGPT parsing mode | UI | [ ] |
|
||||
|
||||
@@ -316,8 +316,8 @@ Every mutating operation checks:
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 23.1 | It should allow uploading PDF files | UI | [ ] |
|
||||
| 23.2 | It should upload the file to S3 and start an AWS Textract job | Integration | [ ] |
|
||||
| 23.3 | It should poll every 5 seconds while the Textract job is in progress | Integration | [ ] |
|
||||
| 23.2 | It should upload the file to S3 and start an AWS Textract job | Integration | SKIPPED |
|
||||
| 23.3 | It should poll every 5 seconds while the Textract job is in progress | Integration | SKIPPED |
|
||||
| 23.4 | Given a successful Textract job, then it should display extracted fields with confidence scores | UI | [ ] |
|
||||
|
||||
### Field Extraction Behaviors
|
||||
@@ -325,8 +325,8 @@ Every mutating operation checks:
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 24.1 | It should extract total from AMOUNT_DUE or TOTAL fields | Unit | [x] |
|
||||
| 24.2 | It should extract customer from CUSTOMER_NUMBER or RECEIVER_NAME, falling back to Solr search | Unit + Integration | [ ] |
|
||||
| 24.3 | It should extract vendor from VENDOR_NAME, falling back to Solr search | Unit + Integration | [ ] |
|
||||
| 24.2 | It should extract customer from CUSTOMER_NUMBER or RECEIVER_NAME, falling back to Solr search | Unit + Integration | [x] |
|
||||
| 24.3 | It should extract vendor from VENDOR_NAME, falling back to Solr search | Unit + Integration | [x] |
|
||||
| 24.4 | It should extract date from INVOICE_RECEIPT_DATE, ORDER_DATE, or DELIVERY_DATE | Unit | [x] |
|
||||
| 24.5 | It should extract invoice number from INVOICE_RECEIPT_ID or PO_NUMBER | Unit | [x] |
|
||||
|
||||
@@ -337,7 +337,7 @@ Every mutating operation checks:
|
||||
| 25.1 | It should show a side-by-side layout with PDF preview and form | UI | [ ] |
|
||||
| 25.2 | It should display alternative values as clickable pills for each field | UI | [ ] |
|
||||
| 25.3 | It should require selecting client and vendor from alternatives (fields disabled until selected) | UI | [ ] |
|
||||
| 25.4 | Given the user saves, then it should create an invoice linked to the textract job | Integration | [ ] |
|
||||
| 25.4 | Given the user saves, then it should create an invoice linked to the textract job | Integration | [x] |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -59,8 +59,8 @@ Every mutating operation checks:
|
||||
| # | 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.2 | It should show the Client column only when multiple clients OR multiple locations are selected | Integration | [x] |
|
||||
| 1.3 | It should display the Vendor column, falling back to `alternate-description` when no vendor is present | Integration | [x] |
|
||||
| 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 | [ ] |
|
||||
@@ -78,41 +78,41 @@ Every mutating operation checks:
|
||||
|
||||
| # | 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.1 | It should filter entries by vendor typeahead selection | Integration | [x] |
|
||||
| 2.2 | It should filter entries by account typeahead selection | Integration | [x] |
|
||||
| 2.3 | It should filter entries by bank account via radio filter | Integration | [x] |
|
||||
| 2.4 | It should refresh the bank account filter when the client selection changes | Integration | [x] |
|
||||
| 2.5 | It should filter entries by date range | Integration | [x] |
|
||||
| 2.6 | It should filter entries by invoice number text search | Integration | [x] |
|
||||
| 2.7 | It should filter entries by account code range (gte/lte inputs) | Integration | [x] |
|
||||
| 2.8 | It should filter entries by amount range (gte/lte inputs) | Integration | [x] |
|
||||
| 2.9 | It should filter to unbalanced entries when "Show unbalanced" is checked | Integration | [x] |
|
||||
| 2.10 | It should support exact-match navigation to a specific entry by ID, bypassing other filters | Integration | [x] |
|
||||
| 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 | [ ] |
|
||||
| 2.12 | 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 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 | [ ] |
|
||||
| 3.1 | It should sort by Client ascending/descending | Integration | [x] |
|
||||
| 3.2 | It should sort by Vendor ascending/descending | Integration | [x] |
|
||||
| 3.3 | It should sort by Source ascending/descending | Integration | [x] |
|
||||
| 3.4 | It should sort by External ID ascending/descending | Integration | [x] |
|
||||
| 3.5 | It should sort by Date ascending/descending | Integration | [x] |
|
||||
| 3.6 | It should sort by Amount ascending/descending | Integration | [x] |
|
||||
| 3.7 | It should sort by Account ascending/descending | Integration | [x] |
|
||||
| 3.8 | It should default to Date ascending | Integration | [x] |
|
||||
| 3.9 | Given sorting by Vendor, then rows should be grouped with break headers | Integration | [x] |
|
||||
| 3.10 | Given sorting by Source, then rows should be grouped with break headers | Integration | [x] |
|
||||
| 3.11 | Given the user clicks a column header twice, then the sort direction should toggle | Integration | [x] |
|
||||
|
||||
### 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.1 | It should display 25 entries per page by default | Integration | [x] |
|
||||
| 4.2 | It should allow changing the per-page count | Integration | [x] |
|
||||
| 4.3 | It should show pagination controls at the bottom of the table | UI | [ ] |
|
||||
|
||||
### Row Action Behaviors
|
||||
@@ -128,8 +128,8 @@ Every mutating operation checks:
|
||||
|
||||
| # | 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 | [ ] |
|
||||
| 6.1 | It should export all matching entries with line-item-level rows | Integration | [x] |
|
||||
| 6.2 | It should include columns: ID, Client, Vendor, Source, External ID, Date, Amount, Account, Debit, Credit | Integration | [x] |
|
||||
|
||||
---
|
||||
|
||||
@@ -143,7 +143,7 @@ Every mutating operation checks:
|
||||
| 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.5 | It should show a total amount input requiring a value of at least $0.01 | Unit + Integration | [x] |
|
||||
| 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 | [ ] |
|
||||
|
||||
@@ -151,10 +151,10 @@ Every mutating operation checks:
|
||||
|
||||
| # | 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.1 | It should allow account typeahead search scoped to the selected client | Integration | [x] |
|
||||
| 8.2 | It should update the location dropdown based on the selected account's required location | Integration | [x] |
|
||||
| 8.3 | It should lock the location dropdown to a fixed location when the account requires it | Integration | [x] |
|
||||
| 8.4 | It should show all client locations when the account has no location restriction | Integration | [x] |
|
||||
| 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 | [ ] |
|
||||
|
||||
@@ -162,23 +162,23 @@ Every mutating operation checks:
|
||||
|
||||
| # | 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 | [ ] |
|
||||
| 9.1 | It should require a client | Unit + Integration | [x] |
|
||||
| 9.2 | It should require a valid date | Unit + Integration | [x] |
|
||||
| 9.3 | It should require a vendor | Unit + Integration | [x] |
|
||||
| 9.4 | It should require an amount of at least $0.01 | Unit + Integration | [x] |
|
||||
| 9.5 | It should require each line item to have an allowed account | Unit + Integration | [x] |
|
||||
| 9.6 | It should require each line item to have a location belonging to the account | Unit + Integration | [x] |
|
||||
| 9.7 | It should validate that debits sum to the total amount | Unit + Integration | [x] |
|
||||
| 9.8 | It should validate that credits sum to the total amount | Unit + Integration | [x] |
|
||||
| 9.9 | It should validate that debits and credits each equal the journal entry amount | Unit + Integration | [x] |
|
||||
| 9.10 | It should block saving when the entry date is on or before the client's `locked-until` date | Integration | [x] |
|
||||
|
||||
### 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.1 | It should generate an external ID in the format `manual-<uuid>` | Unit | [x] |
|
||||
| 10.2 | It should update the client's `ledger-last-change` timestamp | Integration | [x] |
|
||||
| 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 | [ ] |
|
||||
|
||||
@@ -192,41 +192,41 @@ Every mutating operation checks:
|
||||
|---|----------|---------------|--------|
|
||||
| 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 | [ ] |
|
||||
| 11.3 | It should parse tab-separated values with columns: Id, Client, Source, Vendor, Date, Account Code, Location, Debit, Credit | Integration | [x] |
|
||||
|
||||
### 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 | [ ] |
|
||||
| 12.1 | It should validate that all rows have required fields | Integration | [x] |
|
||||
| 12.2 | It should validate that dates are parseable | Unit + Integration | [x] |
|
||||
| 12.3 | It should validate that account codes are numeric or bank account strings | Unit + Integration | [x] |
|
||||
| 12.4 | It should validate that locations are 1-2 characters | Unit + Integration | [x] |
|
||||
| 12.5 | It should validate that debits and credits are valid money amounts | Unit + Integration | [x] |
|
||||
|
||||
### 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 | [ ] |
|
||||
| 13.1 | It should validate that the client code exists | Integration | [x] |
|
||||
| 13.2 | It should validate that the vendor exists, creating a hidden vendor if missing | Integration | [x] |
|
||||
| 13.3 | It should block entries for dates when the client is locked | Integration | [x] |
|
||||
| 13.4 | It should validate that debits and credits balance per entry | Unit + Integration | [x] |
|
||||
| 13.5 | It should warn when an entry totals $0.00 | Unit + Integration | [x] |
|
||||
| 13.6 | It should validate that the location belongs to the client | Integration | [x] |
|
||||
| 13.7 | It should validate that the account code exists | Integration | [x] |
|
||||
| 13.8 | It should validate that bank account codes belong to the client | Integration | [x] |
|
||||
| 13.9 | It should validate that account location requirements are satisfied | Integration | [x] |
|
||||
|
||||
### 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 | [ ] |
|
||||
| 14.1 | It should import successful entries | Integration | [x] |
|
||||
| 14.2 | It should ignore entries with warnings, removing them if they previously existed | Integration | [x] |
|
||||
| 14.3 | It should block import and show error counts when entries have errors | Integration | [x] |
|
||||
| 14.4 | It should retract existing entries by external ID before importing | Integration | [x] |
|
||||
| 14.5 | It should index imported entries in Solr asynchronously | Integration | SKIPPED |
|
||||
|
||||
---
|
||||
|
||||
@@ -237,7 +237,7 @@ Every mutating operation checks:
|
||||
| # | 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.2 | It should default to the first 5 customers when "all" is selected | Integration | [x] |
|
||||
| 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 | [ ] |
|
||||
@@ -248,11 +248,11 @@ Every mutating operation checks:
|
||||
|
||||
| # | 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 | [ ] |
|
||||
| 16.1 | It should compute running balances before generating the report | Integration | [x] |
|
||||
| 16.2 | It should query detailed account snapshots for each client and period end date | Integration | [x] |
|
||||
| 16.3 | It should calculate amounts as debits minus credits for assets, dividends, and expenses | Unit | [x] |
|
||||
| 16.4 | It should calculate amounts as credits minus debits for liabilities, equity, and revenue | Unit | [x] |
|
||||
| 16.5 | It should group data by client, location, and period | Integration | [x] |
|
||||
|
||||
### Report Output Behaviors
|
||||
|
||||
@@ -260,7 +260,7 @@ Every mutating operation checks:
|
||||
|---|----------|---------------|--------|
|
||||
| 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.3 | It should calculate percent of sales for each row | Unit | [x] |
|
||||
| 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 | [ ] |
|
||||
|
||||
@@ -268,17 +268,17 @@ Every mutating operation checks:
|
||||
|
||||
| # | 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 | [ ] |
|
||||
| 18.1 | It should warn when more than 20 clients are selected and truncate to 20 | Integration | [x] |
|
||||
| 18.2 | It should warn about unresolved ledger entries with missing numeric codes | Integration | [x] |
|
||||
| 18.3 | It should show sample links to admin history for invalid entries when the user has `:view :history` permission | Integration | [x] |
|
||||
|
||||
### 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.1 | It should generate a PDF with Calibri Light font at 6pt | Integration | SKIPPED |
|
||||
| 19.2 | It should upload the PDF to S3 at `reports/profit-and-loss/<uuid>/<name>.pdf` | Integration | SKIPPED |
|
||||
| 19.3 | It should persist a report record in Datomic | Integration | SKIPPED |
|
||||
| 19.4 | It should return a modal with a download link | UI | [ ] |
|
||||
|
||||
---
|
||||
@@ -290,7 +290,7 @@ Every mutating operation checks:
|
||||
| # | 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.2 | It should default to the first 5 customers when "all" is selected | Integration | [x] |
|
||||
| 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 | [ ] |
|
||||
@@ -300,10 +300,10 @@ Every mutating operation checks:
|
||||
|
||||
| # | 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 | [ ] |
|
||||
| 21.1 | It should compute running balances before generating the report | Integration | [x] |
|
||||
| 21.2 | It should query account snapshots as of each selected date | Integration | [x] |
|
||||
| 21.3 | It should group accounts into Assets, Liabilities, and Owner's Equity | Integration | [x] |
|
||||
| 21.4 | It should include Retained Earnings as net income across all P&L categories | Unit | [x] |
|
||||
|
||||
### Report Output Behaviors
|
||||
|
||||
@@ -319,15 +319,15 @@ Every mutating operation checks:
|
||||
|
||||
| # | 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 | [ ] |
|
||||
| 23.1 | It should warn when more than 20 clients are selected | Integration | [x] |
|
||||
| 23.2 | It should warn about unresolved ledger entries | Integration | [x] |
|
||||
|
||||
### 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.1 | It should generate a PDF and upload to S3 at `reports/balance-sheet/<uuid>/<name>.pdf` | Integration | SKIPPED |
|
||||
| 24.2 | It should persist a report record in Datomic | Integration | SKIPPED |
|
||||
| 24.3 | It should return a modal with a download link | UI | [ ] |
|
||||
|
||||
---
|
||||
@@ -339,7 +339,7 @@ Every mutating operation checks:
|
||||
| # | 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.2 | It should default to the first 5 customers when "all" is selected | Integration | [x] |
|
||||
| 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 | [ ] |
|
||||
@@ -348,9 +348,9 @@ Every mutating operation checks:
|
||||
|
||||
| # | 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 | [ ] |
|
||||
| 26.1 | It should query account snapshots as of period end plus one day | Integration | [x] |
|
||||
| 26.2 | It should group accounts into Operating Activities, Investment Activities, Financing Activities, and Cash | Integration | [x] |
|
||||
| 26.3 | It should calculate cash flow effect by adding or subtracting based on account code ranges | Unit | [x] |
|
||||
|
||||
### Report Output Behaviors
|
||||
|
||||
@@ -367,15 +367,15 @@ Every mutating operation checks:
|
||||
|
||||
| # | 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 | [ ] |
|
||||
| 28.1 | It should warn when more than 20 clients are selected | Integration | [x] |
|
||||
| 28.2 | It should warn about unresolved ledger entries | Integration | [x] |
|
||||
|
||||
### 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.1 | It should generate a PDF and upload to S3 at `reports/cash-flows/<uuid>/<name>.pdf` | Integration | SKIPPED |
|
||||
| 29.2 | It should persist a report record in Datomic | Integration | SKIPPED |
|
||||
| 29.3 | It should return a modal with a download link | UI | [ ] |
|
||||
|
||||
---
|
||||
@@ -387,7 +387,7 @@ Every mutating operation checks:
|
||||
| # | 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.2 | It should filter ledger entries by the clicked cell's filters: account code range, client, location, and date range | Integration | [x] |
|
||||
| 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 | [ ] |
|
||||
|
||||
@@ -395,9 +395,9 @@ Every mutating operation checks:
|
||||
|
||||
| # | 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 | [ ] |
|
||||
| 31.1 | It should use the same query schema as the main ledger list | Integration | [x] |
|
||||
| 31.2 | It should support sorting and pagination | Integration | [x] |
|
||||
| 31.3 | It should not push URL state on filter or sort changes | Integration | [x] |
|
||||
|
||||
---
|
||||
|
||||
@@ -407,46 +407,46 @@ Every mutating operation checks:
|
||||
|
||||
| # | 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 | [ ] |
|
||||
| 32.1 | It should call `upsert-running-balance` before querying to ensure cached balances are current | Integration | [x] |
|
||||
| 32.2 | It should use `detailed-account-snapshot` Datomic query for raw report data | Integration | [x] |
|
||||
| 32.3 | It should build account lookups per-client via `build-account-lookup` | Integration | [x] |
|
||||
| 32.4 | It should skip entries without numeric codes and warn about unresolved entries | Integration | [x] |
|
||||
|
||||
### 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.1 | It should support PDF export via `clj-pdf` for all three reports | Integration | SKIPPED |
|
||||
| 33.2 | It should use Calibri Light 6pt font on letter size for all PDF exports | Integration | SKIPPED |
|
||||
| 33.3 | It should upload PDFs to the S3 data bucket with a UUID-based key | Integration | SKIPPED |
|
||||
| 33.4 | It should persist report metadata to Datomic with name, client, key, URL, creator, and created timestamp | Integration | SKIPPED |
|
||||
| 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 | [ ] |
|
||||
| 34.1 | It should apply ledger list filters via HTMX on change with a 500ms debounce | Integration | [x] |
|
||||
| 34.2 | It should apply hot filters via HTMX on keyup with a 1000ms debounce | Integration | [x] |
|
||||
| 34.3 | It should refresh the bank account filter when client selection changes | Integration | [x] |
|
||||
| 34.4 | It should support multiple sort keys with ascending and descending direction | Integration | [x] |
|
||||
| 34.5 | It should default to date ascending sort | Integration | [x] |
|
||||
| 34.6 | It should bypass all other filters when an exact match ID filter is active | Integration | [x] |
|
||||
|
||||
### 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 | [ ] |
|
||||
| 35.1 | It should require an authenticated user for all ledger pages | Integration | [x] |
|
||||
| 35.2 | It should require `:read :ledger` permission for the main ledger page | Integration | [x] |
|
||||
| 35.3 | It should require `:edit :ledger` permission for new and edit journal entry | Integration | [x] |
|
||||
| 35.4 | It should require `:import :ledger` permission plus admin assertion for external import | Integration | [x] |
|
||||
| 35.5 | It should require `:read :profit-and-loss` permission for the P&L report | Integration | [x] |
|
||||
| 35.6 | It should require `:read :balance-sheet` permission for the balance sheet | Integration | [x] |
|
||||
| 35.7 | It should require `:read :cash-flows` permission for the cash flows report | Integration | [x] |
|
||||
| 35.8 | It should restrict users to clients they have permission for via `assert-can-see-client` | Integration | [x] |
|
||||
| 35.9 | It should require `:delete :invoice` permission for invoice void actions | Integration | [x] |
|
||||
| 35.10 | It should require `:edit :invoice` permission for invoice edit and unvoid actions | Integration | [x] |
|
||||
|
||||
### Empty State Behaviors
|
||||
|
||||
@@ -460,31 +460,31 @@ Every mutating operation checks:
|
||||
|
||||
| # | 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 | [ ] |
|
||||
| 37.1 | It should block creating journal entries for dates on or before the client's `locked-until` date | Integration | [x] |
|
||||
| 37.2 | It should reject external import entries for locked dates | Integration | [x] |
|
||||
|
||||
### 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.1 | It should compute debit and credit sums per entry for the "Show unbalanced" filter | Unit | [x] |
|
||||
| 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 | [ ] |
|
||||
| 39.1 | It should reject locations other than the fixed location for accounts with fixed locations | Integration | [x] |
|
||||
| 39.2 | It should reject "A" (all) location for accounts without location restrictions | Integration | [x] |
|
||||
| 39.3 | It should validate account location requirements on both the frontend location select and backend schema | Integration | [x] |
|
||||
|
||||
### 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 | [ ] |
|
||||
| 40.1 | It should recompute balances for dirty line items via `refresh-running-balance-cache` | Integration | [x] |
|
||||
| 40.2 | It should mark a changed entry's line items and subsequent entries as dirty | Integration | [x] |
|
||||
| 40.3 | It should skip recomputation for non-dirty entries | Integration | [x] |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -55,33 +55,33 @@ Line items are added and removed dynamically without page reload:
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 2.1 | It should require client selection | Integration | [ ] |
|
||||
| 2.2 | It should require invoice date | Integration | [ ] |
|
||||
| 2.3 | It should require recipient name in "To" field | Integration | [ ] |
|
||||
| 2.4 | It should require invoice number | Integration | [ ] |
|
||||
| 2.5 | It should require at least one line item with description, quantity, and unit price | Integration | [ ] |
|
||||
| 2.6 | It should make recipient address street2 optional | Unit | [ ] |
|
||||
| 2.7 | It should strip whitespace from street2 and treat empty as nil | Unit | [ ] |
|
||||
| 2.8 | It should coerce line items from nested form parameters into a vector | Unit | [ ] |
|
||||
| 2.1 | It should require client selection | Integration | [x] |
|
||||
| 2.2 | It should require invoice date | Integration | SKIPPED |
|
||||
| 2.3 | It should require recipient name in "To" field | Integration | SKIPPED |
|
||||
| 2.4 | It should require invoice number | Integration | SKIPPED |
|
||||
| 2.5 | It should require at least one line item with description, quantity, and unit price | Integration | SKIPPED |
|
||||
| 2.6 | It should make recipient address street2 optional | Unit | [x] |
|
||||
| 2.7 | It should strip whitespace from street2 and treat empty as nil | Unit | [x] |
|
||||
| 2.8 | It should coerce line items from nested form parameters into a vector | Unit | [x] |
|
||||
| 2.9 | It should display validation errors next to the offending fields | UI | [ ] |
|
||||
| 2.10 | It should redisplay the form with entered data preserved when validation fails | Integration | [ ] |
|
||||
| 2.10 | It should redisplay the form with entered data preserved when validation fails | Integration | [x] |
|
||||
|
||||
### Submission Behaviors
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 3.1 | It should filter out line items with empty descriptions before calculation | Unit | [ ] |
|
||||
| 3.2 | It should calculate each line item total as `unit-price * quantity` | Unit | [ ] |
|
||||
| 3.3 | It should calculate subtotal as the sum of all line item totals | Unit | [ ] |
|
||||
| 3.4 | It should calculate tax as `subtotal * (tax-rate / 100)` | Unit | [ ] |
|
||||
| 3.5 | It should calculate total as `subtotal + tax` | Unit | [ ] |
|
||||
| 3.6 | It should format monetary values as `$X,XXX.XX` strings before sending to Lambda | Unit | [ ] |
|
||||
| 3.7 | It should format the invoice date as `normal-date` string before sending to Lambda | Unit | [ ] |
|
||||
| 3.8 | It should invoke the `genpdf` Lambda function with a JSON payload | Integration | [ ] |
|
||||
| 3.9 | It should extract the S3 URL from the Lambda response | Integration | [ ] |
|
||||
| 3.1 | It should filter out line items with empty descriptions before calculation | Unit | [x] |
|
||||
| 3.2 | It should calculate each line item total as `unit-price * quantity` | Unit | [x] |
|
||||
| 3.3 | It should calculate subtotal as the sum of all line item totals | Unit | [x] |
|
||||
| 3.4 | It should calculate tax as `subtotal * (tax-rate / 100)` | Unit | [x] |
|
||||
| 3.5 | It should calculate total as `subtotal + tax` | Unit | [x] |
|
||||
| 3.6 | It should format monetary values as `$X,XXX.XX` strings before sending to Lambda | Unit | [x] |
|
||||
| 3.7 | It should format the invoice date as `normal-date` string before sending to Lambda | Unit | [x] |
|
||||
| 3.8 | It should invoke the `genpdf` Lambda function with a JSON payload | Integration | SKIPPED |
|
||||
| 3.9 | It should extract the S3 URL from the Lambda response | Integration | SKIPPED |
|
||||
| 3.10 | It should display a modal with "Download your invoice" and a link to the S3 URL | UI | [ ] |
|
||||
| 3.11 | Given the Lambda invocation fails, then it should display an error without showing a modal | Integration | [ ] |
|
||||
| 3.12 | Given all line items are empty, then subtotal should be `0.0`, tax should be `0.0`, and total should be `0.0` | Unit | [ ] |
|
||||
| 3.11 | Given the Lambda invocation fails, then it should display an error without showing a modal | Integration | SKIPPED |
|
||||
| 3.12 | Given all line items are empty, then subtotal should be `0.0`, tax should be `0.0`, and total should be `0.0` | Unit | [x] |
|
||||
|
||||
---
|
||||
|
||||
@@ -91,7 +91,7 @@ Line items are added and removed dynamically without page reload:
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 4.1 | It should fetch a new empty line item row via HTMX when "Add line" is clicked | Integration | [ ] |
|
||||
| 4.1 | It should fetch a new empty line item row via HTMX when "Add line" is clicked | Integration | [x] |
|
||||
| 4.2 | It should append the new row to the line items grid | UI | [ ] |
|
||||
| 4.3 | It should render each row with hidden db/id, description input, quantity money-input, unit-price money-input, and delete button | UI | [ ] |
|
||||
| 4.4 | It should allow adding multiple line items | UI | [ ] |
|
||||
@@ -109,10 +109,10 @@ Line items are added and removed dynamically without page reload:
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 6.1 | It should handle negative quantities in line item calculations | Unit | [ ] |
|
||||
| 6.2 | It should show `$0.00` for line items with zero unit price | Unit | [ ] |
|
||||
| 6.3 | It should format large monetary values with comma separators (e.g., `$1,234.56`) | Unit | [ ] |
|
||||
| 6.4 | It should format nil monetary values as `$0.00` | Unit | [ ] |
|
||||
| 6.1 | It should handle negative quantities in line item calculations | Unit | [x] |
|
||||
| 6.2 | It should show `$0.00` for line items with zero unit price | Unit | [x] |
|
||||
| 6.3 | It should format large monetary values with comma separators (e.g., `$1,234.56`) | Unit | [x] |
|
||||
| 6.4 | It should format nil monetary values as `$0.00` | Unit | [x] |
|
||||
|
||||
---
|
||||
|
||||
@@ -122,19 +122,19 @@ Line items are added and removed dynamically without page reload:
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 7.1 | It should invoke `genpdf` Lambda with a JSON payload containing invoice data | Integration | [ ] |
|
||||
| 7.2 | It should include formatted monetary strings in the Lambda payload | Unit | [ ] |
|
||||
| 7.3 | It should include the invoice date as a `normal-date` string in the Lambda payload | Unit | [ ] |
|
||||
| 7.4 | It should extract the S3 URL from a successful Lambda response | Integration | [ ] |
|
||||
| 7.1 | It should invoke `genpdf` Lambda with a JSON payload containing invoice data | Integration | SKIPPED |
|
||||
| 7.2 | It should include formatted monetary strings in the Lambda payload | Unit | SKIPPED |
|
||||
| 7.3 | It should include the invoice date as a `normal-date` string in the Lambda payload | Unit | SKIPPED |
|
||||
| 7.4 | It should extract the S3 URL from a successful Lambda response | Integration | SKIPPED |
|
||||
| 7.5 | It should present the S3 URL as a clickable download link in the modal | UI | [ ] |
|
||||
|
||||
### Error Handling Behaviors
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 8.1 | Given the Lambda returns invalid JSON, then it should propagate an error | Integration | [ ] |
|
||||
| 8.1 | Given the Lambda returns invalid JSON, then it should propagate an error | Integration | SKIPPED |
|
||||
| 8.2 | Given the S3 URL is inaccessible, then the link should still be presented but may fail on click | UI | [ ] |
|
||||
| 8.3 | Given a very large invoice payload, then Lambda payload size limits may apply | Integration | [ ] |
|
||||
| 8.3 | Given a very large invoice payload, then Lambda payload size limits may apply | Integration | SKIPPED |
|
||||
|
||||
---
|
||||
|
||||
@@ -144,26 +144,26 @@ Line items are added and removed dynamically without page reload:
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 9.1 | It should redirect unauthenticated users to `/login` | Integration | [ ] |
|
||||
| 9.2 | It should redirect unauthenticated users back to `/outgoing-invoice/new` after login | Integration | [ ] |
|
||||
| 9.3 | It should apply `wrap-secure` middleware to all routes | Integration | [ ] |
|
||||
| 9.4 | It should apply `wrap-trim-client-ids` middleware to requests | Integration | [ ] |
|
||||
| 9.1 | It should redirect unauthenticated users to `/login` | Integration | [x] |
|
||||
| 9.2 | It should redirect unauthenticated users back to `/outgoing-invoice/new` after login | Integration | [x] |
|
||||
| 9.3 | It should apply `wrap-secure` middleware to all routes | Integration | [x] |
|
||||
| 9.4 | It should apply `wrap-trim-client-ids` middleware to requests | Integration | [x] |
|
||||
|
||||
### Client Selection Behaviors
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 10.1 | It should populate the client typeahead from the `:company-search` endpoint | Integration | [ ] |
|
||||
| 10.2 | It should only show clients the authenticated user has access to | Integration | [ ] |
|
||||
| 10.1 | It should populate the client typeahead from the `:company-search` endpoint | Integration | [x] |
|
||||
| 10.2 | It should only show clients the authenticated user has access to | Integration | [x] |
|
||||
|
||||
### Tax Behaviors
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 11.1 | It should treat a whole number tax (e.g., 10) as 10% | Unit | [ ] |
|
||||
| 11.2 | It should treat a decimal tax (e.g., 8.25) as 8.25% | Unit | [ ] |
|
||||
| 11.3 | It should allow tax rates over 100% | Unit | [ ] |
|
||||
| 11.4 | It should calculate total equal to subtotal when tax is zero | Unit | [ ] |
|
||||
| 11.1 | It should treat a whole number tax (e.g., 10) as 10% | Unit | [x] |
|
||||
| 11.2 | It should treat a decimal tax (e.g., 8.25) as 8.25% | Unit | [x] |
|
||||
| 11.3 | It should allow tax rates over 100% | Unit | [x] |
|
||||
| 11.4 | It should calculate total equal to subtotal when tax is zero | Unit | [x] |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ Check printing involves:
|
||||
| # | 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.2 | It should show the Client column only when viewing payments for multiple clients | Integration | SKIPPED |
|
||||
| 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 | [ ] |
|
||||
@@ -66,41 +66,41 @@ Check printing involves:
|
||||
|
||||
| # | 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 | [ ] |
|
||||
| 2.1 | It should filter payments by vendor typeahead selection | Integration | [x] |
|
||||
| 2.2 | It should filter payments by date range | Integration | [x] |
|
||||
| 2.3 | It should filter payments by check number (exact match or partial text) | Integration | [x] |
|
||||
| 2.4 | It should filter payments by invoice number (exact match) | Integration | [x] |
|
||||
| 2.5 | It should filter payments by amount range (min/max) | Integration | [x] |
|
||||
| 2.6 | It should filter payments by payment type via radio cards (All, Cash, Check, Debit) | Integration | [x] |
|
||||
| 2.7 | It should support exact-match navigation to a specific payment by ID, bypassing other filters | Integration | [x] |
|
||||
| 2.8 | It should filter payments by status via route (`/payments/pending`, `/payments/cleared`, `/payments/voided`) | Integration | [x] |
|
||||
| 2.9 | It should apply all filters via HTMX with debounced triggers | Integration | SKIPPED |
|
||||
| 2.10 | It should combine all filters with AND logic | Integration | [x] |
|
||||
| 2.11 | It should use efficient time-bounded queries for date range filtering | Integration | SKIPPED |
|
||||
| 2.12 | It should parse check number search as Long when possible, falling back to exact string match | Unit + Integration | [x] |
|
||||
| 2.13 | Given multiple filters are applied, when the user changes one filter, then the table should refresh with the combined filter set | Integration | [x] |
|
||||
| 2.14 | It should bypass all other filters when exact-match ID is provided | Integration | [x] |
|
||||
|
||||
### 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 | [ ] |
|
||||
| 3.1 | It should sort by client name ascending/descending | Integration | [x] |
|
||||
| 3.2 | It should sort by vendor name ascending/descending | Integration | [x] |
|
||||
| 3.3 | It should sort by bank account ascending/descending | Integration | [x] |
|
||||
| 3.4 | It should sort by check number ascending/descending | Integration | [x] |
|
||||
| 3.5 | It should sort by date ascending/descending | Integration | [x] |
|
||||
| 3.6 | It should sort by amount ascending/descending | Integration | [x] |
|
||||
| 3.7 | It should sort by status ascending/descending | Integration | [x] |
|
||||
| 3.8 | Given the user clicks a column header twice, then the sort direction should toggle | Integration | [x] |
|
||||
|
||||
### 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 | [ ] |
|
||||
| 4.1 | It should display 25 payments 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 visible float and total float across all matching payments, not just the current page | Unit | [x] |
|
||||
|
||||
### Selection Behaviors
|
||||
|
||||
@@ -108,8 +108,8 @@ Check printing involves:
|
||||
|---|----------|---------------|--------|
|
||||
| 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 | [ ] |
|
||||
| 5.3 | It should allow selecting all filtered payments (up to 250) for bulk operations | Integration | SKIPPED |
|
||||
| 5.4 | Given payments are selected, when the user applies a filter, then the selection should be cleared | Integration | SKIPPED |
|
||||
|
||||
### Row Action Behaviors
|
||||
|
||||
@@ -118,16 +118,16 @@ Check printing involves:
|
||||
| 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 | [ ] |
|
||||
| 6.4 | It should block voiding cleared check payments | Integration | [x] |
|
||||
|
||||
### 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 | [ ] |
|
||||
| 7.1 | It should display a "Visible in float" pill showing the sum of pending payment amounts in the current filter view | Unit | [x] |
|
||||
| 7.2 | It should display a "Total in float" pill showing the sum of all pending payments for the selected client(s) | Unit | [x] |
|
||||
| 7.3 | It should exclude voided payments from float calculations | Unit | [x] |
|
||||
| 7.4 | It should include only pending status payments in float calculations | Unit | [x] |
|
||||
|
||||
---
|
||||
|
||||
@@ -137,10 +137,10 @@ Check printing involves:
|
||||
|---|----------|---------------|--------|
|
||||
| 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 | [ ] |
|
||||
| 8.3 | It should support "All selected" mode to void all payments matching current filters (up to 250) | Integration | [x] |
|
||||
| 8.4 | It should require admin permission for bulk void operations | Integration | [x] |
|
||||
| 8.5 | Given confirmation, when voiding, then the modal should close and a notification should show "Successfully voided X of Y payments" | Integration | SKIPPED |
|
||||
| 8.6 | It should skip payments that already have transactions and skip already-voided payments | Integration | [x] |
|
||||
|
||||
---
|
||||
|
||||
@@ -148,17 +148,17 @@ Check printing involves:
|
||||
|
||||
| # | 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 | [ ] |
|
||||
| 9.1 | It should generate physical check PDFs with MICR encoding at the bottom | Integration | SKIPPED |
|
||||
| 9.2 | It should include payee, amount in numbers and words, date, memo, bank info, and client signature image | Integration | SKIPPED |
|
||||
| 9.3 | It should generate voucher copies with full invoice details below the check | Integration | SKIPPED |
|
||||
| 9.4 | It should store check PDFs in S3 under `checks/{uuid}.pdf` | Integration | SKIPPED |
|
||||
| 9.5 | It should assign check numbers sequentially from the bank account's check number | Integration | [x] |
|
||||
| 9.6 | It should increment the bank account's check number by the number of vendors paid | Integration | [x] |
|
||||
| 9.7 | It should validate that the bank account has a starting check number | Integration | [x] |
|
||||
| 9.8 | It should merge multiple checks into a single PDF at `merged-checks/{uuid}.pdf` | Integration | SKIPPED |
|
||||
| 9.9 | It should group invoices by vendor, creating one check per vendor per batch | Integration | [x] |
|
||||
| 9.10 | It should validate that all invoices belong to the same client and the selected bank account belongs to the same client | Integration | [x] |
|
||||
| 9.11 | It should reject check creation if the total amount is <= $0.00 | Integration | [x] |
|
||||
|
||||
---
|
||||
|
||||
@@ -166,9 +166,9 @@ Check printing involves:
|
||||
|
||||
| # | 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 | [ ] |
|
||||
| 10.1 | It should create pending payments with `payment-type/debit` | Integration | [x] |
|
||||
| 10.2 | It should not generate check PDFs for ACH payments | Integration | [x] |
|
||||
| 10.3 | It should not create transactions for ACH payments | Integration | [x] |
|
||||
|
||||
---
|
||||
|
||||
@@ -176,10 +176,10 @@ Check printing involves:
|
||||
|
||||
| # | 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 | [ ] |
|
||||
| 11.1 | It should allow paying invoices from existing vendor credit with `payment-type/balance-credit` | Integration | [x] |
|
||||
| 11.2 | It should block balance credit payments when multiple vendors are selected | Integration | [x] |
|
||||
| 11.3 | It should offset positive-balance invoices against negative-balance invoices | Integration | [x] |
|
||||
| 11.4 | It should create a single cleared payment for the net amount, consuming credit invoices first-in | Integration | [x] |
|
||||
|
||||
---
|
||||
|
||||
@@ -187,10 +187,10 @@ Check printing involves:
|
||||
|
||||
| # | 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 | [ ] |
|
||||
| 12.1 | It should create payments with `payment-type/cash` automatically marked as cleared | Integration | [x] |
|
||||
| 12.2 | It should create an associated transaction with POSTED status | Integration | [x] |
|
||||
| 12.3 | It should use the account with numeric code 21000 for cash payment transactions | Integration | [x] |
|
||||
| 12.4 | It should set the payment date to the latest invoice date | Integration | [x] |
|
||||
|
||||
---
|
||||
|
||||
@@ -200,33 +200,33 @@ Check printing involves:
|
||||
|
||||
| # | 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 | [ ] |
|
||||
| 13.1 | It should allow voiding pending payments | Integration | [x] |
|
||||
| 13.2 | It should allow voiding cash, debit, and balance-credit payments even when cleared | Integration | [x] |
|
||||
| 13.3 | It should block voiding cleared check payments | Integration | [x] |
|
||||
| 13.4 | It should set the payment amount to 0.0 when voided | Integration | [x] |
|
||||
| 13.5 | It should set the payment status to voided | Integration | [x] |
|
||||
| 13.6 | It should remove all invoice-payment links when voiding | Integration | [x] |
|
||||
| 13.7 | It should restore invoice outstanding balances by adding back the invoice-payment amount | Integration | [x] |
|
||||
| 13.8 | It should revert invoice status to unpaid when restored balance becomes non-zero | Integration | [x] |
|
||||
| 13.9 | It should unlink associated transactions when voiding | Integration | [x] |
|
||||
|
||||
### 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 | [ ] |
|
||||
| 14.1 | It should require client visibility for viewing payments | Integration | [x] |
|
||||
| 14.2 | It should require client visibility for voiding individual payments | Integration | [x] |
|
||||
| 14.3 | It should require admin permission for bulk voiding payments | Integration | [x] |
|
||||
| 14.4 | It should allow viewing S3 check PDFs to all users who can see the payment | Integration | SKIPPED |
|
||||
|
||||
### 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.1 | It should block voiding payments dated before the client's locked-until date | Integration | [x] |
|
||||
| 15.2 | It should check lock dates on individual void operations | Integration | [x] |
|
||||
| 15.3 | It should check lock dates on bulk void operations | Integration | [x] |
|
||||
| 15.4 | It should exclude locked payments from bulk void results | Integration | [x] |
|
||||
| 15.5 | It should show a warning when some selected payments are locked | UI | [ ] |
|
||||
|
||||
---
|
||||
|
||||
@@ -48,9 +48,9 @@ Every mutating operation checks:
|
||||
### 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 | [ ] |
|
||||
| 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 | [ ] |
|
||||
@@ -59,34 +59,36 @@ Every mutating operation checks:
|
||||
### Filtering Behaviors
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 2.1 | It should filter sales orders by date range (start date / end date) | Integration | [ ] |
|
||||
| 2.2 | It should filter sales orders by total amount range (min / max) | Integration | [ ] |
|
||||
| 2.3 | It should filter sales orders by payment method via radio cards: All, Cash, Card, Gift Card, Other | Integration | [ ] |
|
||||
| 2.4 | It should filter sales orders by processor via radio cards: All, Square, Doordash, Uber Eats, Grubhub, Koala, EZCater, No Processor | Integration | [ ] |
|
||||
| 2.5 | It should filter sales orders by category text input matching order line item category | Integration | [ ] |
|
||||
| 2.6 | Given multiple filters are applied, when the user changes one filter, then the table should refresh with the combined filter set | Integration | [ ] |
|
||||
|---|---|---|---|
|
||||
| 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 | [ ] |
|
||||
| 3.2 | It should sort by date ascending/descending | Integration | [ ] |
|
||||
| 3.3 | It should sort by total amount ascending/descending | Integration | [ ] |
|
||||
| 3.4 | It should sort by tax amount ascending/descending | Integration | [ ] |
|
||||
| 3.5 | It should sort by tip amount ascending/descending | Integration | [ ] |
|
||||
| 3.6 | It should sort by source ascending/descending | Integration | [ ] |
|
||||
|---|---|---|---|
|
||||
| 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 | [ ] |
|
||||
| 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 | [ ] |
|
||||
| 4.2 | It should allow changing the per-page count | Integration | [ ] |
|
||||
| 4.3 | It should calculate the total amount and tax across ALL matching sales orders, not just the current page | Unit | [ ] |
|
||||
|---|---|---|---|
|
||||
| 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] |
|
||||
|
||||
---
|
||||
|
||||
@@ -95,9 +97,9 @@ Every mutating operation checks:
|
||||
### 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 | [ ] |
|
||||
| 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 | [ ] |
|
||||
@@ -105,28 +107,28 @@ Every mutating operation checks:
|
||||
### Filtering Behaviors
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 6.1 | It should filter expected deposits by date range | Integration | [ ] |
|
||||
| 6.2 | It should support exact match ID to jump to a specific record, showing a removable pill when active | Integration | [ ] |
|
||||
| 6.3 | Given multiple filters are applied, when the user changes one filter, then the table should refresh with the combined filter set | Integration | [ ] |
|
||||
|---|---|---|---|
|
||||
| 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 | [ ] |
|
||||
| 7.2 | It should sort by location ascending/descending | Integration | [ ] |
|
||||
| 7.3 | It should sort by date ascending/descending | Integration | [ ] |
|
||||
| 7.4 | It should sort by total amount ascending/descending | Integration | [ ] |
|
||||
| 7.5 | It should sort by fee amount ascending/descending | Integration | [ ] |
|
||||
| 7.6 | Given the user clicks a column header twice, then the sort direction should toggle | Integration | [ ] |
|
||||
|---|---|---|---|
|
||||
| 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 | [ ] |
|
||||
| 8.2 | It should allow changing the per-page count | Integration | [ ] |
|
||||
|---|---|---|---|
|
||||
| 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] |
|
||||
|
||||
---
|
||||
|
||||
@@ -135,9 +137,9 @@ Every mutating operation checks:
|
||||
### 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 | [ ] |
|
||||
| 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 | [ ] |
|
||||
@@ -145,29 +147,29 @@ Every mutating operation checks:
|
||||
### Filtering Behaviors
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 10.1 | It should filter tenders by date range | Integration | [ ] |
|
||||
| 10.2 | It should filter tenders by processor via radio cards: All, Square, Doordash, Uber Eats, Grubhub, Koala, EZCater, No Processor | Integration | [ ] |
|
||||
| 10.3 | It should filter tenders by total amount range (min / max) | Integration | [ ] |
|
||||
| 10.4 | Given multiple filters are applied, when the user changes one filter, then the table should refresh with the combined filter set | Integration | [ ] |
|
||||
|---|---|---|---|
|
||||
| 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 | [ ] |
|
||||
| 11.2 | It should sort by date ascending/descending | Integration | [ ] |
|
||||
| 11.3 | It should sort by total amount ascending/descending | Integration | [ ] |
|
||||
| 11.4 | It should sort by tip amount ascending/descending | Integration | [ ] |
|
||||
| 11.5 | It should sort by processor ascending/descending | Integration | [ ] |
|
||||
| 11.6 | Given the user clicks a column header twice, then the sort direction should toggle | Integration | [ ] |
|
||||
|---|---|---|---|
|
||||
| 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 | [ ] |
|
||||
| 12.2 | It should allow changing the per-page count | Integration | [ ] |
|
||||
|---|---|---|---|
|
||||
| 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] |
|
||||
|
||||
---
|
||||
|
||||
@@ -176,35 +178,37 @@ Every mutating operation checks:
|
||||
### 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 | [ ] |
|
||||
| 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 | [ ] |
|
||||
| 14.2 | It should filter refunds by total amount range (min / max) | Integration | [ ] |
|
||||
| 14.3 | Given multiple filters are applied, when the user changes one filter, then the table should refresh with the combined filter set | Integration | [ ] |
|
||||
|---|---|---|---|
|
||||
| 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 | [ ] |
|
||||
| 15.2 | It should sort by date ascending/descending | Integration | [ ] |
|
||||
| 15.3 | It should sort by total amount ascending/descending | Integration | [ ] |
|
||||
|---|---|---|---|
|
||||
| 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 | [ ] |
|
||||
| 15.6 | Given the user clicks a column header twice, then the sort direction should toggle | 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 | [ ] |
|
||||
| 16.2 | It should allow changing the per-page count | Integration | [ ] |
|
||||
|---|---|---|---|
|
||||
| 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] |
|
||||
|
||||
---
|
||||
|
||||
@@ -213,36 +217,38 @@ Every mutating operation checks:
|
||||
### 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 | [ ] |
|
||||
| 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 | [ ] |
|
||||
|---|---|---|---|
|
||||
| 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 | [ ] |
|
||||
| 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 | [ ] |
|
||||
| 19.2 | It should sort by date ascending/descending | Integration | [ ] |
|
||||
| 19.3 | It should sort by paid-in amount ascending/descending | Integration | [ ] |
|
||||
| 19.4 | It should sort by paid-out amount ascending/descending | Integration | [ ] |
|
||||
| 19.5 | It should sort by expected-cash amount ascending/descending | Integration | [ ] |
|
||||
| 19.6 | It should sort by opened-cash amount ascending/descending | Integration | [ ] |
|
||||
| 19.7 | Given the user clicks a column header twice, then the sort direction should toggle | Integration | [ ] |
|
||||
|---|---|---|---|
|
||||
| 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 | [ ] |
|
||||
| 20.2 | It should allow changing the per-page count | Integration | [ ] |
|
||||
|---|---|---|---|
|
||||
| 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] |
|
||||
|
||||
---
|
||||
|
||||
@@ -251,9 +257,9 @@ Every mutating operation checks:
|
||||
### 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 | [ ] |
|
||||
| 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 | [ ] |
|
||||
@@ -264,41 +270,41 @@ Every mutating operation checks:
|
||||
### Filtering Behaviors
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 22.1 | It should filter sales summaries by date range | Integration | [ ] |
|
||||
| 22.2 | Given multiple filters are applied, when the user changes one filter, then the table should refresh with the combined filter set | Integration | [ ] |
|
||||
|---|---|---|---|
|
||||
| 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 | [ ] |
|
||||
| 23.2 | It should sort by date ascending/descending | Integration | [ ] |
|
||||
| 23.3 | It should sort by debits ascending/descending | Integration | [ ] |
|
||||
| 23.4 | It should sort by credits ascending/descending | Integration | [ ] |
|
||||
| 23.5 | Given the user clicks a column header twice, then the sort direction should toggle | Integration | [ ] |
|
||||
|---|---|---|---|
|
||||
| 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 | [ ] |
|
||||
| 24.2 | It should allow changing the per-page count | Integration | [ ] |
|
||||
|---|---|---|---|
|
||||
| 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 | [ ] |
|
||||
| 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 | [ ] |
|
||||
| 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 | [ ] |
|
||||
|
||||
---
|
||||
@@ -308,72 +314,76 @@ Every mutating operation checks:
|
||||
### HTMX Live Filtering Behaviors
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 26.1 | It should trigger table refresh on filter form change with a 500ms debounce | Integration | [ ] |
|
||||
| 26.2 | It should trigger table refresh on hot-filter keyup with a 1000ms debounce | Integration | [ ] |
|
||||
| 26.3 | It should POST to the table route and swap the grid contents | Integration | [ ] |
|
||||
| 26.4 | It should update the browser URL via hx-push-url when filters change | Integration | [ ] |
|
||||
|---|---|---|---|
|
||||
| 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.1–26.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 | [ ] |
|
||||
|---|---|---|---|
|
||||
| 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 | [ ] |
|
||||
| 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 | [ ] |
|
||||
|---|---|---|---|
|
||||
| 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 | [ ] |
|
||||
|---|---|---|---|
|
||||
| 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 | [ ] |
|
||||
| 30.2 | It should support multi-sort with active sorts appearing as removable pills above the grid | Integration | [ ] |
|
||||
| 30.3 | It should remove a sort when the X on its pill is clicked | Integration | [ ] |
|
||||
| 30.4 | It should default to sort by date descending for most pages | Integration | [ ] |
|
||||
|---|---|---|---|
|
||||
| 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 | [ ] |
|
||||
| 32.2 | It should hide the Client column when only one client is in scope | Integration | [ ] |
|
||||
| 32.3 | It should support client-id and client-code URL params | Integration | [ ] |
|
||||
|---|---|---|---|
|
||||
| 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 | [ ] |
|
||||
| 33.2 | It should require admin access (`wrap-admin`) to access Sales Summaries | Integration | [ ] |
|
||||
| 33.3 | It should redirect unauthenticated users | Integration | [ ] |
|
||||
|---|---|---|---|
|
||||
| 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` |
|
||||
@@ -385,12 +395,12 @@ Every mutating operation checks:
|
||||
|
||||
## Existing Tests to Preserve
|
||||
|
||||
- `test/clj/auto_ap/ssr/pos/sales_orders_test.clj` — Sales orders grid behaviors
|
||||
- `test/clj/auto_ap/ssr/pos/expected_deposits_test.clj` — Expected deposits grid behaviors
|
||||
- `test/clj/auto_ap/ssr/pos/tenders_test.clj` — Tenders grid behaviors
|
||||
- `test/clj/auto_ap/ssr/pos/refunds_test.clj` — Refunds grid behaviors
|
||||
- `test/clj/auto_ap/ssr/pos/cash_drawer_shifts_test.clj` — Cash drawer shifts grid behaviors
|
||||
- `test/clj/auto_ap/ssr/admin/sales_summaries_test.clj` — Sales summaries admin behaviors
|
||||
- `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
|
||||
|
||||
@@ -403,3 +413,9 @@ Every mutating operation checks:
|
||||
- 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).
|
||||
|
||||
@@ -57,7 +57,7 @@ Every mutating operation checks:
|
||||
| 1.6 | It should right-align amounts and format them as `$X,XXX.XX` | UI | [ ] |
|
||||
| 1.7 | It should display a links dropdown with links to associated Payment page or Client Overrides | UI | [ ] |
|
||||
| 1.8 | It should show checkboxes for bulk selection on each row | UI | [ ] |
|
||||
| 1.9 | It should group table rows by vendor name (or "No vendor") when sorted by Vendor | Integration | [ ] |
|
||||
| 1.9 | It should group table rows by vendor name (or "No vendor") when sorted by Vendor | Integration | [x] |
|
||||
| 1.10 | It should show the grid title "Transaction" and entity name "register" | UI | [ ] |
|
||||
| 1.11 | It should display a breadcrumb showing "Transactions" linking to the list page | UI | [ ] |
|
||||
|
||||
@@ -203,11 +203,11 @@ Every mutating operation checks:
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 12.1 | It should set transactions to `unapproved` status on import | Integration | [ ] |
|
||||
| 12.1 | It should set transactions to `unapproved` status on import | Integration | [x] |
|
||||
| 12.2 | It should exclude `suppressed` transactions from all list queries including GraphQL | Integration | [ ] |
|
||||
| 12.3 | It should show `requires-feedback` transactions in the dashboard tasks card | Integration | [ ] |
|
||||
| 12.4 | It should allow admin-only bulk status changes via GraphQL mutation `bulk_change_transaction_status` | Integration | [ ] |
|
||||
| 12.5 | It should block modifying locked transactions (before `client/locked-until` or `bank-account/start-date`) | Integration | [ ] |
|
||||
| 12.5 | It should block modifying locked transactions (before `client/locked-until` or `bank-account/start-date`) | Integration | [x] |
|
||||
|
||||
### Coding Behaviors
|
||||
|
||||
@@ -215,9 +215,9 @@ Every mutating operation checks:
|
||||
|---|----------|---------------|--------|
|
||||
| 13.1 | It should allow coding transactions with one or more expense accounts | Integration | [ ] |
|
||||
| 13.2 | It should validate that account totals equal 100% of the transaction amount server-side | Unit + Integration | [ ] |
|
||||
| 13.3 | It should require the location to match the account's fixed location if one is set | Integration | [ ] |
|
||||
| 13.4 | It should distribute amounts proportionally across all client locations when location is "Shared" | Unit | [ ] |
|
||||
| 13.5 | It should reserve location "A" for liabilities/equities/assets | Integration | [ ] |
|
||||
| 13.3 | It should require the location to match the account's fixed location if one is set | Integration | [x] |
|
||||
| 13.4 | It should distribute amounts proportionally across all client locations when location is "Shared" | Unit | [x] |
|
||||
| 13.5 | It should reserve location "A" for liabilities/equities/assets | Integration | [x] |
|
||||
| 13.6 | It should allow admin-only bulk coding via GraphQL mutation `bulk_code_transactions` | Integration | [ ] |
|
||||
|
||||
### Bank Account Filtering Behaviors
|
||||
@@ -243,7 +243,7 @@ Every mutating operation checks:
|
||||
|---|----------|---------------|--------|
|
||||
| 16.1 | It should auto-match transactions to payments by check number or amount on import | Integration | [ ] |
|
||||
| 16.2 | It should create a cleared payment and set the transaction to `approved` with Accounts Payable account when linking | Integration | [ ] |
|
||||
| 16.3 | It should revert the transaction to `unapproved` and clear payment/accounts when unlinking | Integration | [ ] |
|
||||
| 16.3 | It should revert the transaction to `unapproved` and clear payment/accounts when unlinking | Integration | [x] |
|
||||
| 16.4 | It should allow a transaction to pay multiple autopay invoices, creating a payment that clears them all | Integration | [ ] |
|
||||
| 16.5 | It should allow a transaction to pay multiple unpaid invoices for outstanding balances | Integration | [ ] |
|
||||
|
||||
@@ -251,13 +251,13 @@ Every mutating operation checks:
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 17.1 | It should require `:activity :view :subject :transaction` permission to view transactions | Integration | [ ] |
|
||||
| 17.2 | It should require `:activity :insights :subject :transaction` permission to access the insights page | Integration | [ ] |
|
||||
| 17.1 | It should require `:activity :view :subject :transaction` permission to view transactions | Integration | [x] |
|
||||
| 17.2 | It should require `:activity :insights :subject :transaction` permission to access the insights page | Integration | [x] |
|
||||
| 17.3 | It should restrict bulk status changes to admin only | Integration | [ ] |
|
||||
| 17.4 | It should restrict bulk coding to admin only | Integration | [ ] |
|
||||
| 17.5 | It should require power user role with client visibility check to edit transactions | Integration | [ ] |
|
||||
| 17.6 | It should require power user role to match/unlink transactions | Integration | [ ] |
|
||||
| 17.7 | It should redirect unauthenticated users to `/login` for all SSR routes | Integration | [ ] |
|
||||
| 17.7 | It should redirect unauthenticated users to `/login` for all SSR routes | Integration | [x] |
|
||||
|
||||
### Import Processing Behaviors
|
||||
|
||||
@@ -268,7 +268,7 @@ Every mutating operation checks:
|
||||
| 18.3 | It should extract check number from description if present during import | Unit | [ ] |
|
||||
| 18.4 | It should attempt auto-match to pending payment during import | Integration | [ ] |
|
||||
| 18.5 | It should attempt auto-match to expected deposit during import | Integration | [ ] |
|
||||
| 18.6 | It should apply transaction rules for auto-coding during import | Integration | [ ] |
|
||||
| 18.6 | It should apply transaction rules for auto-coding during import | Integration | [x] |
|
||||
| 18.7 | It should apply default vendor if set during import | Integration | [ ] |
|
||||
| 18.8 | It should deduplicate via SHA-256 of `date-bank-account-description-amount-index-client` | Unit | [ ] |
|
||||
| 18.9 | It should skip suppressed transactions on re-import | Integration | [ ] |
|
||||
|
||||
Reference in New Issue
Block a user