Files
discourse-hledger/lib/hledger/equity.rb
T
2026-09-27 10:31:47 +02:00

69 lines
1.8 KiB
Ruby

# frozen_string_literal: true
require "bigdecimal"
module Hledger
module Equity
module_function
# rows: array of [account_name, amounts] where amounts are normalized hashes
# with "commodity" and "quantity". Returns groups per commodity.
def distribute(rows)
buckets = Hash.new { |h, k| h[k] = Hash.new { |hh, hk| hh[hk] = BigDecimal(0) } }
rows.each do |account, amounts|
holder = holder_name(account)
next if holder.blank?
Array(amounts).each do |amount|
next if amount.blank?
commodity = amount["commodity"].to_s
buckets[commodity][holder] += BigDecimal(amount["quantity"]).abs
end
end
buckets
.map do |commodity, holders|
total = holders.values.reduce(BigDecimal(0), :+)
{
"commodity" => commodity,
"total" => amount_hash(commodity, total),
"holders" =>
holders
.map do |holder, quantity|
{
"holder" => holder,
"amount" => amount_hash(commodity, quantity),
"percentage" => percentage(quantity, total),
}
end
.sort_by { |holder| -BigDecimal(holder["amount"]["quantity"]) },
}
end
.sort_by { |group| group["commodity"] }
end
def holder_name(account)
parts = account.to_s.split(":")
return nil unless parts.first == "equity"
parts[1]
end
def percentage(quantity, total)
return "0.00" if total.zero?
format("%.2f", (quantity / total) * 100)
end
def amount_hash(commodity, quantity)
{
"commodity" => commodity,
"quantity" => quantity.to_s("F"),
"display" => [quantity.to_s("F"), commodity].reject(&:blank?).join(" "),
}
end
end
end