worker-automate-hub 0.5.820__py3-none-any.whl → 0.5.912__py3-none-any.whl

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 (22) hide show
  1. worker_automate_hub/api/client.py +121 -10
  2. worker_automate_hub/api/rpa_historico_service.py +1 -0
  3. worker_automate_hub/tasks/jobs/abertura_livros_fiscais.py +11 -14
  4. worker_automate_hub/tasks/jobs/descartes.py +8 -8
  5. worker_automate_hub/tasks/jobs/devolucao_produtos.py +1386 -0
  6. worker_automate_hub/tasks/jobs/extracao_dados_nielsen.py +504 -0
  7. worker_automate_hub/tasks/jobs/extracao_saldo_estoque_fiscal.py +90 -11
  8. worker_automate_hub/tasks/jobs/fidc_gerar_nosso_numero.py +2 -2
  9. worker_automate_hub/tasks/jobs/fidc_remessa_cobranca_cnab240.py +24 -15
  10. worker_automate_hub/tasks/jobs/importacao_extratos.py +538 -0
  11. worker_automate_hub/tasks/jobs/importacao_extratos_748.py +800 -0
  12. worker_automate_hub/tasks/jobs/inclusao_pedidos_ipiranga.py +222 -0
  13. worker_automate_hub/tasks/jobs/inclusao_pedidos_raizen.py +174 -0
  14. worker_automate_hub/tasks/jobs/inclusao_pedidos_vibra.py +327 -0
  15. worker_automate_hub/tasks/jobs/notas_faturamento_sap.py +438 -157
  16. worker_automate_hub/tasks/jobs/opex_capex.py +523 -384
  17. worker_automate_hub/tasks/task_definitions.py +30 -2
  18. worker_automate_hub/utils/util.py +20 -10
  19. {worker_automate_hub-0.5.820.dist-info → worker_automate_hub-0.5.912.dist-info}/METADATA +2 -1
  20. {worker_automate_hub-0.5.820.dist-info → worker_automate_hub-0.5.912.dist-info}/RECORD +22 -15
  21. {worker_automate_hub-0.5.820.dist-info → worker_automate_hub-0.5.912.dist-info}/WHEEL +0 -0
  22. {worker_automate_hub-0.5.820.dist-info → worker_automate_hub-0.5.912.dist-info}/entry_points.txt +0 -0
@@ -14,280 +14,522 @@ from webdriver_manager.chrome import ChromeDriverManager
14
14
  from selenium.webdriver.common.by import By
15
15
  from selenium.webdriver.common.action_chains import ActionChains
16
16
  from selenium.webdriver.common.keys import Keys
17
+ import sys
18
+ import traceback # para logs de exceção detalhados
19
+
20
+ # Ajuste de path para rodar local ou no worker
21
+ sys.path.append(
22
+ os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", ".."))
23
+ )
24
+
17
25
  from worker_automate_hub.api.client import get_config_by_name, send_file
18
26
  from worker_automate_hub.api.datalake_service import send_file_to_datalake
19
27
  from worker_automate_hub.models.dto.rpa_historico_request_dto import (
20
28
  RpaHistoricoStatusEnum,
21
29
  RpaRetornoProcessoDTO,
22
30
  RpaTagDTO,
23
- RpaTagEnum
31
+ RpaTagEnum,
24
32
  )
25
33
  from worker_automate_hub.models.dto.rpa_sap_dto import RpaProcessoSapDTO
26
- from worker_automate_hub.utils.util import worker_sleep
34
+ from worker_automate_hub.utils.util import (
35
+ kill_all_emsys,
36
+ worker_sleep,
37
+ )
38
+ from selenium.webdriver.support.ui import WebDriverWait
39
+ from selenium.webdriver.support import expected_conditions as EC
27
40
 
28
41
  console = Console()
29
42
 
30
43
  DOWNLOADS_PATH = os.path.join(os.path.expanduser("~"), "Downloads")
44
+ console.print(f"[INIT] Downloads dir: {DOWNLOADS_PATH}")
31
45
 
32
- console.print(f"Downloads dir: {DOWNLOADS_PATH}")
46
+ # ==========================
47
+ # CONFIGURAÇÕES / CONSTANTES
48
+ # ==========================
33
49
 
34
50
  class ConfigEntradaSAP(BaseModel):
51
+ """
52
+ Modelo de referência para a configuração de entrada SAP.
53
+ (Pode ou não ser usado pelo DTO interno, mas deixamos aqui como documentação.)
54
+ """
35
55
  user: str
36
56
  password: str
37
57
  empresa: str
38
58
  unique_id: Optional[str] = "default"
