Aller au contenu principal

Créer une facture

L'API créer une facture permet aux utilisateurs de l'OD, à partir de leurs données de facturation, de créer des factures de vente et de générer les fichiers correspondants au format Factur-X, CII ou UBL

Pré-requis

Requête

Pour créer une facture de vente sur l'OD on fait une requête POST sur le chemin d'accès suivant:

v1/ventes/factures

On doit passer:

  • L'identifiant utilisateur de l'OD dans l'en-tête X-OD-User-ID
  • Les données de facturation au format Dougs dans le corps de la requête
  • Optionnellement :
    • Le format de la facture (syntaxe) dans l'en-tête X-OD-Syntaxe
    • L'en-tête X-OD-A_Envoyer avec la valeur true pour créer et envoyer la facture en une seule étape

Les syntaxes possibles sont:

SyntaxeSupportée
Factur-XOui (défaut)
CIINon
UBLNon

Par exemple:

Requête de création d'une facture au format Factur-X
curl \
-X POST \
-H 'X-OD-User-ID: ${userId}' \
-H 'X-OD-Syntaxe: Factur-X' \
-H 'X-OD-A-Envoyer: true' \
-H 'Content-Type: application/json; charset=utf-8' \
-d @facture.json \
http://<host>:<port>/v1/ventes/factures
Données de facturation: Fichier facture.json
{
"logo": {
"mimeType": "image/svg+xml",
"base64": "PHN2ZyBoZWlnaHQ9IjI2IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxnIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+PHBhdGggZD0iTTM2LjUgMjAuNGMtMS41IDAtMi44LS42LTMuNi0xLjhhOC4zIDguMyAwIDAgMS0xLjQtNWMwLTIuMS41LTMuOCAxLjQtNWE0LjQgNC40IDAgMCAxIDMuNy0xLjhjMS42IDAgMi45LjcgMy44IDJMNDAuMyA2VjJoMy42djE4LjNoLTIuN2wtLjctMS43aC0uMmMtLjggMS4zLTIgMi0zLjcgMnptMS4zLTIuOGMuOSAwIDEuNS0uMyAyLS44LjQtLjUuNi0xLjUuNy0yLjd2LS40YzAtMS40LS4yLTIuNC0uNy0zLS40LS43LTEuMS0xLTIuMS0xLS44IDAtMS40LjQtMS45IDEtLjQuNy0uNiAxLjctLjYgM3MuMiAyLjMuNiAzYy41LjYgMS4xLjkgMiAuOXptMTIuNi00YzAgMS4zLjIgMi4zLjcgMyAuNC42IDEgMSAyIDFzMS43LS40IDIuMS0xYy40LS43LjYtMS43LjYtM3MtLjItMi4zLS42LTNjLS40LS42LTEuMS0uOS0yLS45LTEgMC0xLjcuMy0yLjIgMS0uNC42LS42IDEuNi0uNiAzem05IDBhNyA3IDAgMCAxLTEuNiA1IDYuMSA2LjEgMCAwIDEtNC43IDEuOGMtMS4zIDAtMi40LS4yLTMuMy0uOC0xLS41LTEuNy0xLjMtMi4zLTIuNGE4IDggMCAwIDEtLjctMy42IDcgNyAwIDAgMSAxLjYtNSA2LjIgNi4yIDAgMCAxIDQuOC0xLjhjMS4yIDAgMi4zLjMgMy4zLjggMSAuNiAxLjcgMS40IDIuMiAyLjRhOCA4IDAgMCAxIC44IDMuNnptMTIuMyA2LjZsLS40LTEuN0g3MWMtLjQuNi0xIDEuMS0xLjcgMS40LS43LjQtMS41LjUtMi40LjUtMS41IDAtMi43LS40LTMuNC0xLjItLjgtLjgtMS4yLTItMS4yLTMuNlY3LjFINjZ2Ny42YzAgMSAuMSAxLjcuNSAyLjIuMy41LjguNyAxLjYuNyAxIDAgMS43LS4zIDIuMS0xIC41LS43LjctMS44LjctMy40VjcuMWgzLjZ2MTMuMWgtMi44em0xOC0xMy4xdjEuOGwtMiAuNWMuMy42LjUgMS4zLjUgMmE0IDQgMCAwIDEtMS41IDMuM2MtMSAuOC0yLjMgMS4yLTQgMS4ySDgybC0uNi0uMWMtLjMuMy0uNS42LS41IDFzLjcuNyAyIC43SDg1YzEuNCAwIDIuNS4zIDMuMyAxIC44LjUgMS4xIDEuNSAxLjEgMi43YTQgNCAwIDAgMS0xLjkgMy41IDEwIDEwIDAgMCAxLTUuNSAxLjNjLTEuOCAwLTMuMi0uMy00LjItMWEzIDMgMCAwIDEtMS41LTIuNmMwLS44LjMtMS41LjgtMiAuNS0uNiAxLjItMSAyLjItMS4yLS40LS4xLS43LS40LTEtLjctLjMtLjQtLjQtLjgtLjQtMS4yIDAtLjUuMS0uOS40LTEuMmE1IDUgMCAwIDEgMS4zLTFjLS43LS4zLTEuMy0uOC0xLjctMS41LS40LS42LS42LTEuNC0uNi0yLjMgMC0xLjQuNS0yLjUgMS40LTMuM2E2IDYgMCAwIDEgNC0xLjIgMTAuMiAxMC4yIDAgMCAxIDIuMy4zaDQuNnpNNzkuNSAyMmMwIC41LjMuOS44IDEuMS40LjMgMSAuNSAyIC41YTcgNyAwIDAgMCAzLS42Yy43LS4zIDEtLjggMS0xLjQgMC0uNS0uMi0uOS0uNi0xLS40LS4yLTEtLjMtMi0uM0g4MmMtLjcgMC0xLjIuMS0xLjcuNC0uNC40LS43LjgtLjcgMS4zem0xLjMtMTAuN2MwIC43LjIgMS4yLjUgMS43LjQuNC45LjYgMS41LjYuNyAwIDEuMi0uMiAxLjUtLjYuMy0uNS41LTEgLjUtMS43IDAtMS42LS43LTIuNC0yLTIuNHMtMiAuOC0yIDIuNHptMjAuMiA1YzAgMS4zLS41IDIuMy0xLjQgMy0xIC43LTIuMyAxLTQuMiAxbC0yLjQtLjEtMi0uNnYtM2ExMi41IDEyLjUgMCAwIDAgNC41IDEuMWMxLjMgMCAyLS40IDItMS4xIDAtLjMtLjEtLjUtLjMtLjdsLS45LS42LTEuNy0uOGMtMS0uNC0xLjctLjgtMi4yLTEuMi0uNS0uMy0uOC0uNy0xLTEuMi0uMy0uNS0uNC0xLS40LTEuNyAwLTEuMi41LTIgMS40LTIuNy45LS43IDIuMi0xIDMuOC0xIDEuNiAwIDMuMi40IDQuNyAxbC0xLjEgMi42LTEuOS0uNy0xLjctLjJjLTEgMC0xLjYuMy0xLjYuOCAwIC40LjIuNi41LjlsMi4yIDFjMSAuNCAxLjguOCAyLjMgMS4yLjUuMy44LjggMSAxLjIuMy41LjQgMSAuNCAxLjd6IiBmaWxsPSIjMDEzQTUxIi8+PHBhdGggZD0iTTIxIDRsLTguNyA4LjhMOC40IDljLS4yLS4yLS41LS4zLS44LS4zLS40IDAtLjYuMS0uOS4zbC0xLjYgMS43Yy0uMi4yLS4zLjUtLjMuOCAwIC4zIDAgLjYuMy44bDYuMyA2LjRjLjMuMi41LjMuOS4zLjMgMCAuNi0uMS44LS4zTDIzLjQgOC4yQTEyIDEyIDAgMSAxIDIxIDQuMXoiIGZpbGw9IiMxMUFCRUMiLz48L2c+PC9zdmc+",
"alt":"logo_dougs"
},
"docInfo": {
"sujet": "Factur-X Luffy",
"titre": "Facture FAC-2023-123456"
},
"numeroFacture": "FACTURE-2024-02-29",
"typeFacture": "FACTURE_COMMERCIALE",
"cadreFacturation": "FACTURE_BIEN",
"codeDevise": "EUR",
"autresInformationsPrincipales": [
"Cette facture est fictive",
"C'est uniquement pour la démo"
],
"dateFacturation": "2023-12-07",
"titreFacture": "Facture Démo",
"autresInformationsFacture": [
"Démo OD"
],
"client": {
"type": "PROFESSIONNEL",
"nom": "LUFFY SARL",
"nomCommercialSociete": "Mugiwara no Luffy",
"prenomNom": "Luffy Monkey",
"telephone": "0601020304 ",
"email": "monkey.d.luffy@email.com",
"siren": "480470152",
"siret": "81031951745871",
"adresse": {
"adresse1": "21 RUE DES FONTAINES",
"localite": "CLERMONT",
"codePostal": "60600",
"pays": "France",
"codePays": "FR"
},
"tvaIntracommunautaire": "FR95910858869",
"autresInformations": [
"Non ce n'est pas le vrai Luffy"
],
"adresseLivraison": {
"adresse1": "Thousand Sunny",
"localite": "Laugh Tale",
"codePostal": "05",
"pays": "Shinsekai",
"codePays": "FR"
}
},
"lignesFacture": [
{
"titre": "Gomu Gomu no mi",
"description": "Fruit caoutchouc",
"reference": "Paramecia",
"quantite": 1,
"unite": "unité",
"prixUnitaireHT": 2000,
"tauxTVA": 20,
"totalHT": 2000
},
{
"titre": "Mera Mera no mi",
"description": "Fruit feu",
"reference": "Logia",
"quantite": 1,
"unite": "unité",
"prixUnitaireHT": 5000,
"prixUnitaireTTC": 6000,
"tauxTVA": 20,
"totalHT": 6000,
// "remisePourcentage": 20,
"montantTotalRemise": 2000
},
{
"titre": "Uo Uo no mi",
"description": "Fruit dragon Seiryu",
"reference": "Zoan",
"quantite": 1,
"unite": "unité",
"prixUnitaireHT": 9000,
"tauxTVA": 20,
"totalHT": 9000
},
{
"titre": "Ponéglyphe",
"quantite": 3,
"unite": "unité",
"prixUnitaireHT": 8000,
"tauxTVA": 20,
"totalHT": 24000
}
],
"detailsTVA": [
{
"totalHT": 39000,
"tauxTVA": 20,
"totalTVA": 7800
}
],
"totalFacture": {
"totalHT": 41000,
// "remiseGlobalePourcent": 2.5,
// "remiseGlobaleMontant": 1000,
"totalRemiseMontant": 2000,
// "tauxTVA": 20,
"totalNetHT": 39000,
"totalTVA": 7800,
"totalTTC": 46800,
"acompteTTC": 6800,
"totalAPayer": 40000
},
"formulePolitesse": "Merci pour la commande !",
"conditionsPaiement": "La facture est payable à réception. Pas d'escompte en cas de paiement anticipé.",
"mentionsFacture": {
"autresInformations": []
},
// "mentionTva": "EXPORTATION_HORS_UE",
"vendeur": {
"nomSociete": "Joey Remion",
"nomCommercialSociete": "Joey Dougs",
"formeLegale": "EI",
"siren": "347727950",
"siret": "81031951745871",
"tvaIntracommunautaire": "FR47 810319517",
"contact": "Joey D.",
"telephone": "0102030405",
"email": "joey.dougs@dougs.fr",
"assuranceProAssureur": "Assurance Tourix",
"assuranceProCouvertureGeo": "Europe",
"assuranceDecennaleAssureur": "Assurance Tourix",
"assuranceDecennaleCouvertureGeo": "Europe",
"autresInformations": [
"Ce vendeur est bien entendu fictif",
"C'est pour la démo Dougs"
],
"adresse": {
"adresse1": "Mont Corvo",
"localite": "Dawn",
"codePostal": "55000",
"pays": "Shinsekai",
"codePays": "SK"
}
},
"piecesJointes": [
{
"nomFichier": "ticket_de_caisse.pdf",
"contentType": "application/pdf",
"base64": "PHN2ZyBoZWlnaHQ9IjI2IiB4bWxucz0iaHR0cDov..."
}
]
}
PropriétéValeurRequis
mimeTypeType MIME de l'imageOui
base64Données de l'image encodées en Base64Oui
altTexte alternatifOui
widthLargeur de l'image en pixels (max 400)Non
heighthauteur de l'image en pixels (max 400)Non

