Blog de développement

Le serveur fait sa première Factur-X

Dernière Modification le :
2024-01-13

Dès quelques factures par mois, il devient nécessaire d'automatiser Chorus Pro. Il est inexact de croire que Chorus Pro entreprend bien docilement d'Océriser votre facture pour améliorer votre productivité. Quels que soient vos efforts pour optimiser polices ou mots clés, vous serez systématiquement conduit à vérifier et corriger une 'lecture' qui est souvent erronnée. C'est une méchante arnaque! Un trou noir de la productivité des PME... Lire →

La seule parade c'est Factur-X. Il n'y a pas d'alternative, mais au moins, ça marche! Donc, il faut passer à Factur-X. Lire →

En fait, même, si on n'a pas la possibilité de passer à PDF/A et Factur-x, il reste possible d'optimiser un PDF 'classique' pour une lecture optimale par Chorus voire même d'automatiser le dépôt. Lire →

Les Outils PDF et Factur-X

J. van der Knijff, maintient une liste d'outils de manipulation PDF Lire →. L. Abbati en a une aussi. Lire →

Le site officiel de Factur-X fournit une liste d'outils. Lire →. En fait il y en a d'autres, plus récents, certains très pertinents! Explorer →

En Desktop, les utilisateurs de LibreOffice installeront le module proposé par Alexis de Lattre (Akretion) Lire et Télécharger →. Les utilisateurs d'Excel considéreront l'outil Excel d'Admarel Télécharger →

En mode Serveur, on pourra procéder par étapes: création du PDF, conversion en PDF/A, puis ajout du XML de Factur-X, avec des outils gratuits comme: FPDF, Mustang, en ligne de commande

Pour la validation on notera: avepdf.com, verapdf.org et son outil en ligne (difficile à dénicher, au demeurant...), l'outil de validation FNFE-MPE et enfin... Chorus Pro, tortionnaire devenu cobaye.

Mise à niveau PDF/A avec ASPPDF

J'utilise ASPPDF, donc je le décris en premier... Il a été mis à jour pour satisfaire aux nouvelles normes: il faut donc avoir la toute dernière version. Vérifier →. Sans ASPPDF, c'est ici: Lire →

Certaines règles à respecter... En particulier, toutes les polices doivent être incorporées, donc que les polices standard, non incorporées, devront l'être aussi...

					    Set oPDF = Server.CreateObject("Persits.Pdf")	' // Server. c'est pour ASP... En script VBS : CreateObject("Persits.Pdf")	
Set oDoc = oPDF.CreateDocument
Set oPage = oDoc.Pages.Add ("595,3", "841,9")	' //pour être en A4 vertical
	' // la meilleure police en vue d'OCR c'est Tahoma, mais, ici avec Factur-X on ne passe plus par l'OCR, ni l'extracteur de texte!
Set oTimesFont = oDoc.Fonts.LoadFromFile("c:\windows\fonts\times.ttf")
Set oTimesBoldFont = oDoc.Fonts.LoadFromFile("c:\windows\fonts\timesbd.ttf")
Set oTimesItalicFont = oDoc.Fonts.LoadFromFile("c:\windows\fonts\timesi.ttf")
				
De même il nous faut insérer un profile de couleurs. On en trouve un chez ADOBE. Le lien est en bas de la page... Lire et Télécharger →
					    sProfilePath = "C:\Inetpub\MonSite\AdobeRGB1998.icc"
oDoc.AddOutputIntent "AdobeRGB", "Custom", strProfilePath, 3
				

Le métadata proposé en exemple par ASPPDF ne passe pas la validation. On extrait celui d'Akretion avec un outil en ligne : metadata2go. Il est aussi disponible dans le ZIP de ressources.

					    		' on utilise un modèle (template) Metadata extrait d'une factur-X valide plutôt que metadata de persists (ASPPDF)
