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