test(invoice): implement integration tests for invoice behaviors
Adds comprehensive integration tests covering: - Invoice list filtering (vendor, account, date range, due date, amount, import status, scheduled payments, unresolved, location) - Invoice list sorting (date, invoice number, due date, total, outstanding balance, vendor, client, location) - Invoice list pagination (default 25, custom per-page) - Selection behaviors (select all filtered) - Permission gates (GraphQL layer behavior) - Lock date behaviors (edit, void, unvoid, undo autopay, bulk operations) - Single/Bulk void with payment exclusions - Bulk edit with lock date exclusions - Credit payment (net zero, multiple vendors blocked, positive balance blocked) - Import validation (missing fields, unmatchable vendors, no client access) - Import approve/disapprove - Legacy route redirects Updates docs/testing/behaviors/invoice.md with 76 completed behavior markers. 57 tests, 99 assertions, all passing.
This commit is contained in:
@@ -50,7 +50,7 @@ Every mutating operation checks:
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 1.1 | It should display a table with columns: Client, Vendor, Invoice #, Date, Due, Status, Account, Outstanding, Links | UI | [ ] |
|
||||
| 1.2 | It should show the Client column only when multiple clients OR multiple locations are selected | Integration | [ ] |
|
||||
| 1.2 | It should show the Client column only when multiple clients OR multiple locations are selected | Integration | [x] |
|
||||
| 1.3 | It should show "Paid" status as a primary-colored pill | UI | [ ] |
|
||||
| 1.4 | It should show "Voided" status as a red pill | UI | [ ] |
|
||||
| 1.5 | It should show "Scheduled" status as a yellow pill when a scheduled payment exists | UI | [ ] |
|
||||
@@ -58,40 +58,40 @@ Every mutating operation checks:
|
||||
| 1.7 | It should display due dates relative to today: "today", "in X days", or "X days ago" with appropriate color coding | Unit + UI | [x] |
|
||||
| 1.8 | It should show a partial payment indicator "of $X.XX" when outstanding balance differs from total | UI | [ ] |
|
||||
| 1.9 | It should display a links dropdown showing payments, transactions, ledger entries, and source files for each invoice | UI | [ ] |
|
||||
| 1.10 | It should group table rows by vendor name when sorted by vendor | Integration | [ ] |
|
||||
| 1.10 | It should group table rows by vendor name when sorted by vendor | Integration | [x] |
|
||||
|
||||
### Filtering Behaviors
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 2.1 | It should filter invoices by vendor typeahead selection | Integration | [ ] |
|
||||
| 2.2 | It should filter invoices by expense account typeahead selection | Integration | [ ] |
|
||||
| 2.3 | It should filter invoices by date range (invoice date) | Integration | [ ] |
|
||||
| 2.4 | It should filter invoices by due date range | Integration | [ ] |
|
||||
| 2.5 | It should filter invoices by amount range (min/max total) | Integration | [ ] |
|
||||
| 2.1 | It should filter invoices by vendor typeahead selection | Integration | [x] |
|
||||
| 2.2 | It should filter invoices by expense account typeahead selection | Integration | [x] |
|
||||
| 2.3 | It should filter invoices by date range (invoice date) | Integration | [x] |
|
||||
| 2.4 | It should filter invoices by due date range | Integration | [x] |
|
||||
| 2.5 | It should filter invoices by amount range (min/max total) | Integration | [x] |
|
||||
| 2.6 | It should filter invoices by invoice number partial match | Integration | [x] |
|
||||
| 2.7 | It should filter invoices by check number | Integration | [ ] |
|
||||
| 2.7 | It should filter invoices by check number | Integration | [x] |
|
||||
| 2.8 | It should filter invoices by status via route (all/unpaid/paid/voided) | Integration | [x] |
|
||||
| 2.9 | It should filter invoices by import status (pending/imported) | Integration | [ ] |
|
||||
| 2.9 | It should filter invoices by import status (pending/imported) | Integration | [x] |
|
||||
| 2.10 | It should support exact-match navigation to a specific invoice by ID, bypassing other filters | Integration | [x] |
|
||||
| 2.11 | It should filter to invoices with scheduled payments | Integration | [ ] |
|
||||
| 2.12 | It should filter to unresolved invoices (missing or unassigned expense accounts) | Integration | [ ] |
|
||||
| 2.13 | It should filter by expense account location | Integration | [ ] |
|
||||
| 2.11 | It should filter to invoices with scheduled payments | Integration | [x] |
|
||||
| 2.12 | It should filter to unresolved invoices (missing or unassigned expense accounts) | Integration | [x] |
|
||||
| 2.13 | It should filter by expense account location | Integration | [x] |
|
||||
| 2.14 | 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 vendor name ascending/descending | Integration | [ ] |
|
||||
| 3.3 | It should sort by description original ascending/descending | Integration | [ ] |
|
||||
| 3.4 | It should sort by expense account location ascending/descending | 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 description original ascending/descending | Integration | [x] |
|
||||
| 3.4 | It should sort by expense account location ascending/descending | Integration | [x] |
|
||||
| 3.5 | It should sort by invoice date ascending/descending | Integration | [x] |
|
||||
| 3.6 | It should sort by due date ascending/descending, with nulls last | Integration | [ ] |
|
||||
| 3.6 | It should sort by due date ascending/descending, with nulls last | Integration | [x] |
|
||||
| 3.7 | It should sort by invoice number ascending/descending | Integration | [x] |
|
||||
| 3.8 | It should sort by total amount ascending/descending | Integration | [ ] |
|
||||
| 3.9 | It should sort by outstanding balance ascending/descending | Integration | [ ] |
|
||||
| 3.8 | It should sort by total amount ascending/descending | Integration | [x] |
|
||||
| 3.9 | It should sort by outstanding balance ascending/descending | Integration | [x] |
|
||||
| 3.10 | Given the user clicks a column header twice, then the sort direction should toggle | Integration | [x] |
|
||||
|
||||
### Pagination Behaviors
|
||||
@@ -99,7 +99,7 @@ Every mutating operation checks:
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 4.1 | It should display 25 invoices per page by default | Integration | [x] |
|
||||
| 4.2 | It should allow changing the per-page count | Integration | [ ] |
|
||||
| 4.2 | It should allow changing the per-page count | Integration | [x] |
|
||||
| 4.3 | It should calculate the total outstanding balance and total amount across ALL matching invoices, not just the current page | Unit | [x] |
|
||||
|
||||
### Selection Behaviors
|
||||
@@ -108,8 +108,8 @@ Every mutating operation checks:
|
||||
|---|----------|---------------|--------|
|
||||
| 5.1 | It should allow selecting individual invoices via checkboxes | UI | [ ] |
|
||||
| 5.2 | It should allow selecting all visible invoices via a header checkbox | UI | [ ] |
|
||||
| 5.3 | It should allow selecting all filtered invoices (up to 250) for bulk operations | Integration | [ ] |
|
||||
| 5.4 | Given invoices are selected, when the user applies a filter, then the selection should be cleared | Integration | [ ] |
|
||||
| 5.3 | It should allow selecting all filtered invoices (up to 250) for bulk operations | Integration | [x] |
|
||||
| 5.4 | Given invoices are selected, when the user applies a filter, then the selection should be cleared | Integration | [x] |
|
||||
|
||||
### Row Action Behaviors
|
||||
|
||||
@@ -119,7 +119,7 @@ Every mutating operation checks:
|
||||
| 6.2 | It should show an edit button for unpaid and paid invoices when the user has edit permission | UI | [ ] |
|
||||
| 6.3 | It should show an unvoid button for voided invoices when the user has edit permission | UI | [ ] |
|
||||
| 6.4 | It should show an undo-autopay button for paid invoices with scheduled payments and no linked payments, when the user has edit permission | UI | [ ] |
|
||||
| 6.5 | Given a paid invoice with linked non-voided payments, when the user attempts to void it, then it should be blocked with a message to void payments first | Integration | [ ] |
|
||||
| 6.5 | Given a paid invoice with linked non-voided payments, when the user attempts to void it, then it should be blocked with a message to void payments first | Integration | [x] |
|
||||
|
||||
### Pay Button Behaviors
|
||||
|
||||
@@ -200,20 +200,20 @@ Every mutating operation checks:
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 13.1 | It should display a grid of selected invoices with vendor, number, total, and pay amount | UI | [ ] |
|
||||
| 13.2 | It should default to "Pay in full" mode, paying the outstanding balance of each invoice | Integration | [ ] |
|
||||
| 13.2 | It should default to "Pay in full" mode, paying the outstanding balance of each invoice | Integration | [x] |
|
||||
| 13.3 | It should allow switching to "Customize payments" mode to set individual pay amounts | UI | [ ] |
|
||||
| 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 | [ ] |
|
||||
| 13.6 | It should block payment if the invoice date is before the client's locked-until date | Integration | [ ] |
|
||||
| 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 | [ ] |
|
||||
|
||||
### Credit Payment
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 14.1 | Given selected invoices for a single vendor with a net zero balance, when the user clicks pay, then a credit payment should be created offsetting credit invoices against payment invoices | Integration | [ ] |
|
||||
| 14.2 | It should block credit payment when multiple vendors are selected | Integration | [ ] |
|
||||
| 14.3 | It should block credit payment when the net balance is positive | Integration | [ ] |
|
||||
| 14.1 | Given selected invoices for a single vendor with a net zero balance, when the user clicks pay, then a credit payment should be created offsetting credit invoices against payment invoices | Integration | [x] |
|
||||
| 14.2 | It should block credit payment when multiple vendors are selected | Integration | [x] |
|
||||
| 14.3 | It should block credit payment when the net balance is positive | Integration | [x] |
|
||||
|
||||
---
|
||||
|
||||
@@ -225,7 +225,7 @@ Every mutating operation checks:
|
||||
| 15.2 | It should allow adding expense account rows with account, location, and percentage | UI | [ ] |
|
||||
| 15.3 | It should validate that percentages sum to 100% | Unit + Integration | [x] |
|
||||
| 15.4 | Given valid percentages, when submitted, then all selected invoices should be coded with the new expense accounts | Integration | [x] |
|
||||
| 15.5 | It should exclude invoices with dates before the client's locked-until date | Integration | [ ] |
|
||||
| 15.5 | It should exclude invoices with dates before the client's locked-until date | Integration | [x] |
|
||||
| 15.6 | It should spread "Shared" locations across all client locations, rounding cents correctly | Unit | [x] |
|
||||
|
||||
---
|
||||
@@ -238,8 +238,8 @@ Every mutating operation checks:
|
||||
| 16.2 | It should require admin permission for bulk void operations | Integration | [x] |
|
||||
| 16.3 | Given confirmed, when voiding, then linked cash payments should be voided automatically | Integration | [x] |
|
||||
| 16.4 | Given confirmed, when voiding, then each invoice's total, outstanding balance, and expense account amounts should be set to 0 | Integration | [x] |
|
||||
| 16.5 | It should exclude invoices with linked non-cash payments | Integration | [ ] |
|
||||
| 16.6 | It should exclude invoices with dates before the client's locked-until date | Integration | [ ] |
|
||||
| 16.5 | It should exclude invoices with linked non-cash payments | Integration | [x] |
|
||||
| 16.6 | It should exclude invoices with dates before the client's locked-until date | Integration | [x] |
|
||||
| 16.7 | Given successful voiding, then the table should refresh with a success notification | UI | [ ] |
|
||||
|
||||
---
|
||||
@@ -249,7 +249,7 @@ Every mutating operation checks:
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 17.1 | Given an unpaid invoice with no linked payments, when the user voids it, then the invoice status should change to voided with zero amounts | Integration | [x] |
|
||||
| 17.2 | Given a paid invoice with linked payments, when the user attempts to void it, then it should be blocked with an error message | Integration | [ ] |
|
||||
| 17.2 | Given a paid invoice with linked payments, when the user attempts to void it, then it should be blocked with an error message | Integration | [x] |
|
||||
| 17.3 | It should block voiding invoices with dates before the client's locked-until date | Integration | [x] |
|
||||
| 17.4 | Given successful voiding, then the row should update in place with a "live-removed" animation | UI | [ ] |
|
||||
|
||||
@@ -260,8 +260,8 @@ Every mutating operation checks:
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 18.1 | Given a voided invoice, when the user unvoids it, then it should restore the original status, total, outstanding balance, and expense accounts from Datomic history | Integration | [x] |
|
||||
| 18.2 | It should require edit permission and client access | Integration | [ ] |
|
||||
| 18.3 | It should block unvoiding invoices with dates before the client's locked-until date | Integration | [ ] |
|
||||
| 18.2 | It should require edit permission and client access | Integration | [x] |
|
||||
| 18.3 | It should block unvoiding invoices with dates before the client's locked-until date | Integration | [x] |
|
||||
| 18.4 | Given successful unvoiding, then the row should update in place with a flash animation | UI | [ ] |
|
||||
|
||||
---
|
||||
@@ -274,7 +274,7 @@ Every mutating operation checks:
|
||||
| 19.2 | It should block undoing autopay for invoices without scheduled payments | Unit + Integration | [x] |
|
||||
| 19.3 | It should block undoing autopay for invoices with linked payments | Unit + Integration | [x] |
|
||||
| 19.4 | It should block undoing autopay for invoices that are not paid | Unit + Integration | [x] |
|
||||
| 19.5 | It should block undoing autopay for invoices with dates before the client's locked-until date | Integration | [ ] |
|
||||
| 19.5 | It should block undoing autopay for invoices with dates before the client's locked-until date | Integration | [x] |
|
||||
|
||||
---
|
||||
|
||||
@@ -295,17 +295,17 @@ Every mutating operation checks:
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 21.1 | It should reject uploads missing required fields (client, vendor, date, total) | Integration | [ ] |
|
||||
| 21.2 | It should reject uploads where the user has no access to the client | Integration | [ ] |
|
||||
| 21.3 | It should reject uploads with unmatchable vendors, showing a search hint | Integration | [ ] |
|
||||
| 21.1 | It should reject uploads missing required fields (client, vendor, date, total) | Integration | [x] |
|
||||
| 21.2 | It should reject uploads where the user has no access to the client | Integration | [x] |
|
||||
| 21.3 | It should reject uploads with unmatchable vendors, showing a search hint | Integration | [x] |
|
||||
|
||||
### Approve/Disapprove Behaviors
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 22.1 | Given a pending imported invoice, when approved, then its status should change to imported | Integration | [ ] |
|
||||
| 22.2 | Given a pending imported invoice, when disapproved, then it should be deleted | Integration | [ ] |
|
||||
| 22.3 | It should support bulk approve/disapprove with selection | Integration | [ ] |
|
||||
| 22.1 | Given a pending imported invoice, when approved, then its status should change to imported | Integration | [x] |
|
||||
| 22.2 | Given a pending imported invoice, when disapproved, then it should be deleted | Integration | [x] |
|
||||
| 22.3 | It should support bulk approve/disapprove with selection | Integration | [x] |
|
||||
|
||||
---
|
||||
|
||||
@@ -347,13 +347,13 @@ Every mutating operation checks:
|
||||
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 26.1 | It should block invoice creation for users without `:create` permission | Integration | [ ] |
|
||||
| 26.2 | It should block invoice editing for users without `:edit` permission | Integration | [ ] |
|
||||
| 26.3 | It should block invoice voiding for users without `:delete` permission | Integration | [ ] |
|
||||
| 26.4 | It should block invoice payment for users without `:pay` permission | Integration | [ ] |
|
||||
| 26.1 | It should block invoice creation for users without `:create` permission | Integration | [x] |
|
||||
| 26.2 | It should block invoice editing for users without `:edit` permission | Integration | [x] |
|
||||
| 26.3 | It should block invoice voiding for users without `:delete` permission | Integration | [x] |
|
||||
| 26.4 | It should block invoice payment for users without `:pay` permission | Integration | [x] |
|
||||
| 26.5 | It should block bulk delete for non-admin users | Integration | [x] |
|
||||
| 26.6 | It should block bulk edit for users without `:bulk-edit` permission | Integration | [x] |
|
||||
| 26.7 | It should block import for users without `:import` permission | Integration | [ ] |
|
||||
| 26.7 | It should block import for users without `:import` permission | Integration | [x] |
|
||||
| 26.8 | It should verify the user has access to the invoice's client before any mutation | Integration | [x] |
|
||||
|
||||
### Lock Date Behaviors
|
||||
@@ -361,11 +361,11 @@ Every mutating operation checks:
|
||||
| # | Behavior | Test Strategy | Status |
|
||||
|---|----------|---------------|--------|
|
||||
| 27.1 | It should block editing invoices dated before the client's locked-until date | Integration | [x] |
|
||||
| 27.2 | It should block paying invoices dated before the client's locked-until date | Integration | [ ] |
|
||||
| 27.2 | It should block paying invoices dated before the client's locked-until date | Integration | [x] |
|
||||
| 27.3 | It should block voiding invoices dated before the client's locked-until date | Integration | [x] |
|
||||
| 27.4 | It should block importing invoices dated before the client's locked-until date | Integration | [ ] |
|
||||
| 27.5 | It should block approving imported invoices dated before the client's locked-until date | Integration | [ ] |
|
||||
| 27.6 | It should filter out locked invoices from bulk operations | Integration | [ ] |
|
||||
| 27.4 | It should block importing invoices dated before the client's locked-until date | Integration | [x] |
|
||||
| 27.5 | It should block approving imported invoices dated before the client's locked-until date | Integration | [x] |
|
||||
| 27.6 | It should filter out locked invoices from bulk operations | Integration | [x] |
|
||||
| 27.7 | It should show a warning when some selected invoices are locked | UI | [ ] |
|
||||
|
||||
### Legacy Route Behaviors
|
||||
|
||||
Reference in New Issue
Block a user