> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://cla-demo.postmansolutions.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://cla-demo.postmansolutions.com/_mcp/server.

# AP AI ERP Integration

## Purpose

We need to investigate, design, and size the effort required to support ERP integration configuration and the new invoice workflow stages (Matching, Manual, and Filing) in AP AI so we can sequence delivery confidently and surface any architectural risks before sprint commitment.

---

## Scope

The legacy application includes ERP/CRM integration configuration and a multi-stage invoice processing workflow. As part of the SaaS implementation, the following capabilities need to be scoped and designed before development begins:

* **ERP Integration Configuration** (backend & frontend): Provider selection and a request form for unsupported connectors
* **Matching Stage**: PO/Invoice comparison with mismatch visibility and user action paths
* **Manual Stage**: Manual override with notes, line item vouching, and action paths
* **Filing Stage**: Confirmation of ERP filing with action paths to Closed

### Goals

1. Identify integration touchpoints with the existing stage framework and Closed stage transition logic
2. Surface any open questions around ERP connector architecture, data contracts, and permissions
3. Deliver T-shirt size estimates (XS/S/M/L/XL/XXL) per functional area, broken down by UI, API/backend, and integration layer

### T-Shirt Sizing Framework

| Size | Effort    |
| ---- | --------- |
| XS   | \< 1 day  |
| S    | 1–2 days  |
| M    | 3–5 days  |
| L    | 1–2 weeks |
| XL   | 2–3 weeks |
| XXL  | 3+ weeks  |

---

# 1. ERP Integration Configuration

## Overview

The ERP Integration Configuration page provides administrators with the ability to connect their ERP system to AP AI, manage email inboxes for invoice intake, and configure webhooks. This is accessed via the **Connections** tab in the settings area.

*(image not available in export: /Users/karan.[thelakkat@postman.com](mailto:thelakkat@postman.com)/Repos/fern-cla-main/Reference Material/images/ERP UI.png)*

## UI Components

### Tabs

* **Connections** (active) — ERP, inbox, and webhook setup
* **Features** — Feature toggle configuration
* **Workflow Configuration** — Stage-level workflow settings

### Inbox Monitoring Manager

* **Monitored Inboxes** / **Blocked Senders** toggle tabs
* **Add Inbox** button to subscribe new email inboxes
* Displays "No inboxes subscribed." when empty

### Integration Connections

* Displays the currently connected ERP (e.g., "Connected ERP: Epicor 21")
* Shows connection details:
  * ERP Base URL
  * ERP Username
  * Save Enabled flag
* **Disconnect** button to remove the active connection
* **ERP** dropdown — select ERP type (e.g., Epicor, SAP, NetSuite)
* **Select provider** dropdown — choose specific provider instance
* **Request New Connector Support** link — form/request for unsupported ERP connectors

### Webhook Manager

* Add (+) button to register new webhooks
* Displays "No webhooks registered." when empty

## User Workflows

### Connect a New ERP

1. Navigate to Connections tab
2. Select ERP type from dropdown
3. Select provider from dropdown
4. Enter connection credentials (URL, username, password/API key)
5. Save connection
6. Connection details appear in the Integration Connections card

### Disconnect an ERP

1. Click **Disconnect** on the active connection card
2. Confirm disconnection
3. Connection is removed

### Request Unsupported Connector

1. Click **Request New Connector Support**
2. Fill out request form with ERP details
3. Submit for review

### Manage Inboxes

1. Click **Add Inbox** to subscribe a new email inbox for invoice intake
2. Toggle between **Monitored Inboxes** and **Blocked Senders** to manage

## Existing Codebase Findings

The ERP integration layer **already exists** in `common/erp/`. Key findings:

* **Connector abstraction** (`abstract_connector.py`): Defines the `ERPConnector` base class with abstract methods: `connect()`, `get_purchase_order()`, `get_receipts_by_po_number()`, `get_terms()`, `get_all_terms()`, `get_vendor()`, `get_all_vendors()`, `file_invoice()`
* **Supported connectors** (`connectors/__init__.py`): `epicor21`, `sageintacct`, `cla-mocked` — mapped in `connector_mapping`
* **Epicor 21 connector** (`connectors/epicor21/epicor21.py`): Fully implemented with token-based auth, PO retrieval (external + internal), receipts fetch, terms, vendors, and filing via XML-based voucher conversion
* **Sage Intacct connector** (`connectors/sageintacct/connect2.py`): Exists as a second real connector
* **ERP Service** (`service.py`): `ERPService` class with TTL-cached connector instances per client (5 min TTL), exposes `get_purchase_order()`, `get_receipts_by_po_number()`, `file_invoice()`
* **Client config storage**: Credentials stored in CosmosDB (`talaria.clients` container), encrypted per-client via `ClientConfig.decrypt_connection("ERP")`
* **Filing**: Uses XML transaction sets to convert PO to voucher in Epicor via interactive UI server sessions

### Existing API Routes (backend-api)

| Method | Route                                                | Purpose                     |
| ------ | ---------------------------------------------------- | --------------------------- |
| GET    | `/admin/client/{client_name}/connection/{conn_name}` | Get ERP connection config   |
| POST   | `/admin/client/{client_name}/connection`             | Add ERP connection          |
| PUT    | `/admin/client/{client_name}/connection`             | Update ERP connection       |
| PATCH  | `/invoices/{invoiceID}/matcher`                      | Update matching data        |
| PATCH  | `/invoices/{invoiceID}/manual`                       | Update manual/vouching data |
| POST   | `/invoices/{invoiceID}/stage/{viewedStage}`          | Change invoice stage        |
| POST   | `/invoices/{invoiceID}/save`                         | Save line item coding       |
| GET    | `/invoices/{invoice_id}/history`                     | Get invoice audit history   |

### Existing ERP Connector Methods (Epicor 21)

