eco-helpers 2.0.24 → 2.0.29

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +89 -6
  3. data/lib/eco/api/common.rb +0 -1
  4. data/lib/eco/api/common/loaders.rb +2 -0
  5. data/lib/eco/api/common/loaders/base.rb +58 -0
  6. data/lib/eco/api/common/loaders/case_base.rb +33 -0
  7. data/lib/eco/api/common/loaders/error_handler.rb +2 -2
  8. data/lib/eco/api/common/loaders/parser.rb +30 -5
  9. data/lib/eco/api/common/loaders/policy.rb +1 -1
  10. data/lib/eco/api/common/loaders/use_case.rb +1 -1
  11. data/lib/eco/api/common/people/default_parsers/csv_parser.rb +129 -1
  12. data/lib/eco/api/common/people/entries.rb +83 -14
  13. data/lib/eco/api/common/people/entry_factory.rb +11 -10
  14. data/lib/eco/api/common/people/person_attribute_parser.rb +8 -0
  15. data/lib/eco/api/common/people/person_entry.rb +7 -6
  16. data/lib/eco/api/common/people/person_entry_attribute_mapper.rb +55 -16
  17. data/lib/eco/api/common/people/person_factory.rb +4 -2
  18. data/lib/eco/api/common/people/person_parser.rb +7 -1
  19. data/lib/eco/api/common/people/supervisor_helpers.rb +1 -1
  20. data/lib/eco/api/common/version_patches/ecoportal_api/external_person.rb +0 -8
  21. data/lib/eco/api/common/version_patches/ecoportal_api/internal_person.rb +0 -8
  22. data/lib/eco/api/microcases/set_core_with_supervisor.rb +4 -2
  23. data/lib/eco/api/microcases/set_supervisor.rb +29 -8
  24. data/lib/eco/api/microcases/with_each.rb +7 -3
  25. data/lib/eco/api/microcases/with_each_starter.rb +3 -2
  26. data/lib/eco/api/organization/people.rb +7 -1
  27. data/lib/eco/api/session.rb +7 -2
  28. data/lib/eco/api/session/batch.rb +1 -1
  29. data/lib/eco/api/session/batch/job.rb +9 -1
  30. data/lib/eco/api/usecases/default_cases/create_case.rb +10 -1
  31. data/lib/eco/api/usecases/default_cases/create_details_case.rb +10 -1
  32. data/lib/eco/api/usecases/default_cases/create_details_with_supervisor_case.rb +10 -1
  33. data/lib/eco/api/usecases/default_cases/hris_case.rb +25 -1
  34. data/lib/eco/api/usecases/default_cases/to_csv_case.rb +1 -37
  35. data/lib/eco/api/usecases/default_cases/to_csv_detailed_case.rb +42 -0
  36. data/lib/eco/api/usecases/default_cases/upsert_case.rb +10 -1
  37. data/lib/eco/cli/config/default/input.rb +2 -2
  38. data/lib/eco/cli/config/default/options.rb +29 -8
  39. data/lib/eco/cli/config/default/usecases.rb +16 -0
  40. data/lib/eco/cli/config/default/workflow.rb +7 -4
  41. data/lib/eco/cli/config/filters.rb +6 -2
  42. data/lib/eco/cli/config/filters/input_filters.rb +3 -2
  43. data/lib/eco/cli/config/filters/people_filters.rb +3 -2
  44. data/lib/eco/cli/config/help.rb +1 -1
  45. data/lib/eco/cli/config/options_set.rb +6 -4
  46. data/lib/eco/cli/config/use_cases.rb +6 -3
  47. data/lib/eco/csv.rb +2 -0
  48. data/lib/eco/language/models/collection.rb +5 -2
  49. data/lib/eco/version.rb +1 -1
  50. metadata +3 -2
  51. data/lib/eco/api/common/base_loader.rb +0 -72
@@ -5,6 +5,42 @@ module Eco
5
5
  # Class meant to offer a _collection_ of entries, normally used to get parsed input data.
6
6
  # @attr_reader entries [Array<Eco::API::Common::PeopleEntry] a pure `Array` object.
