keepr 0.0.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.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.travis.yml +9 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +62 -0
  7. data/Rakefile +6 -0
  8. data/ci/Gemfile-rails-4-1 +11 -0
  9. data/ci/Gemfile-rails-4-2 +11 -0
  10. data/keepr.gemspec +31 -0
  11. data/lib/generators/keepr/migration/migration_generator.rb +23 -0
  12. data/lib/generators/keepr/migration/templates/migration.rb +72 -0
  13. data/lib/keepr.rb +15 -0
  14. data/lib/keepr/account.rb +120 -0
  15. data/lib/keepr/active_record_extension.rb +27 -0
  16. data/lib/keepr/cost_center.rb +8 -0
  17. data/lib/keepr/group.rb +33 -0
  18. data/lib/keepr/groups_creator.rb +51 -0
  19. data/lib/keepr/groups_creator/asset.txt +36 -0
  20. data/lib/keepr/groups_creator/liability.txt +28 -0
  21. data/lib/keepr/groups_creator/profit_and_loss.txt +31 -0
  22. data/lib/keepr/journal.rb +48 -0
  23. data/lib/keepr/posting.rb +74 -0
  24. data/lib/keepr/tax.rb +13 -0
  25. data/lib/keepr/version.rb +3 -0
  26. data/spec/account_spec.rb +210 -0
  27. data/spec/active_record_extension_spec.rb +60 -0
  28. data/spec/cost_center_spec.rb +16 -0
  29. data/spec/database.yml +3 -0
  30. data/spec/factories/account.rb +7 -0
  31. data/spec/factories/cost_center.rb +6 -0
  32. data/spec/factories/group.rb +6 -0
  33. data/spec/factories/tax.rb +8 -0
  34. data/spec/group_spec.rb +89 -0
  35. data/spec/groups_creator_spec.rb +45 -0
  36. data/spec/journal_spec.rb +111 -0
  37. data/spec/posting_spec.rb +124 -0
  38. data/spec/spec_helper.rb +58 -0
  39. data/spec/support/document.rb +3 -0
  40. data/spec/support/ledger.rb +3 -0
  41. data/spec/support/spec_migration.rb +16 -0
  42. data/spec/tax_spec.rb +35 -0
  43. metadata +213 -0
