Peppol Invoice validation error

Hi all,

As new user of Tryton, I want first thank you for the huge effort that you do to provide and maintain a so great tools, to give your time and energy to contribute in the project keeping the open-source mindset alive.

I have just started an activity and as strong believer and supporter of the open-source mindset, I try to contribute and use as much as possible open-source tools.

Current version of trytond: 7.8.6

During the last days, I have spent a lot of time trying to understand first and then fix issues that I have with Peppol and trying to find the potential missing configuration.

I have tried to send a first invoice via Peppol but I’m getting the following error (logs from Peppyrus)

Fatal validation errors:

[BR-47]-Each VAT breakdown (BG-23) shall be defined through a VAT category code (BT-118).

[BR-48]-Each VAT breakdown (BG-23) shall have a VAT category rate (BT-119), except if the Invoice is not subject to VAT.

[BR-CO-17]-VAT category tax amount (BT-117) = VAT category taxable amount (BT-116) x (VAT category rate (BT-119) / 100), rounded to two decimals.

[BR-CO-04]-Each Invoice line (BG-25) shall be categorized with an Invoiced item VAT category code (BT-151).
[BR-CL-24]-For Mime code in attribute use MIMEMediaType.
[PEPPOL-EN16931-CL001] Mime code must be according to subset of IANA code list.

I have tried to fix the issue by adding the UNECE Category code to Standard rate to add the CBC ID related to BR-47 and BR-48

I have edited the xml to remove the attachment and avoid BR-CL-24 and PEPPOL-EN16931-CL001

But I still have errors.

As I have done multiple attempts, I have now multiple time the same invoice in Tryton. Is it a way to clean this up and remove all the invoice created ?

I should have make all those on the test environment. I will build a quick one tonight.

Thank you for your support,

Thomas

1 Like

Please share the UBL XML so we can have the context for those errors.

You do not need to create a new invoice each time the Peppol submission is failing. You can just click on the retry button, Tryton will re-generate the XML file with the updated data.

For the existing invoice that you posted multiple times, you need to make credit notes to cancel them.

Hi Cédric,

Thank you for your quick reply.

Here is the last try that I have done (I have redacted personal data):

<?xml version="1.0" encoding="UTF-8"?>
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2" xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2" xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

    
    <cbc:CustomizationID>urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0</cbc:CustomizationID>
    <cbc:ProfileID>urn:fdc:peppol.eu:2017:poacc:billing:01:1.0</cbc:ProfileID>
<cbc:ID>CL-001</cbc:ID>
<cbc:IssueDate>2026-03-23</cbc:IssueDate>
<cbc:DueDate>2026-03-30</cbc:DueDate>
<cbc:InvoiceTypeCode>380</cbc:InvoiceTypeCode>
<cbc:DocumentCurrencyCode>EUR</cbc:DocumentCurrencyCode>
<cac:OrderReference>
    <cbc:ID>NA</cbc:ID>
</cac:OrderReference>
<cac:AccountingSupplierParty>
        <cac:Party>
                    <cbc:EndpointID schemeID="0208">1xxxxxxxxx</cbc:EndpointID>
                <cac:PartyIdentification>
                    <cbc:ID schemeID="0208">1xxxxxxxxx</cbc:ID>
                </cac:PartyIdentification>
            <cac:PostalAddress>
                    <cbc:StreetName>Just a street</cbc:StreetName>
                    <cbc:CityName>Somewhere</cbc:CityName>
                    <cbc:PostalZone>0000</cbc:PostalZone>
        <cac:Country>
            <cbc:IdentificationCode>BE</cbc:IdentificationCode>
            <cbc:Name>Belgium</cbc:Name>
        </cac:Country>
    </cac:PostalAddress>
            <cac:PartyTaxScheme>
                        <cbc:CompanyID>BE1xxxxxxxxx</cbc:CompanyID>
                <cac:TaxScheme>
                    <cbc:ID>VAT</cbc:ID>
                </cac:TaxScheme>
            </cac:PartyTaxScheme>
            <cac:PartyLegalEntity>
                <cbc:RegistrationName>My Company</cbc:RegistrationName>
                        <cbc:CompanyID schemeID="0208">1xxxxxxxxx</cbc:CompanyID>
            </cac:PartyLegalEntity>
        </cac:Party>
