docs(cases): complete German reference coverage

This commit is contained in:
2026-08-23 20:19:14 +02:00
parent d395b22301
commit f07e079616
6 changed files with 226 additions and 29 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@govoplan/cases-webui", "name": "@govoplan/cases-webui",
"version": "0.1.20", "version": "0.1.21",
"private": true, "private": true,
"type": "module", "type": "module",
"main": "webui/src/index.ts", "main": "webui/src/index.ts",
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project] [project]
name = "govoplan-cases" name = "govoplan-cases"
version = "0.1.20" version = "0.1.21"
description = "GovOPlaN administrative case context module." description = "GovOPlaN administrative case context module."
readme = "README.md" readme = "README.md"
requires-python = ">=3.12" requires-python = ">=3.12"
+210 -22
View File
@@ -43,7 +43,10 @@ from govoplan_cases.backend.party_context import (
) )
from govoplan_cases.backend.acl import CaseAclProvider from govoplan_cases.backend.acl import CaseAclProvider
from govoplan_cases.backend.db import models as case_models from govoplan_cases.backend.db import models as case_models
from govoplan_cases.backend.dsar_provider import CASES_DSAR_CAPABILITY, CasesDsarProvider from govoplan_cases.backend.dsar_provider import (
CASES_DSAR_CAPABILITY,
CasesDsarProvider,
)
from govoplan_cases.backend.service_intake import ( from govoplan_cases.backend.service_intake import (
CAPABILITY_CASES_SERVICE_INTAKE, CAPABILITY_CASES_SERVICE_INTAKE,
CaseServiceIntake, CaseServiceIntake,
@@ -66,7 +69,7 @@ from govoplan_core.db.base import Base
MODULE_ID = "cases" MODULE_ID = "cases"
MODULE_VERSION = "0.1.20" MODULE_VERSION = "0.1.21"
READ_SCOPE = "cases:case:read" READ_SCOPE = "cases:case:read"
CREATE_SCOPE = "cases:case:create" CREATE_SCOPE = "cases:case:create"
UPDATE_SCOPE = "cases:case:update" UPDATE_SCOPE = "cases:case:update"
@@ -169,20 +172,47 @@ manifest = ModuleManifest(
CAPABILITY_DECISION_REGISTRY, CAPABILITY_DECISION_REGISTRY,
), ),
permissions=( permissions=(
_permission(READ_SCOPE, "View cases", "Read tenant cases and their governed history."), _permission(
_permission(CREATE_SCOPE, "Create cases", "Open a case from a configured case type or service intake."), READ_SCOPE, "View cases", "Read tenant cases and their governed history."
_permission(UPDATE_SCOPE, "Update cases", "Create a guarded immutable case revision."), ),
_permission(ASSIGN_SCOPE, "Assign cases", "Change stable function, assignment, or work-item references on a case."), _permission(
_permission(CLOSE_SCOPE, "Close cases", "Move a case to a configured terminal status."), CREATE_SCOPE,
_permission(SHARE_SCOPE, "Share cases", "Restrict a case and grant object-level access to selected principals."), "Create cases",
_permission(ADMIN_SCOPE, "Administer cases", "Configure tenant case types and statuses."), "Open a case from a configured case type or service intake.",
),
_permission(
UPDATE_SCOPE, "Update cases", "Create a guarded immutable case revision."
),
_permission(
ASSIGN_SCOPE,
"Assign cases",
"Change stable function, assignment, or work-item references on a case.",
),
_permission(
CLOSE_SCOPE, "Close cases", "Move a case to a configured terminal status."
),
_permission(
SHARE_SCOPE,
"Share cases",
"Restrict a case and grant object-level access to selected principals.",
),
_permission(
ADMIN_SCOPE, "Administer cases", "Configure tenant case types and statuses."
),
), ),
role_templates=( role_templates=(
RoleTemplate( RoleTemplate(
slug="case_manager", slug="case_manager",
name="Case manager", name="Case manager",
description="Create, assign, update, and close tenant cases.", description="Create, assign, update, and close tenant cases.",
permissions=(READ_SCOPE, CREATE_SCOPE, UPDATE_SCOPE, ASSIGN_SCOPE, CLOSE_SCOPE, SHARE_SCOPE), permissions=(
READ_SCOPE,
CREATE_SCOPE,
UPDATE_SCOPE,
ASSIGN_SCOPE,
CLOSE_SCOPE,
SHARE_SCOPE,
),
), ),
RoleTemplate( RoleTemplate(
slug="case_reader", slug="case_reader",
@@ -194,7 +224,15 @@ manifest = ModuleManifest(
slug="case_administrator", slug="case_administrator",
name="Case administrator", name="Case administrator",
description="Configure case types and statuses and manage cases.", description="Configure case types and statuses and manage cases.",
permissions=(READ_SCOPE, CREATE_SCOPE, UPDATE_SCOPE, ASSIGN_SCOPE, CLOSE_SCOPE, SHARE_SCOPE, ADMIN_SCOPE), permissions=(
READ_SCOPE,
CREATE_SCOPE,
UPDATE_SCOPE,
ASSIGN_SCOPE,
CLOSE_SCOPE,
SHARE_SCOPE,
ADMIN_SCOPE,
),
), ),
), ),
route_factory=_router, route_factory=_router,
@@ -240,7 +278,11 @@ manifest = ModuleManifest(
label="i18n:govoplan-core.product_area.services_cases", label="i18n:govoplan-core.product_area.services_cases",
icon="landmark", icon="landmark",
description="i18n:govoplan-core.product_area.services_cases_description", description="i18n:govoplan-core.product_area.services_cases_description",
surface_ids=("cases.nav.cases", "cases.route.cases", "cases.route.cases.caseid"), surface_ids=(
"cases.nav.cases",
"cases.route.cases",
"cases.route.cases.caseid",
),
order=20, order=20,
), ),
), ),
@@ -339,14 +381,41 @@ manifest = ModuleManifest(
ModuleInterfaceProvider(name="cases.service_launcher", version="0.1.0"), ModuleInterfaceProvider(name="cases.service_launcher", version="0.1.0"),
ModuleInterfaceProvider(name=CAPABILITY_RECORD_SOURCE_CASES, version="1.0.0"), ModuleInterfaceProvider(name=CAPABILITY_RECORD_SOURCE_CASES, version="1.0.0"),
ModuleInterfaceProvider(name=CASES_DSAR_CAPABILITY, version="0.1.0"), ModuleInterfaceProvider(name=CASES_DSAR_CAPABILITY, version="0.1.0"),
ModuleInterfaceProvider(name=CAPABILITY_TICKET_CASE_ESCALATION, version="1.0.0"), ModuleInterfaceProvider(
name=CAPABILITY_TICKET_CASE_ESCALATION, version="1.0.0"
),
), ),
requires_interfaces=( requires_interfaces=(
ModuleInterfaceRequirement(name="services.definition", version_min="0.1.0", version_max_exclusive="0.2.0", optional=True), ModuleInterfaceRequirement(
ModuleInterfaceRequirement(name="parties.procedure", version_min="0.1.0", version_max_exclusive="0.2.0", optional=True), name="services.definition",
ModuleInterfaceRequirement(name="parties.representation", version_min="0.1.0", version_max_exclusive="0.2.0", optional=True), version_min="0.1.0",
ModuleInterfaceRequirement(name="mandates.resolution", version_min="0.1.0", version_max_exclusive="0.2.0", optional=True), version_max_exclusive="0.2.0",
ModuleInterfaceRequirement(name="decisions.formal_outcome", version_min="0.1.0", version_max_exclusive="0.2.0", optional=True), optional=True,
),
ModuleInterfaceRequirement(
name="parties.procedure",
version_min="0.1.0",
version_max_exclusive="0.2.0",
optional=True,
),
ModuleInterfaceRequirement(
name="parties.representation",
version_min="0.1.0",
version_max_exclusive="0.2.0",
optional=True,
),
ModuleInterfaceRequirement(
name="mandates.resolution",
version_min="0.1.0",
version_max_exclusive="0.2.0",
optional=True,
),
ModuleInterfaceRequirement(
name="decisions.formal_outcome",
version_min="0.1.0",
version_max_exclusive="0.2.0",
optional=True,
),
), ),
capability_factories={ capability_factories={
CAPABILITY_CASES_SERVICE_INTAKE: _service_intake, CAPABILITY_CASES_SERVICE_INTAKE: _service_intake,
@@ -461,7 +530,10 @@ manifest = ModuleManifest(
}, },
metadata={ metadata={
"kind": "workflow", "kind": "workflow",
"help_contexts": ["tickets.action.escalate", "cases.state.ticket-escalation"], "help_contexts": [
"tickets.action.escalate",
"cases.state.ticket-escalation",
],
"consequence_classes": { "consequence_classes": {
"create_case": "Creates one Cases-owned formal procedure and returns its stable reference.", "create_case": "Creates one Cases-owned formal procedure and returns its stable reference.",
"preserve_ticket": "Does not convert, copy, or mutate the Ticket history.", "preserve_ticket": "Does not convert, copy, or mutate the Ticket history.",
@@ -511,6 +583,27 @@ manifest = ModuleManifest(
kind="repository", kind="repository",
), ),
), ),
translations={
"de": {
"title": "Exakte Files-, Mail- und Campaign-Verweise mit einem Vorgang verknüpfen",
"summary": (
"Einen vom zuständigen Modul autorisierten Verweis hinzufügen oder entfernen, ohne dessen Inhalt nach Cases zu kopieren."
),
"body": (
"Für einen aktiven Vorgang können Files, Mail und Campaigns über den versionierten Schnellzugriff-Ergebnisvertrag eine "
"exakte mandantengebundene Auswahl zurückgeben. Cases akzeptiert nur unterstützte Kombinationen aus zuständigem Modul "
"und Verweisart, den passenden Mandanten, eine festgelegte Version und die kanonische lokale Route des zuständigen Moduls. "
"Verknüpfen und Entfernen prüfen die Änderungsberechtigung für den Vorgang erneut, verwenden erwartete Revision und "
"Idempotenz und ergänzen eine unveränderliche Vorgangshistorie. Cases speichert nur typisierte Modulkennung, exakte "
"Version und Modulroute; Dateibytes, Nachrichtenkopf oder -inhalt, Empfangende, Campaign-Inhalt oder eine frühere "
"Zugriffsentscheidung werden nie kopiert. Das Öffnen wechselt stets in das zuständige Modul, das den aktuellen Zugriff "
"erneut prüft. Wird dieses Modul deaktiviert oder der Quellzugriff später entzogen, behält der Vorgang den historischen "
"Verweis, legt aber keinen Quellinhalt offen. Das Entfernen nimmt nur den Verweis aus der nächsten Vorgangsrevision und "
"löscht niemals das Quellobjekt. Ungespeicherte Vorgangsänderungen müssen vor dem Verknüpfen eines Schnellzugriff-Ergebnisses "
"gespeichert oder verworfen werden."
),
}
},
metadata={ metadata={
"kind": "workflow", "kind": "workflow",
"help_contexts": ["cases.detail.references"], "help_contexts": ["cases.detail.references"],
@@ -540,6 +633,27 @@ manifest = ModuleManifest(
kind="repository", kind="repository",
), ),
), ),
translations={
"de": {
"title": "Betroffenenanfragen für Vorgänge",
"summary": (
"Gesteuerte Vorgangstatsachen ausgeben, ohne die antragstellende Identität zu erraten oder undurchsichtige "
"Vorgangsdaten offenzulegen."
),
"body": (
"Der DSAR-Provider von Cases gleicht im exakten Mandanten Konten-, Identitäts- und Mitgliedschaftszuordnungen, "
"Zugriffsfreigaben für Konten oder Identitäten und ausdrückliche Cases-Verweise ab. Wird ein kanonischer Selektor mit "
"einem direkten Verweis kombiniert, muss eine Cases-eigene Beziehung beide bestätigen; andernfalls schließt die Suche "
"sicher. Direkte Vorgangs- und Revisionsverweise exportieren eine typisierte, minimierte Lebenszyklusprojektion. Rohe "
"Snapshots, Metadaten, Suchtext, Freitext-Änderungsgründe, Timeline-Nutzdaten und -Zusammenfassungen, Nachweiskennungen, "
"Anfrage-Digests, Idempotenzschlüssel, Auditkennungen und unbeteiligte Zugriffsbetroffene sind ausgeschlossen. "
"Unveränderliche Vorgangskennungen, Revisionen, Timelines und Zuordnungen werden aufbewahrt. Aktuelle offene Vorgänge und "
"aktive Zugriffsmerkmale erfordern eine berechtigte manuelle Prüfung im normalen Vorgangs-/Zugriffslebenszyklus; eine "
"automatische Löschung wird nicht angeboten. Die Verbindung zwischen antragstellender Person und Identität gehört Parties; "
"Cases leitet sie daher nicht aus Beteiligtenkennungen ab."
),
}
},
), ),
DocumentationTopic( DocumentationTopic(
id="cases.workflow.quick-access-context", id="cases.workflow.quick-access-context",
@@ -603,9 +717,31 @@ manifest = ModuleManifest(
), ),
links=( links=(
DocumentationLink(label="Cases", href="/cases", kind="runtime"), DocumentationLink(label="Cases", href="/cases", kind="runtime"),
DocumentationLink(label="Cases concept", href="govoplan-cases/docs/CONCEPT.md", kind="repository"), DocumentationLink(
DocumentationLink(label="Decisions domain", href="govoplan-decisions/docs/DECISIONS_DOMAIN.md", kind="repository"), label="Cases concept",
href="govoplan-cases/docs/CONCEPT.md",
kind="repository",
), ),
DocumentationLink(
label="Decisions domain",
href="govoplan-decisions/docs/DECISIONS_DOMAIN.md",
kind="repository",
),
),
translations={
"de": {
"title": "Förmliche Entscheidung aus einem Vorgang festhalten",
"summary": "Handlungsbefugnis erneut prüfen und eine exakte Decision-Revision in der Vorgangshistorie bewahren.",
"body": (
"Die Aktion in der Vorgangsdetailansicht akzeptiert nur Tenor, Begründung, Bedingungen, Wirksamkeitszeitpunkt und "
"Änderungsgrund. Cases löst die exakte Vorgangsrevision auf, prüft den objektbezogenen Änderungszugriff, ermittelt über "
"Access die aktuelle Handlungszuweisung und verlangt genau ein wirksames Mandat für cases.formal_decision und den gewählten "
"Decision-Typ. Anschließend wird das Ergebnis über das optionale Decisions-Register aufgezeichnet und die exakte "
"Decision-Revision in derselben Datenbanktransaktion mit einer neuen Vorgangsrevision verknüpft. Eine Wiederholung "
"verwendet eine deterministische Decision-Kennung; abweichende Nutzdaten schließen sicher."
),
}
},
metadata={ metadata={
"kind": "workflow", "kind": "workflow",
"help_contexts": ["cases.action.decide", "cases.decision.editor"], "help_contexts": ["cases.action.decide", "cases.decision.editor"],
@@ -646,8 +782,24 @@ manifest = ModuleManifest(
links=( links=(
DocumentationLink(label="Cases", href="/cases", kind="runtime"), DocumentationLink(label="Cases", href="/cases", kind="runtime"),
DocumentationLink(label="Records", href="/records", kind="runtime"), DocumentationLink(label="Records", href="/records", kind="runtime"),
DocumentationLink(label="Cases concept", href="govoplan-cases/docs/CONCEPT.md", kind="repository"), DocumentationLink(
label="Cases concept",
href="govoplan-cases/docs/CONCEPT.md",
kind="repository",
), ),
),
translations={
"de": {
"title": "Exakte Vorgangsrevision in eine eAkte ablegen",
"summary": "Einen rekonstruierbaren Vorgangs-Snapshot in Records bewahren, ohne die Zuständigkeit von Cases zu verlagern.",
"body": (
"Ist Records aktiviert, löst Cases eine exakte unveränderliche Vorgangsrevision erst auf, nachdem aktuelle "
"Vorgangsberechtigung und objektbezogener Zugriff geprüft wurden. Der abgelegte Verweis enthält Vorgangsnummer, "
"Lebenszyklusstatus, dargestelltes Gültigkeitsintervall, Aufzeichnungszeitpunkt und einen SHA-256-Digest des kanonischen "
"Snapshots. Cases bleibt für den Vorgangslebenszyklus maßgeblich."
),
}
},
metadata={ metadata={
"kind": "workflow", "kind": "workflow",
"help_contexts": ["cases.detail", "records.action.file"], "help_contexts": ["cases.detail", "records.action.file"],
@@ -689,6 +841,23 @@ manifest = ModuleManifest(
kind="repository", kind="repository",
), ),
), ),
translations={
"de": {
"title": "Institutioneller Kontext eines Vorgangs",
"summary": (
"Cases bewahrt Service- und Beteiligtenverweise, ohne institutionelle Definitionen oder Betroffenenstammdaten zu übernehmen."
),
"body": (
"Vorgangstypen und -status sind Mandantenkonfiguration. Jede Anlage oder Änderung schreibt eine unveränderliche, durch "
"optimistische Nebenläufigkeitskontrolle geschützte Revision und ein wiederholungssicheres Timeline-Ereignis. Die "
"Service-Aufnahme hält exakte Bindungen an Service, Mandat, Zuständigkeit, Rechtsgrundlage, Formular, Workflow und Ergebnis "
"fest. Verfahrensbeteiligte stammen von einem optionalen Provider oder einer begrenzten Cases-Kompatibilitätsprojektion. "
"Zuweisungs-, Nachweis-, Decision- und Aktenverweise bleiben stabile Referenzen im Eigentum ihrer Quellmodule. Ist Search "
"aktiviert, liefert Cases eine neu aufbaubare reine Metadatenprojektion. Nachweise und geschützte Decision-Inhalte sind "
"ausgeschlossen; jeder Treffer wird vor der Offenlegung erneut gegen den aktuellen Vorgangszugriff geprüft."
),
}
},
metadata={ metadata={
"help_contexts": [ "help_contexts": [
"cases.list", "cases.list",
@@ -725,7 +894,26 @@ manifest = ModuleManifest(
kind="repository", kind="repository",
), ),
), ),
translations={
"de": {
"title": "Referenz für Vorgangslebenszyklus, Zugriff und Nachweise",
"summary": (
"Revisions-, Status-, Zugriffs- und Verweisfelder gemeinsam mit ihren dauerhaften Folgen erläutern."
),
"body": (
"Änderungen an Titel und Status ergänzen eine unveränderliche Vorgangsrevision, geschützt durch erwartete Revision und "
"stabilen Idempotenzschlüssel. Jede akzeptierte Änderung ergänzt außerdem einen Timeline-Eintrag mit handelnder Person, "
"Zeitpunkt und Änderungsgrund; ein abschließender Status verlangt zusätzlich die Berechtigung zum Schließen. Die Sichtbarkeit "
"eines Vorgangs ist mandantenweit oder eingeschränkt. Eingeschränkte Vorgänge bleiben nur durch administrative Befugnis, "
"Zuweisungs- oder Einheitenkontext, Erstellerbefugnis oder eine ausdrückliche Konto-/Gruppenfreigabe sichtbar. "
"Zugriffsänderungen ergänzen eine weitere unveränderliche Revision und verlangen Bestätigung. Service-, Beteiligten-, "
"Zuweisungs-, Decision- und Aktenverweise bezeichnen Objekte anderer Provider; Cases bewahrt ihre stabilen Kennungen und "
"Versionen, ohne sie zu kopieren oder stillschweigend zu verändern."
),
}
},
metadata={ metadata={
"kind": "reference",
"help_contexts": [ "help_contexts": [
"cases.field.title", "cases.field.title",
"cases.field.status", "cases.field.status",
+12 -3
View File
@@ -10,6 +10,14 @@ REPO_ROOT = Path(__file__).resolve().parents[1]
class CasesInterfaceDocumentationContractTests(unittest.TestCase): class CasesInterfaceDocumentationContractTests(unittest.TestCase):
def test_all_static_topics_have_complete_german_content(self) -> None:
for topic in get_manifest().documentation:
german = (topic.translations or {}).get("de", {})
self.assertEqual({"title", "summary", "body"}, set(german), topic.id)
self.assertTrue(
all(str(value).strip() for value in german.values()), topic.id
)
def test_backend_surfaces_and_hierarchy_remain_declared(self) -> None: def test_backend_surfaces_and_hierarchy_remain_declared(self) -> None:
frontend = get_manifest().frontend frontend = get_manifest().frontend
self.assertIsNotNone(frontend) self.assertIsNotNone(frontend)
@@ -53,15 +61,16 @@ class CasesInterfaceDocumentationContractTests(unittest.TestCase):
self.assertIn("update_case", reference.metadata["consequence_classes"]) self.assertIn("update_case", reference.metadata["consequence_classes"])
self.assertIn("close_case", reference.metadata["consequence_classes"]) self.assertIn("close_case", reference.metadata["consequence_classes"])
self.assertIn("change_access", reference.metadata["consequence_classes"]) self.assertIn("change_access", reference.metadata["consequence_classes"])
self.assertEqual("reference", reference.metadata["kind"])
self.assertIn( self.assertIn(
"link_owner_reference", "link_owner_reference",
owner_links.metadata["consequence_classes"], owner_links.metadata["consequence_classes"],
) )
def test_webui_uses_shared_help_guard_and_confirmation_components(self) -> None: def test_webui_uses_shared_help_guard_and_confirmation_components(self) -> None:
list_page = ( list_page = (REPO_ROOT / "webui/src/features/cases/CasesPage.tsx").read_text(
REPO_ROOT / "webui/src/features/cases/CasesPage.tsx" encoding="utf-8"
).read_text(encoding="utf-8") )
detail_page = ( detail_page = (
REPO_ROOT / "webui/src/features/cases/CaseDetailPage.tsx" REPO_ROOT / "webui/src/features/cases/CaseDetailPage.tsx"
).read_text(encoding="utf-8") ).read_text(encoding="utf-8")
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@govoplan/cases-webui", "name": "@govoplan/cases-webui",
"version": "0.1.20", "version": "0.1.21",
"private": true, "private": true,
"type": "module", "type": "module",
"main": "src/index.ts", "main": "src/index.ts",
+1 -1
View File
@@ -10,7 +10,7 @@ const CaseDetailPage = lazy(() => import("./features/cases/CaseDetailPage"));
export const casesModule: PlatformWebModule = { export const casesModule: PlatformWebModule = {
id: "cases", id: "cases",
label: "i18n:govoplan-cases.cases", label: "i18n:govoplan-cases.cases",
version: "0.1.20", version: "0.1.21",
optionalDependencies: [ optionalDependencies: [
"access", "access",
"addresses", "addresses",