keepr 0.3.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +5 -5
  2. data/.travis.yml +14 -10
  3. data/Gemfile +2 -0
  4. data/LICENSE.txt +1 -1
  5. data/README.md +3 -3
  6. data/Rakefile +3 -1
  7. data/ci/Gemfile-rails-4-2 +4 -4
  8. data/ci/Gemfile-rails-5-0 +4 -4
  9. data/ci/Gemfile-rails-5-1 +12 -0
  10. data/ci/Gemfile-rails-5-2 +12 -0
  11. data/ci/{Gemfile-rails-4-1 → Gemfile-rails-6-0} +4 -4
  12. data/keepr.gemspec +15 -14
  13. data/lib/generators/keepr/migration/migration_generator.rb +5 -3
  14. data/lib/generators/keepr/migration/templates/migration.rb +27 -25
  15. data/lib/keepr.rb +8 -0
  16. data/lib/keepr/account.rb +66 -61
  17. data/lib/keepr/account_export.rb +10 -14
  18. data/lib/keepr/active_record_extension.rb +10 -8
  19. data/lib/keepr/contact_export.rb +6 -7
  20. data/lib/keepr/cost_center.rb +3 -1
  21. data/lib/keepr/group.rb +25 -16
  22. data/lib/keepr/groups_creator.rb +11 -11
  23. data/lib/keepr/journal.rb +19 -16
  24. data/lib/keepr/journal_export.rb +10 -7
  25. data/lib/keepr/posting.rb +26 -21
  26. data/lib/keepr/tax.rb +4 -2
  27. data/lib/keepr/version.rb +3 -1
  28. data/spec/factories/account.rb +6 -4
  29. data/spec/factories/cost_center.rb +5 -3
  30. data/spec/factories/group.rb +5 -3
  31. data/spec/factories/tax.rb +7 -5
  32. data/spec/keepr/account_export_spec.rb +30 -27
  33. data/spec/keepr/account_spec.rb +92 -87
  34. data/spec/keepr/active_record_extension_spec.rb +38 -36
  35. data/spec/keepr/contact_export_spec.rb +20 -17
  36. data/spec/keepr/cost_center_spec.rb +9 -7
  37. data/spec/keepr/group_spec.rb +53 -49
  38. data/spec/keepr/groups_creator_spec.rb +7 -4
  39. data/spec/keepr/journal_export_spec.rb +76 -75
  40. data/spec/keepr/journal_spec.rb +48 -46
  41. data/spec/keepr/posting_spec.rb +25 -23
  42. data/spec/keepr/tax_spec.rb +21 -14
  43. data/spec/spec_helper.rb +13 -11
  44. data/spec/support/contact.rb +2 -0
  45. data/spec/support/document.rb +2 -0
  46. data/spec/support/ledger.rb +2 -0
  47. data/spec/support/spec_migration.rb +3 -1
  48. metadata +23 -22
@@ -1,35 +1,31 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Keepr::AccountExport
2
- def initialize(accounts, header_options={}, &block)
4
+ def initialize(accounts, header_options = {}, &block)
3
5
  @accounts = accounts
4
6
  @header_options = header_options
5
7
  @block = block
6
8
  end
7
9
 
8
- def to_s
9
- export.to_s
10
- end
11
-
12
- def to_file(filename)
13
- export.to_file(filename)
14
- end
10
+ delegate :to_s, :to_file,
11
+ to: :export
15
12
 
16
- private
13
+ private
17
14
 
18
15
  def export
19
16
  export = Datev::AccountExport.new(@header_options)
20
17
 
21
18
  @accounts.reorder(:number).each do |account|
22
- unless account.debtor? || account.creditor?
23
- export << to_datev(account)
24
- end
19
+ export << to_datev(account) unless account.debtor? || account.creditor?
25
20
  end
26
21
 
27
22
  export
28
23
  end
29
24
 
30
25
  def to_datev(account)
