bean_machine 0.0.0.pre1
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +5 -0
- data/LICENSE +20 -0
- data/README.markdown +115 -0
- data/Rakefile +64 -0
- data/VERSION +1 -0
- data/credit_card.dot +9 -0
- data/dsl.rb +44 -0
- data/features/billing.feature +9 -0
- data/features/step_definitions/billing_steps.rb +0 -0
- data/features/support/env.rb +4 -0
- data/lib/bean/machine.rb +30 -0
- data/lib/bean/payment.rb +66 -0
- data/lib/bean/transfer.rb +41 -0
- data/lib/bean/user.rb +25 -0
- data/lib/bean_machine.rb +8 -0
- data/spec/blueprints.rb +35 -0
- data/spec/debug.log +87 -0
- data/spec/payment_spec.rb +70 -0
- data/spec/schema.rb +30 -0
- data/spec/spec_helper.rb +26 -0
- data/spec/transfer_spec.rb +47 -0
- data/spec/user_spec.rb +34 -0
- metadata +188 -0
data/.document
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Josh Robinson
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
## Bean Machine
|
2
|
+
|
3
|
+
Accounting sucks. Well if you are trying to do it right it does. Bean machine gives you the power of an immutable double entry accounting system through the use of a simple transfer method. The idea is to make accounting easy by breaking it into easy to follow steps. Transfer this much from this account to that account.
|
4
|
+
|
5
|
+
### Current Status
|
6
|
+
|
7
|
+
Bean Machine is very rough right now. The basics work but you will need to do some manual work setting up the db and such. It will change and it will break. But that just means it is an active project and will make rapid progress. Until it gets a couple good point releases you probably shouldn't use it in production. I probably will be using it in production to help figure out the best way to take it but you shouldn't. Just don't.
|
8
|
+
|
9
|
+
### Installation
|
10
|
+
|
11
|
+
Bean Machine is hosted on GemCutter ... I mean RubyGems, So just install like a normal gem.
|
12
|
+
|
13
|
+
sudo gem install bean_machine --pre
|
14
|
+
|
15
|
+
### Schema
|
16
|
+
|
17
|
+
Until nice generators are done you can manually make a migration with the following structure. It will change frequently while the kinks are getting worked out.
|
18
|
+
|
19
|
+
create_table :bean_transfers, :force => true do |t|
|
20
|
+
t.references :user
|
21
|
+
t.references :accountable, :polymorphic => true
|
22
|
+
|
23
|
+
t.string :state
|
24
|
+
t.integer :amount
|
25
|
+
t.string :debit
|
26
|
+
t.string :credit
|
27
|
+
t.string :currency, :null => false, :default => 'USD'
|
28
|
+
t.string :event, :null => false, :default => 'transfer'
|
29
|
+
t.boolean :success
|
30
|
+
t.string :reference
|
31
|
+
t.string :message
|
32
|
+
t.text :params
|
33
|
+
t.boolean :test
|
34
|
+
t.boolean :affect_balance
|
35
|
+
end
|
36
|
+
|
37
|
+
### Overview
|
38
|
+
|
39
|
+
Bean Machine is based on the double entry accounting system. Every transfer is moving money from one account to another account. Eventually I will give a nice tutorial on basic accounting principles but it will have to wait.
|
40
|
+
|
41
|
+
### Usage
|
42
|
+
|
43
|
+
An example is worth a thousand words so lets do a quick one.
|
44
|
+
|
45
|
+
class User < ActiveRecord::Base
|
46
|
+
# tell it who has the balances
|
47
|
+
include Bean::User
|
48
|
+
|
49
|
+
has_many :invoices
|
50
|
+
end
|
51
|
+
|
52
|
+
class Invoice < ActiveRecord::Base
|
53
|
+
# this adds the transfer method
|
54
|
+
include Bean::Machine
|
55
|
+
|
56
|
+
belongs_to :user
|
57
|
+
|
58
|
+
# just transfer an amount from a credit account to a debit account.
|
59
|
+
# it doesn't really matter what account you credit and what you
|
60
|
+
# debit as long as you keep it consistant.
|
61
|
+
def pay(amount)
|
62
|
+
transfer amount.to_money, :credit => :payments, :debit => :invoice
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# just make a user
|
67
|
+
@user = User.create
|
68
|
+
|
69
|
+
# give them a couple invoices
|
70
|
+
@invoice = @user.invoices.create
|
71
|
+
|
72
|
+
# pay some amount on the invoice
|
73
|
+
@invoice.pay(10)
|
74
|
+
|
75
|
+
# lookup the balance for a given account
|
76
|
+
@user.balance(:invoice) #=> Money object with value of 1000 cents
|
77
|
+
@user.balance(:invoice).to_s #=> '10.00'
|
78
|
+
|
79
|
+
# pay more on the invoice
|
80
|
+
@invoice.pay(20)
|
81
|
+
|
82
|
+
# and the balance increases
|
83
|
+
@user.balance(:invoice).to_s #=> '30.00'
|
84
|
+
@user.balance(:payments).to_s #=> '30.00'
|
85
|
+
|
86
|
+
|
87
|
+
This example has not been tested yet but is here to give you a basic idea of how it works.
|
88
|
+
|
89
|
+
|
90
|
+
### ToDo
|
91
|
+
|
92
|
+
- Lots and lots
|
93
|
+
- Mixins for common accounting transactions (payment, invoice, etc)
|
94
|
+
- Integrate ActiveMerchant
|
95
|
+
- Pluggable transfer store (noSQL)
|
96
|
+
- ORM independent
|
97
|
+
|
98
|
+
## Note on Patches/Pull Requests
|
99
|
+
|
100
|
+
* Fork the project.
|
101
|
+
* Make your feature addition or bug fix.
|
102
|
+
* Add tests for it. This is important so I don't break it in a
|
103
|
+
future version unintentionally.
|
104
|
+
* Commit, do not mess with rakefile, version, or history.
|
105
|
+
(if you want to have your own version, that is fine but
|
106
|
+
bump version in a commit by itself I can ignore when I pull)
|
107
|
+
* Send me a pull request. Bonus points for topic branches.
|
108
|
+
|
109
|
+
## Sponsored By
|
110
|
+
|
111
|
+
This gem is sponsored by [Teliax][]. [Teliax][] makes business class Voice, [Centrex][](Including Hosted: IVRs, Ring Groups, Extensions and Day Night Mode) and Data services accessible to anyone. You don't have to be a fortune 500 to sound big!
|
112
|
+
|
113
|
+
## Copyright
|
114
|
+
|
115
|
+
Copyright (c) 2010 Josh Robinson . See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "bean_machine"
|
8
|
+
gem.summary = %Q{Kick accounting in the mean bean machine!}
|
9
|
+
gem.description = %Q{Accounting sucks. Well if you are trying to do it right it does. Bean machine gives you the power of an immutable double entry accounting system through the use of a simple transfer method. The idea is to make accounting easy by breaking it into easy to follow steps. Transfer this much from this account to that account.}
|
10
|
+
gem.email = "hexorx@gmail.com"
|
11
|
+
gem.homepage = "http://github.com/hexorx/bean_machine"
|
12
|
+
gem.authors = ["hexorx"]
|
13
|
+
|
14
|
+
gem.add_development_dependency "rspec"
|
15
|
+
gem.add_development_dependency "yard"
|
16
|
+
gem.add_development_dependency "cucumber"
|
17
|
+
gem.add_development_dependency "machinist"
|
18
|
+
gem.add_development_dependency "faker"
|
19
|
+
|
20
|
+
gem.add_dependency "money"
|
21
|
+
gem.add_dependency "activerecord"
|
22
|
+
gem.add_dependency "activemerchant"
|
23
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
24
|
+
end
|
25
|
+
Jeweler::GemcutterTasks.new
|
26
|
+
rescue LoadError
|
27
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
28
|
+
end
|
29
|
+
|
30
|
+
require 'spec/rake/spectask'
|
31
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
32
|
+
spec.libs << 'lib' << 'spec'
|
33
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
34
|
+
end
|
35
|
+
|
36
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
37
|
+
spec.libs << 'lib' << 'spec'
|
38
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
39
|
+
spec.rcov = true
|
40
|
+
end
|
41
|
+
|
42
|
+
task :spec => :check_dependencies
|
43
|
+
|
44
|
+
begin
|
45
|
+
require 'cucumber/rake/task'
|
46
|
+
Cucumber::Rake::Task.new(:features)
|
47
|
+
|
48
|
+
task :features => :check_dependencies
|
49
|
+
rescue LoadError
|
50
|
+
task :features do
|
51
|
+
abort "Cucumber is not available. In order to run features, you must: sudo gem install cucumber"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
task :default => :spec
|
56
|
+
|
57
|
+
begin
|
58
|
+
require 'yard'
|
59
|
+
YARD::Rake::YardocTask.new
|
60
|
+
rescue LoadError
|
61
|
+
task :yardoc do
|
62
|
+
abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
|
63
|
+
end
|
64
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.0.pre1
|
data/credit_card.dot
ADDED
data/dsl.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
class Payment
|
2
|
+
cattr_accessor :gateway
|
3
|
+
|
4
|
+
def charge
|
5
|
+
authorize!(amount).capture!
|
6
|
+
end
|
7
|
+
|
8
|
+
def authorize
|
9
|
+
transfer :from => :payments, :to => :accounts_recievable
|
10
|
+
end
|
11
|
+
|
12
|
+
def capture
|
13
|
+
transfer :accounts_recievable => :cash do
|
14
|
+
response = gateway.capture(amount, authorization, options)
|
15
|
+
record_response(trans, response)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def expire
|
20
|
+
transfer :accounts_recievable => :payment
|
21
|
+
end
|
22
|
+
|
23
|
+
def refund do
|
24
|
+
transfer :cash => :payments
|
25
|
+
end
|
26
|
+
|
27
|
+
protected
|
28
|
+
|
29
|
+
def record_response(trans,response)
|
30
|
+
begin
|
31
|
+
trans.success = response.success?
|
32
|
+
trans.reference = response.authorization
|
33
|
+
trans.message = response.message
|
34
|
+
trans.params = response.params
|
35
|
+
trans.test = response.test?
|
36
|
+
rescue ActiveMerchant::ActiveMerchantError => e
|
37
|
+
trans.success = false
|
38
|
+
trans.reference = nil
|
39
|
+
trans.message = e.message
|
40
|
+
trans.params = {}
|
41
|
+
trans.test = gateway.test?
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
File without changes
|
data/lib/bean/machine.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
module Bean
|
2
|
+
module Machine
|
3
|
+
def self.included(base)
|
4
|
+
base.class_eval do
|
5
|
+
include InstanceMethods
|
6
|
+
has_many :transfers, :as => :accountable, :class_name => 'Bean::Transfer', :order => 'id DESC'
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
module InstanceMethods
|
11
|
+
def bean
|
12
|
+
transfers.stateful.first
|
13
|
+
end
|
14
|
+
|
15
|
+
def transfer(*args,&block)
|
16
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
17
|
+
amount = args.shift.to_money
|
18
|
+
|
19
|
+
options.reverse_merge!({
|
20
|
+
:amount => amount
|
21
|
+
})
|
22
|
+
|
23
|
+
xfer = self.transfers.build(options)
|
24
|
+
xfer.instance_eval(&block)
|
25
|
+
xfer.save
|
26
|
+
xfer
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/bean/payment.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
module Bean
|
2
|
+
class Payment < ActiveRecord::Base
|
3
|
+
include Bean::Machine
|
4
|
+
|
5
|
+
cattr_accessor :gateway
|
6
|
+
|
7
|
+
composed_of :amount, :class_name => 'Money', :mapping => [%w(amount cents), %w(currency currency)]
|
8
|
+
|
9
|
+
attr_accessor :credit_card
|
10
|
+
|
11
|
+
def authorize(amount=amount, options = {})
|
12
|
+
return false if bean
|
13
|
+
card = options.delete(:on) || (credit_card rescue nil)
|
14
|
+
return false unless card
|
15
|
+
|
16
|
+
process(amount,{
|
17
|
+
:event => 'authorize',
|
18
|
+
:state => 'authorized',
|
19
|
+
:credit => 'payment',
|
20
|
+
:debit =>'pending'
|
21
|
+
}) do |gw|
|
22
|
+
gw.authorize(amount, card, options)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def capture(options = {})
|
27
|
+
return false unless bean.state == 'authorized'
|
28
|
+
|
29
|
+
process(amount,{
|
30
|
+
:event => 'capture',
|
31
|
+
:state => 'captured',
|
32
|
+
:credit => 'pending',
|
33
|
+
:debit =>'cash'
|
34
|
+
}) do |gw|
|
35
|
+
gw.capture(amount, bean.reference, options)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def process(*args)
|
42
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
43
|
+
amount = args.shift.to_money
|
44
|
+
|
45
|
+
transfer(amount, options) do
|
46
|
+
begin
|
47
|
+
response = yield Payment.gateway
|
48
|
+
|
49
|
+
self.success = response.success?
|
50
|
+
self.reference = response.authorization
|
51
|
+
self.message = response.message
|
52
|
+
self.params = response.params
|
53
|
+
self.test = response.test
|
54
|
+
rescue ActiveMerchant::ActiveMerchantError => e
|
55
|
+
self.state = 'declined'
|
56
|
+
self.success = false
|
57
|
+
self.reference = nil
|
58
|
+
self.message = e.message
|
59
|
+
self.params = {}
|
60
|
+
self.test = gateway.test?
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Bean
|
2
|
+
Accounts = [:asset, :liability, :expense, :income, :payment, :currency]
|
3
|
+
|
4
|
+
class Transfer < ActiveRecord::Base
|
5
|
+
set_table_name :bean_transfers
|
6
|
+
belongs_to :accountable, :polymorphic => true
|
7
|
+
|
8
|
+
# before_validation :normalize_accounts
|
9
|
+
|
10
|
+
composed_of :amount, :class_name => 'Money', :mapping => [%w(amount cents), %w(currency currency)]
|
11
|
+
|
12
|
+
named_scope :debiting, lambda {|account|
|
13
|
+
{:conditions => {:debit => account.to_s.downcase}}
|
14
|
+
}
|
15
|
+
|
16
|
+
named_scope :crediting, lambda {|account|
|
17
|
+
{:conditions => {:credit => account.to_s.downcase}}
|
18
|
+
}
|
19
|
+
|
20
|
+
named_scope :in_currency, lambda {|currency|
|
21
|
+
{:conditions => {:currency => currency.to_s.upcase}}
|
22
|
+
}
|
23
|
+
|
24
|
+
named_scope :stateful, :conditions => 'state is not NULL'
|
25
|
+
|
26
|
+
def before_validation_with_normalizing
|
27
|
+
self[:debit].downcase!
|
28
|
+
self[:credit].downcase!
|
29
|
+
self[:currency].upcase!
|
30
|
+
before_validation_without_normalizing
|
31
|
+
end
|
32
|
+
alias_method_chain :before_validation, :normalizing
|
33
|
+
|
34
|
+
protected
|
35
|
+
|
36
|
+
def validate
|
37
|
+
errors.add(:amount, 'must be positive') unless self.amount > Money.new(0,self.currency)
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
data/lib/bean/user.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
module Bean
|
2
|
+
module User
|
3
|
+
def self.included(base)
|
4
|
+
base.class_eval do
|
5
|
+
include InstanceMethods
|
6
|
+
has_many :transfers, :class_name => 'Bean::Transfer'
|
7
|
+
end
|
8
|
+
|
9
|
+
Bean::Transfer.class_eval do
|
10
|
+
belongs_to :user, :class_name => base.name
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
module InstanceMethods
|
15
|
+
def balance(account='asset',currency='USD')
|
16
|
+
debits = transfers.debiting(account).in_currency(currency).sum(:amount)
|
17
|
+
credits = transfers.crediting(account).in_currency(currency).sum(:amount)
|
18
|
+
Money.new(debits - credits, currency.to_s)
|
19
|
+
end
|
20
|
+
|
21
|
+
def credit_card
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/bean_machine.rb
ADDED
data/spec/blueprints.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'machinist/active_record'
|
2
|
+
require 'machinist/object'
|
3
|
+
require 'sham'
|
4
|
+
require 'faker'
|
5
|
+
|
6
|
+
class User < ActiveRecord::Base
|
7
|
+
include Bean::User
|
8
|
+
end
|
9
|
+
|
10
|
+
User.blueprint do
|
11
|
+
end
|
12
|
+
|
13
|
+
Bean::Payment.blueprint do
|
14
|
+
amount {}
|
15
|
+
credit_card { ActiveMerchant::Billing::CreditCard.make }
|
16
|
+
end
|
17
|
+
|
18
|
+
Bean::Transfer.blueprint do
|
19
|
+
amount {Money.new(rand(100) + 1)}
|
20
|
+
debit {'asset'}
|
21
|
+
credit {'expense'}
|
22
|
+
end
|
23
|
+
|
24
|
+
ActiveMerchant::Billing::CreditCard.blueprint do
|
25
|
+
number {'1'}
|
26
|
+
month {'8'}
|
27
|
+
year {'2009'}
|
28
|
+
first_name {'Tobias'}
|
29
|
+
last_name {'Luetke'}
|
30
|
+
verification_value {'123'}
|
31
|
+
end
|
32
|
+
|
33
|
+
ActiveMerchant::Billing::CreditCard.blueprint(:bad) do
|
34
|
+
number {'2'}
|
35
|
+
end
|
data/spec/debug.log
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
# Logfile created on Tue Mar 16 13:14:32 -0700 2010 by /
|
2
|
+
[4;36;1mSQL (0.3ms)[0m [0;1mselect sqlite_version(*)[0m
|
3
|
+
[4;35;1mSQL (0.2ms)[0m [0m SELECT name
|
4
|
+
FROM sqlite_master
|
5
|
+
WHERE type = 'table' AND NOT name = 'sqlite_sequence'
|
6
|
+
[0m
|
7
|
+
[4;36;1mSQL (0.5ms)[0m [0;1mCREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL) [0m
|
8
|
+
[4;35;1mSQL (0.2ms)[0m [0m SELECT name
|
9
|
+
FROM sqlite_master
|
10
|
+
WHERE type = 'table' AND NOT name = 'sqlite_sequence'
|
11
|
+
[0m
|
12
|
+
[4;36;1mSQL (0.3ms)[0m [0;1mCREATE TABLE "payments" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "amount" integer, "currency" varchar(255) DEFAULT 'USD' NOT NULL, "reference" varchar(255)) [0m
|
13
|
+
[4;35;1mSQL (0.3ms)[0m [0m SELECT name
|
14
|
+
FROM sqlite_master
|
15
|
+
WHERE type = 'table' AND NOT name = 'sqlite_sequence'
|
16
|
+
[0m
|
17
|
+
[4;36;1mSQL (6.8ms)[0m [0;1mCREATE TABLE "bean_transfers" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "user_id" integer, "accountable_id" integer, "accountable_type" varchar(255), "state" varchar(255), "amount" integer, "debit" varchar(255), "credit" varchar(255), "currency" varchar(255) DEFAULT 'USD' NOT NULL, "event" varchar(255) DEFAULT 'transfer' NOT NULL, "success" boolean, "reference" varchar(255), "message" varchar(255), "params" text, "test" boolean, "affect_balance" boolean) [0m
|
18
|
+
[4;35;1mBean::Transfer Load (0.2ms)[0m [0mSELECT * FROM "bean_transfers" [0m
|
19
|
+
[4;36;1mBean::Transfer Create (0.2ms)[0m [0;1mINSERT INTO "bean_transfers" ("reference", "debit", "amount", "credit", "user_id", "event", "success", "affect_balance", "currency", "params", "accountable_type", "message", "test", "accountable_id", "state") VALUES(NULL, 'liability', 41, 'asset', NULL, 'transfer', NULL, NULL, 'MONKEY', NULL, NULL, NULL, NULL, NULL, NULL)[0m
|
20
|
+
[4;35;1mBean::Transfer Load (0.7ms)[0m [0mSELECT * FROM "bean_transfers" WHERE ("bean_transfers"."id" = 1) [0m
|
21
|
+
[4;36;1mBean::Transfer Create (0.2ms)[0m [0;1mINSERT INTO "bean_transfers" ("reference", "debit", "amount", "credit", "user_id", "event", "success", "affect_balance", "currency", "params", "accountable_type", "message", "test", "accountable_id", "state") VALUES(NULL, 'expense', 31, 'asset', NULL, 'transfer', NULL, NULL, 'USD', NULL, NULL, NULL, NULL, NULL, NULL)[0m
|
22
|
+
[4;35;1mBean::Transfer Load (0.4ms)[0m [0mSELECT * FROM "bean_transfers" WHERE ("bean_transfers"."id" = 2) [0m
|
23
|
+
[4;36;1mBean::Transfer Create (0.2ms)[0m [0;1mINSERT INTO "bean_transfers" ("reference", "debit", "amount", "credit", "user_id", "event", "success", "affect_balance", "currency", "params", "accountable_type", "message", "test", "accountable_id", "state") VALUES(NULL, 'asset', 12, 'payment', NULL, 'transfer', NULL, NULL, 'USD', NULL, NULL, NULL, NULL, NULL, NULL)[0m
|
24
|
+
[4;35;1mBean::Transfer Load (0.4ms)[0m [0mSELECT * FROM "bean_transfers" WHERE ("bean_transfers"."id" = 3) [0m
|
25
|
+
[4;36;1mBean::Transfer Create (0.1ms)[0m [0;1mINSERT INTO "bean_transfers" ("reference", "debit", "amount", "credit", "user_id", "event", "success", "affect_balance", "currency", "params", "accountable_type", "message", "test", "accountable_id", "state") VALUES(NULL, 'expense', 19, 'payment', NULL, 'transfer', NULL, NULL, 'USD', NULL, NULL, NULL, NULL, NULL, NULL)[0m
|
26
|
+
[4;35;1mBean::Transfer Load (0.3ms)[0m [0mSELECT * FROM "bean_transfers" WHERE ("bean_transfers"."id" = 4) [0m
|
27
|
+
[4;36;1mBean::Transfer Create (0.1ms)[0m [0;1mINSERT INTO "bean_transfers" ("reference", "debit", "amount", "credit", "user_id", "event", "success", "affect_balance", "currency", "params", "accountable_type", "message", "test", "accountable_id", "state") VALUES(NULL, 'currency', 60, 'payment', NULL, 'transfer', NULL, NULL, 'USD', NULL, NULL, NULL, NULL, NULL, NULL)[0m
|
28
|
+
[4;35;1mBean::Transfer Load (0.3ms)[0m [0mSELECT * FROM "bean_transfers" WHERE ("bean_transfers"."id" = 5) [0m
|
29
|
+
[4;36;1mBean::Transfer Create (0.1ms)[0m [0;1mINSERT INTO "bean_transfers" ("reference", "debit", "amount", "credit", "user_id", "event", "success", "affect_balance", "currency", "params", "accountable_type", "message", "test", "accountable_id", "state") VALUES(NULL, 'income', 98, 'currency', NULL, 'transfer', NULL, NULL, 'MONKEY', NULL, NULL, NULL, NULL, NULL, NULL)[0m
|
30
|
+
[4;35;1mBean::Transfer Load (0.3ms)[0m [0mSELECT * FROM "bean_transfers" WHERE ("bean_transfers"."id" = 6) [0m
|
31
|
+
[4;36;1mSQL (0.1ms)[0m [0;1mSELECT count(*) AS count_all FROM "bean_transfers" WHERE ("bean_transfers"."debit" = 'asset') [0m
|
32
|
+
[4;35;1mSQL (0.2ms)[0m [0mSELECT count(*) AS count_all FROM "bean_transfers" WHERE ("bean_transfers"."debit" = 'liability') [0m
|
33
|
+
[4;36;1mSQL (0.1ms)[0m [0;1mSELECT count(*) AS count_all FROM "bean_transfers" WHERE ("bean_transfers"."debit" = 'expense') [0m
|
34
|
+
[4;35;1mSQL (0.1ms)[0m [0mSELECT count(*) AS count_all FROM "bean_transfers" WHERE ("bean_transfers"."debit" = 'income') [0m
|
35
|
+
[4;36;1mSQL (0.1ms)[0m [0;1mSELECT count(*) AS count_all FROM "bean_transfers" WHERE ("bean_transfers"."debit" = 'currency') [0m
|
36
|
+
[4;35;1mSQL (0.2ms)[0m [0mSELECT count(*) AS count_all FROM "bean_transfers" WHERE ("bean_transfers"."credit" = 'asset') [0m
|
37
|
+
[4;36;1mSQL (0.2ms)[0m [0;1mSELECT count(*) AS count_all FROM "bean_transfers" WHERE ("bean_transfers"."credit" = 'payment') [0m
|
38
|
+
[4;35;1mSQL (0.2ms)[0m [0mSELECT count(*) AS count_all FROM "bean_transfers" WHERE ("bean_transfers"."credit" = 'currency') [0m
|
39
|
+
[4;36;1mSQL (0.2ms)[0m [0;1mSELECT count(*) AS count_all FROM "bean_transfers" WHERE ("bean_transfers"."currency" = 'USD') [0m
|
40
|
+
[4;35;1mSQL (0.2ms)[0m [0mSELECT count(*) AS count_all FROM "bean_transfers" WHERE ("bean_transfers"."currency" = 'MONKEY') [0m
|
41
|
+
[4;36;1mSQL (0.2ms)[0m [0;1mselect sqlite_version(*)[0m
|
42
|
+
[4;35;1mSQL (0.1ms)[0m [0m SELECT name
|
43
|
+
FROM sqlite_master
|
44
|
+
WHERE type = 'table' AND NOT name = 'sqlite_sequence'
|
45
|
+
[0m
|
46
|
+
[4;36;1mSQL (0.4ms)[0m [0;1mCREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL) [0m
|
47
|
+
[4;35;1mSQL (0.2ms)[0m [0m SELECT name
|
48
|
+
FROM sqlite_master
|
49
|
+
WHERE type = 'table' AND NOT name = 'sqlite_sequence'
|
50
|
+
[0m
|
51
|
+
[4;36;1mSQL (0.2ms)[0m [0;1mCREATE TABLE "payments" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "amount" integer, "currency" varchar(255) DEFAULT 'USD' NOT NULL, "reference" varchar(255)) [0m
|
52
|
+
[4;35;1mSQL (0.2ms)[0m [0m SELECT name
|
53
|
+
FROM sqlite_master
|
54
|
+
WHERE type = 'table' AND NOT name = 'sqlite_sequence'
|
55
|
+
[0m
|
56
|
+
[4;36;1mSQL (0.3ms)[0m [0;1mCREATE TABLE "bean_transfers" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "user_id" integer, "accountable_id" integer, "accountable_type" varchar(255), "state" varchar(255), "amount" integer, "debit" varchar(255), "credit" varchar(255), "currency" varchar(255) DEFAULT 'USD' NOT NULL, "event" varchar(255) DEFAULT 'transfer' NOT NULL, "success" boolean, "reference" varchar(255), "message" varchar(255), "params" text, "test" boolean, "affect_balance" boolean) [0m
|
57
|
+
[4;35;1mUser Create (0.2ms)[0m [0mINSERT INTO users VALUES(NULL)[0m
|
58
|
+
[4;36;1mUser Load (0.4ms)[0m [0;1mSELECT * FROM "users" WHERE ("users"."id" = 1) [0m
|
59
|
+
[4;35;1mBean::Transfer Load (0.1ms)[0m [0mSELECT * FROM "bean_transfers" [0m
|
60
|
+
[4;36;1mBean::Transfer Create (0.2ms)[0m [0;1mINSERT INTO "bean_transfers" ("reference", "debit", "amount", "credit", "user_id", "event", "success", "affect_balance", "currency", "params", "accountable_type", "message", "test", "accountable_id", "state") VALUES(NULL, 'liability', 100, 'asset', 1, 'transfer', NULL, NULL, 'MONKEY', NULL, NULL, NULL, NULL, NULL, NULL)[0m
|
61
|
+
[4;35;1mBean::Transfer Load (0.4ms)[0m [0mSELECT * FROM "bean_transfers" WHERE ("bean_transfers"."id" = 1) [0m
|
62
|
+
[4;36;1mBean::Transfer Create (0.2ms)[0m [0;1mINSERT INTO "bean_transfers" ("reference", "debit", "amount", "credit", "user_id", "event", "success", "affect_balance", "currency", "params", "accountable_type", "message", "test", "accountable_id", "state") VALUES(NULL, 'expense', 200, 'asset', 1, 'transfer', NULL, NULL, 'USD', NULL, NULL, NULL, NULL, NULL, NULL)[0m
|
63
|
+
[4;35;1mBean::Transfer Load (0.3ms)[0m [0mSELECT * FROM "bean_transfers" WHERE ("bean_transfers"."id" = 2) [0m
|
64
|
+
[4;36;1mBean::Transfer Create (0.1ms)[0m [0;1mINSERT INTO "bean_transfers" ("reference", "debit", "amount", "credit", "user_id", "event", "success", "affect_balance", "currency", "params", "accountable_type", "message", "test", "accountable_id", "state") VALUES(NULL, 'asset', 300, 'payment', 1, 'transfer', NULL, NULL, 'USD', NULL, NULL, NULL, NULL, NULL, NULL)[0m
|
65
|
+
[4;35;1mBean::Transfer Load (0.3ms)[0m [0mSELECT * FROM "bean_transfers" WHERE ("bean_transfers"."id" = 3) [0m
|
66
|
+
[4;36;1mBean::Transfer Create (0.1ms)[0m [0;1mINSERT INTO "bean_transfers" ("reference", "debit", "amount", "credit", "user_id", "event", "success", "affect_balance", "currency", "params", "accountable_type", "message", "test", "accountable_id", "state") VALUES(NULL, 'expense', 400, 'payment', 1, 'transfer', NULL, NULL, 'USD', NULL, NULL, NULL, NULL, NULL, NULL)[0m
|
67
|
+
[4;35;1mBean::Transfer Load (0.3ms)[0m [0mSELECT * FROM "bean_transfers" WHERE ("bean_transfers"."id" = 4) [0m
|
68
|
+
[4;36;1mBean::Transfer Create (0.1ms)[0m [0;1mINSERT INTO "bean_transfers" ("reference", "debit", "amount", "credit", "user_id", "event", "success", "affect_balance", "currency", "params", "accountable_type", "message", "test", "accountable_id", "state") VALUES(NULL, 'currency', 500, 'payment', 1, 'transfer', NULL, NULL, 'USD', NULL, NULL, NULL, NULL, NULL, NULL)[0m
|
69
|
+
[4;35;1mBean::Transfer Load (0.3ms)[0m [0mSELECT * FROM "bean_transfers" WHERE ("bean_transfers"."id" = 5) [0m
|
70
|
+
[4;36;1mBean::Transfer Create (0.1ms)[0m [0;1mINSERT INTO "bean_transfers" ("reference", "debit", "amount", "credit", "user_id", "event", "success", "affect_balance", "currency", "params", "accountable_type", "message", "test", "accountable_id", "state") VALUES(NULL, 'income', 600, 'currency', 1, 'transfer', NULL, NULL, 'MONKEY', NULL, NULL, NULL, NULL, NULL, NULL)[0m
|
71
|
+
[4;35;1mBean::Transfer Load (0.3ms)[0m [0mSELECT * FROM "bean_transfers" WHERE ("bean_transfers"."id" = 6) [0m
|
72
|
+
[4;36;1mSQL (0.2ms)[0m [0;1mSELECT sum("bean_transfers".amount) AS sum_amount FROM "bean_transfers" WHERE (((("bean_transfers"."debit" = 'asset' AND "bean_transfers"."currency" = 'USD')) AND ("bean_transfers".user_id = 1)) AND ("bean_transfers".user_id = 1)) [0m
|
73
|
+
[4;35;1mSQL (0.2ms)[0m [0mSELECT sum("bean_transfers".amount) AS sum_amount FROM "bean_transfers" WHERE (((("bean_transfers"."credit" = 'asset' AND "bean_transfers"."currency" = 'USD')) AND ("bean_transfers".user_id = 1)) AND ("bean_transfers".user_id = 1)) [0m
|
74
|
+
[4;36;1mSQL (0.4ms)[0m [0;1mSELECT sum("bean_transfers".amount) AS sum_amount FROM "bean_transfers" WHERE (((("bean_transfers"."debit" = 'asset' AND "bean_transfers"."currency" = 'USD')) AND ("bean_transfers".user_id = 1)) AND ("bean_transfers".user_id = 1)) [0m
|
75
|
+
[4;35;1mSQL (0.2ms)[0m [0mSELECT sum("bean_transfers".amount) AS sum_amount FROM "bean_transfers" WHERE (((("bean_transfers"."credit" = 'asset' AND "bean_transfers"."currency" = 'USD')) AND ("bean_transfers".user_id = 1)) AND ("bean_transfers".user_id = 1)) [0m
|
76
|
+
[4;36;1mSQL (0.3ms)[0m [0;1mSELECT sum("bean_transfers".amount) AS sum_amount FROM "bean_transfers" WHERE (((("bean_transfers"."debit" = 'liability' AND "bean_transfers"."currency" = 'MONKEY')) AND ("bean_transfers".user_id = 1)) AND ("bean_transfers".user_id = 1)) [0m
|
77
|
+
[4;35;1mSQL (0.2ms)[0m [0mSELECT sum("bean_transfers".amount) AS sum_amount FROM "bean_transfers" WHERE (((("bean_transfers"."credit" = 'liability' AND "bean_transfers"."currency" = 'MONKEY')) AND ("bean_transfers".user_id = 1)) AND ("bean_transfers".user_id = 1)) [0m
|
78
|
+
[4;36;1mSQL (0.2ms)[0m [0;1mSELECT sum("bean_transfers".amount) AS sum_amount FROM "bean_transfers" WHERE (((("bean_transfers"."debit" = 'income' AND "bean_transfers"."currency" = 'MONKEY')) AND ("bean_transfers".user_id = 1)) AND ("bean_transfers".user_id = 1)) [0m
|
79
|
+
[4;35;1mSQL (0.2ms)[0m [0mSELECT sum("bean_transfers".amount) AS sum_amount FROM "bean_transfers" WHERE (((("bean_transfers"."credit" = 'income' AND "bean_transfers"."currency" = 'MONKEY')) AND ("bean_transfers".user_id = 1)) AND ("bean_transfers".user_id = 1)) [0m
|
80
|
+
[4;36;1mSQL (0.2ms)[0m [0;1mSELECT sum("bean_transfers".amount) AS sum_amount FROM "bean_transfers" WHERE (((("bean_transfers"."debit" = 'currency' AND "bean_transfers"."currency" = 'MONKEY')) AND ("bean_transfers".user_id = 1)) AND ("bean_transfers".user_id = 1)) [0m
|
81
|
+
[4;35;1mSQL (0.3ms)[0m [0mSELECT sum("bean_transfers".amount) AS sum_amount FROM "bean_transfers" WHERE (((("bean_transfers"."credit" = 'currency' AND "bean_transfers"."currency" = 'MONKEY')) AND ("bean_transfers".user_id = 1)) AND ("bean_transfers".user_id = 1)) [0m
|
82
|
+
[4;36;1mSQL (0.2ms)[0m [0;1mSELECT sum("bean_transfers".amount) AS sum_amount FROM "bean_transfers" WHERE (((("bean_transfers"."debit" = 'currency' AND "bean_transfers"."currency" = 'USD')) AND ("bean_transfers".user_id = 1)) AND ("bean_transfers".user_id = 1)) [0m
|
83
|
+
[4;35;1mSQL (0.2ms)[0m [0mSELECT sum("bean_transfers".amount) AS sum_amount FROM "bean_transfers" WHERE (((("bean_transfers"."credit" = 'currency' AND "bean_transfers"."currency" = 'USD')) AND ("bean_transfers".user_id = 1)) AND ("bean_transfers".user_id = 1)) [0m
|
84
|
+
[4;36;1mSQL (0.2ms)[0m [0;1mSELECT sum("bean_transfers".amount) AS sum_amount FROM "bean_transfers" WHERE (((("bean_transfers"."debit" = 'expense' AND "bean_transfers"."currency" = 'USD')) AND ("bean_transfers".user_id = 1)) AND ("bean_transfers".user_id = 1)) [0m
|
85
|
+
[4;35;1mSQL (0.2ms)[0m [0mSELECT sum("bean_transfers".amount) AS sum_amount FROM "bean_transfers" WHERE (((("bean_transfers"."credit" = 'expense' AND "bean_transfers"."currency" = 'USD')) AND ("bean_transfers".user_id = 1)) AND ("bean_transfers".user_id = 1)) [0m
|
86
|
+
[4;36;1mSQL (0.2ms)[0m [0;1mSELECT sum("bean_transfers".amount) AS sum_amount FROM "bean_transfers" WHERE (((("bean_transfers"."debit" = 'payment' AND "bean_transfers"."currency" = 'USD')) AND ("bean_transfers".user_id = 1)) AND ("bean_transfers".user_id = 1)) [0m
|
87
|
+
[4;35;1mSQL (0.2ms)[0m [0mSELECT sum("bean_transfers".amount) AS sum_amount FROM "bean_transfers" WHERE (((("bean_transfers"."credit" = 'payment' AND "bean_transfers"."currency" = 'USD')) AND ("bean_transfers".user_id = 1)) AND ("bean_transfers".user_id = 1)) [0m
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe Bean::Payment do
|
4
|
+
before(:all) do
|
5
|
+
Bean::Transfer.destroy_all
|
6
|
+
@user = User.make
|
7
|
+
@payment = Bean::Payment.make(:amount => 10.to_money)
|
8
|
+
end
|
9
|
+
|
10
|
+
describe 'authorize' do
|
11
|
+
before(:all) do
|
12
|
+
@transfer = @payment.authorize
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should add a transfer' do
|
16
|
+
@payment.transfers.size.should == 1
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should set the amount on the transfer' do
|
20
|
+
@transfer.amount.should == '$10'.to_money
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should set the event to authorize' do
|
24
|
+
@transfer.event.should == 'authorize'
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should credit the payment account' do
|
28
|
+
@transfer.credit.should == 'payment'
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should debit the authorized account' do
|
32
|
+
@transfer.debit.should == 'pending'
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should fail if there is a state' do
|
36
|
+
@payment.authorize.should == false
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe 'capture' do
|
41
|
+
before(:all) do
|
42
|
+
@authorization = @payment.authorize
|
43
|
+
@transfer = @payment.capture
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'should add a transfer' do
|
47
|
+
@payment.transfers.size.should == 2
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should set the amount on the transfer' do
|
51
|
+
@transfer.amount.should == '$10'.to_money
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'should set the event to authorize' do
|
55
|
+
@transfer.event.should == 'capture'
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should credit the pending account' do
|
59
|
+
@transfer.credit.should == 'pending'
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'should debit the cash account' do
|
63
|
+
@transfer.debit.should == 'cash'
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'should fail unless the state is authorized' do
|
67
|
+
@payment.capture.should == false
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
data/spec/schema.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
ActiveRecord::Migration.verbose = false
|
2
|
+
|
3
|
+
ActiveRecord::Schema.define do
|
4
|
+
create_table :users, :force => true do |t|
|
5
|
+
end
|
6
|
+
|
7
|
+
create_table :payments, :force => true do |t|
|
8
|
+
t.integer :amount
|
9
|
+
t.string :currency, :null => false, :default => 'USD'
|
10
|
+
t.string :reference
|
11
|
+
end
|
12
|
+
|
13
|
+
create_table :bean_transfers, :force => true do |t|
|
14
|
+
t.references :user
|
15
|
+
t.references :accountable, :polymorphic => true
|
16
|
+
|
17
|
+
t.string :state
|
18
|
+
t.integer :amount
|
19
|
+
t.string :debit
|
20
|
+
t.string :credit
|
21
|
+
t.string :currency, :null => false, :default => 'USD'
|
22
|
+
t.string :event, :null => false, :default => 'transfer'
|
23
|
+
t.boolean :success
|
24
|
+
t.string :reference
|
25
|
+
t.string :message
|
26
|
+
t.text :params
|
27
|
+
t.boolean :test
|
28
|
+
t.boolean :affect_balance
|
29
|
+
end
|
30
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
2
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
# require 'sqlite3'
|
6
|
+
require 'active_record'
|
7
|
+
require 'active_merchant'
|
8
|
+
require 'spec'
|
9
|
+
require 'spec/autorun'
|
10
|
+
|
11
|
+
require 'bean_machine'
|
12
|
+
require 'bean/payment'
|
13
|
+
|
14
|
+
ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ':memory:')
|
15
|
+
|
16
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
17
|
+
|
18
|
+
require 'schema'
|
19
|
+
require 'blueprints'
|
20
|
+
|
21
|
+
Bean::Payment.gateway = ActiveMerchant::Billing::BogusGateway.new
|
22
|
+
|
23
|
+
Spec::Runner.configure do |config|
|
24
|
+
config.before(:all) { Sham.reset(:before_all) }
|
25
|
+
config.before(:each) { Sham.reset(:before_each) }
|
26
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe Bean::Transfer do
|
4
|
+
describe 'amount' do
|
5
|
+
it 'should be Money' do
|
6
|
+
Bean::Transfer.make_unsaved.amount.should be_a(Money)
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'should be more than zero' do
|
10
|
+
Bean::Transfer.make_unsaved(:amount => 1.to_money).should be_valid
|
11
|
+
Bean::Transfer.make_unsaved(:amount => 0.to_money).should be_invalid
|
12
|
+
Bean::Transfer.make_unsaved(:amount => -1.to_money).should be_invalid
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe 'scopes' do
|
17
|
+
before(:all) do
|
18
|
+
Bean::Transfer.destroy_all
|
19
|
+
Bean::Transfer.make(:credit => 'asset', :debit => 'liability', :amount => Money.new(rand(100)+1, 'MONKEY'))
|
20
|
+
Bean::Transfer.make(:credit => 'asset', :debit => 'expense', :amount => Money.new(rand(100)+1, 'USD'))
|
21
|
+
Bean::Transfer.make(:credit => 'payment', :debit => 'asset', :amount => Money.new(rand(100)+1, 'USD'))
|
22
|
+
Bean::Transfer.make(:credit => 'payment', :debit => 'expense', :amount => Money.new(rand(100)+1, 'USD'))
|
23
|
+
Bean::Transfer.make(:credit => 'payment', :debit => 'currency', :amount => Money.new(rand(100)+1, 'USd'))
|
24
|
+
Bean::Transfer.make(:credit => 'currency', :debit => 'income', :amount => Money.new(rand(100)+1, 'MONKEY'))
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should scope by debit account' do
|
28
|
+
Bean::Transfer.debiting(:asset).count.should == 1
|
29
|
+
Bean::Transfer.debiting(:liability).count.should == 1
|
30
|
+
Bean::Transfer.debiting(:expense).count.should == 2
|
31
|
+
Bean::Transfer.debiting(:income).count.should == 1
|
32
|
+
Bean::Transfer.debiting(:currency).count.should == 1
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should scope by credit account' do
|
36
|
+
Bean::Transfer.crediting(:asset).count.should == 2
|
37
|
+
Bean::Transfer.crediting(:payment).count.should == 3
|
38
|
+
Bean::Transfer.crediting(:currency).count.should == 1
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'should scope by currency' do
|
42
|
+
Bean::Transfer.in_currency(:USD).count.should == 4
|
43
|
+
Bean::Transfer.in_currency(:MONKEY).count.should == 2
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
data/spec/user_spec.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe Bean::User do
|
4
|
+
describe 'balance' do
|
5
|
+
before(:all) do
|
6
|
+
@user = User.make
|
7
|
+
Bean::Transfer.destroy_all
|
8
|
+
@user.transfers.make(:credit => 'asset', :debit => 'liability', :amount => Money.new(100, 'MONKEY'))
|
9
|
+
@user.transfers.make(:credit => 'asset', :debit => 'expense', :amount => Money.new(200, 'USD'))
|
10
|
+
@user.transfers.make(:credit => 'payment', :debit => 'asset', :amount => Money.new(300, 'USD'))
|
11
|
+
@user.transfers.make(:credit => 'payment', :debit => 'expense', :amount => Money.new(400, 'USD'))
|
12
|
+
@user.transfers.make(:credit => 'payment', :debit => 'currency', :amount => Money.new(500, 'USd'))
|
13
|
+
@user.transfers.make(:credit => 'currency', :debit => 'income', :amount => Money.new(600, 'MONKEY'))
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should return a money object' do
|
17
|
+
@user.balance(:asset).should be_a(Money)
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'should default to assets in USD' do
|
21
|
+
@user.balance.should == Money.new(100,'USD')
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should take the account as the first option and currency as the second' do
|
25
|
+
@user.balance(:liability, :monkey).should == Money.new(100,'MONKEY')
|
26
|
+
@user.balance(:income, :monkey).should == Money.new(600,'MONKEY')
|
27
|
+
@user.balance(:currency, :monkey).should == Money.new(-600,'MONKEY')
|
28
|
+
@user.balance(:currency, :usd).should == Money.new(500,'USD')
|
29
|
+
@user.balance(:expense, :usd).should == Money.new(600,'USD')
|
30
|
+
@user.balance(:payment, :usd).should == Money.new(-1200,'USD')
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
metadata
ADDED
@@ -0,0 +1,188 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: bean_machine
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: true
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- pre1
|
10
|
+
version: 0.0.0.pre1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- hexorx
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-03-20 00:00:00 -06:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: rspec
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 0
|
30
|
+
version: "0"
|
31
|
+
type: :development
|
32
|
+
version_requirements: *id001
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: yard
|
35
|
+
prerelease: false
|
36
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
segments:
|
41
|
+
- 0
|
42
|
+
version: "0"
|
43
|
+
type: :development
|
44
|
+
version_requirements: *id002
|
45
|
+
- !ruby/object:Gem::Dependency
|
46
|
+
name: cucumber
|
47
|
+
prerelease: false
|
48
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
segments:
|
53
|
+
- 0
|
54
|
+
version: "0"
|
55
|
+
type: :development
|
56
|
+
version_requirements: *id003
|
57
|
+
- !ruby/object:Gem::Dependency
|
58
|
+
name: machinist
|
59
|
+
prerelease: false
|
60
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
segments:
|
65
|
+
- 0
|
66
|
+
version: "0"
|
67
|
+
type: :development
|
68
|
+
version_requirements: *id004
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: faker
|
71
|
+
prerelease: false
|
72
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
segments:
|
77
|
+
- 0
|
78
|
+
version: "0"
|
79
|
+
type: :development
|
80
|
+
version_requirements: *id005
|
81
|
+
- !ruby/object:Gem::Dependency
|
82
|
+
name: money
|
83
|
+
prerelease: false
|
84
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
segments:
|
89
|
+
- 0
|
90
|
+
version: "0"
|
91
|
+
type: :runtime
|
92
|
+
version_requirements: *id006
|
93
|
+
- !ruby/object:Gem::Dependency
|
94
|
+
name: activerecord
|
95
|
+
prerelease: false
|
96
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
97
|
+
requirements:
|
98
|
+
- - ">="
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
segments:
|
101
|
+
- 0
|
102
|
+
version: "0"
|
103
|
+
type: :runtime
|
104
|
+
version_requirements: *id007
|
105
|
+
- !ruby/object:Gem::Dependency
|
106
|
+
name: activemerchant
|
107
|
+
prerelease: false
|
108
|
+
requirement: &id008 !ruby/object:Gem::Requirement
|
109
|
+
requirements:
|
110
|
+
- - ">="
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
segments:
|
113
|
+
- 0
|
114
|
+
version: "0"
|
115
|
+
type: :runtime
|
116
|
+
version_requirements: *id008
|
117
|
+
description: Accounting sucks. Well if you are trying to do it right it does. Bean machine gives you the power of an immutable double entry accounting system through the use of a simple transfer method. The idea is to make accounting easy by breaking it into easy to follow steps. Transfer this much from this account to that account.
|
118
|
+
email: hexorx@gmail.com
|
119
|
+
executables: []
|
120
|
+
|
121
|
+
extensions: []
|
122
|
+
|
123
|
+
extra_rdoc_files:
|
124
|
+
- LICENSE
|
125
|
+
- README.markdown
|
126
|
+
files:
|
127
|
+
- .document
|
128
|
+
- .gitignore
|
129
|
+
- LICENSE
|
130
|
+
- README.markdown
|
131
|
+
- Rakefile
|
132
|
+
- VERSION
|
133
|
+
- credit_card.dot
|
134
|
+
- dsl.rb
|
135
|
+
- features/billing.feature
|
136
|
+
- features/step_definitions/billing_steps.rb
|
137
|
+
- features/support/env.rb
|
138
|
+
- lib/bean/machine.rb
|
139
|
+
- lib/bean/payment.rb
|
140
|
+
- lib/bean/transfer.rb
|
141
|
+
- lib/bean/user.rb
|
142
|
+
- lib/bean_machine.rb
|
143
|
+
- spec/blueprints.rb
|
144
|
+
- spec/debug.log
|
145
|
+
- spec/payment_spec.rb
|
146
|
+
- spec/schema.rb
|
147
|
+
- spec/spec_helper.rb
|
148
|
+
- spec/transfer_spec.rb
|
149
|
+
- spec/user_spec.rb
|
150
|
+
has_rdoc: true
|
151
|
+
homepage: http://github.com/hexorx/bean_machine
|
152
|
+
licenses: []
|
153
|
+
|
154
|
+
post_install_message:
|
155
|
+
rdoc_options:
|
156
|
+
- --charset=UTF-8
|
157
|
+
require_paths:
|
158
|
+
- lib
|
159
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
160
|
+
requirements:
|
161
|
+
- - ">="
|
162
|
+
- !ruby/object:Gem::Version
|
163
|
+
segments:
|
164
|
+
- 0
|
165
|
+
version: "0"
|
166
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
167
|
+
requirements:
|
168
|
+
- - ">"
|
169
|
+
- !ruby/object:Gem::Version
|
170
|
+
segments:
|
171
|
+
- 1
|
172
|
+
- 3
|
173
|
+
- 1
|
174
|
+
version: 1.3.1
|
175
|
+
requirements: []
|
176
|
+
|
177
|
+
rubyforge_project:
|
178
|
+
rubygems_version: 1.3.6
|
179
|
+
signing_key:
|
180
|
+
specification_version: 3
|
181
|
+
summary: Kick accounting in the mean bean machine!
|
182
|
+
test_files:
|
183
|
+
- spec/blueprints.rb
|
184
|
+
- spec/payment_spec.rb
|
185
|
+
- spec/schema.rb
|
186
|
+
- spec/spec_helper.rb
|
187
|
+
- spec/transfer_spec.rb
|
188
|
+
- spec/user_spec.rb
|