L'API et l'interface partagent le même port, et la route attrape-tout qui
sert le SPA passait avant le 404 : n'importe quelle URL /api/* erronée
repartait en index.html avec un 200. Côté client, une faute de frappe
dans une URL ne ressemblait pas à une erreur mais à une réponse vide, et
le vrai motif n'apparaissait qu'en inspectant le corps de la réponse.
Les URL commençant par /api/ sont désormais exclues de l'attrape-tout et
retombent sur un 404 JSON. Les routes de l'interface restent servies par
index.html.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Code sans aucun appelant, retiré : DatabaseService.get_revenus_summary
(annoté list[dict] alors qu'il retournait un dict), get_depenses_summary,
get_tag_by_id et storage.file_exists.
Les tags étaient gérés à deux adresses : /api/tags (lecture, appelée par
trois composants) et /api/config/tags (lecture, création, renommage,
appelée par la seule page de configuration). Tout est regroupé sur
/api/tags, dans le module qui leur est dédié ; config.py ne garde que les
settings et ConfigPage est recâblée. Ces routes n'avaient aucun test :
sept en couvrent maintenant la création, le renommage, l'unicité et les
noms vides.
/api/stats disparaît également : sous-ensemble de /api/dashboard/stats,
il n'était appelé par personne.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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>
La liste des documents montrait la date d'import, figee au premier
enregistrement : c'est la date de la derniere extraction ayant produit les
donnees affichees qui renseigne, surtout apres un balayage de re-extraction. La
colonne `extracted_at` la porte, mise a jour a chaque enregistrement, et la date
d'import reste consultable en infobulle.
Le projet n'ayant pas d'outil de migration (`create_all` laisse intactes les
tables existantes), `init_db` rattrape les colonnes ajoutees apres coup : sans
cela une base deja installee cesserait de fonctionner. Les documents deja
presents recoivent leur date d'import, qui est bien celle de leur extraction.
Une fois la selection appliquee sans echec, le balayage renvoie vers la liste des
documents ; en cas d'echec on reste sur place, les lignes concernees portant leur
message.
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>
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>
Permet de distribuer l'application en executable Windows double-clic,
sans Python/Node/terminal pour l'utilisateur final.
- `desktop.py` : demarre le serveur sur un port libre dans un thread et
ouvre une fenetre native (pywebview). `PLESNA_DEBUG=1` active les
DevTools. Commande `plesna-gerance desktop` (import paresseux).
- `paths.py` : resolution centralisee des chemins selon le contexte
(dev, executable PyInstaller, surcharge par variables d'env). Les
donnees vont dans un dossier utilisateur inscriptible (%APPDATA%),
les ressources dans le bundle. connection.py/storage.py utilisent
`get_data_dir()`, app.py `resource_path()`.
- app.py : type MIME `.mjs` force (modules ES / worker PDF.js sous
Windows).
- Groupes de deps optionnels desktop / desktop-linux / build.
- packaging/ : spec PyInstaller, build_windows.ps1, installeur Inno
Setup. Workflow GitHub build-windows.yml (runner Windows).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Config ruff dans pyproject.toml : règles E/W/F/I/UP/B, whitelist des appels
d'injection FastAPI (Depends/File/Form/Query) pour B008, B904 ignoré
(traduction volontaire des exceptions en réponses HTTP)
- Auto-fixes : tri des imports, suppression d'imports inutilisés, annotations
PEP 604, f-strings sans placeholder, modes open redondants
- Suppression de variables inutilisées (config.reset_setting, parser locataires)
ruff check . : All checks passed ; 61 tests OK
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- sql_executor: remplace le filtre regex fragile par une autorisation SQLite
(set_authorizer) en complément de mode=ro ; rejette les instructions multiples
- uploads: lecture bornée des PDF (helper read_upload_limited, limite 20 Mo,
HTTP 413) branchée sur /extract et /save-with-pdf
- tests: suite pytest (54 tests) couvrant amounts, dates, storage, sql_executor,
uploads et DatabaseService.save_document ; pytest ajouté en dépendance dev
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Empêche le path traversal du catch-all SPA (vérif is_relative_to dist)
- Migre on_event("startup") vers le handler lifespan de FastAPI
- Masque les exceptions internes des erreurs 500 (log au lieu de str(e))
- Remplace datetime.utcnow déprécié par un helper UTC timezone-aware
- Simplifie except (ValueError, Exception) redondant
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a key-value settings table (Setting model) with DB > env > default
resolution chain. Replace hardcoded Ollama constants in ollama_service
with dynamic _get_config() lookups. Add /api/config/ endpoints for
settings CRUD and tag management (create, rename).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Clicking the pencil icon in revenus/depenses tables now passes query
params to identify the source row. The edit page auto-expands the
matching section, scrolls to the element, and briefly highlights it
with a blue ring that fades after 2 seconds.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add new /api/revenus endpoints for rental income data
- GET /summary: KPIs, monthly trends, breakdown by property, top unpaid
- GET /details: detailed rental records with filtering
- GET /by-lot: revenue aggregation by unit
- GET /immeubles: properties list with revenue stats
- Add RevenusPage with full dashboard layout
- 6 KPI cards: total revenue, payments, unpaid, collection rate, active tenants, occupied units
- Monthly evolution chart (rent, payments, unpaid)
- Property breakdown donut chart
- Top unpaid tenants list
- Detailed revenue table with pagination and CSV export
- Add filters panel (property, type, dates, unpaid only, history months)
- Display property address instead of code in all components
- Add navigation link in header (Revenus)
- Add dashboard API endpoints for financial summary, recent revenus, monthly trends, and immeuble shortcuts
- Create new dashboard components: FinancialSummary with sparklines, QuickActions, RecentRevenus, MiniTrendChart, ImmeubleShortcuts
- Refactor HomePage with prominent drag & drop zone and data-driven cards
- Financial cards now show last document data with 6-month trend sparklines in background
- Remove redundant import button, keep single upload zone at top
- Backend Python (uv + click + FastAPI)
- CLI: plesna-gerance extract <pdf> pour extraire les donnees
- CLI: plesna-gerance serve pour lancer le serveur API
- API REST: POST /api/extract pour upload et extraction de PDF
- Parsers modulaires: metadata, locataires, operations
- Utilise pdftotext (poppler-utils) pour l'extraction de texte
- Frontend Vue.js + Tailwind CSS
- Interface split-screen: PDF a gauche, donnees a droite
- Preview PDF avec zoom et navigation pages (pdf.js)
- Visualisation structuree des donnees extraites
- Sections depliables: metadata, locataires, operations
- Drag & drop pour upload de PDF
- Extraction automatique a la selection du fichier