# Rapport v1.5.1 — Le courrier devient un outil, et le code se range par sujet

Deux sujets : une fonction nouvelle — l'envoi de courrier — et le rangement du
code, dont ce module a servi de modèle.

**Recette** : 676 tests, 3 273 assertions, tout au vert (630 avant le lot).
`./vendor/bin/pint` passé, `npm run build` relancé, deux migrations.

---

## 1. Plusieurs adresses d'expédition

**Fait.** `AdresseExpedition` porte un libellé, l'adresse, le nom affiché, une
adresse de réponse facultative et les paramètres du serveur sortant. L'hôte, le
port et le chiffrement se pré-remplissent avec ceux du serveur déjà en service :
toutes ces adresses vivent sur le même hébergement, il ne reste que l'adresse et
son mot de passe.

Une adresse est marquée par défaut — une seule à la fois, la règle est posée sur
le modèle et non sur l'écran. La zone d'envoi propose la liste et retient la
dernière employée **pour le destinataire concerné** : la mémoire est lue au
registre du courrier, qui porte désormais l'adresse de chaque envoi, plutôt que
tenue dans une préférence à part qui divergerait.

Sans adresse enregistrée, rien ne change : les messages partent par le fichier de
configuration, comme avant.

### Le mot de passe

Quatre garanties, chacune tenue par le code et non par la consigne :

- **chiffré au repos** par le mécanisme de Laravel : la colonne ne contient rien
  de lisible ;
- **jamais sérialisé** (`$hidden`) : ni export, ni réponse JSON, ni vue qui
  parcourrait les attributs ;
- **jamais journalisé** : le trait d'audit l'écarte par `$secretsAudit` — une
  propriété plutôt qu'une méthode redéfinie, pour qu'ajouter un secret ne demande
  pas de recopier la liste commune ;
- **jamais réaffiché** : le champ se présente vide avec la mention « inchangé »,
  et n'est réécrit que si l'on saisit quelque chose.

### « Tester cette adresse »

Le bouton tente réellement la connexion et l'envoi, puis rapporte l'erreur du
serveur **en clair** — « 535 Incorrect authentication data » dit ce qu'il faut
corriger, là où « l'envoi a échoué » ne dit rien. Une traduction en français
accompagne le message brut. Le mot de passe n'y figure jamais.

*Tests* : le chiffrement au repos, vérifié directement sur la colonne ;
l'absence du secret dans `toArray()`, dans le JSON, dans le journal d'audit et à
l'écran ; l'envoi qui part bien par l'adresse choisie et non par le fichier de
configuration ; la mémoire par destinataire ; le repli sans adresse ; l'erreur
d'authentification rapportée telle quelle.

**À vérifier après déploiement** : créer les adresses de la maison et **tester
chacune**. Noter que `APP_KEY` devient une clé de déchiffrement : la perdre
obligerait à ressaisir les mots de passe.

## 2. La mise en page du message

**Fait.** Trois mises en page, et pas trente : *sobre* (texte et pied de page),
*en-tête de marque* (logo et bandeau — l'unique gabarit d'avant), *courrier
formel* (allure du papier à en-tête, avec date, objet et destinataire).

Chacune se règle : logo, signature, mentions légales, bouton d'action. Un
référentiel unique — `MiseEnPage` — dit quelles options chaque mise en page
accepte : le courrier formel n'a pas de bouton, parce qu'une lettre qu'on imprime
ne se clique pas, et l'option n'est donc pas proposée. Un bouton sans adresse ne
s'affiche pas : un bouton mort vaut moins que pas de bouton.

**L'éditeur** est limité au gras, à l'italique, aux listes, aux liens et aux
titres. La même liste de balises sert à trois choses — ce que l'éditeur propose,
ce que le nettoyage laisse passer, ce que l'habillage sait styler — pour que les
trois ne puissent pas diverger.

**Le gabarit est bâti en tableaux, avec des styles écrits sur chaque balise.**
Outlook ignore les feuilles de style, même incorporées, ne connaît ni flexbox ni
grille, et applique aux `<p>` les marges par défaut du moteur de rendu de Word.
C'est laid à écrire, et c'est la seule façon que le message arrive intact.

**L'aperçu montre le rendu final** — le gabarit réel, dans un cadre isolé pour
que les styles du panneau ne débordent pas dessus — et bascule entre écran large
et téléphone. Il n'envoie rien.

