keepr 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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