keepr 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +44 -0
- data/.travis.yml +9 -9
- data/Gemfile +2 -0
- data/LICENSE.txt +1 -1
- data/README.md +3 -3
- data/Rakefile +2 -0
- data/ci/Gemfile-rails-4-2 +3 -2
- data/ci/Gemfile-rails-5-0 +3 -2
- data/ci/Gemfile-rails-5-1 +3 -2
- data/ci/Gemfile-rails-5-2 +3 -2
- data/ci/{Gemfile-rails-4-1 → Gemfile-rails-6-0} +3 -2
- data/keepr.gemspec +14 -12
- data/lib/generators/keepr/migration/migration_generator.rb +5 -3
- data/lib/generators/keepr/migration/templates/migration.rb +2 -0
- data/lib/keepr.rb +6 -4
- data/lib/keepr/account.rb +37 -37
- data/lib/keepr/account_export.rb +10 -14
- data/lib/keepr/active_record_extension.rb +4 -2
- data/lib/keepr/contact_export.rb +6 -7
- data/lib/keepr/cost_center.rb +2 -0
- data/lib/keepr/group.rb +21 -20
- data/lib/keepr/groups_creator.rb +7 -7
- data/lib/keepr/journal.rb +14 -11
- data/lib/keepr/journal_export.rb +9 -6
- data/lib/keepr/posting.rb +21 -16
- data/lib/keepr/tax.rb +2 -0
- data/lib/keepr/version.rb +3 -1
- data/spec/factories/account.rb +5 -3
- data/spec/factories/cost_center.rb +4 -2
- data/spec/factories/group.rb +4 -2
- data/spec/factories/tax.rb +5 -3
- data/spec/keepr/account_export_spec.rb +22 -19
- data/spec/keepr/account_spec.rb +47 -42
- data/spec/keepr/active_record_extension_spec.rb +20 -18
- data/spec/keepr/contact_export_spec.rb +17 -14
- data/spec/keepr/cost_center_spec.rb +2 -0
- data/spec/keepr/group_spec.rb +39 -35
- data/spec/keepr/groups_creator_spec.rb +7 -4
- data/spec/keepr/journal_export_spec.rb +26 -25
- data/spec/keepr/journal_spec.rb +33 -31
- data/spec/keepr/posting_spec.rb +8 -6
- data/spec/keepr/tax_spec.rb +21 -14
- data/spec/spec_helper.rb +6 -5
- data/spec/support/contact.rb +2 -0
- data/spec/support/document.rb +2 -0
- data/spec/support/ledger.rb +2 -0
- data/spec/support/spec_migration.rb +2 -0
- metadata +28 -14
@@ -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
|
@@ -17,7 +19,7 @@ module Keepr::ActiveRecordExtension
|
|
17
19
|
def has_keepr_journals
|
18
20
|
has_many :keepr_journals, class_name: 'Keepr::Journal', as: :accountable, dependent: :restrict_with_error
|
19
21
|
|
20
|
-
class_eval <<-
|
22
|
+
class_eval <<-CODE, __FILE__, __LINE__ + 1
|
21
23
|
def keepr_booked?
|
22
24
|
keepr_journals.exists?
|
23
25
|
end
|
@@ -27,7 +29,7 @@ module Keepr::ActiveRecordExtension
|
|
27
29
|
where('keepr_journals.id' => nil)
|
28
30
|
}
|
29
31
|
scope :keepr_booked, -> { joins(:keepr_journals) }
|
30
|
-
|
32
|
+
CODE
|
31
33
|
end
|
32
34
|
|
33
35
|
def has_keepr_postings
|
data/lib/keepr/contact_export.rb
CHANGED
@@ -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
|
data/lib/keepr/cost_center.rb
CHANGED
data/lib/keepr/group.rb
CHANGED
@@ -1,15 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class Keepr::Group < ActiveRecord::Base
|
2
4
|
self.table_name = 'keepr_groups'
|
3
5
|
|
4
6
|
has_ancestry orphan_strategy: :restrict
|
5
7
|
|
6
|
-
enum target: [
|
8
|
+
enum target: %i[asset liability profit_and_loss]
|
7
9
|
|
8
10
|
validates_presence_of :name
|
9
11
|
|
10
12
|
has_many :keepr_accounts, class_name: 'Keepr::Account', foreign_key: 'keepr_group_id', dependent: :restrict_with_error
|
11
13
|
|
12
|
-
before_validation :
|
14
|
+
before_validation :set_target_from_parent
|
13
15
|
|
14
16
|
validate :check_result_and_target
|
15
17
|
|
@@ -19,30 +21,29 @@ class Keepr::Group < ActiveRecord::Base
|
|
19
21
|
|
20
22
|
def keepr_postings
|
21
23
|
if is_result
|
22
|
-
Keepr::Posting
|
23
|
-
joins(:keepr_account)
|
24
|
-
where(keepr_accounts: { kind: [
|
25
|
-
|
26
|
-
|
27
|
-
|
24
|
+
Keepr::Posting
|
25
|
+
.joins(:keepr_account)
|
26
|
+
.where(keepr_accounts: { kind: [
|
27
|
+
Keepr::Account.kinds[:revenue],
|
28
|
+
Keepr::Account.kinds[:expense]
|
29
|
+
] })
|
28
30
|
else
|
29
|
-
Keepr::Posting
|
30
|
-
joins(keepr_account: :keepr_group)
|
31
|
-
merge(
|
31
|
+
Keepr::Posting
|
32
|
+
.joins(keepr_account: :keepr_group)
|
33
|
+
.merge(subtree)
|
32
34
|
end
|
33
35
|
end
|
34
36
|
|
35
|
-
private
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
end
|
37
|
+
private
|
38
|
+
|
39
|
+
def set_target_from_parent
|
40
|
+
self.target = parent.target if parent
|
40
41
|
end
|
41
42
|
|
42
43
|
def check_result_and_target
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
44
|
+
return unless is_result
|
45
|
+
|
46
|
+
# Attribute `is_result` allowed for liability target only
|
47
|
+
errors.add :base, :liability_needed_for_result unless liability?
|
47
48
|
end
|
48
49
|
end
|
data/lib/keepr/groups_creator.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
2
3
|
class Keepr::GroupsCreator
|
3
4
|
def initialize(target)
|
4
|
-
raise ArgumentError unless [
|
5
|
+
raise ArgumentError unless %i[balance profit_and_loss].include?(target)
|
5
6
|
|
6
7
|
@target = target
|
7
8
|
end
|
@@ -16,7 +17,8 @@ class Keepr::GroupsCreator
|
|
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)
|
@@ -31,11 +33,9 @@ private
|
|
31
33
|
number, name = line.lstrip.match(/^(.*?)\s(.+)$/).to_a[1..-1]
|
32
34
|
|
33
35
|
attributes = options.merge(name: name, number: number)
|
34
|
-
if @target == :balance && name == 'Jahresüberschuss/Jahresfehlbetrag'
|
35
|
-
attributes[:is_result] = true
|
36
|
-
end
|
36
|
+
attributes[:is_result] = true if @target == :balance && name == 'Jahresüberschuss/Jahresfehlbetrag'
|
37
37
|
|
38
|
-
if depth
|
38
|
+
if depth.zero?
|
39
39
|
parents = []
|
40
40
|
group = Keepr::Group.create!(attributes)
|
41
41
|
else
|
data/lib/keepr/journal.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class Keepr::Journal < ActiveRecord::Base
|
2
4
|
self.table_name = 'keepr_journals'
|
3
5
|
|
@@ -11,7 +13,7 @@ class Keepr::Journal < ActiveRecord::Base
|
|
11
13
|
|
12
14
|
accepts_nested_attributes_for :keepr_postings, allow_destroy: true, reject_if: :all_blank
|
13
15
|
|
14
|
-
default_scope { order({date: :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
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
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
|
data/lib/keepr/journal_export.rb
CHANGED
@@ -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,7 +15,7 @@ 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)
|
@@ -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.
|
34
|
+
main_posting ||= journal.keepr_postings.max_by(&:amount)
|
33
35
|
|
34
|
-
journal.keepr_postings.sort_by { |p| [
|
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
|
-
{
|
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
|
data/lib/keepr/posting.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class Keepr::Posting < ActiveRecord::Base
|
2
4
|
self.table_name = 'keepr_postings'
|
3
5
|
|
@@ -17,19 +19,20 @@ class Keepr::Posting < ActiveRecord::Base
|
|
17
19
|
|
18
20
|
def side
|
19
21
|
@side || begin
|
20
|
-
(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)
|
25
|
-
raise ArgumentError unless [
|
27
|
+
raise ArgumentError unless [SIDE_DEBIT, SIDE_CREDIT].include?(value)
|
28
|
+
|
26
29
|
@side = value
|
27
30
|
return unless amount
|
28
31
|
|
29
32
|
if credit?
|
30
|
-
self.raw_amount = -amount
|
33
|
+
self.raw_amount = -amount.to_d
|
31
34
|
elsif debit?
|
32
|
-
self.raw_amount = amount
|
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
|
-
|
61
|
-
self.raw_amount =
|
62
|
-
|
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
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
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
|
data/lib/keepr/tax.rb
CHANGED
data/lib/keepr/version.rb
CHANGED
data/spec/factories/account.rb
CHANGED
data/spec/factories/group.rb
CHANGED
data/spec/factories/tax.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
FactoryBot.define do
|
2
4
|
factory :tax, class: Keepr::Tax do
|
3
|
-
name 'USt19'
|
4
|
-
description 'Umsatzsteuer 19%'
|
5
|
-
value 19.0
|
5
|
+
name { 'USt19' }
|
6
|
+
description { 'Umsatzsteuer 19%' }
|
7
|
+
value { 19.0 }
|
6
8
|
keepr_account { FactoryBot.create :account, number: 1776 }
|
7
9
|
end
|
8
10
|
end
|
@@ -1,26 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
describe Keepr::AccountExport do
|
4
|
-
let!(:account_1000) { FactoryBot.create :account, kind: :asset, number:
|
5
|
-
let!(:account_1776) { FactoryBot.create :account, kind: :liability, number:
|
6
|
-
let!(:account_4920) { FactoryBot.create :account, kind: :expense, number:
|
7
|
-
let!(:account_8400) { FactoryBot.create :account, kind: :revenue, number:
|
8
|
-
let!(:account_9000) { FactoryBot.create :account, kind: :forward, number:
|
9
|
-
let!(:account_10000) { FactoryBot.create :account, kind: :creditor, number:
|
10
|
-
let!(:account_70000) { FactoryBot.create :account, kind: :debtor, number:
|
6
|
+
let!(:account_1000) { FactoryBot.create :account, kind: :asset, number: 1000, name: 'Kasse' }
|
7
|
+
let!(:account_1776) { FactoryBot.create :account, kind: :liability, number: 1776, name: 'Umsatzsteuer 19 %' }
|
8
|
+
let!(:account_4920) { FactoryBot.create :account, kind: :expense, number: 4920, name: 'Telefon' }
|
9
|
+
let!(:account_8400) { FactoryBot.create :account, kind: :revenue, number: 8400, name: 'Erlöse 19 %' }
|
10
|
+
let!(:account_9000) { FactoryBot.create :account, kind: :forward, number: 9000, name: 'Saldenvorträge Sachkonten' }
|
11
|
+
let!(:account_10000) { FactoryBot.create :account, kind: :creditor, number: 10_000, name: 'Diverse Kreditoren' }
|
12
|
+
let!(:account_70000) { FactoryBot.create :account, kind: :debtor, number: 70_000, name: 'Diverse Debitoren' }
|
11
13
|
|
12
14
|
let(:scope) { Keepr::Account.all }
|
13
15
|
|
14
|
-
let(:export)
|
15
|
-
Keepr::AccountExport.new(
|
16
|
-
|
17
|
-
'
|
18
|
-
'
|
16
|
+
let(:export) do
|
17
|
+
Keepr::AccountExport.new(
|
18
|
+
scope,
|
19
|
+
'Berater' => 1_234_567,
|
20
|
+
'Mandant' => 78_901,
|
21
|
+
'WJ-Beginn' => Date.new(2016, 1, 1),
|
19
22
|
'Bezeichnung' => 'Keepr-Konten'
|
20
|
-
) do
|
23
|
+
) do
|
21
24
|
{ 'Sprach-ID' => 'de-DE' }
|
22
25
|
end
|
23
|
-
|
26
|
+
end
|
24
27
|
|
25
28
|
describe :to_s do
|
26
29
|
subject { export.to_s }
|
@@ -29,17 +32,17 @@ describe Keepr::AccountExport do
|
|
29
32
|
subject.lines[2..-1].map { |line| line.encode(Encoding::UTF_8) }
|
30
33
|
end
|
31
34
|
|
32
|
-
it
|
35
|
+
it 'should return CSV lines' do
|
33
36
|
subject.lines.each { |line| expect(line).to include(';') }
|
34
37
|
end
|
35
38
|
|
36
|
-
it
|
39
|
+
it 'should include header data' do
|
37
40
|
expect(subject.lines[0]).to include('1234567;')
|
38
41
|
expect(subject.lines[0]).to include('78901;')
|
39
42
|
expect(subject.lines[0]).to include('"Keepr-Konten";')
|
40
43
|
end
|
41
44
|
|
42
|
-
it
|
45
|
+
it 'should include all accounts except debtor/creditor' do
|
43
46
|
expect(account_lines.count).to eq(5)
|
44
47
|
|
45
48
|
expect(account_lines[0]).to include('1000;')
|
@@ -58,14 +61,14 @@ describe Keepr::AccountExport do
|
|
58
61
|
expect(account_lines[4]).to include('"Saldenvorträge Sachkonten";')
|
59
62
|
end
|
60
63
|
|
61
|
-
it
|
64
|
+
it 'should include data from block' do
|
62
65
|
expect(account_lines[0]).to include(';"de-DE"')
|
63
66
|
expect(account_lines[1]).to include(';"de-DE"')
|
64
67
|
end
|
65
68
|
end
|
66
69
|
|
67
70
|
describe :to_file do
|
68
|
-
it
|
71
|
+
it 'should create CSV file' do
|
69
72
|
Dir.mktmpdir do |dir|
|
70
73
|
filename = "#{dir}/EXTF_Kontenbeschriftungen.csv"
|
71
74
|
export.to_file(filename)
|