*Tests* : les trois mises en page sont en tableaux, sans feuille de style, sans
flexbox ni grille, bornées à 600 pixels ; chaque balise du corps porte son style
en ligne ; le nettoyage écarte scripts, tableaux bricolés, `onclick` et
`javascript:` ; un texte brut devient des paragraphes ; le courrier formel refuse
le bouton ; l'envoi porte la mise en page choisie ; l'aperçu n'envoie rien,
quelle que soit la mise en page, et le basculement non plus.

## 3. La mise en forme assistée par l'IA

**Fait.** L'action prend le texte au fil de la plume, le contexte — type de
message, destinataire, société — et rend un objet, des paragraphes, des formules
d'appel et de politesse, et la mise en page qui convient.

Les trois règles tiennent par le code :

**L'IA n'invente aucun fait.** La consigne le dit, mais une consigne se
contourne. `GardeFouDesFaits` compare les nombres et les dates de la proposition
à ceux du texte d'origine — et aux valeurs connues du dossier, qui ne sont pas
des inventions. Au moindre écart, la proposition entière est rejetée : une mise
en forme partiellement fausse est plus dangereuse qu'une mise en forme refusée.
Les montants sont normalisés avant comparaison, pour que « 1 200 », « 1200 » et
« 1 200,00 » ne se prennent pas pour trois chiffres différents. Un texte de moins
de vingt caractères est refusé sans même appeler le modèle : on ne paie pas pour
un refus.

**Rien ne part sans relecture.** Le résultat remplit l'éditeur ; l'envoi reste un
geste distinct. « Revenir à ma version » rétablit le texte d'avant en un clic —
une proposition qu'on ne peut pas défaire n'est pas une proposition. Un objet
déjà saisi n'est pas écrasé.

**L'appel est compté et plafonné** sur le mécanisme de la v1.5.0 : le plafond est
posé dans le client de l'API, donc il vaut ici comme ailleurs.

*Tests* : un montant changé fait rejeter la proposition, avec le chiffre fautif
nommé ; une date ajoutée aussi ; une reformulation pure passe ; les montants du
dossier sont admis ; un texte trop maigre est refusé sans appel HTTP ; l'action
ne déclenche aucun envoi et n'écrit rien au registre ; le retour arrière ; le
comptage des jetons ; le refus au plafond, sans requête envoyée.

## 4a. Chaque règle à un seul endroit

**Fait.**

**Les pièces exigées d'un dossier** s'écrivaient à deux endroits :
`Domiciliation::typesObligatoires()` avec des libellés courts pour les voyants,
et `LegalCompleteness::requiredDocuments()` avec des libellés longs pour la
phrase « il manque… ». Deux listes des mêmes trois exigences — qui avaient déjà
divergé sur le nom du contrat, et ne se présentaient pas dans le même ordre. Une
seule liste désormais, `exigencesDocumentaires()`, portant les deux façons de
nommer chaque pièce. Le bandeau des pièces, qui recalculait la règle pour son
compte avec sa propre requête, passe par `LegalCompleteness`.

**La complétude** a une autorité — `LegalCompleteness` — et le modèle n'est plus
qu'un délégué. Elle est enfin mémorisée le temps de la requête : le commentaire
du modèle promettait ce mémo, il n'existait nulle part, et la fiche client
évaluait trois fois le même dossier.

**Le reste dû d'un compte courant** : `Lender::resteDu()` décide, le relevé
montre le chemin. `soldeDuReleve()` prend le résultat à l'autorité plutôt que de
le déduire de son propre cumul.

*Tests* : `tests/Feature/UneSeuleRegleTest.php` vérifie que voyants et liste des
manques sortent du même référentiel, dans le même ordre ; qu'une évaluation de
complétude ne coûte qu'une lecture par requête ; et que le relevé, l'écran des
comptes courants et `resteDu()` disent le même chiffre au dirham près.

## 4b. Le code rangé par domaine

**Fait pour trois domaines sur six.**

