"""Arbitrage entre ce que dit le PDF d'un lot et ce que sa fiche en dit. Le type d'un lot est connu de deux sources : l'extraction PDF, qui le remplit parfois mal et souvent pas du tout, et la fiche saisie à la main, qui fait foi. La règle est donc « la fiche gagne, le PDF comble les trous » — et elle vit ici, en un seul endroit, sous ses deux formes : une expression SQL pour les agrégats, une fonction Python pour l'ORM. Les faire diverger reviendrait à afficher deux types différents pour un même lot selon la page consultée. Le désaccord entre les deux sources n'est jamais résolu en silence : il reste visible via `type_en_ecart` (voir `utils.logements`). """ from sqlalchemy import Select, func from ..database.models import Lot, LotCaracteristiques #: Type de lot retenu, en SQL. Requiert la jointure de `joindre_fiche`. TYPE_LOT_EFFECTIF = func.coalesce(LotCaracteristiques.type, Lot.type) def joindre_fiche(stmt: Select) -> Select: """Ajoute à une requête sur `Lot` la jointure vers sa fiche. La relation est 1↔1 : la jointure ne multiplie aucune ligne, elle peut donc s'ajouter à une requête agrégée sans fausser les totaux. """ return stmt.outerjoin(LotCaracteristiques, LotCaracteristiques.lot_id == Lot.id) def type_effectif(lot: Lot) -> str | None: """Type de lot retenu, depuis un objet chargé par l'ORM.""" fiche = lot.caracteristiques return (fiche.type if fiche else None) or lot.type