Send Back for Correction gives approvers a third decision path — alongside Approve and Reject — that returns a submission to any prior stage for revision without permanently terminating the workflow.
| Scenario | Without Send Back | With Send Back |
|---|---|---|
| Finance spot-checks a form and finds a typo | Must reject; submitter resubmits from scratch | Sends back to the submitter or an earlier stage; history is preserved |
| Stage 3 needs information that was supposed to be collected at Stage 1 | Workflow dead-ends | Stage 3 approver sends back to Stage 1 |
| Approval chain needs an extra sign-off before proceeding | No mechanism | Send to a prior stage; that stage re-approves and advances again |
| Model | Field | Description |
|---|---|---|
WorkflowStage |
allow_send_back (BooleanField, default False) |
Opts a stage in as a valid send-back target |
ApprovalTask |
status = "returned" |
New terminal status — records the closed task without firing rejection hooks |
AuditLog |
action = "send_back" |
Records who sent back, from which stage, to which target stage, and the reason |
For each stage you want approvers to be able to send submissions back to:
- Open Django Admin → Workflow Definitions → [your workflow] → Workflow Stages.
- Select a stage (must have
orderlower than the stage where the approver acts). - Check Allow Send Back.
- Save.
Only stages with
allow_send_back=Trueand a lowerorderthan the current stage appear as target options in the approval UI.
The approval view automatically renders the Send Back for Correction card when at least one prior stage has allow_send_back=True.
- Approver opens the approval screen for a pending submission.
- A collapsible Send Back for Correction card (Bootstrap warning / amber colour) appears below the main decision buttons.
- Approver expands the card, selects the target stage from a dropdown, and types a reason.
- Approver clicks Send Back.
- The system:
- Cancels all sibling pending tasks at the current stage (sets
status = "returned"). - Writes an
AuditLogentry withaction = "send_back", target stage name, and the reason text. - Re-creates tasks at the selected target stage via
_create_stage_tasks. - Leaves
FormSubmission.status = "pending_approval"— the workflow continues.
- Cancels all sibling pending tasks at the current stage (sets
- The assignees at the target stage receive a new task notification email.
| Function | Description |
|---|---|
handle_send_back(submission, task, target_stage) |
Main handler for parent workflow send-backs |
handle_sub_workflow_send_back(task, target_stage) |
Same logic scoped to a SubWorkflowInstance |
Both functions are called from the approve_submission view when decision=send_back is posted.
The view enforces these constraints before calling handle_send_back:
target_stagemust belong to the sameWorkflowDefinitionas the current task.target_stage.ordermust be strictly less than the current stage order.target_stage.allow_send_backmust beTrue.- A non-empty reason string is required.
Any violation results in a validation error and no state change.
Every send-back produces an AuditLog record:
action: send_back
submission: <FormSubmission pk>
user: <approver who triggered send-back>
detail: "Sent back from 'Finance Review' to 'Department Head Review': Missing cost centre code"
ip_address: <request IP>
handle_sub_workflow_send_back mirrors the parent logic but operates on a SubWorkflowInstance rather than the parent FormSubmission. The parent submission status is not affected by a sub-workflow send-back.