double_entry 1.0.1 → 2.0.0.beta5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/CHANGELOG.md +497 -0
- data/README.md +107 -44
- data/double_entry.gemspec +22 -49
- data/lib/active_record/locking_extensions.rb +3 -3
- data/lib/active_record/locking_extensions/log_subscriber.rb +1 -1
- data/lib/double_entry.rb +29 -21
- data/lib/double_entry/account.rb +39 -46
- data/lib/double_entry/account_balance.rb +20 -3
- data/lib/double_entry/balance_calculator.rb +5 -5
- data/lib/double_entry/configurable.rb +1 -0
- data/lib/double_entry/configuration.rb +8 -2
- data/lib/double_entry/errors.rb +13 -13
- data/lib/double_entry/line.rb +7 -6
- data/lib/double_entry/locking.rb +5 -5
- data/lib/double_entry/transfer.rb +37 -30
- data/lib/double_entry/validation.rb +1 -0
- data/lib/double_entry/validation/account_fixer.rb +36 -0
- data/lib/double_entry/validation/line_check.rb +25 -43
- data/lib/double_entry/version.rb +1 -1
- data/lib/generators/double_entry/install/install_generator.rb +22 -1
- data/lib/generators/double_entry/install/templates/initializer.rb +20 -0
- data/lib/generators/double_entry/install/templates/migration.rb +45 -55
- metadata +35 -256
- data/.gitignore +0 -32
- data/.rspec +0 -2
- data/.travis.yml +0 -29
- data/.yardopts +0 -2
- data/Gemfile +0 -2
- data/Rakefile +0 -15
- data/lib/double_entry/reporting.rb +0 -181
- data/lib/double_entry/reporting/aggregate.rb +0 -110
- data/lib/double_entry/reporting/aggregate_array.rb +0 -76
- data/lib/double_entry/reporting/day_range.rb +0 -42
- data/lib/double_entry/reporting/hour_range.rb +0 -45
- data/lib/double_entry/reporting/line_aggregate.rb +0 -16
- data/lib/double_entry/reporting/line_aggregate_filter.rb +0 -79
- data/lib/double_entry/reporting/month_range.rb +0 -94
- data/lib/double_entry/reporting/time_range.rb +0 -59
- data/lib/double_entry/reporting/time_range_array.rb +0 -49
- data/lib/double_entry/reporting/week_range.rb +0 -107
- data/lib/double_entry/reporting/year_range.rb +0 -40
- data/script/jack_hammer +0 -210
- data/script/setup.sh +0 -8
- data/spec/active_record/locking_extensions_spec.rb +0 -110
- data/spec/double_entry/account_balance_spec.rb +0 -7
- data/spec/double_entry/account_spec.rb +0 -130
- data/spec/double_entry/balance_calculator_spec.rb +0 -88
- data/spec/double_entry/configuration_spec.rb +0 -50
- data/spec/double_entry/line_spec.rb +0 -80
- data/spec/double_entry/locking_spec.rb +0 -214
- data/spec/double_entry/performance/double_entry_performance_spec.rb +0 -32
- data/spec/double_entry/performance/reporting/aggregate_performance_spec.rb +0 -50
- data/spec/double_entry/reporting/aggregate_array_spec.rb +0 -123
- data/spec/double_entry/reporting/aggregate_spec.rb +0 -205
- data/spec/double_entry/reporting/line_aggregate_filter_spec.rb +0 -90
- data/spec/double_entry/reporting/line_aggregate_spec.rb +0 -39
- data/spec/double_entry/reporting/month_range_spec.rb +0 -139
- data/spec/double_entry/reporting/time_range_array_spec.rb +0 -169
- data/spec/double_entry/reporting/time_range_spec.rb +0 -45
- data/spec/double_entry/reporting/week_range_spec.rb +0 -103
- data/spec/double_entry/reporting_spec.rb +0 -181
- data/spec/double_entry/transfer_spec.rb +0 -93
- data/spec/double_entry/validation/line_check_spec.rb +0 -99
- data/spec/double_entry_spec.rb +0 -428
- data/spec/generators/double_entry/install/install_generator_spec.rb +0 -30
- data/spec/spec_helper.rb +0 -118
- data/spec/support/accounts.rb +0 -21
- data/spec/support/blueprints.rb +0 -43
- data/spec/support/database.example.yml +0 -21
- data/spec/support/database.travis.yml +0 -24
- data/spec/support/double_entry_spec_helper.rb +0 -27
- data/spec/support/gemfiles/Gemfile.rails-3.2.x +0 -8
- data/spec/support/gemfiles/Gemfile.rails-4.1.x +0 -6
- data/spec/support/gemfiles/Gemfile.rails-4.2.x +0 -5
- data/spec/support/gemfiles/Gemfile.rails-5.0.x +0 -5
- data/spec/support/performance_helper.rb +0 -26
- data/spec/support/reporting_configuration.rb +0 -6
- data/spec/support/schema.rb +0 -74
data/.gitignore
DELETED
@@ -1,32 +0,0 @@
|
|
1
|
-
*.gem
|
2
|
-
*.rbc
|
3
|
-
/.config
|
4
|
-
/coverage/
|
5
|
-
/InstalledFiles
|
6
|
-
/pkg/
|
7
|
-
/spec/reports/
|
8
|
-
/test/tmp/
|
9
|
-
/test/version_tmp/
|
10
|
-
/tmp/
|
11
|
-
|
12
|
-
## Performance profiling
|
13
|
-
/profiles/
|
14
|
-
|
15
|
-
## Documentation cache and generated files:
|
16
|
-
/.yardoc/
|
17
|
-
/_yardoc/
|
18
|
-
/doc/
|
19
|
-
/rdoc/
|
20
|
-
|
21
|
-
## Environment normalisation:
|
22
|
-
/.bundle/
|
23
|
-
/lib/bundler/man/
|
24
|
-
/Gemfile.lock
|
25
|
-
/.ruby-version
|
26
|
-
/.ruby-gemset
|
27
|
-
|
28
|
-
# DoubleEntry specific
|
29
|
-
/bin/
|
30
|
-
/log/
|
31
|
-
/spec/reports/
|
32
|
-
/spec/support/database.yml
|
data/.rspec
DELETED
data/.travis.yml
DELETED
@@ -1,29 +0,0 @@
|
|
1
|
-
language: ruby
|
2
|
-
sudo: false
|
3
|
-
before_script:
|
4
|
-
- cp spec/support/database.travis.yml spec/support/database.yml
|
5
|
-
- mysql -e 'create database double_entry_test;'
|
6
|
-
- psql -c 'create database double_entry_test;' -U postgres
|
7
|
-
script:
|
8
|
-
- bundle exec rake spec
|
9
|
-
- ruby script/jack_hammer -t 2000
|
10
|
-
matrix:
|
11
|
-
include:
|
12
|
-
- rvm: 2.1
|
13
|
-
gemfile: spec/support/gemfiles/Gemfile.rails-3.2.x
|
14
|
-
env: DB=mysql
|
15
|
-
- rvm: 2.2
|
16
|
-
gemfile: spec/support/gemfiles/Gemfile.rails-4.1.x
|
17
|
-
env: DB=mysql
|
18
|
-
- rvm: 2.2
|
19
|
-
gemfile: spec/support/gemfiles/Gemfile.rails-4.2.x
|
20
|
-
env: DB=mysql
|
21
|
-
- rvm: 2.3
|
22
|
-
gemfile: spec/support/gemfiles/Gemfile.rails-5.0.x
|
23
|
-
env: DB=mysql
|
24
|
-
- rvm: 2.3
|
25
|
-
gemfile: spec/support/gemfiles/Gemfile.rails-5.0.x
|
26
|
-
env: DB=sqlite
|
27
|
-
- rvm: 2.3
|
28
|
-
gemfile: spec/support/gemfiles/Gemfile.rails-5.0.x
|
29
|
-
env: DB=postgres
|
data/.yardopts
DELETED
data/Gemfile
DELETED
data/Rakefile
DELETED
@@ -1,15 +0,0 @@
|
|
1
|
-
require 'rspec/core/rake_task'
|
2
|
-
require 'bundler/gem_tasks'
|
3
|
-
|
4
|
-
RSpec::Core::RakeTask.new(:spec) do |t|
|
5
|
-
t.verbose = false
|
6
|
-
t.ruby_opts = '-w'
|
7
|
-
end
|
8
|
-
|
9
|
-
task :default do
|
10
|
-
%w(mysql postgres sqlite).each do |db|
|
11
|
-
puts "Running tests with `DB=#{db}`"
|
12
|
-
ENV['DB'] = db
|
13
|
-
Rake::Task['spec'].execute
|
14
|
-
end
|
15
|
-
end
|
@@ -1,181 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
require 'double_entry/reporting/aggregate'
|
3
|
-
require 'double_entry/reporting/aggregate_array'
|
4
|
-
require 'double_entry/reporting/time_range'
|
5
|
-
require 'double_entry/reporting/day_range'
|
6
|
-
require 'double_entry/reporting/hour_range'
|
7
|
-
require 'double_entry/reporting/week_range'
|
8
|
-
require 'double_entry/reporting/month_range'
|
9
|
-
require 'double_entry/reporting/year_range'
|
10
|
-
require 'double_entry/reporting/line_aggregate'
|
11
|
-
require 'double_entry/reporting/line_aggregate_filter'
|
12
|
-
require 'double_entry/reporting/time_range_array'
|
13
|
-
|
14
|
-
module DoubleEntry
|
15
|
-
# @api private
|
16
|
-
module Reporting
|
17
|
-
include Configurable
|
18
|
-
extend self
|
19
|
-
|
20
|
-
class Configuration
|
21
|
-
attr_accessor :start_of_business, :first_month_of_financial_year
|
22
|
-
|
23
|
-
def initialize #:nodoc:
|
24
|
-
@start_of_business = Time.new(1970, 1, 1)
|
25
|
-
@first_month_of_financial_year = 7
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
class AggregateFunctionNotSupported < RuntimeError; end
|
30
|
-
|
31
|
-
# Perform an aggregate calculation on a set of transfers for an account.
|
32
|
-
#
|
33
|
-
# The transfers included in the calculation can be limited by time range
|
34
|
-
# and provided custom filters.
|
35
|
-
#
|
36
|
-
# @example Find the sum for all $10 :save transfers in all :checking accounts in the current month, made by Australian users (assume the date is January 30, 2014).
|
37
|
-
# time_range = DoubleEntry::Reporting::TimeRange.make(2014, 1)
|
38
|
-
#
|
39
|
-
# DoubleEntry::Line.class_eval do
|
40
|
-
# scope :specific_transfer_amount, ->(amount) { where(:amount => amount.fractional) }
|
41
|
-
# end
|
42
|
-
#
|
43
|
-
# DoubleEntry::Reporting.aggregate(
|
44
|
-
# :sum,
|
45
|
-
# :checking,
|
46
|
-
# :save,
|
47
|
-
# time_range,
|
48
|
-
# :filter => [
|
49
|
-
# :scope => {
|
50
|
-
# :name => :specific_transfer_amount,
|
51
|
-
# :arguments => [Money.new(10_00)]
|
52
|
-
# },
|
53
|
-
# :metadata => {
|
54
|
-
# :user_location => 'AU'
|
55
|
-
# },
|
56
|
-
# ]
|
57
|
-
# )
|
58
|
-
# @param [Symbol] function The function to perform on the set of transfers.
|
59
|
-
# Valid functions are :sum, :count, and :average
|
60
|
-
# @param [Symbol] account The symbol identifying the account to perform
|
61
|
-
# the aggregate calculation on. As specified in the account configuration.
|
62
|
-
# @param [Symbol] code The application specific code for the type of
|
63
|
-
# transfer to perform an aggregate calculation on. As specified in the
|
64
|
-
# transfer configuration.
|
65
|
-
# @param [DoubleEntry::Reporting::TimeRange] Only include transfers in the
|
66
|
-
# given time range in the calculation.
|
67
|
-
# @option options :filter [Array<Hash<Symbol,Hash<Symbol,Object>>>]
|
68
|
-
# An array of custom filter to apply before performing the aggregate
|
69
|
-
# calculation. Filters can be either scope filters, where the name must be
|
70
|
-
# specified, or they can be metadata filters, where the key/value pair to
|
71
|
-
# match on must be specified.
|
72
|
-
# Scope filters must be monkey patched as scopes into the DoubleEntry::Line
|
73
|
-
# class, as the example above shows. Scope filters may also take a list of
|
74
|
-
# arguments to pass into the monkey patched scope, and, if provided, must
|
75
|
-
# be contained within an array.
|
76
|
-
# @return [Money, Fixnum] Returns a Money object for :sum and :average
|
77
|
-
# calculations, or a Fixnum for :count calculations.
|
78
|
-
# @raise [Reporting::AggregateFunctionNotSupported] The provided function
|
79
|
-
# is not supported.
|
80
|
-
#
|
81
|
-
def aggregate(function, account, code, range, options = {})
|
82
|
-
Aggregate.formatted_amount(function, account, code, range, options)
|
83
|
-
end
|
84
|
-
|
85
|
-
# Perform an aggregate calculation on a set of transfers for an account
|
86
|
-
# and return the results in an array partitioned by a time range type.
|
87
|
-
#
|
88
|
-
# The transfers included in the calculation can be limited by a time range
|
89
|
-
# and provided custom filters.
|
90
|
-
#
|
91
|
-
# @example Find the number of all $10 :save transfers in all :checking accounts per month for the entire year (Assume the year is 2014).
|
92
|
-
# DoubleEntry::Reporting.aggregate_array(
|
93
|
-
# :sum,
|
94
|
-
# :checking,
|
95
|
-
# :save,
|
96
|
-
# :range_type => 'month',
|
97
|
-
# :start => '2014-01-01',
|
98
|
-
# :finish => '2014-12-31',
|
99
|
-
# )
|
100
|
-
# @param [Symbol] function The function to perform on the set of transfers.
|
101
|
-
# Valid functions are :sum, :count, and :average
|
102
|
-
# @param [Symbol] account The symbol identifying the account to perform
|
103
|
-
# the aggregate calculation on. As specified in the account configuration.
|
104
|
-
# @param [Symbol] code The application specific code for the type of
|
105
|
-
# transfer to perform an aggregate calculation on. As specified in the
|
106
|
-
# transfer configuration.
|
107
|
-
# @option options :filter [Array<Symbol>, Array<Hash<Symbol, Object>>]
|
108
|
-
# A custom filter to apply before performing the aggregate calculation.
|
109
|
-
# Currently, filters must be monkey patched as scopes into the
|
110
|
-
# DoubleEntry::Line class in order to be used as filters, as the example
|
111
|
-
# shows. If the filter requires a parameter, it must be given in a Hash,
|
112
|
-
# otherwise pass an array with the symbol names for the defined scopes.
|
113
|
-
# @option options :range_type [String] The type of time range to return data
|
114
|
-
# for. For example, specifying 'month' will return an array of the resulting
|
115
|
-
# aggregate calculation for each month.
|
116
|
-
# Valid range_types are 'hour', 'day', 'week', 'month', and 'year'
|
117
|
-
# @option options :start [String] The start date for the time range to perform
|
118
|
-
# calculations in. The default start date is the start_of_business (can
|
119
|
-
# be specified in configuration).
|
120
|
-
# The format of the string must be as follows: 'YYYY-mm-dd'
|
121
|
-
# @option options :finish [String] The finish (or end) date for the time range
|
122
|
-
# to perform calculations in. The default finish date is the current date.
|
123
|
-
# The format of the string must be as follows: 'YYYY-mm-dd'
|
124
|
-
# @return [Array<Money, Fixnum>] Returns an array of Money objects for :sum
|
125
|
-
# and :average calculations, or an array of Fixnum for :count calculations.
|
126
|
-
# The array is indexed by the range_type. For example, if range_type is
|
127
|
-
# specified as 'month', each index in the array will represent a month.
|
128
|
-
# @raise [Reporting::AggregateFunctionNotSupported] The provided function
|
129
|
-
# is not supported.
|
130
|
-
#
|
131
|
-
def aggregate_array(function, account, code, options = {})
|
132
|
-
AggregateArray.new(function, account, code, options)
|
133
|
-
end
|
134
|
-
|
135
|
-
# Identify the scopes with the given account identifier holding at least
|
136
|
-
# the provided minimum balance.
|
137
|
-
#
|
138
|
-
# @example Find users with at least $1,000,000 in their savings accounts
|
139
|
-
# DoubleEntry::Reporting.scopes_with_minimum_balance_for_account(
|
140
|
-
# 1_000_000.dollars,
|
141
|
-
# :savings,
|
142
|
-
# ) # might return the user ids: [ 1423, 12232, 34729 ]
|
143
|
-
# @param [Money] minimum_balance Minimum account balance a scope must have
|
144
|
-
# to be included in the result set.
|
145
|
-
# @param [Symbol] account_identifier
|
146
|
-
# @return [Array<Fixnum>] Scopes
|
147
|
-
#
|
148
|
-
def scopes_with_minimum_balance_for_account(minimum_balance, account_identifier)
|
149
|
-
select_values(sanitize_sql_array([<<-SQL, account_identifier, minimum_balance.cents])).map(&:to_i)
|
150
|
-
SELECT scope
|
151
|
-
FROM #{AccountBalance.table_name}
|
152
|
-
WHERE account = ?
|
153
|
-
AND balance >= ?
|
154
|
-
SQL
|
155
|
-
end
|
156
|
-
|
157
|
-
# This is used by the concurrency test script.
|
158
|
-
#
|
159
|
-
# @api private
|
160
|
-
# @return [Boolean] true if all the amounts for an account add up to the final balance,
|
161
|
-
# which they always should.
|
162
|
-
#
|
163
|
-
def reconciled?(account)
|
164
|
-
scoped_lines = Line.where(:account => "#{account.identifier}")
|
165
|
-
scoped_lines = scoped_lines.where(:scope => "#{account.scope_identity}") if account.scoped?
|
166
|
-
sum_of_amounts = scoped_lines.sum(:amount)
|
167
|
-
final_balance = scoped_lines.order(:id).last[:balance]
|
168
|
-
cached_balance = AccountBalance.find_by_account(account)[:balance]
|
169
|
-
final_balance == sum_of_amounts && final_balance == cached_balance
|
170
|
-
end
|
171
|
-
|
172
|
-
private
|
173
|
-
|
174
|
-
delegate :connection, :to => ActiveRecord::Base
|
175
|
-
delegate :select_values, :to => :connection
|
176
|
-
|
177
|
-
def sanitize_sql_array(sql_array)
|
178
|
-
ActiveRecord::Base.send(:sanitize_sql_array, sql_array)
|
179
|
-
end
|
180
|
-
end
|
181
|
-
end
|
@@ -1,110 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
module DoubleEntry
|
3
|
-
module Reporting
|
4
|
-
class Aggregate
|
5
|
-
attr_reader :function, :account, :code, :range, :filter, :currency
|
6
|
-
|
7
|
-
def self.formatted_amount(function, account, code, range, options = {})
|
8
|
-
new(function, account, code, range, options).formatted_amount
|
9
|
-
end
|
10
|
-
|
11
|
-
def initialize(function, account, code, range, options = {})
|
12
|
-
@function = function.to_s
|
13
|
-
fail AggregateFunctionNotSupported unless %w(sum count average).include?(@function)
|
14
|
-
|
15
|
-
@account = account
|
16
|
-
@code = code ? code.to_s : nil
|
17
|
-
@range = range
|
18
|
-
@filter = options[:filter]
|
19
|
-
@currency = DoubleEntry::Account.currency(account)
|
20
|
-
end
|
21
|
-
|
22
|
-
def amount(force_recalculation = false)
|
23
|
-
if force_recalculation
|
24
|
-
clear_old_aggregates
|
25
|
-
calculate
|
26
|
-
else
|
27
|
-
retrieve || calculate
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
def formatted_amount(value = amount)
|
32
|
-
value ||= 0
|
33
|
-
if function == 'count'
|
34
|
-
value
|
35
|
-
else
|
36
|
-
Money.new(value, currency)
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
private
|
41
|
-
|
42
|
-
def retrieve
|
43
|
-
aggregate = LineAggregate.where(field_hash).first
|
44
|
-
aggregate.amount if aggregate
|
45
|
-
end
|
46
|
-
|
47
|
-
def clear_old_aggregates
|
48
|
-
LineAggregate.delete_all(field_hash)
|
49
|
-
end
|
50
|
-
|
51
|
-
def calculate
|
52
|
-
if range.class == YearRange
|
53
|
-
aggregate = calculate_yearly_aggregate
|
54
|
-
else
|
55
|
-
aggregate = LineAggregate.aggregate(function, account, code, range, filter)
|
56
|
-
end
|
57
|
-
|
58
|
-
if range_is_complete?
|
59
|
-
fields = field_hash
|
60
|
-
fields[:amount] = aggregate || 0
|
61
|
-
LineAggregate.create! fields
|
62
|
-
end
|
63
|
-
|
64
|
-
aggregate
|
65
|
-
end
|
66
|
-
|
67
|
-
def calculate_yearly_aggregate
|
68
|
-
# We calculate yearly aggregates by combining monthly aggregates
|
69
|
-
# otherwise they will get excruciatingly slow to calculate
|
70
|
-
# as the year progresses. (I am thinking mainly of the 'current' year.)
|
71
|
-
# Combining monthly aggregates will mean that the figure will be partially memoized
|
72
|
-
if function == 'average'
|
73
|
-
calculate_yearly_average
|
74
|
-
else
|
75
|
-
result = (1..12).inject(formatted_amount(0)) do |total, month|
|
76
|
-
total + Aggregate.new(function, account, code, MonthRange.new(:year => range.year, :month => month), :filter => filter).formatted_amount
|
77
|
-
end
|
78
|
-
result.is_a?(Money) ? result.cents : result
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
def calculate_yearly_average
|
83
|
-
# need this seperate function, because an average of averages is not the correct average
|
84
|
-
year_range = YearRange.new(:year => range.year)
|
85
|
-
sum = Aggregate.new(:sum, account, code, year_range, :filter => filter).formatted_amount
|
86
|
-
count = Aggregate.new(:count, account, code, year_range, :filter => filter).formatted_amount
|
87
|
-
(count == 0) ? 0 : (sum / count).cents
|
88
|
-
end
|
89
|
-
|
90
|
-
def range_is_complete?
|
91
|
-
Time.now > range.finish
|
92
|
-
end
|
93
|
-
|
94
|
-
def field_hash
|
95
|
-
{
|
96
|
-
:function => function,
|
97
|
-
:account => account,
|
98
|
-
:code => code,
|
99
|
-
:year => range.year,
|
100
|
-
:month => range.month,
|
101
|
-
:week => range.week,
|
102
|
-
:day => range.day,
|
103
|
-
:hour => range.hour,
|
104
|
-
:filter => filter.inspect,
|
105
|
-
:range_type => range.range_type.to_s,
|
106
|
-
}
|
107
|
-
end
|
108
|
-
end
|
109
|
-
end
|
110
|
-
end
|
@@ -1,76 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
module DoubleEntry
|
3
|
-
module Reporting
|
4
|
-
class AggregateArray < Array
|
5
|
-
# An AggregateArray is awesome
|
6
|
-
# It is useful for making reports
|
7
|
-
# It is basically an array of aggregate results,
|
8
|
-
# representing a column of data in a report.
|
9
|
-
#
|
10
|
-
# For example, you could request all sales
|
11
|
-
# broken down by month and it would return an array of values
|
12
|
-
attr_reader :function, :account, :code, :filter, :range_type, :start, :finish, :currency
|
13
|
-
|
14
|
-
def initialize(function, account, code, options)
|
15
|
-
@function = function.to_s
|
16
|
-
@account = account
|
17
|
-
@code = code
|
18
|
-
@filter = options[:filter]
|
19
|
-
@range_type = options[:range_type]
|
20
|
-
@start = options[:start]
|
21
|
-
@finish = options[:finish]
|
22
|
-
@currency = DoubleEntry::Account.currency(account)
|
23
|
-
|
24
|
-
retrieve_aggregates
|
25
|
-
fill_in_missing_aggregates
|
26
|
-
populate_self
|
27
|
-
end
|
28
|
-
|
29
|
-
private
|
30
|
-
|
31
|
-
def populate_self
|
32
|
-
all_periods.each do |period|
|
33
|
-
self << @aggregates[period.key]
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
def fill_in_missing_aggregates
|
38
|
-
# some aggregates may not have been previously calculated, so we can request them now
|
39
|
-
# (this includes aggregates for the still-running period)
|
40
|
-
all_periods.each do |period|
|
41
|
-
unless @aggregates[period.key]
|
42
|
-
@aggregates[period.key] = Aggregate.formatted_amount(function, account, code, period, :filter => filter)
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
# get any previously calculated aggregates
|
48
|
-
def retrieve_aggregates
|
49
|
-
fail ArgumentError, "Invalid range type '#{range_type}'" unless %w(year month week day hour).include? range_type
|
50
|
-
scope = LineAggregate.
|
51
|
-
where(:function => function).
|
52
|
-
where(:range_type => 'normal').
|
53
|
-
where(:account => account.to_s).
|
54
|
-
where(:code => code.to_s).
|
55
|
-
where(:filter => filter.inspect).
|
56
|
-
where(LineAggregate.arel_table[range_type].not_eq(nil))
|
57
|
-
@aggregates = scope.each_with_object({}) do |result, hash|
|
58
|
-
hash[result.key] = formatted_amount(result.amount)
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
def all_periods
|
63
|
-
TimeRangeArray.make(range_type, start, finish)
|
64
|
-
end
|
65
|
-
|
66
|
-
def formatted_amount(amount)
|
67
|
-
amount ||= 0
|
68
|
-
if function == 'count'
|
69
|
-
amount
|
70
|
-
else
|
71
|
-
Money.new(amount, currency)
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|