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.
Files changed (67) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +432 -0
  3. data/README.md +36 -9
  4. data/double_entry.gemspec +20 -48
  5. data/lib/active_record/locking_extensions.rb +3 -3
  6. data/lib/active_record/locking_extensions/log_subscriber.rb +1 -1
  7. data/lib/double_entry/account.rb +38 -45
  8. data/lib/double_entry/account_balance.rb +18 -1
  9. data/lib/double_entry/errors.rb +13 -13
  10. data/lib/double_entry/line.rb +3 -2
  11. data/lib/double_entry/reporting.rb +26 -38
  12. data/lib/double_entry/reporting/aggregate.rb +43 -23
  13. data/lib/double_entry/reporting/aggregate_array.rb +16 -13
  14. data/lib/double_entry/reporting/line_aggregate.rb +3 -2
  15. data/lib/double_entry/reporting/line_aggregate_filter.rb +8 -10
  16. data/lib/double_entry/reporting/line_metadata_filter.rb +33 -0
  17. data/lib/double_entry/transfer.rb +33 -27
  18. data/lib/double_entry/validation.rb +1 -0
  19. data/lib/double_entry/validation/account_fixer.rb +36 -0
  20. data/lib/double_entry/validation/line_check.rb +22 -40
  21. data/lib/double_entry/version.rb +1 -1
  22. data/lib/generators/double_entry/install/install_generator.rb +7 -1
  23. data/lib/generators/double_entry/install/templates/migration.rb +27 -25
  24. metadata +33 -243
  25. data/.gitignore +0 -32
  26. data/.rspec +0 -2
  27. data/.travis.yml +0 -29
  28. data/.yardopts +0 -2
  29. data/Gemfile +0 -2
  30. data/Rakefile +0 -15
  31. data/script/jack_hammer +0 -210
  32. data/script/setup.sh +0 -8
  33. data/spec/active_record/locking_extensions_spec.rb +0 -110
  34. data/spec/double_entry/account_balance_spec.rb +0 -7
  35. data/spec/double_entry/account_spec.rb +0 -130
  36. data/spec/double_entry/balance_calculator_spec.rb +0 -88
  37. data/spec/double_entry/configuration_spec.rb +0 -50
  38. data/spec/double_entry/line_spec.rb +0 -80
  39. data/spec/double_entry/locking_spec.rb +0 -214
  40. data/spec/double_entry/performance/double_entry_performance_spec.rb +0 -32
  41. data/spec/double_entry/performance/reporting/aggregate_performance_spec.rb +0 -50
  42. data/spec/double_entry/reporting/aggregate_array_spec.rb +0 -123
  43. data/spec/double_entry/reporting/aggregate_spec.rb +0 -205
  44. data/spec/double_entry/reporting/line_aggregate_filter_spec.rb +0 -90
  45. data/spec/double_entry/reporting/line_aggregate_spec.rb +0 -39
  46. data/spec/double_entry/reporting/month_range_spec.rb +0 -139
  47. data/spec/double_entry/reporting/time_range_array_spec.rb +0 -169
  48. data/spec/double_entry/reporting/time_range_spec.rb +0 -45
  49. data/spec/double_entry/reporting/week_range_spec.rb +0 -103
  50. data/spec/double_entry/reporting_spec.rb +0 -181
  51. data/spec/double_entry/transfer_spec.rb +0 -93
  52. data/spec/double_entry/validation/line_check_spec.rb +0 -99
  53. data/spec/double_entry_spec.rb +0 -428
  54. data/spec/generators/double_entry/install/install_generator_spec.rb +0 -30
  55. data/spec/spec_helper.rb +0 -118
  56. data/spec/support/accounts.rb +0 -21
  57. data/spec/support/blueprints.rb +0 -43
  58. data/spec/support/database.example.yml +0 -21
  59. data/spec/support/database.travis.yml +0 -24
  60. data/spec/support/double_entry_spec_helper.rb +0 -27
  61. data/spec/support/gemfiles/Gemfile.rails-3.2.x +0 -8
  62. data/spec/support/gemfiles/Gemfile.rails-4.1.x +0 -6
  63. data/spec/support/gemfiles/Gemfile.rails-4.2.x +0 -5
  64. data/spec/support/gemfiles/Gemfile.rails-5.0.x +0 -5
  65. data/spec/support/performance_helper.rb +0 -26
  66. data/spec/support/reporting_configuration.rb +0 -6
  67. data/spec/support/schema.rb +0 -74
