Initial commit
This commit is contained in:
96
app/models/operation.rb
Normal file
96
app/models/operation.rb
Normal file
@@ -0,0 +1,96 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# {Operation} provides generic methods for the accounting operations
|
||||
# models.
|
||||
# @abstract
|
||||
class Operation < ApplicationRecord
|
||||
belongs_to :reference, polymorphic: true
|
||||
belongs_to :currency, foreign_key: :currency_id
|
||||
belongs_to :account, class_name: 'Operations::Account',
|
||||
foreign_key: :code, primary_key: :code
|
||||
|
||||
validates :credit, :debit, numericality: { greater_than_or_equal_to: 0 }
|
||||
validates :currency, :code, presence: true
|
||||
|
||||
validate do
|
||||
errors.add(:account, 'account doesn\'t exist') unless account
|
||||
end
|
||||
|
||||
validate do
|
||||
unless account&.currency_type == currency&.type
|
||||
errors.add(:currency, 'type and account currency type don\'t match')
|
||||
end
|
||||
end
|
||||
|
||||
validate do
|
||||
unless account&.type == self.class.operation_type
|
||||
errors.add(:base, 'Account type and operation type don\'t match')
|
||||
end
|
||||
end
|
||||
|
||||
self.abstract_class = true
|
||||
|
||||
# Returns operation amount with sign.
|
||||
def amount
|
||||
credit.zero? ? -debit : credit
|
||||
end
|
||||
|
||||
class << self
|
||||
def operation_type
|
||||
name.demodulize.downcase
|
||||
end
|
||||
|
||||
def credit!(amount:, currency:, kind: :main, **opt)
|
||||
return if amount.zero?
|
||||
|
||||
opt[:code] ||= Operations::Account.find_by(
|
||||
type: operation_type,
|
||||
kind: kind,
|
||||
currency_type: currency.type
|
||||
).code
|
||||
|
||||
opt.merge(credit: amount, currency_id: currency.id)
|
||||
.yield_self { |attr| new(attr) }
|
||||
.tap(&:save!)
|
||||
end
|
||||
|
||||
def debit!(amount:, currency:, kind: :main, **opt)
|
||||
return if amount.zero?
|
||||
|
||||
opt[:code] ||= Operations::Account.find_by(
|
||||
type: operation_type,
|
||||
kind: kind,
|
||||
currency_type: currency.type
|
||||
).code
|
||||
|
||||
opt.merge(debit: amount, currency_id: currency.id)
|
||||
.yield_self { |attr| new(attr) }
|
||||
.tap(&:save!)
|
||||
end
|
||||
|
||||
def transfer!(amount:, currency:, from_kind:, to_kind:, **opt)
|
||||
params = opt.merge(amount: amount, currency: currency)
|
||||
|
||||
[
|
||||
debit!(params.merge(kind: from_kind)),
|
||||
credit!(params.merge(kind: to_kind))
|
||||
]
|
||||
end
|
||||
|
||||
def balance(currency: nil, created_at_from: nil, created_at_to: nil)
|
||||
if currency.blank?
|
||||
db_balances = all
|
||||
db_balances = db_balances.where('created_at > ?', created_at_from) if created_at_from.present?
|
||||
db_balances = db_balances.where('created_at < ?', created_at_to) if created_at_to.present?
|
||||
db_balances = db_balances.group(:currency_id)
|
||||
.sum('credit - debit')
|
||||
|
||||
Currency.ids.map(&:to_sym).each_with_object({}) do |id, memo|
|
||||
memo[id] = db_balances[id.to_s] || 0
|
||||
end
|
||||
else
|
||||
where(currency: currency).sum('credit - debit')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user