---
title: SAIF.Business.Tasks business rules
summary: Enumerated business rules enforced by the SAIF.Business.Tasks library.
status: draft
owner: SAIF
last_reviewed: 2026-07-30
library: SAIF.Business.Tasks
reference_type: package
---

# SAIF.Business.Tasks business rules

This page captures business rules enforced by this library.

## BR-001: Valid source and category combination

| Field | Value |
| --- | --- |
| **Statement** | A task source code and category code must form a valid combination defined in `TASK_CATEGORY_CODE`. |
| **Trigger** | BOF rule checking evaluates the task source/category pair. |
| **Enforcement** | `clsTaskBusinessRules.ValidSourceCategory()` executes `SELECT Count(*) FROM TASK_CATEGORY_CODE WHERE TASK_SOURCE_CODE = :Source AND CODE = :Category` and gives the concrete subclass a final override hook through `clsTask.OverrideRules("TASK1", ...)`. |
| **Outcome** | Invalid combinations raise the library error message unless the concrete subclass suppresses the rule. |
| **Confidence** | Verified |
| **Tables** | [TASK_CATEGORY_CODE](database-tables.md#task_category_code) |
| **SQL** | [SQL-005](sql/SQL-005-task-category-code-count.md) |
| **Source** | `Business\SAIF.Business.Tasks\clsTaskBusinessRules.vb` |

---

## BR-002: Task date must be current or future

| Field | Value |
| --- | --- |
| **Statement** | A task due date must be today or a future date. |
| **Trigger** | A dirty, non-deleted task changes `TaskDate` and BOF rules are enabled. |
| **Enforcement** | `clsTaskBusinessRules.CheckDate()` delegates to `CommonBusinessRules.IsTodayOrFutureDate()` unless batch rules are active, the task date was not modified, or the value is `Date.MinValue`. |
| **Outcome** | Past-dated tasks receive the task-date validation error unless the concrete subclass overrides the result. |
| **Confidence** | Verified |
| **SQL** | [SQL-001](sql/SQL-001-task-insert.md), [SQL-013](sql/SQL-013-task-update.md) |
| **Source** | `Business\SAIF.Business.Tasks\clsTaskBusinessRules.vb` |

---

## BR-003: Due date adjusted to a workday warning

| Field | Value |
| --- | --- |
| **Statement** | When the library adjusts a due date to the next or prior workday, it emits a warning instead of silently leaving the date unchanged. |
| **Trigger** | `TaskDateModified` becomes `True` after `TaskDateModifier` shifts a non-working day. |
| **Enforcement** | `clsTaskBusinessRules.CheckDateModified()` selects the next-workday or prior-workday warning message from the modifier state and passes the result through `OverrideRules("TASK4", ...)`. |
| **Outcome** | Callers receive a warning that the due date was moved to a workday boundary. |
| **Confidence** | Verified |
| **Source** | `Business\SAIF.Business.Tasks\clsTaskBusinessRules.vb` |

---

## BR-004: Task category code is required

| Field | Value |
| --- | --- |
| **Statement** | Every task must have a category code. |
| **Trigger** | The task is validated through `AddBusinessRules()`. |
| **Enforcement** | `clsTask.AddBusinessRules()` registers `CommonBusinessRules.TextRequired` for `CategoryCode`. |
| **Outcome** | Validation fails with the category-code-required message when the property is blank. |
| **Confidence** | Verified |
| **Source** | `Business\SAIF.Business.Tasks\clsTask.vb` |

---

## BR-005: Source system code is required

| Field | Value |
| --- | --- |
| **Statement** | Every task must have a source system code. |
| **Trigger** | The task is validated through `AddBusinessRules()`. |
| **Enforcement** | `clsTask.AddBusinessRules()` registers `CommonBusinessRules.TextRequired` for `SourceCode`. |
| **Outcome** | Validation fails with the source-system-code-required message when the property is blank. |
| **Confidence** | Verified |
| **Source** | `Business\SAIF.Business.Tasks\clsTask.vb` |

---

## BR-006: Priority code is required

| Field | Value |
| --- | --- |
| **Statement** | Every task must have a priority code. |
| **Trigger** | The task is validated through `AddBusinessRules()`. |
| **Enforcement** | `clsTask.AddBusinessRules()` registers `CommonBusinessRules.TextRequired` for `PriorityCode`. |
| **Outcome** | Validation fails with the priority-code-required message when the property is blank. |
| **Confidence** | Verified |
| **Source** | `Business\SAIF.Business.Tasks\clsTask.vb` |

---

## BR-007: Mail ID is required

| Field | Value |
| --- | --- |
| **Statement** | Every task must have a target mail ID. |
| **Trigger** | The task is validated through `AddBusinessRules()`. |
| **Enforcement** | `clsTask.AddBusinessRules()` registers `CommonBusinessRules.TextRequired` for `MailID`. |
| **Outcome** | Validation fails with the mail-ID-required message when the property is blank. |
| **Confidence** | Verified |
| **Source** | `Business\SAIF.Business.Tasks\clsTask.vb` |

---

## BR-008: Task insert must affect one row

| Field | Value |
| --- | --- |
| **Statement** | A task insert succeeds only when the Oracle function reports one affected row. |
| **Trigger** | `AfterInsert` evaluates the return value from `pk82036_task.task_insert`. |
| **Enforcement** | `AfterInsert` checks `RowsAffected <> 1` and records `Task Insert Failed` when the expected row count is not returned. |
| **Outcome** | Insert failures surface immediately through BOF error messaging. |
| **Confidence** | Verified |
| **Tables** | [TASK](database-tables.md#task) |
| **SQL** | [SQL-001](sql/SQL-001-task-insert.md) |
| **Source** | `Business\SAIF.Business.Tasks\clsTask.vb` |

---

## BR-009: Task update requires a current row version

| Field | Value |
| --- | --- |
| **Statement** | A task update must present the current optimistic-concurrency token. |
| **Trigger** | `AfterUpdate` evaluates the row count returned from `pk82036_task.task_update`. |
| **Enforcement** | The Oracle function matches `TASK_SEQ_NUM` and `UPDATE_SEQ_NUM`; `AfterUpdate` treats any row count other than one as a database-contention error. |
| **Outcome** | Stale updates are rejected and do not advance the task row. |
| **Confidence** | Verified |
| **Tables** | [TASK](database-tables.md#task) |
| **SQL** | [SQL-013](sql/SQL-013-task-update.md) |
| **Source** | `Business\SAIF.Business.Tasks\clsTask.vb` |

---

## BR-010: Task delete requires a current row version

| Field | Value |
| --- | --- |
| **Statement** | A soft delete must present the current optimistic-concurrency token. |
| **Trigger** | `AfterDelete` evaluates the row count returned from `pk82036_task.task_delete`. |
| **Enforcement** | The Oracle function matches `TASK_SEQ_NUM` and `UPDATE_SEQ_NUM`; `AfterDelete` records a BOF database-contention error when the function does not affect exactly one row. |
| **Outcome** | Stale delete attempts do not mark the row deleted. |
| **Confidence** | Verified |
| **Tables** | [TASK](database-tables.md#task) |
| **SQL** | [SQL-014](sql/SQL-014-task-delete.md) |
| **Source** | `Business\SAIF.Business.Tasks\clsTask.vb` |

---

## BR-011: Category notepad requirement is surfaced to callers

| Field | Value |
| --- | --- |
| **Statement** | A category row with `NOTEPAD_REQUIRED_FLAG = 'Y'` causes the category business object to expose a notepad-required result to consuming code. |
| **Trigger** | A category code row is loaded for task-category lookups. |
| **Enforcement** | `clsTaskCategoryCode.IsNotePadRequired` reflects the database flag and supports the `RequiresNotePadEntry` contract on concrete task types. |
| **Outcome** | Consuming applications can require notepad entry when the selected category is configured that way. |
| **Confidence** | Verified |
| **Tables** | [TASK_CATEGORY_CODE](database-tables.md#task_category_code) |
| **SQL** | [SQL-006](sql/SQL-006-task-category-code-by-source-and-code.md), [SQL-007](sql/SQL-007-task-category-code-list-by-source.md) |
| **Source** | `Business\SAIF.Business.Tasks\clsTaskCategoryCode.vb` |

---

## BR-012: Single-task retrieval is keyed only by task sequence number

| Field | Value |
| --- | --- |
| **Statement** | Single-task retrieval is keyed only by task sequence number. |
| **Trigger** | `pk82036_task.task_select` receives `taskseqnum` and opens the cursor. |
| **Enforcement** | The procedure filters `TASK` rows with `WHERE task.task_seq_num = taskseqnum`. |
| **Outcome** | The REF CURSOR returns at most the row for the requested `TASK_SEQ_NUM`. |
| **Confidence** | Verified |
| **Source** | `Business\SAIF.Business.Tasks\clsTask.vb` |
| **Stored procedure** | [pk82036_task.task_select](procedures/pk82036_task.md#task_select) |

---

## BR-013: Insert normalizes zero claim IDs to null

| Field | Value |
| --- | --- |
| **Statement** | Zero claim IDs are normalized to `NULL` during insert. |
| **Trigger** | `pk82036_task.task_insert` builds the `INSERT INTO TASK` values list. |
| **Enforcement** | The function inserts `decode(claimid, 0, null,claimid)` into `CLAIM_ID`. |
| **Outcome** | The `TASK` row does not persist `0` as a claim identifier. |
| **Confidence** | Verified |
| **Tables** | [TASK](database-tables.md#task) |
| **SQL** | [SQL-001](sql/SQL-001-task-insert.md) |
| **Source** | `Business\SAIF.Business.Tasks\clsTask.vb` |
| **Stored procedure** | [pk82036_task.task_insert](procedures/pk82036_task.md#task_insert) |

---

## BR-014: Insert normalizes zero policy numbers to null

| Field | Value |
| --- | --- |
| **Statement** | Zero policy numbers are normalized to `NULL` during insert. |
| **Trigger** | `pk82036_task.task_insert` builds the `INSERT INTO TASK` values list. |
| **Enforcement** | The function inserts `decode(policynum, 0, null,policynum)` into `POLICY_NUM`. |
| **Outcome** | The `TASK` row does not persist `0` as a policy number. |
| **Confidence** | Verified |
| **Tables** | [TASK](database-tables.md#task) |
| **SQL** | [SQL-001](sql/SQL-001-task-insert.md) |
| **Source** | `Business\SAIF.Business.Tasks\clsTask.vb` |
| **Stored procedure** | [pk82036_task.task_insert](procedures/pk82036_task.md#task_insert) |

---

## BR-015: New tasks start active and versioned from zero

| Field | Value |
| --- | --- |
| **Statement** | New tasks start active and versioned from zero. |
| **Trigger** | `pk82036_task.task_insert` writes the initial row. |
| **Enforcement** | The function sets `DELETE_FLAG = 'N'`, `UPDATE_SEQ_NUM = 0`, `CREATE_DATE = SYSDATE`, `UPDATE_DATE = SYSDATE`, and `TASK_SEQ_NUM = TASK_SEQ.NEXTVAL`. |
| **Outcome** | New `TASK` rows start with an active delete state, seeded audit timestamps, and an initial concurrency token. |
| **Confidence** | Verified |
| **Tables** | [TASK](database-tables.md#task) |
| **SQL** | [SQL-001](sql/SQL-001-task-insert.md) |
| **Source** | `Business\SAIF.Business.Tasks\clsTask.vb` |
| **Stored procedure** | [pk82036_task.task_insert](procedures/pk82036_task.md#task_insert) |

---

## BR-016: Task update requires a matching optimistic-concurrency token

| Field | Value |
| --- | --- |
| **Statement** | Updates require a matching optimistic-concurrency token. |
| **Trigger** | `pk82036_task.task_update` executes the `UPDATE TASK` statement. |
| **Enforcement** | The function filters rows with `WHERE task_seq_num = taskseqnum AND update_seq_num = updateseqnum`. |
| **Outcome** | Stale updates leave `SQL%ROWCOUNT = 0` and are rejected by the caller. |
| **Confidence** | Verified |
| **Tables** | [TASK](database-tables.md#task) |
| **SQL** | [SQL-013](sql/SQL-013-task-update.md) |
| **Source** | `Business\SAIF.Business.Tasks\clsTask.vb` |
| **Stored procedure** | [pk82036_task.task_update](procedures/pk82036_task.md#task_update) |

---

## BR-017: Update normalizes zero claim and policy values to null

| Field | Value |
| --- | --- |
| **Statement** | Zero claim and policy values are normalized to `NULL` during update. |
| **Trigger** | `pk82036_task.task_update` sets the mutable `TASK` columns. |
| **Enforcement** | The function updates `CLAIM_ID` with `decode(claimid, 0, null,claimid)` and `POLICY_NUM` with `decode(policynum, 0, null,policynum)`. |
| **Outcome** | Optional claim and policy relationships remain null-valued instead of persisting `0`. |
| **Confidence** | Verified |
| **Tables** | [TASK](database-tables.md#task) |
| **SQL** | [SQL-013](sql/SQL-013-task-update.md) |
| **Source** | `Business\SAIF.Business.Tasks\clsTask.vb` |
| **Stored procedure** | [pk82036_task.task_update](procedures/pk82036_task.md#task_update) |

---

## BR-018: Successful updates always advance audit and version fields

| Field | Value |
| --- | --- |
| **Statement** | Successful updates always advance audit and version fields. |
| **Trigger** | `pk82036_task.task_update` applies the row update. |
| **Enforcement** | The function sets `UPDATE_DATE = SYSDATE`, `UPDATE_SEQ_NUM = updateseqnum + 1`, and `UPDATE_USER = updateuser`. |
| **Outcome** | The row records the latest updater and increments the optimistic-concurrency token. |
| **Confidence** | Verified |
| **Tables** | [TASK](database-tables.md#task) |
| **SQL** | [SQL-013](sql/SQL-013-task-update.md) |
| **Source** | `Business\SAIF.Business.Tasks\clsTask.vb` |
| **Stored procedure** | [pk82036_task.task_update](procedures/pk82036_task.md#task_update) |

---

## BR-019: Task delete is implemented as a soft delete

| Field | Value |
| --- | --- |
| **Statement** | Delete is implemented as a soft delete. |
| **Trigger** | `pk82036_task.task_delete` processes a delete request. |
| **Enforcement** | The function sets `DELETE_FLAG = 'Y'` instead of issuing `DELETE FROM TASK`. |
| **Outcome** | The row remains in `TASK` while being marked as deleted. |
| **Confidence** | Verified |
| **Tables** | [TASK](database-tables.md#task) |
| **SQL** | [SQL-014](sql/SQL-014-task-delete.md) |
| **Source** | `Business\SAIF.Business.Tasks\clsTask.vb` |
| **Stored procedure** | [pk82036_task.task_delete](procedures/pk82036_task.md#task_delete) |

---

## BR-020: Soft delete requires a matching optimistic-concurrency token

| Field | Value |
| --- | --- |
| **Statement** | Soft delete requires a matching optimistic-concurrency token. |
| **Trigger** | `pk82036_task.task_delete` executes the delete-state update. |
| **Enforcement** | The function filters rows with `WHERE task_seq_num = taskseqnum AND update_seq_num = updateseqnum`. |
| **Outcome** | Stale delete attempts return `SQL%ROWCOUNT = 0` and leave the row unchanged. |
| **Confidence** | Verified |
| **Tables** | [TASK](database-tables.md#task) |
| **SQL** | [SQL-014](sql/SQL-014-task-delete.md) |
| **Source** | `Business\SAIF.Business.Tasks\clsTask.vb` |
| **Stored procedure** | [pk82036_task.task_delete](procedures/pk82036_task.md#task_delete) |

---

## BR-021: Task purge is limited to deleted rows in the requested source and category slice

| Field | Value |
| --- | --- |
| **Statement** | Physical purge is limited to already deleted rows in the requested source/category slice. |
| **Trigger** | `pk82036_task.task_purge` executes the `DELETE TASK` statement. |
| **Enforcement** | The procedure requires matching `TASK_SOURCE_CODE`, `TASK_CATEGORY_CODE`, and `DELETE_FLAG = 'Y'` in the `WHERE` clause. |
| **Outcome** | Active rows and rows outside the requested source/category slice are not purged. |
| **Confidence** | Verified |
| **Tables** | [TASK](database-tables.md#task) |
| **SQL** | [SQL-015](sql/SQL-015-task-purge.md) |
| **Source** | `Business\SAIF.Business.Tasks\clsTaskCollection.vb` |
| **Stored procedure** | [pk82036_task.task_purge](procedures/pk82036_task.md#task_purge) |

---

## BR-022: Task purge retention is enforced by whole-day age

| Field | Value |
| --- | --- |
| **Statement** | Purge retention is enforced by whole-day age. |
| **Trigger** | `pk82036_task.task_purge` evaluates the retention predicate. |
| **Enforcement** | The procedure deletes only rows where `TRUNC(update_date) < TRUNC(SYSDATE - TO_NUMBER(inkeepdays))`. |
| **Outcome** | Rows newer than the keep-days threshold remain available even if they are already soft-deleted. |
| **Confidence** | Verified |
| **Tables** | [TASK](database-tables.md#task) |
| **SQL** | [SQL-015](sql/SQL-015-task-purge.md) |
| **Source** | `Business\SAIF.Business.Tasks\clsTaskCollection.vb` |
| **Stored procedure** | [pk82036_task.task_purge](procedures/pk82036_task.md#task_purge) |

---

## BR-023: Task collection retrieval excludes soft-deleted tasks by default

| Field | Value |
| --- | --- |
| **Statement** | A task search excludes soft-deleted rows unless the caller explicitly asks to include them. |
| **Trigger** | `clsTaskCollection.Criteria.FormatRequest` (and the underlying `FormatOtherParameters`) assembles the search `WHERE` clause. |
| **Enforcement** | The criteria appends `AND DELETE_FLAG = 'N'` to the query unless `ShowCompleted` is set to `True`, in which case no delete-flag predicate is added and both active and soft-deleted rows are returned. |
| **Outcome** | Callers see only active tasks by default; `ShowCompleted` is required to surface completed/soft-deleted tasks in a search result. |
| **Confidence** | Verified |
| **Tables** | [TASK](database-tables.md#task) |
| **SQL** | [SQL-008](sql/SQL-008-task-filtered-collection-select.md) |
| **Source** | `Business\SAIF.Business.Tasks\clsTaskCollection.vb` |

---

## BR-024: Priority-level filter cascades to include higher-priority codes

| Field | Value |
| --- | --- |
| **Statement** | Filtering a task search by a minimum `TaskPriority` level includes that level and every higher priority level, not just an exact match. |
| **Trigger** | `clsTaskCollection.Criteria.FormatPriorityParameter` builds the priority predicate when no explicit `TaskPriorityCode` is set and `PriorityLevel` is not `Informational`. |
| **Enforcement** | The predicate expands to an inline `IN (...)` list of literal priority codes: `Low` includes Low/Medium/High/Urgent, `Medium` includes Medium/High/Urgent, `High` includes High/Urgent, and `Urgent` matches only Urgent. |
| **Outcome** | A caller requesting "Medium priority or above" also receives High and Urgent tasks in the same search rather than only exact-Medium matches. |
| **Confidence** | Verified |
| **Tables** | [TASK](database-tables.md#task) |
| **SQL** | [SQL-008](sql/SQL-008-task-filtered-collection-select.md) |
| **Source** | `Business\SAIF.Business.Tasks\clsTaskCollection.vb` |

## Notes
- `TASK3` was not identified in the supplied findings.
- Concrete subclasses can still suppress shared rule results through `OverrideRules(...)` when domain-specific behavior requires it.
- [BR-012](#br-012-single-task-retrieval-is-keyed-only-by-task-sequence-number) through [BR-022](#br-022-task-purge-retention-is-enforced-by-whole-day-age) are derived from the Oracle package documented on the procedures page.

## Related documentation
- [Index](index.md)
- [Overview](overview.md)
- [Architecture](architecture.md)
- [Dependencies](dependencies.md)
- [Operations and processes](operations-processes.md)
- [Database tables](database-tables.md)
- [SQL catalog](sql/index.md)
- [Procedures](procedures/index.md)
- [API reference](api/index.md)
- [Configuration](configuration.md)

## Sources
- `Business\SAIF.Business.Tasks\clsTask.vb`
- `Business\SAIF.Business.Tasks\clsTaskCollection.vb`
- `Business\SAIF.Business.Tasks\clsTaskBusinessRules.vb`
- `Business\SAIF.Business.Tasks\clsTaskCategoryCode.vb`
