has_accounts 1.1.3 → 2.0.0

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.
@@ -16,27 +16,40 @@ class Account < ActiveRecord::Base
16
16
  "%s (%s)" % [title, code]
17
17
  end
18
18
 
19
+ # Parent Account
20
+ # ==============
21
+ belongs_to :parent, :class_name => Account
22
+ attr_accessible :parent, :parent_id
23
+
19
24
  # Account Type
20
25
  # ============
21
26
  belongs_to :account_type
22
27
  attr_accessible :title, :code, :account_type_id, :account_type
23
28
  validates_presence_of :account_type
24
29
 
25
- def is_asset_account?
30
+ def asset_account?
26
31
  Account.by_type(['current_assets', 'capital_assets', 'costs']).exists?(self)
27
32
  end
33
+ # Deprecated
34
+ alias_method :is_asset_account?, :asset_account?
28
35
 
29
- def is_liability_account?
30
- !is_asset_account?
36
+ def liability_account?
37
+ !asset_account?
31
38
  end
39
+ # Deprecated
40
+ alias_method :is_liability_account?, :liability_account?
32
41
 
33
- def is_balance_account?
42
+ def balance_account?
34
43
  Account.by_type(['current_assets', 'capital_assets', 'outside_capital', 'equity_capital']).exists?(self)
35
44
  end
45
+ # Deprecated
46
+ alias_method :is_balance_account?, :balance_account?
36
47
 
37
- def is_profit_account?
38
- !is_balance_account?
48
+ def profit_account?
49
+ !balance_account?
39
50
  end
51
+ # Deprecated
52
+ alias_method :is_profit_account?, :profit_account?
40
53
 
41
54
  scope :by_type, lambda {|value| includes(:account_type).where('account_types.name' => value)} do
42
55
  include AccountScopeExtension
@@ -44,15 +57,17 @@ class Account < ActiveRecord::Base
44
57
 
45
58
  # Tagging
46
59
  # =======
47
- acts_as_taggable
48
- attr_accessible :tag_list
60
+ if defined?(ActsAsTaggableOn) && ActsAsTaggableOn::Tag.table_exists?
61
+ acts_as_taggable
62
+ attr_accessible :tag_list
49
63
 
50
- def self.default_tags
51
- ['invoice:debit', 'invoice:earnings', 'invoice:credit', 'invoice:costs', 'vat:credit', 'vat:debit']
52
- end
64
+ def self.default_tags
65
+ ['invoice:debit', 'invoice:earnings', 'invoice:credit', 'invoice:costs', 'vat:credit', 'vat:debit']
66
+ end
53
67
 
54
- def self.tag_collection
55
- (default_tags + Account.tag_counts.pluck(:name)).uniq
68
+ def self.tag_collection
69
+ (default_tags + Account.tag_counts.pluck(:name)).uniq
70
+ end
56
71
  end
57
72
 
58
73
  # Holder
@@ -68,10 +83,10 @@ class Account < ActiveRecord::Base
68
83
  Booking.by_account(id)
69
84
  end
70
85
 
71
- # Attachments
72
- # ===========
73
- has_many :attachments, :as => :object
74
- accepts_nested_attributes_for :attachments, :reject_if => proc { |attributes| attributes['file'].blank? }
86
+ # Balances grouped by references which have not 0
87
+ def unbalanced_references
88
+ bookings.unbalanced_by_grouped_reference(self.id)
89
+ end
75
90
 
76
91
  # Helpers
77
92
  # =======
@@ -129,7 +144,7 @@ class Account < ActiveRecord::Base
129
144
  def saldo(selector = Date.today, inclusive = true)
130
145
  credit_amount, debit_amount = turnover(selector, inclusive)
131
146
 
132
- amount = credit_amount - debit_amount
147
+ amount = debit_amount - credit_amount
133
148
 
134
149
  return is_asset_account? ? amount : -amount
135
150
  end
@@ -73,8 +73,11 @@ class Booking < ActiveRecord::Base
73
73
  end
74
74
  }
75
75
 
76
+ # Scope for all accounts assigned to account
77
+ #
78
+ # @param account_id [Integer]
76
79
  scope :by_account, lambda {|account_id|
77
- { :conditions => ["debit_account_id = :account_id OR credit_account_id = :account_id", {:account_id => account_id}] }
80
+ where("debit_account_id = :account_id OR credit_account_id = :account_id", :account_id => account_id)
78
81
  } do
