---
suivi: 1502
date: 2026-09-10
sujet: repli_commercial_leads_meta
chantier: leads_marketing
type: fix
statut: poussé
hash: e05a7248
fichiers:
  - config/services.php
  - .env.example
  - app/Services/LeadsMarketingFallbackCommercial.php
  - app/Services/MetaLeadIngestionService.php
  - app/Services/MetaLeadCampagneRattrapageService.php
  - app/Console/Commands/RattrapageLeadsMetaCampagneCommand.php
  - tests/Unit/RepliCommercialLeadsMeta1502SourceTest.php
  - tests/Feature/RepliCommercialLeadsMeta1502FeatureTest.php
  - docs/suivi/SUIVI_1502_repli_commercial_leads_meta.md
---

# SUIVI #1502 — repli Johanna + rattrapage campagnes Meta

## PROMPT ENVOYÉ

Q3 → repli Johanna dans les deux cas (campagne inconnue **ou** déclarée
sans commercial). Rattrapage 32 leads, `--dry-run` / `--force`. Arrêt
CLI si Teams / SharePoint impossibles. Prévention : proposer sans trancher.

## FICHES LUES

- **#1371 LEÇON** — libellé depuis `meta_campaign_codes`, pas Meta
  `getCampaign`. Rattrapage = commande explicite CLI, jamais un
  side-effect de page. Impact : nouvelle commande dédiée (pas
  `--apply` de `meta:campaign-names:backfill`, qui ne touche pas
  `commercial_user_id`).
- **#1368** — `raw_payload` est la trace d’origine ; commercial via
  mapping puis `LEADS_FALLBACK_COMMERCIAL_USER_ID`. Impact : ne pas
  toucher `raw_payload` ; le `1` n’est pas un littéral PHP.
- **#1501 LEÇON** — un lot sur PR n’existe pas pour OVH. Impact :
  push direct `origin/main`, pas de branche `cursor/*`.

## SYNTHÈSE

### A — Où était le `1`

Pas une constante PHP ni `Auth::id()`. Config déjà en place :

- `config/services.php` (bloc `leads_marketing.fallback_commercial_user_id`)
  → `env('LEADS_FALLBACK_COMMERCIAL_USER_ID')` **sans défaut** avant ce lot
- consommé par `MetaLeadIngestionService::fallbackCommercialUserId()`
  (les deux branches : mapping null **et** mapping sans `commercial_user_id`)

Le `1` observé en prod vient de `.env` OVH (`LEADS_FALLBACK_COMMERCIAL_USER_ID=1`).
Mécanisme conservé. Défaut PHP et `.env.example` : **22** (Johanna).
Si OVH a encore `=1`, le défaut 22 **ne s’applique pas** — changer `.env`
puis `optimize:clear`.

Utilisateur de repli absent / introuvable : `Log::error`, `commercial_user_id`
laissé NULL. Pas d’autre id inventé. Le job unitaire (contexte **web**)
envoie alors encore à `LEADS_FALLBACK_EMAIL` (Aurélie) — pas un silence.
`users` n’a pas de flag « désactivé » ni SoftDeletes.

### B — Commande

Signature littérale :

`meta:leads:rattrapage-campagne {--dry-run : Simulation (défaut si --force absent)} {--force : Écrit en base (jamais Graph / Teams / SharePoint)}`

`php artisan list --raw` :

`meta:leads:rattrapage-campagne             Rattache les leads Meta sans nom de campagne à meta_campaign_codes (#1502). Dry-run par défaut.`

Namespace artisan = `meta` (déjà utilisé). Dry-run par défaut.
`--force` écrit `meta_campaign_name`, `code_campagne`, `programme_id`,
`commercial_user_id` depuis `resolveForLead` (actif + soft-delete).
`code` vide sur MONTAUBAN / LEZIGNAN → `code_campagne` vide, **non inventé**.
Exclus 620, 621, 690, 1, 618, 619. Ne touche pas `raw_payload`.

### C / D — ARRÊT CLI

`NotifyCommercialTeams` et `CreateLeadSharePointItem` passent par Microsoft
Graph. `QUEUE_CONNECTION=sync` → un `dispatch()` artisan s’exécuterait
en CLI et échouerait (`cURL error 6`). **Aucun job n’est dispatché.**
`teams_notified_at` n’est **pas** remis à zéro (sinon 32 mails unitaires
via « Relancer la notification »).

Lead 1053 : `sharepoint_action=creer` (item vide). Les autres du lot :
`mettre_a_jour`. `CreateLeadSharePointItem` ne fait que CREATE et ignore
un item déjà présent ; `SharePointService` n’a **pas** de `updateListItem`.
Le bouton `retry-sharepoint` **efface** l’item id puis recrée → doublon
sur les 31, à ne pas utiliser pour eux.

Options (non tranchées) :
1. Route web dédiée (PHP-FPM) : récapitulatif Graph + PATCH SharePoint
   (il faudrait ajouter `updateListItem`) + création 1053.
2. Bouton admin Meta (même code, un clic Johanna / Robin).
3. Cloche seule (pas Teams) + SharePoint plus tard.
4. Lead 1053 seulement : bouton existant « Relancer SharePoint » **une fois**
   le rattachement base fait (crée l’item, ne met pas à jour les 31).

### E — Prévention (non codé)

Aujourd’hui : `Log::warning` seulement. Options :
- Cloche Johanna, scénario court ≤ 64 car. (`alertes.scenario` VARCHAR(64)).
  Coût : AlerteService + garde strlen. Visible le lendemain.
- Compteur sur Prospects marketing (campagne inconnue). Coût : Vue + bundle.
- E-mail Graph à Johanna à la réception (web OK, CLI non). Coût : builder.

## DÉPLOIEMENT-TEST

```
git pull origin main
php artisan optimize:clear
```

Sur OVH `.env` : `LEADS_FALLBACK_COMMERCIAL_USER_ID=22` (si encore `1`).
Puis encore `optimize:clear`.

```
php artisan meta:leads:rattrapage-campagne
php artisan meta:leads:rattrapage-campagne --force
```

Pas de migrate. Pas de journal:sync. Pas de npm/composer/optimize sur OVH.
Pas de route nouvelle → pas de `route:clear` obligatoire.
Front inchangé → pas de rebuild.

Après `--force`, vérifier (Robin, MySQL) : les 32 ont nom + commercial 13/16 ;
les 6 exclus intouchés ; aucun envoi Graph depuis la CLI.

## LEÇON

Le destinataire de repli était déjà une variable d’environnement : changer
la valeur OVH, ne pas remplacer la config par un `22` en dur dans le
service. Graph bloqué en CLI + queue sync = un job dispatché depuis
artisan est un échec silencieux ; ne pas armer `teams_notified_at=null`
sur un rattrapage, cela relancerait les 32 notifications unitaires.
`meta:leads:backfill` ≠ `meta:leads:rattrapage-campagne` (namespaces
déjà existants, ne pas inventer `depot:`).
