# Rapport v1.16.0 — retrouver un client par n'importe quel nom ; l'espace des cabinets ; le rapport des primes

*Base : v1.15.3 (`a6199ce`), elle-même sur v1.15.2 (`371b62e`, en production).
Un seul déploiement **v1.15.2 → v1.16.0** (voir `DEPLOIEMENT.md`). Commits : A
(recherche), B (primes + migration F), C (prime « Rachida »), D (espace
cabinet), E (e-mail du lundi), puis les suites de la relecture.*

Tests : **1 561** (1 560 au vert, 9 660 assertions) ; le seul échec est
l'échec d'environnement connu `PortailSousDomaineTest` (`APP_URL` du `.env` du
Mac). `pint --dirty`, `npm run build` passés. Aucune dépendance composer ni npm.

## La migration (une seule, additive et idempotente)

`database/migrations/2026_10_16_100000_les_cabinets_et_leurs_primes.php` :
`courriers.apporteur_id` (nullable, FK `apporteurs`, `nullOnDelete`, index) ;
`acces_portail.visite_precedente_at` (datetime nullable) ;
`acces_portail.recap_hebdo` (booléen, vrai par défaut) ; réglage
`portail.recap_cabinets` (groupe `portail`, « E-mail du lundi aux cabinets »,
**faux**) ; modèles d'e-mail `portail_releve_primes` et `portail_recap_cabinet`
(insérés s'ils manquent, jamais écrasés). Chaque étape vérifie avant d'écrire.

**Fichier supprimé** (à retirer du serveur) : `resources/views/portail/cabinet/commissions.blade.php`
(remplacé par `primes.blade.php`). Aucun fichier PHP déplacé.

## Sources des chiffres

- **Copie de production du 01/10/2026** montée dans une base jetable
  (`mobdie_v1160`, migrée) : banc, balayages, simulations. Sur cette copie,
  « Comptable Rachida » est l'**apporteur n° 2** (n° 3 = AMG EXPERTISE
  COMPTABLE) ; le prompt dit n° 3 en production : **à vérifier** (voir §C).
- **Jeu de recette des primes** (`tests/Feature/Primes/JeuPrimes.php`) : les 23
  primes de Salima et celle de FES HOSTING, dates et montants de la production
  au 16/10 (prompt §B7).
- **Jeu fictif de la maquette** (`tests/Feature/Recherche/JeuMaquette.php`) :
  les 14 dossiers de `moteur-maquette.js` + FAKIR DÉCOR.

## A — Le moteur de recherche

`App\Services\Recherche\{Texte, IndexClients, RechercheClients, Resultat, Trouvaille}`.
Règles de `moteur.py` portées telles quelles (normalisation, mots vides, score
`sj()` avec la borne des 4 lettres, clé phonétique, Damerau-Levenshtein, mots
accolés, saisie accolée, ET, seuil relatif), champs pondérés (raison sociale 1,
gérant 1, associés 0,9, personnes autorisées 0,9, e-mail 0,85, cabinet 0,8 —
côté MOBDIE seulement), identifiants d'abord (CIN du gérant et des associés,
code, ICE, téléphone, RC avec ou sans ville ; 1,05), départage actif →
en préparation → prospect → clos → alphabet, « pourquoi » (champ qui couvre le
plus de mots, ≈ / ·, mot surligné) et « compris comme ».

**Écart voulu à la référence** : une clé phonétique d'un seul son ne vaut pas
0,88 — sans cela « kkk » ramenait HC IMPORT EXPORT (clé « k » = celle de
« HC »), y compris avec le `moteur.py` de référence ; le bruit doit être nul.

### Le banc — `mobdie:banc-recherche`, copie du 01/10 (128 clients hors corbeille)

| Famille | Cas | Liste aujourd'hui (LIKE) | Recherche du haut aujourd'hui | Moteur : 1er | Moteur : 5 premiers | Seuil exigé | Résultats (moy.) |
|---|---|---|---|---|---|---|---|
| gérant, nom exact | 102 | 5 | 102 | 98 | **102 (100 %)** | ≥ 98 % | 1,3 |
| gérant, une faute | 94 | 0 | 9 | 92 | **94 (100 %)** | ≥ 97 % | 1,7 |
| gérant, autre graphie | 102 | 1 | 1 | 96 | **102 (100 %)** | ≥ 98 % | 1,6 |
| prénom + nom | 100 | 0 | 100 | 97 | **100 (100 %)** | ≥ 98 % | 1,1 |
| société, une faute | 108 | 11 | 11 | 100 | **107 (99,1 %)** | ≥ 96 % | 1,5 |

