double_entry 1.0.1 → 2.0.0.beta1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/CHANGELOG.md +432 -0
- data/README.md +36 -9
- data/double_entry.gemspec +20 -48
- data/lib/active_record/locking_extensions.rb +3 -3
- data/lib/active_record/locking_extensions/log_subscriber.rb +1 -1
- data/lib/double_entry/account.rb +38 -45
- data/lib/double_entry/account_balance.rb +18 -1
- data/lib/double_entry/errors.rb +13 -13
- data/lib/double_entry/line.rb +3 -2
- data/lib/double_entry/reporting.rb +26 -38
- data/lib/double_entry/reporting/aggregate.rb +43 -23
- data/lib/double_entry/reporting/aggregate_array.rb +16 -13
- data/lib/double_entry/reporting/line_aggregate.rb +3 -2
- data/lib/double_entry/reporting/line_aggregate_filter.rb +8 -10
- data/lib/double_entry/reporting/line_metadata_filter.rb +33 -0
- data/lib/double_entry/transfer.rb +33 -27
- 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 +22 -40
- data/lib/double_entry/version.rb +1 -1
- data/lib/generators/double_entry/install/install_generator.rb +7 -1
- data/lib/generators/double_entry/install/templates/migration.rb +27 -25
- metadata +33 -243
- 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/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/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,39 @@ 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
14
|
|
15
|
-
gem.
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
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
|
15
|
+
gem.metadata = {
|
16
|
+
'bug_tracker_uri' => 'https://github.com/envato/double_entry/issues',
|
17
|
+
'changelog_uri' => 'https://github.com/envato/double_entry/blob/master/CHANGELOG.md',
|
18
|
+
'documentation_uri' => 'https://www.rubydoc.info/github/envato/double_entry/',
|
19
|
+
'source_code_uri' => 'https://github.com/envato/double_entry',
|
20
|
+
}
|
39
21
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
Please ensure that you update your database accordingly.
|
46
|
-
POSTINSTALLMESSAGE
|
22
|
+
gem.files = `git ls-files -z`.split("\x0").select do |f|
|
23
|
+
f.match(%r{^(?:double_entry.gemspec|README|LICENSE|CHANGELOG|lib/)})
|
24
|
+
end
|
25
|
+
gem.require_paths = ['lib']
|
26
|
+
gem.required_ruby_version = '>= 2.2.0'
|
47
27
|
|
48
|
-
gem.add_dependency 'money', '>= 6.0.0'
|
49
28
|
gem.add_dependency 'activerecord', '>= 3.2.0'
|
50
29
|
gem.add_dependency 'activesupport', '>= 3.2.0'
|
30
|
+
gem.add_dependency 'money', '>= 6.0.0'
|
51
31
|
gem.add_dependency 'railties', '>= 3.2.0'
|
52
32
|
|
53
|
-
gem.add_development_dependency 'rake'
|
54
33
|
gem.add_development_dependency 'mysql2'
|
55
34
|
gem.add_development_dependency 'pg'
|
35
|
+
gem.add_development_dependency 'rake'
|
56
36
|
gem.add_development_dependency 'sqlite3'
|
57
37
|
|
58
|
-
gem.add_development_dependency 'rspec'
|
59
|
-
gem.add_development_dependency 'rspec-its'
|
60
|
-
gem.add_development_dependency 'rspec-instafail'
|
61
38
|
gem.add_development_dependency 'database_cleaner'
|
39
|
+
gem.add_development_dependency 'factory_bot'
|
62
40
|
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'
|
41
|
+
gem.add_development_dependency 'rspec'
|
42
|
+
gem.add_development_dependency 'rspec-its'
|
72
43
|
gem.add_development_dependency 'ruby-prof'
|
44
|
+
gem.add_development_dependency 'timecop'
|
73
45
|
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/account.rb
CHANGED
@@ -1,79 +1,67 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
+
require 'forwardable'
|
3
|
+
|
2
4
|
module DoubleEntry
|
3
5
|
class Account
|
4
6
|
class << self
|
5
|
-
|
7
|
+
attr_accessor :scope_identifier_max_length, :account_identifier_max_length
|
8
|
+
attr_writer :accounts
|
6
9
|
|
7
10
|
# @api private
|
8
11
|
def accounts
|
9
12
|
@accounts ||= Set.new
|
10
13
|
end
|
11
14
|
|
12
|
-
# @api private
|
13
|
-
def scope_identifier_max_length
|
14
|
-
@scope_identifier_max_length ||= 23
|
15
|
-
end
|
16
|
-
|
17
|
-
# @api private
|
18
|
-
def account_identifier_max_length
|
19
|
-
@account_identifier_max_length ||= 31
|
20
|
-
end
|
21
|
-
|
22
15
|
# @api private
|
23
16
|
def account(identifier, options = {})
|
24
|
-
account = accounts.find(identifier, options[:scope].present?)
|
25
|
-
Instance.new(:account => account, :scope => options[:scope])
|
17
|
+
account = accounts.find(identifier, (options[:scope].present? || options[:scope_identity].present?))
|
18
|
+
Instance.new(:account => account, :scope => options[:scope], :scope_identity => options[:scope_identity])
|
26
19
|
end
|
27
20
|
|
28
21
|
# @api private
|
29
22
|
def currency(identifier)
|
30
|
-
accounts.
|
23
|
+
accounts.find_without_scope(identifier).try(:currency)
|
31
24
|
end
|
32
25
|
end
|
33
26
|
|
34
27
|
# @api private
|
35
|
-
class Set
|
28
|
+
class Set
|
29
|
+
extend Forwardable
|
30
|
+
|
31
|
+
delegate [:each, :map] => :all
|
32
|
+
|
36
33
|
def define(attributes)
|
37
|
-
|
34
|
+
Account.new(attributes).tap do |account|
|
35
|
+
if find_without_scope(account.identifier)
|
36
|
+
fail DuplicateAccount
|
37
|
+
else
|
38
|
+
backing_collection[account.identifier] = account
|
39
|
+
end
|
40
|
+
end
|
38
41
|
end
|
39
42
|
|
40
43
|
def find(identifier, scoped)
|
41
|
-
found_account =
|
42
|
-
account.identifier == identifier && account.scoped? == scoped
|
43
|
-
end
|
44
|
-
fail UnknownAccount, "account: #{identifier} scoped?: #{scoped}" unless found_account
|
45
|
-
found_account
|
46
|
-
end
|
44
|
+
found_account = find_without_scope(identifier)
|
47
45
|
|
48
|
-
|
49
|
-
|
50
|
-
fail DuplicateAccount
|
46
|
+
if found_account && found_account.scoped? == scoped
|
47
|
+
found_account
|
51
48
|
else
|
52
|
-
|
49
|
+
fail UnknownAccount, "account: #{identifier} scoped?: #{scoped}"
|
53
50
|
end
|
54
51
|
end
|
55
52
|
|
56
|
-
def
|
57
|
-
|
53
|
+
def find_without_scope(identifier)
|
54
|
+
backing_collection[identifier]
|
58
55
|
end
|
59
|
-
end
|
60
56
|
|
61
|
-
|
62
|
-
|
63
|
-
@active_record_class = active_record_class
|
57
|
+
def all
|
58
|
+
backing_collection.values
|
64
59
|
end
|
65
60
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
value.id
|
71
|
-
when String, Fixnum
|
72
|
-
value
|
73
|
-
else
|
74
|
-
fail AccountScopeMismatchError, "Expected instance of `#{@active_record_class}`, received instance of `#{value.class}`"
|
75
|
-
end
|
76
|
-
end
|
61
|
+
private
|
62
|
+
|
63
|
+
def backing_collection
|
64
|
+
@backing_collection ||= Hash.new
|
77
65
|
end
|
78
66
|
end
|
79
67
|
|
@@ -84,10 +72,15 @@ module DoubleEntry
|
|
84
72
|
def initialize(args)
|
85
73
|
@account = args[:account]
|
86
74
|
@scope = args[:scope]
|
75
|
+
@scope_identity = args[:scope_identity]
|
87
76
|
ensure_scope_is_valid
|
88
77
|
end
|
89
78
|
|
90
79
|
def scope_identity
|
80
|
+
@scope_identity || call_scope_identifier
|
81
|
+
end
|
82
|
+
|
83
|
+
def call_scope_identifier
|
91
84
|
scope_identifier.call(scope).to_s if scoped?
|
92
85
|
end
|
93
86
|
|
@@ -138,7 +131,7 @@ module DoubleEntry
|
|
138
131
|
|
139
132
|
def ensure_scope_is_valid
|
140
133
|
identity = scope_identity
|
141
|
-
if identity && identity.length > Account.scope_identifier_max_length
|
134
|
+
if identity && Account.scope_identifier_max_length && identity.length > Account.scope_identifier_max_length
|
142
135
|
fail ScopeIdentifierTooLongError,
|
143
136
|
"scope identifier '#{identity}' is too long. Please limit it to #{Account.scope_identifier_max_length} characters."
|
144
137
|
end
|
@@ -153,7 +146,7 @@ module DoubleEntry
|
|
153
146
|
@positive_only = args[:positive_only]
|
154
147
|
@negative_only = args[:negative_only]
|
155
148
|
@currency = args[:currency] || Money.default_currency
|
156
|
-
if identifier.length > Account.account_identifier_max_length
|
149
|
+
if Account.account_identifier_max_length && identifier.length > Account.account_identifier_max_length
|
157
150
|
fail AccountIdentifierTooLongError,
|
158
151
|
"account identifier '#{identifier}' is too long. Please limit it to #{Account.account_identifier_max_length} characters."
|
159
152
|
end
|
@@ -25,7 +25,7 @@ module DoubleEntry
|
|
25
25
|
end
|
26
26
|
|
27
27
|
def account
|
28
|
-
DoubleEntry.account(self[:account].to_sym, :
|
28
|
+
DoubleEntry.account(self[:account].to_sym, :scope_identity => self[:scope])
|
29
29
|
end
|
30
30
|
|
31
31
|
def self.find_by_account(account, options = {})
|
@@ -33,5 +33,22 @@ module DoubleEntry
|
|
33
33
|
scope = scope.lock(true) if options[:lock]
|
34
34
|
scope.first
|
35
35
|
end
|
36
|
+
|
37
|
+
# Identify the scopes with the given account identifier holding at least
|
38
|
+
# the provided minimum balance.
|
39
|
+
#
|
40
|
+
# @example Find users with at least $1,000,000 in their savings accounts
|
41
|
+
# DoubleEntry::AccountBalance.scopes_with_minimum_balance_for_account(
|
42
|
+
# 1_000_000.dollars,
|
43
|
+
# :savings,
|
44
|
+
# ) # might return the user ids: [ '1423', '12232', '34729' ]
|
45
|
+
# @param [Money] minimum_balance Minimum account balance a scope must have
|
46
|
+
# to be included in the result set.
|
47
|
+
# @param [Symbol] account_identifier
|
48
|
+
# @return [Array<String>] Scopes
|
49
|
+
#
|
50
|
+
def self.scopes_with_minimum_balance_for_account(minimum_balance, account_identifier)
|
51
|
+
where(account: account_identifier).where('balance >= ?', minimum_balance.fractional).pluck(:scope)
|
52
|
+
end
|
36
53
|
end
|
37
54
|
end
|
data/lib/double_entry/errors.rb
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
module DoubleEntry
|
3
|
-
class
|
4
|
-
class
|
5
|
-
class
|
6
|
-
class
|
7
|
-
class
|
8
|
-
class
|
9
|
-
class
|
10
|
-
class
|
11
|
-
class
|
12
|
-
class
|
13
|
-
class
|
14
|
-
class
|
15
|
-
class
|
3
|
+
class DoubleEntryError < RuntimeError; end
|
4
|
+
class UnknownAccount < DoubleEntryError; end
|
5
|
+
class AccountIdentifierTooLongError < DoubleEntryError; end
|
6
|
+
class ScopeIdentifierTooLongError < DoubleEntryError; end
|
7
|
+
class TransferNotAllowed < DoubleEntryError; end
|
8
|
+
class TransferIsNegative < DoubleEntryError; end
|
9
|
+
class TransferCodeTooLongError < DoubleEntryError; end
|
10
|
+
class DuplicateAccount < DoubleEntryError; end
|
11
|
+
class DuplicateTransfer < DoubleEntryError; end
|
12
|
+
class AccountWouldBeSentNegative < DoubleEntryError; end
|
13
|
+
class AccountWouldBeSentPositiveError < DoubleEntryError; end
|
14
|
+
class MismatchedCurrencies < DoubleEntryError; end
|
15
|
+
class MissingAccountError < DoubleEntryError; end
|
16
16
|
end
|
data/lib/double_entry/line.rb
CHANGED
@@ -57,6 +57,7 @@ module DoubleEntry
|
|
57
57
|
class Line < ActiveRecord::Base
|
58
58
|
belongs_to :detail, :polymorphic => true
|
59
59
|
has_many :metadata, :class_name => 'DoubleEntry::LineMetadata'
|
60
|
+
scope :with_id_greater_than, ->(id) { where('id > ?', id) }
|
60
61
|
|
61
62
|
def amount
|
62
63
|
self[:amount] && Money.new(self[:amount], currency)
|
@@ -101,7 +102,7 @@ module DoubleEntry
|
|
101
102
|
end
|
102
103
|
|
103
104
|
def account
|
104
|
-
DoubleEntry.account(self[:account].to_sym, :
|
105
|
+
DoubleEntry.account(self[:account].to_sym, :scope_identity => scope)
|
105
106
|
end
|
106
107
|
|
107
108
|
def currency
|
@@ -116,7 +117,7 @@ module DoubleEntry
|
|
116
117
|
end
|
117
118
|
|
118
119
|
def partner_account
|
119
|
-
DoubleEntry.account(self[:partner_account].to_sym, :
|
120
|
+
DoubleEntry.account(self[:partner_account].to_sym, :scope_identity => partner_scope)
|
120
121
|
end
|
121
122
|
|
122
123
|
def partner
|
@@ -9,6 +9,7 @@ require 'double_entry/reporting/month_range'
|
|
9
9
|
require 'double_entry/reporting/year_range'
|
10
10
|
require 'double_entry/reporting/line_aggregate'
|
11
11
|
require 'double_entry/reporting/line_aggregate_filter'
|
12
|
+
require 'double_entry/reporting/line_metadata_filter'
|
12
13
|
require 'double_entry/reporting/time_range_array'
|
13
14
|
|
14
15
|
module DoubleEntry
|
@@ -62,9 +63,12 @@ module DoubleEntry
|
|
62
63
|
# @param [Symbol] code The application specific code for the type of
|
63
64
|
# transfer to perform an aggregate calculation on. As specified in the
|
64
65
|
# transfer configuration.
|
65
|
-
# @param [DoubleEntry::Reporting::TimeRange] Only include transfers in
|
66
|
-
# given time range in the calculation.
|
67
|
-
# @
|
66
|
+
# @param [DoubleEntry::Reporting::TimeRange] range Only include transfers in
|
67
|
+
# the given time range in the calculation.
|
68
|
+
# @param [Symbol] partner_account The symbol identifying the partner account
|
69
|
+
# to perform the aggregate calculatoin on. As specified in the account
|
70
|
+
# configuration.
|
71
|
+
# @param [Array<Hash<Symbol,Hash<Symbol,Object>>>] filter
|
68
72
|
# An array of custom filter to apply before performing the aggregate
|
69
73
|
# calculation. Filters can be either scope filters, where the name must be
|
70
74
|
# specified, or they can be metadata filters, where the key/value pair to
|
@@ -73,13 +77,14 @@ module DoubleEntry
|
|
73
77
|
# class, as the example above shows. Scope filters may also take a list of
|
74
78
|
# arguments to pass into the monkey patched scope, and, if provided, must
|
75
79
|
# be contained within an array.
|
76
|
-
# @return [Money,
|
77
|
-
# calculations, or a
|
80
|
+
# @return [Money, Integer] Returns a Money object for :sum and :average
|
81
|
+
# calculations, or a Integer for :count calculations.
|
78
82
|
# @raise [Reporting::AggregateFunctionNotSupported] The provided function
|
79
83
|
# is not supported.
|
80
84
|
#
|
81
|
-
def aggregate(function
|
82
|
-
Aggregate.formatted_amount(function, account, code, range,
|
85
|
+
def aggregate(function:, account:, code:, range:, partner_account: nil, filter: nil)
|
86
|
+
Aggregate.formatted_amount(function: function, account: account, code: code, range: range,
|
87
|
+
partner_account: partner_account, filter: filter)
|
83
88
|
end
|
84
89
|
|
85
90
|
# Perform an aggregate calculation on a set of transfers for an account
|
@@ -104,54 +109,37 @@ module DoubleEntry
|
|
104
109
|
# @param [Symbol] code The application specific code for the type of
|
105
110
|
# transfer to perform an aggregate calculation on. As specified in the
|
106
111
|
# transfer configuration.
|
107
|
-
# @
|
112
|
+
# @param [Symbol] partner_account The symbol identifying the partner account
|
113
|
+
# to perform the aggregative calculation on. As specified in the account
|
114
|
+
# configuration.
|
115
|
+
# @param [Array<Symbol>, Array<Hash<Symbol, Object>>] filter
|
108
116
|
# A custom filter to apply before performing the aggregate calculation.
|
109
117
|
# Currently, filters must be monkey patched as scopes into the
|
110
118
|
# DoubleEntry::Line class in order to be used as filters, as the example
|
111
119
|
# shows. If the filter requires a parameter, it must be given in a Hash,
|
112
120
|
# otherwise pass an array with the symbol names for the defined scopes.
|
113
|
-
# @
|
114
|
-
# for.
|
121
|
+
# @param [String] range_type The type of time range to return data
|
122
|
+
# for. For example, specifying 'month' will return an array of the resulting
|
115
123
|
# aggregate calculation for each month.
|
116
124
|
# Valid range_types are 'hour', 'day', 'week', 'month', and 'year'
|
117
|
-
# @
|
125
|
+
# @param [String] start The start date for the time range to perform
|
118
126
|
# calculations in. The default start date is the start_of_business (can
|
119
127
|
# be specified in configuration).
|
120
128
|
# The format of the string must be as follows: 'YYYY-mm-dd'
|
121
|
-
# @
|
129
|
+
# @param [String] finish The finish (or end) date for the time range
|
122
130
|
# to perform calculations in. The default finish date is the current date.
|
123
131
|
# The format of the string must be as follows: 'YYYY-mm-dd'
|
124
|
-
# @return [Array<Money,
|
125
|
-
# and :average calculations, or an array of
|
132
|
+
# @return [Array<Money, Integer>] Returns an array of Money objects for :sum
|
133
|
+
# and :average calculations, or an array of Integer for :count calculations.
|
126
134
|
# The array is indexed by the range_type. For example, if range_type is
|
127
135
|
# specified as 'month', each index in the array will represent a month.
|
128
136
|
# @raise [Reporting::AggregateFunctionNotSupported] The provided function
|
129
137
|
# is not supported.
|
130
138
|
#
|
131
|
-
def aggregate_array(function
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
# Identify the scopes with the given account identifier holding at least
|
136
|
-
# the provided minimum balance.
|
137
|
-
#
|
138
|
-
# @example Find users with at least $1,000,000 in their savings accounts
|
139
|
-
# DoubleEntry::Reporting.scopes_with_minimum_balance_for_account(
|
140
|
-
# 1_000_000.dollars,
|
141
|
-
# :savings,
|
142
|
-
# ) # might return the user ids: [ 1423, 12232, 34729 ]
|
143
|
-
# @param [Money] minimum_balance Minimum account balance a scope must have
|
144
|
-
# to be included in the result set.
|
145
|
-
# @param [Symbol] account_identifier
|
146
|
-
# @return [Array<Fixnum>] Scopes
|
147
|
-
#
|
148
|
-
def scopes_with_minimum_balance_for_account(minimum_balance, account_identifier)
|
149
|
-
select_values(sanitize_sql_array([<<-SQL, account_identifier, minimum_balance.cents])).map(&:to_i)
|
150
|
-
SELECT scope
|
151
|
-
FROM #{AccountBalance.table_name}
|
152
|
-
WHERE account = ?
|
153
|
-
AND balance >= ?
|
154
|
-
SQL
|
139
|
+
def aggregate_array(function:, account:, code:, partner_account: nil, filter: nil,
|
140
|
+
range_type: nil, start: nil, finish: nil)
|
141
|
+
AggregateArray.new(function: function, account: account, code: code, partner_account: partner_account,
|
142
|
+
filter: filter, range_type: range_type, start: start, finish: finish)
|
155
143
|
end
|
156
144
|
|
157
145
|
# This is used by the concurrency test script.
|