---
title: Accident injury management
summary: Maintains injury date, injury circumstances, and the injury address on a claim, with a large injury-date cross-validation rule set.
status: draft
owner: SAIF Doc Gen
last_reviewed: 2026-07-30
library: SAIF.Business.Claim.Maintenance
reference_type: package
operation_id: OP-CM-02
trigger: Caller calls clsAccidentInjury.GetAccidentInjury(dpi, claimID) or NewAccidentInjury, edits fields, and calls Save()
---

# Accident injury management

Accident injury management owns the injury-facing columns of the claim row: the injury date and time, exposure and employment-injury codes, injury source, premises and inpatient flags, the free-text SAIF event description, and the injury address. Its distinguishing feature is a large set of cross-validation rules that only fire when the injury date or initial treatment date is actually modified, several of which issue live count queries against sibling claim tables. The object supports retrieve and update only; the claim row itself is never created or deleted here.

| Field | Value |
| --- | --- |
| **Operation ID** | OP-CM-02 |
| **Trigger** | Caller invokes `clsAccidentInjury.GetAccidentInjury(dpi, claimID)` (read) or `NewAccidentInjury` (in-memory instance), edits properties, and calls `Save()` |
| **Inputs** | `ClaimID` (Decimal criteria key); editable fields `ExposureCode`, `EmploymentInjuryCode`, `InitialTreatmentDate`, `InjuredOnPremisesCode`, `InjuryDate`, `InjurySourceCode`, `InjuryTime`, `InPatientFlagCode`, `FederalInjuryLocationCode`, `HardwareCausedFlagCode`, `HighSeasFlag`, `MultipleWorkerFlagCode`, `PersonCausedFlagCode`, `SaifEventDescr`, and the injury address; read-only supporting data (`ClaimTypeCode`, `ClaimStatusCode`, `JurisdictionCode`, `PolicyNum`, `WorkerID`, `BirthDate`, `DeathDate`, `KnowledgeDate`, `Received801Date`, `ReceivedDate`, `LeaveWorkDate`, `DisabilityDate`) |
| **Outputs** | Updated claim row (address, injury, and flag columns plus audit fields); conditional deletion of three-day-wait rows; cascading save or delete of the employee work-address child; federal awards set to pending; broken-rule collection |
| **Primary components** | `clsAccidentInjury.GetAccidentInjury`, `clsAccidentInjury.Fill`, `clsAccidentInjury.IsValid`, `clsAccidentInjury.Save`, `clsAccidentInjury.BeforeUpdate`, `clsAccidentInjury.AfterUpdate`, `clsAccidentInjury.DetermineCountyCode`, `clsAccidentInjury.SetFederalAwardsToPending`, `clsAccidentInjury.CheckForMultipleClaimsForWorker` |
| **Entry point** | `Business\SAIF.Business.Claim.Maintenance\clsAccidentInjury.vb` |

## Steps

