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
- Posséder un compte OD
- Avoir créé une connexion PDP ou PPF
- Avoir des données de facturation au format Dougs
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:
| Syntaxe | Supportée |
|---|---|
| Factur-X | Oui (défaut) |
| CII | Non |
| UBL | Non |
Par exemple:
- cURL
- Typescript
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
const url = 'http://<host>:<port>/v1/ventes/factures'
const data: string = fs.readFileSync('facture.json');
const response: AxiosResponse = await axios.post(url, data, {
headers: {
'X-OD-User-ID': userId,
'X-OD-Syntaxe': 'Factur-X',
'X-OD-A-Envoyer': true,
'Content-Type': 'application/json; charset=utf-8',
}
});
...
// récupération de l'URI de la nouvelle ressource
const uri: string = response.headers['location'];
{
"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..."
}
]
}
Logo
| Propriété | Valeur | Requis |
|---|---|---|
| mimeType | Type MIME de l'image | Oui |
| base64 | Données de l'image encodées en Base64 | Oui |
| alt | Texte alternatif | Oui |
| width | Largeur de l'image en pixels (max 400) | Non |
| height | hauteur de l'image en pixels (max 400) | Non |
Les formats d'image suivants sont supportés:
| Format | Mime-Type |
|---|---|
| JPEG | image/jpeg |
| PNG | image/png |
| SVG | image/svg+xml |
| WebP | image/webp |
| ICO | image/x-icon |
Validation des données
La validation des données de facturation se fait à plusieurs niveaux:
| Validation | Acteur | Erreur détectées |
|---|---|---|
| Types | OD | Données manquantes ou types invalides (ex: number au lieu date) |
| Cohérence | PDP API First | Données incohérentes (voir erreurs PDP) |
| Syntaxe | PDP API First | Structure XML non conforme au schéma (CII D16B pour FacturX) |
| Sémantique | PDP API First | Violation 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:
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:
| Code | Message |
|---|---|
| 400 Bad Request | Requête invalide - Voir messages d'erreur ci-dessous |
| 401 Unauthorized | Informations d'authentification invalides |
| 403 Forbidden | Droits insuffisants pour effectuer cette action |
| 409 Conflict | Une facture avec ce numéro existe déjà |
Messages d'erreur
Les messages d'erreur sont répartis dans les 3 catégories suivantes:
| Tableau | Catégorie | Cause | Référence |
|---|---|---|---|
| xsdErrors | Syntaxique | Violation du schéma XML | eInvoicing-EN16931 CII-D16B |
| schematronErrors | Sémantique | Violation d'une ou plusieurs règles Schematron | eInvoicing-EN16931 Schematron |
| pdpErrors | PDP | Violation d'une ou plusieurs règles PDP | Erreurs PDP |
Tableau xsdErrors
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
{
"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 |
|---|---|
| level | Gravité de l'erreur |
| rule | Identifiant de la règle |
| location | Emplacement XPath de l'erreur dans le document XML |
| test | Code de la règle |
| content | Libellé de l'erreur |
| locationField.id | Identifiant du champ erroné dans les données de facturation EN16931 |
| locationField.field | Emplacement 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].id | identifiant du champ erroné dans les données de facturation EN16931 |
| additionalLocationFields[i].field | Emplacement 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
"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 |
|---|---|
| name | Nom de l'erreur |
| message | Description et emplacement de l'erreur |
Impact
- La facture et le fichier correspondant sont maintenant créés
- A tout moment, il est possible de:
- Consulter la facture via l'API consultation vente
- Envoyer la facture à son destinataire via l'API d'envoi des factures
Les factures et fichiers générés sont des ressources permanentes. Ils ne sont jamais supprimés par l'OD