borutus 0.1.0

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