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.
Files changed (36) hide show
  1. worker_automate_hub/api/client.py +186 -68
  2. worker_automate_hub/api/rpa_historico_service.py +1 -0
  3. worker_automate_hub/cli.py +91 -111
  4. worker_automate_hub/tasks/jobs/abertura_livros_fiscais.py +112 -229
  5. worker_automate_hub/tasks/jobs/descartes.py +91 -77
  6. worker_automate_hub/tasks/jobs/devolucao_produtos.py +1386 -0
  7. worker_automate_hub/tasks/jobs/entrada_de_notas_15.py +3 -46
  8. worker_automate_hub/tasks/jobs/entrada_de_notas_22.py +833 -0
  9. worker_automate_hub/tasks/jobs/entrada_de_notas_36.py +29 -9
  10. worker_automate_hub/tasks/jobs/entrada_de_notas_37.py +619 -0
  11. worker_automate_hub/tasks/jobs/entrada_de_notas_39.py +1 -1
  12. worker_automate_hub/tasks/jobs/entrada_de_notas_9.py +63 -16
  13. worker_automate_hub/tasks/jobs/extracao_dados_nielsen.py +504 -0
  14. worker_automate_hub/tasks/jobs/extracao_saldo_estoque.py +242 -108
  15. worker_automate_hub/tasks/jobs/extracao_saldo_estoque_fiscal.py +688 -0
  16. worker_automate_hub/tasks/jobs/fidc_gerar_nosso_numero.py +2 -2
  17. worker_automate_hub/tasks/jobs/fidc_remessa_cobranca_cnab240.py +25 -16
  18. worker_automate_hub/tasks/jobs/geracao_balancetes_filial.py +330 -0
  19. worker_automate_hub/tasks/jobs/importacao_extratos.py +538 -0
  20. worker_automate_hub/tasks/jobs/importacao_extratos_748.py +800 -0
  21. worker_automate_hub/tasks/jobs/inclusao_pedidos_ipiranga.py +222 -0
  22. worker_automate_hub/tasks/jobs/inclusao_pedidos_raizen.py +174 -0
  23. worker_automate_hub/tasks/jobs/inclusao_pedidos_vibra.py +327 -0
  24. worker_automate_hub/tasks/jobs/notas_faturamento_sap.py +438 -157
  25. worker_automate_hub/tasks/jobs/opex_capex.py +540 -326
  26. worker_automate_hub/tasks/jobs/sped_fiscal.py +8 -8
  27. worker_automate_hub/tasks/jobs/transferencias.py +52 -41
  28. worker_automate_hub/tasks/task_definitions.py +46 -1
  29. worker_automate_hub/tasks/task_executor.py +11 -0
  30. worker_automate_hub/utils/util.py +252 -215
  31. worker_automate_hub/utils/utils_nfe_entrada.py +1 -1
  32. worker_automate_hub/worker.py +1 -9
  33. {worker_automate_hub-0.5.749.dist-info → worker_automate_hub-0.5.912.dist-info}/METADATA +4 -2
  34. {worker_automate_hub-0.5.749.dist-info → worker_automate_hub-0.5.912.dist-info}/RECORD +36 -25
  35. {worker_automate_hub-0.5.749.dist-info → worker_automate_hub-0.5.912.dist-info}/WHEEL +1 -1
  36. {worker_automate_hub-0.5.749.dist-info → worker_automate_hub-0.5.912.dist-info}/entry_points.txt +0 -0
@@ -1,13 +1,11 @@
1
1
  import asyncio
2
2
  import os
3
3
  from datetime import datetime
4
- from pywinauto import Application, timings, findwindows
4
+ from pywinauto import Application, timings, findwindows, Desktop
5
5
  import sys
6
6
  import io
7
-
8
- sys.path.append(
9
- os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", ".."))
10
- )
7
+ import win32gui
8
+ # sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..')))
11
9
 
