---
title: Persist and retrieve SAIF-side integration records
summary: BOF create, retrieve, save, and delete logic for the local US_BANK_USER and US_BANK_NONPAY_NOTICE tables that correlate SAIF policies and users with US Bank identifiers.
status: draft
owner: SAIF
last_reviewed: 2026-07-30
library: SAIF.Business.EPay.USBank
reference_type: package
operation_id: OP-EPAY-07
trigger: BOF Create/Retrieve/Save/Delete calls against clsUSBankUser(Collection) and clsUSBankNonpayNotice(Collection)
---

# Persist and retrieve SAIF-side integration records

This operation covers the BOF persistence classes that give the rest of the library (and its callers) a local, SAIF-side view of US Bank integration state: `clsUSBankUser`/`clsUSBankUserCollection` track which policy/business-online-user pairs have a US Bank profile, and `clsUSBankNonpayNotice`/`clsUSBankNonpayNoticeCollection` record nonpayment notices received from US Bank. Both business objects follow the standard SAIF BOF template — `Create`/`BeforeRetrieve`/`AfterRetrieve`/`BeforeInsert`/`AfterInsert`/`BeforeUpdate`/`AfterUpdate`/`BeforeDelete`/`AfterDelete` — with exact-row-count checks guarding every write.

| Field | Value |
| --- | --- |
| **Operation ID** | OP-EPAY-07 |
| **Trigger** | BOF factory calls (`NewUSBankUser`, `GetUSBankUser`, `GetUSBankUsers`) and subsequent `Save()`/collection `Save()` calls |
| **Inputs** | Policy number, business-online user ID, US Bank user sequence number (user path); policy number, US Bank user sequence number, online payment type code, banking network code, payment amount, notice date, event description (notice path) |
| **Outputs** | Populated single objects or collections; inserted, updated, or deleted rows; `AddErrorMessage` broken-rule entries on row-count mismatches |
| **Primary components** | `clsUSBankUser` (`Create`, `BeforeRetrieve`, `AfterRetrieve`, `BeforeInsert`, `AfterInsert`, `BeforeUpdate`, `AfterUpdate`, `BeforeDelete`, `AfterDelete`), `clsUSBankUserCollection` (`BeforeRetrieve`, `AfterRetrieve`, `Save`), `clsUSBankNonpayNotice` (same lifecycle methods), `clsUSBankNonpayNoticeCollection` |
| **Entry point** | `Business\SAIF.Business.EPay.USBank\clsUSBankUser.vb` |

## Steps

