# Optional validation resources

Brightfern's validator works out of the box using its always-on **business-rule layer**. Dropping the
official artifacts here additionally enables the **XSD** and **Schematron** layers (Schematron needs PHP
`ext-xsl`).

## Schematron (Layer 3)

Place the Peppol BIS Billing 3.0 schematron, **precompiled to XSLT**, at:

```
resources/schematron/peppol-bis-billing.xslt
```

The official rules ship as `.sch` files (EN 16931 + Peppol). Compile them to a single XSLT with an
ISO-Schematron pipeline, e.g.:

```
# using the ISO Schematron XSLT2 skeleton (Saxon)
saxon -xsl:iso_dsdl_include.xsl   -s:PEPPOL-EN16931-UBL.sch  -o:step1.sch
saxon -xsl:iso_abstract_expand.xsl -s:step1.sch              -o:step2.sch
saxon -xsl:iso_svrl_for_xslt2.xsl  -s:step2.sch              -o:peppol-bis-billing.xslt
```

The output emits **SVRL**, which `InvoiceValidator` parses: every `<svrl:failed-assert>` becomes a
validation error (or a warning when flagged as such).

### Factur-X (France) — CII syntax

When Format is set to Factur-X, Brightfern looks for the **CII** rule set at:

```
resources/schematron/cii-en16931.xslt
```

Compile it from `cii/schematron/` in ConnectingEurope/eInvoicing-EN16931 (the same
ISO-Schematron pipeline as above). This slot is worth filling if you invoice French
public bodies: a number of EN 16931 CII rules are **invisible to the XSD**, which
permits much more than the standard allows — so neither the plugin's own
`schemaValidate()` nor the XSD check that `atgp/factur-x` runs at PDF-embedding time
will catch them. `CII-DT-031` (the `currencyID` attribute is forbidden on every
amount except `TaxTotalAmount`) is a concrete example. Without this file you still
get the always-on business-rule layer and a schema-valid document.

### XRechnung (Germany)

When Format is set to XRechnung in Settings, Brightfern looks for a **separate** schematron at:

```
resources/schematron/xrechnung.xslt
```

instead of the Peppol one. Compile it the same way from the official KoSIT XRechnung `.sch` rules
(itplr-kosit/xrechnung-schematron on GitHub). Without this file, XRechnung invoices still get the
always-on business rules plus the correct `CustomizationID` — a large share of real-world validation
— but not full German CIUS rule coverage (the ~20 `BR-DE-*` rules).

### KSeF (Poland) — no schematron

KSeF is the exception: the Ministry of Finance publishes no distributable rule file, because
validation happens server-side when a document is cleared. Poland therefore relies on the
always-on business rules plus the XSD below — which carries far more weight than usual, since
FA(3) pins element order, the mandatory annotation blocks and every field pattern in the schema
itself.

## XSD (Layer 2)

Place the OASIS UBL 2.1 Invoice schema (and its imported schemas) at:

```
resources/xsd/UBL-Invoice-2.1.xsd
```

When present, `DOMDocument::schemaValidate()` runs structural validation before the schematron.

For **KSeF**, fetch the Ministry's FA(3) schema with the helper script instead of placing it by hand
— it rewrites one absolute `schemaLocation` import to a local path, without which every validation
would try to reach `crd.gov.pl` over the network:

```
python bin/fetch-ksef-schema.py
```

That writes `resources/xsd/ksef/schemat_FA(3)_v1-0E.xsd` plus the three base type libraries it
depends on. It also enables the XSD assertions in `tests/Invoice/Fa3SerializerTest.php`, which
otherwise skip.

## Licensing

These artifacts have their own licenses (OASIS / OpenPeppol / KoSIT / the Polish Ministry of Finance).
They are **not** bundled in the repository, and `bin/build-zip.py` also excludes `resources/xsd/` and
`resources/schematron/` from the packaged plugin (`EXCLUDE_PATHS`) so that a developer who happens to
have fetched them locally does not silently change what ships. Add them to your own build per their
terms by dropping the relevant entry from that list.
