""" Validadores Pydantic para campos numéricos y críticos Corrección de auditoría técnica - 05/07/2026 Agregar estos validadores a schemas.py para prevenir datos malformados """ from pydantic import field_validator import logging logger = logging.getLogger(__name__) # ═══════════════════════════════════════════════════════════════════════ # VALIDADORES PARA BcraHistorial # ═══════════════════════════════════════════════════════════════════════ class BcraHistorialValidators: """ Agregar estos validadores a la clase BcraHistorial en schemas.py """ @field_validator('monto_deuda', mode='before') @classmethod def coerce_monto_deuda(cls, v): """Convierte string a float si es necesario""" if v is None: return None if isinstance(v, (int, float)): return float(v) if isinstance(v, str): # Limpiar formato de moneda clean = v.replace('$', '').replace(',', '').replace('.', '').replace('k', '000').strip() try: return float(clean) if clean else None except ValueError: logger.warning(f"No se pudo convertir monto_deuda: {v}") return None return None @field_validator('situacion', mode='before') @classmethod def validate_situacion(cls, v): """Valida que situación esté entre 1-6""" if v is None: return 1 # Default Normal try: sit = int(v) if 1 <= sit <= 6: return sit logger.warning(f"Situación BCRA fuera de rango: {sit}, usando 1 (Normal)") return 1 except (ValueError, TypeError): logger.warning(f"Situación BCRA inválida: {v}, usando 1 (Normal)") return 1 @field_validator('dias_atraso', mode='before') @classmethod def coerce_dias_atraso(cls, v): """Convierte a int de forma segura""" if v is None: return None try: return int(v) except (ValueError, TypeError): logger.warning(f"dias_atraso inválido: {v}") return None # ═══════════════════════════════════════════════════════════════════════ # VALIDADORES PARA ChequeRechazado # ═══════════════════════════════════════════════════════════════════════ class ChequeRechazadoValidators: """ Agregar estos validadores a la clase ChequeRechazado en schemas.py """ @field_validator('monto', mode='before') @classmethod def coerce_monto(cls, v): """Convierte string a float si es necesario""" if v is None: return None if isinstance(v, (int, float)): return float(v) if isinstance(v, str): clean = v.replace('$', '').replace(',', '').replace('.', '').strip() try: return float(clean) if clean else None except ValueError: logger.warning(f"No se pudo convertir monto de cheque: {v}") return None return None # ═══════════════════════════════════════════════════════════════════════ # VALIDADORES PARA Inmueble # ═══════════════════════════════════════════════════════════════════════ class InmuebleValidators: """ Agregar estos validadores a la clase Inmueble en schemas.py """ @field_validator('valuacion_fiscal', mode='before') @classmethod def coerce_valuacion_fiscal(cls, v): """Convierte string a float si es necesario""" if v is None: return None if isinstance(v, (int, float)): return float(v) if isinstance(v, str): clean = v.replace('$', '').replace(',', '').replace('.', '').strip() try: return float(clean) if clean else None except ValueError: logger.warning(f"No se pudo convertir valuacion_fiscal: {v}") return None return None # ═══════════════════════════════════════════════════════════════════════ # VALIDADORES PARA InfraccionTransito # ═══════════════════════════════════════════════════════════════════════ class InfraccionTransitoValidators: """ Agregar estos validadores a la clase InfraccionTransito en schemas.py """ @field_validator('monto', mode='before') @classmethod def coerce_monto(cls, v): """Convierte string a float si es necesario""" if v is None: return None if isinstance(v, (int, float)): return float(v) if isinstance(v, str): clean = v.replace('$', '').replace(',', '').replace('.', '').strip() try: return float(clean) if clean else None except ValueError: logger.warning(f"No se pudo convertir monto de infracción: {v}") return None return None # ═══════════════════════════════════════════════════════════════════════ # EJEMPLO DE USO EN schemas.py # ═══════════════════════════════════════════════════════════════════════ """ # EN EL ARCHIVO schemas.py, AGREGAR: from pydantic import field_validator import logging logger = logging.getLogger(__name__) class BcraHistorial(BaseModel): periodo: Optional[str] = None situacion: Optional[int] = None entidad: Optional[str] = None monto_deuda: Optional[float] = None denominacion: Optional[str] = None dias_atraso: Optional[int] = None # ... resto de campos # VALIDADORES @field_validator('monto_deuda', mode='before') @classmethod def coerce_monto_deuda(cls, v): if v is None: return None if isinstance(v, (int, float)): return float(v) if isinstance(v, str): clean = v.replace('$', '').replace(',', '').replace('.', '').replace('k', '000').strip() try: return float(clean) if clean else None except ValueError: logger.warning(f"No se pudo convertir monto_deuda: {v}") return None return None @field_validator('situacion', mode='before') @classmethod def validate_situacion(cls, v): if v is None: return 1 try: sit = int(v) if 1 <= sit <= 6: return sit logger.warning(f"Situación BCRA fuera de rango: {sit}, usando 1") return 1 except (ValueError, TypeError): return 1 # REPETIR PARA CADA CLASE QUE NECESITE VALIDACIÓN """