1. **Create a new user.** `clsUSBankUser.NewUSBankUser(dpi, policyNumber, businessOnlineUserId)` builds a criteria and calls the data-access layer's `CreateObject`, which invokes `Create()`: allocate a new sequence value ([SQL-002](../sql/SQL-002-select-us-bank-user-sequence.md), [DUAL](../database-tables.md#dual)) and seed the policy number and business-online user ID from the criteria.
2. **Retrieve a user by sequence number or by policy + business-online user.** `GetUSBankUser` builds the appropriate criteria and calls `RetrieveObject`. `BeforeRetrieve` selects the retrieve SQL variant based on which constructor was used — by `US_BANK_USER_SEQ_NUM` ([SQL-007](../sql/SQL-007-retrieve-us-bank-user-by-sequence.md)) or by policy number plus a case-insensitive comparison on business-online user ([SQL-008](../sql/SQL-008-retrieve-us-bank-user-by-policy-and-business-online-user.md)) ([BR-009](../business-rules.md#br-009-user-lookup-is-case-insensitive)) — against [US_BANK_USER](../database-tables.md#us_bank_user). `AfterRetrieve` fills the object from the first result row, if any, and marks it old.
3. **Retrieve a collection of users by policy.** `clsUSBankUserCollection.GetUSBankUsers(dpi, policyNumber)` runs the base retrieve SQL filtered by policy number ([SQL-009](../sql/SQL-009-retrieve-us-bank-users-by-policy.md), [US_BANK_USER](../database-tables.md#us_bank_user)) and, for each returned row, loads a `clsUSBankUser` and adds it to the collection.
4. **Insert a new user.** `BeforeInsert` binds first name, last name, transfer-bank-accounts flag, policy number, business-online user, and the sequence number ([SQL-004](../sql/SQL-004-insert-us-bank-user.md)) ([BR-001](../business-rules.md#br-001-business-online-user-id-is-required), [BR-002](../business-rules.md#br-002-policy-number-is-required)).
   - `AfterInsert`: if rows affected ≠ 1, add `InsertError` ([BR-003](../business-rules.md#br-003-user-inserts-must-affect-exactly-one-row)); otherwise mark the object old.
5. **Update an existing user.** `BeforeUpdate` binds first name, last name, transfer-bank-accounts flag, and the sequence number as the key ([SQL-011](../sql/SQL-011-update-us-bank-user.md)).
   - `AfterUpdate`: if rows affected ≠ 1, add `UpdateError`/DB contention message ([BR-004](../business-rules.md#br-004-user-updates-must-affect-exactly-one-row)); otherwise mark the object old.
6. **Delete a user.** `BeforeDelete` binds the sequence number as the key ([SQL-013](../sql/SQL-013-delete-us-bank-user.md)).
   - `AfterDelete`: if rows affected ≠ 1, add `DeleteError`/DB contention message ([BR-005](../business-rules.md#br-005-user-deletes-must-affect-exactly-one-row)); otherwise mark the object new.
7. **Create a new notice.** The notice `Create()` allocates a new sequence value ([SQL-001](../sql/SQL-001-select-us-bank-nonpay-notice-sequence.md), [DUAL](../database-tables.md#dual)).
8. **Retrieve a notice by sequence, or notices by policy.** Single-notice retrieve binds the notice sequence number ([SQL-005](../sql/SQL-005-retrieve-us-bank-nonpay-notice-by-sequence.md), [US_BANK_NONPAY_NOTICE](../database-tables.md#us_bank_nonpay_notice)); the collection retrieve filters by policy number ([SQL-006](../sql/SQL-006-retrieve-us-bank-nonpay-notices-by-policy.md), [US_BANK_NONPAY_NOTICE](../database-tables.md#us_bank_nonpay_notice)).
9. **Insert a new notice.** `BeforeInsert` binds the notice sequence number, US Bank user sequence number, policy number, online payment type code, payment amount, notice date, event description, and banking network code ([SQL-003](../sql/SQL-003-insert-us-bank-nonpay-notice.md)).
   - `AfterInsert`: if rows affected ≠ 1, add `InsertError` ([BR-006](../business-rules.md#br-006-notice-inserts-must-affect-exactly-one-row)); otherwise mark the object old.
10. **Update an existing notice.** `BeforeUpdate` binds the same fields as insert, keyed by the notice sequence number ([SQL-010](../sql/SQL-010-update-us-bank-nonpay-notice.md)).
    - `AfterUpdate`: if rows affected ≠ 1, add `UpdateError`/DB contention message ([BR-007](../business-rules.md#br-007-notice-updates-must-affect-exactly-one-row)); otherwise mark the object old.
11. **Delete a notice.** `BeforeDelete` binds the notice sequence number as the key ([SQL-012](../sql/SQL-012-delete-us-bank-nonpay-notice.md)).
    - `AfterDelete`: if rows affected ≠ 1, add `DeleteError`/DB contention message ([BR-008](../business-rules.md#br-008-notice-deletes-must-affect-exactly-one-row)); otherwise mark the object new.
12. **Save a user collection.** `clsUSBankUserCollection.Save()` calls `ApplyEdit()` and then the base collection save, which cascades insert/update/delete for each contained `clsUSBankUser` as described in steps 4–6.

## Process flow

The following diagram visualizes the shared create/retrieve/save/delete pattern applied to both the user and notice business objects.

```mermaid
flowchart TD
	Start{Which BOF action?}
	Start -->|Create| Seq[Allocate sequence value<br>from DUAL]
	Seq --> New([New in-memory object,<br>marked dirty/new])
	Start -->|Retrieve single| RetSingle[Select by sequence number<br>or by policy + business-online user]
	RetSingle --> Fill[Fill object; MarkOld]
	Start -->|Retrieve collection| RetColl[Select rows by policy number]
	RetColl --> Loop[Load one business object<br>per row; add to collection]
	Start -->|Insert| Ins[Bind parameters; execute insert]
	Ins --> InsRows{Rows affected = 1?}
	InsRows -->|No| InsErr([Add InsertError])
	InsRows -->|Yes| InsOk([MarkOld])
	Start -->|Update| Upd[Bind parameters; execute update]
	Upd --> UpdRows{Rows affected = 1?}
	UpdRows -->|No| UpdErr([Add UpdateError /<br>DB contention])
	UpdRows -->|Yes| UpdOk([MarkOld])
	Start -->|Delete| Del[Bind key; execute delete]
	Del --> DelRows{Rows affected = 1?}
	DelRows -->|No| DelErr([Add DeleteError /<br>DB contention])
	DelRows -->|Yes| DelOk([MarkNew])
```

## Failure and edge-case behavior

- Every insert, update, and delete path checks the exact rows-affected count and adds a broken-rule error message rather than throwing, so callers must inspect the object's error/broken-rule state after `Save()` to detect a contention or mismatch failure.
- User lookup by policy and business-online user ID is case-insensitive on both sides of the comparison ([BR-009](../business-rules.md#br-009-user-lookup-is-case-insensitive)), so two IDs differing only in case resolve to the same local row.
- The user criteria's `RetrieveMethod` selection falls through to the sequence-number SQL variant for the `RetrieveMethods.None` case textually in source, but the code path that sets `RetrieveMethod` to `None` and still proceeds to build SQL throws `ApplicationException("Invalid RetrieveMethod.")` first, so the fallthrough return statement is effectively unreachable.*
- The collection classes' `Create()` methods are empty overrides — there is no supported "create a blank collection" factory; collections are always populated through a retrieve.
- `clsUSBankUserCollection.Save()` calls `ApplyEdit()` before the base save, committing any pending edit on the currently-edited item before the cascade runs.*

> **Note:** Statements marked with `*` were inferred from code structure, naming
> conventions, or contextual evidence where direct confirmation was not available
> in source artifacts.

## Related documentation

- [Operations and processes](../operations-processes.md)
- [Business rules](../business-rules.md)
- [SQL catalog](../sql/index.md)
- [Database tables](../database-tables.md)

## Sources

- `Business\SAIF.Business.EPay.USBank\clsUSBankUser.vb`
- `Business\SAIF.Business.EPay.USBank\clsUSBankUserCollection.vb`
- `Business\SAIF.Business.EPay.USBank\clsUSBankNonpayNotice.vb`
