# Änderungen an den Prüfregeln (P1–P3)

Datum aller Änderungen: **2026-10-06**. Ausgeführt von `erechnung/regeln-aktualisieren.mjs` (reproduzierbar aus dem
gepinnten ZIP der KoSIT-Prüfkonfiguration, siehe `HERKUNFT.md`); jede geänderte Datei trägt direkt nach der
XML-Deklaration einen Kommentar „buerow: geändert am 2026-10-06 …“ mit Grund, Original-Prüfsumme und Lizenz.

Alle drei Änderungen haben dasselbe Ziel: SaxonJS 2.7 soll dieselben Ergebnisse liefern wie der KoSIT-Validator
(Saxon-HE 12.9 in Java). Die Regeln selbst ändern sich inhaltlich nicht – nur Stellen, an denen SaxonJS von der
XPath-Semantik abweicht, werden so umgeschrieben, dass beide Prozessoren gleich rechnen.

| Datei | Lizenz | Änderungen | Prüfsumme vorher → nachher |
|---|---|---|---|
| `regeln/resources/cii/16b/xsl/EN16931-CII-validation.xsl` | EUPL-1.2 (CEN) | P2 (2 Stellen) | `0911e927…4822199` → `f502bb96…fde3e10` |
| `regeln/resources/xrechnung/3.0.2/xsl/XRechnung-CII-validation.xsl` | Apache-2.0 (KoSIT) | P1 (1), P2 (8), P3 (3 Literale, 4 Stellen) | `ec54f662…4aac40` → `f8ad744a…3eb73bb` |

Die vollständigen Prüfsummen und die Liste jeder einzelnen Ersetzung stehen in `erechnung/manifest.json`
(`dateien.*.korrekturen`).

## P1 – IBAN-Prüfziffer exakt rechnen

- **Was:** In der Funktion `xr:checkIBAN` (Regel BR-DE-19) wird `xs:integer(string-join(…)) mod 97` zu
  `xs:decimal(string-join(…)) mod 97`. Genau eine Stelle; das Werkzeug bricht ab, wenn das Muster nicht genau einmal
  vorkommt oder eine weitere `xs:integer(string-join(`-Stelle auftaucht.
- **Warum:** Für die Prüfziffer wird die IBAN in eine Zahl mit 20–40 Ziffern umgewandelt. SaxonJS rechnet
  `xs:integer` jenseits von 2^53 mit Gleitkomma-Genauigkeit und erhält einen falschen Rest. `xs:decimal` rechnet
  SaxonJS exakt; das Ergebnis entspricht der XPath-Semantik.
- **Beleg:** `selbsttest/gueltig-xrechnung.xml` mit der gültigen Test-IBAN DE89370400440532013000: KoSIT ohne
  Meldung, unverändertes SaxonJS meldet BR-DE-19 (Warnung). Im Abgleich betraf das 29 von 50 buerow-Mustern.
  Gegenprobe: falsche Prüfziffer (`selbsttest/iban-pruefziffer-falsch.xml`, 34-stellig `l12-iban-34-stellen-falsch-xr.xml`)
  meldet weiterhin BR-DE-19.

## P2 – `\s` und `\S` in regulären Ausdrücken

- **Was:** In Regex-Literalen wird `\s` außerhalb von Zeichenklassen zu `[ \t\n\r]`, innerhalb zu ` \t\n\r`,
  `\S` zu `[^ \t\n\r]`. Betroffen: Datumsprüfung CII-DT-097 (EN16931, 2×), E-Mail BR-DE-28, IBAN-Normalisierung,
  Skonto BR-DE-18, Zahlungsarten-Liste (XRechnung, 8×). Nach der Änderung darf kein `\s`/`\S` mehr in einem
  Literal stehen, sonst bricht das Werkzeug ab.
- **Warum:** XPath kennt als `\s` nur Leerzeichen, Tab, LF und CR. SaxonJS übersetzt `\s` wie JavaScript, das auch
  geschütztes Leerzeichen (U+00A0), U+2028/U+2029, U+FEFF und weitere Unicode-Leerzeichen einschließt.