Les formats d'image suivants sont supportés:

FormatMime-Type
JPEGimage/jpeg
PNGimage/png
SVGimage/svg+xml
WebPimage/webp
ICOimage/x-icon

Validation des données

La validation des données de facturation se fait à plusieurs niveaux:

ValidationActeurErreur détectées
TypesODDonnées manquantes ou types invalides (ex: number au lieu date)
CohérencePDP API FirstDonnées incohérentes (voir erreurs PDP)
SyntaxePDP API FirstStructure XML non conforme au schéma (CII D16B pour FacturX)
SémantiquePDP API FirstViolation des règles Schematron (EN16931 pour FacturX)

Réponse

Si tout s'est bien passé, la réponse de l'OD est 201 CREATED avec l'en-tête Location contenant l'emplacement de la ressource:

Réponse type de l'OD pour une création de facture
HTTP/1.1 201 Created
X-Powered-By: Express
X-RateLimit-Limit: 12000
X-RateLimit-Remaining: 11998
X-RateLimit-Reset: 60
Content-Type: application/json; charset=utf-8
Content-Length: 45
ETag: W/"2d-SVoQrUQ9lw1spmQdDLKIlsOgr98"
Date: Thu, 11 Jan 2024 13:45:47 GMT
Connection: keep-alive
Keep-Alive: timeout=5
Location: v1/ventes/factures/26ad3a21-615e-40ea-a05a-2ac8e8a2f373

