---
title: Disability activity tracking
summary: Loads and maintains a claim's disability status history and its time-loss authorization history, enforcing chronological ordering and injury-date cross-checks.
status: draft
owner: SAIF Doc Gen
last_reviewed: 2026-07-30
library: SAIF.Business.Claim.Maintenance
reference_type: package
operation_id: OP-CM-07
trigger: Caller retrieves the disability status or disability authorization collection for a claim, edits or adds items, and calls Save()
---

# Disability activity tracking

Disability activity tracking covers two parallel entity families for a claim: disability status activity, which records the dates on which disability status changed, and disability authorization activity, which records time-loss authorization dates and reason codes. Both collections load newest-first, stamp the claim's injury context onto every item for cross-validation, enforce a non-decreasing date sequence, and persist through the usual insert, update, and delete lifecycle with row-count concurrency checks.

| Field | Value |
| --- | --- |
| **Operation ID** | OP-CM-07 |
| **Trigger** | Caller invokes `clsClaimDisabilityActivityCollection.GetClaimDisabilityActivityCollection` or `clsDisabilityAuthActivityCollection.GetDisabilityAuthActivityCollection`, edits or adds items, and calls `Save()` on the collection* |
| **Inputs** | `ClaimID` (Decimal, key for both families); `DisabilityDate`; `DisabilityAuthReasonCode` and `DisabAuthActivityDate`; the sequence keys `DisabilityActivitySubNum` and `DisabAuthActivitySubNum`; `UpdateNum` as the concurrency token on updates; the acting user from the data-portal info |
| **Outputs** | Inserted, updated, and deleted rows in the claim disability activity and disability authorization activity tables; in-memory latest item and current authorization, the history collection, the maximum disability date, and broken-rule messages |
| **Primary components** | `clsClaimDisabilityActivity`, `clsClaimDisabilityActivityCollection` (`GetLatestRecord`, `GetInjuryDate`, `GetMaxDisabilityDate`, `UpdateHistoryCollections`), `clsDisabilityAuthActivity`, `clsDisabilityAuthActivityCollection` (`NewDisabilityAuthActivity`, `CurrentDisabAuth`) |
| **Entry point** | `Business\SAIF.Business.Claim.Maintenance\clsClaimDisabilityActivityCollection.vb` |

## Steps

