borutus 0.1.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.
Files changed (82) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +346 -0
  4. data/Rakefile +11 -0
  5. data/app/assets/javascripts/borutus/application.js +16 -0
  6. data/app/assets/javascripts/borutus/reports.js +5 -0
  7. data/app/assets/stylesheets/bootstrap-theme.min.css +5 -0
  8. data/app/assets/stylesheets/bootstrap.min.css +5 -0
  9. data/app/assets/stylesheets/borutus/application.css +19 -0
  10. data/app/controllers/borutus/accounts_controller.rb +30 -0
  11. data/app/controllers/borutus/application_controller.rb +4 -0
  12. data/app/controllers/borutus/entries_controller.rb +34 -0
  13. data/app/controllers/borutus/reports_controller.rb +40 -0
  14. data/app/models/borutus/account.rb +180 -0
  15. data/app/models/borutus/amount.rb +24 -0
  16. data/app/models/borutus/amounts_extension.rb +42 -0
  17. data/app/models/borutus/asset.rb +56 -0
  18. data/app/models/borutus/credit_amount.rb +10 -0
  19. data/app/models/borutus/debit_amount.rb +10 -0
  20. data/app/models/borutus/entry.rb +77 -0
  21. data/app/models/borutus/equity.rb +56 -0
  22. data/app/models/borutus/expense.rb +56 -0
  23. data/app/models/borutus/liability.rb +56 -0
  24. data/app/models/borutus/no_tenancy.rb +9 -0
  25. data/app/models/borutus/revenue.rb +56 -0
  26. data/app/models/borutus/tenancy.rb +15 -0
  27. data/app/views/borutus/accounts/index.html.erb +26 -0
  28. data/app/views/borutus/entries/index.html.erb +59 -0
  29. data/app/views/borutus/reports/_account.html.erb +28 -0
  30. data/app/views/borutus/reports/balance_sheet.html.erb +21 -0
  31. data/app/views/borutus/reports/income_statement.html.erb +24 -0
  32. data/app/views/layouts/borutus/_messages.html.erb +9 -0
  33. data/app/views/layouts/borutus/_navigation.html.erb +19 -0
  34. data/app/views/layouts/borutus/_navigation_links.html.erb +5 -0
  35. data/app/views/layouts/borutus/application.html.erb +19 -0
  36. data/config/backtrace_silencers.rb +7 -0
  37. data/config/database.yml +5 -0
  38. data/config/inflections.rb +10 -0
  39. data/config/mime_types.rb +5 -0
  40. data/config/routes.rb +9 -0
  41. data/config/secret_token.rb +7 -0
  42. data/config/session_store.rb +8 -0
  43. data/db/migrate/20160422010135_create_borutus_tables.rb +33 -0
  44. data/lib/borutus.rb +20 -0
  45. data/lib/borutus/engine.rb +5 -0
  46. data/lib/borutus/version.rb +3 -0
  47. data/lib/generators/borutus/USAGE +13 -0
  48. data/lib/generators/borutus/add_date_upgrade_generator.rb +11 -0
  49. data/lib/generators/borutus/base_generator.rb +19 -0
  50. data/lib/generators/borutus/borutus_generator.rb +12 -0
  51. data/lib/generators/borutus/templates/tenant_migration.rb +6 -0
  52. data/lib/generators/borutus/tenancy_generator.rb +12 -0
  53. data/lib/generators/borutus/upgrade_borutus_generator.rb +12 -0
  54. data/spec/controllers/accounts_controller_spec.rb +19 -0
  55. data/spec/controllers/entries_controller_spec.rb +19 -0
  56. data/spec/controllers/reports_controller_spec.rb +24 -0
  57. data/spec/factories/account_factory.rb +35 -0
  58. data/spec/factories/amount_factory.rb +19 -0
  59. data/spec/factories/entry_factory.rb +11 -0
  60. data/spec/lib/borutus_spec.rb +0 -0
  61. data/spec/models/account_spec.rb +140 -0
  62. data/spec/models/amount_spec.rb +13 -0
  63. data/spec/models/asset_spec.rb +7 -0
  64. data/spec/models/credit_amount_spec.rb +7 -0
  65. data/spec/models/debit_amount_spec.rb +7 -0
  66. data/spec/models/entry_spec.rb +170 -0
  67. data/spec/models/equity_spec.rb +7 -0
  68. data/spec/models/expense_spec.rb +7 -0
  69. data/spec/models/liability_spec.rb +7 -0
  70. data/spec/models/revenue_spec.rb +7 -0
  71. data/spec/models/tenancy_spec.rb +45 -0
  72. data/spec/rcov.opts +2 -0
  73. data/spec/routing/accounts_routing_spec.rb +13 -0
  74. data/spec/routing/entries_routing_spec.rb +13 -0
  75. data/spec/spec.opts +4 -0
  76. data/spec/spec_helper.rb +26 -0
  77. data/spec/support/account_shared_examples.rb +60 -0
  78. data/spec/support/active_support_helpers.rb +13 -0
  79. data/spec/support/amount_shared_examples.rb +21 -0
  80. data/spec/support/factory_girl_helpers.rb +8 -0
  81. data/spec/support/shoulda_matchers.rb +8 -0
  82. metadata +243 -0
