Files
integreat/docs/testing/behaviors/outgoing-invoice.md
Bryce d627e3c5d0 refactor(all): rewrite all behavior docs in table format with checkboxes
Rewrite all 11 remaining behavior documents to match the streamlined
invoice.md format:

- dashboard.md: 250 lines, 62 behaviors
- payment.md: 260 lines, behaviors for list, void, check printing, ACH
- transaction.md: 310 lines, list, import, admin insights
- ledger.md: 519 lines, entries, P&L, balance sheet, cash flows
- company.md: 320 lines, profile, 1099s, Plaid/Yodlee, reports
- admin.md: 494 lines, clients, accounts, vendors, rules, jobs, history
- pos.md: 405 lines, sales, deposits, tenders, refunds, shifts
- search-indicators.md: 167 lines, search modal, indicators
- auth.md: 184 lines, login, logout, impersonation, sessions
- outgoing-invoice.md: 192 lines, create, line items, PDF
- legacy-spa.md: 340 lines, all legacy pages (docs only)

All documents now use:
- Testing Patterns section with reusable abstractions
- Numbered tables: # | Behavior | Test Strategy | Status
- It should... behavior descriptions
- Checkboxes [ ]/[x] for tracking implementation
- Cross-Cutting Behaviors for permissions, lock dates, etc.
- Test Data Requirements tables
- Existing Tests to Preserve sections

Total: 3,844 lines of behavior documentation across 12 subsystem docs.
2026-05-04 13:48:51 -07:00

9.7 KiB

Outgoing Invoice Behaviors

Overview

The Outgoing Invoice subsystem allows users to create and generate PDF invoices to send to customers. It provides a form-based workflow for specifying the client (sender), recipient details, invoice metadata (date, number, tax rate), and line items. Upon submission, the system calculates subtotals, applies tax, and invokes an AWS Lambda function (genpdf) to generate a downloadable PDF.

Testing Philosophy

  • Prefer unit tests for pure business logic (calculations, validations, transformations)
  • Use integration tests for database interactions and cross-system flows
  • Use UI tests only for end-to-end happy paths that touch multiple pages
  • Every behavior must be user-visible; no tests for implementation details

Testing Patterns

Pattern: Form Submission with HTMX

Outgoing invoice forms use HTMX for asynchronous submission and partial page updates:

  1. Form fields are rendered server-side with validation state
  2. HTMX handles POST submission and swaps the response into the page
  3. Success responses trigger modal display with PDF download link
  4. Error responses re-render the form with validation errors

Test implications: Unit test validation logic and calculation functions. Integration test the full POST flow. UI test only the happy path.

Pattern: Dynamic Line Items

Line items are added and removed dynamically without page reload:

  1. "Add line" button fetches a new row via HTMX
  2. Each row has description, quantity, unit price inputs, and a delete button
  3. Delete uses Alpine.js to fade out and remove the row
  4. Empty line items are filtered out on submission

Test implications: Unit test the filtering and calculation logic. Integration test HTMX endpoints. UI test the add/remove interactions.


Create Invoice

Form Display Behaviors

# Behavior Test Strategy Status
1.1 It should render a new invoice form with breadcrumbs: Invoices > Outgoing > New UI [ ]
1.2 It should display a client typeahead field labeled "From (client)" UI [ ]
1.3 It should display an invoice number input field UI [ ]
1.4 It should display a date picker pre-filled with today's date in normal-date format UI [ ]
1.5 It should display recipient name "To" field UI [ ]
1.6 It should display recipient address fields: street1, street2, city, state, zip UI [ ]
1.7 It should display a line items grid with one default empty row UI [ ]
1.8 It should display a tax percentage input with default value 10.0 UI [ ]
1.9 It should display a "Generate" button to submit the form UI [ ]
1.10 It should display an "Add line" button to add more line items UI [ ]

Form Validation Behaviors

# 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.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 [ ]

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.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 [ ]

Line Items

Add Line Item Behaviors

# Behavior Test Strategy Status
4.1 It should fetch a new empty line item row via HTMX when "Add line" is clicked Integration [ ]
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 [ ]

Remove Line Item Behaviors

# Behavior Test Strategy Status
5.1 It should fade out a line item row over 500ms when the delete button is clicked UI [ ]
5.2 It should remove the line item row from the DOM after the fade animation UI [ ]
5.3 It should preserve data in remaining line items after deletion UI [ ]
5.4 It should allow deleting all line items, leaving the grid empty UI [ ]

Calculation Behaviors

# 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 [ ]

PDF Generation

Lambda Integration Behaviors

# 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.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.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 [ ]

Cross-Cutting Behaviors

Authentication Behaviors

# 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 [ ]

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 [ ]

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 [ ]

Test Data Requirements

Entity Requirements
Users Authenticated user with access to at least one client
Clients Multiple clients with complete profiles including address (name, street, city, state, zip)
Form Data Valid invoice number strings; valid dates in normal-date format; recipient names and addresses
Line Items Descriptions, quantities (numeric), unit prices (monetary)
Tax Rates Percentage values (e.g., 10.0 for 10%)
AWS Lambda Mock genpdf Lambda returning valid S3 URL; mock genpdf Lambda returning error

Existing Tests to Preserve

  • test/clj/auto_ap/ssr/outgoing_invoice_test.clj — Outgoing invoice form rendering, submission, and PDF generation

Dependencies

  • Datomic (client lookup for typeahead, address data)
  • AWS Lambda (genpdf function generates PDF from invoice data)
  • AWS S3 (generated PDFs stored at data.prod.app.integreatconsult.com/<path>)
  • HTMX (form submission, line item row fetching)
  • Alpine.js (line item row removal animation)
  • Form cursor (auto-ap.ssr.form-cursor) — field state management, error binding