1. **Retrieve the disability status history.** `GetClaimDisabilityActivityCollection(dataPortal, claimID)` builds a criteria and calls the portal's retrieve; `BeforeRetrieve` binds the claim identifier and the statement selects every row for the claim ordered by activity sub-number descending ([SQL-043](../sql/SQL-043.md), [CL_CLAIM_DISABILITY_ACTIVITY](../database-tables.md#cl_claim_disability_activity)).
2. **Materialize the status rows.** `AfterRetrieve` loops the reader, creates one activity object per row, calls `Load` and `Fill` to populate claim identifier, disability date, sub-number, and audit fields, re-attaches a criteria, and adds each item to the collection.
3. **Fetch the injury context.** When the cached injury date is still the minimum date, `GetInjuryDate()` issues a separate request that reads the injury date and occupational-disease code for the claim into collection-level fields ([SQL-042](../sql/SQL-042.md), [CL_INJURY_CLAIM](../database-tables.md#cl_injury_claim)).
4. **Stamp the injury context onto each item.** Every loaded item receives the collection's injury date and occupational-disease code so it can self-validate.
5. **Determine the working "latest" row (`GetLatestRecord`).**
   - If the collection is empty, create a brand-new activity item whose claim identifier is set, whose sub-number and update number are zero, and whose disability date is the minimum date; mark it clean and add it as the latest item.
   - Otherwise, sort descending by sub-number and take the first non-deleted item as the latest; if that item has already been saved, append a copy with the sub-number incremented by one so the caller gets a pre-seeded "add next status" row.
6. **Edit and validate a status item.** Setting the disability date marks the item dirty; the item rules then require the date not to be in the future (checked only when the smart-date value was modified), require a date whenever the object is dirty, and forbid a date earlier than the injury date unless the claim is an occupational-disease claim.
7. **Enforce status chronology at the collection level.** The collection rule sorts items descending by sub-number and rejects the set if any earlier-sequence item's date is less than a later one, so disability dates must be non-decreasing across the sequence; only items that are new and dirty or already saved take part.
8. **Save the disability status collection.** `Save()` applies the pending edit and the base save dispatches per item:
   - Insert: bind the disability date, acting user, claim identifier, and sub-number, then execute the insert; a rows-affected value other than one adds an insert error ([SQL-003](../sql/SQL-003.md), [CL_CLAIM_DISABILITY_ACTIVITY](../database-tables.md#cl_claim_disability_activity)).
   - Update: bind the disability date, update number, acting user, claim identifier, and sub-number and match on all three keys for optimistic concurrency; zero rows affected adds an update error carrying the DB contention message ([SQL-088](../sql/SQL-088.md), [CL_CLAIM_DISABILITY_ACTIVITY](../database-tables.md#cl_claim_disability_activity)) ([BR-CM-001](../business-rules.md#br-cm-001-optimistic-concurrency-updates-fail-if-rowsaffected-1)).
   - Delete: bind the claim identifier and sub-number; zero rows affected adds a delete error carrying the DB contention message ([SQL-107](../sql/SQL-107.md), [CL_CLAIM_DISABILITY_ACTIVITY](../database-tables.md#cl_claim_disability_activity)) ([BR-CM-001](../business-rules.md#br-cm-001-optimistic-concurrency-updates-fail-if-rowsaffected-1)).
9. **Housekeeping after removal.** Removing an item rebuilds the history collection from all list items except clean-new placeholders and re-sorts it descending by sub-number for grid display; the maximum-disability-date helper re-sorts and returns the first non-placeholder item's date, which is used to keep the claim's disability date in step.*
10. **Retrieve the disability authorization history.** `GetDisabilityAuthActivityCollection(dataPortal, claimID)` runs a statement that joins the authorization table to the claim on claim identifier and returns every authorization row plus the injury date, ordered by authorization sub-number descending ([SQL-065](../sql/SQL-065.md), [CL_DISABILITY_AUTH_ACTIVITY](../database-tables.md#cl_disability_auth_activity), [CL_INJURY_CLAIM](../database-tables.md#cl_injury_claim)).
11. **Materialize the authorization rows.** `AfterRetrieve` loads each row into an authorization object — claim identifier, reason code, activity date, sub-number, audit fields, and the joined injury date — tracks the highest sub-number seen, and sorts the collection descending.
12. **Fall back to a separate injury-date read.** If the injury date is still the minimum date after the join-based load, a scalar query supplies it, guarded by a date check before assignment ([SQL-064](../sql/SQL-064.md), [CL_INJURY_CLAIM](../database-tables.md#cl_injury_claim)).
13. **Add a new authorization activity.** `NewDisabilityAuthActivity(claimID)` increments the maximum-sequence counter, creates an empty item (claim identifier and acting user set, reason code defaulted to `0`, activity date empty, sub-number zero, marked new then clean), assigns the collection's injury date and the new sub-number, adds it, and re-sorts descending.
14. **Edit and validate an authorization item.** Setting the reason code or activity date marks the item dirty; the item rules forbid a date more than seven days in the future, require the date when the object is dirty, require a non-zero reason code when dirty, and forbid a date earlier than the injury date.
15. **Enforce authorization chronology at the collection level.** The collection rule sorts descending by sub-number and rejects the set if any earlier-sequence date is less than a later one; unlike the status collection, it compares all rows rather than only dirty or saved ones.
16. **Save the authorization collection.** `Save()` applies the pending edit and the base save dispatches per item:
    - Insert: bind the activity date, acting user, claim identifier, reason code, and sub-number; zero rows affected adds an insert error ([SQL-013](../sql/SQL-013.md), [CL_DISABILITY_AUTH_ACTIVITY](../database-tables.md#cl_disability_auth_activity)).
    - Update: bind the reason code, activity date, update number, acting user, claim identifier, and sub-number, matching on claim, sub-number, and update number; zero rows affected adds an update error carrying the DB contention message ([SQL-095](../sql/SQL-095.md), [CL_DISABILITY_AUTH_ACTIVITY](../database-tables.md#cl_disability_auth_activity)) ([BR-CM-001](../business-rules.md#br-cm-001-optimistic-concurrency-updates-fail-if-rowsaffected-1)).
    - Delete: bind the claim identifier and sub-number; a rows-affected value other than one adds a delete error carrying the DB contention message ([SQL-114](../sql/SQL-114.md), [CL_DISABILITY_AUTH_ACTIVITY](../database-tables.md#cl_disability_auth_activity)) ([BR-CM-001](../business-rules.md#br-cm-001-optimistic-concurrency-updates-fail-if-rowsaffected-1)).
17. **Expose the current authorization.** `CurrentDisabAuth` scans the list for the highest sub-number and, when the collection is empty, returns a transient empty authorization that is never added to the list, so bound consumers always receive a non-null object.

## Process flow

The following diagram visualizes this operation's control flow across both entity families.

```mermaid
flowchart TD
	Start([Caller requests disability data<br>for a claim]) --> Family{Which family?}
	Family -->|Disability status| SRead[Select activity rows<br>ordered by sub-number desc]
	SRead --> SLoad[Load one item per row]
	SLoad --> SInj[Read injury date and<br>occupational disease code]
	SInj --> SStamp[Stamp injury context<br>onto every item]
	SStamp --> SLatest{Collection empty?}
	SLatest -->|Yes| SNew[Create blank latest item]
	SLatest -->|No| SCopy[Take newest item;<br>append incremented copy<br>if it was already saved]
	SNew --> Edit
	SCopy --> Edit
	Family -->|Disability authorization| ARead[Select authorization rows<br>joined to the claim]
	ARead --> ALoad[Load items; track max sequence;<br>sort desc]
	ALoad --> AInj{Injury date resolved?}
	AInj -->|No| AFall[Scalar injury date query<br>guarded by a date check]
	AInj -->|Yes| ANew[NewDisabilityAuthActivity<br>seeds next sequence]
	AFall --> ANew
	ANew --> Edit[Caller edits item fields]
	Edit --> Rules[Item rules: required, future date,<br>not before injury date]
	Rules --> Seq[Collection rule: dates must be<br>non-decreasing by sequence]
	Seq --> Ok{Rules pass?}
	Ok -->|No| Invalid([Broken rules returned;<br>no persistence])
	Ok -->|Yes| Save[Save applies edit,<br>dispatches per item]
	Save --> Kind{Item state}
	Kind -->|New| Ins[Insert row]
	Kind -->|Modified| Upd[Update matching<br>claim, sub-number, update num]
	Kind -->|Removed| Del[Delete by claim<br>and sub-number]
	Ins --> Rows{Expected rows affected?}
	Upd --> Rows
	Del --> Rows
	Rows -->|No| Err([Insert, update, or delete error;<br>DB contention message])
	Rows -->|Yes| Done([Item marked old;<br>history collections rebuilt])
```

## Failure and edge-case behavior

- Concurrency violations — another user updated or deleted the row first, so the update number no longer matches — surface as zero rows affected in the update and delete hooks for both entity families and produce a DB contention broken-rule message rather than a thrown exception ([BR-CM-001](../business-rules.md#br-cm-001-optimistic-concurrency-updates-fail-if-rowsaffected-1)).
- The two insert hooks disagree on the exact row-count test: one treats any count other than one as failure while the other treats only zero as failure. This is a source inconsistency, not a documented business distinction.*
- The two delete hooks are inconsistent in the same way, one testing for zero and the other for a count other than one.*
- The future-date rule on disability status runs only when the smart date's value was modified, so a loaded, unmodified date is never re-validated against today; the required and injury-date rules always evaluate when the object is dirty or a date is set.
- The injury-date cross-check on disability status is bypassed entirely for occupational-disease claims, allowing a disability date earlier than the injury date in that case.*
- If the latest non-deleted status item is itself new and never saved, no extra copy row is appended — the existing new item is reused, avoiding duplicate blank rows.
- The authorization collection's fallback injury-date query guards its scalar result with a date check, silently leaving the injury date at its default when the query returns no row or a non-date value.
- The maximum-date and history-rebuild helpers both special-case clean-new placeholder rows so the auto-generated blank entry does not pollute max-date lookups or the history grid.
- Statement properties that a given class does not use throw an invalid-statement application exception when invoked, guarding against use of the wrong data-access path.
- The two collections each enforce chronological ordering and injury-date cross-checks independently and do not coordinate with each other at the level shown in the source.*

## 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\clsClaimDisabilityActivity.vb`
- `Business\SAIF.Business.Claim.Maintenance\clsClaimDisabilityActivityCollection.vb`
- `Business\SAIF.Business.Claim.Maintenance\clsDisabilityAuthActivity.vb`
- `Business\SAIF.Business.Claim.Maintenance\clsDisabilityAuthActivityCollection.vb`
