---
title: Policy unit CRUD
summary: Creates, retrieves, updates, deletes, and activates/inactivates a Unit Stat policy-level reporting unit and exposes the derived reporting flags used by later report cycles.
status: draft
owner: SAIF
last_reviewed: 2026-07-30
library: SAIF.Business.UnitStat
reference_type: package
operation_id: OP-US-01
trigger: Caller creates a new policy unit for a policy/period/unit-effective-date triple, or retrieves an existing one by sequence number, natural key, or injury date
---

# Policy unit CRUD

A policy unit represents an NCCI reporting period of exposure and loss data for a policy. This operation covers the full lifecycle of `clsPolicyUnit` and `clsPolicyUnitCollection`: creation with sequence-number allocation and default flags, retrieval by three different key shapes, update with optimistic-concurrency checking, deletion, and activation/inactivation with its cascading correction-flag side effect.

| Field | Value |
| --- | --- |
| **Operation ID** | OP-US-01 |
| **Trigger** | `clsPolicyUnit.NewPolicyUnit` / `GetPolicyUnit` / `clsPolicyUnitCollection.GetPolicyUnits`, followed by property edits and `Save()`, or `ActivatePolicyUnit()` / `InactivatePolicyUnit()` |
| **Inputs** | Policy number, policy-period effective date, unit effective/expiration dates, valuation numbers, activity/correction/historical flags, premium set ID |
| **Outputs** | Persisted `US_POLICY_UNIT` row; populated `clsPolicyUnit` or `clsPolicyUnitCollection`; derived reportability/report-type flags |
| **Primary components** | `clsPolicyUnit.Create`, `clsPolicyUnit.BeforeInsert/AfterInsert`, `clsPolicyUnit.BeforeUpdate/AfterUpdate`, `clsPolicyUnit.BeforeDelete/AfterDelete`, `clsPolicyUnit.BeforeRetrieve/AfterRetrieve`, `clsPolicyUnit.ActivatePolicyUnit`, `clsPolicyUnit.InactivatePolicyUnit`, `clsPolicyUnitCollection.BeforeRetrieve/AfterRetrieve` |
| **Entry point** | `Business\SAIF.Business.UnitStat\clsPolicyUnit.vb`, `Business\SAIF.Business.UnitStat\clsPolicyUnitCollection.vb` |

## Steps

