# ApprovalRequest

## Description

ApprovalRequest is the runtime instance of an approval workflow. Every request binds a `requesterId`, a polymorphic `(targetEntityType, targetEntityId)` pair, an action-shaped `purpose` string (e.g. `PRODUCT_ACTIVATION`, `PO_CONFIRMATION`), and an ordered chain of runtime steps together under a single status state machine.

## Domain Model Definitions

### Model type

Stateful

#### State Transitions

```mermaid
stateDiagram-v2
    [*] --> Pending: createApprovalRequest
    Pending --> Approved: approveApprovalStep
    Pending --> Rejected: rejectApprovalStep
    Pending --> RevisionRequested: sendBackApprovalStep (requester mode)
    Pending --> Pending: sendBackApprovalStep (step mode, rewind)
    RevisionRequested --> Pending: resubmitApprovalRequest
    Pending --> Withdrawn: withdrawApprovalRequest
    RevisionRequested --> Withdrawn: withdrawApprovalRequest
    Pending --> Cancelled: cancelApprovalRequest
    RevisionRequested --> Cancelled: cancelApprovalRequest
```

| Operation | From | To | Command |
|-----------|------|----|---------|
| approve | PENDING | APPROVED | [approveApprovalStep](../command/ApproveApprovalStep.md) |
| reject | PENDING | REJECTED | [rejectApprovalStep](../command/RejectApprovalStep.md) |
| sendBack | PENDING | REVISION_REQUESTED | [sendBackApprovalStep](../command/SendBackApprovalStep.md) (requester mode) |
| resubmit | REVISION_REQUESTED | PENDING | [resubmitApprovalRequest](../command/ResubmitApprovalRequest.md) |
| withdraw | PENDING, REVISION_REQUESTED | WITHDRAWN | [withdrawApprovalRequest](../command/WithdrawApprovalRequest.md) |
| cancel | PENDING, REVISION_REQUESTED | CANCELLED | [cancelApprovalRequest](../command/CancelApprovalRequest.md) |

> Step-mode `sendBackApprovalStep` (a `targetApprovalStepId` supplied) does not change the request status — the request stays `PENDING` while the runtime steps rewind to the target step. Only the requester-mode send-back (no target) drives the `sendBack` transition to `REVISION_REQUESTED` above.

### Command Definitions

- [createApprovalRequest](../command/CreateApprovalRequest.md)
- [withdrawApprovalRequest](../command/WithdrawApprovalRequest.md)
- [cancelApprovalRequest](../command/CancelApprovalRequest.md)
- [approveApprovalStep](../command/ApproveApprovalStep.md)
- [rejectApprovalStep](../command/RejectApprovalStep.md)
- [sendBackApprovalStep](../command/SendBackApprovalStep.md)
- [resubmitApprovalRequest](../command/ResubmitApprovalRequest.md)

### Query Definitions

- [getActiveApprovalRequest](../query/GetActiveApprovalRequest.md)
- [listApprovalRequestsByTarget](../query/ListApprovalRequestsByTarget.md)
- [listApprovalRequestsForApprover](../query/ListApprovalRequestsForApprover.md)
- [listApprovalRequestsForRequester](../query/ListApprovalRequestsForRequester.md)

### Models

- ApprovalRequest

### Invariants

- `name` is required and non-empty
- `purpose` is a non-empty opaque string and is not validated against an enum
- `targetEntityType` (free string) and `targetEntityId` (UUID) are both required and stored as-is; no foreign key is enforced against the target
- `requesterId` is required and non-null
- PENDING: the lowest-`stepOrder` `ApprovalStep` is `IN_PROGRESS`
- APPROVED / REJECTED / CANCELLED / WITHDRAWN are terminal: status is immutable and `resolvedAt` is set
- REVISION_REQUESTED is non-terminal: `resolvedAt` is null and the request is back in the requester's hands
- REJECTED: `rejectionReason` is populated
- `sourcePolicyId` is nullable and is never updated after creation

### Relationships

- **References User (cross-module)**: `requesterId` references a `User` row in `user-management`
- **References ApprovalPolicy (optional)**: When created in policy mode, `sourcePolicyId` references the source `ApprovalPolicy` for reporting and revision-lineage traceability via the policy's `name` and `activatedAt`; null in direct mode
- **Has Many ApprovalSteps**: A request owns the ordered runtime steps populated at creation time
- **Has Many ApprovalDecisions**: Every actor-driven transition (APPROVE / REJECT / SEND_BACK / DELEGATE / WITHDRAW / CANCEL / RESUBMIT) writes one immutable `ApprovalDecision` row for this request
- **Polymorphic Target**: `(targetEntityType, targetEntityId)` is opaque polymorphic addressing with no compile-time dependency on any target module; wrapper modules resolve and synchronize the target
