Skip to content

ADR-0063: Dokumentenverwaltung — Linetra ist die Quelle der Wahrheit, Cloud-Speicher spiegeln einseitig

Kontext

Tiefbau- und Glasfaserprojekte erzeugen laufend Dokumente: Verkehrsrechtliche Anordnungen, Leitungsauskünfte, Verträge, Nachträge, Aufmaße, Fotos. Heute liegen sie verstreut in Postfächern, auf Laufwerken und in WhatsApp-Verläufen; Fristen (gültig bis) und Aktenzeichen findet niemand wieder. Gewünscht ist ein Dokumenteneingang wie bei Lexware: Dokumente kommen an, Text wird gelesen, ein Mensch ordnet Typ, Projekt, Bauabschnitt, Felder und Beteiligte zu und legt ab. Danach sind sie per Ordnerstruktur und Volltext auffindbar.

ADR-0005 legt Rohinput-Dateien auf das Volume und schließt OCR/Parser aus (Lizenzregel). ADR-0062 beschreibt Nextcloud als Projektarchiv für Nachrichten-Anhänge, ohne Index und Suche. ADR-0061 fordert Human in the loop und lokale KI. Linetra ist kein Buchhaltungsprogramm: Rechnungen werden abgelegt, nie gebucht.

Entscheidung

  1. Quelle der Wahrheit. Linetra hält Bytes, Metadaten und Index. Nextcloud, SharePoint und Google Drive bekommen später einen einseitigen Spiegel des berechneten Ordnerbaums (P14c / P14e). Bestehende Cloud-Ordner lassen sich später in den Eingang importieren. Keine Zwei-Wege-Synchronisation.
  2. Blob-Store. Inhaltsadressiert pro Mandant: {FILE_STORAGE_DIR}/tenants/{tenant_id}/blobs/{sha[:2]}/{sha256}. Identische Bytes liegen einmal; ein doppelter Upload liefert das vorhandene Dokument (duplicate_of), sofern der Aufrufer es sehen darf. Upload gestreamt in 1-MiB-Blöcken mit SHA-256, Größenlimit aus document_settings (413), MIME aus den Magic Bytes (415 für HTML, SVG, Skripte, Programme; Unbekanntes wird als application/octet-stream nur zum Download gespeichert). Dateinamen werden bereinigt (NFC, für Windows/SharePoint verbotene Zeichen entfernt).
  3. Datenmodell (Mandanten-DB, Migration 0032_documents_core):
    • document_categories, document_types (31 geseedete Tiefbau-Typen; Felder als JSON, Synonyme, Sensitivität, send_policy, retention_years).
    • documents (Status received → review → filed | rejected; Titel, Datum, Aktenzeichen, Gültigkeit, attrs, search_tsv, legal_hold).
    • document_versions (unveränderlich, SHA-256, blob_key, Hash-Kette je Dokument), document_extractions (nur anhängen), document_events (Audit, nur anhängen).
    • document_links mit typisierten Fremdschlüsseln auf Organisation, Person, Verfahren, Auflage, Abschnitts-Auftrag, Tracker-Issue, Geo-Objekt, Entity-Datensatz, Nachricht und verwandtes Dokument; Rollen issuer, recipient, sender, client, contractor, subject, evidence_for, supersedes, reply_to.
    • document_settings (max_upload_mb, allow_unknown_types, min_confidence, rejected_retention_days).
    • work_sections.code (z. B. BA-03), optional, über die vorhandene Abschnitts-API pflegbar.
  4. Ordner-Policy als Projektion. Der Ablagepfad wird aus Projekt, Bauabschnitt, Typ und Metadaten berechnet (filed_folder, filed_name); Umablegen verschiebt nie lokale Bytes. Wurzel {root}/{Kunde}/{Projektcode} {Projektname}/ mit festen Kategorie-Ordnern 10_Vertrag … 99_Sonstiges und darunter Bauabschnitte/{BA-Code} {BA-Name}/. Dateiname {YYYY-MM-DD}_{TYPCODE}_{Kurztitel≤60}[_AZ-{Aktenzeichen}]_v{NN}.{ext}. Einzelheiten: Dokumente (Technik).
  5. Volltextsuche mit Bordmitteln von PostgreSQL: search_tsv gewichtet (A Titel + Aktenzeichen, B Typ/Synonyme/Attribute/Beteiligte, C extrahierter Text bis 400 000 Zeichen, D Dateiname), Anfrage websearch_to_tsquery('german') || websearch_to_tsquery('simple'), damit Aktenzeichen wie 32-1234/2026 treffen. Keine neue Extension; pg_trgm in Phase 3, pgvector zurückgestellt.
  6. Rechte und Grants.
    • Org-Permissions documents.inbox (Standard: Org-Admin + Mitarbeiter; nötig für nicht zugeordnete Eingangsdokumente) und documents.confidential (Standard: Org-Admin).
    • Vertrauliche Typen sehen sonst nur Projekt-Owner und -Bearbeiter. Kunden (Rolle customer) haben in den Phasen 1–3 keinen Dokumentzugriff.
    • Dokumenttabellen nur für {slug}_edit, von {slug}_read entzogen: Kundenlogins über QGIS/API sehen nichts. UPDATE/DELETE auf Versionen, Extraktionen und Events sind entzogen.
    • Löschen nur über die SECURITY DEFINER-Funktion documents_purge(document_id); sie verweigert bei legal_hold.
  7. Unveränderlichkeit. Eine neue Fassung ist eine neue document_versions-Zeile. Jede Zeile trägt prev_record_hash und record_hash (Kette je Dokument); die Detailansicht zeigt den Prüfstatus.
  8. Typfelder als JSON an document_types, nicht im Schema-CMS. Gründe: Schema-CMS-Typen durchlaufen Checkout/Publish (ADR-0046), werden in Projekt-Schemata instanziiert (ADR-0047), erzeugen QGIS-Views (ADR-0049) und werden mit dem Ausgangsschema neu geseedet (ADR-0054). Dokumenttypen brauchen nichts davon; sie brauchen aber Platz für Extraktionshinweise, Synonyme, Sensitivität und send_policy, den das Schema-CMS nicht kennt.
  9. Verarbeitung asynchron: Celery-Task linetra.documents.process_version auf der Queue documents (Service celery-documents), nach dem Commit des Uploads eingereiht; ein Beat-Sweep holt liegengebliebene Extraktionen nach. Textextraktion für PDFs mit pdf-oxide (ADR-0065).

