# État courant du projet VIIZIA

> **Snapshot daté 2026-05-29.** Document vivant à mettre à jour à chaque jalon. Il fait office de synthèse « ce qui est fait / ce qui reste à faire » accessible en un coup d'œil sans relire l'historique Git.

## Vue d'ensemble

| Sujet | Valeur |
|---|---|
| Branche par défaut | `develop` |
| Branche prod | `master` (encore au commit initial, pas de release) |
| Stack | Symfony 7.4 · PHP 8.3 · MySQL 8 · Bulma 1 · Font Awesome 6 · Tabler Icons |
| Design | « SaaS Startup » (violet primaire, Plus Jakarta Sans, top band gradient) |
| Hébergement code | Bitbucket : https://bitbucket.org/neoloop/viizia |

## Ce qui est livré

### Setup & infrastructure
- ✅ Squelette Symfony 7.4 avec Twig, Doctrine, Validator, Security, Form, Intl
- ✅ Docker Compose : nginx, php-fpm 8.3, mysql 8, phpMyAdmin, MailHog (ports 8180-8184)
- ✅ Variables d'env `.env` + `.env.docker.example` + `.env.local`
- ✅ AssetMapper non activé (assets servis depuis `public/` directement via CDN pour CSS/JS externes)
- ✅ Repo Bitbucket configuré (workspace `neoloop`, slug `viizia`, auth via API token)

### Modèle de données (Doctrine)
- ✅ Entités noyau : `Organization`, `User`, `Membership`, `MembershipBankAccount`
- ✅ Entités métier : `BankAccount`, `Category`, `Tag`, `Transaction`, `TransactionTag`, `ImportJob`
- ✅ Enums : `OrganizationStatus`, `MembershipRole`, `MembershipStatus`, `AccessMode`, `BankAccountType`, `CategoryKind`, `TransactionDirection`, `TransactionType`, `ImportSourceType`, `ImportStatus`
- ✅ Validators Symfony custom : `Luhn` (SIREN/SIRET réutilisable), `UniqueOrganizationSlug`, `UniqueUserEmail`
- ✅ Règle métier appliquée serveur : ADMIN ⇒ `access_mode = all_accounts` (PrePersist/PreUpdate)
- ✅ Anti-doublon transaction par hash SHA-256 (bookingDate + direction + amount + labelNormalized **+ occurrenceIndex**) avec UNIQUE(bank_account_id, hash)
- ✅ Recalcul automatique du solde courant d'un compte via `BankAccountBalanceCalculator` après chaque add/edit/delete
- ✅ Seed automatique : 9 catégories + 8 tags par défaut à la création d'une organisation (`OrganizationDefaultsSeeder`)
- ✅ Toutes les migrations appliquées : `Version20260510165129` (entités noyau), `Version20260511192347` (BankAccount + FK), `Version20260512075155` (Transaction/Category/Tag), `Version20260512104925` (direction sur transaction), `Version20260512152053` (ImportJob), `Version20260514135431` (`transaction.occurrence_index` + recalcul des hash)

