blawzoo-plutus 0.5.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. data/.yardopts +2 -0
  2. data/LICENSE +23 -0
  3. data/README.markdown +197 -0
  4. data/Rakefile +11 -0
  5. data/VERSION.yml +5 -0
  6. data/app/controllers/accounts_controller.rb +43 -0
  7. data/app/controllers/transactions_controller.rb +42 -0
  8. data/app/models/account.rb +57 -0
  9. data/app/models/asset.rb +81 -0
  10. data/app/models/equity.rb +82 -0
  11. data/app/models/expense.rb +81 -0
  12. data/app/models/liability.rb +76 -0
  13. data/app/models/revenue.rb +81 -0
  14. data/app/models/transaction.rb +27 -0
  15. data/app/views/accounts/index.html.erb +27 -0
  16. data/app/views/accounts/show.html.erb +69 -0
  17. data/app/views/layouts/accounts.html.erb +68 -0
  18. data/app/views/layouts/transactions.html.erb +68 -0
  19. data/app/views/transactions/index.html.erb +27 -0
  20. data/app/views/transactions/show.html.erb +24 -0
  21. data/doc/Account.html +300 -0
  22. data/doc/AccountsController.html +317 -0
  23. data/doc/Asset.html +610 -0
  24. data/doc/Equity.html +610 -0
  25. data/doc/Expense.html +610 -0
  26. data/doc/Liability.html +588 -0
  27. data/doc/Revenue.html +610 -0
  28. data/doc/Transaction.html +156 -0
  29. data/doc/TransactionsController.html +317 -0
  30. data/doc/_index.html +204 -0
  31. data/doc/class_list.html +36 -0
  32. data/doc/file.README.html +250 -0
  33. data/doc/file_list.html +38 -0
  34. data/doc/frames.html +13 -0
  35. data/doc/index.html +250 -0
  36. data/doc/js/app.js +202 -0
  37. data/doc/js/full_list.js +149 -0
  38. data/doc/js/jquery.js +154 -0
  39. data/doc/method_list.html +275 -0
  40. data/doc/top-level-namespace.html +90 -0
  41. data/lib/plutus.rb +6 -0
  42. data/plutus.gemspec +116 -0
  43. data/spec/controllers/accounts_controller_spec.rb +26 -0
  44. data/spec/controllers/transactions_controller_spec.rb +26 -0
  45. data/spec/factories/account_factory.rb +31 -0
  46. data/spec/factories/transaction_factory.rb +6 -0
  47. data/spec/lib/plutus_spec.rb +0 -0
  48. data/spec/models/account_spec.rb +43 -0
  49. data/spec/models/asset_spec.rb +46 -0
  50. data/spec/models/equity_spec.rb +46 -0
  51. data/spec/models/expense_spec.rb +46 -0
  52. data/spec/models/liability_spec.rb +46 -0
  53. data/spec/models/revenue_spec.rb +46 -0
  54. data/spec/models/transaction_spec.rb +48 -0
  55. data/spec/rcov.opts +2 -0
  56. data/spec/routing/accounts_routing_spec.rb +30 -0
  57. data/spec/routing/transactions_routing_spec.rb +30 -0
  58. data/spec/schema.rb +31 -0
  59. data/spec/spec.opts +4 -0
  60. data/spec/spec_helper.rb +14 -0
  61. data/tasks/plutus_tasks.rake +4 -0
  62. metadata +204 -0