59
+ relatorio: str
60
+
61
+
62
+ def _get_from_config(config, key, default=None):
63
+ """
64
+ Helper para funcionar tanto com dict quanto com BaseModel (Pydantic ou similar).
65
+ """
66
+ if isinstance(config, dict):
67
+ return config.get(key, default)
68
+ return getattr(config, key, default)
69
+
70
+
71
+ # Configuração por tipo de relatório SAP
72
+ SAP_VIEWS = {
73
+ "FAT": {
74
+ "iframe_id": "application-BillingDocument-analyzeRevenue-iframe",
75
+ "input_xpath": '//*[@id="WD86-input"]',
76
+ "url_suffix": "#BillingDocument-analyzeRevenue?sap-ui-tech-hint=WDA",
77
+ },
78
+ "DET": {
79
+ "iframe_id": "application-BillingDocument-analyzeBillingDocItemPricing-iframe",
80
+ "input_xpath": '//*[@id="WDAE-input"]',
81
+ "url_suffix": "#BillingDocument-analyzeBillingDocItemPricing?sap-ui-tech-hint=WDA",
82
+ },
83
+ "PED": {
84
+ "iframe_id": "application-SalesOrder-analyzeIncoming-iframe",
85
+ "input_xpath": '//*[@id="WD86-input"]',
86
+ "url_suffix": "#SalesOrder-analyzeIncoming?sap-ui-tech-hint=WDA",
87
+ },
88
+ }
89
+
90
+ # Mapeamento de empresa para o filtro SAP
91
+ COMPANY_FILTER = {
92
+ "DIS": "SD01",
93
+ "CHA": "CH01",
94
+ "QUE": "QD01",
95
+ }
39
96
 
40
- def get(self, key, default=None):
41
- return getattr(self, key, default)
42
97
 
43
98
  class NotasFaturamentoSAP:
44
- def __init__(self, task: RpaProcessoSapDTO, sap_url, sap_key, sap_token, base_url, directory):
45
- console.print("Inicializando classe NotasFaturamentoSAP.")
99
+ def __init__(
100
+ self,
101
+ task: RpaProcessoSapDTO,
102
+ sap_url: str,
103
+ sap_key: str,
104
+ sap_token: str,
105
+ base_url: str,
106
+ directory: str,
107
+ relatorio_override: Optional[str] = None,
108
+ ):
109
+ console.print("[STEP 0.1] Inicializando classe NotasFaturamentoSAP.")
46
110
  self.task = task
47
111
  self.sap_url = sap_url
48
112
  self.sap_key = sap_key
49
113
  self.sap_token = sap_token
50
- self.user = task.configEntrada.get("user")
51
- self.password = task.configEntrada.get("password")
52
114
  self.base_url = base_url
53
- self.empresa = task.configEntrada.get("empresa")
54
115
  self.directory = directory
116
+
117
+ config = task.configEntrada # pode ser dict ou BaseModel
118
+
119
+ self.user = _get_from_config(config, "user")
120
+ self.password = _get_from_config(config, "password")
121
+ self.empresa = _get_from_config(config, "empresa")
122
+
123
+ # prioridade:
124
+ # 1) override explícito
125
+ # 2) campo relatorio em configEntrada
126
+ # 3) campo relatorio direto no task (se existir)
127
+ self.relatorio = (
128
+ (relatorio_override or "").upper()
129
+ or _get_from_config(config, "relatorio", "").upper()
130
+ or getattr(task, "relatorio", "").upper()
131
+ )
132
+
55
133
  self.unique_id = "default"
56
134
  self.driver = None
57
135
 
58
- async def start_sap_process(self):
59
- console.print("Iniciando o processo SAP.")
60
- sim_service = Service(ChromeDriverManager().install())
61
- self.driver = webdriver.Chrome(service=sim_service)
62
- self.driver.maximize_window()
63
- console.print("Driver inicializado e janela maximizada.")
64
-
65
- await self.save_process_pid()
66
- console.print("PID do processo salvo com sucesso.")
67
- self.driver.get(self.base_url)
68
- console.print(f"Acessando a URL base: {self.base_url}")
69
- await worker_sleep(3)
70
-
71
- if not await self.login():
72
- msg = f"Falha ao realizar login no SAP"
73
- console.print(msg)
74
- return RpaRetornoProcessoDTO(
75
- sucesso=False,
76
- retorno=msg,
77
- status=RpaHistoricoStatusEnum.Falha,
78
- tags=[RpaTagDTO(descricao=RpaTagEnum.Tecnico)]
79
- )
136
+ console.print(
137
+ f"[STEP 0.2] user={self.user} empresa={self.empresa} "
138
+ f"relatorio={self.relatorio}"
139
+ )
80
140
 
