Skip to main content

Approval Module

The approval module provides a complete workflow engine for building approval-based business processes. It supports visual flow design (React Flow compatible), multi-level approval chains, conditional branching, parallel approval, delegation, rollbacks, and transactional event publishing.

Architecture Overview

Flow Category → Flow → Flow Version → Nodes + Edges

Instance → Tasks → Action Logs
ConceptTableDescription
Flow Categoryapv_flow_categoryHierarchical grouping of flows
Flowapv_flowA workflow definition (e.g., "Leave Request")
Flow Versionapv_flow_versionVersioned snapshot with nodes, edges, and form schema
Flow Nodeapv_flow_nodeA step in the workflow (approval, handle, condition, CC)
Flow Edgeapv_flow_edgeDirected connection between nodes
Instanceapv_instanceA running instance of a flow
Taskapv_taskAn individual approval/handle task assigned to a user
Action Logapv_action_logAudit trail of all actions

Configuration

[vef.approval]
auto_migrate = true
outbox_relay_interval = 5
outbox_max_retries = 10
outbox_batch_size = 100

See Configuration Reference for details.

Binding Modes

ModeConstantDescription
StandaloneBindingStandaloneForm data stored in the approval module's own tables
BusinessBindingBusinessLinks to an existing business data table

Business binding connects the approval flow to your domain tables via BusinessTable, BusinessPkField, BusinessTitleField, and BusinessStatusField.

Node Types

Node KindConstantDescription
StartNodeStartEntry point of the workflow
ApprovalNodeApprovalRequires approval action from assignees
HandleNodeHandleRequires processing/handling action
ConditionNodeConditionBranches based on conditions
CCNodeCCSends notifications to specified users
EndNodeEndTerminal point of the workflow

Approval Methods

When a node has multiple assignees:

MethodConstantBehavior
SequentialApprovalSequentialApprovers process one by one in order
ParallelApprovalParallelApprovers process simultaneously

Pass Rules (for Parallel)

RuleConstantBehavior
AllPassAllAll assignees must approve
AnyPassAnyAt least one approval passes
RatioPassRatioA percentage must approve
Any RejectPassAnyRejectAny rejection fails the node

Assignee Types

KindConstantDescription
UserAssigneeUserSpecific users
RoleAssigneeRoleUsers with a role
DepartmentAssigneeDepartmentDepartment head
SelfAssigneeSelfThe applicant
SuperiorAssigneeSuperiorDirect superior
Dept LeaderAssigneeDepartmentLeaderMulti-level supervisor chain
Form FieldAssigneeFormFieldDetermined by a form field value

Instance Lifecycle

submit → Running → approve/reject → Approved/Rejected
→ withdraw → Withdrawn
→ rollback → Returned
→ terminate → Terminated
→ resubmit → Running (again)

Instance Statuses

StatusConstantFinal?
RunningInstanceRunningNo
ApprovedInstanceApprovedYes
RejectedInstanceRejectedYes
WithdrawnInstanceWithdrawnNo
ReturnedInstanceReturnedNo
TerminatedInstanceTerminatedYes

Task Statuses

StatusConstantFinal?
WaitingTaskWaitingNo
PendingTaskPendingNo
ApprovedTaskApprovedYes
RejectedTaskRejectedYes
HandledTaskHandledYes
TransferredTaskTransferredYes
Rolled BackTaskRolledBackYes
CanceledTaskCanceledYes
RemovedTaskRemovedYes
SkippedTaskSkippedYes

Actions

ActionConstantDescription
SubmitActionSubmitStart a new instance
ApproveActionApproveApprove a task
HandleActionHandleComplete a handle task
RejectActionRejectReject a task
TransferActionTransferTransfer to another user
WithdrawActionWithdrawApplicant withdraws
CancelActionCancelCancel a task
RollbackActionRollbackRoll back to a previous node
Add AssigneeActionAddAssigneeDynamically add an assignee
Remove AssigneeActionRemoveAssigneeRemove an assignee
ResubmitActionResubmitResubmit a returned instance
ReassignActionReassignAdmin reassigns a task
TerminateActionTerminateAdmin force-terminates

Rollback Configuration

PropertyOptions
RollbackTypenone, previous, start, any, specified
RollbackDataStrategyclear (reset form), keep (preserve data)

Empty Assignee Handling

When no assignee is found for a node:

ActionConstant
Auto-passEmptyAssigneeAutoPass
Transfer to adminEmptyAssigneeTransferAdmin
Transfer to superiorEmptyAssigneeTransferSuperior
Transfer to applicantEmptyAssigneeTransferApplicant
Transfer to specifiedEmptyAssigneeTransferSpecified

Timeout Handling

ActionConstantBehavior
NoneTimeoutActionNoneMark timeout only
Auto PassTimeoutActionAutoPassAutomatically approve
Auto RejectTimeoutActionAutoRejectAutomatically reject
NotifyTimeoutActionNotifySend notification only
Transfer AdminTimeoutActionTransferAdminTransfer to node admin

Form Data Storage

ModeConstantLocation
JSONStorageJSONapv_instance.form_data (JSONB column)
TableStorageTableDynamic table apv_form_data_{flow_code}

Event Outbox

The approval module uses the transactional outbox pattern for reliable event publishing. Events are written to apv_event_outbox within the same transaction as the approval action, then relayed asynchronously.

StatusConstant
PendingEventOutboxPending
ProcessingEventOutboxProcessing
CompletedEventOutboxCompleted
FailedEventOutboxFailed

Delegation

Users can delegate their approval authority to others:

type Delegation struct {
DelegatorID string // Who delegates
DelegateeID string // Who receives delegation
FlowCategoryID *string // Optional: limit to category
FlowID *string // Optional: limit to specific flow
StartTime timex.DateTime // Delegation start
EndTime timex.DateTime // Delegation end
IsActive bool
}

Flow Definition (React Flow Compatible)

The FlowDefinition struct is compatible with React Flow's JSON format:

type FlowDefinition struct {
Nodes []NodeDefinition `json:"nodes"`
Edges []EdgeDefinition `json:"edges"`
}

Each NodeDefinition contains a Kind and typed Data that is parsed into the appropriate struct (StartNodeData, ApprovalNodeData, HandleNodeData, ConditionNodeData, CCNodeData, EndNodeData).

Instance Number Generation

Implement the InstanceNoGenerator interface to customize instance numbering:

type InstanceNoGenerator interface {
Generate(ctx context.Context, flowCode string) (string, error)
}