---
title: EOB preference management
summary: Retrieves, creates, updates, and deletes a provider location's paperless-EOB delivery preference.
status: draft
owner: SAIF Doc Gen
last_reviewed: 2026-07-30
library: SAIF.Business.Provider.PaymentPreference
reference_type: package
operation_id: OP-PP-01
trigger: Caller retrieves a single EOB preference or the full per-provider collection, edits fields, and calls Save()
---

# EOB preference management

`clsProviderEOB` represents one provider location's paperless-EOB (Explanation of Benefits) delivery preference — which external user receives EOB correspondence and whether delivery is electronic or paper. `clsProviderEOBCollection` lists every location for a provider, including locations that have never configured a preference, by left-outer-joining `PROVIDER_LOCATION` to `PROVIDER_EOB_INTERFACE`. Retrieval, creation, update, and deletion all share the same optimistic-concurrency and default-flag rules, so they are documented together.

| Field | Value |
| --- | --- |
| **Operation ID** | OP-PP-01 |
| **Trigger** | Caller invokes `GetProviderEOBs`/`NewProviderEOBs` (collection) or `GetProviderEOB`/`NewProviderEOB` (single object), edits the returned object(s), and calls `Save()` |
| **Inputs** | `ProviderSeqNum`, `ProviderLocationSeqNum`, `ExtUserTypeSeqNum`, `PaperlessEOBFlag`, `DefaultPrimaryFlag` |
| **Outputs** | Populated `clsProviderEOB` / `clsProviderEOBCollection`; persisted `PROVIDER_EOB_INTERFACE` row on save; `InsertError`/`DBContentionError` on save failure |
| **Primary components** | `clsProviderEOB.GetProviderEOB`, `clsProviderEOB.NewProviderEOB`, `clsProviderEOB.BeforeInsert`/`AfterInsert`, `clsProviderEOB.BeforeUpdate`/`AfterUpdate`, `clsProviderEOB.BeforeDelete`/`AfterDelete`, `clsProviderEOBCollection.GetProviderEOBs`/`NewProviderEOBs`, `clsProviderEOBCollection.AfterRetrieve` |
| **Entry point** | `Business\SAIF.Business.Provider.PaymentPreference\clsProviderEOB.vb`, `Business\SAIF.Business.Provider.PaymentPreference\clsProviderEOBCollection.vb` |

## Steps