### Authentification & sécurité
- ✅ Symfony Security : `User implements UserInterface, PasswordAuthenticatedUserInterface`
- ✅ Hashage password : **argon2id** (cf. spec)
- ✅ Provider Doctrine sur `email`
- ✅ Firewall `main` avec `form_login` + `logout` + `remember_me 30 jours`
- ✅ CSRF actif (login + suppression d'org/membership)
- ✅ Access control : `/login`, `/logout`, `/forgot-password`, `/reset-password` publics · routes org-scoped `/admin/organizations/{id}/edit`, memberships, bank-accounts, properties… accessibles aux `ROLE_USER` + voter `ORG_ADMIN` · routes globales (`/admin/organizations`, `/admin/rental-platforms`, `/admin/permissions`) réservées au `ROLE_SUPERADMIN` · imports/transactions/tags selon droits délégués
- ✅ **Mot de passe oublié (E02)** — flow complet : demande e-mail, lien signé 1 h, reset, envoi via MailHog (dev) / SMTP (prod). Cf. [`deploiement-production.md`](../guidelines/deploiement-production.md)
- ✅ **Administration org par profil ADMIN** — voter `ORG_ADMIN` (`OrganizationAdminChecker` + `OrganizationAdminVoter`) : un ADMIN actif sur l'org courante accède à la fiche org (paramètres, membres, comptes, biens, plateformes org) via sidebar « Mon organisation » / « Membres » ; imports/transactions/tags automatiques pour ADMIN
- ✅ `LoginListener` met à jour `user.last_login_at` sur LoginSuccessEvent
- ✅ `#[IsGranted]` sur les controllers admin (ceinture + bretelles)

### Écrans livrés
- ✅ **E01** Login — split-screen SaaS Startup (logo + features + testimonial à gauche, form à droite)
- ✅ **E16** Mon compte — 3 onglets (Informations / Sécurité / Préférences)
  - Édition profil (firstName, lastName, email avec unicité, birthDate, birthPlace)
  - Changement de mot de passe (vérification du current côté serveur)
  - Choix du thème Bulma : **clair / sombre / système** (localStorage + anti-flash dans `<head>`)
- ✅ **E17** Backoffice organisations — CRUD complet côté SUPERADMIN ; **édition org partagée** avec l'ADMIN d'organisation (org courante uniquement)
  - Liste avec tableau Bulma (nom, slug, type, SIREN, statut, nb membres, date)
  - Création avec formulaire 3 boxes (Identité / Type / Adresse)
  - Validation Luhn SIREN/SIRET à la soumission (côté serveur, pas Ajax temps réel)
  - Conditional fields entreprise (JS vanilla, toggle is_company)
  - Slug auto-généré depuis le nom (slugify côté client + serveur)
  - Édition avec onglets `Informations` / `Utilisateurs` (fragments URL `#informations`, `#utilisateurs`)
  - Suppression avec confirmation + CSRF token
- ✅ **E14 / E15** Membres d'une organisation — gestion depuis l'onglet « Utilisateurs » de l'edit org (**SUPERADMIN** ou **ADMIN** de l'org courante)
  - Ajout d'un user existant à une org
  - Création d'un nouveau user à la volée (avec mot de passe initial saisi par l'admin)
  - Modification rôle / mode d'accès / statut
  - Révocation (hard delete) avec confirmation
  - Limite : pas de système d'invitation par mail token (à faire pour E15 complet)
- ✅ **E06 / E07** Comptes bancaires (BankAccount) — CRUD via onglet « Comptes » (**SUPERADMIN** ou **ADMIN** org courante)
  - Onglet « Comptes » sur l'edit org
  - Validation IBAN (Symfony Iban mod 97), montants en BIGINT centimes (`MoneyAmountType`), filtre Twig `\|money` pour l'affichage
  - Soft delete (positionne `deleted_at`)
- ✅ **E08 / E09** Transactions — CRUD complet par compte bancaire (sous-onglet du compte)
  - Direction explicite (Entrée/Dépense via radios) + montant toujours positif en BDD, signe géré par `direction`
  - Affectation d'une catégorie + tags multiples (UI checkboxes pill colorées)
  - Recalcul automatique du solde du compte après chaque opération
  - Soft delete
- ✅ **E08 (partie liste globale)** — écran SUPERADMIN **`/admin/transactions`** avec filtres (période incl. « Toutes les périodes » par défaut, compte, catégorie, type, sens, montants, tags ET/OU, recherche libellé, tri, pagination ~50), lien depuis la sidebar + « Vue liste » depuis un compte. Détail : [`docs/evolutions/002-note-impl-liste-transactions-e08.md`](../evolutions/002-note-impl-liste-transactions-e08.md).
- ✅ **E10 / E11** Imports CSV — wizard 2 étapes (upload + mapping) avec exécution synchrone
  - Détection auto séparateur / encodage à l'upload
  - **Mapping inline** : un rôle par colonne dans l'en-tête du tableau d'aperçu + script [`import-mapping.js`](../../public/js/import-mapping.js) (modes date / montant, pas de double rôle, récap des rôles requis)
  - Mapping configurable : séparateur, encodage, format date/nombre, mode **une date** ou **Mois-Année FR + jour** (`two_columns_fr`), montants **signé** ou **débit/crédit**
  - Chaque transaction importée peut porter `import_job_id` (liaison vers `ImportJob`, nullable) — base pour un futur rollback
  - Dédoublonnage par hash en BDD (avec **occurrence** pour les lignes métier identiques le même jour)
  - Rapport KPI (lignes lues / importées / ignorées / durée), message d'erreur en cas d'échec partiel
  - Fichier source conservé dans `/uploads/{org_id}/{job_id}/`
  - Item « Imports » de la sidebar actif pour SUPERADMIN
