Skip to content

Validation Engine

The heart of the product. Rule-based, extensible, and auditable.


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

Ordered by priority (critical checks run first):

#CheckDefault SeverityDescription
1Contract validityCriticalIs the contract active? Is the invoice date within the contract period?
2Rate verificationHighDoes each line item’s rate match the approved rate card for that role/service category?
3Billing incrementMediumAre hours billed in correct increments? (e.g., 0.7 hrs in a 6-min increment system should be flagged)
4Monthly hour capHighDo total hours per role/service exceed the monthly limit defined in rate card?
5Cumulative hour capHighDo total hours across ALL invoices for this contract exceed the lifetime limit?
6Monthly ceilingCriticalDoes total billing this month (including this invoice) exceed the monthly NTE?
7Contract ceilingCriticalDoes cumulative billing (including this invoice) exceed the total contract NTE?
8Expense validationMediumAre expenses within allowed categories? Within per-instance and monthly caps?
9Markup checkMediumIf expenses include markup, is it within the allowed percentage?
10Pre-approval checkMediumDo expenses requiring pre-approval have documented approval?
11Duplicate detectionHighHas this invoice number been submitted before? Are there line items matching previous invoices?
12Arithmetic checkLowDoes quantity x rate = amount? Does sum of line items = subtotal? Does subtotal + tax = total?
13Service approvalMediumIs the service category listed as approved under this contract?
14Date validationMediumAre service dates within the billing period? Are any on weekends/holidays (configurable)?

Input: invoice.contractId, invoice.invoiceDate
Logic:
- Load contract
- If contract.status != 'active' → CRITICAL finding
- If invoice.invoiceDate < contract.startDate → CRITICAL finding
- If invoice.invoiceDate > contract.endDate → CRITICAL finding
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.quantity
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"
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) * approvedRate
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 finding
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 finding
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 item

For checks 4, 5, 6, and 7, the engine needs historical data. This is handled via the ContractUsageSummary table.

1. Invoice submitted
2. Validation engine runs all checks
3. If invoice is approved:
a. Update ContractUsageSummary for this contract + period
b. Recalculate: totalBilled, totalHoursByRole, cumulativeBilledToDate
c. Update remainingCeiling

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.


SeverityCriteriaAuto-Assigned When
CriticalContract breach or financial impact > 10% of invoice totalCeiling breach, expired contract
HighFinancial impact 5-10% of invoice total OR repeated patternRate mismatch, hour overages, duplicates
MediumFinancial impact 1-5% OR policy violationExpense breaches, increment violations
LowFinancial impact < 1% OR administrative issueRounding errors, date anomalies

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.6

Use this score to sort invoices on the dashboard — highest risk first.


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.