← Back to all articles

Časté validační chyby Peppol BIS 3.0 a jak je opravit

SealDoc Team · · 6 min read

Validační selhání Peppol BIS 3.0 spadají do předvídatelné sady kategorií. Chybové zprávy odkazují na kódy obchodních pravidel (BR-xx) a čísla obchodních termínů (BT-xx) ze standardu EN16931, jejichž pochopení vyžaduje přečtení specifikace. Tento článek mapuje nejčastější selhání na jejich příčiny a poskytuje konkrétní opravy.

Všechny příklady používají syntaxi UBL 2.1. Pro ekvivalenty CII viz UBL vs CII.

BR-01 až BR-06: chybějící povinná pole záhlaví

Tato pravidla vynucují přítomnost povinných polí nejvyšší úrovně. Selžou, pokud element chybí nebo je prázdný.

PravidloPoleBTOprava
BR-01Identifikátor specifikaceBT-24Přidejte CustomizationID s přesným URI Peppol BIS 3.0
BR-02Číslo fakturyBT-1Přidejte neprázdný element ID
BR-03Datum vystavení fakturyBT-2Přidejte IssueDate ve formátu YYYY-MM-DD
BR-04Kód měny fakturyBT-5Přidejte DocumentCurrencyCode s kódem ISO 4217
BR-05Název prodávajícíhoBT-27Přidejte AccountingSupplierParty/Party/PartyName/Name
BR-06Název kupujícíhoBT-44Přidejte AccountingCustomerParty/Party/PartyName/Name

Specificky k BR-01: nejčastější příčinou je použití základního URI EN16931 namísto URI Peppol BIS 3.0. Vypadají podobně, ale jsou různé:

Špatně (základní EN16931):

<cbc:CustomizationID>urn:cen.eu:en16931:2017</cbc:CustomizationID>

Správně (Peppol BIS 3.0):

<cbc:CustomizationID>
  urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0::2.1
</cbc:CustomizationID>

Validátor kontroluje přesný řetězec. Kontext toho, proč na tom záleží, viz Úvod do Peppol BIS 3.0.

BR-CO-10: součet částek řádků neodpovídá celkové hodnotě

[BR-CO-10]-Sum of Invoice line net amount (BT-106) = 
sum of Invoice line net amount (BT-131).

Toto pravidlo vyžaduje, aby LegalMonetaryTotal/LineExtensionAmount (BT-106) rovnalo součtu všech hodnot InvoiceLine/LineExtensionAmount (BT-131).

Selhání je téměř vždy způsobeno chybou zaokrouhlování. Pokud zaokrouhlíte každou částku řádku nezávisle před součtem, součet zaokrouhlených hodnot se může lišit od zaokrouhleného součtu surových hodnot.

Špatně (zaokrouhlování po řádcích, pak sčítání):

// Line 1: 3 × 33.333... = 100.00 (rounded)
// Line 2: 3 × 33.333... = 100.00 (rounded)
// Sum of rounded: 200.00
// But raw sum: 3 × 33.333... + 3 × 33.333... = 200.00 exactly in this case

Selhání je zřetelnější u sazeb jako 7% DPH na různá množství, kde se zaokrouhlování hromadí přes mnoho řádků.

Správný přístup: sečtěte surové nezaokrouhlené hodnoty přes všechny řádky, pak zaokrouhlete agregát jednou:

var lineNetTotal = invoiceLines.Sum(l => l.Quantity * l.UnitPrice);  // raw
var lineNetTotalRounded = Math.Round(lineNetTotal, 2, MidpointRounding.AwayFromZero);

BR-CO-15: selhání aritmetiky celkové výše DPH faktury

[BR-CO-15]-Invoice total VAT amount (BT-110) = 
sum of VAT category tax amount (BT-117).

Celková výše DPH v TaxTotal/TaxAmount musí být rovna součtu hodnot TaxTotal/TaxSubtotal/TaxAmount přes všechny daňové kategorie.

U jednosazbové faktury to vypadá triviálně, ale selhání nastávají, když:

  • Existuje více sazeb DPH a zaokrouhlování se aplikuje po kategoriích, nikoli na agregát
  • Je přítomna kategorie nulové sazby nebo osvobození a její TaxAmount je 0.00, nikoli chybějící
  • TaxAmount na úrovni TaxTotal se počítá nezávisle od částek TaxSubtotal

Oprava: nejprve vypočítejte všechny hodnoty TaxSubtotal/TaxAmount, sečtěte je a použijte tento součet jako hodnotu TaxTotal/TaxAmount. Nepočítejte TaxTotal/TaxAmount samostatně.

BR-CO-16: splatná částka

[BR-CO-16]-Amount due for payment (BT-115) = 
Invoice total with VAT (BT-112) - Paid amount (BT-113) + Rounding amount (BT-114).

Pokud BT-113 (zaplacená částka) a BT-114 (částka zaokrouhlení) nejsou přítomny, zjednodušuje se to na BT-115 = BT-112. Selhání nastává, když PayableAmount a TaxInclusiveAmount se liší o více než povolenou toleranci zaokrouhlování.