31
- { 'Konto' => account.number,
32
- 'Kontenbeschriftung' => account.name.slice(0,40)
26
+ {
27
+ 'Konto' => account.number,
28
+ 'Kontenbeschriftung' => account.name.slice(0, 40)
33
29
  }.merge(@block ? @block.call(account) : {})
34
30
  end
35
31
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Keepr::ActiveRecordExtension
2
4
  def self.included(base)
3
5
  base.extend ClassMethods
@@ -5,19 +7,19 @@ module Keepr::ActiveRecordExtension
5
7
 
6
8
  module ClassMethods
7
9
  def has_one_keepr_account
8
- has_one :keepr_account, :class_name => 'Keepr::Account', :as => :accountable, :dependent => :restrict_with_error
9
- has_many :keepr_postings, :class_name => 'Keepr::Posting', :through => :keepr_account, :dependent => :restrict_with_error
10
+ has_one :keepr_account, class_name: 'Keepr::Account', as: :accountable, dependent: :restrict_with_error
11
+ has_many :keepr_postings, class_name: 'Keepr::Posting', through: :keepr_account, dependent: :restrict_with_error
10
12
  end
11
13
 
12
14
  def has_many_keepr_accounts
13
- has_many :keepr_accounts, :class_name => 'Keepr::Account', :as => :accountable, :dependent => :restrict_with_error
14
- has_many :keepr_postings, :class_name => 'Keepr::Posting', :through => :keepr_accounts, :dependent => :restrict_with_error
15
+ has_many :keepr_accounts, class_name: 'Keepr::Account', as: :accountable, dependent: :restrict_with_error
16
+ has_many :keepr_postings, class_name: 'Keepr::Posting', through: :keepr_accounts, dependent: :restrict_with_error
15
17
  end
16
18
 
17
19
  def has_keepr_journals
18
- has_many :keepr_journals, :class_name => 'Keepr::Journal', :as => :accountable, :dependent => :restrict_with_error
20
+ has_many :keepr_journals, class_name: 'Keepr::Journal', as: :accountable, dependent: :restrict_with_error
19
21
 
20
- class_eval <<-EOT
22
+ class_eval <<-CODE, __FILE__, __LINE__ + 1
21
23
  def keepr_booked?
22
24
  keepr_journals.exists?
23
25
  end
@@ -27,11 +29,11 @@ module Keepr::ActiveRecordExtension
27
29
  where('keepr_journals.id' => nil)
28
30
  }
29
31
  scope :keepr_booked, -> { joins(:keepr_journals) }
30
- EOT
32
+ CODE
31
33
  end
32
34
 
33
35
  def has_keepr_postings
34
- has_many :keepr_postings, :class_name => 'Keepr::Posting', :as => :accountable, :dependent => :restrict_with_error
36
+ has_many :keepr_postings, class_name: 'Keepr::Posting', as: :accountable, dependent: :restrict_with_error
35
37
  end
36
38
  end
37
39
  end
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Keepr::ContactExport
2
- def initialize(accounts, header_options={}, &block)
4
+ def initialize(accounts, header_options = {}, &block)
3
5
  raise ArgumentError unless block_given?
4
6
 
5
7
  @accounts = accounts
@@ -15,22 +17,19 @@ class Keepr::ContactExport
15
17
  export.to_file(filename)
16
18
  end
17
19
 
18
- private
20
+ private
19
21
 
20
22
  def export
21
23
  export = Datev::ContactExport.new(@header_options)
22
24
 
23
25
  @accounts.reorder(:number).each do |account|
24
- if account.debtor? || account.creditor?
25
- export << to_datev(account)
26
- end
26
+ export << to_datev(account) if account.debtor? || account.creditor?
27
27
  end
28
28
 
29
29
  export
30
30
  end
31
31
 
32
32
  def to_datev(account)
33
- { 'Konto' => account.number
34
- }.merge(@block.call(account))
33
+ { 'Konto' => account.number }.merge(@block.call(account))
35
34
  end
36
35
  end
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Keepr::CostCenter < ActiveRecord::Base
2
4
  self.table_name = 'keepr_cost_centers'
3
5
 
4
6
  validates_presence_of :number, :name
5
7
  validates_uniqueness_of :number
6
8
 
7
- has_many :keepr_postings, :class_name => 'Keepr::Posting', :foreign_key => 'keepr_cost_center_id', :dependent => :restrict_with_error
9
+ has_many :keepr_postings, class_name: 'Keepr::Posting', foreign_key: 'keepr_cost_center_id', dependent: :restrict_with_error
8
10
  end
@@ -1,42 +1,51 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Keepr::Group < ActiveRecord::Base
2
4
  self.table_name = 'keepr_groups'
3
5
 
