double_double 0.1.0 → 0.2.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.
- data/README.md +168 -7
- data/double_double.gemspec +1 -1
- data/lib/double_double/account.rb +13 -10
- data/lib/double_double/amount.rb +7 -6
- data/lib/double_double/asset.rb +1 -1
- data/lib/double_double/equity.rb +1 -1
- data/lib/double_double/expense.rb +1 -1
- data/lib/double_double/liability.rb +1 -1
- data/lib/double_double/{right_side_account.rb → normal_credit_account.rb} +1 -1
- data/lib/double_double/{left_side_account.rb → normal_debit_account.rb} +1 -1
- data/lib/double_double/revenue.rb +1 -1
- data/lib/double_double/transaction.rb +36 -29
- data/lib/double_double/version.rb +1 -1
- data/lib/double_double.rb +2 -2
- data/spec/models/account_spec.rb +36 -38
- data/spec/models/amount_spec.rb +2 -1
- data/spec/models/asset_spec.rb +7 -2
- data/spec/models/credit_amount_spec.rb +50 -28
- data/spec/models/debit_amount_spec.rb +51 -29
- data/spec/models/equity_spec.rb +7 -2
- data/spec/models/expense_spec.rb +7 -2
- data/spec/models/liability_spec.rb +7 -2
- data/spec/models/revenue_spec.rb +7 -2
- data/spec/models/transaction_spec.rb +122 -53
- data/spec/support/all_account_types.rb +19 -11
- data/spec/support/normal_credit_account_types.rb +124 -0
- data/spec/support/normal_debit_account_types.rb +124 -0
- metadata +8 -8
- data/spec/support/left_side_account_types.rb +0 -125
- data/spec/support/right_side_account_types.rb +0 -125
data/README.md
CHANGED
@@ -3,10 +3,12 @@
|
|
3
3
|
[](https://gemnasium.com/crftr/double_double)
|
4
4
|
[](https://codeclimate.com/github/crftr/double_double)
|
5
5
|
|
6
|
-
A double-entry accounting system.
|
6
|
+
A double-entry accrual accounting system for your application. Currency-agnostic but uses the Money gem. Account holder support and contexts are built-in.
|
7
7
|
|
8
8
|
## Installation
|
9
9
|
|
10
|
+
### Gem
|
11
|
+
|
10
12
|
Add this line to your application's Gemfile:
|
11
13
|
|
12
14
|
gem 'double_double'
|
@@ -19,22 +21,181 @@ Or install it yourself as:
|
|
19
21
|
|
20
22
|
$ gem install double_double
|
21
23
|
|
22
|
-
|
24
|
+
### Database structure
|
25
|
+
|
26
|
+
Create the expected database structure. If using Rails, generate a migration:
|
27
|
+
|
28
|
+
$ rails generate migration CreateDoubleDouble
|
29
|
+
|
30
|
+
Edit the migration to match:
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
class CreateDoubleDouble < ActiveRecord::Migration
|
34
|
+
def change
|
35
|
+
create_table :double_double_accounts do |t|
|
36
|
+
t.string :name, null: false
|
37
|
+
t.integer :number, null: false
|
38
|
+
t.string :type, null: false
|
39
|
+
t.boolean :contra, default: false
|
40
|
+
end
|
41
|
+
add_index :double_double_accounts, [:name, :type]
|
42
|
+
|
43
|
+
create_table :double_double_transactions do |t|
|
44
|
+
t.string :description
|
45
|
+
t.references :transaction_type
|
46
|
+
t.timestamps
|
47
|
+
end
|
48
|
+
add_index :double_double_transactions, :transaction_type_id
|
23
49
|
|
24
|
-
|
50
|
+
create_table :double_double_transaction_types do |t|
|
51
|
+
t.integer :number, null: false
|
52
|
+
t.string :description, null: false
|
53
|
+
end
|
54
|
+
add_index :double_double_transaction_types, :number
|
25
55
|
|
26
|
-
|
56
|
+
create_table :double_double_amounts do |t|
|
57
|
+
t.string :type
|
58
|
+
t.references :account
|
59
|
+
t.references :transaction
|
60
|
+
t.references :accountee, polymorphic: true
|
61
|
+
t.references :context, polymorphic: true
|
62
|
+
t.references :initiator, polymorphic: true
|
63
|
+
t.integer :amount_cents, limit: 8, default: 0, null: false
|
64
|
+
t.string :currency
|
65
|
+
end
|
66
|
+
add_index :double_double_amounts, :context_id
|
67
|
+
add_index :double_double_amounts, :context_type
|
68
|
+
add_index :double_double_amounts, :initiator_id
|
69
|
+
add_index :double_double_amounts, :initiator_type
|
70
|
+
add_index :double_double_amounts, :accountee_id
|
71
|
+
add_index :double_double_amounts, :accountee_type
|
72
|
+
add_index :double_double_amounts, :type
|
73
|
+
add_index :double_double_amounts, [:account_id, :transaction_id]
|
74
|
+
add_index :double_double_amounts, [:transaction_id, :account_id]
|
75
|
+
end
|
76
|
+
end
|
77
|
+
```
|
78
|
+
|
79
|
+
Rake the new migration
|
80
|
+
|
81
|
+
$ rake db:migrate
|
82
|
+
|
83
|
+
## Overview
|
84
|
+
|
85
|
+
[Double-entry accounting][1] practices have been [traced back to the 13th century][2]. double_double strives to make accepted practices accessible and relatively easy to implement within other applications.
|
86
|
+
|
87
|
+
[1]: http://en.wikipedia.org/wiki/Double-entry_bookkeeping_system
|
88
|
+
[2]: http://en.wikipedia.org/wiki/Double-entry_bookkeeping_system#History
|
89
|
+
|
90
|
+
As with many off-the-shelf accounting systems, this project supports:
|
91
|
+
* **Accountee**: an account holder.
|
92
|
+
* **Context**: to track activity on invoices, purchase orders, jobs, campaigns, etc.
|
93
|
+
* **Initiator**: *who* authorized or performed the action.
|
94
|
+
|
95
|
+
### Accounts
|
96
|
+
|
97
|
+
All accounts created in a double-entry system make up the [chart of accounts][3]. This collection of accounts will determine how money is tracked as it moves through the system. It is important to design and create the chart of accounts prior to creating transactions. *If we want people to hold "an individual account" in this system, we will configure them as an accountee, not with a new account. __See the section on accountees__ *
|
98
|
+
|
99
|
+
[3]: http://en.wikipedia.org/wiki/Chart_of_accounts
|
100
|
+
|
101
|
+
In double_double, all accounts created are considered to be the chart of accounts. All accounts are "on the books."
|
102
|
+
|
103
|
+
**Account Class** | **Normal Balance** | **Description** | **Example Uses**
|
104
|
+
------------------------- | ------------------ | -------------------------------------------------- | -------------------------------------------
|
105
|
+
`DoubleDouble::Asset` | Debit | Resources owned or controlled | Cash, Office Computers, Grandma's Jewelry
|
106
|
+
`DoubleDouble::Liability` | Credit | Obligations | Accounts Payable, Bank Loan
|
107
|
+
`DoubleDouble::Equity` | Credit | The claim to assets after all liabilities are paid | Paid-In Capital, Dividends, Retained Earnings
|
108
|
+
`DoubleDouble::Revenue` | Credit | Income | Sales, Interest Earned
|
109
|
+
`DoubleDouble::Expense` | Debit | Expenditures | Utilities, Salaries, Rent, Taco Tuesday
|
110
|
+
|
111
|
+
Accounts have the following attributes:
|
112
|
+
|
113
|
+
* **name**
|
114
|
+
* **number**, for reporting purposes
|
115
|
+
* **contra** flag, _optional_ `default: false`
|
116
|
+
|
117
|
+
An example 'Cash' asset account as account number `20`
|
118
|
+
|
119
|
+
```ruby
|
120
|
+
DoubleDouble::Asset.create! name: 'Cash', number: 20
|
121
|
+
```
|
122
|
+
|
123
|
+
An example 'Sales' revenue account and a 'Discounts' contra revenue account.
|
124
|
+
|
125
|
+
```ruby
|
126
|
+
DoubleDouble::Revenue.create! name: 'Sales', number: 40
|
127
|
+
DoubleDouble::Revenue.create! name: 'Discounts', number: 50, contra: true
|
128
|
+
```
|
129
|
+
|
130
|
+
Contra accounts are used to offset a related account of the same class. *The example above is a common method to track sales. The full __sales value__ of the sale would be assigned to 'Sales' while any discounts given would be assigned to 'Discounts.'*
|
131
|
+
|
132
|
+
|
133
|
+
### Amounts & Transactions
|
134
|
+
|
135
|
+
* TODO: Transaction basics
|
136
|
+
* TODO: Amount basics
|
137
|
+
* TODO: Accountees
|
138
|
+
* TODO: Contexts
|
139
|
+
* TODO: Initiated_by
|
140
|
+
|
141
|
+
## Example Scenarios
|
142
|
+
|
143
|
+
### Basic Scenarios
|
144
|
+
|
145
|
+
#### Basic Scenario: We are creating a personal application to only track loan payments back to Grandpa.
|
146
|
+
|
147
|
+
We've decided to keep things very simple and only create two accounts:
|
148
|
+
* 'Cash' an asset account.
|
149
|
+
* 'Grandpa Loan' a liability account.
|
150
|
+
|
151
|
+
```ruby
|
152
|
+
DoubleDouble::Asset.create! name:'Cash', number: 11
|
153
|
+
DoubleDouble::Liability.create! name:'Grandpa Loan', number: 12
|
154
|
+
```
|
155
|
+
Grandpa was kind enough to loan us $800 USD in cash for college textbooks. To enter this we will require a transaction which will affect both 'Cash' and 'Grandpa Loan'
|
156
|
+
```ruby
|
157
|
+
DoubleDouble::Transaction.create!(
|
158
|
+
description:
|
159
|
+
'We received a loan from Grandpa',
|
160
|
+
debits:[
|
161
|
+
{account: 'Cash', amount: '$800'}],
|
162
|
+
credits:[
|
163
|
+
{account: 'Grandpa Loan', amount: '$800'}])
|
164
|
+
```
|
165
|
+
But because we were surprised to have the option to buy a few used textbooks we can return some of Grandpa's loan. We will return $320.
|
166
|
+
```ruby
|
167
|
+
DoubleDouble::Transaction.create!(
|
168
|
+
description:
|
169
|
+
'Payed back $320 to Grandpa',
|
170
|
+
debits:[
|
171
|
+
{account: 'Grandpa Loan', amount: '$320'}],
|
172
|
+
credits:[
|
173
|
+
{account: 'Cash', amount: '$320'}])
|
174
|
+
```
|
175
|
+
If we want to know how much we still owed Grandpa we can look at the balance of the account.
|
176
|
+
```ruby
|
177
|
+
DoubleDouble::Account.find_by_name('Grandpa Loan').balance.to_s # => "480.00"
|
178
|
+
```
|
27
179
|
|
28
|
-
|
180
|
+
### Realistic Scenarios
|
181
|
+
* TODO: Write a realistic scenario
|
182
|
+
|
183
|
+
### Complex Scenarios
|
184
|
+
* TODO: Write a realistic & complex scenario
|
185
|
+
|
186
|
+
## Tests
|
187
|
+
|
188
|
+
All code is backed by Rspec tests. Clone this repository and either `rspec spec` or `rake spec` if Rake is installed.
|
29
189
|
|
30
190
|
## Contributing
|
31
191
|
|
32
192
|
1. Fork it
|
33
193
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
34
|
-
3. Commit your changes (`git commit -am 'Add some feature'`)
|
194
|
+
3. Commit your changes & tests (`git commit -am 'Add some feature'`)
|
35
195
|
4. Push to the branch (`git push origin my-new-feature`)
|
36
196
|
5. Create new Pull Request
|
37
197
|
|
38
198
|
## Notes
|
39
199
|
|
40
|
-
double_double was
|
200
|
+
double_double was influenced by mbulat's plutus project and regularly working with quickbooks.
|
201
|
+
|
data/double_double.gemspec
CHANGED
@@ -18,7 +18,7 @@ Gem::Specification.new do |gem|
|
|
18
18
|
gem.require_paths = ['lib']
|
19
19
|
|
20
20
|
gem.add_dependency('money', '~> 5.1')
|
21
|
-
gem.add_dependency
|
21
|
+
gem.add_dependency('activerecord', '~> 3.2.11')
|
22
22
|
gem.add_development_dependency('sqlite3')
|
23
23
|
gem.add_development_dependency('rspec', '~> 2.12')
|
24
24
|
gem.add_development_dependency('factory_girl')
|
@@ -40,14 +40,14 @@ module DoubleDouble
|
|
40
40
|
|
41
41
|
validates_presence_of :type, :name, :number
|
42
42
|
validates_uniqueness_of :name, :number
|
43
|
+
validates_length_of :name, :minimum => 1
|
43
44
|
|
44
45
|
def side_balance(is_debit, hash)
|
45
|
-
hash_keys = hash.keys
|
46
46
|
a = is_debit ? DoubleDouble::DebitAmount.scoped : DoubleDouble::CreditAmount.scoped
|
47
47
|
a = a.where(account_id: self.id)
|
48
|
-
a = a.by_context(hash[:
|
49
|
-
a = a.by_initiator(hash[:
|
50
|
-
a = a.by_accountee(hash[:
|
48
|
+
a = a.by_context(hash[:context]) if hash.has_key? :context
|
49
|
+
a = a.by_initiator(hash[:initiator]) if hash.has_key? :initiator
|
50
|
+
a = a.by_accountee(hash[:accountee]) if hash.has_key? :accountee
|
51
51
|
Money.new(a.sum(:amount_cents))
|
52
52
|
end
|
53
53
|
|
@@ -64,12 +64,12 @@ module DoubleDouble
|
|
64
64
|
#
|
65
65
|
# @return [Money] The value balance of all accounts
|
66
66
|
def self.trial_balance
|
67
|
-
raise(NoMethodError, "undefined method 'trial_balance'") unless self
|
67
|
+
raise(NoMethodError, "undefined method 'trial_balance'") unless self == DoubleDouble::Account
|
68
68
|
Asset.balance - (Liability.balance + Equity.balance + Revenue.balance - Expense.balance)
|
69
69
|
end
|
70
70
|
|
71
71
|
def self.balance
|
72
|
-
raise(NoMethodError, "undefined method 'balance'") if self
|
72
|
+
raise(NoMethodError, "undefined method 'balance'") if self == DoubleDouble::Account
|
73
73
|
accounts_balance = Money.new(0)
|
74
74
|
accounts = self.all
|
75
75
|
accounts.each do |acct|
|
@@ -83,17 +83,20 @@ module DoubleDouble
|
|
83
83
|
end
|
84
84
|
|
85
85
|
protected
|
86
|
-
#
|
86
|
+
# The balance method that derived Accounts utilize.
|
87
|
+
#
|
88
|
+
# Nornal Debit Accounts:
|
87
89
|
# if contra { credits_balance(hash) - debits_balance(hash) }
|
88
90
|
# else { debits_balance(hash) - credits_balance(hash) }
|
89
91
|
#
|
90
|
-
#
|
92
|
+
# Normal Credit Accounts:
|
91
93
|
# if contra { debits_balance(hash) - credits_balance(hash) }
|
92
94
|
# else { credits_balance(hash) - debits_balance(hash) }
|
93
95
|
#
|
94
96
|
# @return [Money] The balance of the account instance
|
95
|
-
def child_account_balance(
|
96
|
-
|
97
|
+
def child_account_balance(is_normal_debit_account, hash = {})
|
98
|
+
raise(NoMethodError, "undefined method 'balance'") if self == DoubleDouble::Account
|
99
|
+
if (is_normal_debit_account && contra) || !(is_normal_debit_account || contra)
|
97
100
|
credits_balance(hash) - debits_balance(hash)
|
98
101
|
else
|
99
102
|
debits_balance(hash) - credits_balance(hash)
|
data/lib/double_double/amount.rb
CHANGED
@@ -7,21 +7,22 @@ module DoubleDouble
|
|
7
7
|
class Amount < ActiveRecord::Base
|
8
8
|
self.table_name = 'double_double_amounts'
|
9
9
|
|
10
|
-
attr_accessible :account, :amount, :transaction, :context, :initiator, :accountee
|
10
|
+
attr_accessible :account, :amount, :transaction, :context, :initiator, :accountee, as: :transation_builder
|
11
11
|
|
12
12
|
belongs_to :transaction
|
13
13
|
belongs_to :account
|
14
|
+
belongs_to :accountee, polymorphic: true
|
14
15
|
belongs_to :context, polymorphic: true
|
15
16
|
belongs_to :initiator, polymorphic: true
|
16
|
-
belongs_to :accountee, polymorphic: true
|
17
17
|
|
18
|
-
scope :
|
19
|
-
scope :
|
20
|
-
scope :
|
18
|
+
scope :by_accountee, ->(a) { where(accountee_id: a.id, accountee_type: a.class.base_class) }
|
19
|
+
scope :by_context, ->(c) { where(context_id: c.id, context_type: c.class.base_class) }
|
20
|
+
scope :by_initiator, ->(i) { where(initiator_id: i.id, initiator_type: i.class.base_class) }
|
21
21
|
|
22
22
|
# scope :by_transaction_type_number, -> {|tt_num| where( transaction: {transaction_type: {number: tt_num}})}
|
23
23
|
|
24
|
-
validates_presence_of :type, :
|
24
|
+
validates_presence_of :type, :transaction, :account
|
25
|
+
validates :amount_cents, numericality: {greater_than: 0}
|
25
26
|
|
26
27
|
composed_of :amount,
|
27
28
|
class_name: "Money",
|
data/lib/double_double/asset.rb
CHANGED
data/lib/double_double/equity.rb
CHANGED
@@ -29,8 +29,8 @@ module DoubleDouble
|
|
29
29
|
|
30
30
|
has_many :credit_amounts
|
31
31
|
has_many :debit_amounts
|
32
|
-
has_many :credit_accounts, :
|
33
|
-
has_many :debit_accounts, :
|
32
|
+
has_many :credit_accounts, through: :credit_amounts, source: :account
|
33
|
+
has_many :debit_accounts, through: :debit_amounts, source: :account
|
34
34
|
|
35
35
|
validates_presence_of :description
|
36
36
|
validate :has_credit_amounts?
|
@@ -45,27 +45,31 @@ module DoubleDouble
|
|
45
45
|
# transaction = DoubleDouble::Transaction.build(
|
46
46
|
# description: "Sold some widgets",
|
47
47
|
# debits: [
|
48
|
-
# {account: "Accounts Receivable", amount: 50}],
|
48
|
+
# {account: "Accounts Receivable", amount: 50, context_id: 20, context_type: 'Job'}],
|
49
49
|
# credits: [
|
50
|
-
# {account: "Sales Revenue",
|
51
|
-
# {account: "Sales Tax Payable",
|
50
|
+
# {account: "Sales Revenue", amount: 45},
|
51
|
+
# {account: "Sales Tax Payable", amount: 5}])
|
52
52
|
#
|
53
53
|
# @return [DoubleDouble::Transaction] A Transaction with built credit and debit objects ready for saving
|
54
|
-
def self.build
|
55
|
-
t = Transaction.new(
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
new_credit_amount = prepare_amount_parameters credit.merge!({transaction: t})
|
62
|
-
t.credit_amounts << CreditAmount.new(new_credit_amount)
|
63
|
-
end
|
64
|
-
t.transaction_type = hash[:transaction_type] if hash.has_key?(:transaction_type)
|
54
|
+
def self.build args
|
55
|
+
t = Transaction.new()
|
56
|
+
t.description = args[:description]
|
57
|
+
t.transaction_type = args[:transaction_type] if args.has_key? :transaction_type
|
58
|
+
|
59
|
+
add_amounts_to_transaction(args[:debits], t, true)
|
60
|
+
add_amounts_to_transaction(args[:credits], t, false)
|
65
61
|
t
|
66
62
|
end
|
67
63
|
|
64
|
+
def self.create! args
|
65
|
+
t = build args
|
66
|
+
t.save!
|
67
|
+
end
|
68
|
+
|
68
69
|
private
|
70
|
+
|
71
|
+
# Validation
|
72
|
+
|
69
73
|
def has_credit_amounts?
|
70
74
|
errors[:base] << "Transaction must have at least one credit amount" if self.credit_amounts.blank?
|
71
75
|
end
|
@@ -84,21 +88,24 @@ module DoubleDouble
|
|
84
88
|
credit_amount_total - debit_amount_total
|
85
89
|
end
|
86
90
|
|
87
|
-
|
88
|
-
prepared_params = { account: Account.find_by_name(args[:account]), transaction: args[:transaction], amount: args[:amount]}
|
89
|
-
|
90
|
-
args_keys = args.keys
|
91
|
-
if (args_keys & [:context_id, :context_type]).count == 2
|
92
|
-
prepared_params.merge!({context_id: args[:context_id], context_type: args[:context_type]})
|
93
|
-
end
|
94
|
-
|
95
|
-
if (args_keys & [:initiator_id, :initiator_type]).count == 2
|
96
|
-
prepared_params.merge!({initiator_id: args[:initiator_id], initiator_type: args[:initiator_type]})
|
97
|
-
end
|
91
|
+
# Assist transaction building
|
98
92
|
|
99
|
-
|
100
|
-
|
93
|
+
def self.add_amounts_to_transaction amounts, transaction, add_to_debits = true
|
94
|
+
return if amounts.nil? || amounts.count == 0
|
95
|
+
amounts.each do |amt|
|
96
|
+
amount_parameters = prepare_amount_parameters amt.merge!({transaction: transaction})
|
97
|
+
new_amount = add_to_debits ? DebitAmount.new : CreditAmount.new
|
98
|
+
new_amount.assign_attributes(amount_parameters, as: :transation_builder)
|
99
|
+
transaction.debit_amounts << new_amount if add_to_debits
|
100
|
+
transaction.credit_amounts << new_amount unless add_to_debits
|
101
101
|
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def self.prepare_amount_parameters args
|
105
|
+
prepared_params = { account: Account.find_by_name(args[:account]), transaction: args[:transaction], amount: args[:amount]}
|
106
|
+
prepared_params.merge!({accountee: args[:accountee]}) if args.has_key? :accountee
|
107
|
+
prepared_params.merge!({context: args[:context]}) if args.has_key? :context
|
108
|
+
prepared_params.merge!({initiator: args[:initiator]}) if args.has_key? :initiator
|
102
109
|
prepared_params
|
103
110
|
end
|
104
111
|
end
|
data/lib/double_double.rb
CHANGED
@@ -5,8 +5,8 @@ require 'double_double/version'
|
|
5
5
|
|
6
6
|
# Accounts
|
7
7
|
require 'double_double/account'
|
8
|
-
require 'double_double/
|
9
|
-
require 'double_double/
|
8
|
+
require 'double_double/normal_credit_account'
|
9
|
+
require 'double_double/normal_debit_account'
|
10
10
|
require 'double_double/asset'
|
11
11
|
require 'double_double/equity'
|
12
12
|
require 'double_double/expense'
|
data/spec/models/account_spec.rb
CHANGED
@@ -2,26 +2,26 @@ module DoubleDouble
|
|
2
2
|
describe Account do
|
3
3
|
|
4
4
|
it "should not allow creating an account without a subtype" do
|
5
|
-
account =
|
5
|
+
account = DoubleDouble::Account.new(name: 'Cash', number: 10)
|
6
6
|
account.should_not be_valid
|
7
7
|
end
|
8
8
|
|
9
9
|
it "should be unique per name" do
|
10
|
-
|
11
|
-
account =
|
10
|
+
DoubleDouble::Asset.new(name: 'Petty Cash', number: 10).save
|
11
|
+
account = DoubleDouble::Liability.new(name: 'Petty Cash', number: 11)
|
12
12
|
account.should_not be_valid
|
13
13
|
account.errors[:name].should == ["has already been taken"]
|
14
14
|
end
|
15
15
|
|
16
16
|
it "should be unique per number" do
|
17
|
-
|
18
|
-
account =
|
17
|
+
DoubleDouble::Asset.new(name: 'Cash', number: 22).save
|
18
|
+
account = DoubleDouble::Liability.new(name: 'Loan', number: 22)
|
19
19
|
account.should_not be_valid
|
20
20
|
account.errors[:number].should == ["has already been taken"]
|
21
21
|
end
|
22
22
|
|
23
23
|
it "should not have a balance method" do
|
24
|
-
|
24
|
+
-> {Account.balance}.should raise_error(NoMethodError)
|
25
25
|
end
|
26
26
|
|
27
27
|
it "should have a trial balance" do
|
@@ -31,39 +31,37 @@ module DoubleDouble
|
|
31
31
|
|
32
32
|
it "should report a trial balance of 0 with correct transactions (with a contrived example of transactions)" do
|
33
33
|
# credit accounts
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
34
|
+
FactoryGirl.create(:liability, name: 'liability acct')
|
35
|
+
FactoryGirl.create(:equity, name: 'equity acct')
|
36
|
+
FactoryGirl.create(:revenue, name: 'revenue acct')
|
37
|
+
FactoryGirl.create(:asset, name: 'contra asset acct', :contra => true)
|
38
|
+
FactoryGirl.create(:expense, name: 'contra expense acct', :contra => true)
|
39
39
|
# debit accounts
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
t.save
|
66
|
-
|
40
|
+
FactoryGirl.create(:asset, name: 'asset acct')
|
41
|
+
FactoryGirl.create(:expense, name: 'expense acct')
|
42
|
+
FactoryGirl.create(:liability, name: 'contra liability acct', :contra => true)
|
43
|
+
FactoryGirl.create(:equity, name: 'contra equity acct', :contra => true)
|
44
|
+
FactoryGirl.create(:revenue, name: 'contra revenue acct', :contra => true)
|
45
|
+
Transaction.create!(
|
46
|
+
description: 'spec transaction 01',
|
47
|
+
debits: [{account: 'liability acct', amount: 100_000}],
|
48
|
+
credits: [{account: 'asset acct', amount: 100_000}])
|
49
|
+
Transaction.create!(
|
50
|
+
description: 'spec transaction 02',
|
51
|
+
debits: [{account: 'equity acct', amount: 1_000}],
|
52
|
+
credits: [{account: 'expense acct', amount: 1_000}])
|
53
|
+
Transaction.create!(
|
54
|
+
description: 'spec transaction 03',
|
55
|
+
debits: [{account: 'revenue acct', amount: 40_404}],
|
56
|
+
credits: [{account: 'contra liability acct', amount: 40_404}])
|
57
|
+
Transaction.create!(
|
58
|
+
description: 'spec transaction 04',
|
59
|
+
debits: [{account: 'contra asset acct', amount: 2}],
|
60
|
+
credits: [{account: 'contra equity acct', amount: 2}])
|
61
|
+
Transaction.create!(
|
62
|
+
description: 'spec transaction 05',
|
63
|
+
debits: [{account: 'contra expense acct', amount: 333}],
|
64
|
+
credits: [{account: 'contra revenue acct', amount: 333}])
|
67
65
|
Account.trial_balance.should == 0
|
68
66
|
end
|
69
67
|
end
|
data/spec/models/amount_spec.rb
CHANGED
@@ -2,7 +2,8 @@ module DoubleDouble
|
|
2
2
|
describe Amount do
|
3
3
|
|
4
4
|
it "should not allow creating an amount without a subtype" do
|
5
|
-
|
5
|
+
cash = DoubleDouble::Asset.create!(name:'Cash', number: 11)
|
6
|
+
amount = DoubleDouble::Amount.new(amount: 50, account: cash)
|
6
7
|
amount.should_not be_valid
|
7
8
|
end
|
8
9
|
end
|
data/spec/models/asset_spec.rb
CHANGED
@@ -5,8 +5,13 @@ module DoubleDouble
|
|
5
5
|
let(:account_type) {:asset}
|
6
6
|
end
|
7
7
|
|
8
|
-
it_behaves_like "a
|
9
|
-
let(:
|
8
|
+
it_behaves_like "a normal debit account type" do
|
9
|
+
let(:normal_debit_account_type) {:asset}
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should create a proper Asset account" do
|
13
|
+
-> { DoubleDouble::Asset.create! name: 'Asset acct', number: 20
|
14
|
+
}.should change(DoubleDouble::Asset, :count).by(1)
|
10
15
|
end
|
11
16
|
end
|
12
17
|
end
|