237 lines
12 KiB
Markdown
237 lines
12 KiB
Markdown
# Duplikat → neue Version (Design)
|
||
|
||
Stand: 02.11.2026
|
||
|
||
## Worum es geht
|
||
|
||
Wird ein Beleg ein zweites Mal mit derselben **internen Belegnummer** nach
|
||
Paperless gebracht, soll er nicht als zweites, eigenständiges Dokument landen.
|
||
Stattdessen sieht der Benutzer beide Belege nebeneinander und entscheidet:
|
||
|
||
- **Als neue Version hinzufügen** – die Datei wird über die Versionsfunktion
|
||
von Paperless-NGX an das vorhandene Dokument gehängt.
|
||
- **Verwerfen** – der Beleg wird nicht importiert.
|
||
|
||
Typische Fälle:
|
||
|
||
1. **Korrigierte Rechnung**: Lieferant schickt eine fehlerbereinigte Fassung mit
|
||
derselben Rechnungsnummer.
|
||
2. **Nachträglich geteilt**: Ein Dokument enthielt zwei Belege. Der Benutzer
|
||
teilt das PDF **von Hand** und sendet es neu; der Teil mit der bereits
|
||
vergebenen Belegnummer wird zur neuen Version.
|
||
|
||
## Ziele
|
||
|
||
- Gleicher Vergleichsdialog im **E-Mail-Import** und im **Posteingang (Scanner)**.
|
||
- Oben im Dialog eine Leiste mit **Auffälligkeiten**: abweichende
|
||
**Seitenzahl** und abweichende **Wortzahl**. Darunter beide PDFs nebeneinander.
|
||
- Maßgeblich für „Belegnummer existiert schon“ ist **Paperless** (Custom Field 7
|
||
= interne Belegnummer). Das ist die wichtige Prüfung.
|
||
|
||
## Nicht-Ziele
|
||
|
||
- Umbau der Task-Bearbeitung – läuft unverändert weiter.
|
||
- **Checksummen-Duplikate umgehen** (gleiche Datei erneut importieren) – bleibt
|
||
wie heute, wird in `docs/todos.md` notiert.
|
||
- Inhaltliche Analyse (Beträge, Datum, Positionen) für die Auffälligkeiten –
|
||
dafür gibt es noch keine Auswertung.
|
||
- Metadaten des vorhandenen Dokuments ändern (Titel, Tags, Custom Fields,
|
||
Korrespondent bleiben unverändert; nur die Datei kommt als Version dazu).
|
||
- Nachgelagerte Automatik für die Version (Postprocessing-Regeln, Mail,
|
||
WebDAV-Export, Agrarmonitor-Austausch/Storno) – wird vorerst **unterdrückt**,
|
||
die fachliche Logik kommt später (siehe Todos).
|
||
|
||
## Anti-Ziele (wäre ein Fehlschlag, auch wenn es „funktioniert“)
|
||
|
||
- Ein Beleg mit vorhandener Belegnummer landet doch als zweites Dokument in
|
||
Paperless, ohne dass der Benutzer es so entschieden hat.
|
||
- Eine Version wird an das **falsche** Dokument gehängt.
|
||
- „Verwerfen“ lässt eine reservierte Belegnummer oder einen halb angelegten
|
||
Task zurück.
|
||
|
||
## Paperless-API (am Live-System geprüft, API v10)
|
||
|
||
- `POST /api/documents/{id}/update_version/` – multipart, Feld `document`
|
||
(Datei, Pflicht), `version_label` (optional, max. 64 Zeichen). Antwort: String
|
||
(vermutlich die Consume-Task-ID; Verarbeitung asynchron).
|
||
- `PATCH|DELETE /api/documents/{id}/versions/{version_id}/` – Label ändern/löschen.
|
||
- `DocumentVersionInfo`: `id`, `added`, `version_label`, `checksum`, `is_root`.
|
||
|
||
## Ablauf E-Mail-Import (`MailImportWizard.tsx`)
|
||
|
||
Schritte: 0 Zuordnung → 1 Bearbeitung → 2 Abschluss.
|
||
|
||
1. In Schritt 1 („Bearbeitung“) hat ein Beleg den Modus **Manuell** und eine
|
||
Belegnummer (Jahr + 6-stellige Nummer → `JJJJ-NNNNNN`).
|
||
2. Beim Klick auf **Weiter** (Schritt 1 → 2) prüft das Backend für jeden
|
||
manuellen Beleg in Paperless, ob die Belegnummer schon vergeben ist (CF7 und
|
||
ASN, siehe „Eindeutigkeit des Treffers“).
|
||
3. Gibt es Treffer, öffnet sich **vor** dem Schrittwechsel der Vergleichsdialog
|
||
– nacheinander für jeden betroffenen Beleg.
|
||
- Links: vorhandenes Dokument aus Paperless. Rechts: der neue Beleg (bei
|
||
geteilten Anhängen nur die gewählten Seiten).
|
||
- **Als neue Version hinzufügen** → Beleg wird für den Import als
|
||
„Version zu Dokument X“ markiert.
|
||
- **Verwerfen** → Beleg wird auf `IGNORE` gestellt. Stehen danach alle Belege
|
||
der Mail auf `IGNORE`, greift die bestehende Logik (Mail als ignoriert
|
||
markieren).
|
||
- Dialog schließen (X) ohne Entscheidung → kein Schrittwechsel, der Benutzer
|
||
bleibt in Schritt 1.
|
||
4. Beim **Import ausführen** werden Version-Belege nicht per `post_document`
|
||
hochgeladen, sondern per `update_version` an Dokument X gehängt.
|
||
- Die ASN-Sperre (`validateAsnNotExists`) und der Belegnummer-Duplikatcheck
|
||
entfallen für diese Belege – sonst würde der Import abbrechen.
|
||
- Hochgeladen wird wie beim normalen Import das Original-PDF bzw. die
|
||
gewählten Seiten, ohne eingebrannten Stempel.
|
||
- Kein neuer Enrichment-Task in der `tasks`-Tabelle nötig (Metadaten bleiben).
|
||
- Im `Attachment`-Datensatz wird die Dokument-ID X unter dem jeweiligen
|
||
Bereich eingetragen, damit der Anhang als importiert gilt.
|
||
5. Die Checksummen-Prüfung (`recheckDuplicates`) bleibt unverändert.
|
||
|
||
## Ablauf Posteingang (`InboxDetailPage.tsx`, `inbox-postprocessor`)
|
||
|
||
Heute: `runPaperless` meldet ein Duplikat mit `duplicateOfDocumentId`; der
|
||
Wizard bietet „Neue Version erstellen“ (= Duplikatprüfung überspringen und als
|
||
zweites Dokument hochladen), „Vergleichen“ und „Überspringen“. Im
|
||
Vergleichsdialog ist „Neue Version anlegen“ noch ein TODO.
|
||
|
||
Neu:
|
||
|
||
- Liefert der Duplikat-Treffer eine Paperless-Dokument-ID, öffnet sich der
|
||
Vergleichsdialog **automatisch**.
|
||
- **Als neue Version hinzufügen** → Backend verarbeitet den Abschnitt erneut,
|
||
lädt die fertige PDF (nach allen Bearbeitungen/Stempeln) aber per
|
||
`update_version` an Dokument X hoch statt per `post_document`. Danach ist der
|
||
Abschnitt „erfolgreich“.
|
||
- **Verwerfen** → entspricht dem heutigen „Überspringen“ (Text wird umbenannt).
|
||
- Der bisherige Weg „Duplikatprüfung überspringen und als eigenes Dokument
|
||
hochladen“ entfällt in der Oberfläche.
|
||
- Duplikate **ohne** Dokument-ID (Checksumme, lokaler Task ohne
|
||
`PaperlessDocumentID`) behalten den heutigen Weg: nur Verwerfen/Überspringen.
|
||
- Rechts im Dialog wird der **betroffene Abschnitt** gezeigt, nicht das ganze
|
||
Inbox-Dokument (heute zeigt der Dialog das ganze Dokument).
|
||
|
||
## Nachgelagerte Automatik unterdrücken
|
||
|
||
Entscheidung des Nutzers: Nach dem Hinzufügen einer Version bekommt das
|
||
(Stamm-)Dokument den Tag **„neue Version hochgeladen“ (ID 21)**. Der Nutzer
|
||
konfiguriert die Postprocessing-Regeln so, dass Belege mit Tag 21 ignoriert
|
||
werden. Im Code gibt es **keine** eigene Unterdrückung (keine Markierung, keine
|
||
Änderung an der Webhook-Warteschlange).
|
||
|
||
- Reihenfolge: **erst** Tag 21 setzen (Tags lesen, 21 ergänzen, vorhandene
|
||
behalten), **dann** `update_version`. Schlägt der Versions-Upload fehl, wird
|
||
Tag 21 wieder entfernt – aber nur, wenn er vorher nicht schon gesetzt war.
|
||
- Entfernen von Tag 21 ist Sache des Nutzers.
|
||
- Die Anreicherung in `processDocumentById` (Tag 17, bereit/nicht bereit,
|
||
Titel-Template) läuft beim Webhook weiterhin – nur die Regeln greifen nicht.
|
||
|
||
Im **Posteingang** betrifft die Abfrage nur die Aktion „An Paperless senden“.
|
||
Die übrigen Aktionen des Barcode-Templates (MAIL, EXPORT) davor und danach
|
||
laufen normal: Die Aktionen davor sind beim Erkennen des Duplikats bereits
|
||
gelaufen und werden nicht wiederholt; die Aktionen danach werden nach der
|
||
Entscheidung (Version **oder** Verwerfen) ganz normal ausgeführt.
|
||
|
||
## Vergleichsdialog (gemeinsame Komponente)
|
||
|
||
- Eine Komponente für beide Stellen (aus `CompareModal` herauslösen).
|
||
- Titel z. B. „Belegnummer 2026-000123 existiert bereits“.
|
||
- **Auffälligkeiten-Leiste** oben (Ant Design `Alert`/`Tag`s):
|
||
- Seitenzahl: „Seiten: 2 (vorhanden) → 3 (neu)“ – hervorgehoben bei Abweichung.
|
||
- Wortzahl: „Wörter: 412 → 398“ – hervorgehoben bei Abweichung.
|
||
- Ist eine Zahl nicht ermittelbar (z. B. PDF ohne Textebene), steht dort
|
||
„nicht ermittelbar“ statt einer Abweichung.
|
||
- Keine Abweichung → kurzer neutraler Hinweis „Seiten- und Wortzahl gleich“.
|
||
- Darunter beide PDFs nebeneinander (mobil untereinander), wie heute.
|
||
- Knöpfe: **Verwerfen** / **Als neue Version hinzufügen** (primär).
|
||
|
||
### Kennzahlen ermitteln (Backend-Endpunkt)
|
||
|
||
- Vorhanden (Paperless): Seitenzahl aus `page_count` des Dokuments, Wortzahl aus
|
||
dessen `content` (OCR-Text von Paperless). Verglichen wird mit der aktuellen
|
||
(neuesten) Fassung des Dokuments.
|
||
- Neu: Seitenzahl per `pdf-lib`, Wortzahl aus der Textebene per `pdf-parse`
|
||
(beides bereits Abhängigkeiten im Backend). Keine Textebene → „nicht
|
||
ermittelbar“.
|
||
- Wortzählung: Text an Leerraum trennen, nur Tokens mit mindestens einem
|
||
Buchstaben oder einer Ziffer zählen.
|
||
|
||
## Versionsbezeichnung
|
||
|
||
`version_label` = „Korrektur vom TT.MM.JJJJ“ (Datum des Hinzufügens). Max. 64 Zeichen.
|
||
|
||
## Eindeutigkeit des Treffers
|
||
|
||
Geprüft wird immer gegen Paperless, und zwar über **beide** Merkmale, die aus
|
||
der internen Belegnummer folgen:
|
||
|
||
- CF7 = interne Belegnummer (z. B. `2026-000123`)
|
||
- ASN = interne Belegnummer ohne Bindestrich (z. B. `2026000123`) – auch im
|
||
Posteingang, unabhängig von einem im Template separat hinterlegten ASN-Feld.
|
||
|
||
„Als neue Version hinzufügen“ wird nur angeboten, wenn alle Treffer auf **genau
|
||
ein** Dokument zeigen. Zeigen CF7- und ASN-Treffer auf verschiedene Dokumente
|
||
oder gibt es mehrere CF7-Treffer, zeigt der Dialog einen Hinweis und bietet nur
|
||
**Verwerfen** an. Dafür muss die Suche alle Treffer liefern, nicht nur den ersten.
|
||
|
||
## Am Live-System zu prüfen (vor/bei der Umsetzung)
|
||
|
||
- Antwort von `update_version` (Task-ID?) und wie der Abschluss erkennbar ist.
|
||
- Liefern CF7-Suche, `GET /documents/{id}/` (`content`, `page_count`) und
|
||
`/download/` bei einem versionierten Dokument die Stamm-ID bzw. die neueste
|
||
Fassung? `update_version` muss immer auf die Stamm-ID zielen.
|
||
|
||
## Todos-Einträge
|
||
|
||
In `docs/todos.md` ergänzen:
|
||
|
||
1. „Duplikatprüfung per Checksumme gezielt umgehen können, wenn ein bereits
|
||
importiertes Dokument bewusst erneut importiert (z. B. geteilt) werden soll.“
|
||
2. „Neue Version eines Belegs in Agrarmonitor nachziehen (derzeit unterdrückt):
|
||
- Beleg noch im AM-Posteingang → dort löschen und neue Fassung senden.
|
||
- Beleg verarbeitet, aber nicht gebucht → im jeweiligen Einkauf das
|
||
vorhandene Dokument löschen und die neue Fassung hochladen.
|
||
- Beleg bereits gebucht → Storno-Beleg zum gebuchten Beleg erstellen und die
|
||
neue Fassung über die jeweilige E-Mail-Adresse neu in den AM-Posteingang
|
||
senden.
|
||
Außerdem klären, ob Postprocessing-Regeln (Mail an Buchhaltung, Export) für
|
||
Versionen laufen sollen.“
|
||
3. „Task-Bearbeitung (`PaperlessTaskProcessorService`, 30-s-Cron) auf den
|
||
Paperless-Webhook umstellen. Beim Upload werden die Metadaten schon gesetzt;
|
||
übrig bleiben Owner entfernen, Notizen, Anlage-Verknüpfung (Feld 8) und die
|
||
lokale Buchführung (Task/Attachment/documents) – die brauchen die
|
||
Dokument-ID nach dem Consume.“
|
||
|
||
## Dem Umsetzer überlassen
|
||
|
||
- Schnitt der Backend-Endpunkte (eigener Endpunkt für „Belegnummern prüfen“ und
|
||
„Kennzahlen“, oder zusammengefasst).
|
||
- Wie das Ergebnis von `update_version` verfolgt wird (Task-Polling wie beim
|
||
normalen Upload, sofern die Antwort eine Task-ID ist).
|
||
- Tests: mindestens Unit-Tests für Wortzählung/Auffälligkeiten und für die
|
||
Entscheidung Upload vs. Version im Backend.
|
||
|
||
## Eigene Entscheidungen (nicht ausdrücklich vom Nutzer)
|
||
|
||
- Versionsbezeichnung „Korrektur vom …“.
|
||
- Version nur bei eindeutigem Treffer (CF7 und ASN zeigen auf dasselbe
|
||
Dokument); sonst nur Verwerfen.
|
||
- E-Mail-Import: Version wird wie normale Importe ohne eingebrannten Stempel
|
||
hochgeladen; keine erneute Barcode-Notiz (die hat das Stammdokument schon).
|
||
|
||
- Automatisches Öffnen des Dialogs im Posteingang; Wegfall von „als eigenes
|
||
Dokument hochladen“.
|
||
- Dialog schließen ohne Entscheidung = im Schritt bleiben.
|
||
- Rechts nur der betroffene Abschnitt statt des ganzen Inbox-Dokuments.
|
||
|
||
## Ergebnisse Live-Prüfung (02.11.2026, nur lesend)
|
||
|
||
- `GET /api/documents/` liefert je Dokument `root_document` (bei Stammdokumenten
|
||
`null`) und `versions` (Liste inkl. des Stamms selbst mit `is_root: true`).
|
||
Normalisierung `root_document ?? id` ist damit tragfähig.
|
||
- Im System gibt es derzeit kein Dokument mit mehr als einer Version; ob
|
||
Versionen in Listen-/Suchergebnissen als eigene Einträge auftauchen, ist
|
||
daher offen – die Normalisierung deckt beide Fälle ab.
|
||
- Der schreibende Teil (Test-Upload einer Version, Antwortformat, Webhook)
|
||
wird bei der Abnahme in Task 7 mit Freigabe des Nutzers nachgeholt.
|