diff --git a/pyproject.toml b/pyproject.toml index 7865f82..02efaaf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "govoplan-xrechnung" -version = "0.1.20" +version = "0.1.21" description = "GovOPlaN inbound XRechnung validation connector." readme = "README.md" requires-python = ">=3.12" diff --git a/src/govoplan_xrechnung/__init__.py b/src/govoplan_xrechnung/__init__.py index 2238e83..ad8af0b 100644 --- a/src/govoplan_xrechnung/__init__.py +++ b/src/govoplan_xrechnung/__init__.py @@ -1,4 +1,3 @@ """GovOPlaN XRechnung connector.""" -__version__ = "0.1.19" - +__version__ = "0.1.21" diff --git a/src/govoplan_xrechnung/backend/manifest.py b/src/govoplan_xrechnung/backend/manifest.py index 2959976..791c770 100644 --- a/src/govoplan_xrechnung/backend/manifest.py +++ b/src/govoplan_xrechnung/backend/manifest.py @@ -16,7 +16,7 @@ from govoplan_core.core.provider_governance import declared_module_architecture MODULE_ID = "xrechnung" -MODULE_VERSION = "0.1.20" +MODULE_VERSION = "0.1.21" READ_SCOPE = "xrechnung:validation:read" EXECUTE_SCOPE = "xrechnung:validation:execute" HANDOFF_SCOPE = "xrechnung:handoff:create" @@ -123,8 +123,105 @@ manifest = ModuleManifest( ], "verification": "Confirm artifact and profile digests, technical outcome, validation-step count, formal validity, VARL assessment, invoice digest, and report digest before following the handoff reference.", }, + structured_translation_version="1", + structured_translations={ + "de": { + "prerequisites": [ + "Eine exakte KoSIT-Engine und eine eigenständige XRechnung-Konfiguration sind lokal installiert.", + "Die Administration hat alle Artefaktprüfsummen und die erwartete Zahl der Prüfschritte freigegeben.", + "Das Profil ist in der installationsbezogenen Positivliste für den Eingangszeitpunkt der Rechnung freigegeben.", + "Der fachlich verantwortliche Rechnungsablauf liefert einen stabilen Mandanten- und Quellverweis.", + ], + "steps": [ + "Ausführbare Datei, Engine-Prüfsumme, vollständige Prüfsumme der Konfigurationsstruktur und Szenariopfad prüfen.", + "Ein ausdrückliches Positivlistenprofil oder den ausdrücklich konfigurierten Installationsstandard auswählen.", + "Unsicheres oder übergroßes XML abweisen und anschließend das festgelegte Profil mit begrenzter Zeit und Ausgabe ausführen.", + "Technische Vollständigkeit getrennt von der semantischen Gültigkeit des Berichts prüfen.", + "Nur bei vollständigem, formal gültigem und die Rechnung annehmendem Bericht eine Übergabe erzeugen.", + ], + "limitations": [ + "Diese Version aktiviert standardmäßig keine XRechnung- oder Konfigurationsversion.", + "Der Konnektor erzeugt einen Übergabevertrag, führt aber weder Freigabe der Verbindlichkeit noch Buchung.", + "Ein syntaktisch gültiger Bericht ist nicht vertrauenswürdig, wenn die Runner-Ausgabe einen technischen Fehler meldet.", + ], + "consequences": [ + "Jede Änderung eines festgelegten Artefakts ändert die Profilprüfsumme und erfordert eine neue Validierung.", + "Ungültige Rechnungen bewahren Diagnosen, dürfen aber nicht in die gültige Rechnungsübergabe gelangen.", + "Technischer Fehler ergibt unbekannte Konformität, niemals semantische Ablehnung oder Annahme.", + ], + "verification": "Vor dem Folgen des Übergabeverweises Artefakt- und Profilprüfsummen, technisches Ergebnis, Prüfschrittzahl, formale Gültigkeit, VARL-Bewertung, Rechnungsprüfsumme und Berichtsprüfsumme bestätigen.", + } + }, order=100, ), + DocumentationTopic( + id="xrechnung.reference.validation-profile-and-handoff", + title="XRechnung validation-profile and handoff consequences", + summary=( + "Understand receive-time profile selection, immutable artifact approval, " + "technical uncertainty, and the evidence boundary of a valid-invoice handoff." + ), + body=( + "A validation profile names exact engine, configuration, scenario, and artifact " + "digests. Its approval has an actor, decision reference, receive-time window, and " + "approved, suspended, or retired state. Selection uses the invoice receive time and " + "never infers a product version. Unknown, not-yet-valid, expired, suspended, retired, " + "or digest-mismatched profiles fail closed. Validation evidence distinguishes runner " + "completion, report completeness, formal validity, and semantic assessment. A handoff " + "binds the exact invoice, report, and profile digests but does not approve a payable, " + "book an invoice, retain the source file, or replace Records custody." + ), + layer="available", + documentation_types=("admin", "user"), + audience=("user", "operator", "module_admin", "auditor"), + related_modules=("files", "procurement", "payments", "records", "audit"), + links=( + DocumentationLink( + label="Inbound validation profile", + href="docs/INBOUND_VALIDATION.md", + kind="repository", + ), + ), + translations={ + "de": { + "title": "Folgen von XRechnung-Prüfprofil und Übergabe", + "summary": ( + "Profilauswahl nach Eingangszeit, unveränderliche Artefaktfreigabe, technische Ungewissheit und die Nachweisgrenze einer gültigen Rechnungsübergabe verstehen." + ), + "body": ( + "Ein Prüfprofil bezeichnet exakte Prüfsummen von Engine, Konfiguration, Szenario und Artefakten. Seine " + "Freigabe enthält Akteur, Entscheidungsverweis, Eingangszeitfenster und den Zustand freigegeben, gesperrt " + "oder ausgemustert. Die Auswahl verwendet den Eingangszeitpunkt der Rechnung und leitet niemals eine " + "Produktversion ab. Unbekannte, noch nicht gültige, abgelaufene, gesperrte, ausgemusterte oder bei der " + "Prüfsumme abweichende Profile werden geschlossen abgewiesen. Der Validierungsnachweis unterscheidet " + "Runner-Abschluss, Berichtsvollständigkeit, formale Gültigkeit und semantische Bewertung. Eine Übergabe " + "bindet exakte Rechnungs-, Berichts- und Profilprüfsummen, genehmigt aber keine Verbindlichkeit, bucht keine " + "Rechnung, bewahrt keine Quelldatei und ersetzt nicht die Verwahrung durch Records." + ), + } + }, + metadata={ + "kind": "reference", + "consequence_classes": { + "approve_profile": "Makes one exact verified artifact set eligible only in its receive-time window.", + "suspend_or_retire_profile": "Blocks selection without rewriting prior validation evidence.", + "fail_closed": "Treats unknown profile or technical outcome as untrusted rather than valid or invalid.", + "create_handoff": "Binds valid evidence for an owner workflow without approving, booking, or storing the invoice.", + }, + }, + structured_translation_version="1", + structured_translations={ + "de": { + "consequence_classes": { + "approve_profile": "Macht genau einen geprüften Artefaktsatz ausschließlich in seinem Eingangszeitfenster auswählbar.", + "suspend_or_retire_profile": "Sperrt die Auswahl, ohne frühere Validierungsnachweise umzuschreiben.", + "fail_closed": "Behandelt unbekanntes Profil oder technisches Ergebnis als nicht vertrauenswürdig statt als gültig oder ungültig.", + "create_handoff": "Bindet gültige Nachweise für einen Eigentümerablauf, ohne die Rechnung zu genehmigen, zu buchen oder zu speichern.", + } + } + }, + order=110, + ), ), architecture=declared_module_architecture( layer="data_reporting_integration", diff --git a/tests/test_documentation.py b/tests/test_documentation.py new file mode 100644 index 0000000..79e03e9 --- /dev/null +++ b/tests/test_documentation.py @@ -0,0 +1,16 @@ +from __future__ import annotations + +from govoplan_core.core.modules import documentation_structured_translation_issues +from govoplan_xrechnung.backend.manifest import manifest + + +def test_xrechnung_german_workflow_and_reference_are_complete() -> None: + topics = {topic.id: topic for topic in manifest.documentation} + assert len(topics) == 2 + assert topics["xrechnung.inbound-validation"].metadata["kind"] == "workflow" + assert topics["xrechnung.reference.validation-profile-and-handoff"].metadata["kind"] == "reference" + for topic in topics.values(): + assert all(topic.translations["de"].get(key) for key in ("title", "summary", "body")) + assert topic.structured_translation_version == "1" + assert "de" in topic.structured_translations + assert documentation_structured_translation_issues(topic) == () diff --git a/tests/test_validation.py b/tests/test_validation.py index c693b8e..ac01c98 100644 --- a/tests/test_validation.py +++ b/tests/test_validation.py @@ -154,5 +154,5 @@ def test_technical_failures_never_trust_a_valid_looking_report( def test_manifest_does_not_select_an_active_standard_version() -> None: manifest = get_manifest() - assert manifest.version == "0.1.20" + assert manifest.version == "0.1.21" assert "none is activated by default" in manifest.architecture.known_limits[0].lower()