7
7
  class Entries < Eco::Language::Models::Collection
8
+ # Error class that allows to handle cases where multiple entries were found for the same criterion.
9
+ # @note its main purpose to prevent the false pairing of duplicates or override information between different people.
10
+ class MultipleSearchResults < StandardError
11
+ attr_reader :candidates, :property
12
+ # @param msg [String] the basic message error.
13
+ # @param candiates [Array<PersonEntry>] the entries that match the same search criterion.
14
+ # @param property [String] the property of the entry model that triggered the error (base of the search criterion).
15
+ def initialize(msg, candidates: [], property: "email")
16
+ @candidates = candidates
17
+ @property = property
18
+ super(msg + " " + candidates_summary)
19
+ end
20
+
21
+ # @param with_index [Boolean] to add an index to each candidate description.
22
+ # @return [Array<String>] the `candidates` identified
23
+ def identify_candidates(with_index: false)
24
+ candidates.map.each_with_index do |entry, i|
25
+ index = with_index ? "#{i}. " : ""
26
+ "#{index} #{entry.identify}"
27
+ end
28
+ end
29
+
30
+ # @return [Person] the `candidate` in the `index` position
31
+ def candidate(index)
32
+ candidates[index]
33
+ end
34
+
35
+ private
36
+
37
+ def candidates_summary
38
+ lines = ["The following entries have the same '#{property}':"]
39
+ lines.concat(identify_candidates(with_index: true)).join("\n ")
40
+ end
41
+
42
+ end
43
+
8
44
  # build the shortcuts of Collection
9
45
  attr_collection :id, :external_id, :email, :name, :supervisor_id
10
46
 
@@ -54,19 +90,34 @@ module Eco
54
90
  # @!group Searchers
55
91
 
56
92
  # Search function to find an `entry` based on one of different options
93
+ # It searches an entry using the parameters given.
94
+ # @note This is how the search function actually works:
95
+ # 1. if eP `id` is given, returns the entry (if found), otherwise...
96
+ # 2. if `external_id` is given, returns the entry (if found), otherwise...
97
+ # 3. if `strict` is `false` and `email` is given:
98
+ # - if there is only 1 entry with that email, returns that entry, otherwise...
99
+ # - if found but, there are many candidate entries, it raises MultipleSearchResults error
100
+ # - if entry `external_id` matches `email`, returns that entry
101
+ # @raise MultipleSearchResults if there are multiple entries with the same `email`
102
+ # and there's no other criteria to find the entry. It only gets to this point if
103
+ # `external_id` was **not** provided and we are **not** in 'strict' search mode.
104
+ # However, it could be we were in `strict` mode and `external_id` was not provided.
105
+ # @param id [String] the `internal id` of the person
106
+ # @param external_id [String] the `exernal_id` of the person
107
+ # @param email [String] the `email` of the person
108
+ # @param strict [Boolean] if should perform a `:soft` or a `:strict` search. `strict` will avoid repeated email addresses.
109
+ # @return [Entry, nil] the entry we were searching, or `nil` if not found.
57
110
  def entry(id: nil, external_id: nil, email: nil, strict: false)
58
111
  init_caches
59
- pers = nil
60
- pers = @by_id[id]&.first if id
61
- pers = @by_external_id[external_id&.strip]&.first if !pers && !external_id.to_s.strip.empty?
62
-
63
- # strict prevents taking existing user for searched person with same email
64
- # specially useful if the organisation ensures all have external id (no need for email search)
65
- if !pers && (!strict || external_id.to_s.strip.empty?)
66
- pers = @by_email[email&.downcase.strip]&.first if !pers && !email.to_s.strip.empty?
67
- pers = @by_external_id[email&.downcase.strip]&.first if !pers && !email.to_s.strip.empty?
68
- end
69
- pers
112
+ # normalize values
113
+ ext_id = !external_id.to_s.strip.empty? && external_id.strip
114
+ email = !email.to_s.strip.empty? && email.downcase.strip
115
+
116
+ e = nil
117
+ e ||= @by_id[id]&.first
118
+ e ||= @by_external_id[ext_id]&.first
119
+ e ||= entry_by_email(email) unless strict && ext_id
120
+ e
70
121
  end
