double_entry 2.0.0.beta5 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3fac339d10077ee1c807e16d37f4aa6bd7ad4d6179f8f94b4ad114085e83b83a
4
- data.tar.gz: 5f50033972098dfada8195c0bd17923005f2e0601ed1a694cf25c43080d2f939
3
+ metadata.gz: 496d452f002312016c033d18ba23257b677a07072fdea0d5dfc85f77563a3cc3
4
+ data.tar.gz: a53729a0facc3b920c70c87050cacd93743cea8f2895974fbddbd32925126807
5
5
  SHA512:
6
- metadata.gz: 2fb0041f4cf1af34416a5af798d1b7b32675565f39f9f0abec80d40d1ad30159dc1fe716427a41a43c1c608f9b1cd8ff63c998ee2c974895a28c873ba0e7250e
7
- data.tar.gz: 66be7ef0d59ca846b5c9c588d68dd72be1577c2ef03069fc93d148ec7bc821aaf265b66ae75ad1cece6649d1605412f41f3e81284ba2d29c513804f6484af368
6
+ metadata.gz: 626f6ee8bc46253bec15f21ef9a619d894fc5748dca7aa2a07518032777f0c2313889393f7d8d31f974bdd11c26aa309b329f816e1648915978131e228a9d740
7
+ data.tar.gz: bf503a7c96e23fcde06a04fc64e38799341a60be9c73443eda3d3ffe2583191f33e3ddfb61acb349e4062b1ae568784a49a0d3bbb54c1efce70ccc966057e395
data/CHANGELOG.md CHANGED
@@ -7,7 +7,41 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
- ## [2.0.0.beta5] - 2020-01-25
10
+ [Unreleased]: https://github.com/envato/double_entry/compare/v2.0.0...HEAD
11
+
12
+ ## [2.0.0] - 2023-10-25
13
+
14
+ ### Fixed
15
+
16
+ - Ensure LineCheck and AccountFixer can work correctly with unscoped accounts ([#207]).
17
+ - Fixes for running on Ruby 3 ([#212]).
18
+
19
+ ### Changed
20
+
21
+ - Return `[credit, debit]` from `DoubleEntry.transfer` ([#190]).
22
+ - Run the test suite against Rails 6.1, 7.0, 7.1, and Ruby 3.1, 3.2 ([#203], [#214], [#217]).
23
+ - Migrate CI to run on GitHub Actions ([#205])
24
+
25
+ ### Removed
26
+
27
+ - Removed support for Rails < 6.1, and Ruby < 3.0 ([#215], [#217]).
28
+
29
+ ### Added
30
+
31
+ - Add `credit` and `debit` scopes to the `Line` model ([#192]).
32
+
33
+ [2.0.0]: https://github.com/envato/double_entry/compare/v2.0.0.beta5...v2.0.0
34
+ [#190]: https://github.com/envato/double_entry/pull/190
35
+ [#192]: https://github.com/envato/double_entry/pull/192
36
+ [#203]: https://github.com/envato/double_entry/pull/203
37
+ [#205]: https://github.com/envato/double_entry/pull/205
38
+ [#207]: https://github.com/envato/double_entry/pull/207
39
+ [#212]: https://github.com/envato/double_entry/pull/212
40
+ [#214]: https://github.com/envato/double_entry/pull/214
41
+ [#215]: https://github.com/envato/double_entry/pull/215
42
+ [#217]: https://github.com/envato/double_entry/pull/217
43
+
44
+ ## [2.0.0.beta5] - 2021-02-24
11
45
 
12
46
  ### Changed
13
47
 
@@ -471,7 +505,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
471
505
 
472
506
  - Library released as Open Source!
473
507
 
474
- [Unreleased]: https://github.com/envato/double_entry/compare/v2.0.0.beta5...HEAD
475
508
  [2.0.0.beta5]: https://github.com/envato/double_entry/compare/v2.0.0.beta4...v2.0.0.beta5
476
509
  [2.0.0.beta4]: https://github.com/envato/double_entry/compare/v2.0.0.beta3...v2.0.0.beta4
477
510
  [2.0.0.beta3]: https://github.com/envato/double_entry/compare/v2.0.0.beta2...v2.0.0.beta3
data/README.md CHANGED
@@ -3,7 +3,7 @@
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=master)](https://travis-ci.org/envato/double_entry)
6
+ [![Build Status](https://github.com/envato/double_entry/actions/workflows/ci-workflow.yml/badge.svg)](https://github.com/envato/double_entry/actions/workflows/ci-workflow.yml)
7
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)
@@ -13,7 +13,7 @@ Keep track of all the monies!
13
13
  DoubleEntry is an accounting system based on the principles of a
14
14
  [Double-entry Bookkeeping](http://en.wikipedia.org/wiki/Double-entry_bookkeeping_system)
15
15
  system. While this gem acts like a double-entry bookkeeping system, as it creates
16
- two entries in the database for each transfer, it does *not* enforce accounting rules.
16
+ two entries in the database for each transfer, it does *not* enforce accounting rules, other than optionally ensuring a balance is positive, and through an allowlist of approved transfers.
17
17
 
18
18
  DoubleEntry uses the [Money gem](https://github.com/RubyMoney/money) to encapsulate operations on currency values.
19
19
 
@@ -22,18 +22,15 @@ 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.3.x
26
- * 2.4.x
27
- * 2.5.x
28
- * 2.6.x
29
25
  * 2.7.x
26
+ * 3.0.x
27
+ * 3.1.x
28
+ * 3.2.x
30
29
 
31
30
  Rails
32
- * 4.2.x
33
- * 5.0.x
34
- * 5.1.x
35
- * 5.2.x
36
31
  * 6.0.x
32
+ * 6.1.x
33
+ * 7.0.x
37
34
 
38
35
  Databases
39
36
  * MySQL
@@ -164,7 +161,15 @@ See [DoubleEntry::Locking](lib/double_entry/locking.rb) for more info.
164
161
 
165
162
  ### Account Checker/Fixer
166
163
 
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.
164
+ DoubleEntry tries really hard to make sure that stored account balances reflect the running balances from the `double_entry_lines` table, but there is always the unlikely possibility that something will go wrong and the calculated balance might get out of sync with the actual running balance of the lines.
165
+
166
+ DoubleEntry therefore provides a couple of tools to give you some confidence that things are working as expected.
167
+
168
+ The `DoubleEntry::Validation::LineCheck` will check the `double_entry_lines` table to make sure that the `balance` column correctly reflects the calculated running balance, and that the `double_entry_account_balances` table has the correct value in the `balance` column. If either one of these turn out to be incorrect then it will write an entry into the `double_entry_line_checks` table reporting on the differences.
169
+
170
+ You can alternatively pass a `fixer` to the `DoubleEntry::Validation::LineCheck.perform` method which will try and correct the balances. This gem provides the `DoubleEntry::Validation::AccountFixer` class which will correct the balance if it's out of sync.
171
+
172
+ Using these classes is optional and both are provided for additional safety checks. If you want to make use of them then it's recommended to run them 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 prevent new transactions from being written for a short time.
168
173
 
169
174
  Here are examples that could go in your scheduled job, depending on your needs:
170
175
 
@@ -245,6 +250,20 @@ DoubleEntry.configure do |config|
245
250
  end
246
251
  ```
247
252
 
253
+ ## Testing with RSpec
254
+
255
+ Transfering money needs to be run as a top level transaction. This conflicts with RSpec's default behavior of creating a new transaction for every test, causing an exception of type `DoubleEntry::Locking::LockMustBeOutermostTransaction` to be raised. This behavior may be disabled by adding the following lines into your `rails_helper.rb`.
256
+
257
+ ```ruby
258
+ RSpec.configure do |config|
259
+ # ...
260
+ # This first line should already be there. You will need to add the second one
261
+ config.use_transactional_fixtures = true
262
+ DoubleEntry::Locking.configuration.running_inside_transactional_fixtures = true
263
+ # ...
264
+ end
265
+ ```
266
+
248
267
  ## Jackhammer
249
268
 
250
269
  Run a concurrency test on the code.
data/double_entry.gemspec CHANGED
@@ -24,12 +24,12 @@ Gem::Specification.new do |gem|
24
24
  f.match(%r{^(?:double_entry.gemspec|README|LICENSE|CHANGELOG|lib/)})
25
25
  end
26
26
  gem.require_paths = ['lib']
27
- gem.required_ruby_version = '>= 2.3.0'
27
+ gem.required_ruby_version = '>= 3'
28
28
 
29
- gem.add_dependency 'activerecord', '>= 3.2.0'
30
- gem.add_dependency 'activesupport', '>= 3.2.0'
29
+ gem.add_dependency 'activerecord', '>= 6.1.0'
30
+ gem.add_dependency 'activesupport', '>= 6.1.0'
31
31
  gem.add_dependency 'money', '>= 6.0.0'
32
- gem.add_dependency 'railties', '>= 3.2.0'
32
+ gem.add_dependency 'railties', '>= 6.1.0'
33
33
 
34
34
  gem.add_development_dependency 'mysql2'
35
35
  gem.add_development_dependency 'pg'
@@ -57,6 +57,8 @@ module DoubleEntry
57
57
  class Line < ActiveRecord::Base
58
58
  belongs_to :detail, polymorphic: true, required: false
59
59
  has_many :metadata, class_name: 'DoubleEntry::LineMetadata' unless -> { DoubleEntry.config.json_metadata }
60
+ scope :credits, -> { where('amount > 0') }
61
+ scope :debits, -> { where('amount < 0') }
60
62
  scope :with_id_greater_than, ->(id) { where('id > ?', id) }
61
63
 
62
64
  def amount
@@ -76,6 +76,7 @@ module DoubleEntry
76
76
  end
77
77
 
78
78
  def process(amount, options)
79
+ credit = debit = nil
79
80
  from_account = options[:from]
80
81
  to_account = options[:to]
81
82
  code = options[:code]
@@ -91,6 +92,7 @@ module DoubleEntry
91
92
  credit, debit = create_lines(amount, code, detail, from_account, to_account, metadata)
92
93
  create_line_metadata(credit, debit, metadata) if metadata && !DoubleEntry.config.json_metadata
93
94
  end
95
+ [credit, debit]
94
96
  end
95
97
 
96
98
  def create_lines(amount, code, detail, from_account, to_account, metadata)
@@ -23,7 +23,7 @@ module DoubleEntry
23
23
  def lines_for_account(account)
24
24
  Line.where(
25
25
  account: account.identifier.to_s,
26
- scope: account.scope_identity.to_s
26
+ scope: account.scope_identity&.to_s
27
27
  ).order(:id)
28
28
  end
29
29
 
@@ -48,34 +48,50 @@ module DoubleEntry
48
48
  end
49
49
 
50
50
  def running_balance_correct?(line, log)
51
- # Another work around for the MySQL 5.1 query optimiser bug that causes the ORDER BY
52
- # on the query to fail in some circumstances, resulting in an old balance being
53
- # returned. This was biting us intermittently in spec runs.
54
- # See http://bugs.mysql.com/bug.php?id=51431
55
- force_index = if Line.connection.adapter_name.match(/mysql/i)
56
- 'FORCE INDEX (lines_scope_account_id_idx)'
57
- else
58
- ''
59
- end
51
+ previous_line = find_previous_line(line.account.identifier.to_s, line.scope, line.id)
52
+
53
+ previous_balance = previous_line.length == 1 ? previous_line[0].balance : Money.zero(line.account.currency)
54
+
55
+ if line.balance != (line.amount + previous_balance)
56
+ log << line_error_message(line, previous_line, previous_balance)
57
+ end
58
+
59
+ line.balance == previous_balance + line.amount
60
+ end
60
61
 
62
+ def find_previous_line(identifier, scope, id)
61
63
  # yes, it needs to be find_by_sql, because any other find will be affected
62
64
  # by the find_each call in perform!
63
- previous_line = Line.find_by_sql([<<-SQL, line.account.identifier.to_s, line.scope, line.id])
65
+
66
+ if scope.nil?
67
+ Line.find_by_sql([<<-SQL, identifier, id])
68
+ SELECT * FROM #{Line.quoted_table_name} #{force_index}
69
+ WHERE account = ?
70
+ AND scope IS NULL
71
+ AND id < ?
72
+ ORDER BY id DESC
73
+ LIMIT 1
74
+ SQL
75
+ else
76
+ Line.find_by_sql([<<-SQL, identifier, scope, id])
64
77
  SELECT * FROM #{Line.quoted_table_name} #{force_index}
65
78
  WHERE account = ?
66
79
  AND scope = ?
67
80
  AND id < ?
68
81
  ORDER BY id DESC
69
82
  LIMIT 1
70
- SQL
71
-
72
- previous_balance = previous_line.length == 1 ? previous_line[0].balance : Money.zero(line.account.currency)
73
-
74
- if line.balance != (line.amount + previous_balance)
75
- log << line_error_message(line, previous_line, previous_balance)
83
+ SQL
76
84
  end
85
+ end
77
86
 
78
- line.balance == previous_balance + line.amount
87
+ def force_index
88
+ # Another work around for the MySQL 5.1 query optimiser bug that causes the ORDER BY
89
+ # on the query to fail in some circumstances, resulting in an old balance being
90
+ # returned. This was biting us intermittently in spec runs.
91
+ # See http://bugs.mysql.com/bug.php?id=51431
92
+ return '' unless Line.connection.adapter_name.match(/mysql/i)
93
+
94
+ 'FORCE INDEX (lines_scope_account_id_idx)'
79
95
  end
80
96
 
81
97
  def line_error_message(line, previous_line, previous_balance)
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module DoubleEntry
4
- VERSION = '2.0.0.beta5'
4
+ VERSION = '2.0.0'
5
5
  end
data/lib/double_entry.rb CHANGED
@@ -64,7 +64,7 @@ module DoubleEntry
64
64
  # @example Transfer $20 from a user's checking to savings account
65
65
  # checking_account = DoubleEntry.account(:checking, scope: user)
66
66
  # savings_account = DoubleEntry.account(:savings, scope: user)
67
- # DoubleEntry.transfer(
67
+ # credit, debit = DoubleEntry.transfer(
68
68
  # 20.dollars,
69
69
  # from: checking_account,
70
70
  # to: savings_account,
@@ -80,6 +80,7 @@ module DoubleEntry
80
80
  # type of transfer. As specified in the transfer configuration.
81
81
  # @option options :detail [ActiveRecord::Base] ActiveRecord model
82
82
  # associated (via a polymorphic association) with the transfer.
83
+ # @return [[Line, Line]] The credit & debit (in that order) created by the transfer
83
84
  # @raise [DoubleEntry::TransferIsNegative] The amount is less than zero.
84
85
  # @raise [DoubleEntry::TransferNotAllowed] A transfer between these
85
86
  # accounts with the provided code is not allowed. Check configuration.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: double_entry
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0.beta5
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Envato
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-02-24 00:00:00.000000000 Z
11
+ date: 2023-10-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 3.2.0
19
+ version: 6.1.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: 3.2.0
26
+ version: 6.1.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: activesupport
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: 3.2.0
33
+ version: 6.1.0
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: 3.2.0
40
+ version: 6.1.0
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: money
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -58,14 +58,14 @@ dependencies:
58
58
  requirements:
59
59
  - - ">="
60
60
  - !ruby/object:Gem::Version
61
- version: 3.2.0
61
+ version: 6.1.0
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
- version: 3.2.0
68
+ version: 6.1.0
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: mysql2
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -220,7 +220,7 @@ dependencies:
220
220
  - - ">="
221
221
  - !ruby/object:Gem::Version
222
222
  version: '0'
223
- description:
223
+ description:
224
224
  email:
225
225
  - rubygems@envato.com
226
226
  executables: []
@@ -256,10 +256,10 @@ licenses:
256
256
  - MIT
257
257
  metadata:
258
258
  bug_tracker_uri: https://github.com/envato/double_entry/issues
259
- changelog_uri: https://github.com/envato/double_entry/blob/v2.0.0.beta5/CHANGELOG.md
260
- documentation_uri: https://www.rubydoc.info/gems/double_entry/2.0.0.beta5
261
- source_code_uri: https://github.com/envato/double_entry/tree/v2.0.0.beta5
262
- post_install_message:
259
+ changelog_uri: https://github.com/envato/double_entry/blob/v2.0.0/CHANGELOG.md
260
+ documentation_uri: https://www.rubydoc.info/gems/double_entry/2.0.0
261
+ source_code_uri: https://github.com/envato/double_entry/tree/v2.0.0
262
+ post_install_message:
263
263
  rdoc_options: []
264
264
  require_paths:
265
265
  - lib
@@ -267,16 +267,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
267
267
  requirements:
268
268
  - - ">="
269
269
  - !ruby/object:Gem::Version
270
- version: 2.3.0
270
+ version: '3'
271
271
  required_rubygems_version: !ruby/object:Gem::Requirement
272
272
  requirements:
273
- - - ">"
273
+ - - ">="
274
274
  - !ruby/object:Gem::Version
275
- version: 1.3.1
275
+ version: '0'
276
276
  requirements: []
277
- rubyforge_project:
278
- rubygems_version: 2.7.3
279
- signing_key:
277
+ rubygems_version: 3.4.21
278
+ signing_key:
280
279
  specification_version: 4
281
280
  summary: Tools to build your double entry financial ledger
282
281
  test_files: []