| Method                          | Epicor API Called                                                                   | Purpose                        |
| ------------------------------- | ----------------------------------------------------------------------------------- | ------------------------------ |
| `get_external_purchase_order()` | `GET /api/purchasing/purchaseorders?$query=ExternalPoNo eq '{po}'`                  | Fetch PO by external number    |
| `get_internal_purchase_order()` | `GET /api/purchasing/purchaseorders?$query=PoNo eq '{po}'`                          | Fetch PO by internal number    |
| `get_receipts_by_po_number()`   | `GET /odataservice/odata/view/p21_material_receipts_view?$filter=po_number eq {po}` | Fetch unvouched receipts       |
| `get_terms()`                   | `GET /odataservice/odata/table/terms?filter=terms_id eq '{id}'`                     | Get payment terms              |
| `get_all_terms()`               | `GET /odataservice/odata/table/terms`                                               | Get all terms                  |
| `get_vendor()`                  | `GET /api/entity/vendors?$query=VendorId eq {id}`                                   | Get vendor details             |
| `file_invoice()`                | Interactive UI session (XML voucher conversion)                                     | File invoice to ERP            |
| `get_user_token()`              | `POST /api/security/token/`                                                         | Auth token (username/password) |

## T-Shirt Sizing (Revised)

| Layer             | Size         | Rationale                                                                                                                                                                                             |
| ----------------- | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **UI**            | M (3–5 days) | Connections page with 3 cards (Inbox Manager, Integration Connections, Webhook Manager), dropdowns, form inputs, disconnect flow. Tabs for Connections/Features/Workflow Configuration.               |
| **API / Backend** | S (1–2 days) | Connection CRUD routes already exist (`/admin/client/{name}/connection`). Connector abstraction, caching, and service layer already built. May need minor adjustments for SaaS multi-tenant patterns. |
| **Integration**   | S (1–2 days) | Epicor 21 and Sage Intacct connectors already implemented. Token auth, PO fetch, receipts, terms, vendors, and filing all exist. May need testing/validation in new SaaS environment.                 |

**Overall: M (3–5 days)** — significantly reduced from L because the backend and integration layers already exist. Primary effort is the **UI build**.

## Proposed User Stories

### ERP-US-1: ERP Provider Selection UI

**As an** admin, **I want to** select an ERP type and provider from dropdowns **so that** I can configure my ERP connection.

* AC: ERP type dropdown populated from provider registry
* AC: Provider dropdown filters based on selected ERP type
* AC: Validation prevents saving without required fields

### ERP-US-2: ERP Connection Management

**As an** admin, **I want to** connect and disconnect my ERP system **so that** AP AI can file invoices to my ERP.

* AC: Successful connection displays ERP name, URL, username, and save enabled status
* AC: Disconnect button removes the active connection after confirmation
* AC: Connection errors display meaningful error messages

### ERP-US-3: ERP Connector Request Form

**As an** admin, **I want to** request support for an ERP not in the provider list **so that** my organization can still onboard.

* AC: Request form captures ERP name, version, and contact info
* AC: Submission confirms the request was received

### ERP-US-4: Inbox Monitoring Manager

**As an** admin, **I want to** add and manage monitored email inboxes **so that** invoices are automatically ingested.

* AC: Add Inbox flow captures inbox configuration
* AC: Monitored Inboxes tab lists active subscriptions
* AC: Blocked Senders tab allows managing sender blocklist

### ERP-US-5: Webhook Manager

**As an** admin, **I want to** register webhooks **so that** external systems are notified of invoice events.

* AC: Add button opens webhook configuration form
* AC: Registered webhooks displayed in list with edit/delete options

---

# 2. Matching Stage

## Overview

The Matching Stage is the second stage in the invoice workflow (Intake → **Matching** → Manual → Filing → Closed). It performs an automated comparison between the Purchase Order (PO) and the submitted Invoice to identify mismatches in quantities, pricing, and line items. Users review discrepancies and decide how to proceed.

*(image not available in export: /Users/karan.[thelakkat@postman.com](mailto:thelakkat@postman.com)/Repos/fern-cla-main/Reference Material/images/Matching Stage.png)*

## UI Components

### Workflow Breadcrumb

* Linear stage indicator: Intake → **Matching** → Manual → Filing → Closed
* Each stage shows a status icon (green check = complete, yellow warning = current with issues)

### Error Banner

* Displays at the top when matching errors are detected
* Example: "Error — This invoice has matching errors"

### Invoice Header

| Field          | Description                 |
| -------------- | --------------------------- |
| Invoice Date   | Date the invoice was issued |
| Due Date       | Payment due date            |
| Vendor         | Vendor name                 |
| Vendor Address | Vendor's address            |

### Side-by-Side Comparison

**Left Panel — Purchase Order**

* PO Number and Terms displayed at top
* Columns: Receipt #, Description, Qty, Unit Price, Amount
* Mismatched cells highlighted in red

**Right Panel — Invoice**

* Invoice Number and Terms displayed at top
* Terms dropdown (editable, e.g., Net 30)
* Columns: Description, Qty, Unit Price, Amount
* Mismatched cells highlighted in red

### Mismatch Highlighting

* Cells with discrepancies between PO and Invoice are highlighted with a red background and dashed border
* Example: PO shows Qty 0 / $0.00 but Invoice shows Qty 1 / $53.42

### Action Buttons

| Button       | Action                                                         |
| ------------ | -------------------------------------------------------------- |
| **Rerun**    | Re-execute the matching algorithm (e.g., after PO updates)     |
| **Manual**   | Send the invoice directly to the Manual stage for human review |
| **Reject**   | Reject the invoice (red, destructive action)                   |
| **Continue** | Accept the match results and advance to the next stage         |

## User Workflows

### Invoice with No Mismatches

