worker-automate-hub 0.5.820__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.
Files changed (24) 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 +223 -0
  13. worker_automate_hub/tasks/jobs/inclusao_pedidos_raizen.py +187 -0
  14. worker_automate_hub/tasks/jobs/inclusao_pedidos_vibra.py +345 -0
  15. worker_automate_hub/tasks/jobs/lista_clientes_sap.py +631 -0
  16. worker_automate_hub/tasks/jobs/lista_devolucoes_sap.py +626 -0
  17. worker_automate_hub/tasks/jobs/notas_faturamento_sap.py +438 -157
  18. worker_automate_hub/tasks/jobs/opex_capex.py +523 -384
  19. worker_automate_hub/tasks/task_definitions.py +38 -2
  20. worker_automate_hub/utils/util.py +20 -10
  21. {worker_automate_hub-0.5.820.dist-info → worker_automate_hub-0.5.921.dist-info}/METADATA +2 -1
  22. {worker_automate_hub-0.5.820.dist-info → worker_automate_hub-0.5.921.dist-info}/RECORD +24 -15
  23. {worker_automate_hub-0.5.820.dist-info → worker_automate_hub-0.5.921.dist-info}/WHEEL +0 -0
  24. {worker_automate_hub-0.5.820.dist-info → worker_automate_hub-0.5.921.dist-info}/entry_points.txt +0 -0
@@ -2,12 +2,13 @@ from decimal import ROUND_HALF_UP, Decimal
2
2
  import threading
3
3
  from typing import Optional
4
4
  import aiohttp
5
-
5
+ import re
6
+ from collections import defaultdict
6
7
  import aiohttp
7
8
  import requests
8
9
  from aiohttp import ClientSession
9
10
  from rich.console import Console
10
-
11
+ from typing import List, Dict, Any
11
12
  from worker_automate_hub.api.helpers.api_helpers import handle_api_response
12
13
  from worker_automate_hub.config.settings import load_env_config
13
14
  from worker_automate_hub.models.dao.rpa_configuracao import RpaConfiguracao
@@ -498,6 +499,89 @@ async def get_valor_remessa_cobranca(date: str):
498
499
  logger.info(err_msg)
499
500
 
500
501
 
502
+ async def get_notas_produtos(codFornecedor: int, codEmpresa: int, itens: List[Dict[str, Any]]) -> Dict[str, Any]:
503
+ """
504
+ Função única:
505
+ - Converte itens (aceita chaves codigo/codigoProduto e quantidade/qtd)
506
+ - Chama /nf-supplier/checker com Authorization: Basic
507
+ - Normaliza tipos do retorno
508
+ - Entrega:
509
+ {
510
+ "lista": [ {codItem, qtdTotal, notas, valorUnitario}, ... ],
511
+ "por_codigo": { codItem: {...}, ... }
512
+ }
513
+
514
+ Retorno esperado da API (ex):
515
+ [
516
+ {"codItem": 19969, "qtdTotal": 15, "notas": ["1418727","1410744"], "valorUnitario": 5.29},
517
+ {"codItem": 29272, "qtdTotal": 10, "notas": ["1418727"], "valorUnitario": 7.12}
518
+ ]
519
+ """
520
+ # --- Carrega config
521
+ env_config, _ = load_env_config()
522
+ url_base = env_config["API_BASE_URL"].rstrip("/")
523
+ url = f"{url_base}/nf-supplier/checker"
524
+
525
+ # --- Header Basic (aceita token puro ou já "Basic ...")
526
+ token = (env_config.get("API_AUTHORIZATION") or "").strip()
527
+ auth_header = token if token.lower().startswith("basic ") else f"Basic {token}"
528
+
529
+ # --- Converte itens de entrada
530
+ itens_convertidos: List[Dict[str, int]] = []
531
+ for it in itens or []:
532
+ codigo = re.findall(r"\d+", it.get("descricaoProduto"))[0]
533
+ quantidade = it.get("quantidade", it.get("qtd"))
534
+ if codigo is None or quantidade is None:
535
+ logger.warning(f"Item incompleto: {it}")
536
+ console.print(f"⚠️ Item incompleto: {it}", style="yellow")
537
+ continue
538
+ try:
539
+ itens_convertidos.append({"codigo": int(codigo), "quantidade": int(quantidade)})
540
+ except Exception:
541
+ logger.warning(f"Item inválido (não numérico): {it}")
542
+ console.print(f"⚠️ Item inválido (não numérico): {it}", style="yellow")
543
+
544
+ body = {
545
+ "codFornecedor": int(codFornecedor),
546
+ "codEmpresa": int(codEmpresa),
547
+ "itens": itens_convertidos,
548
+ }
549
+ headers = {"Authorization": auth_header, "Content-Type": "application/json"}
550
+
551
+ # --- Chamada HTTP
552
+ async with aiohttp.ClientSession(connector=aiohttp.TCPConnector(verify_ssl=False)) as session:
553
+ async with session.post(url, json=body, headers=headers) as resp:
554
+ text = await resp.text()
555
+ if resp.status != 200:
556
+ raise RuntimeError(f"HTTP {resp.status} ao chamar {url}: {text}")
557
+ try:
558
+ data = await resp.json()
559
+ except Exception:
560
+ raise RuntimeError(f"Resposta não-JSON do servidor: {text[:600]}")
561
+
562
+ console.print(f"✅ Resposta da API: {data}", style="bold green")
563
+ logger.info(f"nf-supplier/checker -> {data}")
564
+
565
+ if not isinstance(data, list):
566
+ raise ValueError(f"Formato inesperado da API: {type(data)} -> {data}")
567
+
568
+ # --- Normaliza tipos e monta índices
569
+ lista_norm: List[Dict[str, Any]] = []
570
+ por_codigo: Dict[int, Dict[str, Any]] = {}
571
+
572
+ for row in data:
573
+ cod = int(row.get("codItem"))
574
+ item_norm = {
575
+ "codItem": cod,
576
+ "qtdTotal": int(row.get("qtdTotal", 0)),
577
+ "notas": [str(n) for n in (row.get("notas") or [])],
578
+ "valorUnitario": float(row.get("valorUnitario", 0.0)),
579
+ }
580
+ lista_norm.append(item_norm)
581
+ por_codigo[cod] = item_norm
582
+
583
+ return {"lista": lista_norm, "por_codigo": por_codigo}
584
+
501
585
  async def get_status_nf_emsys(chave: int):
