worker-automate-hub 0.5.912__py3-none-any.whl → 0.5.921__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.
- worker_automate_hub/tasks/jobs/inclusao_pedidos_ipiranga.py +5 -4
- worker_automate_hub/tasks/jobs/inclusao_pedidos_raizen.py +48 -35
- worker_automate_hub/tasks/jobs/inclusao_pedidos_vibra.py +21 -3
- worker_automate_hub/tasks/jobs/lista_clientes_sap.py +631 -0
- worker_automate_hub/tasks/jobs/lista_devolucoes_sap.py +626 -0
- worker_automate_hub/tasks/task_definitions.py +8 -0
- {worker_automate_hub-0.5.912.dist-info → worker_automate_hub-0.5.921.dist-info}/METADATA +1 -1
- {worker_automate_hub-0.5.912.dist-info → worker_automate_hub-0.5.921.dist-info}/RECORD +10 -8
- {worker_automate_hub-0.5.912.dist-info → worker_automate_hub-0.5.921.dist-info}/WHEEL +0 -0
- {worker_automate_hub-0.5.912.dist-info → worker_automate_hub-0.5.921.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,626 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import io
|
|
3
|
+
import os
|
|
4
|
+
import json
|
|
5
|
+
import shutil
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
import requests
|
|
8
|
+
from pydantic import BaseModel
|
|
9
|
+
from typing import Optional
|
|
10
|
+
from rich.console import Console
|
|
11
|
+
from selenium import webdriver
|
|
12
|
+
from selenium.webdriver.chrome.service import Service
|
|
13
|
+
from webdriver_manager.chrome import ChromeDriverManager
|
|
14
|
+
from selenium.webdriver.common.by import By
|
|
15
|
+
from selenium.webdriver.common.action_chains import ActionChains
|
|
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
|
+
|
|
25
|
+
from worker_automate_hub.api.client import get_config_by_name, send_file
|
|
26
|
+
from worker_automate_hub.api.datalake_service import send_file_to_datalake
|
|
27
|
+
from worker_automate_hub.models.dto.rpa_historico_request_dto import (
|
|
28
|
+
RpaHistoricoStatusEnum,
|
|
29
|
+
RpaRetornoProcessoDTO,
|
|
30
|
+
RpaTagDTO,
|
|
31
|
+
RpaTagEnum,
|
|
32
|
+
)
|
|
33
|
+
from worker_automate_hub.models.dto.rpa_sap_dto import RpaProcessoSapDTO
|
|
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
|
|
40
|
+
|
|
41
|
+
console = Console()
|
|
42
|
+
|
|
43
|
+
DOWNLOADS_PATH = os.path.join(os.path.expanduser("~"), "Downloads")
|
|
44
|
+
console.print(f"[INIT] Downloads dir: {DOWNLOADS_PATH}")
|
|
45
|
+
|
|
46
|
+
# ==========================
|
|
47
|
+
# CONFIGURAÇÕES / CONSTANTES
|
|
48
|
+
# ==========================
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class ConfigEntradaSAP(BaseModel):
|
|
52
|
+
"""
|
|
53
|
+
Modelo de referência para a configuração de entrada SAP.
|
|
54
|
+
Agora empresa/relatorio são opcionais e têm default.
|
|
55
|
+
"""
|
|
56
|
+
user: str
|
|
57
|
+
password: str
|
|
58
|
+
empresa: Optional[str] = "DIS"
|
|
59
|
+
unique_id: Optional[str] = "default"
|
|
60
|
+
relatorio: Optional[str] = "FAT"
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _get_from_config(config, key, default=None):
|
|
64
|
+
"""
|
|
65
|
+
Helper para funcionar tanto com dict quanto com BaseModel (Pydantic ou similar).
|
|
66
|
+
"""
|
|
67
|
+
if isinstance(config, dict):
|
|
68
|
+
return config.get(key, default)
|
|
69
|
+
return getattr(config, key, default)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
# (Mantidos para possível uso futuro)
|
|
73
|
+
SAP_VIEWS = {}
|
|
74
|
+
COMPANY_FILTER = {}
|
|
75
|
+
|
|
76
|
+
def clear_downloads_folder():
|
|
77
|
+
"""
|
|
78
|
+
Remove todos os ARQUIVOS da pasta Downloads do usuário.
|
|
79
|
+
Não remove pastas internas.
|
|
80
|
+
"""
|
|
81
|
+
try:
|
|
82
|
+
console.print("[CLEAN] Limpando pasta Downloads...")
|
|
83
|
+
|
|
84
|
+
for item in os.listdir(DOWNLOADS_PATH):
|
|
85
|
+
full_path = os.path.join(DOWNLOADS_PATH, item)
|
|
86
|
+
|
|
87
|
+
# apenas arquivos (evita excluir pastas do usuário)
|
|
88
|
+
if os.path.isfile(full_path):
|
|
89
|
+
try:
|
|
90
|
+
os.remove(full_path)
|
|
91
|
+
console.print(f"[CLEAN] Arquivo removido: {item}")
|
|
92
|
+
except Exception as e:
|
|
93
|
+
console.print(f"[CLEAN][WARN] Não foi possível remover {item}: {e}")
|
|
94
|
+
|
|
95
|
+
console.print("[CLEAN] Downloads limpo com sucesso!")
|
|
96
|
+
|
|
97
|
+
except Exception as e:
|
|
98
|
+
console.print(f"[CLEAN][ERRO] Falha ao limpar Downloads: {e}")
|
|
99
|
+
console.print(traceback.format_exc())
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class ListaDevolucoesSAP:
|
|
104
|
+
def __init__(
|
|
105
|
+
self,
|
|
106
|
+
task: RpaProcessoSapDTO,
|
|
107
|
+
sap_url: str,
|
|
108
|
+
sap_key: str,
|
|
109
|
+
sap_token: str,
|
|
110
|
+
base_url: str,
|
|
111
|
+
directory: str,
|
|
112
|
+
relatorio_override: Optional[str] = None,
|
|
113
|
+
):
|
|
114
|
+
console.print("[STEP 0.1] Inicializando classe ListaClientesSAP.")
|
|
115
|
+
self.task = task
|
|
116
|
+
self.sap_url = sap_url
|
|
117
|
+
self.sap_key = sap_key
|
|
118
|
+
self.sap_token = sap_token
|
|
119
|
+
self.base_url = base_url
|
|
120
|
+
self.directory = directory
|
|
121
|
+
|
|
122
|
+
config = task.configEntrada # pode ser dict ou BaseModel
|
|
123
|
+
|
|
124
|
+
self.user = _get_from_config(config, "user")
|
|
125
|
+
self.password = _get_from_config(config, "password")
|
|
126
|
+
|
|
127
|
+
# empresa padrão DIS se não vier na config (não usamos no fluxo atual, mas deixei)
|
|
128
|
+
self.empresa = _get_from_config(config, "empresa", "DIS") or "DIS"
|
|
129
|
+
|
|
130
|
+
# relatorio default FAT (não usamos no fluxo atual, mas deixei)
|
|
131
|
+
self.relatorio = (
|
|
132
|
+
(relatorio_override or "").upper()
|
|
133
|
+
or _get_from_config(config, "relatorio", "").upper()
|
|
134
|
+
or getattr(task, "relatorio", "").upper()
|
|
135
|
+
or "FAT"
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
self.unique_id = "default"
|
|
139
|
+
self.driver = None
|
|
140
|
+
|
|
141
|
+
console.print(
|
|
142
|
+
f"[STEP 0.2] user={self.user} empresa={self.empresa} "
|
|
143
|
+
f"relatorio={self.relatorio}"
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
async def start_sap_process(self) -> RpaRetornoProcessoDTO:
|
|
147
|
+
"""
|
|
148
|
+
Fluxo principal do SAP com logs de etapa:
|
|
149
|
+
- KILL_ALL_EMSYS
|
|
150
|
+
- INIT_CHROMEDRIVER
|
|
151
|
+
- INIT_WEBDRIVER
|
|
152
|
+
- VALIDATE_CONFIG
|
|
153
|
+
- SAVE_PID
|
|
154
|
+
- GET_BASE_URL
|
|
155
|
+
- LOGIN
|
|
156
|
+
- CLICK_EXIBIR_LISTA_CLIENTES
|
|
157
|
+
- EXPORTAR_LISTA_CLIENTES
|
|
158
|
+
"""
|
|
159
|
+
step = "INIT"
|
|
160
|
+
console.print("[STEP] Iniciando o processo SAP.")
|
|
161
|
+
|
|
162
|
+
try:
|
|
163
|
+
# 0) Validar config básica
|
|
164
|
+
step = "VALIDATE_CONFIG"
|
|
165
|
+
if not self.user or not self.password:
|
|
166
|
+
msg = f"[{step}] Credenciais SAP inválidas: user={self.user} password={'***' if self.password else None}"
|
|
167
|
+
console.print("[ERRO] " + msg)
|
|
168
|
+
return RpaRetornoProcessoDTO(
|
|
169
|
+
sucesso=False,
|
|
170
|
+
retorno=msg,
|
|
171
|
+
status=RpaHistoricoStatusEnum.Falha,
|
|
172
|
+
tags=[RpaTagDTO(descricao=RpaTagEnum.Tecnico)],
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
if not self.base_url:
|
|
176
|
+
msg = f"[{step}] base_url não configurada."
|
|
177
|
+
console.print("[ERRO] " + msg)
|
|
178
|
+
return RpaRetornoProcessoDTO(
|
|
179
|
+
sucesso=False,
|
|
180
|
+
retorno=msg,
|
|
181
|
+
status=RpaHistoricoStatusEnum.Falha,
|
|
182
|
+
tags=[RpaTagDTO(descricao=RpaTagEnum.Tecnico)],
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
# 1) Matar instâncias antigas
|
|
186
|
+
step = "KILL_ALL_EMSYS"
|
|
187
|
+
console.print(f"[STEP={step}] Matando instâncias EMsys/Chrome antigas...")
|
|
188
|
+
await kill_all_emsys()
|
|
189
|
+
console.print(f"[STEP={step}] kill_all_emsys concluído.")
|
|
190
|
+
|
|
191
|
+
clear_downloads_folder()
|
|
192
|
+
console.print("[CLEAN] Downloads limpo antes da exportação.")
|
|
193
|
+
|
|
194
|
+
# 2) Instalar ChromeDriver
|
|
195
|
+
step = "INIT_CHROMEDRIVER"
|
|
196
|
+
console.print(f"[STEP={step}] Instalando/obtendo ChromeDriver...")
|
|
197
|
+
sim_service = Service(ChromeDriverManager().install())
|
|
198
|
+
console.print(f"[STEP={step}] ChromeDriverManager instalado com sucesso.")
|
|
199
|
+
|
|
200
|
+
# 3) Inicializar webdriver
|
|
201
|
+
step = "INIT_WEBDRIVER"
|
|
202
|
+
console.print(f"[STEP={step}] Inicializando webdriver.Chrome...")
|
|
203
|
+
self.driver = webdriver.Chrome(service=sim_service)
|
|
204
|
+
self.driver.maximize_window()
|
|
205
|
+
console.print(f"[STEP={step}] Driver inicializado e janela maximizada.")
|
|
206
|
+
|
|
207
|
+
# 4) Salvar PID
|
|
208
|
+
step = "SAVE_PID"
|
|
209
|
+
console.print(f"[STEP={step}] Salvando PID do Chrome...")
|
|
210
|
+
ret_pid = await self.save_process_pid()
|
|
211
|
+
if isinstance(ret_pid, RpaRetornoProcessoDTO) and not ret_pid.sucesso:
|
|
212
|
+
console.print(f"[STEP={step}] Falha ao salvar PID, retornando erro.")
|
|
213
|
+
return ret_pid
|
|
214
|
+
console.print(f"[STEP={step}] PID salvo com sucesso.")
|
|
215
|
+
|
|
216
|
+
# 5) Acessar URL base
|
|
217
|
+
step = "GET_BASE_URL"
|
|
218
|
+
console.print(f"[STEP={step}] Acessando URL base do SAP...")
|
|
219
|
+
console.print(f"[STEP={step}] base_url: {self.base_url}")
|
|
220
|
+
self.driver.get(self.base_url)
|
|
221
|
+
console.print(f"[STEP={step}] driver.get(base_url) concluído.")
|
|
222
|
+
await worker_sleep(3)
|
|
223
|
+
|
|
224
|
+
# 6) Login
|
|
225
|
+
step = "LOGIN"
|
|
226
|
+
console.print(f"[STEP={step}] Realizando login no SAP...")
|
|
227
|
+
login_ok = await self.login()
|
|
228
|
+
if not login_ok:
|
|
229
|
+
msg = f"Falha ao realizar login no SAP (etapa {step})"
|
|
230
|
+
console.print(f"[STEP={step}] {msg}")
|
|
231
|
+
return RpaRetornoProcessoDTO(
|
|
232
|
+
sucesso=False,
|
|
233
|
+
retorno=msg,
|
|
234
|
+
status=RpaHistoricoStatusEnum.Falha,
|
|
235
|
+
tags=[RpaTagDTO(descricao=RpaTagEnum.Tecnico)],
|
|
236
|
+
)
|
|
237
|
+
console.print(f"[STEP={step}] Login realizado com sucesso.")
|
|
238
|
+
|
|
239
|
+
# 7) Clicar na tela inicial: "Exibir lista de clientes"
|
|
240
|
+
step = "EXIBIR PARTIDAS INDIVIDUAIS NO RAZÃO"
|
|
241
|
+
try:
|
|
242
|
+
console.print(f"[STEP={step}] Procurando botão 'Exibir partidas individuais no razão' na home...")
|
|
243
|
+
await worker_sleep(5) # tempo para a tela inicial carregar
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
# Tenta com aproximação que ignora soft-hyphen
|
|
247
|
+
botao_exibir = WebDriverWait(self.driver, 20).until(
|
|
248
|
+
EC.element_to_be_clickable(
|
|
249
|
+
(
|
|
250
|
+
By.XPATH,
|
|
251
|
+
"//span[contains(translate(normalize-space(.), '', ''), 'Exibir partidas individuais')]",
|
|
252
|
+
)
|
|
253
|
+
)
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
botao_exibir.click()
|
|
258
|
+
console.print(f"[STEP={step}] Botão 'Exibir lista de clientes' clicado com sucesso!")
|
|
259
|
+
await worker_sleep(5)
|
|
260
|
+
|
|
261
|
+
except Exception as e:
|
|
262
|
+
console.print(f"[STEP={step}][ERRO] Não foi possível clicar em 'Exibir lista de clientes': {e}")
|
|
263
|
+
console.print("[STEP={step}] Abortando processo.")
|
|
264
|
+
return RpaRetornoProcessoDTO(
|
|
265
|
+
sucesso=False,
|
|
266
|
+
retorno=f"Erro ao clicar em 'Exibir lista de clientes': {e}",
|
|
267
|
+
status=RpaHistoricoStatusEnum.Falha,
|
|
268
|
+
tags=[RpaTagDTO(descricao=RpaTagEnum.Tecnico)],
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
# 8) Fluxo simples de exportação (Início + Exportar)
|
|
272
|
+
step = "EXPORTAR_PARTIDAS_INDIVIDUAIS_NO_RAZÃO"
|
|
273
|
+
console.print(f"[STEP={step}] Iniciando fluxo de exportação da lista de clientes...")
|
|
274
|
+
await self.exportar_devolucoes()
|
|
275
|
+
console.print(f"[STEP={step}] Exportação concluída com sucesso.")
|
|
276
|
+
|
|
277
|
+
return RpaRetornoProcessoDTO(
|
|
278
|
+
sucesso=True,
|
|
279
|
+
retorno="Processo SAP executado com sucesso.",
|
|
280
|
+
status=RpaHistoricoStatusEnum.Sucesso,
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
except Exception as e:
|
|
284
|
+
tb = traceback.format_exc()
|
|
285
|
+
msg = (
|
|
286
|
+
f"Erro em start_sap_process na etapa '{step}': "
|
|
287
|
+
f"{type(e).__name__}: {e}"
|
|
288
|
+
)
|
|
289
|
+
console.print("[ERRO] " + msg)
|
|
290
|
+
console.print("[ERRO] Traceback completo:")
|
|
291
|
+
console.print(tb)
|
|
292
|
+
|
|
293
|
+
return RpaRetornoProcessoDTO(
|
|
294
|
+
sucesso=False,
|
|
295
|
+
retorno=msg + "\n" + tb,
|
|
296
|
+
status=RpaHistoricoStatusEnum.Falha,
|
|
297
|
+
tags=[RpaTagDTO(descricao=RpaTagEnum.Tecnico)],
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
async def save_process_pid(self):
|
|
301
|
+
try:
|
|
302
|
+
console.print("[STEP PID] Salvando o PID do processo do Chrome.")
|
|
303
|
+
pid = str(self.driver.service.process.pid)
|
|
304
|
+
file_path = f"c:\\tmp\\chrome_pid_{self.unique_id}.txt"
|
|
305
|
+
os.makedirs(os.path.dirname(file_path), exist_ok=True)
|
|
306
|
+
with open(file_path, "w") as f:
|
|
307
|
+
f.write(pid)
|
|
308
|
+
console.print(f"[STEP PID] PID salvo em: {file_path}")
|
|
309
|
+
except Exception as e:
|
|
310
|
+
msg = f"Erro ao salvar PID: {e}"
|
|
311
|
+
console.print(f"[ERRO PID] {msg}")
|
|
312
|
+
console.print("[ERRO PID] Traceback:")
|
|
313
|
+
console.print(traceback.format_exc())
|
|
314
|
+
return RpaRetornoProcessoDTO(
|
|
315
|
+
sucesso=False,
|
|
316
|
+
retorno=msg,
|
|
317
|
+
status=RpaHistoricoStatusEnum.Falha,
|
|
318
|
+
tags=[RpaTagDTO(descricao=RpaTagEnum.Tecnico)],
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
async def login(self) -> bool:
|
|
322
|
+
try:
|
|
323
|
+
console.print("[LOGIN] Iniciando login no SAP.")
|
|
324
|
+
for i in range(30):
|
|
325
|
+
inputs = self.driver.find_elements(By.CLASS_NAME, "loginInputField")
|
|
326
|
+
console.print(
|
|
327
|
+
f"[LOGIN] Tentativa {i+1}/30 - qtd campos encontrados: {len(inputs)}"
|
|
328
|
+
)
|
|
329
|
+
if len(inputs) >= 2:
|
|
330
|
+
console.print("[LOGIN] Campos de entrada de login encontrados.")
|
|
331
|
+
break
|
|
332
|
+
await worker_sleep(1)
|
|
333
|
+
|
|
334
|
+
inputs = self.driver.find_elements(By.CLASS_NAME, "loginInputField")
|
|
335
|
+
if len(inputs) < 2:
|
|
336
|
+
console.print("[LOGIN][ERRO] Não encontrou campos de login.")
|
|
337
|
+
return False
|
|
338
|
+
|
|
339
|
+
inputs[0].send_keys(self.user)
|
|
340
|
+
console.print("[LOGIN] Usuário inserido no campo de login.")
|
|
341
|
+
await worker_sleep(2)
|
|
342
|
+
inputs[1].send_keys(self.password)
|
|
343
|
+
console.print("[LOGIN] Senha inserida no campo de login.")
|
|
344
|
+
await worker_sleep(1)
|
|
345
|
+
|
|
346
|
+
for i in range(10):
|
|
347
|
+
login_btn = self.driver.find_elements(By.ID, "LOGIN_SUBMIT_BLOCK")
|
|
348
|
+
console.print(
|
|
349
|
+
f"[LOGIN] Tentativa {i+1}/10 - btn login encontrado? {bool(login_btn)}"
|
|
350
|
+
)
|
|
351
|
+
if login_btn:
|
|
352
|
+
login_btn[0].click()
|
|
353
|
+
console.print("[LOGIN] Botão de login clicado.")
|
|
354
|
+
break
|
|
355
|
+
await worker_sleep(1)
|
|
356
|
+
|
|
357
|
+
await worker_sleep(3)
|
|
358
|
+
console.print("[LOGIN] Login finalizado (sem exceção).")
|
|
359
|
+
return True
|
|
360
|
+
except Exception as e:
|
|
361
|
+
console.print("[LOGIN][ERRO] Exceção ao realizar login.")
|
|
362
|
+
console.print(f"[LOGIN][ERRO] Tipo: {type(e).__name__}")
|
|
363
|
+
console.print(f"[LOGIN][ERRO] str(e): {str(e)}")
|
|
364
|
+
console.print("[LOGIN][ERRO] Traceback:")
|
|
365
|
+
console.print(traceback.format_exc())
|
|
366
|
+
return False
|
|
367
|
+
|
|
368
|
+
async def wait_for_xlsx_download(self, timeout_seconds=600):
|
|
369
|
+
"""
|
|
370
|
+
Espera até 10 minutos por algum arquivo .xlsx aparecer nos Downloads.
|
|
371
|
+
"""
|
|
372
|
+
console.print("[DW] Aguardando arquivo XLSX aparecer na pasta Downloads...")
|
|
373
|
+
start = datetime.now()
|
|
374
|
+
|
|
375
|
+
while (datetime.now() - start).total_seconds() < timeout_seconds:
|
|
376
|
+
for file in os.listdir(DOWNLOADS_PATH):
|
|
377
|
+
if file.lower().endswith(".xlsx"):
|
|
378
|
+
full_path = os.path.join(DOWNLOADS_PATH, file)
|
|
379
|
+
console.print(f"[DW] Arquivo encontrado: {full_path}")
|
|
380
|
+
return full_path
|
|
381
|
+
|
|
382
|
+
await worker_sleep(2)
|
|
383
|
+
|
|
384
|
+
raise TimeoutError("Nenhum arquivo XLSX encontrado após 10 minutos.")
|
|
385
|
+
|
|
386
|
+
async def exportar_devolucoes(self):
|
|
387
|
+
console.print("[EXP] Iniciando passos de 'Início' e 'Exportar'...")
|
|
388
|
+
try:
|
|
389
|
+
# Clicar em "Início"
|
|
390
|
+
console.print("[EXP] Procurando botão 'Início'...")
|
|
391
|
+
botao_inicio = WebDriverWait(self.driver, 30).until(
|
|
392
|
+
EC.element_to_be_clickable(
|
|
393
|
+
(By.XPATH, "//bdi[contains(normalize-space(.), 'Iniciar')]")
|
|
394
|
+
)
|
|
395
|
+
)
|
|
396
|
+
botao_inicio.click()
|
|
397
|
+
console.print("[EXP] Botão 'Iniciar' clicado.")
|
|
398
|
+
await worker_sleep(5)
|
|
399
|
+
|
|
400
|
+
# Clicar em "Exportar" (botão)
|
|
401
|
+
console.print("[EXP] Localizando botão de Exportar...")
|
|
402
|
+
botao_exportar = WebDriverWait(self.driver, 30).until(
|
|
403
|
+
EC.element_to_be_clickable((By.XPATH, "//button[@aria-label='Abrir menu']"))
|
|
404
|
+
)
|
|
405
|
+
botao_exportar.click()
|
|
406
|
+
console.print("[EXP] Botão 'Exportar' clicado.")
|
|
407
|
+
|
|
408
|
+
await worker_sleep(3)
|
|
409
|
+
|
|
410
|
+
# Clicar na opção "Exportar" do menu
|
|
411
|
+
actions = ActionChains(self.driver)
|
|
412
|
+
actions.send_keys(Keys.ENTER)
|
|
413
|
+
actions.perform()
|
|
414
|
+
console.print("[EXP] Opção 'Exportar' clicada.")
|
|
415
|
+
|
|
416
|
+
try:
|
|
417
|
+
# Clicar em "Exportar" para baixar
|
|
418
|
+
console.print("[EXP] Clicarndo no botao 'Exportar para baixar...")
|
|
419
|
+
botao_exportar = WebDriverWait(self.driver, 30).until(
|
|
420
|
+
EC.element_to_be_clickable((By.XPATH, "//bdi[text()='Exportar']"))
|
|
421
|
+
)
|
|
422
|
+
botao_exportar.click()
|
|
423
|
+
console.print("[EXP] Botão 'Exportar' clicado.")
|
|
424
|
+
except:
|
|
425
|
+
pass
|
|
426
|
+
|
|
427
|
+
# AQUI — aguarda até 10 MIN pelo arquivo
|
|
428
|
+
arquivo_baixado = await self.wait_for_xlsx_download()
|
|
429
|
+
console.print(f"[EXP] Download concluído! Arquivo: {arquivo_baixado}")
|
|
430
|
+
|
|
431
|
+
console.print("[EXP] Chamando rename_file...")
|
|
432
|
+
await self.rename_file()
|
|
433
|
+
console.print("[EXP] rename_file concluído.")
|
|
434
|
+
|
|
435
|
+
except Exception as e:
|
|
436
|
+
console.print("[EXP][ERRO] Exceção em exportar_devolucoes.")
|
|
437
|
+
console.print(f"[EXP][ERRO] Tipo: {type(e).__name__}")
|
|
438
|
+
console.print(f"[EXP][ERRO] str(e): {str(e)}")
|
|
439
|
+
console.print("[EXP][ERRO] Traceback:")
|
|
440
|
+
console.print(traceback.format_exc())
|
|
441
|
+
raise
|
|
442
|
+
|
|
443
|
+
async def rename_file(self):
|
|
444
|
+
console.print("[RF] Iniciando renomeação e movimentação do arquivo.")
|
|
445
|
+
try:
|
|
446
|
+
# === 1) Localizar qualquer XLSX nos downloads ===
|
|
447
|
+
xlsx_files = [
|
|
448
|
+
os.path.join(DOWNLOADS_PATH, f)
|
|
449
|
+
for f in os.listdir(DOWNLOADS_PATH)
|
|
450
|
+
if f.lower().endswith(".xlsx")
|
|
451
|
+
]
|
|
452
|
+
|
|
453
|
+
if not xlsx_files:
|
|
454
|
+
msg = "[RF][ERRO] Nenhum arquivo XLSX encontrado na pasta Downloads."
|
|
455
|
+
console.print(msg)
|
|
456
|
+
return RpaRetornoProcessoDTO(
|
|
457
|
+
sucesso=False,
|
|
458
|
+
retorno=msg,
|
|
459
|
+
status=RpaHistoricoStatusEnum.Falha,
|
|
460
|
+
tags=[RpaTagDTO(descricao=RpaTagEnum.Tecnico)],
|
|
461
|
+
)
|
|
462
|
+
|
|
463
|
+
# pega o arquivo mais recente
|
|
464
|
+
current_path = max(xlsx_files, key=os.path.getmtime)
|
|
465
|
+
|
|
466
|
+
console.print(f"[RF] Arquivo encontrado: {current_path}")
|
|
467
|
+
|
|
468
|
+
# === 2) Criar nome SIM_DEV + timestamp ===
|
|
469
|
+
date_now = datetime.now().strftime("%Y%m%d%H%M%S")
|
|
470
|
+
filename = f"SIM_DEV_{date_now}.xlsx"
|
|
471
|
+
final_path = os.path.join(DOWNLOADS_PATH, filename)
|
|
472
|
+
|
|
473
|
+
console.print(f"[RF] Novo filename: {filename}")
|
|
474
|
+
|
|
475
|
+
# === 3) Renomear ===
|
|
476
|
+
os.rename(current_path, final_path)
|
|
477
|
+
console.print(f"[RF] Arquivo renomeado para {final_path}.")
|
|
478
|
+
|
|
479
|
+
# === 4) Ler conteúdo para envio ===
|
|
480
|
+
with open(final_path, "rb") as file:
|
|
481
|
+
file_bytes = io.BytesIO(file.read())
|
|
482
|
+
|
|
483
|
+
await worker_sleep(2)
|
|
484
|
+
|
|
485
|
+
# === 5) Enviar para o datalake ===
|
|
486
|
+
try:
|
|
487
|
+
console.print(f"[RF] directory: {self.directory}")
|
|
488
|
+
console.print(f"[RF] file: {final_path}")
|
|
489
|
+
send_file_request = await send_file_to_datalake(
|
|
490
|
+
self.directory, file_bytes, filename, "xlsx"
|
|
491
|
+
)
|
|
492
|
+
console.print(f"[RF] Resposta send_file_to_datalake: {send_file_request}")
|
|
493
|
+
except Exception as e:
|
|
494
|
+
console.print(f"[RF][ERRO] Erro ao enviar o arquivo: {e}", style="bold red")
|
|
495
|
+
console.print("[RF][ERRO] Traceback:")
|
|
496
|
+
console.print(traceback.format_exc())
|
|
497
|
+
return RpaRetornoProcessoDTO(
|
|
498
|
+
sucesso=False,
|
|
499
|
+
retorno=f"Erro ao enviar o arquivo: {e}",
|
|
500
|
+
status=RpaHistoricoStatusEnum.Falha,
|
|
501
|
+
tags=[RpaTagDTO(descricao=RpaTagEnum.Tecnico)],
|
|
502
|
+
)
|
|
503
|
+
|
|
504
|
+
await worker_sleep(1)
|
|
505
|
+
|
|
506
|
+
# === 6) Deletar arquivo final após envio ===
|
|
507
|
+
if os.path.exists(final_path):
|
|
508
|
+
try:
|
|
509
|
+
os.remove(final_path)
|
|
510
|
+
console.print(f"[RF] Arquivo deletado: {final_path}")
|
|
511
|
+
except Exception as e:
|
|
512
|
+
msg = f"Erro ao deletar o arquivo: {e}"
|
|
513
|
+
console.print(f"[RF][ERRO] {msg}")
|
|
514
|
+
console.print("[RF][ERRO] Traceback:")
|
|
515
|
+
console.print(traceback.format_exc())
|
|
516
|
+
return RpaRetornoProcessoDTO(
|
|
517
|
+
sucesso=False,
|
|
518
|
+
retorno=msg,
|
|
519
|
+
status=RpaHistoricoStatusEnum.Falha,
|
|
520
|
+
tags=[RpaTagDTO(descricao=RpaTagEnum.Tecnico)],
|
|
521
|
+
)
|
|
522
|
+
|
|
523
|
+
except Exception as e:
|
|
524
|
+
msg = f"Erro ao manipular arquivo XLSX: {e}"
|
|
525
|
+
console.print(f"[RF][ERRO] {msg}")
|
|
526
|
+
console.print("[RF][ERRO] Traceback:")
|
|
527
|
+
console.print(traceback.format_exc())
|
|
528
|
+
return RpaRetornoProcessoDTO(
|
|
529
|
+
sucesso=False,
|
|
530
|
+
retorno=msg,
|
|
531
|
+
status=RpaHistoricoStatusEnum.Falha,
|
|
532
|
+
tags=[RpaTagDTO(descricao=RpaTagEnum.Tecnico)],
|
|
533
|
+
)
|
|
534
|
+
|
|
535
|
+
|
|
536
|
+
async def send_message_to_webhook(self, message: str):
|
|
537
|
+
console.print("[WEBHOOK] Enviando mensagem ao webhook.")
|
|
538
|
+
await worker_sleep(2)
|
|
539
|
+
try:
|
|
540
|
+
payload = {"text": "📢 " + message}
|
|
541
|
+
webhook_url = f"{self.sap_url}/key={self.sap_key}&token={self.sap_token}"
|
|
542
|
+
requests.post(
|
|
543
|
+
webhook_url,
|
|
544
|
+
data=json.dumps(payload),
|
|
545
|
+
headers={"Content-Type": "application/json"},
|
|
546
|
+
timeout=10,
|
|
547
|
+
)
|
|
548
|
+
console.print("[WEBHOOK] Mensagem enviada ao webhook com sucesso.")
|
|
549
|
+
await worker_sleep(2)
|
|
550
|
+
except Exception as e:
|
|
551
|
+
msg = f"Erro ao enviar mensagem ao webhook: {e}"
|
|
552
|
+
console.print(f"[WEBHOOK][ERRO] {msg}")
|
|
553
|
+
console.print("[WEBHOOK][ERRO] Traceback:")
|
|
554
|
+
console.print(traceback.format_exc())
|
|
555
|
+
return RpaRetornoProcessoDTO(
|
|
556
|
+
sucesso=False,
|
|
557
|
+
retorno=msg,
|
|
558
|
+
status=RpaHistoricoStatusEnum.Falha,
|
|
559
|
+
tags=[RpaTagDTO(descricao=RpaTagEnum.Tecnico)],
|
|
560
|
+
)
|
|
561
|
+
|
|
562
|
+
|
|
563
|
+
async def lista_devolucoes_sap(task: RpaProcessoSapDTO) -> RpaRetornoProcessoDTO:
|
|
564
|
+
console.print("[MAIN] Iniciando processo de lista de clientes SAP.")
|
|
565
|
+
notas_sap = None
|
|
566
|
+
try:
|
|
567
|
+
console.print("[MAIN] Buscando configuração 'SAP_Faturamento'...")
|
|
568
|
+
config = await get_config_by_name("SAP_Faturamento")
|
|
569
|
+
console.print(f"[MAIN] config recebido: {config}")
|
|
570
|
+
sap_url = config.conConfiguracao.get("sapUrl")
|
|
571
|
+
sap_key = config.conConfiguracao.get("sapKey")
|
|
572
|
+
sap_token = config.conConfiguracao.get("sapToken")
|
|
573
|
+
base_url = config.conConfiguracao.get("baseUrl")
|
|
574
|
+
directory = config.conConfiguracao.get("directoryBucket")
|
|
575
|
+
console.print(
|
|
576
|
+
f"[MAIN] sap_url={sap_url} base_url={base_url} directory={directory}"
|
|
577
|
+
)
|
|
578
|
+
|
|
579
|
+
# PEGAR RELATORIO DE FORMA SEGURA (default FAT – não interfere no fluxo atual)
|
|
580
|
+
relatorio = None
|
|
581
|
+
config_entrada = task.configEntrada
|
|
582
|
+
|
|
583
|
+
if isinstance(config_entrada, dict):
|
|
584
|
+
relatorio = config_entrada.get("relatorio")
|
|
585
|
+
else:
|
|
586
|
+
relatorio = getattr(config_entrada, "relatorio", None)
|
|
587
|
+
|
|
588
|
+
if not relatorio:
|
|
589
|
+
relatorio = "FAT"
|
|
590
|
+
|
|
591
|
+
console.print(f"[MAIN] Relatório usado: {relatorio}")
|
|
592
|
+
|
|
593
|
+
notas_sap = ListaDevolucoesSAP(
|
|
594
|
+
task,
|
|
595
|
+
sap_url,
|
|
596
|
+
sap_key,
|
|
597
|
+
sap_token,
|
|
598
|
+
base_url,
|
|
599
|
+
directory,
|
|
600
|
+
relatorio_override=relatorio,
|
|
601
|
+
)
|
|
602
|
+
|
|
603
|
+
console.print("[MAIN] Chamando start_sap_process...")
|
|
604
|
+
resultado = await notas_sap.start_sap_process()
|
|
605
|
+
console.print(f"[MAIN] Resultado do start_sap_process: sucesso={resultado.sucesso}")
|
|
606
|
+
|
|
607
|
+
return resultado
|
|
608
|
+
|
|
609
|
+
except Exception as ex:
|
|
610
|
+
console.print("[MAIN][ERRO] Exceção em lista_clientes_sap.")
|
|
611
|
+
console.print(f"[MAIN][ERRO] Tipo: {type(ex).__name__}")
|
|
612
|
+
console.print(f"[MAIN][ERRO] str(ex): {str(ex)}")
|
|
613
|
+
console.print("[MAIN][ERRO] Traceback completo:")
|
|
614
|
+
console.print(traceback.format_exc())
|
|
615
|
+
return RpaRetornoProcessoDTO(
|
|
616
|
+
sucesso=False,
|
|
617
|
+
retorno=f"Erro na automação SAP: {ex}",
|
|
618
|
+
status=RpaHistoricoStatusEnum.Falha,
|
|
619
|
+
tags=[RpaTagDTO(descricao=RpaTagEnum.Tecnico)],
|
|
620
|
+
)
|
|
621
|
+
finally:
|
|
622
|
+
if notas_sap and notas_sap.driver:
|
|
623
|
+
console.print("[MAIN] Fechando driver no final do processo.")
|
|
624
|
+
notas_sap.driver.quit()
|
|
625
|
+
console.print("[MAIN] Fim do processo (finally).")
|
|
626
|
+
|
|
@@ -129,6 +129,12 @@ from worker_automate_hub.tasks.jobs.importacao_extratos_748 import (
|
|
|
129
129
|
from worker_automate_hub.tasks.jobs.extracao_dados_nielsen import (
|
|
130
130
|
extracao_dados_nielsen,
|
|
131
131
|
)
|
|
132
|
+
from worker_automate_hub.tasks.jobs.lista_clientes_sap import (
|
|
133
|
+
lista_clientes_sap,
|
|
134
|
+
)
|
|
135
|
+
from worker_automate_hub.tasks.jobs.lista_devolucoes_sap import (
|
|
136
|
+
lista_devolucoes_sap,
|
|
137
|
+
)
|
|
132
138
|
|
|
133
139
|
task_definitions = {
|
|
134
140
|
"5b295021-8df7-40a1-a45e-fe7109ae3902": exemplo_processo,
|
|
@@ -244,6 +250,8 @@ task_definitions = {
|
|
|
244
250
|
"153a7bf9-8cab-41fd-b6d3-63d881ac1cf9": importacao_extratos,
|
|
245
251
|
"80345c74-29af-4a6a-8438-86061acf2531": importacao_extratos_748,
|
|
246
252
|
"e8ca47cf-c49b-437c-9028-50bcfa5fe021": extracao_dados_nielsen,
|
|
253
|
+
"02cd28a1-cc69-4a49-a46b-56687d615092": lista_clientes_sap,
|
|
254
|
+
"7a4e1ea5-852f-48b3-99e3-5c2910632fe3": lista_devolucoes_sap,
|
|
247
255
|
|
|
248
256
|
}
|
|
249
257
|
|