""" Modelo de Mantenimiento para registrar servicios de mantenimiento. """ from datetime import date, datetime from sqlalchemy import ( Boolean, Date, DateTime, Float, ForeignKey, Index, String, Text, ) from sqlalchemy.orm import Mapped, mapped_column, relationship from app.core.database import Base from app.models.base import TimestampMixin class Mantenimiento(Base, TimestampMixin): """Modelo para registrar mantenimientos de vehículos.""" __tablename__ = "mantenimientos" id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True) # Relaciones vehiculo_id: Mapped[int] = mapped_column( ForeignKey("vehiculos.id", ondelete="CASCADE"), nullable=False, index=True, ) tipo_mantenimiento_id: Mapped[int] = mapped_column( ForeignKey("tipos_mantenimiento.id", ondelete="RESTRICT"), nullable=False, index=True, ) # Estado estado: Mapped[str] = mapped_column( String(20), default="programado", nullable=False, ) # programado, en_proceso, completado, cancelado, vencido # Fechas fecha_programada: Mapped[date] = mapped_column(Date, nullable=False) fecha_realizada: Mapped[date | None] = mapped_column(Date, nullable=True) # Odómetro odometro_programado: Mapped[float | None] = mapped_column(Float, nullable=True) odometro_realizado: Mapped[float | None] = mapped_column(Float, nullable=True) # Costos costo_estimado: Mapped[float | None] = mapped_column(Float, nullable=True) costo_real: Mapped[float | None] = mapped_column(Float, nullable=True) costo_mano_obra: Mapped[float | None] = mapped_column(Float, nullable=True) costo_refacciones: Mapped[float | None] = mapped_column(Float, nullable=True) # Proveedor proveedor: Mapped[str | None] = mapped_column(String(100), nullable=True) proveedor_direccion: Mapped[str | None] = mapped_column(String(255), nullable=True) proveedor_telefono: Mapped[str | None] = mapped_column(String(20), nullable=True) # Documentación numero_factura: Mapped[str | None] = mapped_column(String(50), nullable=True) numero_orden: Mapped[str | None] = mapped_column(String(50), nullable=True) # Detalles descripcion: Mapped[str | None] = mapped_column(Text, nullable=True) trabajos_realizados: Mapped[str | None] = mapped_column(Text, nullable=True) refacciones_usadas: Mapped[str | None] = mapped_column(Text, nullable=True) # JSON array # Notas notas: Mapped[str | None] = mapped_column(Text, nullable=True) # Técnico responsable tecnico: Mapped[str | None] = mapped_column(String(100), nullable=True) # Próximo mantenimiento (para calcular el siguiente) proximo_km: Mapped[float | None] = mapped_column(Float, nullable=True) proxima_fecha: Mapped[date | None] = mapped_column(Date, nullable=True) # Archivos adjuntos (JSON array de URLs) archivos_adjuntos: Mapped[str | None] = mapped_column(Text, nullable=True) # Recordatorios enviados recordatorio_enviado: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) # Relaciones ORM vehiculo: Mapped["Vehiculo"] = relationship( "Vehiculo", back_populates="mantenimientos", lazy="selectin", ) tipo_mantenimiento: Mapped["TipoMantenimiento"] = relationship( "TipoMantenimiento", back_populates="mantenimientos", lazy="selectin", ) # Índices __table_args__ = ( Index("idx_mantenimientos_vehiculo_fecha", "vehiculo_id", "fecha_programada"), Index("idx_mantenimientos_estado", "estado"), Index("idx_mantenimientos_fecha_prog", "fecha_programada"), ) @property def esta_vencido(self) -> bool: """Verifica si el mantenimiento está vencido.""" if self.estado in ["completado", "cancelado"]: return False return self.fecha_programada < date.today() @property def dias_para_vencimiento(self) -> int | None: """Calcula los días restantes para el vencimiento.""" if self.estado in ["completado", "cancelado"]: return None return (self.fecha_programada - date.today()).days def __repr__(self) -> str: return f""