Automated bill audit orchestration¶
BillAuditEngine.AuditBill is the single entry point for the automated bill-audit workflow. It retrieves a bill and its prior audit (if any), validates the bill and its status, generates or refreshes audit-specific data by enriching every service line with claim, provider, and national-drug-code context, delegates to the bill-edit, service-edit, and MCO-determination operations, invokes the rule-based audit engine, and finally saves the result unless running in read-only mode.
| Field | Value |
|---|---|
| Operation ID | OP-AA-01 |
| Trigger | Caller invokes BillAuditEngine.AuditBill(inBillID, ForceCompleteReAudit, ReadOnlyMode) |
| Inputs | BillID (Decimal), ForceCompleteReAudit (Boolean, default False), ReadOnlyMode (Boolean, default False), IDataPortalInfo supplied at engine construction |
| Outputs | AuditResults enum value in AuditResult; explanatory text in AuditResultMessage; populated clsBillAudit exposed as ProcessedAuditData; persisted CBP_BILL/audit data unless ReadOnlyMode is set; timing/diagnostic entries in TrailMessages |
| Primary components | BillAuditEngine.AuditBill, BillAuditEngine.RetrieveBill, BillAuditEngine.GenerateAuditData, BillAuditEngine.SaveAuditData, BillAuditEngine.GetProvider, BillAuditEngine.GetClaim, BillAuditEngine.GetNationalDrugCode |
| Entry point | Business\SAIF.Business.Claim.Bill.AutomatedAudit\BillAuditEngine.vb |
Steps¶
- Reset run state. Clear the audit trail and broken-rules collection, and record the requested
ReadOnlyModeandForceCompleteReAuditflags. Start an audit-trail group tagged"Audit Bill (<BillID>)"and log the mode flags and portal ID. - Initialize result state. Set
AuditResult = Success, clearAuditResultMessage, clear any priorAuditExceptions, and clearProcessedAuditData. - Retrieve the bill.
RetrieveBill(inBillID)callsclsBillAudit.GetBillAudit(DPI, inBillID). - Sub-step:
GetBillAuditissuesIDataPortalInfo.DataAccessLayer.RetrieveObjectagainst a criteria keyed byBillIDand returns the hydratedclsBillAudit, which lazily exposesClaim,BillingProvider,MCOInformation,History, andPharmacyEligibilityRange.
Origin:
clsBillAuditextendsclsBilland its persistence surface (Save,OnSave,OnSaveComplete,BeforeUpdate,GenerateStatusRecord) is implemented by Bill CRUD and Audit and audited services inSAIF.Business.Claim.Bill. - IfReadOnlyModeis requested, callBill.SetReadOnlyMode()immediately after retrieval. 4. Validate the bill. IfBill.IsValidisFalse: setAuditResult = InvalidBill(BR-001), copyBill.ErrorMessagesintoAuditResultMessage, propagate the bill's broken rules onto the engine, clone the bill intoProcessedAuditData, and returnTrue— no data is saved. 5. Validate bill status. UnlessForceCompleteReAuditis set, requireBill.CurrentBillStatusto beEntered,Reviewed,Audited, orApproved(BR-002). - On any other status: setAuditResult = InvalidStatus, message"Incorrect status for audit: <status>", clone the bill, and returnTrue. 6. Generate/enrich audit data (GenerateAuditData). 1. If the bill is already audited and this is not a forced re-audit, clear the prior audit's messages so stale messages are not shown alongside the new pass. 2. Decide whether the previous audit is eligible for deletion and regeneration: - Not eligible (already audited, status is notEntered, and not forced): keep the existing audit data unchanged. - Eligible (forced re-audit on an already-audited bill, statusEntered, or no prior audit): unless read-only, delete the prior audit (DeletePreviousAudit— deletesBill.Auditand saves the deletion viaSaveChildObject; returnsFalseand aborts on error) and callBill.GenerateAudit()to build a fresh audit header and audited-service rows.
> **Origin**: `GenerateAudit`/`OnGenerateAudit`/`OnGenerateAuditService` are implemented on `clsBillAudit`, which overrides base behavior from `clsBill`/`clsAudit` in [Audit and audited services](../../saif-business-claim-bill/operations/audit-and-audited-services.md) (`SAIF.Business.Claim.Bill`). The override sets `Audit.AuditReceiptDate`, `Audit.PrintEobFlag` (based on a hard-coded non-EOB provider tax-ID list), and applies interpreter-service (`D0004`/`D0006`) minimum-quantity and 15-minute-rounding normalization to each newly generated audited service.
- On `GenerateAudit()` failure: return `False` (propagates to step 7 below).
-
If data generation succeeded so far, enrich the bill:
-
If
Bill.ClaimIDis populated, retrieve claim context viaGetClaim(ClaimID).- Sub-step:
GetClaimcallsclsClaimData.GetData(DPI, ClaimID); if the returned claimIsReplaced, it re-retrieves usingClaim.ReplacedClaimIDand adds an informational message"Claim <old> has been merged to <new>.".
Origin: Claim retrieval and the merge/replacement chain are implemented by Retrieve claim summary data in
SAIF.Business.View.Claim. - IfBill.ProviderLocationIDis populated, retrieve billing-provider context viaGetProvider(ProviderLocationID), caching results per provider ID for the run.Origin: Provider location retrieval is implemented by Retrieve provider location in
SAIF.BOF.Business.View.Provider. - For each billed service, build aclsBillAuditServicewrapper (GetAuditServicelocates or generates the matching audited-service row) and enrich it: - Resolve servicing/prescribing provider location viaGetProviderwhen a non-zero location ID is present. - For pharmacy-type services, resolve the national drug record viaGetNationalDrugCode, trying the existingNationalDrugCodefirst and falling back toServiceCodefor new records.Origin: National drug code retrieval and price history are implemented by National drug validation in
SAIF.Business.Claim.Bill. - ForInpatientHospitalbills, force each service'sServiceDate/ServiceEndDateto the bill's statement from/thru dates when those are populated. - For non-InpatientHospitalbills with at least one service, recompute the bill'sStatementFromDate/StatementThruDateas the min/max of the audited services' dates. 4. ReturnTrueif enrichment completed;Falsepropagates to the caller as a generation failure. 7. Handle generation failure. If step 6 returnsFalse: setAuditResult = FailAuditGeneration, message"Failed to generate audit data.", clone the bill, and returnFalse(this is one of the three cases whereAuditBillreports failure to the caller). 8. Guard against an empty bill. IfBill.Services.Count = 0, callBillAuditor.GenerateError(Bill, "Bill data - no services to audit")(an informational audit message, not a fatal error at this point). 9. Run bill edit validation. Delegate to Bill edit validation viaBillEditor.EditBill(Bill)(BR-003). - On failure: setAuditResult = FailBillEdits, message fromBillEditor.ErrorMessages, and remove any audited-service rows that were newly generated for this run (IsNew) so they are not persisted with an invalid bill. 10. Run service edit and adjustment per service, when bill edits pass. For each item inBill.ServiceData, delegate to Service edit and adjustment: - CallServiceEditor.EditService(Bill, Service); aFalseresult marksPassServiceEdits = Falsefor this run. - If the serviceIsAuditable(ReauditCode = "R"): remove any prior adjustments for that service, then callServiceEditor.AdjustService(Bill, Service); aFalseresult also marksPassServiceEdits = False. - Propagate broken rules fromServiceEditorto the engine after each service. - After all services: sumBill.Audit.TotalAllowedAmountandTotalMcoAdminFeeAmountacrossBill.ServiceData(BR-004). 11. Determine whether there is anything to audit. IfBill.ServiceData.ForReAudit.Count > 0: - If service edits failed: setAuditResult = FailServiceEdits. - Otherwise: - UnlessBill.BillType = WorkerReimbursement, delegate to MCO determination viaMCOManager.DetermineMCO(Bill). - Delegate to Bill audit rule execution viaBillAuditor.AuditBill(Bill). - On failure: setAuditResult = FailAuditCheck, message fromBillAuditor.ErrorMessages. 12. Propagate remaining broken rules. Copy broken rules fromBillEditorandBillAuditoronto the engine's rule collection. 13. Save the result (SaveAuditData). - IfReadOnlyMode: callBill.GenerateStatusRecord()only (builds an in-memoryAUDstatus row and routing target) and log"Process Only Mode - Data Not Saved!"— no database write occurs. - Otherwise, ifBill.IsDirtyorAuditedServiceCount = 0, callBill.Save(). - Sub-step:
Origin:
Save()/OnSave()/OnSaveComplete()/BeforeUpdate()cascade the bill header (CBP_BILLvia an inline update binding:BillTotalAmount,:ClaimID,:ClaimNumber,:ClaimCheckValue,:StatementFromDate,:StatementThruDate,:DRGCode,:UpdateUser,:UpdateDate,:BillID* against CBP_BILL) together withAudit,StatusHistory, andDiagnosischild collections, implemented by Bill CRUD and Audit and audited services inSAIF.Business.Claim.Bill. Before saving,OnSave()resets any non-blankReauditCodeback to a single blank so re-audited services are not repeatedly re-flagged on the next run. - On save failure: setAuditResult = FailAuditSave, message fromBill.ErrorMessages(one of the three cases whereAuditBillreports failure to the caller). 14. Clone and return. Store the final bill state inProcessedAuditData(CloneBill). The whole method body is wrapped in aTry/Catch; any unhandled exception setsAuditResult = EnvironmentError, captures the exception inAuditExceptions, and is followed by theFinallyblock stopping the audit-trail group. The method returnsFalseonly forEnvironmentError,FailAuditGeneration, orFailAuditSave; all other outcomes (includingInvalidBill,InvalidStatus,FailBillEdits,FailServiceEdits, andFailAuditCheck) returnTrue.* -
Process flow¶
flowchart TD
Start([AuditBill invoked]) --> ResetState[Reset trail and rule state]
ResetState --> Retrieve[RetrieveBill: clsBillAudit.GetBillAudit]
Retrieve --> Valid{Bill.IsValid?}
Valid -->|No| InvalidBill([AuditResult = InvalidBill;<br>return True, no save])
Valid -->|Yes| StatusCheck{Status eligible<br>or ForceCompleteReAudit?}
StatusCheck -->|No| InvalidStatus([AuditResult = InvalidStatus;<br>return True])
StatusCheck -->|Yes| Generate[GenerateAuditData:<br>delete/regenerate audit,<br>enrich claim/provider/NDC]
Generate -->|Failed| FailGen([AuditResult = FailAuditGeneration;<br>return False])
Generate -->|Ok| BillEdits[Bill edit validation]
BillEdits -->|Fatal error| FailBillEdits([AuditResult = FailBillEdits;<br>discard new audit rows])
BillEdits -->|Ok| ServiceLoop[Service edit and adjustment<br>per service line]
ServiceLoop --> ForAudit{ForReAudit.Count > 0?}
ForAudit -->|No| Save
ForAudit -->|Yes, failed| FailServiceEdits([AuditResult = FailServiceEdits])
ForAudit -->|Yes, passed| MCO{BillType =<br>WorkerReimbursement?}
MCO -->|No| MCODetermine[MCO determination]
MCO -->|Yes| AuditRun
MCODetermine --> AuditRun[Bill audit rule execution]
AuditRun -->|Failed| FailAuditCheck([AuditResult = FailAuditCheck])
AuditRun -->|Ok| Save[SaveAuditData]
FailServiceEdits --> Save
FailAuditCheck --> Save
FailBillEdits --> Save
Save -->|ReadOnly| StatusOnly([GenerateStatusRecord only;<br>no DB write])
Save -->|Failed| FailSave([AuditResult = FailAuditSave;<br>return False])
Save -->|Ok| Clone[Clone bill into ProcessedAuditData]
StatusOnly --> Clone
Clone --> Done([Return True/False<br>per AuditResult])
Failure and edge-case behavior¶
- An invalid bill or ineligible status is not treated as a hard failure:
AuditBillreturnsTrue, and callers must checkAuditResultto detect these conditions. - Any exception anywhere in the pipeline is caught, recorded in
AuditExceptions, and reported asEnvironmentError; the audit-trail group is always stopped viaFinallyregardless of outcome. - If
GenerateAuditDatafails partway through deleting the previous audit, the method returnsFalseimmediately without attempting to regenerate — a prior audit may already be deleted with no replacement saved.* ReadOnlyModenever callsBill.Save(); only an in-memoryAUDstatus record is produced, so noReadOnlyModerun can persist changes even if later logic assumes a save occurred.- Only
EnvironmentError,FailAuditGeneration, andFailAuditSavecauseAuditBillto returnFalse; all business-rule failures returnTrue, which can be surprising for callers that treat the boolean return as a simple success/failure flag.
Related documentation¶
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\BillAuditEngine.vbBusiness\SAIF.Business.Claim.Bill.AutomatedAudit\clsBillAudit.vbBusiness\SAIF.Business.Claim.Bill.AutomatedAudit\clsBillAuditService.vbBusiness\SAIF.Business.Claim.Bill.AutomatedAudit\AuditUtilities\AuditTrailManager.vb