# TANRAIL ONE — Phase 4.0 Workflows & State Machines

## 1. Customer Invoice Lifecycle Workflow

### 1.1 State Machine Diagram
```text
[ DRAFT ] ──(issue)──► [ ISSUED ] ──(record payment)──► [ PARTIALLY_PAID ]
    │                      │                                   │
    │                      ├────(full payment)─────────────────┼──────► [ PAID ]
    │                      │                                   │
    └──(void)──► [ VOIDED ] └──(credit note)──► [ CANCELLED ] ◄┘
```

### 1.2 State Definitions
- `DRAFT`: Invoice being created or modified. No financial or AR impact.
- `ISSUED`: Invoice finalized and presented to customer. Triggers AR recognition journal (`DR AR, CR Revenue, CR Tax`). Immutable.
- `PARTIALLY_PAID`: Payments allocated, but `balance_due > 0`.
- `PAID`: `balance_due == 0`. Fully settled.
- `CANCELLED`: Neutralized via Credit Note or full reversal.
- `VOIDED`: Draft invoice cancelled before issuance.

### 1.3 State Transitions & Side Effects
1. `DRAFT` → `ISSUED`:
   - Validates lines, tax calculation, customer assignment.
   - Generates sequential `invoice_number`.
   - Dispatches `InvoiceIssued` event.
   - Financial listener posts GL Journal: `DR AR`, `CR Revenue`, `CR Tax Liability`.
2. `ISSUED` → `PARTIALLY_PAID`:
   - Triggered when `PaymentAllocation` amount is saved where `amount < balance_due`.
3. `PARTIALLY_PAID` / `ISSUED` → `PAID`:
   - Triggered when total allocated payments equal invoice total (`balance_due == 0`).
4. `ISSUED` → `CANCELLED`:
   - Triggered via `CreditNote` full reversal.

---

## 2. Supplier Invoice (Vendor Bill) Workflow

### 2.1 State Machine Diagram
```text
[ DRAFT ] ──(submit)──► [ RECEIVED ] ──(approve/match)──► [ APPROVED ] ──(pay)──► [ PARTIALLY_PAID ]
    │                        │                                 │                       │
    └──(void)──► [ VOIDED ]  └──(reject)──► [ VOIDED ]         └──(full payment)───────┼──► [ PAID ]
```

### 2.2 Transitions & Side Effects
1. `DRAFT` → `RECEIVED`: Bill entered into system.
2. `RECEIVED` → `APPROVED`:
   - 3-Way Match validation executes against PO and Receipt.
   - Dispatches `SupplierInvoiceApproved` event.
   - Financial listener posts GL Journal: `DR Expense / Inventory`, `CR Accounts Payable`.
3. `APPROVED` → `PAID`:
   - Supplier payment allocated (`balance_due == 0`).

---

## 3. Three-Way Matching Workflow & Tolerances

### 3.1 3-Way Match Process
When a `SupplierInvoice` is submitted for approval:
```text
  [ PURCHASE ORDER ] ◄──────(check price & qty)───────┐
          │                                           │
          ▼                                           ▼
  [ PURCHASE RECEIPT ] ◄────(check received qty)─── [ SUPPLIER INVOICE ]
```

### 3.2 Matching Rules & Tolerances
1. **Quantity Match**: `Billed Quantity <= Received Quantity`.
   - Over-billing (`Billed Quantity > Received Quantity`) is blocked unless manager override is logged.
2. **Price Match**: `Billed Unit Price <= PO Unit Price`.
   - Unit price increase beyond configured percentage tolerance (default `0.00%`) flags bill as `DISCREPANCY`.
3. **Handling Discrepancies**:
   - `DISCREPANCY` status blocks automated GL posting and payment allocation until an authorized Manager explicitly approves the variance.

---

## 4. Payment Allocation Workflow

### 4.1 Generic Allocation Algorithm
When a `Payment` (customer) or `SupplierPayment` (vendor) is executed:

```text
Input: Payment, Array of Target Documents [{id: 10, amount: 50.00}]

1. Verify Payment.organization_id == TargetDocument.organization_id
2. Verify Payment.business_unit_id == TargetDocument.business_unit_id
3. Verify SUM(Allocations) <= Payment.unallocated_amount
4. For each TargetDocument:
     a. Verify TargetDocument.status in [ISSUED, APPROVED, PARTIALLY_PAID]
     b. Verify Allocation.amount <= TargetDocument.balance_due
     c. Create PaymentAllocation record
     d. Update TargetDocument.amount_paid += Allocation.amount
     e. Update TargetDocument.balance_due -= Allocation.amount
     f. Update TargetDocument.status (PAID if balance_due == 0, else PARTIALLY_PAID)
     g. Update Payment.unallocated_amount -= Allocation.amount
5. Post GL Journal:
   Customer: DR Cash/Bank, CR AR
   Supplier: DR AP, CR Cash/Bank
```

### 4.2 Overpayment & Unallocated Credits Policy
- If `Payment.amount > SUM(Allocations)`:
  - The remaining amount is stored in `Payment.unallocated_amount`.
  - The customer's AR sub-ledger shows a credit balance until allocated to future invoices or refunded.

---

## 5. Credit Note Workflow

### 5.1 Reversal Types
1. **Full Reversal**: Reverses entire invoice total. Invoice status becomes `CANCELLED`.
2. **Partial Reversal**: Reverses specific lines or dollar amounts.
3. **Accounting Impact**:
   - Dispatches `CreditNoteIssued` event.
   - Financial listener posts GL Journal:
     - `DEBIT Sales Revenue` (subtotal)
     - `DEBIT Tax Liability` (tax)
     - `CREDIT Accounts Receivable` (total)