Erreurs

Si une erreur s'est produite, les codes et messages suivants peuvent être retournés:

CodeMessage
400 Bad RequestRequête invalide - Voir messages d'erreur ci-dessous
401 UnauthorizedInformations d'authentification invalides
403 ForbiddenDroits insuffisants pour effectuer cette action
409 ConflictUne facture avec ce numéro existe déjà

Messages d'erreur

Les messages d'erreur sont répartis dans les 3 catégories suivantes:

TableauCatégorieCauseRéférence
xsdErrorsSyntaxiqueViolation du schéma XMLeInvoicing-EN16931 CII-D16B
schematronErrorsSémantiqueViolation d'une ou plusieurs règles SchematroneInvoicing-EN16931 Schematron
pdpErrorsPDPViolation d'une ou plusieurs règles PDPErreurs PDP

Tableau xsdErrors

info

En principe il n'y a jamais d'erreurs syntaxiques retournées par l'OD, car celui-ci n'est pas directement responsable de la génération du XML. Le tableau xsdErrors est cependant maintenu à toutes fins utiles.

Tableau schematronErrors

Exemple d'erreur sémantique
{
"xsdErrors": [],
"schematronErrors": [
{
"level": "fatal",
"rule": "BR-CO-16",
"location": "/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeSettlement/ram:SpecifiedTradeSettlementHeaderMonetarySummation",
"test": "(xs:decimal(ram:DuePayableAmount) = xs:decimal(ram:GrandTotalAmount) - xs:decimal(ram:TotalPrepaidAmount) + xs:decimal(ram:RoundingAmount)) or ((xs:decimal(ram:DuePayableAmount) = xs:decimal(ram:GrandTotalAmount) + xs:decimal(ram:RoundingAmount)) and not (xs:decimal(ram:TotalPrepaidAmount))) or ((xs:decimal(ram:DuePayableAmount) = xs:decimal(ram:GrandTotalAmount) - xs:decimal(ram:TotalPrepaidAmount)) and not (xs:decimal(ram:RoundingAmount))) or ((xs:decimal(ram:DuePayableAmount) = xs:decimal(ram:GrandTotalAmount)) and not (xs:decimal(ram:TotalPrepaidAmount)) and not (xs:decimal(ram:RoundingAmount)))",
"content": "[BR-CO-16]-Amount due for payment (BT-115) = Invoice total amount with VAT (BT-112) -Paid amount (BT-113) +Rounding amount (BT-114).",
"locationField": {
"id": "BG-22",
"field": "Unkown"
},
"additionalLocationFields": [
{
"id": "BT-112",
"field": "totalFacture.totalTTC"
},
{
"id": "BT-113",
"field": "totalFacture.acompteTTC"
},
{
"id": "BT-114",
"field": null
},
{
"id": "BT-115",
"field": "totalFacture.totalAPayer"
}
]
}
],
"pdpErrors": []
}

