---
suivi: 1047
date: 2026-07-29
sujet: Diagnostic filtre/regroupement programme × tâches opérationnelles (Mes tâches)
chantier: taches_suivi
type: diagnostic
statut: poussé
hash: 2399c08c
fichiers:
  - tools/diag/diag_taches_1047.php
  - docs/suivi/SUIVI_1047_diag_filtre_programme_taches_operationnelles.md
---

## PROMPT ENVOYÉ

SUIVI #1047 — PHASE 1 diagnostic uniquement. Bug Robin : tâche opérationnelle (auto)
associée à un programme mal filtrée/regroupée. Trancher H1 (absente) vs H2 (mauvais
groupe). Script lecture seule + fiche docs. Aucun .vue / fix / journal. Commit distinct
du lot parallèle #1048.

## SYNTHÈSE

### Verdict
**H2** — la tâche opérationnelle apparaît, mais dans le groupe « Sans programme »
(`prog-none`), alors qu’un programme est rattaché via la facture. H1 écartée : le back
charge bien `Task::ouvertes()` sans filtre programme SQL ; l’item est dans le payload,
mal classé côté front.

### A — Nature
- **A1.** Source = table `tasks` / `App\Models\Task` (≠ `taches_suivi`). Discriminant UI
  `nature: 'operationnelle'` dans `MesTachesController::serializeTaskRow` (~L206–242).
  Auto-création : `TaskSyncService` + sync sous `app/Services/Tasks/` (`erp_context`).
- **A2.** **Pas de `tasks.programme_id`.** Rattachement **déduit** :
  `erp_context.facture_id` → `factures.programme_id`. Le controller charge déjà
  `programme_id` (L84) mais **ne l’émet pas** dans le payload serialize. Les tâches
  suivi passent par `etiquettes` type `programme`.
- **A3.** Chiffres : exécuter le script en prod (MySQL local DOWN / synthétique inutile).

### B — Filtre / regroupement
- **B1.** `MesTachesController::index` (~L25–89) + `resources/js/utils/mesTachesGrouping.js`
  L134–168 (`groupe === 'programme'`).
- **B2.** Même `groupMesTachesItems` pour les deux natures, mais **données asymétriques** :
  suivi a `etiquettes[]` ; ops n’ont ni `etiquettes` ni `programme_id`.
- **B3.** Ops → toujours `prog-none` (filtre `!(t.etiquettes?.length)`). Jamais `prog-<id>`.
- **B4.** Regroupement **100 % front**. Query `filtre` = seulement `toutes|en_retard`.
  Aucun WHERE programme sur `tasks`.

### C — Reproduction
Preuve code + simulation script. Exemple concret (id / programme) = sortie prod du
script section C1–C2. Clé calculée = `prog-none` ; passe colonne programme attendue = **NON**.

### D — Recommandation (sans coder)
- **D1.** Cause : regroupement programme ne lit que `etiquettes[]` ; ops n’en ont pas
  malgré `factures.programme_id` déjà chargé.
- **D2.** Fix minimal : BACK exposer `programme_id` (+ libelle) dans `serializeTaskRow` ;
  FRONT étendre le groupe `programme` pour ops via `programme_id` (ou étiquette virtuelle
  côté serialize). Les deux pour cohérence affichage.
- **D3.** Suivi inchangé si branche ops seule. Regroupement **projet** déjà « Sans projet »
  pour ops (cause parallèle, hors scope).
- **D4.** Visibilité : `Task::visiblePourUtilisateur` = assignee / groupe M365 — indépendant
  du programme. Exposer `programme_id` sur tâches déjà visibles **n’élargit pas** qui voit
  quoi. Option A Projet (suivi) non affectée.

## DÉPLOIEMENT / TEST

1. `git pull origin main`
2. Sur OVH (racine app) :
   `php tools/diag/diag_taches_1047.php`
3. Coller sections A3 / C1–C3 du script dans le ticket. Aucune migration. Aucun
   `view:clear` / `journal:sync` (docs + script only).
4. Ne pas toucher `TacheSuiviKanbanCard.vue` / `TacheSuiviListItem.vue` (#1048 parallèle).

## LEÇON

Un rattachement métier **déduit** (facture → programme) invisible au front produit un
faux « Sans programme » : le symptôme ressemble à H1 (« pas dans le programme ») mais
est H2 (mauvais groupe). Toujours vérifier ce que `serialize*` émet, pas seulement ce
que le controller charge en eager.