- ✅ **Import PDF — revue et enrichissement (livré 2026-05-28)**
  - Prévisualisation ligne par ligne, saisie manuelle catégorie/propriété/plateforme
  - Détection automatique de mots-clés via `ImportEnrichmentMemoService` (sous-chaîne, min 2 caractères, sens débit/crédit respecté) ; les règles mémo sont désormais **uniquement manuelles** (longues règles auto-générées supprimées + migration de purge)
  - Bouton « ignorer » + annulation du skip (croix sur le tag « Ignorée »)
  - Navigation `< Imports >` entre tous les fichiers PDF (triés `createdAt DESC, id DESC`, boutons désactivés en bout de liste)
  - Boutons d'action dupliqués en haut **et** en bas de la page de revue
  - Notifications flash avec bouton ✕ fonctionnel
  - Lien « Voir les transactions » filtré sur le compte bancaire du job (`/admin/transactions?accounts[]=X`)
  - Onglet « Transactions » supprimé de la page édition du compte bancaire (redondant avec la liste globale)
- ✅ **E05 Dashboard** (livré 2026-05-28)
  - KPI réels : solde consolidé, recettes, dépenses, résultat net avec δ vs période précédente
  - Donut par catégorie de dépenses
  - **Graphique « Évolution du solde »** (Chart.js) : recettes / dépenses / trésorerie projetée
    - Granularité automatique (≤ 31 j → jours, > 31 j → mois) + **sélecteur manuel Jours / Mois / Années** (param `?chart_granularity=`, compatible avec tous les filtres de période)
    - Axes Y gauche et droit synchronisés (même échelle)
  - Derniers mouvements récents en bas du dashboard

