Segregation of Duties¶
Applies to: All subscriptions
Purpose¶
Explain the maker-checker model, state precisely what changes when it is relaxed, and make clear whose decision that is. This setting is one of the first things an auditor asks about, and "it was easier during testing" is not an answer anyone wants to give.
Audience¶
The customer's finance control owner and internal audit, primarily. System administrators, who make the change. Implementation consultants, who must not make it unilaterally.
Prerequisites¶
- Roles and Permissions
- The customer's own internal control policy
Reference¶
The model¶
Segregation of duties separates preparation from commitment. The person who prepares a document is not the person who commits it.
| Function | Role | Can |
|---|---|---|
| Maker | Tax Executive | Create, edit, correct, print, view |
| Checker | Tax Controller | Everything the maker can, plus generate, cancel, approve, issue, promote |
The control exists because the acts on the checker's side are irreversible or externally visible: registering an invoice with the government, cancelling a registration, issuing a document, promoting a template that changes how every future document is interpreted.
Four further separations are built into the role set:
| Separation | Effect |
|---|---|
| Administration from transactions | An org admin manages users and configuration but cannot commit a transaction |
| Logistics from tax | Logistics roles hold the e-Way Bill family but cannot register invoices |
| Assurance from operations | The auditor role can view and print, and nothing else |
| Sign-off from processing | The Approver role holds invoice.approve and no transactional permission, so approval authority can be granted without processing authority |
The two role axes¶
Approval in Complifly is governed by two independent fields on the user record, and both are required. Setting one without the other is the most common approval misconfiguration, and it is silent — no error, no log entry, no refused request.
| System role | Approval role | |
|---|---|---|
| Stored in | sys_user.role |
sys_user.approval_role_key |
| Governed by | config/policies.js — the permission matrix |
dbo.mst_approval_role — the seniority ladder |
| Answers | May this person call the approve endpoint? | Which level of a multi-level approval are they? |
| Values that can approve | Approver, Tax Controller, Super Admin | Finance Executive, Finance Manager, Finance Controller, Business Head, Plant Head, Director, CFO |
| Missing → | 403 on the approve call; no button in the UI |
The user is never named by any level, so nothing reaches them |
The approval ladder¶
Seven levels, junior to senior. The rank matters: when several rules fire on one document, the levels they require are merged and ordered by rank.
| Rank | Approval role | Typically |
|---|---|---|
| 10 | finance_executive |
AP/AR staff |
| 20 | finance_manager |
Team lead, routine value |
| 30 | finance_controller |
Owns the ledger; the usual second signature |
| 40 | business_head |
Commercial owner of the relationship |
| 40 | plant_head |
Same rank — a peer, not a further step |
| 50 | director |
Board-level sign-off |
| 60 | cfo |
Highest step |
Assign both fields together, on Administration > Admin & Logs > Users & Roles. The ladder is also served by GET /api/admin/approval-roles for anyone provisioning users programmatically.
A level with no holder stalls silently
Nothing checks that somebody holds each approval role the matrix uses. If exactly one person holds finance_controller and they leave, every document routed through that level stops, with no alert. Assign each level used by your matrix to at least two people — any holder can clear it, and the history records which one did.
Controls the approval workflow enforces regardless of role¶
| Control | Behaviour |
|---|---|
| Self-approval | Whoever submitted a document cannot decide it, whatever role they hold. Checked on identity, not role, and not waivable — not by an org admin, not by a Super Admin |
| Sequence | Levels are decided in order. Level 2 cannot act before level 1 has |
| Administrative override | An administrator with admin.api may force-approve or force-reject, but must give a written reason, and every remaining level is stamped OVERRIDDEN rather than APPROVED — a bypass is legible as a bypass |
What relaxing it does¶
Setting SOD_ENFORCED to off collapses maker permissions into checker permissions. Specifically:
| Role | Gains, when relaxed |
|---|---|
| Tax Executive | The full checker set — generate, cancel, approve, issue, promote |
| Org Admin | The full checker set, adding transactional authority to configuration authority |
| Transport Manager, Logistics Operator | The full checker set, including IRN generation |
| Approver | Nothing. Deliberately excluded from the collapse |
Note the breadth. Relaxation is not a narrow adjustment to one workflow: it grants transactional authority to roles that were deliberately built without it, including logistics and administration.
Approver is excluded on purpose. An approver who inherited the checker set would gain invoice.create and invoice.issue — the ability to raise a document and then sign it off, which is the single outcome the role was added to prevent. A tenant running in SME mode therefore keeps this one separation even though it has relaxed the others.
Two approval controls also survive relaxation entirely: the approval level must still match, and nobody can approve a document they submitted. Relaxation widens who can be asked to approve; it never lets a level be skipped.
When relaxation is legitimate¶
There is a real case for it. A small business where one person genuinely performs preparation and commitment — because there is no second person — gains nothing from a control that only produces a blocked screen. The setting exists for that situation.
It is not legitimate as:
- A way past an approval step that is slowing down user acceptance testing
- A default applied because nobody decided
- A temporary measure at go-live that is never reviewed
The distinction is whether a control owner decided, with the consequence understood.
Whose decision it is¶
The customer's finance control owner, not the implementation team and not the system administrator who makes the change.
Record:
| Record | Why |
|---|---|
| Who decided | An auditor will ask |
| When | To establish the period affected |
| The business reason | "No second person available" is a reason; "faster" is not |
| Compensating controls, if any | Review of issued documents, for example |
| Review date | Circumstances change as an organisation grows |
If relaxation is not acceptable but staffing is thin¶
Options short of relaxing the setting:
| Option | Trade-off |
|---|---|
| One checker covering several areas | Concentrates authority in one person, but keeps preparation separate |
| A checker outside the immediate team | Slower, but preserves the control |
| Relax for one GSTIN only | The setting is per registration, so a small subsidiary can differ from the group |
| Relax with a compensating review | Documented review of committed documents |
The third option is under-used. Because the setting is per GSTIN, a group does not have to choose one posture for every registration.
Validation¶
| Check | Method | Pass condition |
|---|---|---|
| Current state known | Check the setting per GSTIN | Known for every registration, not assumed |
| Enforcement works | With it on, sign in as a tax executive and attempt to generate an IRN | Refused |
| Relaxation works as documented | With it off in a test environment, repeat | Permitted |
| Breadth understood | With it off, check what a logistics operator can now do | Confirm the customer intended to grant transactional authority to logistics |
| Decision recorded | Change record | Names the decision-maker, date and reason |
| Consistent across registrations | Compare the setting across all GSTINs | Deliberate, whether uniform or not |
| Compensating controls in place | Where relaxed, confirm the agreed review is happening | Evidenced, not merely planned |
| Review scheduled | Operations calendar | A date to revisit the decision |
Troubleshooting¶
| Symptom | Cause | Action |
|---|---|---|
| A maker cannot generate an IRN | Segregation enforced. Working as designed | Explain the model. Do not relax the setting to resolve a single ticket |
| A maker can suddenly generate an IRN | Segregation relaxed | Confirm this was a recorded decision. If not, re-enable and investigate who changed it |
| A logistics user can register invoices | Relaxation collapsed their permissions into the checker set | Expected effect. If unintended, this is the strongest argument for re-enabling |
| Auditor asks who authorised relaxation | No record kept | Record every control-relevant change at the time. Reconstructing it later is unconvincing |
| Approvals block a go-live | Approval thresholds, not segregation | Check the Invoice Generation thresholds first; they are a separate control |
| One registration behaves differently | Setting differs per GSTIN | Confirm it is deliberate |
| Relaxed during testing and never reviewed | The classic failure | Add a review date at the moment of relaxation, not afterwards |
| Customer wants partial relaxation for one action | Not supported — relaxation is all or nothing per GSTIN | Consider per-GSTIN relaxation, or a role change with the consequence accepted |
| An approver sees the workflow panel but no Approve button | Their system role does not hold invoice.approve |
Assign the Approver system role (or Tax Controller). The approval role alone is not enough |
| An approver can approve, but no document ever reaches them | No approval_role_key on their user record, or a different one |
Check both fields on the user. The panel names the role the document is waiting on |
| A document sits PENDING with nobody able to act | The level names an approval role no active user holds | Assign that approval role to someone. This is why levels want two holders |
A senior approval appears in the history as OVERRIDDEN |
It was taken through Force Approve rather than by the named approver | Expected if an administrator bypassed. If it is happening routinely, the approvers are not set up — fix the roles rather than normalising the override |
| A user cannot approve their own document even as Super Admin | Self-approval is blocked on identity and is not waivable | Correct behaviour. Use Force Approve with a reason, or have another holder of the level decide |
Related Articles¶
- Roles and Permissions — the permission sets that collapse
- Feature Flags — where the setting lives
- Audit Log Review — evidencing control operation
- Invoice Generation Module — approval thresholds, a separate control