---
suivi: 1947
date: 2026-10-16
sujet: Diag FE ExtBillUpdate — dimension Fournisseur par ligne
chantier: factures-electroniques / comptabilisation
type: diagnostic
statut: poussé
hash: a05f4a4cc
fichiers:
  - app/Intacct/Functions/AccountsPayable/ExtBillUpdate.php
  - app/Intacct/Functions/AccountsPayable/ExtBillCreate.php
  - app/Services/IntacctService.php
  - app/Services/FactureElectronique/FactureElectroniqueComptabilisation.php
  - app/Services/FactureElectronique/FactureElectroniqueSage.php
  - app/Http/Controllers/ComptabilisationController.php
  - tests/Unit/FactureElectroniqueComptabilisation1853Test.php
---

# SUIVI #1947 — diagnostic FE : « Sélectionner une Fournisseurdimension »

## Fiches lues

- #1853 LEÇON / mapping : VENDORID documenté **en en-tête seulement** ; lignes = ACCOUNTNO, LOCATIONID, CLASSID, PROJECTID, DEPARTMENTID. Pas de VENDORID ligne. ExtBillUpdate, jamais second bill, jamais paiement.
- #1945 LEÇON : même page Comptabilisation ; compte/mode exigés mais non envoyés à Sage ; Net prépayé = affichage. Propositions P2-1 (établissement) et P2-3 (prépayé) touchent les mêmes fichiers front/controller → **sérialiser** avec ce lot.
- #1940–#1942 : paiement FE uniquement volet Paiement ; raccourci ≠ ExtBillUpdate.
- #1845 LEÇON : import lecture seule du brouillon ; `intacct_brouillon_recordno` ≠ `intacct_bill_id`.

## PROMPT ENVOYÉ

PHASE 1 diagnostic. Dépôt 2541 BECM F4050, E-04, ExtBillUpdate échoue : « Sélectionner une Fournisseurdimension… ». Message utilisateur MESSAGE_REJOUABLE. Reconstituer payload FE vs classique, VENDORID en-tête/lignes, lignes du brouillon d’origine, paiement FE, tests #1853, proposition PHASE 2. Coordination #1945. Ne rien renvoyer à Sage.

## SYNTHÈSE

### Cause (très probable)

`ExtBillUpdate` pose `VENDORID` **uniquement en en-tête**. Les `APBILLITEM` n’ont pas de dimension Fournisseur. Le classique (`create` / `ExtBillCreate`) pose `VENDORID` **sur chaque ligne** via `BillLineCreate::setVendorId($spec['vendor_id'])`. Sage HECTARE exige cette dimension au niveau ligne → rejet ; le brouillon reste Draft → message « L'envoi n'a pas abouti. Le brouillon est toujours en préparation… » (`MESSAGE_REJOUABLE`).

Le mapping #1853 a officialisé l’oubli (VENDORID = en-tête seul).

### Chaîne (dépôt 2541)

`envoyerIntacctCompta` → `executerComptabilisationApbill` → `envoyerDepotApbillEtPaiementIntacct` (`ComptabilisationController.php:1972-1976` : branche FE immédiate) → `completerBrouillonElectronique` (`:1876`, log `:1909`, wrap MESSAGE_REJOUABLE `:1922-1923`) → `FactureElectroniqueComptabilisation::envoyer` (`:95-98`) → `IntacctService::updateApbillBrouillonElectronique` (`:2568-2577`) → `ExtBillUpdate::fonction` + `sendComptabilisationRequest(…, 'E-04')`.

### 1. Payload FE reconstitué (masqué)

Produit par `ExtBillUpdate.php:52-60` + `ligne()` `:76-109`. Aucun envoi pendant ce diagnostic. Reconstructible aussi via `POST …/apercu-envoi-sage` (`ComptabilisationController.php:773-806` → `apercu()` sans POST Sage d’update — **une lecture** d’en-tête brouillon seulement).

    <function controlid="elec2541uXXXXXXXX">
      <update><APBILL>
        <RECORDNO>…</RECORDNO>
        <VENDORID>F***</VENDORID>          <!-- en-tête SEUL -->
        <WHENDUE>MM/DD/YYYY</WHENDUE>
        <ACTION>Submit</ACTION>
        <APBILLITEMS>
          <APBILLITEM>
            <ACCOUNTNO>6*****</ACCOUNTNO>
            <TRX_AMOUNT>…</TRX_AMOUNT>
            <LOCATIONID>…</LOCATIONID>     <!-- si saisi -->
            <CLASSID>…</CLASSID>
            <PROJECTID>…</PROJECTID>
            <DEPARTMENTID>…</DEPARTMENTID>
            <TAXENTRIES>…</TAXENTRIES>
            <!-- PAS de VENDORID ligne -->
          </APBILLITEM>
          <!-- retenues : montants négatifs, même absence VENDORID -->
        </APBILLITEMS>
      </APBILL></update>
    </function>

`VENDORID` : en-tête `ExtBillUpdate.php:55`. Lignes : boucle LOCATIONID/CLASSID/PROJECTID/DEPARTMENTID seulement (`:92-102`) — **pas** de clé `vendor_id`.

### 2. Payload classique (create) — comparaison

| Champ | Classique create | FE update |
| --- | --- | --- |
| VENDORID en-tête | oui (`ExtBillCreate.php:39`) | oui (`ExtBillUpdate.php:55`) |
| **VENDORID par ligne** | **oui** (`IntacctService.php:2104` `setVendorId` ; XML `ExtBillCreate.php:92`) | **non** |
| LOCATIONID / DEPT / PROJECT / CLASS | oui | oui |
| WHENCREATED, RECORDID, DOCNUMBER, CURRENCY… | oui | non (conservés du brouillon) |
| ACTION Submit + RECORDNO | non | oui |
| APPYMT / payment | optionnel hors FE | jamais (`envoyerDepot…` court-circuite) |

