Validation Engine
Validation Engine
Section titled “Validation Engine”The heart of the product. Rule-based, extensible, and auditable.
Architecture: Pipeline Pattern
Section titled “Architecture: Pipeline Pattern”The engine uses a pipeline of independent checks. Each check runs against an invoice and produces zero or more findings.
Invoice → [Check 1] → [Check 2] → [Check 3] → ... → [Findings Collection]Each check is:
- Independent — can run in isolation
- Configurable — can be enabled/disabled per contract
- Deterministic — same input always produces same output
- Auditable — every finding includes expected vs actual values
Validation Checks
Section titled “Validation Checks”Ordered by priority (critical checks run first):
| # | Check | Default Severity | Description |
|---|---|---|---|
| 1 | Contract validity | Critical | Is the contract active? Is the invoice date within the contract period? |
| 2 | Rate verification | High | Does each line item’s rate match the approved rate card for that role/service category? |
| 3 | Billing increment | Medium | Are hours billed in correct increments? (e.g., 0.7 hrs in a 6-min increment system should be flagged) |
| 4 | Monthly hour cap | High | Do total hours per role/service exceed the monthly limit defined in rate card? |
| 5 | Cumulative hour cap | High | Do total hours across ALL invoices for this contract exceed the lifetime limit? |
| 6 | Monthly ceiling | Critical | Does total billing this month (including this invoice) exceed the monthly NTE? |
| 7 | Contract ceiling | Critical | Does cumulative billing (including this invoice) exceed the total contract NTE? |
| 8 | Expense validation | Medium | Are expenses within allowed categories? Within per-instance and monthly caps? |
| 9 | Markup check | Medium | If expenses include markup, is it within the allowed percentage? |
| 10 | Pre-approval check | Medium | Do expenses requiring pre-approval have documented approval? |
| 11 | Duplicate detection | High | Has this invoice number been submitted before? Are there line items matching previous invoices? |
| 12 | Arithmetic check | Low | Does quantity x rate = amount? Does sum of line items = subtotal? Does subtotal + tax = total? |
| 13 | Service approval | Medium | Is the service category listed as approved under this contract? |
| 14 | Date validation | Medium | Are service dates within the billing period? Are any on weekends/holidays (configurable)? |
Check Details
Section titled “Check Details”Check 1: Contract Validity
Section titled “Check 1: Contract Validity”Input: invoice.contractId, invoice.invoiceDateLogic: - Load contract - If contract.status != 'active' → CRITICAL finding - If invoice.invoiceDate < contract.startDate → CRITICAL finding - If invoice.invoiceDate > contract.endDate → CRITICAL findingCheck 2: Rate Verification
Section titled “Check 2: Rate Verification”Input: Each line item where itemType = 'service'Logic: For each service line item: - Find matching rate card by (serviceCategory, roleLevel, effectiveDate) - If no rate card found → HIGH finding ("no approved rate for this role/service") - If lineItem.rate != rateCard.approvedRate → HIGH finding - expectedValue = rateCard.approvedRate - actualValue = lineItem.rate - variance = (lineItem.rate - rateCard.approvedRate) * lineItem.quantityCheck 3: Billing Increment
Section titled “Check 3: Billing Increment”Input: Each line item where rateType = 'hourly'Logic: - Get billingIncrement from rate card - Convert increment to decimal hours (6min = 0.1, 15min = 0.25, etc.) - If lineItem.quantity % incrementDecimal != 0 → MEDIUM finding - Description: "Hours billed as X.XX but billing increment is Y minutes"Check 4: Monthly Hour Cap
Section titled “Check 4: Monthly Hour Cap”Input: All service line items grouped by (serviceCategory, roleLevel)Logic: For each group: - Get rate card maxHoursPerMonth - If maxHoursPerMonth is null, skip - Sum hours from this invoice for this group - Add hours from other invoices in same billing period - If total > maxHoursPerMonth → HIGH finding - expectedValue = maxHoursPerMonth - actualValue = total hours - variance = (total - maxHoursPerMonth) * approvedRateCheck 5: Cumulative Hour Cap
Section titled “Check 5: Cumulative Hour Cap”Input: All service line items grouped by (serviceCategory, roleLevel)Logic: For each group: - Get rate card maxHoursTotal - If maxHoursTotal is null, skip - Get cumulative hours from ContractUsageSummary - Add hours from this invoice - If total > maxHoursTotal → HIGH findingCheck 6 & 7: Ceiling Checks
Section titled “Check 6 & 7: Ceiling Checks”Check 6 (Monthly): - Get all invoices for this contract in same month - Sum totalAmount + this invoice totalAmount - If sum > contract.monthlyCeiling → CRITICAL finding
Check 7 (Contract Total): - Get cumulativeBilledToDate from ContractUsageSummary - Add this invoice totalAmount - If sum > contract.totalCeiling → CRITICAL findingCheck 11: Duplicate Detection
Section titled “Check 11: Duplicate Detection”Logic: Step 1 — Invoice number check: - Search for existing invoice with same vendorId + invoiceNumber - If found → HIGH finding ("duplicate invoice number")
Step 2 — Line item similarity check: - For each line item, search previous invoices for items with matching: - serviceCategory + roleLevel + date + quantity + rate - If match found → HIGH finding ("potential duplicate line item") - Reference the matching invoice and line itemCumulative Tracking
Section titled “Cumulative Tracking”For checks 4, 5, 6, and 7, the engine needs historical data. This is handled via the ContractUsageSummary table.
Update Flow
Section titled “Update Flow”1. Invoice submitted2. Validation engine runs all checks3. If invoice is approved: a. Update ContractUsageSummary for this contract + period b. Recalculate: totalBilled, totalHoursByRole, cumulativeBilledToDate c. Update remainingCeilingImportant: Pre-Approval Validation
Section titled “Important: Pre-Approval Validation”When running ceiling and cap checks, include the current invoice in the calculation even though it hasn’t been approved yet. This catches breaches before approval, not after.
Severity Assignment Rules
Section titled “Severity Assignment Rules”| Severity | Criteria | Auto-Assigned When |
|---|---|---|
| Critical | Contract breach or financial impact > 10% of invoice total | Ceiling breach, expired contract |
| High | Financial impact 5-10% of invoice total OR repeated pattern | Rate mismatch, hour overages, duplicates |
| Medium | Financial impact 1-5% OR policy violation | Expense breaches, increment violations |
| Low | Financial impact < 1% OR administrative issue | Rounding errors, date anomalies |
Invoice Risk Score
Section titled “Invoice Risk Score”A single number to rank invoices by risk level. Higher = more attention needed.
riskScore = sum(findingSeverityWeights) / invoiceTotal * 100
Severity weights: critical = variance * 4 high = variance * 3 medium = variance * 2 low = variance * 1
Example: Invoice total: R100,000 Findings: - Rate mismatch (HIGH): variance R5,000 → 5,000 * 3 = 15,000 - Expense exceeded (MEDIUM): variance R800 → 800 * 2 = 1,600
riskScore = (15,000 + 1,600) / 100,000 * 100 = 16.6Use this score to sort invoices on the dashboard — highest risk first.
Extensibility
Section titled “Extensibility”The pipeline pattern makes it easy to add new checks post-MVP:
- Trend analysis — flag vendors whose rates creep up gradually
- Time pattern detection — flag suspiciously consistent hours (exactly 8.0 every day)
- Cross-vendor comparison — flag if one vendor charges significantly more than others for same service
- AI-powered anomaly detection — use ML to identify unusual patterns
Each new check is simply a new function added to the pipeline.