---
suivi: 1326
date: 2026-08-13
sujet: reprise_siwi_actions_erp
chantier: marches_reprise
type: feature
statut: poussé
hash: 410dbbe7
fichiers:
  - database/migrations/2026_08_13_170000_add_travail_liaison_to_siwi_operations_table.php
  - app/Models/SiwiOperation.php
  - app/Models/SiwiMarche.php
  - app/Services/SiwiRepriseActionService.php
  - app/Services/SiwiImportService.php
  - app/Services/SiwiImportPageService.php
  - app/Http/Controllers/SiwiImportController.php
  - routes/web.php
  - resources/js/Pages/SiwiImport/Index.vue
  - public/build/
  - database/data/journal_mises_a_jour_post_2026_06_12.php
  - docs/suivi/SUIVI_1326_reprise_siwi_actions_erp.md
---

## PROMPT ENVOYÉ

LOT E #1326 — branchement actions ERP + migration TRAVAIL `siwi_operations`
(complément validé). Créer/rattacher programme, créer marché + `reference_siwi`,
reprise, RG, hors périmètre / écarter / verrou, lot avec échec partiel.
Hors dépôt documentaire (lot F).

## SYNTHÈSE

### Fiches lues
- `#1324` : SOURCE vs TRAVAIL ; `siwi:import` ne touche pas TRAVAIL.
- `#1325` : page staging ; actions UI désactivées ; colonnes manquantes signalées.
- `#1323` : `reference_siwi` UNIQUE, reprise HT, lock MCH.

Approche : migration TRAVAIL explicite (pas de schéma furtif) puis service unique
`SiwiRepriseActionService` qui réutilise les chemins Programme / Marché / reprise.

### Migration TRAVAIL (7 colonnes)
UNE seule `ALTER` (pas de data). Indexes courts, **pas de FK** (même convention
que `marche_hectarion_id` / `decision_user_id` #1324 — découplage staging,
évite erreur 1553 au drop). `down` retire les 7 colonnes + indexes.

Colonnes : `programme_hectarion_id`, `etat_liaison_programme` (`cree`|`rattache`),
`entite_id`, `sharepoint_sync_status` **NOT NULL DEFAULT `non_tente`**
(`non_tente`|`en_attente`|`ok`|`en_echec`), `sharepoint_sync_error`,
`programme_lie_user_id`, `programme_lie_at`.

**Idempotence** : ces 7 colonnes sont TRAVAIL ; `upsertOperation` ne les écrit
jamais. Preuve locale : marker avant `siwi:import --apply` sample → inchangé après.

### Autorité dégrisage
- `etat_programme` = décision humaine (`non_repris` + motif).
- `etat_liaison_programme` = **fait** (`cree`/`rattache`) → **fait foi pour
  débloquer les marchés**.
- `non_repris` **refusé** si déjà lié (`estLie()` / CDC D6).

### SharePoint (QUEUE sync)
`CreateSharePointArborescence` catch sans rethrow → appel **direct**
`SharePointService::creerArborescenceProgramme`. Échec → `en_echec` + erreur,
**sans** bloquer création programme ni dégrisage (CDC D7). Rejeu :
`POST /siwi-import/operations/{code}/sharepoint-sync` (GS_GROUPE) — pas la
route `programmes/.../sharepoint-sync` (GS_DSI + redirect edit).

Temps observé création programme locale : **~4237 ms** (Graph OK) ;
avec mock SP false : **~67 ms** puis `en_echec`.

### Fournisseurs
Création fournisseur **hors lot** (trop lourd / incomplete localement). Match
par `nom` exact (case-insensitive) ; sinon message clair.

### Dettes inchangées (#1323)
1. MRES ignore reprises.
2. Bilan ignore reprises.
3. Pas de soft delete reprise.

+ Lot F : dépôt documents.

## DÉPLOIEMENT-TEST

```bash
git pull origin main
php artisan migrate --force
php artisan optimize:clear
php artisan view:clear
php artisan journal:sync
# URL : /siwi-import (GS_GROUPE)
```

## LEÇON

Deux colonnes d’état sur le même objet divergent toujours si l’on ne dit pas
laquelle est autoritaire : documenter `etat_liaison_programme` pour le
dégrisage. Un job queue `sync` qui avale les exceptions donne un faux `ok` —
mesurer le retour métier (bool / exception) et écrire un état explicite
(`non_tente`/`en_attente`/`ok`/`en_echec`), jamais NULL. DDL MySQL = commit
implicite : une seule opération de schéma par migration.
