# MatchPurchaseBill

## Permission Scope

purchaseBill

## Overview

MatchPurchaseBill runs procurement matching on a draft supplier bill. For PO-linked bill lines, it compares each line against the referenced purchase-order line and, when 3-way matching is required, against cumulative posted goods receipts. PO-unlinked bill lines are included in bill amount calculation but skip PO-based quantity and price variance checks. Matching results are recorded as append-only ThreeWayMatchEvent records for audit. The bill then becomes `MATCHED` or `BLOCKED`.

## Business Rules

- Target purchase bill must exist
- Only bills in `DRAFT` status can be matched
- Matching mode must be explicitly recorded as `TWO_WAY` or `THREE_WAY`
- PO-linked lines: 2-way matching compares billed quantity and billed price to the purchase order
- PO-linked lines: 3-way matching also requires billed quantity to stay within cumulative posted receipt quantity, subject to configured tolerance
- PO-unlinked lines: included in bill amount calculation but skip PO-based variance checks
- Price variance above tolerance results in `BLOCKED` instead of `MATCHED`
- Matching creates ThreeWayMatchEvent records for each bill-line-to-receipt-line correspondence
- Matched bills calculate due schedule from the payment-term snapshot, increase purchase-order billed quantity (for PO-linked lines), and update the purchase order's `billingStatus` accordingly
- Blocked bills preserve variance reason and comparison basis without emitting downstream AP readiness or updating `billingStatus`
- PO-linked lines compare against the line's current quantity and unitPrice; bill lines reference the stable line ID directly
- 3-way match uses the line's received quantity directly: the cumulative posted receipt quantity is the sum of all posted GoodsReceiptLine records referencing the same line ID
- Item ACTIVE status is not checked — matching is a post-order flow; an item deactivated after the purchase order was committed must not block bill settlement

## Process Flow

```mermaid
flowchart TD
    A[Receive match request] --> B{Purchase bill exists and is DRAFT?}
    B -->|No| C[Return not found or invalid status]
    B -->|Yes| D{Matching mode provided?}
    D -->|No| E[Return MATCH_MODE_REQUIRED]
    D -->|Yes| F[Compare each bill line to PO line]
    F --> G{THREE_WAY?}
    G -->|Yes| H[Compare billed qty to posted receipts for line]
    G -->|No| I[Skip receipt comparison]
    H --> J{Within quantity and price tolerance?}
    I --> J
    J -->|No| K[Record variance evidence and set BLOCKED]
    J -->|Yes| L[Calculate due schedule and set MATCHED]
    L --> M["Recalculate billingStatus for affected POs"]
    K --> N[Return blocked bill]
    M --> O[Return matched bill]
```

## External Dependencies

- [getGoodsReceipt](../queries/GetGoodsReceipt.md) - Source cumulative posted receipt evidence for 3-way matching

## Error Scenarios

- **PURCHASE_BILL_NOT_FOUND**: Referenced purchase bill does not exist
- **PURCHASE_BILL_NOT_DRAFT**: Purchase bill is not in `DRAFT` status
- **MATCH_MODE_REQUIRED**: No matching mode was provided
- **PURCHASE_ORDER_CANCELLED**: Referenced purchase order is cancelled
- **PAYMENT_SCHEDULE_CALCULATION_FAILED**: Due schedule could not be derived from the payment-term snapshot

## Test Cases

- matches a valid 2-way purchase bill
- matches a valid 3-way purchase bill when posted receipt quantity is sufficient
- blocks a bill when 3-way quantity exceeds posted receipt quantity
- blocks a bill when price variance exceeds tolerance
- records matching mode on the bill
- derives purchase-order billing status when the bill is matched
- matches a purchase bill with PO-unlinked lines (skip PO variance checks, include in amount)
- creates ThreeWayMatchEvent records for bill-line-to-receipt-line correspondence
- returns error when purchase bill does not exist
- returns error when purchase bill is not draft
- returns error when matching mode is missing
- returns error when a referenced purchase order is cancelled
- returns error when payment schedule calculation fails
- detects price variance when ordered unit price is zero and billed is positive
- skips bill lines when resolved order line is not found
- skips bill lines when purchase order for the order line is not found
