# Starseed

CRM/ERP en architecture **modular monolith DDD** — Symfony 8 (API Platform 4) + Nuxt 4.

Le backend est la **source de vérité unique** : il décide des modules actifs et de
l'organisation de la sidebar. Le frontend scanne `frontend/modules/*/` comme layers
Nuxt et consomme l'API pour la navigation.

---

## Sommaire

- [Stack](#stack)
- [Prérequis](#prérequis)
- [Démarrage rapide](#démarrage-rapide)
- [Dev local : avec ou sans données de seed](#dev-local--avec-ou-sans-données-de-seed)
- [Comptes (dev)](#comptes-dev)
- [Bases de données : dev et test](#bases-de-données--dev-et-test)
- [Tests](#tests)
- [Déploiement : seed RBAC en recette / prod](#déploiement--seed-rbac-en-recette--prod)
- [Commandes make](#commandes-make)
- [Architecture](#architecture)
- [Structure du dépôt](#structure-du-dépôt)
- [CI/CD](#cicd)
- [Conventions](#conventions)

---

## Stack

- **Backend** : PHP 8.4, Symfony 8, API Platform 4, Doctrine ORM, PostgreSQL 16
- **Frontend** : Nuxt 4 (SPA, SSR off), Vue 3, Pinia, Tailwind CSS, @malio/layer-ui, @nuxtjs/i18n
- **Auth** : JWT HTTP-only cookie (Lexik), login sur `/login_check`
- **Infra** : Docker Compose (dev + prod multi-stage)
- **CI/CD** : Gitea Actions (auto-tag + build Docker)

| Service       | Port |
|---------------|------|
| API (Nginx)   | 8083 |
| Frontend dev  | 3004 |
| PostgreSQL    | 5437 |

---

## Prérequis

- Docker + Docker Compose
- `make`
- `nvm` (la version de Node est fixée par `.nvmrc`, voir `make node-use`)

Toutes les commandes `make` s'exécutent dans le container PHP (`php-starseed-fpm`) ;
rien n'est requis sur l'hôte hormis Docker — **sauf les tests E2E**, qui tournent sur
l'hôte (navigateur réel, voir [Tests](#tests)).

---

## Démarrage rapide

```bash
make start      # Démarre les containers Docker
make install    # Composer + clés JWT + migrations + permissions + BDD de test
make dev-nuxt   # Serveur Nuxt avec hot reload (http://localhost:3004)
```

`make install` prépare une base de dev **vierge** (schéma + RBAC structurel, sans
données de démo) et la base de **test**. Pour obtenir des comptes et des données de
démo prêtes à l'emploi, lis la section suivante.

> Override local possible : `make` lit `infra/dev/.env.docker`, surchargé par
> `infra/dev/.env.docker.local` s'il existe (créé automatiquement par `make env-init`).

---

## Dev local : avec ou sans données de seed

Le projet distingue deux états de base de données de dev. Les **fixtures Doctrine sont
en `require-dev`** : elles n'existent qu'en dev, jamais dans le build de prod.

### Sans données de seed (base vierge)

C'est ce que produit `make install`. La base contient :

- le **schéma** complet (toutes les migrations jouées) ;
- les **rôles système** `admin` / `user` (seedés en SQL par la migration RBAC) ;
- le **catalogue de permissions** synchronisé (`app:sync-permissions`).

Mais **aucun compte utilisateur ni donnée métier**. Pour pouvoir te connecter,
crée toi-même un compte :

```bash
make shell
php bin/console app:create-user admin monMotDePasse --admin   # compte ROLE_ADMIN
```

Optionnel — provisionner les **rôles métier** (bureau / compta / commerciale / usine
+ matrice RBAC § 2.7) sans comptes de démo :

```bash
php bin/console app:seed-rbac
```

Cet état est utile pour repartir d'une base propre, reproduire un bug sur données
minimales, ou tester un parcours d'onboarding réel.

### Avec données de seed (base de démo)

`make db-reset` (ou `make fixtures` après un `make install`) recharge la base de dev
avec un jeu complet de données de démonstration, **idempotent** :

```bash
make db-reset   # ATTENTION : drop + recrée la base de dev, puis charge tout le seed
```

Ce que les fixtures posent :

- **3 utilisateurs système** : `admin` (ROLE_ADMIN), `alice`, `bob` (ROLE_USER),
  rattachés à des sites distincts ;
- **3 sites** : Chatellerault, Saint-Jean, Pommevic ;
- **les comptes de démo RBAC métier** (`bureau`, `compta`, `commerciale`, `usine`,
  mot de passe `demo`) avec la matrice § 2.7 attachée ;
- les **référentiels et données métier** des modules (catégories, clients de démo,
  référentiels comptables…).

Toutes les fixtures sont rejouables sans effet de bord (lookup par clé naturelle,
aucun doublon).

> Différence avec `make install` : `install` ne charge **pas** les fixtures sur la base
> de dev (il alimente uniquement la base de test). Utilise `make db-reset` ou
> `make fixtures` quand tu veux des données de démo en dev.

---

## Comptes (dev)

Disponibles uniquement après `make db-reset` / `make fixtures` (état « avec seed ») :

| Username      | Password | Rôle       | RBAC métier                                                   |
|---------------|----------|------------|---------------------------------------------------------------|
| `admin`       | `admin`  | ROLE_ADMIN | bypass complet (`is_admin`)                                   |
| `alice`       | `alice`  | ROLE_USER  | —                                                             |
| `bob`         | `bob`    | ROLE_USER  | —                                                             |
| `bureau`      | `demo`   | ROLE_USER  | clients : view + manage                                       |
| `compta`      | `demo`   | ROLE_USER  | clients : view + accounting.view / manage                     |
| `commerciale` | `demo`   | ROLE_USER  | clients : view + manage                                       |
| `usine`       | `demo`   | ROLE_USER  | aucun accès clients                                           |

---

## Bases de données : dev et test

Deux bases distinctes vivent dans le **même container PostgreSQL** (port 5437) :

| Base       | Environnement | Construite par                       | Usage                          |
|------------|---------------|--------------------------------------|--------------------------------|
| `<db>`      | `dev`         | `make install` / `make db-reset`     | développement manuel, dev-nuxt |
| `<db>_test` | `test`        | `make test-db-setup`                 | PHPUnit (jamais touchée à la main) |

Le suffixe `_test` est appliqué **automatiquement** par Doctrine quand `APP_ENV=test`
(config `when@test` dans `config/packages/doctrine.yaml`). La base de test est donc
totalement **isolée** de la base de dev : lancer `make test` ne touche jamais tes
données de dev.

`make test-db-setup` fait davantage que jouer les migrations, car certaines structures
ne sont pas portées par des migrations « métier » :

1. `doctrine:migrations:migrate` — schéma métier réel ;
2. `doctrine:schema:update --force` — crée les tables mappées en `when@test`
   uniquement (entités de test) ;
3. `app:apply-column-comments` — réapplique les `COMMENT ON COLUMN` que
   `schema:update` efface sur les tables managées par l'ORM (garde-fou
   `ColumnsHaveSqlCommentTest`) ;
4. `fixtures:load` → `sync-permissions` → `seed-rbac` — dans cet ordre précis
   (le purger des fixtures vide la table `permission`, donc la sync passe après) ;
5. recréation des **index partiels uniques** (`LOWER(...) WHERE ...`) non exprimables
   en attributs ORM, indispensables aux tests d'unicité (RG-1.07, RG-1.16, RG-1.03/1.29).

`make install` et `make db-reset` appellent déjà `test-db-setup` : tu n'as à le
relancer à la main que si la base de test diverge (nouvelle migration, nouvelle
permission) sans vouloir reseed la base de dev.

---

## Tests

| Suite             | Commande         | Outil                | Où                                |
|-------------------|------------------|----------------------|-----------------------------------|
| Back              | `make test`      | PHPUnit              | container PHP, base `<db>_test`   |
| Front unitaire    | `make nuxt-test` | Vitest (happy-dom)   | container Node, < 30 s            |
| Front E2E         | `make test-e2e`  | Playwright           | **hôte** (navigateur réel requis) |
| Tout (back+front) | `make test-all`  | PHPUnit + Vitest     | —                                 |

### Tests back (PHPUnit)

```bash
make test                       # toute la suite
make test FILES=tests/Module/Commercial   # un dossier / fichier ciblé
```

PHPUnit force `APP_ENV=test` (`phpunit.dist.xml`) : les tests tournent **toujours**
sur la base `<db>_test`, jamais sur la base de dev. Prérequis : que la base de test
existe — c'est le cas après `make install`. Si elle a divergé, rejoue
`make test-db-setup` (cf. [Bases de données](#bases-de-données--dev-et-test)).

### Tests front unitaires (Vitest)

```bash
make nuxt-test   # composables, utils, stores — rapide et stable
```

C'est la **place par défaut** pour étendre la couverture (cf. règle d'or ci-dessous).

### Tests E2E (Playwright)

Suite volontairement minimaliste (login + matrice RBAC sidebar). **Règle d'or : un
nouveau test E2E ne s'ajoute que si un bug critique est passé en prod** — sinon,
préférer un test Vitest ou étendre un persona existant.

Bootstrap (une fois par poste) :

```bash
make install-e2e-deps   # télécharge Chromium + libs système (apt/dnf, sudo)
```

Workflow :

```bash
# Terminal 1 — containers, seed des personas, serveur dev
make start && make seed-e2e && make dev-nuxt

# Terminal 2 — tests
make test-e2e            # headless
make test-e2e-ui         # UI interactive (debug)
```

> Toute permission testable touche **3 miroirs** à garder alignés : `config/sidebar.php`,
> `frontend/tests/e2e/_fixtures/personas.ts`, `SeedE2ECommand.php`.

---

## Déploiement : seed RBAC en recette / prod

Les fixtures Doctrine étant en `require-dev`, elles sont **absentes du build de prod**.
Le RBAC métier (rôles `bureau` / `compta` / `commerciale` / `usine` + matrice § 2.7)
est seedé par une **commande applicative idempotente**, jouée dans l'étape de release,
**après** les migrations et la synchronisation des permissions :

```bash
php bin/console doctrine:migrations:migrate --no-interaction
php bin/console app:sync-permissions      # pose les permissions (commercial.clients.*, …)
php bin/console app:seed-rbac             # PROD : rôles + matrice § 2.7 (sans comptes démo)
```

En **recette / staging**, ajouter le flag pour disposer de logins de test. Le mot de
passe est fourni **explicitement** (jamais en dur, jamais committé) :

```bash
php bin/console app:seed-rbac --with-demo-users --password='<mot-de-passe>'
# ou via la variable d'environnement RBAC_DEMO_PASSWORD
```

La commande est rejouable sans effet de bord (aucun doublon de rôle, de lien ou de
compte). Pour créer un premier administrateur en prod :

```bash
php bin/console app:create-user <username> <password> --admin
```

---

## Commandes make

`make` (sans argument) ou `make help` affiche l'aide colorée. Les principales :

| Commande                       | Description                                              |
|--------------------------------|----------------------------------------------------------|
| `make start` / `stop` / `restart` | Cycle de vie des containers                          |
| `make install`                 | Install complet (base dev vierge + base de test)         |
| `make reset`                   | Tout supprimer et réinstaller (**drop la BDD**)          |
| `make dev-nuxt`                | Serveur Nuxt hot reload (port 3004)                      |
| `make shell` / `shell-root`    | Shell bash dans le container PHP                         |
| `make migration-migrate`       | Jouer les migrations Doctrine                            |
| `make fixtures`                | Charger les fixtures (données de démo dev)               |
| `make sync-permissions`        | Synchroniser le catalogue RBAC                           |
| `make seed-rbac`               | Seed RBAC métier (rôles + matrice § 2.7)                 |
| `make db-reset`                | Reset base dev : drop + migrate + fixtures + RBAC        |
| `make test-db-setup`           | (Re)construire la base de test                           |
| `make test`                    | PHPUnit (back)                                           |
| `make nuxt-test`               | Vitest (front unitaire)                                  |
| `make test-all`                | PHPUnit + Vitest                                         |
| `make test-e2e` / `test-e2e-ui`| Playwright (E2E, sur l'hôte)                             |
| `make seed-e2e`                | Seed des 6 personas E2E                                  |
| `make php-cs-fixer-allow-risky`| Fix du code style PHP                                    |
| `make php-cs-fixer-check`      | Dry-run du fixer (CI / avant push)                       |
| `make logs-dev`                | Tail des logs Symfony                                    |

---

## Architecture

**Modular Monolith DDD** : chaque module est un bounded context autonome,
activable / désactivable par tenant. Le backend est la seule source de vérité pour
l'activation des modules et l'organisation de la sidebar.

- `config/modules.php` — liste des modules actifs
- `config/sidebar.php` — structure de la sidebar (sections + items avec module owner)
- `GET /api/modules` — IDs des modules actifs (public)
- `GET /api/sidebar` — sections filtrées par modules actifs + routes désactivées (public)

**Désactiver un module** : commenter sa ligne dans `config/modules.php`, vider le cache.
Ses items de sidebar disparaissent et ses routes sont bloquées par le middleware front.
Le code reste dans le bundle (layer auto-détecté) → réactivation instantanée.

**Réorganiser la sidebar** : éditer `config/sidebar.php` uniquement — le code des
modules n'est pas touché.

**Communication inter-modules** : jamais d'import direct d'un module à l'autre. Passer
par `Shared/Domain/Contract/` (interfaces) ou des domain events.

---

## Structure du dépôt

```
src/                              # Backend Symfony
  Shared/                         # Noyau technique partagé (Domain/, Application/Bus/, Infrastructure/ApiPlatform/)
  Module/
    Core/                         # Module obligatoire (auth, users, RBAC)
      CoreModule.php              # Déclaration (ID, LABEL, REQUIRED, permissions())
      Domain/ Application/ Infrastructure/
    Commercial/ Catalog/ Sites/   # Modules métier
config/
  modules.php                     # Source de vérité : activation
  sidebar.php                     # Source de vérité : navigation
  packages/                       # Config Symfony (doctrine, api_platform, security…)
migrations/                       # Migrations d'initialisation (namespace racine : setup, RBAC, seed de base)
frontend/                         # App Nuxt 4 (SPA)
  app/                            # Shell : layouts, middlewares (auth.global, modules.global)
  shared/                         # Code inter-modules (composables, stores, utils, types)
  modules/                        # Layers Nuxt auto-détectés (core/, commercial/…)
  i18n/locales/                   # Traductions (sidebar.*, audit.entity.*, …)
infra/
  dev/                            # Docker dev (Dockerfile, nginx, php.ini, xdebug, .env.docker)
  prod/                           # Docker prod (multi-stage, nginx, php-prod.ini)
.gitea/workflows/                 # CI Gitea (auto-tag, build Docker)
```

---

## CI/CD

- **Auto Tag** : push sur `develop` → bump `config/version.yaml` → tag `vX.Y.Z`
- **Build Docker** : push tag `v*` → build image multi-stage → push Gitea Registry

Secrets requis dans Gitea :

- `RELEASE_TOKEN` — PAT avec droits `write:repository`
- `REGISTRY_TOKEN` — token pour le registry Docker

---

## Conventions

### Commits

```
<type>(<scope optionnel>) : <message>
```

Espaces obligatoires autour du `:`. Types : `build`, `chore`, `ci`, `docs`, `feat`,
`fix`, `perf`, `refactor`, `revert`, `style`, `test`.

### Langue

- UI et communication : **français**
- Code (classes, méthodes, variables) : **anglais**
- Commentaires (PHP, TS, Vue) : **français**

> Règles détaillées : `CLAUDE.md` et `.claude/rules/`.