- **Beleg:** `test/fixtures/erechnung/t07-datum-nbsp-zf.xml` (Datum mit NBSP): KoSIT abgelehnt mit CII-DT-097,
  unverändertes SaxonJS annehmbar. Ebenso `neg-11-datum-nbsp.xml` aus dem Abgleich; `r01-email-nbsp-ende-xr.xml`:
  KoSIT ohne Meldung, unverändertes SaxonJS BR-DE-28 (Warnung).

## P3 – Regex-Punkt

- **Was:** Jeder unmaskierte `.` außerhalb von Zeichenklassen in einem Regex-Literal wird zu `[^\n\r]`.
  Betroffen (nur XRechnung): `'#.+#'` (BR-DE-18, 2×), `XR-TELEPHONE-REGEX` `'.*([0-9].*){3,}.*'` (BR-DE-27),
  `XR-URL-REGEX` `'^([a-zA-Z])([a-zA-Z0-9+.-])+:.*'` (BR-TMP-2). Das Werkzeug sucht alle Regex-Argumente von
  `matches`, `replace`, `tokenize` und `analyze-string` (auch über `xsl:variable`) und bricht ab, wenn danach noch
  ein unmaskierter Punkt steht, ein Argument nicht auflösbar ist oder Flags verwendet werden.
- **Warum:** In XPath passt `.` auf jedes Zeichen außer LF und CR. SaxonJS übersetzt ihn wie JavaScript, wo er
  zusätzlich U+2028 und U+2029 ausschließt.
- **Beleg:** `test/fixtures/erechnung/r08-skonto-u2028-xr.xml` (Skontozeile, Folgezeile mit U+2028): KoSIT
  abgelehnt mit BR-DE-18, unverändertes SaxonJS annehmbar. Ebenso `r09-skonto-u2029-xr.xml`;
  `r05-telefon-u2028-xr.xml`: KoSIT ohne Meldung, unverändertes SaxonJS BR-DE-27 (Warnung).

## Nicht korrigiert: BR-CO-10 bei Zeilenbeträgen mit mehr als zwei Nachkommastellen

Für `xs:decimal(sum(…))` über Beträge aus dem Dokument wandelt Saxon in Java den Gleitkommawert exakt um
(1,005 → 1,00499…), SaxonJS über die kürzeste Darstellung (1,005). Liegt eine Summe von Zeilenbeträgen (BT-131)
mit drei oder mehr Nachkommastellen nahe einem halben Cent, meldet SaxonJS deshalb zusätzlich BR-CO-10
(Belege: `gegen/d04-halbercent-zeilensumme-zf.xml` mit 1,005 gegen Kopf 1,00 und `d05-halbercent-2.675-xr.xml`
mit 2,675 gegen 2,67 – KoSIT: BR-DEC-23 und BR-S-08, SaxonJS zusätzlich BR-CO-10). Das Urteil bleibt gleich
(abgelehnt), weil bei mehr als zwei Nachkommastellen immer BR-DEC-23 greift; buerow selbst schreibt nur zwei
Nachkommastellen. Eine Korrektur wie P1–P3 gibt es dafür bewusst nicht: Die exakte Umrechnung double → BigDecimal
von Java lässt sich in XPath nicht sauber nachbilden. Der Fall ist als Fixture mit dokumentierter Abweichung
festgehalten (`test/fixtures/erechnung/d04-halbercent-zeilensumme-zf.xml`), damit ein SaxonJS-Update, das dieses
Verhalten ändert, auffällt. Dieselbe Ursache hat BR-CO-10 bei Beträgen ab etwa 10^13 €.

## Unverändert

`scenarios.xml`, `default-report.xsl`, `xrechnung-report.xsl` und die UN/CEFACT-XSD sind byteweise aus der
Konfiguration übernommen. Die SEF-Dateien (`erechnung/sef/`) sind Kompilate der geänderten XSLTs.

## Weitergabe (EUPL-1.2)

Die geänderte Fassung von `EN16931-CII-validation.xsl` steht unter der EUPL-1.2. Nach Art. 5 der EUPL muss sie
allen, denen buerow die Prüfung bereitstellt – auch als Online-Dienst –, im Quelltext zugänglich sein, zusammen
mit dieser Änderungsliste und dem Lizenztext (`EUPL-1.2.txt`).
