---
title: Financial transaction and remark management
summary: Creates, retrieves, updates, and deletes a claim financial transaction together with its detail lines and single pay remark.
status: draft
owner: SAIF Doc Gen
last_reviewed: 2026-07-30
library: SAIF.Business.Claim.Payment
reference_type: embedded
operation_id: OP-CP-01
trigger: A payment/refund/transfer workflow (for example clsPaymentBase.CreateFinancialTransactions) creates a transaction, or a caller retrieves transaction history for a claim
---

# Financial transaction and remark management

A financial transaction is the permanent, append-only ledger entry every payment, refund, and transfer in this library ultimately writes: a header row plus one or more detail lines and an optional remark. Almost every other operation in `SAIF.Business.Claim.Payment` ends by building one of these through `clsPaymentBase`, so this operation documents the shared object that all payee/payment flows delegate to for persistence.

| Field | Value |
| --- | --- |
| **Operation ID** | OP-CP-01 |
| **Trigger** | `clsFinancialTrans.NewFinancialTrans(dpi, claimData [, tranType])` (create), `clsFinancialTrans.GetTransaction(dpi, tranID, tranType)` or one of nine `clsFinancialTransCollection` factories (retrieve) |
| **Inputs** | `IDataPortalInfo`, claim data or claim ID, transaction type code (Payment/Refund/Transfer), status, issue date, benefit sequence number, payment ID, payee/address data, detail amounts/dates/flags, remark code |
| **Outputs** | Persisted `CL_FINANCIAL_TRANS` / `CL_FINANCIAL_TRANS_DTL` / remark rows; populated object graph; boolean save result; broken-rule/error messages |
| **Primary components** | `clsFinancialTrans.Create/SetHistory/ApplyValueChecks/ValidAddress`, `clsFinancialTransDtl`, `clsFinancialTransDtlCollection`, `clsFinancialTransCollection`, `clsFinancialTranRemark` |
| **Entry point** | `Business\SAIF.Business.Claim.Payment\FinancialTransaction\clsFinancialTrans.vb` |

## Steps

