Aller au contenu

Modèle de données

Schéma complet : sql/carbure.sql (MariaDB/MySQL, InnoDB, utf8mb4). Les évolutions d'une base existante sont dans sql/migrations/ et appliquées par Migrator (voir Migrations).

erDiagram
    users ||--o{ devices : "possède"
    users ||--o{ api_tokens : "possède"
    users ||--o{ bank_account : "a ajouté"
    users ||--o{ bank_transaction : "rattachement"
    bank_transaction_category ||--o{ bank_transaction : "catégorise"
    bank_transaction_category ||--o{ bank_transaction_category : "parent"
    bank_transaction_category ||--o{ bank_transaction_category_keyword : "mots-clés"
    bank_transaction_category ||--o{ budget : "budgétée"

    users {
        int id PK
        varchar username UK
        varchar password "bcrypt"
        varchar firstname
        varchar lastname
        varchar email UK
        tinyint is_admin
        varchar language "fr ou en"
        decimal alert_threshold "seuil d'alerte, NULL = désactivé"
        datetime last_login
        tinyint failed_logins
        datetime locked_until "NULL = non bloqué"
    }
    devices {
        int id PK
        int user_id FK
        varchar name
        varchar token UK "token APNs"
        timestamp lastLogin
    }
    api_tokens {
        int id PK
        int user_id FK
        varchar name
        char token_hash UK "SHA-256"
        varchar token_hint
        timestamp created_at
        datetime last_used_at
        datetime expires_at "NULL = sans expiration"
    }
    bank_account {
        int id PK
        varchar bank_name "backend woob"
        varchar account_number
        int user_id FK "qui l'a ajouté"
        datetime last_sync_at
        enum last_sync_status "OK, ERROR"
        varchar last_sync_message
    }
    bank_transaction {
        bigint id PK
        varchar uuid UK "md5(rdate, libellé, montant)"
        timestamp imported
        date date "indexée"
        date rdate "date réelle"
        tinyint type
        varchar label
        tinyint category FK
        decimal amount
        varchar card
        tinyint pointed "vérifiée"
        int user FK
    }
    bank_transaction_category {
        tinyint id PK
        varchar name UK
        tinyint parent_category FK
        enum type "DEBIT, CREDIT, HORS-BUDGET"
        varchar icon
        varchar color
    }
    bank_transaction_category_keyword {
        int id PK
        varchar keyword UK
        tinyint category FK
    }
    budget {
        bigint id PK
        tinyint category FK
        decimal amount
        date date "1er du mois"
    }
    budget_insight {
        int id PK
        varchar name
        varchar color
        varchar icon
        varchar sql "requête avec {month} et {year}"
    }
    settings {
        varchar name PK
        varchar value
    }
    schema_migrations {
        varchar version PK
        timestamp applied_at
    }

Tables

Table Rôle
users Comptes. is_admin donne le profil administrateur ; language la langue du portail ; alert_threshold le montant à partir duquel une nouvelle dépense déclenche une notification (NULL = désactivé) ; last_login la dernière connexion réussie ; failed_logins les échecs de connexion d'affilée et locked_until la fin du blocage (24 h après 5 échecs).
devices Appareils iOS (token APNs), enregistrés à la connexion. Supprimés avec l'utilisateur.
api_tokens Jetons d'accès des agents IA (serveur MCP) : seule l'empreinte SHA-256 (token_hash) est stockée, token_hint (début du jeton) sert à le reconnaître ; expires_at NULL = sans expiration. Supprimés avec l'utilisateur.
bank_account Comptes bancaires suivis par le foyer : account_number@bank_name forme le bankId passé à woob. user_id est l'utilisateur qui a ajouté le compte (« Ajouté par ») ; last_sync_at, last_sync_status, last_sync_message décrivent la dernière synchronisation. Unicité (account_number, bank_name, user_id) ; l'API refuse en plus un compte déjà suivi par le foyer.
bank_transaction Transactions. uuid unique sert au dédoublonnage ; user = premier utilisateur ayant ajouté le compte (MIN(user_id)), sans effet sur la visibilité ; suppression de l'utilisateur interdite tant qu'il a des transactions (ON DELETE RESTRICT) ; index transaction_date sur date.
bank_transaction_category Catégories hiérarchiques (parent_category, 0 = racine). La catégorie 0 « Non catégorisé » (HORS-BUDGET) est créée par l'installation et protégée.
bank_transaction_category_keyword Règles : mots-clés (en majuscules) recherchés dans les libellés pour catégoriser automatiquement.
budget Budget d'une catégorie pour un mois (unique par catégorie et date), en DECIMAL(10,2).
budget_insight Insights : requête SQL renvoyant une colonne amount, avec les marqueurs {month} et {year} remplacés par des entiers ; icon (icône Material, facultative) et color (13 couleurs).
settings Réglages modifiés depuis le portail (mcp_enabled).
schema_migrations Migrations appliquées et leur date ; créée par sql/carbure.sql (version 2026-10-13_base) ou par Migrator. La version du schéma est la dernière migration enregistrée.

Migrations

Les migrations de la version 1.0 (2026-10-03_audit à 2026-10-13_rule_notify : droits administrateur, alertes, utf8mb4, statut de synchronisation, jetons d'API, réglages, dernière connexion, verrouillage, notification des règles) sont intégrées à sql/carbure.sql et ont été retirées du dépôt. Une base créée depuis sql/carbure.sql a la version 2026-10-13_base.

Les prochaines migrations iront dans sql/migrations/ avec leur ligne -- applied-if: <requête>, qui permet de reconnaître une migration déjà appliquée (à la main, ou par sql/carbure.sql). Voir Développement.

Modèle « foyer »

Toutes les données (comptes, transactions, budgets, catégories, insights) sont partagées par les utilisateurs. bank_account.user_id et bank_transaction.user indiquent qui a ajouté le compte synchronisé, sans restreindre la visibilité. Seuls les appareils, les jetons d'accès et les préférences (langue, seuil d'alerte) sont propres à chaque utilisateur.

Limites connues

  • budget_insight stocke du SQL exécuté par le serveur : il est encadré (administrateurs seulement, mots-clés et tables interdits, transaction en lecture seule), mais des indicateurs définis dans le code seraient plus sûrs. Une requête d'insight écrite avec MONTH(date) / YEAR(date) n'utilise pas l'index sur les dates : préférer un intervalle.