81
- console.print("Login realizado com sucesso.")
82
- await self.download_files(self.empresa, "FAT")
83
- if self.driver:
84
- try:
85
- self.driver.quit()
86
- self.driver = None
87
- console.print("Driver fechado e referência removida.")
88
- except Exception as e:
89
- msg = f"Erro ao fechar o driver: {e}"
90
- console.print(msg)
141
+ async def start_sap_process(self) -> RpaRetornoProcessoDTO:
142
+ """
143
+ Fluxo principal do SAP com logs de etapa:
144
+ - KILL_ALL_EMSYS
145
+ - INIT_CHROMEDRIVER
146
+ - INIT_WEBDRIVER
147
+ - VALIDATE_CONFIG
148
+ - SAVE_PID
149
+ - GET_BASE_URL
150
+ - LOGIN
151
+ - DOWNLOAD_FILES
152
+ """
153
+ step = "INIT"
154
+ console.print("[STEP] Iniciando o processo SAP.")
155
+
156
+ try:
157
+ # 0) Validar config básica
158
+ step = "VALIDATE_CONFIG"
159
+ if not self.user or not self.password:
160
+ msg = f"[{step}] Credenciais SAP inválidas: user={self.user} password={'***' if self.password else None}"
161
+ console.print("[ERRO] " + msg)
162
+ return RpaRetornoProcessoDTO(
163
+ sucesso=False,
164
+ retorno=msg,
165
+ status=RpaHistoricoStatusEnum.Falha,
166
+ tags=[RpaTagDTO(descricao=RpaTagEnum.Tecnico)],
167
+ )
168
+
169
+ if not self.base_url:
170
+ msg = f"[{step}] base_url não configurada."
171
+ console.print("[ERRO] " + msg)
91
172
  return RpaRetornoProcessoDTO(
92
173
  sucesso=False,
93
174
  retorno=msg,
94
175
  status=RpaHistoricoStatusEnum.Falha,
95
- tags=[RpaTagDTO(descricao=RpaTagEnum.Tecnico)]
176
+ tags=[RpaTagDTO(descricao=RpaTagEnum.Tecnico)],
96
177
  )
97
178
 
179
+ if self.relatorio not in SAP_VIEWS:
180
+ msg = f"[{step}] Tipo de relatório não mapeado: {self.relatorio}"
181
+ console.print("[ERRO] " + msg)
182
+ return RpaRetornoProcessoDTO(
183
+ sucesso=False,
184
+ retorno=msg,
185
+ status=RpaHistoricoStatusEnum.Falha,
186
+ tags=[RpaTagDTO(descricao=RpaTagEnum.Tecnico)],
187
+ )
188
+
189
+ if self.empresa not in COMPANY_FILTER:
190
+ msg = f"[{step}] Empresa não mapeada: {self.empresa}"
191
+ console.print("[ERRO] " + msg)
192
+ return RpaRetornoProcessoDTO(
193
+ sucesso=False,
194
+ retorno=msg,
195
+ status=RpaHistoricoStatusEnum.Falha,
196
+ tags=[RpaTagDTO(descricao=RpaTagEnum.Tecnico)],
197
+ )
198
+
199
+ # 1) Matar instâncias antigas
200
+ step = "KILL_ALL_EMSYS"
201
+ console.print(f"[STEP={step}] Matando instâncias EMsys/Chrome antigas...")
202
+ await kill_all_emsys()
203
+ console.print(f"[STEP={step}] kill_all_emsys concluído.")
204
+
205
+ # 2) Instalar ChromeDriver
206
+ step = "INIT_CHROMEDRIVER"
207
+ console.print(f"[STEP={step}] Instalando/obtendo ChromeDriver...")
208
+ sim_service = Service(ChromeDriverManager().install())
209
+ console.print(f"[STEP={step}] ChromeDriverManager instalado com sucesso.")
210
+
211
+ # 3) Inicializar webdriver
212
+ step = "INIT_WEBDRIVER"
213
+ console.print(f"[STEP={step}] Inicializando webdriver.Chrome...")
214
+ self.driver = webdriver.Chrome(service=sim_service)
215
+ self.driver.maximize_window()
216
+ console.print(f"[STEP={step}] Driver inicializado e janela maximizada.")
217
+
218
+ # 4) Salvar PID
219
+ step = "SAVE_PID"
220
+ console.print(f"[STEP={step}] Salvando PID do Chrome...")
221
+ ret_pid = await self.save_process_pid()
222
+ if isinstance(ret_pid, RpaRetornoProcessoDTO) and not ret_pid.sucesso:
223
+ console.print(f"[STEP={step}] Falha ao salvar PID, retornando erro.")
224
+ return ret_pid
225
+ console.print(f"[STEP={step}] PID salvo com sucesso.")
226
+
227
+ # 5) Acessar URL base
228
+ step = "GET_BASE_URL"
229
+ console.print(f"[STEP={step}] Acessando URL base do SAP...")
230
+ console.print(f"[STEP={step}] base_url: {self.base_url}")
231
+ self.driver.get(self.base_url)
232
+ console.print(f"[STEP={step}] driver.get(base_url) concluído.")
233
+ await worker_sleep(3)
234
+
235
+ # 6) Login
236
+ step = "LOGIN"
237
+ console.print(f"[STEP={step}] Realizando login no SAP...")
238
+ login_ok = await self.login()
239
+ if not login_ok:
240
+ msg = f"Falha ao realizar login no SAP (etapa {step})"
241
+ console.print(f"[STEP={step}] {msg}")
242
+ return RpaRetornoProcessoDTO(
243
+ sucesso=False,
244
+ retorno=msg,
245
+ status=RpaHistoricoStatusEnum.Falha,
246
+ tags=[RpaTagDTO(descricao=RpaTagEnum.Tecnico)],
247
+ )
248
+ console.print(f"[STEP={step}] Login realizado com sucesso.")
249
+
250
+ # 7) Download / Export
251
+ step = "DOWNLOAD_FILES"
252
+ console.print(f"[STEP={step}] Iniciando download de arquivos...")
253
+ await self.download_files(self.empresa, self.relatorio)
254
+ console.print(f"[STEP={step}] Download de arquivos concluído.")
255
+
256
+ return RpaRetornoProcessoDTO(
257
+ sucesso=True,
258
+ retorno="Processo SAP executado com sucesso.",
259
+ status=RpaHistoricoStatusEnum.Sucesso,
260
+ )
261
+
262
+ except Exception as e:
263
+ tb = traceback.format_exc()
264
+ msg = (
265
+ f"Erro em start_sap_process na etapa '{step}': "
266
+ f"{type(e).__name__}: {e}"
267
+ )
268
+ console.print("[ERRO] " + msg)
269
+ console.print("[ERRO] Traceback completo:")
270
+ console.print(tb)
271
+
272
+ return RpaRetornoProcessoDTO(
273
+ sucesso=False,
274
+ retorno=msg + "\n" + tb,
275
+ status=RpaHistoricoStatusEnum.Falha,
276
+ tags=[RpaTagDTO(descricao=RpaTagEnum.Tecnico)],
277
+ )
278
+
98
279
  async def save_process_pid(self):