</cac:AccountingSupplierParty>
<cac:AccountingCustomerParty>
        <cac:Party>
                    <cbc:EndpointID schemeID="9925">bexxxxxxxxxx</cbc:EndpointID>
            <cac:PostalAddress>
                <cbc:CityName>Somewhere</cbc:CityName>
                <cbc:PostalZone>0000</cbc:PostalZone>
                    <cac:AddressLine>
                        <cbc:Line>
                            Just an address
                        </cbc:Line>
                    </cac:AddressLine>
        <cac:Country>
            <cbc:IdentificationCode>BE</cbc:IdentificationCode>
            <cbc:Name>Belgique</cbc:Name>
        </cac:Country>
    </cac:PostalAddress>
            <cac:PartyTaxScheme>
                        <cbc:CompanyID>BExxxxxxxxxx</cbc:CompanyID>
                <cac:TaxScheme>
                    <cbc:ID>VAT</cbc:ID>
                </cac:TaxScheme>
            </cac:PartyTaxScheme>
            <cac:PartyLegalEntity>
                <cbc:RegistrationName>Client Company</cbc:RegistrationName>
            </cac:PartyLegalEntity>
        </cac:Party>
</cac:AccountingCustomerParty>
        <cac:PaymentTerms>
            <cbc:Amount currencyID="EUR">695.75</cbc:Amount>
            <cbc:PaymentDueDate>2026-03-30</cbc:PaymentDueDate>
        </cac:PaymentTerms>
<cac:TaxTotal>
    <cbc:TaxAmount currencyID="EUR">120.75</cbc:TaxAmount>
    <cac:TaxSubtotal>
        <cbc:TaxableAmount currencyID="EUR">575.00</cbc:TaxableAmount>
        <cbc:TaxAmount currencyID="EUR">120.75</cbc:TaxAmount>
        <cbc:Percent>21</cbc:Percent>
        <cac:TaxCategory>
            <cbc:ID>S</cbc:ID>
            <cbc:Percent>21</cbc:Percent>
        </cac:TaxCategory>
    </cac:TaxSubtotal>
</cac:TaxTotal>
<cac:LegalMonetaryTotal>
    <cbc:LineExtensionAmount currencyID="EUR">575.00</cbc:LineExtensionAmount>
    <cbc:TaxExclusiveAmount currencyID="EUR">575.00</cbc:TaxExclusiveAmount>
    <cbc:TaxInclusiveAmount currencyID="EUR">695.75</cbc:TaxInclusiveAmount>
    <cbc:PayableAmount currencyID="EUR">695.75</cbc:PayableAmount>
</cac:LegalMonetaryTotal>
<cac:InvoiceLine>
    <cbc:ID>1</cbc:ID>
    <cbc:InvoicedQuantity unitCode="HUR">5.0</cbc:InvoicedQuantity>
    <cbc:LineExtensionAmount currencyID="EUR">695.75</cbc:LineExtensionAmount>
    <cac:Item>
        <cbc:Name>A product</cbc:Name>
        <cac:ClassifiedTaxCategory>
            <cbc:ID>S</cbc:ID>
            <cbc:Percent>21</cbc:Percent>
        </cac:ClassifiedTaxCategory>
    </cac:Item>
    <cac:Price>
        <cbc:PriceAmount currencyID="EUR">115.0000</cbc:PriceAmount>
    </cac:Price>
</cac:InvoiceLine>
</Invoice>

And here are the error I got from Peppyrus:

Fatal validation errors:

[BR-47]-Each VAT breakdown (BG-23) shall be defined through a VAT category code (BT-118).
[BR-48]-Each VAT breakdown (BG-23) shall have a VAT category rate (BT-119), except if the Invoice is not subject to VAT.

