double_entry 0.3.1 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +46 -17
- data/lib/double_entry.rb +50 -15
- data/lib/double_entry/line.rb +3 -13
- data/lib/double_entry/reporting.rb +48 -31
- data/lib/double_entry/transfer.rb +4 -12
- data/lib/double_entry/version.rb +1 -1
- data/lib/generators/double_entry/install/templates/migration.rb +0 -1
- data/spec/double_entry/line_spec.rb +0 -24
- data/spec/double_entry_spec.rb +7 -21
- data/spec/support/schema.rb +0 -1
- metadata +2 -2
data/README.md
CHANGED
@@ -31,16 +31,27 @@ Rails Versions: Rails 3.2.x, 4.0.x, 4.1.x
|
|
31
31
|
|
32
32
|
In your application's `Gemfile`, add:
|
33
33
|
|
34
|
-
|
34
|
+
```ruby
|
35
|
+
gem 'double_entry'
|
36
|
+
```
|
35
37
|
|
36
|
-
|
38
|
+
Download and install the gem with Bundler:
|
37
39
|
|
38
|
-
|
39
|
-
|
40
|
+
```sh
|
41
|
+
bundle
|
42
|
+
```
|
40
43
|
|
41
|
-
|
44
|
+
Generate Rails schema migrations for the required tables:
|
42
45
|
|
43
|
-
|
46
|
+
```sh
|
47
|
+
rails generate double_entry:install
|
48
|
+
```
|
49
|
+
|
50
|
+
Update the local database:
|
51
|
+
|
52
|
+
```sh
|
53
|
+
rake db:migrate
|
54
|
+
```
|
44
55
|
|
45
56
|
|
46
57
|
## Interface
|
@@ -91,7 +102,12 @@ will return the current balance for an account as a Money object.
|
|
91
102
|
To transfer money between accounts:
|
92
103
|
|
93
104
|
```ruby
|
94
|
-
DoubleEntry.transfer(
|
105
|
+
DoubleEntry.transfer(
|
106
|
+
20.dollars,
|
107
|
+
:from => one_account,
|
108
|
+
:to => another_account,
|
109
|
+
:code => :a_business_code_for_this_type_of_transfer,
|
110
|
+
)
|
95
111
|
```
|
96
112
|
|
97
113
|
The possible transfers, and their codes, should be defined in the configuration.
|
@@ -107,8 +123,9 @@ manually lock the accounts you're using:
|
|
107
123
|
|
108
124
|
```ruby
|
109
125
|
DoubleEntry.lock_accounts(account_a, account_b) do
|
110
|
-
#
|
126
|
+
# Perhaps transfer some money
|
111
127
|
DoubleEntry.transfer(20.dollars, :from => account_a, :to => account_b, :code => :purchase)
|
128
|
+
# Perform other tasks that should be commited atomically with the transfer of funds...
|
112
129
|
end
|
113
130
|
```
|
114
131
|
|
@@ -214,33 +231,45 @@ correct balances from the lines table.
|
|
214
231
|
|
215
232
|
## Future Direction
|
216
233
|
|
217
|
-
|
234
|
+
See the Github project [issues](https://github.com/envato/double_entry/issues).
|
218
235
|
|
219
236
|
## Development Environment Setup
|
220
237
|
|
221
238
|
1. Clone this repo.
|
222
239
|
|
223
|
-
|
240
|
+
```sh
|
241
|
+
git clone git@github.com:envato/double_entry.git && cd double_entry
|
242
|
+
```
|
224
243
|
|
225
244
|
2. Run the included setup script to install the gem dependencies.
|
226
245
|
|
227
|
-
|
246
|
+
```sh
|
247
|
+
./script/setup.sh
|
248
|
+
```
|
228
249
|
|
229
|
-
3. Install MySQL and PostgreSQL.
|
250
|
+
3. Install MySQL and PostgreSQL. We run tests against both databases.
|
230
251
|
4. Create a database in MySQL.
|
231
252
|
|
232
|
-
|
253
|
+
```sh
|
254
|
+
mysql -u root -e 'create database double_entry_test;'
|
255
|
+
```
|
233
256
|
|
234
257
|
5. Create a database in PostgreSQL.
|
235
258
|
|
236
|
-
|
259
|
+
```sh
|
260
|
+
psql -c 'create database double_entry_test;' -U postgres
|
261
|
+
```
|
237
262
|
|
238
263
|
6. Specify how the tests should connect to the database
|
239
264
|
|
240
|
-
|
241
|
-
|
265
|
+
```sh
|
266
|
+
cp spec/support/{database.example.yml,database.yml}
|
267
|
+
vim spec/support/database.yml
|
268
|
+
```
|
242
269
|
|
243
270
|
7. Run the tests
|
244
271
|
|
245
|
-
|
272
|
+
```sh
|
273
|
+
bundle exec rake
|
274
|
+
```
|
246
275
|
|
data/lib/double_entry.rb
CHANGED
@@ -31,7 +31,7 @@ module DoubleEntry
|
|
31
31
|
#
|
32
32
|
# @example Obtain the 'cash' account for a user
|
33
33
|
# DoubleEntry.account(:cash, scope: user)
|
34
|
-
# @param
|
34
|
+
# @param [Symbol] identifier The symbol identifying the desired account. As
|
35
35
|
# specified in the account configuration.
|
36
36
|
# @option options :scope Limit the account to the given scope. As specified
|
37
37
|
# in the account configuration.
|
@@ -56,12 +56,12 @@ module DoubleEntry
|
|
56
56
|
# checking_account = DoubleEntry.account(:checking, scope: user)
|
57
57
|
# savings_account = DoubleEntry.account(:savings, scope: user)
|
58
58
|
# DoubleEntry.transfer(
|
59
|
-
#
|
60
|
-
# from
|
61
|
-
# to
|
62
|
-
# code
|
59
|
+
# 20.dollars,
|
60
|
+
# :from => checking_account,
|
61
|
+
# :to => savings_account,
|
62
|
+
# :code => :save,
|
63
63
|
# )
|
64
|
-
# @param
|
64
|
+
# @param [Money] amount The quantity of money to transfer from one account
|
65
65
|
# to the other.
|
66
66
|
# @option options :from [DoubleEntry::Account::Instance] Transfer money out
|
67
67
|
# of this account.
|
@@ -69,7 +69,6 @@ module DoubleEntry
|
|
69
69
|
# this account.
|
70
70
|
# @option options :code [Symbol] The application specific code for this
|
71
71
|
# type of transfer. As specified in the transfer configuration.
|
72
|
-
# @option options :meta [String] Metadata to associate with this transfer.
|
73
72
|
# @option options :detail [ActiveRecord::Base] ActiveRecord model
|
74
73
|
# associated (via a polymorphic association) with the transfer.
|
75
74
|
# @raise [DoubleEntry::TransferIsNegative] The amount is less than zero.
|
@@ -82,14 +81,50 @@ module DoubleEntry
|
|
82
81
|
|
83
82
|
# Get the current or historic balance of an account.
|
84
83
|
#
|
85
|
-
# @
|
86
|
-
#
|
87
|
-
#
|
88
|
-
# @
|
89
|
-
#
|
90
|
-
# @
|
91
|
-
#
|
92
|
-
#
|
84
|
+
# @example Obtain the current balance of my checking account
|
85
|
+
# checking_account = DoubleEntry.account(:checking, :scope => user)
|
86
|
+
# DoubleEntry.balance(checking_account)
|
87
|
+
# @example Obtain the current balance of my checking account (without account or user model)
|
88
|
+
# DoubleEntry.balance(:checking, :scope => user_id)
|
89
|
+
# @example Obtain a historic balance of my checking account
|
90
|
+
# checking_account = DoubleEntry.account(:checking, :scope => user)
|
91
|
+
# DoubleEntry.balance(checking_account, :at => Time.new(2012, 1, 1))
|
92
|
+
# @example Obtain the net balance of my checking account during may
|
93
|
+
# checking_account = DoubleEntry.account(:checking, :scope => user)
|
94
|
+
# DoubleEntry.balance(
|
95
|
+
# checking_account,
|
96
|
+
# :from => Time.new(2012, 5, 1, 0, 0, 0),
|
97
|
+
# :to => Time.new(2012, 5, 31, 23, 59, 59),
|
98
|
+
# )
|
99
|
+
# @example Obtain the balance of salary deposits made to my checking account during may
|
100
|
+
# checking_account = DoubleEntry.account(:checking, :scope => user)
|
101
|
+
# DoubleEntry.balance(
|
102
|
+
# checking_account,
|
103
|
+
# :code => :salary,
|
104
|
+
# :from => Time.new(2012, 5, 1, 0, 0, 0),
|
105
|
+
# :to => Time.new(2012, 5, 31, 23, 59, 59),
|
106
|
+
# )
|
107
|
+
# @example Obtain the balance of salary & lottery deposits made to my checking account during may
|
108
|
+
# checking_account = DoubleEntry.account(:checking, :scope => user)
|
109
|
+
# DoubleEntry.balance(
|
110
|
+
# checking_account,
|
111
|
+
# :codes => [ :salary, :lottery ],
|
112
|
+
# :from => Time.new(2012, 5, 1, 0, 0, 0),
|
113
|
+
# :to => Time.new(2012, 5, 31, 23, 59, 59),
|
114
|
+
# )
|
115
|
+
# @param [DoubleEntry::Account:Instance, Symbol] account Find the balance
|
116
|
+
# for this account
|
117
|
+
# @option options :scope [Object] The scope identifier of the account (only
|
118
|
+
# needed if the provided account is a symbol).
|
119
|
+
# @option options :from [Time] used with :to, consider only the time
|
120
|
+
# between these dates
|
121
|
+
# @option options :to [Time] used with :from, consider only the time
|
122
|
+
# between these dates
|
123
|
+
# @option options :at [Time] obtain the account balance at this time
|
124
|
+
# @option options :code [Symbol] consider only the transfers with this code
|
125
|
+
# @option options :codes [Array<Symbol>] consider only the transfers with
|
126
|
+
# these codes
|
127
|
+
# @return [Money] The balance
|
93
128
|
def balance(account, options = {})
|
94
129
|
BalanceCalculator.calculate(account, options)
|
95
130
|
end
|
data/lib/double_entry/line.rb
CHANGED
@@ -72,16 +72,6 @@ module DoubleEntry
|
|
72
72
|
self[:code].try(:to_sym)
|
73
73
|
end
|
74
74
|
|
75
|
-
def meta=(meta)
|
76
|
-
self[:meta] = Marshal.dump(meta)
|
77
|
-
meta
|
78
|
-
end
|
79
|
-
|
80
|
-
def meta
|
81
|
-
meta = self[:meta]
|
82
|
-
meta ? Marshal.load(meta) : {}
|
83
|
-
end
|
84
|
-
|
85
75
|
def account=(account)
|
86
76
|
self[:account] = account.identifier.to_s
|
87
77
|
self.scope = account.scope_identity
|
@@ -107,18 +97,18 @@ module DoubleEntry
|
|
107
97
|
end
|
108
98
|
|
109
99
|
def pair
|
110
|
-
if
|
100
|
+
if decrease?
|
111
101
|
[self, partner]
|
112
102
|
else
|
113
103
|
[partner, self]
|
114
104
|
end
|
115
105
|
end
|
116
106
|
|
117
|
-
def
|
107
|
+
def decrease?
|
118
108
|
amount < Money.empty
|
119
109
|
end
|
120
110
|
|
121
|
-
def
|
111
|
+
def increase?
|
122
112
|
amount > Money.empty
|
123
113
|
end
|
124
114
|
|
@@ -34,28 +34,36 @@ module DoubleEntry
|
|
34
34
|
# and provided custom filters.
|
35
35
|
#
|
36
36
|
# @example Find the sum for all $10 :save transfers in all :checking accounts in the current month (assume the date is January 30, 2014).
|
37
|
-
# time_range = DoubleEntry::TimeRange.make(2014, 1)
|
38
|
-
#
|
37
|
+
# time_range = DoubleEntry::Reporting::TimeRange.make(2014, 1)
|
38
|
+
#
|
39
|
+
# DoubleEntry::Line.class_eval do
|
39
40
|
# scope :ten_dollar_transfers, -> { where(:amount => 10_00) }
|
40
41
|
# end
|
41
|
-
#
|
42
|
-
#
|
42
|
+
#
|
43
|
+
# DoubleEntry::Reporting.aggregate(
|
44
|
+
# :sum,
|
45
|
+
# :checking,
|
46
|
+
# :save,
|
47
|
+
# :range => time_range,
|
48
|
+
# :filter => [ :ten_dollar_transfers ],
|
49
|
+
# )
|
50
|
+
# @param [Symbol] function The function to perform on the set of transfers.
|
43
51
|
# Valid functions are :sum, :count, and :average
|
44
|
-
# @param
|
52
|
+
# @param [Symbol] account The symbol identifying the account to perform
|
45
53
|
# the aggregate calculation on. As specified in the account configuration.
|
46
|
-
# @param
|
54
|
+
# @param [Symbol] code The application specific code for the type of
|
47
55
|
# transfer to perform an aggregate calculation on. As specified in the
|
48
56
|
# transfer configuration.
|
49
|
-
# @option options :range [DoubleEntry::TimeRange] Only include
|
50
|
-
# in the given time range in the calculation.
|
51
|
-
# @option options :filter [Array
|
57
|
+
# @option options :range [DoubleEntry::Reporting::TimeRange] Only include
|
58
|
+
# transfers in the given time range in the calculation.
|
59
|
+
# @option options :filter [Array<Symbol>, Array<Hash<Symbol, Object>>]
|
52
60
|
# A custom filter to apply before performing the aggregate calculation.
|
53
|
-
# Currently, filters must be monkey patched as scopes into the
|
54
|
-
# class in order to be used as filters, as the example
|
55
|
-
# If the filter requires a parameter, it must be given in a Hash,
|
56
|
-
# pass an array with the symbol names for the defined scopes.
|
57
|
-
# @return Returns a Money object for :sum and :average
|
58
|
-
# Fixnum for :count calculations.
|
61
|
+
# Currently, filters must be monkey patched as scopes into the
|
62
|
+
# DoubleEntry::Line class in order to be used as filters, as the example
|
63
|
+
# shows. If the filter requires a parameter, it must be given in a Hash,
|
64
|
+
# otherwise pass an array with the symbol names for the defined scopes.
|
65
|
+
# @return [Money, Fixnum] Returns a Money object for :sum and :average
|
66
|
+
# calculations, or a Fixnum for :count calculations.
|
59
67
|
# @raise [Reporting::AggregateFunctionNotSupported] The provided function
|
60
68
|
# is not supported.
|
61
69
|
#
|
@@ -70,20 +78,27 @@ module DoubleEntry
|
|
70
78
|
# and provided custom filters.
|
71
79
|
#
|
72
80
|
# @example Find the number of all $10 :save transfers in all :checking accounts per month for the entire year (Assume the year is 2014).
|
73
|
-
# DoubleEntry.aggregate_array(
|
74
|
-
#
|
81
|
+
# DoubleEntry::Reporting.aggregate_array(
|
82
|
+
# :sum,
|
83
|
+
# :checking,
|
84
|
+
# :save,
|
85
|
+
# :range_type => 'month',
|
86
|
+
# :start => '2014-01-01',
|
87
|
+
# :finish => '2014-12-31',
|
88
|
+
# )
|
89
|
+
# @param [Symbol] function The function to perform on the set of transfers.
|
75
90
|
# Valid functions are :sum, :count, and :average
|
76
|
-
# @param
|
91
|
+
# @param [Symbol] account The symbol identifying the account to perform
|
77
92
|
# the aggregate calculation on. As specified in the account configuration.
|
78
|
-
# @param
|
93
|
+
# @param [Symbol] code The application specific code for the type of
|
79
94
|
# transfer to perform an aggregate calculation on. As specified in the
|
80
95
|
# transfer configuration.
|
81
|
-
# @option options :filter [Array
|
96
|
+
# @option options :filter [Array<Symbol>, Array<Hash<Symbol, Object>>]
|
82
97
|
# A custom filter to apply before performing the aggregate calculation.
|
83
|
-
# Currently, filters must be monkey patched as scopes into the
|
84
|
-
# class in order to be used as filters, as the example
|
85
|
-
# If the filter requires a parameter, it must be given in a Hash,
|
86
|
-
# pass an array with the symbol names for the defined scopes.
|
98
|
+
# Currently, filters must be monkey patched as scopes into the
|
99
|
+
# DoubleEntry::Line class in order to be used as filters, as the example
|
100
|
+
# shows. If the filter requires a parameter, it must be given in a Hash,
|
101
|
+
# otherwise pass an array with the symbol names for the defined scopes.
|
87
102
|
# @option options :range_type [String] The type of time range to return data
|
88
103
|
# for. For example, specifying 'month' will return an array of the resulting
|
89
104
|
# aggregate calculation for each month.
|
@@ -95,7 +110,7 @@ module DoubleEntry
|
|
95
110
|
# @option options :finish [String] The finish (or end) date for the time range
|
96
111
|
# to perform calculations in. The default finish date is the current date.
|
97
112
|
# The format of the string must be as follows: 'YYYY-mm-dd'
|
98
|
-
# @return [Array
|
113
|
+
# @return [Array<Money, Fixnum>] Returns an array of Money objects for :sum
|
99
114
|
# and :average calculations, or an array of Fixnum for :count calculations.
|
100
115
|
# The array is indexed by the range_type. For example, if range_type is
|
101
116
|
# specified as 'month', each index in the array will represent a month.
|
@@ -110,14 +125,15 @@ module DoubleEntry
|
|
110
125
|
# the provided minimum balance.
|
111
126
|
#
|
112
127
|
# @example Find users with at least $1,000,000 in their savings accounts
|
113
|
-
# DoubleEntry.scopes_with_minimum_balance_for_account(
|
114
|
-
#
|
115
|
-
# :savings
|
116
|
-
# ) # might return user ids: [ 1423, 12232, 34729 ]
|
117
|
-
# @param
|
128
|
+
# DoubleEntry::Reporting.scopes_with_minimum_balance_for_account(
|
129
|
+
# 1_000_000.dollars,
|
130
|
+
# :savings,
|
131
|
+
# ) # might return the user ids: [ 1423, 12232, 34729 ]
|
132
|
+
# @param [Money] minimum_balance Minimum account balance a scope must have
|
118
133
|
# to be included in the result set.
|
119
|
-
# @param
|
134
|
+
# @param [Symbol] account_identifier
|
120
135
|
# @return [Array<Fixnum>] Scopes
|
136
|
+
#
|
121
137
|
def scopes_with_minimum_balance_for_account(minimum_balance, account_identifier)
|
122
138
|
select_values(sanitize_sql_array([<<-SQL, account_identifier, minimum_balance.cents])).map {|scope| scope.to_i }
|
123
139
|
SELECT scope
|
@@ -132,6 +148,7 @@ module DoubleEntry
|
|
132
148
|
# @api private
|
133
149
|
# @return [Boolean] true if all the amounts for an account add up to the final balance,
|
134
150
|
# which they always should.
|
151
|
+
#
|
135
152
|
def reconciled?(account)
|
136
153
|
scoped_lines = Line.where(:account => "#{account.identifier}", :scope => "#{account.scope}")
|
137
154
|
sum_of_amounts = scoped_lines.sum(:amount)
|
@@ -5,10 +5,10 @@ module DoubleEntry
|
|
5
5
|
# @api private
|
6
6
|
def self.transfer(defined_transfers, amount, options = {})
|
7
7
|
raise TransferIsNegative if amount < Money.empty
|
8
|
-
from, to, code,
|
8
|
+
from, to, code, detail = options[:from], options[:to], options[:code], options[:detail]
|
9
9
|
defined_transfers.
|
10
10
|
find!(from, to, code).
|
11
|
-
process
|
11
|
+
process(amount, from, to, code, detail)
|
12
12
|
end
|
13
13
|
|
14
14
|
# @api private
|
@@ -44,24 +44,17 @@ module DoubleEntry
|
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
|
-
attr_accessor :code, :from, :to, :description
|
47
|
+
attr_accessor :code, :from, :to, :description
|
48
48
|
|
49
49
|
def initialize(attributes)
|
50
|
-
@meta_requirement = []
|
51
50
|
attributes.each { |name, value| send("#{name}=", value) }
|
52
51
|
end
|
53
52
|
|
54
|
-
def process
|
53
|
+
def process(amount, from, to, code, detail)
|
55
54
|
if from.scope_identity == to.scope_identity and from.identifier == to.identifier
|
56
55
|
raise TransferNotAllowed.new
|
57
56
|
end
|
58
57
|
|
59
|
-
meta_requirement.each do |key|
|
60
|
-
if meta[key].nil?
|
61
|
-
raise RequiredMetaMissing.new
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
58
|
Locking.lock_accounts(from, to) do
|
66
59
|
credit, debit = Line.new, Line.new
|
67
60
|
|
@@ -74,7 +67,6 @@ module DoubleEntry
|
|
74
67
|
credit.amount, debit.amount = -amount, amount
|
75
68
|
credit.account, debit.account = from, to
|
76
69
|
credit.code, debit.code = code, code
|
77
|
-
credit.meta, debit.meta = meta, meta
|
78
70
|
credit.detail, debit.detail = detail, detail
|
79
71
|
credit.balance, debit.balance = credit_balance.balance, debit_balance.balance
|
80
72
|
|
data/lib/double_entry/version.rb
CHANGED
@@ -10,13 +10,11 @@ describe DoubleEntry::Line do
|
|
10
10
|
:account => account,
|
11
11
|
:partner_account => partner_account,
|
12
12
|
:code => code,
|
13
|
-
:meta => meta,
|
14
13
|
)
|
15
14
|
}
|
16
15
|
let(:account) { DoubleEntry.account(:test, :scope => "17") }
|
17
16
|
let(:partner_account) { DoubleEntry.account(:test, :scope => "72") }
|
18
17
|
let(:code) { :test_code }
|
19
|
-
let(:meta) { "test meta" }
|
20
18
|
before { persisted_line.save! }
|
21
19
|
subject { DoubleEntry::Line.last }
|
22
20
|
|
@@ -41,28 +39,6 @@ describe DoubleEntry::Line do
|
|
41
39
|
its("partner_account.account.identifier") { should eq :test }
|
42
40
|
its("partner_account.scope") { should eq "91" }
|
43
41
|
end
|
44
|
-
|
45
|
-
context "given meta = 'the meta'" do
|
46
|
-
let(:meta) { "the meta" }
|
47
|
-
its(:meta) { should eq "the meta" }
|
48
|
-
end
|
49
|
-
|
50
|
-
context "given meta = nil" do
|
51
|
-
let(:meta) { nil }
|
52
|
-
its(:meta) { should eq nil }
|
53
|
-
end
|
54
|
-
|
55
|
-
context "given meta has not been persisted (NULL)" do
|
56
|
-
let(:persisted_line) {
|
57
|
-
DoubleEntry::Line.new(
|
58
|
-
:amount => Money.new(10_00),
|
59
|
-
:balance => Money.empty,
|
60
|
-
:account => DoubleEntry.account(:savings, :scope => User.make!),
|
61
|
-
:code => code,
|
62
|
-
)
|
63
|
-
}
|
64
|
-
its(:meta) { should eq Hash.new }
|
65
|
-
end
|
66
42
|
end
|
67
43
|
|
68
44
|
it "has a table name prefixed with double_entry_" do
|
data/spec/double_entry_spec.rb
CHANGED
@@ -100,7 +100,7 @@ describe DoubleEntry do
|
|
100
100
|
end
|
101
101
|
|
102
102
|
config.define_transfers do |transfers|
|
103
|
-
transfers.define(:from => :savings, :to => :cash, :code => :xfer
|
103
|
+
transfers.define(:from => :savings, :to => :cash, :code => :xfer)
|
104
104
|
end
|
105
105
|
end
|
106
106
|
end
|
@@ -115,7 +115,6 @@ describe DoubleEntry do
|
|
115
115
|
:from => savings,
|
116
116
|
:to => cash,
|
117
117
|
:code => :xfer,
|
118
|
-
:meta => { :ref => 'shopping!' },
|
119
118
|
)
|
120
119
|
end
|
121
120
|
|
@@ -137,7 +136,6 @@ describe DoubleEntry do
|
|
137
136
|
:from => savings,
|
138
137
|
:to => cash,
|
139
138
|
:code => :yfer,
|
140
|
-
:meta => { :ref => 'shopping!' },
|
141
139
|
)
|
142
140
|
}.to raise_error DoubleEntry::TransferNotAllowed
|
143
141
|
end
|
@@ -151,18 +149,6 @@ describe DoubleEntry do
|
|
151
149
|
)
|
152
150
|
}.to raise_error DoubleEntry::TransferNotAllowed
|
153
151
|
end
|
154
|
-
|
155
|
-
it 'raises an exception when required meta data is omitted' do
|
156
|
-
expect {
|
157
|
-
DoubleEntry.transfer(
|
158
|
-
Money.new(100_00),
|
159
|
-
:from => savings,
|
160
|
-
:to => cash,
|
161
|
-
:code => :xfer,
|
162
|
-
:meta => {},
|
163
|
-
)
|
164
|
-
}.to raise_error DoubleEntry::RequiredMetaMissing
|
165
|
-
end
|
166
152
|
end
|
167
153
|
|
168
154
|
describe 'lines' do
|
@@ -173,7 +159,7 @@ describe DoubleEntry do
|
|
173
159
|
accounts.define(:identifier => :b)
|
174
160
|
end
|
175
161
|
|
176
|
-
description = ->(line) { "Money goes #{line.
|
162
|
+
description = ->(line) { "Money goes #{line.decrease? ? 'out' : 'in'}: #{line.amount.format}" }
|
177
163
|
config.define_transfers do |transfers|
|
178
164
|
transfers.define(:code => :xfer, :from => :a, :to => :b, :description => description)
|
179
165
|
end
|
@@ -208,11 +194,11 @@ describe DoubleEntry do
|
|
208
194
|
expect(credit_line.partner_account).to eq debit_line.account
|
209
195
|
end
|
210
196
|
|
211
|
-
it 'knows if it is
|
212
|
-
expect(credit_line).to
|
213
|
-
expect(debit_line).to
|
214
|
-
expect(credit_line).to_not
|
215
|
-
expect(debit_line).to_not
|
197
|
+
it 'knows if it is an increase or decrease' do
|
198
|
+
expect(credit_line).to be_decrease
|
199
|
+
expect(debit_line).to be_increase
|
200
|
+
expect(credit_line).to_not be_increase
|
201
|
+
expect(debit_line).to_not be_decrease
|
216
202
|
end
|
217
203
|
|
218
204
|
it 'can reference its partner' do
|
data/spec/support/schema.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: double_entry
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -16,7 +16,7 @@ authors:
|
|
16
16
|
autorequire:
|
17
17
|
bindir: bin
|
18
18
|
cert_chain: []
|
19
|
-
date: 2014-07-
|
19
|
+
date: 2014-07-17 00:00:00.000000000 Z
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
22
22
|
name: money
|