worker-automate-hub 0.5.820__py3-none-any.whl → 0.5.912__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- worker_automate_hub/api/client.py +121 -10
- worker_automate_hub/api/rpa_historico_service.py +1 -0
- worker_automate_hub/tasks/jobs/abertura_livros_fiscais.py +11 -14
- worker_automate_hub/tasks/jobs/descartes.py +8 -8
- worker_automate_hub/tasks/jobs/devolucao_produtos.py +1386 -0
- worker_automate_hub/tasks/jobs/extracao_dados_nielsen.py +504 -0
- worker_automate_hub/tasks/jobs/extracao_saldo_estoque_fiscal.py +90 -11
- worker_automate_hub/tasks/jobs/fidc_gerar_nosso_numero.py +2 -2
- worker_automate_hub/tasks/jobs/fidc_remessa_cobranca_cnab240.py +24 -15
- 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 +523 -384
- worker_automate_hub/tasks/task_definitions.py +30 -2
- worker_automate_hub/utils/util.py +20 -10
- {worker_automate_hub-0.5.820.dist-info → worker_automate_hub-0.5.912.dist-info}/METADATA +2 -1
- {worker_automate_hub-0.5.820.dist-info → worker_automate_hub-0.5.912.dist-info}/RECORD +22 -15
- {worker_automate_hub-0.5.820.dist-info → worker_automate_hub-0.5.912.dist-info}/WHEEL +0 -0
- {worker_automate_hub-0.5.820.dist-info → worker_automate_hub-0.5.912.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
|
-
|
|
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
|
-
|
|
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
|
|
569
|
-
raise ValueError("Para buscar por número da nota é obrigatório informar também 'serie_nota' e '
|
|
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["
|
|
656
|
+
params["fornecedorCnpj"] = fornecedor
|
|
573
657
|
|
|
574
658
|
else:
|
|
575
|
-
raise ValueError("É necessário informar 'chave' ou ('numero_nota' + 'serie_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"
|
|
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"
|
|
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}
|
|
@@ -8,7 +8,7 @@ import os
|
|
|
8
8
|
from pywinauto.findwindows import ElementNotFoundError
|
|
9
9
|
from pywinauto.keyboard import send_keys
|
|
10
10
|
|
|
11
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|