refactor: factorise la création d'engine et normalise solde_montant

- connection: création de l'engine centralisée dans _build_engine (source
  unique de config) ; init_db réutilise reset_connection + get_engine au lieu
  de dupliquer create_engine
- service: _normalize_amount garantit qu'un montant non numérique issu de
  l'extraction (string, dict…) n'entre jamais en base dans une colonne Float ;
  appliqué à solde_montant
- tests: couverture de _normalize_amount et du solde sous forme de string

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-11 10:15:09 +02:00
parent 635a094591
commit 4606d6785b
3 changed files with 60 additions and 29 deletions

View File

@@ -10,6 +10,7 @@ from sqlalchemy.exc import IntegrityError
from .models import Document, Immeuble, Lot, Locataire, Revenu, Depense, Tag
from . import storage
from ..utils.amounts import parse_amount
class DuplicateDocumentError(Exception):
@@ -95,6 +96,21 @@ class DatabaseService:
except (ValueError, TypeError):
return None
@staticmethod
def _normalize_amount(value: Any) -> float | None:
"""Normalise un montant en float (ou None si non interprétable).
Garantit qu'un type non numérique issu de l'extraction (string, dict…)
n'entre jamais en base dans une colonne Float.
"""
if isinstance(value, bool): # bool est un int en Python, on l'exclut
return None
if isinstance(value, (int, float)):
return float(value)
if isinstance(value, str):
return parse_amount(value)
return None
def save_document(
self,
data: dict[str, Any],
@@ -186,7 +202,7 @@ class DatabaseService:
json_data=json.dumps(data, ensure_ascii=False, default=str),
editeur_nom=editeur_info.get("nom"),
editeur_siret=editeur_info.get("siret"),
solde_montant=solde_info.get("montant"),
solde_montant=self._normalize_amount(solde_info.get("montant")),
solde_type=solde_info.get("type"),
solde_date_arrete=self._parse_date(solde_info.get("date_arrete")),
pdf_path=pdf_path,