---
title: Task record lifecycle — create, retrieve, insert, update, soft-delete
summary: Governs the in-memory initialization and Oracle-backed CRUD lifecycle of a single TASK row through the shared clsTask base class.
status: draft
owner: SAIF
last_reviewed: 2026-07-30
library: SAIF.Business.Tasks
reference_type: package
operation_id: OP-TASK-01
trigger: A concrete task subclass constructs, retrieves, or saves a clsTask-derived instance
---

# Task record lifecycle — create, retrieve, insert, update, soft-delete

`clsTask` is an abstract BOF business object; every concrete task type in the SAIF task ecosystem inherits it and shares one CRUD lifecycle against the `TASK` table through the `pk82036_task` Oracle package. The lifecycle covers five phases that a caller may trigger independently: in-memory initialization of a new task, single-row retrieval by task sequence number, insert of a new row, update of an existing row, and soft-delete of a row. All five phases are implemented as overrides of the BOF data-portal template methods (`Create`, `BeforeRetrieve`/`AfterRetrieve`, `BeforeInsert`/`AfterInsert`, `BeforeUpdate`/`AfterUpdate`, `BeforeDelete`/`AfterDelete`) on the same class, and insert/update share the same field-level validation rule set, so they are documented together as one operation.

| Field | Value |
| --- | --- |
| **Operation ID** | OP-TASK-01 |
| **Trigger** | A concrete task subclass calls `Create()` (new task), triggers a BOF retrieve with a `TaskID` criterion, or calls `Save()` on a new/dirty/deleted task instance |
| **Inputs** | Task field values (`CategoryCode`, `SourceCode`, `PriorityCode`, `MailID`, `Text`, `TaskDate`, `ActionCode`, `ClaimID`, `PolicyNum`, `ImageSeqNum`, `TaskSecurity`, `TaskContext` values), `TaskSeqNum`, `UpdateSeqNum`, acting user from `IDataPortalInfo` |
| **Outputs** | Populated task object (retrieve); inserted/updated/soft-deleted `TASK` row and a rows-affected result (insert/update/delete); broken-rule collection when validation fails |
| **Primary components** | `clsTask.Create`, `clsTask.BeforeRetrieve`, `clsTask.AfterRetrieve`, `clsTask.BeforeInsert`, `clsTask.AfterInsert`, `clsTask.BeforeUpdate`, `clsTask.AfterUpdate`, `clsTask.BeforeDelete`, `clsTask.AfterDelete`, `clsTask.AddBusinessRules`, `clsTask.AssignTaskDate` |
| **Entry point** | `Business\SAIF.Business.Tasks\clsTask.vb` |

## Steps

1. **Initialize a new task.** A concrete subclass constructs a task; `Create()` resets `Text`, `SourceCode`, `CategoryCode`, `ActionCode` to empty strings, sets `NotepadFlag` and `DeleteFlag` to `N`, and seeds `CreateUser` from the criteria's explicit create-user value or, if that is blank, from `MyCriteria.DataPortalInfo.UserID`.
   - `UpdateUser`, `CreateDate`, and `UpdateDate` are stamped with the create user and the current time, and the smart-value fields (`ClaimID`, `PolicyNumber`, `TaskDate`, `ImageSeqNum`, `UpdateUser`, `Mailid`) are marked as their own originals so change tracking starts clean.
