ExReconcile - ledger reconciliation for Elixir.
Given two lists of ExReconcile.Transaction structs (e.g. a bank export and an
accounting system export), ExReconcile finds matching pairs, surfaces discrepancies,
and identifies transactions that appear in only one source.
Quick start
left = [
ExReconcile.Transaction.new(id: "REF-1", amount: 1050, date: ~D[2024-01-15], description: "Coffee"),
ExReconcile.Transaction.new(id: "REF-2", amount: 5000, date: ~D[2024-01-16], description: "Taxi")
]
right = [
ExReconcile.Transaction.new(id: "REF-1", amount: 1050, date: ~D[2024-01-15], description: "COFFEE SHOP"),
ExReconcile.Transaction.new(id: "REF-3", amount: 200, date: ~D[2024-01-17], description: "Unknown")
]
result = ExReconcile.reconcile(left, right, match_on: [:id])
# %ExReconcile.Result{
# matched: [],
# discrepancies: [{txn_ref1_left, txn_ref1_right, [%{field: :description, ...}]}],
# unmatched_left: [txn_ref2],
# unmatched_right: [txn_ref3]
# }
IO.puts ExReconcile.format(result)Matching strategies
Control matching with the :match_on option and tolerance settings:
# Match on ID (exact), ignore description differences
ExReconcile.reconcile(left, right,
match_on: [:id],
description_match: :ignore
)
# Match on amount + date, allow ±2 days and ±5 cents
ExReconcile.reconcile(left, right,
match_on: [:amount, :date],
amount_tolerance: 5,
date_tolerance: 2
)See ExReconcile.Config for all available options.
Summary
Functions
Format a reconciliation result as a human-readable text report.
Reconcile two lists of transactions.
Functions
@spec format( ExReconcile.Result.t(), keyword() ) :: String.t()
Format a reconciliation result as a human-readable text report.
Options
:title- report header. Defaults to"Reconciliation Report".:show_matched- include the full list of matched pairs. Defaults tofalse.
Examples
iex> result = ExReconcile.reconcile([], [])
iex> ExReconcile.format(result) =~ "CLEAN"
true
@spec reconcile([Transaction.t()], [Transaction.t()], keyword()) :: ExReconcile.Result.t()
Reconcile two lists of transactions.
Returns an ExReconcile.Result with four fields:
:matched-[{left_txn, right_txn}]perfectly reconciled pairs:discrepancies-[{left_txn, right_txn, [diff]}]pairs that match by key but differ in one or more field values:unmatched_left- transactions inleftwith no counterpart inright:unmatched_right- transactions inrightwith no counterpart inleft
Options
Accepts the same keyword options as ExReconcile.Config.new/1.
| Option | Default |
|---|---|
:match_on | [:amount, :date] |
:amount_tolerance | 0 |
:date_tolerance | 0 |
:description_match | :case_insensitive |
Examples
iex> alias ExReconcile.Transaction
iex> left = [Transaction.new(amount: 100, date: ~D[2024-01-01])]
iex> right = [Transaction.new(amount: 100, date: ~D[2024-01-01])]
iex> result = ExReconcile.reconcile(left, right)
iex> length(result.matched)
1
iex> result.unmatched_left
[]
iex> alias ExReconcile.Transaction
iex> left = [Transaction.new(id: "X1", amount: 100)]
iex> right = [Transaction.new(id: "X1", amount: 105)]
iex> result = ExReconcile.reconcile(left, right, match_on: [:id])
iex> [{_l, _r, diffs}] = result.discrepancies
iex> hd(diffs).field
:amount