L'accueil ne decrivait qu'un document : les quatre tuiles lisaient le
dernier compte rendu importe, sans rien dire de ce que contient la base.
Il resume desormais une annee civile, avec un selecteur des annees
presentes. L'annee par defaut est celle du dernier compte rendu et non
l'annee en cours : la fenetre etait calee sur `date.today()`, si bien que
sans import depuis quelques mois l'ecran se serait vide alors que la base
est pleine.
Ce que les tuiles annoncent :
- recettes facturees, report exclu, avec l'encaisse et le recouvrement ;
- depenses, debit et credits recus ;
- net reverse, rapproche des soldes annonces par les comptes rendus ;
- restant du, date, car un stock ne se cumule pas d'un mois sur l'autre.
Le net reverse vaut « encaisse - debit + credit », et cette egalite tombe
au centime sur le solde extrait du PDF pour avril, mai et juin 2026. Elle
s'ecarte de 288,52 EUR en fevrier et de 1 188,03 EUR en mars, les deux mois
dont l'extraction a par ailleurs des defauts. L'ecart est donc affiche et
jamais lisse : c'est le seul controle de bout en bout dont on dispose sur
la qualite d'une extraction. Les regles vivent dans services/tresorerie.py,
a cote de celles des revenus.
Le lot compte est celui qui a ete facture dans l'annee : la table `lots`
retient deux ecritures par lot (« 0001 » et « 01 »), sequelle de la
normalisation des numeros, et en annoncerait 40 la ou il y en a 20.
Le graphique passe du vert et rouge au bleu et ambre : sous deuteranopie,
green-400 et red-400 ne se separent qu'a un delta E de 7,9, sous le seuil
de 8. La paire retenue tient a 30,2.
`/recent-revenus` disparait — c'etait le dernier agregat a compter les
lignes de report — et `/immeubles-shortcuts` est borne a la meme annee que
le reste de l'ecran, deux perimetres sur un ecran donnant deux montants
sans que rien ne les distingue.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Refuser une modification, c'est reenvoyer la valeur actuelle du document. Mais
l'enregistrement ramene les donnees a une forme canonique (aujourd'hui les
numeros de lot) : sur ces champs, le refus n'est pas conserve. L'interface le
laissait croire.
Plutot que de traiter le cas des lots en dur, le serveur devient seul juge de ce
qu'il stocke et l'annonce : `canonicalize_extraction` regroupe les reecritures
faites a la persistance, `save_document` et l'apercu passent par elle, et
/api/documents/{id}/re-extract renvoie `previous_canonical` a cote de
`previous_data`. Le front compare ces deux jeux et signale, sous chaque case
decochee, la valeur que l'enregistrement reecrira — sans connaitre aucune regle.
Une transformation ajoutee plus tard remontera donc toute seule dans l'interface.
Toutes les differences restent visibles et toutes les cases actives : voir ce
qui change et pouvoir se prononcer prime, quitte a etre prevenu que ce champ-la
reviendra reecrit.
Supprime au passage frontend/src/utils/lots.js, qui dupliquait la regle de
normalisation cote client.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Le balayage n'imposait que le tout ou rien par document. Chaque modification a
desormais sa case : un champ de metadonnee, le nom ou le type d'un locataire,
une operation ajoutee ou supprimee. Les champs textuels retenus sont en outre
modifiables a la main, pour les cas ou ni l'ancienne ni la nouvelle valeur ne
convient. Tout reste coche par defaut : le cas courant tient toujours en un clic.
Les montants et lignes d'un locataire forment un seul bloc : les retenir
separement produirait un total ne correspondant plus a ses lignes, alors que ces
chiffres alimentent les revenus et les depenses.
Le document enregistre est construit par fusion (mergeExtraction) : on part des
donnees en base et on n'y applique que ce qui est retenu. Le diff porte donc
maintenant de quoi rejouer chaque changement (identifiant, chemin, valeurs
brutes) et l'index d'origine des operations supprimees, pour les remettre a leur
place si on refuse leur disparition. Les tags suivent la liste d'operations
finale, y compris ceux d'une operation restauree.
Une reecriture de forme d'un numero de lot ("0001" -> "01") est signalee comme
imposee et sa case verrouillee : le backend normalise tout enregistrement, une
case sans effet aurait laisse croire l'inverse.
Ajoute vitest : la fusion decide de ce qui est ecrit en base, elle est couverte
par 15 tests.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Nouvelle page /re-extraction, accessible depuis la liste des documents : elle
rejoue l'extraction de chaque PDF stocke, document par document, avec une
progression et une estimation du temps restant (une extraction prend plusieurs
secondes) et un bouton d'arret. Rien n'est ecrit en base pendant le balayage.
Les documents dont l'extraction change sont listes avec le nombre de
differences par section, deja coches ; les inchanges sont replies et les
documents sans PDF ou en erreur isoles. Le detail depliable reutilise
l'affichage de diff de la re-extraction unitaire (extrait dans
ExtractionDiffDetails). Un seul bouton enregistre toute la selection.
Les tags de depenses sont reportes par signature d'operation et non par index :
une operation ajoutee ou retiree par le nouveau parser ne decale plus les
categories. Ce qui n'est pas reapparie recoit une prediction, modifiable dans le
detail avant enregistrement.
L'edition d'un document et l'application du balayage passent par
PUT /api/documents/{id}, qui vise le document par son ID.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
La comparaison portait sur « crediteur » sans accent alors que le parser
ecrit « créditeur » des qu'il a lu la mention dans le PDF : aucun solde
ne passait le test, tous s'affichaient en rouge.
Compare desormais sur une forme normalisee (sans accents ni casse), ce
qui couvre les deux formes presentes en base sans avoir a migrer les
documents deja extraits.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Remplace les parseurs texte+regex (fragiles sur l'alignement en colonnes des
PDF Oralia mal construits) par une extraction géométrique : reconstruction des
lignes visuelles par regroupement vertical tolérant des mots, puis affectation
de chaque valeur à sa colonne via les filets du tableau (pdfplumber find_tables).
Corrections apportées :
- locataires : montant/libellé « divers » dans la bonne colonne (plus le total
cumulé du lot), nom de locataire correct (le logo/en-tête hors filets est
ignoré), lignes multi-période et pages recollées.
- opérations : fournisseur séparé de la description (TOTALENERGIES ≠ DIDIER
NETTOYAGE, PPR ≠ BOUVARD), colonne Déductible remplie, Débit/Crédit distingués,
fournisseur des honoraires reporté sur le bloc, fragments de description
recollés (LATAPY, AUDOUIN).
- code lot : gère « S10 - », « S 17 - » (espace) et « S01 SOLDE » (sans tiret).
Branchés dans extractor.py avec repli sur les anciens parseurs si un tableau n'a
pas de filets détectables. Validés par réconciliation comptable : locataires
124/124 lots, opérations 7/7 PDF et 25/25 catégories, au centime.
Ajoute le bouton « Relancer l'extraction » dans l'écran d'édition : re-extrait
depuis le PDF stocké et met en évidence les différences avec la version
précédente (panneau récapitulatif + anneaux « modifié » sur les cartes). Le diff
des opérations s'aligne par contenu (robuste aux changements d'ordre/nombre).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>