Separis
Interne · Compta BNCOutil interne de comptabilité pour profession libérale (médecin BNC). Ingère les relevés bancaires PDF de deux banques (CCF et SG), catégorise automatiquement les transactions par apprentissage de règles, réconcilie les relevés carte déférée, gère les amortissements linéaires, et génère la déclaration fiscale 2035 ainsi qu'un export Sage Expert. Interface web Flask + mobile légère accessibles via Tailscale. Stack : Python 3, pdfplumber, SQLite, Flask.
Pourquoi j'ai construit ça
Un médecin libéral (BNC) utilisait le même compte pour tout : honoraires pro et dépenses perso mêlés sur les mêmes relevés. Confier cette compta à un cabinet, c'était accepter une surfacturation — démêler le pro du perso, ligne par ligne, revient au comptable, et ce tri se facture. Il voulait reprendre la main et faire sa compta lui-même. Mais Sage, le logiciel comptable de destination, est tout sauf ergonomique pour ce travail de tri. D'où Separis : tout faire dans une interface rapide — import PDF → catégorisation qui apprend le tri pro/perso → réconciliation carte déférée → amortissements → déclaration 2035 — puis sortir un fichier prêt à importer dans Sage. L'outil est strictement interne — il touche des données fiscales réelles et n'a aucune vocation à être public. Il tourne en local avec accès Tailscale depuis le téléphone, pour valider les transactions le soir même après une consultation.
Décisions techniques structurantes
Toute valeur monétaire est stockée en centimes (`INTEGER`) — jamais en euros flottants. La contrainte est imposée au niveau du schéma SQLite (`test_amounts_are_integer_centimes`) et tenue jusqu'au format de sortie Sage Expert (la fonction `cents_to_fr` formate en virgule française). Aucun arrondi flottant ne peut créer ou détruire un centime dans les écritures en partie double.
La catégorisation est un pipeline ordonné : les règles apprises (priorité 1/2/3) passent avant les règles builtin. La première correspondance sur le libellé normalisé gagne. La normalisation supprime les accents, les références SEPA et normalise les espaces avant la comparaison — ce qui rend les règles résilientes aux variations de format d'une banque à l'autre. Un mot-clé appris sur une décision manuelle remonte automatiquement à la surface pour les prochains imports.
La CCF sépare le relevé compte (avec un CUMUL DÉBITS DIFFÉRÉS par carte) et le relevé carte (les lignes détaillées). Le matcher relie ces deux sources et vérifie que la somme nette (débit − crédit) des lignes carte catégorisées retombe exactement sur le cumul du compte. Un écart de 1 centime produit un statut 'mismatch' persisté en base — il n'est pas ignoré. Cette rigueur m'a permis de détecter plusieurs décalages de dates entre les deux relevés.
Chaque échéance de prêt est décomposée en trois entrées comptables (pret_amortissement → 164000, pret_interets → 661100, pret_assurance → 638000) à partir du tableau d'amortissement importé en PDF. Ces entrées alimentent directement les cases AGAPS 6700 (frais financiers) et 6380 (assurances) sans jamais mélanger capital et charges. Le total des trois doit retomber sur le débit brut de l'échéance.
Sage Expert importe un format texte cp1252 avec des sections #Compte et #LigneComptable. Chaque fichier généré est encodé en cp1252 (erreurs remplacées), son SHA-256 est stocké en base avec le nombre d'écritures, et l'historique des exports est consultable dans l'interface. Les transactions Amazon avec part pro et part perso produisent deux paires d'écritures distinctes (compte de charge + 108000) pour ne jamais contaminer le compte exploitant avec des charges déductibles.
Le challenge non-trivial
Le Plan Comptable Général utilise des codes à 6 chiffres (700000, 636400, 662000…) ; le formulaire AGAPS 2035 a ses propres cases (7000, 6500, 6620…). Le mappage entre les deux n'est pas 1-pour-1 : certains codes internes tombent dans des pseudo-clés (agrégats perso, flux bancaires bruts, entrées de prêt) ; certains codes peuvent alimenter plusieurs cases. La vraie difficulté : ne rien perdre silencieusement. J'ai ajouté un diagnostic de couverture automatique (diagnose_2035) qui liste les codes utilisés en décisions mais non mappés à une case, les transactions pro sans code, et les transactions perso avec un code 6/7XXX suspect. Ce diagnostic s'affiche en temps réel dans l'interface et bloque toute déclaration incomplète.
Leçon retenue
Un pipeline de données fiscales n'a pas le droit à l'ambiguïté : chaque centime doit être traçable de la ligne PDF brute jusqu'à la case AGAPS, en passant par la décision de catégorisation et l'écriture Sage. Dès que j'ai toléré une approximation — 'ce code est probablement correct' — elle s'est retrouvée dans la déclaration finale. La solution n'est pas de redoubler de vigilance manuelle, c'est de rendre les erreurs impossibles à ignorer : montants en centimes entiers dans toute la base, réconciliation exacte au centime, diagnostic de couverture obligatoire avant export. Le pipeline n'est pas une aide à la saisie — c'est une contrainte formelle sur les données.
Fonctionnalités
Extraction structurée des relevés CCF compte, CCF carte déférée et SG compte via pdfplumber : dates réelles, montants en centimes, normalisation des libellés, déduplication SHA-256 à l'import
Moteur de règles à deux niveaux : règles builtin (CARMF, URSSAF, AGAPS, Branchet, Orange…) + règles apprises depuis les décisions manuelles, ordonnées par priorité. Première correspondance gagne, résultat stocké en review_decisions
Moteur de matching qui lie le CUMUL DÉBITS DIFFÉRÉS d'un relevé compte CCF à l'import carte correspondant : vérifie que la somme pro+perso+non catégorisé retombe exactement sur le cumul; statut matched/mismatch en base
Calcul de la dotation annuelle par immobilisation en convention française 30/360 (norme expert-comptable) : prorata jours actifs, VNC en fin d'exercice, garde-fou anti-dépassement de VO — alimenté dans la case CH du 2035
Mapping complet des codes internes (PCG) vers les cases AGAPS du formulaire 2035-A/B : recettes (7000), charges sociales (6500, 6364), redevances (6511), assurances (6380), frais bancaires (6270), déplacements (6250), cotisations syndicales (6660), intérêts emprunt (6700)… avec diagnostic de couverture et points d'attention
Génère un fichier texte cp1252 au format Sage Expert : sections #Compte (codification 6 chiffres) + #LigneComptable (écritures en partie double). Gère les Amazon splits (part pro + part perso en 108000), les entrées de prêt (intérêts, capital, assurance), checksum SHA-256 et historique des exports en base
Interface légère /m optimisée mobile (sans cache) pour la revue transaction par transaction en déplacement; tableau d'audit desktop avec filtres par mois/banque/catégorie, validation bulk, et système de flags 🚩 pour marquer les transactions à revoir
10 tables avec contraintes CHECK sur les enums (banque, type de doc, catégorie, source de décision), clés étrangères activées, montants toujours en centimes entiers, suppression en cascade manuelle pour préserver l'intégrité à la réimportation d'un PDF
87 tests pytest couvrant parseurs CCF/SG (fixtures JSON réelles), catégoriseur, réconciliateur, amortissements, exporter Sage, spliteur de prêt, normalisation des libellés et pipeline e2e import→catégorisation→export
Stack technique
🔒 Outil interne — pas de version publique