71
122
 
72
123
  # Search function to find an `entry` based on one of different options
@@ -136,15 +187,33 @@ module Eco
136
187
 
137
188
  private
138
189
 
190
+ def entry_by_email(email, prevent_multiple_match: false)
191
+ return nil unless email
192
+
193
+ candidates = @by_email[email] || []
194
+ return candidates.first if candidates.length == 1
195
+
196
+ if prevent_multiple_match && !candidates.empty?
197
+ msg = "Multiple search results match the criteria."
198
+ raise MultipleSearchResults.new(msg, candidates: candidates, property: "email")
199
+ end
200
+
201
+ @by_external_id[email]&.first
202
+ end
203
+
139
204
  def init_caches
140
205
  return if @caches_init
141
- @by_id = to_h
142
- @by_external_id = to_h('external_id')
143
- @by_email = to_h('email')
206
+ @by_id = no_nil_key(to_h)
207
+ @by_external_id = no_nil_key(to_h('external_id'))
208
+ @by_email = no_nil_key(to_h('email'))
144
209
  @array_supers = sort_by_supervisors(@items)
145
210
  @caches_init = true
146
211
  end
147
212
 
213
+ def no_nil_key(hash)
214
+ hash.tap {|h| h.delete(nil)}
215
+ end
216
+
148
217
  end
149
218
  end
150
219
  end
@@ -80,21 +80,21 @@ module Eco
80
80
  # @param data [Array<Hash>] data to be parsed. It cannot be used alongside with `file:`
81
81
  # @param file [String] absolute or relative path to the input file. It cannot be used alongside with `data:`.
82
82
  # @param format [Symbol] it must be used when you use the option `file:` (i.e. `:xml`, `:csv`), as it specifies the format of the input `file:`.
83
- # @param encoding [String] optional parameter to read `file:` by expecting certain encoding.
83
+ # @param options [Hash] further options.
84
+ # @option options [String] :encoding optional parameter to read `file:` by expecting certain encoding.
85
+ # @option options [Boolean] :check_headers signals if the `csv` file headers should be expected.
84
86
  # @return [Eco::API::Common::People::Entries] collection of `Eco::API::Common::People::PersonEntry`.
85
- def entries(data: (no_data = true; nil), file: (no_file = true; nil), format: (no_format = true; nil), encoding: nil)
87
+ def entries(data: (no_data = true; nil), file: (no_file = true; nil), format: (no_format = true; nil), **options)
86
88
  fatal("You should at least use data: or file:, but not both") if no_data == no_file
87
89
  fatal("You must specify a valid format: (symbol) when you use file.") if file && no_format
88
90
  fatal("Format should be a Symbol. Given '#{format}'") if format && !format.is_a?(Symbol)
89
91
  fatal("There is no parser/serializer for format ':#{format.to_s}'") unless no_format || @person_parser.defined?(format)
90
92
 
91
- kargs = {}
92
- kargs.merge!(content: data) unless no_data
93
- kargs.merge!(file: file) unless no_file
94
- kargs.merge!(format: format) unless no_format
95
- kargs.merge!(encoding: encoding) if encoding
93
+ options.merge!(content: data) unless no_data
94
+ options.merge!(file: file) unless no_file
95
+ options.merge!(format: format) unless no_format
96
96
 
97
- Entries.new(to_array_of_hashes(**kargs), klass: PersonEntry, factory: self)
97
+ Entries.new(to_array_of_hashes(**options), klass: PersonEntry, factory: self)
98
98
  end
99
99
 
100
100
  def to_array_of_hashes(**kargs)
@@ -118,7 +118,8 @@ module Eco
118
118
  logger.error("Input data as 'Hash' not supported. Expecting 'Enumerable' or 'String'")
119
119
  exit(1)
120
120
  when String