Častá příčina: výpočet PayableAmount ze zaokrouhlených částek řádků a TaxInclusiveAmount z oddělené kalkulace celkové hodnoty, která produkuje mírně odlišný výsledek.

Oprava: vypočítejte jednu kanonickou celkovou hodnotu, použijte ji pro TaxInclusiveAmount i PayableAmount.

BR-S-08 a BR-S-09: konzistence sazby a výše DPH

[BR-S-08]-For each different value of VAT category rate (BT-119) where the 
VAT category code (BT-118) is "S", the VAT category taxable amount (BT-116) 
in a VAT breakdown (BG-23) shall equal the sum of Invoice line net amounts 
(BT-131) where the VAT category code of the Invoice line (BT-151) is "S" 
and the VAT rate for the Invoice line (BT-152) equals the VAT category rate.

Toto pravidlo seskupuje všechny řádky podle sazby DPH a ověřuje, že zdanitelná částka v každém TaxSubtotal odpovídá součtu řádků s danou sazbou. Selže, když:

  • Řádek má sazbu DPH, pro kterou neexistuje odpovídající TaxSubtotal
  • Existuje TaxSubtotal pro sazbu, ale žádné řádky danou sazbu nepoužívají
  • Rozdíly v zaokrouhlování se hromadí mezi výpočty na úrovni řádků a mezisoučtů

Oprava: sestavujte elementy TaxSubtotal dynamicky z položek řádků, seskupených podle (kód kategorie DPH, sazba DPH), nikoli je konstruujte nezávisle.

BR-E-01 / BR-AE-01: reverse charge a faktury osvobozené od daně

[BR-E-01]-An Invoice that contains an Invoice line (BG-25), a Document level 
allowance (BG-20) or a Document level charge (BG-21) where the VAT category 
code (BT-151) is "E" shall contain exactly one VAT breakdown...

Faktury s osvobozenou (E), přenesenou daňovou povinností (AE) nebo nulovou sazbou (Z) DPH vyžadují specifické struktury TaxCategory, které se liší od faktur se standardní (S) sazbou.

Časté chyby:

  • Použití hodnoty ID S pro všechny řádky bez ohledu na skutečné daňové zacházení
  • Vynechání elementů TaxExemptionReasonCode nebo TaxExemptionReason požadovaných pro osvobozené kategorie
  • Chybějící AccountingSupplierParty/Party/PartyTaxScheme, když faktura používá přenesenou daňovou povinnost

Pro přenesenou daňovou povinnost (AE) musí být přítomen daňový identifikátor kupujícího a rovněž daňový identifikátor prodávajícího.

Pravidla rozšíření specifická pro Peppol: Německo

Německé profily Peppol přidávají pravidla nad rámec základu EN16931. Časté chyby:

BR-DE-TMP-32: datum doručení (BT-72) je povinné pro německé B2G faktury. Přidejte Delivery/ActualDeliveryDate ve formátu YYYY-MM-DD.

BR-DE-18: musí být přítomen buď daňový identifikátor prodávajícího (BT-31) nebo daňové registrační číslo (BT-32). Chybí-li oboje, dochází k odmítnutí. Pokud je prodávající osvobozen od DPH, uveďte BT-32 s daňovým registračním číslem.

BR-DE-16: reference kupujícího (BT-10, Leitweg-ID) je povinná pro německé B2G faktury. Formát Leitweg-ID je validován: musí odpovídat vzoru \d{2,12}-\d{4,12}-\d{2}.

Efektivní ladění

Výstup Schematron chyb obsahuje umístění XPath:

<svrl:failed-assert location="/Invoice[1]/cac:LegalMonetaryTotal[1]">
  <svrl:text>[BR-CO-10]-Sum of Invoice line net amounts...</svrl:text>
</svrl:failed-assert>

Použijte umístění XPath pro přímou navigaci k selhávajícímu elementu. U aritmetických pravidel přidejte ladící výstup, který zaznamenává surové hodnoty před zaokrouhlením, abyste mohli sledovat, kde nesrovnalost vzniká.

Předkompilované soubory XSLT z vydání Peppol BIS na GitHubu jsou autoritativními validačními artefakty. Spouštějte je se Saxon HE, jak je popsáno v článku generování faktur Peppol BIS 3.0 v C#. Spuštění vůči nejnovějšímu vydání je důležité: sady pravidel se aktualizují s každým vydáním Peppol BIS a pravidlo, které projde starším XSLT, může selhat vůči aktuálnímu.

SealDoc a validační chyby

SealDoc spouští Schematron validaci EN16931 a Peppol BIS 3.0 na každé faktuře před doručením. Pokud validace selže, API vrátí strukturovanou chybovou odpověď mapující každé selhání na jeho kód BR, porušený BT a umístění XPath v odeslaném dokumentu.

Pro organizace ladící existující pipeline generování faktur veřejný validátor SealDoc přijímá soubory faktur UBL a CII, spouští aktuální XSLT Peppol BIS 3.0 a vrací úplný seznam selhání s popisem v přirozeném jazyce. Sady pravidel jsou udržovány aktuální s vydáními Peppol BIS, takže validátor vždy odráží pravidla, která použije přijímající přístupový bod.


← Back to all articles