Files
govoplan-erp/src/govoplan_erp/backend/manifest.py
T
zemion 51dc33d8ae
Module Package Release / publish-packages (push) Successful in 10s
docs(erp): complete German reference contract
2026-08-23 19:31:23 +02:00

329 lines
16 KiB
Python

from __future__ import annotations
from datetime import UTC, datetime
from govoplan_core.core.access import (
CAPABILITY_AUTH_PERMISSION_EVALUATOR,
CAPABILITY_AUTH_PRINCIPAL_RESOLVER,
)
from govoplan_core.core.modules import (
DocumentationCondition,
DocumentationLink,
DocumentationTopic,
ModuleManifest,
PermissionDefinition,
RoleTemplate,
)
from govoplan_core.core.provider_governance import (
ExternalProviderDeclaration,
ExternalProviderRuntimeState,
ExternalProviderStateContext,
ExternalProviderStateProviderRegistration,
ProviderBehaviorDeclaration,
ProviderObjectDeclaration,
declared_module_architecture,
)
MODULE_ID = "erp"
MODULE_VERSION = "0.1.20"
READ_SCOPE = "erp:payables:read"
PLAN_SCOPE = "erp:payables:plan"
RECONCILE_SCOPE = "erp:bookings:reconcile"
ADMIN_SCOPE = "erp:integration:admin"
ERP_PROVIDER_ID = "erp.payables_target"
def _permission(scope: str, label: str, description: str) -> PermissionDefinition:
module_id, resource, action = scope.split(":", 2)
return PermissionDefinition(
scope=scope,
label=label,
description=description,
category="ERP",
level="tenant",
module_id=module_id,
resource=resource,
action=action,
)
ERP_PROVIDER = ExternalProviderDeclaration(
id=ERP_PROVIDER_ID,
module_id=MODULE_ID,
label="Configured ERP payable and booking target",
maturity="read",
operations=("read", "preview", "dry_run"),
objects=(
ProviderObjectDeclaration(
object_type="payable_export",
field_groups=("identity", "amount", "coding", "evidence"),
authority_modes=("native_authoritative",),
default_authority_mode="native_authoritative",
),
ProviderObjectDeclaration(
object_type="booking_observation",
field_groups=("correlation", "status", "amount", "evidence"),
authority_modes=("external_authoritative",),
default_authority_mode="external_authoritative",
),
),
behavior=ProviderBehaviorDeclaration(
revision_tokens=(
"Payable revisions and mapping revisions bind export plans; external revisions "
"bind booking observations."
),
concurrency=(
"Only the exact payable revision and reviewed profile digest may be reconciled."
),
freshness=(
"Every booking observation carries an aware timestamp and external revision."
),
health=(
"Profile mapping, export planning, target transport, correlation lookup, and "
"reconciliation are reported separately."
),
max_read_items=1000,
idempotency=(
"The exact payable projection digest is the default idempotency identity."
),
retry=(
"No export retry is allowed until the target is queried by the exact plan correlation."
),
timeout_seconds=30,
conflicts=(
"Tenant, payable, provider, profile, plan, currency, and amount mismatches are quarantined."
),
outcome_unknown=(
"A missing response is unknown, never evidence that a payable was or was not booked."
),
outcome_unknown_supported=True,
evidence=(
"Payable, profile, payload, plan, external revision, and observation digests form evidence."
),
correction=(
"Correct the source payable or mapping and issue a new revision-bound export plan."
),
rollback="An external booking is not assumed to be transactionally reversible.",
compensation=(
"Reversals are separate correlated observations and never overwrite the original booking."
),
reconciliation=(
"Look up the external booking by stable plan correlation and compare amount, currency, "
"profile, revision, and evidence before retry."
),
outage=(
"Module-owned payable state remains authoritative while export and reconciliation wait."
),
classifications=("confidential", "restricted"),
purposes=("payable export", "booking reconciliation"),
retention=(
"Owning finance modules retain payables; ERP and Audit retain only governed integration evidence."
),
secret_handling=(
"Profiles contain only a connection reference; credentials never enter plans or evidence."
),
),
documentation_topic_ids=("erp.payable-reconciliation",),
)
def _provider_states(
context: ExternalProviderStateContext,
) -> tuple[ExternalProviderRuntimeState, ...]:
del context
return (
ExternalProviderRuntimeState(
provider_id=ERP_PROVIDER_ID,
observed_at=datetime.now(UTC),
configured=False,
active=False,
health="inactive",
freshness="not_applicable",
conflict="not_applicable",
recovery="unsupported",
detail="No target-tested ERP product binding is configured; dispatch is disabled.",
),
)
manifest = ModuleManifest(
id=MODULE_ID,
name="ERP",
version=MODULE_VERSION,
dependencies=("access",),
optional_dependencies=("procurement", "payments", "ledger", "files", "audit", "policy"),
required_capabilities=(
CAPABILITY_AUTH_PRINCIPAL_RESOLVER,
CAPABILITY_AUTH_PERMISSION_EVALUATOR,
),
permissions=(
_permission(READ_SCOPE, "View ERP integration", "Read non-secret profiles, plans, and reconciliation evidence."),
_permission(PLAN_SCOPE, "Plan payable export", "Create a digest-bound, effect-free payable export plan."),
_permission(RECONCILE_SCOPE, "Reconcile ERP booking", "Interpret a correlated external booking observation."),
_permission(ADMIN_SCOPE, "Administer ERP integration", "Configure and verify ERP mappings and recovery behavior."),
),
role_templates=(
RoleTemplate(
slug="erp_integration_operator",
name="ERP integration operator",
description="Plan payable exports and reconcile correlated booking observations.",
permissions=(READ_SCOPE, PLAN_SCOPE, RECONCILE_SCOPE),
),
RoleTemplate(
slug="erp_integration_administrator",
name="ERP integration administrator",
description="Configure and verify governed ERP target bindings.",
permissions=(READ_SCOPE, PLAN_SCOPE, RECONCILE_SCOPE, ADMIN_SCOPE),
),
),
external_providers=(ERP_PROVIDER,),
external_provider_state_providers=(
ExternalProviderStateProviderRegistration(
module_id=MODULE_ID,
provider_id=ERP_PROVIDER_ID,
provider=_provider_states,
),
),
documentation=(
DocumentationTopic(
id="erp.boundary",
title="ERP integration boundary",
summary="Exchange governed finance projections without making GovOPlaN a replacement ERP or moving payable authority into the connector.",
body=(
"ERP owns product profiles, mapping and transport plans, correlation, and external booking observations. Procurement, Payments, and Ledger continue to own approvals, payables, payments, and accounting projections. An export plan is not evidence of external booking, and an unconfigured provider declaration is not a production integration."
),
layer="available",
documentation_types=("admin", "user"),
audience=("user", "operator", "module_admin", "auditor"),
related_modules=("procurement", "payments", "ledger", "audit"),
links=(
DocumentationLink(
label="ERP integration boundary",
href="docs/PAYABLE_EXPORT_AND_RECONCILIATION.md",
kind="repository",
),
),
translations={
"de": {
"title": "Integrationsgrenze des ERP-Moduls",
"summary": "Gesteuerte Finanzprojektionen austauschen, ohne GovOPlaN zum Ersatz-ERP zu machen oder die Verantwortung für Verbindlichkeiten in den Konnektor zu verlagern.",
"body": "ERP verantwortet Produktprofile, Zuordnung und Transportpläne, Korrelation sowie externe Buchungsbeobachtungen. Procurement, Payments und Ledger bleiben für Freigaben, Verbindlichkeiten, Zahlungen und Buchhaltungsprojektionen zuständig. Ein Übergabeplan ist kein Buchungsnachweis; eine unkonfigurierte Anbieterdeklaration ist keine produktive Integration.",
}
},
metadata={"kind": "reference"},
order=90,
),
DocumentationTopic(
id="erp.payable-reconciliation",
title="Plan payable exports and reconcile ERP bookings",
summary=(
"Bind an exact payable revision to an effect-free export plan and quarantine "
"uncorrelated or unmapped external booking observations."
),
body=(
"Procurement, Payments, or Ledger owns the payable and supplies integer minor-unit "
"amounts, stable references, an invoice digest, and a revision. ERP combines this "
"projection with an administrator-reviewed product profile, schema, company code, "
"mapping revision, connection reference, and status mapping. The resulting canonical "
"payload and plan are digest-bound but cannot dispatch in this release. Reconciliation "
"accepts only an observation tied to the exact plan, provider, tenant, payable, amount, "
"currency, and evidence. Unknown statuses or mismatches are quarantined. Bookings, "
"rejections, and reversals remain separate evidence-bearing outcomes."
),
layer="configured",
documentation_types=("admin", "user"),
audience=("user", "operator", "module_admin", "auditor"),
related_modules=("procurement", "payments", "ledger", "files", "audit"),
conditions=(
DocumentationCondition(
any_scopes=(READ_SCOPE, PLAN_SCOPE, RECONCILE_SCOPE, ADMIN_SCOPE),
),
),
links=(
DocumentationLink(
label="ERP payable and booking contract",
href="docs/PAYABLE_EXPORT_AND_RECONCILIATION.md",
kind="repository",
),
),
translations={
"de": {
"title": "Kreditorische Übergaben planen und ERP-Buchungen abgleichen",
"summary": "Eine exakte Verbindlichkeitsrevision an einen wirkungsfreien Übergabeplan binden und nicht korrelierte oder unbekannte ERP-Buchungsstände sperren.",
"body": "Procurement, Payments oder Ledger verantwortet die Verbindlichkeit und liefert ganzzahlige Nebenwährungseinheiten, stabile Referenzen, die Rechnungsprüfsumme und eine Revision. ERP verbindet diese Projektion mit einem administrativ geprüften Produktprofil, Schema, Buchungskreis, Mapping-Revision, Verbindungsreferenz und einer Statuszuordnung. Nutzdaten und Plan sind kanonisch prüfsummengebunden, können in dieser Version aber nicht versendet werden. Der Abgleich akzeptiert nur Beobachtungen, die exakt zu Plan, Anbieter, Mandant, Verbindlichkeit, Betrag, Währung und Nachweis passen. Unbekannte Zustände und Abweichungen werden isoliert. Buchung, Ablehnung und Storno bleiben getrennte nachweisgebundene Ergebnisse.",
}
},
metadata={
"kind": "workflow",
"prerequisites": [
"The owning finance module supplies an approved immutable payable revision.",
"An administrator has reviewed the target schema and raw-status mapping.",
"The connection reference resolves through a deployment-owned credential boundary.",
],
"steps": [
"Build and review the exact canonical export payload and plan digests.",
"Dispatch only through a separately target-tested adapter.",
"Read the booking by stable plan correlation after success, timeout, or retry.",
"Record, wait, or quarantine the deterministic reconciliation decision.",
],
"limitations": [
"No ERP product, schema, endpoint, or transport is selected by this release.",
"Plans cannot dispatch and observations are not persisted by this module.",
],
"consequences": [
"Changing the payable or mapping produces a new plan and idempotency identity.",
"A timeout never implies success or failure.",
"Reversals remain linked outcomes rather than destructive status replacement.",
],
},
structured_translation_version="1",
structured_translations={
"de": {
"prerequisites": [
"Das fachlich verantwortliche Finanzmodul liefert eine freigegebene unveränderliche Verbindlichkeitsrevision.",
"Eine administrierende Person hat Zielschema und Rohstatuszuordnung geprüft.",
"Die Verbindungsreferenz wird über eine installationsverantwortete Berechtigungsgrenze aufgelöst.",
],
"steps": [
"Exakte kanonische Übergabenutzdaten und Planprüfsummen erzeugen und prüfen.",
"Nur über einen getrennt am Ziel geprüften Adapter versenden.",
"Die Buchung nach Erfolg, Zeitüberschreitung oder Wiederholung über die stabile Plankorrelation lesen.",
"Die deterministische Abgleichentscheidung erfassen, abwarten oder isolieren.",
],
"limitations": [
"Diese Version wählt weder ERP-Produkt, Schema, Endpunkt noch Transport aus.",
"Pläne können nicht versenden und Beobachtungen werden von diesem Modul nicht gespeichert.",
],
"consequences": [
"Eine Änderung an Verbindlichkeit oder Zuordnung erzeugt einen neuen Plan und eine neue Idempotenzidentität.",
"Eine Zeitüberschreitung bedeutet weder Erfolg noch Fehlschlag.",
"Stornos bleiben verknüpfte Ergebnisse, statt den ursprünglichen Status destruktiv zu ersetzen.",
],
}
},
order=100,
),
),
architecture=declared_module_architecture(
layer="data_reporting_integration",
kind="integration",
maturity="vertical_slice",
documentation_ref="docs/PAYABLE_EXPORT_AND_RECONCILIATION.md",
test_ref="tests/test_payables.py",
known_limits=(
"A named ERP product, schema, transport, credentials, and target test are required before dispatch.",
),
supported_authority_modes=("native_authoritative", "external_authoritative"),
owned_concepts=("ERP payable profile", "payable export plan", "booking observation mapping"),
non_owned_concepts=("invoice", "payable approval", "ledger entry", "payment"),
recovery_docs=("docs/PAYABLE_EXPORT_AND_RECONCILIATION.md",),
security_docs=("docs/PAYABLE_EXPORT_AND_RECONCILIATION.md",),
operations_docs=("docs/PAYABLE_EXPORT_AND_RECONCILIATION.md",),
),
)
def get_manifest() -> ModuleManifest:
return manifest