double_entry 0.0.1.pre → 0.1.0.pre.pre.alpha
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 +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
|