Accounting policies answer how should this event be accounted for? They are
Datalog-backed rules in Logistiki.Knowledge.Program.
Policy selection
The policy/2 relation is derived from the event's facts:
rule policy(E, :cash_deposit) do
event_type(E, :deposit_received)
end
rule policy(E, :corporate_wire_fee) do
event_type(E, :fee_assessed)
event_fee_type(E, :wire_fee)
event_entity_type(E, :corporate)
end
rule policy(E, :retail_wire_fee) do
event_type(E, :fee_assessed)
event_fee_type(E, :wire_fee)
event_entity_type(E, :individual)
end
rule policy(E, :internal_transfer) do
event_type(E, :transfer_settled)
end
rule policy(E, :interest_accrual) do
event_type(E, :interest_accrued)
end
rule policy(E, :refund_paid) do
event_type(E, :refund_issued)
endBecause policies are derived from multiple facts, the knowledge layer genuinely selects a policy based on the event's properties (event type, fee type, entity type, product, jurisdiction, ...) rather than hard-coding a mapping.
Posting templates
Templates answer which postings should be generated by this policy? They are static facts:
fact template(:cash_deposit)
facts :template_posting do
row(:cash_deposit, 1, :debit, :cash_account, :event_amount, :event_currency)
row(:cash_deposit, 2, :credit, :client_liability_account, :event_amount, :event_currency)
endEach template posting spec has a sequence, a direction (:debit/:credit),
a symbolic role, and the amount/currency variables (:event_amount /
:event_currency in v0.1.0).
Account mappings
Account mappings answer which concrete account code should a symbolic role
resolve to? They are derived by account_role/3 rules:
rule account_role(E, :client_liability_account, C) do
event_account(E, C)
end
rule account_role(E, :cash_account, C) do
event_cash_account(E, C)
end
# Static fallback for roles not carried by the event:
rule account_role(E, R, C) do
account_role_static(R, C)
event_type(E, _)
endA static fallback is provided for roles an event does not carry directly (e.g.
account_role_static(:interest_expense_account, "EXPENSES:INTEREST")).
Constraints
requires_dimension/2 facts declare which dimensions a policy requires (e.g.
requires_dimension(:cash_deposit, :entity_id)). The runtime records these for
audit; hard invariants are enforced in Elixir.
Business rules vs. accounting policies vs. ledger invariants
Three distinct rule categories (see Logistiki.Knowledge.Rules):
- Business rules (Datalog): should the event be processed / blocked / approved?
- Accounting policies (Datalog): which policy, template, roles, dimensions?
- Ledger invariants (pure Elixir): debits equal credits, postings positive, posted journals immutable, etc. — never Datalog.
Datalog may say what should happen. Elixir enforces what must always be true.