1
0
Fork 0
mirror of https://github.com/maybe-finance/maybe.git synced 2025-08-04 21:15:19 +02:00

Multi-currency support: Money + Currency class improvements (#553)

* Money improvements

* Replace all old money usage
This commit is contained in:
Zach Gollwitzer 2024-03-18 11:21:00 -04:00 committed by GitHub
parent e5750d1a13
commit fe2fa0eac1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
43 changed files with 2982 additions and 196 deletions

View file

@ -1,5 +1,6 @@
class Account < ApplicationRecord
include Syncable
include Monetizable
validates :family, presence: true
@ -9,6 +10,8 @@ class Account < ApplicationRecord
has_many :valuations
has_many :transactions
monetize :balance
enum :status, { ok: "ok", syncing: "syncing", error: "error" }, validate: true
scope :active, -> { where(is_active: true) }

View file

@ -6,8 +6,8 @@ class Account::BalanceCalculator
def daily_balances(start_date = nil)
calc_start_date = [ start_date, @account.effective_start_date ].compact.max
valuations = @account.valuations.where("date >= ?", calc_start_date).order(:date).select(:date, :value)
transactions = @account.transactions.where("date > ?", calc_start_date).order(:date).select(:date, :amount)
valuations = @account.valuations.where("date >= ?", calc_start_date).order(:date).select(:date, :value, :currency)
transactions = @account.transactions.where("date > ?", calc_start_date).order(:date).select(:date, :amount, :currency)
oldest_entry = [ valuations.first, transactions.first ].compact.min_by(&:date)
net_transaction_flows = transactions.sum(&:amount)

View file

@ -1,7 +1,9 @@
class AccountBalance < ApplicationRecord
belongs_to :account
include Monetizable
belongs_to :account
validates :account, :date, :balance, presence: true
monetize :balance
scope :in_period, ->(period) { period.date_range.nil? ? all : where(date: period.date_range) }

View file

@ -0,0 +1,14 @@
module Monetizable
extend ActiveSupport::Concern
class_methods do
def monetize(*fields)
fields.each do |field|
define_method("#{field}_money") do
value = self.send(field)
value.nil? ? nil : Money.new(value, currency)
end
end
end
end
end

View file

@ -1,9 +1,13 @@
class Family < ApplicationRecord
include Monetizable
has_many :users, dependent: :destroy
has_many :accounts, dependent: :destroy
has_many :transactions, through: :accounts
has_many :transaction_categories, dependent: :destroy, class_name: "Transaction::Category"
monetize :net_worth, :assets, :liabilities
def snapshot(period = Period.all)
query = accounts.active.joins(:balances)
.where("account_balances.currency = ?", self.currency)
@ -35,7 +39,7 @@ class Family < ApplicationRecord
end
def assets
accounts.active.assets.sum(:balance)
accounts.active.assets.sum(:balance)
end
def liabilities

View file

@ -1,32 +0,0 @@
class Money
attr_reader :amount, :currency
def self.from_amount(amount, currency = "USD")
Money.new(amount, currency)
end
def initialize(amount, currency = :USD)
@amount = amount
@currency = currency
end
def cents(precision: nil)
_precision = precision || CURRENCY_OPTIONS[@currency.to_sym][:precision]
return "" unless _precision.positive?
fractional_part = @amount.to_s.split(".")[1] || ""
fractional_part = fractional_part[0, _precision].ljust(_precision, "0")
end
def symbol
CURRENCY_OPTIONS[@currency.to_sym][:symbol]
end
def separator
CURRENCY_OPTIONS[@currency.to_sym][:separator]
end
def precision
CURRENCY_OPTIONS[@currency.to_sym][:precision]
end
end

View file

@ -14,7 +14,7 @@ class MoneySeries
{
raw: current,
date: current.date,
value: Money.from_amount(current.send(@accessor), current.currency),
value: Money.new(current.send(@accessor), current.currency),
trend: Trend.new(
current: current.send(@accessor),
previous: previous&.send(@accessor),

View file

@ -1,4 +1,6 @@
class Transaction < ApplicationRecord
include Monetizable
belongs_to :account
belongs_to :category, optional: true
@ -6,6 +8,12 @@ class Transaction < ApplicationRecord
after_commit :sync_account
monetize :amount
scope :inflows, -> { where("amount > 0") }
scope :outflows, -> { where("amount < 0") }
scope :active, -> { where(excluded: false) }
def self.ransackable_attributes(auth_object = nil)
%w[name amount date]
end

View file

@ -1,9 +1,10 @@
class Valuation < ApplicationRecord
include Monetizable
belongs_to :account
validates :account, :date, :value, presence: true
after_commit :sync_account
monetize :value
scope :in_period, ->(period) { period.date_range.nil? ? all : where(date: period.date_range) }