---
suivi: 1800
date: 2026-10-07
sujet: Arrêt — montant_ht sert à l'écriture Sage
chantier: factures
type: diagnostic
statut: poussé
hash: 7fe43689
fichiers:
  - docs/suivi/SUIVI_1800_montant_ht_plafond.md
---

# SUIVI #1800 — arrêt avant correctif

Étape 0 seulement. Aucun code. Aucune écriture en base. Aucun appel Sage.

Le correctif demandé (recalculer `depot_factures.montant_ht` depuis les lignes, et s’en servir pour le plafond) est arrêté : cette colonne sert à l’écriture Sage sur le chemin multi-fournisseur.

Fiches lues : #1799 (le plafond lit la colonne `montant_ht`, pas les lignes), #1778 (les lignes Sage CIE viennent des écritures, pas de cette colonne), #1796 (libération RG hors sujet). Base locale : `127.0.0.1:3306` refusée. Les 22 écarts et les quatre dépassements confirmés n’ont pas été relus ici. Les chiffres cités sont ceux du prompt (lectures prod du 06/10).

## a. Qui écrit quoi

| Montant | Où | Qui l’écrit | Quand |
|---|---|---|---|
| `depot_factures.montant_ht` | colonne | `DepotFactureClaudeAnalysisService.php:109` | à l’analyse OCR, valeur unique `montant_ht` du JSON |
| même colonne | | `DepotFactureController.php:1964-1967` | à chaque enregistrement, copie de `validated['montant_ht']` |
| `ocr_raw.saisie.montant_ht` | JSON | `DepotFactureClaudeAnalysisService.php:507-511` puis `:96` | OCR, si le nombre est présent |
| même clé | | `DepotFactureController.php:1852` | enregistrement, même `validated['montant_ht']` |
| Lignes « Base HT à régler » | `form.lignes[].base_ht`, stockées en `saisie.lignes_tva` | saisie écran ; à défaut une ligne bâtie depuis `montant_ht` (`FactureController.php:1685-1696`) | le `watch` (`DepotFactureSaisieForm.vue:5269-5276`) recopie la somme dans `form.montant_ht` |
| HT brut du décompte | calcul d’écran (base reconstituée si retenues) | pas une colonne | affichage |
| HT à régler | somme des `base_ht` | pas une colonne distincte de `montant_ht` une fois le formulaire synchronisé | |

Le prompt OCR (`DepotFactureClaudeAnalysisService.php:153` et la règle « Montants : nombres décimaux ») demande un seul `montant_ht`. Il ne distingue pas le cumul « Montant H.T. » de la ligne de situation.

Dépôt 2216, lecture prod citée : colonne et `saisie.montant_ht` = 77 592,80 ; lignes base = 3 716,80. Le plafond serveur (`MontantFactureSousTraitantService.php:898-910`) prend la colonne dès qu’elle est positive et ignore le montant posté.

## Sage — motif d’arrêt

L’envoi APBILL lit la colonne.

Facture mono (`ComptabilisationController.php:1573` puis `:1604-1606`) : `montantHtFacture` part de `depot.montant_ht`, puis est remplacé par la somme des débits des lignes comptables dès que cette somme dépasse 0. Le `trx_amount` Sage (`:2165-2169`, `IntacctService.php:2093`) est le débit de la ligne, pas la colonne. Le paramètre `$montantHtFacture` de `buildApbillLineSpecsFromCandidates` (`:2122`) n’est pas utilisé pour le montant de ligne.

Facture multi-fournisseur sans étiquette `ventilation_fournisseur_id` sur les lignes (`:2360-2372` et `:2539`) : le ratio est `ventilation.montant_ht / depot.montant_ht`. Ce ratio multiplie les montants envoyés à Sage. Changer le sens de la colonne change les montants du bill.

Autres usages de la colonne dans le même envoi, qui ne portent pas le `trx_amount` mono :

- garde : HT nul ou négatif interdit l’envoi, sauf avoir (`:1094-1102`) ;
- photo d’envoi `photoEnvoiSage.montant_ht` (`:1653`), après remplacement par la somme des lignes en mono ;
- à la validation, `factures.montant_ht` est copié depuis le montant validé (`DepotFactureController.php:2397`), fiche ERP, pas le XML Sage ;
- avoir Sage : la photo retient la somme des `trx_amount` (`AvoirIntacctApbillService.php:143-145`), et la colonne sert de garde de signe (`:496-501`).

Les écritures générées à l’écran partent des `base_ht` (`ventilationsMonoDepuisLignesTva`, `DepotFactureSaisieForm.vue:6968-6979`), pas de la colonne. Le chemin mono déjà étiqueté n’envoie donc pas 77 592,80 à Sage si les lignes comptables ont été générées depuis 3 716,80. Le chemin multi non étiqueté, lui, s’en sert.

