Files
discourse-hledger/lib/hledger/account_tree.rb
T

67 lines
1.8 KiB
Ruby

# frozen_string_literal: true
module Hledger
# Expands hledger's flat account balance rows into a full account tree.
#
# hledger's machine-readable balance output is always flat and omits parent
# accounts, so ancestors are synthesized here and their balances aggregated
# per commodity.
module AccountTree
module_function
# entries: [[full_account_name, [{ "commodity" =>, "quantity" => BigDecimal, "style" => }]], ...]
#
# Returns tree-ordered rows (parents before children), each:
# { "account" =>, "label" =>, "depth" =>, "root" =>, "amounts" => [...] }
# with "amounts" reusing the per-commodity shape of the input.
def expand(entries)
order = []
nodes = {}
entries.each do |name, amounts|
segments = name.to_s.split(":")
segments.each_with_index do |segment, index|
prefix = segments.first(index + 1).join(":")
node =
nodes[prefix] ||= begin
order << prefix
{
"account" => prefix,
"label" => capitalize(segment),
"depth" => index,
"root" => index.zero?,
"amounts" => {
},
}
end
Array(amounts).each { |amount| add_amount(node["amounts"], amount) }
end
end
order.map do |prefix|
node = nodes.fetch(prefix)
node.merge("amounts" => node["amounts"].values)
end
end
def add_amount(bucket, amount)
commodity = amount["commodity"].to_s
if (existing = bucket[commodity])
existing["quantity"] += amount["quantity"]
else
bucket[commodity] = amount.merge
end
end
def capitalize(segment)
text = segment.to_s
return text unless text.match?(/\A[a-z]/)
text[0].upcase + text[1..]
end
end
end