DevVersPA
Documentation API
Retour

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.json

Astuce 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 →
  1. Téléchargez le fichier Facture_Francais_Fnfe.json ci-contre et enregistrez-le localement.
  2. 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é. Menu Descriptions externes — Importer un XML ou un JSON dans ce projet
  3. 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">
  4. 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é JSONDescriptionEN 16931
numeroNuméro de factureBT-1 — Obligatoire
date_emissionDate d'émission (YYYY-MM-DD)BT-2 — Obligatoire
code_typeType de facture (380 = facture commerciale, 381 = avoir)BT-3 — Obligatoire
code_deviseCode devise ISO 4217 (ex : EUR)BT-5 — Obligatoire
code_date_point_tvaCode date de point TVA (ex : "72" = date de facturation)BT-8 — Obligatoire
date_echeance_paiementDate d'échéance du paiement (YYYY-MM-DD)BT-9 — Obligatoire
conditions_paiementTexte libre des conditions de paiementBT-20 — Recommandé
controle_processusIdentifiant de spécification (urn:cen.eu:en16931:2017)BT-24 — Obligatoire
notesTableau de notes (code_sujet + note)BT-22 — Optionnel
vendeurDonnées du vendeur (BG-4)BG-4 — Obligatoire
acheteurDonnées de l'acheteur (BG-7)BG-7 — Obligatoire
instructions_paiementMode et coordonnées de paiement (BG-16)BG-16 — Recommandé
totauxMontants récapitulatifs de la facture (BG-22)BG-22 — Obligatoire
detail_tvaTableau de ventilation par taux TVA (BG-23)BG-23 — Obligatoire
lignesTableau 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.

ChampTypeExempleBT / BG
numerochaîne"F20260023"BT-1
date_emissionchaîne (YYYY-MM-DD)"2026-01-31"BT-2
code_typeentier380BT-3
code_devisechaîne"EUR"BT-5
code_date_point_tvachaîne"72"BT-8
date_echeance_paiementchaîne (YYYY-MM-DD)"2026-03-02"BT-9
conditions_paiementchaîne"PAIEMENT 30 JOURS NET"BT-20
reference_acheteurchaîne"SERVEXEC"BT-10
reference_bon_commandechaîne"PO201925478"BT-13
reference_contratchaîne"CT2018120802"BT-12
reference_projetchaî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).

ChampTypeObligatoireBT / BG
namechaîneOuiBT-27 / BT-44
identifiant_tvachaîneOui (si pas d'immatriculation)BT-31 / BT-48
identifiant_immatriculation_legale.valuechaîneOui (si pas de TVA)BT-30 / BT-47
adresse_postale.ligne_adresse1chaîneOuiBT-35 / BT-50
adresse_postale.villechaîneOuiBT-37 / BT-52
adresse_postale.code_postalchaîneOuiBT-38 / BT-53
adresse_postale.code_payschaîne (ISO 3166)OuiBT-40 / BT-55
adresse_electronique.valuechaîneRecommandéBT-34 / BT-49
contact.point_contactchaîneOptionnelBT-41 / BT-56
informations_juridiques_supplementaireschaîneOptionnelBT-33

Lignes de facture

Chaque élément du tableau lignes représente une ligne de facture (BG-25).

ChampTypeObligatoireBT / BG
identifiantchaîneOuiBT-126
quantite_factureechaîne décimaleOuiBT-129
code_quantite_factureechaîne (UN/ECE Rec 20)OuiBT-130
montant_netchaîne décimaleOuiBT-131
informations_tva.code_categorie_tva_article_facturechaîne (S, E, Z, K…)OuiBT-151
informations_tva.taux_tva_article_facturechaîne décimaleSi catégorie SBT-152
informations_article.namechaîneOuiBT-153
informations_article.descriptionchaîneOptionnelBT-154
details_prix.prix_net_articlechaîne décimaleOuiBT-146
details_prix.prix_brut_articlechaîne décimaleOui (si remise prix)BT-148
details_prix.quantite_base_prix_articlechaîne décimaleOptionnelBT-149
details_prix.remise_prix_articlechaîne décimaleOptionnelBT-147
remisestableauOptionnelBG-27
fraistableauOptionnelBG-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.

ChampTypeObligatoireBT / BG
code_categorie_tvachaîne (S, E, Z, K, AE…)OuiBT-118
montant_imposable_categorie_tvachaîne décimaleOuiBT-116
montant_taxe_categorie_tvachaîne décimaleOuiBT-117
taux_categorie_tvachaîne décimaleSi catégorie SBT-119
identifiant_tvachaîneRecommandé ("VAT")BT-118-1
motif_exoneration_tvachaîneSi catégorie E/K/Z/AEBT-120
code_motif_exoneration_tvachaîne (VATEX-EU-*)Si exonérationBT-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.

ChampCalculBT / BG
montant_total_lignes_factureSomme des montant_net de toutes les lignesBT-106
montant_total_remisesSomme des remises niveau documentBT-107
montant_total_fraisSomme des frais niveau documentBT-108
total_sans_tvaBT-106 − BT-107 + BT-108BT-109
montant_total_tva.valueSomme des montant_taxe_categorie_tvaBT-110
montant_total_tva.code_deviseDevise (ex: "EUR")
total_avec_tvaBT-109 + BT-110BT-112
montant_du_pour_paiementBT-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