### 3. Lignes du brouillon importé (#1845)

- Import : lecture seule d’un Draft e-invoice déjà créé dans Sage ; ERP ne crée pas les lignes.
- Relecture ERP (`FactureElectroniqueSage.php:92`) : champs **en-tête seulement** (RECORDNO, VENDORID, TOTALENTERED, STATE…) — **aucune** APBILLITEM lue.
- Update : envoie un bloc `APBILLITEMS` sans `LINE_NO` / `RECORDNO` de ligne / `<delete>`. Comportement Sage (remplacement vs ajout) **non déterminable** depuis le code.
- Dans les deux hypothèses (remplace ou ajoute), les lignes poussées par Hectarion **n’ont pas** VENDORID → même rejet dimension.

### 4. Paiement FE — réponse nette

**Non.** `envoyerDepotApbillEtPaiementIntacct` pour une FE retourne uniquement `completerBrouillonElectronique` (`:1974-1976`) — aucun APPYMT, aucun `payment` dans ExtBillUpdate.

Garde-fous déjà en place :
- `executerComptabilisationApbill` refuse `inclureApercuPaiement` pour FE (`:856-860`) ;
- `confirmerPaiementApresCompta` → MESSAGE_PAIEMENT_VOLET ;
- commentaire service : « Aucun create, aucun paiement ».

**FE prépayée (reste 0)** si succès ExtBillUpdate : le bill passerait Submitted **sans** paiement Sage. Le prépayé XML n’est pas lu dans ce chemin. Le volet Paiement pourrait ensuite proposer un montant à tort (#1945 P2-4 / suite) — hors cause du rejet actuel.

Conformité règle « compta ≠ paiement FE » : **déjà respectée** sur ce chemin. PHASE 2 = ne pas réintroduire de paiement, pas le retirer.

### 5. Pourquoi #1853 n’a pas vu le rejet

- Test XML (`FactureElectroniqueComptabilisation1853Test.php:19-68`) : assert VENDORID **une fois** (en-tête) ; aucune assert « un VENDORID par APBILLITEM » ; pas de comparaison au XML classique.
- Envoi mocké (`createMock` IntacctService) — zéro appel Sage réel.
- Tests source (présence de chaînes) — pas de règles de dimensions société.
- Mapping #1853 a documenté VENDORID en-tête seul → le trou était « spécifié ».

### Coordination #1945

Même page (`DepotFactureSaisieForm`, `ComptabilisationController`). #1945 = UI/obligations compte & établissement ; #1947 = XML ExtBillUpdate VENDORID ligne. Recouvrement files à sérialiser (pas deux builds front parallèles). P2-1 #1945 (LOCATIONID optionnel) ne doit pas retirer une dimension encore exigée par Sage : traiter dimensions ensemble.

## Proposition PHASE 2 (pas coder)

| # | Correctif | Fichiers | Risque | Tests |
| --- | --- | --- | --- | --- |
| P2-A | Poser `<VENDORID>` sur chaque `APBILLITEM` (= `vendor_id` du spec), comme `IntacctService.php:2104` | `ExtBillUpdate.php` `ligne()` ; évent. passer `vendor_id` dans chaque line_spec depuis `preparerBrouillonElectronique` | Inchangé en-tête ; multi-fournisseur FE déjà refusé | XML : ≥1 VENDORID/ligne ; égalité en-tête ; dépôt 2541 manuel |
| P2-B | Assert régression : XML FE contient VENDORID dans chaque APBILLITEM ; pas d’APPYMT ; pas de create | `FactureElectroniqueComptabilisation1853Test.php` | Faible | Unit |
| P2-C | (Option) Lire lignes brouillon avant update pour confirmer replace vs append ; documenter | `FactureElectroniqueSage` | Lecture Sage seule | Diag / aperçu enrichi |
| P2-D | Confirmer (test source) absence paiement FE sur `envoyerDepotApbillEtPaiementIntacct` | déjà vrai ; renforcer test | Nul | Unit |
| P2-E | Manuel prod 2541 après correctif : Aperçu XML → VENDORID lignes → Comptabiliser → Draft → Submitted ; pas d’APPYMT | — | Rejeu MESSAGE_REJOUABLE si autre dimension | Prod |

**Hors scope / ne pas toucher** : flux create classique ; second bill ; `montant_ht` ; CIE ; volet Paiement (sauf alerte prépayé déjà #1945).

**Garde-fous** : rien envoyé pendant le diag ; PHASE 2 = update seul ; centime TTC ; payé confirmé ; paiement FE = volet Paiement.

## DÉPLOIEMENT-TEST

Diagnostic seul : `git pull origin main`. Pas de migrate / build / journal:sync.

Contrôle sans écriture : Aperçu de l’envoi Sage sur 2541 → XML sans VENDORID dans APBILLITEM (preuve visuelle). Ne pas recliquer Comptabiliser tant que P2-A n’est pas poussé (rejoue le même rejet).

## LEÇON

Le classique pose la dimension Fournisseur **par ligne** ; le FE ExtBillUpdate #1853 ne l’a posée qu’en en-tête, et le mapping du lot l’a figé. Un test unitaire qui assert un seul `<VENDORID>` et mocke Sage ne voit jamais une règle de dimension société. MESSAGE_REJOUABLE masque l’IntacctException métier : toujours lire `laravel.log` pour la cause Sage réelle.
