---
suivi: 1780
date: 2026-10-02
sujet: Une facture dont le bill Sage est extourné revient à Comptabilisée
chantier: factures / paiement
type: correctif
statut: poussé
hash:
fichiers:
  - app/Services/FactureExtourneSageConstatService.php
  - app/Services/FacturePaiementStatutSyncService.php
  - app/Services/FactureDecomptabilisationService.php
  - app/Services/FactureSuperAdminCorrectionService.php
  - app/Http/Controllers/FactureController.php
  - app/Http/Controllers/FactureAnnulerComptabilisationController.php
  - app/Http/Controllers/Admin/FactureSuperAdminController.php
  - resources/js/Pages/Admin/FacturesSuperadmin/Show.vue
  - resources/js/Components/Comptabilisation/AnnulerComptabilisationDialog.vue
  - tests/Unit/FactureExtourneSageConstat1780Test.php
  - database/data/journal_mises_a_jour_post_2026_06_12.php
  - docs/suivi/SUIVI_1780_constater_extourne_sage.md
---

# SUIVI #1780 — extourne Sage ramenée à Comptabilisée

Fiches lues : aucune fiche antérieure sur l'extourne d'un bill en cours de paiement. #1778 (CIE) non modifié : `DepotFactureSaisieForm.vue` et `CieService` intacts. Dernier numéro présent avant ce lot : 1778. 1779 absent.

## Étape 0 — constat (avant correctif)

- Route `routes/web.php` 516-519 : GET et POST `/factures/{facture}/annuler-comptabilisation`.
- `FactureAnnulerComptabilisationController` : `show` 17-23 renvoie l'aperçu ; `store` 26-59 refuse si `peut_annuler` est faux, relit les paiements déjà annulés, puis `decomptabiliser`. Droit : groupes comptabilité, admin ERP, DSI (`ensureComptabilisationUser` 62-73).
- `FactureDecomptabilisationService::decomptabiliser` 105-153 : facture et dépôt à `a_comptabiliser`, numéros Sage effacés côté ERP, ventilations détachées, lignes de circuit comptabilisation / mise en paiement / paiement effectif supprimées, retenues de garantie et compte prorata locaux retirés. Aucune écriture Sage dans cette méthode. `relirePaiementsAnnules` 270-342 lit seulement les paiements déjà marqués annulés et bloque si Sage contredit ou ne répond pas.
- `peutDecomptabiliser` 191-211 : faux dès qu'un `paiements_intacct` n'est pas annulé. Sinon vrai si la facture est comptabilisée, en paiement ou payée, ou si le dépôt est comptabilisé.
- Masque sur la fiche : `Factures/Show.vue` 725-727 exige `display_statut === 'comptabilisee'`. Ce statut vient de `factures.statut` (`FactureStatutService::normalize`, appelé `FactureController` vers 799). FAC-2026-1764 est `en_paiement` : le bouton n'est pas affiché. Le formulaire de saisie, lui, regarde le dépôt (`DepotFactureSaisieForm.vue` 3956-3959), déjà `comptabilisee` dans ce cas. Ce fichier n'est pas modifié.
- Paiement préparé : `PaiementAppymtExecutionService` 94-107 crée une ligne `paiements_intacct` et un APPYMT Sage ; 145-151 pose `en_paiement` et l'état préparation. `FactureWorkflowService::synchroniserApresAppymtAch` 92-100 clôt la mise en paiement et ouvre le paiement effectif.
- Déposé sur Suite : pas une table. Libellé de frise quand l'état est `paiement_disponible` (`EtatPaiementSage` 80). La tâche « virement à exécuter » est ouverte par `VirementAExecuterTaskHooks::upsertAfterPaiementDisponible`.
- `STATE=Reversed` : `IntacctPaiementStatusService::mapperVersEtatErp` 484-488 renvoie null. Le bouton concluait `extourne_apbill` et ne changeait rien (`FacturePaiementStatutSyncService` 359-377). La remontée automatique ne posait pas « payé » : sans signal, elle ne mettait à jour que la date de synchro (755-762). `WHENPAID` était lu mais non appliqué.

Décomptabiliser n'envoie aucune suppression de bill. Le correctif n'est pas dangereux sur ce point. Le piège : après avoir marqué le paiement annulé dans l'ERP, la relecture des APPYMT encore `STATE=C` (ou supprimés dans Suite) aurait bloqué le bouton. Cette relecture est sautée seulement si la lecture du bill, faite au clic, dit extourné.

## Correctif

Actualiser statut Sage, et la lecture du panneau super-admin, lorsque chaque bill lié est extourné et que la facture est en cours de paiement : facture et dépôt reviennent à Comptabilisée, le bill reste, les lignes `paiements_intacct` passent à l'état annulé sans suppression, le paiement effectif en attente passe sans objet, la tâche de virement est annulée, historique `extourne_sage_constatee`. Une facture déjà payée confirmée ne recule pas. Une lecture en échec ne conclut pas. Un compte sans le groupe comptabilité, admin ERP ou DSI reçoit un refus 403, lu sur `m365GroupNames()`.

La remontée automatique ignore le bill extourné pour le statut et écrit une incohérence « bill extourné » au lieu de « payé ». Le panneau super-admin affiche « Bill Sage extourné » tant que le numéro est encore lié.

Décomptabiliser, si la lecture du bill dit extourné, détache les numéros côté ERP comme avant et n'appelle pas Sage en écriture. Il ne relit pas les APPYMT dans ce seul cas.

## Tests manuels

Non joués ici : pas d'accès à FAC-2026-1764 ni à Sage depuis cette session. À faire sur cette facture, puis sur une facture payée non extournée, une lecture en échec, et une usurpation sans le groupe.

## LEÇON

`WHENPAID` et `TOTALPAID` sur un bill Reversed ne sont pas un paiement. Les traiter comme tels fige la facture en paiement et cache Décomptabiliser. Revenir à Comptabilisée laisse le numéro Sage en place : l'écriture existe encore, elle est seulement extournée.