79
82
  # Returns array of all booking titles.
80
83
  def titles
@@ -89,6 +92,80 @@ class Booking < ActiveRecord::Base
89
92
  end
90
93
  end
91
94
 
95
+ # All involved accounts
96
+ #
97
+ # @returns all involved credit and debit accounts
98
+ def self.accounts
99
+ Account.where(:id => pluck(:debit_account_id).uniq + pluck(:credit_account_id).uniq)
100
+ end
101
+
102
+ # Accounts with balances
103
+ #
104
+ # @returns [Hash] with involved accounts as keys and balances as values
105
+ def self.balances
106
+ account_balances = accounts.map do |account|
107
+ [account, balance_by(account)]
108
+ end
109
+
110
+ Hash[account_balances]
111
+ end
112
+
113
+ # Accounted bookings
114
+ # ==================
115
+ SELECT_ACCOUNTED_AMOUNT=
116
+ 'CASE WHEN credit_account_id = debit_account_id THEN 0.0 WHEN credit_account_id = %{account_id} THEN -bookings.amount WHEN debit_account_id = %{account_id} THEN bookings.amount ELSE 0 END'
117
+
118
+ private
119
+ def self.get_account_id(account_or_id)
120
+ if account_or_id.is_a? Account
121
+ return account_or_id.id
122
+ elsif Account.exists?(account_or_id)
123
+ return account_or_id
124
+ else
125
+ raise "argument needs to be a record of type Account or an id for an existing Account record."
126
+ end
127
+ end
128
+
129
+ public
130
+
131
+ # Scope where booking amounts are signed according to debit or credit side
132
+ #
133
+ # @param account_or_id Account id or object
134
+ scope :accounted_by, lambda {|account_or_id|
135
+ select("bookings.*, #{SELECT_ACCOUNTED_AMOUNT % {:account_id => get_account_id(account_or_id)}} AS amount")
136
+ }
137
+
138
+ # Balance of bookings for the specified account
139
+ #
140
+ # @param account_or_id Account id or object
141
+ def self.balance_by(account_or_id)
142
+ BigDecimal.new(sum(SELECT_ACCOUNTED_AMOUNT % {:account_id => get_account_id(account_or_id)}), 2)
143
+ end
144
+
145
+ # Balance of bookings for the specified account with 0 balance, grouped by reference
146
+ #
147
+ # @param account_or_id Account id or object
148
+ def self.unbalanced_by_grouped_reference(account_or_id)
149
+ # Do a manual sum using select() to be able to give it an alias we can use in having()
150
+ summs = group(:reference_type, :reference_id).having("balance != 0.0").select("sum(#{SELECT_ACCOUNTED_AMOUNT % {:account_id => get_account_id(account_or_id)}}) AS balance, reference_type, reference_id")
151
+
152
+ # Simulate Rails grouped summing result format
153
+ grouped = Hash[summs.map{ |group| [[group[:reference_type], group[:reference_id]], group[:balance]] }]
154
+
155
+ # Convert value to BigDecimal
156
+ Hash[grouped.map{|reference, value| [reference, BigDecimal.new(value, 2)] }]
157
+ end
158
+
159
+ # Balance of bookings for the specified account, grouped by reference
160
+ #
161
+ # @param account_or_id Account id or object
162
+ def self.balance_by_grouped_reference(account_or_id)
163
+ grouped = group(:reference_type, :reference_id).sum(SELECT_ACCOUNTED_AMOUNT % {:account_id => get_account_id(account_or_id)})
164
+
165
+ # Convert value to BigDecimal
166
+ Hash[grouped.map{|reference, value| [reference, BigDecimal.new(value, 2)] }]
167
+ end
168
+
92
169
  scope :by_text, lambda {|value|
93
170
  text = '%' + value + '%'
94
171
 
@@ -146,9 +223,9 @@ class Booking < ActiveRecord::Base
146
223
  end
147
224
 
148
225
  if account.is_asset_account?
149
- return -(balance)
150
- else
151
226
  return balance
227
+ else
228
+ return -(balance)
152
229
  end
153
230
  end
154
231
 
@@ -47,8 +47,12 @@ class BookingTemplate < ActiveRecord::Base
47
47
 
48
48
  # Tagging
49
49
  # =======
50
- acts_as_taggable
51
- attr_accessible :tag_list
50
+ if defined?(ActsAsTaggableOn) && ActsAsTaggableOn::Tag.table_exists?
51
+ acts_as_taggable
52
+ attr_accessible :tag_list
53
+
54
+ acts_as_taggable_on :include_in_saldo
55
+ end
52
56
 
53
57
  def booking_parameters(params = {})
54
58
  params = HashWithIndifferentAccess.new(params)
@@ -93,6 +97,7 @@ class BookingTemplate < ActiveRecord::Base
93
97
  end
94
98
 
95
99
  # Factory methods
100
+ # ===============
96
101
  def build_booking(params = {})
97
102
  Booking.new(booking_parameters(params))
98
103
  end
@@ -101,8 +106,18 @@ class BookingTemplate < ActiveRecord::Base
101
106
  Booking.create(booking_parameters(params))
102
107
  end
103
108
 
109
+ # Build booking for template
110
+ #
111
+ # Raises an exception if template for given [code] cannot be found.
112
+ #
113
+ # @param code [String] to lookup template
114
+ # @param params [Hash] parameters to set on the Booking
115
+ # @return [Booking] unsaved Booking
104
116
  def self.build_booking(code, params = {})
105
- find_by_code(code).try(:build_booking, params)
117
+ template = find_by_code(code)
118
+ raise "BookingTemplate not found for '#{code}'" unless template
119
+
120
+ template.build_booking params
106
121
  end
107
122
 
108
123
  def self.create_booking(code, params = {})
@@ -148,9 +163,6 @@ class BookingTemplate < ActiveRecord::Base
148
163
  line_item
149
164
  end
150
165
 
151
- # Tagging
152
- acts_as_taggable_on :include_in_saldo
153
-
154
166
  # Importer
155
167
  # ========
156
168
  attr_accessible :matcher
@@ -13,7 +13,7 @@ en:
13
13
  code: No.
14
14
  title: Title
15
15
  saldo: Saldo
16
- account_type: Acocunt type
16
+ account_type: Account type
17
17
  bank_account:
18
18
  number: Account no.
19
19
  iban: IBAN
@@ -0,0 +1,5 @@
1
+ class ChangeBookingsAmountToUseDecimalScope < ActiveRecord::Migration
2
+ def up
3
+ change_column :bookings, :amount, :decimal, :precision => 10, :scale => 2
4
+ end
5
+ end
@@ -0,0 +1,9 @@
1
+ class AddIndexOnAccountParentId < ActiveRecord::Migration
2
+ def up
3
+ add_index :accounts, :parent_id
4
+ end
5
+
6
+ def down
7
+ remove_index :accounts, :parent_id
8
+ end
9
+ end
@@ -1,6 +1,11 @@
1
1
  require 'has_accounts'
2
+ #
3
+ # Rails
2
4
  require 'rails'
3
5
 
6
+ # Date/Time handling
7
+ require 'validates_timeliness'
8
+
4
9
  module HasAccounts
5
10
  class Railtie < Rails::Engine
6
11
  engine_name "has_accounts"
@@ -1,3 +1,3 @@
1
1
  module HasAccounts
2
- VERSION = "1.1.3"
2
+ VERSION = "2.0.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: has_accounts
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.3
4
+ version: 2.0.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-08-16 00:00:00.000000000 Z
12
+ date: 2014-02-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
16
- requirement: &12373140 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,32 +21,15 @@ dependencies:
21
21
  version: '3.1'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *12373140
25
- - !ruby/object:Gem::Dependency
26
- name: has_vcards
27
- requirement: &12391500 !ruby/object:Gem::Requirement
28
- none: false
29
- requirements:
30
- - - ! '>='
31
- - !ruby/object:Gem::Version
32
- version: '0'
33
- type: :runtime
34
- prerelease: false
35
- version_requirements: *12391500
36
- - !ruby/object:Gem::Dependency
37
- name: acts-as-taggable-on
38
- requirement: &12388840 !ruby/object:Gem::Requirement
24
+ version_requirements: !ruby/object:Gem::Requirement
39
25
  none: false
40
26
  requirements:
41
- - - ! '>='
27
+ - - ~>
42
28
  - !ruby/object:Gem::Version
43
- version: '0'
44
- type: :runtime
45
- prerelease: false
46
- version_requirements: *12388840
29
+ version: '3.1'
47
30
  - !ruby/object:Gem::Dependency
48
31
  name: validates_timeliness
49
- requirement: &12388360 !ruby/object:Gem::Requirement
32
+ requirement: !ruby/object:Gem::Requirement
50
33
  none: false
51
34
  requirements:
52
35
  - - ! '>='
@@ -54,18 +37,12 @@ dependencies:
54
37
  version: '0'
55
38
  type: :runtime
56
39
  prerelease: false
57
- version_requirements: *12388360
58
- - !ruby/object:Gem::Dependency
59
- name: inherited_resources
60
- requirement: &12387640 !ruby/object:Gem::Requirement
40
+ version_requirements: !ruby/object:Gem::Requirement
61
41
  none: false
62
42
  requirements:
63
43
  - - ! '>='
64
44
  - !ruby/object:Gem::Version
65
45
  version: '0'
66
- type: :runtime
67
- prerelease: false
68
- version_requirements: *12387640
69
46
  description: HasAccounts is a full featured Rails 3 gem providing models for financial
70
47
  accounting.
71
48
  email:
@@ -79,17 +56,16 @@ files:
79
56
  - app/models/account.rb
80
57
  - app/models/account_scope_extension.rb
81
58
  - app/models/account_type.rb
82
- - app/models/bank.rb
83
- - app/models/bank_account.rb
84
59
  - app/models/booking.rb
85
60
  - app/models/booking_template.rb
86
61
  - config/locales/de.yml
87
62
  - config/locales/en.yml
88
63
  - db/migrate/20111108000000_create_has_accounts_tables.rb
89
64
  - db/migrate/20111109093857_use_string_for_account_number.rb
90
- - db/migrate/20111230121259_bank_now_inherits_from_person.rb
91
65
  - db/migrate/20111230222702_add_template_to_bookings.rb
92
66
  - db/migrate/20130707121400_create_booking_templates.rb
67
+ - db/migrate/20131021123821_change_bookings_amount_to_use_decimal_scope.rb
68
+ - db/migrate/20131105212025_add_index_on_account_parent_id.rb
93
69
  - lib/has_accounts.rb
94
70
  - lib/has_accounts/class_methods.rb
95
71
  - lib/has_accounts/core_ext/rounding.rb
@@ -119,7 +95,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
119
95
  version: '0'
120
96
  requirements: []
121
97
  rubyforge_project:
122
- rubygems_version: 1.8.11
98
+ rubygems_version: 1.8.23
123
99
  signing_key:
124
100
  specification_version: 3
125
101
  summary: HasAccounts provides models for financial accounting.
data/app/models/bank.rb DELETED
@@ -1,12 +0,0 @@
1
- class Bank < Person
2
- # Access restrictions
3
- attr_accessible :swift, :clearing
4
-
5
- has_many :bank_accounts
6
-
7
- def to_s
8
- return "" unless vcard
9
-
10
- [vcard.full_name, vcard.locality].compact.join(', ')
11
- end
12
- end
@@ -1,6 +0,0 @@
1
- class BankAccount < Account
2
- # Access restrictions
3
- attr_accessible :pc_id, :esr_id, :bank_id
4
-
5
- belongs_to :bank
6
- end
@@ -1,28 +0,0 @@
1
- class LegacyBank < ActiveRecord::Base
2
- set_table_name 'banks'
3
- end
4
-
5
- class BankNowInheritsFromPerson < ActiveRecord::Migration
6
- def up
7
- add_column :people, :swift, :string
8
- add_column :people, :clearing, :string
9
-
10
- for bank in LegacyBank.all
11
- vcard = Vcard.where(:object_type => 'Bank', :object_id => bank.id).first
12
- person = Bank.create!(
13
- :vcard => vcard,
14
- :swift => bank.swift,
15
- :clearing => bank.clearing
16
- )
17
-
18
- if vcard
19
- vcard.object = person
20
- vcard.save!
21
- else
22
- person.build_vcard.save
23
- end
24
- end
25
-
26
- drop_table :banks
27
- end
28
- end