double_entry 0.10.0 → 0.10.1
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/.rspec +2 -1
- data/.rubocop.yml +55 -0
- data/.travis.yml +23 -12
- data/README.md +5 -1
- data/Rakefile +8 -3
- data/double_entry.gemspec +4 -3
- data/lib/active_record/locking_extensions.rb +28 -40
- data/lib/active_record/locking_extensions/log_subscriber.rb +4 -4
- data/lib/double_entry.rb +0 -2
- data/lib/double_entry/account.rb +13 -16
- data/lib/double_entry/account_balance.rb +0 -4
- data/lib/double_entry/balance_calculator.rb +4 -5
- data/lib/double_entry/configurable.rb +0 -2
- data/lib/double_entry/configuration.rb +2 -3
- data/lib/double_entry/errors.rb +2 -2
- data/lib/double_entry/line.rb +13 -16
- data/lib/double_entry/locking.rb +13 -18
- data/lib/double_entry/reporting.rb +2 -3
- data/lib/double_entry/reporting/aggregate.rb +90 -88
- data/lib/double_entry/reporting/aggregate_array.rb +58 -58
- data/lib/double_entry/reporting/day_range.rb +37 -35
- data/lib/double_entry/reporting/hour_range.rb +40 -37
- data/lib/double_entry/reporting/line_aggregate.rb +27 -28
- data/lib/double_entry/reporting/month_range.rb +67 -67
- data/lib/double_entry/reporting/time_range.rb +40 -38
- data/lib/double_entry/reporting/time_range_array.rb +3 -5
- data/lib/double_entry/reporting/week_range.rb +77 -78
- data/lib/double_entry/reporting/year_range.rb +27 -27
- data/lib/double_entry/transfer.rb +14 -15
- data/lib/double_entry/validation/line_check.rb +92 -86
- data/lib/double_entry/version.rb +1 -1
- data/lib/generators/double_entry/install/install_generator.rb +1 -2
- data/lib/generators/double_entry/install/templates/migration.rb +0 -2
- data/script/jack_hammer +1 -1
- data/spec/active_record/locking_extensions_spec.rb +45 -38
- data/spec/double_entry/account_balance_spec.rb +4 -5
- data/spec/double_entry/account_spec.rb +43 -44
- data/spec/double_entry/balance_calculator_spec.rb +6 -8
- data/spec/double_entry/configuration_spec.rb +14 -16
- data/spec/double_entry/line_spec.rb +25 -26
- data/spec/double_entry/locking_spec.rb +34 -39
- data/spec/double_entry/reporting/aggregate_array_spec.rb +8 -10
- data/spec/double_entry/reporting/aggregate_spec.rb +84 -44
- data/spec/double_entry/reporting/line_aggregate_spec.rb +7 -6
- data/spec/double_entry/reporting/month_range_spec.rb +109 -103
- data/spec/double_entry/reporting/time_range_array_spec.rb +145 -135
- data/spec/double_entry/reporting/time_range_spec.rb +36 -35
- data/spec/double_entry/reporting/week_range_spec.rb +82 -76
- data/spec/double_entry/reporting_spec.rb +9 -13
- data/spec/double_entry/transfer_spec.rb +13 -15
- data/spec/double_entry/validation/line_check_spec.rb +73 -79
- data/spec/double_entry_spec.rb +65 -68
- data/spec/generators/double_entry/install/install_generator_spec.rb +7 -10
- data/spec/spec_helper.rb +68 -10
- data/spec/support/accounts.rb +2 -4
- data/spec/support/double_entry_spec_helper.rb +4 -4
- data/spec/support/gemfiles/Gemfile.rails-3.2.x +1 -0
- metadata +31 -2
@@ -1,10 +1,8 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
require 'spec_helper'
|
3
|
-
|
4
|
-
describe DoubleEntry::BalanceCalculator do
|
5
2
|
|
3
|
+
RSpec.describe DoubleEntry::BalanceCalculator do
|
6
4
|
describe '#calculate' do
|
7
|
-
let(:account) { DoubleEntry
|
5
|
+
let(:account) { DoubleEntry.account(:test, :scope => scope) }
|
8
6
|
let(:scope) { User.make! }
|
9
7
|
let(:from) { nil }
|
10
8
|
let(:to) { nil }
|
@@ -42,7 +40,7 @@ describe DoubleEntry::BalanceCalculator do
|
|
42
40
|
|
43
41
|
it 'ignores the time range when summing the lines' do
|
44
42
|
expect(relation).to_not have_received(:where).with(
|
45
|
-
:created_at => Time.parse('2014-06-19 10:09:18 +1000')..Time.parse('2014-06-19 20:09:18 +1000')
|
43
|
+
:created_at => Time.parse('2014-06-19 10:09:18 +1000')..Time.parse('2014-06-19 20:09:18 +1000'),
|
46
44
|
)
|
47
45
|
expect(relation).to_not have_received(:sum)
|
48
46
|
end
|
@@ -55,7 +53,7 @@ describe DoubleEntry::BalanceCalculator do
|
|
55
53
|
|
56
54
|
it 'scopes the lines summed to times within the given range' do
|
57
55
|
expect(relation).to have_received(:where).with(
|
58
|
-
:created_at => Time.parse('2014-06-19 10:09:18 +1000')..Time.parse('2014-06-19 20:09:18 +1000')
|
56
|
+
:created_at => Time.parse('2014-06-19 10:09:18 +1000')..Time.parse('2014-06-19 20:09:18 +1000'),
|
59
57
|
)
|
60
58
|
expect(relation).to have_received(:sum).with(:amount)
|
61
59
|
end
|
@@ -72,10 +70,10 @@ describe DoubleEntry::BalanceCalculator do
|
|
72
70
|
end
|
73
71
|
|
74
72
|
context 'when a list of codes is provided' do
|
75
|
-
let(:codes) {
|
73
|
+
let(:codes) { %w(code1 code2) }
|
76
74
|
|
77
75
|
it 'scopes the lines summed by the given codes' do
|
78
|
-
expect(relation).to have_received(:where).with(:code =>
|
76
|
+
expect(relation).to have_received(:where).with(:code => %w(code1 code2))
|
79
77
|
expect(relation).to have_received(:sum).with(:amount)
|
80
78
|
end
|
81
79
|
end
|
@@ -1,28 +1,26 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
|
3
|
-
describe DoubleEntry::Configuration do
|
4
|
-
|
2
|
+
RSpec.describe DoubleEntry::Configuration do
|
5
3
|
its(:accounts) { should be_a DoubleEntry::Account::Set }
|
6
4
|
its(:transfers) { should be_a DoubleEntry::Transfer::Set }
|
7
5
|
|
8
|
-
describe
|
9
|
-
context
|
6
|
+
describe 'max lengths' do
|
7
|
+
context 'given a max length has not been set' do
|
10
8
|
its(:code_max_length) { should be 47 }
|
11
9
|
its(:scope_identifier_max_length) { should be 23 }
|
12
10
|
its(:account_identifier_max_length) { should be 31 }
|
13
11
|
end
|
14
12
|
|
15
|
-
context
|
13
|
+
context 'given a code max length of 10 has been set' do
|
16
14
|
before { subject.code_max_length = 10 }
|
17
15
|
its(:code_max_length) { should be 10 }
|
18
16
|
end
|
19
17
|
|
20
|
-
context
|
18
|
+
context 'given a scope identifier max length of 11 has been set' do
|
21
19
|
before { subject.scope_identifier_max_length = 11 }
|
22
20
|
its(:scope_identifier_max_length) { should be 11 }
|
23
21
|
end
|
24
22
|
|
25
|
-
context
|
23
|
+
context 'given an account identifier max length of 9 has been set' do
|
26
24
|
before { subject.account_identifier_max_length = 9 }
|
27
25
|
its(:account_identifier_max_length) { should be 9 }
|
28
26
|
end
|
@@ -34,19 +32,19 @@ describe DoubleEntry::Configuration do
|
|
34
32
|
end
|
35
33
|
end
|
36
34
|
|
37
|
-
describe
|
38
|
-
it
|
39
|
-
expect
|
35
|
+
describe '#define_accounts' do
|
36
|
+
it 'yields the accounts set' do
|
37
|
+
expect do |block|
|
40
38
|
subject.define_accounts(&block)
|
41
|
-
|
39
|
+
end.to yield_with_args(be_a DoubleEntry::Account::Set)
|
42
40
|
end
|
43
41
|
end
|
44
42
|
|
45
|
-
describe
|
46
|
-
it
|
47
|
-
expect
|
43
|
+
describe '#define_transfers' do
|
44
|
+
it 'yields the transfers set' do
|
45
|
+
expect do |block|
|
48
46
|
subject.define_transfers(&block)
|
49
|
-
|
47
|
+
end.to yield_with_args(be_a DoubleEntry::Transfer::Set)
|
50
48
|
end
|
51
49
|
end
|
52
50
|
end
|
@@ -1,12 +1,11 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
expect(DoubleEntry::Line.table_name).to eq "double_entry_lines"
|
2
|
+
RSpec.describe DoubleEntry::Line do
|
3
|
+
it 'has a table name prefixed with double_entry_' do
|
4
|
+
expect(DoubleEntry::Line.table_name).to eq 'double_entry_lines'
|
6
5
|
end
|
7
6
|
|
8
|
-
describe
|
9
|
-
let(:line_to_persist)
|
7
|
+
describe 'persistance' do
|
8
|
+
let(:line_to_persist) do
|
10
9
|
DoubleEntry::Line.new(
|
11
10
|
:amount => Money.new(10_00),
|
12
11
|
:balance => Money.zero,
|
@@ -14,9 +13,9 @@ describe DoubleEntry::Line do
|
|
14
13
|
:partner_account => partner_account,
|
15
14
|
:code => code,
|
16
15
|
)
|
17
|
-
|
18
|
-
let(:account) { DoubleEntry.account(:test, :scope =>
|
19
|
-
let(:partner_account) { DoubleEntry.account(:test, :scope =>
|
16
|
+
end
|
17
|
+
let(:account) { DoubleEntry.account(:test, :scope => '17') }
|
18
|
+
let(:partner_account) { DoubleEntry.account(:test, :scope => '72') }
|
20
19
|
let(:code) { :test_code }
|
21
20
|
|
22
21
|
subject(:persisted_line) do
|
@@ -24,33 +23,33 @@ describe DoubleEntry::Line do
|
|
24
23
|
line_to_persist.reload
|
25
24
|
end
|
26
25
|
|
27
|
-
describe
|
28
|
-
context
|
26
|
+
describe 'attributes' do
|
27
|
+
context 'given code = :the_code' do
|
29
28
|
let(:code) { :the_code }
|
30
29
|
its(:code) { should eq :the_code }
|
31
30
|
end
|
32
31
|
|
33
|
-
context
|
32
|
+
context 'given code = nil' do
|
34
33
|
let(:code) { nil }
|
35
34
|
specify { expect { line_to_persist.save! }.to raise_error }
|
36
35
|
end
|
37
36
|
|
38
|
-
context
|
39
|
-
let(:account) { DoubleEntry.account(:test, :scope =>
|
40
|
-
its(
|
41
|
-
its(
|
37
|
+
context 'given account = :test, 54 ' do
|
38
|
+
let(:account) { DoubleEntry.account(:test, :scope => '54') }
|
39
|
+
its('account.account.identifier') { should eq :test }
|
40
|
+
its('account.scope') { should eq '54' }
|
42
41
|
end
|
43
42
|
|
44
|
-
context
|
45
|
-
let(:partner_account) { DoubleEntry.account(:test, :scope =>
|
46
|
-
its(
|
47
|
-
its(
|
43
|
+
context 'given partner_account = :test, 91 ' do
|
44
|
+
let(:partner_account) { DoubleEntry.account(:test, :scope => '91') }
|
45
|
+
its('partner_account.account.identifier') { should eq :test }
|
46
|
+
its('partner_account.scope') { should eq '91' }
|
48
47
|
end
|
49
48
|
|
50
|
-
context
|
51
|
-
let(:account) { DoubleEntry.account(:btc_test, :scope =>
|
52
|
-
let(:partner_account) { DoubleEntry.account(:btc_test, :scope =>
|
53
|
-
its(:currency) { should eq
|
49
|
+
context 'currency' do
|
50
|
+
let(:account) { DoubleEntry.account(:btc_test, :scope => '17') }
|
51
|
+
let(:partner_account) { DoubleEntry.account(:btc_test, :scope => '72') }
|
52
|
+
its(:currency) { should eq 'BTC' }
|
54
53
|
end
|
55
54
|
end
|
56
55
|
|
@@ -74,8 +73,8 @@ describe DoubleEntry::Line do
|
|
74
73
|
end
|
75
74
|
end
|
76
75
|
|
77
|
-
it
|
78
|
-
expect(DoubleEntry::Line.table_name).to eq
|
76
|
+
it 'has a table name prefixed with double_entry_' do
|
77
|
+
expect(DoubleEntry::Line.table_name).to eq 'double_entry_lines'
|
79
78
|
end
|
80
79
|
end
|
81
80
|
end
|
@@ -1,8 +1,6 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
require 'spec_helper'
|
3
|
-
|
4
|
-
describe DoubleEntry::Locking do
|
5
2
|
|
3
|
+
RSpec.describe DoubleEntry::Locking do
|
6
4
|
before do
|
7
5
|
@config_accounts = DoubleEntry.configuration.accounts
|
8
6
|
@config_transfers = DoubleEntry.configuration.transfers
|
@@ -33,16 +31,16 @@ describe DoubleEntry::Locking do
|
|
33
31
|
end
|
34
32
|
end
|
35
33
|
|
36
|
-
@account_a = DoubleEntry.account(:account_a, :scope =>
|
37
|
-
@account_b = DoubleEntry.account(:account_b, :scope =>
|
38
|
-
@account_c = DoubleEntry.account(:account_c, :scope =>
|
39
|
-
@account_d = DoubleEntry.account(:account_d, :scope =>
|
34
|
+
@account_a = DoubleEntry.account(:account_a, :scope => '1')
|
35
|
+
@account_b = DoubleEntry.account(:account_b, :scope => '2')
|
36
|
+
@account_c = DoubleEntry.account(:account_c, :scope => '3')
|
37
|
+
@account_d = DoubleEntry.account(:account_d, :scope => '4')
|
40
38
|
@account_e = DoubleEntry.account(:account_e)
|
41
39
|
end
|
42
40
|
|
43
|
-
it
|
41
|
+
it 'creates missing account balance records' do
|
44
42
|
expect do
|
45
|
-
DoubleEntry::Locking.lock_accounts(@account_a) {
|
43
|
+
DoubleEntry::Locking.lock_accounts(@account_a) {}
|
46
44
|
end.to change(DoubleEntry::AccountBalance, :count).by(1)
|
47
45
|
|
48
46
|
account_balance = DoubleEntry::AccountBalance.find_by_account(@account_a)
|
@@ -50,7 +48,7 @@ describe DoubleEntry::Locking do
|
|
50
48
|
expect(account_balance.balance).to eq Money.new(0)
|
51
49
|
end
|
52
50
|
|
53
|
-
it
|
51
|
+
it 'takes the balance for new account balance records from the lines table' do
|
54
52
|
DoubleEntry::Line.create!(
|
55
53
|
:account => @account_a,
|
56
54
|
:partner_account => @account_b,
|
@@ -67,7 +65,7 @@ describe DoubleEntry::Locking do
|
|
67
65
|
)
|
68
66
|
|
69
67
|
expect do
|
70
|
-
DoubleEntry::Locking.lock_accounts(@account_a) {
|
68
|
+
DoubleEntry::Locking.lock_accounts(@account_a) {}
|
71
69
|
end.to change(DoubleEntry::AccountBalance, :count).by(1)
|
72
70
|
|
73
71
|
account_balance = DoubleEntry::AccountBalance.find_by_account(@account_a)
|
@@ -75,43 +73,43 @@ describe DoubleEntry::Locking do
|
|
75
73
|
expect(account_balance.balance).to eq Money.new(10_00)
|
76
74
|
end
|
77
75
|
|
78
|
-
it
|
79
|
-
expect
|
76
|
+
it 'prohibits locking inside a regular transaction' do
|
77
|
+
expect do
|
80
78
|
DoubleEntry::AccountBalance.transaction do
|
81
79
|
DoubleEntry::Locking.lock_accounts(@account_a, @account_b) do
|
82
80
|
end
|
83
81
|
end
|
84
|
-
|
82
|
+
end.to raise_error(DoubleEntry::Locking::LockMustBeOutermostTransaction)
|
85
83
|
end
|
86
84
|
|
87
|
-
it
|
88
|
-
expect
|
85
|
+
it 'prohibits a transfer inside a regular transaction' do
|
86
|
+
expect do
|
89
87
|
DoubleEntry::AccountBalance.transaction do
|
90
88
|
DoubleEntry.transfer(Money.new(10_00), :from => @account_a, :to => @account_b, :code => :test)
|
91
89
|
end
|
92
|
-
|
90
|
+
end.to raise_error(DoubleEntry::Locking::LockMustBeOutermostTransaction)
|
93
91
|
end
|
94
92
|
|
95
93
|
it "allows a transfer inside a lock if we've locked the transaction accounts" do
|
96
|
-
expect
|
94
|
+
expect do
|
97
95
|
DoubleEntry::Locking.lock_accounts(@account_a, @account_b) do
|
98
96
|
DoubleEntry.transfer(Money.new(10_00), :from => @account_a, :to => @account_b, :code => :test)
|
99
97
|
end
|
100
|
-
|
98
|
+
end.to_not raise_error
|
101
99
|
end
|
102
100
|
|
103
101
|
it "does not allow a transfer inside a lock if the right locks aren't held" do
|
104
|
-
expect
|
102
|
+
expect do
|
105
103
|
DoubleEntry::Locking.lock_accounts(@account_a, @account_c) do
|
106
104
|
DoubleEntry.transfer(Money.new(10_00), :from => @account_a, :to => @account_b, :code => :test)
|
107
105
|
end
|
108
|
-
|
106
|
+
end.to raise_error(DoubleEntry::Locking::LockNotHeld, 'No lock held for account: account_b, scope 2')
|
109
107
|
end
|
110
108
|
|
111
|
-
it
|
109
|
+
it 'allows nested locks if the outer lock locks all the accounts' do
|
112
110
|
expect do
|
113
111
|
DoubleEntry::Locking.lock_accounts(@account_a, @account_b) do
|
114
|
-
DoubleEntry::Locking.lock_accounts(@account_a, @account_b) {
|
112
|
+
DoubleEntry::Locking.lock_accounts(@account_a, @account_b) {}
|
115
113
|
end
|
116
114
|
end.to_not raise_error
|
117
115
|
end
|
@@ -119,15 +117,15 @@ describe DoubleEntry::Locking do
|
|
119
117
|
it "prohibits nested locks if the out lock doesn't lock all the accounts" do
|
120
118
|
expect do
|
121
119
|
DoubleEntry::Locking.lock_accounts(@account_a) do
|
122
|
-
DoubleEntry::Locking.lock_accounts(@account_a, @account_b) {
|
120
|
+
DoubleEntry::Locking.lock_accounts(@account_a, @account_b) {}
|
123
121
|
end
|
124
|
-
end.to raise_error(DoubleEntry::Locking::LockNotHeld,
|
122
|
+
end.to raise_error(DoubleEntry::Locking::LockNotHeld, 'No lock held for account: account_b, scope 2')
|
125
123
|
end
|
126
124
|
|
127
|
-
it
|
125
|
+
it 'rolls back a locking transaction' do
|
128
126
|
DoubleEntry::Locking.lock_accounts(@account_a, @account_b) do
|
129
127
|
DoubleEntry.transfer(Money.new(10_00), :from => @account_a, :to => @account_b, :code => :test)
|
130
|
-
|
128
|
+
fail ActiveRecord::Rollback
|
131
129
|
end
|
132
130
|
expect(DoubleEntry.balance(@account_a)).to eq Money.new(0)
|
133
131
|
expect(DoubleEntry.balance(@account_b)).to eq Money.new(0)
|
@@ -137,27 +135,24 @@ describe DoubleEntry::Locking do
|
|
137
135
|
expect do
|
138
136
|
DoubleEntry::Locking.lock_accounts(@account_a, @account_b) do
|
139
137
|
DoubleEntry.transfer(Money.new(10_00), :from => @account_a, :to => @account_b, :code => :test)
|
140
|
-
|
138
|
+
fail 'Yeah, right'
|
141
139
|
end
|
142
|
-
end.to raise_error(
|
140
|
+
end.to raise_error('Yeah, right')
|
143
141
|
expect(DoubleEntry.balance(@account_a)).to eq Money.new(0)
|
144
142
|
expect(DoubleEntry.balance(@account_b)).to eq Money.new(0)
|
145
143
|
end
|
146
144
|
|
147
|
-
it
|
145
|
+
it 'allows locking a scoped account and a non scoped account' do
|
148
146
|
expect do
|
149
|
-
DoubleEntry::Locking.lock_accounts(@account_d, @account_e)
|
150
|
-
# do nothing
|
151
|
-
end
|
147
|
+
DoubleEntry::Locking.lock_accounts(@account_d, @account_e) {}
|
152
148
|
end.to_not raise_error
|
153
149
|
end
|
154
150
|
|
155
151
|
# sqlite cannot handle these cases so they don't run when DB=sqlite
|
156
|
-
describe
|
157
|
-
|
158
|
-
it "allows multiple threads to lock at the same time" do
|
152
|
+
describe 'concurrent locking', :unless => ENV['DB'] == 'sqlite' do
|
153
|
+
it 'allows multiple threads to lock at the same time' do
|
159
154
|
expect do
|
160
|
-
threads =
|
155
|
+
threads = []
|
161
156
|
|
162
157
|
threads << Thread.new do
|
163
158
|
sleep 0.05
|
@@ -177,8 +172,8 @@ describe DoubleEntry::Locking do
|
|
177
172
|
end.to_not raise_error
|
178
173
|
end
|
179
174
|
|
180
|
-
it
|
181
|
-
threads =
|
175
|
+
it 'allows multiple threads to lock accounts without balances at the same time' do
|
176
|
+
threads = []
|
182
177
|
expect do
|
183
178
|
threads << Thread.new { DoubleEntry::Locking.lock_accounts(@account_a, @account_b) { sleep 0.1 } }
|
184
179
|
threads << Thread.new { DoubleEntry::Locking.lock_accounts(@account_c, @account_d) { sleep 0.1 } }
|
@@ -1,8 +1,6 @@
|
|
1
|
-
require 'spec_helper'
|
2
1
|
module DoubleEntry
|
3
2
|
module Reporting
|
4
|
-
describe AggregateArray do
|
5
|
-
|
3
|
+
RSpec.describe AggregateArray do
|
6
4
|
let(:user) { User.make! }
|
7
5
|
let(:start) { nil }
|
8
6
|
let(:finish) { nil }
|
@@ -10,7 +8,7 @@ module DoubleEntry
|
|
10
8
|
let(:function) { :sum }
|
11
9
|
let(:account) { :savings }
|
12
10
|
let(:transfer_code) { :bonus }
|
13
|
-
subject(:aggregate_array)
|
11
|
+
subject(:aggregate_array) do
|
14
12
|
Reporting.aggregate_array(
|
15
13
|
function,
|
16
14
|
account,
|
@@ -19,7 +17,7 @@ module DoubleEntry
|
|
19
17
|
:start => start,
|
20
18
|
:finish => finish,
|
21
19
|
)
|
22
|
-
|
20
|
+
end
|
23
21
|
|
24
22
|
context 'given a deposit was made in 2007 and 2008' do
|
25
23
|
before do
|
@@ -37,7 +35,7 @@ module DoubleEntry
|
|
37
35
|
context 'when called with range type of "year"' do
|
38
36
|
let(:range_type) { 'year' }
|
39
37
|
let(:start) { '2006-08-03' }
|
40
|
-
it { should eq [
|
38
|
+
it { should eq [Money.zero, Money.new(10_00), Money.new(20_00), Money.zero] }
|
41
39
|
end
|
42
40
|
end
|
43
41
|
end
|
@@ -56,7 +54,7 @@ module DoubleEntry
|
|
56
54
|
let(:range_type) { 'month' }
|
57
55
|
let(:start) { '2006-09-01' }
|
58
56
|
let(:finish) { '2007-01-02' }
|
59
|
-
it { should eq [
|
57
|
+
it { should eq [Money.zero, Money.new(10_00), Money.zero, Money.new(20_00), Money.zero] }
|
60
58
|
end
|
61
59
|
|
62
60
|
context 'given the date is 2007-02-02' do
|
@@ -65,7 +63,7 @@ module DoubleEntry
|
|
65
63
|
context 'when called with range type of "month"' do
|
66
64
|
let(:range_type) { 'month' }
|
67
65
|
let(:start) { '2006-08-03' }
|
68
|
-
it { should eq [
|
66
|
+
it { should eq [Money.zero, Money.zero, Money.new(10_00), Money.zero, Money.new(20_00), Money.zero, Money.zero] }
|
69
67
|
end
|
70
68
|
end
|
71
69
|
end
|
@@ -81,7 +79,7 @@ module DoubleEntry
|
|
81
79
|
perform_btc_deposit(user, 100_000_000)
|
82
80
|
end
|
83
81
|
|
84
|
-
it { should eq [
|
82
|
+
it { should eq [Money.new(200_000_000, :btc)] }
|
85
83
|
end
|
86
84
|
|
87
85
|
context 'when called with range type of "invalid_and_should_not_work"' do
|
@@ -96,7 +94,7 @@ module DoubleEntry
|
|
96
94
|
let(:start) { '2006-08-03' }
|
97
95
|
let(:function) { :invalid_function }
|
98
96
|
it 'raises an AggregateFunctionNotSupported error' do
|
99
|
-
expect{ aggregate_array }.to raise_error AggregateFunctionNotSupported
|
97
|
+
expect { aggregate_array }.to raise_error AggregateFunctionNotSupported
|
100
98
|
end
|
101
99
|
end
|
102
100
|
end
|
@@ -1,9 +1,7 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
require 'spec_helper'
|
3
2
|
module DoubleEntry
|
4
3
|
module Reporting
|
5
|
-
describe Aggregate do
|
6
|
-
|
4
|
+
RSpec.describe Aggregate do
|
7
5
|
let(:user) { User.make! }
|
8
6
|
let(:expected_weekly_average) do
|
9
7
|
(Money.new(20_00) + Money.new(40_00) + Money.new(50_00)) / 3
|
@@ -35,64 +33,84 @@ module DoubleEntry
|
|
35
33
|
end
|
36
34
|
|
37
35
|
it 'should store the aggregate for quick retrieval' do
|
38
|
-
Reporting.aggregate(:sum, :savings, :bonus,
|
39
|
-
:range => TimeRange.make(:year => 2009, :month => 10))
|
36
|
+
Reporting.aggregate(:sum, :savings, :bonus, :range => TimeRange.make(:year => 2009, :month => 10))
|
40
37
|
expect(LineAggregate.count).to eq 1
|
41
38
|
end
|
42
39
|
|
43
40
|
it 'should only store the aggregate once if it is requested more than once' do
|
44
|
-
Reporting.aggregate(:sum, :savings, :bonus,
|
45
|
-
|
46
|
-
Reporting.aggregate(:sum, :savings, :bonus,
|
47
|
-
:range => TimeRange.make(:year => 2009, :month => 9))
|
48
|
-
Reporting.aggregate(:sum, :savings, :bonus,
|
49
|
-
:range => TimeRange.make(:year => 2009, :month => 10))
|
41
|
+
Reporting.aggregate(:sum, :savings, :bonus, :range => TimeRange.make(:year => 2009, :month => 9))
|
42
|
+
Reporting.aggregate(:sum, :savings, :bonus, :range => TimeRange.make(:year => 2009, :month => 9))
|
43
|
+
Reporting.aggregate(:sum, :savings, :bonus, :range => TimeRange.make(:year => 2009, :month => 10))
|
50
44
|
expect(LineAggregate.count).to eq 2
|
51
45
|
end
|
52
46
|
|
53
47
|
it 'should calculate the complete year correctly' do
|
54
48
|
expect(
|
55
|
-
Reporting.aggregate(
|
49
|
+
Reporting.aggregate(
|
50
|
+
:sum, :savings, :bonus,
|
51
|
+
:range => TimeRange.make(:year => 2009)
|
52
|
+
),
|
56
53
|
).to eq Money.new(200_00)
|
57
54
|
end
|
58
55
|
|
59
56
|
it 'should calculate seperate months correctly' do
|
60
57
|
expect(
|
61
|
-
Reporting.aggregate(
|
58
|
+
Reporting.aggregate(
|
59
|
+
:sum, :savings, :bonus,
|
60
|
+
:range => TimeRange.make(:year => 2009, :month => 10)
|
61
|
+
),
|
62
62
|
).to eq Money.new(110_00)
|
63
63
|
expect(
|
64
|
-
Reporting.aggregate(
|
64
|
+
Reporting.aggregate(
|
65
|
+
:sum, :savings, :bonus,
|
66
|
+
:range => TimeRange.make(:year => 2009, :month => 11)
|
67
|
+
),
|
65
68
|
).to eq Money.new(90_00)
|
66
69
|
end
|
67
70
|
|
68
71
|
it 'should calculate seperate weeks correctly' do
|
69
72
|
# Week 40 - Mon Sep 28, 2009 to Sun Oct 4 2009
|
70
73
|
expect(
|
71
|
-
Reporting.aggregate(
|
74
|
+
Reporting.aggregate(
|
75
|
+
:sum, :savings, :bonus,
|
76
|
+
:range => TimeRange.make(:year => 2009, :week => 40)
|
77
|
+
),
|
72
78
|
).to eq Money.new(60_00)
|
73
79
|
end
|
74
80
|
|
75
81
|
it 'should calculate seperate days correctly' do
|
76
82
|
# 1 Nov 2009
|
77
83
|
expect(
|
78
|
-
Reporting.aggregate(
|
84
|
+
Reporting.aggregate(
|
85
|
+
:sum, :savings, :bonus,
|
86
|
+
:range => TimeRange.make(:year => 2009, :week => 44, :day => 7)
|
87
|
+
),
|
79
88
|
).to eq Money.new(90_00)
|
80
89
|
end
|
81
90
|
|
82
91
|
it 'should calculate seperate hours correctly' do
|
83
92
|
# 1 Nov 2009
|
84
93
|
expect(
|
85
|
-
Reporting.aggregate(
|
94
|
+
Reporting.aggregate(
|
95
|
+
:sum, :savings, :bonus,
|
96
|
+
:range => TimeRange.make(:year => 2009, :week => 44, :day => 7, :hour => 0)
|
97
|
+
),
|
86
98
|
).to eq Money.new(40_00)
|
87
99
|
expect(
|
88
|
-
Reporting.aggregate(
|
100
|
+
Reporting.aggregate(
|
101
|
+
:sum, :savings, :bonus,
|
102
|
+
:range => TimeRange.make(:year => 2009, :week => 44, :day => 7, :hour => 1)
|
103
|
+
),
|
89
104
|
).to eq Money.new(50_00)
|
90
105
|
end
|
91
106
|
|
92
107
|
it 'should calculate, but not store aggregates when the time range is still current' do
|
93
108
|
Timecop.freeze Time.local(2009, 11, 21) do
|
94
109
|
expect(
|
95
|
-
Reporting.aggregate(
|
110
|
+
Reporting.aggregate(
|
111
|
+
:sum, :savings, :bonus,
|
112
|
+
:range => TimeRange.make(:year => 2009, :month => 11)
|
113
|
+
),
|
96
114
|
).to eq Money.new(90_00)
|
97
115
|
expect(LineAggregate.count).to eq 0
|
98
116
|
end
|
@@ -101,7 +119,10 @@ module DoubleEntry
|
|
101
119
|
it 'should calculate, but not store aggregates when the time range is in the future' do
|
102
120
|
Timecop.freeze Time.local(2009, 11, 21) do
|
103
121
|
expect(
|
104
|
-
Reporting.aggregate(
|
122
|
+
Reporting.aggregate(
|
123
|
+
:sum, :savings, :bonus,
|
124
|
+
:range => TimeRange.make(:year => 2009, :month => 12)
|
125
|
+
),
|
105
126
|
).to eq Money.new(0)
|
106
127
|
expect(LineAggregate.count).to eq 0
|
107
128
|
end
|
@@ -109,51 +130,71 @@ module DoubleEntry
|
|
109
130
|
|
110
131
|
it 'should calculate monthly all_time ranges correctly' do
|
111
132
|
expect(
|
112
|
-
Reporting.aggregate(
|
133
|
+
Reporting.aggregate(
|
134
|
+
:sum, :savings, :bonus,
|
135
|
+
:range => TimeRange.make(:year => 2009, :month => 12, :range_type => :all_time)
|
136
|
+
),
|
113
137
|
).to eq Money.new(200_00)
|
114
138
|
end
|
115
139
|
|
116
140
|
it 'calculates the average monthly all_time ranges correctly' do
|
117
141
|
expect(
|
118
|
-
Reporting.aggregate(
|
142
|
+
Reporting.aggregate(
|
143
|
+
:average, :savings, :bonus,
|
144
|
+
:range => TimeRange.make(:year => 2009, :month => 12, :range_type => :all_time)
|
145
|
+
),
|
119
146
|
).to eq expected_monthly_average
|
120
147
|
end
|
121
148
|
|
122
149
|
it 'returns the correct count for weekly all_time ranges correctly' do
|
123
150
|
expect(
|
124
|
-
Reporting.aggregate(
|
151
|
+
Reporting.aggregate(
|
152
|
+
:count, :savings, :bonus,
|
153
|
+
:range => TimeRange.make(:year => 2009, :month => 12, :range_type => :all_time)
|
154
|
+
),
|
125
155
|
).to eq 5
|
126
156
|
end
|
127
157
|
|
128
158
|
it 'should calculate weekly all_time ranges correctly' do
|
129
159
|
expect(
|
130
|
-
Reporting.aggregate(
|
160
|
+
Reporting.aggregate(
|
161
|
+
:sum, :savings, :bonus,
|
162
|
+
:range => TimeRange.make(:year => 2009, :week => 43, :range_type => :all_time)
|
163
|
+
),
|
131
164
|
).to eq Money.new(110_00)
|
132
165
|
end
|
133
166
|
|
134
167
|
it 'calculates the average weekly all_time ranges correctly' do
|
135
168
|
expect(
|
136
|
-
Reporting.aggregate(
|
169
|
+
Reporting.aggregate(
|
170
|
+
:average, :savings, :bonus,
|
171
|
+
:range => TimeRange.make(:year => 2009, :week => 43, :range_type => :all_time)
|
172
|
+
),
|
137
173
|
).to eq expected_weekly_average
|
138
174
|
end
|
139
175
|
|
140
176
|
it 'returns the correct count for weekly all_time ranges correctly' do
|
141
177
|
expect(
|
142
|
-
Reporting.aggregate(
|
178
|
+
Reporting.aggregate(
|
179
|
+
:count, :savings, :bonus,
|
180
|
+
:range => TimeRange.make(:year => 2009, :week => 43, :range_type => :all_time)
|
181
|
+
),
|
143
182
|
).to eq 3
|
144
183
|
end
|
145
184
|
|
146
|
-
it
|
147
|
-
expect
|
148
|
-
Reporting.aggregate(
|
149
|
-
|
185
|
+
it 'raises an AggregateFunctionNotSupported exception' do
|
186
|
+
expect do
|
187
|
+
Reporting.aggregate(
|
188
|
+
:not_supported_calculation, :savings, :bonus,
|
189
|
+
:range => TimeRange.make(:year => 2009, :week => 43, :range_type => :all_time)
|
190
|
+
)
|
191
|
+
end.to raise_error(AggregateFunctionNotSupported)
|
150
192
|
end
|
151
193
|
|
152
194
|
context 'filters' do
|
153
|
-
|
154
195
|
let(:range) { TimeRange.make(:year => 2011, :month => 10) }
|
155
196
|
|
156
|
-
|
197
|
+
DoubleEntry::Line.class_eval do
|
157
198
|
scope :test_filter, -> { where(:amount => 10_00) }
|
158
199
|
end
|
159
200
|
|
@@ -168,42 +209,41 @@ module DoubleEntry
|
|
168
209
|
end
|
169
210
|
|
170
211
|
it 'saves filtered aggregations' do
|
171
|
-
expect
|
212
|
+
expect do
|
172
213
|
Reporting.aggregate(:sum, :savings, :bonus, :range => range, :filter => [:test_filter])
|
173
|
-
|
214
|
+
end.to change { LineAggregate.count }.by 1
|
174
215
|
end
|
175
216
|
|
176
217
|
it 'saves filtered aggregation only once for a range' do
|
177
|
-
expect
|
218
|
+
expect do
|
178
219
|
Reporting.aggregate(:sum, :savings, :bonus, :range => range, :filter => [:test_filter])
|
179
220
|
Reporting.aggregate(:sum, :savings, :bonus, :range => range, :filter => [:test_filter])
|
180
|
-
|
221
|
+
end.to change { LineAggregate.count }.by 1
|
181
222
|
end
|
182
223
|
|
183
224
|
it 'saves filtered aggregations and non filtered aggregations separately' do
|
184
|
-
expect
|
225
|
+
expect do
|
185
226
|
Reporting.aggregate(:sum, :savings, :bonus, :range => range, :filter => [:test_filter])
|
186
227
|
Reporting.aggregate(:sum, :savings, :bonus, :range => range)
|
187
|
-
|
228
|
+
end.to change { LineAggregate.count }.by 2
|
188
229
|
end
|
189
230
|
|
190
231
|
it 'loads the correct saved aggregation' do
|
191
|
-
|
192
232
|
# cache the results for filtered and unfiltered aggregations
|
193
233
|
Reporting.aggregate(:sum, :savings, :bonus, :range => range, :filter => [:test_filter])
|
194
234
|
Reporting.aggregate(:sum, :savings, :bonus, :range => range)
|
195
235
|
|
196
236
|
# ensure a second call loads the correct cached value
|
197
237
|
expect(
|
198
|
-
Reporting.aggregate(:sum, :savings, :bonus, :range => range, :filter => [:test_filter])
|
238
|
+
Reporting.aggregate(:sum, :savings, :bonus, :range => range, :filter => [:test_filter]),
|
199
239
|
).to eq Money.new(10_00)
|
200
240
|
expect(
|
201
|
-
Reporting.aggregate(:sum, :savings, :bonus, :range => range)
|
241
|
+
Reporting.aggregate(:sum, :savings, :bonus, :range => range),
|
202
242
|
).to eq Money.new(19_00)
|
203
243
|
end
|
204
244
|
end
|
205
245
|
end
|
206
|
-
describe Aggregate,
|
246
|
+
RSpec.describe Aggregate, 'currencies' do
|
207
247
|
let(:user) { User.make! }
|
208
248
|
before do
|
209
249
|
perform_btc_deposit(user, 100_000_000)
|
@@ -212,8 +252,8 @@ module DoubleEntry
|
|
212
252
|
|
213
253
|
it 'should calculate the sum in the correct currency' do
|
214
254
|
expect(
|
215
|
-
Reporting.aggregate(:sum, :btc_savings, :btc_test_transfer, :range => TimeRange.make(:year => Time.now.year))
|
216
|
-
).to eq
|
255
|
+
Reporting.aggregate(:sum, :btc_savings, :btc_test_transfer, :range => TimeRange.make(:year => Time.now.year)),
|
256
|
+
).to eq(Money.new(300_000_000, :btc))
|
217
257
|
end
|
218
258
|
end
|
219
259
|
end
|