1. Invoice enters Matching stage automatically after Intake
2. PO and Invoice line items are compared
3. No mismatches found → all cells are clean
4. User clicks **Continue** → Invoice advances to Filing stage (skips Manual)

### Invoice with Mismatches

1. Invoice enters Matching stage
2. Error banner displays: "This invoice has matching errors"
3. User reviews side-by-side comparison with red-highlighted cells
4. User decides:
   * **Continue** → Accept mismatches and advance (to Manual or Filing based on workflow config)
   * **Manual** → Route to Manual stage for line-item-level review
   * **Reject** → Reject the invoice entirely
   * **Rerun** → Re-execute matching (e.g., if PO was updated in ERP)

### Rerun Matching

1. User updates PO data in ERP
2. User clicks **Rerun** on the Matching stage
3. System re-fetches PO data and re-runs comparison
4. Updated results displayed

## Existing Codebase Findings

* **PO retrieval**: `ERPService.get_purchase_order()` and `ERPService.get_receipts_by_po_number()` already exist and call the Epicor 21 API
* **Matching route**: `PATCH /invoices/{invoiceID}/matcher` exists in `invoice_v2.py` — this is the endpoint for updating matching data
* **Stage transitions**: `POST /invoices/{invoiceID}/stage/{viewedStage}` handles stage changes (Continue, Reject, Manual routing)
* **Receipt fields**: Epicor connector fetches: `receipt_number`, `po_number`, `po_line_number`, `unit_cost`, `extended_cost`, `qty_ordered`, `item_id`, `item_desc`, `receipt_line_unit_of_measure`, `qty_vouched`, `vendor_name`
* **PO lookup**: Tries external PO number first (`ExternalPoNo`), then internal (`PoNo`)
* **Audit logging**: `POST /databases/{db_name}/audit_logging` and `GET /invoices/{invoice_id}/history` exist in the datastore API

## T-Shirt Sizing (Revised)

| Layer             | Size         | Rationale                                                                                                                                                                                                           |
| ----------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **UI**            | M (3–5 days) | Side-by-side comparison layout, dynamic mismatch highlighting, error banner, invoice header, terms dropdown, 4 action buttons, workflow breadcrumb integration.                                                     |
| **API / Backend** | M (3–5 days) | PO retrieval and stage transition routes already exist. Need to build: mismatch detection logic, comparison response format, rerun endpoint. Matching data persistence via existing `PATCH /invoices/{id}/matcher`. |
| **Integration**   | S (1–2 days) | PO fetch and receipts fetch from Epicor already implemented. Receipt fields already defined. May need minor data normalization for the comparison view.                                                             |

**Overall: M (3–5 days)** — reduced from L because PO retrieval, stage transitions, and audit logging already exist.

## Proposed User Stories

### MATCH-US-1: PO/Invoice Side-by-Side Comparison

**As a** reviewer, **I want to** see the PO and Invoice line items side by side **so that** I can identify discrepancies.

* AC: PO data displayed on the left, Invoice data on the right
* AC: Line items aligned by matching logic (description/SKU)
* AC: Columns show Receipt #, Description, Qty, Unit Price, Amount

### MATCH-US-2: Mismatch Highlighting

**As a** reviewer, **I want to** see mismatched cells highlighted in red **so that** discrepancies are immediately visible.

* AC: Cells where PO and Invoice values differ are highlighted
* AC: Mismatches include quantity, unit price, and amount differences

### MATCH-US-3: Matching Error Banner

**As a** reviewer, **I want to** see a clear error message when the invoice has matching errors **so that** I know action is required.

* AC: Error banner appears at top of page when mismatches exist
* AC: Banner is hidden when no mismatches are detected

### MATCH-US-4: Matching Stage Actions

**As a** reviewer, **I want to** choose between Rerun, Manual, Reject, and Continue **so that** I can route the invoice appropriately.

* AC: Rerun re-executes matching and refreshes results
* AC: Manual routes invoice to Manual stage
* AC: Reject moves invoice to rejected status with confirmation
* AC: Continue advances invoice to the next stage

### MATCH-US-5: Rerun Matching

**As a** reviewer, **I want to** rerun the matching after a PO is updated **so that** I see the latest comparison.

* AC: Rerun fetches latest PO data from ERP
* AC: Comparison results are refreshed in the UI
* AC: Previous match results are replaced

---

# 3. Manual Stage

## Overview

The Manual Stage is the third stage in the invoice workflow (Intake → Matching → **Manual** → Filing → Closed). It allows reviewers to manually override line item matching, vouch for invoice line items against PO receipts, add notes explaining adjustments, and approve or reject the invoice before it advances to Filing.

*(image not available in export: /Users/karan.[thelakkat@postman.com](mailto:thelakkat@postman.com)/Repos/fern-cla-main/Reference Material/images/Manual Stage.png)*

## UI Components

### Workflow Breadcrumb

* Linear stage indicator: Intake → Matching → **Manual** → Filing → Closed
* Manual stage highlighted as current (green check on previous stages)

### Invoice Header

| Field          | Description                 |
| -------------- | --------------------------- |
| Invoice Date   | Date the invoice was issued |
| Due Date       | Payment due date            |
| Vendor         | Vendor name                 |
| Vendor Address | Vendor's address            |

### Left Panel — PO / Receipt Data

* **Terms** displayed (e.g., Net 30)
* **Vouch All?** checkbox — bulk vouch all line items at once
* Table columns:
  * Receipt #
  * Item ID
  * Item Description
  * UOM (Unit of Measure)
  * QTY (Rem) — remaining quantity on PO
  * QTY (Inv) — quantity on invoice
  * Unit Price
  * Freight (Inv)
  * Ext Cost
* Subtotal, Freight, and Total rows
* Displays "No results." when no PO receipts are matched

