Skip to content

Bank account management

A clsBankAccount represents one payee's bank-account record — routing number, account number, account type, and status — and implements the IBankAccount contract consumed by SAIF.Business.Payment. Every EFT authorization and payment preference in this library ultimately points at one clsBankAccount. A related, smaller object, clsBankAccountTransfer, links a bank account to a US Bank online-banking session (US_BANK_USER_SEQ_NUM) once the account has been transferred/enrolled at US Bank.

Field Value
Operation ID OP-EFT-01
Trigger Caller calls clsBankAccount.NewBankAccount to start a new account, or GetBankAccount/GetBankAccounts to retrieve one or more existing accounts by ID or create user; edits properties and calls Save()
Inputs Bank routing number, account number, account type/status/data-source codes, business/consumer code, nickname, holder/owner name; for transfer, a US_BANK_USER_SEQ_NUM
Outputs Persisted/updated BANK_ACCOUNT row with encrypted routing and account numbers; soft-deleted (status = DELETED) or, if PurgeRecord is set, hard-deleted row; persisted BANK_ACCOUNT_TRANSFER link row
Primary components clsBankAccount.BeforeCreate/AfterCreate, BeforeInsert/AfterInsert, BeforeRetrieve/AfterRetrieve, BeforeUpdate/AfterUpdate, BeforeDelete/AfterDelete, AddBusinessRules; clsBankAccountCollection; clsBankAccountTransfer
Entry point Business\SAIF.Business.Payment.EFT\clsBankAccount.vb

Steps

  1. Obtain the next bank-account sequence number. NewBankAccount triggers BeforeCreate/AfterCreate, which executes SELECT BANK_ACCOUNT_seq.NEXTVAL FROM DUAL as a scalar request (SQL-001, DUAL) and stores the result as BankAccountID; the create user is stamped from the data-portal info.
  2. Caller populates account fields. Routing number and account number setters encrypt the incoming value via SAIF.Security.clsSecureEncrypt.EncryptString before storing it, and only re-encrypt/mark dirty if the decrypted stored value differs from the new value; account type, status, data-source, business/consumer code, nickname, and owner name are set directly.
  3. Validate before save. IsValid/CheckRules runs the registered business rules:
  4. Required-field rules: non-zero BankAccountID, non-blank account number, bank name, holder name, business/consumer code, data-source code, status code, owner name, create IP address, create user, and a required create date (BR-EFT-05 governs the write side of these fields).
  5. HasBadCharacters: rejects </> characters in the nickname or holder name.
  6. ValidRoutingNumber: decrypts the routing number and requires exactly 9 numeric digits with no separators.
    • On new accounts only (Level Two): IsNewRoutingNumVerifed additionally requires NewRoutingNumVerified = True before the account can be saved — this flag is normally set upstream by Payment preference selection after a successful bank-validator check, or automatically by EFT authorization / consent when a default routing number is applied from the authorization type.
  7. ValidAccountNumber (only when ValidateBankAccountNumberForOldBankAccount applies): decrypts the account number and requires a length of 5–17 characters, numeric unless the account is a debit card, with no separators.
  8. Level Two code-validity rules: ValidAccountDataSouceCode, ValidAccountStatusCode, ValidPaymentReturnReasonCode (only if not blank), ValidAccountTypeCode, ValidConsummerBusnessCode — each defers to the corresponding lookup object's own IsValid (see Code table lookups).
  9. On any failure: the broken-rule collection accumulates messages and Save() proceeds to fail validation; no database call is attempted.
  10. Insert a new account. BeforeInsert/AfterInsert executes INSERT INTO BANK_ACCOUNT (...) with the encrypted routing/account numbers and all descriptive fields (SQL-009, BANK_ACCOUNT).
  11. On failure (RowsAffected <> 1): adds an InsertError message (BR-EFT-05).
  12. Retrieve an existing account. BeforeRetrieve/AfterRetrieve selects the full column set by BankAccountID (SQL-030, BANK_ACCOUNT); a companion collection retrieval selects by CREATE_USER (case-insensitive) (SQL-031).
  13. A commented-out alternate retrieval via stored procedure PK72970_EPAY.retrieve_BankAccount exists in source but is not wired into BeforeRetrieve — dead code.*
  14. Update an existing account. BeforeUpdate/AfterUpdate stamps UpdateDate/UpdateUser and executes a full-column UPDATE BANK_ACCOUNT keyed on BankAccountID (SQL-077, BANK_ACCOUNT); CREATE_DATE/CREATE_IP_ADDR/CREATE_USER are intentionally excluded from the SET clause so audit-creation fields never change.
  15. On failure (RowsAffected <> 1): adds an UpdateError message (BR-EFT-05).
  16. Delete an account. BeforeDelete branches on the PurgeRecord flag (BR-EFT-11):
  17. Default (PurgeRecord = False): issues a soft-delete UPDATE BANK_ACCOUNT SET BANK_ACCOUNT_STATUS_CODE = 'DELETED', UPDATE_DATE = ..., UPDATE_USER = ... (SQL-088, BANK_ACCOUNT) — the row remains for historical linkage to authorizations and payment details (BR-EFT-04).
  18. If PurgeRecord = True: issues a physical DELETE BANK_ACCOUNT WHERE BANK_ACCOUNT_SEQ_NUM = :BankAccountID, permanently removing the row.
  19. On failure (RowsAffected <> 1): adds a DeleteError message.
  20. Link the account to a US Bank session (optional). clsBankAccountTransfer.Create() copies BankAccountSeqNum/USBankUserSeqNum from the criteria (no sequence round-trip — the composite key is supplied by the caller) and inserts into BANK_ACCOUNT_TRANSFER (SQL-010, BANK_ACCOUNT_TRANSFER); retrieval selects by the same composite key (SQL-036), and updates/deletes follow the same pattern (SQL-078, SQL-089).
  21. Return the outcome. Save() calls ApplyEdit(), dispatches to SaveChild() or the base Save() depending on whether the account is a child object, and calls MarkOld() on success; the boolean result reflects ErrorCount = 0.

