---
title: EFT record management
summary: Retrieves, creates, updates, and deletes a provider location's EFT (direct-deposit) configuration record.
status: draft
owner: SAIF Doc Gen
last_reviewed: 2026-07-30
library: SAIF.Business.Provider.PaymentPreference
reference_type: package
operation_id: OP-PP-02
trigger: Caller retrieves a clsProviderEFT for a provider location, edits it, and calls Save()
---

# EFT record management

`clsProviderEFT` represents one provider location's `PROVIDER_EFT_INTERFACE` row: which external user administers the location's electronic funds transfer preference, when that administration window is effective, and whether the location defaults to its primary payment method. Retrieval, creation, update, and deletion of this record share the same optimistic-concurrency behavior, so they are documented as a single operation; assigning or unassigning the underlying payment preference itself is a separate, related operation.

| Field | Value |
| --- | --- |
| **Operation ID** | OP-PP-02 |
| **Trigger** | Caller invokes `GetProviderEFT`/`NewProviderEFT`, edits the returned object, and calls `Save()` |
| **Inputs** | `ProviderSeqNum`, `ProviderLocationSeqNum`, `ExtUserTypeSeqNum`, `EffDate`, `ExpDate`, `defaultToPrimaryFlag` |
| **Outputs** | Populated `clsProviderEFT`; persisted `PROVIDER_EFT_INTERFACE` row on save; `InsertError`/`DBContentionError` on save failure |
| **Primary components** | `clsProviderEFT.GetProviderEFT`, `clsProviderEFT.NewProviderEFT`, `clsProviderEFT.BeforeRetrieve`/`AfterRetrieve`, `clsProviderEFT.BeforeInsert`/`AfterInsert`, `clsProviderEFT.BeforeUpdate`/`AfterUpdate`, `clsProviderEFT.BeforeDelete`/`AfterDelete`, `clsProviderEFT.SetExtUserTypeSeqNum` |
| **Entry point** | `Business\SAIF.Business.Provider.PaymentPreference\clsProviderEFT.vb` |

## Steps