4
- has_ancestry :orphan_strategy => :restrict
6
+ has_ancestry orphan_strategy: :restrict
5
7
 
6
- enum :target => [ :asset, :liability, :profit_and_loss ]
8
+ enum target: %i[asset liability profit_and_loss]
7
9
 
8
10
  validates_presence_of :name
9
11
 
10
- has_many :keepr_accounts, :class_name => 'Keepr::Account', :foreign_key => 'keepr_group_id', :dependent => :restrict_with_error
12
+ has_many :keepr_accounts, class_name: 'Keepr::Account', foreign_key: 'keepr_group_id', dependent: :restrict_with_error
11
13
 
12
- before_validation :get_from_parent
14
+ before_validation :set_target_from_parent
13
15
 
14
16
  validate :check_result_and_target
15
17
 
16
18
  def self.result
17
- where(:is_result => true).first
19
+ where(is_result: true).first
18
20
  end
19
21
 
20
22
  def keepr_postings
21
23
  if is_result
22
- Keepr::Posting.joins(:keepr_account).where(:keepr_accounts => { :kind => [ Keepr::Account.kinds[:revenue],
23
- Keepr::Account.kinds[:expense] ] })
24
+ Keepr::Posting
25
+ .joins(:keepr_account)
26
+ .where(keepr_accounts: { kind: [
27
+ Keepr::Account.kinds[:revenue],
28
+ Keepr::Account.kinds[:expense]
29
+ ] })
24
30
  else
25
- Keepr::Posting.joins(:keepr_account => :keepr_group).merge(self.subtree)
31
+ Keepr::Posting
32
+ .joins(keepr_account: :keepr_group)
33
+ .merge(subtree)
26
34
  end
27
35
  end
28
36
 
29
- private
30
- def get_from_parent
31
- if self.parent
32
- self.target = self.parent.target
37
+ private
38
+
39
+ def set_target_from_parent
40
+ self.class.unscoped do
41
+ self.target = parent.target if parent
33
42
  end
34
43
  end
35
44
 
36
45
  def check_result_and_target
37
- if is_result
38
- # Attribute `is_result` allowed for liability target only
39
- errors.add :base, :liability_needed_for_result unless liability?
40
- end
46
+ return unless is_result
47
+
48
+ # Attribute `is_result` allowed for liability target only
49
+ errors.add :base, :liability_needed_for_result unless liability?
41
50
  end
42
51
  end
@@ -1,7 +1,8 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
+
2
3
  class Keepr::GroupsCreator
3
4
  def initialize(target)
4
- raise ArgumentError unless [ :balance, :profit_and_loss ].include?(target)
5
+ raise ArgumentError unless %i[balance profit_and_loss].include?(target)
5
6
 
6
7
  @target = target
7
8
  end
@@ -9,14 +10,15 @@ class Keepr::GroupsCreator
9
10
  def run
10
11
  case @target
11
12
  when :balance then
12
- load 'asset.txt', :target => :asset
13
- load 'liability.txt', :target => :liability
13
+ load 'asset.txt', target: :asset
14
+ load 'liability.txt', target: :liability
14
15
  when :profit_and_loss
15
- load 'profit_and_loss.txt', :target => :profit_and_loss
16
+ load 'profit_and_loss.txt', target: :profit_and_loss
16
17
  end
17
18
  end
18
19
 
19
- private
20
+ private
21
+
20
22
  def load(filename, options)
21
23
  full_filename = File.join(File.dirname(__FILE__), "groups_creator/#{filename}".downcase)
22
24
  lines = File.readlines(full_filename)
@@ -30,12 +32,10 @@ private
30
32
  # Remove leading spaces and separate number and name
31
33
  number, name = line.lstrip.match(/^(.*?)\s(.+)$/).to_a[1..-1]
32
34
 
33
- attributes = options.merge(:name => name, :number => number)
34
- if @target == :balance && name == 'Jahresüberschuss/Jahresfehlbetrag'
35
- attributes[:is_result] = true
36
- end
35
+ attributes = options.merge(name: name, number: number)
36
+ attributes[:is_result] = true if @target == :balance && name == 'Jahresüberschuss/Jahresfehlbetrag'
37
37
 
38
- if depth == 0
38
+ if depth.zero?
39
39
  parents = []
40
40
  group = Keepr::Group.create!(attributes)
41
41
  else
