67 lines
1.8 KiB
Ruby
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
|