---
title: Reimbursement recording
summary: Records a DCBS reimbursement payment, its per-claim detail lines, and the expense-type amount breakdowns that the balancing run later matches against billed expenses.
status: draft
owner: SAIF Doc Gen
last_reviewed: 2026-07-30
library: SAIF.Business.Claim.EAIP
reference_type: package
operation_id: OP-EAIP-07
trigger: Caller creates or retrieves a reimbursement through clsReimbursement, edits its details and amounts, and calls Save
---

# Reimbursement recording

When DCBS pays back a batch of EAIP bills, the payment arrives as one remittance covering many claims. This operation records that remittance as a three-level structure: a reimbursement header carrying the received, posted, and reconciled dates and the total amount; one detail row per claim named on the remittance; and, at both levels, an amount breakdown by expense type. Nothing here matches the money to bills — that is the job of [DCBS reimbursement balancing](dcbs-reimbursement-balancing.md). This operation's responsibility is to capture what DCBS said it paid, keyed so the balancing run can find it.

| Field | Value |
| --- | --- |
| **Operation ID** | OP-EAIP-07 |
| **Trigger** | `clsReimbursement.NewReimbursement(dpi)` or one of the shared retrieval methods, followed by edits and `Save()` |
| **Inputs** | `IDataPortalInfo`; received date, posted date, reimbursement amount, and explanation text; per-detail DCBS claim number, DCBS WCD claim number, DCBS worker name, request received date, injury date, total amount, and disabling indicator; per-amount expense type code with disabling, non-disabling, and total amounts (header level) or reimbursed amount (detail level) |
| **Outputs** | An `EAIP_REIMBURSEMENT` row with its detail and amount children; resolved claim data on each detail; a claim-not-found rule violation for an unresolvable DCBS claim number; DB contention errors when a statement affects no rows |
| **Primary components** | `clsReimbursement` (`Create`, `AddInsertUpdateParms`, `AfterInsert`, `AfterUpdate`, `GetReimbursementsByReceivedDate`, `GetReimbursementsByPostedDate`, `GetReimbursements`, `OverrideBusinessRules`), `clsReimbursementDetail` (`DcbsSaifClaimNum`, `ClaimData`, `BeforeUpdate`, `Rule_ValidClaim`), `clsAmount`, `clsDetailAmount` |
| **Entry point** | `Business\SAIF.Business.Claim.EAIP\Reimbursement\clsReimbursement.vb` |

## Steps

