eco-helpers 2.0.24 → 2.0.29

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 (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