@@ -1,17 +1,19 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Keepr::Journal < ActiveRecord::Base
2
4
  self.table_name = 'keepr_journals'
3
5
 
4
6
  validates_presence_of :date
5
- validates_uniqueness_of :number, :allow_blank => true
7
+ validates_uniqueness_of :number, allow_blank: true
6
8
 
7
- has_many :keepr_postings, -> { order(:amount => :desc) },
8
- :class_name => 'Keepr::Posting', :foreign_key => 'keepr_journal_id', :dependent => :destroy
9
+ has_many :keepr_postings, -> { order(amount: :desc) },
10
+ class_name: 'Keepr::Posting', foreign_key: 'keepr_journal_id', dependent: :destroy
9
11
 
10
- belongs_to :accountable, :polymorphic => true
12
+ belongs_to :accountable, polymorphic: true
11
13
 
12
- accepts_nested_attributes_for :keepr_postings, :allow_destroy => true, :reject_if => :all_blank
14
+ accepts_nested_attributes_for :keepr_postings, allow_destroy: true, reject_if: :all_blank
13
15
 
14
- default_scope { order({:date => :desc}, {:id => :desc}) }
16
+ default_scope { order({ date: :desc }, id: :desc) }
15
17
 
16
18
  validate :validate_postings
17
19
 
@@ -31,7 +33,8 @@ class Keepr::Journal < ActiveRecord::Base
31
33
  before_update :check_permanent
32
34
  before_destroy :check_permanent
33
35
 
34
- private
36
+ private
37
+
35
38
  def existing_postings
36
39
  keepr_postings.to_a.delete_if(&:marked_for_destruction?)
37
40
  end
@@ -51,15 +54,15 @@ private
51
54
  end
52
55
 
53
56
  def check_permanent
54
- if self.permanent_was
55
- # If marked as permanent, no changes are allowed
56
- errors.add :base, :changes_not_allowed
57
-
58
- if ActiveRecord::VERSION::MAJOR < 5
59
- false
60
- else
61
- throw :abort
62
- end
57
+ return unless permanent_was
58
+
59
+ # If marked as permanent, no changes are allowed
60
+ errors.add :base, :changes_not_allowed
61
+
62
+ if ActiveRecord::VERSION::MAJOR < 5
63
+ false
64
+ else
65
+ throw :abort
63
66
  end
64
67
  end
65
68
  end
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Keepr::JournalExport
2
- def initialize(journals, header_options={}, &block)
4
+ def initialize(journals, header_options = {}, &block)
3
5
  @journals = journals
4
6
  @header_options = header_options
5
7
  @block = block
@@ -13,12 +15,12 @@ class Keepr::JournalExport
13
15
  export.to_file(filename)
14
16
  end
15
17
 
16
- private
18
+ private
17
19
 
18
20
  def export
19
21
  export = Datev::BookingExport.new(@header_options)
20
22
 
21
- @journals.includes(:keepr_postings => :keepr_account).reorder(:date, :id).each do |journal|
23
+ @journals.includes(keepr_postings: :keepr_account).reorder(:date, :id).each do |journal|
22
24
  to_datev(journal).each do |hash|
23
25
  export << hash
24
26
  end
@@ -29,19 +31,20 @@ private
29
31
 
30
32
  def to_datev(journal)
31
33
  main_posting = journal.keepr_postings.find { |p| p.keepr_account.debtor? || p.keepr_account.creditor? }
32
- main_posting ||= journal.keepr_postings.sort_by(&:amount).last
34
+ main_posting ||= journal.keepr_postings.max_by(&:amount)
33
35
 
34
- journal.keepr_postings.sort_by { |p| [ p.side == main_posting.side ? 1 : 0, -p.amount ] }.map do |posting|
36
+ journal.keepr_postings.sort_by { |p| [p.side == main_posting.side ? 1 : 0, -p.amount] }.map do |posting|
35
37
  next if posting == main_posting
36
38
 