### Right Panel — Invoice Data

* **Terms** mapping displayed (e.g., "NET 30 DAYS mapped to Net 30")
* Table columns:
  * Prod Code
  * Prod Description
  * Qty
  * Unit Price
  * Ext Cost
* Subtotal, Freight, and Total rows

### Manual Stage Notes

* Textarea: "Add notes about the manual adjustment..."
* Accept (green check) and Cancel (red X) buttons for the note
* Notes are saved as part of the manual review record

### Action Buttons

| Button      | Action                                          |
| ----------- | ----------------------------------------------- |
| **Reject**  | Reject the invoice (red, destructive action)    |
| **Approve** | Approve the invoice and advance to Filing stage |

## User Workflows

### Manual Review with Vouching

1. Invoice arrives at Manual stage (from Matching or directly)
2. Reviewer sees PO receipt data on the left, invoice data on the right
3. Reviewer vouches individual line items or clicks **Vouch All?**
4. Reviewer adds notes explaining any adjustments
5. Reviewer clicks **Approve** → Invoice advances to Filing

### Manual Review — No PO Match

1. Invoice has no matching PO receipts ("No results." on left panel)
2. Reviewer reviews invoice data on the right
3. Reviewer adds notes explaining the approval rationale
4. Reviewer clicks **Approve** or **Reject**

### Reject Invoice

1. Reviewer determines the invoice cannot be approved
2. Reviewer optionally adds notes
3. Reviewer clicks **Reject** → Invoice is rejected

## Existing Codebase Findings

* **Manual route**: `PATCH /invoices/{invoiceID}/manual` exists in `invoice_v2.py` — this handles vouching/manual stage updates
* **Vouching in ERP**: The Epicor receipt data includes `receipt_line_vouch_complete` and `qty_vouched` fields — vouching is an ERP-level concept (marking receipt lines as verified against an invoice)
* **Terms mapping**: `get_all_terms()` returns a dict of `{terms_id: terms_desc}` from Epicor. The mapping shown in the UI ("NET 30 DAYS" → "Net 30") is likely matching the invoice's raw terms text to the ERP's `terms_desc` values.
* **Stage transitions**: Same `POST /invoices/{invoiceID}/stage/{viewedStage}` route handles Approve/Reject
* **Receipt fields available for vouching**: `receipt_number`, `po_line_number`, `item_id`, `item_desc`, `unit_cost`, `extended_cost`, `qty_ordered`, `receipt_line_unit_of_measure`, `receipt_line_unit_quantity`, `qty_vouched`

## T-Shirt Sizing (Revised)

| Layer             | Size         | Rationale                                                                                                                                                                                           |
| ----------------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **UI**            | M (3–5 days) | Two-panel layout with PO receipts and invoice data, Vouch All checkbox, individual line item vouching, Manual Stage Notes textarea with save/cancel, terms mapping display, Approve/Reject buttons. |
| **API / Backend** | S (1–2 days) | Manual update route already exists (`PATCH /invoices/{id}/manual`). Stage transition route exists. Need to confirm vouching state persistence and notes storage format.                             |
| **Integration**   | XS (\<1 day) | Receipt data and terms already fetched by existing Epicor connector methods. No new ERP integration needed.                                                                                         |

**Overall: S–M (2–5 days)** — reduced because the API route and ERP integration already exist. Primary effort is UI.

## Proposed User Stories

### MAN-US-1: Manual Review Layout

**As a** reviewer, **I want to** see PO receipt data alongside invoice data **so that** I can manually verify line items.