### Layout et design system
- ✅ **Top band gradient** signature (violet → rose → ambre, 4px, fixed top)
- ✅ **Navbar sticky** avec logo VIIZIA + date du mois + dropdown profil
- ✅ **Sidebar sticky** avec org switcher en haut (si org active), sections Principal / Configuration / Administration, user pill en bas (profil d'adhésion affiché : Admin, Manager…)
- ✅ **Configuration** : liens « Mon organisation » et « Membres » actifs pour profil **ADMIN** · Tags & catégories selon droits délégués · item « Comptes » (liste dédiée) encore en stub
- ✅ **Imports** et **Transactions** actifs selon droits délégués (ADMIN : automatique)
- ✅ Responsive mobile basique (sidebar passe en haut < 768px)
- ✅ **Thème clair / sombre / système** appliqué partout (variables Bulma `--bulma-scheme-main`, `--bulma-scheme-main-bis`)
- ✅ Plus Jakarta Sans + Tabler Icons — migration FA → Tabler **partielle** (chrome en Tabler : navbar, sidebar, login, dropdown profil ; écrans admin/account encore en FA)

### Documentation
- ✅ Vision produit, architecture, modèle de données, rôles, import (specs)
- ✅ Guidelines : code, git workflow, docker, UI Bulma, security checklist, troubleshooting, **déploiement production**
- ✅ Mémoire `~/.claude` du projet : 8 notes (project, feedback, references)

### Conventions adoptées
- ✅ **Conventional Commits** (`feat(scope): …`, `fix(scope): …`, `docs(scope): …`, etc.) sur les 23 commits
- ✅ **Préfixe `\` sur les fonctions PHP internes** dans le nouveau code (`\sprintf`, `\count`, `\strlen`…) — optimisation opcache, hint PHP6616 de l'IDE
- ✅ **Pas de branches `feature/*`** : tous les commits vont directement sur `develop` (workflow solo validé)
- ✅ Toutes les actions destructives passent par un **token CSRF** (suppression d'org, révocation de membership, logout)

## Ce qui reste à faire

### Priorité P0 — pré-requis MVP utilisable

**Métier locatif** (validé 2026-05-13, sera fait avant le retour à l'import) :

| Ticket | Sujet | Effort | Dépend de |
|---|---|---|---|
| VIZ-N | **Entité `Property` + CRUD (E20)** dans un onglet de l'edit org | Moyen | — |
| VIZ-N | **Entité globale `RentalPlatform` + CRUD (E21)** côté SUPERADMIN | Moyen | — |
| VIZ-N | **Entité `OrganizationRentalPlatform` + activation (E22)** dans un onglet de l'edit org | Petit | RentalPlatform |
| VIZ-N | **FK `property_id` et `rental_platform_id` sur Transaction** + exposition dans le formulaire | Petit | Property + RentalPlatform |

**Suite déjà identifiée** :

| Ticket | Sujet | Effort | Dépend de |
|---|---|---|---|
| VIZ-N | **Import CSV évolué** : parser tolérant aux montants `1 234,56 €`, mode date 2 colonnes (jour + mois textuel FR), encodage ISO-8859-15, auto-création de Category + Tag + Property + RentalPlatform au mapping | Moyen | Property + RentalPlatform |
| VIZ-N | **Liste / détail transactions (E08, E09) — périmètre spec complet** : filtres bien/plateforme sur la liste, rôles hors SUPERADMIN, page détail `/transactions/{id}`, export CSV, actions masse selon maquette | Gros | Voters + Property / RentalPlatform (métier) |
| ~~VIZ-N~~ | ~~**Dashboard E05** avec KPI réels (solde, recettes, dépenses, graphe)~~ | ~~Moyen~~ | **✅ Livré 2026-05-28** |
| VIZ-N | **Dashboard par bien** (rentabilité locative) | Moyen | Property |
| VIZ-N | **Org switcher fonctionnel (E04)** pour user multi-membership | Petit | — |
| VIZ-N | **Voters Symfony** : `BankAccountVoter`, `TransactionVoter`, `ImportVoter`, `MembershipVoter`, `TagVoter`, `PropertyVoter`, `RentalPlatformVoter` | Moyen | BankAccount / Transaction / Property |

### Priorité P1 — confort et complétude MVP
| Ticket | Sujet | Effort |
|---|---|---|
| VIZ-N | **Mot de passe oublié (E02)** + flow reset par mail | Moyen | **✅ Livré 2026-05-29** |
| VIZ-N | **Rate-limiting** sur le login (5 tentatives / 15 min) | Petit |
| VIZ-N | **Import PDF (E10)** par banque (BNP, SG, CA, LCL, BPCE, Boursorama) | Gros (1 commit par parser) |
| VIZ-N | **Catalogue des tags (E19)** | Petit |
| VIZ-N | **Paramètres org côté ADMIN d'org (E18)** | Petit | **✅ Livré 2026-05-29** (voter `ORG_ADMIN`, sidebar) |
| VIZ-N | **Acceptation d'invitation par token (E03)** | Moyen |
| VIZ-N | **Refonte dashboard SaaS** avec KPI cards style maquette | Petit |
| VIZ-N | **Tests unitaires** : Luhn validator, callbacks d'entités, voters | Petit |
| VIZ-N | **CI Bitbucket Pipelines** (lint + tests) | Petit |

### Dette technique reconnue
- Migration progressive des icônes **Font Awesome → Tabler Icons** dans l'existant (chrome OK, écrans admin/account à passer)
- **Git local en 2.14** (très ancien) — `brew install git` recommandé pour les commandes modernes
- **AssetMapper / build assets** non activé — à voir si on ajoute Stimulus + Vite quand l'app grossit
- **Healthchecks Docker** : présents sur MySQL seulement, à ajouter sur les autres services pour la prod
- **`MembershipBankAccount.bank_account_id`** : colonne `BIGINT NOT NULL` avec index unique `(membership_id, bank_account_id)`, mais **pas de FK** vers `bank_account` (la table n'existe pas encore). FK à ajouter dans la migration `BankAccount`.
- Cosmétique : colonnes `address_line1`/`address_line2` (sans `_` avant le chiffre) — la spec dit `address_line_1` ; trivial à corriger via `#[ORM\Column(name: ...)]` quand on touche l'entité
- **Symfony Messenger configuré mais inutilisé** : `MESSENGER_TRANSPORT_DSN=doctrine://default` est dans `.env`, aucun job async livré pour l'instant. Sera exploité pour les imports CSV/PDF (cf. spec 04).

### Évolutions post-MVP (cf. `docs/evolutions/README.md`)
- Connecteurs bancaires DSP2 (Bridge / Powens)
- Catégorisation automatique (règles + ML)
- Exports comptables (FEC, OFX)
- Multi-devise avec conversion temps réel
- API REST publique + tokens
- App mobile (PWA puis natif)
- White-label par organisation

## Métriques du projet

- **Commits sur `develop`** : 23 (depuis le 2026-05-08, soit 4 jours)
- **Fichiers PHP livrés** : 31 dans `src/` (5 controllers, 5 forms, 6 validators, 4 repositories, 4 entities, 4 enums, 1 listener, 1 Kernel, +1 migration)
- **Templates Twig** : 13
- **Lignes de doc** : ~2 500
- **Couverture de tests** : 0 % (pas encore de tests automatisés)

## Risques et points d'attention

1. **Pas de tests automatisés** — risque de régression à chaque ajout. À adresser dans un ticket dédié dès qu'on a 2-3 services métier livrés.
2. **`access_mode` restreint** — entités et validators en place, mais le filtrage des comptes/transactions par membership reste à implémenter (voters objet).
3. **Sessions** — pas de gestion de session timeout explicite, pas de listing des sessions actives sur le profil. À voir plus tard.
4. **Logs et monitoring** — Monolog par défaut Symfony, pas de configuration adaptée prod. Idem audit log non implémenté (cf. spec entité `audit_log` qui existe en spec mais pas en code).
5. **Bind mounts Docker Desktop Mac** — piège récurrent : après un sleep / rebuild / changement de `compose.yaml`, le container peut voir un dossier vide alors que les fichiers existent côté hôte. Symptôme : modifications invisibles dans le navigateur. **Fix** : `docker compose restart <service>` (cf. [troubleshooting.md](../guidelines/troubleshooting.md) section dédiée).

## Prochain pas validé

**Niveau métier locatif** (validé 2026-05-13) :

1. **Property** — entité bien immobilier tenant-scoped, CRUD via onglet « Biens » sur edit org
2. **RentalPlatform** — référentiel global (cross-tenant), CRUD via `/admin/rental-platforms` (SUPERADMIN)
3. **OrganizationRentalPlatform** — activation par org via onglet « Plateformes » sur edit org
4. **FK sur Transaction** : `property_id` + `rental_platform_id` exposées dans le formulaire transaction

Pourquoi avant l'import évolué : l'utilisateur a des fichiers réels avec une colonne « Appartement » (A001, A101…) et une colonne « Plateforme » (Booking, Airbnb…) qu'on veut mapper proprement. Avoir les bonnes entités d'abord permet à l'import suivant de les exploiter avec auto-création si besoin.

**Suite après ce module** :
- Import CSV évolué (parser tolérant aux montants `1 234,56 €`, mode date 2 colonnes, auto-création Category/Tag/Property/RentalPlatform au mapping)
- Étendre la **liste transactions** au-delà du MVP SUPERADMIN (voters, filtres bien/plateforme, E09 dédié selon spec)
- Dashboard par bien (rentabilité locative)

---

*Dernière mise à jour : 2026-05-29.* **Mot de passe oublié (E02)** : flow complet + doc prod [`deploiement-production.md`](../guidelines/deploiement-production.md). **Administration org ADMIN** : voter `ORG_ADMIN`, sidebar « Mon organisation » / « Membres », droits délégués auto pour ADMIN. **Import** : journal dans [`001-import-reste-a-faire.md`](../evolutions/001-import-reste-a-faire.md) (§ 2026-05-14) — `occurrence_index`, hash, FK `import_job_id`, garde mapping CSV vs PDF, mapping inline + JS, spec [04](../specifications/04-import-donnees.md). **Liste transactions** : [`002-note-impl-liste-transactions-e08.md`](../evolutions/002-note-impl-liste-transactions-e08.md) ; périmètre E08 complet (filtres bien/plateforme, rôles restreints, E09 dédié) reste dans le backlog. **Dashboard E05** : livré (KPI, graphique évolution, granularité jours/mois/années, axes Y synchronisés). **Import PDF** : revue livrée (mots-clés, skip/unskip, navigation, actions bas de page, notifications).
