BR-DE-18 A line 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
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.
This rule is implemented and its error payload below is real, but the long-form write-up — normative text, worked example, divergence note — is not written yet. Everything the library knows about this rule is on this page. Ask and we will prioritise it.
- Business term
BT-20- Severity
fatal
What the library returns
The exact object in result.errors when this rule fires.
Generated by running @attestwire/en16931, not transcribed:
{
"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 locates the element in the generated UBL document,
which is where a KoSIT or Peppol validator will report the same problem.
A passing value
"paymentTerms": "Zahlbar innerhalb von 30 Tagen.\n#SKONTO#TAGE=14#PROZENT=2.00#\n"The 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 "#".