Approval Chains
What. Reusable sign-off workflows. A chain has ordered steps; each step names an approver (a specific user, a department head, or a role). A chain is assigned to one or more entities (a process, a department, or a document type) - when a version of that entity is submitted, the chain is materialized into approvals rows. Where.
/approval-chains.
Why approval chains exist
Without chains, every document and process would need a hard-coded approval list. Chains let you say:
"For everything in DG Office, the head approves first, then the Compliance officer, then the DG."
…and apply that policy to 50 documents and 200 processes in one place.
The model
ApprovalChain (id, name)
├─ ChainStep (order, approver_type, approver_user_id|null, mode, sla)
├─ ChainStep (order=2, …)
└─ ChainAssignment (target_type=process|department|document_type, target_id)
When a version is submitted:
- Resolve the applicable chain by looking up assignments. Specificity ranking: process > department > document_type.
- Materialize - for each step, create an
approvalsrow withentity_type='process_version'|'document_version',entity_id,order_index=step.order,approver_user_idresolved from the step,decision='pending'. - Approvers see the row in My Workspace + on the entity detail page.
Create a chain
- Side nav → Approval Chains → New Chain.
- Fill Name (e.g. DG Office process approval) + optional description.
- Add Steps in order:
- Approver type =
User(specific person),Department head(resolved at materialize-time), orRole. - For User: pick the user from the dropdown.
- Mode =
All must approveorAny one approves. - SLA hours - soft target for completion (UI hint).
- Approver type =
- Add Assignments - what this chain applies to:
- Target type = Process / Department / Document Type
- Target = the actual row (e.g. department
DG Office) - Priority - when multiple chains match, higher priority wins
- Click Save → redirects to the chain list with a snackbar "Approval chain saved".
Edit a chain
Open the chain → fields are editable. Save replaces all steps and assignments atomically (no orphan rows).
Already-materialized approvals are not retroactively re-materialized when you edit a chain. The version you submitted yesterday keeps the old chain's structure. Only new submissions pick up the change.
Resolution rules
When a process is submitted, the resolver runs:
- Look up chain assignments where
target_type='process'andtarget_id = process.id. If found, that's the chain (most specific). - Else look up
target_type='department'andtarget_id = process.department_id. (For docs, also fall back totarget_type='document_type'andtarget_id = doc.doc_type.) - Tie-break by
priority(higher wins). - If nothing matches, submission fails with "No approval chain assigned to this process or its department."
Approver-type details
user-approver_user_idis set, materializes a row for that exact user.department_head- at materialize time, resolve the head of the entity's department. If the head is null, materialization throws.role- every active member of the org with that role becomes an approver row. Use Mode=Any one approvesso the first to respond resolves the step.
Test it
- Create a chain "Test chain" with 2 steps: step 1 = department head, step 2 = a specific user. Assign to the DG Office department with priority 100.
- Save → land back on
/approval-chainswith the snackbar. - Submit a process in DG Office. The Approvals tab on the process shows 2 steps with the right approvers.
- Approve step 1 as the dept head. Step 2 stays pending.
- Approve step 2 as the named user. Process auto-flips to Approved.
- Edit the chain to add a 3rd step. Submit a different process. The new process gets 3 steps; the previously-submitted one keeps its 2.
- Try submitting a process in a department with no assigned chain. The submit fails with the "No approval chain…" error message.