@@ -0,0 +1,8 @@
1
+ class Keepr::CostCenter < ActiveRecord::Base
2
+ self.table_name = 'keepr_cost_centers'
3
+
4
+ validates_presence_of :number, :name
5
+ validates_uniqueness_of :number
6
+
7
+ has_many :keepr_postings, :class_name => 'Keepr::Posting', :foreign_key => 'keepr_cost_center_id', :dependent => :restrict_with_error
8
+ end
@@ -0,0 +1,33 @@
1
+ class Keepr::Group < ActiveRecord::Base
2
+ self.table_name = 'keepr_groups'
3
+
4
+ has_ancestry :orphan_strategy => :restrict
5
+
6
+ enum :target => [ :asset, :liability, :profit_and_loss ]
7
+
8
+ validates_presence_of :name
9
+
10
+ has_many :keepr_accounts, :class_name => 'Keepr::Account', :foreign_key => 'keepr_group_id', :dependent => :restrict_with_error
11
+
12
+ before_validation :get_from_parent
13
+
14
+ def self.result
15
+ where(:is_result => true).first
16
+ end
17
+
18
+ def keepr_postings
19
+ if is_result
20
+ Keepr::Posting.joins(:keepr_account).where(:keepr_accounts => { :kind => [ Keepr::Account.kinds[:revenue],
21
+ Keepr::Account.kinds[:expense] ] })
22
+ else
23
+ Keepr::Posting.joins(:keepr_account => :keepr_group).merge(self.subtree)
24
+ end
25
+ end
26
+
27
+ private
28
+ def get_from_parent
29
+ if self.parent
30
+ self.target = self.parent.target
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,51 @@
1
+ # encoding: utf-8
2
+ class Keepr::GroupsCreator
3
+ def initialize(target)
4
+ @target = target
5
+ end
6
+
7
+ def run
8
+ case @target
9
+ when :balance then
10
+ load 'asset.txt', :target => :asset
11
+ load 'liability.txt', :target => :liability
12
+ when :profit_and_loss
13
+ load 'profit_and_loss.txt', :target => :profit_and_loss
14
+ else
15
+ raise ArgumentError
16
+ end
17
+ end
18
+
19
+ private
20
+ def load(filename, options)
21
+ full_filename = File.join(File.dirname(__FILE__), "groups_creator/#{filename}".downcase)
22
+ lines = File.readlines(full_filename)
23
+ last_depth = 0
24
+ parents = []
25
+
26
+ lines.each do |line|
27
+ # Count leading spaces to calc hierarchy depth
28
+ depth = line[/\A */].size / 2
29
+
30
+ # Remove leading spaces and separate number and name
31
+ number, name = line.lstrip.match(/^(.*?)\s(.+)$/).to_a[1..-1]
32
+
33
+ attributes = options.merge(:name => name, :number => number)
34
+ if @target == :balance && name == 'Jahresüberschuss/Jahresfehlbetrag'
35
+ attributes[:is_result] = true
36
+ end
37
+
38
+ if depth == 0
39
+ parents = []
40
+ group = Keepr::Group.create!(attributes)
41
+ else
42
+ parents.pop if depth <= last_depth
43
+ parents.pop if depth < last_depth
44
+ group = parents.last.children.create!(attributes)
45
+ end
46
+ parents.push(group)
47
+
48
+ last_depth = depth
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,36 @@
1
+ A. Anlagevermögen
2
+ I. Immaterielle Vermögensgegenstände
3
+ 1. Selbst geschaffene gewerbliche Schutzrechte und ähnliche Rechte und Werte
4
+ 2. entgeltlich erworbene Konzessionen, gewerbliche Schutzrechte und ähnliche Rechte und Werte sowie Lizenzen an solchen Rechten und Werten
5
+ 3. Geschäfts- oder Firmenwert
6
+ 4. geleistete Anzahlungen
7
+ II. Sachanlagen
8
+ 1. Grundstücke, grundstücksgleiche Rechte und Bauten einschließlich der Bauten auf fremden Grundstücken
9
+ 2. technische Anlagen und Maschinen
10
+ 3. andere Anlagen, Betriebs- und Geschäftsausstattung
11
+ 4. geleistete Anzahlungen und Anlagen im Bau
12
+ III. Finanzanlagen
13
+ 1. Anteile an verbundenen Unternehmen
14
+ 2. Ausleihungen an verbundene Unternehmen
15
+ 3. Beteiligungen
16
+ 4. Ausleihungen an Unternehmen, mit denen ein Beteiligungsverhältnis besteht
17
+ 5. Wertpapiere des Anlagevermögens
18
+ 6. sonstige Ausleihungen
19
+ B. Umlaufvermögen
20
+ I. Vorräte
21
+ 1. Roh-, Hilfs- und Betriebsstoffe
22
+ 2. unfertige Erzeugnisse, unfertige Leistungen
23
+ 3. fertige Erzeugnisse und Waren
24
+ 4. geleistete Anzahlungen
25
+ II. Forderungen und sonstige Vermögensgegenstände
26
+ 1. Forderungen aus Lieferungen und Leistungen
27
+ 2. Forderungen gegen verbundene Unternehmen
28
+ 3. Forderungen gegen Unternehmen, mit denen ein Beteiligungsverhältnis besteht
29
+ 4. sonstige Vermögensgegenstände
30
+ III. Wertpapiere
31
+ 1. Anteile an verbundenen Unternehmen
32
+ 2. sonstige Wertpapiere
33
+ IV. Kassenbestand, Bundesbankguthaben, Guthaben bei Kreditinstituten und Schecks
34
+ C. Rechnungsabgrenzungsposten
35
+ D. Aktive latente Steuern
36
+ E. Aktiver Unterschiedsbetrag aus der Vermögensverrechnung
@@ -0,0 +1,28 @@
1
+ A. Eigenkapital
2
+ I. Gezeichnetes Kapital
3
+ II. Kapitalrücklage
4
+ III. Gewinnrücklagen
5
+ 1. gesetzliche Rücklage
6
+ 2. Rücklage für Anteile an einem herrschenden oder mehrheitlich beteiligten Unternehmen
7
+ 3. satzungsmäßige Rücklagen
8
+ 4. andere Gewinnrücklagen
9
+ IV. Gewinnvortrag/Verlustvortrag
10
+ V. Jahresüberschuss/Jahresfehlbetrag
11
+ B. Rückstellungen
12
+ 1. Rückstellungen für Pensionen und ähnliche Verpflichtungen
13
+ 2. Steuerrückstellungen
14
+ 3. sonstige Rückstellungen
15
+ C. Verbindlichkeiten
16
+ 1. Anleihen
17
+ davon konvertibel
18
+ 2. Verbindlichkeiten gegenüber Kreditinstituten
19
+ 3. erhaltene Anzahlungen auf Bestellungen
20
+ 4. Verbindlichkeiten aus Lieferungen und Leistungen
21
+ 5. Verbindlichkeiten aus der Annahme gezogener Wechsel und der Ausstellung eigener Wechsel
22
+ 6. Verbindlichkeiten gegenüber verbundenen Unternehmen
23
+ 7. Verbindlichkeiten gegenüber Unternehmen, mit denen ein Beteiligungsverhältnis besteht
24
+ 8. sonstige Verbindlichkeiten
25
+ a) davon aus Steuern
26
+ b) davon im Rahmen der sozialen Sicherheit
27
+ D. Rechnungsabgrenzungsposten
28
+ E. Passive latente Steuern
@@ -0,0 +1,31 @@
1
+ 01. Umsatzerlöse
2
+ 02. Erhöhung oder Verminderung des Bestands an fertigen und unfertigen Erzeugnissen
3
+ 03. andere aktivierte Eigenleistungen
4
+ 04. sonstige betriebliche Erträge
5
+ 05. Materialaufwand
6
+ a) Aufwendungen für Roh-, Hilfs- und Betriebsstoffe und für bezogene Waren
7
+ b) Aufwendungen für bezogene Leistungen
8
+ 06. Personalaufwand
9
+ a) Löhne und Gehälter
10
+ b) soziale Abgaben und Aufwendungen für Altersversorgung und für Unterstützung
11
+ davon für Altersversorgung
12
+ 07. Abschreibungen
13
+ a) auf immaterielle Vermögensgegenstände des Anlagevermögens und Sachanlagen
14
+ b) auf Vermögensgegenstände des Umlaufvermögens, soweit diese die in der Kapitalgesellschaft üblichen Abschreibungen überschreiten
15
+ 08. sonstige betriebliche Aufwendungen
16
+ 09. Erträge aus Beteiligungen
17
+ a) davon aus verbundenen Unternehmen
18
+ 10. Erträge aus anderen Wertpapieren und Ausleihungen des Finanzanlagevermögens
19
+ a) davon aus verbundenen Unternehmen
20
+ 11. sonstige Zinsen und ähnliche Erträge
21
+ a) davon aus verbundenen Unternehmen
22
+ 12. Abschreibungen auf Finanzanlagen und auf Wertpapiere des Umlaufvermögens
23
+ 13. Zinsen und ähnliche Aufwendungen
24
+ davon an verbundene Unternehmen
25
+ 14. Ergebnis der gewöhnlichen Geschäftstätigkeit
26
+ 15. außerordentliche Erträge
27
+ 16. außerordentliche Aufwendungen
28
+ 17. außerordentliches Ergebnis
29
+ 18. Steuern vom Einkommen und vom Ertrag
30
+ 19. sonstige Steuern
31
+ 20. Jahresüberschuss/Jahresfehlbetrag
@@ -0,0 +1,48 @@
1
+ class Keepr::Journal < ActiveRecord::Base
2
+ self.table_name = 'keepr_journals'
3
+
4
+ validates_presence_of :date
5
+ validates_uniqueness_of :number, :allow_blank => true
6
+
7
+ has_many :keepr_postings, -> { order(:amount => :desc) },
8
+ :class_name => 'Keepr::Posting', :foreign_key => 'keepr_journal_id', :dependent => :destroy
9
+
10
+ belongs_to :accountable, :polymorphic => true
11
+
12
+ accepts_nested_attributes_for :keepr_postings, :allow_destroy => true, :reject_if => :all_blank
13
+
14
+ default_scope { order({:date => :desc}, {:id => :desc}) }
15
+
16
+ validate :validate_postings
17
+
18
+ def credit_postings
19
+ existing_postings.select(&:credit?)
20
+ end
21
+
22
+ def debit_postings
23
+ existing_postings.select(&:debit?)
24
+ end
25
+
26
+ def amount
27
+ debit_postings.sum(&:amount)
28
+ end
29
+
30
+ after_initialize :set_defaults
31
+
32
+ private
33
+ def existing_postings
34
+ keepr_postings.to_a.delete_if(&:marked_for_destruction?)
35
+ end
36
+
37
+ def set_defaults
38
+ self.date ||= Date.today
39
+ end
40
+
41
+ def validate_postings
42
+ if existing_postings.map(&:keepr_account_id).uniq.length < 2
43
+ errors.add(:base, 'At least two accounts have to be booked!')
44
+ elsif existing_postings.sum(&:raw_amount) != 0
45
+ errors.add(:base, 'Debit does not match credit!')
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,74 @@
1
+ class Keepr::Posting < ActiveRecord::Base
2
+ self.table_name = 'keepr_postings'
3
+
4
+ validates_presence_of :keepr_account_id, :amount
5
+ validate :cost_center_validation
6
+
7
+ belongs_to :keepr_account, :class_name => 'Keepr::Account'
8
+ belongs_to :keepr_journal, :class_name => 'Keepr::Journal'
9
+ belongs_to :keepr_cost_center, :class_name => 'Keepr::CostCenter'
10
+
11
+ SIDE_DEBIT = 'debit'
12
+ SIDE_CREDIT = 'credit'
13
+
14
+ scope :debits, -> { where('amount >= 0') }
15
+ scope :credits, -> { where('amount < 0') }
16
+
17
+ def side
18
+ @side || begin
19
+ (raw_amount < 0 ? SIDE_CREDIT : SIDE_DEBIT) if raw_amount
20
+ end
21
+ end
22
+
23
+ def side=(value)
24
+ @side = value
25
+
26
+ if credit?
27
+ self.raw_amount = -amount if amount
28
+ elsif debit?
29
+ self.raw_amount = amount if amount
30
+ else
31
+ raise ArgumentError
32
+ end
33
+ end
34
+
35
+ def debit?
36
+ side == SIDE_DEBIT
37
+ end
38
+
39
+ def credit?
40
+ side == SIDE_CREDIT
41
+ end
42
+
43
+ def raw_amount
44
+ read_attribute(:amount)
45
+ end
46
+
47
+ def raw_amount=(value)
48
+ write_attribute(:amount, value)
49
+ end
50
+
51
+ def amount
52
+ raw_amount.try(:abs)
53
+ end
54
+
55
+ def amount=(value)
56
+ raise ArgumentError.new('Negative amount not allowed!') if value.to_f < 0
57
+ @side ||= SIDE_DEBIT
58
+
59
+ if credit?
60
+ self.raw_amount = -value
61
+ else
62
+ self.raw_amount = value
63
+ end
64
+ end
65
+
66
+ private
67
+ def cost_center_validation
68
+ if keepr_cost_center
69
+ unless keepr_account.profit_and_loss?
70
+ errors.add(:keepr_cost_center_id, 'allowed for expense or revenue accounts only')
71
+ end
72
+ end
73
+ end
74
+ end
data/lib/keepr/tax.rb ADDED
@@ -0,0 +1,13 @@
1
+ class Keepr::Tax < ActiveRecord::Base
2
+ self.table_name = 'keepr_taxes'
3
+
4
+ validates_presence_of :name, :value, :keepr_account_id
5
+ validates_numericality_of :value
6
+
7
+ belongs_to :keepr_account, :class_name => 'Keepr::Account'
8
+ has_many :keepr_accounts, :class_name => 'Keepr::Account', :foreign_key => 'keepr_tax_id', :dependent => :restrict_with_error
9
+
10
+ validate do |tax|
11
+ tax.errors.add(:keepr_account_id, 'circular reference') if tax.keepr_account.try(:keepr_tax) == tax
12
+ end
13
+ end
@@ -0,0 +1,3 @@
1
+ module Keepr
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,210 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe Keepr::Account do
5
+ describe :number_as_string do
6
+ it "should return number with leading zeros for low values" do
7
+ account = Keepr::Account.new(:number => 999)
8
+ expect(account.number_as_string).to eq('0999')
9
+ end
10
+
11
+ it "should return number unchanged for high values" do
12
+ account = Keepr::Account.new(:number => 70000)
13
+ expect(account.number_as_string).to eq('70000')
14
+ end
15
+ end
16
+
17
+ describe :to_s do
18
+ it "should format" do
19
+ account = Keepr::Account.new(:number => 27, :name => 'Software')
20
+ expect(account.to_s).to eq('0027 (Software)')
21
+ end
22
+ end
23
+ end
24
+
25
+ describe Keepr::Account do
26
+ let!(:account_1000) { FactoryGirl.create(:account, :number => 1000) }
27
+ let!(:account_1200) { FactoryGirl.create(:account, :number => 1200) }
28
+
29
+ before :each do
30
+ Keepr::Journal.create! :date => Date.yesterday,
31
+ :keepr_postings_attributes => [
32
+ { :keepr_account => account_1000, :amount => 20, :side => 'debit' },
33
+ { :keepr_account => account_1200, :amount => 20, :side => 'credit' }
34
+ ]
35
+
36
+ Keepr::Journal.create! :date => Date.yesterday,
37
+ :keepr_postings_attributes => [
38
+ { :keepr_account => account_1000, :amount => 10, :side => 'credit' },
39
+ { :keepr_account => account_1200, :amount => 10, :side => 'debit' },
40
+ ]
41
+
42
+ Keepr::Journal.create! :date => Date.today,
43
+ :keepr_postings_attributes => [
44
+ { :keepr_account => account_1000, :amount => 200, :side => 'debit' },
45
+ { :keepr_account => account_1200, :amount => 200, :side => 'credit' }
46
+ ]
47
+
48
+ Keepr::Journal.create! :date => Date.today,
49
+ :keepr_postings_attributes => [
50
+ { :keepr_account => account_1000, :amount => 100, :side => 'credit' },
51
+ { :keepr_account => account_1200, :amount => 100, :side => 'debit' },
52
+ ]
53
+ end
54
+
55
+ describe "with group" do
56
+ let!(:result_group) { FactoryGirl.create(:group, :is_result => true) }
57
+ let!(:normal_group) { FactoryGirl.create(:group, :is_result => false) }
58
+
59
+ it "should not be assigned to result group" do
60
+ account = FactoryGirl.build(:account, :number => 123, :keepr_group => result_group)
61
+ expect(account).to_not be_valid
62
+ expect(account.errors[:keepr_group_id]).to be_present
63
+ end
64
+
65
+ it "should be assigned to normal group" do
66
+ account = FactoryGirl.build(:account, :number => 123, :keepr_group => normal_group)
67
+ expect(account).to be_valid
68
+ expect(account.errors[:keepr_group_id]).to be_blank
69
+ end
70
+ end
71
+
72
+ describe :balance do
73
+ it 'should calc total' do
74
+ expect(account_1000.balance).to eq(110)
75
+ expect(account_1200.balance).to eq(-110)
76
+ end
77
+
78
+ it 'should calc total for a given date (including)' do
79
+ expect(account_1000.balance(Date.today)).to eq(110)
80
+ expect(account_1200.balance(Date.today)).to eq(-110)
81
+ end
82
+
83
+ it 'should calc total for a given date (excluding)' do
84
+ expect(account_1000.balance(Date.yesterday)).to eq(10)
85
+ expect(account_1200.balance(Date.yesterday)).to eq(-10)
86
+ end
87
+
88
+ it 'should calc total for Range' do
89
+ expect(account_1000.balance(Date.yesterday...Date.today)).to eq(110)
90
+ expect(account_1200.balance(Date.yesterday...Date.today)).to eq(-110)
91
+
92
+ expect(account_1000.balance(Date.today...Date.tomorrow)).to eq(100)
93
+ expect(account_1200.balance(Date.today...Date.tomorrow)).to eq(-100)
94
+ end
95
+
96
+ it 'should fail for other param' do
97
+ expect { account_1000.balance(0) }.to raise_error(ArgumentError)
98
+ end
99
+ end
100
+
101
+ describe :with_sums do
102
+ it 'should work without param' do
103
+ account1, account2 = Keepr::Account.with_sums.having('sum_amount <> 0')
104
+
105
+ expect(account1.number).to eq(1000)
106
+ expect(account1.balance).to eq(110)
107
+ expect(account2.number).to eq(1200)
108
+ expect(account2.balance).to eq(-110)
109
+ end
110
+
111
+ it 'should work with Date' do
112
+ account1, account2 = Keepr::Account.with_sums(Date.yesterday).having('sum_amount <> 0')
113
+
114
+ expect(account1.number).to eq(1000)
115
+ expect(account1.sum_amount).to eq(10)
116
+ expect(account2.number).to eq(1200)
117
+ expect(account2.sum_amount).to eq(-10)
118
+ end
119
+
120
+ it 'should work with Range' do
121
+ account1, account2 = Keepr::Account.with_sums(Date.today..Date.tomorrow).having('sum_amount <> 0')
122
+
123
+ expect(account1.number).to eq(1000)
124
+ expect(account1.sum_amount).to eq(100)
125
+ expect(account2.number).to eq(1200)
126
+ expect(account2.sum_amount).to eq(-100)
127
+ end
128
+
129
+ it 'should raise for other param' do
130
+ expect { Keepr::Account.with_sums(Time.current) }.to raise_error(ArgumentError)
131
+ expect { Keepr::Account.with_sums(0) }.to raise_error(ArgumentError)
132
+ expect { Keepr::Account.with_sums(:foo) }.to raise_error(ArgumentError)
133
+ end
134
+ end
135
+ end
136
+
137
+ describe Keepr::Account, 'with subaccounts' do
138
+ let!(:account_1400) { FactoryGirl.create(:account, :number => 1400) }
139
+ let!(:account_10000) { FactoryGirl.create(:account, :number => 10000, :parent => account_1400) }
140
+ let!(:account_8400) { FactoryGirl.create(:account, :number => 8400) }
141
+
142
+ before :each do
143
+ Keepr::Journal.create! :date => Date.yesterday,
144
+ :keepr_postings_attributes => [
145
+ { :keepr_account => account_10000, :amount => 20, :side => 'debit' },
146
+ { :keepr_account => account_8400, :amount => 20, :side => 'credit' }
147
+ ]
148
+ end
149
+
150
+ describe :keepr_postings do
151
+ it 'should include postings from descendant accounts' do
152
+ expect(account_1400.keepr_postings.size).to eq(1)
153
+ expect(account_10000.keepr_postings.size).to eq(1)
154
+ end
155
+ end
156
+
157
+ describe :balance do
158
+ it 'should include postings from descendant accounts' do
159
+ expect(account_1400.reload.balance).to eq(20)
160
+ expect(account_10000.reload.balance).to eq(20)
161
+ end
162
+
163
+ it 'should include postings from descendant accounts with date given' do
164
+ expect(account_1400.balance(Date.today)).to eq(20)
165
+ expect(account_10000.balance(Date.today)).to eq(20)
166
+ end
167
+ end
168
+
169
+ describe :with_sums do
170
+ it 'should calc balance' do
171
+ expect(Keepr::Account.with_sums.
172
+ select { |a| (a.sum_amount || 0) != 0 }.
173
+ map { |a| [a.number, a.sum_amount] }).
174
+ to eq([[8400, -20], [10000, 20]])
175
+ end
176
+ end
177
+
178
+ describe :merged_with_sums do
179
+ it 'should calc merged balance' do
180
+ expect(Keepr::Account.merged_with_sums.
181
+ select { |a| (a.sum_amount || 0) != 0 }.
182
+ map { |a| [a.number, a.sum_amount] }).
183
+ to eq([[1400, 20], [8400, -20]])
184
+ end
185
+ end
186
+ end
187
+
188
+ describe Keepr::Account, 'with tax' do
189
+ let!(:tax_account) { Keepr::Account.create! :number => 1776,
190
+ :name => 'Umsatzsteuer 19%',
191
+ :kind => :asset }
192
+
193
+ let!(:tax) { Keepr::Tax.create! :name => 'USt19',
194
+ :description => 'Umsatzsteuer 19%',
195
+ :value => 19.0,
196
+ :keepr_account => tax_account }
197
+
198
+ it "should link to tax" do
199
+ account = Keepr::Account.new :number => 8400,
200
+ :name => 'Erlöse 19% USt',
201
+ :kind => :revenue,
202
+ :keepr_tax => tax
203
+ expect(tax_account).to be_valid
204
+ end
205
+
206
+ it "should avoid circular reference" do
207
+ tax_account.keepr_tax_id = tax.id
208
+ expect(tax_account).to be_invalid
209
+ end
210
+ end