---
suivi: 1766
date: 2026-09-30
sujet: Remplacer le document principal d'une facture
chantier: factures / dépôt
type: évolution
statut: poussé
hash:
fichiers:
  - app/Services/DepotDocumentPrincipalRemplacementService.php
  - app/Http/Controllers/DepotDocumentPrincipalController.php
  - app/Http/Controllers/FactureController.php
  - app/Support/FactureHistoriquePresenter.php
  - routes/web.php
  - resources/js/Pages/Factures/Show.vue
  - database/data/journal_mises_a_jour_post_2026_06_12.php
  - tests/Unit/DepotDocumentPrincipalRemplacement1766Test.php
  - public/build/
  - docs/suivi/SUIVI_1766_remplacer_document_principal.md
---

# SUIVI #1766 — remplacer le document principal

## PROMPT ENVOYÉ

Un devis validé NV1 et NV2 doit pouvoir être remplacé par la vraie facture avant comptabilisation. Le nouveau PDF devient le document principal (aperçu et envoi à Sage). L'ancien reste en pièce jointe « Devis remplacé — nom — date ». Bouton réservé à GS_COMPTABILITE ou GS_ADMIN_ERP via m365GroupNames() brut. Alerte sur les montants. Pas de retour en validation. Pas d'appel Sage.

## FICHES LUES

- #1765 : même zone fiche facture / routes / journal sujet factures. Leçon : lot en série, rebase avant le build, ne pas retoucher le suivi chèque.
- Aucune fiche sur le PDF principal ou les pièces jointes dans les SUIVI 17xx. Constat fait dans le code.

## CONSTAT

- Document principal dépôt : DepotFactureController.php:229-232 écrit le disque `factures`, chemin `{entité|sans-entite}/{année}/{uuid}.pdf`. Colonne `depot_factures.chemin_stockage` posée à DepotFactureController.php:459. Colonnes DepotFacture.php:164-167 (`nom_fichier_original`, `chemin_stockage`, `taille_fichier`).
- Aperçu : DepotFactureController.php:3120-3138, GET `/depot-factures/{uuid}/pdf`, fichier local. Pas SharePoint.
- Ancien chemin programme : FactureController.php:350 et FactureController.php:1634-1636, `factures.fichier_path` sous `storage/app/`. L'aperçu fiche privilégie désormais le dépôt quand `depot_uuid` existe, pour coller au fichier envoyé à Sage.
- Pièces jointes : table `documents_factures`, DocumentFactureController.php:26-40, stockage DocumentFactureStorage.php:22-28 sous `.../joints/`. Le devis remplacé n'est pas recopié : la ligne pointe le chemin déjà stocké.
- Sage à la comptabilisation : ComptabilisationController.php:1660-1682 lit `chemin_stockage` sur le disque `factures` et appelle createSupdocFacturePdf. Remplacer la colonne suffit. Ce lot n'appelle pas Sage.
- Historique : Historisable.php:14-38, action `document_principal_remplace`.
- Relire la facture : non proposé. DepotFactureController.php:903-915 refuse l'analyse hors statuts de saisie (422). Les montants restent éditables par Modifier.

## SYNTHÈSE

Bouton « Remplacer le document » sur la fiche, prop serveur `peut_remplacer_document`. Route POST `depot-factures.remplacer-document` dans le groupe auth, sans middleware de groupes. Contrôle brut GS_COMPTABILITE ou GS_ADMIN_ERP. Facture déjà comptabilisée (ou au-delà) : bouton absent, route 403. Le nouveau PDF est écrit avant la transaction. Si l'enregistrement échoue, seul ce nouveau fichier est supprimé. L'ancien n'est jamais effacé. NV1 et NV2 inchangés.

## DÉPLOIEMENT-TEST

git pull origin main
php artisan route:clear
php artisan view:clear
php artisan journal:sync

Pas de migration. Pas de build sur le serveur.

1. Usurpation compta, facture validée non comptabilisée : bouton, alerte des montants, Remplacer, PDF affiché = nouvelle facture, devis en pièce jointe, historique tracé.
2. Comptabilisation ensuite : le PDF principal (la facture) part dans Sage.
3. Usurpation hors compta : bouton absent, POST 403.
4. Facture comptabilisée : bouton absent.
5. Échec d'enregistrement : le nouveau fichier est supprimé, l'ancien reste. Couvert par le test unitaire (Storage::fake). L'usurpation navigateur n'a pas été rejouée depuis cette session.

## LEÇON

L'ancien devis doit rester à son chemin. Seul le fichier uuid neuf est supprimé si la transaction échoue. Ne pas passer par la suppression des pièces jointes, qui efface le fichier. Ne pas proposer l'OCR après validation : l'analyse est refusée dès que le statut a quitté la saisie.
