# README

## Overview

The Approval module provides a generic, polymorphic approval workflow engine for any target entity in an ERP — for example purchase orders or journal entries. It owns reusable ApprovalPolicy templates, runtime ApprovalRequest instances bound to a target by polymorphic reference, an ordered chain of ApprovalSteps with multi-assignee voting, and an immutable ApprovalDecision log. Steps support a single-user or role-based assignee model.

The module is integrated via a **wrapper module pattern**: a per-purpose wrapper module combines this module with a base module and exposes orchestration commands. Base modules remain unaware of approval — the wrapper is responsible for syncing the target after a request resolves.

Decision history is canonical inside the module — every approve, reject, send-back, delegate, withdraw, cancel, and resubmit produces an immutable `ApprovalDecision` row, and a single `getApprovalDecisionHistory` query returns the full chronological timeline. Self-approval is blocked at the command boundary.

## Key Features

- **[Approval Policy Configuration](docs/feature/approval-policy-configuration.md)** — Define reusable approval templates with purpose tags, ordered steps, and per-step assignee lists (user or role) under a DRAFT → ACTIVE → INACTIVE lifecycle (terminal — no reactivation); in-flight Requests snapshot Policy contents at creation time and are unaffected by subsequent Policy edits or deactivation
- **[Approval Request Lifecycle](docs/feature/approval-request-lifecycle.md)** — Create runtime ApprovalRequests from a Policy or from an inline step list, bind them to any target by polymorphic reference, drive the PENDING → APPROVED / REJECTED / CANCELLED / WITHDRAWN state machine plus the non-terminal REVISION_REQUESTED send-back loop, and expose a single-target exclusivity guard via `getActiveApprovalRequest`
- **[Approval Step Routing](docs/feature/approval-step-routing.md)** — Sequentially advance through ordered steps, expand role assignees to current role members at the moment each step becomes active, evaluate completion against per-step quorum and required-approver rules, and cascade rejection from any step to the whole Request
- **[Approval Decision Recording](docs/feature/approval-decision-recording.md)** — Record every approver-driven and admin-driven transition (APPROVE / REJECT / SEND_BACK / DELEGATE / WITHDRAW / CANCEL / RESUBMIT) as an immutable `ApprovalDecision` row, enforce segregation of duties (no self-approval), and surface the rejection reason on the Request

## Module Scope

### In Scope

- Polymorphic approval routing for any target entity
- Reusable Policy templates with a DRAFT → ACTIVE → INACTIVE lifecycle
- Runtime Request instances with snapshot isolation from Policy
- Historical resolution of which Policy revision applied to a past Request
- Request creation from a Policy or from an inline step list
- Ordered multi-step routing with OR / AND / Majority approval semantics per step
- Per-assignee voting with user-level or role-level assignment
- Role-based assignee expansion at step activation, frozen for the duration of that step
- Sequential step advancement on full step approval, fast-fail rejection cascade from any step to the whole Request, and per-step delegation to another approver
- Send-back for revision: an approver returns an in-flight Request to the requester, who revises the target and resubmits
- In-flight Request termination by requester withdrawal or admin cancellation
- Self-approval prevention
- Immutable decision log with a chronological history query
- Single-target exclusivity check (whether an active Request already exists for a target)
- Policy lookup for wrapper module selection
- Per-target historical Request listing
- Per-user "My Inbox" listing of actionable Requests
- Per-requester listing of own Requests, defaulting to those awaiting resubmission

### Out of Scope

- Dynamic rule engine for selecting which Policy to apply at request time
- Dynamic approval-group resolution by walking organizational hierarchies (manager-of-manager chains, supervisor escalation)
- Timer-driven escalation, automatic approval, reminder notifications, and out-of-office auto-delegation rules
- Push-back to a prior approver step; send-back returns to the requester only
- Resume-from-the-middle on resubmit
- Parallel different-attribute steps running concurrently at the same order position
- BPMN-style conditional gateways or mid-flight branching based on data changes
- Notification dispatch to email, Slack, Teams, or other channels
- Integration with external workflow engines such as Camunda or Temporal

### Scope Decision Rationale

The module is scoped to the lifecycle mechanics common to every ERP-grade approval system, so applications can adopt approval routing without each base module reinventing the same DRAFT/SUBMITTED/APPROVED states inline.

A dynamic rule engine is intentionally excluded because Policy selection in practice depends on application-specific attributes that are clearer and more testable as TypeScript code in the wrapper command than as a stored runtime expression. Administrators can still register multiple ACTIVE Policies for the same purpose tag and let the wrapper command pick.

The module deliberately does not own the target entity's lifecycle. Approval state and target state are independent facts, and synchronizing them is the wrapper command's job; this keeps the approval module composable across very different target types.

Send-back returns the Request to the requester (never to a prior approver) and resubmission restarts from the first step, matching where the major ERPs converge: the requester owns the target, so revisions naturally flow through them, and a full restart avoids selective-restart rules.

Company scope is intentionally not duplicated on approval entities. Inheriting it via the target avoids depending on `organization` (forcing every adopter to install Company support) or accepting denormalization drift; queries that need to filter by Company resolve the target first.

## Module Dependencies

- [user-management](../user-management/README.md) — `User` and `Role` models are referenced by approval models; `listUsersByRole` is used to expand role-based assignees at step activation; per-command permissions are managed via user-management's RBAC; durability of the decision log relies on user-management's no-hard-delete invariant for users
- **Target modules** (no compile-time dependency) — target binding is polymorphic; integration happens via wrapper modules
