ADR-0063: Dokumentenverwaltung — Linetra ist die Quelle der Wahrheit, Cloud-Speicher spiegeln einseitig
- Status: accepted
- Datum: 2026-10-08
- Phase: Dokumente (P14a)
- Lastenheft: DMS-01…DMS-07 (DMS-08…DMS-15 folgen in P14b–P14e)
- Bezug: ADR-0005, ADR-0055, ADR-0060, ADR-0061, ADR-0062
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
- 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.
- 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 ausdocument_settings(413), MIME aus den Magic Bytes (415für HTML, SVG, Skripte, Programme; Unbekanntes wird alsapplication/octet-streamnur zum Download gespeichert). Dateinamen werden bereinigt (NFC, für Windows/SharePoint verbotene Zeichen entfernt). - 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(Statusreceived→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_linksmit typisierten Fremdschlüsseln auf Organisation, Person, Verfahren, Auflage, Abschnitts-Auftrag, Tracker-Issue, Geo-Objekt, Entity-Datensatz, Nachricht und verwandtes Dokument; Rollenissuer,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.
- 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-Ordnern10_Vertrag…99_Sonstigesund darunterBauabschnitte/{BA-Code} {BA-Name}/. Dateiname{YYYY-MM-DD}_{TYPCODE}_{Kurztitel≤60}[_AZ-{Aktenzeichen}]_v{NN}.{ext}. Einzelheiten: Dokumente (Technik). - Volltextsuche mit Bordmitteln von PostgreSQL:
search_tsvgewichtet (A Titel + Aktenzeichen, B Typ/Synonyme/Attribute/Beteiligte, C extrahierter Text bis 400 000 Zeichen, D Dateiname), Anfragewebsearch_to_tsquery('german') || websearch_to_tsquery('simple'), damit Aktenzeichen wie32-1234/2026treffen. Keine neue Extension;pg_trgmin Phase 3,pgvectorzurückgestellt. - Rechte und Grants.
- Org-Permissions
documents.inbox(Standard: Org-Admin + Mitarbeiter; nötig für nicht zugeordnete Eingangsdokumente) unddocuments.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}_readentzogen: Kundenlogins über QGIS/API sehen nichts.UPDATE/DELETEauf Versionen, Extraktionen und Events sind entzogen. - Löschen nur über die
SECURITY DEFINER-Funktiondocuments_purge(document_id); sie verweigert beilegal_hold.
- Org-Permissions
- Unveränderlichkeit. Eine neue Fassung ist eine neue
document_versions-Zeile. Jede Zeile trägtprev_record_hashundrecord_hash(Kette je Dokument); die Detailansicht zeigt den Prüfstatus. - 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 undsend_policy, den das Schema-CMS nicht kennt. - Verarbeitung asynchron: Celery-Task
linetra.documents.process_versionauf der Queuedocuments(Servicecelery-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_coreund Control-Migration0044_document_permissions:./scripts/alembic_upgrade_all.sh. Neuer Compose-Servicecelery-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 vontenants/; 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.