class Account::TransactionBuilder include ActiveModel::Model TYPES = %w[income expense interest transfer_in transfer_out].freeze attr_accessor :type, :amount, :date, :account, :transfer_account_id validates :type, :amount, :date, presence: true validates :type, inclusion: { in: TYPES } def save if valid? transfer? ? create_transfer : create_transaction end end private def transfer? %w[transfer_in transfer_out].include?(type) end def create_transfer return create_unlinked_transfer(account.id, signed_amount) if transfer_account_id.blank? from_account_id = type == "transfer_in" ? transfer_account_id : account.id to_account_id = type == "transfer_in" ? account.id : transfer_account_id outflow = create_unlinked_transfer(from_account_id, signed_amount.abs) inflow = create_unlinked_transfer(to_account_id, signed_amount.abs * -1) Account::Transfer.create! entries: [ outflow, inflow ] inflow end def create_unlinked_transfer(account_id, amount) build_entry(account_id, amount, marked_as_transfer: true).tap(&:save!) end def create_transaction build_entry(account.id, signed_amount).tap(&:save!) end def build_entry(account_id, amount, marked_as_transfer: false) Account::Entry.new \ account_id: account_id, amount: amount, currency: account.currency, date: date, marked_as_transfer: marked_as_transfer, entryable: Account::Transaction.new end def signed_amount case type when "expense", "transfer_out" amount.to_d else amount.to_d * -1 end end end