| Domaine | État | Ce qui a bougé |
|---|---|---|
| **Courrier** | déménagé | modèles, mailables, services, travaux, policies, deux écrans, la page d'envoi, six fichiers de tests |
| **Facturation** | déménagé | quatre modèles, quatre services, quatre policies, l'exception de série, la numérotation, deux écrans, sept fichiers de tests |
| **Comptabilité** | déménagé | dix modèles, six services, dix policies, l'observateur, sept écrans, neuf fichiers de tests |
| **Domiciliation** | **laissé en place** | voir ci-dessous |
| **Assistance** | **laissé en place** | voir ci-dessous |
| **Administration** | **laissé en place** | voir ci-dessous |

Trois mécanismes dépendaient du rangement, et ont dû être rendus indépendants
avant que le premier fichier ne bouge :

- **la découverte de Filament**, qui se fait par le chemin : le panneau balaie
  désormais `app/Domaines` en plus de `app/Filament` ;
- **la recherche des policies**, qui ne connaissait que `App\Models\X` →
  `App\Policies\XPolicy` : une règle supplémentaire mène au domaine, avec repli
  sur l'ancien chemin, sans quoi le mode strict de la v1.5.0 lève dès le premier
  modèle déplacé ;
- **les noms de classe stockés en base** — `audit_logs.auditable_type`,
  `transactions.source_type` : une table de correspondance fixe un alias stable
  par modèle déplacé, de sorte qu'aucune ligne existante ne devient illisible.
  Le trait d'audit écrit désormais le nom morphologique plutôt que `static::class`,
  et le seul endroit qui écrivait une trace à la main a été aligné : deux
  écritures de la même trace portaient deux étiquettes différentes.

### Ce qui n'a pas été déplacé, et pourquoi

**Domiciliation.** C'est le cœur, et il est référencé par tout le reste :
`DomiciliationClient` apparaît dans les trois domaines déjà déplacés, dans
l'assistance, dans les documents, dans la conformité. Le déplacer dans le même
lot aurait touché presque tous les fichiers du projet en une fois — c'est-à-dire
exactement le déménagement raté que la consigne demandait d'éviter. Il vaut mieux
un domaine à la fois, avec la suite verte entre chacun, et celui-ci mérite son
propre lot.

**Assistance.** Elle dépend de `PerimetreDonnees`, qui interroge sept domaines à
la fois — c'est sa raison d'être. La déplacer avant que les domaines qu'elle lit
ne soient rangés reviendrait à figer ses dépendances dans le mauvais sens.

**Administration.** Utilisateurs, rôles, réglages, audit et sauvegarde servent
tous les domaines. Ils relèvent du noyau commun autant que d'un domaine ; le
choix demande une décision de fond qui n'avait pas à être prise à la sauvette en
fin de lot.

L'outillage du déménagement — déplacement, réécriture des références, reprise des
namespaces et des imports rompus — a servi trois fois et fonctionne. Le prochain
lot n'aura pas à le réinventer.

## 4c. L'historique et l'architecture

**Fait.** Les treize rapports quittent la racine pour `docs/rapports/` et sont
résumés dans un **`CHANGELOG.md`** unique, par version, de la plus récente à la
plus ancienne, chaque entrée renvoyant à son rapport d'origine. Ils n'ont pas été
réécrits : ils sont rangés.

**`ARCHITECTURE.md`** tient sur une page : le principe du rangement par domaine,
l'état de chacun, ce qui reste au noyau commun, les trois mécanismes qui
dépendent du rangement et qu'il faut connaître avant de déplacer quoi que ce
soit, et le tableau des règles qui n'existent qu'à un seul endroit.

---

## Ce que l'exploitant doit vérifier après déploiement

1. **`composer dump-autoload`** après la mise à jour — trois domaines ont changé
   de place, un autochargeur en cache pointerait sur des fichiers absents.
2. **Créer et tester les adresses d'expédition.** Le bouton « Tester » envoie
   réellement : c'est le seul moyen de savoir si un mot de passe est le bon.
3. **`APP_KEY` protège désormais des mots de passe.** Vérifier que la sauvegarde
   du `.env` existe.
4. **Envoyer un message dans chacune des trois mises en page** et l'ouvrir dans
   Outlook si la maison en a un.
5. **Ajuster le plafond mensuel d'IA** si la mise en forme assistée est employée
   souvent : elle consomme sur le même compteur que les analyses de pièces.
6. **Ajouter `MAIL_TIMEOUT=30`** au `.env` s'il n'y est pas encore (v1.5.0).

Aucun écran, aucune adresse et aucune donnée ne changent du fait du rangement.