502
586
  """
503
587
  Procura o status de nota fiscal no EMSYS.
@@ -538,7 +622,7 @@ async def get_dados_nf_emsys(
538
622
  chave: Optional[int] = None,
539
623
  numero_nota: Optional[int] = None,
540
624
  serie_nota: Optional[int] = None,
541
- filial_nota: Optional[int] = None
625
+ fornecedor: Optional[int] = None
542
626
  ):
543
627
  """
544
628
  Consulta a NF no EMSYS (ahead-nota) e retorna os campos essenciais:
@@ -548,7 +632,7 @@ async def get_dados_nf_emsys(
548
632
  chave (int, opcional): Chave de acesso da NF.
549
633
  numero_nota (int, opcional): Número da NF.
550
634
  serie_nota (int, opcional): Série da NF (obrigatória se numero_nota for informado).
551
- filial_nota (int, opcional): Filial da NF (obrigatória se numero_nota for informado).
635
+ fornecedor (int, opcional): Filial da NF (obrigatória se numero_nota for informado).
552
636
 
553
637
  Returns:
554
638
  list[dict]: Lista de notas no formato esperado.
@@ -565,14 +649,14 @@ async def get_dados_nf_emsys(
565
649
 
566
650
  # Caso 2: veio numero_nota → exige serie e filial
567
651
  elif numero_nota is not None:
568
- if serie_nota is None or filial_nota is None:
569
- raise ValueError("Para buscar por número da nota é obrigatório informar também 'serie_nota' e 'filial_nota'.")
652
+ if serie_nota is None or fornecedor is None:
653
+ raise ValueError("Para buscar por número da nota é obrigatório informar também 'serie_nota' e 'fornecedor'.")
570
654
  params["numeroNfe"] = numero_nota
571
655
  params["serieNfe"] = serie_nota
572
- params["empresaCodigo"] = filial_nota
656
+ params["fornecedorCnpj"] = fornecedor
573
657
 
574
658
  else:
575
- raise ValueError("É necessário informar 'chave' ou ('numero_nota' + 'serie_nota' + 'filial_nota').")
659
+ raise ValueError("É necessário informar 'chave' ou ('numero_nota' + 'serie_nota' + 'fornecedor').")
576
660
 
577
661
  headers_basic = {"Authorization": f"Basic {env_config['API_AUTHORIZATION']}"}
578
662
 
@@ -582,8 +666,9 @@ async def get_dados_nf_emsys(
582
666
  ) as session:
583
667
  async with session.get(url, headers=headers_basic, params=params) as response:
584
668
  if response.status != 200:
669
+ body = await response.text() # ✅ lê o conteúdo do body
585
670
  raise Exception(
586
- f"Erro ao comunicar com endpoint do Simplifica: {await response.text()}"
671
+ f"({response.status}): {body}"
587
672
  )
588
673
 
589
674
  data = await response.json()
@@ -656,7 +741,7 @@ async def get_dados_nf_emsys(
656
741
  return resultado
657
742
 
658
743
  except Exception as e:
659
- raise Exception(f"Erro ao comunicar com endpoint do Simplifica: {e}")
744
+ raise Exception(f"{e}")
660
745
 
661
746
 
662
747
 
@@ -843,3 +928,29 @@ async def download_file_from_historico(uuid: str):
843
928
  )
844
929
  console.print(f"\n{err_msg}\n", style="bold green")
845
930
  logger.info(err_msg)
931
+
932
+ async def get_mfa_code(key: str):
933
+ try:
934
+ env, _ = load_env_config()
935
+ headers = {
936
+ "Content-Type": "application/json",
937
+ "Authorization": "Basic " + env["API_AUTHORIZATION"],
938
+ }
939
+ payload = {"key": key}
940
+
941
+ response = requests.post(
942
+ env["API_BASE_URL"] + "/redis/get-redis-code",
943
+ json=payload,
944
+ headers=headers,
945
+ )
946
+
947
+ if response.status_code == 200 and response.json() is not None:
948
+ return {"code": response.json(), "status_code": 200}
949
+ else:
950
+ raise Exception(
951
+ f"Error to get mfa code, message: {response.text}, status_code {response.status_code}"
952
+ )
953
+
954
+ except Exception as e:
955
+ logger.error(f"Error to get mfa code: {str(e)}")
956
+ return {"code": None, "status_code": 500}
@@ -53,6 +53,7 @@ async def create_store_historico(
53
53
  or task.configEntrada.get("chaveCte")
54
54
  or task.configEntrada.get("empresa")
55
55
  or task.configEntrada.get("uuidSimplifica")
56
+ or task.configEntrada.get("identificador")
56
57
  or ""
57
58
  )
58
59
 
@@ -8,7 +8,7 @@ import os
8
8
  from pywinauto.findwindows import ElementNotFoundError
9
9
  from pywinauto.keyboard import send_keys
10
10
 
11
- # sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..')))
11
+ sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..')))
12
12
  from worker_automate_hub.utils.logger import logger
13
13
  from worker_automate_hub.models.dto.rpa_historico_request_dto import (
14
14
  RpaHistoricoStatusEnum,
@@ -45,6 +45,8 @@ console = Console()
45
45
 
46
46
  emsys = EMSys()
47
47
 
48
+ # ASSETS_PATH = r"C:\Users\automatehub\Documents\GitHub\worker-automate-hub\assets\abertura_livros"
49
+ ASSETS_PATH = r"assets\abertura_livros"
48
50
 
49
51
  @repeat(times=10, delay=5)
50
52
  async def wait_aguarde_window_closed(app, timeout=60):
@@ -136,7 +138,7 @@ async def abertura_livros_fiscais(task: RpaProcessoEntradaDTO) -> RpaRetornoProc
136
138
  console.print("Aguardando janela 'Movimento de Livro Fiscal' aparecer...")
137
139
 
138
140
  # Tempo limite de espera (em segundos)
139
- timeout = 60
141
+ timeout = 3600
140
142
  inicio = time.time()
141
143
 
142
144
  # Espera até a janela aparecer
@@ -192,8 +194,7 @@ async def abertura_livros_fiscais(task: RpaProcessoEntradaDTO) -> RpaRetornoProc
192
194
  saida_checkbox.click_input()
193
195
 
194
196
  console.print("Aguardar marcar caixa de saida")
195
- # imagem = r"C:\Users\automatehub\Documents\GitHub\worker-automate-hub\assets\abertura_livros\saida_marcada.png"
196
- imagem = "assets\\abertura_livros\\saida_marcada.png"
197
+ imagem = fr"{ASSETS_PATH}\saida_marcada.png"
197
198
 
198
199
  tempo_limite = 600 # 10 minutos
199
200
  intervalo = 2 # segundos entre verificações
@@ -329,8 +330,7 @@ async def abertura_livros_fiscais(task: RpaProcessoEntradaDTO) -> RpaRetornoProc
329
330
  await worker_sleep(5)
330
331
 
331
332
  console.print("Aguardar o término de carregar")
332
- # imagem = r"C:\Users\automatehub\Documents\GitHub\worker-automate-hub\assets\abertura_livros\livros_incluidos.png"
333
- imagem = "assets\\abertura_livros\\livros_incluidos.png"
333
+ imagem = fr"{ASSETS_PATH}\livros_incluidos.png"
334
334
 
335
335
  tempo_limite = 6600 # 1h
336
336
  intervalo = 2 # segundos entre as verificações
@@ -372,8 +372,7 @@ async def abertura_livros_fiscais(task: RpaProcessoEntradaDTO) -> RpaRetornoProc
372
372
 
373
373
  # 2. Verifica e trata janela de confirmação TMessageForm
374
374
  try:
375
- # img_dialog = r"C:\Users\automatehub\Documents\GitHub\worker-automate-hub\assets\abertura_livros\notas_rejeitadas.png"
376
- img_dialog = "assets\\abertura_livros\\notas_rejeitadas.png"
375
+ img_dialog = fr"{ASSETS_PATH}\notas_rejeitadas.png"
377
376
  if os.path.exists(img_dialog):
378
377
  caixa = pyautogui.locateOnScreen(
379
378
  img_dialog, confidence=0.9, grayscale=True
@@ -397,8 +396,7 @@ async def abertura_livros_fiscais(task: RpaProcessoEntradaDTO) -> RpaRetornoProc
397
396
 
398
397
  await worker_sleep(5)
399
398
 
400
- # img_dialog = r"C:\Users\automatehub\Documents\GitHub\worker-automate-hub\assets\abertura_livros\gerar_rel_notas_rejeitadas.png"
401
- img_dialog = "assets\\abertura_livros\\gerar_rel_notas_rejeitadas.png"
399
+ img_dialog = fr"{ASSETS_PATH}\gerar_rel_notas_rejeitadas.png"
402
400
  if os.path.exists(img_dialog):
403
401
  caixa = pyautogui.locateOnScreen(
404
402
  img_dialog, confidence=0.9, grayscale=True
@@ -476,8 +474,7 @@ async def abertura_livros_fiscais(task: RpaProcessoEntradaDTO) -> RpaRetornoProc
476
474
  # Verificando se a foi confirmado os livres
477
475
  console.print("Verificando se os livros foram confirmados")
478
476
 
479
- # imagem = r"C:\Users\automatehub\Documents\GitHub\worker-automate-hub\assets\abertura_livros\confirmado_livros.png"
480
- imagem = "assets\\abertura_livros\\confirmado_livros.png"
477
+ imagem = fr"{ASSETS_PATH}\confirmado_livros.png"
481
478
  if os.path.exists(imagem):
482
479
  caixa = pyautogui.locateOnScreen(imagem, confidence=0.9, grayscale=True)
483
480
  else:
@@ -610,8 +607,7 @@ async def abertura_livros_fiscais(task: RpaProcessoEntradaDTO) -> RpaRetornoProc
610
607
 
611
608
  console.print("Clicando no botão incluir apuração")
612
609
  # Clicar em incluir apuração
613
- # imagem = r"C:\Users\automatehub\Documents\GitHub\worker-automate-hub\assets\abertura_livros\btn_incluir_apuracao.png"
614
- imagem = "assets\\abertura_livros\\btn_incluir_apuracao.png"
610
+ imagem = fr"{ASSETS_PATH}\btn_incluir_apuracao.png"
615
611
 
616
612
  # Tenta localizar a imagem na tela
617
613
  localizacao = pyautogui.locateCenterOnScreen(imagem, confidence=0.9)
@@ -648,3 +644,4 @@ async def abertura_livros_fiscais(task: RpaProcessoEntradaDTO) -> RpaRetornoProc
648
644
  status=RpaHistoricoStatusEnum.Falha,
649
645
  tags=[RpaTagDTO(descricao=RpaTagEnum.Tecnico)],
650
646
  )
647
+
@@ -432,7 +432,7 @@ async def descartes(task: RpaProcessoEntradaDTO) -> RpaRetornoProcessoDTO:
432
432
  task.configEntrada["filialEmpresaOrigem"] + ALMOXARIFADO_DEFAULT
433
433
  )
434
434
  pyautogui.hotkey("tab")
435
- await worker_sleep(2)
435
+ await worker_sleep(10)
436
436
  console.print(
437
437
  f"\nDigitou almoxarifado {task.configEntrada["filialEmpresaOrigem"] + ALMOXARIFADO_DEFAULT}",
438
438
  style="bold green",
@@ -462,7 +462,7 @@ async def descartes(task: RpaProcessoEntradaDTO) -> RpaRetornoProcessoDTO:
462
462
  pyautogui.hotkey("del")
463
463
  pyautogui.write(item["codigoProduto"])
464
464
  pyautogui.hotkey("tab")
465
- await worker_sleep(2)
465
+ await worker_sleep(10)
466
466
  console.print(f"\nDigitou item {item['codigoProduto']}", style="bold green")
467
467
 
468
468
  # Checa tela de pesquisa de item
@@ -523,7 +523,7 @@ async def descartes(task: RpaProcessoEntradaDTO) -> RpaRetornoProcessoDTO:
523
523
 
524
524
  screenshot_path = take_screenshot()
525
525
 
526
- await worker_sleep(2)
526
+ await worker_sleep(5)
527
527
 
528
528
  # Seleciona o Saldo Disponivel e verifica se ah possibilidade do descarte
529
529
  console.print(
@@ -536,7 +536,7 @@ async def descartes(task: RpaProcessoEntradaDTO) -> RpaRetornoProcessoDTO:
536
536
  class_name="TDBIEditNumber", found_index=9
537
537
  ).window_text()
538
538
  except Exception as error:
539
- console.print(f"Erro ao selecionar o Saldo Disponivel: {error}")
539
+ console.print(f"Erro ao selecionar o Saldo Disponivel: {str(error)}")
540
540
  await send_to_webhook(
541
541
  task.configEntrada["urlRetorno"],
542
542
  "ERRO",
@@ -572,7 +572,7 @@ async def descartes(task: RpaProcessoEntradaDTO) -> RpaRetornoProcessoDTO:
572
572
  f"Saldo Disponivel: '{amount_avaliable}'", style="bold green"
573
573
  )
574
574
  except Exception as error:
575
- console.print(f"Erro ao converter o Saldo Disponível: {error}")
575
+ console.print(f"Erro ao converter o Saldo Disponível: {str(error)}")
576
576
  await send_to_webhook(
577
577
  task.configEntrada["urlRetorno"],
578
578
  "ERRO",
@@ -772,7 +772,7 @@ async def descartes(task: RpaProcessoEntradaDTO) -> RpaRetornoProcessoDTO:
772
772
  )
773
773
  except Exception as e:
774
774
  console.print(
775
- f"Falha ao clicar no botão OK de pré-venda incluída: {e}",
775
+ f"Falha ao clicar no botão OK de pré-venda incluída: {str(e)}",
776
776
  style="red",
777
777
  )
778
778
  return RpaRetornoProcessoDTO(
@@ -794,7 +794,7 @@ async def descartes(task: RpaProcessoEntradaDTO) -> RpaRetornoProcessoDTO:
794
794
 
795
795
  except Exception as e:
796
796
  console.print(
797
- f"O Botão OK de pré-venda incluída não foi encontrado: {e}", style="red"
797
+ f"O Botão OK de pré-venda incluída não foi encontrado: {str(e)}", style="red"
798
798
  )
799
799
  return RpaRetornoProcessoDTO(
800
800
  sucesso=False,
@@ -1041,7 +1041,7 @@ async def descartes(task: RpaProcessoEntradaDTO) -> RpaRetornoProcessoDTO:
1041
1041
  )
1042
1042
 
1043
1043
  except Exception as ex:
1044
- log_msg = f"Erro Processo Descartes: {ex} | Número da nota: {nota_fiscal} | Valor: {valor_nota}"
1044
+ log_msg = f"Erro Processo Descartes: {str(ex)} | Número da nota: {nota_fiscal} | Valor: {valor_nota}"
1045
1045
  logger.error(log_msg)
1046
1046
  console.print(log_msg, style="bold red")
1047
1047
  await send_to_webhook(