1
0
Fork 0
mirror of https://github.com/maybe-finance/maybe.git synced 2025-07-18 20:59:39 +02:00
Maybe/app/models/account/opening_balance_manager.rb

100 lines
2.7 KiB
Ruby
Raw Normal View History

class Account::OpeningBalanceManager
Result = Struct.new(:success?, :changes_made?, :error, keyword_init: true)
def initialize(account)
@account = account
end
def has_opening_anchor?
opening_anchor_valuation.present?
end
# Most accounts should have an opening anchor. If not, we derive the opening date from the oldest entry date
def opening_date
return opening_anchor_valuation.entry.date if opening_anchor_valuation.present?
[
account.entries.valuations.order(:date).first&.date,
account.entries.where.not(entryable_type: "Valuation").order(:date).first&.date&.prev_day
].compact.min || Date.current
end
def opening_balance
opening_anchor_valuation&.entry&.amount || 0
end
def set_opening_balance(balance:, date: nil)
resolved_date = date || default_date
# Validate date is before oldest entry
if date && oldest_entry_date && resolved_date >= oldest_entry_date
return Result.new(success?: false, changes_made?: false, error: "Opening balance date must be before the oldest entry date")
end
if opening_anchor_valuation.nil?
create_opening_anchor(
balance: balance,
date: resolved_date
)
Result.new(success?: true, changes_made?: true, error: nil)
else
changes_made = update_opening_anchor(balance: balance, date: date)
Result.new(success?: true, changes_made?: changes_made, error: nil)
end
end
private
attr_reader :account
def opening_anchor_valuation
@opening_anchor_valuation ||= account.valuations.opening_anchor.includes(:entry).first
end
def oldest_entry_date
@oldest_entry_date ||= account.entries.minimum(:date)
end
def default_date
if oldest_entry_date
[ oldest_entry_date - 1.day, 2.years.ago.to_date ].min
else
2.years.ago.to_date
end
end
def create_opening_anchor(balance:, date:)
account.entries.create!(
date: date,
name: Valuation.build_opening_anchor_name(account.accountable_type),
amount: balance,
currency: account.currency,
entryable: Valuation.new(
kind: "opening_anchor"
)
)
end
def update_opening_anchor(balance:, date: nil)
changes_made = false
ActiveRecord::Base.transaction do
# Update associated entry attributes
entry = opening_anchor_valuation.entry
if entry.amount != balance
entry.amount = balance
changes_made = true
end
if date.present? && entry.date != date
entry.date = date
changes_made = true
end
entry.save! if entry.changed?
end
changes_made
end
end