1. **Create the reimbursement header.** `NewReimbursement(dpi)` runs `Create()`, which takes the next value of `eaip_reimb_seq` as the reimbursement identifier and records the create stamp ([SQL-005](../sql/SQL-005.md), [EAIP_REIMB_SEQ](../database-tables.md#eaip_reimb_seq)). Existing remittances are opened by identifier through `pk82500_eaip.eaip_reimbursement_select` ([SQL-024](../sql/SQL-024.md), [Details](../procedures/pk82500_eaip.md#eaip_reimbursement_select)).
2. **Record the remittance facts.** The caller sets the received date (when SAIF got the payment), the posted date (the DCBS posting date that keys the balancing batch), the reimbursement amount, and the free-text explanation. The reconciled date, reconciled amount, and reconciled flag are left for the balancing run to fill in.
3. **Add a detail row per claim.** `clsReimbursementDetail.NewReimbursementDetail` takes the next value of `eaip_reimb_detail_seq` and links itself to the parent reimbursement ([SQL-006](../sql/SQL-006.md), [EAIP_REIMB_DETAIL_SEQ](../database-tables.md#eaip_reimb_detail_seq)). Each detail carries what DCBS reported: the DCBS SAIF claim number, the DCBS WCD claim number, the DCBS worker name, the request received date, the injury date, the total amount for that claim, and the disabling indicator.
4. **Resolve the claim behind the DCBS claim number.** Setting `DcbsSaifClaimNum` clears the cached claim data, so the next read of `ClaimData` re-resolves it. The `SAIFClaimNum`, `ClaimID`, and `WorkerName` properties all read through that resolved claim data rather than through the DCBS-supplied text, which is what lets the balancing run find SAIF's own billing rows for a claim DCBS identified by its own number.
   > **Origin**: Claim resolution, the formatted worker name, and the validity check behind it are implemented by `clsClaimData` in `SAIF.Business.View.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-view-claim/operations-processes.md) in the `SAIF.Business.View.Claim` documentation set.
5. **Validate the claim.** The detail registers exactly one rule, bound to `DcbsSaifClaimNum`: the detail is valid when it is not dirty or the resolved claim data reports itself valid; otherwise the message "Error: Claim number not found." is raised. A clean (unedited) detail is never re-validated, so a claim that later becomes unresolvable does not invalidate a stored remittance.
6. **Break the detail down by expense type.** Each detail carries a collection of `clsDetailAmount` keyed by the detail identifier plus the expense type code, holding the reimbursed amount for that type and a read-only reconciled indicator. Unlike the header and detail rows, these have no sequence of their own: `Create()` sets only the create stamp and the criteria's create-statement property throws an invalid-statement exception, because the composite key is supplied by the caller.
7. **Break the header down by expense type as well.** The header carries a parallel collection of `clsAmount` keyed by the reimbursement identifier plus the expense type code, holding the disabling amount, the non-disabling amount, and the total. It is built the same way, with no sequence and a throwing create-statement property.
8. **Persist the header.** `AddInsertUpdateParms` binds the reimbursement identifier, received date, posted date, reconciled date, reconciled flag, reimbursement amount, reconciled amount, explanation text, and audit stamp; `BeforeUpdate` additionally binds `LastUpdateNum` and increments the update information. Insert targets `pk82500_eaip.eaip_reimbursement_insert` ([SQL-015](../sql/SQL-015.md), [Details](../procedures/pk82500_eaip.md#eaip_reimbursement_insert)) and update targets `eaip_reimbursement_update` ([SQL-035](../sql/SQL-035.md), [Details](../procedures/pk82500_eaip.md#eaip_reimbursement_update)); each checks the affected row count and adds a DB contention error when it is not one.
9. **Cascade the children.** Both `AfterInsert` and `AfterUpdate` save the detail collection and then the amount collection, but only after their own row-count check passed. A failed header write therefore leaves the children unsaved rather than orphaned.
10. **Persist the details.** `clsReimbursementDetail.BeforeUpdate` first recomputes `TotalAmount` as the sum of its detail amounts' reimbursed amounts, so the stored total always agrees with the breakdown that produced it. The parameter binding uses the resolved `WorkerName` when it is non-blank and falls back to the DCBS-supplied worker name otherwise. Insert, update, and delete target `pk82500_eaip.eaip_reimb_detail_insert`, `_update`, and `_delete` ([SQL-016](../sql/SQL-016.md), [SQL-036](../sql/SQL-036.md), [SQL-046](../sql/SQL-046.md), [Details](../procedures/pk82500_eaip.md#eaip_reimb_detail_insert)). A single detail is read by identifier through `GetReimbDetailByID` and the collection for a reimbursement through `eaip_reimb_detail_select` ([SQL-025](../sql/SQL-025.md), [Details](../procedures/pk82500_eaip.md#getreimbdetailbyid)).
11. **Persist the amount breakdowns.** `clsAmount` writes through `EAIP_Reimb_Amount_insert`, `_update`, and `_delete` ([SQL-013](../sql/SQL-013.md), [SQL-033](../sql/SQL-033.md), [SQL-043](../sql/SQL-043.md), [Details](../procedures/pk82500_eaip.md#eaip_reimb_amount_insert)) and reads a single row through `GetReimbAmountbyType` or the whole set through `EAIP_Reimb_Amount_Select` ([SQL-022](../sql/SQL-022.md), [Details](../procedures/pk82500_eaip.md#getreimbamountbytype)). `clsDetailAmount` mirrors this against `reimb_detail_amount_insert`, `_update`, `_delete`, `GetReimbDetailAmountbyType`, and `reimb_detail_amount_Select` ([SQL-014](../sql/SQL-014.md), [SQL-034](../sql/SQL-034.md), [SQL-044](../sql/SQL-044.md), [SQL-023](../sql/SQL-023.md), [Details](../procedures/pk82500_eaip.md#reimb_detail_amount_insert)).
12. **Allow the expense type to change.** Because the expense type code is part of the key, `BeforeUpdate` on both amount classes binds the previous code as `LastExpenseTypeCode` alongside `LastUpdateNum`, and `AfterInsert`/`AfterUpdate` re-synchronize the criteria's expense type code to the current value. This is what lets an operator correct a misclassified breakdown line in place rather than deleting and re-adding it.
13. **Delete.** The header's delete targets `pk82500_eaip.eaip_reimbursement_delete` ([SQL-045](../sql/SQL-045.md), [Details](../procedures/pk82500_eaip.md#eaip_reimbursement_delete)); a code comment records that the child details and amounts are removed by database triggers, so this library issues no cascading child deletes.
14. **Retrieve remittances for downstream work.** Three shared readers serve the balancing run and the operator screens: by received date through `GetReimbByReceivedDate`, by posted date through `GetReimbByPostedDate` ([Details](../procedures/pk82500_eaip.md#getreimbbyposteddate)) — the entry point for [DCBS reimbursement balancing](dcbs-reimbursement-balancing.md) — and by reconciled flag through `GetReimbReconciledFlag` ([Details](../procedures/pk82500_eaip.md#getreimbreconciledflag)), which drives the outstanding-remittance worklist.
15. **Bypass validation for batch work.** Setting `OverrideBusinessRules` on the header makes `IsValid` return true unconditionally, regardless of broken rules on the object or its children. The balancing run relies on this so that a remittance naming a claim SAIF cannot resolve can still be saved with its reconciliation outcome recorded, rather than blocking the whole batch.*

## Process flow

The following diagram visualizes this operation's control flow.

```mermaid
flowchart TD
	Start([Remittance received from DCBS]) --> New[NewReimbursement: eaip_reimb_seq nextval,<br>create stamp]
	New --> Head[Set received date, posted date,<br>amount, explanation]
	Head --> Det[Add a detail per claim:<br>eaip_reimb_detail_seq nextval]
	Det --> Resolve[Setting DcbsSaifClaimNum clears cache;<br>ClaimData re-resolves the SAIF claim]
	Resolve --> Rule{Detail dirty and<br>claim data invalid?}
	Rule -->|Yes| Broken[Rule violation:<br>Claim number not found]
	Rule -->|No| Amt[Add expense-type amounts at<br>detail level and header level]
	Broken --> Amt
	Amt --> Save([Save])
	Save --> Ovr{OverrideBusinessRules set?}
	Ovr -->|Yes| Write
	Ovr -->|No| Valid{IsValid?}
	Valid -->|No| Reject([Save rejected])
	Valid -->|Yes| Write[Insert or update the header<br>with LastUpdateNum check]
	Write --> Rows{RowsAffected = 1?}
	Rows -->|No| Err([DB contention error;<br>children not saved])
	Rows -->|Yes| Kids[Save details: recompute TotalAmount<br>from detail amounts, then save amounts]
	Kids --> Done([Remittance recorded and<br>ready for balancing])
```

## Failure and edge-case behavior

- `OverrideBusinessRules` suppresses all validation on the header, including violations raised by its children. Anything saved through that path bypasses the claim-number check entirely.
- The detail's claim rule short-circuits on `Not IsDirty`, so an existing detail whose claim later becomes invalid never reports a violation.
- `TotalAmount` on a detail is recomputed only in `BeforeUpdate`. On an initial insert the caller-supplied total is stored as given and may disagree with the amount breakdown until the row is next updated.*
- Both amount classes are keyed by a composite that includes the expense type code, and both throw an invalid-statement exception from the create-statement property. A duplicate expense type on the same parent is rejected by the database's own key constraint rather than by a business rule.*
- Child rows are deleted only by database triggers when the parent is deleted; deleting a child through this library requires deleting it individually before the parent save.
- Every statement checks its affected row count and raises a DB contention error on anything other than one, but the header, detail, and amount saves are separate statements with no surrounding transaction in this code, so a mid-cascade failure can leave a partially saved remittance.*
- The detail binds the DCBS worker name only when the resolved worker name is blank. A resolvable claim therefore overwrites what DCBS reported with SAIF's own name, which is exactly the value the balancing run later compares for name mismatches.

## 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.EAIP\Reimbursement\clsReimbursement.vb`
- `Business\SAIF.Business.Claim.EAIP\Reimbursement\clsReimbursementDetail.vb`
- `Business\SAIF.Business.Claim.EAIP\Reimbursement\clsAmount.vb`
- `Business\SAIF.Business.Claim.EAIP\Reimbursement\clsDetailAmount.vb`
