"""Utilidades compartidas por los módulos del pipeline.""" import os import logging import tomllib logger = logging.getLogger("pipeline") def ruta_carpeta(subcarpeta: str) -> str: """Retorna la ruta absoluta de ``subcarpeta`` relativa a la raíz del proyecto.""" return os.path.join(os.path.dirname(__file__), subcarpeta) def log(msg: str, level: int = 1, last: bool = False, severity: int = logging.INFO) -> None: """ Registra ``msg`` con la indentación de árbol usada en el log del pipeline. Args: msg: Mensaje a registrar. level: Nivel de indentación del árbol (1-4). last: True si es el último hijo de su rama (``└──`` en vez de ``├──``). severity: Nivel de ``logging`` (``logging.WARNING``, ``logging.ERROR``…). Por defecto ``logging.INFO``. """ branch = " └── " if last else " ├── " pfx = {1: branch, 2: f" │ {branch}", 3: f" │ │ {branch}", 4: f" │ │ │ {branch}"}.get(level, branch) logger.log(severity, f"{pfx}{msg}") def version_del_proyecto() -> str: """Versión de ``pyproject.toml`` (por ejemplo ``1.2.0``), o ``desconocida`` si no se puede leer.""" try: with open(os.path.join(os.path.dirname(os.path.abspath(__file__)), "pyproject.toml"), "rb") as f: return tomllib.load(f)["project"]["version"] except (OSError, KeyError, tomllib.TOMLDecodeError): return "desconocida" # Diferencia máxima, en soles, entre el importe de la factura y el de SAP para darlos por iguales. TOLERANCIA_IMPORTE = 0.01 def importes_coinciden(a: float, b: float) -> bool: """ True si dos importes son iguales **a nivel de céntimos**, que es lo que muestra un comprobante. La diferencia se redondea a 2 decimales y se admite hasta ``TOLERANCIA_IMPORTE`` (1 céntimo). Comparar la diferencia sin redondear (``abs(a - b) < 0.01``) no es fiable: con coma flotante, 431.41 - 431.40 da 0.01000000000004775 (se rechazaba) y 624.39 - 624.38 da 0.00999999999999091 (se aceptaba), de modo que el mismo céntimo pasaba o no según el importe. """ return round(abs(a - b), 2) <= TOLERANCIA_IMPORTE