115 lines
4.4 KiB
Python
115 lines
4.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"))
|
|
|
|
@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})>"
|