* AC: Left panel shows PO receipt details (Receipt #, Item ID, Description, UOM, quantities, pricing)
* AC: Right panel shows invoice line items (Prod Code, Description, Qty, Unit Price, Ext Cost)
* AC: Totals (Subtotal, Freight, Total) displayed on each side

### MAN-US-2: Line Item Vouching

**As a** reviewer, **I want to** vouch individual invoice line items against PO receipts **so that** I can confirm they match.

* AC: Each line item has a vouch action
* AC: Vouched items are visually indicated
* AC: Vouch state is persisted

### MAN-US-3: Vouch All

**As a** reviewer, **I want to** vouch all line items at once **so that** I can expedite review when everything matches.

* AC: "Vouch All?" checkbox vouches all line items
* AC: Unchecking removes vouch from all items

### MAN-US-4: Manual Stage Notes

**As a** reviewer, **I want to** add notes explaining my manual adjustments **so that** there is an audit trail.

* AC: Textarea allows free-text notes
* AC: Save (check) and Cancel (X) buttons for note entry
* AC: Notes are persisted and visible in audit history

### MAN-US-5: Approve / Reject Invoice

**As a** reviewer, **I want to** approve or reject the invoice **so that** it advances to Filing or is stopped.

* AC: Approve advances invoice to Filing stage
* AC: Reject moves invoice to rejected status with confirmation prompt
* AC: Both actions are audit-logged with timestamp and user

---

# 4. Filing Stage

## Overview

The Filing Stage is the fourth and final active stage in the invoice workflow (Intake → Matching → Manual → **Filing** → Closed). It handles the submission of the approved invoice to the connected ERP system and confirms the filing status. Once filed successfully, the invoice transitions to Closed.

*(image not available in export: /Users/karan.[thelakkat@postman.com](mailto:thelakkat@postman.com)/Repos/fern-cla-main/Reference Material/images/Filling Stage.png)*

## UI Components

### Workflow Breadcrumb

* Linear stage indicator: Intake → Matching → Manual → **Filing** → Closed
* All previous stages show green checks (completed)

### Left Panel — Invoice Document Viewer

* Rendered image/PDF of the original invoice document
* Navigation controls:
  * Previous / Next page arrows
  * Page indicator (e.g., "1 of 1")
* Zoom controls:
  * Zoom in / Zoom out buttons
  * Current zoom level displayed (e.g., "120%")
* Copy / export buttons

### Right Panel — Filing Status

* **Success Banner**: "Filed successfully!" (green highlight)
* **Invoice Filing Status** card:
  * Confirmation message with timestamp: "Invoice filed to ERP on \[date] @ \[time]"
  * Example: "Invoice filed to ERP on 03/05/2026 @ 12:15:55 AM."

### Action Buttons

| Button       | Action                                                               |
| ------------ | -------------------------------------------------------------------- |
| **Reject**   | Reject the filing (red, destructive — returns invoice for re-review) |
| **Continue** | Confirm filing and transition invoice to Closed                      |

## User Workflows

### Successful Filing

1. Invoice arrives at Filing stage after Manual approval
2. System automatically files the invoice to the connected ERP
3. "Filed successfully!" banner and timestamp are displayed
4. Reviewer verifies the filing details and original invoice document
5. Reviewer clicks **Continue** → Invoice transitions to **Closed**

### Filing Failure

1. Invoice arrives at Filing stage
2. ERP filing fails (connection error, validation error, etc.)
3. Error status is displayed instead of success banner
4. Reviewer can:
   * **Reject** → Route back for re-review
   * Retry filing (if supported)

### Reject After Filing

1. Reviewer notices an issue with the filed invoice
2. Reviewer clicks **Reject**
3. Invoice is returned to an earlier stage or rejected queue

## Existing Codebase Findings

* **Filing already implemented**: `ERPConnector.file_invoice()` → `Epicor21Connector.file_invoice()` iterates over `sync_data.purchaseOrders` and calls `convert_to_voucher()` for each PO
* **Filing mechanism**: Uses Epicor's interactive UI server sessions (XML-based). Creates a session, opens the "Convert PO to Voucher" window, populates tabs with invoice/PO data, and submits. This is **not a REST API call** — it automates the Epicor desktop UI via server-side sessions.
* **SyncData model**: The filing payload is a `SyncData` object containing: `invoiceID`, `purchaseOrders` (dict of `SyncDataPurchaseOrder` with PO ID, invoice number/date, line items with quantities/prices/freight, subtotals, tax, surcharges, shipping)
* **XML transaction**: `filing_xml.py` builds XML with tabs: voucher info (Tab 1), accounts (Tab 2), terms (Tab 3), miscellaneous, line items (Tab 17), purchase accounts (Tab 18)
* **Filing errors**: Custom `InvoiceFilingError` exception raised on failure
* **DELETE\_filing.py**: Exists — suggests there's logic to reverse/delete a filing
* **Save enabled flag**: `self.save_enabled` is loaded from config but its usage in the filing flow needs confirmation
* **Mock ERP**: `POST /{client_name}/invoice/file` exists in mock-services for testing

## T-Shirt Sizing (Revised)

| Layer             | Size         | Rationale                                                                                                                                                                       |
| ----------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **UI**            | S (1–2 days) | Split layout with document viewer (existing component) on left, status card on right. Success/error banner, filing timestamp display, Continue/Reject buttons. Minimal new UI.  |
| **API / Backend** | S (1–2 days) | `file_invoice()` already implemented in ERPService. Stage transition route exists. Need: filing status tracking/persistence, timestamp recording, error status surfacing to UI. |
| **Integration**   | XS (\<1 day) | Epicor filing via XML voucher conversion already fully implemented. `SyncData` model defined. Mock ERP filing endpoint exists for testing.                                      |

**Overall: S–M (2–4 days)** — significantly reduced because the entire filing pipeline (SyncData → XML → Epicor session) is already built.

## Proposed User Stories

### FILE-US-1: Invoice Document Viewer

**As a** reviewer, **I want to** view the original invoice document during Filing **so that** I can verify what was filed.

* AC: Invoice document rendered on the left panel
* AC: Zoom in/out and page navigation controls functional
* AC: Document is read-only at this stage

### FILE-US-2: ERP Filing Execution

**As the** system, **I want to** file the approved invoice to the connected ERP **so that** the invoice is recorded in the customer's financial system.

* AC: Invoice data is sent to the ERP via the configured connector
* AC: Filing timestamp is recorded
* AC: Success or failure status is returned and displayed

### FILE-US-3: Filing Status Display

**As a** reviewer, **I want to** see the filing status and timestamp **so that** I know the invoice was successfully filed.

* AC: "Filed successfully!" banner displayed on success
* AC: Filing timestamp shown (date and time)
* AC: Error message displayed on failure

### FILE-US-4: Continue to Closed

**As a** reviewer, **I want to** confirm the filing and close the invoice **so that** the workflow is complete.

* AC: Continue button transitions invoice to Closed status
* AC: Invoice is no longer editable after closing
* AC: Closed status is reflected in the workflow breadcrumb

### FILE-US-5: Reject Filed Invoice

**As a** reviewer, **I want to** reject a filed invoice **so that** issues can be addressed before closing.

* AC: Reject button available even after successful filing
* AC: Confirmation prompt before rejection
* AC: Invoice routed to appropriate queue for re-review

---

# 5. Architectural Risks & Mitigations

| # | Risk                                                                                                                                                                                                      | Impact   | Likelihood | Mitigation                                                                                                                                                                           |
| - | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| 1 | **ERP connector unavailability during Filing** — If the ERP is down or unreachable when an invoice hits the Filing stage, invoices pile up with no path forward.                                          | High     | Medium     | Implement retry queue with exponential backoff. Surface filing failures with clear error states. Allow manual retry from the UI.                                                     |
| 2 | **Single ERP connection per tenant** — If only one ERP connection is supported, tenants with multiple ERP instances (e.g., by region or entity) cannot be fully onboarded.                                | Medium   | Medium     | Confirm with Product whether multi-connection support is needed at launch. Design the data model to support multiple connections even if UI is single-connection initially.          |
| 3 | **Multi-tenancy data isolation** — As a SaaS platform, ERP credentials, invoice data, and PO data must be strictly isolated between tenants. Leaking one tenant's data to another is a critical failure.  | Critical | Low        | Enforce tenant-scoped queries at the data access layer. ERP credentials encrypted per tenant. Penetration testing during QA.                                                         |
| 4 | **PO data staleness in Matching** — If PO data is cached rather than fetched in real-time from the ERP, mismatches may be based on outdated data, leading to false positives.                             | Medium   | Medium     | Real-time PO fetch on Matching stage entry. Rerun button as fallback. Consider a configurable cache TTL if performance is a concern.                                                 |
| 5 | **Matching algorithm accuracy** — Line item matching by description text is fragile (vendor descriptions often differ from PO descriptions). False mismatches erode user trust.                           | High     | High       | Investigate multi-signal matching (description + SKU + unit price + quantity). Consider AI/fuzzy matching as a future enhancement. Start with exact match + configurable tolerance.  |
| 6 | **Credential rotation and expiry** — ERP API keys or session tokens may expire, breaking the connection silently. Invoices could fail at Filing without clear cause.                                      | Medium   | Medium     | Health check endpoint that validates ERP connectivity on a schedule. Surface connection status on the Connections page. Alert admins before credentials expire (if expiry is known). |
| 7 | **Stage framework coupling** — The new stages (Matching, Manual, Filing) must integrate with the existing Intake stage and Closed transition. Tight coupling could make future stage additions difficult. | Medium   | Low        | Design stages as pluggable components with a shared stage interface. Each stage owns its own UI, API, and transition logic. Stage ordering should be configurable.                   |
| 8 | **Webhook delivery reliability** — Webhooks that fail silently (no retry, no logging) leave external systems out of sync with invoice status.                                                             | Medium   | Medium     | Implement webhook delivery with retry (3 attempts with backoff). Log all delivery attempts with status codes. Surface failed deliveries in the Webhook Manager UI.                   |

---

# 6. Stage Transition Logic

## Invoice Lifecycle Flow

There are **3 possible paths** through the workflow. Every invoice starts at Intake. At each stage, the user picks an action that determines what happens next.

### Path 1 — Happy Path (no mismatches)

```
INTAKE → MATCHING → FILING → CLOSED
          [Continue]  [Continue]
```

Invoice matches cleanly. User clicks Continue at Matching (skips Manual), filing happens automatically, user clicks Continue to close.

### Path 2 — Manual Review Required

```
INTAKE → MATCHING → MANUAL → FILING → CLOSED
          [Manual]   [Approve] [Continue]
```

Mismatches found or user wants to review. User clicks Manual, reviews and vouches line items, clicks Approve to file, then Continue to close.

### Path 3 — Rejected (from any stage)

```
INTAKE → MATCHING → REJECTED
          [Reject]

INTAKE → MATCHING → MANUAL → REJECTED
          [Manual]   [Reject]

INTAKE → MATCHING → ... → FILING → REJECTED
                            [Reject]
```

User clicks Reject at Matching, Manual, or Filing. Invoice moves to `closed` stage with `rejected` substatus. **This is final — no re-entry.**

### Visual Summary

```
              INTAKE
                │
                ▼ (auto)
             MATCHING ──[Reject]──► REJECTED
              │    │
    [Continue] │    │ [Manual]
              │    ▼
              │  MANUAL ──[Reject]──► REJECTED
              │    │
              │    │ [Approve]
              ▼    ▼
              FILING ──[Reject]──► REJECTED
                │
                │ [Continue]
                ▼
              CLOSED
```

## Transition Rules

| From     | To                   | Trigger                | Conditions                                 |
| -------- | -------------------- | ---------------------- | ------------------------------------------ |
| Intake   | Matching             | Automatic              | Invoice passes intake validation           |
| Matching | Manual               | User clicks "Manual"   | Any — user-initiated routing               |
| Matching | Filing               | User clicks "Continue" | No mismatches (or user accepts mismatches) |
| Matching | Rejected             | User clicks "Reject"   | Confirmation required                      |
| Manual   | Filing               | User clicks "Approve"  | Vouching completed (TBD if required)       |
| Manual   | Rejected             | User clicks "Reject"   | Confirmation required                      |
| Filing   | Closed               | User clicks "Continue" | ERP filing successful                      |
| Filing   | Rejected / Re-review | User clicks "Reject"   | Confirmation required                      |

## Open Transition Questions

* Can an invoice skip Matching and go directly to Manual? (e.g., no PO on file)
* Can a Rejected invoice be re-opened and re-enter the workflow?
* Does "Reject" at Filing reverse the ERP filing, or is it an internal-only status change?
* Is Closed truly terminal, or can a Closed invoice be re-opened?
* Can the stage order be reconfigured per tenant (e.g., skip Manual entirely)?

---

# 7. Dependencies & Recommended Build Order

## Dependency Map

```
ERP Integration Config ◄──────────────────────────────────┐
        │                                                  │
        │ (ERP connection required for PO fetch)           │
        ▼                                                  │
  Matching Stage                                           │
        │                                                  │
        │ (reuses PO data, matching results)               │
        ▼                                                  │
   Manual Stage                                            │
        │                                                  │
        │ (ERP connection required for filing)             │
        ▼                                                  │
   Filing Stage ───────────────────────────────────────────┘
```

## Recommended Build Order

| Phase       | Area                   | Rationale                                                                                                                                                  |
| ----------- | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Phase 1** | ERP Integration Config | Foundation — everything else depends on an active ERP connection. Build the connector abstraction, Epicor 21 adapter, and Connections UI.                  |
| **Phase 2** | Matching Stage         | Depends on ERP connection for PO retrieval. Build matching algorithm, side-by-side UI, and mismatch detection.                                             |
| **Phase 3** | Manual Stage           | Depends on Matching output (mismatch data, PO line items). Build vouching, notes, and approve/reject flow. Can partially parallel with Phase 2 (UI work).  |
| **Phase 4** | Filing Stage           | Depends on ERP connection (for filing) and Manual/Matching (for approved invoices to file). Build filing execution, status display, and Closed transition. |

## Cross-Cutting Dependencies

* **Stage framework & breadcrumb** — Shared component needed by all 3 stages. Build early in Phase 2.
* **Audit logging** — Used by Matching (Continue with mismatches), Manual (approve/reject + notes), and Filing (filing confirmation). Build as a shared service in Phase 1 or early Phase 2.
* **Invoice document viewer** — Used in Filing stage. Confirm if this exists from Intake or needs to be built. If new, start in Phase 3.
* **Rejection flow** — All stages support Reject. Define the rejection queue/status handling once and reuse.

---

# 8. Repository Design

## Structure

The codebase follows a **feature-based** folder structure under a versioned `v1/` root. Each feature is self-contained with its own route, controller, service, models/types, and co-located tests.

```
src/
├── app.py                              # FastAPI app entry point, middleware, startup
│
├── api/
│   ├── health/
│   │   └── health_route.py             # Health check endpoint
│   └── v1/
│       ├── erp_config/
│       │   ├── __init__.py
│       │   ├── erp_config_route.py     # Route registration + schema/validation
│       │   ├── erp_config_controller.py # Request handlers (extract params, call service)
│       │   ├── erp_config_service.py   # Business logic
│       │   ├── erp_config_types.py     # Request/response models (Pydantic)
│       │   └── tests/
│       │       └── test_erp_config_service.py
│       │
│       ├── matching/
│       │   ├── __init__.py
│       │   ├── matching_route.py
│       │   ├── matching_controller.py
│       │   ├── matching_service.py
│       │   ├── matching_types.py
│       │   └── tests/
│       │       └── test_matching_service.py
│       │
│       ├── manual/
│       │   ├── __init__.py
│       │   ├── manual_route.py
│       │   ├── manual_controller.py
│       │   ├── manual_service.py
│       │   ├── manual_types.py
│       │   └── tests/
│       │       └── test_manual_service.py
│       │
│       ├── filing/
│       │   ├── __init__.py
│       │   ├── filing_route.py
│       │   ├── filing_controller.py
│       │   ├── filing_service.py
│       │   ├── filing_types.py
│       │   └── tests/
│       │       └── test_filing_service.py
│       │
│       └── __init__.py
│
├── auth/
│   ├── roles.py                        # User role definitions
│   └── scopes.py                       # Permission scopes per feature
│
├── clients/
│   ├── erp_client.py                   # ERP connector client (wraps common/erp)
│   └── tests/
│       └── test_erp_client.py
│
├── config/
│   ├── __init__.py
│   ├── env.py                          # Environment variables with defaults
│   └── app_config.py                   # Azure App Configuration integration
│
├── errors/
│   ├── __init__.py
│   └── http_errors.py                  # HTTP error factory (400, 401, 404, 429, 500)
│
├── middleware/
│   ├── auth_middleware.py              # JWT validation (dev HS256 / prod RS256 via Azure AD)
│   ├── logger_middleware.py            # App Insights (prod) or console (dev) logging
│   └── request_id_middleware.py        # X-Request-Id correlation propagation
│
├── types/
│   ├── api.py                          # Shared DTOs (Invoice, PurchaseOrder, etc.)
│   └── enums.py                        # InvoiceStage, MatchStatus, VouchStatus, etc.
│
├── routes/
│   └── __init__.py                     # Route registration orchestrator
│
└── conftest.py                         # Shared pytest fixtures (test client, auth mocks, DB mocks)
```

## Conventions

### Feature Folder Contents

| File              | Responsibility                                                                                                                                |
| ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `*_route.py`      | Route registration with path, schema validation, auth decorators (`preValidation`), and scope checks. Wires controller handlers to endpoints. |
| `*_controller.py` | Request handlers. Extracts headers/params/body, calls service, returns response. Thin layer — no business logic.                              |
| `*_service.py`    | Business logic. Orchestrates between clients, data access, and other services. Handles data mapping and error translation.                    |
| `*_types.py`      | Pydantic models for request bodies, response schemas, and internal DTOs.                                                                      |
| `tests/`          | Unit tests co-located with the feature. Mock external clients and test service logic.                                                         |

### Layer Responsibilities

```
Route (route.py)
  ↓ defines path, method, schema, auth
Controller (controller.py)
  ↓ extracts request data, calls service
Service (service.py)
  ↓ business logic, orchestration, error handling
Client (clients/erp_client.py)
  ↓ wraps external API calls (ERP, datastore)
External API (Epicor, CosmosDB, etc.)
```

### Auth & Scopes

Auth scopes are defined per feature:

* `auth/scopes.py` — defines permission scopes per feature (e.g., `ERP_CONFIG_WRITE`, `INVOICES_READ`, `INVOICES_APPROVE`)
* `auth/roles.py` — maps roles to scope sets (e.g., `admin` → all scopes, `reviewer` → read + approve)
* Routes specify `valid_scopes` in config and use `authenticate` + `authorize` middleware

### Middleware Stack (app.py startup)

Middleware initialization order:

1. **Logging** — App Insights (production) or pretty console (development)
2. **App Configuration** — Azure App Config or env vars via `config/app_config.py`
3. **Authentication** — JWT validation middleware (dev HS256 / prod Azure AD RS256)
4. **Authorization decorators** — Scope-based access control
5. **Request ID propagation** — `X-Request-Id` header for correlation
6. **CORS** — Configured allowed origins, methods, headers
7. **Route registration** — All feature routes registered via `routes/__init__.py`
8. **Startup** — Server starts listening

### Error Handling

`errors/http_errors.py` provides a factory for consistent error responses:

```python
# HTTP error factory
def bad_request(message: str, details: dict = None) -> HTTPException: ...
def unauthorized(message: str) -> HTTPException: ...
def not_found(message: str) -> HTTPException: ...
def rate_limited(message: str, retry_after: int = 5) -> HTTPException: ...
def internal(message: str) -> HTTPException: ...
```

### Testing

* **Framework**: `pytest`
* **Location**: Co-located with each feature under `tests/`
* **Naming**: `test_<feature>_service.py` (primary), `test_<feature>_controller.py` (optional)
* **Mocking**: `pytest-mock` / `unittest.mock` for external clients (ERP, datastore)
* **Shared fixtures**: `conftest.py` at `src/` root for test client, auth bypass, mock DB
* **Run all**: `pytest src/`
* **Run feature**: `pytest src/api/v1/matching/tests/`
* **Coverage**: `pytest --cov=src/`

### Config (config/)

* `env.py` — All environment variables with typed defaults
* `app_config.py` — Azure App Configuration integration for runtime feature flags and secrets

### Router Registration (routes/**init**.py)

```python
from src.api.v1.erp_config.erp_config_route import erp_config_routes
from src.api.v1.matching.matching_route import matching_routes
from src.api.v1.manual.manual_route import manual_routes
from src.api.v1.filing.filing_route import filing_routes
from src.api.health.health_route import health_routes

def register_routes(app):
    app.include_router(health_routes, tags=["Health"])
    app.include_router(erp_config_routes, prefix="/api/v1/erp", tags=["ERP Config"])
    app.include_router(matching_routes, prefix="/api/v1/matching", tags=["Matching"])
    app.include_router(manual_routes, prefix="/api/v1/manual", tags=["Manual"])
    app.include_router(filing_routes, prefix="/api/v1/filing", tags=["Filing"])
```

---

# 9. Assumptions

| #  | Assumption                                                                                                                                                   | Impact if Wrong                                                                                                                                        |
| -- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| 1  | **Intake stage already exists and is functional.** Matching receives invoices from a working Intake stage.                                                   | If Intake isn't built, Matching has no input. Would need to stub or build Intake first.                                                                |
| 2  | **Single ERP connection per tenant at launch.** The UI shows one active connection; we assume multi-connection is out of scope for v1.                       | If multi-connection is needed, the data model and UI need to support connection selection per invoice. Adds M effort to ERP Config.                    |
| 3  | **Epicor 21 is the only ERP at launch.** Other providers (SAP, NetSuite, etc.) are future work.                                                              | If multiple ERPs are needed at launch, each adapter adds M–L effort. The abstraction layer must be solid from day one.                                 |
| 4  | **Invoice document viewer is an existing reusable component.** The Filing stage UI shows a doc viewer that likely exists from Intake.                        | If it needs to be built from scratch, add S–M effort to Filing stage UI.                                                                               |
| 5  | **PO data is available via the ERP API.** Matching depends on fetching PO details from the connected ERP in real time or near-real time.                     | If the ERP doesn't expose PO data via API, matching would need a manual upload or import mechanism. Major scope change.                                |
| 6  | **Users have a single role for now.** We assume no complex role-based permissions (e.g., separate Approver vs Reviewer roles) for v1.                        | If RBAC is required, add S–M effort per stage for permission checks and UI conditionals.                                                               |
| 7  | **The Closed stage is a terminal status, not a separate UI view.** Once Continue is clicked in Filing, the invoice is marked Closed with no further actions. | If Closed has its own view (summary, print, export), add S effort for a new stage UI.                                                                  |
| 8  | **Webhook delivery is best-effort with retries.** We assume webhooks don't need guaranteed delivery (e.g., message broker).                                  | If guaranteed delivery is required, adds M–L effort for a message queue integration (e.g., RabbitMQ, Azure Service Bus).                               |
| 9  | **Invoice-to-PO relationship is 1:1.** One invoice maps to one PO.                                                                                           | If an invoice can span multiple POs (or a PO can have multiple invoices), the Matching stage data model and UI need significant rework. Adds L effort. |
| 10 | **The SaaS platform handles authentication/authorization externally.** Login, SSO, and tenant identification are handled by an existing auth layer.          | If auth needs to be built, that's a separate epic and prerequisite to all of this work.                                                                |

---

# Summary — T-Shirt Sizing Overview

| Functional Area            | UI       | API / Backend | Integration | Overall        |
| -------------------------- | -------- | ------------- | ----------- | -------------- |
| **ERP Integration Config** | M (3–5d) | S (1–2d)      | S (1–2d)    | **M (3–5d)**   |
| **Matching Stage**         | M (3–5d) | M (3–5d)      | S (1–2d)    | **M (3–5d)**   |
| **Manual Stage**           | M (3–5d) | S (1–2d)      | XS (\<1d)   | **S–M (2–5d)** |
| **Filing Stage**           | S (1–2d) | S (1–2d)      | XS (\<1d)   | **S–M (2–4d)** |

**Total estimated effort: \~L (1–2 weeks)** — significantly reduced from the original XL estimate because the ERP connector layer, filing pipeline, PO retrieval, receipt fetching, stage transitions, and audit logging already exist in the codebase. The primary effort is **UI development** across all four areas.

---

# Acceptance Criteria (Spike)

* [ ] Designs are reviewed and accepted by the product owner prior to sprint commitment
* [ ] Each functional area has a documented T-shirt size estimate with rationale
* [ ] All open questions are answered or have an identified owner and resolution path
* [ ] The development team can decompose the spike output into sprint-ready user stories without requiring additional design sessions