[BR-CO-17]-VAT category tax amount (BT-117) = VAT category taxable amount (BT-116) x (VAT category rate (BT-119) / 100), rounded to two decimals.

[BR-CO-04]-Each Invoice line (BG-25) shall be categorized with an Invoiced item VAT category code (BT-151).
[BR-CL-24]-For Mime code in attribute use MIMEMediaType.
[PEPPOL-EN16931-CL001] Mime code must be according to subset of IANA code list.

I don’t have a button to retry to send the invoice potentially because in Administration > Peppol > Documents all the invoices stay in Processing and not in Failed like they are in Peppyrus.

Best regards,

Thomas

There is a scheduled task that should update the status to fail.

You need to set the “UNECE Code” “VAT” for the tax.

The only format allowed by Peppol that is compatible with Tryton is PDF. So you must setup the output format of the invoice report to PDF.

png, jgp, csv and ods are also allowed but they do not make sense for the invoice.

Hi Ced,

Thank you for your tips.

I definitely need to learn Tryton further.

I have run the actions to sync peppol manually and it switch to failed.

After adapting the UNECE Code to VAT it fixes all the issues related to the content.

I was also changing the template keeping “Template Extension” as OpenDocument Text and change the Extension to “Portable Document Format”.

But when I try to resend the peppol invoice, it is still the odt format that is sent.

I was looking further in the forum and I have tried to remove the cache in the “account_invoice” table directly in postgres and even trying to change the “invoice_report_format” but each time an regenerate the invoice it is back to an odt.

Could it be that it is because Libreoffice need to be installed ?
As ODS or CSV are allowed it should be possible to convert the current odt genshi model to ods ? or to create a text genshi template to get a csv.
I feel it could be a good way to avoid the dependency with libreoffice. I will give it a try tomorrow

Thank you for your help so far.

Thomas

I created Add note in the documentation about the scheduled task to update Peppol document status (!3192) · Merge requests · Tryton / Tryton · GitLab to document it.
Also Add an "Update Status" button to the Peppol document (e6e5d9c22571) · Commits · Tryton / Tryton · GitLab added a button to update it directly and Sign in · GitLab added a webhook.

This is because the report is generated and stored when posted. You can force a regeneration via the “Invoice (revised)” from the print menu. This will regenerate the report and historise the previous.
I created Update invoice documentation to describe the revised report (!3193) · Merge requests · Tryton / Tryton · GitLab to document it.

Yes you need libreoffice to convert to PDF (or setup the document-converter via [report] convert_command.

I guess libreoffice can do that. But I see no usage for the recipient of the invoice. My guess about the permission for these formats is when the business wants to provide some sort of reporting with the invoice like a timesheet report.

I think we could skip to include the invoice report if it is not an allowed extension for the Peppol network. Because including the PDF invoice is not a requirement (but a nice to have). See Do not include document with non-supported format by Peppol (#14708) · Issues · Tryton / Tryton · GitLab

Hi Ced,

Thank you for all the details and for all the changes in the documentation.

I have finally succeeded to send my first invoice without converting and without using libreoffice. For sure in a really hacky way as I have changed the invoice.fodt to a text file and made some change in the code of edocument_ubl/edocument to force the mimetypes to ‘txt/csv’

An interesting attachment that we could send via Peppol attachment could be an EPC QR code (https://en.wikipedia.org/wiki/EPC_QR_code) that allow the recipient to directly scan it with his Bank application.

Here is a quick and dirty code that I use in an other automation and that generate this type of QR code.

import qrcode
def generateFile(bic, name, iban, amount, reason, invoiceref, info, filename):
    text = f"""BCD
001
1
SCT
{bic}
{name}
{iban}
{amount}
{reason}
{invoiceref}

{info}
"""
    img = qrcode.make(text)
    img.save(filename+".png")

def main():
    generateFile("BIC", "My Company", "BEXX XXXX XXXX XXXX", "5,0", "","+++xxx/xxxx/xxxxx+++","","InvoiceQR")

if __name__ == "__main__":
    main()

I was looking for a way to replace the document-converter by a script generating it but It will still require some time to dive further in the code.

2 Likes