121
- to_array_of_hashes(content: person_parser.parse(format, content))
121
+ deps = {check_headers: true} if kargs[:check_headers]
122
+ to_array_of_hashes(content: person_parser.parse(format, content, deps: deps || {}))
122
123
  when Enumerable
123
124
  sample = content.to_a.first
124
125
  case sample
@@ -164,7 +165,7 @@ module Eco
164
165
 
165
166
  run = true
166
167
  if Eco::API::Common::Session::FileManager.file_exists?(file)
167
- prompt_user("The file '#{file}' already exists. Do you want to overwrite it? (Y/n):", default: "Y") do |response|
168
+ prompt_user("Do you want to overwrite it? (Y/n):", explanation: "The file '#{file}' already exists.", default: "Y") do |response|
168
169
  run = (response == "") || reponse.upcase.start_with?("Y")
169
170
  end
170
171
  end
@@ -6,6 +6,14 @@ module Eco
6
6
  # Class to define a parser/serializer.
7
7
  class PersonAttributeParser < Eco::Language::Models::ParserSerializer
8
8
 
9
+ # @note
10
+ # - This was introduced at a later stage and might not be available for certain org-parsers configs
11
+ # @return [RequiredAttrs]
12
+ def required_attrs
13
+ @required_attrs ||= @dependencies[:required_attrs]
14
+ #@required_attrs ||= RequiredAttrs.new(attr, :unkown, [attr])
15
+ end
16
+
9
17
  # @see Eco::Language::Models::ParserSerializer#def_parser
10
18
  # @note
11
19
  # - additionally, you can declare a callback `active:` to determine if when the
@@ -188,7 +188,7 @@ module Eco
188
188
  # @param person [Ecoportal::API::V1::Person] the person we want to set the core values to.
189
189
  # @param exclude [String, Array<String>] core attributes that should not be set/changed to the person.
190
190
  def set_core(person, exclude: nil)
191
- scoped_attrs = @emap.core_attrs - into_a(exclude)
191
+ scoped_attrs = @emap.core_attrs(@final_entry) - into_a(exclude)
192
192
  @final_entry.slice(*scoped_attrs).each do |attr, value|
193
193
  begin
194
194
  set_part(person, attr, value)
@@ -210,7 +210,7 @@ module Eco
210
210
  # @param exclude [String, Array<String>] account properties that should not be set/changed to the person.
211
211
  def set_account(person, exclude: nil)
212
212
  person.account = {} if !person.account
213
- scoped_attrs = @emap.account_attrs - into_a(exclude)
213
+ scoped_attrs = @emap.account_attrs(@final_entry) - into_a(exclude)
214
214
  @final_entry.slice(*scoped_attrs).each do |attr, value|
215
215
  set_part(person.account, attr, value)
216
216
  end
@@ -224,7 +224,7 @@ module Eco
224
224
  # @param exclude [String, Array<String>] schema field attributes that should not be set/changed to the person.
225
225
  def set_details(person, exclude: nil)
226
226
  person.add_details(@person_parser.schema) if !person.details || !person.details.schema_id
227
- scoped_attrs = @emap.details_attrs - into_a(exclude)
227
+ scoped_attrs = @emap.details_attrs(@final_entry) - into_a(exclude)
228
228
  @final_entry.slice(*scoped_attrs).each do |attr, value|
229
229
  set_part(person.details, attr, value)
230
230
  end
@@ -326,12 +326,13 @@ module Eco
326
326
  # @param internal_entry [Hash] the entry with the **internal** _attribute_ names and values but the **external** types.
327
327
  # @return [Hash] the `parsed entry` with the **internal** final attributes names, values and types.
328
328
  def _final_parsing(internal_entry)
329
- core_account = @emap.account_attrs + @emap.core_attrs
330
- core_account_hash = internal_entry.slice(*core_account).each_with_object({}) do |(attr, value), hash|
329
+ core_account_attrs = @emap.account_attrs(internal_entry) + @emap.core_attrs(internal_entry)
330
+ core_account_hash = internal_entry.slice(*core_account_attrs).each_with_object({}) do |(attr, value), hash|
331
331
  hash[attr] = _parse_type(attr, value)