sMetaDataFilePath = "C:\Inetpub\MonSite\metadata.txt"
Set objFile = objFSO.OpenTextFile(sMetaDataFilePath, 1, true)	'ForReading = 1, true
sMetadata = objFile.ReadAll
objFile.Close
Set objFile = nothing
sDateFacture_ISO = ToIsoDate (date(now()))
sDateFacture_pour_XML = Replace (sDateFacture_ISO, "-", "") 	' // mise au format ISO de la date d'aujoud'hui
NumeroFacture = "23001"

sMetadata = Replace(sMetadata, "<rdf:li xml:lang=""x-default"">LE FOURNISSEUR: Invoice F20220023</rdf:li>", " <rdf:li xml:lang=""x-default"">MaSociété: Facture " &  numero_facture & "</rdf:li>")
sMetadata = Replace(sMetadata, "<rdf:li>LE FOURNISSEUR</rdf:li>" ,  "<rdf:li>MaSociété</rdf:li>")
sMetadata = Replace(sMetadata, "<rdf:li xml:lang=""x-default"">Invoice F20220023 dated 2022-01-31 issued by LE FOURNISSEUR</rdf:li>" , "<rdf:li xml:lang=""x-default"">Invoice " &  numero_facture & " dated " & sDateFacture_ISO & " issued by MaSociété</rdf:li>")
sMetadata = Replace(sMetadata, "<pdf:Producer>PyPDF4</pdf:Producer>" , "<pdf:Producer>Persits Software AspPDF - www.persits.com</pdf:Producer>")
sMetadata = Replace(sMetadata, "<xmp:CreatorTool>factur-x python lib v2.3 by Alexis de Lattre</xmp:CreatorTool>" , "<xmp:CreatorTool>factur-x for MaSociété by MySelf</xmp:CreatorTool>")
sMetadata = Replace(sMetadata, "<xmp:CreateDate>2022-04-12T20:05:03+00:00</xmp:CreateDate>" , "<xmp:CreateDate>" & sDateFacture_ISO & "T20:05:03+00:00</xmp:CreateDate>")
sMetadata = Replace(sMetadata, "<xmp:ModifyDate>2022-04-12T20:05:03+00:00</xmp:ModifyDate>" , "<xmp:ModifyDate>" & sDateFacture_ISO & "T20:05:03+00:00</xmp:ModifyDate>")

oDoc.MetaData = sMetadata	' // on insère le metadata (XMP)
	
Public Function ToIsoDate(datetime)
	ToIsoDate = CStr(Year(datetime)) & "-" & StrN2(Month(datetime)) & "-" & StrN2(Day(datetime))
End Function  
Private Function StrN2(n)
	If Len(CStr(n)) < 2 Then StrN2 = "0" & n Else StrN2 = n
End Function
		
				

On insère le XML de Factur-X

On doit ajouter cette annotation. ASPPDF suggère AFRelationship=0. En fait, if faut AFRelationship=1 pour passer la validation...

					    	' // AFRelationship entry must be 1
Set oAnnot = oPage.Annots.Add("", "x=1,y=700;width=10;height=10; Type=FileAttachment; names=true; AF=1;", "Paperclip", xmloutput)

				

Chorus propose une série d'exemples. Télécharger →. J'ai extrait mon modèle XML minimal de la facture modèle d'Akretion.

					    
' // Attention: le séparateur décimal est ici le point. Se référer à la norme pour voir ce qu'il en est exactement.
TaxBasisTotalAmount = Replace (Replace (Basket_HT, " ", ""), ",", ".")
TaxTotalAmount = Replace (Replace (Basket_TVA, " ", ""), ",", ".")'Basket_TVA
GrandTotalAmount = Replace (Replace (Basket_TTC, " ", ""), ",", ".")
DuePayableAmount = GrandTotalAmount
	' // à personnaliser
sMaSociete = "Ma Boutique"
sMonSIRET = "79237773100023"
sMonNumeroTVA = "FR86792377731"
sNomClient = "MonClient"
sChorus_service = "Voirie"
sSIRET_Client = "21690266800013"
sDateFacture_pour_XML = Replace (ToIsoDate (date(now())), "-", "")	' // le format pour Factur-X est un format ISO simplifié
Set objFile = objFSO.OpenTextFile(xml_Template, 1, true) 	' //  1 = ForReading
sContent = objFile.ReadAll
objFile.Close

