Kailing TechnologyExpense Control and Reimbursement System › How to design a voucher rule engine

Voucher rule engine design: reimbursement-to-voucher is more than a mapping table

Kailing Technology · 2026-09-05

A reimbursement form records "who, for what, and how much was spent," while a voucher requires "debit and credit accounts, amounts, and auxiliary accounting dimensions." The translation layer in between cannot be solved by a single table—The same expense type, across different departments, different projects, and different invoice types, maps to different accounts. What is truly needed is a rule engine that is configurable, traceable, and supports trial calculation.

The engine must derive four types of information from documents

Derivation targetDetermining factorDescription
Debit AccountExpense Type + Dimensions such as Department / ProjectExpense Attribution: The Most Core and Most Complex
Credit accountPayment methodDetermined by "who advanced the money first"
Tax amount splittingInvoice type + expense type deductibility + tax rateDetermines whether the expense amount includes tax
Supplementary accounting dimensionsDirect pass-through of document fieldsNo mapping required, but completeness must be verified

Of the four categories, only the first requires complex rules, yet it is precisely the part that determines the success or failure of the entire solution.

The most common design mistake: writing conditions as fixed columns

Many implementations design matching conditions as fixed columns on a rule table—one column for dept_id, one for project_id, and one for invoice_type. The consequences of doing this are:Each additional matching dimension requires changing the table structure, changing the code, and re-releasing the version

The correct approach is to separate rules and conditions into two tables, with no limit on the number of condition rows, and an AND relationship between them:

voucher_rule Mapping rule main table rule_type Rule type: debit account / credit account / tax handling target_subject Accounting account code used after matching priority Priority; smaller value takes precedence effective_from Effective date effective_to Expiration date (empty = valid long-term) enabled Whether enabled remark Business meaning description (for finance personnel to read) voucher_rule_cond Rule condition table (one rule may contain multiple conditions, AND) rule_id Owning rule field Field name: expense_type / dept / project_type / pay_method ... operator Operator: = / in / like / is_null value Condition value

After this design, adding a new matching dimension only requires adding a value to the field enum, with no need to change the table structure or release a new version.

Account master data cannot be omitted

An account cannot just store a code string in a rule table; it must have an independent master data table. The key is required_dims This field—it enables the system to complete dimension integrity validation at the voucher generation stage, rather than waiting until after it is pushed to the general ledger and rejected.

subject Accounting account master data code Account code name Account name is_leaf Whether it is a leaf-level account (only leaf-level accounts allow posting) direction Balance direction: debit / credit required_dims Required auxiliary accounting dimensions, such as ["dept", "project"] effective_from Effective date

Priority: Do not let finance fill it in manually

Having finance staff manually fill in priority numbers is almost certain to produce errors—they need to compare the precision of all rules in their heads. A more reliable approach is for the system to calculate automatically based on the number of conditions:Rules with more conditions are more precise and have higher priority

priority = 1000 - (number of conditions × 100) Rule A: expense type = travel expense AND department = sales department → 2 conditions → priority 800 Rule B: expense type = travel expense → 1 condition → priority 900 Rule C: (no conditions, fallback) → 0 conditions → priority 1000 The travel expense of the sales department matches A, B, and C simultaneously; take A in ascending priority order

On the basis of automatic calculation, a manual fine-tuning entry is retained to handle special cases where the number of conditions is the same but sequence needs to be distinguished.

There must be fallback rules

The matching process is: filter candidate rules (type match, enabled, document date within effective period) → judge one by one whether all conditions are fully satisfied → if multiple are hit, take the first in ascending priority order → if none are hit, use the fallback rule.

Fallback rules cannot be omitted. Without it, when a customer adds a new expense type, voucher generation will fail, and at that point the reimbursement form has already been approved and cannot be rolled back, directly blocking the business. A fallback account (such as "Unclassified Expenses") allows the business to proceed first, with finance adjusting afterward.

The trial calculation function likewise cannot be omitted.

The engine must provide trial calculation capability: input a historical reimbursement form and output the complete derivation chain—which rule was hit, why it was hit, and what account is ultimately generated.

Trial calculation output itemsContent examples
Input summaryExpense Type = Travel Expenses - Accommodation, Department = Sales Department, Invoice Type = Special VAT Invoice, Tax Rate 6%
Candidate rules3 hits: Rule A (800) / Rule B (900) / Fallback (1000)
Ultimately adoptsRule A — Conditions "Expense Type=Travel Expense" and "Department=Sales Department" are both satisfied
Derivation resultDebit 660101 Selling Expenses—Travel Expenses

Without this function, misconfigured rules can only be discovered when voucher generation errors occur, making troubleshooting extremely costly, and finance staff cannot self-verify.

Historical vouchers cannot be affected by rule changes

Half a year later, when finance adjusts the mapping rules, you still need to be able to explain "why this historical voucher was assigned this account at the time". This relies on two mechanisms: the effective_from / effective_to validity period of the rule table, and Record the hit_rule_id rule snapshot on the voucher entry

When generating vouchers, select the rule version by the period of the "document date," not by the current time.

How is this scenario handled in Kailing Technology's expense control system?

A data model that separates rules from conditions; adding new matching dimensions requires no table structure changes or releases; priority is automatically calculated by the number of conditions with manual fine-tuning retained; built-in fallback rules and simulation tools allow finance to self-serve input historical documents and view the complete derivation chain; voucher entries record snapshots of hit rules, so rule changes do not affect already-generated historical vouchers.

Learn about the Kailing Technology expense control and reimbursement system →

Common Questions

Is it not enough to use one mapping table for converting reimbursement to vouchers?

No. The same expense type may correspond to different accounts in different departments, different projects and different invoice types, and a one-to-one mapping table cannot express multi-condition matching and priority.

Priority: Why can't finance fill it in?

Finance needs to compare the precision of all rules in the mind; once there are many rules, errors are inevitable. Automatic calculation by number of conditions is more reliable, while retaining manual fine-tuning for special cases.

Will fallback rules lead to misrecorded accounts?

A small number of documents will enter pending classification expenses, but this is much better than voucher generation failure or approved reimbursement forms getting stuck. Finance can make centralized adjustments afterward.

If rules change, will previous vouchers change?

No. Rule tables have effective periods. Voucher entries record the rule ID hit at that time. The rule version is selected according to the document date, so historical vouchers can be fully traced.