Begründung

Eine einzige führende Instanz macht Suche, Rechte, Versionen und Audit überprüfbar; ein Zwei-Wege-Abgleich mit Cloud-Speichern würde jede dieser Eigenschaften an Dritte abgeben. Die Ordnerstruktur ist trotzdem sichtbar und wiederverwendbar, weil sie aus den Metadaten deterministisch folgt — der Spiegel muss nur den berechneten Baum schreiben. Inhaltsadressierung spart Platz, macht Duplikate sichtbar und die Hash-Kette billig. Die Mandanten-DB und die Grants nutzen die vorhandene Trennung (ADR-0031, ADR-0055): was {slug}_read nicht sieht, sehen Kunden nicht. Postgres-FTS reicht für Aktenzeichen und deutsche Texte und braucht keinen zusätzlichen Dienst.

Verworfene Alternativen

Alternative Warum verworfen
Zwei-Wege-Synchronisation mit Cloud-Speichern Konflikte, Löschsemantik und Rechte laufen auseinander; Index und Audit wären nicht mehr zuverlässig
Cloud (Nextcloud) als Primärablage Suche, Rechte, Versionen und Hash-Kette hingen an einem externen Dienst; ADR-0062 hält deshalb das Volume als Primärablage
schema_cms-Kinds für Dokumenttypen wiederverwenden Checkout/Publish, Projekt-Instanziierung, QGIS-Views und Ausgangsschema-Reseed passen nicht; Extraktionshinweise und send_policy hätten keinen Ort
Eigenes DMS-Produkt (z. B. Paperless, Nextcloud-Apps) daneben betreiben Zweiter Rechte- und Identitätsbestand; Verknüpfung mit Verfahren, Aufgaben und Geo-Objekten nur über Schnittstellen
pgvector jetzt Kein Anwendungsfall in 1a; Schlagwort- und Aktenzeichen-Suche genügen. Zurückgestellt
Dateibaum als Primärschlüssel (Pfad = Identität) Umbenennen und Umablegen würde Verweise brechen; der Pfad ist deshalb nur Projektion

Folgen

  • Neue Tenant-Migration 0032_documents_core und Control-Migration 0044_document_permissions: ./scripts/alembic_upgrade_all.sh. Neuer Compose-Service celery-documents.
  • ADR-0062 wird teilweise abgelöst: Nextcloud wird zum Spiegel statt Archiv; SharePoint/Drive werden in ADR-0064 (geplant) neu bewertet.
  • Das Mandanten-Backup enthält tenants/{tenant_id}/ (Dokument-Blobs); Organisation löschen entfernt das Verzeichnis. Die ZIP wird noch im Speicher gebaut (offen).
  • entity_files (Bild-/Dokumentfelder des Schema-CMS) hat weiterhin keinen Projektbezug und liegt außerhalb von tenants/; Korrektur und Aufgehen in den Dokumenten in Phase 3.
  • Persönliche QGIS-Logins von Mitarbeitern erben _edit (ADR-0055) und können Dokumenttabellen lesen. Akzeptiert, als offener Punkt im Lastenheft geführt.
  • Dateinamen der Rohinput- und Feldfoto-Uploads werden mit derselben Bereinigung behandelt; der Rohinput-Upload prüft nun die Projektmitgliedschaft.
  • Weitere Phasen: Roadmap P14.