@@ -0,0 +1,2 @@
1
+ app/**/*.rb
2
+ LICENSE
data/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ Copyright (c) 2010 Michael Bulat
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ Except as contained in this notice, the name(s) of the above copyright holders
14
+ shall not be used in advertising or otherwise to promote the sale, use or
15
+ other dealings in this Software without prior written authorization.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ THE SOFTWARE.
@@ -0,0 +1,197 @@
1
+ Plutus
2
+ =================
3
+
4
+ This plutus plugin is a Ruby on Rails Engine which provides a double entry accounting system for your application.
5
+
6
+ NOTE: This version of Plutus is compatable with RAILS 3
7
+ =======================================================
8
+
9
+ For the rails 2 version, you can go here:
10
+
11
+ [https://github.com/mbulat/plutus/tree/rails2](https://github.com/mbulat/plutus/tree/rails2)
12
+
13
+ Gems in RubyGems.org >= 0.5.0 support Rails 3
14
+
15
+ Installation
16
+ ============
17
+
18
+ - Add the gem to your Gemfile `gem "plutus"`
19
+
20
+ - generate migration files `rails g plutus`
21
+
22
+ - run migrations `rake db:migrate`
23
+
24
+ Overview
25
+ ========
26
+
27
+ The plutus plugin provides a complete double entry accounting system for use in any Ruby on Rails application. The plugin follows general [Double Entry Bookkeeping](http://en.wikipedia.org/wiki/Double-entry_bookkeeping_system) practices. All calculations are done using [BigDecimal](http://www.ensta.fr/~diam/ruby/online/ruby-doc-stdlib/libdoc/bigdecimal/rdoc/classes/BigDecimal.html) in order to prevent floating point rounding errors. The plugin requires a decimal type on your database as well.
28
+
29
+ The system consists of a table that maintains your accounts and a table for recording transactions. Transactions are the recording of debits and credits to various accounts. The transaction table, which records your business transactions is, essentially, your accounting [Journal](http://en.wikipedia.org/wiki/Journal_entry)
30
+
31
+ Posting to a [Ledger](http://en.wikipedia.org/wiki/General_ledger) can be considered to happen automatically, since Accounts have the reverse `has_many` relationship to either its credit or debit transactions
32
+
33
+ Accounts
34
+ --------
35
+
36
+ The Account class represents accounts in the system. The Account table uses single table inheritance to store information on each type of account (Asset, Liability, Equity, Revenue, Expense). Each account must be subclassed as one of the following types:
37
+
38
+ TYPE | NORMAL BALANCE | DESCRIPTION
39
+ --------------------------------------------------------------------------
40
+ Asset | Debit | Resources owned by the Business Entity
41
+ Liability | Credit | Debts owed to outsiders
42
+ Equity | Credit | Owners rights to the Assets
43
+ Revenue | Credit | Increases in owners equity
44
+ Expense | Debit | Assets or services consumed in the generation of revenue
45
+
46
+ Each account can also be marked as a "Contra Account". A contra account will have its normal balance swapped. For example, to remove equity, a "Drawing" account may be created as a contra equity account as follows:
47
+
48
+ Equity.create(:name => "Drawing", contra => true)
49
+
50
+ At all times the balance of all accounts should conform to the [Accounting
51
+ Equation](http://en.wikipedia.org/wiki/Accounting_equation)
52
+
53
+ Assets = Liabilties + Owner's Equity
54
+
55
+ Every account object has a `has_many` association of credit and debit transactions, which means that each account object also acts as its own [Ledger](http://en.wikipedia.org/wiki/General_ledger), and exposes a method to calculate the balance of the account.
56
+
57
+ See the {Account} and {Transaction} classes for more information.
58
+
59
+
60
+
61
+ Examples
62
+ ========
63
+
64
+ Recording a Transaction
65
+ -----------------------
66
+
67
+ Let's assume we're accounting on an [Accrual basis](http://en.wikipedia.org/wiki/Accounting_methods). We've just taken a customer's order for some widgets, which we've also billed him for. At this point we've actually added a liability to the company until we deliver the goods. To record this transaction we'd need two accounts.
68
+
69
+ >> Liability.create(:name => "Unearned Revenue")
70
+ >> Asset.create(:name => "Cash")
71
+
72
+ We'd then record the following transaction.
73
+
74
+ >> Transaction.create(:description => "Order placed for widgets",
75
+ :credit_account => Liability.find_by_name("Unearned Revenue"),
76
+ :debit_account => Asset.find_by_name("Cash"),
77
+ :amount => 1000)
78
+
79
+ Associating Documents
80
+ ---------------------
81
+
82
+ Although Plutus does not provide a mechanism for generating invoices or orders, it is possible to associate any such commercial document with a given transaction.
83
+
84
+ Suppose we pull up our latest invoice in order to generate a transaction for plutus (we'll assume you already have an Invoice model):
85
+
86
+ >> invoice = Invoice.last
87
+
88
+ We'll also assume that we have not yet received payment for the order, so we'll need an "Accounts Receivable" Asset:
89
+
90
+ >> Asset.create(:name => "Accounts Receivable")
91
+
92
+ Next, we'll go ahead and create a transaction for this invoice:
93
+
94
+ >> Transaction.create(:description => "Order placed for widgets",
95
+ :credit_account => Liability.find_by_name("Unearned Revenue"),
96
+ :debit_account => Asset.find_by_name("Accounts Receivable"),
97
+ :amount => invoice.amount,
98
+ :commercial_document => invoice)
99
+
100
+ The commercial document attribute on the transaction is a polymorphic association allowing you to associate any record from your models with a transaction (i.e. Bills, Invoices, Receipts, Returns, etc.)
101
+
102
+ Checking the Balance of an Individual Account
103
+ ----------------------------------------------
104
+
105
+ Each account can report on its own balance. This number should normally be positive. If the number is negative, you may have a problem.
106
+
107
+ >> cash = Asset.find_by_name("Cash")
108
+ >> cash.balance
109
+ => #<BigDecimal:103259bb8,'0.2E4',4(12)>
110
+
111
+
112
+ Checking the Balance of an Account Type
113
+ ---------------------------------------
114
+
115
+ Each subclass of accounts can report on the total balance of all the accounts of that type. This number should normally be positive. If the number is negative, you may have a problem.
116
+
117
+ >> Asset.balance
118
+ => #<BigDecimal:103259bb8,'0.2E4',4(12)>
119
+
120
+ Calculating the Trial Balance
121
+ -----------------------------
122
+
123
+ The [Trial Balance](http://en.wikipedia.org/wiki/Trial_balance) for all accounts on the system can be found through the abstract Account class. This value should be 0 unless there is an error in the system.
124
+
125
+ >> Account.trial_balance
126
+ => #<BigDecimal:1031c0d28,'0.0',4(12)>
127
+
128
+ Contra Accounts and Complex Transactions
129
+ ----------------------------------------
130
+
131
+ For complex transaction, you should always ensure that you are balancing your accounts via the [Accounting Equation](http://en.wikipedia.org/wiki/Accounting_equation).
132
+
133
+ Assets = Liabilties + Owner's Equity
134
+
135
+ For example, let's assume the owner of a business wants to withdraw cash. First we'll assume that we have an asset account for "Cash" which the funds will be drawn from. We'll then need an Equity account to record where the funds are going, however, in this case, we can't simply create a regular Equity account. The "Cash" account must be credited for the decrease in its balance since it's an Asset. Likewise, Equity accounts are typically credited when there is an increase in their balance. Equity is considered an owner's rights to Assets in the business. In this case however, we are not simply increasing the owners right's to assets within the business; we are actually removing capital from the business altogether. Hence both sides of our accounting equation will see a decrease. In order to accomplish this, we need to create a Contra-Equity account we'll call "Drawings". Since Equity accounts normally have credit balances, a Contra-Equity account will have a debit balance, which is what we need for our transaction.
136
+
137
+ >> Equity.create(:name => "Drawing", :contra => true)
138
+ >> Asset.create(:name => "Cash")
139
+
140
+ We would then create the following transaction:
141
+
142
+ >> Transaction.create(:description => "Owner withdrawing cash",
143
+ :credit_account => Asset.find_by_name("Cash"),
144
+ :debit_account => Equity.find_by_name("Drawing"),
145
+ :amount => 1000.00)
146
+
147
+ To make the example clearer, imagine instead that the owner decides to invest his money into the business in exchange for some type of equity security. In this case we might have the following accounts:
148
+
149
+
150
+ >> Equity.create(:name => "Common Stock")
151
+ >> Asset.create(:name => "Cash")
152
+
153
+ And out transaction would be:
154
+
155
+ >> Transaction.create(:description => "Owner investing cash",
156
+ :credit_account => Equity.find_by_name("Common Stock"),
157
+ :debit_account => Asset.find_by_name("Cash"),
158
+ :amount => 1000.00)
159
+
160
+ In this case, we've increase our cash Asset, and simultaneously increased the other side of our accounting equation in
161
+ Equity, keeping everything balanced.
162
+
163
+ Access & Security
164
+ =================
165
+
166
+ The Engine provides controllers and views for viewing Accounts and Transactions via the {AccountsController} and {TransactionsController} classes. The controllers will render HTML, XML and JSON, and are compatible with [ActiveResource](http://api.rubyonrails.org/classes/ActiveResource/Base.html)
167
+
168
+ Routing is NOT supplied by the engine. You can add routes to your application in your config/routes.rb with something like the following
169
+
170
+ resources :transactions, :only => [:index, :show], :conditions => { :method => :get }
171
+ resources :accounts, :only => [:index, :show], :conditions => { :method => :get }
172
+
173
+ *NOTE: If you enable routing, you should ensure that your ApplicationController enforces its own authentication and authorization, which this controller will inherit.*
174
+
175
+ Testing
176
+ =======
177
+
178
+ [Rspec](http://rspec.info/) tests are provided. Install both the rpsec and rspec-rails gems, and install this plugin
179
+ into a working rails application to run the tests.
180
+
181
+ ToDo
182
+ ====
183
+
184
+ * Better views, including paging and ledgers
185
+ * Namespacing to allow overriding engine classes
186
+ * Reference for common accounting transactions
187
+
188
+ Reference
189
+ =========
190
+
191
+ For a complete reference on Accounting principles, we recommend the following textbook
192
+
193
+ [http://amzn.com/0324662963](http://amzn.com/0324662963)
194
+
195
+ * * *
196
+
197
+ Copyright (c) 2010 Michael Bulat
@@ -0,0 +1,11 @@
1
+ require 'bundler'
2
+
3
+ require 'rspec/core/rake_task'
4
+
5
+ desc "run specs"
6
+ RSpec::Core::RakeTask.new do |t|
7
+ t.rspec_opts = ["-c", "-f d", "-r ./spec/spec_helper.rb"]
8
+ t.pattern = 'spec/**/*_spec.rb'
9
+ end
10
+
11
+ task :default => :spec
@@ -0,0 +1,5 @@
1
+ ---
2
+ :major: 0
3
+ :minor: 5
4
+ :build:
5
+ :patch: 2
@@ -0,0 +1,43 @@
1
+ # This controller provides restful route handling for Accounts.
2
+ #
3
+ # The controller supports ActiveResource, and provides for
4
+ # HMTL, XML, and JSON presentation.
5
+ #
6
+ # == Security:
7
+ # Only GET requests are supported. You should ensure that your application
8
+ # controller enforces its own authentication and authorization, which this
9
+ # controller will inherit.
10
+ #
11
+ # @author Michael Bulat
12
+ class AccountsController < ApplicationController
13
+ unloadable
14
+
15
+ # @example
16
+ # GET /accounts
17
+ # GET /accounts.xml
18
+ # GET /accounts.json
19
+ def index
20
+ @accounts = Account.all
21
+
22
+ respond_to do |format|
23
+ format.html # index.html.erb
24
+ format.xml { render :xml => @accounts }
25
+ format.json { render :json => @accounts }
26
+ end
27
+ end
28
+
29
+ # @example
30
+ # GET /accounts/1
31
+ # GET /accounts/1.xml
32
+ # GET /accounts/1.json
33
+ def show
34
+ @account = Account.find(params[:id])
35
+
36
+ respond_to do |format|
37
+ format.html # show.html.erb
38
+ format.xml { render :xml => @account }
39
+ format.json { render :json => @account }
40
+ end
41
+ end
42
+
43
+ end
@@ -0,0 +1,42 @@
1
+ # This controller provides restful route handling for Transactions.
2
+ #
3
+ # The controller supports ActiveResource, and provides for
4
+ # HMTL, XML, and JSON presentation.
5
+ #
6
+ # == Security:
7
+ # Only GET requests are supported. You should ensure that your application
8
+ # controller enforces its own authentication and authorization, which this
9
+ # controller will inherit.
10
+ #
11
+ # @author Michael Bulat
12
+ class TransactionsController < ApplicationController
13
+ unloadable
14
+ # @example
15
+ # GET /transactions
16
+ # GET /transactions.xml
17
+ # GET /transactions.json
18
+ def index
19
+ @transactions = Transaction.all
20
+
21
+ respond_to do |format|
22
+ format.html # index.html.erb
23
+ format.xml { render :xml => @transactions }
24
+ format.json { render :json => @transactions }
25
+ end
26
+ end
27
+
28
+ # @example
29
+ # GET /transactions/1
30
+ # GET /transactions/1.xml
31
+ # GET /transactions/1.json
32
+ def show
33
+ @transaction = Transaction.find(params[:id])
34
+
35
+ respond_to do |format|
36
+ format.html # show.html.erb
37
+ format.xml { render :xml => @transaction }
38
+ format.json { render :json => @transaction }
39
+ end
40
+ end
41
+
42
+ end
@@ -0,0 +1,57 @@
1
+ #
2
+ # == Overview:
3
+ #
4
+ # The Account class represents accounts in the system. Each account must be subclassed as one of the following types:
5
+ #
6
+ # TYPE | NORMAL BALANCE | DESCRIPTION
7
+ # --------------------------------------------------------------------------
8
+ # Asset | Debit | Resources owned by the Business Entity
9
+ # Liability | Credit | Debts owed to outsiders
10
+ # Equity | Credit | Owners rights to the Assets
11
+ # Revenue | Credit | Increases in owners equity
12
+ # Expense | Debit | Assets or services consumed in the generation of revenue
13
+ #
14
+ # Each account can also be marked as a "Contra Account". A contra account will have it's
15
+ # normal balance swapped. For example, to remove equity, a "Drawing" account may be created
16
+ # as a contra equity account as follows:
17
+ #
18
+ # Equity.create(:name => "Drawing", contra => true)
19
+ #
20
+ # At all times the balance of all accounts should conform to the "accounting equation"
21
+ # Assets = Liabilties + Owner's Equity
22
+ #
23
+ # Each sublclass account acts as it's own ledger. See the individual subclasses for a
24
+ # description.
25
+ #
26
+ # @abstract
27
+ # An account must be a subclass to be saved to the database. The Account class
28
+ # has a singleton method {trial_balance} to calculate the balance on all Accounts.
29
+ #
30
+ # @see http://en.wikipedia.org/wiki/Accounting_equation Accounting Equation
31
+ # @see http://en.wikipedia.org/wiki/Debits_and_credits Debits, Credits, and Contra Accounts
32
+ #
33
+ # @author Michael Bulat
34
+ class Account < ActiveRecord::Base
35
+
36
+ validates_presence_of :type, :name
37
+
38
+ has_many :credit_transactions, :class_name => "Transaction", :foreign_key => "credit_account_id"
39
+ has_many :debit_transactions, :class_name => "Transaction", :foreign_key => "debit_account_id"
40
+
41
+ # The trial balance of all accounts in the system. This should always equal zero,
42
+ # otherwise there is an error in the system.
43
+ #
44
+ # @example
45
+ # >> Account.trial_balance.to_i
46
+ # => 0
47
+ #
48
+ # @return [BigDecimal] The decimal value balance of all accounts
49
+ def self.trial_balance
50
+ unless self.new.class == Account
51
+ raise(NoMethodError, "undefined method 'trial_balance'")
52
+ else
53
+ Asset.balance - (Liability.balance + Equity.balance + Revenue.balance - Expense.balance)
54
+ end
55
+ end
56
+
57
+ end
@@ -0,0 +1,81 @@
1
+ # The Asset class is an account type used to represents resources owned by the business entity.
2
+ #
3
+ # === Normal Balance
4
+ # The normal balance on Asset accounts is a *Debit*.
5
+ #
6
+ # @see http://en.wikipedia.org/wiki/Asset Assets
7
+ #
8
+ # @author Michael Bulat
9
+ class Asset < Account
10
+
11
+ # The credit balance for the account.
12
+ #
13
+ # @example
14
+ # >> asset.credits_balance
15
+ # => #<BigDecimal:103259bb8,'0.1E4',4(12)>
16
+ #
17
+ # @return [BigDecimal] The decimal value credit balance
18
+ def credits_balance
19
+ credits_balance = BigDecimal.new('0')
20
+ credit_transactions.each do |credit_transaction|
21
+ credits_balance += credit_transaction.amount
22
+ end
23
+ return credits_balance
24
+ end
25
+
26
+ # The debit balance for the account.
27
+ #
28
+ # @example
29
+ # >> asset.debits_balance
30
+ # => #<BigDecimal:103259bb8,'0.3E4',4(12)>
31
+ #
32
+ # @return [BigDecimal] The decimal value credit balance
33
+ def debits_balance
34
+ debits_balance = BigDecimal.new('0')
35
+ debit_transactions.each do |debit_transaction|
36
+ debits_balance += debit_transaction.amount
37
+ end
38
+ return debits_balance
39
+ end
40
+
41
+ # The balance of the account.
42
+ #
43
+ # Assets have normal debit balances, so the credits are subtracted from the debits
44
+ # unless this is a contra account, in which debits are subtracted from credits
45
+ #
46
+ # @example
47
+ # >> asset.balance
48
+ # => #<BigDecimal:103259bb8,'0.2E4',4(12)>
49
+ #
50
+ # @return [BigDecimal] The decimal value balance
51
+ def balance
52
+ unless contra
53
+ debits_balance - credits_balance
54
+ else
55
+ credits_balance - debits_balance
56
+ end
57
+ end
58
+
59
+ # This class method is used to return
60
+ # the balance of all Asset accounts.
61
+ #
62
+ # Contra accounts are automatically subtracted from the balance.
63
+ #
64
+ # @example
65
+ # >> Asset.balance
66
+ # => #<BigDecimal:1030fcc98,'0.82875E5',8(20)>
67
+ #
68
+ # @return [BigDecimal] The decimal value balance
69
+ def self.balance
70
+ accounts_balance = BigDecimal.new('0')
71
+ accounts = self.find(:all)
72
+ accounts.each do |asset|
73
+ unless asset.contra
74
+ accounts_balance += asset.balance
75
+ else
76
+ accounts_balance -= asset.balance
77
+ end
78
+ end
79
+ accounts_balance
80
+ end
81
+ end