Recalculer `montant_ht` en HT brut, comme demandé au point 2 du correctif, modifierait ce ratio. Le lot s’arrête avant tout code.

## b. Les 22 écarts

Non classés. La base locale est injoignable, et les lignes `lignes_tva` n’ont pas été relues. Sans le HT brut recalculé depuis ces lignes, on ne sépare pas un écart normal (brut vs à régler, avoir) d’un cumul OCR resté en en-tête.

Seule ligne déjà qualifiée par la lecture prod citée :

| id | numéro | statut | montant_ht | saisie.montant_ht | HT brut recalculé | classe |
|---|---|---|---|---|---|---|
| 2216 | 2026-6192 SIT 11 | non relu | 77 592,80 | 77 592,80 | 3 716,80 (lignes citées) | (2) cumul OCR resté en en-tête |

Exemples cités, classe non posée : 2333 (266,59 / 1 332,97), 2282 (4 490,95 / 14 969,86), 2231 (−346,15 / 290,31), 2278 (338,82 / 408,62), 2289 (24 251,96 / 26 190,02), saisies à 0. La commande de classement est en bas.

## c. Dépassements confirmés

Non recalculés. Il manque, pour chaque marché, le plafond, la reprise et le HT brut de chaque dépôt. Poser « réel » ou « faux » sans ces lignes inventerait le cumul.

| marché | plafond cité | cumul logué | dépassement réel |
|---|---|---|---|
| 309 | 1 140 | 59 169 | non calculé |
| 37 | 132 189 | 189 094 | non calculé |
| 261 | 689 945 | 1 260 556 | non calculé |
| 273 | 6 900 | 48 260 | non calculé |

## d. CIE dépôt 2216

Le texte « Retenues CIE disponibles non chargées » est posé uniquement quand le GET échoue (`DepotFactureSaisieForm.vue:4653-4662` et le `catch` `:4851-4857`). Une réponse 200 avec une liste vide affiche « aucun CIE dû » (`:4755-4756`).

L’appel part dès que marché et fournisseur sont remplis (`:4739-4741`), même si le poste n’a pas de CIE. `authorizeCieRead` (`AuthorizesCieAccess.php:29-37`) répond 403 hors des groupes de lecture ; le front affiche alors le même texte. `applicable_cie` absent est traité comme vrai (`DepotFactureController.php:4466`).

Logs locaux : aucune ligne pour ce dépôt. Le statut HTTP du 2216 n’est pas connu d’ici. Cause certaine : l’appel a échoué. Cause précise (403, 500 ou délai) : le grep serveur, en bas. Rien n’a été corrigé.

## Commandes (lecture seule)

Classement des écarts (HT brut = somme `saisie.lignes_tva[].base_ht`, sinon non calculé) :

```text
php artisan tinker --execute='$rows=App\Models\DepotFacture::query()->whereNotNull("ocr_raw")->orderByDesc("id")->limit(400)->get(["id","numero_facture","statut","montant_ht","ocr_raw"]); $out=[]; foreach($rows as $d){ $saisie=data_get($d->ocr_raw,"saisie.montant_ht"); $ht=(float)$d->montant_ht; if(!(is_numeric($saisie)&&abs($ht-(float)$saisie)>0.009)){ continue; } $lignes=data_get($d->ocr_raw,"saisie.lignes_tva"); $brut=null; if(is_array($lignes)&&$lignes!==[]){ $brut=0.0; foreach($lignes as $l){ if(is_array($l)){ $brut+= (float)($l["base_ht"]??0); } } $brut=round($brut,2); } $classe="non_calcule"; if($brut!==null){ $classe=abs($ht-$brut)<=0.009?"1_ecart_normal_ou_lignes_egales_entete":(abs((float)$saisie-$ht)<=0.009&&abs($ht-$brut)>1?"2_entete_different_des_lignes":"3_autre"); } $out[]=["id"=>$d->id,"numero"=>$d->numero_facture,"statut"=>$d->statut,"montant_ht"=>$ht,"saisie"=>$saisie,"ht_brut_lignes"=>$brut,"classe"=>$classe]; } echo json_encode($out, JSON_UNESCAPED_UNICODE);'
```

Dépassements (marchés 309, 37, 261, 273) : même lecture que #1799, une ligne par marché, sans écrire.

```text
grep -n "cie-retenue-dues\|Retenues CIE\|2216" storage/logs/laravel.log | tail -n 80
```

## LEÇON

`montant_ht` n’est pas qu’un indicateur de marché. Sur un dépôt multi-fournisseur dont les lignes comptables ne sont pas étiquetées par ventilation, c’est le dénominateur qui répartit les montants envoyés à Sage. Un correctif de plafond qui réécrit cette colonne doit d’abord sortir la colonne du calcul Sage, ou introduire un montant de marché distinct. Ne pas le faire dans le même lot que le plafond.
