gmao/app_new/contracts/models.py
root 0b3fff43e5
Some checks are pending
CI - Tests et Syntax / lint-and-test (push) Waiting to run
Complète le suivi opérationnel de la GMAO
2026-08-14 23:26:22 +00:00

133 lines
5.4 KiB
Python

"""
Modele Contract - GMAO College
Suivi des contrats d'entreprise pour les equipements/lots.
"""
from datetime import datetime, timezone, date
from ..extensions import db
class Contract(db.Model):
"""Contrat d'entreprise pour un lot ou un equipement."""
__tablename__ = "contracts"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(200), nullable=False)
contract_number = db.Column(db.String(100), nullable=True)
# Liaison : un contrat est lie a un lot et/ou un equipement
lot_id = db.Column(db.Integer, db.ForeignKey("lots.id"), nullable=True)
equipment_id = db.Column(db.Integer, db.ForeignKey("equipments.id"), nullable=True)
company_id = db.Column(db.Integer, db.ForeignKey("companies.id"), nullable=True)
# Dates
signed_date = db.Column(db.Date, nullable=True)
start_date = db.Column(db.Date, nullable=False)
end_date = db.Column(db.Date, nullable=True) # NULL = duree indeterminee
# Tacite reconduction
tacit_renewal = db.Column(db.Boolean, default=True)
renewal_period_days = db.Column(db.Integer, default=365) # reconduction par periode de X jours
# Delai de preavis pour resilier (en jours avant la date de fin)
notice_period_days = db.Column(db.Integer, nullable=True)
notice_deadline = db.Column(db.Date, nullable=True) # date limite pour envoyer le preavis
# Montant
annual_amount = db.Column(db.Float, nullable=True)
currency = db.Column(db.String(3), default="EUR")
# Statut
status = db.Column(db.String(20), default="actif") # actif, expire, resilie, en_attente
notes = db.Column(db.Text, nullable=True)
visit_interval_days = db.Column(db.Integer, nullable=True)
last_visit_date = db.Column(db.Date, nullable=True)
next_visit_date = db.Column(db.Date, nullable=True)
# Documents
contract_file = db.Column(db.String(500), nullable=True)
created_at = db.Column(db.DateTime, default=lambda: datetime.now(timezone.utc))
updated_at = db.Column(db.DateTime, default=lambda: datetime.now(timezone.utc), onupdate=lambda: datetime.now(timezone.utc))
# Relations
lot = db.relationship("Lot", backref=db.backref("contracts", lazy="dynamic"))
equipment = db.relationship("Equipment", backref=db.backref("contracts", lazy="dynamic"))
company = db.relationship("Company", backref=db.backref("contracts", lazy="dynamic"))
visits = db.relationship("ContractVisit", back_populates="contract", cascade="all, delete-orphan",
order_by="ContractVisit.scheduled_date")
@property
def is_active(self):
"""True si le contrat est actif aujourd'hui."""
if self.status != "actif":
return False
today = date.today()
if self.start_date and today < self.start_date:
return False
if self.end_date and today > self.end_date:
return False
return True
@property
def is_expiring_soon(self):
"""True si le contrat expire dans les 90 prochains jours."""
if not self.end_date:
return False
days_left = (self.end_date - date.today()).days
return 0 <= days_left <= 90
@property
def days_until_expiry(self):
"""Nombre de jours avant l'expiration (negatif = expire)."""
if not self.end_date:
return None
return (self.end_date - date.today()).days
@property
def notice_deadline_calculated(self):
"""Calcule la date limite de preavis si pas definie manuellement."""
if self.notice_deadline:
return self.notice_deadline
if self.end_date and self.notice_period_days:
from datetime import timedelta
return self.end_date - timedelta(days=self.notice_period_days)
return None
@property
def must_send_notice_now(self):
"""True si le preavis doit etre envoye maintenant (deadline proche)."""
deadline = self.notice_deadline_calculated
if not deadline:
return False
days_left = (deadline - date.today()).days
return days_left <= 60 # alerte 60 jours avant
@property
def status_color(self):
"""Couleur du badge."""
if not self.is_active:
return "danger" if self.status == "expire" else "secondary"
if self.is_expiring_soon:
return "warning"
if self.must_send_notice_now:
return "warning"
return "success"
def __repr__(self):
return f"<Contract {self.name} ({self.status})>"
class ContractVisit(db.Model):
"""Visite planifiée ou réalisée par le prestataire d'un contrat."""
__tablename__ = "contract_visits"
__table_args__ = (db.UniqueConstraint("contract_id", "scheduled_date", name="uq_contract_visit_date"),)
id = db.Column(db.Integer, primary_key=True)
contract_id = db.Column(db.Integer, db.ForeignKey("contracts.id", ondelete="CASCADE"), nullable=False, index=True)
scheduled_date = db.Column(db.Date, nullable=False, index=True)
status = db.Column(db.String(20), nullable=False, default="planifiee")
completed_at = db.Column(db.DateTime, nullable=True)
report = db.Column(db.Text, nullable=True)
created_at = db.Column(db.DateTime, default=lambda: datetime.now(timezone.utc), nullable=False)
contract = db.relationship("Contract", back_populates="visits")