sContent = replace(sContent, "<rsm:ExchangedDocument><ram:ID>F19042</ram:ID>" , "<rsm:ExchangedDocument><ram:ID>" & numero_facture &"</ram:ID>")
sContent = replace(sContent, "<udt:DateTimeString format=""102"">20191221</udt:DateTimeString>", "<udt:DateTimeString format=""102"">" & sDateFacture_pour_XML & "</udt:DateTimeString>")
sContent = replace(sContent, "<ram:BuyerReference>SRVIT</ram:BuyerReference>", "<ram:BuyerReference>" & sChorus_service & "</ram:BuyerReference>")
sContent = replace(sContent, "<ram:SellerTradeParty><ram:Name>Akretion France</ram:Name>", "<ram:SellerTradeParty><ram:Name>" & sMaSociete & "</ram:Name>")
'le siret du Granuloshop = 91510597700010
sContent = replace(sContent, "<ram:SpecifiedLegalOrganization><ram:ID schemeID=""0002"">79237773100023</ram:ID>"  , "<ram:SpecifiedLegalOrganization><ram:ID schemeID=""0002"">" & sMaSociete & "</ram:ID>")
'le Numero TVA du Granuloshop : FR81915105977
sContent = replace(sContent, "<ram:ID schemeID=""VA"">FR86792377731</ram:ID>" , "<ram:ID schemeID=""VA"">" & sMonNumeroTVA & "</ram:ID>")
sContent = replace(sContent, "<ram:Name>Mairie de Villeurbanne</ram:Name>", "<ram:Name>" & sNomClient & "</ram:Name>")
' le SIRET client
sContent = replace(sContent, "<ram:ID schemeID=""0002"">21690266800013</ram:ID>" , "<ram:ID schemeID=""0002"">" & sSIRET_Client & "</ram:ID>")
sContent = replace(sContent, "<ram:IssuerAssignedID>PO19012</ram:IssuerAssignedID></ram:BuyerOrderReferencedDocument>"  , "<ram:IssuerAssignedID>" & sEngagement & "</ram:IssuerAssignedID></ram:BuyerOrderReferencedDocument>")
sContent = replace(sContent, "<ram:TaxBasisTotalAmount>347.00</ram:TaxBasisTotalAmount>" ,  "<ram:TaxBasisTotalAmount>" & TaxBasisTotalAmount & "</ram:TaxBasisTotalAmount>")
sContent = replace(sContent, "<ram:TaxTotalAmount currencyID=""EUR"">69.40</ram:TaxTotalAmount>", "<ram:TaxTotalAmount currencyID=""EUR"">" & TaxTotalAmount & "</ram:TaxTotalAmount>")
sContent = replace(sContent, "<ram:GrandTotalAmount>416.40</ram:GrandTotalAmount>"  , "<ram:GrandTotalAmount>" & GrandTotalAmount & "</ram:GrandTotalAmount>")
sContent = replace(sContent, "<ram:DuePayableAmount>416.40</ram:DuePayableAmount>" , "<ram:DuePayableAmount>" & DuePayableAmount & "</ram:DuePayableAmount>")
sContent = "<?xml version='1.0' encoding='UTF-8'?>" & sContent
Set objFile = objFSO.OpenTextFile(xmloutput, ForWriting, True)   'True va creer un nouveau fichier
objFile.Write  sContent
objFile.Close
					    
				    

Dans le cas particulier où le TVA n'est pas facturée, par exemple l'échange intracommunautaire, il faut penser à mettre la TVA à 0.00, car sinon cela ne passe pas le validateur.

La vraie vie: déposer sur Chorus en un clin d'oeil

Ce qui est remarquable, c'est que maintenant Chorus PRO va identifier la facture comme étant au format hybride Factur-x et vous dispenser de la page de correction à laquelle vous aviez systématiquement droit: le gain de temps et de productivité est énorme!

Nous ajoutons cette ligne au PDF: "Cette facture est au format Factur-x: norme professionnelle agrée par l'Etat et Chorus Pro."