Process flow

flowchart TD
    Start([Caller creates or retrieves<br>a bank account]) --> New{New account?}
    New -->|Yes| Seq[Fetch BANK_ACCOUNT_SEQ.NEXTVAL]
    New -->|No| Retrieve[Retrieve by BankAccountID<br>or CreateUser]
    Seq --> Edit[Caller sets routing/account<br>numbers, type, status]
    Edit --> Validate{IsValid?}
    Validate -->|No| Fail([Broken rules returned;<br>no database call])
    Validate -->|Yes| Insert[INSERT BANK_ACCOUNT]
    Retrieve --> EditExisting[Caller edits fields]
    EditExisting --> ValidateU{IsValid?}
    ValidateU -->|No| Fail
    ValidateU -->|Yes| Update[UPDATE BANK_ACCOUNT]
    Insert --> Rows{RowsAffected = 1?}
    Update --> Rows
    Rows -->|No| Contention([InsertError / UpdateError added])
    Rows -->|Yes| Done([Save returns ErrorCount = 0])
    Retrieve --> Delete{Delete requested?}
    Delete -->|PurgeRecord = True| Purge[DELETE BANK_ACCOUNT row]
    Delete -->|PurgeRecord = False| Soft[UPDATE status = DELETED]
    Purge --> RowsD{RowsAffected = 1?}
    Soft --> RowsD
    RowsD -->|No| DeleteErr([DeleteError added])
    RowsD -->|Yes| Done

Failure and edge-case behavior

  • A RowsAffected value other than 1 on insert, update, or delete adds an error message but does not raise an exception; the caller must check ErrorCount/IsValid after Save() (BR-EFT-05).
  • Soft-delete is the default and preserves the row (and its historical linkage to authorizations/payment details) with BANK_ACCOUNT_STATUS_CODE = 'DELETED'; only an explicit PurgeRecord = True performs a hard delete (BR-EFT-11).
  • New accounts cannot be saved until NewRoutingNumVerified = True, which is normally set by an upstream bank-validator check (see Payment preference selection) or by the authorization-type cascade in EFT authorization / consent (BR-EFT-09).
  • IsValid/IsDirty overrides on clsBankAccount contain only placeholder comments for child-object validation that was never implemented — no child business objects are actually validated from this class today.*
  • A commented-out alternate retrieval SQL referencing stored procedure PK72970_EPAY.retrieve_BankAccount exists in source but is not reachable from any code path — dead code.*
  • Several duplicate/no-op parameter-binding calls exist in the Insert and Update SQL builders (e.g., UpdateDate/UpdateIPAddr/UpdateUser/BankAccountID added twice); these are redundant but harmless.*

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.Payment.EFT\clsBankAccount.vb
  • Business\SAIF.Business.Payment.EFT\clsBankAccountCollection.vb
  • Business\SAIF.Business.Payment.EFT\clsBankAccountTransfer.vb