1. **Retrieve the accident/injury record.** `GetAccidentInjury(inDataportal, inClaimID)` builds a `Criteria` holding the data-portal info and `ClaimID` and calls `DataAccessLayer.RetrieveObject` ([SQL-026](../sql/SQL-026.md), [CL_INJURY_CLAIM](../database-tables.md#cl_injury_claim), [CL_CLAIM_PERSON](../database-tables.md#cl_claim_person)).
   - `BeforeRetrieve` builds the request from `MyCriteria.RetrieveSQL` and binds `ClaimID`.
   - `AfterRetrieve` reads the first row and calls `Fill`; if no row is read, the object remains unfilled and no explicit not-found error is raised.*
2. **Populate in-memory state (`Fill`).**
   - Map the claim and person columns (claim type and status, jurisdiction, dates, flags, policy number, birth and death dates) into smart-typed fields.
   - Build the injury address through `SetUpInjuryAddress()` with state, zip, and zip+4 validation enabled, normalize zip padding and blank handling, and mark the address clean.
   - Load the child work-address object for the same claim.
     - Sub-step: the work-address object issues its own retrieve keyed by `ClaimID` and hydrates the employer-provided work address used for validation and display.

     > **Origin**: The employee work-address retrieve is implemented by `clsClaimEmployeeWorkAddress` (`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](../../saif-business-claim/operations-processes.md) in the `SAIF.Business.Claim` documentation set.
   - `AfterRetrieve` calls `MarkOld()` so the freshly loaded object is not dirty.
3. **Alternate construction from a shared reader (`Load`).** When a parent business object already holds an open reader, `Load` calls `Fill`, constructs a fresh criteria object, and calls `MarkOld()` without issuing its own query.
4. **Caller edits fields.** Each setter trims and compares the incoming value and calls `MarkDirty()` only on a real change.
   - Setting `InjuryDate` resets the cached cross-check counts to `-1` so the dependent rules re-query.
   - Setting `InjuryTime` appends `.00` to a non-blank value before storing.
5. **Validate the object (`IsValid`).**
   - `MyBase.IsValid` runs the registered rule set (step 6).
   - Compute `m_InjuryDateModified`, `m_InitialTreatmentDateModified`, and `m_ExposureCodeModified` from the corresponding modified flags.
   - Compute `m_InpatientFlagCodeChangedfromNtoY`, true only when the inpatient flag moved from `N` to `Y`.
   - Validate the child work-address object and propagate its rules onto this object; if the child is invalid, the whole object is invalid and no persistence occurs.
6. **Evaluate the injury-date and initial-treatment-date rule set.** All of these fire only when the relevant date's modified flag is set, so pre-existing data is not re-flagged on unrelated saves:
   - Requiredness and plausibility: [BR-CM-040](../business-rules.md#br-cm-040-injury-date-is-required-when-modified), [BR-CM-041](../business-rules.md#br-cm-041-injury-date-more-than-one-year-in-the-past-warns-the-user), [BR-CM-056](../business-rules.md#br-cm-056-initial-treatment-date-must-fall-between-1850-and-today).
   - Duplicate-claim warning: [BR-CM-042](../business-rules.md#br-cm-042-another-claim-within-fourteen-days-for-the-same-worker-warns-the-user), which runs a self-join count over the claim table ([SQL-021](../sql/SQL-021.md), [CL_INJURY_CLAIM](../database-tables.md#cl_injury_claim)) and returns the duplicate claim number for the message.
   - Wage re-verification warning on disease claims: [BR-CM-043](../business-rules.md#br-cm-043-initial-treatment-date-change-on-a-disease-claim-requires-wage-re-verification).
   - In-memory date comparisons against claim and person dates: [BR-CM-044](../business-rules.md#br-cm-044-injury-date-may-not-follow-the-knowledge-date), [BR-CM-045](../business-rules.md#br-cm-045-injury-date-may-not-follow-the-801-received-date), [BR-CM-046](../business-rules.md#br-cm-046-injury-date-may-not-follow-the-stamped-in-received-date), [BR-CM-047](../business-rules.md#br-cm-047-injury-date-may-not-follow-the-leave-work-date), [BR-CM-048](../business-rules.md#br-cm-048-injury-date-may-not-precede-the-worker-birth-date), [BR-CM-049](../business-rules.md#br-cm-049-injury-date-may-not-follow-the-worker-death-date), [BR-CM-054](../business-rules.md#br-cm-054-injury-date-may-not-follow-the-disability-date-on-injury-claims), [BR-CM-055](../business-rules.md#br-cm-055-ppd-or-ptd-occupational-disease-claims-require-a-disability-or-initial-treatment-date).
   - Lazily queried cross-table comparisons, each executed only when its cached count is still `-1`:
     - [BR-CM-050](../business-rules.md#br-cm-050-injury-date-may-not-follow-a-work-release-date) ([SQL-027](../sql/SQL-027.md), [CL_CLAIM_WORK_RELEASE_STATUS](../database-tables.md#cl_claim_work_release_status)).
     - [BR-CM-051](../business-rules.md#br-cm-051-injury-date-may-not-follow-a-decision-request-effective-date) ([SQL-025](../sql/SQL-025.md), [CL_CLAIM_REQUEST_DECISION](../database-tables.md#cl_claim_request_decision)).
     - [BR-CM-052](../business-rules.md#br-cm-052-injury-date-may-not-follow-a-disability-authorization-activity-date) ([SQL-023](../sql/SQL-023.md), [CL_DISABILITY_AUTH_ACTIVITY](../database-tables.md#cl_disability_auth_activity)).
     - [BR-CM-053](../business-rules.md#br-cm-053-injury-date-may-not-follow-a-medically-stationary-date) ([SQL-024](../sql/SQL-024.md), [CL_MEDICAL_CONDITION](../database-tables.md#cl_medical_condition)).
   - Delegated service checks: [BR-CM-057](../business-rules.md#br-cm-057-injury-date-may-not-change-while-unposted-payments-or-adjustments-exist) blocks the change outright when unposted payments or adjustments exist, and [BR-CM-058](../business-rules.md#br-cm-058-missing-policy-coverage-at-injury-date-warns-the-user) warns when no policy coverage exists at the injury date for policy numbers greater than 17.
     - Sub-step: `ClaimServices.HasUnpostedPaymentsOrAdjustments` inspects the claim's payment and adjustment queues for records that have not yet posted and returns a boolean used directly as the rule result.
     - Sub-step: `PolicyServices.CoverageExists` resolves whether the policy had coverage in force on the injury date.

     > **Origin**: Both checks are implemented by the claim and policy service classes in `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](../../saif-business-claim/operations-processes.md) in the `SAIF.Business.Claim` documentation set.
   - If any error-severity rule breaks: the save is blocked and the caller inspects the broken-rule collection; warnings do not block.
7. **Save the object (`Save`).**
   - If the work-address child is not new and reports empty, delete it through its own delete path.
   - Otherwise, if that child is dirty, save it through `SaveChildObject`.
   - If federal award records exist with benefit payments, `SetFederalAwardsToPending()` sets each award status to `P` and clears each benefit payment's status and approver, then saves the award collection if dirty.
   - Call `AcceptChanges()`, then `MyBase.Save()`, and call `MarkOld()` on success.
8. **Build and execute the update (`BeforeUpdate`).**
   - Call `DetermineCountyCode()` first (step 9) so the derived county code is current.
   - Capture the update user from the data-portal info.
   - Strip a single leading zero from the county code when it is longer than one character before binding it.
   - Bind all editable fields, the address city/state/zip/zip+4, and the `ClaimID`/`UpdateNum` concurrency key to the parameterized update ([SQL-081](../sql/SQL-081.md), [CL_INJURY_CLAIM](../database-tables.md#cl_injury_claim)).
   - If the inpatient flag changed from `N` to `Y`: queue a second non-query that deletes the claim's three-day-wait rows, because an inpatient stay invalidates waiting-period tracking ([SQL-106](../sql/SQL-106.md), [CL_CLAIM_TD_THREE_DAY_WAIT](../database-tables.md#cl_claim_td_three_day_wait)).
9. **Derive the county code (`DetermineCountyCode`).**
   - Reset the address county to `0`.
   - If a non-blank state code other than `OR` is present, set the county to the non-Oregon constant `37`.
   - Otherwise, if a zip code is present, look up the county code by zip and use the result when it is not null ([SQL-022](../sql/SQL-022.md), [ZIP_MCO_COUNTY](../database-tables.md#zip_mco_county)).
10. **Verify the update (`AfterUpdate`).**
    - If no row was affected — a stale `UpdateNum` or a removed claim — add `UpdateError` with the DB contention message ([BR-CM-001](../business-rules.md#br-cm-001-optimistic-concurrency-updates-fail-if-rowsaffected-1)).
11. **Load federal awards on demand.** `WorkerFederalAwards` lazily retrieves the worker's active PTD, PPD, and fatal awards only when the injury date was modified and the cache is empty; the result drives the pending-award side effect in step 7.
12. **Aggregate dirtiness (`IsDirty`).** The object combines its own dirty flag with the injury address and the work-address child, so a nested address change marks the whole object dirty.

## Process flow

The following diagram visualizes this operation's control flow.

```mermaid
flowchart TD
	Start([GetAccidentInjury claimID]) --> Read[BeforeRetrieve issues<br>injury select]
	Read --> Fill[Fill maps columns,<br>builds injury address,<br>loads work address child]
	Fill --> Clean[MarkOld]
	Clean --> Edit[Caller edits injury fields]
	Edit --> Valid[IsValid runs]
	Valid --> Child{Work address child valid?}
	Child -->|No| Blocked([Object invalid;<br>no persistence])
	Child -->|Yes| Rules{Injury date or treatment<br>date modified?}
	Rules -->|Yes| Cross[Evaluate date rules;<br>lazily run cross-table counts<br>and service checks]
	Rules -->|No| SaveStart[Save begins]
	Cross --> Errors{Any error-severity<br>rule broken?}
	Errors -->|Yes| Blocked
	Errors -->|No| SaveStart
	SaveStart --> WorkAddr[Delete or save<br>work address child]
	WorkAddr --> Awards{Federal awards<br>with payments?}
	Awards -->|Yes| Pending[Set awards to pending<br>and save collection]
	Awards -->|No| County[DetermineCountyCode]
	Pending --> County
	County --> Update[BeforeUpdate issues<br>claim update]
	Update --> Inpatient{Inpatient flag N to Y?}
	Inpatient -->|Yes| DeleteWait[Delete three-day-wait rows]
	Inpatient -->|No| Rows{RowsAffected > 0?}
	DeleteWait --> Rows
	Rows -->|Yes| Ok([MarkOld; save succeeded])
	Rows -->|No| Contention([DB contention error added;<br>changes retained in memory])
```

## Failure and edge-case behavior

- If the retrieve finds no row for the given `ClaimID`, the object is returned unfilled with default values rather than raising a not-found error.*
- Optimistic concurrency failure — a stale `UpdateNum` or a concurrently deleted row — surfaces as a DB contention error rather than a database exception ([BR-CM-001](../business-rules.md#br-cm-001-optimistic-concurrency-updates-fail-if-rowsaffected-1)); the caller must inspect the broken-rule collection.
- An injury-date change is blocked outright, not merely warned, when unposted payments or adjustments exist, preventing a mismatch between the injury date and already-posted financials ([BR-CM-057](../business-rules.md#br-cm-057-injury-date-may-not-change-while-unposted-payments-or-adjustments-exist)).
- The "injury date after X" checks fire only when the comparison date is populated and only when the injury date was actually modified, so unchanged legacy data is not re-flagged on unrelated saves.
- The lazy count fields are cached at `-1` as a "not yet queried" sentinel and are reset only by an injury-date change, so repeated validation passes without a date change reuse the cached counts.
- Changing the inpatient flag from `N` to `Y` deletes the claim's three-day-wait rows as an update side effect, with no user confirmation modeled in this class.
- The policy-coverage check only runs when the policy number is greater than 17 — a legacy threshold that appears to separate migrated from current policy numbering* — and is a warning rather than a blocking error.
- Zip normalization treats a stored zip of `0` as blank and left-pads shorter non-zero zips with zeros.
- Commented-out legacy code (body parts, DB2 awards, non-federal award pending logic, jurisdiction lookup) is present but inactive and is not part of current behavior.

## 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.Maintenance\clsAccidentInjury.vb`
