KB-212 – Accounting Engine Architecture¶
Knowledge Base ID: KB-212
Chapter: 9 - Account Resolution Pipeline
Project: BLACK ERP
Version: 0.9
Status: Draft
Last Updated: 2026-06-26
Applies To: ADempiere 3.9.4
Purpose¶
This chapter explains the complete account resolution pipeline inside the ADempiere Accounting Engine.
Previous chapters introduced:
- Doc
- Fact
- FactLine
- Accounting Metadata
- Account Configuration
This chapter connects them together and follows the actual execution flow used during document posting.
Unlike previous chapters, this document is based directly on the ADempiere 3.9.4 source code.
High Level Flow¶
When a document is posted, ADempiere executes the following pipeline.
Business Document
│
▼
Doc_*
│
▼
createFacts()
│
▼
Resolve Accounts
│
▼
Fact.createLine()
│
▼
FactLine.setAccount()
│
▼
Currency Conversion
│
▼
Fact.save()
│
▼
FACT_ACCT
Every accounting document follows this architecture.
Step 1 — Posting Starts¶
The Posting Engine begins inside the corresponding document class.
Examples:
Doc_Invoice
Doc_Payment
Doc_AllocationHdr
Doc_Movement
Doc_MatchInv
Doc_MatchPO
Each implementation overrides:
createFacts(MAcctSchema as)
This method is responsible for building all accounting entries.
Conceptually:
Invoice
↓
Doc_Invoice.createFacts()
Step 2 — Resolving the Required Account¶
Whenever an accounting line must be created, the document requests an account.
The source code exposes two key methods:
getValidCombinationId(...)
and
getAccount(...)
The first method determines which accounting metadata table must be queried.
The second converts the resulting Account Combination into an MAccount object.
Step 3 — Account Type Resolution¶
ADempiere defines internal Account Types.
Examples include:
ACCTTYPE_C_Receivable
ACCTTYPE_V_Liability
ACCTTYPE_Charge
ACCTTYPE_UnallocatedCash
ACCTTYPE_BankAsset
ACCTTYPE_CashExpense
ACCTTYPE_WriteOff
ACCTTYPE_ProjectAsset
These constants identify the business meaning of the requested account.
The Posting Engine never requests accounts by General Ledger number.
Instead, it requests them by business purpose.
Conceptually:
Need Vendor Liability
↓
ACCTTYPE_V_Liability
Step 4 — Metadata Lookup¶
The method
getValidCombinationId()
contains the metadata lookup logic.
Depending on the requested Account Type, it executes SQL against different accounting tables.
Examples:
Vendor Accounting
SELECT V_Liability_Acct
FROM C_BP_Vendor_Acct
Customer Accounting
SELECT C_Receivable_Acct
FROM C_BP_Customer_Acct
Charge Accounting
SELECT CH_Expense_Acct
FROM C_Charge_Acct
Bank Accounting
SELECT B_Asset_Acct
FROM C_BankAccount_Acct
Cash Book Accounting
SELECT CB_Expense_Acct
FROM C_CashBook_Acct
This confirms that ADempiere resolves accounts through metadata instead of hardcoded logic.
Step 5 — Building MAccount¶
Once the Valid Combination ID has been obtained, the Posting Engine creates an MAccount object.
Conceptually:
C_ValidCombination
↓
MAccount
MAccount represents the complete accounting combination including:
- Natural Account
- Organization
- Business Partner
- Product
- Project
- Campaign
- Activity
- User Elements
- Sub Account
This object is passed to the Fact layer.
Step 6 — Creating the FactLine¶
The Posting Engine calls
Fact.createLine(...)
This method creates a new FactLine.
Inputs:
- DocLine
- MAccount
- Currency
- Debit Amount
- Credit Amount
Conceptually:
DocLine
+
MAccount
↓
Fact.createLine()
↓
FactLine
At this point no database record exists yet.
Step 7 — Expanding the Accounting Dimensions¶
Immediately after creating the FactLine, ADempiere executes:
FactLine.setAccount()
This method decomposes the MAccount object into individual accounting dimensions.
Examples:
- Account_ID
- Sub Account
- User Element 1
- User Element 2
- Organization
- Project
- Campaign
- Activity
The composite accounting combination becomes explicit FactLine fields.
Step 8 — Currency Conversion¶
FactLine then executes:
convert()
Responsibilities include:
- Source Amount
- Accounting Amount
- Currency Conversion
- Accounting Currency Precision
After conversion, both document currency and accounting currency are available.
Step 9 — Account Validation¶
Before saving, Fact executes:
checkAccounts()
Validation includes:
- Account exists
- Element Value exists
- Account is Active
- Account is not Summary
- Account can be posted
Only valid accounts continue to persistence.
Step 10 — Persisting FACT_ACCT¶
Finally,
Fact.save()
stores every FactLine into
FACT_ACCT
The posting process ends here.
Conceptually:
Fact
↓
FactLine
↓
FACT_ACCT
Complete Resolution Pipeline¶
The entire execution flow can be summarized as follows.
Business Document
↓
Doc_Invoice.createFacts()
↓
Account Type
↓
getValidCombinationId()
↓
Accounting Metadata Tables
↓
C_ValidCombination
↓
MAccount
↓
Fact.createLine()
↓
FactLine.setAccount()
↓
convert()
↓
checkAccounts()
↓
Fact.save()
↓
FACT_ACCT
BLACK ERP Engineering Notes¶
The Mexican VAT Cash Basis implementation integrates naturally into this pipeline.
The customization extends the posting logic inside:
Doc_AllocationHdr.createFacts()
Additional VAT accounting entries are generated before the standard Fact object is persisted.
The customization therefore respects the standard Accounting Engine architecture.
No modifications were required to:
- Fact
- FactLine
- MAccount
- FACT_ACCT
The extension simply injects additional accounting lines using the existing infrastructure.
This design minimizes future upgrade risks.
Engineering Principles¶
The source code demonstrates several important architectural decisions.
- Business documents never know General Ledger accounts.
- Metadata resolves accounting configuration.
- MAccount encapsulates the accounting combination.
- Fact creates accounting entries.
- FactLine expands accounting dimensions.
- Validation occurs before persistence.
- FACT_ACCT is only the final storage layer.
Key Takeaways¶
- Posting begins inside createFacts().
- Accounts are requested by business purpose, not account number.
- Metadata determines the correct Valid Combination.
- MAccount represents the accounting combination.
- Fact creates accounting entries.
- FactLine expands dimensions and performs currency conversion.
- Account validation occurs before persistence.
- FACT_ACCT stores the final accounting result.
Next Chapter¶
Chapter 10 explores how individual document implementations (Invoice, Payment, Allocation, Shipment, Inventory and others) generate different accounting flows while sharing the same Posting Engine.
Revision History¶
| Version | Date | Description |
|---|---|---|
| 0.9 | 2026-06-26 | Added complete Account Resolution Pipeline based on ADempiere source code walkthrough. |