# Coding standards

## PHP / Symfony

- **Version** : PHP 8.3, types stricts (`declare(strict_types=1);`) en tête de chaque fichier.
- **Style** : PSR-12 + règles Symfony (`@Symfony` preset).
- **Linter** : PHP-CS-Fixer + PHPStan niveau 8 (objectif : niveau 9 sur le code métier).
- **Architecture** :
  - Controllers fins : pas de logique métier, déléguer aux services.
  - Services dans `src/Service/<Domaine>/`.
  - Entités avec attributs Doctrine, pas de logique métier dans les entités à part les invariants.
  - Repositories avec méthodes nommées (`findActiveByOrganization`, pas de `findByCriteria` générique).
- **Nommage** :
  - Classes : `PascalCase`.
  - Méthodes / variables : `camelCase`.
  - Constantes : `UPPER_SNAKE_CASE`.
  - Routes : `kebab-case` (`/bank-accounts/{id}`).
- **Injection de dépendances** : constructor promotion par défaut.
  ```php
  public function __construct(
      private readonly EntityManagerInterface $em,
      private readonly OrganizationContext $orgContext,
  ) {}
  ```
- **Tests** : un test unitaire pour toute classe de service, un test d'intégration pour tout flux critique (auth, import, calcul de solde).
  - Couverture minimale visée : 70 % sur `src/Service/`.

## JavaScript

- **Pas de framework lourd** (React/Vue) en v1.
- **Stimulus** pour les interactions (controllers dans `assets/controllers/`).
- **Vanilla JS** (ES2022+) pour le reste, sans transpilation.
- **Pas de jQuery**.
- Préférer `fetch` aux requêtes XHR.
- Pas de logique métier en JS — la source de vérité reste le serveur.
- Dépendances JS via AssetMapper + import maps.

## CSS

- **Bulma** comme base, pas de Bootstrap mélangé.
- Customisations dans `assets/styles/app.scss` avec variables Sass de Bulma.
- Classes utilitaires Bulma à privilégier (`is-flex`, `has-text-centered`, etc.).
- CSS custom uniquement quand Bulma ne suffit pas. Préfixer les classes custom : `.viz-` (ex. `.viz-balance-card`).
- Pas de styles inline dans les templates Twig (sauf valeurs dynamiques type couleur catégorie).

## Twig

- Templates dans `templates/` avec arborescence miroir des controllers.
- Partials préfixés `_` (ex. `_partials/transaction-row.html.twig`).
- `{% block %}` toujours nommé explicitement.
- Pas de logique métier en Twig — préparer les données dans le controller / un view-model.

## SQL / Doctrine

- Migrations versionnées, jamais de modification ad-hoc.
- Index explicites pour toute requête fréquente (cf. spec modèle de données).
- DQL ou QueryBuilder : pas de SQL natif sauf cas très rares (et alors dans un repository dédié, commenté).
- **Toute requête sur une entité tenant-scoped doit passer par l'`OrganizationFilter`** ou être explicitement justifiée et auditée.

## Erreurs et exceptions

- Exceptions métier dans `src/Exception/` (`BankAccountNotFoundException`, `ImportFailedException`…).
- Pas de `catch (\Exception $e)` génériques sans rethrow ou log structuré.
- Logs via Monolog avec contexte (`['organization_id' => …, 'user_id' => …]`).

## Internationalisation

- L'interface est en **français** en MVP.
- Tous les libellés visibles passent par le composant `translator` (`{{ 'dashboard.title'|trans }}`) pour faciliter une i18n future.
- Catalogue dans `translations/messages.fr.yaml`.

## Date et fuseau horaire

- Toutes les dates en base : **UTC**.
- Affichage : fuseau de l'utilisateur (Europe/Paris par défaut, configurable plus tard).
- Format affiché : `d/m/Y` pour les dates, `d/m/Y H:i` pour les datetimes.

## Argent

- **Stockage : `BIGINT` (entier signé), unité = la plus petite unité de la devise** (centime pour EUR/USD, yen pour JPY, fils pour BHD à 3 décimales).
- Une colonne sœur `currency_minor_units` (TINYINT, défaut 2) sur les comptes/transactions multi-devise pour gérer les exotismes.
- Manipulation : entiers natifs PHP. **Jamais de `float`, jamais de `DECIMAL`**.
- Bibliothèque recommandée : `moneyphp/money` pour les opérations complexes (conversion, allocation, formatage), avec backend BCMath. Mais pour des additions/soustractions simples, l'arithmétique entière suffit.
- **Affichage uniquement** via une extension Twig `{{ amount|money(currency, minor_units) }}` ou `money_suffix`, qui divisent par 10^minor_units puis formatent ; **EUR** toujours en style français (**espace fins insécables** entre milliers ; **« € » après le montant**, ex. `1 234,56 €`).
- **Jamais de division/multiplication par 100 dans le code métier** — toujours passer par le formatter à l'affichage.
- Saisie utilisateur : on accepte `12,34` ou `12.34` côté formulaire, on convertit en BIGINT (1234) à la persistance via un `DataTransformer` Symfony.

## Identifiants légaux (SIREN / SIRET)

- **Validation Luhn** obligatoire à la saisie côté serveur (Validator dédié `LuhnValidator`).
- SIREN : 9 chiffres, validation Luhn.
- SIRET : 14 chiffres, validation Luhn **et** cohérence (les 9 premiers chiffres = SIREN de l'organisation).
- Pas d'appel API INSEE Sirene en MVP. Envisageable en v2 pour vérifier l'existence réelle de l'établissement (API publique gratuite).
- Stockage : exactement 9 / 14 caractères, sans espaces ni séparateurs.
- Affichage : on peut formater avec espaces (`123 456 789` ou `123 456 789 00012`) via un filtre Twig dédié.
