---
title: Manage expenses
summary: Creates, validates, and approves third-party recovery expenses against escalating dollar-amount approval tiers.
status: draft
owner: SAIF
last_reviewed: 2026-07-30
library: SAIF.Business.Claim.ThirdParty
reference_type: package
operation_id: OP-TP-03
trigger: clsThrdpartyExpense.NewThrdpartyExpense / GetThrdpartyExpense and clsThrdpartyExpenseApproval.GetThrdpartyExpenseApproval followed by Save()
---

# Manage expenses

Recovery expenses (`clsThrdpartyExpense`) are individual cost entries tied to a case, and the expense-approval record (`clsThrdpartyExpenseApproval`) tracks the approval status for the case's expense total. As the running total of a case's expenses crosses fixed dollar thresholds, the approval object automatically resets the approval code to require the next level of sign-off.

| Field | Value |
| --- | --- |
| **Operation ID** | OP-TP-03 |
| **Trigger** | `clsThrdpartyExpense.NewThrdpartyExpense` / `GetThrdpartyExpense` followed by property edits and `Save()`; `clsThrdpartyExpenseApproval.GetThrdpartyExpenseApproval` followed by an approval-code edit and `Save()` |
| **Inputs** | `ThrdpartyCaseSeqNum`, expense text/type, expense date, expense amount, recovery amount, budget code, expense-approval code |
| **Outputs** | Persisted `THRDPARTY_EXPENSE` rows; persisted expense-approval code on the case; approver user/date stamped when an expense is approved |
| **Primary components** | `clsThrdpartyExpense.AddBusinessRules`, `clsThrdpartyExpense.Save` (inherited), `clsThrdpartyExpenseApproval.SetExpenseApproval`, `clsThrdpartyExpenseApproval.AddBusinessRules` |
| **Entry point** | `Business\SAIF.Business.Claim.ThirdParty\clsThrdpartyExpense.vb` |

## Steps

