The Approvals module provides a configurable multi-level authorization engine that governs commercial decisions across B2B Import ERP. Rather than hard-coding who approves what, the system lets each tenant define reusable approval flows (sequential, parallel, or mixed), attach amount-based rules to business objects, and route incoming approval requests to the right people automatically. Every decision — regardless of outcome — is recorded immutably in bothDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/ency07/B2B-import/llms.txt
Use this file to discover all available pages before exploring further.
approval_request_steps and the central audit_log.
What Can Be Approved
The approval engine currently supports the following object types, as defined by theentity_type CHECK constraint in approval_rules and approval_requests:
Quotations
The primary use case. Quotes entering
EN_REVISION automatically trigger approval routing based on the configured amount rules.Invoices
Invoice approval workflows for billing authorization before issuance to clients.
Contracts
Contract sign-off workflows for formal service agreements.
Approval States
Eachapproval_requests record and each approval_request_steps record tracks its own state independently:
CANCELADA) by an administrator before it reaches a final decision.
| State | Description |
|---|---|
PENDIENTE | Awaiting action from the assigned approver |
EN_PROCESO | At least one step approved; remaining steps still pending |
APROBADA | All required steps approved; linked object updated accordingly |
RECHAZADA | A required step was rejected; linked object set to RECHAZADA |
AJUSTES_SOLICITADOS | Approver requested changes; linked quote returned to EN_REVISION |
CANCELADA | Request cancelled by administrator |
Approval Flow
The following sequence describes how a Quote triggers and moves through the approval engine:- Commercial executive moves the quote from
BORRADOR→EN_REVISION. - The
route_quote_approvalstrigger fires (BEFORE UPDATE OF statusonquotes) and:- Searches
approval_rulesfor an active rule matchingentity_type = 'QUOTE'and the quote’stotal_amount. - If a rule with a
flow_idis found: creates anapproval_requestsrecord (PENDIENTE) and instantiates oneapproval_request_stepsrow per step in the flow. - If no rule is found: escalates automatically to the
GERENTE_GENERALrole with a single-step request. - If
flow_id = NULLon the rule: auto-approves the quote immediately.
- Searches
- Approver calls
resolve_approval_step(requestId, stepOrder, decision, comments)with a mandatory comment. - The function validates the approver’s identity and role, checks sequential ordering (in
SECUENCIALflow type), updates the step, and evaluates whether all required steps have been resolved. - If all required steps are
APROBADA: theapproval_requestsstatus becomesAPROBADAand the quote status is set toAPROBADA. - If any step is
RECHAZADA: the request and the quote both becomeRECHAZADA. - If a step is
AJUSTES_SOLICITADOS: the request status becomesAJUSTES_SOLICITADOSand the quote is returned toEN_REVISIONfor the commercial team to revise.
While an approval request is in
PENDIENTE or EN_PROCESO state, the quote and all of its line items are locked against modification. Attempting to edit them raises a database-level exception.Multi-Level Approvals
Workflows can chain any number of approvers. Each step in anapproval_flow is assigned either to a role (role_id) or a specific user (user_id) — never both. The step_order determines the sequence.
| Flow Type | Behaviour |
|---|---|
SECUENCIAL | Steps must be resolved in order; step N cannot be approved until step N-1 is APROBADA |
PARALELA | All steps can be resolved simultaneously; the request resolves when all required steps complete |
MIXTA | A combination of sequential and parallel steps, defined per flow configuration |
APROBADA status, all steps marked required = true must be individually approved. Optional steps (required = false) do not block final resolution.
approval_requests Table — Key Columns
The following columns on the approval_requests table carry the most operationally significant information:
| Column | Type | Description |
|---|---|---|
request_code | varchar(50) | Auto-generated code APR-000001; unique per tenant |
entity_type | varchar(100) | Object being approved: QUOTE, INVOICE, CONTRACT |
entity_id | uuid | FK to the object’s primary key |
flow_id | uuid | FK to approval_flows; NULL for escalation requests |
requested_by | uuid | User who triggered the approval (e.g. the commercial executive) |
requested_at | timestamp | When the approval was requested |
status | enum | Current state of the request (see table above) |
resolved_by | uuid | User who made the final decision |
resolved_at | timestamp | Timestamp of the final decision |
comments | text | Final decision comment (populated by resolve_approval_step) |
approval_request_steps, including the step-level comments, resolved_by, and resolved_at.
RBAC — Role-Based Access
| Action | Roles |
|---|---|
| Configure flows & rules | ADMINISTRADOR, GERENTE_GENERAL, SUPER_ADMIN |
Approve / reject quotes (quotes.approve) | GERENTE_GENERAL, DIRECTOR_COMERCIAL |
| View approval requests | All authenticated tenant users |
| Cancel an approval request | ADMINISTRADOR, GERENTE_GENERAL, SUPER_ADMIN |
Audit Trail
Every approval decision is permanently recorded across three layers:approval_request_steps— the per-step decision record withresolved_by,resolved_at, and the fullcommentstext.business_events— semantic events emitted byresolve_approval_step()anddispatch_approval_events():
| Event Code | Emitted When |
|---|---|
APPROVAL_REQUEST_CREATED | New approval request instantiated |
APPROVAL_REQUEST_APPROVED | A step is approved |
APPROVAL_REQUEST_REJECTED | A step is rejected |
APPROVAL_REQUEST_ADJUSTMENTS_REQUESTED | Approver requests changes |
APPROVAL_REQUEST_CANCELLED | Request cancelled by administrator |
APPROVAL_FLOW_CREATED | A new approval flow is configured |
APPROVAL_RULE_CREATED | A new amount rule is added |
audit_log— theprocess_audit_log()trigger fires on everyINSERT,UPDATE, orDELETEacross all approval tables, capturing the full before/after JSONB diff.
block_physical_approval_delete. All records are permanent and can only be logically deactivated via soft-delete.