POST /api/documents/{id}/re-extract retourne desormais, en plus des donnees
re-extraites, les donnees actuelles et les tags des depenses : un seul appel
suffit pour comparer avant/apres et reporter les tags. L'endpoint devient
synchrone pour que FastAPI l'execute dans un thread, l'extraction bloquant
plusieurs secondes par PDF.
PUT /api/documents/{id} enregistre la nouvelle extraction en visant le
document par son ID (save_document(replace_document_id=...)) : une extraction
qui corrige la reference ou la date met a jour le bon document au lieu d'en
creer un second, et refuse la collision avec un document voisin. Le PDF
stocke est conserve.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
120 lines
4.1 KiB
Python
120 lines
4.1 KiB
Python
"""Tests du remplacement d'un document par son ID (re-extraction validee)."""
|
|
|
|
import copy
|
|
from datetime import date
|
|
|
|
import pytest
|
|
|
|
from plesna_gerance.database import storage
|
|
from plesna_gerance.database.models import Depense, Document, Tag
|
|
from plesna_gerance.database.service import DatabaseService, DuplicateDocumentError
|
|
|
|
|
|
@pytest.fixture
|
|
def pdf_bytes():
|
|
"""Contenu binaire arbitraire : le stockage ne relit jamais le PDF ici."""
|
|
return b"%PDF-1.4 fake"
|
|
|
|
|
|
def test_replace_document_met_a_jour_meme_si_la_reference_change(
|
|
db_session, sample_data
|
|
):
|
|
"""Une re-extraction qui corrige la reference met a jour le meme document.
|
|
|
|
C'est tout l'interet du remplacement par ID : avec un enregistrement par
|
|
(reference, date), ce cas creerait un second document.
|
|
"""
|
|
service = DatabaseService(db_session)
|
|
original = service.save_document(data=sample_data)
|
|
original_id = original.id
|
|
|
|
new_data = copy.deepcopy(sample_data)
|
|
new_data["metadata"]["document"]["reference"] = "REF001-CORRIGEE"
|
|
|
|
updated = service.save_document(data=new_data, replace_document_id=original_id)
|
|
|
|
assert db_session.query(Document).count() == 1
|
|
assert updated.reference == "REF001-CORRIGEE"
|
|
assert updated.date == date(2024, 1, 15)
|
|
|
|
|
|
def test_replace_document_conserve_le_pdf_et_reecrit_le_json(
|
|
db_session, sample_data, pdf_bytes
|
|
):
|
|
service = DatabaseService(db_session)
|
|
original = service.save_document(
|
|
data=sample_data, source_file="cr.pdf", pdf_content=pdf_bytes
|
|
)
|
|
pdf_path, json_path = original.pdf_path, original.json_path
|
|
assert pdf_path and json_path
|
|
|
|
new_data = copy.deepcopy(sample_data)
|
|
new_data["situation_locataires"][0]["locataire"]["nom"] = "MARTIN"
|
|
|
|
# Re-enregistrement sans PDF : celui deja stocke doit etre conserve.
|
|
updated = service.save_document(
|
|
data=new_data, source_file="cr.pdf", replace_document_id=original.id
|
|
)
|
|
|
|
assert updated.pdf_path == pdf_path
|
|
assert updated.json_path == json_path
|
|
assert storage.read_pdf(pdf_path) == pdf_bytes
|
|
stored = storage.read_json(json_path)
|
|
assert stored["situation_locataires"][0]["locataire"]["nom"] == "MARTIN"
|
|
|
|
|
|
def test_replace_document_refuse_une_collision_avec_un_autre_document(
|
|
db_session, sample_data
|
|
):
|
|
"""La nouvelle extraction ne doit pas ecraser un document voisin."""
|
|
service = DatabaseService(db_session)
|
|
premier = service.save_document(data=sample_data)
|
|
|
|
autre_data = copy.deepcopy(sample_data)
|
|
autre_data["metadata"]["document"]["date"] = "2024-02-15"
|
|
service.save_document(data=autre_data)
|
|
|
|
# Le premier document re-extrait porterait la (reference, date) du second.
|
|
collision_data = copy.deepcopy(autre_data)
|
|
with pytest.raises(DuplicateDocumentError):
|
|
service.save_document(data=collision_data, replace_document_id=premier.id)
|
|
|
|
assert db_session.query(Document).count() == 2
|
|
|
|
|
|
def test_replace_document_inconnu_leve_value_error(db_session, sample_data):
|
|
service = DatabaseService(db_session)
|
|
with pytest.raises(ValueError):
|
|
service.save_document(data=sample_data, replace_document_id=4242)
|
|
|
|
|
|
def test_get_depenses_tags_puis_report_sur_la_nouvelle_extraction(
|
|
db_session, sample_data
|
|
):
|
|
"""Les tags actuels sont indexes comme les operations, et reportables."""
|
|
service = DatabaseService(db_session)
|
|
tag = Tag(nom="ENTRETIEN")
|
|
db_session.add(tag)
|
|
db_session.flush()
|
|
|
|
original = service.save_document(
|
|
data=sample_data, depenses_tags=[{"index": 0, "tag_id": tag.id}]
|
|
)
|
|
|
|
tags = service.get_depenses_tags(original.id)
|
|
assert tags == [{"index": 0, "tag_id": tag.id, "tag_nom": "ENTRETIEN"}]
|
|
|
|
# Report sur une nouvelle extraction du meme document.
|
|
new_data = copy.deepcopy(sample_data)
|
|
new_data["recapitulatif_operations"][0]["montants"]["debit"] = 75.0
|
|
updated = service.save_document(
|
|
data=new_data,
|
|
depenses_tags=[{"index": t["index"], "tag_id": t["tag_id"]} for t in tags],
|
|
replace_document_id=original.id,
|
|
)
|
|
|
|
depenses = db_session.query(Depense).filter_by(document_id=updated.id).all()
|
|
assert len(depenses) == 1
|
|
assert depenses[0].debit == 75.0
|
|
assert depenses[0].tag_id == tag.id
|