[{"data":1,"prerenderedAt":409},["ShallowReactive",2],{"project-\u002Fprojects\u002Ffinixa-fr":3,"related-\u002Fprojects\u002Ffinixa-fr":388,"legal-link-fr":407},{"id":4,"title":5,"body":6,"description":360,"extension":361,"git":362,"git_soon":365,"meta":366,"navigation":367,"order":368,"path":369,"seo":370,"stack":371,"stem":381,"type":382,"updated":383,"web":384,"__hash__":387},"projects_fr\u002Fprojects\u002Ffinixa.md","Finixa",{"type":7,"value":8,"toc":334},"minimark",[9,17,22,29,33,122,126,131,138,142,157,161,172,176,187,192,196,214,218,221,225,242,246,249,252,256,260,266,268,272,277,279,283,288,290,294,300,302,306,312,314,318,323,325,328],[10,11,12,16],"p",{},[13,14,15],"strong",{},"Ajouter une dépense en moins de cinq secondes depuis son téléphone"," : c'est la contrainte qui a guidé chaque décision de Finixa, du schéma PostgreSQL jusqu'au geste de saisie.",[18,19,21],"h2",{"id":20},"quel-est-le-contexte-de-finixa","Quel est le contexte de Finixa ?",[10,23,24,25,28],{},"Finixa est un MVP de SaaS de finances personnelles centré sur une contrainte UX précise : ",[13,26,27],{},"ajouter une dépense le plus vite possible depuis un mobile",". Le projet couvre le suivi de dépenses et revenus, la gestion de budgets par catégorie et sous-catégorie, le budget par enveloppe (50\u002F30\u002F20), les transactions récurrentes virtuelles, et des statistiques mensuelles avec RPCs PostgreSQL. L'app est packagée pour iOS\u002FAndroid via Capacitor en plus du déploiement web.",[18,30,32],{"id":31},"quelle-stack-technique","Quelle stack technique ?",[34,35,36,48,66,76,82,88,102,112],"ul",{},[37,38,39,42,43,47],"li",{},[13,40,41],{},"Nuxt 4 + Vue 3 (SPA, hash routing)"," — SSR désactivé intentionnellement : le hash routing est nécessaire pour la compatibilité Capacitor, le protocole ",[44,45,46],"code",{},"capacitor:\u002F\u002F"," ne supportant pas le push-state routing.",[37,49,50,53,54,57,58,61,62,65],{},[13,51,52],{},"TypeScript strict"," — Les montants sont signés en base (",[44,55,56],{},"-50"," = dépense, ",[44,59,60],{},"+1000"," = revenu). Le discriminant central est ",[44,63,64],{},"TransactionType ('depense' | 'revenu' | 'epargne')",", utilisé de la base de données jusqu'aux composants.",[37,67,68,71,72,75],{},[13,69,70],{},"Pinia (15 stores)"," — Architecture en trois couches : ",[44,73,74],{},"app\u002Fservices\u002F"," isole les appels Supabase, les stores détiennent l'état et exposent les mutations, les pages et composants consomment les stores. Aucun appel Supabase direct dans les composants.",[37,77,78,81],{},[13,79,80],{},"Supabase (PostgreSQL + Auth + RLS)"," — Row Level Security sur toutes les tables. Les vues calculées (stats, récurrences virtuelles) sont des RPCs PostgreSQL appelées directement depuis les services.",[37,83,84,87],{},[13,85,86],{},"Edge Functions (Deno)"," — Quatre fonctions pour les intégrations externes : ajout rapide de transaction via API key, digest quotidien, push notifications, listing de catégories.",[37,89,90,93,94,97,98,101],{},[13,91,92],{},"SCSS custom (design system par tokens)"," — Pas de Tailwind. Les tokens sont auto-injectés dans chaque composant via ",[44,95,96],{},"additionalData"," dans ",[44,99,100],{},"nuxt.config",". Les composants utilisent uniquement des CSS custom properties sémantiques.",[37,103,104,107,108,111],{},[13,105,106],{},"Capacitor 8"," — Packaging iOS\u002FAndroid, ",[44,109,110],{},"@capacitor\u002Fpreferences"," pour le stockage local sécurisé (PIN, thème, onboarding).",[37,113,114,117,118,121],{},[13,115,116],{},"@nuxtjs\u002Fi18n"," — Localisation complète FR\u002FEN, stratégie ",[44,119,120],{},"no_prefix"," compatible hash routing.",[18,123,125],{"id":124},"points-techniques-notables","Points techniques notables",[127,128,130],"h3",{"id":129},"_1-flux-dajout-de-transaction","1 · Flux d'ajout de transaction",[10,132,133,134,137],{},"Du tap sur le bouton jusqu'à la mise à jour réactive de l'interface — la séparation des couches en action. Le composant ",[44,135,136],{},"add.vue"," ne connaît que le store, le store ne connaît que le service, le service ne connaît que Supabase.",[127,139,141],{"id":140},"_2-date-dopération-vs-date-comptable","2 · Date d'opération vs date comptable",[10,143,144,145,148,149,152,153,156],{},"Chaque transaction a une ",[44,146,147],{},"date"," (quand l'opération a lieu) et une ",[44,150,151],{},"accounting_date"," (nullable, quand elle est imputée au budget). Pour les récurrentes avec ",[44,154,155],{},"accounting_offset: 'next_month'",", la date comptable est fixée au 1er du mois suivant lors de la matérialisation. Cas d'usage concret : un abonnement prélevé le 28 décembre mais comptabilisé sur le budget de janvier.",[127,158,160],{"id":159},"_3-versioning-des-budgets-de-sous-catégories","3 · Versioning des budgets de sous-catégories",[10,162,163,164,167,168,171],{},"Modifier un budget ne réécrit pas l'historique : une nouvelle ligne est créée avec ",[44,165,166],{},"valid_from",", l'ancienne est fermée avec ",[44,169,170],{},"valid_to",". Les requêtes de stats utilisent la date pour trouver le budget en vigueur à cette époque — les statistiques de janvier restent exactes même si le budget a changé en mai.",[127,173,175],{"id":174},"_4-récurrences-virtuelles-et-matérialisation-lazy","4 · Récurrences virtuelles et matérialisation lazy",[10,177,178,179,182,183,186],{},"Les transactions récurrentes ne sont pas persistées en base par défaut. À chaque chargement, ",[44,180,181],{},"getDatesInRange()"," calcule les occurrences sur la période et ",[44,184,185],{},"getOccurrenceKey()"," génère un identifiant stable. Seules les occurrences non encore enregistrées sont affichées comme \"virtuelles\". La persistance n'a lieu que lorsque l'utilisateur coche une occurrence.",[10,188,189,191],{},[44,190,181],{}," gère le clamping des jours-du-mois (ex. mensuel le 31 → 28 fév.) et tous les cas limites de fréquences hebdomadaires, trimestrielles et annuelles.",[127,193,195],{"id":194},"_5-rpcs-postgresql-pour-toutes-les-agrégations","5 · RPCs PostgreSQL pour toutes les agrégations",[10,197,198,199,202,203,202,206,209,210,213],{},"Aucune logique de stats côté front. Toutes les vues calculées sont des fonctions SQL : ",[44,200,201],{},"get_subcategory_stats",", ",[44,204,205],{},"get_variable_daily_remaining",[44,207,208],{},"recurring_virtual_for_month",", etc. Appelées via ",[44,211,212],{},"supabase.rpc()"," depuis les services.",[127,215,217],{"id":216},"_6-système-dapi-key-pour-intégrations-externes","6 · Système d'API key pour intégrations externes",[10,219,220],{},"Les Edge Functions Deno exposent des endpoints authentifiés par SHA-256 d'une clé générée par l'utilisateur. Permet d'ajouter une transaction depuis un raccourci iOS Shortcuts sans exposer les credentials Supabase.",[18,222,224],{"id":223},"design-system-scss","Design system SCSS",[10,226,227,228,231,232,235,236,97,238,241],{},"Les tokens sont définis en deux couches : palette primitive (jamais utilisée directement dans les composants) et propriétés CSS sémantiques (seules utilisées dans les composants). Le fichier de tokens est ",[13,229,230],{},"auto-injecté"," dans chaque ",[44,233,234],{},"\u003Cstyle lang=\"scss\">"," via ",[44,237,96],{},[44,239,240],{},"nuxt.config.ts"," — aucun import manuel nécessaire.",[18,243,245],{"id":244},"ce-que-jai-appris-apporté","Ce que j'ai appris \u002F apporté",[10,247,248],{},"La partie la plus complexe a été la logique de récurrences : calculer des occurrences virtuelles stables sur n'importe quelle fenêtre temporelle, avec clamping des jours (ex. mensuel le 31 → 28 fév.), tout en gérant la désynchronisation entre date d'opération et date comptable. Le versioning des budgets en base a nécessité de repenser le schéma pour que les requêtes de stats soient déterministes quelle que soit la période interrogée.",[250,251],"hr",{},[18,253,255],{"id":254},"schémas","Schémas",[127,257,259],{"id":258},"architecture-globale","Architecture globale",[10,261,262],{},[263,264],"img",{"alt":259,"src":265},"\u002Fdiagrams\u002Ffinixa\u002F01-architecture.svg",[250,267],{},[127,269,271],{"id":270},"flux-dajout-de-transaction","Flux d'ajout de transaction",[10,273,274],{},[263,275],{"alt":271,"src":276},"\u002Fdiagrams\u002Ffinixa\u002F02-add-transaction-flow.svg",[250,278],{},[127,280,282],{"id":281},"date-dopération-vs-date-comptable","Date d'opération vs date comptable",[10,284,285],{},[263,286],{"alt":282,"src":287},"\u002Fdiagrams\u002Ffinixa\u002F03-accounting-date.svg",[250,289],{},[127,291,293],{"id":292},"versioning-des-budgets","Versioning des budgets",[10,295,296],{},[263,297],{"alt":298,"src":299},"Versioning des budgets de sous-catégories","\u002Fdiagrams\u002Ffinixa\u002F04-budget-versioning.svg",[250,301],{},[127,303,305],{"id":304},"récurrences-virtuelles","Récurrences virtuelles",[10,307,308],{},[263,309],{"alt":310,"src":311},"Récurrences virtuelles et matérialisation lazy","\u002Fdiagrams\u002Ffinixa\u002F05-virtual-recurring.svg",[250,313],{},[127,315,317],{"id":316},"flux-dauthentification-api-key","Flux d'authentification API key",[10,319,320],{},[263,321],{"alt":317,"src":322},"\u002Fdiagrams\u002Ffinixa\u002F06-api-key-flow.svg",[250,324],{},[127,326,224],{"id":327},"design-system-scss-1",[10,329,330],{},[263,331],{"alt":332,"src":333},"Design system SCSS — tokens et injection","\u002Fdiagrams\u002Ffinixa\u002F07-scss-design-system.svg",{"title":335,"searchDepth":336,"depth":336,"links":337},"",2,[338,339,340,349,350,351],{"id":20,"depth":336,"text":21},{"id":31,"depth":336,"text":32},{"id":124,"depth":336,"text":125,"children":341},[342,344,345,346,347,348],{"id":129,"depth":343,"text":130},3,{"id":140,"depth":343,"text":141},{"id":159,"depth":343,"text":160},{"id":174,"depth":343,"text":175},{"id":194,"depth":343,"text":195},{"id":216,"depth":343,"text":217},{"id":223,"depth":336,"text":224},{"id":244,"depth":336,"text":245},{"id":254,"depth":336,"text":255,"children":352},[353,354,355,356,357,358,359],{"id":258,"depth":343,"text":259},{"id":270,"depth":343,"text":271},{"id":281,"depth":343,"text":282},{"id":292,"depth":343,"text":293},{"id":304,"depth":343,"text":305},{"id":316,"depth":343,"text":317},{"id":327,"depth":343,"text":224},"Application web mobile-first de gestion de finances personnelles — suivi de dépenses, budgets par enveloppe et transactions récurrentes.","md",[363,364],"Répertoire Git","https:\u002F\u002Fgithub.com\u002FOwenLB\u002Ffinixa",false,{},true,1,"\u002Fprojects\u002Ffinixa",{"title":5,"description":360},[372,373,374,375,376,377,378,379,380],"Nuxt 4","Vue 3","TypeScript","Pinia","Supabase","PostgreSQL","SCSS","Capacitor","Deno (Edge Functions)","projects\u002Ffinixa","App web & mobile","2026-07-17",[385,386],"Voir l'app","https:\u002F\u002Ffinixa.net\u002F","UBFHKEV-prvWaRE-pQ2yeGYxuWGLMe6AGcM31pIFee8",[389,393,397,402],{"title":390,"type":391,"path":392,"order":336},"Peliculle","App iOS","\u002Fprojects\u002Fpeliculle",{"title":394,"type":395,"path":396,"order":343},"Scan Auto","App web","\u002Fprojects\u002Fscanauto",{"title":398,"type":399,"path":400,"order":401},"Portfolio Développeur","Site","\u002Fprojects\u002Fportfolio-dev",4,{"title":403,"type":404,"path":405,"order":406},"Portfolio Photo","Site full-stack","\u002Fprojects\u002Fportfolio-photo",5,{"title":408},"Mentions légales",1784470460484]