double_double 0.2.0 → 0.2.3
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +38 -11
- data/double_double.gemspec +9 -7
- data/lib/double_double/account.rb +31 -31
- data/lib/double_double/amount.rb +0 -4
- data/lib/double_double/transaction.rb +12 -7
- data/lib/double_double/transaction_type.rb +14 -3
- data/lib/double_double/version.rb +1 -1
- data/spec/models/account_spec.rb +1 -1
- data/spec/models/credit_amount_spec.rb +1 -0
- data/spec/models/debit_amount_spec.rb +1 -0
- data/spec/models/transaction_spec.rb +55 -31
- data/spec/models/transaction_type_spec.rb +26 -0
- data/spec/spec_helper.rb +4 -5
- data/spec/support/normal_credit_account_types.rb +112 -108
- data/spec/support/normal_debit_account_types.rb +112 -108
- data/spec/support/readme_scenarios.rb +50 -0
- metadata +7 -5
- data/spec/factories/transaction_type_factory.rb +0 -14
data/README.md
CHANGED
@@ -33,8 +33,8 @@ Edit the migration to match:
|
|
33
33
|
class CreateDoubleDouble < ActiveRecord::Migration
|
34
34
|
def change
|
35
35
|
create_table :double_double_accounts do |t|
|
36
|
-
t.string :name, null: false
|
37
36
|
t.integer :number, null: false
|
37
|
+
t.string :name, null: false
|
38
38
|
t.string :type, null: false
|
39
39
|
t.boolean :contra, default: false
|
40
40
|
end
|
@@ -42,31 +42,31 @@ class CreateDoubleDouble < ActiveRecord::Migration
|
|
42
42
|
|
43
43
|
create_table :double_double_transactions do |t|
|
44
44
|
t.string :description
|
45
|
+
t.references :initiator, polymorphic: true
|
45
46
|
t.references :transaction_type
|
46
47
|
t.timestamps
|
47
48
|
end
|
49
|
+
add_index :double_double_transactions, :initiator_id
|
50
|
+
add_index :double_double_transactions, :initiator_type
|
48
51
|
add_index :double_double_transactions, :transaction_type_id
|
49
52
|
|
50
53
|
create_table :double_double_transaction_types do |t|
|
51
|
-
t.integer :number, null: false
|
52
54
|
t.string :description, null: false
|
53
55
|
end
|
54
|
-
add_index :double_double_transaction_types, :
|
56
|
+
add_index :double_double_transaction_types, :description
|
55
57
|
|
56
58
|
create_table :double_double_amounts do |t|
|
57
59
|
t.string :type
|
58
60
|
t.references :account
|
59
61
|
t.references :transaction
|
60
|
-
t.references :accountee, polymorphic: true
|
61
62
|
t.references :context, polymorphic: true
|
62
|
-
t.references :
|
63
|
+
t.references :accountee, polymorphic: true
|
64
|
+
|
63
65
|
t.integer :amount_cents, limit: 8, default: 0, null: false
|
64
66
|
t.string :currency
|
65
67
|
end
|
66
68
|
add_index :double_double_amounts, :context_id
|
67
69
|
add_index :double_double_amounts, :context_type
|
68
|
-
add_index :double_double_amounts, :initiator_id
|
69
|
-
add_index :double_double_amounts, :initiator_type
|
70
70
|
add_index :double_double_amounts, :accountee_id
|
71
71
|
add_index :double_double_amounts, :accountee_type
|
72
72
|
add_index :double_double_amounts, :type
|
@@ -144,13 +144,15 @@ Contra accounts are used to offset a related account of the same class. *The ex
|
|
144
144
|
|
145
145
|
#### Basic Scenario: We are creating a personal application to only track loan payments back to Grandpa.
|
146
146
|
|
147
|
-
We've decided to keep things very simple and only create
|
147
|
+
We've decided to keep things very simple and only create a few accounts:
|
148
148
|
* 'Cash' an asset account.
|
149
149
|
* 'Grandpa Loan' a liability account.
|
150
|
+
* 'Spending' an expense account
|
150
151
|
|
151
152
|
```ruby
|
152
153
|
DoubleDouble::Asset.create! name:'Cash', number: 11
|
153
154
|
DoubleDouble::Liability.create! name:'Grandpa Loan', number: 12
|
155
|
+
DoubleDouble::Expense.create! name:'Spending', number: 13
|
154
156
|
```
|
155
157
|
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
158
|
```ruby
|
@@ -162,7 +164,23 @@ DoubleDouble::Transaction.create!(
|
|
162
164
|
credits:[
|
163
165
|
{account: 'Grandpa Loan', amount: '$800'}])
|
164
166
|
```
|
165
|
-
|
167
|
+
We buy our college textbooks.
|
168
|
+
|
169
|
+
```ruby
|
170
|
+
DoubleDouble::Transaction.create!(
|
171
|
+
description:
|
172
|
+
'Purchase textbooks from bookstore',
|
173
|
+
debits:[
|
174
|
+
{account: 'Spending', amount: '$480'}],
|
175
|
+
credits:[
|
176
|
+
{account: 'Cash', amount: '$480'}])
|
177
|
+
```
|
178
|
+
How much cash is left?
|
179
|
+
|
180
|
+
```ruby
|
181
|
+
DoubleDouble::Account.named('Cash').balance.to_s # => "320.00"
|
182
|
+
```
|
183
|
+
We deceided that we wanted to return $320 of the loan.
|
166
184
|
```ruby
|
167
185
|
DoubleDouble::Transaction.create!(
|
168
186
|
description:
|
@@ -172,10 +190,19 @@ DoubleDouble::Transaction.create!(
|
|
172
190
|
credits:[
|
173
191
|
{account: 'Cash', amount: '$320'}])
|
174
192
|
```
|
175
|
-
|
193
|
+
How much do we still owe Grandpa?
|
176
194
|
```ruby
|
177
|
-
DoubleDouble::Account.
|
195
|
+
DoubleDouble::Account.named('Grandpa Loan').balance.to_s # => "480.00"
|
178
196
|
```
|
197
|
+
How much did we spend?
|
198
|
+
```ruby
|
199
|
+
DoubleDouble::Account.named('Spending').balance.to_s # => "480.00"
|
200
|
+
```
|
201
|
+
How much cash do we have left?
|
202
|
+
```ruby
|
203
|
+
DoubleDouble::Account.named('Cash').balance.to_s # => "0.00"
|
204
|
+
```
|
205
|
+
|
179
206
|
|
180
207
|
### Realistic Scenarios
|
181
208
|
* TODO: Write a realistic scenario
|
data/double_double.gemspec
CHANGED
@@ -17,11 +17,13 @@ Gem::Specification.new do |gem|
|
|
17
17
|
gem.test_files = gem.files.grep(%r{^(test|spec|features|account_types)/})
|
18
18
|
gem.require_paths = ['lib']
|
19
19
|
|
20
|
-
gem.
|
21
|
-
|
22
|
-
gem.
|
23
|
-
gem.
|
24
|
-
gem.add_development_dependency
|
25
|
-
gem.add_development_dependency
|
26
|
-
gem.add_development_dependency
|
20
|
+
gem.required_ruby_version = '>= 1.9.2'
|
21
|
+
|
22
|
+
gem.add_dependency 'money', '~> 5.1'
|
23
|
+
gem.add_dependency 'activerecord', '~> 3.2.11'
|
24
|
+
gem.add_development_dependency 'sqlite3'
|
25
|
+
gem.add_development_dependency 'rspec', '~> 2.12'
|
26
|
+
gem.add_development_dependency 'factory_girl'
|
27
|
+
gem.add_development_dependency 'database_cleaner'
|
28
|
+
gem.add_development_dependency 'pry'
|
27
29
|
end
|
@@ -42,15 +42,30 @@ module DoubleDouble
|
|
42
42
|
validates_uniqueness_of :name, :number
|
43
43
|
validates_length_of :name, :minimum => 1
|
44
44
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
45
|
+
class << self
|
46
|
+
# The trial balance of all accounts in the system. This should always equal zero,
|
47
|
+
# otherwise there is an error in the system.
|
48
|
+
#
|
49
|
+
# @return [Money] The value balance of all accounts
|
50
|
+
def trial_balance
|
51
|
+
raise(NoMethodError, "undefined method 'trial_balance'") unless self == DoubleDouble::Account
|
52
|
+
Asset.balance - (Liability.balance + Equity.balance + Revenue.balance - Expense.balance)
|
53
|
+
end
|
54
|
+
|
55
|
+
def balance
|
56
|
+
raise(NoMethodError, "undefined method 'balance'") if self == DoubleDouble::Account
|
57
|
+
accounts_balance = self.all.inject(Money.new(0)) {|sum, acct| acct.contra ? (sum - acct.balance) : (sum + acct.balance)}
|
58
|
+
end
|
59
|
+
|
60
|
+
def named account_name
|
61
|
+
self.where(name: account_name.to_s).first
|
62
|
+
end
|
63
|
+
|
64
|
+
def numbered account_number
|
65
|
+
self.where(number: account_number.to_i).first
|
66
|
+
end
|
52
67
|
end
|
53
|
-
|
68
|
+
|
54
69
|
def credits_balance(hash = {})
|
55
70
|
side_balance(false, hash)
|
56
71
|
end
|
@@ -59,30 +74,15 @@ module DoubleDouble
|
|
59
74
|
side_balance(true, hash)
|
60
75
|
end
|
61
76
|
|
62
|
-
# The trial balance of all accounts in the system. This should always equal zero,
|
63
|
-
# otherwise there is an error in the system.
|
64
|
-
#
|
65
|
-
# @return [Money] The value balance of all accounts
|
66
|
-
def self.trial_balance
|
67
|
-
raise(NoMethodError, "undefined method 'trial_balance'") unless self == DoubleDouble::Account
|
68
|
-
Asset.balance - (Liability.balance + Equity.balance + Revenue.balance - Expense.balance)
|
69
|
-
end
|
70
|
-
|
71
|
-
def self.balance
|
72
|
-
raise(NoMethodError, "undefined method 'balance'") if self == DoubleDouble::Account
|
73
|
-
accounts_balance = Money.new(0)
|
74
|
-
accounts = self.all
|
75
|
-
accounts.each do |acct|
|
76
|
-
if acct.contra
|
77
|
-
accounts_balance -= acct.balance
|
78
|
-
else
|
79
|
-
accounts_balance += acct.balance
|
80
|
-
end
|
81
|
-
end
|
82
|
-
accounts_balance
|
83
|
-
end
|
84
|
-
|
85
77
|
protected
|
78
|
+
|
79
|
+
def side_balance(is_debit, hash)
|
80
|
+
a = is_debit ? DoubleDouble::DebitAmount.scoped : DoubleDouble::CreditAmount.scoped
|
81
|
+
a = a.where(account_id: self.id)
|
82
|
+
a = a.by_context(hash[:context]) if hash.has_key? :context
|
83
|
+
a = a.by_accountee(hash[:accountee]) if hash.has_key? :accountee
|
84
|
+
Money.new(a.sum(:amount_cents))
|
85
|
+
end
|
86
86
|
# The balance method that derived Accounts utilize.
|
87
87
|
#
|
88
88
|
# Nornal Debit Accounts:
|
data/lib/double_double/amount.rb
CHANGED
@@ -13,13 +13,9 @@ module DoubleDouble
|
|
13
13
|
belongs_to :account
|
14
14
|
belongs_to :accountee, polymorphic: true
|
15
15
|
belongs_to :context, polymorphic: true
|
16
|
-
belongs_to :initiator, polymorphic: true
|
17
16
|
|
18
17
|
scope :by_accountee, ->(a) { where(accountee_id: a.id, accountee_type: a.class.base_class) }
|
19
18
|
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
|
-
|
22
|
-
# scope :by_transaction_type_number, -> {|tt_num| where( transaction: {transaction_type: {number: tt_num}})}
|
23
19
|
|
24
20
|
validates_presence_of :type, :transaction, :account
|
25
21
|
validates :amount_cents, numericality: {greater_than: 0}
|
@@ -7,8 +7,8 @@ module DoubleDouble
|
|
7
7
|
# debit transactions
|
8
8
|
#
|
9
9
|
# @example
|
10
|
-
# cash = DoubleDouble::Asset.
|
11
|
-
# accounts_receivable = DoubleDouble::Asset.
|
10
|
+
# cash = DoubleDouble::Asset.named('Cash')
|
11
|
+
# accounts_receivable = DoubleDouble::Asset.named('Accounts Receivable')
|
12
12
|
#
|
13
13
|
# debit_amount = DoubleDouble::DebitAmount.new(account: 'cash', amount: 1000)
|
14
14
|
# credit_amount = DoubleDouble::CreditAmount.new(account: 'accounts_receivable', amount: 1000)
|
@@ -26,6 +26,7 @@ module DoubleDouble
|
|
26
26
|
attr_accessible :description
|
27
27
|
|
28
28
|
belongs_to :transaction_type
|
29
|
+
belongs_to :initiator, polymorphic: true
|
29
30
|
|
30
31
|
has_many :credit_amounts
|
31
32
|
has_many :debit_amounts
|
@@ -37,7 +38,8 @@ module DoubleDouble
|
|
37
38
|
validate :has_debit_amounts?
|
38
39
|
validate :amounts_cancel?
|
39
40
|
|
40
|
-
scope :
|
41
|
+
scope :by_transaction_type, ->(tt) { where(transaction_type: tt)}
|
42
|
+
scope :by_initiator, ->(i) { where(initiator_id: i.id, initiator_type: i.class.base_class) }
|
41
43
|
|
42
44
|
# Simple API for building a transaction and associated debit and credit amounts
|
43
45
|
#
|
@@ -45,7 +47,7 @@ module DoubleDouble
|
|
45
47
|
# transaction = DoubleDouble::Transaction.build(
|
46
48
|
# description: "Sold some widgets",
|
47
49
|
# debits: [
|
48
|
-
# {account: "Accounts Receivable", amount: 50,
|
50
|
+
# {account: "Accounts Receivable", amount: 50, context: @some_active_record_object}],
|
49
51
|
# credits: [
|
50
52
|
# {account: "Sales Revenue", amount: 45},
|
51
53
|
# {account: "Sales Tax Payable", amount: 5}])
|
@@ -55,7 +57,7 @@ module DoubleDouble
|
|
55
57
|
t = Transaction.new()
|
56
58
|
t.description = args[:description]
|
57
59
|
t.transaction_type = args[:transaction_type] if args.has_key? :transaction_type
|
58
|
-
|
60
|
+
t.initiator = args[:initiator] if args.has_key? :initiator
|
59
61
|
add_amounts_to_transaction(args[:debits], t, true)
|
60
62
|
add_amounts_to_transaction(args[:credits], t, false)
|
61
63
|
t
|
@@ -66,6 +68,10 @@ module DoubleDouble
|
|
66
68
|
t.save!
|
67
69
|
end
|
68
70
|
|
71
|
+
def transaction_type
|
72
|
+
self.transaction_type_id.nil? ? UnassignedTransactionType : TransactionType.find(self.transaction_type_id)
|
73
|
+
end
|
74
|
+
|
69
75
|
private
|
70
76
|
|
71
77
|
# Validation
|
@@ -102,10 +108,9 @@ module DoubleDouble
|
|
102
108
|
end
|
103
109
|
|
104
110
|
def self.prepare_amount_parameters args
|
105
|
-
prepared_params = { account: Account.
|
111
|
+
prepared_params = { account: Account.named(args[:account]), transaction: args[:transaction], amount: args[:amount]}
|
106
112
|
prepared_params.merge!({accountee: args[:accountee]}) if args.has_key? :accountee
|
107
113
|
prepared_params.merge!({context: args[:context]}) if args.has_key? :context
|
108
|
-
prepared_params.merge!({initiator: args[:initiator]}) if args.has_key? :initiator
|
109
114
|
prepared_params
|
110
115
|
end
|
111
116
|
end
|
@@ -3,9 +3,20 @@ module DoubleDouble
|
|
3
3
|
self.table_name = 'double_double_transaction_types'
|
4
4
|
|
5
5
|
has_many :transactions
|
6
|
-
attr_accessible :
|
6
|
+
attr_accessible :description
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
validates :description, length: { minimum: 6 }, presence: true, uniqueness: true
|
9
|
+
|
10
|
+
def self.of description_given
|
11
|
+
TransactionType.where(description: description_given.to_s).first
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class UnassignedTransactionType
|
16
|
+
class << self
|
17
|
+
def description
|
18
|
+
'unassigned'
|
19
|
+
end
|
20
|
+
end
|
10
21
|
end
|
11
22
|
end
|
data/spec/models/account_spec.rb
CHANGED
@@ -62,7 +62,7 @@ module DoubleDouble
|
|
62
62
|
description: 'spec transaction 05',
|
63
63
|
debits: [{account: 'contra expense acct', amount: 333}],
|
64
64
|
credits: [{account: 'contra revenue acct', amount: 333}])
|
65
|
-
Account.trial_balance.should
|
65
|
+
Account.trial_balance.should eq(0)
|
66
66
|
end
|
67
67
|
end
|
68
68
|
end
|
@@ -63,6 +63,7 @@ module DoubleDouble
|
|
63
63
|
@loan.credits_balance({context: @job}).should == Money.new(123 + 321)
|
64
64
|
@loan.credits_balance({context: @po}).should == Money.new(275)
|
65
65
|
@loan.credits_balance.should == Money.new(123 + 321 + 275 + 999)
|
66
|
+
Account.trial_balance.should eq(0)
|
66
67
|
end
|
67
68
|
end
|
68
69
|
end
|
@@ -63,6 +63,7 @@ module DoubleDouble
|
|
63
63
|
@cash.debits_balance({context: @job}).should == Money.new(123 + 321)
|
64
64
|
@cash.debits_balance({context: @po}).should == Money.new(275)
|
65
65
|
@cash.debits_balance.should == Money.new(123 + 321 + 275 + 999)
|
66
|
+
Account.trial_balance.should eq(0)
|
66
67
|
end
|
67
68
|
end
|
68
69
|
end
|
@@ -4,24 +4,27 @@ module DoubleDouble
|
|
4
4
|
before(:each) do
|
5
5
|
@cash = DoubleDouble::Asset.create!(name:'Cash_11', number: 1011)
|
6
6
|
@loan = DoubleDouble::Liability.create!(name:'Loan_12', number: 1012)
|
7
|
-
|
7
|
+
# dummy objects to stand-in for accountees
|
8
|
+
@user1 = DoubleDouble::Asset.create!(name:'some user1', number: 8991)
|
9
|
+
@user2 = DoubleDouble::Asset.create!(name:'some user2', number: 8992)
|
8
10
|
# dummy objects to stand-in for a context
|
9
11
|
@campaign1 = DoubleDouble::Asset.create!(name:'campaign_test1', number: 9991)
|
10
12
|
@campaign2 = DoubleDouble::Asset.create!(name:'campaign_test2', number: 9992)
|
11
13
|
end
|
12
14
|
|
13
|
-
it
|
15
|
+
it_behaves_like "it can run the README scenarios"
|
16
|
+
|
17
|
+
it 'should create a Transaction using the create! method' do
|
14
18
|
-> {
|
15
19
|
Transaction.create!(
|
16
20
|
description: 'spec transaction 01',
|
17
21
|
debits: [{account: 'Cash_11', amount: 10}],
|
18
22
|
credits: [{account: 'Loan_12', amount: 9},
|
19
23
|
{account: 'Loan_12', amount: 1}])
|
20
|
-
|
21
24
|
}.should change(DoubleDouble::Transaction, :count).by(1)
|
22
25
|
end
|
23
26
|
|
24
|
-
it 'should not create a
|
27
|
+
it 'should not create a Transaction using the build method' do
|
25
28
|
-> {
|
26
29
|
Transaction.build(
|
27
30
|
description: 'spec transaction 01',
|
@@ -111,8 +114,52 @@ module DoubleDouble
|
|
111
114
|
t.errors['base'].should == ['The credit and debit amounts are not equal']
|
112
115
|
end
|
113
116
|
|
114
|
-
describe '
|
115
|
-
it 'should
|
117
|
+
describe 'transaction_types' do
|
118
|
+
it 'should create a Transaction with a TransactionType of Unassigned if none is passed in' do
|
119
|
+
t = Transaction.build(
|
120
|
+
description: 'spec transaction 01',
|
121
|
+
debits: [{account: 'Cash_11', amount: 10}],
|
122
|
+
credits: [{account: 'Loan_12', amount: 9},
|
123
|
+
{account: 'Loan_12', amount: 1}])
|
124
|
+
t.transaction_type.description.should eq('unassigned')
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'should create a Transaction with a TransactionType of Unassigned if none is passed in' do
|
128
|
+
TransactionType.create!(description: 'donation')
|
129
|
+
t = Transaction.build(
|
130
|
+
description: 'spec transaction 01',
|
131
|
+
transaction_type: TransactionType.of(:donation),
|
132
|
+
debits: [{account: 'Cash_11', amount: 10}],
|
133
|
+
credits: [{account: 'Loan_12', amount: 9},
|
134
|
+
{account: 'Loan_12', amount: 1}])
|
135
|
+
t.transaction_type.description.should eq('donation')
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
describe 'amount accountee references' do
|
140
|
+
it 'should allow a Transaction to be built describing the accountee in the hash' do
|
141
|
+
Transaction.create!(
|
142
|
+
description: 'Sold some widgets',
|
143
|
+
debits: [{account: 'Cash_11', amount: 60, context: @campaign1, accountee: @user1},
|
144
|
+
{account: 'Cash_11', amount: 40, context: @campaign2, accountee: @user1},
|
145
|
+
{account: 'Cash_11', amount: 4, context: @campaign2, accountee: @user2}],
|
146
|
+
credits: [{account: 'Loan_12', amount: 45},
|
147
|
+
{account: 'Loan_12', amount: 9},
|
148
|
+
{account: 'Loan_12', amount: 50, context: @campaign1}])
|
149
|
+
Amount.by_accountee(@user1).count.should eq(2)
|
150
|
+
Amount.by_accountee(@user2).count.should eq(1)
|
151
|
+
|
152
|
+
@cash.debits_balance(context: @campaign1, accountee: @user1).should eq(60)
|
153
|
+
@cash.debits_balance(context: @campaign1, accountee: @user2).should eq(0)
|
154
|
+
@cash.debits_balance(context: @campaign2, accountee: @user1).should eq(40)
|
155
|
+
@cash.debits_balance(context: @campaign2, accountee: @user2).should eq(4)
|
156
|
+
@cash.debits_balance(context: @campaign2).should eq(44)
|
157
|
+
Account.trial_balance.should eq(0)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
describe 'amount context references' do
|
162
|
+
it 'should allow a Transaction to be built describing the context in the hash' do
|
116
163
|
Transaction.create!(
|
117
164
|
description: 'Sold some widgets',
|
118
165
|
debits: [{account: 'Cash_11', amount: 60, context: @campaign1},
|
@@ -122,34 +169,11 @@ module DoubleDouble
|
|
122
169
|
{account: 'Loan_12', amount: 50, context: @campaign1}])
|
123
170
|
Amount.by_context(@campaign1).count.should eq(2)
|
124
171
|
Amount.by_context(@campaign2).count.should eq(1)
|
172
|
+
|
125
173
|
@cash.debits_balance(context: @campaign1).should eq(60)
|
126
174
|
@cash.debits_balance(context: @campaign2).should eq(40)
|
175
|
+
Account.trial_balance.should eq(0)
|
127
176
|
end
|
128
177
|
end
|
129
|
-
|
130
|
-
describe 'README.md scenarios' do
|
131
|
-
it 'should perform BASIC SCENARIO A correctly' do
|
132
|
-
DoubleDouble::Asset.create! name:'Cash', number: 11
|
133
|
-
DoubleDouble::Liability.create! name:'Grandpa Loan', number: 12
|
134
|
-
# 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'
|
135
|
-
DoubleDouble::Transaction.create!(
|
136
|
-
description:
|
137
|
-
'We received a loan from Grandpa',
|
138
|
-
debits:[
|
139
|
-
{account: 'Cash', amount: '$800'}],
|
140
|
-
credits:[
|
141
|
-
{account: 'Grandpa Loan', amount: '$800'}])
|
142
|
-
# But say that we wanted to return $320 because we were able to purchase a few used books.
|
143
|
-
DoubleDouble::Transaction.create!(
|
144
|
-
description:
|
145
|
-
'Payed back $320 to Grandpa',
|
146
|
-
debits:[
|
147
|
-
{account: 'Grandpa Loan', amount: '$320'}],
|
148
|
-
credits:[
|
149
|
-
{account: 'Cash', amount: '$320'}])
|
150
|
-
# If we wanted to know how much we still owed Grandpa, we could look at the balance of the account.
|
151
|
-
DoubleDouble::Account.find_by_name('Grandpa Loan').balance.to_s.should eq("480.00")
|
152
|
-
end
|
153
|
-
end
|
154
178
|
end
|
155
179
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module DoubleDouble
|
2
|
+
describe TransactionType do
|
3
|
+
|
4
|
+
it 'should create a TransactionType using the create! method' do
|
5
|
+
-> {
|
6
|
+
TransactionType.create!(description: '123456')
|
7
|
+
}.should change(DoubleDouble::TransactionType, :count).by(1)
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'should not be valid without a description' do
|
11
|
+
-> {
|
12
|
+
TransactionType.create!(description: '')
|
13
|
+
}.should raise_error(ActiveRecord::RecordInvalid)
|
14
|
+
t = TransactionType.new(description: '')
|
15
|
+
t.should_not be_valid
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should not be valid without a long-enough description' do
|
19
|
+
-> {
|
20
|
+
TransactionType.create!(description: '12345')
|
21
|
+
}.should raise_error(ActiveRecord::RecordInvalid)
|
22
|
+
t = TransactionType.new(description: '12345')
|
23
|
+
t.should_not be_valid
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -19,23 +19,24 @@ ActiveRecord::Migration.verbose = false
|
|
19
19
|
|
20
20
|
create_table :double_double_transactions do |t|
|
21
21
|
t.string :description
|
22
|
+
t.references :initiator, polymorphic: true
|
22
23
|
t.references :transaction_type
|
23
24
|
t.timestamps
|
24
25
|
end
|
26
|
+
add_index :double_double_transactions, :initiator_id
|
27
|
+
add_index :double_double_transactions, :initiator_type
|
25
28
|
add_index :double_double_transactions, :transaction_type_id
|
26
29
|
|
27
30
|
create_table :double_double_transaction_types do |t|
|
28
|
-
t.integer :number, null: false
|
29
31
|
t.string :description, null: false
|
30
32
|
end
|
31
|
-
add_index :double_double_transaction_types, :
|
33
|
+
add_index :double_double_transaction_types, :description
|
32
34
|
|
33
35
|
create_table :double_double_amounts do |t|
|
34
36
|
t.string :type
|
35
37
|
t.references :account
|
36
38
|
t.references :transaction
|
37
39
|
t.references :context, polymorphic: true
|
38
|
-
t.references :initiator, polymorphic: true
|
39
40
|
t.references :accountee, polymorphic: true
|
40
41
|
|
41
42
|
t.integer :amount_cents, limit: 8, default: 0, null: false
|
@@ -43,8 +44,6 @@ ActiveRecord::Migration.verbose = false
|
|
43
44
|
end
|
44
45
|
add_index :double_double_amounts, :context_id
|
45
46
|
add_index :double_double_amounts, :context_type
|
46
|
-
add_index :double_double_amounts, :initiator_id
|
47
|
-
add_index :double_double_amounts, :initiator_type
|
48
47
|
add_index :double_double_amounts, :accountee_id
|
49
48
|
add_index :double_double_amounts, :accountee_type
|
50
49
|
add_index :double_double_amounts, :type
|
@@ -1,124 +1,128 @@
|
|
1
1
|
# Liability, Equity, and Revenue account types
|
2
2
|
shared_examples "a normal credit account type" do
|
3
3
|
describe "<<" do
|
4
|
+
|
5
|
+
describe "basic behavior" do
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
@acct1 = FactoryGirl.create(normal_credit_account_type, name: 'acct1')
|
9
|
+
@acct2_contra = FactoryGirl.create(normal_credit_account_type, name: 'acct2_contra', :contra => true)
|
10
|
+
@other_account = FactoryGirl.create("not_#{normal_credit_account_type}".to_sym, name: 'other_account')
|
11
|
+
end
|
4
12
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
contra_account.balance.should < 0
|
14
|
-
end
|
13
|
+
it "should report a NEGATIVE balance when an account is debited" do
|
14
|
+
DoubleDouble::Transaction.create!(
|
15
|
+
description: 'Sold some widgets',
|
16
|
+
debits: [{account: 'acct1', amount: Money.new(75)}],
|
17
|
+
credits: [{account: 'acct2_contra', amount: Money.new(75)}])
|
18
|
+
@acct1.balance.should < 0
|
19
|
+
@acct2_contra.balance.should < 0
|
20
|
+
end
|
15
21
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
contra_account.balance.should > 0
|
25
|
-
end
|
22
|
+
it "should report a POSITIVE balance when an account is credited" do
|
23
|
+
DoubleDouble::Transaction.create!(
|
24
|
+
description: 'Sold some widgets',
|
25
|
+
debits: [{account: 'acct2_contra', amount: Money.new(75)}],
|
26
|
+
credits: [{account: 'acct1', amount: Money.new(75)}])
|
27
|
+
@acct1.balance.should > 0
|
28
|
+
@acct2_contra.balance.should > 0
|
29
|
+
end
|
26
30
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
DoubleDouble.const_get(normal_credit_account_type.to_s.capitalize).balance.should be_kind_of(Money)
|
38
|
-
end
|
31
|
+
it "should report a POSITIVE balance across the account type when CREDITED
|
32
|
+
and using an unrelated type for the balanced side transaction" do
|
33
|
+
DoubleDouble::Transaction.create!(
|
34
|
+
description: 'Sold some widgets',
|
35
|
+
debits: [{account: 'other_account', amount: Money.new(50)}],
|
36
|
+
credits: [{account: 'acct1', amount: Money.new(50)}])
|
37
|
+
DoubleDouble.const_get(normal_credit_account_type.to_s.capitalize).should respond_to(:balance)
|
38
|
+
DoubleDouble.const_get(normal_credit_account_type.to_s.capitalize).balance.should > 0
|
39
|
+
DoubleDouble.const_get(normal_credit_account_type.to_s.capitalize).balance.should be_kind_of(Money)
|
40
|
+
end
|
39
41
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
DoubleDouble.const_get(normal_credit_account_type.to_s.capitalize).balance.should be_kind_of(Money)
|
42
|
+
it "should report a NEGATIVE balance across the account type when DEBITED
|
43
|
+
and using an unrelated type for the balanced side transaction" do
|
44
|
+
DoubleDouble::Transaction.create!(
|
45
|
+
description: 'Sold some widgets',
|
46
|
+
debits: [{account: 'acct1', amount: Money.new(50)}],
|
47
|
+
credits: [{account: 'other_account', amount: Money.new(50)}])
|
48
|
+
DoubleDouble.const_get(normal_credit_account_type.to_s.capitalize).should respond_to(:balance)
|
49
|
+
DoubleDouble.const_get(normal_credit_account_type.to_s.capitalize).balance.should < 0
|
50
|
+
DoubleDouble.const_get(normal_credit_account_type.to_s.capitalize).balance.should be_kind_of(Money)
|
51
|
+
end
|
51
52
|
end
|
52
53
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
54
|
+
describe "context" do
|
55
|
+
|
56
|
+
it "should return the balance with respect to context is supplied" do
|
57
|
+
acct1 = FactoryGirl.create(normal_credit_account_type, name: 'acct1')
|
58
|
+
acct2 = FactoryGirl.create(normal_credit_account_type, name: 'acct2')
|
59
|
+
other_account = FactoryGirl.create("not_#{normal_credit_account_type}".to_sym, name: 'other_account')
|
60
|
+
a1 = rand(1_000_000_000)
|
61
|
+
a2 = rand(1_000_000_000)
|
62
|
+
a3 = rand(1_000_000_000)
|
63
|
+
a4 = rand(1_000_000_000)
|
64
|
+
@project1 = FactoryGirl.create(normal_credit_account_type)
|
65
|
+
@invoice555 = FactoryGirl.create(normal_credit_account_type)
|
63
66
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
67
|
+
DoubleDouble::Transaction.create!(
|
68
|
+
description: 'Sold some widgets',
|
69
|
+
debits: [{account: 'other_account', amount: Money.new(a1)}],
|
70
|
+
credits: [{account: 'acct1', amount: Money.new(a1), context: @project1}])
|
71
|
+
DoubleDouble::Transaction.create!(
|
72
|
+
description: 'Sold something',
|
73
|
+
debits: [{account: 'other_account', amount: Money.new(a2)}],
|
74
|
+
credits: [{account: 'acct1', amount: Money.new(a2), context: @project1}])
|
75
|
+
DoubleDouble::Transaction.create!(
|
76
|
+
description: 'Sold something',
|
77
|
+
debits: [{account: 'other_account', amount: Money.new(a3)}],
|
78
|
+
credits: [{account: 'acct1', amount: Money.new(a3), context: @invoice555}])
|
79
|
+
DoubleDouble::Transaction.create!(
|
80
|
+
description: 'Sold something',
|
81
|
+
debits: [{account: 'other_account', amount: Money.new(a3)}],
|
82
|
+
credits: [{account: 'acct1', amount: Money.new(a3)}])
|
80
83
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
84
|
+
DoubleDouble::Transaction.create!(
|
85
|
+
description: 'Sold something',
|
86
|
+
debits: [{account: 'acct1', amount: Money.new(a4), context: @project1}],
|
87
|
+
credits: [{account: 'other_account', amount: Money.new(a4)}])
|
88
|
+
DoubleDouble::Transaction.create!(
|
89
|
+
description: 'Sold something',
|
90
|
+
debits: [{account: 'acct1', amount: Money.new(a2), context: @project1}],
|
91
|
+
credits: [{account: 'other_account', amount: Money.new(a2)}])
|
92
|
+
DoubleDouble::Transaction.create!(
|
93
|
+
description: 'Sold something',
|
94
|
+
debits: [{account: 'acct1', amount: Money.new(a3), context: @invoice555}],
|
95
|
+
credits: [{account: 'other_account', amount: Money.new(a3)}])
|
96
|
+
DoubleDouble::Transaction.create!(
|
97
|
+
description: 'Sold something',
|
98
|
+
debits: [{account: 'acct1', amount: Money.new(a3)}],
|
99
|
+
credits: [{account: 'other_account', amount: Money.new(a3)}])
|
97
100
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
101
|
+
DoubleDouble::Transaction.create!(
|
102
|
+
description: 'Sold something',
|
103
|
+
debits: [{account: 'acct2', amount: Money.new(a4), context: @project1}],
|
104
|
+
credits: [{account: 'other_account', amount: Money.new(a4)}])
|
105
|
+
DoubleDouble::Transaction.create!(
|
106
|
+
description: 'Sold something',
|
107
|
+
debits: [{account: 'acct2', amount: Money.new(a2), context: @project1}],
|
108
|
+
credits: [{account: 'other_account', amount: Money.new(a2)}])
|
109
|
+
DoubleDouble::Transaction.create!(
|
110
|
+
description: 'Sold something',
|
111
|
+
debits: [{account: 'acct2', amount: Money.new(a3), context: @invoice555}],
|
112
|
+
credits: [{account: 'other_account', amount: Money.new(a3)}])
|
113
|
+
DoubleDouble::Transaction.create!(
|
114
|
+
description: 'Sold something',
|
115
|
+
debits: [{account: 'acct2', amount: Money.new(a3)}],
|
116
|
+
credits: [{account: 'other_account', amount: Money.new(a3)}])
|
114
117
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
118
|
+
acct1.balance({context: @project1}).should == Money.new((a1 + a2) - (a4 + a2))
|
119
|
+
acct1.balance({context: @invoice555}).should == Money.new(a3 - a3)
|
120
|
+
acct1.balance.should == Money.new((a1 + a2 + a3 + a3) - (a4 + a2 + a3 + a3))
|
121
|
+
|
122
|
+
acct2.balance({context: @project1}).should == Money.new(- (a4 + a2))
|
123
|
+
acct2.balance({context: @invoice555}).should == Money.new(- a3)
|
124
|
+
acct2.balance.should == Money.new(- (a4 + a2 + a3 + a3))
|
125
|
+
end
|
122
126
|
end
|
123
127
|
end
|
124
128
|
end
|
@@ -1,124 +1,128 @@
|
|
1
1
|
# Asset and Expense account types
|
2
2
|
shared_examples "a normal debit account type" do
|
3
3
|
describe "<<" do
|
4
|
+
|
5
|
+
describe "basic behavior" do
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
@acct1 = FactoryGirl.create(normal_debit_account_type, name: 'acct1')
|
9
|
+
@acct2_contra = FactoryGirl.create(normal_debit_account_type, name: 'acct2_contra', :contra => true)
|
10
|
+
@other_account = FactoryGirl.create("not_#{normal_debit_account_type}".to_sym, name: 'other_account')
|
11
|
+
end
|
4
12
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
contra_account.balance.should > 0
|
14
|
-
end
|
13
|
+
it "should report a POSITIVE balance when an account is DEBITED" do
|
14
|
+
DoubleDouble::Transaction.create!(
|
15
|
+
description: 'Sold some widgets',
|
16
|
+
debits: [{account: 'acct1', amount: Money.new(75)}],
|
17
|
+
credits: [{account: 'acct2_contra', amount: Money.new(75)}])
|
18
|
+
@acct1.balance.should > 0
|
19
|
+
@acct2_contra.balance.should > 0
|
20
|
+
end
|
15
21
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
contra_account.balance.should < 0
|
25
|
-
end
|
22
|
+
it "should report a NEGATIVE balance when an account is CREDITED" do
|
23
|
+
DoubleDouble::Transaction.create!(
|
24
|
+
description: 'Sold some widgets',
|
25
|
+
debits: [{account: 'acct2_contra', amount: Money.new(75)}],
|
26
|
+
credits: [{account: 'acct1', amount: Money.new(75)}])
|
27
|
+
@acct1.balance.should < 0
|
28
|
+
@acct2_contra.balance.should < 0
|
29
|
+
end
|
26
30
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
DoubleDouble.const_get(normal_debit_account_type.to_s.capitalize).balance.should be_kind_of(Money)
|
38
|
-
end
|
31
|
+
it "should report a NEGATIVE balance across the account type when CREDITED
|
32
|
+
and using an unrelated type for the balanced side transaction" do
|
33
|
+
DoubleDouble::Transaction.create!(
|
34
|
+
description: 'Sold some widgets',
|
35
|
+
debits: [{account: 'other_account', amount: Money.new(50)}],
|
36
|
+
credits: [{account: 'acct1', amount: Money.new(50)}])
|
37
|
+
DoubleDouble.const_get(normal_debit_account_type.to_s.capitalize).should respond_to(:balance)
|
38
|
+
DoubleDouble.const_get(normal_debit_account_type.to_s.capitalize).balance.should < 0
|
39
|
+
DoubleDouble.const_get(normal_debit_account_type.to_s.capitalize).balance.should be_kind_of(Money)
|
40
|
+
end
|
39
41
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
DoubleDouble.const_get(normal_debit_account_type.to_s.capitalize).balance.should be_kind_of(Money)
|
42
|
+
it "should report a POSITIVE balance across the account type when DEBITED
|
43
|
+
and using an unrelated type for the balanced side transaction" do
|
44
|
+
DoubleDouble::Transaction.create!(
|
45
|
+
description: 'Sold some widgets',
|
46
|
+
debits: [{account: 'acct1', amount: Money.new(50)}],
|
47
|
+
credits: [{account: 'other_account', amount: Money.new(50)}])
|
48
|
+
DoubleDouble.const_get(normal_debit_account_type.to_s.capitalize).should respond_to(:balance)
|
49
|
+
DoubleDouble.const_get(normal_debit_account_type.to_s.capitalize).balance.should > 0
|
50
|
+
DoubleDouble.const_get(normal_debit_account_type.to_s.capitalize).balance.should be_kind_of(Money)
|
51
|
+
end
|
51
52
|
end
|
52
53
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
54
|
+
describe "context" do
|
55
|
+
|
56
|
+
it "should return the balance with respect to context is supplied" do
|
57
|
+
acct1 = FactoryGirl.create(normal_debit_account_type, name: 'acct1')
|
58
|
+
acct2 = FactoryGirl.create(normal_debit_account_type, name: 'acct2')
|
59
|
+
other_account = FactoryGirl.create("not_#{normal_debit_account_type}".to_sym, name: 'other_account')
|
60
|
+
a1 = rand(1_000_000_000)
|
61
|
+
a2 = rand(1_000_000_000)
|
62
|
+
a3 = rand(1_000_000_000)
|
63
|
+
a4 = rand(1_000_000_000)
|
64
|
+
@project1 = FactoryGirl.create(normal_debit_account_type)
|
65
|
+
@invoice555 = FactoryGirl.create(normal_debit_account_type)
|
63
66
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
67
|
+
DoubleDouble::Transaction.create!(
|
68
|
+
description: 'Sold some widgets',
|
69
|
+
debits: [{account: 'other_account', amount: Money.new(a1)}],
|
70
|
+
credits: [{account: 'acct1', amount: Money.new(a1), context: @project1}])
|
71
|
+
DoubleDouble::Transaction.create!(
|
72
|
+
description: 'Sold something',
|
73
|
+
debits: [{account: 'other_account', amount: Money.new(a2)}],
|
74
|
+
credits: [{account: 'acct1', amount: Money.new(a2), context: @project1}])
|
75
|
+
DoubleDouble::Transaction.create!(
|
76
|
+
description: 'Sold something',
|
77
|
+
debits: [{account: 'other_account', amount: Money.new(a3)}],
|
78
|
+
credits: [{account: 'acct1', amount: Money.new(a3), context: @invoice555}])
|
79
|
+
DoubleDouble::Transaction.create!(
|
80
|
+
description: 'Sold something',
|
81
|
+
debits: [{account: 'other_account', amount: Money.new(a3)}],
|
82
|
+
credits: [{account: 'acct1', amount: Money.new(a3)}])
|
80
83
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
84
|
+
DoubleDouble::Transaction.create!(
|
85
|
+
description: 'Sold something',
|
86
|
+
debits: [{account: 'acct1', amount: Money.new(a4), context: @project1}],
|
87
|
+
credits: [{account: 'other_account', amount: Money.new(a4)}])
|
88
|
+
DoubleDouble::Transaction.create!(
|
89
|
+
description: 'Sold something',
|
90
|
+
debits: [{account: 'acct1', amount: Money.new(a2), context: @project1}],
|
91
|
+
credits: [{account: 'other_account', amount: Money.new(a2)}])
|
92
|
+
DoubleDouble::Transaction.create!(
|
93
|
+
description: 'Sold something',
|
94
|
+
debits: [{account: 'acct1', amount: Money.new(a3), context: @invoice555}],
|
95
|
+
credits: [{account: 'other_account', amount: Money.new(a3)}])
|
96
|
+
DoubleDouble::Transaction.create!(
|
97
|
+
description: 'Sold something',
|
98
|
+
debits: [{account: 'acct1', amount: Money.new(a3)}],
|
99
|
+
credits: [{account: 'other_account', amount: Money.new(a3)}])
|
97
100
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
101
|
+
DoubleDouble::Transaction.create!(
|
102
|
+
description: 'Sold something',
|
103
|
+
debits: [{account: 'acct2', amount: Money.new(a4), context: @project1}],
|
104
|
+
credits: [{account: 'other_account', amount: Money.new(a4)}])
|
105
|
+
DoubleDouble::Transaction.create!(
|
106
|
+
description: 'Sold something',
|
107
|
+
debits: [{account: 'acct2', amount: Money.new(a2), context: @project1}],
|
108
|
+
credits: [{account: 'other_account', amount: Money.new(a2)}])
|
109
|
+
DoubleDouble::Transaction.create!(
|
110
|
+
description: 'Sold something',
|
111
|
+
debits: [{account: 'acct2', amount: Money.new(a3), context: @invoice555}],
|
112
|
+
credits: [{account: 'other_account', amount: Money.new(a3)}])
|
113
|
+
DoubleDouble::Transaction.create!(
|
114
|
+
description: 'Sold something',
|
115
|
+
debits: [{account: 'acct2', amount: Money.new(a3)}],
|
116
|
+
credits: [{account: 'other_account', amount: Money.new(a3)}])
|
114
117
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
118
|
+
acct1.balance({context: @project1}).should == Money.new((a4 + a2) - (a1 + a2))
|
119
|
+
acct1.balance({context: @invoice555}).should == Money.new(a3 - a3)
|
120
|
+
acct1.balance.should == Money.new((a4 + a2 + a3 + a3) - (a1 + a2 + a3 + a3))
|
121
|
+
|
122
|
+
acct2.balance({context: @project1}).should == Money.new((a4 + a2))
|
123
|
+
acct2.balance({context: @invoice555}).should == Money.new(a3)
|
124
|
+
acct2.balance.should == Money.new((a4 + a2 + a3 + a3))
|
125
|
+
end
|
122
126
|
end
|
123
127
|
end
|
124
128
|
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
shared_examples "it can run the README scenarios" do
|
2
|
+
describe "<<" do
|
3
|
+
|
4
|
+
it 'should perform BASIC SCENARIO A correctly' do
|
5
|
+
DoubleDouble::Asset.create! name:'Cash', number: 11
|
6
|
+
DoubleDouble::Liability.create! name:'Grandpa Loan', number: 12
|
7
|
+
DoubleDouble::Expense.create! name:'Spending', number: 13
|
8
|
+
# 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'
|
9
|
+
DoubleDouble::Transaction.create!(
|
10
|
+
description:
|
11
|
+
'We received a loan from Grandpa',
|
12
|
+
debits:[
|
13
|
+
{account: 'Cash', amount: '$800'}],
|
14
|
+
credits:[
|
15
|
+
{account: 'Grandpa Loan', amount: '$800'}])
|
16
|
+
# We buy our college textbooks. Luckily we had more than enough.
|
17
|
+
DoubleDouble::Transaction.create!(
|
18
|
+
description:
|
19
|
+
'Purchase textbooks from bookstore',
|
20
|
+
debits:[
|
21
|
+
{account: 'Spending', amount: '$480'}],
|
22
|
+
credits:[
|
23
|
+
{account: 'Cash', amount: '$480'}])
|
24
|
+
# How much cash is left?
|
25
|
+
DoubleDouble::Account.named('Cash').balance.to_s.should eq("320.00")
|
26
|
+
# We deceided that we wanted to return $320 of the loan.
|
27
|
+
DoubleDouble::Transaction.create!(
|
28
|
+
description:
|
29
|
+
'Payed back $320 to Grandpa',
|
30
|
+
debits:[
|
31
|
+
{account: 'Grandpa Loan', amount: '$320'}],
|
32
|
+
credits:[
|
33
|
+
{account: 'Cash', amount: '$320'}])
|
34
|
+
# How much do we still owed Grandpa?
|
35
|
+
DoubleDouble::Account.named('Grandpa Loan').balance.to_s.should eq("480.00")
|
36
|
+
# How much did we spend?
|
37
|
+
DoubleDouble::Account.named('Spending').balance.to_s.should eq("480.00")
|
38
|
+
# How much cash do we have left?
|
39
|
+
DoubleDouble::Account.named('Cash').balance.to_s.should eq("0.00")
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should perform the REALISTIC SCENARIO correctly' do
|
43
|
+
pending "TODO"
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'should perform the COMPLEX SCENARIO correctly' do
|
47
|
+
pending "TODO"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: double_double
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-02-04 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: money
|
@@ -156,7 +156,6 @@ files:
|
|
156
156
|
- spec/factories/account_factory.rb
|
157
157
|
- spec/factories/amount_factory.rb
|
158
158
|
- spec/factories/transaction_factory.rb
|
159
|
-
- spec/factories/transaction_type_factory.rb
|
160
159
|
- spec/models/account_spec.rb
|
161
160
|
- spec/models/amount_spec.rb
|
162
161
|
- spec/models/asset_spec.rb
|
@@ -167,10 +166,12 @@ files:
|
|
167
166
|
- spec/models/liability_spec.rb
|
168
167
|
- spec/models/revenue_spec.rb
|
169
168
|
- spec/models/transaction_spec.rb
|
169
|
+
- spec/models/transaction_type_spec.rb
|
170
170
|
- spec/spec_helper.rb
|
171
171
|
- spec/support/all_account_types.rb
|
172
172
|
- spec/support/normal_credit_account_types.rb
|
173
173
|
- spec/support/normal_debit_account_types.rb
|
174
|
+
- spec/support/readme_scenarios.rb
|
174
175
|
homepage: https://github.com/crftr/double_double
|
175
176
|
licenses: []
|
176
177
|
post_install_message:
|
@@ -182,7 +183,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
182
183
|
requirements:
|
183
184
|
- - ! '>='
|
184
185
|
- !ruby/object:Gem::Version
|
185
|
-
version:
|
186
|
+
version: 1.9.2
|
186
187
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
187
188
|
none: false
|
188
189
|
requirements:
|
@@ -200,7 +201,6 @@ test_files:
|
|
200
201
|
- spec/factories/account_factory.rb
|
201
202
|
- spec/factories/amount_factory.rb
|
202
203
|
- spec/factories/transaction_factory.rb
|
203
|
-
- spec/factories/transaction_type_factory.rb
|
204
204
|
- spec/models/account_spec.rb
|
205
205
|
- spec/models/amount_spec.rb
|
206
206
|
- spec/models/asset_spec.rb
|
@@ -211,8 +211,10 @@ test_files:
|
|
211
211
|
- spec/models/liability_spec.rb
|
212
212
|
- spec/models/revenue_spec.rb
|
213
213
|
- spec/models/transaction_spec.rb
|
214
|
+
- spec/models/transaction_type_spec.rb
|
214
215
|
- spec/spec_helper.rb
|
215
216
|
- spec/support/all_account_types.rb
|
216
217
|
- spec/support/normal_credit_account_types.rb
|
217
218
|
- spec/support/normal_debit_account_types.rb
|
219
|
+
- spec/support/readme_scenarios.rb
|
218
220
|
has_rdoc:
|
@@ -1,14 +0,0 @@
|
|
1
|
-
FactoryGirl.define do
|
2
|
-
factory :transaction_type, class: DoubleDouble::TransactionType do |type|
|
3
|
-
type.description { FactoryGirl.generate(:transaction_type_description) }
|
4
|
-
type.number { FactoryGirl.generate(:transaction_type_number) }
|
5
|
-
end
|
6
|
-
|
7
|
-
sequence :transaction_type_description do |n|
|
8
|
-
"transaction type description #{n}"
|
9
|
-
end
|
10
|
-
|
11
|
-
sequence :transaction_type_number do |n|
|
12
|
-
9000 + n
|
13
|
-
end
|
14
|
-
end
|