double_entry 0.10.3 → 1.0.0
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 +4 -4
- data/.gitignore +3 -0
- data/.rubocop.yml +5 -1
- data/README.md +16 -0
- data/double_entry.gemspec +35 -0
- data/lib/double_entry.rb +1 -0
- data/lib/double_entry/line.rb +1 -0
- data/lib/double_entry/line_metadata.rb +18 -0
- data/lib/double_entry/locking.rb +11 -0
- data/lib/double_entry/reporting.rb +26 -14
- data/lib/double_entry/reporting/aggregate.rb +11 -15
- data/lib/double_entry/reporting/aggregate_array.rb +1 -1
- data/lib/double_entry/reporting/line_aggregate.rb +2 -23
- data/lib/double_entry/reporting/line_aggregate_filter.rb +81 -0
- data/lib/double_entry/transfer.rb +42 -25
- data/lib/double_entry/version.rb +1 -1
- data/lib/generators/double_entry/install/templates/migration.rb +10 -0
- data/spec/double_entry/locking_spec.rb +29 -0
- data/spec/double_entry/performance/double_entry_performance_spec.rb +32 -0
- data/spec/double_entry/performance/reporting/aggregate_performance_spec.rb +50 -0
- data/spec/double_entry/reporting/aggregate_array_spec.rb +10 -10
- data/spec/double_entry/reporting/aggregate_spec.rb +57 -114
- data/spec/double_entry/reporting/line_aggregate_filter_spec.rb +90 -0
- data/spec/double_entry/reporting/line_aggregate_spec.rb +35 -7
- data/spec/double_entry/reporting_spec.rb +136 -0
- data/spec/double_entry/transfer_spec.rb +58 -1
- data/spec/support/performance_helper.rb +26 -0
- data/spec/support/schema.rb +9 -0
- metadata +124 -4
data/lib/double_entry/version.rb
CHANGED
@@ -54,9 +54,19 @@ class CreateDoubleEntryTables < ActiveRecord::Migration
|
|
54
54
|
t.text "log"
|
55
55
|
t.timestamps :null => false
|
56
56
|
end
|
57
|
+
|
58
|
+
create_table "double_entry_line_metadata", :force => true do |t|
|
59
|
+
t.integer "line_id", :null => false
|
60
|
+
t.string "key", :limit => 48, :null => false
|
61
|
+
t.string "value", :limit => 64, :null => false
|
62
|
+
t.timestamps :null => false
|
63
|
+
end
|
64
|
+
|
65
|
+
add_index "double_entry_line_metadata", ["line_id", "key", "value"], :name => "lines_meta_line_id_key_value_idx"
|
57
66
|
end
|
58
67
|
|
59
68
|
def self.down
|
69
|
+
drop_table "double_entry_line_metadata"
|
60
70
|
drop_table "double_entry_line_checks"
|
61
71
|
drop_table "double_entry_line_aggregates"
|
62
72
|
drop_table "double_entry_lines"
|
@@ -148,6 +148,35 @@ RSpec.describe DoubleEntry::Locking do
|
|
148
148
|
end.to_not raise_error
|
149
149
|
end
|
150
150
|
|
151
|
+
context 'handling ActiveRecord::StatementInvalid errors' do
|
152
|
+
context 'non lock wait timeout errors' do
|
153
|
+
let(:error) { ActiveRecord::StatementInvalid.new('some other error') }
|
154
|
+
before do
|
155
|
+
allow(DoubleEntry::AccountBalance).to receive(:with_restart_on_deadlock).
|
156
|
+
and_raise(error)
|
157
|
+
end
|
158
|
+
|
159
|
+
it 're-raises the ActiveRecord::StatementInvalid error' do
|
160
|
+
expect do
|
161
|
+
DoubleEntry::Locking.lock_accounts(@account_d, @account_e) {}
|
162
|
+
end.to raise_error(error)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
context 'lock wait timeout errors' do
|
167
|
+
before do
|
168
|
+
allow(DoubleEntry::AccountBalance).to receive(:with_restart_on_deadlock).
|
169
|
+
and_raise(ActiveRecord::StatementInvalid, 'lock wait timeout')
|
170
|
+
end
|
171
|
+
|
172
|
+
it 'raises a LockWaitTimeout error' do
|
173
|
+
expect do
|
174
|
+
DoubleEntry::Locking.lock_accounts(@account_d, @account_e) {}
|
175
|
+
end.to raise_error(DoubleEntry::Locking::LockWaitTimeout)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
151
180
|
# sqlite cannot handle these cases so they don't run when DB=sqlite
|
152
181
|
describe 'concurrent locking', :unless => ENV['DB'] == 'sqlite' do
|
153
182
|
it 'allows multiple threads to lock at the same time' do
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module DoubleEntry
|
2
|
+
RSpec.describe DoubleEntry do
|
3
|
+
describe 'transfer performance' do
|
4
|
+
include PerformanceHelper
|
5
|
+
let(:user) { User.make! }
|
6
|
+
let(:amount) { Money.new(10_00) }
|
7
|
+
let(:test) { DoubleEntry.account(:test, :scope => user) }
|
8
|
+
let(:savings) { DoubleEntry.account(:savings, :scope => user) }
|
9
|
+
|
10
|
+
it 'creates a lot of transfers quickly without metadata' do
|
11
|
+
profile_transfers_with_metadata(nil)
|
12
|
+
# local results: 6.44, 5.93, 5.94
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'creates a lot of transfers quickly with metadata' do
|
16
|
+
big_metadata = {}
|
17
|
+
8.times { |i| big_metadata["key#{i}".to_sym] = "value#{i}" }
|
18
|
+
profile_transfers_with_metadata(big_metadata)
|
19
|
+
# local results: 21.2, 21.6, 20.9
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def profile_transfers_with_metadata(metadata)
|
24
|
+
start_profiling
|
25
|
+
options = { :from => test, :to => savings, :code => :bonus }
|
26
|
+
options[:metadata] = metadata if metadata
|
27
|
+
100.times { Transfer.transfer(amount, options) }
|
28
|
+
profile_name = metadata ? 'transfer-with-metadata' : 'transfer'
|
29
|
+
stop_profiling(profile_name)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module DoubleEntry
|
2
|
+
module Reporting
|
3
|
+
RSpec.describe Aggregate do
|
4
|
+
include PerformanceHelper
|
5
|
+
let(:user) { User.make! }
|
6
|
+
let(:amount) { Money.new(10_00) }
|
7
|
+
let(:test) { DoubleEntry.account(:test, :scope => user) }
|
8
|
+
let(:savings) { DoubleEntry.account(:savings, :scope => user) }
|
9
|
+
|
10
|
+
subject(:transfer) { Transfer.transfer(amount, options) }
|
11
|
+
|
12
|
+
context '200 transfers in a single day, half with metadata' do
|
13
|
+
# Surprisingly, the number of transfers makes no difference to the time taken to aggregate them. Some sample results:
|
14
|
+
# 20,000 => 524ms
|
15
|
+
# 10,000 => 573ms
|
16
|
+
# 1,000 => 486ms
|
17
|
+
# 100 => 608ms
|
18
|
+
# 10 => 509ms
|
19
|
+
# 1 => 473ms
|
20
|
+
before do
|
21
|
+
Timecop.freeze Time.local(2015, 06, 30) do
|
22
|
+
100.times { Transfer.transfer(amount, :from => test, :to => savings, :code => :bonus) }
|
23
|
+
100.times { Transfer.transfer(amount, :from => test, :to => savings, :code => :bonus, :metadata => { :country => 'AU', :tax => 'GST' }) }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'calculates monthly all_time ranges quickly without a filter' do
|
28
|
+
profile_aggregation_with_filter(nil)
|
29
|
+
# local results: 517ms, 484ms, 505ms, 482ms, 525ms
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'calculates monthly all_time ranges quickly with a filter' do
|
33
|
+
profile_aggregation_with_filter([:metadata => { :country => 'AU' }])
|
34
|
+
# local results when run independently (caching improves performance when run consecutively):
|
35
|
+
# 655ms, 613ms, 597ms, 607ms, 627ms
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def profile_aggregation_with_filter(filter)
|
40
|
+
start_profiling
|
41
|
+
range = TimeRange.make(:year => 2015, :month => 06, :range_type => :all_time)
|
42
|
+
options = {}
|
43
|
+
options[:filter] = filter if filter
|
44
|
+
Reporting.aggregate(:sum, :savings, :bonus, range, options)
|
45
|
+
profile_name = filter ? 'aggregate-with-metadata' : 'aggregate'
|
46
|
+
stop_profiling(profile_name)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -5,11 +5,11 @@ module DoubleEntry
|
|
5
5
|
let(:start) { nil }
|
6
6
|
let(:finish) { nil }
|
7
7
|
let(:range_type) { 'year' }
|
8
|
-
let(:function) {
|
8
|
+
let(:function) { 'sum' }
|
9
9
|
let(:account) { :savings }
|
10
10
|
let(:transfer_code) { :bonus }
|
11
11
|
subject(:aggregate_array) do
|
12
|
-
|
12
|
+
AggregateArray.new(
|
13
13
|
function,
|
14
14
|
account,
|
15
15
|
transfer_code,
|
@@ -42,17 +42,17 @@ module DoubleEntry
|
|
42
42
|
|
43
43
|
context 'and some aggregates were created previously' do
|
44
44
|
before do
|
45
|
-
|
46
|
-
|
47
|
-
allow(
|
45
|
+
Aggregate.formatted_amount(function, account, transfer_code, years[0])
|
46
|
+
Aggregate.formatted_amount(function, account, transfer_code, years[1])
|
47
|
+
allow(Aggregate).to receive(:formatted_amount)
|
48
48
|
end
|
49
49
|
|
50
|
-
it 'only asks
|
51
|
-
expect(
|
52
|
-
expect(
|
50
|
+
it 'only asks Aggregate for the non-existent ones' do
|
51
|
+
expect(Aggregate).not_to receive(:formatted_amount).with(function, account, transfer_code, years[0], :filter => nil)
|
52
|
+
expect(Aggregate).not_to receive(:formatted_amount).with(function, account, transfer_code, years[1], :filter => nil)
|
53
53
|
|
54
|
-
expect(
|
55
|
-
expect(
|
54
|
+
expect(Aggregate).to receive(:formatted_amount).with(function, account, transfer_code, years[2], :filter => nil)
|
55
|
+
expect(Aggregate).to receive(:formatted_amount).with(function, account, transfer_code, years[3], :filter => nil)
|
56
56
|
aggregate_array
|
57
57
|
end
|
58
58
|
end
|
@@ -33,166 +33,113 @@ module DoubleEntry
|
|
33
33
|
end
|
34
34
|
|
35
35
|
it 'should store the aggregate for quick retrieval' do
|
36
|
-
Aggregate.new(:sum, :savings, :bonus,
|
36
|
+
Aggregate.new(:sum, :savings, :bonus, TimeRange.make(:year => 2009, :month => 10)).amount
|
37
37
|
expect(LineAggregate.count).to eq 1
|
38
38
|
end
|
39
39
|
|
40
40
|
it 'should only store the aggregate once if it is requested more than once' do
|
41
|
-
Aggregate.new(:sum, :savings, :bonus,
|
42
|
-
Aggregate.new(:sum, :savings, :bonus,
|
43
|
-
Aggregate.new(:sum, :savings, :bonus,
|
41
|
+
Aggregate.new(:sum, :savings, :bonus, TimeRange.make(:year => 2009, :month => 9)).amount
|
42
|
+
Aggregate.new(:sum, :savings, :bonus, TimeRange.make(:year => 2009, :month => 9)).amount
|
43
|
+
Aggregate.new(:sum, :savings, :bonus, TimeRange.make(:year => 2009, :month => 10)).amount
|
44
44
|
expect(LineAggregate.count).to eq 2
|
45
45
|
end
|
46
46
|
|
47
47
|
it 'should calculate the complete year correctly' do
|
48
|
-
|
49
|
-
|
50
|
-
:sum, :savings, :bonus,
|
51
|
-
:range => TimeRange.make(:year => 2009)
|
52
|
-
).formatted_amount,
|
53
|
-
).to eq Money.new(200_00)
|
48
|
+
amount = Aggregate.new(:sum, :savings, :bonus, TimeRange.make(:year => 2009)).formatted_amount
|
49
|
+
expect(amount).to eq Money.new(200_00)
|
54
50
|
end
|
55
51
|
|
56
52
|
it 'should calculate seperate months correctly' do
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
).to eq Money.new(110_00)
|
63
|
-
expect(
|
64
|
-
Aggregate.new(
|
65
|
-
:sum, :savings, :bonus,
|
66
|
-
:range => TimeRange.make(:year => 2009, :month => 11)
|
67
|
-
).formatted_amount,
|
68
|
-
).to eq Money.new(90_00)
|
53
|
+
amount = Aggregate.new(:sum, :savings, :bonus, TimeRange.make(:year => 2009, :month => 10)).formatted_amount
|
54
|
+
expect(amount).to eq Money.new(110_00)
|
55
|
+
|
56
|
+
amount = Aggregate.new(:sum, :savings, :bonus, TimeRange.make(:year => 2009, :month => 11)).formatted_amount
|
57
|
+
expect(amount).to eq Money.new(90_00)
|
69
58
|
end
|
70
59
|
|
71
60
|
it 'should calculate seperate weeks correctly' do
|
72
61
|
# Week 40 - Mon Sep 28, 2009 to Sun Oct 4 2009
|
73
|
-
|
74
|
-
|
75
|
-
:sum, :savings, :bonus,
|
76
|
-
:range => TimeRange.make(:year => 2009, :week => 40)
|
77
|
-
).formatted_amount,
|
78
|
-
).to eq Money.new(60_00)
|
62
|
+
amount = Aggregate.new(:sum, :savings, :bonus, TimeRange.make(:year => 2009, :week => 40)).formatted_amount
|
63
|
+
expect(amount).to eq Money.new(60_00)
|
79
64
|
end
|
80
65
|
|
81
66
|
it 'should calculate seperate days correctly' do
|
82
67
|
# 1 Nov 2009
|
83
|
-
|
84
|
-
|
85
|
-
:sum, :savings, :bonus,
|
86
|
-
:range => TimeRange.make(:year => 2009, :week => 44, :day => 7)
|
87
|
-
).formatted_amount,
|
88
|
-
).to eq Money.new(90_00)
|
68
|
+
amount = Aggregate.new(:sum, :savings, :bonus, TimeRange.make(:year => 2009, :week => 44, :day => 7)).formatted_amount
|
69
|
+
expect(amount).to eq Money.new(90_00)
|
89
70
|
end
|
90
71
|
|
91
72
|
it 'should calculate seperate hours correctly' do
|
92
73
|
# 1 Nov 2009
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
).formatted_amount,
|
98
|
-
).to eq Money.new(40_00)
|
99
|
-
expect(
|
100
|
-
Aggregate.new(
|
101
|
-
:sum, :savings, :bonus,
|
102
|
-
:range => TimeRange.make(:year => 2009, :week => 44, :day => 7, :hour => 1)
|
103
|
-
).formatted_amount,
|
104
|
-
).to eq Money.new(50_00)
|
74
|
+
amount = Aggregate.new(:sum, :savings, :bonus, TimeRange.make(:year => 2009, :week => 44, :day => 7, :hour => 0)).formatted_amount
|
75
|
+
expect(amount).to eq Money.new(40_00)
|
76
|
+
amount = Aggregate.new(:sum, :savings, :bonus, TimeRange.make(:year => 2009, :week => 44, :day => 7, :hour => 1)).formatted_amount
|
77
|
+
expect(amount).to eq Money.new(50_00)
|
105
78
|
end
|
106
79
|
|
107
80
|
it 'should calculate, but not store aggregates when the time range is still current' do
|
108
81
|
Timecop.freeze Time.local(2009, 11, 21) do
|
109
|
-
|
110
|
-
|
111
|
-
:sum, :savings, :bonus,
|
112
|
-
:range => TimeRange.make(:year => 2009, :month => 11)
|
113
|
-
).formatted_amount,
|
114
|
-
).to eq Money.new(90_00)
|
82
|
+
amount = Aggregate.new(:sum, :savings, :bonus, TimeRange.make(:year => 2009, :month => 11)).formatted_amount
|
83
|
+
expect(amount).to eq Money.new(90_00)
|
115
84
|
expect(LineAggregate.count).to eq 0
|
116
85
|
end
|
117
86
|
end
|
118
87
|
|
119
88
|
it 'should calculate, but not store aggregates when the time range is in the future' do
|
120
89
|
Timecop.freeze Time.local(2009, 11, 21) do
|
121
|
-
|
122
|
-
|
123
|
-
:sum, :savings, :bonus,
|
124
|
-
:range => TimeRange.make(:year => 2009, :month => 12)
|
125
|
-
).formatted_amount,
|
126
|
-
).to eq Money.new(0)
|
90
|
+
amount = Aggregate.new(:sum, :savings, :bonus, TimeRange.make(:year => 2009, :month => 12)).formatted_amount
|
91
|
+
expect(amount).to eq Money.new(0)
|
127
92
|
expect(LineAggregate.count).to eq 0
|
128
93
|
end
|
129
94
|
end
|
130
95
|
|
131
96
|
it 'should calculate monthly all_time ranges correctly' do
|
132
|
-
|
133
|
-
|
134
|
-
:sum, :savings, :bonus,
|
135
|
-
:range => TimeRange.make(:year => 2009, :month => 12, :range_type => :all_time)
|
136
|
-
).formatted_amount,
|
137
|
-
).to eq Money.new(200_00)
|
97
|
+
amount = Aggregate.new(:sum, :savings, :bonus, TimeRange.make(:year => 2009, :month => 12, :range_type => :all_time)).formatted_amount
|
98
|
+
expect(amount).to eq Money.new(200_00)
|
138
99
|
end
|
139
100
|
|
140
101
|
it 'calculates the average monthly all_time ranges correctly' do
|
141
|
-
|
142
|
-
|
143
|
-
:average, :savings, :bonus,
|
144
|
-
:range => TimeRange.make(:year => 2009, :month => 12, :range_type => :all_time)
|
145
|
-
).formatted_amount,
|
146
|
-
).to eq expected_monthly_average
|
102
|
+
amount = Aggregate.new(:average, :savings, :bonus, TimeRange.make(:year => 2009, :month => 12, :range_type => :all_time)).formatted_amount
|
103
|
+
expect(amount).to eq expected_monthly_average
|
147
104
|
end
|
148
105
|
|
149
106
|
it 'returns the correct count for weekly all_time ranges correctly' do
|
150
|
-
|
151
|
-
|
152
|
-
:count, :savings, :bonus,
|
153
|
-
:range => TimeRange.make(:year => 2009, :month => 12, :range_type => :all_time)
|
154
|
-
).formatted_amount,
|
155
|
-
).to eq 5
|
107
|
+
amount = Aggregate.new(:count, :savings, :bonus, TimeRange.make(:year => 2009, :month => 12, :range_type => :all_time)).formatted_amount
|
108
|
+
expect(amount).to eq 5
|
156
109
|
end
|
157
110
|
|
158
111
|
it 'should calculate weekly all_time ranges correctly' do
|
159
|
-
|
160
|
-
|
161
|
-
:sum, :savings, :bonus,
|
162
|
-
:range => TimeRange.make(:year => 2009, :week => 43, :range_type => :all_time)
|
163
|
-
).formatted_amount,
|
164
|
-
).to eq Money.new(110_00)
|
112
|
+
amount = Aggregate.new(:sum, :savings, :bonus, TimeRange.make(:year => 2009, :week => 43, :range_type => :all_time)).formatted_amount
|
113
|
+
expect(amount).to eq Money.new(110_00)
|
165
114
|
end
|
166
115
|
|
167
116
|
it 'calculates the average weekly all_time ranges correctly' do
|
168
|
-
|
169
|
-
|
170
|
-
:average, :savings, :bonus,
|
171
|
-
:range => TimeRange.make(:year => 2009, :week => 43, :range_type => :all_time)
|
172
|
-
).formatted_amount,
|
173
|
-
).to eq expected_weekly_average
|
117
|
+
amount = Aggregate.new(:average, :savings, :bonus, TimeRange.make(:year => 2009, :week => 43, :range_type => :all_time)).formatted_amount
|
118
|
+
expect(amount).to eq expected_weekly_average
|
174
119
|
end
|
175
120
|
|
176
121
|
it 'returns the correct count for weekly all_time ranges correctly' do
|
177
|
-
|
178
|
-
|
179
|
-
:count, :savings, :bonus,
|
180
|
-
:range => TimeRange.make(:year => 2009, :week => 43, :range_type => :all_time)
|
181
|
-
).formatted_amount,
|
182
|
-
).to eq 3
|
122
|
+
amount = Aggregate.new(:count, :savings, :bonus, TimeRange.make(:year => 2009, :week => 43, :range_type => :all_time)).formatted_amount
|
123
|
+
expect(amount).to eq 3
|
183
124
|
end
|
184
125
|
|
185
126
|
it 'raises an AggregateFunctionNotSupported exception' do
|
186
127
|
expect do
|
187
128
|
Aggregate.new(
|
188
|
-
:not_supported_calculation, :savings, :bonus,
|
189
|
-
:range => TimeRange.make(:year => 2009, :week => 43, :range_type => :all_time)
|
129
|
+
:not_supported_calculation, :savings, :bonus, TimeRange.make(:year => 2009, :week => 43, :range_type => :all_time)
|
190
130
|
).amount
|
191
131
|
end.to raise_error(AggregateFunctionNotSupported)
|
192
132
|
end
|
193
133
|
|
194
134
|
context 'filters' do
|
195
135
|
let(:range) { TimeRange.make(:year => 2011, :month => 10) }
|
136
|
+
let(:filter) do
|
137
|
+
[
|
138
|
+
:scope => {
|
139
|
+
:name => :test_filter,
|
140
|
+
},
|
141
|
+
]
|
142
|
+
end
|
196
143
|
|
197
144
|
DoubleEntry::Line.class_eval do
|
198
145
|
scope :test_filter, -> { where(:amount => 10_00) }
|
@@ -210,36 +157,35 @@ module DoubleEntry
|
|
210
157
|
|
211
158
|
it 'saves filtered aggregations' do
|
212
159
|
expect do
|
213
|
-
Aggregate.new(:sum, :savings, :bonus,
|
160
|
+
Aggregate.new(:sum, :savings, :bonus, range, :filter => filter).amount
|
214
161
|
end.to change { LineAggregate.count }.by 1
|
215
162
|
end
|
216
163
|
|
217
164
|
it 'saves filtered aggregation only once for a range' do
|
218
165
|
expect do
|
219
|
-
Aggregate.new(:sum, :savings, :bonus,
|
220
|
-
Aggregate.new(:sum, :savings, :bonus,
|
166
|
+
Aggregate.new(:sum, :savings, :bonus, range, :filter => filter).amount
|
167
|
+
Aggregate.new(:sum, :savings, :bonus, range, :filter => filter).amount
|
221
168
|
end.to change { LineAggregate.count }.by 1
|
222
169
|
end
|
223
170
|
|
224
171
|
it 'saves filtered aggregations and non filtered aggregations separately' do
|
225
172
|
expect do
|
226
|
-
Aggregate.new(:sum, :savings, :bonus,
|
227
|
-
Aggregate.new(:sum, :savings, :bonus,
|
173
|
+
Aggregate.new(:sum, :savings, :bonus, range, :filter => filter).amount
|
174
|
+
Aggregate.new(:sum, :savings, :bonus, range).amount
|
228
175
|
end.to change { LineAggregate.count }.by 2
|
229
176
|
end
|
230
177
|
|
231
178
|
it 'loads the correct saved aggregation' do
|
232
179
|
# cache the results for filtered and unfiltered aggregations
|
233
|
-
Aggregate.new(:sum, :savings, :bonus,
|
234
|
-
Aggregate.new(:sum, :savings, :bonus,
|
180
|
+
Aggregate.new(:sum, :savings, :bonus, range, :filter => filter).amount
|
181
|
+
Aggregate.new(:sum, :savings, :bonus, range).amount
|
235
182
|
|
236
183
|
# ensure a second call loads the correct cached value
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
).to eq Money.new(19_00)
|
184
|
+
amount = Aggregate.new(:sum, :savings, :bonus, range, :filter => filter).formatted_amount
|
185
|
+
expect(amount).to eq Money.new(10_00)
|
186
|
+
|
187
|
+
amount = Aggregate.new(:sum, :savings, :bonus, range).formatted_amount
|
188
|
+
expect(amount).to eq Money.new(19_00)
|
243
189
|
end
|
244
190
|
end
|
245
191
|
end
|
@@ -251,11 +197,8 @@ module DoubleEntry
|
|
251
197
|
end
|
252
198
|
|
253
199
|
it 'should calculate the sum in the correct currency' do
|
254
|
-
|
255
|
-
|
256
|
-
:sum, :btc_savings, :btc_test_transfer, :range => TimeRange.make(:year => Time.now.year)
|
257
|
-
).formatted_amount,
|
258
|
-
).to eq(Money.new(300_000_000, :btc))
|
200
|
+
amount = Aggregate.new(:sum, :btc_savings, :btc_test_transfer, TimeRange.make(:year => Time.now.year)).formatted_amount
|
201
|
+
expect(amount).to eq(Money.new(300_000_000, :btc))
|
259
202
|
end
|
260
203
|
end
|
261
204
|
end
|