Les agrégats par immeuble joignaient revenus et locataires à partir du
lot. Comme un lot accumule ses occupants successifs, chaque ligne de
revenu était comptée autant de fois qu'il y a eu de locataires : aucune
erreur levée, juste des montants faux qui dérivent avec l'ancienneté du
parc.
Sur la base réelle (40 lots, 46 locataires), /api/revenus/immeubles et
le bloc by_immeuble de /api/revenus/summary annonçaient :
total 348 111,95 € au lieu de 332 899,45 € (+4,6 %)
réglés 100 697,66 € au lieu de 85 545,16 € (+17,7 %)
taux 28,9 % au lieu de 25,7 %
Les montants sont désormais agrégés par immeuble AVANT la jointure des
locataires, qui ne sert plus qu'au dénombrement. Les deux endpoints
partagent ces sous-requêtes et la construction de la réponse.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
117 lines
3.5 KiB
Python
117 lines
3.5 KiB
Python
"""Fixtures partagées pour les tests."""
|
|
|
|
import pytest
|
|
|
|
from plesna_gerance.database import connection
|
|
|
|
|
|
def pytest_addoption(parser):
|
|
"""Ajoute --regen-golden pour refiger les références des parseurs."""
|
|
parser.addoption(
|
|
"--regen-golden",
|
|
action="store_true",
|
|
default=False,
|
|
help="Réécrit les références de tests/golden au lieu de les comparer.",
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def regen_golden(request) -> bool:
|
|
"""Vrai quand la suite est lancée avec --regen-golden."""
|
|
return request.config.getoption("--regen-golden")
|
|
|
|
|
|
@pytest.fixture
|
|
def db_session(tmp_path, monkeypatch):
|
|
"""Session SQLAlchemy sur une base SQLite temporaire et isolée.
|
|
|
|
Configure aussi PLESNA_DB_PATH / PLESNA_STORAGE_PATH pour que les helpers
|
|
qui lisent l'environnement (sql_executor, storage) ciblent le temp dir.
|
|
"""
|
|
db_path = tmp_path / "test.sqlite"
|
|
monkeypatch.setenv("PLESNA_DB_PATH", str(db_path))
|
|
monkeypatch.setenv("PLESNA_STORAGE_PATH", str(tmp_path / "documents"))
|
|
|
|
connection.reset_connection()
|
|
connection.init_db(db_path)
|
|
|
|
SessionLocal = connection.get_session_factory()
|
|
session = SessionLocal()
|
|
try:
|
|
yield session
|
|
finally:
|
|
session.close()
|
|
connection.reset_connection()
|
|
|
|
|
|
@pytest.fixture
|
|
def api_client(db_session):
|
|
"""Client HTTP sur l'application FastAPI, branché sur la base de test.
|
|
|
|
La dépendance `get_session` est surchargée pour partager la session du test :
|
|
les données créées dans le test sont visibles par les endpoints, sans passer
|
|
par la base réelle de l'utilisateur.
|
|
"""
|
|
from fastapi.testclient import TestClient
|
|
|
|
from plesna_gerance.api.app import app
|
|
from plesna_gerance.database import get_session
|
|
|
|
app.dependency_overrides[get_session] = lambda: db_session
|
|
try:
|
|
with TestClient(app) as client:
|
|
yield client
|
|
finally:
|
|
app.dependency_overrides.clear()
|
|
|
|
|
|
@pytest.fixture
|
|
def sample_data():
|
|
"""Données extraites minimales mais complètes pour save_document."""
|
|
return {
|
|
"metadata": {
|
|
"document": {
|
|
"reference": "REF001",
|
|
"date": "2024-01-15",
|
|
"type": "COMPTE RENDU DE GESTION",
|
|
},
|
|
"immeuble": {
|
|
"code": "IMM1",
|
|
"adresse": "4 RUE SERVIENT",
|
|
"ville": "LYON",
|
|
"code_postal": "69003",
|
|
},
|
|
"editeur": {"nom": "ORALIA", "siret": "12345678901234"},
|
|
"solde": {
|
|
"montant": 100.0,
|
|
"type": "crediteur",
|
|
"date_arrete": "2024-01-15",
|
|
},
|
|
},
|
|
"situation_locataires": [
|
|
{
|
|
"lot": {"numero": "01", "type": "Appartement"},
|
|
"locataire": {"nom": "DUPONT"},
|
|
"lignes": [
|
|
{
|
|
"type": "loyer",
|
|
"periode": {"debut": "2024-01-01", "fin": "2024-01-31"},
|
|
"loyers": 500.0,
|
|
"total": 500.0,
|
|
"regles": 500.0,
|
|
"impayes": 0.0,
|
|
}
|
|
],
|
|
}
|
|
],
|
|
"recapitulatif_operations": [
|
|
{
|
|
"categorie": "DEPENSES_LOCATIVES",
|
|
"sous_categorie": "Nettoyage",
|
|
"fournisseur": "ACME",
|
|
"description": "Nettoyage immeuble",
|
|
"montants": {"debit": 50.0},
|
|
}
|
|
],
|
|
}
|