1. **Retrieve the collection for a provider.** `GetProviderEOBs`/`NewProviderEOBs(dpi, providerSeqNum)` build a `Criteria` and fetch every location for the provider, left-outer-joined to any existing `PROVIDER_EOB_INTERFACE` row and the external-user/email/person chain ([SQL-007](../sql/SQL-007-select-provider-locations-with-eob-preference.md), [PROVIDER_LOCATION](../database-tables.md#provider_location), [PROVIDER_EOB_INTERFACE](../database-tables.md#provider_eob_interface), [EXT_USER_TYPE](../database-tables.md#ext_user_type), [EXT_USER](../database-tables.md#ext_user), [EMAIL_ADDRESS](../database-tables.md#email_address), [PERSON](../database-tables.md#person)).
   - `AfterRetrieve` iterates each row, constructs a new `clsProviderEOB`, and calls its `Load` method to fill it from that row before adding it to the collection.
2. **Fill each row and determine new-vs-old state.** `Fill()` copies all columns onto the object, and the constant `UserName` fallback resolves to `PAPER_EOB` ("Paper EOB") whenever the joined person name is blank, signaling no external user is linked ([BR-17](../business-rules.md#br-17-username-on-clsprovidereob-returns-the-constant-papereob-paper-eob-when-the-underlying-name-is-blank-indicating-no-external-user-is-linked-to-the-record)).
   - `Fill()` marks the object New when `UpdateNum = 0` and Old otherwise, so a location with no prior EOB preference row (left-outer-joined nulls) is still returned as an editable object that will Insert rather than Update on save ([BR-18](../business-rules.md#br-18-a-retrieved-eob-preference-row-with-updatenum-equal-to-zero-is-treated-as-unsaved-and-marked-new-so-save-falls-through-to-insert-instead-of-update)).
3. **Retrieve a single preference.** `GetProviderEOB(dpi, providerSeqNum, providerLocationSeqNum)` builds a `Criteria` and fetches one row.
   - If `ProviderSeqNum <> 0`: `BeforeRetrieve` binds both `ProviderSeqNum` and `ProviderLocationSeqNum` and issues the by-provider-and-location query ([SQL-005](../sql/SQL-005-select-provider-eob-interface-by-provider-location.md), [PROVIDER_EOB_INTERFACE](../database-tables.md#provider_eob_interface)).
   - If `ProviderSeqNum = 0`: `BeforeRetrieve` binds only `ProviderLocationSeqNum` and issues the location-only variant instead ([SQL-006](../sql/SQL-006-select-provider-eob-interface-by-location.md)).
   - `AfterRetrieve` fills the first returned row, applying the same New/Old determination as step 2 ([BR-18](../business-rules.md#br-18-a-retrieved-eob-preference-row-with-updatenum-equal-to-zero-is-treated-as-unsaved-and-marked-new-so-save-falls-through-to-insert-instead-of-update)).
4. **Create.** `NewProviderEOB(dpi, providerSeqNum, providerLocationSeqNum)` constructs a new object and `Create()` seeds the keys, update user, and defaults `PaperlessEOBFlag`/`DefaultPrimaryFlag` to `"N"`.
   - `BeforeInsert` re-derives `PaperlessEOBFlag`: `"Y"` only when `ExtUserTypeSeqNum` is populated and non-zero at insert time, otherwise `"N"` ([BR-01](../business-rules.md#br-01-paperlesseobflag-is-set-to-y-only-when-extusertypeseqnum-is-non-zero-and-non-empty-at-insert-time-otherwise-defaults-to-n)); `DefaultPrimaryFlag` is always reset to `"N"` on insert regardless of any caller-set value ([BR-02](../business-rules.md#br-02-defaultprimaryflag-is-always-initialized-to-n-on-new-eob-creation)).
   - `AfterInsert` executes the insert ([SQL-001](../sql/SQL-001-insert-provider-eob-interface.md), [PROVIDER_EOB_INTERFACE](../database-tables.md#provider_eob_interface)) and checks the rows-affected count.
     - On a rows-affected value other than `1`: add `InsertError` ("Insert Failed") and leave the object marked New ([BR-03](../business-rules.md#br-03-insert-contention-check-if-rowsaffected-1-after-insert-an-inserterror-error-message-is-added-and-the-record-is-not-marked-old)).
     - On success: `MarkOld()`.
5. **Update.** `BeforeUpdate` refreshes `UpdateDate`/`UpdateUser` and binds all mutable fields plus the in-memory `UpdateNum` as a `WHERE` predicate.
   - `AfterUpdate` executes the update, which increments `UPDATE_NUM` at the database and requires the bound `UpdateNum` to match the current row ([SQL-013](../sql/SQL-013-update-provider-eob-interface.md), [PROVIDER_EOB_INTERFACE](../database-tables.md#provider_eob_interface)).
     - On a rows-affected value other than `1`: add `DBContentionError` ([BR-04](../business-rules.md#br-04-update-contention-check-if-rowsaffected-1-after-update-dbcontentionerror-is-added-updatenum-must-match-for-the-where-clause-to-succeed)).
     - On success: `MarkOld()`.
     - Note: unlike `clsProviderEFT.AfterUpdate`, `clsProviderEOB.AfterUpdate` does not increment the in-memory `UpdateNum` after a successful update, even though the database value advances; a subsequent update against the same in-memory instance would then fail contention until the object is re-retrieved.*
6. **Delete.** `BeforeDelete` binds the provider/location keys; `AfterDelete` executes the delete ([SQL-015](../sql/SQL-015-delete-provider-eob-interface.md), [PROVIDER_EOB_INTERFACE](../database-tables.md#provider_eob_interface)).
   - On a rows-affected value other than `1`: add `DBContentionError` ([BR-05](../business-rules.md#br-05-delete-contention-check-if-rowsaffected-1-after-delete-dbcontentionerror-is-added)).
   - On success: `MarkNew()`.
7. **Collection save.** `clsProviderEOBCollection.Save()` calls `ApplyEdit()` and delegates to the base collection save, which persists each changed child `clsProviderEOB` through steps 4–6.

## Process flow

```mermaid
flowchart TD
	Start([Caller requests EOB preference]) --> Which{Single or collection?}
	Which -->|Collection| ListQ[Query provider locations<br>left-joined to PROVIDER_EOB_INTERFACE]
	ListQ --> Fill1[Fill each row;<br>UpdateNum = 0 marks New]
	Which -->|Single, ProviderSeqNum not 0| ByBoth[Query by provider + location]
	Which -->|Single, ProviderSeqNum is 0| ByLoc[Query by location only]
	ByBoth --> Fill2[Fill row; UpdateNum = 0 marks New]
	ByLoc --> Fill2
	Fill1 --> Edit[Caller edits fields]
	Fill2 --> Edit
	Edit --> IsNew{Object marked New?}
	IsNew -->|Yes| Insert[Insert: derive PaperlessEOBFlag,<br>reset DefaultPrimaryFlag]
	IsNew -->|No, edited| Update[Update with UpdateNum<br>as WHERE predicate]
	IsNew -->|No, removed| Delete[Delete by provider + location]
	Insert --> Rows1{RowsAffected = 1?}
	Update --> Rows2{RowsAffected = 1?}
	Delete --> Rows3{RowsAffected = 1?}
	Rows1 -->|No| ErrIns([InsertError added])
	Rows1 -->|Yes| Old([MarkOld])
	Rows2 -->|No| ErrUpd([DBContentionError added])
	Rows2 -->|Yes| Old
	Rows3 -->|No| ErrDel([DBContentionError added])
	Rows3 -->|Yes| New([MarkNew])
```

## Failure and edge-case behavior

- Insert, update, and delete all treat any rows-affected value other than `1` as a failure and add an error message instead of throwing; the object's in-memory state (New/Old) only advances on success.
- `clsProviderEOB.AfterUpdate` never increments the in-memory `UpdateNum` on success, unlike the EFT object's equivalent hook; reusing the same in-memory instance for a second update without re-retrieving it will likely fail the `WHERE UPDATE_NUM = :UpdateNum` predicate and surface a false contention error.*
- Because the collection query left-outer-joins every provider location, a provider with no configured EOB preference for a location still yields a row (all EOB columns null) rather than omitting it; `UpdateNum = 0` on that null row is what causes the object to be treated as New ([BR-18](../business-rules.md#br-18-a-retrieved-eob-preference-row-with-updatenum-equal-to-zero-is-treated-as-unsaved-and-marked-new-so-save-falls-through-to-insert-instead-of-update)).
- `clsProviderEOB`'s own `Criteria.RetrieveSQL`/`RetrieveSqlLoc` selection is driven purely by whether the caller passed `ProviderSeqNum = 0`; passing an explicit `0` is the only way to reach the location-only query.

## 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.Provider.PaymentPreference\clsProviderEOB.vb`
- `Business\SAIF.Business.Provider.PaymentPreference\clsProviderEOBCollection.vb`
