---
title: Audit bill comparison
summary: Compares two clsBillAudit snapshots field-by-field and reports mismatches.
status: draft
owner: SAIF Doc Gen
last_reviewed: 2026-07-30
library: SAIF.Business.Claim.Bill.AutomatedAudit
reference_type: package
operation_id: OP-AA-06
trigger: Caller invokes clsAuditCompare.CompareBills(Audit1, Audit2, ForceCompare)
---

# Audit bill comparison

`clsAuditCompare.CompareBills` is a standalone diagnostic utility, independent of the `BillAuditEngine.AuditBill` pipeline, that compares two `clsBillAudit` snapshots — typically a pre- and post-audit copy of the same bill — across header fields, status, audit totals, messages, adjustments, and audited-service lines, and reports every mismatch as a comparison error.

| Field | Value |
| --- | --- |
| **Operation ID** | OP-AA-06 |
| **Trigger** | Caller invokes `clsAuditCompare.CompareBills(inAudit1, inAudit2, Optional ForceCompare As Boolean = False)` |
| **Inputs** | Two `clsBillAudit` instances; optional `ForceCompare` flag to bypass the matching-`BillID` precondition |
| **Outputs** | Boolean match result (`True` when no compare errors were added); collection of comparison error messages describing each mismatched field |
| **Primary components** | `clsAuditCompare.CompareBills`, `clsAuditCompare.CompareField` |
| **Entry point** | `Business\SAIF.Business.Claim.Bill.AutomatedAudit\AuditUtilities\clsAuditCompare.vb` |

## Steps

1. **Store inputs.** `CompareBills` stores the two supplied `clsBillAudit` objects.
2. **Check matching bill identity.** Unless `ForceCompare = True`, the two bills must share the same `BillID`; otherwise add error `"Bills do not have matching ID's - Compare Cancelled."` and the comparison for identity stops here (other sections below still run against the given inputs).
3. **Compare bill header properties.** Compare `BillTotalAmount`, `StatementFromDate`, `StatementThruDate`, `ClaimID`, `DRGCode`, `ClaimNumber`, and `ClaimCheckValue` using `CompareField` (see step 8 for field-specific comparison rules).
4. **Compare latest status records.** Select each bill's latest status row by `UpdateDate` descending and compare `StatusCode` and `ResponsiblePartyID`. If either bill has no status record, add error `"Could not determine matching Status Records"`.
5. **Compare audit-level properties.** Compare `AuditReceiptDate`, `ChargeableCostFlag`, `PrintEobFlag`, `SpecialHandlingCode`, `TotalAllowedAmount`, `TotalChargedAmount`, and `TotalMcoAdminFeeAmount`.
6. **Compare audit messages.** If the message counts differ, add a non-match result with message `"Comparison of Message Text and Category cancelled."` Otherwise, sort both message collections by category/text and compare `MessageText` and `MessageCategory` pairwise.
7. **Compare adjustments.** If the adjustment counts differ, add a non-match result with message `"Comparison of property elements cancelled."` Otherwise, sort both adjustment collections by `AuditServiceID` and code and compare `AuditAdjustmentAmount`, `AuditAdjustmentCode`, and `AuditServiceID` pairwise.
8. **Compare audited services.** If the service counts differ, add a non-match result with message `"Property comparison has been cancelled for this service."` Otherwise, compare a broad set of audited-service fields by matching `ServiceID`. `CompareField` applies type-specific rules:
   - `MessageText` — compares letters case-insensitively, then digits (excluding zeroes), then punctuation, with distinct mismatch messages: `"Values do not have the same letters."`, `"Values do not have the same digits."`, `"Values do not have the same punctuation marks."`
   - String fields such as `DRGCode`, `McoEligibilityStatusCode`, service modifiers, and `ServiceCode` — trimmed string comparison.
   - Decimal `*Amount` fields — numeric comparison, displayed as currency in the mismatch message.
   - `DateTime` fields — exact comparison.
   - All other fields — case-insensitive string comparison.
   - On any exception while comparing a field, add error `"<Class>/<Key>-<Prop>: Could not be compared - <exception>"` rather than allowing the exception to propagate.
9. **Return overall result.** `CompareBills` returns `True` only if no comparison errors were added across steps 2–8.

## Process flow

```mermaid
flowchart TD
	Start([CompareBills invoked]) --> IDCheck{ForceCompare or<br>BillID matches?}
	IDCheck -->|No| IDError[Add error:<br>IDs do not match]
	IDCheck -->|Yes| Header[Compare bill header fields]
	IDError --> Header
	Header --> Status[Compare latest status records]
	Status --> Audit[Compare audit-level totals/flags]
	Audit --> Messages{Message counts match?}
	Messages -->|No| MsgCancelled[Add: comparison cancelled]
	Messages -->|Yes| MsgCompare[Compare sorted message pairs]
	MsgCancelled --> Adjustments
	MsgCompare --> Adjustments{Adjustment counts match?}
	Adjustments -->|No| AdjCancelled[Add: comparison cancelled]
	Adjustments -->|Yes| AdjCompare[Compare sorted adjustment pairs]
	AdjCancelled --> Services
	AdjCompare --> Services{Service counts match?}
	Services -->|No| SvcCancelled[Add: comparison cancelled]
	Services -->|Yes| SvcCompare[Compare matched service fields<br>via CompareField]
	SvcCancelled --> Done
	SvcCompare --> Done([Return True if no errors added])
```

## Failure and edge-case behavior

- A count mismatch in messages, adjustments, or services cancels field-by-field comparison for that section entirely and reports a single cancellation message rather than identifying which specific rows differ.
- `ForceCompare = True` allows comparing two bills with different `BillID` values; all subsequent sections still run against whatever data each bill contains, which can produce a large number of unrelated mismatches.
- Exceptions during an individual field compare are caught and reported as comparison errors rather than aborting the whole comparison, so a single malformed field does not prevent the rest of the comparison from completing.
- This operation is not invoked anywhere in the `BillAuditEngine.AuditBill` pipeline traced in [Automated bill audit orchestration](automated-bill-audit-orchestration.md); it is a standalone tool for callers who need to verify audit results independently.*
- Source contains a commented-out Oracle-vs-DB2 `DispenseAsWrittenCode` normalization branch that is excluded from this operation's behavior as dead code.

## Related documentation
- [Operations and processes](../operations-processes.md)
- [Business rules](../business-rules.md)
- [SQL catalog](../sql/index.md)
- [Database tables](../database-tables.md)

> **Note:** Statements marked with `*` were inferred from code structure, naming conventions, or
> contextual evidence where direct confirmation was not available in source artifacts.

## Sources
- `Business\SAIF.Business.Claim.Bill.AutomatedAudit\AuditUtilities\clsAuditCompare.vb`
