odorico 1.0.0

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 (31) hide show
  1. checksums.yaml +7 -0
  2. data/bin/odorico +3 -0
  3. data/lib/odorico/arquivo_atestado_de_saude_ocupacional.rb +60 -0
  4. data/lib/odorico/arquivo_aviso_previo.rb +69 -0
  5. data/lib/odorico/arquivo_cargo.rb +60 -0
  6. data/lib/odorico/arquivo_comunicacao_de_acidente_de_trabalho.rb +86 -0
  7. data/lib/odorico/arquivo_condicao_ambiental_de_trabalho.rb +83 -0
  8. data/lib/odorico/arquivo_data.rb +251 -0
  9. data/lib/odorico/arquivo_empregado.rb +401 -0
  10. data/lib/odorico/arquivo_empregado_afastamento_inicio.rb +109 -0
  11. data/lib/odorico/arquivo_empregado_afastamento_termino.rb +102 -0
  12. data/lib/odorico/arquivo_empregado_desligamento.rb +237 -0
  13. data/lib/odorico/arquivo_empregado_historico_cadastral.rb +194 -0
  14. data/lib/odorico/arquivo_empregado_historico_contratual.rb +157 -0
  15. data/lib/odorico/arquivo_empresa.rb +158 -0
  16. data/lib/odorico/arquivo_estabelecimento.rb +138 -0
  17. data/lib/odorico/arquivo_horario.rb +69 -0
  18. data/lib/odorico/arquivo_lotacao.rb +60 -0
  19. data/lib/odorico/arquivo_trabalhador_sem_vinculo.rb +305 -0
  20. data/lib/odorico/arquivo_trabalhador_sem_vinculo_historico_contratual.rb +86 -0
  21. data/lib/odorico/arquivo_trabalhador_sem_vinculo_termino.rb +204 -0
  22. data/lib/odorico/console.rb +11 -0
  23. data/lib/odorico/gerador_de_arquivos.rb +126 -0
  24. data/lib/odorico/gerador_de_arquivos_csv.rb +25 -0
  25. data/lib/odorico/jornada.rb +21 -0
  26. data/lib/odorico/processamento.rb +375 -0
  27. data/lib/odorico/sistema_operacional.rb +33 -0
  28. data/lib/odorico/types.rb +203 -0
  29. data/lib/odorico/version.rb +3 -0
  30. data/lib/odorico.rb +115 -0
  31. metadata +184 -0
