Skip to content

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

  1. Dispatch the retrieve. GetClaimDetail(inDataportal, inClaimID) builds a Criteria(inDataportal, inClaimID) and calls DataAccessLayer.RetrieveObject, which the BOF data portal routes to BeforeRetrieve and AfterRetrieve.
  2. Build the primary select. BeforeRetrieve assigns DataRequest.SQLStatement = MyCriteria.RetrieveSQL, names the request ClaimDetailRetrieve, and binds the ClaimID parameter (SQL-040, CL_INJURY_CLAIM, joined to CL_FORM_CODE for the form-code description).
  3. Read the row and hydrate the object. AfterRetrieve reads the first row and calls Fill(Reader).
  4. If the reader returns no row: Fill is skipped, only MarkOld() runs, and the caller receives an object with default/empty field values rather than an explicit not-found error.
  5. 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).
  6. Resolve the responsible-adjuster display name. If ClaimRespCode is non-blank, GetAdjusterName(ClaimRespCode) delegates to clsClaimResponsibilityData.GetData(...); when the returned data is valid, RespAdjName prefers the adjuster's preferred name over the first name and otherwise falls back to the description. A blank ClaimRespCode clears RespAdjName.
  7. 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 the SAIF.Business.Claim documentation set. 6. Resolve the compensability-adjuster display name. The same lookup pattern runs against CompensabilityRespCode and populates CompAdjName. 7. Look up the merged claim for voided claims. If ClaimStatusCode is V (voided), GetMergedClaim() issues a scalar query joining the merged-claim table to the claim table to populate MergedClaimNum (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) and GetConditionsCount() counts all conditions for the claim (SQL-034, CL_MEDICAL_CONDITION). WCDClaimCount and OWCPClaimCount are initialized to -1 as 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 through ValueModified. 10. Lazy-load related collections on first access. ClaimMcoEligibility, ClaimTypeActivity, ClaimStatusActivity, ClaimDisabilityActivity, ClaimRequestDecision, DisabilityAuthActivity, Covid19TestingHistoryCollection, and WorkerPharmacyEligibilityReport each retrieve their own child object the first time they are read. McoPlanID is derived by GetMCOPlanID (SQL-036, MCO_GSA_LOCATION) and cached until McoID is reassigned. 11. Caller edits properties. Every mutable setter compares the old and new value and calls MarkDirty() only on a real change. Setting JurisdictionCode, ClaimStatusCode, ClaimTypeCode, DisabilityDate, or KnowledgeDate also latches a "modified" tracking flag consumed by IsValid. Setting WCDClaimNum stamps WCDClaimNumUpdateUser/WCDClaimNumUpdateDate; setting McoID clears the cached McoPlanID. 12. Run IsValid() before every save. - Backfill ClaimTypeCode, ClaimStatusCode, and DisabilityDate from the latest child-activity rows when the corresponding save sentinel is still empty, so values driven by child-collection saves earlier in the same Save() 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 to I with today's date (BR-CM-029). - If the status changed to Denied, set MedicareORMTermDate from the claim-status-activity effective date; if it changed to Open, clear it (BR-CM-030). - Call MyBase.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-only JurisdictionModified, ClaimStatusCodeModified, ClaimTypeCodeModified, ClaimTypeChangedToDisabling, DisabilityDateModified, and KnowledgeDateModified flags. - Return ErrorCount = 0; the boolean computed by MyBase.IsValid is 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 during IsValid(): - 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()). - If ClaimMcoEligibility is dirty: copy McoID, McoEligibilityReasonCode, and McoEligibilityStatusCode onto the header, save the child collection, and derive the pharmacy formulary code from the MCO plan and enrollment status (BR-CM-034). - If ClaimStatusActivity is dirty: save the collection and re-derive ClaimStatusCode from GetMaxClaimStatusCode into the save sentinel consumed by step 12. - If ClaimTypeActivity is dirty: save the collection and re-derive ClaimTypeCode from GetMaxClaimTypeCode. - If ClaimRequestDecision is dirty: save the collection and re-derive ClaimRequestTypeCode from GetMaxClaimRequestTypeCode. - If ClaimDisabilityActivity is dirty: save the collection and re-derive DisabilityDate from GetMaxDisabilityDate. - If DisabilityAuthActivity is 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. - If JurisdictionCode was modified, run the jurisdiction cascade (step 15) (BR-CM-038). - If ActiveClaimFlag changed from Y to N, run the legal-representation termination (step 16) (BR-CM-039). - If this is the first decision transitioning to Accept or Denied, copy ClaimRespCode into CompensabilityRespCode (BR-CM-037). - If a claim-policy-history child object exists, save it. - If Covid19TestResultCode or TestedForCovid19Flag changed, create a new testing-history row, copy the current values onto it, add it to the collection, and save the collection (append-only history). - Call AcceptChanges(), then MyBase.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) zeroes InmateNum; entering Inmate forces PolicyNum = 8 with a blank sub-code and creates a new claim-policy-history row; leaving State (1) clears WCDClaimNum; leaving Federal clears OwcpClaimNum and resets OwcpServiceOfficeCode to 0 (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 sets EXP_DATE = TRUNC(SYSDATE) and the end-reason code to 1 for 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 to MyCriteria.UpdateSQL as a non-query named ClaimDetailUpdate, m_UpdateUser is stamped from the data-portal user, and roughly twenty-eight parameters are bound, including the ClaimID/UpdateNum pair used in the WHERE clause 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), and GetMCOPlanID (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: AfterUpdate rejects the save with a DB contention error whenever the row count is not exactly 1, covering both a stale UpdateNum and a concurrently deleted row (BR-CM-001).
  • Silent empty retrieve: when the primary select returns no rows, AfterRetrieve skips Fill and returns an object with default values; a non-null clsClaimDetail does 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_InjuryDate is 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: ClaimStatusCodeValid and PharmacyEligibilityStatusCodeValid share the source rule ID clsClaimDetail08, and DisabilityTreatmentDateValid and PharmacyEligibilityCaremarkCodeValid share clsClaimDetail09 — 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 by Fill and cannot be corrected through this object even though validation rules compare against them.
  • Cached MCO plan: McoPlanID is cached after first computation and invalidated only when McoID is re-set, so an external change to the plan mapping mid-session leaves a stale value.

Sources

  • Business\SAIF.Business.Claim.Maintenance\clsClaimDetail.vb