@@ -1,4 +1,4 @@
1
- # encoding: utf-8
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 = ['Anthony Sellitti', 'Keith Pitt', 'Martin Jagusch', 'Martin Spickermann', 'Mark Turnley', 'Orien Madgwick', 'Pete Yandall', 'Stephanie Staub', 'Giancarlo Salamanca']
11
- gem.email = ['anthony.sellitti@envato.com', 'me@keithpitt.com', '_@mj.io', 'spickemann@gmail.com', 'mark@envato.com', '_@orien.io', 'pete@envato.com', 'staub.steph@gmail.com', 'giancarlo@salamanca.net.au']
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.files = `git ls-files`.split($/)
16
- gem.executables = gem.files.grep(%r{bin/}).map { |f| File.basename(f) }
17
- gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
- gem.require_paths = ['lib']
19
-
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
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
- 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
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 'machinist'
64
- gem.add_development_dependency 'timecop'
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.active_record', :exception => exception)
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.active_record', :exception => exception)
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.active_record', :exception => exception)
66
+ ActiveSupport::Notifications.publish('deadlock_retry.double_entry', :exception => exception)
67
67
 
68
68
  retry
69
69
  else
@@ -26,4 +26,4 @@ module ActiveRecord
26
26
  end
27
27
  end
28
28
 
29
- ActiveRecord::LockingExtensions::LogSubscriber.attach_to :active_record
29
+ ActiveRecord::LockingExtensions::LogSubscriber.attach_to :double_entry
@@ -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
- attr_writer :accounts, :scope_identifier_max_length, :account_identifier_max_length
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.detect { |a| a.identifier == identifier }.try(:currency)
23
+ accounts.find_without_scope(identifier).try(:currency)
31
24
  end
32
25
  end
33
26
 
34
27
  # @api private
35
- class Set < Array
28
+ class Set
29
+ extend Forwardable
30
+
31
+ delegate [:each, :map] => :all
32
+
36
33
  def define(attributes)
37
- self << Account.new(attributes)
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 = detect do |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
- def <<(account)
49
- if any? { |a| a.identifier == account.identifier }
50
- fail DuplicateAccount
46
+ if found_account && found_account.scoped? == scoped
47
+ found_account
51
48
  else
52
- super
49
+ fail UnknownAccount, "account: #{identifier} scoped?: #{scoped}"
53
50
  end
54
51
  end
55
52
 
56
- def active_record_scope_identifier(active_record_class)
57
- ActiveRecordScopeFactory.new(active_record_class).scope_identifier
53
+ def find_without_scope(identifier)
54
+ backing_collection[identifier]
58
55
  end
59
- end
60
56
 
61
- class ActiveRecordScopeFactory
62
- def initialize(active_record_class)
63
- @active_record_class = active_record_class
57
+ def all
58
+ backing_collection.values
64
59
  end
65
60
 
66
- def scope_identifier
67
- lambda do |value|
68
- case value
69
- when @active_record_class
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, :scope => self[:scope])
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
@@ -1,16 +1,16 @@
1
1
  # encoding: utf-8
2
2
  module DoubleEntry
3
- class UnknownAccount < RuntimeError; end
4
- class AccountIdentifierTooLongError < RuntimeError; end
5
- class ScopeIdentifierTooLongError < RuntimeError; end
6
- class TransferNotAllowed < RuntimeError; end
7
- class TransferIsNegative < RuntimeError; end
8
- class TransferCodeTooLongError < RuntimeError; end
9
- class DuplicateAccount < RuntimeError; end
10
- class DuplicateTransfer < RuntimeError; end
11
- class AccountWouldBeSentNegative < RuntimeError; end
12
- class AccountWouldBeSentPositiveError < RuntimeError; end
13
- class MismatchedCurrencies < RuntimeError; end
14
- class MissingAccountError < RuntimeError; end
15
- class AccountScopeMismatchError < RuntimeError; end
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
@@ -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, :scope => scope)
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, :scope => partner_scope)
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 the
66
- # given time range in the calculation.
67
- # @option options :filter [Array<Hash<Symbol,Hash<Symbol,Object>>>]
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, Fixnum] Returns a Money object for :sum and :average
77
- # calculations, or a Fixnum for :count calculations.
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, account, code, range, options = {})
82
- Aggregate.formatted_amount(function, account, code, range, options)
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
- # @option options :filter [Array<Symbol>, Array<Hash<Symbol, Object>>]
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
- # @option options :range_type [String] The type of time range to return data
114
- # for. For example, specifying 'month' will return an array of the resulting
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
- # @option options :start [String] The start date for the time range to perform
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
- # @option options :finish [String] The finish (or end) date for the time range
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, Fixnum>] Returns an array of Money objects for :sum
125
- # and :average calculations, or an array of Fixnum for :count calculations.
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, account, code, options = {})
132
- AggregateArray.new(function, account, code, options)
133
- end
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.