12
10
  from worker_automate_hub.models.dto.rpa_historico_request_dto import (
13
11
  RpaHistoricoStatusEnum,
@@ -52,158 +50,286 @@ async def extracao_saldo_estoque(task: RpaProcessoEntradaDTO):
52
50
  periodo_format = periodo.replace("/", "")
53
51
  filial = task.configEntrada["filialEmpresaOrigem"]
54
52
  historico_id = task.historico_id
53
+
54
+ console.print("Finalizando processos antigos do EMSys...", style="bold yellow")
55
55
  await kill_all_emsys()
56
56
 
57
+ console.print("Iniciando EMSys...", style="bold green")
57
58
  app = Application(backend="win32").start("C:\\Rezende\\EMSys3\\EMSys3_35.exe")
58
59
  warnings.filterwarnings(
59
60
  "ignore",
60
61
  category=UserWarning,
61
62
  message="32-bit application should be automated using 32-bit Python",
62
63
  )
63
- console.print("\nEMSys iniciando...", style="bold green")
64
+
65
+ console.print("Fazendo login no EMSys...", style="bold cyan")
64
66
  return_login = await login_emsys(
65
67
  config.conConfiguracao, app, task, filial_origem=filial
66
68
  )
67
69
 
68
- if return_login.sucesso == True:
70
+ if return_login.sucesso:
71
+ console.print("Login realizado com sucesso", style="bold green")
69
72
  type_text_into_field(
70
73
  "Rel. Saldo Estoque ", app["TFrmMenuPrincipal"]["Edit"], True, "50"
71
74
  )
72
75
  pyautogui.press("enter")
73
76
  await worker_sleep(2)
74
77
  pyautogui.press("enter")
75
-
76
78
  else:
77
79
  logger.info(f"\nError Message: {return_login.retorno}")
78
- console.print(f"\nError Message: {return_login.retorno}", style="bold red")
80
+ console.print(f"Erro no login: {return_login.retorno}", style="bold red")
79
81
  return return_login
80
82
 
81
83
  await worker_sleep(2)
82
84
 
83
- ##### Janela Relatório Saldos do Estoque #####
84
- # Marcar check box data
85
+ console.print("Abrindo janela Relatório de Saldo de Estoque...", style="bold cyan")
85
86
  app = Application().connect(class_name="TFrmRelSaldoEstoque", timeout=60)
86
87
  main_window = app["TFrmRelSaldoEstoque"]
87
88
  main_window.set_focus()
88
89
 
89
- # Captura o campo de data
90
- data_chk = main_window.child_window(
91
- class_name="TCheckBox", found_index=3
92
- ).click_input()
90
+ console.print("Marcando campo de data...", style="bold cyan")
91
+ main_window.child_window(class_name="TCheckBox", found_index=3).click_input()
93
92
 
94
93
  await worker_sleep(2)
95
- # Insere a data
96
- data_input = main_window.child_window(class_name="TDBIEditDate", found_index=0)
97
94
 
95
+ console.print(f"Inserindo período: {periodo}", style="bold cyan")
96
+ data_input = main_window.child_window(class_name="TDBIEditDate", found_index=0)
98
97
  data_input.set_edit_text(periodo)
99
98
 
100
- # Clicar em gerar relatório
101
- btn_gerar = main_window.child_window(
102
- class_name="TBitBtn", found_index=0
103
- ).click_input()
99
+ console.print("Gerando relatório...", style="bold cyan")
100
+ main_window.child_window(class_name="TBitBtn", found_index=0).click_input()
104
101
 
105
- # Aguarda até 60 segundos para a janela aparecer
106
102
  timings.wait_until_passes(
107
103
  timeout=1800,
108
104
  retry_interval=1,
109
105
  func=lambda: Application().connect(class_name="TFrmPreviewRelatorio"),
110
106
  )
107
+ await worker_sleep(2)
111
108
 
112
- await worker_sleep(10)
113
-
114
- # Conecta à janela Preview Relatorio
109
+ console.print("Abrindo Preview Relatório...", style="bold cyan")
115
110
  app = Application().connect(class_name="TFrmPreviewRelatorio")
116
111
  main_window = app["TFrmPreviewRelatorio"]
117
112
  main_window.set_focus()
118
113
 
119
- # Clicar em salvar
120
- caminho = r"assets\\extracao_relatorios\\btn_salvar.png"
121
- # Verifica se o arquivo existe
122
- if os.path.isfile(caminho):
123
- print("A imagem existe:", caminho)
124
-
125
- # Procura a imagem na tela
126
- pos = pyautogui.locateCenterOnScreen(
127
- caminho, confidence=0.9
128
- ) # ajuste o confidence se necessário
129
- if pos:
130
- pyautogui.click(pos) # clica no centro da imagem
131
- print("Clique realizado na imagem.")
114
+ max_tentativas = 5
115
+ tentativa = 1
116
+ sucesso = False
117
+
118
+ # defina caminho_arquivo ANTES para não ficar indefinido
119
+ caminho_arquivo = rf"C:\Users\automatehub\Downloads\saldo_estoque_{periodo_format}_{filial}.xlsx"
120
+
121
+ while tentativa <= max_tentativas and not sucesso:
122
+ console.print(f"Tentativa {tentativa} de {max_tentativas}", style="bold cyan")
123
+
124
+ # 1) Abrir o picker pelo botão (imagem)
125
+ console.print("Procurando botão de salvar (imagem)...", style="bold cyan")
126
+ caminho_img = r'assets\\extracao_relatorios\\btn_salvar.png'
127
+ if os.path.isfile(caminho_img):
128
+ pos = pyautogui.locateCenterOnScreen(caminho_img, confidence=0.9)
129
+ if pos:
130
+ pyautogui.click(pos)
131
+ console.print("Clique realizado no botão salvar", style="bold green")
132
+ else:
133
+ console.print("Imagem encontrada mas não está visível na tela", style="bold yellow")
132
134
  else:
133
- print("Imagem encontrada no disco, mas não está visível na tela.")
134
- else:
135
- print("A imagem NÃO existe:", caminho)
136
-
137
- await worker_sleep(2)
138
-
139
- # Conecta na janela Configuração para Salvar Arquivo
140
- app = Application().connect(class_name="TFrmRelatorioFormato")
141
- main_window = app["TFrmRelatorioFormato"]
142
- main_window.set_focus()
143
- # Acessa o ComboBox pelo identificador conhecido
144
- combo = main_window.ComboBox
145
-
146
- # Garante que existe "Excel" na lista
147
- itens = combo.texts()
148
- print("Itens do ComboBox:", itens)
149
-
150
- # Seleciona o Excel correto (o segundo da lista, índice 8)
151
- combo.select(8)
152
-
153
- await worker_sleep(2)
154
-
155
- # Clicar em Salvar
156
- main_window.child_window(
157
- class_name="TBitBtn", found_index=1
158
- ).click_input()
135
+ console.print("Imagem do botão salvar NÃO existe", style="bold red")
136
+
137
+ await worker_sleep(8)
138
+
139
+ # 2) Selecionar formato Excel (desambiguando múltiplas TFrmRelatorioFormato)
140
+ console.print("Selecionando formato Excel...", style="bold cyan")
141
+ try:
142
+ desktop = Desktop(backend="win32")
143
+
144
+ # Liste todas as visíveis
145
+ wins_visiveis = desktop.windows(class_name="TFrmRelatorioFormato", visible_only=True)
146
+ if not wins_visiveis:
147
+ raise RuntimeError("Janela de formato não apareceu.")
148
+
149
+ # 2.1) Tente a janela em foco (foreground)
150
+ h_fore = win32gui.GetForegroundWindow()
151
+ alvo = None
152
+ for w in wins_visiveis:
153
+ if w.handle == h_fore:
154
+ alvo = w
155
+ break
156
+
157
+ # 2.2) Se não estiver em foco, pegue a que contém um TComboBox (a 'Configuração para Salvar arq...')
158
+ if alvo is None:
159
+ candidatos = []
160
+ for w in wins_visiveis:
161
+ try:
162
+ if w.child_window(class_name="TComboBox").exists(timeout=0.8):
163
+ candidatos.append(w)
164
+ except Exception:
165
+ pass
166
+ if candidatos:
167
+ alvo = candidatos[-1] # a mais recente
168
+ else:
169
+ alvo = wins_visiveis[-1] # fallback
170
+
171
+ # Trabalhe via WindowSpecification
172
+ spec_fmt = desktop.window(handle=alvo.handle)
173
+ spec_fmt.wait("visible", timeout=10)
174
+ win_fmt = spec_fmt.wrapper_object()
175
+ win_fmt.set_focus()
176
+
177
+ # Acessar o ComboBox
178
+ try:
179
+ combo_spec = spec_fmt.child_window(class_name="TComboBox")
180
+ except Exception:
181
+ combo_spec = spec_fmt.child_window(control_type="ComboBox")
182
+ combo_spec.wait("exists enabled", timeout=10)
183
+ combo = combo_spec.wrapper_object()
184
+
185
+ textos = combo.texts()
186
+ console.print(f"Itens do ComboBox: {textos}", style="bold yellow")
187
+
188
+ # Seleção por índice conhecido; fallback por texto
189
+ try:
190
+ combo.select(8)
191
+ except Exception:
192
+ alvo_idx = None
193
+ for i, t in enumerate(textos):
194
+ if "EXCEL" in str(t).upper() or "XLSX" in str(t).upper():
195
+ alvo_idx = i
196
+ break
197
+ if alvo_idx is None:
198
+ console.print("Não foi possível localizar a opção de Excel no ComboBox.", style="bold red")
199
+ tentativa += 1
200
+ await worker_sleep(2)
201
+ continue
202
+ combo.select(alvo_idx)
203
+
204
+ await worker_sleep(1)
205
+
206
+ # Clique em OK
207
+ btn_ok_spec = spec_fmt.child_window(class_name="TBitBtn", found_index=1)
208
+ btn_ok_spec.wait("enabled", timeout=5)
209
+ btn_ok_spec.click_input()
210
+
211
+ # Aguarde a janela de formato desaparecer
212
+ try:
213
+ spec_fmt.wait_not("visible", timeout=10)
214
+ except Exception:
215
+ pass
216
+
217
+ # Feche possíveis duplicatas remanescentes (defensivo)
218
+ for w in desktop.windows(class_name="TFrmRelatorioFormato", visible_only=True):
219
+ if w.handle != alvo.handle:
220
+ try:
221
+ w.close()
222
+ except Exception:
223
+ pass
224
+
225
+ except Exception as e:
226
+ console.print(f"Falha ao selecionar formato: {e}", style="bold red")
227
+ tentativa += 1
228
+ await worker_sleep(3)
229
+ continue
230
+
231
+ await worker_sleep(5)
232
+
233
+ # 3) Janela "Salvar para arquivo"
234
+ console.print("Abrindo janela de salvar arquivo...", style="bold cyan")
235
+ try:
236
+ app_save = Application(backend="win32").connect(title_re="Salvar para arquivo|Salvar como|Save As", timeout=30)
237
+ spec_save = app_save.window(title_re="Salvar para arquivo|Salvar como|Save As")
238
+ spec_save.wait("visible", timeout=30)
239
+ win_save = spec_save.wrapper_object()
240
+ except Exception as e:
241
+ console.print(f"Não achou a janela 'Salvar para arquivo': {e}", style="bold red")
242
+ tentativa += 1
243
+ await worker_sleep(3)
244
+ continue
245
+
246
+ # 3.1) Remover arquivo pré-existente
247
+ if os.path.exists(caminho_arquivo):
248
+ try:
249
+ os.remove(caminho_arquivo)
250
+ console.print("Arquivo existente removido para evitar prompt de sobrescrita.", style="bold yellow")
251
+ except Exception as e:
252
+ console.print(f"Não foi possível remover o arquivo existente: {e}", style="bold red")
253
+
254
+ # 3.2) Preencher nome e salvar
255
+ try:
256
+ campo_spec = spec_save.child_window(class_name="Edit", control_id=1148)
257
+ campo_spec.wait("exists enabled visible", timeout=10)
258
+ campo_nome = campo_spec.wrapper_object()
259
+ campo_nome.set_focus()
260
+ try:
261
+ campo_nome.set_edit_text("")
262
+ except Exception:
263
+ campo_nome.type_keys("^a{DELETE}", pause=0.02)
264
+
265
+ campo_nome.type_keys(caminho_arquivo, with_spaces=True, pause=0.01)
266
+ console.print(f"Arquivo configurado para: {caminho_arquivo}", style="bold green")
267
+
268
+ await worker_sleep(1)
269
+
270
+ btn_salvar_spec = spec_save.child_window(class_name="Button", found_index=0)
271
+ btn_salvar_spec.wait("enabled", timeout=10)
272
+ btn_salvar_spec.click_input()
273
+
274
+ # Esperar a janela sumir
275
+ try:
276
+ spec_save.wait_not("visible", timeout=15)
277
+ except Exception:
278
+ pass
279
+
280
+ except Exception as e:
281
+ console.print(f"Erro ao confirmar salvar: {e}", style="bold red")
282
+ tentativa += 1
283
+ await worker_sleep(3)
284
+ continue
159
285
 
160
- await worker_sleep(5)
161
-
162
- # Conecta na janela "Salvar para arquivo"
163
- app = Application().connect(title_re="Salvar para arquivo", timeout=30)
164
- main_window = app.window(title_re="Salvar para arquivo")
165
-
166
- # Campo Nome (Edit) - use set_edit_text para evitar problemas de escape
167
- campo_nome = main_window.child_window(
168
- class_name="Edit", control_id=1148
169
- ).wrapper_object()
170
- caminho_arquivo = f"C:\\Users\\automatehub\\Downloads\\saldo_estoque_{periodo_format}_{filial}.xlsx"
171
- campo_nome.set_focus()
172
- campo_nome.set_edit_text(caminho_arquivo)
173
-
174
- print("✅ Texto inserido no campo Nome")
175
-
176
- await worker_sleep(2)
177
-
178
- # Clicar em ok para salvar
179
- main_window.child_window(
180
- class_name="Button", found_index=0
181
- ).click_input()
182
-
183
- await worker_sleep(20)
184
-
185
- # caminho_img = r"assets\\extracao_relatorios\\janela_printing.png"
286
+ await worker_sleep(2)
186
287
 
187
- # Aguarda até a janela com título "Printing" (ou "Salvando...") fechar
188
-
189
- try:
190
- app = Application().connect(title_re="Printing") # conecta se existir
191
- janela = app.window(title_re="Printing")
288
+ # 3.3) Confirmar sobrescrita (se houver)
289
+ try:
290
+ app_conf = Application(backend="win32").connect(
291
+ title_re="Confirm(ar)?( )?Salvar( )?Como|Confirm Save As", timeout=3
292
+ )
293
+ spec_conf = app_conf.window(title_re="Confirm(ar)?( )?Salvar( )?Como|Confirm Save As")
294
+ spec_conf.wait("visible", timeout=3)
295
+ spec_conf.child_window(class_name="Button", found_index=0).click_input()
296
+ console.print("Confirmação de sobrescrita respondida.", style="bold yellow")
297
+ except Exception:
298
+ pass
192
299
 
193
- print("⏳ Aguardando a janela 'Printing' sumir...")
194
- janela.wait_not("visible", timeout=60) # espera até 60 segundos
195
- print("✅ Janela 'Printing' fechada.")
300
+ await worker_sleep(2)
196
301
 
197
- except findwindows.ElementNotFoundError:
198
- print("⚠️ Janela 'Printing' não estava aberta.")
302
+ # 4) Aguardar 'Printing' (se existir)
303
+ console.print("Aguardando finalização do processo de impressão/salvamento...", style="bold cyan")
304
+ try:
305
+ app_print = Application(backend="win32").connect(title_re="Printing", timeout=5)
306
+ spec_print = app_print.window(title_re="Printing")
307
+ try:
308
+ spec_print.wait_not("visible", timeout=60)
309
+ console.print("Janela 'Printing' fechada.", style="bold green")
310
+ except Exception:
311
+ console.print("Janela 'Printing' não fechou no tempo esperado. Seguindo.", style="bold yellow")
312
+ except findwindows.ElementNotFoundError:
313
+ console.print("Janela 'Printing' não apareceu.", style="bold yellow")
314
+ except Exception as e:
315
+ console.print(f"Erro ao aguardar 'Printing': {e}", style="bold yellow")
316
+
317
+ # 5) Validar arquivo salvo
318
+ if os.path.exists(caminho_arquivo):
319
+ console.print(f"Arquivo encontrado: {caminho_arquivo}", style="bold green")
320
+ with open(caminho_arquivo, "rb") as f:
321
+ file_bytes = io.BytesIO(f.read())
322
+ sucesso = True
323
+ else:
324
+ console.print("Arquivo não encontrado, tentando novamente...", style="bold red")
325
+ tentativa += 1
326
+ await worker_sleep(3)
199
327
 
200
- nome_com_extensao = f"saldo_estoque_{periodo_format}_{filial}.xlsx"
201
- # o arquivo
202
- print(caminho_arquivo)
203
- with open(f"{caminho_arquivo}", "rb") as file:
204
- file_bytes = io.BytesIO(file.read())
328
+ if not sucesso:
329
+ console.print("Falha após 5 tentativas. Arquivo não foi gerado.", style="bold red")
205
330
 
206
- console.print("Enviar Excel para o BOF")
331
+ nome_com_extensao = f'saldo_estoque_{periodo_format}_{filial}.xlsx'
332
+ console.print("Enviando arquivo XLS para o BOF...", style="bold cyan")
207
333
  try:
208
334
  await send_file(
209
335
  historico_id,
@@ -212,8 +338,13 @@ async def extracao_saldo_estoque(task: RpaProcessoEntradaDTO):
212
338
  file_bytes,
213
339
  file_extension="xlsx",
214
340
  )
215
- console.print("Removendo arquivo XLS da pasta downloads")
216
- os.remove(f"{caminho_arquivo}")
341
+ console.print("Removendo arquivo da pasta downloads", style="bold yellow")
342
+ os.remove(caminho_arquivo)
343
+ return RpaRetornoProcessoDTO(
344
+ sucesso=True,
345
+ retorno="Relatório enviado com sucesso!",
346
+ status=RpaHistoricoStatusEnum.Sucesso,
347
+ )
217
348
 
218
349
  except Exception as e:
219
350
  console.print(f"Erro ao enviar o arquivo: {e}", style="bold red")
@@ -223,6 +354,9 @@ async def extracao_saldo_estoque(task: RpaProcessoEntradaDTO):
223
354
  status=RpaHistoricoStatusEnum.Falha,
224
355
  tags=[RpaTagDTO(descricao=RpaTagEnum.Tecnico)],
225
356
  )
357
+
358
+ console.print("Processo concluído com sucesso!", style="bold green")
359
+
226
360
  except Exception as ex:
227
361
  retorno = f"Erro Processo Fechamento Balancete: {str(ex)}"
228
362
  logger.error(retorno)