Appearance
GL Posting Approval
GL Posting Approval controls when a GL draft batch can post immediately and when it must stop for approval first.
Navigation: Administration -> Accounting -> GL Posting Approval
This page has four working areas:
Approval ChainsApproval PoliciesAuthority LimitsPending Approvals
What The Engine Does
Approval is evaluated at the draft-batch level, not line by line.
The runtime checks GL posting in this order:
- Posting date, fiscal period, and posting-rule validation must pass.
- Pre-post handler work such as
VALIDATE,RESOLVE, andPREVIEWmust pass. - Override rules are evaluated.
- Approval policies are evaluated by priority.
- If a policy matches, the batch is routed to the linked chain.
- If no policy matched, authority limits for the preparer role are evaluated.
- If no policy matched and the authority limit is exceeded, submit fails with an authority-limit error.
- If no policy matched and no authority-limit block applies, entity-level fallback approval is checked.
- After final approval, deferred posting work runs and the journals are posted.
This means approval does not replace posting validation. Approval sits on top of posting eligibility.
Screen Layout
Approval Chains
Use this tab to define who approves and in what order.
Approval Policies
Use this tab to define when a chain should apply.
Authority Limits
Use this tab to define what a preparer role may post directly before approval is required.
Pending Approvals
Use this tab to review, approve, reject, or return submitted GL posting batches.
Approval Chains
An approval chain defines the approver path.
Examples:
- branch accountant then branch manager
- finance reviewer and controller in parallel
- any one senior approver
Chain Header Fields
| Field | What it means | Runtime implication |
|---|---|---|
Chain Name | Business-facing name of the chain. | Shown in admin lists, approval routing traces, and matched-policy references. |
Chain Code | Backend-generated identifier. | Used as a stable system reference. Users do not type it. |
Chain Type | SEQUENTIAL, PARALLEL, or ANY_ONE. | Controls whether steps run one after another, all together, or stop after the first approval. |
SLA Hours | Optional default turnaround target for the chain. | Used as the default SLA when a step does not carry its own SLA. |
Description | Plain-language purpose of the chain. | Does not change routing logic, but improves support and audit readability. |
Active | Whether the chain is available for policy routing. | Inactive chains should not be used by effective approval policies. |
Chain Step Fields
| Field | What it means | Runtime implication |
|---|---|---|
Step Order | Sequence number of the step. | Determines progression order in sequential chains. |
Role | Approver role for the step. | Users mapped to this role become eligible approvers for the step. |
Specific User | Optional named approver. | Narrows the step to one user where supported by the chain design. |
Business Unit Scope | SAME, ANY, or SPECIFIC. | Controls whether approvers must belong to the batch BU, any BU, or one chosen BU. |
Specific Business Unit | Required when scope is SPECIFIC. | Restricts the step to approvers assigned in that BU only. |
SLA Hours | Step-specific turnaround target. | Overrides the chain SLA for this step. |
Can Delegate | Whether the approver may delegate. | Allows delegation flows where the engine supports them. |
Is Mandatory | Whether the step must be completed. | In parallel chains, a non-mandatory step may not block completion. |
Approval Policies
An approval policy decides when a draft batch should be routed into a chain.
Examples:
- high-value manual journal
- reversal batch
- backdated posting
- projected abnormal balance batch
Policy Header Fields
| Field | What it means | Runtime implication |
|---|---|---|
Policy Name | Business-facing name of the policy. | Shown in admin lists and matched-policy diagnostics. |
Policy Code | Backend-generated identifier. | Used as the stable system reference. Users do not type it. |
Priority | Evaluation rank, where lower values are checked first. | The first matching active policy normally wins, so priority directly affects routing. |
Approval Chain | The chain to use when the policy matches. | Links policy evaluation to a concrete approval path. |
Business Unit Scope | Optional BU filter for the policy itself. | Blank means the policy can match globally; populated means it only applies in that BU. |
Description | Plain-language purpose of the policy. | Helps support, audit, and policy maintenance. |
Active | Whether the policy is enabled. | Inactive policies remain in history but should not match live batches. |
Condition Tree Model
Each policy contains one or more conditions.
Conditions can be either:
- a leaf condition: compares one posting attribute to one operand
- a group node: combines child conditions with
ANDorOR
Important UI behavior:
Condition #is the form-local reference numberParent Group / Conditionuses that form-local number, not the databasecondition_id- if
Group Operatoris filled withANDorOR, that row acts as a grouping node and itsattributeand comparison fields are usually left empty - the condition catalog is backend-owned; the form loads attributes, operators, and code-backed value options from the API before the drawer opens
Condition Fields
| Field | What it means | Runtime implication |
|---|---|---|
Condition # | Visual reference number inside the form. | Used by the UI to let other rows point to this row as a parent. |
Attribute | The posting attribute to inspect, such as total_amount, source_type, or currency_code. | Comes from the backend condition-metadata catalog and tells the evaluator what value to read from the draft-batch context. |
Comparison Operator | Operator such as eq, gt, between, in, or intersects. | Comes from the backend condition-metadata catalog and determines which operand field is meaningful and how evaluation is performed. |
Group Operator | AND or OR for group nodes. | Makes the row a grouping node that combines child rows instead of evaluating a direct comparison itself. |
Parent Group / Condition | Optional parent row reference. | Builds the condition tree instead of leaving the row as a root condition. |
Value (text / code) | String-style operand. | The UI is backend-driven. Where the backend exposes a finite code set, the field behaves like a guided select/combobox; where the backend marks the attribute as open-ended, typed code entry is still allowed. |
Value (JSON / list) | JSON array or object operand. | Used for list-style or array-style operators such as in, not_in, or intersects. |
Value (numeric) | Numeric operand or lower bound. | Used for numeric operators such as gt, gte, lt, lte, and between. |
Value High (numeric) | Upper bound for range comparisons. | Used only for between. |
Sort Order | Explicit ordering value. | Keeps condition rendering and evaluation order stable when multiple sibling rows exist. |
How To Use The Value Fields
| Operator | Main value field | Typical usage |
|---|---|---|
eq, neq | Value (text / code) or Value (numeric) | Use numeric for amounts/counts and text/code for string-style fields. |
gt, gte, lt, lte | Value (numeric) | Use for thresholds such as amount or count. |
between | Value (numeric) and Value High (numeric) | Use for numeric ranges. |
in, not_in | Value (JSON / list) | Use JSON arrays or comma-separated lists. |
contains | Value (text / code) | Use for substring checks. |
is_null, is_not_null | none | No operand is required. |
intersects | Value (JSON / list) | Use for array overlap such as COA IDs or foundation types. |
value_text Clarification
The backend stores value_text as VARCHAR(500), but operationally it is often a system code field rather than free prose.
The important rule now is this:
- the backend is the source of truth for which attributes exist
- the backend is the source of truth for which operators each attribute allows
- the backend is the source of truth for any finite code list shown in the UI
- the frontend should not invent its own condition catalog
Examples where value_text should normally be a controlled code:
source_typerule_header_codecurrency_codejournal_entry_typepreparer_role_typefiscal_period_status
Examples:
source_type = MANUALjournal_entry_type = REGULARcurrency_code = UGXpreparer_role_type = TELLER
So the safe rule is:
- if the attribute is code-like, enter the system code, not a descriptive sentence
- if the operator is
contains, use real text only when the underlying attribute itself is truly textual
The current UI reflects this by calling the backend metadata endpoint before opening the policy form. The backend now tells the UI whether an operand should be authored through:
- a backend-controlled single select
- a backend-controlled multi-select
- a tenant lookup field
- a tenant multi-lookup field
- numeric entry
- open text/code entry
Practical examples:
currency_code,rule_header_code: tenant lookup, single select behaviorbusiness_unit_id: tenant lookup that stores the numeric BU IDcoa_ids,projected_abnormal_coa_ids: tenant multi-lookupjournal_entry_type,preparer_role_type,foundation_types: backend-controlled select or multi-selectsource_type: backend-controlled single select, currently expected asMANUALorSYSTEMcontains: only makes sense for genuinely textual attributes; code-backed attributes should usually use exact match operators
Important distinction:
preparer_role_typeis a system role-type code such asTELLERorADMINISTRATOR, not a lookup to individual roleshas_subledgeris a Yes/No system code, not a subledger pickerbusiness_unit_iduses a tenant lookup but stores a numeric IDrule_header_codeandcurrency_codeuse tenant lookups but store code valuesfoundation_typesandprojected_abnormal_foundation_typesuse backend-controlled multi-select values, not free JSON typing in the normal case
Operand Control Mapping
The policy form should not make every operand look like generic text. It should choose the control from the backend-owned attribute semantics.
| Attribute style | UI control | Examples | Stored value |
|---|---|---|---|
| Tenant single lookup, text/code | Lookup picker | currency_code, rule_header_code | Code/text value |
| Tenant single lookup, numeric | Lookup picker | business_unit_id | Numeric ID |
| Tenant multi-lookup | Multi lookup picker | coa_ids, projected_abnormal_coa_ids | JSON array of IDs |
| Backend-controlled single select | Select / combobox | journal_entry_type, preparer_role_type, boolean-like flags | System code |
| Backend-controlled multi-select | Multi-select | foundation_types, projected_abnormal_foundation_types | JSON array of system codes |
| Open text/code entry | Text input | truly free-text attributes only | Raw text/code |
| Numeric entry | Number field | total_amount, entry_count, numeric ranges | Numeric value |
Common Condition Attributes
These are the main policy attributes exposed in the current UI:
| Attribute | Meaning | Usually compare with |
|---|---|---|
total_amount | Total batch amount. | Numeric threshold. |
max_single_entry_amount | Largest single journal entry amount in the batch. | Numeric threshold. |
entry_count | Number of journal entries in the batch. | Numeric threshold. |
line_count | Number of journal lines in the batch. | Numeric threshold. |
source_type | Origin of the posting. | Backend-controlled single select using system source codes. |
rule_header_code | GL posting rule code. | Tenant lookup storing the rule code. |
currency_code | Batch currency. | Tenant currency lookup storing the currency code. |
journal_entry_type | Posting classification. | Backend-controlled single select. |
has_till_session | Whether the batch is tied to a till session. | Boolean-like comparison, usually 1 or 0. |
is_cross_bu | Whether multiple BUs are involved. | Boolean-like comparison, usually 1 or 0. |
is_backdated | Whether journal date is earlier than today. | Boolean-like comparison, usually 1 or 0. |
is_future_dated | Whether journal date is later than today. | Boolean-like comparison, usually 1 or 0. |
has_subledger | Whether any line uses a subledger. | Boolean-like comparison, usually 1 or 0. |
preparer_role_type | Role type of the submitting user. | Backend-controlled single select. |
business_unit_id | Submitting BU. | Tenant business-unit lookup storing the numeric BU ID. |
coa_ids | Impacted chart-of-account IDs. | Multi-lookup with in, not_in, or intersects. |
foundation_types | Impacted foundation account categories. | Backend-controlled multi-select. |
projected_abnormal_balance_count | Number of accounts projected abnormal. | Numeric threshold. |
has_projected_abnormal_balance | Whether any abnormal-balance risk exists. | Boolean-like comparison, usually 1 or 0. |
projected_negative_debit_nature_count | Count of debit-nature accounts projected negative. | Numeric threshold. |
projected_negative_credit_nature_count | Count of credit-nature accounts projected negative. | Numeric threshold. |
projected_abnormal_coa_ids | COA IDs projected abnormal. | Multi-lookup with in, not_in, or intersects. |
projected_abnormal_foundation_types | Foundation types projected abnormal. | Backend-controlled multi-select. |
Authority Limits
Authority limits control what a preparer role may post directly before approval is required.
Use authority limits for ceilings. Use approval policies for routing logic.
Authority Limit Fields
| Field | What it means | Runtime implication |
|---|---|---|
Business Unit | Optional BU scope. | Blank means the limit applies across BUs; populated means it only applies in that BU. |
Role | Preparer role the limit applies to. | The draft submitter is checked against this role before direct posting is allowed. |
Currency | Currency in which the thresholds are interpreted. | Threshold evaluation uses this currency context. |
Max Single Entry Amount | Ceiling for one journal entry. | Exceeding it means the user cannot post directly under this limit. |
Max Daily Total | Ceiling for what that role can submit in a day. | Exceeding it means approval is required. |
Max Batch Total | Ceiling for the total batch amount. | Exceeding it means approval is required. |
Allowed Source Types | Optional source filter list. | Limit applies only to those posting source codes; blank means all sources. |
Allowed GL Posting Rules | Optional rule filter list. | Limit applies only to the selected GL posting rules; blank means all rules. |
Active | Whether the limit is enabled. | Inactive limits remain in history but do not affect live posting decisions. |
Important:
- authority limits are not linked to approval chains
- if a policy already matched, the batch routes by policy before authority limits are considered
- if no policy matched and a matching authority limit is exceeded, submit fails with an authority-limit error
- authority limits do not choose approvers; policies and chains do
Pending Approvals
This tab is where approvers work on submitted GL draft batches.
Common actions:
ApproveRejectReturnView History
What Each Action Means
| Action | Meaning | Runtime implication |
|---|---|---|
Approve | Accept the batch at the current step. | Moves the batch to the next approval step or final posting if this was the last required approval. |
Reject | Stop the batch completely. | The batch does not post and remains rejected until a new business action is taken. |
Return | Send the batch back for correction. | The submitter can reopen, edit, and resubmit the draft. |
View History | Inspect the approval timeline. | Does not change state; used for audit and support. |
Visibility Rules
- approvers are resolved from the current chain step
- business-unit scope on the step is enforced
- the submitter cannot approve their own batch
If a batch is not showing for an approver, check:
- the current chain step role
- the user’s role assignment
- the step BU scope
- whether the draft is already moved to another step or final state
Posting Status Flow
Typical states are:
DRAFTSUBMITTEDPENDING_APPROVALAPPROVEDPOSTINGPOSTED
Failure/return states include:
RETURNEDREJECTEDFAILEDOVERRIDE_REQUIREDwhere override rules are in use
Practical Configuration Patterns
High-Value Manual Journals
- authority limit blocks direct posting above the ceiling
- policy checks
source_type = MANUAL - policy checks
total_amount > threshold - matched chain routes to finance approvers
Teller Over Threshold To Manager Chain
- create the teller authority limit to define what may post directly
- create an approval policy that checks fields such as
preparer_role_type = TELLER,total_amount,source_type, andrule_header_code - link that policy to the manager chain
- do not expect the authority limit itself to choose the manager chain
Backdated Posting Control
- policy checks
is_backdated = 1 - optionally add
business_unit_idorsource_type - route to a stricter chain than ordinary postings
Reversal Control
- policy checks
journal_entry_type = REVERSAL - optionally add amount thresholds or rule-code filters
- route to senior review
Troubleshooting
Batch Posted Directly But Should Have Gone For Approval
Check:
- whether a higher-priority policy should have matched first
- whether an authority limit allowed it after no policy matched
- whether the intended policy was active
- whether the policy priority was too low
- whether the condition values used the correct system codes
Policy Did Not Match
Check:
attributeoperator- whether you used
value_text,value_numeric, orvalue_jsoncorrectly - whether the operand should be a code instead of plain text
- whether the current UI/backend contract expects
rule_header_codeinstead of legacyrule_header_id - whether the policy BU scope excluded the batch
Submit Failed On Authority Limit
Current runtime behavior:
- this happens only when no approval policy matched the batch
- the submit call fails with an authority-limit error instead of auto-picking a chain
- if the business wants approval routing on that scenario, configure a matching approval policy and attach the intended chain
Approver Cannot See The Batch
Check:
- the current chain step role
- the user’s business-unit role assignment
- step BU scope
- whether the submitter is trying to approve their own batch
