feat: normalise les numéros de lot sur 2 chiffres
Les PDF et les saisies manuelles écrivent le même lot de plusieurs façons
("6", "06", "0006"), et chaque forme créait jusqu'ici un lot distinct en
base. utils/lots.py fixe la forme canonique sur 2 chiffres et sert de point
d'entrée unique pour la normalisation.
Elle est appliquée à la source dans les parseurs (locataires texte et
tableau, codes lot des opérations), et en dernier recours dans
get_or_create_lot et save_document, pour couvrir les extractions éditées à
la main via l'API. Les numéros à plus de 2 chiffres significatifs ne sont
pas tronqués.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
79
src/plesna_gerance/utils/lots.py
Normal file
79
src/plesna_gerance/utils/lots.py
Normal file
@@ -0,0 +1,79 @@
|
||||
"""Utilitaires pour la normalisation des numéros de lot."""
|
||||
|
||||
import re
|
||||
from typing import Any
|
||||
|
||||
#: Largeur canonique d'un numéro de lot (ex: "06", "17").
|
||||
LOT_NUMERO_WIDTH = 2
|
||||
|
||||
#: Numéro utilisé quand aucun lot n'est identifiable.
|
||||
LOT_NUMERO_INCONNU = "00"
|
||||
|
||||
|
||||
def normalize_lot_numero(value: str | int | None) -> str | None:
|
||||
"""Normalise un numéro de lot sur exactement 2 chiffres.
|
||||
|
||||
Les PDF et les saisies manuelles utilisent des formats variés ("6", "06",
|
||||
"0006"). Tous doivent aboutir au même numéro canonique, sinon un même lot
|
||||
est dupliqué en base.
|
||||
|
||||
Gère:
|
||||
- "0006" -> "06"
|
||||
- "6" -> "06"
|
||||
- "06" -> "06"
|
||||
- "0020" -> "20"
|
||||
- "0000" -> "00"
|
||||
|
||||
Les numéros comportant plus de 2 chiffres significatifs (ex: "0123") sont
|
||||
conservés sans leurs zéros de tête ("123") : les tronquer perdrait de
|
||||
l'information.
|
||||
|
||||
Args:
|
||||
value: Numéro de lot dans un format quelconque
|
||||
|
||||
Returns:
|
||||
Numéro sur 2 chiffres, ou None si aucun chiffre n'est présent
|
||||
"""
|
||||
if value is None:
|
||||
return None
|
||||
|
||||
digits = re.sub(r"\D", "", str(value))
|
||||
if not digits:
|
||||
return None
|
||||
|
||||
significant = digits.lstrip("0")
|
||||
if not significant:
|
||||
return LOT_NUMERO_INCONNU
|
||||
|
||||
return significant.zfill(LOT_NUMERO_WIDTH)
|
||||
|
||||
|
||||
def normalize_extraction_lots(data: dict[str, Any]) -> dict[str, Any]:
|
||||
"""Normalise, sur place, tous les numéros de lot d'une extraction.
|
||||
|
||||
Couvre les deux emplacements où un numéro de lot apparaît:
|
||||
- ``situation_locataires[].lot.numero``
|
||||
- ``recapitulatif_operations[].lot_numero``
|
||||
|
||||
Utile pour les extractions éditées à la main via l'API, où le numéro saisi
|
||||
peut arriver sous n'importe quelle forme.
|
||||
|
||||
Args:
|
||||
data: Extraction (metadata, situation_locataires, recapitulatif_operations)
|
||||
|
||||
Returns:
|
||||
Le même dict, numéros de lot normalisés
|
||||
"""
|
||||
if not isinstance(data, dict):
|
||||
return data
|
||||
|
||||
for situation in data.get("situation_locataires") or []:
|
||||
lot = situation.get("lot")
|
||||
if isinstance(lot, dict) and lot.get("numero") is not None:
|
||||
lot["numero"] = normalize_lot_numero(lot["numero"])
|
||||
|
||||
for operation in data.get("recapitulatif_operations") or []:
|
||||
if isinstance(operation, dict) and operation.get("lot_numero") is not None:
|
||||
operation["lot_numero"] = normalize_lot_numero(operation["lot_numero"])
|
||||
|
||||
return data
|
||||
Reference in New Issue
Block a user