# ResubmitApprovalRequest

## Permission Scope

request

## Overview

ResubmitApprovalRequest is the requester's reply to a send-back. After revising the target (out-of-band — the approval module performs no change detection), the requester calls this command to return a `REVISION_REQUESTED` request to `PENDING` and restart approval from the first step. The runtime steps and assignees are reset for a new round; the prior round's decisions remain in the immutable `ApprovalDecision` log.

## Business Rules

- Only the original requester (`ctx.actorId === ApprovalRequest.requesterId`) may resubmit
- Allowed only from `REVISION_REQUESTED`; rejected on `PENDING` and on every terminal state
- Writes one `ApprovalDecision` row: `decision = RESUBMIT`, `approvalStepId = null`, optional `comment`
- Returns the request to `PENDING`, resets every `ApprovalStep` to `PENDING` (`resolvedAt` cleared), and re-activates the lowest-`stepOrder` step
- Resets `APPROVED` assignees to `PENDING` (`resolvedAt` cleared); `DELEGATED` rows are not reset — the delegatee already holds the seat
- Reuses the frozen assignee set: no role re-expansion, no policy re-read; a never-reached step still expands its role seed when reached
- Never deletes or mutates prior `ApprovalDecision` rows

## Process Flow

```mermaid
flowchart TD
    A[Receive resubmit request] --> B{Request exists?}
    B -->|No| BX[Return error: REQUEST_NOT_FOUND]
    B -->|Yes| C{Request.status = REVISION_REQUESTED?}
    C -->|No| CX[Return error: INVALID_STATUS_TRANSITION]
    C -->|Yes| D{ctx.actorId === Request.requesterId?}
    D -->|No| DX[Return error: NOT_REQUESTER]
    D -->|Yes| E[Insert RESUBMIT decision]
    E --> F[Reset steps and APPROVED assignees to PENDING<br/>DELEGATED rows left as-is]
    F --> G[Re-activate lowest-stepOrder step]
    G --> H[Update request: status = PENDING]
    H --> I[Return the resubmitted request]
```

## External Dependencies

- None

## Error Scenarios

- **REQUEST_NOT_FOUND**: Specified `approvalRequestId` does not exist
- **INVALID_STATUS_TRANSITION**: Target entity is not in a status that permits this operation
- **NOT_REQUESTER**: `ctx.actorId` does not match `ApprovalRequest.requesterId`

## Test Cases

- transitions REVISION_REQUESTED to PENDING and writes a RESUBMIT decision
- resets every step to PENDING, resets approved assignees, re-activates the first step, and sets the request PENDING
- does not reset DELEGATED assignees — the delegatee row remains the seat's only voter
- re-activates the lowest-stepOrder step
- accepts an optional comment and stores it on the decision row
- does not delete or mutate any prior ApprovalDecision row
- throws NOT_REQUESTER when called by a non-requester user
- throws INVALID_STATUS_TRANSITION when the request is PENDING
- throws INVALID_STATUS_TRANSITION when the request is APPROVED
- throws INVALID_STATUS_TRANSITION when the request is REJECTED
- throws INVALID_STATUS_TRANSITION when the request is CANCELLED
- throws INVALID_STATUS_TRANSITION when the request is WITHDRAWN
- throws REQUEST_NOT_FOUND for a non-existent approvalRequestId
