class Skr::GlTransaction
A transaction is a record of a business event that has financial consequences. It consists of an at least one credit and at least one debit Transactions can be nested, with each level compacting all the entries that were made on it
require 'skr/core' customer = Customer.find_by_code "MONEYBAGS" GlTransaction.record( source: invoice, description: "Invoice Example" ) do | transaction | transaction.location = Location.default # <- could also specify in record's options Sku.where( code: ['HAT','STRING'] ).each do | sku | transaction.add_posting( amount: sku.default_price, debit: sku.gl_asset_account, credit: customer.gl_receivables_account ) end end
Public Class Methods
@return [GlTransaction] the current transaction that's in progress
# File lib/skr/gl_transaction.rb, line 71 def self.current glt = Thread.current[:gl_transaction] glt ? glt.last : nil end
@param owner [Skr::Model] @param amount [BigDecimal] @param debit [GlAccount] @param credit [GlAccount] @param options [Hash] options to pass to the [GlTransaction] if one is created
# File lib/skr/gl_transaction.rb, line 102 def self.push_or_save( owner: nil, amount: nil, debit:nil, credit:nil, options:{} ) if glt = self.current # we push glt.add_posting( amount: amount, debit: debit, credit: credit ) else options.merge!({ source: owner, location: options[:location] || owner.location }) glt = GlTransaction.new( options ) glt.add_posting( amount: amount, debit: debit, credit: credit ) glt.save end glt end
Start a new nested GlTransaction
When a transaction is created, it can have @return [GlTransaction] new transaction @yield [GlTransaction] new transaction
# File lib/skr/gl_transaction.rb, line 80 def self.record( attributes={} ) Thread.current[:gl_transaction] ||= [] glt = GlTransaction.new( attributes ) Thread.current[:gl_transaction].push( glt ) Skr::Core.logger.debug "B4 GlTransaction" results = yield glt Thread.current[:gl_transaction].pop if results if results.is_a?(Hash) && results.has_key?(:attributes) glt.assign_attributes( results[:attributes] ) end glt._save_recorded Skr::Core.logger.debug "AF GlTransaction new=#{glt.new_record?} #{glt.errors.full_messages}" end return glt end
Public Instance Methods
@private
# File lib/skr/gl_transaction.rb, line 118 def _save_recorded compact( 'debits' ) compact( 'credits' ) self.save if self.credits.any? || self.debits.any? self end
Add a debit/credit pair to the transaction with amount @param amount [BigDecimal] the amount to apply to each posting @param debit [GlAccount] @param credit [GlAccount]
# File lib/skr/gl_transaction.rb, line 47 def add_posting( amount: nil, debit: nil, credit: nil ) Skr::Core.logger.debug "GlTransaction add_posting #{debit} : #{credit}" self.credits.build( location: @location, is_debit: false, account: credit, amount: amount ) self.debits.build( location: @location, is_debit: true, account: debit, amount: amount * -1 ) end
@yield [GlPosting] each posting associated with the Transaction
# File lib/skr/gl_transaction.rb, line 65 def each_posting self.credits.each{ |posting| yield posting } self.debits.each{ |posting| yield posting } end
Passes the location onto the postings. @param location [Location]
# File lib/skr/gl_transaction.rb, line 57 def location=(location) @location = location each_posting do | posting | posting.location = location end end
Private Instance Methods
# File lib/skr/gl_transaction.rb, line 127 def compact( assoc_name ) accounts = self.send( assoc_name ).to_a self.send( assoc_name + "=", [] ) account_numbers = accounts.group_by{ |posting| posting.account_number } account_numbers.each do | number, matching | amount = matching.sum(&:amount) self.send( assoc_name ).build({ account_number: number, is_debit: ( assoc_name == "debits" ), amount: amount, }) end end
# File lib/skr/gl_transaction.rb, line 141 def ensure_postings_correct if debits.total != ( -1 * credits.total ) self.errors.add(:credits, "must equal debits") self.errors.add(:debits, "must equal credits") return false end true end
# File lib/skr/gl_transaction.rb, line 150 def set_defaults self.period ||= GlPeriod.current end