332
332
  end
333
333
 
334
- details_hash = internal_entry.slice(*@emap.details_attrs).each_with_object({}) do |(attr, value), hash|
334
+ details_attrs = @emap.details_attrs(internal_entry)
335
+ details_hash = internal_entry.slice(*details_attrs).each_with_object({}) do |(attr, value), hash|
335
336
  hash[attr] = _parse_type(attr, value, schema: @person_parser.schema)
336
337
  end
337
338
 
@@ -3,18 +3,11 @@ module Eco
3
3
  module Common
4
4
  module People
5
5
 
6
- # @attr_reader core_attrs [Array<String>] core attributes that are present in the person entry.
7
- # @attr_reader details_attrs [Array<String>] schema details attributes that are present in the person entry.
8
- # @attr_reader account_attrs [Array<String>] account attributes that are present in the person entry.
9
- # @attr_reader all_model_attrs [Array<String>] all the attrs that are present in the person entry.
10
- # @attr_reader internal_attrs [Array<String>] all the internally named attributes that the person entry has.
11
- # @attr_reader aliased_attrs [Array<String>] only those internal attributes present in the person entry that have an internal/external name mapping.
12
6
  # @attr_reader direct_attrs [Array<String>] only those internal attributes present in the person entry that do **not** have an internal/external name mapping.
13
7
  class PersonEntryAttributeMapper
14
8
  @@cached_warnings = {}
15
9
 
16
- attr_reader :core_attrs, :details_attrs, :account_attrs, :all_model_attrs
17
- attr_reader :internal_attrs, :aliased_attrs, :direct_attrs
10
+ attr_reader :aliased_attrs, :direct_attrs
18
11
 
19
12
  # Helper class tied to `PersonEntry` that allows to track which attributes of a person entry are present
20
13
  # and how they should be mapped between internal and external names if applicable.
@@ -38,17 +31,64 @@ module Eco
38
31
 
39
32
  if parsing?
40
33
  @external_entry = data
41
- init_attr_trackers
42
34
  else # SERIALIZING
43
35
  @person = data
44
- @internal_attrs = @person_parser.all_model_attrs
45
- @aliased_attrs = @attr_map.list(:internal)
46
36
  end
37
+ end
47
38
 
48
- @core_attrs = @person_parser.target_attrs_core(@internal_attrs)
49
- @details_attrs = @person_parser.target_attrs_details(@internal_attrs)
50
- @account_attrs = @person_parser.target_attrs_account(@internal_attrs)
51
- @all_model_attrs = @core_attrs | @account_attrs | @details_attrs
39
+ # @return [Array<String>] only those internal attributes present in the person entry that have an internal/external name mapping.
40
+ def aliased_attrs
41
+ return @aliased_attrs unless !@aliased_attrs
42
+ if parsing?
43
+ init_attr_trackers
44
+ else
45
+ @aliased_attrs = @attr_map.list(:internal)
46
+ end
47
+ @aliased_attrs
48
+ end
49
+
50
+ # @return [Array<String>] all the internally named attributes that the person entry has.
51
+ def internal_attrs(data = nil)
52
+ return @internal_attrs unless data || !@internal_attrs
53
+ if parsing?
54
+ init_attr_trackers unless @internal_attrs
55
+ if data
56
+ return data.keys & @person_parser.all_model_attrs
57
+ end
58
+ else
59
+ @internal_attrs = @person_parser.all_model_attrs
60
+ end
61
+ @internal_attrs
62
+ end
63
+
64
+
65
+ # @return [Array<String>] all the attrs that are present in the person entry.
66
+ def all_model_attrs(data = nil)
67
+ core_attrs(data) | account_attrs(data) | details_attrs(data)
68
+ end
69
+
70
+ # @return [Array<String>] core attributes that are present in the person entry.
71
+ def core_attrs(data = nil)
72
+ return @core_attrs unless data || !@core_attrs
73
+ @person_parser.target_attrs_core(internal_attrs(data)).tap do |core_attrs|
74
+ @core_attrs ||= core_attrs
75
+ end
76
+ end
77
+
78
+ # @return [Array<String>] schema details attributes that are present in the person entry.
79
+ def details_attrs(data = nil)
80
+ return @details_attrs unless data || !@details_attrs
81
+ @person_parser.target_attrs_details(internal_attrs(data)).tap do |details_attrs|
82
+ @details_attrs ||= details_attrs
83
+ end
84
+ end
85
+
86
+ # @return [Array<String>] account attributes that are present in the person entry.
87
+ def account_attrs(data = nil)
88
+ return @account_attrs unless data || !@account_attrs
89
+ @person_parser.target_attrs_account(internal_attrs(data)).tap do |account_attrs|
90
+ @account_attrs ||= account_attrs
91
+ end
52
92
  end