1. **Create a new transaction.** `NewFinancialTrans` assigns `ClaimFinancialTransSeqNum` from a sequence, `ClaimID` from the criteria's claim data, and eagerly loads an (initially empty) detail collection and the child remark placeholder. Two `ClaimID`-only overloads are marked obsolete and internally resolve full claim data via `clsClaimData` (`SAIF.Business.View.Claim`, not yet migrated — see [Payee management](payee-management.md) for the related note).
2. **Apply create defaults** ([BR-CP-007](../business-rules.md#br-cp-007-new-financial-transactions-default-to-released-indemnity-current-user-initiated)): status Released, medical/indemnity display code `I`, issue/entry date today, initiator the logged-on user. `SetHistory()` snapshots the claim's jurisdiction, status, type, coverage, injury date, policy, tail-coverage, preferred-worker, federal-benefit, NCE, HWR, and own-motion values onto the transaction row.
3. **Add a detail line.** `InsertTranDetail` creates a `clsFinancialTransDtl`, assigns the transaction type code, and numbers it `TranDetail.Count + 1` — a 1-based sub-number with no gap or uniqueness re-check.
4. **Apply value checks before insert** ([BR-CP-005](../business-rules.md#br-cp-005-reimbursement-program-precedence-on-a-transaction-detail), [BR-CP-006](../business-rules.md#br-cp-006-claim-cost-category-suppressed-for-non-chargeable-or-reimbursable-details)). `ApplyValueChecks` sets each detail's reimbursement program (SD, then Retro Reserve, then NCE, in that precedence) and clears or sets the claim cost category based on chargeability, payee type, and reimbursable-program membership; an uncategorized detail is forced non-chargeable.
5. **Validate the payee address** ([BR-CP-004](../business-rules.md#br-cp-004-payee-address-must-be-valid-before-a-transaction-can-save)). `ValidAddress` runs as a level-one rule on `AddressLine1` unless `IgnoreBusinessRules` is set or the object is an existing record whose address was already valid.
   - Worker/Beneficiary payees require a previously validated address (delegated to `clsPayeeBase`/`clsProviderAddress`, `SAIF.Business.View.Provider` — not yet migrated).
   - Attorney/Other/Medical Provider payees require address line 1, plus city and state unless the address is foreign.
   - On failure: broken rule naming the payee ("Error: Worker/Beneficiary/Provider address is invalid \<PayeeFullName\>").
6. **Insert the header.** `BeforeInsert` normalizes blank special-handling codes and zero zip/provider-location values to NULL, and prefers the parent provider location when the payee's address is a parent location. The header insert itself has no catalogued SQL entry in this library's SQL index; it is described here from the traced stored-procedure call rather than linked.
   - On a rows-affected value other than 1: `"Insert Failed"` error added; child detail and remark are **not** saved.
7. **Insert the detail line** ([SQL-020](../sql/SQL-020.md), [CL_FINANCIAL_TRANS_DTL](../database-tables.md#cl_financial_trans_dtl)). Only runs after the header insert succeeds.
   - On a rows-affected value other than 1: `"Insert Failed"` error added.
8. **Insert the remark, if any.** When `PayRemarkCode` is blank the remark object is marked clean and no request is sent; otherwise it is inserted ([SQL-054](../sql/SQL-054.md) covers the matching retrieve path; the remark insert/update/delete procedures were traced by name but have no distinct catalog entry beyond the retrieve).
9. **Update an existing transaction.** Only `PaymentMethodCode`, `PaymentID`, and `FinancialTransactionStatusCode` may be changed after creation — every other field is insert-only, append-style history.
   - On a rows-affected value other than 1: `DBContentionError` added ([BR-CP-001](../business-rules.md#br-cp-001-optimistic-concurrency-on-all-stored-procedure-based-operations)).
10. **Update or delete the remark.** Clearing `PayRemarkCode` to empty on an existing remark routes the save through the delete path instead of update ([SQL-101](../sql/SQL-101.md) update, [SQL-124](../sql/SQL-124.md) delete).
    - On a rows-affected value other than 1: `DBContentionError` added.
11. **Retrieve a transaction or transaction history.** `GetTransaction` retrieves by sequence number and type; `clsFinancialTransCollection` exposes nine retrieval factories keyed by claim, issue date, benefit sequence, recovery eligibility, status, payment ID, or payment category, each backed by its own stored procedure ([SQL-055](../sql/SQL-055.md), [SQL-056](../sql/SQL-056.md), [SQL-057](../sql/SQL-057.md), [SQL-058](../sql/SQL-058.md), [SQL-059](../sql/SQL-059.md), [SQL-060](../sql/SQL-060.md), [SQL-061](../sql/SQL-061.md), [SQL-062](../sql/SQL-062.md), [SQL-063](../sql/SQL-063.md), [SQL-065](../sql/SQL-065.md), [CL_FINANCIAL_TRANS](../database-tables.md#cl_financial_trans)).
    - If no row matches (single retrieve): the returned object is populated as a placeholder and a runtime-added rule reports "No valid transaction found for Id and Type supplied."
    - For `ByStatus`, `ByStatusAndIssueDate`, and `ByClaim` retrievals, detail lines for the whole result set are eagerly batch-loaded and grouped by transaction; other retrieval types load details lazily per transaction (N+1 pattern).
12. **Bulk status transition.** `updateFinancialTranStatus` moves every matching transaction from one status to another for a claim/issue-date/payment-type in a single stored-procedure call ([SQL-102](../sql/SQL-102.md)) and returns the raw rows-affected count with no `<> 1` check and no legal-transition validation of its own.
13. **Ad hoc aggregate queries.** `HasOverLapAmt` sums detail amounts for a claim/category whose date range overlaps a proposed period; `PreviousPaidAmt`/`PreviousPaidDays` sum prior positive payments or refunds for an exact from/thru match (note: `PreviousPaidDays` must be read only after `PreviousPaidAmt` — an ordering dependency, not a rule); `LatestPaymentDate` finds the latest posted, non-8F, non-penalty transaction thru-date; `GetLastPaymentThruDate` builds its category list by string formatting rather than binding (low risk — values are enum-derived, not user input).
14. **First-payment determination** ([BR-CP-008](../business-rules.md#br-cp-008-first-payment-determined-by-absence-of-a-posted-transaction)). `FirstPayment` returns true only while none of the claim's transactions has reached Posted status.

## Process flow

```mermaid
flowchart TD
	Start([Payment workflow or caller<br>requests a transaction]) --> Which{Create or retrieve?}
	Which -->|Create| New[NewFinancialTrans:<br>seed header, snapshot claim data]
	New --> Detail[Add detail line]
	Detail --> Checks[Apply value checks:<br>reimbursement program,<br>cost category]
	Checks --> AddrRule{Payee address valid?}
	AddrRule -->|No| Blocked([Broken rule added;<br>save blocked])
	AddrRule -->|Yes| InsHeader[Insert header]
	InsHeader --> Rows1{RowsAffected = 1?}
	Rows1 -->|No| Fail1([Insert Failed;<br>children not saved])
	Rows1 -->|Yes| InsDetail[Insert detail line]
	InsDetail --> InsRemark{Remark code blank?}
	InsRemark -->|Yes| SkipRemark[No remark row created]
	InsRemark -->|No| InsRemarkRow[Insert remark row]
	Which -->|Retrieve| Retr[Select factory by<br>claim / status / issue date / etc.]
	Retr --> Found{Row(s) found?}
	Found -->|No| Placeholder([Placeholder object;<br>not-found rule added])
	Found -->|Yes| Loaded([Populated transaction<br>or collection returned])
	SkipRemark --> Done([Save complete])
	InsRemarkRow --> Done
```

## Failure and edge-case behavior

- Detail-line update always throws `ApplicationException(InvalidSQLProperty)` because `clsFinancialTransDtl.Criteria.UpdateSQL` is not implemented — detail lines are effectively insert-only in practice.
- Detail-line delete uses an inline statement with unbound literal placeholders and a mismatched column name (`claim_financial_trans_id` instead of `claim_financial_trans_seq_num`); it is very likely a dead/non-functional path.
- `clsFinancialTransDtlCollection.Remove` and `clsFinancialTransCollection.Remove` are self-recursive — calling either with a matching item causes a stack overflow rather than removing it.
- A missing remark on retrieve is not an error; a clean, empty placeholder remark object is returned instead.
- Static code-map caches (`RecoveryTypeCodes`, `FinancialTransTypeCodes`) are process-wide and only refresh when explicitly requested.
- `GetTransaction` sets `IgnoreBusinessRules = True` on the returned object, which suppresses the address-validity rule but not the runtime-added not-found rule.
- The header insert/update/retrieve stored procedures were traced from source but have no distinct entry in this library's SQL catalog separate from the collection-level entries cited above; they are described in prose rather than linked to avoid citing a non-existent SQL ID.

## 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.Payment\FinancialTransaction\clsFinancialTrans.vb`
- `Business\SAIF.Business.Claim.Payment\FinancialTransaction\clsFinancialTransCollection.vb`
- `Business\SAIF.Business.Claim.Payment\FinancialTransaction\clsFinancialTransDtl.vb`
- `Business\SAIF.Business.Claim.Payment\FinancialTransaction\clsFinancialTransDtlCollection.vb`
- `Business\SAIF.Business.Claim.Payment\FinancialTransaction\clsFinancialTranRemark.vb`
