---
suivi: 1472
date: 2026-09-07
sujet: Socle demande de pièces aux suiveurs (table + Task)
chantier: juridique / tâches opérationnelles
type: feature
statut: poussé
hash: 0dec9b53
fichiers:
  - database/migrations/2026_09_07_160000_create_demande_juridique_pieces_demandees_table.php
  - app/Models/DemandeJuridiquePieceDemandee.php
  - app/Models/DemandeJuridique.php
  - app/Models/DemandeJuridiqueHistorique.php
  - app/Models/Task.php
  - app/Models/TaskType.php
  - app/Services/TaskSyncService.php
  - app/Services/Tasks/DemandePieceJuridiqueTaskSync.php
  - app/Services/Juridique/DemandeJuridiquePieceDemandeeService.php
  - app/Services/Juridique/DemandeJuridiqueSuiveurService.php
  - app/Support/Juridique/DemandeJuridiquePieceDemandeeResolver.php
  - app/Support/Juridique/DemandeJuridiqueHistoriqueService.php
  - app/Support/Juridique/DemandeJuridiquePresenter.php
  - app/Http/Controllers/DemandeJuridiquePieceDemandeeController.php
  - app/Http/Controllers/DemandeJuridiqueController.php
  - app/Http/Requests/Juridique/StoreDemandeJuridiquePieceDemandeeRequest.php
  - app/Http/Requests/Juridique/UpdateDemandeJuridiquePieceDemandeeEcheanceRequest.php
  - app/Http/Requests/Juridique/UpdateDemandeJuridiquePieceDemandeeDestinataireRequest.php
  - routes/web.php
  - tests/Unit/DemandePieceJuridique1472Test.php
  - tests/Unit/DemandePieceJuridique1472SourceTest.php
  - tests/Feature/DemandePieceJuridique1472FeatureTest.php
  - docs/suivi/SUIVI_1472_demande_piece_juridique_socle.md
---

# SUIVI #1472 — Socle demande de pièces juridiques

## PROMPT ENVOYÉ

Socle uniquement (pas d’UI #1473) : table dédiée + Task miroir, échéance
manuelle, endpoints création/validation/suppression/échéance/destinataire,
historique, tests. Pattern #1458. Pas de .vue / pas de rebuild.

## BASE GIT

```
git log --oneline -1 origin/main
0b8da06d chore(suivi): hash fiche #1471
```

(Attendu prompt : `30c8865d` #1470 — main a avancé via #1471. Rebase
explicite sur tip à jour.)

## FICHES LUES

- **#1470** — diagnostic complet (architecture ii, échéance colonne vs API,
  peutAcceder suiveur, idempotence).
- **#1458** — CompleterSaisieFactureTaskSync / seed updateOrCreate.
- **#1469** — ChronologieBuilder : ne pas le modifier ; nouveaux événements
  entrent comme `evenement` via historique.
- **#1451** — peutGererSuiveurs via m365GroupNames() brut.

## SYNTHÈSE

Table `demande_juridique_pieces_demandees` (SoftDeletes, pas de UNIQUE
libelle×suiveur). Type `demande_piece_juridique` / libellé « Fournir une pièce »,
`delai_heures` null. Taskable = la ligne. TaskSyncService étendu de façon
additive (`?CarbonInterface $echeanceAt`, `updateEcheanceManuelle`, closed
reasons #1472). Props `pieces_demandees` exposées dans serializeDetail pour #1473.

## TABLEAUX DE MAPPING

### Événements

| action métier | ligne | Task | statut | closed_reason | historique |
|---|---|---|---|---|---|
| Création | insert | upsert | nouveau | — | piece_demandee |
| Validation (coche) | fournie_at + fournie_par_id | close | realise | piece_fournie | piece_demandee_validee (qui) |
| Suppression juriste | soft-delete | close | annule | piece_demandee_supprimee | piece_demandee_supprimee |
| Suiveur retiré | soft-delete ouvertes | close | annule | suiveur_retire | piece_demandee_supprimee |
| Demande Terminé/Annulé | soft-delete ouvertes | close | annule | demande_juridique_cloturee | — |
| Demande soft-delete | soft-delete ouvertes | close | annule | demande_juridique_supprimee | — |
| Échéance modifiée | echeance_at | updateEcheanceManuelle | ouvert | — | piece_demandee_echeance |
| Destinataire changé | user_id | reassign | ouvert | — | piece_demandee_destinataire |
| Second upsert | inchangé | 1 seule Task | — | — | — |
| Task déjà fermée | — | jamais ressuscitée | — | — | — |

### Droits

| action | qui | contrôle | compte refus |
|---|---|---|---|
| Créer | GS_JURIDIQUE∪DSI∪ADMIN_ERP | Resolver::peutCreer + FormRequest | collab GS_COMMERCIAL |
| Valider | juriste OU destinataire ligne | Resolver::peutValider (`user_id` exact) | autre suiveur |
| Supprimer / échéance / destinataire | juriste | Resolver::peutModifier/Supprimer | hors juriste |
| Accès fiche | peutAcceder | ParticipantResolver | tiers hors demande |

## TaskSyncService — détail ligne par ligne

1. `upsertFromEvent` : 6e param optionnel `$echeanceAt = null`.
2. Attributs : `echeance_source` / `echeance_at` **uniquement à la création**
   (plus d’écrasement sur update d’une tâche ouverte).
3. Si `$echeanceAt !== null` → `manuel` ; sinon `type` + `delai_heures` (inchangé).
4. `upsertFromEventForGroupe` : même règle « échéance à la création seulement ».
5. `applyClosureToTask` : 5 nouvelles reasons (#1472).
6. Nouvelle méthode `updateEcheanceManuelle`.
7. Preuve non-régression : test 72 h / 48 h via upsert sans `$echeanceAt`.

## Relecture SQL migration

- `Schema::create` sans `->after()`.
- FK courtes (`djpdem_*`).
- Pas de UNIQUE métier (relance autorisée).
- Index : demande ; user ; (demande, fournie_at).
- Seed type `updateOrCreate` idempotent.

## DÉPLOIEMENT-TEST

```
git pull origin main
php artisan migrate --force
find app/ config/ database/ -name "*.php" -exec touch {} +
php artisan route:clear
php artisan view:clear
```

OPcache touch **obligatoire** (PHP applicatif modifié). `route:clear` car
nouvelles routes. Pas de npm / composer / optimize / config:cache.

## LEÇON

Échéance manuelle = param optionnel + set-on-create only. Taskable = ligne
pièce, pas DemandeJuridique (idempotence N×N). Valider = juriste OU
destinataire de la ligne, jamais un autre suiveur.