Bruit : « xqzt » 0, « bonjour » 0, « zzzz » 0, « facture » 0, « kkk » 0.
Tous les seuils tenus ; ≤ 3 résultats en moyenne. (Les fautes sont tirées par
le générateur de PHP, graine 1610 : 94 cas « une faute » contre 96 en Python.)

### Vitesse

2 000 dossiers synthétiques (test `VitesseRechercheTest`) : **médiane 5,2 ms**
par recherche (20 saisies, index chaud ; max 14,7 ms) — objectif < 50 ms,
test < 150 ms. Reconstruction de l'index : **74 ms, 4 requêtes** (une par
table). Sur la copie : index 7 ms, recherche médiane 0,6 ms.

### Les endroits branchés sur le moteur

- **Liste Clients** : `searchUsing` (parmi les dossiers de l'onglet, des
  filtres et du rôle), tri par pertinence en `CASE`, puce « pourquoi » sous la
  raison sociale, en-tête « N clients trouvés · du plus proche au moins
  proche » et la correction ; un clic sur un en-tête reprend le tri habituel
  (l'en-tête ne promet alors plus la pertinence).
- **Recherche du haut** : `getGlobalSearchResults` (raison sociale + badge,
  « pourquoi » surligné, « code · cabinet », correction sous le dernier résultat
  — Filament n'a pas de pied de liste : écart de présentation).
- **`ChampClient`** (un composant, champ et filtre) : InvoiceForm, QuoteForm,
  CourrierResource, CourrierRecuPage (« Pour quel client ? »), BoiteDeReception,
  EnvoyerUnMessage (« Domicilié » et « Domiciliés », choix multiple), filtre et
  dépôt de ClientDocumentsTable, ResilierAction, NouvelleFactureAction,
  ContratClientExistantAction, Demandes, ClientsRelationManager (rattacher à
  un cabinet). Le champ propose, ne sélectionne jamais seul ; le rôle
  cloisonné ne voit que son périmètre.
- **Colonnes « Client »** : Courrier, Relances, Résiliations, Analyses IA,
  Échéancier global, Recouvrement, Pièces (avec sa recherche ICE/code gardée),
  Factures et Devis (avec `client_nom` gardé).
- **« Mes clients » du cabinet** : moteur restreint au cabinet, sans le champ
  « cabinet » ; suggestions JSON dès 3 lettres (`cabinet.clients.suggestions`,
  throttle 60/min) ; Entrée filtre la liste.
- **Assistance** : code ou ICE exact (dans le périmètre du rôle), sinon le
  moteur — retenu si ≥ 0,85 et 0,1 devant le second, sinon « ambigu » avec
  les candidats et ce qui les a fait sortir.
- **Doublon possible** : critères exacts gardés + raison sociale approchée
  (≥ 0,88, jugée dans les deux sens) ; puce « Doublon possible — proche de
  NORDVENT, 2026-136 ». Formulaire public : `dejaDomiciliee` inchangé (test).

## B — La situation des primes (source unique)

`App\Services\Primes\SituationDesPrimes` : Dû(D) = acquises ≤ D − versées ≤ D ;
identité vérifiée à chaque calcul (exception sinon) ; versée d'avance, « enregistrée
le », reste à régler (avances déduites), à revoir hors totaux, contrôle
(cabinet ou tous les cabinets seulement). Lecteurs branchés : relevé de la fiche
du cabinet (« depuis le début → aujourd'hui »), `CommissionsCabinet::etat`
(**défaut corrigé** : primes des collaborateurs comprises), synthèse de la
page Primes (colonne « Cabinet »), assistance, reçu du portail (périmètre
`duCabinet`, bénéficiaire et cabinet nommés), PDF, Excel, e-mails.

### Les attendus du §B7, recalculés par le jeu de recette (tous au vert)

| Cas | Dû au début | Acquises | Versées | Dû à la fin |
|---|---|---|---|---|
| Rami, septembre 2026 | 100 | 700 (7) | 500 (1 versement, op. 1899) | 300 |
| Rami, 01/10 → 16/10 | 300 | 300 (3) | 600 (op. 1945, 6 primes) | 0 |
| Rami, depuis le début | 0 | 2 300 (23) | 2 300 (700+500+500+600) | 0 |
| Rami, juin / juillet / août | 0 / 500 / 300 | 500 / 500 / 300 | 0 / 700 / 500 | 500 / 300 / 100 |
| Rami au 15/09 | 100 | 500 (5) | 500 | **100** = OPTIMA + Fakhar Zakia − OL IMPERIAL |
| Toutes, depuis le début | 0 | 2 600 | 2 600 | 0 (avant comme après §C) |
| Tous les cabinets | 0 | 2 300 → 2 600 après §C | idem | 0 |
| Sans cabinet | FES HOSTING 300 avant, vide après §C | | | |

Texte : « Primes à régler à Salima : OPTIMA LEGAL 100 ; Fakhar Zakia 100 ;
Azirar Loubna 100 ; total 300 DH » ; au 15/09 : « … ; avance déjà versée (OL
IMPERIAL) − 100 ; total 100 DH ». Espace cabinet de Rami : **2 300 versés, 0 à
recevoir**. Test croisé : situation, espace cabinet, relevé, synthèse et
assistance rendent les mêmes totaux.

**Rapport des primes** : bouton sur Primes et dans l'onglet « Commission » du
cabinet (présélectionné) ; puces Ce mois / Mois dernier (défaut) / Ce
trimestre / Cette année / Depuis le début / Dates… ; Cabinet (quatre
périmètres) ; Bénéficiaire ; « Ce que dira le rapport » ; « Ajouter le
contrôle » ; Excel et PDF par **route signée relative** (`signed:relative`) et
`SortieDocument` ; « voir les siennes » → ses seules primes, sans envoi. PDF
relu (rendu vérifié en image : aucune ligne coupée, accents justes, en-tête de
tableau répété au saut de page, « Page x / y »). Excel relu (onglets, nombres
et dates en vraies cellules, formats DH, en-têtes figés).

**Envoyer au cabinet** : un e-mail par destinataire coché (accès qui
reçoivent les copies + e-mail de la fiche par défaut), salué au prénom, PDF
joint sans « Contrôle », rangé `apporteur_id` + onglet « Courrier » de la
fiche du cabinet ; jamais dans « Courrier à retirer » ni « Courrier de mes
clients » (test). **Écart** : l'application ne sait pas lier un modèle
d'e-mail à une adresse d'expédition → l'adresse par défaut (la maquette montre
facturation@ et domiciliation@).

## C — La prime « Rachida »

`reprise:rattacher-au-cabinet {apporteur} --beneficiaire=Rachida` ; sans
l'option, propose les bénéficiaires sans compte qui ressemblent au contact.
**Garde ajoutée** : refus si le nom ne ressemble pas au contact du cabinet,
avec la liste des cabinets (sur la copie, `… 3 --beneficiaire=Rachida`
visait AMG EXPERTISE COMPTABLE) ; `--sans-ressemblance` pour passer outre.

Sortie sur la copie (`… 2 --beneficiaire=Rachida`) — **attendue à l'identique
en production, au bon numéro** : « Ce qui serait fait : 1 prime reliée au
cabinet Comptable Rachida : FES HOSTING, 300,00 DH, versée le 08/09/2026
(opération n° 1900) ; 1 client rattaché : FES HOSTING ; aucun mouvement
d'argent ». `mobdie:diagnostic` (famille 4) cite FES HOSTING avec la commande
avant, plus après. Pas de seconde commission « nouveau client » (test).

## D — L'espace des cabinets (écran par écran, face à la maquette)

- **Cadre** : conforme (menu, « Cabinet comptable · N collaborateurs », menu
  de la personne avec l'interrupteur « E-mail du lundi » seulement quand
  l'envoi est allumé). Téléphone : Accueil, Clients, +, Primes, Plus (Lien
  client, Courrier, E-mail du lundi, Déconnexion) ; vérifié à 360 px sans
  défilement horizontal.
- **Accueil** : conforme (« Bonjour {prénom} », dernière visite, quatre
  chiffres dont Primes à recevoir, « À faire », « Quoi de neuf » par
  `FilDuCabinet`, « Tout l'historique » paginé). Phrase « …et dans l'e-mail du
  lundi » seulement si allumé et abonné. Événements datés : contrat légalisé,
  paiement déclaré (en vérification, validé, refusé), pièce validée/refusée,
  plis reçus (recommandés), demande par le lien client, prime acquise,
  versement (groupé). **Non montré faute de date** : le passage d'un dossier
  à « Actif » (statut sans date propre).
- **Mes clients** : conforme ; « + N en cours » = onglet ; Tous = En cours +
  Actifs + Terminés (gratuit compté actif) ; « Exporter (Excel) » en requêtes
  groupées (test : nombre de requêtes constant). La ligne « Entrée : filtrer
  la liste » n'est pas reprise dans les suggestions (l'Entrée filtre bien).
- **Mes primes** (`/cabinet/primes`, `/cabinet/commissions` redirige en 301) :
  conforme ; la règle vient du barème (pas de phrase figée — « Les
  renouvellements ne donnent rien » seulement si le barème le dit) ;
  « Restait à recevoir au … » ; « Pas encore acquises » ; Excel et relevé.
- **Nouveau client express** : Les pièces · Vérifier · Formule et envoi ; source
  de chaque champ ; écart à trancher (« C'est la même personne » / « Ce n'est
  pas la même » → noté au dossier, ou « écart d'identité à vérifier » en
  concordance de la pièce, sans bloquer) ; « À compléter (N) » ; bénéficiaire
  effectif proposé (**écart déclaré** : « Je confirme » ajouté à « Modifier ») ;
  pied vérifié dans le code (le client reçoit projet et devis, cabinet en
  copie ; MOBDIE valide ensuite les pièces). Toutes les règles des quatre
  étapes publiques exigées avant l'envoi (test règle par règle) ; formulaire
  public inchangé (test).
- **Fichiers** : natures signées `releve` et `export` (15 min) ; un autre
  cabinet ne reçoit que ses propres données ; lien expiré → 403.

## E — L'e-mail du lundi

`portail:recap-cabinets` (simulation par défaut ; `--appliquer` refusé si le
réglage est éteint — il l'est au déploiement) ; planifié lundi 08:00
Africa/Casablanca. Rien à dire → rien d'envoyé ; un échec n'arrête pas les
autres ; rangé chez le cabinet ; « Ne plus le recevoir » : page puis bouton
POST (accès actif) ; « M'envoyer un exemple » (Réglages → Espace client), au
gérant seul, non rangé. **Écart déclaré** : pied neutre (« pour le cabinet … »).
Simulation sur la copie : 1 accès de test (Rami), « partirait ».

## Relecture indépendante et suites

Un sous-agent a relu `v1.15.3..HEAD` point par point. Corrigé : **liens du
rapport signés en relatif refusés (403) — grave** ; requêtes par prime dans
le calcul ; faux « doublon possible » sur un mot commun ; « Quoi de neuf »
manquant les faits du jour de la visite et les versements d'un cabinet sans
client ; garde « nouveau client » comptant les primes annulées ; rattachement
d'un bénéficiaire tiré de primes reliées ailleurs ; assistance non bornée au
rôle sur un code exact (antérieur au lot) ; champ « Domiciliés » hors moteur ;
banc qui vidait l'index ; désabonnement d'un accès désactivé ; en-tête de
liste qui annonçait la pertinence malgré un tri choisi. **Non corrigé,
assumé** : liens signés de 15 min créés à l'affichage (une page laissée
ouverte plus longtemps se recharge) ; `CommissionService::solde()` garde sa
somme pour la fenêtre « Verser » (identique sauf prime à date future) ; une
saisie accolée n'est comparée qu'aux mots simples (comme la référence).

## Sorties attendues en production (simulations)

Voir `DEPLOIEMENT.md` : diagnostic (NOVERA, DEVANO dans « échéance due sur un
contrat expiré/annulé » ; FES HOSTING en famille 4), contrat-en-double 123 et
128, Rachida, banc, récapitulatif du lundi (éteint).
