worker-automate-hub 0.5.749__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.
- worker_automate_hub/api/client.py +186 -68
- worker_automate_hub/api/rpa_historico_service.py +1 -0
- worker_automate_hub/cli.py +91 -111
- worker_automate_hub/tasks/jobs/abertura_livros_fiscais.py +112 -229
- worker_automate_hub/tasks/jobs/descartes.py +91 -77
- worker_automate_hub/tasks/jobs/devolucao_produtos.py +1386 -0
- worker_automate_hub/tasks/jobs/entrada_de_notas_15.py +3 -46
- worker_automate_hub/tasks/jobs/entrada_de_notas_22.py +833 -0
- worker_automate_hub/tasks/jobs/entrada_de_notas_36.py +29 -9
- worker_automate_hub/tasks/jobs/entrada_de_notas_37.py +619 -0
- worker_automate_hub/tasks/jobs/entrada_de_notas_39.py +1 -1
- worker_automate_hub/tasks/jobs/entrada_de_notas_9.py +63 -16
- worker_automate_hub/tasks/jobs/extracao_dados_nielsen.py +504 -0
- worker_automate_hub/tasks/jobs/extracao_saldo_estoque.py +242 -108
- worker_automate_hub/tasks/jobs/extracao_saldo_estoque_fiscal.py +688 -0
- worker_automate_hub/tasks/jobs/fidc_gerar_nosso_numero.py +2 -2
- worker_automate_hub/tasks/jobs/fidc_remessa_cobranca_cnab240.py +25 -16
- worker_automate_hub/tasks/jobs/geracao_balancetes_filial.py +330 -0
- worker_automate_hub/tasks/jobs/importacao_extratos.py +538 -0
- worker_automate_hub/tasks/jobs/importacao_extratos_748.py +800 -0
- worker_automate_hub/tasks/jobs/inclusao_pedidos_ipiranga.py +222 -0
- worker_automate_hub/tasks/jobs/inclusao_pedidos_raizen.py +174 -0
- worker_automate_hub/tasks/jobs/inclusao_pedidos_vibra.py +327 -0
- worker_automate_hub/tasks/jobs/notas_faturamento_sap.py +438 -157
- worker_automate_hub/tasks/jobs/opex_capex.py +540 -326
- worker_automate_hub/tasks/jobs/sped_fiscal.py +8 -8
- worker_automate_hub/tasks/jobs/transferencias.py +52 -41
- worker_automate_hub/tasks/task_definitions.py +46 -1
- worker_automate_hub/tasks/task_executor.py +11 -0
- worker_automate_hub/utils/util.py +252 -215
- worker_automate_hub/utils/utils_nfe_entrada.py +1 -1
- worker_automate_hub/worker.py +1 -9
- {worker_automate_hub-0.5.749.dist-info → worker_automate_hub-0.5.912.dist-info}/METADATA +4 -2
- {worker_automate_hub-0.5.749.dist-info → worker_automate_hub-0.5.912.dist-info}/RECORD +36 -25
- {worker_automate_hub-0.5.749.dist-info → worker_automate_hub-0.5.912.dist-info}/WHEEL +1 -1
- {worker_automate_hub-0.5.749.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
|
|
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
|
-
|
|
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__(
|
|
45
|
-
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
|
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
|
|
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
|
|
337
|
+
console.print("[LOGIN] Login finalizado (sem exceção).")
|
|
144
338
|
return True
|
|
145
339
|
except Exception as e:
|
|
146
|
-
console.print(
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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
|
-
|
|
172
|
-
|
|
173
|
-
|
|
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
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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
|
-
|
|
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
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
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("
|
|
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
|
-
|
|
463
|
+
with open(final_path, "rb") as file:
|
|
464
|
+
file_bytes = io.BytesIO(file.read())
|
|
237
465
|
|
|
238
|
-
await worker_sleep(
|
|
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(
|
|
244
|
-
|
|
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(
|
|
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(
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
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(
|
|
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).")
|