99
280
  try:
100
- console.print("Salvando o PID do processo do Chrome.")
281
+ console.print("[STEP PID] Salvando o PID do processo do Chrome.")
101
282
  pid = str(self.driver.service.process.pid)
102
283
  file_path = f"c:\\tmp\\chrome_pid_{self.unique_id}.txt"
284
+ os.makedirs(os.path.dirname(file_path), exist_ok=True)
103
285
  with open(file_path, "w") as f:
104
286
  f.write(pid)
105
- console.print(f"PID salvo em: {file_path}")
287
+ console.print(f"[STEP PID] PID salvo em: {file_path}")
106
288
  except Exception as e:
107
289
  msg = f"Erro ao salvar PID: {e}"
108
- console.print(msg)
290
+ console.print(f"[ERRO PID] {msg}")
291
+ console.print("[ERRO PID] Traceback:")
292
+ console.print(traceback.format_exc())
109
293
  return RpaRetornoProcessoDTO(
110
294
  sucesso=False,
111
295
  retorno=msg,
112
296
  status=RpaHistoricoStatusEnum.Falha,
113
- tags=[RpaTagDTO(descricao=RpaTagEnum.Tecnico)]
297
+ tags=[RpaTagDTO(descricao=RpaTagEnum.Tecnico)],
114
298
  )
115
299
 
116
- async def login(self):
300
+ async def login(self) -> bool:
117
301
  try:
118
- console.print("Iniciando login no SAP.")
119
- for _ in range(30):
302
+ console.print("[LOGIN] Iniciando login no SAP.")
303
+ for i in range(30):
120
304
  inputs = self.driver.find_elements(By.CLASS_NAME, "loginInputField")
305
+ console.print(
306
+ f"[LOGIN] Tentativa {i+1}/30 - qtd campos encontrados: {len(inputs)}"
307
+ )
121
308
  if len(inputs) >= 2:
122
- console.print("Campos de entrada de login encontrados.")
309
+ console.print("[LOGIN] Campos de entrada de login encontrados.")
123
310
  break
124
311
  await worker_sleep(1)
125
312
 
126
313
  inputs = self.driver.find_elements(By.CLASS_NAME, "loginInputField")
314
+ if len(inputs) < 2:
315
+ console.print("[LOGIN][ERRO] Não encontrou campos de login.")
316
+ return False
317
+
127
318
  inputs[0].send_keys(self.user)
128
- console.print("Usuário inserido no campo de login.")
319
+ console.print("[LOGIN] Usuário inserido no campo de login.")
129
320
  await worker_sleep(2)
130
321
  inputs[1].send_keys(self.password)
131
- console.print("Senha inserida no campo de login.")
322
+ console.print("[LOGIN] Senha inserida no campo de login.")
132
323
  await worker_sleep(1)
133
324
 
134
- for _ in range(10):
325
+ for i in range(10):
135
326
  login_btn = self.driver.find_elements(By.ID, "LOGIN_SUBMIT_BLOCK")
