has_accounts 1.1.3 → 2.0.0

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