Attestwire › Rule reference › BR-DE-18
BR-DE-18 Skonto in the payment terms follows a fixed grammar
noun · XRechnung · fatal · BT-20
Your invoice was refused because an early-payment discount (Skonto) in the payment terms does not follow the prescribed notation. For XRechnung, a line that starts with “#” is a machine-readable Skonto entry and must look exactly like this: #SKONTO#TAGE=n#PROZENT=n.nn#, optionally followed by #BASISBETRAG=n.nn#. In the standard the field is BT-20, and BR-DE-18 is the rule that checks the notation.
Diese Regel auf Deutsch: BR-DE-18 — auf Deutsch erklärt.
- Business term
BT-20— Payment terms- Severity
fatal- Applies to
xrechnung-ubl, xrechnung-cii
The fix
Write each discount on its own line, for example "#SKONTO#TAGE=14#PROZENT=2.00#", and end the last one with a line break. Add the base amount only when the discount applies to part of the invoice rather than to the whole amount due (BT-115).
What the rule requires
The form is deliberately rigid, because it is the one place where the free text in BT-20 turns into something a machine can read: #SKONTO#TAGE=14#PROZENT=2.00#, optionally with a fourth segment #BASISBETRAG=1000.00# before the closing #. TAGE is the number of days, PROZENT the discount rate, and BASISBETRAG the base amount the discount applies to.
The details that trip people up: every segment is separated and terminated by #. Everything is upper case. The percentage has exactly two decimals with a dot, never a comma, and no sign. No whitespace is allowed anywhere inside the entry. Each entry stands on its own line and ends with a line break.
You can still write prose in BT-20; it just must not start a line with #. Zahlbar innerhalb von 30 Tagen. with the Skonto line underneath is exactly what the standard intends.
In this library the field is paymentTerms.
Die Informationen zur Gewährung von Skonto müssen wie folgt im Element "Payment terms" (BT-20) übermittelt werden: Anzugeben ist im ersten Segment "SKONTO", im zweiten "TAGE=n", im dritten "PROZENT=n". Prozentzahlen sind ohne Vorzeichen sowie mit Punkt getrennt von zwei Nachkommastellen anzugeben. Liegt dem zu berechnenden Betrag nicht BT-115, "fälliger Betrag" zugrunde, sondern nur ein Teil des fälligen Betrags der Rechnung, ist der Grundwert zur Berechnung von Skonto als viertes Segment "BASISBETRAG=n" gemäß dem semantischen Datentypen Amount anzugeben.
Jeder Eintrag beginnt mit einer #, die Segmente sind mit einer # getrennt und eine Zeile schließt mit einer # ab. Am Ende einer vollständigen Skontoangabe muss ein XML-konformer Zeilenumbruch folgen. Alle Angaben zur Gewährung von Skonto müssen in Großbuchstaben gemacht werden. Zusätzliches Whitespace (Leerzeichen, Tabulatoren oder Zeilenumbrüche) ist nicht zulässig.
Andere Zeichen oder Texte als in den oberen Vorgaben genannt sind nicht zulässig.
A passing value
"paymentTerms": "Zahlbar innerhalb von 30 Tagen.\n#SKONTO#TAGE=14#PROZENT=2.00#\n"Why it exists
Skonto is the one payment term the recipient has to be able to calculate automatically: the period decides when to pay, and the percentage decides how much. Free text cannot carry that. So XRechnung put a small syntax inside a text field, and that is why it is so unforgiving: a grammar that allows exceptions is not a grammar.
What the library returns
The full error returned by @attestwire/en16931. It includes the rule ID,
affected field and suggested correction. Developers can use this object in their application:
{
"rule": "BR-DE-18",
"field": "BT-20",
"severity": "fatal",
"message": "Line 1 of the payment terms (BT-20) starts with \"#\", which marks it as a Skonto (early-payment discount) entry, but \"#SKONTO#TAGE=14#PROZENT=2#\" does not match the grammar XRechnung prescribes. The format is rigid on purpose — it is the one place the standard turns free-text payment terms into something machine-readable — and it is: #SKONTO#TAGE=n#PROZENT=n.nn# with an optional fourth segment #BASISBETRAG=n.nn# before the closing #. Every segment is separated and terminated by \"#\", everything is upper case, the percentage carries exactly two decimals with a dot (never a comma) and no sign, and no whitespace of any kind is allowed inside the entry.",
"fix": "Write each discount as its own line, e.g. \"#SKONTO#TAGE=14#PROZENT=2.00#\". Add the base amount only when the discount applies to part of the invoice rather than to the full amount due (BT-115): \"#SKONTO#TAGE=14#PROZENT=2.00#BASISBETRAG=1000.00#\". Each entry must end with a newline. Human-readable prose is fine in BT-20 as well — it just must not start a line with \"#\".",
"example": "\"paymentTerms\": \"Zahlbar innerhalb von 30 Tagen.\\n#SKONTO#TAGE=14#PROZENT=2.00#\\n\"",
"xpath": "/ubl:Invoice/cac:PaymentTerms/cbc:Note",
"docsUrl": "https://attestwire.com/rules/BR-DE-18"
}
xpath is always a UBL path. On a CII invoice, look for the
matching CII field instead.
How our check differs
We match KoSIT’s own regular expression ($XR-SKONTO-REGEX in its common.sch) against each line that starts with #, after trimming the line, and we also report a correctly formed block that is not followed by a line break. A line that does not start with # is never checked, so prose in BT-20 is left alone.