1. **Create.** `NewPolicyUnit(dataPortal, policyNumber, policyPeriodEffectiveDate, unitEffectiveDate)` invokes `Create()`, which allocates `PolicyUnitSeqNum` from the `US_POLICY_UNIT_SEQ` sequence and seeds the natural key (`PolicyNumber`, `PolicyPeriodEffectiveDate`, `UnitEffectiveDate`) from the criteria.
2. **New-object defaults.** As properties are first read on a new object, getters substitute defaults instead of the blank initial value: `ActiveUnitFlag` → `'Y'` ([BR-US-014](../business-rules.md#br-us-014-active-unit-flag-defaults-to-y-when-not-set)), `LossEditFlag` and `ExposureEditFlag` → `'Y'` ([BR-US-015](../business-rules.md#br-us-015-losseditflag-and-exposureeditflag-default-to-y-when-not-set), [BR-US-026](../business-rules.md#br-us-026-losseditflag-default-is-y-set-in-member-variable-initializer)), `UnitCorrectionFlag`/`ExposureCorrectionFlag`/`LossCorrectionFlag` → `'N'` ([BR-US-016](../business-rules.md#br-us-016-correction-flags-unitcorrectionflag-exposurecorrectionflag-losscorrectionflag-default-to-n)), `HistoricalFlag` → `'N'` ([BR-US-017](../business-rules.md#br-us-017-historicalflag-defaults-to-n-when-not-set)).
3. **Validate.** `Save()` calls `ApplyEdit()` then the framework validation pipeline runs `ValidPolicyUnitSeqNum`, requiring `PolicyUnitSeqNum > 0` ([BR-US-001](../business-rules.md#br-us-001-policy-unit-sequence-number-must-be-greater-than-zero)).
   - On failure: the broken-rule collection blocks the save; `IsValid` returns `False`.
4. **Insert.** `BeforeInsert` sets `UpdateNumber = 0`, stamps `UpdateUser`/`UpdateDate` from the data-portal info, and builds the parameterized insert against every column ([SQL-004](../sql/SQL-004-us-policy-unit-insert.md), [US_POLICY_UNIT](../database-tables.md#us-policy-unit)). `AfterInsert` checks the rows-affected result:
   - If `RowsAffected <> 1`: adds `InsertError` with the literal message "Insert Failed" (distinct from the update/delete contention message).
   - Otherwise: `MarkOld()`.
5. **Retrieve by sequence, natural key, or injury date.** `Criteria.RetrieveSQL` dispatches on `RetrieveType`:
   - By sequence number ([SQL-022](../sql/SQL-022-us-policy-unit-row-by-sequence.md)) — returns any row regardless of active status.
   - By natural key (policy number, policy-period effective date, unit effective date) — the WHERE clause requires `ACTIVE_UNIT_FLAG = 'Y'` ([SQL-021](../sql/SQL-021-us-policy-unit-active-row-by-natural-key.md)) ([BR-US-006](../business-rules.md#br-us-006-a-policy-unit-retrieved-by-natural-key-must-be-active-activeunitflag-y)).
   - By policy number and an injury/retrieve date falling within `[UNIT_EFF_DATE, UNIT_EXP_DATE)` — also requires `ACTIVE_UNIT_FLAG = 'Y'` ([SQL-020](../sql/SQL-020-us-policy-unit-active-row-by-retrieve-date.md)) ([BR-US-007](../business-rules.md#br-us-007-a-policy-unit-retrieved-by-injury-date-must-be-active)).
   - `AfterRetrieve` fills the object from the first result row when present and always calls `MarkOld()` — a no-match retrieve returns a blank but "old" object rather than raising a not-found error.
6. **Retrieve a collection.** `clsPolicyUnitCollection.GetPolicyUnits` supports two public shapes — by policy number ([SQL-024](../sql/SQL-024-us-policy-unit-collection-by-policy.md)) and by policy number plus policy-period effective date ([SQL-025](../sql/SQL-025-us-policy-unit-collection-by-policy-period.md)) — both ordered by policy number, policy-period effective date, and unit effective date.
   - A third internal retrieve type (`RetrieveTypes.LossEditFlag`, filtering `LOSS_EDIT_FLAG = 'Y'`, [SQL-023](../sql/SQL-023-us-policy-unit-rows-requiring-loss-edits.md)) is wired into `Criteria.RetrieveSQL` but has no corresponding public factory method on this class, so it is unreachable through the documented public surface.**
7. **Update.** `BeforeUpdate` increments `UpdateNumber`, restamps `UpdateUser`/`UpdateDate`, and rebinds every column ([SQL-029](../sql/SQL-029-us-policy-unit-update.md), [US_POLICY_UNIT](../database-tables.md#us-policy-unit)). `AfterUpdate` checks rows affected:
   - If `RowsAffected <> 1`: adds `UpdateError` with `clsConstants.Messages.DBContentionError` ([BR-US-020](../business-rules.md#br-us-020-database-contention-on-update-or-delete-is-detected-by-checking-rowsaffected-1)).
   - Otherwise: `MarkOld()`.
8. **Delete.** `BeforeDelete` binds `PolicyUnitSeqNum` to the delete-by-sequence statement ([SQL-035](../sql/SQL-035-us-policy-unit-delete.md)). `AfterDelete` checks rows affected:
   - If `RowsAffected <> 1`: adds `DeleteError` with `clsConstants.Messages.DBContentionError` ([BR-US-020](../business-rules.md#br-us-020-database-contention-on-update-or-delete-is-detected-by-checking-rowsaffected-1)).
   - Otherwise: `MarkNew()`.
9. **Activate/inactivate.** `ActivatePolicyUnit()` sets `ActiveUnitFlag`, `ExposureEditFlag`, and `LossEditFlag` to `'Y'`; if the unit `IsReported` (`LastReportedValuationNumber > 0`), it also sets `ExposureCorrectionFlag` and `LossCorrectionFlag` to `'Y'` so the next cycle reports the reactivation as a correction ([BR-US-012](../business-rules.md#br-us-012-activating-a-policy-unit-that-has-been-previously-reported-must-also-set-correction-flags)). `InactivatePolicyUnit()` sets only `ActiveUnitFlag = 'N'`.
10. **Derived reporting properties.** Read-only properties used by later report-cycle logic: `IsReportable(processDate)` compares the process date to `LastReportedValuationDate` ([BR-US-008](../business-rules.md#br-us-008-a-policy-unit-is-reportable-only-if-the-process-date-is-on-or-before-the-last-valuation-date)); `IsOriginalReport`/`IsOriginalFirstReport`/`IsCycleReport` classify the report by `UnitValuationNumber` versus `LastReportedValuationNumber` ([BR-US-009](../business-rules.md#br-us-009-an-original-report-exists-when-unitvaluationnumber-0-and-unitvaluationnumber-lastreportedvaluationnumber), [BR-US-010](../business-rules.md#br-us-010-a-first-original-report-is-one-that-is-an-original-report-and-has-unitvaluationnumber-1), [BR-US-011](../business-rules.md#br-us-011-a-cycle-report-is-one-with-unitvaluationnumber-1)); `IsSubjectToNonCompensableRules` compares `UnitEffectiveDate` to the 2013-01-01 constant ([BR-US-013](../business-rules.md#br-us-013-non-compensable-rules-apply-only-to-policy-units-effective-on-or-after-2013-01-01)).
    - `NCCIPolicyNumber` lazily derives its value through `UnitStatServices.GetNCCIPolicyNumber(...)` and `LastReportedValuationDate` lazily derives through `UnitStatServices.GetLastValuationDate(...)` — `UnitStatServices` is referenced but its implementation is not present in this library's source, so the exact derivation logic is unconfirmed.**
11. **Child collections.** `ClaimUnits` and `AuditConditions` are lazily loaded on first access (`clsClaimUnitCollection.GetClaimUnits`, `clsAuditConditionCollection.GetPolicyUnitAudits`) and registered as child objects so `IsValid`, `IsDirty`, and the cascading `SaveObject`/`SaveChildObjects` pipeline include them automatically.
    - The claim-unit collection logic is documented separately; see [Claim unit maintenance](claim-unit-maintenance.md).
    - The audit-condition collection logic is documented separately; see [Audit condition maintenance](audit-condition-maintenance.md).

## Process flow

The following diagram visualizes the policy-unit create/retrieve/update/delete lifecycle and the activation side effect.

```mermaid
flowchart TD
	Start([Caller creates or retrieves<br>a policy unit]) --> Which{Create, retrieve,<br>update, delete, or activate?}
	Which -->|Create| Seq[Allocate PolicyUnitSeqNum<br>from US_POLICY_UNIT_SEQ]
	Seq --> Defaults[Apply flag defaults:<br>ActiveUnitFlag=Y, edit flags=Y,<br>correction flags=N, HistoricalFlag=N]
	Defaults --> ValidateSeq{PolicyUnitSeqNum > 0?}
	ValidateSeq -->|No| Blocked([Broken rule blocks save])
	ValidateSeq -->|Yes| Insert[Insert US_POLICY_UNIT row]
	Insert --> InsRows{RowsAffected = 1?}
	InsRows -->|No| InsErr([InsertError: Insert Failed])
	InsRows -->|Yes| MarkOldA([MarkOld: object persisted])
	Which -->|Retrieve| RetKey{Sequence, natural key,<br>or injury date?}
	RetKey -->|Sequence| RetSeq[Select by sequence<br>any status]
	RetKey -->|Natural key| RetNat[Select by natural key<br>ACTIVE_UNIT_FLAG = Y]
	RetKey -->|Injury date| RetDate[Select by date range<br>ACTIVE_UNIT_FLAG = Y]
	RetSeq --> Fill[Fill object if a row found;<br>MarkOld regardless]
	RetNat --> Fill
	RetDate --> Fill
	Which -->|Update| Upd[Increment UpdateNumber;<br>update all columns]
	Upd --> UpdRows{RowsAffected = 1?}
	UpdRows -->|No| Contention([UpdateError: DB contention])
	UpdRows -->|Yes| MarkOldB([MarkOld])
	Which -->|Delete| Del[Delete by PolicyUnitSeqNum]
	Del --> DelRows{RowsAffected = 1?}
	DelRows -->|No| ContentionDel([DeleteError: DB contention])
	DelRows -->|Yes| MarkNewA([MarkNew])
	Which -->|Activate| Act[Set ActiveUnitFlag,<br>ExposureEditFlag, LossEditFlag = Y]
	Act --> Reported{IsReported?}
	Reported -->|Yes| Corr([Set ExposureCorrectionFlag,<br>LossCorrectionFlag = Y])
	Reported -->|No| DoneAct([Activation complete])
```

## Failure and edge-case behavior

- Insert failures use a distinct literal message ("Insert Failed") instead of the shared `DBContentionError` message used by update and delete failures.
- The `LossEditFlag` collection retrieve type is reachable only through the internal `Criteria`/`RetrieveType` mechanism; no public factory method on `clsPolicyUnitCollection` exposes it, so [SQL-023](../sql/SQL-023-us-policy-unit-rows-requiring-loss-edits.md) currently has no confirmed caller inside this library.**
- A retrieve that matches no row still marks the object `Old` rather than surfacing a not-found condition; callers must check the populated state themselves.
- `NCCIPolicyNumber` and `LastReportedValuationDate` depend on `UnitStatServices`, whose implementing type is not present in the supplied source, so their exact derivation rules are unconfirmed.**
- Optimistic concurrency (`RowsAffected <> 1`) is the only contention-detection mechanism for update and delete; no row versioning or locking is used.

## 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. Statements
> marked with `**` could not be confirmed; treat as unknown until verified.

## Sources

- `Business\SAIF.Business.UnitStat\clsPolicyUnit.vb`
- `Business\SAIF.Business.UnitStat\clsPolicyUnitCollection.vb`
