Approval Workflow
Block account.move posting until every required approver in an amount-banded, ordered chain has signed off.
Why this module
Approval Workflow
Posting is the enforcement point
The gate sits on the account.move post pipeline, before sequence numbering and reconciliation fire. Until the chain reaches approved, the move cannot post, and the block names the exact policy, pending group, and step that stopped it. No half-posted, gap-numbered moves slip through.
Concurrency and audit, not just a status field
Two approvers clicking at once cannot both advance a step: an atomic compare-and-set with a stale-step guard lets one win and hands the other a clean retry. Every transition lands on an append-only log where only the comment is writable and deletion is refused at the model level.
A tested SLA engine, not a checkbox
Per-rule SLA hours compute a due time and an on-track, at-risk, or breached state. An hourly cron sends a pre-deadline reminder and escalates a breached request exactly once to a configured group, with per-record error isolation so one bad rule cannot stall the batch. The full lifecycle is covered by tests.
Day in the life
A vendor bill clears the chain, then someone edits it after approval. What happens?
A bill arrives for ninety thousand. The amount-band policy routes it through three ordered steps: manager, then director, then CFO. Each approver sees a To-Do activity land in their personal inbox as their step comes up, and any member of the responsible group can sign off to advance. Two managers happen to approve in the same second; the atomic step pointer lets one through and quietly hands the other a retry, so the chain never double-advances. With all three signatures in, posting is allowed and the move posts cleanly. Later an AP clerk opens the still-draft move and bumps a line, pushing the total past the configured material threshold. Because the change clears both the percentage and the absolute floor, the approved request silently resets to step zero and a chatter note records why, so the next post attempt is blocked until the chain re-signs. Meanwhile a separate request has sat unsigned past its SLA window: the hourly sweep marks it breached, posts a reminder, and escalates it once to the configured group, never storming it on the next pass.
Edge cases
The cases most modules quietly ignore.
In the shipped code today, each one a place where a cheaper module silently does the wrong thing.
The post-time gate runs before upstream posting, so sequence numbering and reconciliation side effects only fire after approval. You never get a half-posted, gap-numbered move that approval later has to unwind.
Two approvers in the same group clicking approve at the same instant cannot both advance the step. A raw SQL compare-and-set with a stale-step guard lets one win and gives the other a clean retry, and the race is covered by a regression test.
Step order is pinned to an explicit sequence column, not the approver group's primary key. Creating or deleting unrelated security groups never silently reorders an in-flight approval chain.
The re-approval threshold has both a percentage and an optional absolute floor, so a large-percentage but tiny-absolute change can be exempted from re-signing while a genuinely material change still rolls the request back to step zero.
Re-approval is enforced on a plain write, not just on edit-and-post. Editing a watched field on a draft move that already had an approved request silently resets the request and posts a chatter note, so the next post attempt blocks.
A breached request is escalated exactly once. A last-escalated guard stops the hourly cron from storming the escalation group on every pass.
The SLA sweep wraps each request in its own try/except, so one bad rule (a deleted group, a missing field) logs a warning instead of aborting the whole batch.
What is inside
Built to do the job, end to end.
- Amount-band approval policies. One policy per company and document type, with ordered rules. An amount band selects the single approver group for that step, and first-match rule routing picks the band that applies.
- Ordered single-group steps. Each step is bound to exactly one approver group; any member can sign off to advance. Steps run in the policy's sequence order, pinned to an explicit sequence column so unrelated group changes never reorder a live chain.
- Atomic state machine. Advancing the current step uses a raw SQL UPDATE with a stale-step guard, so concurrent approvers cannot both advance the same step. One wins, the other retries cleanly.
- Block posting until approved. A matching policy blocks the account.move post pipeline until the request reaches approved, with an error that names the policy, the pending group, and the step. The gate runs before upstream posting.
- Re-approval on material change. A material amount change after full approval rolls the request back to step zero. The threshold is a percentage with an optional absolute floor, configurable per policy; the watched edit fields are a fixed set.
- Append-only approval log. Every transition (submitted, approved, rejected, withdrawn, restarted, reset, escalated, completed) lands as a row with actor, timestamp, and comment. Only the comment is writable and deletion is refused at the model level.
- SLA and escalation engine. Per-rule SLA hours compute a due time and an on-track, at-risk, or breached state. An hourly cron sends a pre-deadline reminder and performs one-shot auto-escalation to a configured group, with per-record error isolation.
- Reject, withdraw, restart. Any approver in the pending group can reject (terminal). The requester or an accounting manager can withdraw the whole request. Withdrawn or rejected requests restart from step zero.
- Activity-driven notifications. Each new step schedules a To-Do activity on the members of the responsible group, surfaced in their personal activity inbox and kanban.
Honest about the edges
What this does not do, so nothing surprises you.
- Each step is bound to exactly one approver group. There is no per-step un-approve and no delegation or substitution; withdraw cancels the whole request rather than rolling back a single signature.
- Re-approval on material change is keyed on the amount delta only. A partner change that leaves the total unchanged does not reset the request, and the set of watched edit fields (lines, amounts, currency, partner) is fixed in code, not admin-configurable.
- Re-approval fires only after the request is fully approved. A material edit made while approval is still in progress does not reset or rewind partial signatures, and edits during partial approval are not gated.
- An approver group with zero members is not separately guarded: a step whose group is empty schedules no activity and simply cannot advance, with no explicit message.
- Step activities notify only the first several members of a group, so very large approver groups will not raise an activity for the trailing members. There is no separation-of-duties constraint, so the same user can prepare and approve unless your group membership prevents it.
- Notifications are activity-driven (To-Do items), not email. Shipped email templates and SLA chatter posts are not wired into the step-advance path.
odoo 19 approval workflow, vendor bill approval, multi-step approval accounting, journal entry approval, approval matrix by amount, odoo community spend authorization, invoice approval hierarchy, approval SLA escalation odoo, block posting until approved, audit log approval odoo
Languages
Available in 19 languages
The interface ships translated out of the box. Switch language in Odoo and the fields, menus, and messages follow.
Please log in to comment on this module