53
93
 
54
94
  # To know if currently the object is in parse or serialize mode.
@@ -151,7 +191,6 @@ module Eco
151
191
  def_unlinked = @person_parser.undefined_model_attrs.select { |attr| !to_external(attr) }
152
192
  # (def) those with parser or alias:
153
193
  def_linked = def_all_attrs - def_unlinked
154
-
155
194
  # (data) data attributes (actual attributes of the entry)
156
195
  data_attrs = attributes(@external_entry)
157
196
  # (data) attributes of the data that come directly as internal attribute names
@@ -10,7 +10,7 @@ module Eco
10
10
 
11
11
  attr_reader :schema, :schema_attrs
12
12
 
13
- def initialize(person: {}, schema: {}, account: {}, modifier: Common::People::PersonModifier.new)
13
+ def initialize(person: nil, schema: {}, account: {}, modifier: Common::People::PersonModifier.new)
14
14
  @modifier = Common::People::PersonModifier.new(modifier)
15
15
  @person = person
16
16
  @account = account
@@ -77,7 +77,9 @@ module Eco
77
77
  when Hash
78
78
  JSON.parse(person.to_json)
79
79
  else
80
- {}
80
+ {
81
+ "subordinates" => 0
82
+ }
81
83
  end
82
84
  end
83
85
 
@@ -59,9 +59,15 @@ module Eco
59
59
 
60
60
  # @!group Scopping attributes (identifying, presence & active)
61
61
 
62
+ # @return [Array<Eco::API::Common::Loaders::Parser::RequiredAttrs>]
63
+ def required_attrs
64
+ @parsers.values_at(*all_attrs(include_defined_parsers: true)).compact.map(&:required_attrs).compact
65
+ end
66
+
62
67
  # All the internal name attributes, including _core_, _account_ and _details_.
63
68
  def all_attrs(include_defined_parsers: false)
64
- all_model_attrs | defined_model_attrs
69
+ return all_model_attrs | defined_model_attrs if include_defined_parsers
70
+ all_model_attrs
65
71
  end
66
72
 
67
73
  # Scopes `source_attrs` using the _**core** attributes_.
@@ -15,7 +15,7 @@ module Eco
15
15
  # Reorders as follows:
16
16
  # 1. supervisors, people with no supervisor or where their supervisor not present
17
17
  # 2. subordinates
18
- # @return [Array<Entry>] `values` sorted by supervisors/subordinates
18
+ # @return [Array<PersonEntry>] `values` sorted by supervisors/subordinates
19
19
  def sort_by_supervisors(values, supervisors_first: true)
20
20
  raise "Expected non hash Enumerable. Given: #{values.class}" if values.is_a?(Hash)
21
21
  return [] unless values && values.is_a?(Enumerable)
@@ -5,14 +5,6 @@ module Ecoportal
5
5
  class Person
6
6
  attr_accessor :entry
7
7
 
8
- def reset_details!
9
- doc["details"] = JSON.parse(original_doc["details"])
10
- end
11
-
12
- def consolidate_details!
13
- original_doc["details"] = JSON.parse(doc["details"])
14
- end
15
-
16
8
  def identify(section = :person)
17
9
  if entry && section == :entry
18
10
  entry.to_s(:identify)
@@ -3,14 +3,6 @@ module Ecoportal
3
3
  class Internal