327
+ console.print(
328
+ f"[LOGIN] Tentativa {i+1}/10 - btn login encontrado? {bool(login_btn)}"
329
+ )
136
330
  if login_btn:
137
331
  login_btn[0].click()
138
- console.print("Botão de login clicado.")
332
+ console.print("[LOGIN] Botão de login clicado.")
139
333
  break
140
334
  await worker_sleep(1)
141
335
 
142
336
  await worker_sleep(3)
143
- console.print("Login realizado com sucesso.")
337
+ console.print("[LOGIN] Login finalizado (sem exceção).")
144
338
  return True
145
339
  except Exception as e:
146
- console.print(f"Erro ao realizar login: {e}")
340
+ console.print("[LOGIN][ERRO] Exceção ao realizar login.")
341
+ console.print(f"[LOGIN][ERRO] Tipo: {type(e).__name__}")
342
+ console.print(f"[LOGIN][ERRO] str(e): {str(e)}")
343
+ console.print("[LOGIN][ERRO] Traceback:")
344
+ console.print(traceback.format_exc())
147
345
  return False
148
346
 
149
347
  async def download_files(self, company: str, file_type: str):
150
- console.print(f"Iniciando download de arquivos para empresa {company} e tipo {file_type}.")
348
+ console.print(
349
+ f"[DW] Iniciando download de arquivos para empresa {company} e tipo {file_type}."
350
+ )
351
+ console.print(f"[DW] self.relatorio={self.relatorio}")
352
+
353
+ view_conf = SAP_VIEWS.get(file_type)
354
+ if not view_conf:
355
+ console.print(f"[DW][ERRO] Tipo de arquivo não mapeado: {file_type}")
356
+ return
357
+
151
358
  doc_url = self.get_document_url(file_type)
152
- console.print(f"Acessando a URL do documento: {doc_url}")
359
+ console.print(f"[DW] Acessando a URL do documento: {doc_url}")
153
360
  self.driver.get(doc_url)
361
+ console.print("[DW] URL do documento carregada.")
154
362
  await worker_sleep(15)
155
363
 
156
- if len(self.driver.find_elements(By.ID, "searchFieldInShell-input-inner")) < 1:
157
- for _ in range(10):
158
- sf = self.driver.find_elements(By.ID, "sf")
159
- if sf:
160
- sf[0].click()
161
- console.print("Elemento 'sf' clicado.")
162
- break
163
- await worker_sleep(1)
364
+ campo = None
164
365
 
165
- for _ in range(10):
166
- if len(self.driver.find_elements(By.ID, "searchFieldInShell-input-inner")) > 0:
167
- console.print("Campo de busca encontrado.")
168
- break
169
- await worker_sleep(1)
366
+ try:
367
+ console.print("[DW] Localizando iframe e campo de filtro...")
368
+ iframe = WebDriverWait(self.driver, 30).until(
369
+ EC.presence_of_element_located((By.ID, view_conf["iframe_id"]))
370
+ )
371
+ self.driver.switch_to.frame(iframe)
372
+ console.print("[DW] Iframe principal do SAP acessado com sucesso.")
170
373
 
171
- self.driver.find_element(By.ID, "searchFieldInShell-input-inner").click()
172
- console.print("Campo de busca clicado.")
173
- await worker_sleep(5)
374
+ campo = WebDriverWait(self.driver, 30).until(
375
+ EC.presence_of_element_located((By.XPATH, view_conf["input_xpath"]))
376
+ )
377
+ console.print("[DW] Input de filtro localizado dentro do iframe.")
174
378
 