2. **Populate task fields.** The caller (or concrete subclass) sets the task's properties before validation or persistence. Setting `TaskDate` runs `AssignTaskDate`, which leaves the date unchanged for `CurrentDay` mode but, for `NextWorkDay` or `PriorWorkDay` mode, rolls the date forward or backward off weekends/non-working days and flags `TaskDateModified = True` ([BR-003](../business-rules.md#br-003-due-date-adjusted-to-a-workday-warning)).
3. **Retrieve a single task.** `BeforeRetrieve` issues a stored-procedure call to `pk82036_task.task_select`, binding only the `TaskID` (task sequence number) and a REF CURSOR output ([SQL-002](../sql/SQL-002-task-select.md), [TASK](../database-tables.md#task)) ([BR-012](../business-rules.md#br-012-single-task-retrieval-is-keyed-only-by-task-sequence-number)).
   - `AfterRetrieve` reads the cursor; if a row is returned, `Fill()` populates all task fields including the embedded `TaskContext` values, then `PostFill()` (implemented by the concrete subclass) runs, and the instance is marked old (clean, not new).
   - If no row is returned, the object is left unfilled and is still marked old.
4. **Validate before save.** `AddBusinessRules()` registers text-required checks for `CategoryCode`, `SourceCode`, `PriorityCode`, and `MailID` ([BR-004](../business-rules.md#br-004-task-category-code-is-required), [BR-005](../business-rules.md#br-005-source-system-code-is-required), [BR-006](../business-rules.md#br-006-priority-code-is-required), [BR-007](../business-rules.md#br-007-mail-id-is-required)), delegating to the `TextRequired` helper on [`CommonBusinessRules`](../../saif-business/api/SAIF.Business/commonbusinessrules.md) (`SAIF.Business`).
   - `clsTaskBusinessRules.ValidSourceCategory` runs a scalar count of `TASK_CATEGORY_CODE` for the task's source/category pair ([SQL-005](../sql/SQL-005-task-category-code-count.md), [TASK_CATEGORY_CODE](../database-tables.md#task_category_code)) ([BR-001](../business-rules.md#br-001-valid-source-and-category-combination)).
     - If the count is zero, the concrete subclass gets a final say through `OverrideRules("TASK1", ...)`; if the subclass does not override, the mismatch is added as a broken rule.
   - `clsTaskBusinessRules.CheckDate` runs only when the task is dirty and not deleted, and only if the date was adjusted by `AssignTaskDate` or is still `Date.MinValue`; it then delegates to `CommonBusinessRules.IsTodayOrFutureDate` (`SAIF.Business`) ([BR-002](../business-rules.md#br-002-task-date-must-be-current-or-future)).
     - Batch-rule tasks (`BatchRules = True`) skip this check entirely.
     - If the date is in the past, the subclass may override through `OverrideRules("TASK2", ...)`; otherwise the past-date error is recorded.
   - `clsTaskBusinessRules.CheckDateModified` reports the workday-adjustment warning recorded in step 2, selecting the next-workday or prior-workday wording and again allowing subclass override through `OverrideRules("TASK4", ...)` ([BR-003](../business-rules.md#br-003-due-date-adjusted-to-a-workday-warning)).
5. **Insert a new task.** `BeforeInsert` calls `pk82036_task.task_insert`, binding the full set of task and context fields plus a rows-affected return parameter ([SQL-001](../sql/SQL-001-task-insert.md), [TASK](../database-tables.md#task)).
   - The Oracle function normalizes a zero `ClaimID` to `NULL` ([BR-013](../business-rules.md#br-013-insert-normalizes-zero-claim-ids-to-null)) and a zero `PolicyNum` to `NULL` ([BR-014](../business-rules.md#br-014-insert-normalizes-zero-policy-numbers-to-null)), and seeds `DELETE_FLAG = 'N'`, `UPDATE_SEQ_NUM = 0`, `CREATE_DATE`/`UPDATE_DATE = SYSDATE`, and a new `TASK_SEQ_NUM` from `TASK_SEQ.NEXTVAL` ([BR-015](../business-rules.md#br-015-new-tasks-start-active-and-versioned-from-zero)).
   - `AfterInsert` checks the rows-affected value ([BR-008](../business-rules.md#br-008-task-insert-must-affect-one-row)):
     - If not exactly `1`: add `InsertError` with the message `Task Insert Failed`.
     - If `1`: remove any prior `InsertError` and mark the object old.
6. **Update an existing task.** `BeforeUpdate` re-stamps `UpdateDate` to now and `UpdateUser` from the data-portal info, then calls `pk82036_task.task_update`, binding the mutable task/context fields plus `TaskSeqNum`, `UpdateSeqNum`, and a rows-affected return parameter ([SQL-013](../sql/SQL-013-task-update.md), [TASK](../database-tables.md#task)).
   - The Oracle function matches on `TASK_SEQ_NUM AND UPDATE_SEQ_NUM` ([BR-016](../business-rules.md#br-016-task-update-requires-a-matching-optimistic-concurrency-token)), normalizes zero `ClaimID`/`PolicyNum` to `NULL` ([BR-017](../business-rules.md#br-017-update-normalizes-zero-claim-and-policy-values-to-null)), and on a match advances `UPDATE_DATE`, `UPDATE_SEQ_NUM + 1`, and `UPDATE_USER` ([BR-018](../business-rules.md#br-018-successful-updates-always-advance-audit-and-version-fields)).
   - `AfterUpdate` checks the rows-affected value ([BR-009](../business-rules.md#br-009-task-update-requires-a-current-row-version)):
     - If not exactly `1`: add `UpdateError` with the shared database-contention message.
     - If `1`: remove any prior `UpdateError`, increment the in-memory `UpdateSeqNum`, and mark the object old.
7. **Soft-delete a task.** A concrete subclass marks the task completed and satisfies whatever delete-eligibility semantics it defines, then `BeforeDelete` sets `UpdateUser` from the data-portal info and calls `pk82036_task.task_delete`, binding `TaskSeqNum`, `UpdateSeqNum`, `UpdateUser`, and a rows-affected return parameter ([SQL-014](../sql/SQL-014-task-delete.md), [TASK](../database-tables.md#task)).
   - The Oracle function matches on `TASK_SEQ_NUM AND UPDATE_SEQ_NUM` ([BR-020](../business-rules.md#br-020-soft-delete-requires-a-matching-optimistic-concurrency-token)) and sets `DELETE_FLAG = 'Y'` instead of physically removing the row ([BR-019](../business-rules.md#br-019-task-delete-is-implemented-as-a-soft-delete)).
   - `AfterDelete` checks the rows-affected value ([BR-010](../business-rules.md#br-010-task-delete-requires-a-current-row-version)):
     - If not exactly `1`: add `DeleteError` with the shared database-contention message.
     - If `1`: remove any prior `DeleteError`.

## Process flow

The following diagram visualizes the five phases of the task record lifecycle and their shared validation and concurrency handling.

```mermaid
flowchart TD
	Start([Concrete task subclass<br>drives the lifecycle]) --> Which{Which phase?}
	Which -->|New| Create[Create: seed defaults,<br>audit stamps]
	Which -->|Retrieve| RetSel[Call task_select<br>by TaskID]
	RetSel --> RetFill{Row returned?}
	RetFill -->|Yes| Fill[Fill fields and<br>TaskContext, PostFill]
	RetFill -->|No| Empty([Object left unfilled,<br>marked old])
	Which -->|Save new/dirty| Validate{AddBusinessRules<br>pass?}
	Validate -->|No| Broken([Broken rules returned;<br>no DB call])
	Validate -->|Yes, new| Insert[Call task_insert]
	Validate -->|Yes, existing| Update[Call task_update]
	Which -->|Save deleted| Delete[Call task_delete]
	Insert --> InsRows{RowsAffected = 1?}
	InsRows -->|No| InsErr([InsertError added])
	InsRows -->|Yes| InsOk([Marked old])
	Update --> UpdRows{RowsAffected = 1?}
	UpdRows -->|No| UpdErr([UpdateError: contention])
	UpdRows -->|Yes| UpdOk([UpdateSeqNum incremented,<br>marked old])
	Delete --> DelRows{RowsAffected = 1?}
	DelRows -->|No| DelErr([DeleteError: contention])
	DelRows -->|Yes| DelOk([DeleteError cleared])
```

## Failure and edge-case behavior

- A rows-affected value other than `1` is treated as a hard failure on insert, update, and delete alike — the message differs (`Task Insert Failed` for insert; the shared database-contention message for update and delete) but the effect is the same: the in-memory object is not marked clean and the caller sees a broken rule.
- The `ValidSourceCategory`, `CheckDate`, and `CheckDateModified` rules all route their failing result through `clsTask.OverrideRules`, a no-op hook in the base class; a concrete subclass can silently suppress any of the three by overriding it, so the documented outcomes assume no override is applied.*
- `CheckDate` is bypassed entirely when `BatchRules` is `True`, when the task is not dirty, when the task is deleted, or when the date was not modified and is not `Date.MinValue` — so an already-valid date is never re-validated on a routine save.
- `AssignTaskDate` only adjusts the date when it is neither `Date.MinValue` nor `Date.MaxValue`; sentinel dates pass through unmodified and unflagged.
- Retrieval silently returns an empty, "old" object when `pk82036_task.task_select` finds no matching row — there is no explicit not-found error surfaced by this class.*

## 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.Tasks\clsTask.vb`
- `Business\SAIF.Business.Tasks\clsTaskBusinessRules.vb`