4
4
  class Person
5
5
 
6
- def reset_account!
7
- doc["account"] = JSON.parse(original_doc["account"])
8
- end
9
-
10
- def consolidate_account!
11
- original_doc["account"] = JSON.parse(doc["account"])
12
- end
13
-
14
6
  def new?(doc = :initial)
15
7
  ref_doc = (doc == :original) ? original_doc : initial_doc
16
8
  !ref_doc["details"] && !ref_doc["account"]
@@ -12,9 +12,11 @@ module Eco
12
12
  unless options.dig(:exclude, :core) && !person.new?
13
13
  micro.set_core(entry, person, options)
14
14
  if entry.supervisor_id?
15
- micro.set_supervisor(entry.supervisor_id, person, people, options) do |unkown_id|
15
+ micro.set_supervisor(person, entry.supervisor_id, people, options) do |unknown_id|
16
16
  # delay setting supervisor if does not exit
17
- supers_job.add(person) {|person| person.supervisor_id = unkown_id}
17
+ supers_job.add(person) do |person|
18
+ micro.set_supervisor(person, unknown_id, people, options)
19
+ end
18
20
  end
19
21
  end
20
22
  end
@@ -1,22 +1,27 @@
1
1
  module Eco
2
2
  module API
3
3
  class MicroCases
4
- # Special snippet to decide if the `supervisor_id` is set now or in a later batch job `supers_job`.
5
- # @note delaying the setting of a `supervisor_id` can save errors when the supervisor still does not exit.
6
- # @param sup_id [nil, String] the **supervisor id** we should set on the `person`.
4
+ # Unique access point to set the `supervisor_id` value on a person.
7
5
  # @param person [Ecoportal::API::V1::Person] the person we want to update, carrying the changes to be done.
8
- # @param people [Eco::API::Organization::People] target existing _People_ of the current update.
6
+ # @param sup_id [nil, String] the **supervisor id** we should set on the `person`.
7
+ # @param people [Eco::API::Organization::People] _People_ involved in the current update.
9
8
  # @param options [Hash] the options.
10
9
  # @yield [supervisor_id] callback when the supervisor_id is **unknown** (not `nil` nor any one's in `people`).
11
10
  # @yieldparam supervisor_id [String] the **unknown** `supervisor_id`.
12
- def set_supervisor(sup_id, person, people, options)
11
+ def set_supervisor(person, sup_id, people, options)
13
12
  unless options.dig(:exclude, :core) || options.dig(:exclude, :supervisor)
14
- micro.with_supervisor(sup_id, people) do |supervisor|
13
+ cur_id = person.supervisor_id
14
+ cur_super = cur_id && with_supervisor(cur_id, people)
15
+ micro.with_supervisor(sup_id, people) do |new_super|
15
16
  if !sup_id
16
17
  person.supervisor_id = nil
17
- elsif supervisor
18
- person.supervisor_id = supervisor.id
18
+ descrease_subordinates(cur_super)
19
+ elsif new_super && id = new_super.id
20
+ person.supervisor_id = id
21
+ descrease_subordinates(cur_super)
22
+ increase_subordinates(new_super)
19
23
  elsif !block_given?
24
+ descrease_subordinates(cur_super)
20
25
  person.supervisor_id = sup_id
21
26
  else
22
27
  yield(sup_id) if block_given?
@@ -25,6 +30,22 @@ module Eco
25
30
  end
26
31
  end
27
32
 
33
+ private
34
+
35
+ def descrease_subordinates(person, by = 1)
36
+ if person.is_a?(Ecoportal::API::V1::Person)
37
+ person.subordinates -= by
38
+ #person.subordinates = 0 if person.subordinates < 0
39
+ end
40
+ end
41
+
42
+ def increase_subordinates(person, by = 1)
43
+ if person.is_a?(Ecoportal::API::V1::Person)
44
+ #person.subordinates = 0 if person.subordinates < 0
45
+ person.subordinates += by
46
+ end
47
+ end
48
+
28
49
  end
29
50
  end
30
51
  end