module Twobook
Constants
- PRECISION
- SCALE
- VERSION
Public Class Methods
configuration()
click to toggle source
# File lib/twobook/configuration.rb, line 6 def self.configuration @configuration end
configure() { |configuration| ... }
click to toggle source
# File lib/twobook/configuration.rb, line 10 def self.configure @configuration ||= Configuration.new yield(@configuration) end
ensure_transaction!(accounts, event)
click to toggle source
# File lib/twobook/event_processing.rb, line 39 def self.ensure_transaction!(accounts, event) assets = sum_entries_under_account_type(:assets, accounts, event) liabilities = sum_entries_under_account_type(:liabilities, accounts, event) revenue = sum_entries_under_account_type(:revenue, accounts, event) expenses = sum_entries_under_account_type(:expenses, accounts, event) sum = assets - liabilities - revenue + expenses if sum.nonzero? report = accounts.map do |a| "#{a.name}: #{a.entries.select { |e| e.event == event }.map(&:amount).sum}" end.join("\n") raise "Invalid transaction: must sum to zero, but summed to #{sum}. \n#{report}" end end
simulate(event, input_accounts = [])
click to toggle source
# File lib/twobook/event_processing.rb, line 2 def self.simulate(event, input_accounts = []) return simulate_chain(event, input_accounts) if event.is_a?(Array) raise 'Cannot simulate: event has no agreements' unless event.agreements.any? handlers = event.agreements.reduce([]) do |memo, agreement| memo + agreement.handlers_for(event) end new_accounts = handlers.reduce(Set.new(input_accounts)) do |processing_accounts, handler| handler.run(processing_accounts) end # All entries added while processesing that event should satisfy the accounting equation. ensure_transaction! new_accounts, event new_accounts end
simulate_chain(events, input_accounts = [])
click to toggle source
# File lib/twobook/event_processing.rb, line 20 def self.simulate_chain(events, input_accounts = []) sorted_groups = events.group_by(&:happened_at) sorted = [] sorted_groups.keys.sort.each do |time| group = sorted_groups[time] # Assign order to groups of events with the same timestamp according to the order passed in sorted_group = if group.any? { |event| event.partial_order.nil? } group.map.with_index { |event, i| event.update_partial_order(i) } else group.sort_by(&:partial_order) end sorted_group.each { |event| sorted << event } end sorted.reduce(input_accounts) do |accounts, event| simulate(event, accounts) end end
sum_entries_under_account_type(type, accounts, event)
click to toggle source
# File lib/twobook/event_processing.rb, line 56 def self.sum_entries_under_account_type(type, accounts, event) accounts.select { |a| a.class.account_type == type }.map do |a| a.entries.select { |e| e.event == event }.map(&:amount).sum end.sum end
where(**conditions)
click to toggle source
# File lib/twobook/helpers.rb, line 2 def self.where(**conditions) AccountQuery.where(**conditions) end
wrap_number(n)
click to toggle source
# File lib/twobook/number_handling.rb, line 19 def self.wrap_number(n) ::BigDecimal.new(n, PRECISION).round(SCALE) end