37
- { 'Umsatz (ohne Soll/Haben-Kz)' => posting.amount,
39
+ {
40
+ 'Umsatz (ohne Soll/Haben-Kz)' => posting.amount,
38
41
  'Soll/Haben-Kennzeichen' => 'S',
39
42
  'Konto' => posting.debit? ? posting.keepr_account.number : main_posting.keepr_account.number,
40
43
  'Gegenkonto (ohne BU-Schlüssel)' => posting.credit? ? posting.keepr_account.number : main_posting.keepr_account.number,
41
44
  'BU-Schlüssel' => '40', # Steuerautomatik deaktivieren
42
45
  'Belegdatum' => journal.date,
43
46
  'Belegfeld 1' => journal.number,
44
- 'Buchungstext' => journal.subject.slice(0,60),
47
+ 'Buchungstext' => journal.subject.slice(0, 60),
45
48
  'Festschreibung' => journal.permanent
46
49
  }.merge(@block ? @block.call(posting) : {})
47
50
  end.compact
@@ -1,13 +1,15 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Keepr::Posting < ActiveRecord::Base
2
4
  self.table_name = 'keepr_postings'
3
5
 
4
6
  validates_presence_of :keepr_account_id, :amount
5
7
  validate :cost_center_validation
6
8
 
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
- belongs_to :accountable, :polymorphic => true
9
+ belongs_to :keepr_account, class_name: 'Keepr::Account'
10
+ belongs_to :keepr_journal, class_name: 'Keepr::Journal'
11
+ belongs_to :keepr_cost_center, class_name: 'Keepr::CostCenter'
12
+ belongs_to :accountable, polymorphic: true
11
13
 
12
14
  SIDE_DEBIT = 'debit'
13
15
  SIDE_CREDIT = 'credit'
@@ -17,19 +19,20 @@ class Keepr::Posting < ActiveRecord::Base
17
19
 
18
20
  def side
19
21
  @side || begin
20
- (raw_amount < 0 ? SIDE_CREDIT : SIDE_DEBIT) if raw_amount
22
+ (raw_amount.negative? ? SIDE_CREDIT : SIDE_DEBIT) if raw_amount
21
23
  end
22
24
  end
23
25
 
24
26
  def side=(value)
27
+ raise ArgumentError unless [SIDE_DEBIT, SIDE_CREDIT].include?(value)
28
+
25
29
  @side = value
30
+ return unless amount
26
31
 
27
32
  if credit?
28
- self.raw_amount = -amount if amount
33
+ self.raw_amount = -amount.to_d
29
34
  elsif debit?
30
- self.raw_amount = amount if amount
31
- else
32
- raise ArgumentError
35
+ self.raw_amount = amount.to_d
33
36
  end
34
37
  end
35
38
 
@@ -54,23 +57,25 @@ class Keepr::Posting < ActiveRecord::Base
54
57
  end
55
58
 
56
59
  def amount=(value)
57
- raise ArgumentError.new('Negative amount not allowed!') if value.to_f < 0
58
60
  @side ||= SIDE_DEBIT
59
61
 
60
- if credit?
61
- self.raw_amount = -value
62
- else
63
- self.raw_amount = value
62
+ unless value
63
+ self.raw_amount = nil
64
+ return
64
65
  end
66
+
67
+ raise ArgumentError, 'Negative amount not allowed!' if value.to_d.negative?
68
+
69
+ self.raw_amount = credit? ? -value.to_d : value.to_d
65
70
  end
66
71
 
67
- private
72
+ private
73
+
68
74
  def cost_center_validation
69
- if keepr_cost_center
70
- unless keepr_account.profit_and_loss?
71
- # allowed for expense or revenue accounts only
72
- errors.add :keepr_cost_center_id, :allowed_for_expense_or_revenue_only
73
- end
74
- end
75
+ return unless keepr_cost_center
76
+ return if keepr_account.profit_and_loss?
77
+
78
+ # allowed for expense or revenue accounts only
79
+ errors.add :keepr_cost_center_id, :allowed_for_expense_or_revenue_only
75
80
  end
76
81
  end
@@ -1,11 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Keepr::Tax < ActiveRecord::Base
2
4
  self.table_name = 'keepr_taxes'
3
5
 
4
6
  validates_presence_of :name, :value, :keepr_account_id
5
7
  validates_numericality_of :value
6
8
 
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
+ belongs_to :keepr_account, class_name: 'Keepr::Account'
10
+ has_many :keepr_accounts, class_name: 'Keepr::Account', foreign_key: 'keepr_tax_id', dependent: :restrict_with_error
9
11
 
10
12
  validate do |tax|
11
13
  tax.errors.add(:keepr_account_id, :circular_reference) if tax.keepr_account.try(:keepr_tax) == tax