Préparer le JSON de facture pour WS_2
Ce guide explique comment construire le paramètre JsonFacture attendu par
WS_2_CreerFacturX,
en respectant le profil EN 16931 de la norme Factur-X. Ce JSON est beaucoup plus simple à programmer que le XML de Factur-X, les données peuvent être renseignées dans n'importe quel ordre et la conformité au format Factur-X est déléguée à la plateforme agréée, notamment en cas de changement de version du format Factur-X. Ce JSON a aussi l'avantage d'être en français.
Fichier JSON exemple
Facture réelle au format EN 16931 / Factur-X, prête à tester.
Télécharger Facture_Francais_Fnfe.jsonAstuce Windev® — complétion automatique
Importez ce fichier dans Windev® pour obtenir la complétion automatique sur toutes les clés JSON.
Voir la procédure →
- Téléchargez le fichier Facture_Francais_Fnfe.json ci-contre et enregistrez-le localement.
-
Dans l'Explorateur de projet Windev®, sous la section Descriptions externes, cliquez sur Importer un XML ou un JSON dans ce projet…, puis sélectionnez le fichier JSON téléchargé.
- Dans votre code, déclarez votre variable JSON avec l'attribut de description pointant vers le fichier importé :
FacturJson est un JSON <description="Facture_Francais_Fnfe"> - Windev® active la complétion automatique sur toutes les clés lors de la saisie (
FacturJson.vendeur.name,FacturJson.lignes[n].prix_unitaire, etc.).
Structure générale du JSON
Le JSON de facture est divisé en grandes sections. Les sections marquées Obligatoire (EN 16931) doivent impérativement être renseignées pour produire un fichier Factur-X valide.
| Clé JSON | Description | EN 16931 |
|---|---|---|
numero | Numéro de facture | BT-1 — Obligatoire |
date_emission | Date d'émission (YYYY-MM-DD) | BT-2 — Obligatoire |
code_type | Type de facture (380 = facture commerciale, 381 = avoir) | BT-3 — Obligatoire |
code_devise | Code devise ISO 4217 (ex : EUR) | BT-5 — Obligatoire |
code_date_point_tva | Code date de point TVA (ex : "72" = date de facturation) | BT-8 — Obligatoire |
date_echeance_paiement | Date d'échéance du paiement (YYYY-MM-DD) | BT-9 — Obligatoire |
conditions_paiement | Texte libre des conditions de paiement | BT-20 — Recommandé |
controle_processus | Identifiant de spécification (urn:cen.eu:en16931:2017) | BT-24 — Obligatoire |
notes | Tableau de notes (code_sujet + note) | BT-22 — Optionnel |
vendeur | Données du vendeur (BG-4) | BG-4 — Obligatoire |
acheteur | Données de l'acheteur (BG-7) | BG-7 — Obligatoire |
instructions_paiement | Mode et coordonnées de paiement (BG-16) | BG-16 — Recommandé |
totaux | Montants récapitulatifs de la facture (BG-22) | BG-22 — Obligatoire |
detail_tva | Tableau de ventilation par taux TVA (BG-23) | BG-23 — Obligatoire |
lignes | Tableau des lignes de facture (BG-25) | BG-25 — Obligatoire |
En-tête de la facture
L'en-tête regroupe les informations d'identification de la facture, les références croisées et les notes.
| Champ | Type | Exemple | BT / BG |
|---|---|---|---|
numero | chaîne | "F20260023" | BT-1 |
date_emission | chaîne (YYYY-MM-DD) | "2026-01-31" | BT-2 |
code_type | entier | 380 | BT-3 |
code_devise | chaîne | "EUR" | BT-5 |
code_date_point_tva | chaîne | "72" | BT-8 |
date_echeance_paiement | chaîne (YYYY-MM-DD) | "2026-03-02" | BT-9 |
conditions_paiement | chaîne | "PAIEMENT 30 JOURS NET" | BT-20 |
reference_acheteur | chaîne | "SERVEXEC" | BT-10 |
reference_bon_commande | chaîne | "PO201925478" | BT-13 |
reference_contrat | chaîne | "CT2018120802" | BT-12 |
reference_projet | chaîne | "PROJET2547" | BT-11 |
controle_processus : pour un profil EN 16931 standard, utilisez "identifiant_specification": "urn:cen.eu:en16931:2017" et "type_processus_metier": "S1".
Vendeur et acheteur
Les blocs vendeur (BG-4) et acheteur (BG-7) partagent la même structure. Les champs minimaux obligatoires EN 16931 sont le nom, l'adresse postale complète et l'identifiant TVA (ou l'immatriculation légale).
| Champ | Type | Obligatoire | BT / BG |
|---|---|---|---|
name | chaîne | Oui | BT-27 / BT-44 |
identifiant_tva | chaîne | Oui (si pas d'immatriculation) | BT-31 / BT-48 |
identifiant_immatriculation_legale.value | chaîne | Oui (si pas de TVA) | BT-30 / BT-47 |
adresse_postale.ligne_adresse1 | chaîne | Oui | BT-35 / BT-50 |
adresse_postale.ville | chaîne | Oui | BT-37 / BT-52 |
adresse_postale.code_postal | chaîne | Oui | BT-38 / BT-53 |
adresse_postale.code_pays | chaîne (ISO 3166) | Oui | BT-40 / BT-55 |
adresse_electronique.value | chaîne | Recommandé | BT-34 / BT-49 |
contact.point_contact | chaîne | Optionnel | BT-41 / BT-56 |
informations_juridiques_supplementaires | chaîne | Optionnel | BT-33 |
Lignes de facture
Chaque élément du tableau lignes représente une ligne de facture (BG-25).
| Champ | Type | Obligatoire | BT / BG |
|---|---|---|---|
identifiant | chaîne | Oui | BT-126 |
quantite_facturee | chaîne décimale | Oui | BT-129 |
code_quantite_facturee | chaîne (UN/ECE Rec 20) | Oui | BT-130 |
montant_net | chaîne décimale | Oui | BT-131 |
informations_tva.code_categorie_tva_article_facture | chaîne (S, E, Z, K…) | Oui | BT-151 |
informations_tva.taux_tva_article_facture | chaîne décimale | Si catégorie S | BT-152 |
informations_article.name | chaîne | Oui | BT-153 |
informations_article.description | chaîne | Optionnel | BT-154 |
details_prix.prix_net_article | chaîne décimale | Oui | BT-146 |
details_prix.prix_brut_article | chaîne décimale | Oui (si remise prix) | BT-148 |
details_prix.quantite_base_prix_article | chaîne décimale | Optionnel | BT-149 |
details_prix.remise_prix_article | chaîne décimale | Optionnel | BT-147 |
remises | tableau | Optionnel | BG-27 |
frais | tableau | Optionnel | BG-28 |
code_quantite_facturee : utilisez le code UN/ECE Rec 20 approprié — C62 (unité), HUR (heure), DAY (jour), KGM (kilogramme), MTR (mètre), etc.
montant_net : montant HT net de la ligne après application des remises et frais de ligne.
Détail TVA
Le tableau detail_tva (BG-23) contient une entrée par taux de TVA distinct présent dans la facture. La somme des montant_imposable_categorie_tva doit être égale à totaux.total_sans_tva.
| Champ | Type | Obligatoire | BT / BG |
|---|---|---|---|
code_categorie_tva | chaîne (S, E, Z, K, AE…) | Oui | BT-118 |
montant_imposable_categorie_tva | chaîne décimale | Oui | BT-116 |
montant_taxe_categorie_tva | chaîne décimale | Oui | BT-117 |
taux_categorie_tva | chaîne décimale | Si catégorie S | BT-119 |
identifiant_tva | chaîne | Recommandé ("VAT") | BT-118-1 |
motif_exoneration_tva | chaîne | Si catégorie E/K/Z/AE | BT-120 |
code_motif_exoneration_tva | chaîne (VATEX-EU-*) | Si exonération | BT-121 |
Codes catégorie TVA courants : S = taux standard, E = exonéré (art. 261), Z = taux zéro, K = livraison intracommunautaire, AE = autoliquidation.
Totaux de la facture
Le bloc totaux (BG-22) est calculé à partir des lignes, remises et frais au niveau document.
| Champ | Calcul | BT / BG |
|---|---|---|
montant_total_lignes_facture | Somme des montant_net de toutes les lignes | BT-106 |
montant_total_remises | Somme des remises niveau document | BT-107 |
montant_total_frais | Somme des frais niveau document | BT-108 |
total_sans_tva | BT-106 − BT-107 + BT-108 | BT-109 |
montant_total_tva.value | Somme des montant_taxe_categorie_tva | BT-110 |
montant_total_tva.code_devise | Devise (ex: "EUR") | — |
total_avec_tva | BT-109 + BT-110 | BT-112 |
montant_du_pour_paiement | BT-112 (ou ajusté si acomptes) | BT-115 |
Exemple WDLangage complet
La procédure utilise une seule variable de type JSON.
Les lignes de facture et les ventilations TVA sont alimentées par des boucles
sur les tableaux internes de votre application (issus d'une requête, d'une collection, etc.).
Les tableaux taLignes et taTVA sont déclarés comme structures globales
(à adapter à votre modèle de données WinDev).
// ═══════════════════════════════════════════════════════════════
// Structures de données (déclaration globale ou dans l'en-tête)
// ═══════════════════════════════════════════════════════════════
// Structure d'une ligne de facture
STRUCTURE stLigneFacture
sId est chaîne // Numéro de ligne
nQuantite est réel // Quantité facturée
sCodeUnite est chaîne // Code unité UN/ECE (C62, HUR, DAY…)
nMontantNet est réel // Montant HT net de la ligne
nPrixNet est réel // Prix net unitaire
nPrixBrut est réel // Prix brut unitaire (avant remise)
nRemisePrix est réel // Remise sur prix unitaire
sNomArticle est chaîne // Désignation de l'article
sDescArticle est chaîne // Description (optionnel)
sRefVendeur est chaîne // Référence article vendeur
sCategorieTVA est chaîne // Code catégorie TVA : S, E, K, Z, AE…
nTauxTVA est réel // Taux TVA (0 si hors taxe)
FIN
// Structure d'une ligne de ventilation TVA
STRUCTURE stVentilationTVA
sCategorie est chaîne // Code catégorie TVA
nTaux est réel // Taux TVA (0 si exonéré)
nBaseHT est réel // Montant imposable
nMontantTVA est réel // Montant de taxe
sMotifExo est chaîne // Motif exonération (si E/K/Z/AE)
sCodeMotifExo est chaîne // Code VATEX- (si exonéré)
FIN
// Tableaux remplis depuis votre base de données / requête
taLignes est TABLEAU de stLigneFacture
taTVA est TABLEAU de stVentilationTVA
// ═══════════════════════════════════════════════════════════════
// PROCÉDURE ConstruireJsonFacture
// Paramètres : tableaux de lignes et de ventilations TVA
// Retourne : chaîne JSON prête pour WS_2_CreerFacturX
// ═══════════════════════════════════════════════════════════════
PROCÉDURE ConstruireJsonFacture(
taLignes est TABLEAU de stLigneFacture,
taTVA est TABLEAU de stVentilationTVA
) : chaîne
// Variable JSON unique — toute la facture est construite ici
jFacture est JSON
// ── En-tête ──────────────────────────────────────────────────
jFacture.numero = "F20260023" // BT-1
jFacture.date_emission = "2026-01-31" // BT-2 YYYY-MM-DD
jFacture.code_type = 380 // BT-3 380=facture, 381=avoir
jFacture.code_devise = "EUR" // BT-5
jFacture.code_date_point_tva = "72" // BT-8 72=date facture
jFacture.date_echeance_paiement = "2026-03-02" // BT-9
jFacture.conditions_paiement = "PAIEMENT 30 JOURS NET" // BT-20
jFacture.reference_bon_commande = "PO201925478" // BT-13
// Contrôle processus — obligatoire EN16931
jFacture.controle_processus.type_processus_metier = "S1"
jFacture.controle_processus.identifiant_specification = "urn:cen.eu:en16931:2017"
// Notes (optionnel) — ajout direct via jFacture.notes[nIdx]
JSONAjouteElement(jFacture.notes)
nIdx est entier = JSONNombreElements(jFacture.notes) - 1
jFacture.notes[nIdx].code_sujet = "REG"
jFacture.notes[nIdx].note = "SARL au capital de 50 000 EUR – RCS PARIS 123 456 782"
JSONAjouteElement(jFacture.notes)
nIdx = JSONNombreElements(jFacture.notes) - 1
jFacture.notes[nIdx].code_sujet = "PMD"
jFacture.notes[nIdx].note = "Tout retard engendre des pénalités (3× taux légal)."
// ── Vendeur (BG-4) ────────────────────────────────────────────
jFacture.vendeur.name = "MON ENTREPRISE" // BT-27
jFacture.vendeur.identifiant_tva = "FR11123456782" // BT-31
jFacture.vendeur.informations_juridiques_supplementaires = "SARL AU CAPITAL DE 50 000 EUROS"
jFacture.vendeur.adresse_electronique.value = "contact@monentreprise.fr"
jFacture.vendeur.adresse_electronique.scheme = "EM"
jFacture.vendeur.adresse_postale.ligne_adresse1 = "35 rue d'ici" // BT-35
jFacture.vendeur.adresse_postale.ville = "PARIS" // BT-37
jFacture.vendeur.adresse_postale.code_postal = "75018" // BT-38
jFacture.vendeur.adresse_postale.code_pays = "FR" // BT-40
jFacture.vendeur.contact.point_contact = "M. DUPONT"
jFacture.vendeur.contact.numero_telephone = "01 02 03 04 05"
jFacture.vendeur.contact.adresse_e_mail = "contact@monentreprise.fr"
// ── Acheteur (BG-7) ───────────────────────────────────────────
jFacture.acheteur.name = "MON CLIENT" // BT-44
jFacture.acheteur.identifiant_tva = "FR05987654321" // BT-48
jFacture.acheteur.adresse_electronique.value = "compta@monclient.fr"
jFacture.acheteur.adresse_electronique.scheme = "EM"
jFacture.acheteur.adresse_postale.ligne_adresse1 = "1 avenue du Client" // BT-50
jFacture.acheteur.adresse_postale.ville = "LYON" // BT-52
jFacture.acheteur.adresse_postale.code_postal = "69001" // BT-53
jFacture.acheteur.adresse_postale.code_pays = "FR" // BT-55
// ── Instructions de paiement (BG-16) ─────────────────────────
jFacture.instructions_paiement.code_type_moyen_paiement = "30" // 30=virement
jFacture.instructions_paiement.texte_moyen_paiement = "Virement bancaire"
jFacture.instructions_paiement.information_remise = "F20260023"
JSONAjouteElement(jFacture.instructions_paiement.virements)
nIdx = JSONNombreElements(jFacture.instructions_paiement.virements) - 1
jFacture.instructions_paiement.virements[nIdx].identifiant_compte_paiement.value = "FR76 1234 5678 9012 3456 7890 123"
jFacture.instructions_paiement.virements[nIdx].nom_compte_paiement = "MON COMPTE BANCAIRE"
jFacture.instructions_paiement.virements[nIdx].identifiant_fournisseur_service_paiement = "BNPAFRPP"
// ── Lignes : boucle sur le tableau des lignes ─────────────────
// taLignes est rempli depuis votre base de données (requête, collection…)
nI est entier
nTotalLignes est réel = 0
POUR nI = 1 À Dimension(taLignes)
JSONAjouteElement(jFacture.lignes)
nIdx = JSONNombreElements(jFacture.lignes) - 1
jFacture.lignes[nIdx].identifiant = taLignes[nI].sId // BT-126
jFacture.lignes[nIdx].quantite_facturee = NumériqueVersChaine(taLignes[nI].nQuantite, "9.4") // BT-129
jFacture.lignes[nIdx].code_quantite_facturee = taLignes[nI].sCodeUnite // BT-130
jFacture.lignes[nIdx].montant_net = NumériqueVersChaine(taLignes[nI].nMontantNet, "9.2") // BT-131
jFacture.lignes[nIdx].details_prix.prix_net_article = NumériqueVersChaine(taLignes[nI].nPrixNet, "9.4") // BT-146
jFacture.lignes[nIdx].details_prix.prix_brut_article = NumériqueVersChaine(taLignes[nI].nPrixBrut, "9.4") // BT-148
jFacture.lignes[nIdx].details_prix.quantite_base_prix_article = "1.0000"
jFacture.lignes[nIdx].details_prix.code_unite_quantite = taLignes[nI].sCodeUnite
SI taLignes[nI].nRemisePrix 0 ALORS
jFacture.lignes[nIdx].details_prix.remise_prix_article = NumériqueVersChaine(taLignes[nI].nRemisePrix, "9.4") // BT-147
FIN
jFacture.lignes[nIdx].informations_tva.code_categorie_tva_article_facture = taLignes[nI].sCategorieTVA // BT-151
SI taLignes[nI].sCategorieTVA = "S" ALORS
jFacture.lignes[nIdx].informations_tva.taux_tva_article_facture = NumériqueVersChaine(taLignes[nI].nTauxTVA, "9.2") // BT-152
FIN
jFacture.lignes[nIdx].informations_article.name = taLignes[nI].sNomArticle // BT-153
jFacture.lignes[nIdx].informations_article.description = taLignes[nI].sDescArticle // BT-154
jFacture.lignes[nIdx].informations_article.identifiant_vendeur = taLignes[nI].sRefVendeur
nTotalLignes += taLignes[nI].nMontantNet
FIN
// ── Détail TVA : boucle sur le tableau des ventilations ───────
// taTVA : une entrée par taux distinct, calculée avant l'appel
// Σ nBaseHT doit être égal à total_sans_tva (BT-109)
nTotalTVA est réel = 0
POUR nI = 1 À Dimension(taTVA)
JSONAjouteElement(jFacture.detail_tva)
nIdx = JSONNombreElements(jFacture.detail_tva) - 1
jFacture.detail_tva[nIdx].code_categorie_tva = taTVA[nI].sCategorie // BT-118
jFacture.detail_tva[nIdx].identifiant_tva = "VAT"
jFacture.detail_tva[nIdx].montant_imposable_categorie_tva = NumériqueVersChaine(taTVA[nI].nBaseHT, "9.2") // BT-116
jFacture.detail_tva[nIdx].montant_taxe_categorie_tva = NumériqueVersChaine(taTVA[nI].nMontantTVA, "9.2") // BT-117
SI taTVA[nI].nTaux > 0 ALORS
jFacture.detail_tva[nIdx].taux_categorie_tva = NumériqueVersChaine(taTVA[nI].nTaux, "9.2") // BT-119
FIN
SI taTVA[nI].sMotifExo "" ALORS
jFacture.detail_tva[nIdx].motif_exoneration_tva = taTVA[nI].sMotifExo // BT-120
jFacture.detail_tva[nIdx].code_motif_exoneration_tva = taTVA[nI].sCodeMotifExo // BT-121
FIN
nTotalTVA += taTVA[nI].nMontantTVA
FIN
// ── Totaux (BG-22) ────────────────────────────────────────────
// Calculés à partir des boucles ci-dessus
jFacture.totaux.montant_total_lignes_facture = NumériqueVersChaine(nTotalLignes, "9.2") // BT-106
jFacture.totaux.montant_total_remises = "0.00" // BT-107
jFacture.totaux.montant_total_frais = "0.00" // BT-108
jFacture.totaux.total_sans_tva = NumériqueVersChaine(nTotalLignes, "9.2") // BT-109
jFacture.totaux.montant_total_tva.value = NumériqueVersChaine(nTotalTVA, "9.2") // BT-110
jFacture.totaux.montant_total_tva.code_devise = "EUR"
jFacture.totaux.total_avec_tva = NumériqueVersChaine(nTotalLignes + nTotalTVA, "9.2") // BT-112
jFacture.totaux.montant_du_pour_paiement = NumériqueVersChaine(nTotalLignes + nTotalTVA, "9.2") // BT-115
// ── Sérialisation finale ──────────────────────────────────────
RENVOYER SerialiserJSON(jFacture) Utilisation avec WS_2_CreerFacturX
// ── Remplissage des tableaux depuis votre modèle de données ──
// (exemple — à adapter à vos tables WinDev)
taLignes est TABLEAU de stLigneFacture
taTVA est TABLEAU de stVentilationTVA
// Ligne 1 : prestation (TVA 20 %)
taLignes[1].sId = "1"
taLignes[1].nQuantite = 2
taLignes[1].sCodeUnite = "HUR"
taLignes[1].nMontantNet = 200.00
taLignes[1].nPrixNet = 100.00
taLignes[1].nPrixBrut = 100.00
taLignes[1].sNomArticle = "PRESTATION DE CONSEIL"
taLignes[1].sDescArticle = "Conseil informatique — janvier 2026"
taLignes[1].sRefVendeur = "ART_0001"
taLignes[1].sCategorieTVA = "S"
taLignes[1].nTauxTVA = 20.00
// Ligne 2 : fournitures (TVA 10 %)
taLignes[2].sId = "2"
taLignes[2].nQuantite = 5
taLignes[2].sCodeUnite = "C62"
taLignes[2].nMontantNet = 125.00
taLignes[2].nPrixNet = 25.00
taLignes[2].nPrixBrut = 30.00
taLignes[2].nRemisePrix = 5.00
taLignes[2].sNomArticle = "FOURNITURES BUREAU"
taLignes[2].sDescArticle = "Ramettes papier A4 80g"
taLignes[2].sRefVendeur = "ART_0254"
taLignes[2].sCategorieTVA = "S"
taLignes[2].nTauxTVA = 10.00
// Ventilations TVA
taTVA[1].sCategorie = "S"
taTVA[1].nTaux = 20.00
taTVA[1].nBaseHT = 200.00
taTVA[1].nMontantTVA = 40.00
taTVA[2].sCategorie = "S"
taTVA[2].nTaux = 10.00
taTVA[2].nBaseHT = 125.00
taTVA[2].nMontantTVA = 12.50
// ── Appel WS_2 ───────────────────────────────────────────────
sJsonFacture est chaîne = ConstruireJsonFacture(taLignes, taTVA)
sContenuPDF est chaîne = Encodage(
fChargeBuffer("C:\Factures\FAC_2026_023.pdf"),
encodageBase64URL
)
sFacturX est chaîne = WS_2_CreerFacturX(
gsToken, // Token obtenu via WS_1
sJsonFacture, // JSON construit par ConstruireJsonFacture()
sContenuPDF // PDF source en Base64
)
SI Gauche(sFacturX, 6) = "Erreur" ALORS
Erreur("Échec WS_2 :" + RC + sFacturX)
RENVOYER
FIN
// sFacturX contient le Factur-X PDF/A-3 en Base64
// → Transmettre via WS_8_EnvoyerFactureDeVente Validation préalable : avant d'envoyer à la PA, utilisez WS_3_ValidationFacturX pour vérifier la conformité du fichier Factur-X généré.
Voir aussi
- WS_2_CreerFacturX — référence du point d'entrée
- WS_3_ValidationFacturX — valider un Factur-X existant
- WS_8_EnvoyerFactureDeVente — envoyer le Factur-X à la PA
- Lexique — définition des termes EN 16931 (BT/BG)
Documentation API
Retour