double_entry 1.0.1 → 2.0.0.beta5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CHANGELOG.md +497 -0
- data/README.md +107 -44
- data/double_entry.gemspec +22 -49
- data/lib/active_record/locking_extensions.rb +3 -3
- data/lib/active_record/locking_extensions/log_subscriber.rb +1 -1
- data/lib/double_entry.rb +29 -21
- data/lib/double_entry/account.rb +39 -46
- data/lib/double_entry/account_balance.rb +20 -3
- data/lib/double_entry/balance_calculator.rb +5 -5
- data/lib/double_entry/configurable.rb +1 -0
- data/lib/double_entry/configuration.rb +8 -2
- data/lib/double_entry/errors.rb +13 -13
- data/lib/double_entry/line.rb +7 -6
- data/lib/double_entry/locking.rb +5 -5
- data/lib/double_entry/transfer.rb +37 -30
- data/lib/double_entry/validation.rb +1 -0
- data/lib/double_entry/validation/account_fixer.rb +36 -0
- data/lib/double_entry/validation/line_check.rb +25 -43
- data/lib/double_entry/version.rb +1 -1
- data/lib/generators/double_entry/install/install_generator.rb +22 -1
- data/lib/generators/double_entry/install/templates/initializer.rb +20 -0
- data/lib/generators/double_entry/install/templates/migration.rb +45 -55
- metadata +35 -256
- data/.gitignore +0 -32
- data/.rspec +0 -2
- data/.travis.yml +0 -29
- data/.yardopts +0 -2
- data/Gemfile +0 -2
- data/Rakefile +0 -15
- data/lib/double_entry/reporting.rb +0 -181
- data/lib/double_entry/reporting/aggregate.rb +0 -110
- data/lib/double_entry/reporting/aggregate_array.rb +0 -76
- data/lib/double_entry/reporting/day_range.rb +0 -42
- data/lib/double_entry/reporting/hour_range.rb +0 -45
- data/lib/double_entry/reporting/line_aggregate.rb +0 -16
- data/lib/double_entry/reporting/line_aggregate_filter.rb +0 -79
- data/lib/double_entry/reporting/month_range.rb +0 -94
- data/lib/double_entry/reporting/time_range.rb +0 -59
- data/lib/double_entry/reporting/time_range_array.rb +0 -49
- data/lib/double_entry/reporting/week_range.rb +0 -107
- data/lib/double_entry/reporting/year_range.rb +0 -40
- data/script/jack_hammer +0 -210
- data/script/setup.sh +0 -8
- data/spec/active_record/locking_extensions_spec.rb +0 -110
- data/spec/double_entry/account_balance_spec.rb +0 -7
- data/spec/double_entry/account_spec.rb +0 -130
- data/spec/double_entry/balance_calculator_spec.rb +0 -88
- data/spec/double_entry/configuration_spec.rb +0 -50
- data/spec/double_entry/line_spec.rb +0 -80
- data/spec/double_entry/locking_spec.rb +0 -214
- data/spec/double_entry/performance/double_entry_performance_spec.rb +0 -32
- data/spec/double_entry/performance/reporting/aggregate_performance_spec.rb +0 -50
- data/spec/double_entry/reporting/aggregate_array_spec.rb +0 -123
- data/spec/double_entry/reporting/aggregate_spec.rb +0 -205
- data/spec/double_entry/reporting/line_aggregate_filter_spec.rb +0 -90
- data/spec/double_entry/reporting/line_aggregate_spec.rb +0 -39
- data/spec/double_entry/reporting/month_range_spec.rb +0 -139
- data/spec/double_entry/reporting/time_range_array_spec.rb +0 -169
- data/spec/double_entry/reporting/time_range_spec.rb +0 -45
- data/spec/double_entry/reporting/week_range_spec.rb +0 -103
- data/spec/double_entry/reporting_spec.rb +0 -181
- data/spec/double_entry/transfer_spec.rb +0 -93
- data/spec/double_entry/validation/line_check_spec.rb +0 -99
- data/spec/double_entry_spec.rb +0 -428
- data/spec/generators/double_entry/install/install_generator_spec.rb +0 -30
- data/spec/spec_helper.rb +0 -118
- data/spec/support/accounts.rb +0 -21
- data/spec/support/blueprints.rb +0 -43
- data/spec/support/database.example.yml +0 -21
- data/spec/support/database.travis.yml +0 -24
- data/spec/support/double_entry_spec_helper.rb +0 -27
- data/spec/support/gemfiles/Gemfile.rails-3.2.x +0 -8
- data/spec/support/gemfiles/Gemfile.rails-4.1.x +0 -6
- data/spec/support/gemfiles/Gemfile.rails-4.2.x +0 -5
- data/spec/support/gemfiles/Gemfile.rails-5.0.x +0 -5
- data/spec/support/performance_helper.rb +0 -26
- data/spec/support/reporting_configuration.rb +0 -6
- data/spec/support/schema.rb +0 -74
data/README.md
CHANGED
@@ -3,8 +3,8 @@
|
|
3
3
|
|
4
4
|
[![License MIT](https://img.shields.io/badge/license-MIT-brightgreen.svg)](https://github.com/envato/double_entry/blob/master/LICENSE.md)
|
5
5
|
[![Gem Version](https://badge.fury.io/rb/double_entry.svg)](http://badge.fury.io/rb/double_entry)
|
6
|
-
[![Build Status](https://travis-ci.org/envato/double_entry.svg?branch=
|
7
|
-
[![Code Climate](https://codeclimate.com/github/envato/double_entry.
|
6
|
+
[![Build Status](https://travis-ci.org/envato/double_entry.svg?branch=master)](https://travis-ci.org/envato/double_entry)
|
7
|
+
[![Code Climate](https://codeclimate.com/github/envato/double_entry/badges/gpa.svg)](https://codeclimate.com/github/envato/double_entry)
|
8
8
|
|
9
9
|
![Show me the Money](http://24.media.tumblr.com/tumblr_m3bwbqNJIG1rrgbmqo1_500.gif)
|
10
10
|
|
@@ -22,15 +22,18 @@ DoubleEntry uses the [Money gem](https://github.com/RubyMoney/money) to encapsul
|
|
22
22
|
DoubleEntry is tested against:
|
23
23
|
|
24
24
|
Ruby
|
25
|
-
* 2.1.x
|
26
|
-
* 2.2.x
|
27
25
|
* 2.3.x
|
26
|
+
* 2.4.x
|
27
|
+
* 2.5.x
|
28
|
+
* 2.6.x
|
29
|
+
* 2.7.x
|
28
30
|
|
29
31
|
Rails
|
30
|
-
* 3.2.x
|
31
|
-
* 4.1.x
|
32
32
|
* 4.2.x
|
33
33
|
* 5.0.x
|
34
|
+
* 5.1.x
|
35
|
+
* 5.2.x
|
36
|
+
* 6.0.x
|
34
37
|
|
35
38
|
Databases
|
36
39
|
* MySQL
|
@@ -53,6 +56,8 @@ bundle
|
|
53
56
|
|
54
57
|
Generate Rails schema migrations for the required tables:
|
55
58
|
|
59
|
+
> The default behavior is to store metadata in a json(b) column rather than a separate `double_entry_line_metadata` table. If you would like the old (1.x) behavior, you can add `--no-json-metadata`.
|
60
|
+
|
56
61
|
```sh
|
57
62
|
rails generate double_entry:install
|
58
63
|
```
|
@@ -67,7 +72,7 @@ rake db:migrate
|
|
67
72
|
## Interface
|
68
73
|
|
69
74
|
The entire API for recording financial transactions is available through a few
|
70
|
-
methods in the
|
75
|
+
methods in the [DoubleEntry](lib/double_entry.rb) module. For full details on
|
71
76
|
what the API provides, please view the documentation on these methods.
|
72
77
|
|
73
78
|
A configuration file should be used to define a set of accounts, and potential
|
@@ -88,12 +93,12 @@ than scoped accounts due to lock contention.
|
|
88
93
|
To get a particular account:
|
89
94
|
|
90
95
|
```ruby
|
91
|
-
account = DoubleEntry.account(:spending, :
|
96
|
+
account = DoubleEntry.account(:spending, scope: user)
|
92
97
|
```
|
93
98
|
|
94
99
|
(This actually returns an Account::Instance object.)
|
95
100
|
|
96
|
-
See
|
101
|
+
See [DoubleEntry::Account](lib/double_entry/account.rb) for more info.
|
97
102
|
|
98
103
|
|
99
104
|
### Balances
|
@@ -114,15 +119,15 @@ To transfer money between accounts:
|
|
114
119
|
```ruby
|
115
120
|
DoubleEntry.transfer(
|
116
121
|
Money.new(20_00),
|
117
|
-
:
|
118
|
-
:
|
119
|
-
:
|
122
|
+
from: one_account,
|
123
|
+
to: another_account,
|
124
|
+
code: :a_business_code_for_this_type_of_transfer,
|
120
125
|
)
|
121
126
|
```
|
122
127
|
|
123
128
|
The possible transfers, and their codes, should be defined in the configuration.
|
124
129
|
|
125
|
-
See
|
130
|
+
See [DoubleEntry::Transfer](lib/double_entry/transfer.rb) for more info.
|
126
131
|
|
127
132
|
### Metadata
|
128
133
|
|
@@ -131,10 +136,10 @@ You may associate arbitrary metadata with transfers, for example:
|
|
131
136
|
```ruby
|
132
137
|
DoubleEntry.transfer(
|
133
138
|
Money.new(20_00),
|
134
|
-
:
|
135
|
-
:
|
136
|
-
:
|
137
|
-
:
|
139
|
+
from: one_account,
|
140
|
+
to: another_account,
|
141
|
+
code: :a_business_code_for_this_type_of_transfer,
|
142
|
+
metadata: {key1: ['value 1', 'value 2'], key2: 'value 3'},
|
138
143
|
)
|
139
144
|
```
|
140
145
|
|
@@ -147,7 +152,7 @@ manually lock the accounts you're using:
|
|
147
152
|
```ruby
|
148
153
|
DoubleEntry.lock_accounts(account_a, account_b) do
|
149
154
|
# Perhaps transfer some money
|
150
|
-
DoubleEntry.transfer(Money.new(20_00), :
|
155
|
+
DoubleEntry.transfer(Money.new(20_00), from: account_a, to: account_b, code: :purchase)
|
151
156
|
# Perform other tasks that should be commited atomically with the transfer of funds...
|
152
157
|
end
|
153
158
|
```
|
@@ -155,8 +160,23 @@ end
|
|
155
160
|
The lock_accounts call generates a database transaction, which must be the
|
156
161
|
outermost transaction.
|
157
162
|
|
158
|
-
See
|
163
|
+
See [DoubleEntry::Locking](lib/double_entry/locking.rb) for more info.
|
164
|
+
|
165
|
+
### Account Checker/Fixer
|
166
|
+
|
167
|
+
Although it's unlikely, DoubleEntry has tools for checking accounts to make sure they tie out, and optionally fixing them if there is a discrepancy. It's recommended to run this in a scheduled job, somewhere on the order of hourly to daily, depending on transaction volume. Keep in mind that this process locks accounts as it inspects their balances, so it will have an prevent new transactions from being written for a short time.
|
159
168
|
|
169
|
+
Here are examples that could go in your scheduled job, depending on your needs:
|
170
|
+
|
171
|
+
```ruby
|
172
|
+
# Check all accounts & write the results to the double_entry_line_checks table
|
173
|
+
DoubleEntry::Validation::LineCheck.perform!
|
174
|
+
|
175
|
+
# Check & fix accounts (results will also be written to the table)
|
176
|
+
DoubleEntry::Validation::LineCheck.perform!(fixer: DoubleEntry::Validation::AccountFixer.new)
|
177
|
+
```
|
178
|
+
|
179
|
+
See [DoubleEntry::Validation](lib/double_entry/validation) for more info.
|
160
180
|
|
161
181
|
## Implementation
|
162
182
|
|
@@ -168,13 +188,12 @@ Lines table entries also store the running balance for the account. To retrieve
|
|
168
188
|
the current balance for an account, we find the most recent lines table entry
|
169
189
|
for it.
|
170
190
|
|
171
|
-
See
|
191
|
+
See [DoubleEntry::Line](lib/double_entry/line.rb) for more info.
|
172
192
|
|
173
193
|
AccountBalance records cache the current balance for each Account, and are used
|
174
194
|
to perform database level locking.
|
175
195
|
|
176
|
-
Transfer metadata is stored
|
177
|
-
See **DoubleEntry::LineMetadata** for more info.
|
196
|
+
Transfer metadata is stored in a json(b) column on both the source and destination lines of the transfer.
|
178
197
|
|
179
198
|
## Configuration
|
180
199
|
|
@@ -182,7 +201,7 @@ A configuration file should be used to define a set of accounts, optional scopes
|
|
182
201
|
the accounts, and permitted transfers between those accounts.
|
183
202
|
|
184
203
|
The configuration file should be kept in your application's load path. For example,
|
185
|
-
*config/initializers/double_entry.rb
|
204
|
+
*config/initializers/double_entry.rb*. By default, this file will be created when you run the installer, but you will need to fill out your accounts.
|
186
205
|
|
187
206
|
For example, the following specifies two accounts, savings and checking.
|
188
207
|
Each account is scoped by User (where User is an object with an ID), meaning
|
@@ -194,15 +213,21 @@ This configuration also specifies that money can be transferred between the two
|
|
194
213
|
require 'double_entry'
|
195
214
|
|
196
215
|
DoubleEntry.configure do |config|
|
216
|
+
# Use json(b) column in double_entry_lines table to store metadata instead of separate metadata table
|
217
|
+
config.json_metadata = true
|
218
|
+
|
197
219
|
config.define_accounts do |accounts|
|
198
|
-
user_scope =
|
199
|
-
|
200
|
-
|
220
|
+
user_scope = ->(user) do
|
221
|
+
raise 'not a User' unless user.class.name == 'User'
|
222
|
+
user.id
|
223
|
+
end
|
224
|
+
accounts.define(identifier: :savings, scope_identifier: user_scope, positive_only: true)
|
225
|
+
accounts.define(identifier: :checking, scope_identifier: user_scope)
|
201
226
|
end
|
202
227
|
|
203
228
|
config.define_transfers do |transfers|
|
204
|
-
transfers.define(:
|
205
|
-
transfers.define(:
|
229
|
+
transfers.define(from: :checking, to: :savings, code: :deposit)
|
230
|
+
transfers.define(from: :savings, to: :checking, code: :withdraw)
|
206
231
|
end
|
207
232
|
end
|
208
233
|
```
|
@@ -215,7 +240,7 @@ Transfers between accounts of different currencies are not allowed.
|
|
215
240
|
```ruby
|
216
241
|
DoubleEntry.configure do |config|
|
217
242
|
config.define_accounts do |accounts|
|
218
|
-
accounts.define(:
|
243
|
+
accounts.define(identifier: :savings, scope_identifier: user_scope, currency: 'AUD')
|
219
244
|
end
|
220
245
|
end
|
221
246
|
```
|
@@ -267,40 +292,78 @@ See the Github project [issues](https://github.com/envato/double_entry/issues).
|
|
267
292
|
|
268
293
|
## Development Environment Setup
|
269
294
|
|
295
|
+
We're using Docker to provide a convenient and consistent environment for
|
296
|
+
executing tests during development. This allows engineers to quickly set up
|
297
|
+
a productive development environment.
|
298
|
+
|
299
|
+
Note: Most development files are mounted in the Docker container. This
|
300
|
+
enables engineers to edit files in their favourite editor (on the host
|
301
|
+
OS) and have the changes immediately available in the Docker container
|
302
|
+
to be exercised.
|
303
|
+
|
304
|
+
One exception to this is the RSpec configuration. Changes to these files will
|
305
|
+
require a rebuild of the Docker image (step 2).
|
306
|
+
|
307
|
+
Prerequisites:
|
308
|
+
|
309
|
+
* Docker
|
310
|
+
* Docker Compose
|
311
|
+
* Git
|
312
|
+
|
270
313
|
1. Clone this repo.
|
271
314
|
|
272
315
|
```sh
|
273
316
|
git clone git@github.com:envato/double_entry.git && cd double_entry
|
274
317
|
```
|
275
318
|
|
276
|
-
2.
|
319
|
+
2. Build the Docker image we'll use to run tests
|
277
320
|
|
278
321
|
```sh
|
279
|
-
|
322
|
+
docker-compose build --pull double_entry
|
280
323
|
```
|
281
324
|
|
282
|
-
3.
|
283
|
-
|
325
|
+
3. Startup a container and attach a terminal. This will also start up a
|
326
|
+
MySQL and Postgres database.
|
284
327
|
|
285
328
|
```sh
|
286
|
-
|
329
|
+
docker-compose run --rm double_entry ash
|
287
330
|
```
|
288
331
|
|
289
|
-
|
332
|
+
4. Run the tests
|
290
333
|
|
291
334
|
```sh
|
292
|
-
|
335
|
+
DB=mysql bundle exec rspec
|
336
|
+
DB=postgres bundle exec rspec
|
337
|
+
DB=sqlite bundle exec rspec
|
293
338
|
```
|
294
339
|
|
295
|
-
|
340
|
+
5. When finished, exit the container terminal and shut down the databases.
|
296
341
|
|
297
342
|
```sh
|
298
|
-
|
299
|
-
|
343
|
+
exit
|
344
|
+
docker-compose down
|
300
345
|
```
|
301
346
|
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
347
|
+
## Contributors
|
348
|
+
|
349
|
+
Many thanks to those who have contributed to both this gem, and the library upon which it was based, over the years:
|
350
|
+
* Anthony Sellitti - @asellitt
|
351
|
+
* Clinton Forbes - @clinton
|
352
|
+
* Eaden McKee - @eadz
|
353
|
+
* Giancarlo Salamanca - @salamagd
|
354
|
+
* Jiexin Huang - @jiexinhuang
|
355
|
+
* Keith Pitt - @keithpitt
|
356
|
+
* Kelsey Hannan - @KelseyDH
|
357
|
+
* Mark Turnley - @rabidcarrot
|
358
|
+
* Martin Jagusch - @MJIO
|
359
|
+
* Martin Spickermann - @spickermann
|
360
|
+
* Mary-Anne Cosgrove - @macosgrove
|
361
|
+
* Orien Madgwick - @orien
|
362
|
+
* Pete Yandall - @notahat
|
363
|
+
* Rizal Muthi - @rizalmuthi
|
364
|
+
* Ryan Allen - @ryan-allen
|
365
|
+
* Samuel Cochran - @sj26
|
366
|
+
* Stefan Wrobel - @swrobel
|
367
|
+
* Stephanie Staub - @stephnacios
|
368
|
+
* Trung Lê - @joneslee85
|
369
|
+
* Vahid Ta'eed - @vahid
|
data/double_entry.gemspec
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
|
2
2
|
lib = File.expand_path('../lib', __FILE__)
|
3
3
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
4
|
|
@@ -7,67 +7,40 @@ require 'double_entry/version'
|
|
7
7
|
Gem::Specification.new do |gem|
|
8
8
|
gem.name = 'double_entry'
|
9
9
|
gem.version = DoubleEntry::VERSION
|
10
|
-
gem.authors = ['
|
11
|
-
gem.email = ['
|
10
|
+
gem.authors = ['Envato']
|
11
|
+
gem.email = ['rubygems@envato.com']
|
12
12
|
gem.summary = 'Tools to build your double entry financial ledger'
|
13
13
|
gem.homepage = 'https://github.com/envato/double_entry'
|
14
|
-
|
15
|
-
|
16
|
-
gem.
|
17
|
-
|
14
|
+
gem.license = 'MIT'
|
15
|
+
|
16
|
+
gem.metadata = {
|
17
|
+
'bug_tracker_uri' => 'https://github.com/envato/double_entry/issues',
|
18
|
+
'changelog_uri' => "https://github.com/envato/double_entry/blob/v#{gem.version}/CHANGELOG.md",
|
19
|
+
'documentation_uri' => "https://www.rubydoc.info/gems/double_entry/#{gem.version}",
|
20
|
+
'source_code_uri' => "https://github.com/envato/double_entry/tree/v#{gem.version}",
|
21
|
+
}
|
22
|
+
|
23
|
+
gem.files = `git ls-files -z`.split("\x0").select do |f|
|
24
|
+
f.match(%r{^(?:double_entry.gemspec|README|LICENSE|CHANGELOG|lib/)})
|
25
|
+
end
|
18
26
|
gem.require_paths = ['lib']
|
27
|
+
gem.required_ruby_version = '>= 2.3.0'
|
19
28
|
|
20
|
-
gem.post_install_message = <<-'POSTINSTALLMESSAGE'
|
21
|
-
Please note the following changes in DoubleEntry:
|
22
|
-
- New table `double_entry_line_metadata` has been introduced and is *required* for
|
23
|
-
aggregate reporting filtering to work. Existing applications must manually manage
|
24
|
-
this change via a migration similar to the following:
|
25
|
-
|
26
|
-
class CreateDoubleEntryLineMetadata < ActiveRecord::Migration
|
27
|
-
def self.up
|
28
|
-
create_table "#{DoubleEntry.table_name_prefix}line_metadata", :force => true do |t|
|
29
|
-
t.integer "line_id", :null => false
|
30
|
-
t.string "key", :limit => 48, :null => false
|
31
|
-
t.string "value", :limit => 64, :null => false
|
32
|
-
t.timestamps :null => false
|
33
|
-
end
|
34
|
-
|
35
|
-
add_index "#{DoubleEntry.table_name_prefix}line_metadata",
|
36
|
-
["line_id", "key", "value"],
|
37
|
-
:name => "lines_meta_line_id_key_value_idx"
|
38
|
-
end
|
39
|
-
|
40
|
-
def self.down
|
41
|
-
drop_table "#{DoubleEntry.table_name_prefix}line_metadata"
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
Please ensure that you update your database accordingly.
|
46
|
-
POSTINSTALLMESSAGE
|
47
|
-
|
48
|
-
gem.add_dependency 'money', '>= 6.0.0'
|
49
29
|
gem.add_dependency 'activerecord', '>= 3.2.0'
|
50
30
|
gem.add_dependency 'activesupport', '>= 3.2.0'
|
31
|
+
gem.add_dependency 'money', '>= 6.0.0'
|
51
32
|
gem.add_dependency 'railties', '>= 3.2.0'
|
52
33
|
|
53
|
-
gem.add_development_dependency 'rake'
|
54
34
|
gem.add_development_dependency 'mysql2'
|
55
35
|
gem.add_development_dependency 'pg'
|
36
|
+
gem.add_development_dependency 'rake'
|
56
37
|
gem.add_development_dependency 'sqlite3'
|
57
38
|
|
58
|
-
gem.add_development_dependency 'rspec'
|
59
|
-
gem.add_development_dependency 'rspec-its'
|
60
|
-
gem.add_development_dependency 'rspec-instafail'
|
61
39
|
gem.add_development_dependency 'database_cleaner'
|
40
|
+
gem.add_development_dependency 'factory_bot'
|
62
41
|
gem.add_development_dependency 'generator_spec'
|
63
|
-
gem.add_development_dependency '
|
64
|
-
gem.add_development_dependency '
|
65
|
-
gem.add_development_dependency 'test-unit'
|
66
|
-
|
67
|
-
gem.add_development_dependency 'pry'
|
68
|
-
gem.add_development_dependency 'pry-doc'
|
69
|
-
gem.add_development_dependency 'pry-byebug' if RUBY_VERSION >= '2.0.0'
|
70
|
-
gem.add_development_dependency 'pry-stack_explorer'
|
71
|
-
gem.add_development_dependency 'awesome_print'
|
42
|
+
gem.add_development_dependency 'rspec'
|
43
|
+
gem.add_development_dependency 'rspec-its'
|
72
44
|
gem.add_development_dependency 'ruby-prof'
|
45
|
+
gem.add_development_dependency 'timecop'
|
73
46
|
end
|
@@ -18,7 +18,7 @@ module ActiveRecord
|
|
18
18
|
yield
|
19
19
|
rescue ActiveRecord::StatementInvalid => exception
|
20
20
|
if exception.message =~ /deadlock/i || exception.message =~ /database is locked/i
|
21
|
-
ActiveSupport::Notifications.publish('deadlock_restart.
|
21
|
+
ActiveSupport::Notifications.publish('deadlock_restart.double_entry', exception: exception)
|
22
22
|
|
23
23
|
raise ActiveRecord::RestartTransaction
|
24
24
|
else
|
@@ -46,7 +46,7 @@ module ActiveRecord
|
|
46
46
|
yield
|
47
47
|
rescue ActiveRecord::StatementInvalid, ActiveRecord::RecordNotUnique => exception
|
48
48
|
if exception.message =~ /duplicate/i || exception.message =~ /ConstraintException/
|
49
|
-
ActiveSupport::Notifications.publish('duplicate_ignore.
|
49
|
+
ActiveSupport::Notifications.publish('duplicate_ignore.double_entry', exception: exception)
|
50
50
|
|
51
51
|
# Just ignore it...someone else has already created the record.
|
52
52
|
else
|
@@ -63,7 +63,7 @@ module ActiveRecord
|
|
63
63
|
if exception.message =~ /deadlock/i || exception.message =~ /database is locked/i
|
64
64
|
# Somebody else is in the midst of creating the record. We'd better
|
65
65
|
# retry, so we ensure they're done before we move on.
|
66
|
-
ActiveSupport::Notifications.publish('deadlock_retry.
|
66
|
+
ActiveSupport::Notifications.publish('deadlock_retry.double_entry', exception: exception)
|
67
67
|
|
68
68
|
retry
|
69
69
|
else
|
data/lib/double_entry.rb
CHANGED
@@ -4,6 +4,7 @@ require 'active_record/locking_extensions'
|
|
4
4
|
require 'active_record/locking_extensions/log_subscriber'
|
5
5
|
require 'active_support/all'
|
6
6
|
require 'money'
|
7
|
+
require 'rails/railtie'
|
7
8
|
|
8
9
|
require 'double_entry/version'
|
9
10
|
require 'double_entry/errors'
|
@@ -14,9 +15,6 @@ require 'double_entry/account_balance'
|
|
14
15
|
require 'double_entry/balance_calculator'
|
15
16
|
require 'double_entry/locking'
|
16
17
|
require 'double_entry/transfer'
|
17
|
-
require 'double_entry/line'
|
18
|
-
require 'double_entry/line_metadata'
|
19
|
-
require 'double_entry/reporting'
|
20
18
|
require 'double_entry/validation'
|
21
19
|
|
22
20
|
# Keep track of all the monies!
|
@@ -24,6 +22,16 @@ require 'double_entry/validation'
|
|
24
22
|
# This module provides the public interfaces for everything to do with
|
25
23
|
# transferring money around the system.
|
26
24
|
module DoubleEntry
|
25
|
+
class Railtie < ::Rails::Railtie
|
26
|
+
# So we can access user config from initializer in their app
|
27
|
+
config.after_initialize do
|
28
|
+
unless DoubleEntry.config.json_metadata
|
29
|
+
require 'double_entry/line_metadata'
|
30
|
+
end
|
31
|
+
require 'double_entry/line'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
27
35
|
class << self
|
28
36
|
# Get the particular account instance with the provided identifier and
|
29
37
|
# scope.
|
@@ -58,9 +66,9 @@ module DoubleEntry
|
|
58
66
|
# savings_account = DoubleEntry.account(:savings, scope: user)
|
59
67
|
# DoubleEntry.transfer(
|
60
68
|
# 20.dollars,
|
61
|
-
# :
|
62
|
-
# :
|
63
|
-
# :
|
69
|
+
# from: checking_account,
|
70
|
+
# to: savings_account,
|
71
|
+
# code: :save,
|
64
72
|
# )
|
65
73
|
# @param [Money] amount The quantity of money to transfer from one account
|
66
74
|
# to the other.
|
@@ -83,35 +91,35 @@ module DoubleEntry
|
|
83
91
|
# Get the current or historic balance of an account.
|
84
92
|
#
|
85
93
|
# @example Obtain the current balance of my checking account
|
86
|
-
# checking_account = DoubleEntry.account(:checking, :
|
94
|
+
# checking_account = DoubleEntry.account(:checking, scope: user)
|
87
95
|
# DoubleEntry.balance(checking_account)
|
88
96
|
# @example Obtain the current balance of my checking account (without account or user model)
|
89
|
-
# DoubleEntry.balance(:checking, :
|
97
|
+
# DoubleEntry.balance(:checking, scope: user_id)
|
90
98
|
# @example Obtain a historic balance of my checking account
|
91
|
-
# checking_account = DoubleEntry.account(:checking, :
|
92
|
-
# DoubleEntry.balance(checking_account, :
|
99
|
+
# checking_account = DoubleEntry.account(:checking, scope: user)
|
100
|
+
# DoubleEntry.balance(checking_account, at: Time.new(2012, 1, 1))
|
93
101
|
# @example Obtain the net balance of my checking account during may
|
94
|
-
# checking_account = DoubleEntry.account(:checking, :
|
102
|
+
# checking_account = DoubleEntry.account(:checking, scope: user)
|
95
103
|
# DoubleEntry.balance(
|
96
104
|
# checking_account,
|
97
|
-
# :
|
98
|
-
# :
|
105
|
+
# from: Time.new(2012, 5, 1, 0, 0, 0),
|
106
|
+
# to: Time.new(2012, 5, 31, 23, 59, 59),
|
99
107
|
# )
|
100
108
|
# @example Obtain the balance of salary deposits made to my checking account during may
|
101
|
-
# checking_account = DoubleEntry.account(:checking, :
|
109
|
+
# checking_account = DoubleEntry.account(:checking, scope: user)
|
102
110
|
# DoubleEntry.balance(
|
103
111
|
# checking_account,
|
104
|
-
# :
|
105
|
-
# :
|
106
|
-
# :
|
112
|
+
# code: :salary,
|
113
|
+
# from: Time.new(2012, 5, 1, 0, 0, 0),
|
114
|
+
# to: Time.new(2012, 5, 31, 23, 59, 59),
|
107
115
|
# )
|
108
116
|
# @example Obtain the balance of salary & lottery deposits made to my checking account during may
|
109
|
-
# checking_account = DoubleEntry.account(:checking, :
|
117
|
+
# checking_account = DoubleEntry.account(:checking, scope: user)
|
110
118
|
# DoubleEntry.balance(
|
111
119
|
# checking_account,
|
112
|
-
# :
|
113
|
-
# :
|
114
|
-
# :
|
120
|
+
# codes: [ :salary, :lottery ],
|
121
|
+
# from: Time.new(2012, 5, 1, 0, 0, 0),
|
122
|
+
# to: Time.new(2012, 5, 31, 23, 59, 59),
|
115
123
|
# )
|
116
124
|
# @param [DoubleEntry::Account:Instance, Symbol] account Find the balance
|
117
125
|
# for this account
|