feat: décrit les logements dans un référentiel saisi à la main
Les lots n'étaient connus que par l'extraction PDF : un numéro, un type souvent vide, et rien sur le bien lui-même. Cette table de caractéristiques (surface, étage, bâtiment, chauffage, DPE, rapprochement impôts) donne au référentiel une source de vérité indépendante des comptes rendus. Table séparée de `lots` à dessein : une ré-extraction ne peut alors pas écraser la saisie, et le désaccord sur le type de lot reste visible au lieu d'être arbitré en silence. La fiche gagne, le PDF comble les trous. Échéance du DPE et écart de surface ne sont pas stockés mais calculés : une colonne dérivée finirait par mentir après une correction. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
34
src/plesna_gerance/services/referentiel.py
Normal file
34
src/plesna_gerance/services/referentiel.py
Normal file
@@ -0,0 +1,34 @@
|
||||
"""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
|
||||
Reference in New Issue
Block a user