Lorsqu'une erreur se produit, l'OD la retourne sous la forme suivante:

PropriétéValeur
levelGravité de l'erreur
ruleIdentifiant de la règle
locationEmplacement XPath de l'erreur dans le document XML
testCode de la règle
contentLibellé de l'erreur
locationField.idIdentifiant du champ erroné dans les données de facturation EN16931
locationField.fieldEmplacement du champ erroné dans les données de facturation

En plus du champ dont la valeur est erronée, l'OD remonte tous les champs pris en considération pour l'application de la règle. Cette information est utile pour comprendre la source de l'erreur:

PropriétéValeur
additionalLocationFields[i].ididentifiant du champ erroné dans les données de facturation EN16931
additionalLocationFields[i].fieldEmplacement du champ dans les données de facturation

Les identifiants de champ EN16931 sont toujours disponibles, mais pas forcément leurs emplacements dans les données de facturation, car celles-ci peuvent avoir différents formats, et ne pas contenir de valeur pour certains champs. Dans ce cas, la valeur de l'emplacement est null, undefined ou Unknown [TODO point à améliorer]

Tableau pdpErrors

Si l'utilisateur de l'OD est connecté à la PDP API First, celle-ci peut retourner d'autres erreurs

Exemple d'erreur PDP
"pdpErrors": [
{
"name": "InconsistentSellerSirenWithCompany",
"message": "Seller's SIREN in invoice does not match company's SIREN : '347727952' in invoice and '347727950' in company"
}
]

Lorsqu'une erreur se produit, l'OD la retourne sous la forme suivante:

PropriétéValeur
nameNom de l'erreur
messageDescription et emplacement de l'erreur

Impact

info

Les factures et fichiers générés sont des ressources permanentes. Ils ne sont jamais supprimés par l'OD