Bill CRUD¶
Bill CRUD is the central maintenance operation for a medical bill. It allocates a bill identifier from the legacy CBP sequence generator, loads or persists the CBP_BILL header row, generates an audit whenever a bill is saved, and cascades the save into four dependent child collections — diagnosis, procedure, service, and status — each of which owns its own table and its own optimistic-concurrency check. The header itself carries only one explicit business rule (a non-zero bill ID); the remaining validation surface is the shared concurrency contract enforced identically across the header and every child collection.
| Field | Value |
|---|---|
| Operation ID | OP-CB-01 |
| Trigger | Caller calls clsBill.NewBill(inDataportal) to create a bill or clsBill.GetBill(inDataportal, inBillID) to load one, edits properties and child collections, and calls Save() |
| Inputs | BillID (retrieve/create key); mutable header fields (bill type, entry source, status, dates, amounts); child collection edits (Diagnosis, Procedures, Services, StatusHistory); IDataPortalInfo |
| Outputs | Populated clsBill instance on retrieve; persisted header row plus cascaded child-collection rows and a generated clsAudit on save; broken rules and DB-contention errors on failure |
| Primary components | clsBill.NewBill, clsBill.GetBill, clsBill.Create, clsBill.BeforeInsert/AfterInsert, clsBill.BeforeUpdate/AfterUpdate, clsBill.BeforeDelete/AfterDelete, clsBill.OnSave, clsBill.OnSaveComplete, clsBill.GenerateAudit, clsBill.AddBusinessRules |
| Entry point | Business\SAIF.Business.Claim.Bill\Core\clsBill.vb |
Steps¶
- Create a new bill.
NewBill(inDataportal)callsDataAccessLayer.CreateObject(New BillCriteria(inDataportal, 0)), which dispatches toCreate().Create()allocates a newBillIDthroughGetLegacyID(MyCriteria.DataPortalInfo), which resolves the legacy CBP sequence. - Sub-step:
GetLegacyID(clsLegacyIDBase) callsGetNextCBPSeqNum(inDPI, LegacyIDType)forCBPIDType.Bill, which invokes the Oracle stored procedure and returns the next sequence value forCBP_BILL, or throws"Failed to retrieve new ID from Legacy System"if no value comes back (SQL-012).
Origin: The sequence allocation is implemented by
mdlCBPSupportingData.GetNextCBPSeqNum, which calls the Oracle proceduresp82385_get_next_bill_seqnum. See Stored procedure operations and PR-001. 2. Retrieve an existing bill.GetBill(inDataportal, inBillID)callsDataAccessLayer.RetrieveObject(New BillCriteria(...)).BeforeRetrieveassigns the bill-select SQL againstCBP_BILLouter-joined toCBP_AUDIT(SQL-001 family; retrieve path).AfterRetrievefills the object from the first row if present, then callsMarkOld(). 3. Enforce the header validation rule.AddBusinessRules()registersCommonBusinessRules.NonZeroDecimalRequiredagainstBillID, producing"Invalid Bill ID"when the bill ID is zero or unset (BR-002). 4. Caller edits header and child collections. Property setters onclsBillmark the header dirty;Diagnosis,Procedures,Services, andStatusHistoryare independently editable collections that track their own dirty state. 5. Insert a new header.BeforeInsert/AfterInsertbuild a non-query insert intoCBP_BILLwith all header columns (SQL-001, CBP_BILL). - On a rows-affected value other than1: add"Insert Failed"(BR-004). - On success:MarkOld(). 6. Update the header.BeforeUpdatefirst checksMyBase.IsDirty; if the header has no changes, the update is skipped entirely and only dirty child collections are saved. When dirty, it issues a non-query update againstCBP_BILLkeyed byBILL_IDandUPDATE_NUM(SQL-030, CBP_BILL) (BR-002). - On a rows-affected value other than1: add a DB-contention error (BR-004). 7. Delete the header.BeforeDelete/AfterDeletedelete theCBP_BILLrow byBILL_ID(SQL-040, CBP_BILL). - On a rows-affected value other than1: add a DB-contention error (BR-004). 8. Stamp audit fields (OnSave). Before each insert/update/delete,OnSave()setsUpdateDate = NowandUpdateUserfrom the data-portal user. - If the bill is marked deleted: the audit for the bill is deleted and the child collections are cleared instead of saved. 9. Persist the diagnosis collection. WhenDiagnosisis dirty,clsBillDiagnosisCollection.Save()callsApplyEdit()thenMyBase.Save(), cascading to eachclsBillDiagnosisitem. - Create:NewDiagnosis(inDataportal, inBillID)setsBillIDfrom criteria; insert writesBillID,DiagnosisCode,DiagnosisTypeCode,DiagnosisPointerCode,UpdateUser,UpdateDateintoCBP_BILL_DIAGNOSIS(SQL-007, CBP_BILL_DIAGNOSIS). - Update: keyed byBILL_ID + DIAGNOSIS_CODE + UPDATE_NUM, incrementingUPDATE_NUM(SQL-036, CBP_BILL_DIAGNOSIS). - Delete: keyed byBILL_ID + DIAGNOSIS_CODE(SQL-046, CBP_BILL_DIAGNOSIS). - Every insert/update/delete enforces the shared concurrency rule (BR-004); no object-level business rules are registered onclsBillDiagnosis. 10. Persist the procedure collection.clsBillProcedureCollection.NewProcedure(...)computes the nextBillProcedureIDasMax(existing) + 1(or1if none exist), then delegates toclsBillProcedure.NewProcedure(inDataportal, inBillID, inBillProcedureID). - Create: insert writes procedure fields intoCBP_BILL_PROCEDURE(SQL-008, CBP_BILL_PROCEDURE);ServiceandProcedureproperty lookups resolve reference data through the service manager and procedure-code list. - Update: keyed byBILL_ID + BILL_PROCEDURE_ID + UPDATE_NUM(SQL-037, CBP_BILL_PROCEDURE). - Delete: byBILL_ID + BILL_PROCEDURE_ID(SQL-047, CBP_BILL_PROCEDURE). - Concurrency failures follow BR-004; no object-level rules registered.
> **Origin**: The `Service` lookup used during procedure edits is implemented by [Service reference lookups](service-reference-lookups.md) (`clsServiceManager.GetService`) in this same library.
-
Persist the service collection.
clsBillServiceCollection.NewService()computes the nextServiceIDasMax(existing) + 1(or1), then delegates toclsBillService.NewService(...), which setsBillID,ServiceID,UpdateUser, and defaultsCompoundDrugFlag = "N".- Create: insert writes service fields into
CBP_BILL_SERVICE(SQL-009, CBP_BILL_SERVICE);ServiceDescription/SecondaryServiceDescriptionresolve through the linked service's description list at the service date. - Update: keyed by
BILL_ID + SERVICE_ID + UPDATE_NUM(SQL-038, CBP_BILL_SERVICE). - Delete: by
BILL_ID + SERVICE_ID(SQL-048, CBP_BILL_SERVICE). - Concurrency failures follow BR-004; no object-level rules registered.
Origin: The service-description lookup is implemented by Service reference lookups (
clsServiceInfo.ServiceDescriptions) in this same library. 12. Persist the status-history collection.clsBillStatusCollection.NewStatus(...)blocks directAdd()calls with"Status can only be added via a Bill", so new status rows can only be created through the collection's own factory methods; each new item resolves its own identifier throughCriteria.IsLegacy— eitherGetLegacyID(...)for legacy-style IDs orGetNextSeqNum("BILL_STATUS_SEQ")otherwise. - Create: insert writes status fields intoCBP_BILL_STATUS(SQL-010, CBP_BILL_STATUS);StatusCoderesolves throughGetBillStatusCodeMap(...). - Update: keyed byBILL_ID + STATUS ID + UPDATE_NUM(SQL-039, CBP_BILL_STATUS). - Delete: by bill and status identifier (SQL-049, CBP_BILL_STATUS). - Concurrency failures follow BR-004; no object-level rules registered (AddBusinessRulesis empty). 13. Generate the audit on save (GenerateAudit/OnGenerateAudit). After the header and child collections are saved,GenerateAudit()createsclsAudit.NewAudit(...)for the bill, setsAuditReceiptDateandChargeableCostFlag, and then, for eachBillServicein the saved collection, callsAudit.AuditedServices.NewAuditedService(BillService.ServiceID)and copies service, provider, date, amount, diagnosis, and prescriber fields onto the new audited-service line, applying conditional null checks per field. - If a duplicateServiceIDalready exists on the audit: the audited-service factory rejects it (BR-005).Origin: The generated audit and its audited-service lines are persisted by Audit and audited services (
clsAudit,clsAuditedService), which this step invokes to record the bill's audit trail. 14. Complete the save (OnSaveComplete). After the header insert/update/delete succeeds,OnSaveComplete()savesProcedures,Diagnosis,Services, andStatusHistory(each only if dirty) and persists the generatedAudit. If the bill itself was deleted, the audit is deleted and the child collections are cleared rather than saved. - Create: insert writes service fields into
Process flow¶
The following diagram visualizes the bill save cascade from header validation through child-collection persistence and audit generation.
flowchart TD
Start([Caller edits Bill header<br>and child collections]) --> Rule{BillID non-zero?}
Rule -->|No| Fail([Broken rule;<br>save refused])
Rule -->|Yes| HeaderDirty{Header dirty?}
HeaderDirty -->|Yes| HeaderSave[Insert/Update/Delete<br>CBP_BILL]
HeaderDirty -->|No| SkipHeader[Skip header write]
HeaderSave --> Rows{RowsAffected = 1?}
Rows -->|No| Contention([Contention error])
Rows -->|Yes| Children
SkipHeader --> Children[Save dirty child collections:<br>Diagnosis, Procedure, Service, Status]
Children --> Audit[GenerateAudit:<br>create Audit + Audited Service lines]
Audit --> Dup{Duplicate ServiceID<br>on audit?}
Dup -->|Yes| DupErr([Duplicate-service exception])
Dup -->|No| Complete[OnSaveComplete:<br>persist Audit and children]
Complete --> Done([Save returns success])
Failure and edge-case behavior¶
- A header that is not dirty skips its own update entirely; only dirty child collections are saved, so a caller that only edits a child collection never triggers a header-row rewrite.
- Every insert/update/delete across the header and all four child collections shares the same rows-affected contract; a
0or unexpected row count is reported as"Insert Failed"or a DB-contention error rather than a distinguishing message per table (BR-004). - If the bill is deleted, its audit is deleted and its child collections are cleared instead of saved — a deleted bill never triggers a child-collection insert/update.
clsBillStatusCollection.Add()is deliberately blocked outside the collection's own factory methods, so any caller attempting to add a status object directly receives"Status can only be added via a Bill"rather than a silently accepted item.*GetLegacyIDthrows immediately if the Oracle sequence procedure returns no value, so a new bill or new legacy-style status row cannot be created at all without a valid sequence result.
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\Core\clsBill.vbBusiness\SAIF.Business.Claim.Bill\Core\clsBillCollection.vbBusiness\SAIF.Business.Claim.Bill\Core\clsBillDiagnosis.vbBusiness\SAIF.Business.Claim.Bill\Core\clsBillDiagnosisCollection.vbBusiness\SAIF.Business.Claim.Bill\Core\clsBillProcedure.vbBusiness\SAIF.Business.Claim.Bill\Core\clsBillProcedureCollection.vbBusiness\SAIF.Business.Claim.Bill\Core\clsBillService.vbBusiness\SAIF.Business.Claim.Bill\Core\clsBillServiceCollection.vbBusiness\SAIF.Business.Claim.Bill\Core\clsBillStatus.vbBusiness\SAIF.Business.Claim.Bill\Core\clsBillStatusCollection.vbBusiness\SAIF.Business.Claim.Bill\Core\clsLegacyIDBase.vbBusiness\SAIF.Business.Claim.Bill\Core\clsServiceBase.vbBusiness\SAIF.Business.Claim.Bill\AuditSupport\mdlCBPSupportingData.vb