double_entry 0.0.1.pre → 0.1.0.pre.pre.alpha
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +13 -5
- data/.gitignore +5 -6
- data/.rspec +1 -0
- data/.travis.yml +19 -0
- data/.yardopts +2 -0
- data/Gemfile +0 -1
- data/LICENSE.md +19 -0
- data/README.md +221 -14
- data/Rakefile +12 -0
- data/double_entry.gemspec +30 -15
- data/gemfiles/Gemfile.rails-3.2.0 +5 -0
- data/gemfiles/Gemfile.rails-4.0.0 +5 -0
- data/gemfiles/Gemfile.rails-4.1.0 +5 -0
- data/lib/active_record/locking_extensions.rb +61 -0
- data/lib/double_entry.rb +267 -2
- data/lib/double_entry/account.rb +82 -0
- data/lib/double_entry/account_balance.rb +31 -0
- data/lib/double_entry/aggregate.rb +118 -0
- data/lib/double_entry/aggregate_array.rb +65 -0
- data/lib/double_entry/configurable.rb +52 -0
- data/lib/double_entry/day_range.rb +38 -0
- data/lib/double_entry/hour_range.rb +40 -0
- data/lib/double_entry/line.rb +147 -0
- data/lib/double_entry/line_aggregate.rb +37 -0
- data/lib/double_entry/line_check.rb +118 -0
- data/lib/double_entry/locking.rb +187 -0
- data/lib/double_entry/month_range.rb +92 -0
- data/lib/double_entry/reporting.rb +16 -0
- data/lib/double_entry/time_range.rb +55 -0
- data/lib/double_entry/time_range_array.rb +43 -0
- data/lib/double_entry/transfer.rb +70 -0
- data/lib/double_entry/version.rb +3 -1
- data/lib/double_entry/week_range.rb +99 -0
- data/lib/double_entry/year_range.rb +39 -0
- data/lib/generators/double_entry/install/install_generator.rb +22 -0
- data/lib/generators/double_entry/install/templates/migration.rb +68 -0
- data/script/jack_hammer +201 -0
- data/script/setup.sh +8 -0
- data/spec/active_record/locking_extensions_spec.rb +54 -0
- data/spec/double_entry/account_balance_spec.rb +8 -0
- data/spec/double_entry/account_spec.rb +23 -0
- data/spec/double_entry/aggregate_array_spec.rb +75 -0
- data/spec/double_entry/aggregate_spec.rb +168 -0
- data/spec/double_entry/double_entry_spec.rb +391 -0
- data/spec/double_entry/line_aggregate_spec.rb +8 -0
- data/spec/double_entry/line_check_spec.rb +88 -0
- data/spec/double_entry/line_spec.rb +72 -0
- data/spec/double_entry/locking_spec.rb +154 -0
- data/spec/double_entry/month_range_spec.rb +131 -0
- data/spec/double_entry/reporting_spec.rb +25 -0
- data/spec/double_entry/time_range_array_spec.rb +149 -0
- data/spec/double_entry/time_range_spec.rb +43 -0
- data/spec/double_entry/week_range_spec.rb +88 -0
- data/spec/generators/double_entry/install/install_generator_spec.rb +33 -0
- data/spec/spec_helper.rb +47 -0
- data/spec/support/accounts.rb +26 -0
- data/spec/support/blueprints.rb +34 -0
- data/spec/support/database.example.yml +16 -0
- data/spec/support/database.travis.yml +18 -0
- data/spec/support/double_entry_spec_helper.rb +19 -0
- data/spec/support/reporting_configuration.rb +6 -0
- data/spec/support/schema.rb +71 -0
- metadata +277 -18
- data/LICENSE.txt +0 -22
@@ -0,0 +1,88 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe DoubleEntry::LineCheck do
|
5
|
+
|
6
|
+
describe '#last' do
|
7
|
+
|
8
|
+
context 'Given some checks have been created' do
|
9
|
+
before do
|
10
|
+
Timecop.freeze 3.minutes.ago do
|
11
|
+
DoubleEntry::LineCheck.create! :last_line_id => 100, :errors_found => false, :log => ''
|
12
|
+
end
|
13
|
+
Timecop.freeze 1.minute.ago do
|
14
|
+
DoubleEntry::LineCheck.create! :last_line_id => 300, :errors_found => false, :log => ''
|
15
|
+
end
|
16
|
+
Timecop.freeze 2.minutes.ago do
|
17
|
+
DoubleEntry::LineCheck.create! :last_line_id => 200, :errors_found => false, :log => ''
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should find the newest LineCheck created (by creation_date)' do
|
22
|
+
expect(DoubleEntry::LineCheck.last.last_line_id).to eq 300
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '#perform!' do
|
29
|
+
subject(:performed_line_check) { DoubleEntry::LineCheck.perform! }
|
30
|
+
|
31
|
+
context 'Given a user with 100 dollars' do
|
32
|
+
before { User.make!(:savings_balance => Money.new(100_00)) }
|
33
|
+
|
34
|
+
context 'And all is consistent' do
|
35
|
+
|
36
|
+
context 'And all lines have been checked' do
|
37
|
+
before { DoubleEntry::LineCheck.perform! }
|
38
|
+
|
39
|
+
it { should be_nil }
|
40
|
+
|
41
|
+
it 'should not persist a new LineCheck' do
|
42
|
+
expect {
|
43
|
+
DoubleEntry::LineCheck.perform!
|
44
|
+
}.to_not change { DoubleEntry::LineCheck.count }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
it { should be_instance_of DoubleEntry::LineCheck }
|
49
|
+
its(:errors_found) { should eq false }
|
50
|
+
|
51
|
+
it 'should persist the LineCheck' do
|
52
|
+
line_check = DoubleEntry::LineCheck.perform!
|
53
|
+
expect(DoubleEntry::LineCheck.last).to eq line_check
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context 'And there is a consistency error in lines' do
|
58
|
+
before { DoubleEntry::Line.order(:id).limit(1).update_all('balance = balance + 1') }
|
59
|
+
|
60
|
+
its(:errors_found) { should be true }
|
61
|
+
its(:log) { should match /Error on line/ }
|
62
|
+
|
63
|
+
it 'should correct the running balance' do
|
64
|
+
expect {
|
65
|
+
DoubleEntry::LineCheck.perform!
|
66
|
+
}.to change { DoubleEntry::Line.order(:id).first.balance }.by Money.new(-1)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context 'And there is a consistency error in account balance' do
|
71
|
+
before { DoubleEntry::AccountBalance.order(:id).limit(1).update_all('balance = balance + 1') }
|
72
|
+
|
73
|
+
its(:errors_found) { should be true }
|
74
|
+
|
75
|
+
it 'should correct the account balance' do
|
76
|
+
expect {
|
77
|
+
DoubleEntry::LineCheck.perform!
|
78
|
+
}.to change { DoubleEntry::AccountBalance.order(:id).first.balance }.by Money.new(-1)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
it "has a table name prefixed with double_entry_" do
|
84
|
+
expect(DoubleEntry::LineCheck.table_name).to eq "double_entry_line_checks"
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "spec_helper"
|
3
|
+
describe DoubleEntry::Line do
|
4
|
+
|
5
|
+
describe "persistent attributes" do
|
6
|
+
let(:persisted_line) {
|
7
|
+
DoubleEntry::Line.new(
|
8
|
+
:amount => Money.new(10_00),
|
9
|
+
:balance => Money.empty,
|
10
|
+
:account => account,
|
11
|
+
:partner_account => partner_account,
|
12
|
+
:code => code,
|
13
|
+
:meta => meta,
|
14
|
+
)
|
15
|
+
}
|
16
|
+
let(:account) { DoubleEntry.account(:test, :scope => "17") }
|
17
|
+
let(:partner_account) { DoubleEntry.account(:test, :scope => "72") }
|
18
|
+
let(:code) { :test_code }
|
19
|
+
let(:meta) { "test meta" }
|
20
|
+
before { persisted_line.save! }
|
21
|
+
subject { DoubleEntry::Line.last }
|
22
|
+
|
23
|
+
context "given code = :the_code" do
|
24
|
+
let(:code) { :the_code }
|
25
|
+
its(:code) { should eq :the_code }
|
26
|
+
end
|
27
|
+
|
28
|
+
context "given code = nil" do
|
29
|
+
let(:code) { nil }
|
30
|
+
its(:code) { should eq nil }
|
31
|
+
end
|
32
|
+
|
33
|
+
context "given account = :test, 54 " do
|
34
|
+
let(:account) { DoubleEntry.account(:test, :scope => "54") }
|
35
|
+
its("account.account.identifier") { should eq :test }
|
36
|
+
its("account.scope") { should eq "54" }
|
37
|
+
end
|
38
|
+
|
39
|
+
context "given partner_account = :test, 91 " do
|
40
|
+
let(:partner_account) { DoubleEntry.account(:test, :scope => "91") }
|
41
|
+
its("partner_account.account.identifier") { should eq :test }
|
42
|
+
its("partner_account.scope") { should eq "91" }
|
43
|
+
end
|
44
|
+
|
45
|
+
context "given meta = 'the meta'" do
|
46
|
+
let(:meta) { "the meta" }
|
47
|
+
its(:meta) { should eq "the meta" }
|
48
|
+
end
|
49
|
+
|
50
|
+
context "given meta = nil" do
|
51
|
+
let(:meta) { nil }
|
52
|
+
its(:meta) { should eq nil }
|
53
|
+
end
|
54
|
+
|
55
|
+
context "given meta has not been persisted (NULL)" do
|
56
|
+
let(:persisted_line) {
|
57
|
+
DoubleEntry::Line.new(
|
58
|
+
:amount => Money.new(10_00),
|
59
|
+
:balance => Money.empty,
|
60
|
+
:account => DoubleEntry.account(:savings, :scope => User.make!),
|
61
|
+
:code => code,
|
62
|
+
)
|
63
|
+
}
|
64
|
+
its(:meta) { should eq Hash.new }
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
it "has a table name prefixed with double_entry_" do
|
69
|
+
expect(DoubleEntry::Line.table_name).to eq "double_entry_lines"
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
@@ -0,0 +1,154 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe DoubleEntry::Locking do
|
5
|
+
|
6
|
+
before(:all) { @saved_accounts, @saved_transfers = DoubleEntry.accounts, DoubleEntry.transfers }
|
7
|
+
after(:all) { DoubleEntry.accounts, DoubleEntry.transfers = @saved_accounts, @saved_transfers }
|
8
|
+
|
9
|
+
before do
|
10
|
+
scope = lambda {|x| x }
|
11
|
+
|
12
|
+
DoubleEntry.accounts = DoubleEntry::Account::Set.new.tap do |accounts|
|
13
|
+
accounts << DoubleEntry::Account.new(:identifier => :account_a, :scope_identifier => scope)
|
14
|
+
accounts << DoubleEntry::Account.new(:identifier => :account_b, :scope_identifier => scope)
|
15
|
+
accounts << DoubleEntry::Account.new(:identifier => :account_c, :scope_identifier => scope)
|
16
|
+
accounts << DoubleEntry::Account.new(:identifier => :account_d, :scope_identifier => scope)
|
17
|
+
end
|
18
|
+
|
19
|
+
DoubleEntry.transfers = DoubleEntry::Transfer::Set.new.tap do |transfers|
|
20
|
+
transfers << DoubleEntry::Transfer.new(:from => :account_a, :to => :account_b, :code => :test)
|
21
|
+
transfers << DoubleEntry::Transfer.new(:from => :account_c, :to => :account_d, :code => :test)
|
22
|
+
end
|
23
|
+
|
24
|
+
@account_a = DoubleEntry.account(:account_a, :scope => "1")
|
25
|
+
@account_b = DoubleEntry.account(:account_b, :scope => "2")
|
26
|
+
@account_c = DoubleEntry.account(:account_c, :scope => "3")
|
27
|
+
@account_d = DoubleEntry.account(:account_d, :scope => "4")
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should create missing account balance records" do
|
31
|
+
expect do
|
32
|
+
DoubleEntry::Locking.lock_accounts(@account_a) { }
|
33
|
+
end.to change(DoubleEntry::AccountBalance, :count).by(1)
|
34
|
+
|
35
|
+
account_balance = DoubleEntry::AccountBalance.find_by_account(@account_a)
|
36
|
+
expect(account_balance).to_not be_nil
|
37
|
+
expect(account_balance.balance).to eq Money.new(0)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should take the balance for new account balance records from the lines table" do
|
41
|
+
DoubleEntry::Line.create!(:account => @account_a, :amount => Money.new(3_00), :balance => Money.new( 3_00), :code => :test)
|
42
|
+
DoubleEntry::Line.create!(:account => @account_a, :amount => Money.new(7_00), :balance => Money.new(10_00), :code => :test)
|
43
|
+
|
44
|
+
expect do
|
45
|
+
DoubleEntry::Locking.lock_accounts(@account_a) { }
|
46
|
+
end.to change(DoubleEntry::AccountBalance, :count).by(1)
|
47
|
+
|
48
|
+
account_balance = DoubleEntry::AccountBalance.find_by_account(@account_a)
|
49
|
+
expect(account_balance).to_not be_nil
|
50
|
+
expect(account_balance.balance).to eq Money.new(10_00)
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should not allow locking inside a regular transaction" do
|
54
|
+
expect {
|
55
|
+
DoubleEntry::AccountBalance.transaction do
|
56
|
+
DoubleEntry::Locking.lock_accounts(@account_a, @account_b) do
|
57
|
+
end
|
58
|
+
end
|
59
|
+
}.to raise_error(DoubleEntry::Locking::LockMustBeOutermostTransaction)
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should not allow a transfer inside a regular transaction" do
|
63
|
+
expect {
|
64
|
+
DoubleEntry::AccountBalance.transaction do
|
65
|
+
DoubleEntry.transfer(Money.new(10_00), :from => @account_a, :to => @account_b, :code => :test)
|
66
|
+
end
|
67
|
+
}.to raise_error(DoubleEntry::Locking::LockMustBeOutermostTransaction)
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should allow a transfer inside a lock if we've locked the transaction accounts" do
|
71
|
+
expect {
|
72
|
+
DoubleEntry::Locking.lock_accounts(@account_a, @account_b) do
|
73
|
+
DoubleEntry.transfer(Money.new(10_00), :from => @account_a, :to => @account_b, :code => :test)
|
74
|
+
end
|
75
|
+
}.to_not raise_error
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should not allow a transfer inside a lock if the right locks aren't held" do
|
79
|
+
expect {
|
80
|
+
DoubleEntry::Locking.lock_accounts(@account_a, @account_c) do
|
81
|
+
DoubleEntry.transfer(Money.new(10_00), :from => @account_a, :to => @account_b, :code => :test)
|
82
|
+
end
|
83
|
+
}.to raise_error(DoubleEntry::Locking::LockNotHeld, "No lock held for account: account_b, scope 2")
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should allow nested locks if the outer lock locks all the accounts" do
|
87
|
+
expect do
|
88
|
+
DoubleEntry::Locking.lock_accounts(@account_a, @account_b) do
|
89
|
+
DoubleEntry::Locking.lock_accounts(@account_a, @account_b) { }
|
90
|
+
end
|
91
|
+
end.to_not raise_error
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should not allow nested locks if the out lock doesn't lock all the accounts" do
|
95
|
+
expect do
|
96
|
+
DoubleEntry::Locking.lock_accounts(@account_a) do
|
97
|
+
DoubleEntry::Locking.lock_accounts(@account_a, @account_b) { }
|
98
|
+
end
|
99
|
+
end.to raise_error(DoubleEntry::Locking::LockNotHeld, "No lock held for account: account_b, scope 2")
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should roll back a locking transaction" do
|
103
|
+
DoubleEntry::Locking.lock_accounts(@account_a, @account_b) do
|
104
|
+
DoubleEntry.transfer(Money.new(10_00), :from => @account_a, :to => @account_b, :code => :test)
|
105
|
+
raise ActiveRecord::Rollback
|
106
|
+
end
|
107
|
+
expect(DoubleEntry.balance(@account_a)).to eq Money.new(0)
|
108
|
+
expect(DoubleEntry.balance(@account_b)).to eq Money.new(0)
|
109
|
+
end
|
110
|
+
|
111
|
+
it "should roll back a locking transaction if there's an exception" do
|
112
|
+
expect do
|
113
|
+
DoubleEntry::Locking.lock_accounts(@account_a, @account_b) do
|
114
|
+
DoubleEntry.transfer(Money.new(10_00), :from => @account_a, :to => @account_b, :code => :test)
|
115
|
+
raise "Yeah, right"
|
116
|
+
end
|
117
|
+
end.to raise_error("Yeah, right")
|
118
|
+
expect(DoubleEntry.balance(@account_a)).to eq Money.new(0)
|
119
|
+
expect(DoubleEntry.balance(@account_b)).to eq Money.new(0)
|
120
|
+
end
|
121
|
+
|
122
|
+
it "should allow multiple threads to lock at the same time" do
|
123
|
+
threads = Array.new
|
124
|
+
|
125
|
+
expect do
|
126
|
+
threads << Thread.new do
|
127
|
+
sleep 0.05
|
128
|
+
DoubleEntry::Locking.lock_accounts(@account_a, @account_b) do
|
129
|
+
DoubleEntry.transfer(Money.new(10_00), :from => @account_a, :to => @account_b, :code => :test)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
threads << Thread.new do
|
134
|
+
DoubleEntry::Locking.lock_accounts(@account_c, @account_d) do
|
135
|
+
sleep 0.1
|
136
|
+
DoubleEntry.transfer(Money.new(10_00), :from => @account_c, :to => @account_d, :code => :test)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
threads.each(&:join)
|
141
|
+
end.to_not raise_error
|
142
|
+
end
|
143
|
+
|
144
|
+
it "should allow multiple threads to lock accounts without balances at the same time" do
|
145
|
+
threads = Array.new
|
146
|
+
|
147
|
+
expect do
|
148
|
+
threads << Thread.new { DoubleEntry::Locking.lock_accounts(@account_a, @account_b) { } }
|
149
|
+
threads << Thread.new { DoubleEntry::Locking.lock_accounts(@account_c, @account_d) { } }
|
150
|
+
|
151
|
+
threads.each(&:join)
|
152
|
+
end.to_not raise_error
|
153
|
+
end
|
154
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "spec_helper"
|
3
|
+
describe DoubleEntry::MonthRange do
|
4
|
+
|
5
|
+
describe "::from_time" do
|
6
|
+
subject(:from_time) { DoubleEntry::MonthRange.from_time(given_time) }
|
7
|
+
|
8
|
+
context "given the Time 31st March 2012" do
|
9
|
+
let(:given_time) { Time.new(2012, 3, 31) }
|
10
|
+
its(:year) { should eq 2012 }
|
11
|
+
its(:month) { should eq 3 }
|
12
|
+
end
|
13
|
+
|
14
|
+
context "given the Date 31st March 2012" do
|
15
|
+
let(:given_time) { Date.parse("2012-03-31") }
|
16
|
+
its(:year) { should eq 2012 }
|
17
|
+
its(:month) { should eq 3 }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "::reportable_months" do
|
22
|
+
subject(:reportable_months) { DoubleEntry::MonthRange.reportable_months }
|
23
|
+
|
24
|
+
context "The date is 1st March 1970" do
|
25
|
+
before { Timecop.freeze(Time.new(1970, 3, 1)) }
|
26
|
+
|
27
|
+
it { should eq [
|
28
|
+
DoubleEntry::MonthRange.new(year: 1970, month: 1),
|
29
|
+
DoubleEntry::MonthRange.new(year: 1970, month: 2),
|
30
|
+
DoubleEntry::MonthRange.new(year: 1970, month: 3),
|
31
|
+
] }
|
32
|
+
|
33
|
+
context "My business started on 5th Feb 1970" do
|
34
|
+
before do
|
35
|
+
DoubleEntry::Reporting.configure do |config|
|
36
|
+
config.start_of_business = Time.new(1970, 2, 5)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
it { should eq [
|
41
|
+
DoubleEntry::MonthRange.new(year: 1970, month: 2),
|
42
|
+
DoubleEntry::MonthRange.new(year: 1970, month: 3),
|
43
|
+
] }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context "The date is 1st Jan 1970" do
|
48
|
+
before { Timecop.freeze(Time.new(1970, 1, 1)) }
|
49
|
+
|
50
|
+
it { should eq [ DoubleEntry::MonthRange.new(year: 1970, month: 1) ] }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe "::beginning_of_financial_year" do
|
55
|
+
let(:month_range) { DoubleEntry::MonthRange.new(:year => year, :month => month) }
|
56
|
+
let(:year) { 2014 }
|
57
|
+
|
58
|
+
context "the first month of the financial year is July" do
|
59
|
+
subject(:beginning_of_financial_year) { month_range.beginning_of_financial_year }
|
60
|
+
context "returns the current year if the month is after July" do
|
61
|
+
let(:month) { 10 }
|
62
|
+
it { should eq(DoubleEntry::MonthRange.new(:year => 2014, :month => 7)) }
|
63
|
+
end
|
64
|
+
|
65
|
+
context "returns the previous year if the month is before July" do
|
66
|
+
let(:month) { 3 }
|
67
|
+
it { should eq(DoubleEntry::MonthRange.new(:year => 2013, :month => 7)) }
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context "the first month of the financial year is January" do
|
72
|
+
subject(:beginning_of_financial_year) { month_range.beginning_of_financial_year }
|
73
|
+
|
74
|
+
before do
|
75
|
+
DoubleEntry::Reporting.configure do |config|
|
76
|
+
config.first_month_of_financial_year = 1
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
context "returns the current year if the month is after January" do
|
81
|
+
let(:month) { 10 }
|
82
|
+
it { should eq(DoubleEntry::MonthRange.new(:year => 2014, :month => 1)) }
|
83
|
+
end
|
84
|
+
|
85
|
+
context "returns the current year if the month is January" do
|
86
|
+
let(:month) { 1 }
|
87
|
+
it { should eq(DoubleEntry::MonthRange.new(:year => 2014, :month => 1)) }
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context "the first month of the financial year is December" do
|
92
|
+
subject(:beginning_of_financial_year) { month_range.beginning_of_financial_year }
|
93
|
+
|
94
|
+
before do
|
95
|
+
DoubleEntry::Reporting.configure do |config|
|
96
|
+
config.first_month_of_financial_year = 12
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
context "returns the previous year if the month is before December (in the same year)" do
|
101
|
+
let(:month) { 11 }
|
102
|
+
it { should eq(DoubleEntry::MonthRange.new(:year => 2013, :month => 12)) }
|
103
|
+
end
|
104
|
+
|
105
|
+
context "returns the previous year if the month is after December (in the next year)" do
|
106
|
+
let(:year) { 2015 }
|
107
|
+
let(:month) { 1 }
|
108
|
+
it { should eq(DoubleEntry::MonthRange.new(:year => 2014, :month => 12)) }
|
109
|
+
end
|
110
|
+
|
111
|
+
context "returns the current year if the month is December" do
|
112
|
+
let(:month) { 12 }
|
113
|
+
it { should eq(DoubleEntry::MonthRange.new(:year => 2014, :month => 12)) }
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
context "Given a start time of 3rd Dec 1982" do
|
118
|
+
subject(:reportable_months) { DoubleEntry::MonthRange.reportable_months(from: Time.new(1982, 12, 3)) }
|
119
|
+
|
120
|
+
context "The date is 2nd Feb 1983" do
|
121
|
+
before { Timecop.freeze(Time.new(1983, 2, 2)) }
|
122
|
+
|
123
|
+
it { should eq [
|
124
|
+
DoubleEntry::MonthRange.new(year: 1982, month: 12),
|
125
|
+
DoubleEntry::MonthRange.new(year: 1983, month: 1),
|
126
|
+
DoubleEntry::MonthRange.new(year: 1983, month: 2),
|
127
|
+
] }
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|