175
- actions = ActionChains(self.driver)
176
- console.print("Executando ações para navegar pelos campos.")
177
- for _ in range(34):
178
- actions.send_keys(Keys.TAB)
179
- actions.perform()
180
- await worker_sleep(0.5)
181
- actions.send_keys(Keys.ENTER)
182
- actions.perform()
183
- await worker_sleep(5)
184
- actions.send_keys(Keys.ENTER)
185
- actions.perform()
186
- await worker_sleep(5)
187
- actions.send_keys(Keys.ENTER)
188
- actions.perform()
189
- await worker_sleep(15)
190
- console.print("Ações concluídas.")
191
- if self.driver:
192
- try:
193
- self.driver.quit()
194
- self.driver = None
195
- console.print("Driver fechado e referência removida.")
196
- except Exception as e:
197
- msg = f"Erro ao fechar o driver: {e}"
198
- console.print(msg)
199
- return RpaRetornoProcessoDTO(
200
- sucesso=False,
201
- retorno=msg,
202
- status=RpaHistoricoStatusEnum.Falha,
203
- tags=[RpaTagDTO(descricao=RpaTagEnum.Tecnico)]
379
+ if not campo:
380
+ console.print(
381
+ "[DW][ERRO] Campo de filtro não localizado. Abortando exportação."
382
+ )
383
+ return
384
+
385
+ console.print(f"[DW] Preenchendo empresa para company={company}")
386
+ filtro_empresa = COMPANY_FILTER.get(company)
387
+ if not filtro_empresa:
388
+ console.print(f"[DW][ERRO] Empresa não mapeada: {company}")
389
+ return
390
+
391
+ campo.send_keys(filtro_empresa)
392
+ await worker_sleep(2)
393
+
394
+ console.print("[DW] Clicando em 'Início' após filtros...")
395
+ botao_inicio = WebDriverWait(self.driver, 20).until(
396
+ EC.element_to_be_clickable(
397
+ (By.XPATH, "//div[@role='button' and contains(., 'nício')]")
204
398
  )
205
- await self.rename_file(company, file_type)
399
+ )
400
+ botao_inicio.click()
401
+ console.print("[DW] Botão 'Início' clicado.")
402
+ await worker_sleep(5)
403
+
404
+ console.print("[DW] Localizando botão de exportar...")
405
+ botao_exportar = WebDriverWait(self.driver, 30).until(
406
+ EC.element_to_be_clickable((By.XPATH, "//div[@title='Exportar']"))
407
+ )
408
+
409
+ botao_exportar.click()
410
+ console.print("[DW] Botão de exportar clicado.")
411
+
412
+ opcao_exportar = WebDriverWait(self.driver, 20).until(
413
+ EC.element_to_be_clickable((By.XPATH, "//span[text()='Exportar']"))
414
+ )
415
+ opcao_exportar.click()
416
+ console.print("[DW] Opção 'Exportar' clicada com sucesso!")
417
+
418
+ await worker_sleep(2)
419
+
420
+ console.print("[DW] Pressionando ENTER para confirmar exportação...")
421
+ actions = ActionChains(self.driver)
422
+ actions.send_keys(Keys.ENTER)
423
+ actions.perform()
424
+
425
+ await worker_sleep(15)
426
+
427
+ console.print("[DW] Chamando rename_file...")
428
+ await self.rename_file(company, file_type)
429
+ console.print("[DW] rename_file concluído.")
430
+
431
+ except Exception as e:
432
+ console.print("[DW][ERRO] Exceção em download_files.")
433
+ console.print(f"[DW][ERRO] Tipo: {type(e).__name__}")
434
+ console.print(f"[DW][ERRO] str(e): {str(e)}")
435
+ console.print("[DW][ERRO] Traceback:")
436
+ console.print(traceback.format_exc())
437
+ raise # deixa subir para o try/except do start_sap_process
206
438
 
207
439
  def get_document_url(self, file_type: str) -> str:
208
- console.print(f"Obtendo URL do documento para o tipo {file_type}.")
209
- mapping = {
210
- "FAT": "#BillingDocument-analyzeRevenue?sap-ui-tech-hint=WDA",
211
- "DET": "#BillingDocument-analyzeBillingDocItemPricing?sap-ui-tech-hint=WDA",
212
- "PED": "#SalesOrder-analyzeIncoming?sap-ui-tech-hint=WDA",
213
- }
214
- suffix = mapping.get(file_type.upper(), "")
215
- return f"{self.base_url}{suffix}"
440
+ console.print(f"[URL] Obtendo URL do documento para o tipo {file_type}.")
441
+ view_conf = SAP_VIEWS.get(file_type)
442
+ suffix = view_conf["url_suffix"] if view_conf else ""
443
+ url_final = f"{self.base_url}{suffix}"
444
+ console.print(f"[URL] URL final: {url_final}")
445
+ return url_final
216
446
 
217
447
  async def rename_file(self, company: str, file_type: str):
218
- console.print("Arquivos baixados com sucesso")
219
- console.print("Iniciando renomeação e movimentação do arquivo.")
448
+ console.print("[RF] Iniciando renomeação e movimentação do arquivo.")
220
449
  try:
221
450
  current_path = os.path.join(DOWNLOADS_PATH, "export.xlsx")
451
+ console.print(f"[RF] current_path esperado: {current_path}")
452
+ console.print(f"[RF] Arquivo existe? {os.path.exists(current_path)}")
453
+
222
454
  date_now = datetime.now().strftime("%Y%m%d%H%M%S")
223
455
  filename = f"{company}_{file_type}_{date_now}.xlsx"
224
456
  final_path = os.path.join(DOWNLOADS_PATH, filename)
225
-
226
- console.print(f"filename: {filename}")
227
-
457
+
458
+ console.print(f"[RF] Novo filename: {filename}")
459
+
228
460
  os.rename(current_path, final_path)
229
- console.print(f"Arquivo renomeado para {final_path}.")
230
-
231
- with open(final_path, 'rb') as file:
232
- file_bytes = io.BytesIO(file.read())
233
-
234
- await worker_sleep(5)
461
+ console.print(f"[RF] Arquivo renomeado para {final_path}.")
235
462
 
236
- await self.send_message_to_webhook(f"Arquivo gerado via RPA: {filename}")
463
+ with open(final_path, "rb") as file:
464
+ file_bytes = io.BytesIO(file.read())
237
465
 
238
- await worker_sleep(2)
466
+ await worker_sleep(5)
239
467
 
240
468
  try:
241
- console.print(f"directory: {self.directory}")
242
- console.print(f"file: {final_path}")
243
- send_file_request = await send_file_to_datalake(self.directory, file_bytes, filename, "xlsx")
244
- console.print(send_file_request)
469
+ console.print(f"[RF] directory: {self.directory}")
470
+ console.print(f"[RF] file: {final_path}")
471
+ send_file_request = await send_file_to_datalake(
472
+ self.directory, file_bytes, filename, "xlsx"
473
+ )
474
+ console.print(
475
+ f"[RF] Resposta send_file_to_datalake: {send_file_request}"
476
+ )
245
477
  except Exception as e:
246
- console.print(f"Erro ao enviar o arquivo: {e}", style="bold red")
478
+ console.print(
479
+ f"[RF][ERRO] Erro ao enviar o arquivo: {e}", style="bold red"
480
+ )
481
+ console.print("[RF][ERRO] Traceback:")
482
+ console.print(traceback.format_exc())
247
483
  return RpaRetornoProcessoDTO(
248
484
  sucesso=False,
249
485
  retorno=f"Erro ao enviar o arquivo: {e}",
250
486
  status=RpaHistoricoStatusEnum.Falha,
251
- tags=[RpaTagDTO(descricao=RpaTagEnum.Tecnico)]
487
+ tags=[RpaTagDTO(descricao=RpaTagEnum.Tecnico)],
252
488
  )
253
-
489
+
254
490
  await worker_sleep(2)
255
491
 
256
492
  if final_path and os.path.exists(final_path):
257
493
  try:
258
494
  os.remove(final_path)
259
- console.print(f"Arquivo deletado: {final_path}")
495
+ console.print(f"[RF] Arquivo deletado: {final_path}")
260
496
  except Exception as e:
261
497
  msg = f"Erro ao deletar o arquivo: {e}"
262
- console.print(msg)
498
+ console.print(f"[RF][ERRO] {msg}")
499
+ console.print("[RF][ERRO] Traceback:")
500
+ console.print(traceback.format_exc())
263
501
  return RpaRetornoProcessoDTO(
264
502
  sucesso=False,
265
503
  retorno=msg,
266
504
  status=RpaHistoricoStatusEnum.Falha,
267
- tags=[RpaTagDTO(descricao=RpaTagEnum.Tecnico)]
505
+ tags=[RpaTagDTO(descricao=RpaTagEnum.Tecnico)],
268
506
  )
269
507
 
270
508
  except FileNotFoundError as fne:
271
509
  msg = f"Não foi possível renomear o arquivo, error: {fne}"
272
- console.print(msg)
510
+ console.print(f"[RF][ERRO] {msg}")
511
+ console.print("[RF][ERRO] Traceback:")
512
+ console.print(traceback.format_exc())
273
513
  return RpaRetornoProcessoDTO(
274
514
  sucesso=False,
275
515
  retorno=msg,
276
516
  status=RpaHistoricoStatusEnum.Falha,
277
- tags=[RpaTagDTO(descricao=RpaTagEnum.Tecnico)]
517
+ tags=[RpaTagDTO(descricao=RpaTagEnum.Tecnico)],
278
518
  )
279
519
  except Exception as e:
280
520
  msg = f"Erro ao renomear/mover arquivo: {e}"
281
- console.print(msg)
521
+ console.print(f"[RF][ERRO] {msg}")
522
+ console.print("[RF][ERRO] Traceback:")
523
+ console.print(traceback.format_exc())
282
524
  return RpaRetornoProcessoDTO(
283
525
  sucesso=False,
284
526
  retorno=msg,
285
527
  status=RpaHistoricoStatusEnum.Falha,
286
- tags=[RpaTagDTO(descricao=RpaTagEnum.Tecnico)]
528
+ tags=[RpaTagDTO(descricao=RpaTagEnum.Tecnico)],
287
529
  )
288
530
 
289
531
  async def send_message_to_webhook(self, message: str):
290
- console.print("Enviando mensagem ao webhook.")
532
+ console.print("[WEBHOOK] Enviando mensagem ao webhook.")
291
533
  await worker_sleep(2)
292
534
  try:
293
535
  payload = {"text": "📢 " + message}
@@ -296,44 +538,83 @@ class NotasFaturamentoSAP:
296
538
  webhook_url,
297
539
  data=json.dumps(payload),
298
540
  headers={"Content-Type": "application/json"},
299
- timeout=10
541
+ timeout=10,
300
542
  )