@@ -0,0 +1,126 @@
1
+ require_relative 'gerador_de_arquivos_csv'
2
+
3
+ class GeradorDeArquivos
4
+ private
5
+
6
+ def initialize(gerador_de_arquivos_csv:, arquivos_convertidos:)
7
+ @gerador_de_arquivos_csv = gerador_de_arquivos_csv
8
+ @arquivos_convertidos = arquivos_convertidos
9
+ end
10
+
11
+ attr_reader :gerador_de_arquivos_csv, :arquivos_convertidos
12
+
13
+ public
14
+
15
+ def executar
16
+ cargos =
17
+ arquivos_convertidos.select { _1[:fw_tipo] == 'cargo' }
18
+ lotacoes =
19
+ arquivos_convertidos.select { _1[:fw_tipo] == 'lotacao' }
20
+ empregado_desligamento =
21
+ arquivos_convertidos.select { _1[:fw_tipo] == 'empregado_desligamento' }
22
+ empregado_historico_cadastral =
23
+ arquivos_convertidos.select { _1[:fw_tipo] == 'empregado_historico_cadastral' }
24
+ empregado_historico_contratual =
25
+ arquivos_convertidos.select { _1[:fw_tipo] == 'empregado_historico_contratual' }
26
+ empregado =
27
+ arquivos_convertidos.select { _1[:fw_tipo] == 'empregado' }
28
+ empresa =
29
+ arquivos_convertidos.select { _1[:fw_tipo] == 'empresa' }
30
+ estabelecimento =
31
+ arquivos_convertidos.select { _1[:fw_tipo] == 'estabelecimento' }
32
+ horario =
33
+ arquivos_convertidos.select { _1[:fw_tipo] == 'horario' }
34
+ empregados_afastamentos_inicio =
35
+ arquivos_convertidos.select { _1[:fw_tipo] == 'empregado_afastamento_inicio' }
36
+ empregados_afastamentos_termino =
37
+ arquivos_convertidos.select { _1[:fw_tipo] == 'empregado_afastamento_termino' }
38
+ trabalhador_sem_vinculo =
39
+ arquivos_convertidos.select { _1[:fw_tipo] == 'trabalhador_sem_vinculo' }
40
+ trabalhador_sem_vinculo_historico_contratual =
41
+ arquivos_convertidos.select { _1[:fw_tipo] == 'trabalhador_sem_vinculo_historico_contratual' }
42
+ trabalhador_sem_vinculo_termino =
43
+ arquivos_convertidos.select { _1[:fw_tipo] == 'trabalhador_sem_vinculo_termino' }
44
+ aviso_previo =
45
+ arquivos_convertidos.select { _1[:fw_tipo] == 'aviso_previo' }
46
+ comunicacao_de_acidente_de_trabalho =
47
+ arquivos_convertidos.select { _1[:fw_tipo] == 'comunicacao_de_acidente_de_trabalho' }
48
+ atestado_de_saude_ocupacional =
49
+ arquivos_convertidos.select { _1[:fw_tipo] == 'atestado_de_saude_ocupacional' }
50
+ condicao_ambiental_de_trabalho =
51
+ arquivos_convertidos.select { _1[:fw_tipo] == 'condicao_ambiental_de_trabalho' }
52
+
53
+ gerador_de_arquivos_csv.executar(
54
+ nome_do_arquivo: 'cargo',
55
+ dados: cargos
56
+ )
57
+ gerador_de_arquivos_csv.executar(
58
+ nome_do_arquivo: 'lotacao',
59
+ dados: lotacoes
60
+ )
61
+ gerador_de_arquivos_csv.executar(
62
+ nome_do_arquivo: 'empregado_desligamento',
63
+ dados: empregado_desligamento
64
+ )
65
+ gerador_de_arquivos_csv.executar(
66
+ nome_do_arquivo: 'empregado_historico_cadastral',
67
+ dados: empregado_historico_cadastral
68
+ )
69
+ gerador_de_arquivos_csv.executar(
70
+ nome_do_arquivo: 'empregado_historico_contratual',
71
+ dados: empregado_historico_contratual
72
+ )
73
+ gerador_de_arquivos_csv.executar(
74
+ nome_do_arquivo: 'empregado',
75
+ dados: empregado
76
+ )
77
+ gerador_de_arquivos_csv.executar(
78
+ nome_do_arquivo: 'empresa',
79
+ dados: empresa
80
+ )
81
+ gerador_de_arquivos_csv.executar(
82
+ nome_do_arquivo: 'estabelecimento',
83
+ dados: estabelecimento
84
+ )
85
+ gerador_de_arquivos_csv.executar(
86
+ nome_do_arquivo: 'horario',
87
+ dados: horario
88
+ )
89
+ gerador_de_arquivos_csv.executar(
90
+ nome_do_arquivo: 'empregado_afastamento_inicio',
91
+ dados: empregados_afastamentos_inicio
92
+ )
93
+ gerador_de_arquivos_csv.executar(
94
+ nome_do_arquivo: 'empregado_afastamento_termino',
95
+ dados: empregados_afastamentos_termino
96
+ )
97
+ gerador_de_arquivos_csv.executar(
98
+ nome_do_arquivo: 'trabalhador_sem_vinculo',
99
+ dados: trabalhador_sem_vinculo
100
+ )
101
+ gerador_de_arquivos_csv.executar(
102
+ nome_do_arquivo: 'trabalhador_sem_vinculo_historico_contratual',
103
+ dados: trabalhador_sem_vinculo_historico_contratual
104
+ )
105
+ gerador_de_arquivos_csv.executar(
106
+ nome_do_arquivo: 'trabalhador_sem_vinculo_termino',
107
+ dados: trabalhador_sem_vinculo_termino
108
+ )
109
+ gerador_de_arquivos_csv.executar(
110
+ nome_do_arquivo: 'aviso_previo',
111
+ dados: aviso_previo
112
+ )
113
+ gerador_de_arquivos_csv.executar(
114
+ nome_do_arquivo: 'comunicacao_de_acidente_de_trabalho',
115
+ dados: comunicacao_de_acidente_de_trabalho
116
+ )
117
+ gerador_de_arquivos_csv.executar(
118
+ nome_do_arquivo: 'atestado_de_saude_ocupacional',
119
+ dados: atestado_de_saude_ocupacional
120
+ )
121
+ gerador_de_arquivos_csv.executar(
122
+ nome_do_arquivo: 'condicao_ambiental_de_trabalho',
123
+ dados: condicao_ambiental_de_trabalho
124
+ )
125
+ end
126
+ end
@@ -0,0 +1,25 @@
1
+ require 'csv'
2
+ require_relative 'sistema_operacional'
3
+
4
+ class GeradorDeArquivosCsv
5
+ private
6
+
7
+ def initialize(pasta_destino)
8
+ @pasta_destino = pasta_destino
9
+ end
10
+
11
+ attr_reader :pasta_destino
12
+
13
+ public
14
+
15
+ def executar(nome_do_arquivo:, dados:)
16
+ if dados.any?
17
+ CSV.open("#{pasta_destino}#{SistemaOperacional.separador}#{nome_do_arquivo}.csv", 'w') do |csv|
18
+ csv << dados.first.keys
19
+ dados.each { csv << _1.values }
20
+ end
21
+ else
22
+ puts "# Gerador de CSV: Não existe dados para o arquivo: #{nome_do_arquivo}"
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,21 @@
1
+ class Jornada
2
+ def self.consistente?(tipo_jornada, horarios) # rubocop:disable Metrics/CyclomaticComplexity
3
+ case tipo_jornada
4
+ when nil
5
+ true
6
+ when 1
7
+ horarios.all? { (1..7).cover? _1[:dia] } &&
8
+ horarios.map { _1[:dia] }.then { _1 == _1.uniq }
9
+ when 2
10
+ horarios.size == 1 && horarios.first[:dia] == 8
11
+ when 3
12
+ horarios.all? { _1[:dia] == 8 }
13
+ when 9
14
+ horarios.nil? || horarios.empty?
15
+ end
16
+ end
17
+
18
+ def self.reconstrucao_fiel?(tipo_jornada)
19
+ ![2, 3].include? tipo_jornada
20
+ end
21
+ end
@@ -0,0 +1,375 @@
1
+ require_relative 'types'
2
+ require_relative 'arquivo_data'
3
+ require_relative 'arquivo_cargo'
4
+ require_relative 'arquivo_empregado'
5
+ require_relative 'arquivo_empresa'
6
+ require_relative 'arquivo_horario'
7
+ require_relative 'arquivo_lotacao'
8
+ require_relative 'arquivo_estabelecimento'
9
+ require_relative 'arquivo_trabalhador_sem_vinculo'
10
+ require_relative 'arquivo_empregado_historico_cadastral'
11
+ require_relative 'arquivo_empregado_historico_contratual'
12
+ require_relative 'arquivo_empregado_desligamento'
13
+ require_relative 'arquivo_trabalhador_sem_vinculo_historico_contratual'
14
+ require_relative 'arquivo_empregado_afastamento_inicio'
15
+ require_relative 'arquivo_empregado_afastamento_termino'
16
+ require_relative 'arquivo_trabalhador_sem_vinculo_termino'
17
+ require_relative 'arquivo_aviso_previo'
18
+ require_relative 'arquivo_comunicacao_de_acidente_de_trabalho'
19
+ require_relative 'arquivo_atestado_de_saude_ocupacional'
20
+ require_relative 'arquivo_condicao_ambiental_de_trabalho'
21
+ require_relative 'gerador_de_arquivos'
22
+ require_relative 'sistema_operacional'
23
+
24
+ class Processamento
25
+ private
26
+
27
+ def initialize(caminho_do_diretorio_data, grupo_informado)
28
+ @caminho_do_diretorio_data = caminho_do_diretorio_data
29
+ @grupo_informado = grupo_informado
30
+
31
+ @erros = []
32
+ @erros_lotacoes_gerais = []
33
+ @alertas = []
34
+ @arquivos_convertidos = []
35
+ @prefixos_nao_configurados = Set.new
36
+ @gerador_de_arquivos_csv = GeradorDeArquivosCsv.new(pasta_destino_csv)
37
+ end
38
+
39
+ attr_reader :caminho_do_diretorio_data,
40
+ :grupo_informado,
41
+ :arquivos_convertidos,
42
+ :alertas,
43
+ :erros,
44
+ :prefixos_nao_configurados,
45
+ :gerador_de_arquivos_csv
46
+
47
+ public
48
+
49
+ def executar
50
+ return arquivos_data_nao_encontrados_erro if arquivos_data_nao_encontrados?
51
+
52
+ processar_arquivos_data
53
+
54
+ ajustar_afastamentos_de_inicio
55
+
56
+ gerar_erros_afastamentos_com_termino_sem_inicio
57
+ gerar_erros_horarios_duplicados
58
+ gerar_erros_cargos_duplicados
59
+ gerar_erros_empregado_com_contrato_concomitante
60
+
61
+ criar_pasta_destino_csv
62
+
63
+ erros_ordenados = erros.sort_by { [_1[:type_file], _1[:file_name], _1[:error_message]] }
64
+ alertas_ordenados = alertas.sort_by { [_1[:type_file], _1[:file_name], _1[:alert_message]] }
65
+ gerador_de_arquivos_csv.executar(nome_do_arquivo: 'INFO_erros', dados: erros_ordenados)
66
+ gerador_de_arquivos_csv.executar(nome_do_arquivo: 'INFO_alertas', dados: alertas_ordenados)
67
+ GeradorDeArquivos.new(
68
+ gerador_de_arquivos_csv: gerador_de_arquivos_csv,
69
+ arquivos_convertidos: arquivos_convertidos
70
+ ).executar
71
+
72
+ {
73
+ arquivos_convertidos: arquivos_convertidos,
74
+ erros: erros_ordenados,
75
+ alertas: alertas_ordenados,
76
+ prefixos_nao_configurados: prefixos_nao_configurados,
77
+ quantidade_de_erros: erros_ordenados.size,
78
+ quantidade_de_alertas: alertas_ordenados.size,
79
+ quantidade_de_arquivos_validos: arquivos_convertidos.size
80
+ }
81
+ end
82
+
83
+ private
84
+
85
+ def arquivos_data_nao_encontrados?
86
+ arquivos_data_nao_encontrados.any?
87
+ end
88
+
89
+ def arquivos_data_nao_encontrados
90
+ @arquivos_data_nao_encontrados ||=
91
+ (arquivos_html_que_requerem_arquivo_data - arquivos_data.map { _1.delete_suffix('.data') })
92
+ .map { "#{_1}.data" }
93
+ end
94
+
95
+ def arquivos_html_que_requerem_arquivo_data
96
+ @arquivos_html_que_requerem_arquivo_data ||=
97
+ [
98
+ *arquivos_html.select { _1.start_with?('CAR') },
99
+ *arquivos_html.select { _1.start_with?('HOR') },
100
+ *arquivos_html.select { _1.start_with?('LOT') },
101
+ *arquivos_html
102
+ .select { _1.start_with?('TRA') }
103
+ .reject { _1.include?('CAD') || _1.include?('CON') || _1.include?('DES') }
104
+ ].map { _1.delete_suffix('.html') }
105
+ end
106
+
107
+ def arquivos_html
108
+ @arquivos_html ||= arquivos_do_diretorio_data.select { File.extname(_1) == '.html' }
109
+ end
110
+
111
+ def arquivos_do_diretorio_data
112
+ @arquivos_do_diretorio_data ||= Dir.entries(caminho_do_diretorio_data).sort
113
+ end
114
+
115
+ def arquivos_data_nao_encontrados_erro
116
+ @arquivos_data_nao_encontrados_erro ||=
117
+ begin
118
+ fim_de_linha = SistemaOperacional.windows? ? "\r\n" : "\n"
119
+ <<~HEREDOC
120
+ Arquivo(s) .data não encontrado(s):
121
+ #{arquivos_data_nao_encontrados.join("#{fim_de_linha} ")}
122
+
123
+ AÇÃO SUGERIDA:
124
+ Executar robô novamente. Caso o problema continue, tratar com a equipe do robô.
125
+ HEREDOC
126
+ end
127
+ end
128
+
129
+ def arquivos_data
130
+ @arquivos_data ||=
131
+ arquivos_do_diretorio_data
132
+ .select { File.extname(_1) == '.data' }
133
+ .tap do |arquivos|
134
+ # move para o início da lista para garantir que a empresa seja processada antes dos desligamentos (validação do
135
+ # grupo)
136
+ arquivo_de_empresa = arquivos.select { _1.start_with?('EMP') }.min
137
+ arquivos.insert(0, arquivos.delete(arquivo_de_empresa)) if arquivo_de_empresa
138
+ end
139
+ end
140
+
141
+ def processar_arquivos_data # rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
142
+ grupo_da_empresa = nil
143
+ arquivos_data.each do |nome_do_arquivo_data| # rubocop:disable Metrics/BlockLength
144
+ caminho_do_arquivo_data = "#{caminho_do_diretorio_data}#{separador}#{nome_do_arquivo_data}"
145
+ nome_base_do_arquivo_data = File.basename(nome_do_arquivo_data)
146
+ prefixo = nome_base_do_arquivo_data[0, 3]
147
+
148
+ case prefixo
149
+ when 'EMP'
150
+ arquivo_empresa = ArquivoEmpresa.new(caminho_do_arquivo_data, grupo_informado)
151
+ processar(arquivo_empresa)
152
+ grupo_da_empresa = arquivo_empresa.grupo_da_empresa
153
+ when 'EST'
154
+ processar(ArquivoEstabelecimento.new(caminho_do_arquivo_data))
155
+ when 'CAR'
156
+ processar(ArquivoCargo.new(caminho_do_arquivo_data))
157
+ when 'HOR'
158
+ processar(ArquivoHorario.new(caminho_do_arquivo_data))
159
+ when 'LOT'
160
+ processar(ArquivoLotacao.new(caminho_do_arquivo_data))
161
+ when 'TRA'
162
+ sufixo = nome_base_do_arquivo_data.split('-').last[0, 3]
163
+ if sufixo == 'ADM'
164
+ if nome_base_do_arquivo_data.include?('TSV_')
165
+ processar(ArquivoTrabalhadorSemVinculo.new(caminho_do_arquivo_data))
166
+ else
167
+ processar(ArquivoEmpregado.new(caminho_do_arquivo_data))
168
+ end
169
+ elsif nome_base_do_arquivo_data.include?('-ACA[')
170
+ processar(ArquivoEmpregadoHistoricoCadastral.new(caminho_do_arquivo_data))
171
+ elsif nome_base_do_arquivo_data.include?('-ACO[')
172
+ if nome_base_do_arquivo_data.include?('TSV_')
173
+ processar(ArquivoTrabalhadorSemVinculoHistoricoContratual.new(caminho_do_arquivo_data))
174
+ else
175
+ processar(ArquivoEmpregadoHistoricoContratual.new(caminho_do_arquivo_data))
176
+ end
177
+ elsif nome_base_do_arquivo_data.include?('-IAF[') && !nome_base_do_arquivo_data.include?('TSV_')
178
+ processar(ArquivoEmpregadoAfastamentoInicio.new(caminho_do_arquivo_data))
179
+ elsif nome_base_do_arquivo_data.include?('-TAF[') && !nome_base_do_arquivo_data.include?('TSV_')
180
+ processar(ArquivoEmpregadoAfastamentoTermino.new(caminho_do_arquivo_data))
181
+ elsif sufixo == 'GER'
182
+ if nome_base_do_arquivo_data.include?('TSV_')
183
+ processar(ArquivoTrabalhadorSemVinculoTermino.new(caminho_do_arquivo_data))
184
+ else
185
+ processar(ArquivoEmpregadoDesligamento.new(caminho_do_arquivo_data, grupo_da_empresa))
186
+ end
187
+ elsif sufixo == 'AVI'
188
+ processar(ArquivoAvisoPrevio.new(caminho_do_arquivo_data))
189
+ else
190
+ prefixos_nao_configurados << "#{prefixo}-#{sufixo}"
191
+ end
192
+ when 'CAT'
193
+ processar(ArquivoComunicacaoDeAcidenteDeTrabalho.new(caminho_do_arquivo_data))
194
+ when 'ASO'
195
+ processar(ArquivoAtestadoDeSaudeOcupacional.new(caminho_do_arquivo_data))
196
+ when 'AMB'
197
+ processar(ArquivoCondicaoAmbientalDeTrabalho.new(caminho_do_arquivo_data))
198
+ else
199
+ prefixos_nao_configurados << prefixo
200
+ end
201
+ end
202
+ end
203
+
204
+ def processar(arquivo)
205
+ arquivo.processar
206
+ if arquivo.erros.empty?
207
+ arquivos_convertidos << arquivo.atributos
208
+ alertas.push(*arquivo.alertas)
209
+ else
210
+ erros.push(*arquivo.erros)
211
+ end
212
+ arquivo.atributos
213
+ end
214
+
215
+ def ajustar_afastamentos_de_inicio
216
+ afastamentos_de_inicio.each do |afastamento|
217
+ afastamentos_no_inicio_da_fase_2
218
+ .find { _1[:matricula] == afastamento[:matricula] && _1[:data_inicio_afastamento] == afastamento[:data_inicio_afastamento] } # rubocop:disable Layout/LineLength
219
+ &.then do |afastamento_no_inicio_da_fase_2|
220
+ afastamento[:codigo_motivo] = afastamento_no_inicio_da_fase_2[:codigo_motivo]
221
+ end
222
+ end
223
+ end
224
+
225
+ def afastamentos_de_inicio
226
+ @afastamentos_de_inicio ||= arquivos_convertidos.select { _1[:fw_tipo] == 'empregado_afastamento_inicio' }
227
+ end
228
+
229
+ def afastamentos_no_inicio_da_fase_2
230
+ @afastamentos_no_inicio_da_fase_2 ||=
231
+ arquivos_convertidos
232
+ .select { _1[:fw_tipo] == 'empregado' && _1[:data_inicio_afastamento] }
233
+ .map do |empregado|
234
+ {
235
+ matricula: empregado[:matricula],
236
+ data_inicio_afastamento: empregado[:data_inicio_afastamento].to_s,
237
+ codigo_motivo: empregado[:motivo_afastamento]
238
+ }
239
+ end
240
+ end
241
+
242
+ def afastamentos_de_termino_ou_completos
243
+ arquivos_convertidos.select { _1[:fw_tipo] == 'empregado_afastamento_termino' }
244
+ end
245
+
246
+ def gerar_erros_afastamentos_com_termino_sem_inicio
247
+ afastamentos_incompletos
248
+ .select { _1[:fw_tipo] == 'empregado_afastamento_termino' && _1[:inicio_termino_enviados_juntos].nil? }
249
+ .each do |afastamento|
250
+ erros << {
251
+ file_name: afastamento[:file_name],
252
+ type_file: 'empregado_afastamento_termino',
253
+ error_message: "Empregado #{afastamento[:matricula]} possui afastamento de término " \
254
+ "#{afastamento[:data_termino_afastamento]} sem início."
255
+ }
256
+ end
257
+ end
258
+
259
+ def gerar_erros_horarios_duplicados
260
+ horarios
261
+ .map { _1[:codigo_horario_contratual] }
262
+ .tally
263
+ .select { |_, qtd| qtd > 1 }
264
+ .each_key do |codigo_do_horario|
265
+ erros << {
266
+ file_name: '- -',
267
+ type_file: 'horario',
268
+ error_message: "O Horário com código #{codigo_do_horario} possui histórico."
269
+ }
270
+ end
271
+ end
272
+
273
+ def horarios
274
+ arquivos_convertidos.select { _1[:fw_tipo] == 'horario' }
275
+ end
276
+
277
+ def gerar_erros_cargos_duplicados
278
+ cargos
279
+ .map { _1[:codigo_cargo] }
280
+ .tally
281
+ .select { |_, qtd| qtd > 1 }
282
+ .each_key do |codigo_do_cargo|
283
+ erros << {
284
+ file_name: '- -',
285
+ type_file: 'cargo',
286
+ error_message: "O Cargo com código #{codigo_do_cargo} possui histórico."
287
+ }
288
+ end
289
+ end
290
+
291
+ def cargos
292
+ arquivos_convertidos.select { _1[:fw_tipo] == 'cargo' }
293
+ end
294
+
295
+ def gerar_erros_empregado_com_contrato_concomitante
296
+ trabalhadores
297
+ .map { _1[:cpf] }
298
+ .tally
299
+ .select { |_, qtd| qtd > 1 }
300
+ .keys
301
+ .select { contrato_concomitante?(_1) }
302
+ .each do |trabalhador_cpf|
303
+ erros << {
304
+ file_name: '- -',
305
+ type_file: 'empregado',
306
+ error_message: "O Trabalhador com CPF #{trabalhador_cpf} possui contratos de emprego concomitantes."
307
+ }
308
+ end
309
+ end
310
+
311
+ def trabalhadores
312
+ arquivos_convertidos
313
+ .select { _1[:fw_tipo] == 'empregado' }
314
+ .sort_by { _1[:data_admissao_celetista] }
315
+ end
316
+
317
+ def contrato_concomitante?(trabalhador_cpf)
318
+ desligamentos_do_trabalhador = desligamentos.select { _1[:cpf] == trabalhador_cpf }
319
+
320
+ contratos_do_trabalhador =
321
+ trabalhadores
322
+ .select { _1[:cpf] == trabalhador_cpf }
323
+ .map do |trabalhador|
324
+ inicio = trabalhador[:data_admissao_celetista]
325
+ fim = (desligamentos_do_trabalhador.find { _1[:data_admissao] == inicio } || {})[:data_desligamento]
326
+ Struct.new(:inicio, :fim).new(inicio, fim)
327
+ end
328
+
329
+ contratos_do_trabalhador
330
+ .each_cons(2)
331
+ .any? { |contrato_1, contrato_2| contrato_1.fim.nil? || contrato_1.fim >= contrato_2.inicio }
332
+ end
333
+
334
+ def desligamentos
335
+ arquivos_convertidos
336
+ .select { _1[:fw_tipo] == 'empregado_desligamento' }
337
+ .sort_by { _1[:data_desligamento] }
338
+ end
339
+
340
+ def afastamentos_incompletos
341
+ @afastamentos_incompletos ||=
342
+ unir_inicio_termino_afastamentos
343
+ .values
344
+ .select { _1.size < 2 }
345
+ .flatten
346
+ end
347
+
348
+ def unir_inicio_termino_afastamentos
349
+ (afastamentos_de_inicio + afastamentos_de_termino_ou_completos)
350
+ .group_by { [_1[:matricula], _1[:data_inicio_afastamento]] }
351
+ end
352
+
353
+ def criar_pasta_destino_csv
354
+ criar_pasta(pasta_base)
355
+ criar_pasta(pasta_destino_csv)
356
+ end
357
+
358
+ def criar_pasta(pasta)
359
+ Dir.mkdir(pasta)
360
+ rescue Errno::EEXIST
361
+ nil
362
+ end
363
+
364
+ def pasta_base
365
+ @pasta_base ||= "#{SistemaOperacional.windows? ? 'C:\\tmp\\' : '/tmp/'}arquivos_csv_fw"
366
+ end
367
+
368
+ def pasta_destino_csv
369
+ @pasta_destino_csv ||= "#{pasta_base}#{separador}#{Time.now.strftime('%Y%m%d_%H%M%S')}"
370
+ end
371
+
372
+ def separador
373
+ @separador ||= SistemaOperacional.separador
374
+ end
375
+ end
@@ -0,0 +1,33 @@
1
+ module SistemaOperacional
2
+ class << self
3
+ def separador
4
+ windows? ? '\\' : '/'
5
+ end
6
+
7
+ def windows?
8
+ nome == :windows
9
+ end
10
+
11
+ def nome
12
+ host_os = RbConfig::CONFIG['host_os']
13
+ case host_os
14
+ when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
15
+ :windows
16
+ when /darwin|mac os/
17
+ :macosx
18
+ when /linux/
19
+ :linux
20
+ when /solaris|bsd/
21
+ :unix
22
+ else
23
+ raise NaoIdentificado, host_os
24
+ end
25
+ end
26
+ end
27
+
28
+ class NaoIdentificado < StandardError
29
+ def initialize(nome_do_sistema_operacional)
30
+ super("Sistema operacional desconhecido: #{nome_do_sistema_operacional}")
31
+ end
32
+ end
33
+ end