1. **Create or retrieve an expense.** `Create()` seeds a new expense row against a `ThrdpartyCaseSeqNum`; retrieval reads existing rows by case ([SQL-053](../sql/sql-053-pk82033_thirdparty-thrdparty_expense_select.md)/[SQL-054](../sql/sql-054-pk82033_thirdparty-thrdparty_expense_select.md), [THRDPARTY_EXPENSE](../database-tables.md#thrdparty_expense)).
2. **Validate the expense (`IsValid`), skipped entirely for a deleted expense:**
   - Expense text (type) is required ([BR-018](../business-rules.md#br-018-expense-type-amount-and-expense-date-are-required-and-must-fall-within-the-claims-injury-to-today-window)).
   - Expense amount is required (non-zero) and must not be negative ([BR-018](../business-rules.md#br-018-expense-type-amount-and-expense-date-are-required-and-must-fall-within-the-claims-injury-to-today-window)).
   - Expense date, if entered, must fall between the claim's date of injury and today; the date-of-injury bound is obtained by retrieving the owning case.
     > **Origin**: The date-of-injury bound is read from `clsThirdPartyCase.GetThirdPartyCase`, documented in [Maintain recovery case](maintain-recovery-case.md) (`SAIF.Business.Claim.ThirdParty`).
   - Budget code is required whenever a non-zero recovery amount is entered ([BR-019](../business-rules.md#br-019-budget-code-required-whenever-recovery-amount-entered-recovery-amount-cannot-exceed-expense-amount)).
   - Recovery amount must not be negative and must not exceed the expense amount ([BR-019](../business-rules.md#br-019-budget-code-required-whenever-recovery-amount-entered-recovery-amount-cannot-exceed-expense-amount)).
   - A confidential-claim security check exists as a rule stub but always returns `True`; it has no enforced effect today.*
3. **Persist the expense** through the inherited BOF save lifecycle: insert ([SQL-024](../sql/sql-024-pk82033_thirdparty-thrdparty_expense_insert.md)), update ([SQL-079](../sql/sql-079-pk82033_thirdparty-thrdparty_expense_update.md)), or delete ([SQL-092](../sql/sql-092-pk82033_thirdparty-thrdparty_expense_delete.md)) against [THRDPARTY_EXPENSE](../database-tables.md#thrdparty_expense).
   - On a rows-affected value other than `1`: an `InsertError`/`UpdateError`/`DeleteError` message is added.
4. **Retrieve the expense-approval record (`clsThrdpartyExpenseApproval`)** for the case ([SQL-052](../sql/sql-052-pk82033_thirdparty-thrdparty_expapproval_select.md), [THRDPARTY_EXPENSE](../database-tables.md#thrdparty_expense)); if no code is stored, it defaults to `NOTR` (not required).
5. **Recompute the approval code (`SetExpenseApproval`)** based on the case's running `ExpenseCollection.ExpenseTotal` against two fixed limits:
   - Below the lower limit: the approval code is forced to `NOTR` regardless of its prior value.
   - At or above the lower limit but below the upper limit, and the collection needs approval: transitioning into `APPR` (approved) stamps `ApproverUser`/`ApprovalDate` on any not-yet-approved expenses; transitioning out of `DISA` back toward approved re-checks whether any expense is still unapproved.
6. **Validate the approval code (`IsValid`), evaluated only while the approval object is dirty:**
   - If the total is at or above the lower limit and below the upper limit and the collection needs approval, an approval code of `NOTR` or blank produces a warning that supervisor approval is required ([BR-020](../business-rules.md#br-020-expense-totals-of-5000-or-more-require-supervisor-approval-25000-or-more-require-director-approval)).
   - If the total is at or above the upper limit and the collection needs approval, the same condition produces a warning that director approval is required ([BR-020](../business-rules.md#br-020-expense-totals-of-5000-or-more-require-supervisor-approval-25000-or-more-require-director-approval)).
   - Both checks are warnings, not hard errors, so the expense can still be saved with an unresolved approval code.
7. **Persist the approval code** on the case-scoped approval row ([SQL-078](../sql/sql-078-pk82033_thirdparty-thrdparty_expapproval_update.md), [THRDPARTY_EXPENSE](../database-tables.md#thrdparty_expense)).

## Process flow

The following diagram visualizes expense validation and the escalating approval-tier logic.

```mermaid
flowchart TD
	Start([Caller creates or edits expense]) --> ERules[Validate type, amount,<br>expense date, budget code,<br>recovery amount]
	ERules --> EOk{Valid?}
	EOk -->|No| Fail([Broken rules returned])
	EOk -->|Yes| ESave[Insert, update, or delete<br>THRDPARTY_EXPENSE]
	ESave --> Rows{RowsAffected = 1?}
	Rows -->|No| Contention([Insert/Update/Delete error added])
	Rows -->|Yes| Recompute[Recompute expense total<br>for the case]
	Recompute --> Tier{Total vs. thresholds}
	Tier -->|"< $5,000"| NotReq[Approval code forced to NOTR]
	Tier -->|"$5,000-$24,999"| Lower{Approval code<br>NOTR or blank?}
	Tier -->|">= $25,000"| Upper{Approval code<br>NOTR or blank?}
	Lower -->|Yes| WarnLower([Warning: supervisor<br>approval required])
	Lower -->|No| ApprSave[Persist approval code]
	Upper -->|Yes| WarnUpper([Warning: director<br>approval required])
	Upper -->|No| ApprSave
	NotReq --> ApprSave
	WarnLower --> ApprSave
	WarnUpper --> ApprSave
	ApprSave --> Done([Approval record saved])
```

## Failure and edge-case behavior

- The $5,000/$25,000 approval-tier checks are warnings, so an expense total can cross a threshold and still be saved without a resolved approval code; enforcement relies on UI-level review of the warning.*
- The confidential-claim security rule for expenses is present in code as a permanently-`True` stub; it does not currently block access to expenses on confidential claims.*
- The budget-code requirement only applies when a recovery amount is entered — an expense with no recovery amount can be saved without a budget code.
- `ValidExpenseDate` retrieves the owning case on every validation pass to obtain the date-of-injury bound, adding a database round trip per validation cycle.*

> **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.Claim.ThirdParty\clsThrdpartyExpense.vb`
- `Business\SAIF.Business.Claim.ThirdParty\clsThrdpartyExpenseApproval.vb`
- `Business\SAIF.Business.Claim.ThirdParty\clsThrdpartyExpenseCollection.vb`
- `Business\SAIF.Business.Claim.ThirdParty\clsThirdPartyConstants.vb`
