Building a Visual Approval Workflow Engine

Every ERP ends up needing approvals: purchase orders above a limit, price discounts, work-order release, credit notes. Buying a full BPM suite is heavy; hand-coding if (amount > 50000) sendTo(manager) for every case rots quickly. The middle path is a small workflow engine with a visual designer. This article describes the model we build, deliberately pragmatic.

Scope it down

Resist the temptation to build a general-purpose BPM engine. In our experience, approval flows in ERP need exactly four node types:

  • Start / end — bookends of the definition.
  • Approval — a task assigned to a person or role (department head, finance controller), with a timeout and an escalation path.
  • Condition — a boolean expression on document fields (amount, department, item category) deciding which branch to take.
  • Countersign group — multiple approvers in and (all must approve) or or (first decides) mode.

Four node types cover essentially every real approval we have met. Anything fancier — loops, subprocesses, message events — is where general BPM tools spend 80% of their complexity budget for 5% of the value.

Definitions, versions and instances

A process definition is a versioned graph: nodes, edges, and per-node configuration. An instance is one run attached to one business document (a purchase order, a discount request). The rules that keep this safe:

  • Definitions are immutable once published. Editing a published flow creates a new version.
  • An instance binds to the definition version it was started with, and stores a snapshot. A flow started under v3 keeps running under v3 even after v4 publishes — mid-flight documents never silently change rules.
  • Only one version is the default for new instances; old versions retire naturally as their instances finish.

The instance state machine

Keep the instance state model embarrassingly simple. Each instance has a current node; each node task has a state of pending / approved / rejected. Transitions:

  1. Start → first node task created, assigned per node config (role resolution happens at assignment time, so a vacant position does not stall the flow silently — it escalates).
  2. Approver acts → task resolved, engine evaluates the node's completion rule (countersign mode, vote thresholds).
  3. Next node chosen by evaluating outgoing condition edges in order.
  4. Reject → instance goes to a terminal REJECTED state, optionally with a "resubmit" path that restarts the flow.
  5. No more nodes → APPROVED; the engine fires an event the business module listens for.

Every action — assignment, approval, rejection, escalation — is an append-only log entry on the instance. The approval trail a manager sees is just this log rendered; so is the audit evidence.

Integration with business modules

The engine owns orchestration, never business data. Business modules attach a workflow to a document type and subscribe to the APPROVED / REJECTED events. This keeps the engine a pure component of the modular monolith: sales, purchasing, production and finance all reuse the same engine without the engine knowing what a "sales order" is. Timeouts and escalations run on a scheduled sweeper that scans pending tasks past their deadline.

The visual designer

The designer is a canvas where consultants drag the four node types, connect them, and configure each node in a side panel. Under the hood it edits the same JSON graph the engine executes — no model-to-model translation. Validation before publish checks the boring-but-fatal things: unreachable nodes, conditions without else branches, approval nodes without assignees. Keep the canvas simple (grid snap, straight edges); you are drawing approval chains, not circuit boards. For the rendering side of similarly dense enterprise screens, see Enterprise Web UI Patterns.

Conclusion

A workflow engine earns its keep by being smaller than people expect: four node types, versioned immutable definitions, a per-node state machine, an append-only action log, and event-based integration. Build that well and every approval in the ERP — present and future — becomes configuration instead of code.