1. **Retrieve.** `GetProviderEFT(dpi, providerSeqNum, providerLocationSeqNum)` builds a `Criteria` and fetches the row by both keys, joined to the external-user/person chain for display ([SQL-008](../sql/SQL-008-select-provider-eft-interface-by-provider-location.md), [PROVIDER_EFT_INTERFACE](../database-tables.md#provider_eft_interface), [EXT_USER_TYPE](../database-tables.md#ext_user_type), [EXT_USER](../database-tables.md#ext_user), [PERSON](../database-tables.md#person)).
   - `AfterRetrieve` fills the first row and always calls `MarkOld()` — unlike `clsProviderEOB`, there is no `UpdateNum = 0` New/Old determination here; a retrieved object is always considered persisted.*
2. **Create.** `NewProviderEFT(dpi, providerSeqNum, providerLocSeqNum)` constructs a new object; `Create()` seeds the keys, update user, and update date from the criteria and data-portal info.
3. **Insert.** `BeforeInsert` sets `UpdateDate`/`UpdateUser`/`UpdateNum = 1`, and if `ExtUserTypeSeqNum` is empty calls `SetExtUserTypeSeqNum()` to derive it from context ([BR-11](../business-rules.md#br-11-extusertypeseqnum-is-cleared-set-empty-when-the-eft-is-being-expired-expdate-minvalue-and-no-explicit-new-value-supplied-or-when-no-admin-user-is-present-it-is-set-from-the-incoming-invalue-when-a-new-eft-is-being-opened-and-from-adminuserseqnum-otherwise)).
   - `AfterInsert` executes the insert ([SQL-003](../sql/SQL-003-insert-provider-eft-interface.md), [PROVIDER_EFT_INTERFACE](../database-tables.md#provider_eft_interface)) and checks the rows-affected count.
     - On a rows-affected value other than `1`: add `InsertError` ("Insert Failed") ([BR-12](../business-rules.md#br-12-eft-insert-contention-rowsaffected-1-triggers-inserterror-child-payment-preferences-are-saved-only-after-a-successful-insert-savechildobject-called-in-afterinsert)).
     - On success: save the child payment-preference object(s) held in `m_StartingProviderPaymentPreference`/`m_NewProviderPaymentPreference` (see [EFT payment location assignment](eft-payment-location-assignment.md)), then `MarkOld()` ([BR-12](../business-rules.md#br-12-eft-insert-contention-rowsaffected-1-triggers-inserterror-child-payment-preferences-are-saved-only-after-a-successful-insert-savechildobject-called-in-afterinsert)).
4. **Update.** `BeforeUpdate` refreshes `UpdateDate`/`UpdateUser`, re-derives `ExtUserTypeSeqNum` via `SetExtUserTypeSeqNum()` when it is empty or zero ([BR-11](../business-rules.md#br-11-extusertypeseqnum-is-cleared-set-empty-when-the-eft-is-being-expired-expdate-minvalue-and-no-explicit-new-value-supplied-or-when-no-admin-user-is-present-it-is-set-from-the-incoming-invalue-when-a-new-eft-is-being-opened-and-from-adminuserseqnum-otherwise)), and defaults `EffDate` to today if it is still `Date.MinValue` ([BR-16](../business-rules.md#br-16-if-effdate-is-dateminvalue-at-update-time-it-is-defaulted-to-todaydate-before-persisting)).
   - `AfterUpdate` executes the update ([SQL-014](../sql/SQL-014-update-provider-eft-interface.md), [PROVIDER_EFT_INTERFACE](../database-tables.md#provider_eft_interface)) and checks the rows-affected count.
     - On a rows-affected value other than `1`: add `DBContentionError` ([BR-13](../business-rules.md#br-13-eft-update-contention-rowsaffected-1-triggers-dbcontentionerror-updatenum-is-incremented-and-child-preferences-saved-only-on-success)).
     - On success: increment the in-memory `UpdateNum`, save the child payment-preference object(s), then `MarkOld()` ([BR-13](../business-rules.md#br-13-eft-update-contention-rowsaffected-1-triggers-dbcontentionerror-updatenum-is-incremented-and-child-preferences-saved-only-on-success)).
5. **Delete.** `BeforeDelete` binds the provider/location keys; `AfterDelete` executes the delete ([SQL-016](../sql/SQL-016-delete-provider-eft-interface.md), [PROVIDER_EFT_INTERFACE](../database-tables.md#provider_eft_interface)).
   - On a rows-affected value other than `1`: add `DBContentionError`.
   - On success: `MarkNew()`.
6. **Save wrapper.** `Save()` calls `ApplyEdit()` before delegating to the BOF base save (which routes to steps 3–5 depending on object state), and returns `ErrorCount = 0`.

## Process flow

```mermaid
flowchart TD
	Start([Caller requests EFT record]) --> Retrieve[Retrieve by provider + location]
	Retrieve --> MarkOld1[MarkOld regardless of UpdateNum]
	Start2([Caller creates new EFT]) --> Create[Create: seed keys, update user, update date]
	Create --> Edit[Caller edits fields<br>and optionally assigns payment preference]
	MarkOld1 --> Edit
	Edit --> State{Object state?}
	State -->|New| Insert[Insert: UpdateNum = 1,<br>derive ExtUserTypeSeqNum]
	State -->|Modified| Update[Update: derive ExtUserTypeSeqNum,<br>default EffDate to today]
	State -->|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| SaveChild1[Save child payment preference]
	Rows2 -->|No| ErrUpd([DBContentionError added])
	Rows2 -->|Yes| IncNum[Increment in-memory UpdateNum]
	IncNum --> SaveChild2[Save child payment preference]
	Rows3 -->|No| ErrDel([DBContentionError added])
	Rows3 -->|Yes| MarkNew([MarkNew])
	SaveChild1 --> Old([MarkOld])
	SaveChild2 --> Old
```

## Failure and edge-case behavior

- Insert and update both treat any rows-affected value other than `1` as a failure, adding an error instead of throwing; the child payment-preference object is only saved after the parent EFT row itself persists successfully.
- `AfterRetrieve` unconditionally marks the object Old, so a `GetProviderEFT` call for a provider location that has never had a row returns an object with all-default field values that still behaves as "existing" until the caller inspects the data — there is no analogous New/Old signal to the one `clsProviderEOB` derives from `UpdateNum`.*
- `SetExtUserTypeSeqNum` is history-sensitive: it clears the value when the record is being expired or no admin user is available, but sets it from an explicit override or the ambient admin user otherwise ([BR-11](../business-rules.md#br-11-extusertypeseqnum-is-cleared-set-empty-when-the-eft-is-being-expired-expdate-minvalue-and-no-explicit-new-value-supplied-or-when-no-admin-user-is-present-it-is-set-from-the-incoming-invalue-when-a-new-eft-is-being-opened-and-from-adminuserseqnum-otherwise)); the same method also runs implicitly whenever [EFT payment location assignment](eft-payment-location-assignment.md) sets `ExtUserTypeSeqNum`.
- If `EffDate` is left at `Date.MinValue` going into an update, it is silently defaulted to today rather than surfaced as a validation error ([BR-16](../business-rules.md#br-16-if-effdate-is-dateminvalue-at-update-time-it-is-defaulted-to-todaydate-before-persisting)).

## 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\clsProviderEFT.vb`
