Skip to content

account_invoice_en16931: the schematron validation is silently skipped when no Saxon server is reachable #44

Description

@almumu

Summary

When the Saxon server is not reachable, generate_en16931_xml() completes normally and the invoice is emitted as if it had passed the schematron. No exception, nothing visible to the user. On an instance where nobody deployed a Saxon server, every document would go out unvalidated, and the first thing to catch the errors would be the accredited platform rejecting them.

This is not an edge case: the validation is always attempted. check_schematron is set unconditionally, and the conditions below it only choose which schematron applies:

# account_invoice_en16931/models/account_move.py (18.0)
        check_schematron = "base"
        if (
            hasattr(self, "fr_directory_partner_entity_type")
            and self.fr_directory_company_entity_type == "private"
            and not self.env.context.get("chorus_old_xml_syntax")
        ):
            if self.fr_directory_partner_entity_type == "private":
                check_schematron = "fr-ctc"
            elif self.fr_directory_partner_entity_type == "public":
                check_schematron = "fr-chorus"

Why it is silent

The module calls generate_xml() without raise_if_http_error:

# account_invoice_en16931/models/account_move.py:853-862 (18.0)
            xml_bytes = generate_xml(
                data_dict,
                flavor=flavor,
                level=level,
                check_xsd=True,
                check_schematron=check_schematron,
                saxon_server_url=saxon_server_url,
                saxon_server_codedb_base_url=saxon_server_codedb_base_url,
                saxon_server_codedb_dir=saxon_server_codedb_dir,
            )

raise_if_http_error does not appear anywhere in this repository (0 hits on 18.0 and on the 16.0 backport of #33), so the library default applies — facturx.xml_check_schematron(..., raise_if_http_error=False) in factur-x 6.6. With that default, a failed POST to the Saxon server is logged as a warning and the check is skipped rather than raising.

So the library offers the choice and the module currently takes the silent one. For a compliance module that seems like the wrong default.

Reproduction

Calling the check directly on a valid EN16931 XML with no Saxon server listening:

  • raise_if_http_error=False (what the module uses) → returns without raising, i.e. the document is treated as validated.
  • raise_if_http_error=TrueRuntimeError: Check 'base' failed in the POST request to saxon server on http://localhost:5000/transform: ... Failed to establish a new connection.

With a server up, the controls behave correctly: a valid document passes, and the same document with a corrupted InvoiceCurrencyCode reports two errors citing the rule ([BR-CO-15]). The validation itself works — the only problem is what happens when the server is absent.

Second point: the server needs two undocumented adjustments

Even with willemvlh/saxon-server running (the one referenced in pyfrctc), the request fails with HTTP 400 out of the box:

{"statusCode":400,"exceptionType":"TransformationException",
 "message":"Access to URI https://raw.githubusercontent.com/akretion/factur-x/refs/heads/master/src/facturx/xsd_and_schematron/facturx-en16931/FACTUR-X_EN16931_codedb.xml has been prohibited"}

The schematron stylesheet resolves an external URI for the code database, and Saxon blocks external access under its default configuration. Passing saxon_server_codedb_dir rewrites it to a local path, which is blocked as well (Access to URI file:/codedb/... has been prohibited). It only works once the server runs in permissive mode (--insecure for that image) and the codedb file is
made available to it.

Worth a line in the README, since a deployment that misses this ends up in exactly the silent-skip situation above — the validation looks enabled and never runs.

Proposal

  1. Pass raise_if_http_error=True in the generate_xml() calls, or expose it as a setting that defaults to raising. Failing loudly when the validation cannot run is safer than emitting a document that was never checked.
  2. Document the Saxon server requirements (permissive configuration + codedb availability).

Seen on 18.0 (line numbers above) and on the 16.0 backport of #33.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions