double_double 0.2.0 → 0.2.3
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 +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
|