Claim detail CRUD¶
Claim detail CRUD is the central maintenance operation for a claim header. It loads the CL_INJURY_CLAIM row for a claim, enriches it with denormalized adjuster names and condition counts, exposes every mutable header field for editing, runs roughly twenty field-level validation rules, cascades saves into the dependent child collections (MCO eligibility, status/type/disability activity, request decisions, pharmacy eligibility, policy history, COVID-19 testing history), derives dependent fields, and finally updates the header under optimistic concurrency. The business object supports retrieve and update only — creation and deletion of claim rows happen elsewhere.
| Field | Value |
|---|---|
| Operation ID | OP-CM-01 |
| Trigger | Caller invokes clsClaimDetail.GetClaimDetail(dpi, claimID) to load a claim, edits properties, and calls Save(); also loaded as a nested object by other claim-maintenance business objects |
| Inputs | ClaimID (retrieve key via Criteria); mutable header fields (ActiveClaimFlag, ExternalViewFlag, ClaimStatusCode, ClaimTypeCode, CoverageCode, payroll class/sub codes, InjOccupationalDiseaseCode, InmateNum, JurisdictionCode, MCO fields, OwcpClaimNum, OwcpServiceOfficeCode, Federal8FFlag, policy fields, ClaimRequestTypeCode, ClaimRespCode, CompensabilityRespCode, DisabilityDate, KnowledgeDate, WCDClaimNum, MedicareORMTermDate, TestedForCovid19Flag, Covid19TestResultCode); UpdateNum; IDataPortalInfo |
| Outputs | Populated clsClaimDetail instance on retrieve; boolean success plus mutated child objects and refreshed UpdateNum/UpdateDate/UpdateUser on save; validation error and warning messages |
| Primary components | clsClaimDetail.GetClaimDetail, clsClaimDetail.BeforeRetrieve, clsClaimDetail.AfterRetrieve, clsClaimDetail.Fill, clsClaimDetail.IsValid, clsClaimDetail.AddBusinessRules, clsClaimDetail.Save, clsClaimDetail.BeforeUpdate, clsClaimDetail.AfterUpdate, clsClaimDetail.ProcessJurisdictionCodeChanges, clsClaimDetail.ProcessLegalRep |
| Entry point | Business\SAIF.Business.Claim.Maintenance\clsClaimDetail.vb |
Steps¶
- Dispatch the retrieve.
GetClaimDetail(inDataportal, inClaimID)builds aCriteria(inDataportal, inClaimID)and callsDataAccessLayer.RetrieveObject, which the BOF data portal routes toBeforeRetrieveandAfterRetrieve. - Build the primary select.
BeforeRetrieveassignsDataRequest.SQLStatement = MyCriteria.RetrieveSQL, names the requestClaimDetailRetrieve, and binds theClaimIDparameter (SQL-040, CL_INJURY_CLAIM, joined toCL_FORM_CODEfor the form-code description). - Read the row and hydrate the object.
AfterRetrievereads the first row and callsFill(Reader). - If the reader returns no row:
Fillis skipped, onlyMarkOld()runs, and the caller receives an object with default/empty field values rather than an explicit not-found error. - Map the reader onto member fields (
Fill). Roughly thirty-five columns are mapped, including claim status/type, coverage, payroll class codes, form code and description, MCO fields, OWCP fields, policy fields, responsibility codes,InjuryDate,DisabilityDate,KnowledgeDate,MedicareORMTermDate, claim score, and the COVID-19 test fields (SQL-040 result set; no further database access). - Resolve the responsible-adjuster display name. If
ClaimRespCodeis non-blank,GetAdjusterName(ClaimRespCode)delegates toclsClaimResponsibilityData.GetData(...); when the returned data is valid,RespAdjNameprefers the adjuster's preferred name over the first name and otherwise falls back to the description. A blankClaimRespCodeclearsRespAdjName. - Sub-step: the responsibility-data object performs its own retrieve of the claim-responsibility reference row and exposes the adjuster name fields consumed here.
Origin: The claim-responsibility lookup is implemented in
clsClaimResponsibilityData(SAIF.Business.Claim) and executed on behalf of this operation. That library is documented in the previous format, so no per-operation page exists; see Operations and processes in theSAIF.Business.Claimdocumentation set. 6. Resolve the compensability-adjuster display name. The same lookup pattern runs againstCompensabilityRespCodeand populatesCompAdjName. 7. Look up the merged claim for voided claims. IfClaimStatusCodeisV(voided),GetMergedClaim()issues a scalar query joining the merged-claim table to the claim table to populateMergedClaimNum(SQL-037, CL_MERGED_CLAIM, CL_INJURY_CLAIM). 8. Compute condition counts used later by validation.GetAcceptCondMedStationaryCount()counts accepted conditions with no medically-stationary date (SQL-032, CL_MEDICAL_CONDITION, CL_COND_COMPENSABILITY_DECISN) andGetConditionsCount()counts all conditions for the claim (SQL-034, CL_MEDICAL_CONDITION).WCDClaimCountandOWCPClaimCountare initialized to-1as a not-yet-checked sentinel. These counts feed BR-CM-020 and BR-CM-027. 9. Finalize the retrieve.MarkOld()clears the object's modified flags so later edits are detectable throughValueModified. 10. Lazy-load related collections on first access.ClaimMcoEligibility,ClaimTypeActivity,ClaimStatusActivity,ClaimDisabilityActivity,ClaimRequestDecision,DisabilityAuthActivity,Covid19TestingHistoryCollection, andWorkerPharmacyEligibilityReporteach retrieve their own child object the first time they are read.McoPlanIDis derived byGetMCOPlanID(SQL-036, MCO_GSA_LOCATION) and cached untilMcoIDis reassigned. 11. Caller edits properties. Every mutable setter compares the old and new value and callsMarkDirty()only on a real change. SettingJurisdictionCode,ClaimStatusCode,ClaimTypeCode,DisabilityDate, orKnowledgeDatealso latches a "modified" tracking flag consumed byIsValid. SettingWCDClaimNumstampsWCDClaimNumUpdateUser/WCDClaimNumUpdateDate; settingMcoIDclears the cachedMcoPlanID. 12. RunIsValid()before every save. - BackfillClaimTypeCode,ClaimStatusCode, andDisabilityDatefrom the latest child-activity rows when the corresponding save sentinel is still empty, so values driven by child-collection saves earlier in the sameSave()call are picked up. - If the status changed to Closed or Denied and the pharmacy-eligibility record is not already Ineligible, force the pharmacy status toIwith today's date (BR-CM-029). - If the status changed to Denied, setMedicareORMTermDatefrom the claim-status-activity effective date; if it changed to Open, clear it (BR-CM-030). - CallMyBase.IsValid, then re-register child rule propagation (ValidateChildObject) for the MCO eligibility, status/type activity, request decision, disability activity, disability authorization, and pharmacy eligibility children, because the base call clears child rules. - Refresh the read-onlyJurisdictionModified,ClaimStatusCodeModified,ClaimTypeCodeModified,ClaimTypeChangedToDisabling,DisabilityDateModified, andKnowledgeDateModifiedflags. - ReturnErrorCount = 0; the boolean computed byMyBase.IsValidis discarded, so the effective gate is the error count. 13. Evaluate the registered field-level rules.AddBusinessRules()wires roughly twenty rules that run whenever their bound property is set and again duringIsValid(): - Code and flag domains: BR-CM-009, BR-CM-010, BR-CM-013, BR-CM-014, BR-CM-016. - Date constraints: BR-CM-011, BR-CM-012, BR-CM-017, BR-CM-022. - Uniqueness checks that issue live scalar queries: BR-CM-015 (SQL-041, CL_INJURY_CLAIM) and BR-CM-019 (SQL-038, CL_INJURY_CLAIM). - Status and decision consistency: BR-CM-018, BR-CM-020, BR-CM-021. - Warnings that do not block the save: BR-CM-023, BR-CM-024, BR-CM-025, BR-CM-026, BR-CM-027, BR-CM-028, BR-CM-031. - Pharmacy eligibility consistency: BR-CM-032 and BR-CM-033. - If any error-severity rule breaks: the error count is non-zero and the save is refused; warnings are surfaced but allow the save to continue. - Dead code:DisabAuthActivityDate_InjuryDate(clsClaimDetail106) carries rule metadata but is never registered, so it never executes. 14. Orchestrate the save (Save()). - IfClaimMcoEligibilityis dirty: copyMcoID,McoEligibilityReasonCode, andMcoEligibilityStatusCodeonto the header, save the child collection, and derive the pharmacy formulary code from the MCO plan and enrollment status (BR-CM-034). - IfClaimStatusActivityis dirty: save the collection and re-deriveClaimStatusCodefromGetMaxClaimStatusCodeinto the save sentinel consumed by step 12. - IfClaimTypeActivityis dirty: save the collection and re-deriveClaimTypeCodefromGetMaxClaimTypeCode. - IfClaimRequestDecisionis dirty: save the collection and re-deriveClaimRequestTypeCodefromGetMaxClaimRequestTypeCode. - IfClaimDisabilityActivityis dirty: save the collection and re-deriveDisabilityDatefromGetMaxDisabilityDate. - IfDisabilityAuthActivityis dirty: save the collection; no header field is re-derived. - If the latest or pending decision is an accept decision and the guard conditions hold: derive the pharmacy eligibility status from the MCO plan (BR-CM-035); otherwise, if the status moved from Closed to Open, apply the reopen-specific pharmacy status (BR-CM-036). - If the pharmacy eligibility record ended up dirty from any branch, save it. - IfJurisdictionCodewas modified, run the jurisdiction cascade (step 15) (BR-CM-038). - IfActiveClaimFlagchanged fromYtoN, run the legal-representation termination (step 16) (BR-CM-039). - If this is the first decision transitioning to Accept or Denied, copyClaimRespCodeintoCompensabilityRespCode(BR-CM-037). - If a claim-policy-history child object exists, save it. - IfCovid19TestResultCodeorTestedForCovid19Flagchanged, create a new testing-history row, copy the current values onto it, add it to the collection, and save the collection (append-only history). - CallAcceptChanges(), thenMyBase.Save()(which runs steps 12, 17, and 18), mark the object old on success, and reset the save sentinels for the next cycle. 15. Cascade jurisdiction-code transitions (ProcessJurisdictionCodeChanges). Using the in-memory jurisdiction map, leaving Inmate (2) zeroesInmateNum; entering Inmate forcesPolicyNum = 8with a blank sub-code and creates a new claim-policy-history row; leaving State (1) clearsWCDClaimNum; leaving Federal clearsOwcpClaimNumand resetsOwcpServiceOfficeCodeto0(BR-CM-038). 16. Auto-terminate open legal representation (ProcessLegalRep). A scalar query retrieves the current open legal-representative effective date (SQL-035, CL_CLAIM_LEGAL_REPRESENTATIVE); if a row exists, an update setsEXP_DATE = TRUNC(SYSDATE)and the end-reason code to1for that claim and effective date (SQL-086, CL_CLAIM_LEGAL_REPRESENTATIVE) (BR-CM-039). - No row count is checked on this secondary update, so a concurrent change silently no-ops. 17. Build the header update (BeforeUpdate). The request is set toMyCriteria.UpdateSQLas a non-query namedClaimDetailUpdate,m_UpdateUseris stamped from the data-portal user, and roughly twenty-eight parameters are bound, including theClaimID/UpdateNumpair used in theWHEREclause for optimistic concurrency (SQL-087, CL_INJURY_CLAIM) (BR-CM-001). 18. Verify the update (AfterUpdate). The non-query result is cast to an integer row count. - If the row count is not exactly 1:AddErrorMessage("UpdateError", DBContentionError)reports that another user updated the row first or that the row no longer exists (BR-CM-001). - Otherwise: any prior update error is cleared. 19. Aggregate dirtiness (IsDirty). The header combines its own dirty flag with those of the MCO eligibility, status/type activity, disability activity, request decision, disability authorization, pharmacy eligibility, and COVID-19 testing history children, so the caller knows whether a save is required. 20. Serve supplementary read-only lookups.GetPMAWorkerNumberCount(SQL-039, CL_CLAIM_PERSON),GetClaimBenefitPeriodCount(SQL-033, CL_BENEFIT_PERIOD, which appends an equality or inequality predicate on the payment-category code), andGetMCOPlanID(SQL-036, MCO_GSA_LOCATION) feed the UI and validation without participating in the retrieve/update path.
No create or delete path exists: the nested Criteria class throws InvalidSQLProperty for CreateSQL, InsertSQL, and DeleteSQL.
Process flow¶
The following diagram visualizes this operation's control flow from retrieve through validated save.
flowchart TD
Start([GetClaimDetail dpi claimID]) --> Sel[BeforeRetrieve builds select<br>and binds ClaimID]
Sel --> Row{Row returned?}
Row -->|No| Empty([Object marked old<br>and returned unfilled])
Row -->|Yes| Fill[Fill maps all columns]
Fill --> Resp{ClaimRespCode set?}
Resp -->|Yes| RespName[Resolve responsible<br>adjuster name]
Resp -->|No| Comp{CompensabilityRespCode set?}
RespName --> Comp
Comp -->|Yes| CompName[Resolve compensability<br>adjuster name]
Comp -->|No| Void{Status = Voided?}
CompName --> Void
Void -->|Yes| Merged[Look up merged claim number]
Void -->|No| Counts[Compute condition counts]
Merged --> Counts
Counts --> Old[MarkOld and return object]
Old --> Edit[Caller edits properties]
Edit --> Save[Save orchestrates child saves<br>and field derivations]
Save --> Juris{Jurisdiction modified?}
Juris -->|Yes| Cascade[Cascade identifier defaults]
Juris -->|No| Active{ActiveClaimFlag Y to N?}
Cascade --> Active
Active -->|Yes| LegalRep[Terminate open legal rep]
Active -->|No| Valid{IsValid: error count = 0?}
LegalRep --> Valid
Valid -->|No| Blocked([Broken rules returned;<br>no update issued])
Valid -->|Yes| Update[BeforeUpdate issues header update]
Update --> Rows{RowsAffected = 1?}
Rows -->|Yes| Ok([Errors cleared; object marked old])
Rows -->|No| Contention([DB contention error added;<br>save reported as failed])
Failure and edge-case behavior¶
- Optimistic concurrency:
AfterUpdaterejects the save with a DB contention error whenever the row count is not exactly 1, covering both a staleUpdateNumand a concurrently deleted row (BR-CM-001). - Silent empty retrieve: when the primary select returns no rows,
AfterRetrieveskipsFilland returns an object with default values; a non-nullclsClaimDetaildoes not imply a valid claim. - Secondary legal-rep update has no contention check: the legal-representative update does not verify its row count, unlike the main claim update, so a concurrent modification silently no-ops.
- Dead and broken rule:
DisabAuthActivityDate_InjuryDateis never registered, and its logic sets the result to false unconditionally whenever a disability-authorization activity date is present, so re-enabling it would always fail validation. - Duplicate internal rule-data IDs:
ClaimStatusCodeValidandPharmacyEligibilityStatusCodeValidshare the source rule IDclsClaimDetail08, andDisabilityTreatmentDateValidandPharmacyEligibilityCaremarkCodeValidshareclsClaimDetail09— a cross-referencing ambiguity rather than a functional defect. - Uniqueness rules issue live queries as a validation side effect: the WCD and OWCP uniqueness checks run a count query on every property set and every
IsValid()pass while the field is non-blank, so repeated edits cause repeated round trips. - Pharmacy-eligibility branch ordering is significant: the MCO-driven, accept-decision, and reopen branches are mutually exclusive and keyed on overlapping conditions; an already-dirty pharmacy record is respected so later branches do not overwrite a pending change, but reordering the conditions would change behavior.
- Read-only supporting fields:
InjuryDate,InitialTreatmentDate, and several other fields are populated only byFilland cannot be corrected through this object even though validation rules compare against them. - Cached MCO plan:
McoPlanIDis cached after first computation and invalidated only whenMcoIDis re-set, so an external change to the plan mapping mid-session leaves a stale value.
Related documentation¶
Sources¶
Business\SAIF.Business.Claim.Maintenance\clsClaimDetail.vb