@@ -0,0 +1,4 @@
1
+ module Borutus
2
+ class ApplicationController < ActionController::Base
3
+ end
4
+ end
@@ -0,0 +1,34 @@
1
+ module Borutus
2
+ # This controller provides restful route handling for Entries.
3
+ #
4
+ # The controller supports ActiveResource, and provides for
5
+ # HMTL, XML, and JSON presentation.
6
+ #
7
+ # == Security:
8
+ # Only GET requests are supported. You should ensure that your application
9
+ # controller enforces its own authentication and authorization, which this
10
+ # controller will inherit.
11
+ #
12
+ # @author Michael Bulat
13
+ class EntriesController < Borutus::ApplicationController
14
+ unloadable
15
+ # @example
16
+ # GET /entries
17
+ # GET /entries.xml
18
+ # GET /entries.json
19
+ def index
20
+ if params[:order] == 'ascending'
21
+ order = 'ASC'
22
+ else
23
+ order = 'DESC'
24
+ end
25
+ @entries = Entry.page(params[:page]).per(params[:limit]).order("date #{order}")
26
+
27
+ respond_to do |format|
28
+ format.html # index.html.erb
29
+ format.xml { render :xml => @entries }
30
+ format.json { render :json => @entries }
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,40 @@
1
+ module Borutus
2
+ # == Security:
3
+ # Only GET requests are supported. You should ensure that your application
4
+ # controller enforces its own authentication and authorization, which this
5
+ # controller will inherit.
6
+ #
7
+ # @author Michael Bulat
8
+ class ReportsController < ::Borutus::ApplicationController
9
+ unloadable
10
+
11
+ # @example
12
+ # GET /reports/balance_sheet
13
+ def balance_sheet
14
+ first_entry = Borutus::Entry.order('date ASC').first
15
+ @from_date = first_entry ? first_entry.date: Date.today
16
+ @to_date = params[:date] ? Date.parse(params[:date]) : Date.today
17
+ @assets = Borutus::Asset.all
18
+ @liabilities = Borutus::Liability.all
19
+ @equity = Borutus::Equity.all
20
+
21
+ respond_to do |format|
22
+ format.html # index.html.erb
23
+ end
24
+ end
25
+
26
+ # @example
27
+ # GET /reports/income_statement
28
+ def income_statement
29
+ @from_date = params[:from_date] ? Date.parse(params[:from_date]) : Date.today.at_beginning_of_month
30
+ @to_date = params[:to_date] ? Date.parse(params[:to_date]) : Date.today
31
+ @revenues = Borutus::Revenue.all
32
+ @expenses = Borutus::Expense.all
33
+
34
+ respond_to do |format|
35
+ format.html # index.html.erb
36
+ end
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,180 @@
1
+ module Borutus
2
+ # The Account class represents accounts in the system. Each account must be subclassed as one of the following types:
3
+ #
4
+ # TYPE | NORMAL BALANCE | DESCRIPTION
5
+ # --------------------------------------------------------------------------
6
+ # Asset | Debit | Resources owned by the Business Entity
7
+ # Liability | Credit | Debts owed to outsiders
8
+ # Equity | Credit | Owners rights to the Assets
9
+ # Revenue | Credit | Increases in owners equity
10
+ # Expense | Debit | Assets or services consumed in the generation of revenue
11
+ #
12
+ # Each account can also be marked as a "Contra Account". A contra account will have it's
13
+ # normal balance swapped. For example, to remove equity, a "Drawing" account may be created
14
+ # as a contra equity account as follows:
15
+ #
16
+ # Borutus::Equity.create(:name => "Drawing", contra => true)
17
+ #
18
+ # At all times the balance of all accounts should conform to the "accounting equation"
19
+ # Borutus::Assets = Liabilties + Owner's Equity
20
+ #
21
+ # Each sublclass account acts as it's own ledger. See the individual subclasses for a
22
+ # description.
23
+ #
24
+ # @abstract
25
+ # An account must be a subclass to be saved to the database. The Account class
26
+ # has a singleton method {trial_balance} to calculate the balance on all Accounts.
27
+ #
28
+ # @see http://en.wikipedia.org/wiki/Accounting_equation Accounting Equation
29
+ # @see http://en.wikipedia.org/wiki/Debits_and_credits Debits, Credits, and Contra Accounts
30
+ #
31
+ # @author Michael Bulat
32
+ class Account < ActiveRecord::Base
33
+ class_attribute :normal_credit_balance
34
+
35
+ has_many :amounts
36
+ has_many :credit_amounts, :extend => AmountsExtension, :class_name => 'Borutus::CreditAmount'
37
+ has_many :debit_amounts, :extend => AmountsExtension, :class_name => 'Borutus::DebitAmount'
38
+ has_many :entries, through: :amounts, source: :entry
39
+ has_many :credit_entries, :through => :credit_amounts, :source => :entry, :class_name => 'Borutus::Entry'
40
+ has_many :debit_entries, :through => :debit_amounts, :source => :entry, :class_name => 'Borutus::Entry'
41
+
42
+ validates_presence_of :type
43
+
44
+ def self.types
45
+ [
46
+ ::Borutus::Asset,
47
+ ::Borutus::Equity,
48
+ ::Borutus::Expense,
49
+ ::Borutus::Liability,
50
+ ::Borutus::Revenue,
51
+ ]
52
+ end
53
+
54
+ if Borutus.enable_tenancy
55
+ include Borutus::Tenancy
56
+ else
57
+ include Borutus::NoTenancy
58
+ end
59
+
60
+ # The balance of the account. This instance method is intended for use only
61
+ # on instances of account subclasses.
62
+ #
63
+ # If the account has a normal credit balance, the debits are subtracted from the credits
64
+ # unless this is a contra account, in which case credits are substracted from debits.
65
+ #
66
+ # For a normal debit balance, the credits are subtracted from the debits
67
+ # unless this is a contra account, in which case debits are subtracted from credits.
68
+ #
69
+ # Takes an optional hash specifying :from_date and :to_date for calculating balances during periods.
70
+ # :from_date and :to_date may be strings of the form "yyyy-mm-dd" or Ruby Date objects
71
+ #
72
+ # @example
73
+ # >> liability.balance({:from_date => "2000-01-01", :to_date => Date.today})
74
+ # => #<BigDecimal:103259bb8,'0.1E4',4(12)>
75
+ #
76
+ # @example
77
+ # >> liability.balance
78
+ # => #<BigDecimal:103259bb8,'0.2E4',4(12)>
79
+ #
80
+ # @return [BigDecimal] The decimal value balance
81
+ def balance(options={})
82
+ if self.class == Borutus::Account
83
+ raise(NoMethodError, "undefined method 'balance'")
84
+ else
85
+ if self.normal_credit_balance ^ contra
86
+ credits_balance(options) - debits_balance(options)
87
+ else
88
+ debits_balance(options) - credits_balance(options)
89
+ end
90
+ end
91
+ end
92
+
93
+ # The credit balance for the account.
94
+ #
95
+ # Takes an optional hash specifying :from_date and :to_date for calculating balances during periods.
96
+ # :from_date and :to_date may be strings of the form "yyyy-mm-dd" or Ruby Date objects
97
+ #
98
+ # @example
99
+ # >> asset.credits_balance({:from_date => "2000-01-01", :to_date => Date.today})
100
+ # => #<BigDecimal:103259bb8,'0.1E4',4(12)>
101
+ #
102
+ # @example
103
+ # >> asset.credits_balance
104
+ # => #<BigDecimal:103259bb8,'0.1E4',4(12)>
105
+ #
106
+ # @return [BigDecimal] The decimal value credit balance
107
+ def credits_balance(options={})
108
+ credit_amounts.balance(options)
109
+ end
110
+
111
+ # The debit balance for the account.
112
+ #
113
+ # Takes an optional hash specifying :from_date and :to_date for calculating balances during periods.
114
+ # :from_date and :to_date may be strings of the form "yyyy-mm-dd" or Ruby Date objects
115
+ #
116
+ # @example
117
+ # >> asset.debits_balance({:from_date => "2000-01-01", :to_date => Date.today})
118
+ # => #<BigDecimal:103259bb8,'0.1E4',4(12)>
119
+ #
120
+ # @example
121
+ # >> asset.debits_balance
122
+ # => #<BigDecimal:103259bb8,'0.3E4',4(12)>
123
+ #
124
+ # @return [BigDecimal] The decimal value credit balance
125
+ def debits_balance(options={})
126
+ debit_amounts.balance(options)
127
+ end
128
+
129
+ # This class method is used to return the balance of all accounts
130
+ # for a given class and is intended for use only on account subclasses.
131
+ #
132
+ # Contra accounts are automatically subtracted from the balance.
133
+ #
134
+ # Takes an optional hash specifying :from_date and :to_date for calculating balances during periods.
135
+ # :from_date and :to_date may be strings of the form "yyyy-mm-dd" or Ruby Date objects
136
+ #
137
+ # @example
138
+ # >> Borutus::Liability.balance({:from_date => "2000-01-01", :to_date => Date.today})
139
+ # => #<BigDecimal:103259bb8,'0.1E4',4(12)>
140
+ #
141
+ # @example
142
+ # >> Borutus::Liability.balance
143
+ # => #<BigDecimal:1030fcc98,'0.82875E5',8(20)>
144
+ #
145
+ # @return [BigDecimal] The decimal value balance
146
+ def self.balance(options={})
147
+ if self.new.class == Borutus::Account
148
+ raise(NoMethodError, "undefined method 'balance'")
149
+ else
150
+ accounts_balance = BigDecimal.new('0')
151
+ accounts = self.all
152
+ accounts.each do |account|
153
+ if account.contra
154
+ accounts_balance -= account.balance(options)
155
+ else
156
+ accounts_balance += account.balance(options)
157
+ end
158
+ end
159
+ accounts_balance
160
+ end
161
+ end
162
+
163
+ # The trial balance of all accounts in the system. This should always equal zero,
164
+ # otherwise there is an error in the system.
165
+ #
166
+ # @example
167
+ # >> Account.trial_balance.to_i
168
+ # => 0
169
+ #
170
+ # @return [BigDecimal] The decimal value balance of all accounts
171
+ def self.trial_balance
172
+ if self.new.class == Borutus::Account
173
+ Borutus::Asset.balance - (Borutus::Liability.balance + Borutus::Equity.balance + Borutus::Revenue.balance - Borutus::Expense.balance)
174
+ else
175
+ raise(NoMethodError, "undefined method 'trial_balance'")
176
+ end
177
+ end
178
+
179
+ end
180
+ end
@@ -0,0 +1,24 @@
1
+ module Borutus
2
+ # The Amount class represents debit and credit amounts in the system.
3
+ #
4
+ # @abstract
5
+ # An amount must be a subclass as either a debit or a credit to be saved to the database.
6
+ #
7
+ # @author Michael Bulat
8
+ class Amount < ActiveRecord::Base
9
+ belongs_to :entry, :class_name => 'Borutus::Entry'
10
+ belongs_to :account, :class_name => 'Borutus::Account'
11
+
12
+ validates_presence_of :type, :amount, :entry, :account
13
+ # attr_accessible :account, :account_name, :amount, :entry
14
+
15
+ delegate :name, to: :account, prefix: true, allow_nil: true
16
+
17
+ # Assign an account by name
18
+ def account_name=(name)
19
+ self.account = Account.find_by_name!(name)
20
+ end
21
+
22
+ protected
23
+ end
24
+ end
@@ -0,0 +1,42 @@
1
+ module Borutus
2
+ # Association extension for has_many :amounts relations. Internal.
3
+ module AmountsExtension
4
+ # Returns a sum of the referenced Amount objects.
5
+ #
6
+ # Takes a hash specifying :from_date and :to_date for calculating balances during periods.
7
+ # :from_date and :to_date may be strings of the form "yyyy-mm-dd" or Ruby Date objects
8
+ #
9
+ # This runs the summation in the database, so it only works on persisted records.
10
+ #
11
+ # @example
12
+ # credit_amounts.balance({:from_date => "2000-01-01", :to_date => Date.today})
13
+ # => #<BigDecimal:103259bb8,'0.2E4',4(12)>
14
+ #
15
+ # @return [BigDecimal] The decimal value balance
16
+ def balance(hash={})
17
+ if hash[:from_date] && hash[:to_date]
18
+ from_date = hash[:from_date].kind_of?(Date) ? hash[:from_date] : Date.parse(hash[:from_date])
19
+ to_date = hash[:to_date].kind_of?(Date) ? hash[:to_date] : Date.parse(hash[:to_date])
20
+ includes(:entry).where('borutus_entries.date' => from_date..to_date).sum(:amount)
21
+ else
22
+ sum(:amount)
23
+ end
24
+ end
25
+
26
+ # Returns a sum of the referenced Amount objects.
27
+ #
28
+ # This is used primarly in the validation step in Borutus::Entry
29
+ # in order to ensure that the debit and credits are canceling out.
30
+ #
31
+ # Since this does not use the database for sumation, it may be used on non-persisted records.
32
+ def balance_for_new_record
33
+ balance = BigDecimal.new('0')
34
+ each do |amount_record|
35
+ if amount_record.amount && !amount_record.marked_for_destruction?
36
+ balance += amount_record.amount # unless amount_record.marked_for_destruction?
37
+ end
38
+ end
39
+ return balance
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,56 @@
1
+ module Borutus
2
+ # The Asset class is an account type used to represents resources owned by the business entity.
3
+ #
4
+ # === Normal Balance
5
+ # The normal balance on Asset accounts is a *Debit*.
6
+ #
7
+ # @see http://en.wikipedia.org/wiki/Asset Assets
8
+ #
9
+ # @author Michael Bulat
10
+ class Asset < ::Borutus::Account
11
+
12
+ self.normal_credit_balance = false
13
+
14
+ # The balance of the account.
15
+ #
16
+ # Assets have normal debit balances, so the credits are subtracted from the debits
17
+ # unless this is a contra account, in which debits are subtracted from credits
18
+ #
19
+ # Takes an optional hash specifying :from_date and :to_date for calculating balances during periods.
20
+ # :from_date and :to_date may be strings of the form "yyyy-mm-dd" or Ruby Date objects
21
+ #
22
+ # @example
23
+ # >> asset.balance({:from_date => "2000-01-01", :to_date => Date.today})
24
+ # => #<BigDecimal:103259bb8,'0.1E4',4(12)>
25
+ #
26
+ # @example
27
+ # >> asset.balance
28
+ # => #<BigDecimal:103259bb8,'0.2E4',4(12)>
29
+ #
30
+ # @return [BigDecimal] The decimal value balance
31
+ def balance(options={})
32
+ super(options)
33
+ end
34
+
35
+ # This class method is used to return
36
+ # the balance of all Asset accounts.
37
+ #
38
+ # Contra accounts are automatically subtracted from the balance.
39
+ #
40
+ # Takes an optional hash specifying :from_date and :to_date for calculating balances during periods.
41
+ # :from_date and :to_date may be strings of the form "yyyy-mm-dd" or Ruby Date objects
42
+ #
43
+ # @example
44
+ # >> Borutus::Asset.balance({:from_date => "2000-01-01", :to_date => Date.today})
45
+ # => #<BigDecimal:103259bb8,'0.1E4',4(12)>
46
+ #
47
+ # @example
48
+ # >> Borutus::Asset.balance
49
+ # => #<BigDecimal:1030fcc98,'0.82875E5',8(20)>
50
+ #
51
+ # @return [BigDecimal] The decimal value balance
52
+ def self.balance(options={})
53
+ super(options)
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,10 @@
1
+ module Borutus
2
+ # The CreditAmount class represents credit entries in the entry journal.
3
+ #
4
+ # @example
5
+ # credit_amount = Borutus::CreditAmount.new(:account => revenue, :amount => 1000)
6
+ #
7
+ # @author Michael Bulat
8
+ class CreditAmount < ::Borutus::Amount
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ module Borutus
2
+ # The DebitAmount class represents debit entries in the entry journal.
3
+ #
4
+ # @example
5
+ # debit_amount = Borutus::DebitAmount.new(:account => cash, :amount => 1000)
6
+ #
7
+ # @author Michael Bulat
8
+ class DebitAmount < ::Borutus::Amount
9
+ end
10
+ end
@@ -0,0 +1,77 @@
1
+ module Borutus
2
+ # Entries are the recording of debits and credits to various accounts.
3
+ # This table can be thought of as a traditional accounting Journal.
4
+ #
5
+ # Posting to a Ledger can be considered to happen automatically, since
6
+ # Accounts have the reverse 'has_many' relationship to either it's credit or
7
+ # debit entries
8
+ #
9
+ # @example
10
+ # cash = Borutus::Asset.find_by_name('Cash')
11
+ # accounts_receivable = Borutus::Asset.find_by_name('Accounts Receivable')
12
+ #
13
+ # debit_amount = Borutus::DebitAmount.new(:account => cash, :amount => 1000)
14
+ # credit_amount = Borutus::CreditAmount.new(:account => accounts_receivable, :amount => 1000)
15
+ #
16
+ # entry = Borutus::Entry.new(:description => "Receiving payment on an invoice")
17
+ # entry.debit_amounts << debit_amount
18
+ # entry.credit_amounts << credit_amount
19
+ # entry.save
20
+ #
21
+ # @see http://en.wikipedia.org/wiki/Journal_entry Journal Entry
22
+ #
23
+ # @author Michael Bulat
24
+ class Entry < ActiveRecord::Base
25
+ before_save :default_date
26
+
27
+ if ActiveRecord::VERSION::MAJOR > 4
28
+ belongs_to :commercial_document, :polymorphic => true, optional: true
29
+ else
30
+ belongs_to :commercial_document, :polymorphic => true
31
+ end
32
+
33
+ has_many :credit_amounts, :extend => AmountsExtension, :class_name => 'Borutus::CreditAmount', :inverse_of => :entry
34
+ has_many :debit_amounts, :extend => AmountsExtension, :class_name => 'Borutus::DebitAmount', :inverse_of => :entry
35
+ has_many :credit_accounts, :through => :credit_amounts, :source => :account, :class_name => 'Borutus::Account'
36
+ has_many :debit_accounts, :through => :debit_amounts, :source => :account, :class_name => 'Borutus::Account'
37
+
38
+ validates_presence_of :description
39
+ validate :has_credit_amounts?
40
+ validate :has_debit_amounts?
41
+ validate :amounts_cancel?
42
+
43
+ # Support construction using 'credits' and 'debits' keys
44
+ accepts_nested_attributes_for :credit_amounts, :debit_amounts, allow_destroy: true
45
+ alias_method :credits=, :credit_amounts_attributes=
46
+ alias_method :debits=, :debit_amounts_attributes=
47
+ # attr_accessible :credits, :debits
48
+
49
+ # Support the deprecated .build method
50
+ def self.build(hash)
51
+ ActiveSupport::Deprecation.warn('Borutus::Transaction.build() is deprecated (use new instead)', caller)
52
+ new(hash)
53
+ end
54
+
55
+ def initialize(*args)
56
+ super
57
+ end
58
+
59
+ private
60
+ def default_date
61
+ todays_date = ActiveRecord::Base.default_timezone == :utc ? Time.now.utc : Time.now
62
+ self.date ||= todays_date
63
+ end
64
+
65
+ def has_credit_amounts?
66
+ errors[:base] << "Entry must have at least one credit amount" if self.credit_amounts.blank?
67
+ end
68
+
69
+ def has_debit_amounts?
70
+ errors[:base] << "Entry must have at least one debit amount" if self.debit_amounts.blank?
71
+ end
72
+
73
+ def amounts_cancel?
74
+ errors[:base] << "The credit and debit amounts are not equal" if credit_amounts.balance_for_new_record != debit_amounts.balance_for_new_record
75
+ end
76
+ end
77
+ end