301
- console.print("Mensagem enviada ao webhook com sucesso.")
543
+ console.print("[WEBHOOK] Mensagem enviada ao webhook com sucesso.")
302
544
  await worker_sleep(2)
303
545
  except Exception as e:
304
546
  msg = f"Erro ao enviar mensagem ao webhook: {e}"
305
- console.print(msg)
547
+ console.print(f"[WEBHOOK][ERRO] {msg}")
548
+ console.print("[WEBHOOK][ERRO] Traceback:")
549
+ console.print(traceback.format_exc())
306
550
  return RpaRetornoProcessoDTO(
307
551
  sucesso=False,
308
552
  retorno=msg,
309
553
  status=RpaHistoricoStatusEnum.Falha,
310
- tags=[RpaTagDTO(descricao=RpaTagEnum.Tecnico)]
554
+ tags=[RpaTagDTO(descricao=RpaTagEnum.Tecnico)],
311
555
  )
312
556
 
557
+
313
558
  async def notas_faturamento_sap(task: RpaProcessoSapDTO) -> RpaRetornoProcessoDTO:
314
- console.print("Iniciando processo de notas de faturamento SAP.")
559
+ console.print("[MAIN] Iniciando processo de notas de faturamento SAP.")
560
+ notas_sap = None
315
561
  try:
562
+ console.print("[MAIN] Buscando configuração 'SAP_Faturamento'...")
316
563
  config = await get_config_by_name("SAP_Faturamento")
317
- console.print(f"config: {config}")
564
+ console.print(f"[MAIN] config recebido: {config}")
318
565
  sap_url = config.conConfiguracao.get("sapUrl")
319
566
  sap_key = config.conConfiguracao.get("sapKey")
320
567
  sap_token = config.conConfiguracao.get("sapToken")
321
568
  base_url = config.conConfiguracao.get("baseUrl")
322
569
  directory = config.conConfiguracao.get("directoryBucket")
323
- console.print(sap_url, sap_key, sap_token, base_url, directory)
324
- notas_sap = NotasFaturamentoSAP(task, sap_url, sap_key, sap_token, base_url, directory)
325
- await notas_sap.start_sap_process()
326
- console.print("Processo de automação SAP finalizado com sucesso.")
327
- return RpaRetornoProcessoDTO(
328
- sucesso=True,
329
- retorno="Processo de automação SAP finalizado com sucesso.",
330
- status=RpaHistoricoStatusEnum.Sucesso,
570
+ console.print(
571
+ f"[MAIN] sap_url={sap_url} base_url={base_url} directory={directory}"
572
+ )
573
+
574
+ # PEGAR RELATORIO DE FORMA SEGURA
575
+ relatorio = None
576
+ config_entrada = task.configEntrada
577
+
578
+ if isinstance(config_entrada, dict):
579
+ relatorio = config_entrada.get("relatorio")
580
+ else:
581
+ relatorio = getattr(config_entrada, "relatorio", None)
582
+
583
+ if not relatorio:
584
+ relatorio = "FAT"
585
+
586
+ console.print(f"[MAIN] Relatório usado: {relatorio}")
587
+
588
+ notas_sap = NotasFaturamentoSAP(
589
+ task,
590
+ sap_url,
591
+ sap_key,
592
+ sap_token,
593
+ base_url,
594
+ directory,
595
+ relatorio_override=relatorio,
331
596
  )
597
+
598
+ console.print("[MAIN] Chamando start_sap_process...")
599
+ resultado = await notas_sap.start_sap_process()
600
+ console.print(f"[MAIN] Resultado do start_sap_process: sucesso={resultado.sucesso}")
601
+
602
+ return resultado
603
+
332
604
  except Exception as ex:
333
- console.print(f"Erro na automação SAP: {ex}")
605
+ console.print("[MAIN][ERRO] Exceção em notas_faturamento_sap.")
606
+ console.print(f"[MAIN][ERRO] Tipo: {type(ex).__name__}")
607
+ console.print(f"[MAIN][ERRO] str(ex): {str(ex)}")
608
+ console.print("[MAIN][ERRO] Traceback completo:")
609
+ console.print(traceback.format_exc())
334
610
  return RpaRetornoProcessoDTO(
335
611
  sucesso=False,
336
612
  retorno=f"Erro na automação SAP: {ex}",
337
613
  status=RpaHistoricoStatusEnum.Falha,
338
- tags=[RpaTagDTO(descricao=RpaTagEnum.Tecnico)]
614
+ tags=[RpaTagDTO(descricao=RpaTagEnum.Tecnico)],
339
615
  )
616
+ finally:
617
+ if notas_sap and notas_sap.driver:
618
+ console.print("[MAIN] Fechando driver no final do processo.")
619
+ notas_sap.driver.quit()
620
+ console.print("[MAIN] Fim do processo (finally).")