double_entry 0.6.1 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +26 -19
- data/.travis.yml +4 -4
- data/Gemfile +0 -1
- data/README.md +16 -3
- data/double_entry.gemspec +4 -5
- data/lib/active_record/locking_extensions.rb +1 -1
- data/lib/double_entry.rb +8 -1
- data/lib/double_entry/account.rb +15 -3
- data/lib/double_entry/account_balance.rb +13 -3
- data/lib/double_entry/balance_calculator.rb +3 -2
- data/lib/double_entry/errors.rb +2 -1
- data/lib/double_entry/line.rb +31 -12
- data/lib/double_entry/locking.rb +0 -1
- data/lib/double_entry/reporting/aggregate.rb +9 -5
- data/lib/double_entry/reporting/aggregate_array.rb +5 -1
- data/lib/double_entry/reporting/line_aggregate.rb +0 -1
- data/lib/double_entry/transfer.rb +5 -3
- data/lib/double_entry/validation/line_check.rb +3 -3
- data/lib/double_entry/version.rb +1 -1
- data/lib/generators/double_entry/install/templates/migration.rb +1 -1
- data/spec/double_entry/account_spec.rb +12 -0
- data/spec/double_entry/balance_calculator_spec.rb +4 -21
- data/spec/double_entry/line_spec.rb +49 -38
- data/spec/double_entry/locking_spec.rb +2 -2
- data/spec/double_entry/reporting/aggregate_array_spec.rb +18 -3
- data/spec/double_entry/reporting/aggregate_spec.rb +13 -0
- data/spec/double_entry/validation/line_check_spec.rb +16 -0
- data/spec/double_entry_spec.rb +40 -10
- data/spec/spec_helper.rb +14 -8
- data/spec/support/accounts.rb +9 -5
- data/spec/support/blueprints.rb +9 -0
- data/spec/support/database.example.yml +1 -1
- data/spec/support/database.travis.yml +1 -1
- data/spec/support/double_entry_spec_helper.rb +8 -0
- data/{gemfiles/Gemfile.rails-3.2.0 → spec/support/gemfiles/Gemfile.rails-3.2.x} +2 -2
- data/spec/support/gemfiles/Gemfile.rails-4.0.x +5 -0
- data/spec/support/gemfiles/Gemfile.rails-4.1.x +5 -0
- metadata +16 -28
- data/db/.gitkeep +0 -0
- data/gemfiles/Gemfile.rails-4.0.0 +0 -5
- data/gemfiles/Gemfile.rails-4.1.0 +0 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 05aef36c2326f83e2a5e81ef732b88eb375d40db
|
4
|
+
data.tar.gz: 14e53582a9aaa42cfb5acbff3273d036d29f7c5b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6affae6cedb93232bd40af21f8e2d9386e90014aaed30bfa41d4bf0ff3117d16879e21afc07eaea78e5308e7ccb5ae4b12d6d7e27a7854613a3df21df39bf267
|
7
|
+
data.tar.gz: 8176ff4543e6ffaad668ef63fe2862e472ebb7a160971f6d6749446ed350e334ebe309536e20e2c315b57a2849a17a21fb6a0c931ae93a0f068b40416fb63a17
|
data/.gitignore
CHANGED
@@ -1,22 +1,29 @@
|
|
1
1
|
*.gem
|
2
2
|
*.rbc
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
log/
|
3
|
+
/.config
|
4
|
+
/coverage/
|
5
|
+
/InstalledFiles
|
6
|
+
/pkg/
|
7
|
+
/spec/reports/
|
8
|
+
/test/tmp/
|
9
|
+
/test/version_tmp/
|
10
|
+
/tmp/
|
11
|
+
|
12
|
+
## Documentation cache and generated files:
|
13
|
+
/.yardoc/
|
14
|
+
/_yardoc/
|
15
|
+
/doc/
|
16
|
+
/rdoc/
|
17
|
+
|
18
|
+
## Environment normalisation:
|
19
|
+
/.bundle/
|
20
|
+
/lib/bundler/man/
|
22
21
|
/Gemfile.lock
|
22
|
+
/.ruby-version
|
23
|
+
/.ruby-gemset
|
24
|
+
|
25
|
+
# DoubleEntry specific
|
26
|
+
/bin/
|
27
|
+
/log/
|
28
|
+
/spec/reports/
|
29
|
+
/spec/support/database.yml
|
data/.travis.yml
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
language: ruby
|
2
2
|
rvm:
|
3
|
-
- 2.1.
|
3
|
+
- 2.1.4
|
4
4
|
- 2.0.0
|
5
5
|
- 1.9.3
|
6
6
|
env:
|
@@ -15,6 +15,6 @@ script:
|
|
15
15
|
- rake spec
|
16
16
|
- ruby script/jack_hammer -t 2000
|
17
17
|
gemfile:
|
18
|
-
- gemfiles/Gemfile.rails-3.2.
|
19
|
-
- gemfiles/Gemfile.rails-4.0.
|
20
|
-
- gemfiles/Gemfile.rails-4.1.
|
18
|
+
- spec/support/gemfiles/Gemfile.rails-3.2.x
|
19
|
+
- spec/support/gemfiles/Gemfile.rails-4.0.x
|
20
|
+
- spec/support/gemfiles/Gemfile.rails-4.1.x
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -26,6 +26,7 @@ Rails Versions: Rails 3.2.x, 4.0.x, 4.1.x
|
|
26
26
|
**Databases Supported:**
|
27
27
|
* MySQL
|
28
28
|
* PostgreSQL
|
29
|
+
* SQLite
|
29
30
|
|
30
31
|
## Installation
|
31
32
|
|
@@ -124,7 +125,7 @@ manually lock the accounts you're using:
|
|
124
125
|
```ruby
|
125
126
|
DoubleEntry.lock_accounts(account_a, account_b) do
|
126
127
|
# Perhaps transfer some money
|
127
|
-
DoubleEntry.transfer(
|
128
|
+
DoubleEntry.transfer(Money.new(20_00), :from => account_a, :to => account_b, :code => :purchase)
|
128
129
|
# Perform other tasks that should be commited atomically with the transfer of funds...
|
129
130
|
end
|
130
131
|
```
|
@@ -181,6 +182,19 @@ DoubleEntry.configure do |config|
|
|
181
182
|
end
|
182
183
|
```
|
183
184
|
|
185
|
+
By default an account's currency is the same as Money.default_currency from the money gem.
|
186
|
+
|
187
|
+
You can also specify a currency on a per account basis.
|
188
|
+
Transfers between accounts of different currencies are not allowed.
|
189
|
+
|
190
|
+
```ruby
|
191
|
+
DoubleEntry.configure do |config|
|
192
|
+
config.define_accounts do |accounts|
|
193
|
+
accounts.define(:identifier => :savings, :scope_identifier => user_scope, :currency => :aud)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
```
|
197
|
+
|
184
198
|
## Jackhammer
|
185
199
|
|
186
200
|
Run a concurrency test on the code.
|
@@ -240,7 +254,7 @@ See the Github project [issues](https://github.com/envato/double_entry/issues).
|
|
240
254
|
./script/setup.sh
|
241
255
|
```
|
242
256
|
|
243
|
-
3. Install MySQL and
|
257
|
+
3. Install MySQL, PostgreSQL and SQLite. We run tests against all three databases.
|
244
258
|
4. Create a database in MySQL.
|
245
259
|
|
246
260
|
```sh
|
@@ -265,4 +279,3 @@ See the Github project [issues](https://github.com/envato/double_entry/issues).
|
|
265
279
|
```sh
|
266
280
|
bundle exec rake
|
267
281
|
```
|
268
|
-
|
data/double_entry.gemspec
CHANGED
@@ -19,11 +19,10 @@ Gem::Specification.new do |gem|
|
|
19
19
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
20
20
|
gem.require_paths = ['lib']
|
21
21
|
|
22
|
-
gem.add_dependency 'money', '>=
|
23
|
-
gem.add_dependency '
|
24
|
-
gem.add_dependency '
|
25
|
-
gem.add_dependency '
|
26
|
-
gem.add_dependency 'railties', '>= 3.0.0'
|
22
|
+
gem.add_dependency 'money', '>= 6.0.0'
|
23
|
+
gem.add_dependency 'activerecord', '>= 3.2.0'
|
24
|
+
gem.add_dependency 'activesupport', '>= 3.2.0'
|
25
|
+
gem.add_dependency 'railties', '>= 3.2.0'
|
27
26
|
|
28
27
|
gem.add_development_dependency 'rake'
|
29
28
|
gem.add_development_dependency 'mysql2'
|
@@ -48,7 +48,7 @@ module ActiveRecord
|
|
48
48
|
begin
|
49
49
|
yield
|
50
50
|
rescue ActiveRecord::StatementInvalid, ActiveRecord::RecordNotUnique => exception
|
51
|
-
if exception.message =~ /duplicate/i || exception.message =~ /
|
51
|
+
if exception.message =~ /duplicate/i || exception.message =~ /ConstraintException/
|
52
52
|
# Just ignore it...someone else has already created the record.
|
53
53
|
else
|
54
54
|
raise
|
data/lib/double_entry.rb
CHANGED
@@ -3,7 +3,6 @@ require 'active_record'
|
|
3
3
|
require 'active_record/locking_extensions'
|
4
4
|
require 'active_support/all'
|
5
5
|
require 'money'
|
6
|
-
require 'encapsulate_as_money'
|
7
6
|
|
8
7
|
require 'double_entry/version'
|
9
8
|
require 'double_entry/errors'
|
@@ -129,6 +128,14 @@ module DoubleEntry
|
|
129
128
|
BalanceCalculator.calculate(account, options)
|
130
129
|
end
|
131
130
|
|
131
|
+
# Get the currency of an account.
|
132
|
+
#
|
133
|
+
# @param [DoubleEntry::Account:Instance, Symbol] account Find the currency for this account
|
134
|
+
# @return [Currency] the currency
|
135
|
+
def currency(account)
|
136
|
+
Account.currency(configuration.accounts, account)
|
137
|
+
end
|
138
|
+
|
132
139
|
# Lock accounts in preparation for transfers.
|
133
140
|
#
|
134
141
|
# This creates a transaction, and uses database-level locking to ensure
|
data/lib/double_entry/account.rb
CHANGED
@@ -8,6 +8,17 @@ module DoubleEntry
|
|
8
8
|
DoubleEntry::Account::Instance.new(:account => account, :scope => options[:scope])
|
9
9
|
end
|
10
10
|
|
11
|
+
# @api private
|
12
|
+
def self.currency(defined_accounts, account)
|
13
|
+
code = account.is_a?(Symbol) ? account : account.identifier
|
14
|
+
|
15
|
+
found_account = defined_accounts.detect do |account|
|
16
|
+
account.identifier == code
|
17
|
+
end
|
18
|
+
|
19
|
+
found_account.currency
|
20
|
+
end
|
21
|
+
|
11
22
|
# @api private
|
12
23
|
class Set < Array
|
13
24
|
def define(attributes)
|
@@ -47,7 +58,7 @@ module DoubleEntry
|
|
47
58
|
|
48
59
|
class Instance
|
49
60
|
attr_accessor :account, :scope
|
50
|
-
delegate :identifier, :scope_identifier, :scoped?, :positive_only, :to => :account
|
61
|
+
delegate :identifier, :scope_identifier, :scoped?, :positive_only, :currency, :to => :account
|
51
62
|
|
52
63
|
def initialize(attributes)
|
53
64
|
attributes.each { |name, value| send("#{name}=", value) }
|
@@ -93,7 +104,7 @@ module DoubleEntry
|
|
93
104
|
end
|
94
105
|
|
95
106
|
def to_s
|
96
|
-
"\#{Account account: #{identifier} scope: #{scope}}"
|
107
|
+
"\#{Account account: #{identifier} scope: #{scope} currency: #{currency}}"
|
97
108
|
end
|
98
109
|
|
99
110
|
def inspect
|
@@ -101,10 +112,11 @@ module DoubleEntry
|
|
101
112
|
end
|
102
113
|
end
|
103
114
|
|
104
|
-
attr_accessor :identifier, :scope_identifier, :positive_only
|
115
|
+
attr_accessor :identifier, :scope_identifier, :positive_only, :currency
|
105
116
|
|
106
117
|
def initialize(attributes)
|
107
118
|
attributes.each { |name, value| send("#{name}=", value) }
|
119
|
+
self.currency ||= Money.default_currency
|
108
120
|
end
|
109
121
|
|
110
122
|
def scoped?
|
@@ -9,9 +9,16 @@ module DoubleEntry
|
|
9
9
|
#
|
10
10
|
# Account balances are created on demand when transfers occur.
|
11
11
|
class AccountBalance < ActiveRecord::Base
|
12
|
-
extend EncapsulateAsMoney
|
13
12
|
|
14
|
-
|
13
|
+
delegate :currency, :to => :account
|
14
|
+
|
15
|
+
def balance
|
16
|
+
self[:balance] && Money.new(self[:balance], currency)
|
17
|
+
end
|
18
|
+
|
19
|
+
def balance=(money)
|
20
|
+
self[:balance] = (money && money.fractional)
|
21
|
+
end
|
15
22
|
|
16
23
|
def account=(account)
|
17
24
|
self[:account] = account.identifier.to_s
|
@@ -19,6 +26,10 @@ module DoubleEntry
|
|
19
26
|
account
|
20
27
|
end
|
21
28
|
|
29
|
+
def account
|
30
|
+
DoubleEntry.account(self[:account].to_sym, :scope => self[:scope])
|
31
|
+
end
|
32
|
+
|
22
33
|
def self.find_by_account(account, options = {})
|
23
34
|
scope = where(:scope => account.scope_identity, :account => account.identifier.to_s)
|
24
35
|
scope = scope.lock(true) if options[:lock]
|
@@ -28,4 +39,3 @@ module DoubleEntry
|
|
28
39
|
end
|
29
40
|
|
30
41
|
end
|
31
|
-
|
@@ -18,10 +18,11 @@ module DoubleEntry
|
|
18
18
|
options = Options.new(account, args)
|
19
19
|
relations = RelationBuilder.new(options)
|
20
20
|
lines = relations.build
|
21
|
+
currency = DoubleEntry.currency(account)
|
21
22
|
|
22
23
|
if options.between? || options.code?
|
23
24
|
# from and to or code lookups have to be done via sum
|
24
|
-
Money.new(lines.sum(:amount))
|
25
|
+
Money.new(lines.sum(:amount), currency)
|
25
26
|
else
|
26
27
|
# all other lookups can be performed with running balances
|
27
28
|
result = lines.
|
@@ -29,7 +30,7 @@ module DoubleEntry
|
|
29
30
|
order('id DESC').
|
30
31
|
limit(1).
|
31
32
|
pluck(:balance)
|
32
|
-
result.empty? ? Money.
|
33
|
+
result.empty? ? Money.zero(currency) : Money.new(result.first, currency)
|
33
34
|
end
|
34
35
|
end
|
35
36
|
|
data/lib/double_entry/errors.rb
CHANGED
@@ -7,5 +7,6 @@ module DoubleEntry
|
|
7
7
|
class DuplicateAccount < RuntimeError; end
|
8
8
|
class DuplicateTransfer < RuntimeError; end
|
9
9
|
class AccountWouldBeSentNegative < RuntimeError; end
|
10
|
-
|
10
|
+
class MismatchedCurrencies < RuntimeError; end
|
11
|
+
class MissingAccountError < RuntimeError; end;
|
11
12
|
end
|
data/lib/double_entry/line.rb
CHANGED
@@ -56,11 +56,24 @@ module DoubleEntry
|
|
56
56
|
# by account, or account and code, over a particular period.
|
57
57
|
#
|
58
58
|
class Line < ActiveRecord::Base
|
59
|
-
extend EncapsulateAsMoney
|
60
59
|
|
61
60
|
belongs_to :detail, :polymorphic => true
|
62
61
|
|
63
|
-
|
62
|
+
def amount
|
63
|
+
self[:amount] && Money.new(self[:amount], currency)
|
64
|
+
end
|
65
|
+
|
66
|
+
def amount=(money)
|
67
|
+
self[:amount] = (money && money.fractional)
|
68
|
+
end
|
69
|
+
|
70
|
+
def balance
|
71
|
+
self[:balance] && Money.new(self[:balance], currency)
|
72
|
+
end
|
73
|
+
|
74
|
+
def balance=(money)
|
75
|
+
self[:balance] = (money && money.fractional)
|
76
|
+
end
|
64
77
|
|
65
78
|
def save(*)
|
66
79
|
check_balance_will_not_be_sent_negative
|
@@ -81,20 +94,26 @@ module DoubleEntry
|
|
81
94
|
self[:code].try(:to_sym)
|
82
95
|
end
|
83
96
|
|
84
|
-
def account=(
|
85
|
-
self[:account] =
|
86
|
-
self.scope =
|
87
|
-
account
|
97
|
+
def account=(_account)
|
98
|
+
self[:account] = _account.identifier.to_s
|
99
|
+
self.scope = _account.scope_identity
|
100
|
+
raise "Missing Account" unless account
|
101
|
+
_account
|
88
102
|
end
|
89
103
|
|
90
104
|
def account
|
91
105
|
DoubleEntry.account(self[:account].to_sym, :scope => scope)
|
92
106
|
end
|
93
107
|
|
94
|
-
def
|
95
|
-
self[:
|
96
|
-
|
97
|
-
|
108
|
+
def currency
|
109
|
+
account.currency if self[:account]
|
110
|
+
end
|
111
|
+
|
112
|
+
def partner_account=(_partner_account)
|
113
|
+
self[:partner_account] = _partner_account.identifier.to_s
|
114
|
+
self.partner_scope = _partner_account.scope_identity
|
115
|
+
raise "Missing Partner Account" unless partner_account
|
116
|
+
_partner_account
|
98
117
|
end
|
99
118
|
|
100
119
|
def partner_account
|
@@ -114,11 +133,11 @@ module DoubleEntry
|
|
114
133
|
end
|
115
134
|
|
116
135
|
def decrease?
|
117
|
-
amount < Money.
|
136
|
+
amount < Money.zero
|
118
137
|
end
|
119
138
|
|
120
139
|
def increase?
|
121
|
-
amount > Money.
|
140
|
+
amount > Money.zero
|
122
141
|
end
|
123
142
|
|
124
143
|
# Query out just the id and created_at fields for lines, without
|
data/lib/double_entry/locking.rb
CHANGED
@@ -162,7 +162,6 @@ module DoubleEntry
|
|
162
162
|
@accounts_without_balances.each do |account|
|
163
163
|
# Get the initial balance from the lines table.
|
164
164
|
balance = account.balance
|
165
|
-
|
166
165
|
# Try to create the balance record, but ignore it if someone else has done it in the meantime.
|
167
166
|
AccountBalance.create_ignoring_duplicates!(:account => account, :balance => balance)
|
168
167
|
end
|
@@ -8,7 +8,7 @@ module DoubleEntry
|
|
8
8
|
@function = function.to_s
|
9
9
|
raise AggregateFunctionNotSupported unless %w[sum count average].include?(@function)
|
10
10
|
|
11
|
-
@account = account
|
11
|
+
@account = account
|
12
12
|
@code = code ? code.to_s : nil
|
13
13
|
@options = options
|
14
14
|
@range = options[:range]
|
@@ -25,17 +25,17 @@ module DoubleEntry
|
|
25
25
|
end
|
26
26
|
|
27
27
|
def formatted_amount
|
28
|
-
Aggregate.formatted_amount(function, amount)
|
28
|
+
Aggregate.formatted_amount(function, amount, currency)
|
29
29
|
end
|
30
30
|
|
31
|
-
def self.formatted_amount(function, amount)
|
31
|
+
def self.formatted_amount(function, amount, currency)
|
32
32
|
safe_amount = amount || 0
|
33
33
|
|
34
34
|
case function.to_s
|
35
35
|
when 'count'
|
36
36
|
safe_amount
|
37
37
|
else
|
38
|
-
Money.new(safe_amount)
|
38
|
+
Money.new(safe_amount, currency)
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
@@ -46,6 +46,10 @@ module DoubleEntry
|
|
46
46
|
aggregate.amount if aggregate
|
47
47
|
end
|
48
48
|
|
49
|
+
def currency
|
50
|
+
DoubleEntry.currency(account)
|
51
|
+
end
|
52
|
+
|
49
53
|
def clear_old_aggregates
|
50
54
|
LineAggregate.delete_all(field_hash)
|
51
55
|
end
|
@@ -75,7 +79,7 @@ module DoubleEntry
|
|
75
79
|
when 'average'
|
76
80
|
calculate_yearly_average
|
77
81
|
else
|
78
|
-
zero = Aggregate.formatted_amount(function, 0)
|
82
|
+
zero = Aggregate.formatted_amount(function, 0, currency)
|
79
83
|
|
80
84
|
result = (1..12).inject(zero) do |total, month|
|
81
85
|
total += Reporting.aggregate(function, account, code,
|
@@ -54,7 +54,7 @@ module DoubleEntry
|
|
54
54
|
where(:filter => filter.inspect).
|
55
55
|
where(LineAggregate.arel_table[range_type].not_eq(nil)).
|
56
56
|
inject({}) do |hash, result|
|
57
|
-
hash[result.key] = Aggregate.formatted_amount(function, result.amount)
|
57
|
+
hash[result.key] = Aggregate.formatted_amount(function, result.amount, currency)
|
58
58
|
hash
|
59
59
|
end
|
60
60
|
end
|
@@ -62,6 +62,10 @@ module DoubleEntry
|
|
62
62
|
def all_periods
|
63
63
|
TimeRangeArray.make(range_type, start, finish)
|
64
64
|
end
|
65
|
+
|
66
|
+
def currency
|
67
|
+
DoubleEntry.currency(account)
|
68
|
+
end
|
65
69
|
end
|
66
70
|
end
|
67
71
|
end
|
@@ -4,7 +4,7 @@ module DoubleEntry
|
|
4
4
|
|
5
5
|
# @api private
|
6
6
|
def self.transfer(defined_transfers, amount, options = {})
|
7
|
-
raise TransferIsNegative if amount < Money.
|
7
|
+
raise TransferIsNegative if amount < Money.zero
|
8
8
|
from, to, code, detail = options[:from], options[:to], options[:code], options[:detail]
|
9
9
|
defined_transfers.
|
10
10
|
find!(from, to, code).
|
@@ -52,9 +52,11 @@ module DoubleEntry
|
|
52
52
|
|
53
53
|
def process(amount, from, to, code, detail)
|
54
54
|
if from.scope_identity == to.scope_identity and from.identifier == to.identifier
|
55
|
-
raise TransferNotAllowed.new
|
55
|
+
raise TransferNotAllowed.new("from and to are identical")
|
56
|
+
end
|
57
|
+
if to.currency != from.currency
|
58
|
+
raise MismatchedCurrencies.new("Missmatched currency (#{to.currency} <> #{from.currency})")
|
56
59
|
end
|
57
|
-
|
58
60
|
Locking.lock_accounts(from, to) do
|
59
61
|
credit, debit = Line.new, Line.new
|
60
62
|
|
@@ -4,7 +4,6 @@ require 'set'
|
|
4
4
|
module DoubleEntry
|
5
5
|
module Validation
|
6
6
|
class LineCheck < ActiveRecord::Base
|
7
|
-
extend EncapsulateAsMoney
|
8
7
|
|
9
8
|
default_scope -> { order('created_at') }
|
10
9
|
|
@@ -65,7 +64,8 @@ module DoubleEntry
|
|
65
64
|
# yes, it needs to be find_by_sql, because any other find will be affected
|
66
65
|
# by the find_each call in perform!
|
67
66
|
previous_line = Line.find_by_sql(["SELECT * FROM #{Line.quoted_table_name} #{force_index} WHERE account = ? AND scope = ? AND id < ? ORDER BY id DESC LIMIT 1", line.account.identifier.to_s, line.scope, line.id])
|
68
|
-
|
67
|
+
|
68
|
+
previous_balance = previous_line.length == 1 ? previous_line[0].balance : Money.zero(line.account.currency)
|
69
69
|
|
70
70
|
if line.balance != (line.amount + previous_balance)
|
71
71
|
log << line_error_message(line, previous_line, previous_balance)
|
@@ -93,7 +93,7 @@ module DoubleEntry
|
|
93
93
|
|
94
94
|
def recalculate_account(account)
|
95
95
|
DoubleEntry.lock_accounts(account) do
|
96
|
-
recalculated_balance = Money.
|
96
|
+
recalculated_balance = Money.zero(account.currency)
|
97
97
|
|
98
98
|
lines_for_account(account).each do |line|
|
99
99
|
recalculated_balance += line.amount
|
data/lib/double_entry/version.rb
CHANGED
@@ -41,9 +41,9 @@ class CreateDoubleEntryTables < ActiveRecord::Migration
|
|
41
41
|
t.integer "day"
|
42
42
|
t.integer "hour"
|
43
43
|
t.integer "amount"
|
44
|
-
t.timestamps
|
45
44
|
t.string "filter"
|
46
45
|
t.string "range_type"
|
46
|
+
t.timestamps
|
47
47
|
end
|
48
48
|
|
49
49
|
add_index "double_entry_line_aggregates", ["function", "account", "code", "year", "month", "week", "day"], :name => "line_aggregate_idx"
|
@@ -20,6 +20,18 @@ module DoubleEntry
|
|
20
20
|
expect(a1.hash).to eq a2.hash
|
21
21
|
expect(a1.hash).to_not eq b.hash
|
22
22
|
end
|
23
|
+
|
24
|
+
describe "currency" do
|
25
|
+
it "defaults to USD currency" do
|
26
|
+
account = DoubleEntry::Account.new(:identifier => "savings", :scope_identifier => identity_scope)
|
27
|
+
expect(DoubleEntry::Account::Instance.new(:account => account).currency).to eq("USD")
|
28
|
+
end
|
29
|
+
|
30
|
+
it "allows the currency to be set" do
|
31
|
+
account = DoubleEntry::Account.new(:identifier => "savings", :scope_identifier => identity_scope, :currency => "AUD")
|
32
|
+
expect(DoubleEntry::Account::Instance.new(:account => account).currency).to eq("AUD")
|
33
|
+
end
|
34
|
+
end
|
23
35
|
end
|
24
36
|
|
25
37
|
describe Account::Set do
|
@@ -4,8 +4,8 @@ require 'spec_helper'
|
|
4
4
|
describe DoubleEntry::BalanceCalculator do
|
5
5
|
|
6
6
|
describe '#calculate' do
|
7
|
-
let(:account) {
|
8
|
-
let(:scope) {
|
7
|
+
let(:account) { DoubleEntry::account(:test, :scope => scope) }
|
8
|
+
let(:scope) { double(:id => 1) }
|
9
9
|
let(:from) { nil }
|
10
10
|
let(:to) { nil }
|
11
11
|
let(:at) { nil }
|
@@ -28,10 +28,10 @@ describe DoubleEntry::BalanceCalculator do
|
|
28
28
|
|
29
29
|
describe 'what happens with different accounts' do
|
30
30
|
context 'when the given account is a symbol' do
|
31
|
-
let(:account) { :
|
31
|
+
let(:account) { :test }
|
32
32
|
|
33
33
|
it 'scopes the lines summed by the account symbol' do
|
34
|
-
expect(DoubleEntry::Line).to have_received(:where).with(:account => '
|
34
|
+
expect(DoubleEntry::Line).to have_received(:where).with(:account => 'test')
|
35
35
|
end
|
36
36
|
|
37
37
|
context 'with a scopeable entity provided' do
|
@@ -48,23 +48,6 @@ describe DoubleEntry::BalanceCalculator do
|
|
48
48
|
end
|
49
49
|
end
|
50
50
|
end
|
51
|
-
|
52
|
-
context 'when the given account is DoubleEntry::Account-like' do
|
53
|
-
let(:account) do
|
54
|
-
DoubleEntry::Account::Instance.new(
|
55
|
-
:account => DoubleEntry::Account.new(
|
56
|
-
:identifier => 'account_identity',
|
57
|
-
:scope_identifier => lambda { |scope_id| scope_id },
|
58
|
-
),
|
59
|
-
:scope => 'account_scope_identity'
|
60
|
-
)
|
61
|
-
end
|
62
|
-
|
63
|
-
it 'scopes the lines summed by the accounts identifier and its scope identity' do
|
64
|
-
expect(DoubleEntry::Line).to have_received(:where).with(:account => 'account_identity')
|
65
|
-
expect(relation).to have_received(:where).with(:scope => 'account_scope_identity')
|
66
|
-
end
|
67
|
-
end
|
68
51
|
end
|
69
52
|
|
70
53
|
describe 'what happens with different times' do
|
@@ -1,12 +1,15 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
require "spec_helper"
|
3
3
|
describe DoubleEntry::Line do
|
4
|
+
it "has a table name prefixed with double_entry_" do
|
5
|
+
expect(DoubleEntry::Line.table_name).to eq "double_entry_lines"
|
6
|
+
end
|
4
7
|
|
5
|
-
describe "
|
8
|
+
describe "persistance" do
|
6
9
|
let(:persisted_line) {
|
7
10
|
DoubleEntry::Line.new(
|
8
11
|
:amount => Money.new(10_00),
|
9
|
-
:balance => Money.
|
12
|
+
:balance => Money.zero,
|
10
13
|
:account => account,
|
11
14
|
:partner_account => partner_account,
|
12
15
|
:code => code,
|
@@ -15,53 +18,61 @@ describe DoubleEntry::Line do
|
|
15
18
|
let(:account) { DoubleEntry.account(:test, :scope => "17") }
|
16
19
|
let(:partner_account) { DoubleEntry.account(:test, :scope => "72") }
|
17
20
|
let(:code) { :test_code }
|
18
|
-
before { persisted_line.save! }
|
19
21
|
subject { DoubleEntry::Line.last }
|
20
22
|
|
21
|
-
|
22
|
-
|
23
|
-
its(:code) { should eq :the_code }
|
24
|
-
end
|
23
|
+
describe "attributes" do
|
24
|
+
before { persisted_line.save! }
|
25
25
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
26
|
+
context "given code = :the_code" do
|
27
|
+
let(:code) { :the_code }
|
28
|
+
its(:code) { should eq :the_code }
|
29
|
+
end
|
30
30
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
31
|
+
context "given code = nil" do
|
32
|
+
let(:code) { nil }
|
33
|
+
its(:code) { should eq nil }
|
34
|
+
end
|
35
|
+
|
36
|
+
context "given account = :test, 54 " do
|
37
|
+
let(:account) { DoubleEntry.account(:test, :scope => "54") }
|
38
|
+
its("account.account.identifier") { should eq :test }
|
39
|
+
its("account.scope") { should eq "54" }
|
40
|
+
end
|
36
41
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
42
|
+
context "given partner_account = :test, 91 " do
|
43
|
+
let(:partner_account) { DoubleEntry.account(:test, :scope => "91") }
|
44
|
+
its("partner_account.account.identifier") { should eq :test }
|
45
|
+
its("partner_account.scope") { should eq "91" }
|
46
|
+
end
|
47
|
+
|
48
|
+
context "currency" do
|
49
|
+
let(:account) { DoubleEntry.account(:btc_test, :scope => "17") }
|
50
|
+
let(:partner_account) { DoubleEntry.account(:btc_test, :scope => "72") }
|
51
|
+
its(:currency) { should eq "BTC" }
|
52
|
+
end
|
41
53
|
end
|
42
|
-
end
|
43
54
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
55
|
+
describe '#save' do
|
56
|
+
context 'when balance is sent negative' do
|
57
|
+
let(:account) {
|
58
|
+
DoubleEntry.account(:savings, :scope => '17', :positive_only => true)
|
59
|
+
}
|
49
60
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
61
|
+
let(:line) {
|
62
|
+
DoubleEntry::Line.new(
|
63
|
+
:balance => Money.new(-1),
|
64
|
+
:account => account,
|
65
|
+
)
|
66
|
+
}
|
56
67
|
|
57
|
-
|
58
|
-
|
68
|
+
it 'raises AccountWouldBeSentNegative exception' do
|
69
|
+
expect { line.save }.to raise_error DoubleEntry::AccountWouldBeSentNegative
|
70
|
+
end
|
59
71
|
end
|
60
72
|
end
|
61
|
-
end
|
62
73
|
|
63
|
-
|
64
|
-
|
74
|
+
it "has a table name prefixed with double_entry_" do
|
75
|
+
expect(DoubleEntry::Line.table_name).to eq "double_entry_lines"
|
76
|
+
end
|
65
77
|
end
|
66
|
-
|
67
78
|
end
|
@@ -51,8 +51,8 @@ describe DoubleEntry::Locking do
|
|
51
51
|
end
|
52
52
|
|
53
53
|
it "takes the balance for new account balance records from the lines table" do
|
54
|
-
DoubleEntry::Line.create!(:account => @account_a, :amount => Money.new(3_00), :balance => Money.new( 3_00), :code => :test)
|
55
|
-
DoubleEntry::Line.create!(:account => @account_a, :amount => Money.new(7_00), :balance => Money.new(10_00), :code => :test)
|
54
|
+
DoubleEntry::Line.create!(:account => @account_a, :partner_account => @account_b, :amount => Money.new(3_00), :balance => Money.new( 3_00), :code => :test)
|
55
|
+
DoubleEntry::Line.create!(:account => @account_a, :partner_account => @account_b, :amount => Money.new(7_00), :balance => Money.new(10_00), :code => :test)
|
56
56
|
|
57
57
|
expect do
|
58
58
|
DoubleEntry::Locking.lock_accounts(@account_a) { }
|
@@ -8,12 +8,13 @@ module DoubleEntry
|
|
8
8
|
let(:finish) { nil }
|
9
9
|
let(:range_type) { 'year' }
|
10
10
|
let(:function) { :sum }
|
11
|
-
|
11
|
+
let(:account) { :savings }
|
12
|
+
let(:transfer_code) { :bonus }
|
12
13
|
subject(:aggregate_array) {
|
13
14
|
Reporting.aggregate_array(
|
14
15
|
function,
|
15
|
-
|
16
|
-
|
16
|
+
account,
|
17
|
+
transfer_code,
|
17
18
|
:range_type => range_type,
|
18
19
|
:start => start,
|
19
20
|
:finish => finish,
|
@@ -69,6 +70,20 @@ module DoubleEntry
|
|
69
70
|
end
|
70
71
|
end
|
71
72
|
|
73
|
+
context 'when account is in BTC currency' do
|
74
|
+
let(:account) { :btc_savings }
|
75
|
+
let(:range_type) { 'year' }
|
76
|
+
let(:start) { "#{Time.now.year}-01-01" }
|
77
|
+
let(:transfer_code) { :btc_test_transfer }
|
78
|
+
|
79
|
+
before do
|
80
|
+
perform_btc_deposit(user, 100_000_000)
|
81
|
+
perform_btc_deposit(user, 100_000_000)
|
82
|
+
end
|
83
|
+
|
84
|
+
it { should eq [ Money.new(200_000_000, :btc) ] }
|
85
|
+
end
|
86
|
+
|
72
87
|
context 'when called with range type of "invalid_and_should_not_work"' do
|
73
88
|
let(:range_type) { 'invalid_and_should_not_work' }
|
74
89
|
it 'raises an argument error' do
|
@@ -203,5 +203,18 @@ module DoubleEntry
|
|
203
203
|
end
|
204
204
|
end
|
205
205
|
end
|
206
|
+
describe Aggregate, "currencies" do
|
207
|
+
let(:user) { User.make! }
|
208
|
+
before do
|
209
|
+
perform_btc_deposit(user, 100_000_000)
|
210
|
+
perform_btc_deposit(user, 200_000_000)
|
211
|
+
end
|
212
|
+
|
213
|
+
it 'should calculate the sum in the correct currency' do
|
214
|
+
expect(
|
215
|
+
Reporting.aggregate(:sum, :btc_savings, :btc_test_transfer, :range => TimeRange.make(:year => Time.now.year))
|
216
|
+
).to eq Money.new(300_000_000, :btc)
|
217
|
+
end
|
218
|
+
end
|
206
219
|
end
|
207
220
|
end
|
@@ -80,6 +80,22 @@ module DoubleEntry::Validation
|
|
80
80
|
end
|
81
81
|
end
|
82
82
|
|
83
|
+
|
84
|
+
context 'Given a user with a non default currency balance' do
|
85
|
+
before { User.make!(:bitcoin_balance => Money.new(100_00, 'BTC')) }
|
86
|
+
its(:errors_found) { should eq false }
|
87
|
+
context 'And there is a consistency error in lines' do
|
88
|
+
before { DoubleEntry::Line.order(:id).limit(1).update_all('balance = balance + 1') }
|
89
|
+
|
90
|
+
its(:errors_found) { should eq true }
|
91
|
+
it 'should correct the running balance' do
|
92
|
+
expect {
|
93
|
+
LineCheck.perform!
|
94
|
+
}.to change { DoubleEntry::Line.order(:id).first.balance }.by Money.new(-1, 'BTC')
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
83
99
|
it "has a table name prefixed with double_entry_" do
|
84
100
|
expect(LineCheck.table_name).to eq "double_entry_line_checks"
|
85
101
|
end
|
data/spec/double_entry_spec.rb
CHANGED
@@ -97,17 +97,20 @@ describe DoubleEntry do
|
|
97
97
|
accounts.define(:identifier => :savings)
|
98
98
|
accounts.define(:identifier => :cash)
|
99
99
|
accounts.define(:identifier => :trash)
|
100
|
+
accounts.define(:identifier => :bitbucket, :currency => :btc)
|
100
101
|
end
|
101
102
|
|
102
103
|
config.define_transfers do |transfers|
|
103
104
|
transfers.define(:from => :savings, :to => :cash, :code => :xfer)
|
105
|
+
transfers.define(:from => :trash, :to => :bitbucket, :code => :mismatch_xfer)
|
104
106
|
end
|
105
107
|
end
|
106
108
|
end
|
107
109
|
|
108
|
-
let(:savings)
|
109
|
-
let(:cash)
|
110
|
-
let(:trash)
|
110
|
+
let(:savings) { DoubleEntry.account(:savings) }
|
111
|
+
let(:cash) { DoubleEntry.account(:cash) }
|
112
|
+
let(:trash) { DoubleEntry.account(:trash) }
|
113
|
+
let(:bitbucket) { DoubleEntry.account(:bitbucket) }
|
111
114
|
|
112
115
|
it 'can transfer from an account to an account, if the transfer is allowed' do
|
113
116
|
DoubleEntry.transfer(
|
@@ -149,6 +152,17 @@ describe DoubleEntry do
|
|
149
152
|
)
|
150
153
|
}.to raise_error DoubleEntry::TransferNotAllowed
|
151
154
|
end
|
155
|
+
|
156
|
+
it 'raises an exception when the transfer is not allowed (mismatched currencies)' do
|
157
|
+
expect {
|
158
|
+
DoubleEntry.transfer(
|
159
|
+
Money.new(100_00),
|
160
|
+
:from => trash,
|
161
|
+
:to => bitbucket,
|
162
|
+
:code => :mismatch_xfer
|
163
|
+
)
|
164
|
+
}.to raise_error DoubleEntry::MismatchedCurrencies
|
165
|
+
end
|
152
166
|
end
|
153
167
|
|
154
168
|
describe 'lines' do
|
@@ -219,10 +233,12 @@ describe DoubleEntry do
|
|
219
233
|
|
220
234
|
describe 'balances' do
|
221
235
|
|
222
|
-
let(:work)
|
223
|
-
let(:savings)
|
224
|
-
let(:cash)
|
225
|
-
let(:store)
|
236
|
+
let(:work) { DoubleEntry.account(:work) }
|
237
|
+
let(:savings) { DoubleEntry.account(:savings) }
|
238
|
+
let(:cash) { DoubleEntry.account(:cash) }
|
239
|
+
let(:store) { DoubleEntry.account(:store) }
|
240
|
+
let(:btc_store) { DoubleEntry.account(:btc_store) }
|
241
|
+
let(:btc_wallet) { DoubleEntry.account(:btc_wallet) }
|
226
242
|
|
227
243
|
before do
|
228
244
|
DoubleEntry.configure do |config|
|
@@ -231,6 +247,8 @@ describe DoubleEntry do
|
|
231
247
|
accounts.define(:identifier => :cash)
|
232
248
|
accounts.define(:identifier => :savings)
|
233
249
|
accounts.define(:identifier => :store)
|
250
|
+
accounts.define(:identifier => :btc_store, :currency => 'BTC')
|
251
|
+
accounts.define(:identifier => :btc_wallet, :currency => 'BTC')
|
234
252
|
end
|
235
253
|
|
236
254
|
config.define_transfers do |transfers|
|
@@ -240,6 +258,7 @@ describe DoubleEntry do
|
|
240
258
|
transfers.define(:code => :purchase, :from => :cash, :to => :store)
|
241
259
|
transfers.define(:code => :layby, :from => :cash, :to => :store)
|
242
260
|
transfers.define(:code => :deposit, :from => :cash, :to => :store)
|
261
|
+
transfers.define(:code => :btc_ex, :from => :btc_store, :to => :btc_wallet)
|
243
262
|
end
|
244
263
|
end
|
245
264
|
|
@@ -267,7 +286,8 @@ describe DoubleEntry do
|
|
267
286
|
end
|
268
287
|
|
269
288
|
Timecop.freeze 1.week.from_now do
|
270
|
-
#
|
289
|
+
# it's the future, man
|
290
|
+
DoubleEntry.transfer(Money.new(200_00, 'BTC'), :from => btc_store, :code => :btc_ex, :to => btc_wallet)
|
271
291
|
end
|
272
292
|
end
|
273
293
|
|
@@ -276,14 +296,19 @@ describe DoubleEntry do
|
|
276
296
|
expect(cash.balance).to eq Money.new(100_00)
|
277
297
|
expect(savings.balance).to eq Money.new(300_00)
|
278
298
|
expect(store.balance).to eq Money.new(600_00)
|
299
|
+
expect(btc_wallet.balance).to eq Money.new(200_00, 'BTC')
|
279
300
|
end
|
280
301
|
|
281
302
|
it 'should have correct account balance records' do
|
282
|
-
[work, cash, savings, store].each do |account|
|
303
|
+
[work, cash, savings, store, btc_wallet].each do |account|
|
283
304
|
expect(DoubleEntry::AccountBalance.find_by_account(account).balance).to eq account.balance
|
284
305
|
end
|
285
306
|
end
|
286
307
|
|
308
|
+
it 'should have correct account balance currencies' do
|
309
|
+
expect(DoubleEntry::AccountBalance.find_by_account(btc_wallet).balance.currency).to eq 'BTC'
|
310
|
+
end
|
311
|
+
|
287
312
|
it 'affects origin/destination balance after transfer' do
|
288
313
|
savings_balance = savings.balance
|
289
314
|
cash_balance = cash.balance
|
@@ -303,6 +328,10 @@ describe DoubleEntry do
|
|
303
328
|
expect(cash.balance(:from => 3.weeks.ago, :to => 2.weeks.ago)).to eq Money.new(500_00)
|
304
329
|
end
|
305
330
|
|
331
|
+
it 'can be queried between two points in time, even in the future' do
|
332
|
+
expect(btc_wallet.balance(:from => Time.now, :to => 2.weeks.from_now)).to eq Money.new(200_00, 'BTC')
|
333
|
+
end
|
334
|
+
|
306
335
|
it 'can report on balances, scoped by code' do
|
307
336
|
expect(cash.balance(:code => :salary)).to eq Money.new(1_000_00)
|
308
337
|
end
|
@@ -341,6 +370,8 @@ describe DoubleEntry do
|
|
341
370
|
end
|
342
371
|
|
343
372
|
let(:bank) { DoubleEntry.account(:bank) }
|
373
|
+
let(:cash) { DoubleEntry.account(:cash) }
|
374
|
+
let(:savings) { DoubleEntry.account(:savings) }
|
344
375
|
|
345
376
|
let(:john) { User.make! }
|
346
377
|
let(:johns_cash) { DoubleEntry.account(:cash, :scope => john) }
|
@@ -386,5 +417,4 @@ describe DoubleEntry do
|
|
386
417
|
expect(DoubleEntry.balance(:savings)).to eq ryans_savings.balance + johns_savings.balance
|
387
418
|
end
|
388
419
|
end
|
389
|
-
|
390
420
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -5,11 +5,19 @@ require 'active_support'
|
|
5
5
|
|
6
6
|
db_engine = ENV['DB'] || 'mysql'
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
FileUtils.mkdir_p 'tmp'
|
10
9
|
FileUtils.mkdir_p 'log'
|
11
10
|
FileUtils.rm 'log/test.log', :force => true
|
12
11
|
|
12
|
+
database_config_file = File.expand_path("../support/database.yml", __FILE__)
|
13
|
+
if File.exists?(database_config_file)
|
14
|
+
ActiveRecord::Base.establish_connection YAML.load_file(database_config_file)[db_engine]
|
15
|
+
else
|
16
|
+
puts "Please configure your spec/support/database.yml file."
|
17
|
+
puts "See spec/support/database.example.yml"
|
18
|
+
exit 1
|
19
|
+
end
|
20
|
+
|
13
21
|
# Buffered Logger was deprecated in ActiveSupport 4.0.0 and was removed in 4.1.0
|
14
22
|
# Logger was added in ActiveSupport 4.0.0
|
15
23
|
if defined? ActiveSupport::Logger
|
@@ -33,16 +41,14 @@ RSpec.configure do |config|
|
|
33
41
|
config.include DoubleEntrySpecHelper
|
34
42
|
|
35
43
|
config.before(:suite) do
|
36
|
-
DatabaseCleaner.strategy = :
|
37
|
-
DatabaseCleaner.clean_with(:deletion)
|
44
|
+
DatabaseCleaner.strategy = :truncation
|
38
45
|
end
|
39
46
|
|
40
|
-
config.before
|
41
|
-
DatabaseCleaner.
|
47
|
+
config.before do
|
48
|
+
DatabaseCleaner.clean
|
42
49
|
end
|
43
50
|
|
44
|
-
config.after
|
51
|
+
config.after do
|
45
52
|
Timecop.return
|
46
|
-
DatabaseCleaner.clean
|
47
53
|
end
|
48
54
|
end
|
data/spec/support/accounts.rb
CHANGED
@@ -5,15 +5,19 @@ DoubleEntry.configure do |config|
|
|
5
5
|
# A set of accounts to test with
|
6
6
|
config.define_accounts do |accounts|
|
7
7
|
user_scope = accounts.active_record_scope_identifier(User)
|
8
|
-
accounts.define(:identifier => :savings,
|
9
|
-
accounts.define(:identifier => :checking,
|
10
|
-
accounts.define(:identifier => :test,
|
8
|
+
accounts.define(:identifier => :savings, :scope_identifier => user_scope, :positive_only => true)
|
9
|
+
accounts.define(:identifier => :checking, :scope_identifier => user_scope, :positive_only => true)
|
10
|
+
accounts.define(:identifier => :test, :scope_identifier => user_scope)
|
11
|
+
accounts.define(:identifier => :btc_test, :scope_identifier => user_scope, :currency => "BTC")
|
12
|
+
accounts.define(:identifier => :btc_savings, :scope_identifier => user_scope, :currency => "BTC")
|
11
13
|
end
|
12
14
|
|
13
15
|
# A set of allowed transfers between accounts
|
14
16
|
config.define_transfers do |transfers|
|
15
|
-
transfers.define(:from => :test,
|
16
|
-
transfers.define(:from => :test,
|
17
|
+
transfers.define(:from => :test, :to => :savings, :code => :bonus)
|
18
|
+
transfers.define(:from => :test, :to => :checking, :code => :pay)
|
19
|
+
transfers.define(:from => :savings, :to => :test, :code => :test_withdrawal)
|
20
|
+
transfers.define(:from => :btc_test, :to => :btc_savings, :code => :btc_test_transfer)
|
17
21
|
end
|
18
22
|
|
19
23
|
end
|
data/spec/support/blueprints.rb
CHANGED
@@ -2,6 +2,7 @@ class UserBlueprint < Machinist::ActiveRecord::Blueprint
|
|
2
2
|
def make!(attributes = {})
|
3
3
|
savings_balance = attributes.delete(:savings_balance)
|
4
4
|
checking_balance = attributes.delete(:checking_balance)
|
5
|
+
bitcoin_balance = attributes.delete(:bitcoin_balance)
|
5
6
|
user = super(attributes)
|
6
7
|
if savings_balance
|
7
8
|
DoubleEntry.transfer(
|
@@ -19,6 +20,14 @@ class UserBlueprint < Machinist::ActiveRecord::Blueprint
|
|
19
20
|
:code => :pay,
|
20
21
|
)
|
21
22
|
end
|
23
|
+
if bitcoin_balance
|
24
|
+
DoubleEntry.transfer(
|
25
|
+
bitcoin_balance,
|
26
|
+
:from => DoubleEntry.account(:btc_test, :scope => user),
|
27
|
+
:to => DoubleEntry.account(:btc_savings, :scope => user),
|
28
|
+
:code => :btc_test_transfer,
|
29
|
+
)
|
30
|
+
end
|
22
31
|
user
|
23
32
|
end
|
24
33
|
end
|
@@ -16,4 +16,12 @@ module DoubleEntrySpecHelper
|
|
16
16
|
)
|
17
17
|
end
|
18
18
|
|
19
|
+
def perform_btc_deposit(user, amount)
|
20
|
+
DoubleEntry.transfer(Money.new(amount, :btc),
|
21
|
+
:from => DoubleEntry.account(:btc_test, :scope => user),
|
22
|
+
:to => DoubleEntry.account(:btc_savings, :scope => user),
|
23
|
+
:code => :btc_test_transfer,
|
24
|
+
)
|
25
|
+
end
|
26
|
+
|
19
27
|
end
|
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.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Anthony Sellitti
|
@@ -15,7 +15,7 @@ authors:
|
|
15
15
|
autorequire:
|
16
16
|
bindir: bin
|
17
17
|
cert_chain: []
|
18
|
-
date: 2014-
|
18
|
+
date: 2014-11-12 00:00:00.000000000 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: money
|
@@ -23,70 +23,56 @@ dependencies:
|
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 6.0.0
|
27
27
|
type: :runtime
|
28
28
|
prerelease: false
|
29
29
|
version_requirements: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
34
|
-
- !ruby/object:Gem::Dependency
|
35
|
-
name: encapsulate_as_money
|
36
|
-
requirement: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - ">="
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '0'
|
41
|
-
type: :runtime
|
42
|
-
prerelease: false
|
43
|
-
version_requirements: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - ">="
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '0'
|
33
|
+
version: 6.0.0
|
48
34
|
- !ruby/object:Gem::Dependency
|
49
35
|
name: activerecord
|
50
36
|
requirement: !ruby/object:Gem::Requirement
|
51
37
|
requirements:
|
52
38
|
- - ">="
|
53
39
|
- !ruby/object:Gem::Version
|
54
|
-
version: 3.2.
|
40
|
+
version: 3.2.0
|
55
41
|
type: :runtime
|
56
42
|
prerelease: false
|
57
43
|
version_requirements: !ruby/object:Gem::Requirement
|
58
44
|
requirements:
|
59
45
|
- - ">="
|
60
46
|
- !ruby/object:Gem::Version
|
61
|
-
version: 3.2.
|
47
|
+
version: 3.2.0
|
62
48
|
- !ruby/object:Gem::Dependency
|
63
49
|
name: activesupport
|
64
50
|
requirement: !ruby/object:Gem::Requirement
|
65
51
|
requirements:
|
66
52
|
- - ">="
|
67
53
|
- !ruby/object:Gem::Version
|
68
|
-
version: 3.
|
54
|
+
version: 3.2.0
|
69
55
|
type: :runtime
|
70
56
|
prerelease: false
|
71
57
|
version_requirements: !ruby/object:Gem::Requirement
|
72
58
|
requirements:
|
73
59
|
- - ">="
|
74
60
|
- !ruby/object:Gem::Version
|
75
|
-
version: 3.
|
61
|
+
version: 3.2.0
|
76
62
|
- !ruby/object:Gem::Dependency
|
77
63
|
name: railties
|
78
64
|
requirement: !ruby/object:Gem::Requirement
|
79
65
|
requirements:
|
80
66
|
- - ">="
|
81
67
|
- !ruby/object:Gem::Version
|
82
|
-
version: 3.
|
68
|
+
version: 3.2.0
|
83
69
|
type: :runtime
|
84
70
|
prerelease: false
|
85
71
|
version_requirements: !ruby/object:Gem::Requirement
|
86
72
|
requirements:
|
87
73
|
- - ">="
|
88
74
|
- !ruby/object:Gem::Version
|
89
|
-
version: 3.
|
75
|
+
version: 3.2.0
|
90
76
|
- !ruby/object:Gem::Dependency
|
91
77
|
name: rake
|
92
78
|
requirement: !ruby/object:Gem::Requirement
|
@@ -249,11 +235,7 @@ files:
|
|
249
235
|
- LICENSE.md
|
250
236
|
- README.md
|
251
237
|
- Rakefile
|
252
|
-
- db/.gitkeep
|
253
238
|
- double_entry.gemspec
|
254
|
-
- gemfiles/Gemfile.rails-3.2.0
|
255
|
-
- gemfiles/Gemfile.rails-4.0.0
|
256
|
-
- gemfiles/Gemfile.rails-4.1.0
|
257
239
|
- lib/active_record/locking_extensions.rb
|
258
240
|
- lib/double_entry.rb
|
259
241
|
- lib/double_entry/account.rb
|
@@ -308,6 +290,9 @@ files:
|
|
308
290
|
- spec/support/database.example.yml
|
309
291
|
- spec/support/database.travis.yml
|
310
292
|
- spec/support/double_entry_spec_helper.rb
|
293
|
+
- spec/support/gemfiles/Gemfile.rails-3.2.x
|
294
|
+
- spec/support/gemfiles/Gemfile.rails-4.0.x
|
295
|
+
- spec/support/gemfiles/Gemfile.rails-4.1.x
|
311
296
|
- spec/support/reporting_configuration.rb
|
312
297
|
- spec/support/schema.rb
|
313
298
|
homepage: https://github.com/envato/double_entry
|
@@ -359,5 +344,8 @@ test_files:
|
|
359
344
|
- spec/support/database.example.yml
|
360
345
|
- spec/support/database.travis.yml
|
361
346
|
- spec/support/double_entry_spec_helper.rb
|
347
|
+
- spec/support/gemfiles/Gemfile.rails-3.2.x
|
348
|
+
- spec/support/gemfiles/Gemfile.rails-4.0.x
|
349
|
+
- spec/support/gemfiles/Gemfile.rails-4.1.x
|
362
350
|
- spec/support/reporting_configuration.rb
|
363
351
|
- spec/support/schema.rb
|
data/db/.gitkeep
DELETED
File without changes
|