# Paperless-ngx per Scripting & Custom Fields erweitern

> Für die Nerds: das papierlose Büro per Scripting überwachen und erweitern - wie ich Paperless-ngx mit einer eigenen Middleware um fehlende Funktionen ergänze.

_Quelle: https://tbsch.de/post/2024-08-28-paperless-guide-customization/ · Stand: 2026-07-14_


Nachdem ich meine grundlegende Arbeitsweise mit *Paperless-ngx* nun detailliert vorgestellt habe, möchte ich noch etwas Content für die richtigen Nerds unter uns abliefern. Auf der Seite über [Klassifizierungen](/post/2024-02-11-paperless-guide-classification/) habe ich ein paar Mal von einer **Middleware** gesprochen. Eigentlich trifft es das Wort gar nicht richtig, da es in *Paperless-ngx* derzeit keine Möglichkeiten gibt, eigene Programmabläufe während der Laufzeit des Systems einzubringen. Und komm mir jetzt nicht mit den neuen Workflows... das Feature hätte Potenzial, wenn... ach... lassen wir das fürs Erste.

## Dunkelverarbeitung

Na, kennst du das Wort? Gerade im Büro einer größeren Firma werden wir häufig mit diesem Begriff konfrontiert. Es bezeichnet die automatisierte Erledigung wiederkehrender Aufgaben, ohne dass ein Mensch darauf Einfluss nehmen muss. In deinem papierlosen Büro wirst du dich sicher auch immer wieder selbst dabei ertappen, die immer und immer selben Tätigkeiten zu erledigen. Haha, habe ich dich ertappt :sunglasses:?

Eigentlich bringt *Paperless-ngx* schon viele tolle Möglichkeiten mit, wie zum Beispiel die Auto-Klassifizierung. Wenn es jedoch an die Details geht, müssen wir immer wieder selbst nacharbeiten.

## Ein Beispiel

Wenn ich meine allmonatlichen Kontoauszüge an *Paperless-ngx* übergebe, sollen sie immer eine Kombination aus Jahr und Monat im Titel tragen: `2024/03`. Und zwar nicht des Monats, in dem das Dokument erstellt oder vom *Document Consumer* konsumiert wurde, sondern für den Monat, für den der Kontoauszug schlichtweg ist. Ganz einfach.

Das System generiert keine passenden Titel für meine Kontoauszüge, und manuelle Anpassungen sind oft umständlich, besonders wenn Dokumente über das Share Sheet vom iPhone hochgeladen werden. Und mal im Ernst: möchtest du bei jedem Dokument wirklich noch darüber nachdenken, den Titel anzupassen? Versuche es ruhig. Das klappt sicher anfangs noch gut :grin:. Auf der Zeitachse führt es zu Inkonsistenzen in der Benennung deiner Dokumente.

## Paperless Secretary

Aus diesem und anderen Gründen erstellte ich für *Paperless-ngx* ein Python-Modul namens **pypaperless**, welches die API anspricht und einem Entwickler die Möglichkeit gibt, Einfluss auf sein DMS zu nehmen.

<div class="tb-gh-card"><div class="tb-gh-og-gate" data-name="consent-gate-github"><a class="tb-gh-og-link" href="https://github.com/tb1337/paperless-api" target="_blank" rel="noopener noreferrer" aria-label="tb1337/paperless-api auf GitHub">
        <img class="tb-gh-og" data-name="consent-gate-github"
          data-src="https://opengraph.githubassets.com/0/tb1337/paperless-api"
          alt="tb1337/paperless-api auf GitHub" loading="lazy" decoding="async" width="1200" height="600" referrerpolicy="no-referrer">
      </a>
    </div>
    <a class="tb-gh-link" href="https://github.com/tb1337/paperless-api" target="_blank" rel="noopener noreferrer">
      <svg aria-hidden="true"
  xmlns="http://www.w3.org/2000/svg"
 
 
  viewBox="0 0 24 24"
  fill="none"
  stroke="currentColor"
  stroke-width="2"
  stroke-linecap="round"
  stroke-linejoin="round"
 
>
  <path stroke="none" d="M0 0h24v24H0z" fill="none" />
  <path d="M9 19c-4.3 1.4 -4.3 -2.5 -6 -3m12 5v-3.5c0 -1 .1 -1.4 -.5 -2c2.8 -.3 5.5 -1.4 5.5 -6a4.6 4.6 0 0 0 -1.3 -3.2a4.2 4.2 0 0 0 -.1 -3.2s-1.1 -.3 -3.5 1.3a12.3 12.3 0 0 0 -6.2 0c-2.4 -1.6 -3.5 -1.3 -3.5 -1.3a4.2 4.2 0 0 0 -.1 3.2a4.6 4.6 0 0 0 -1.3 3.2c0 4.6 2.7 5.7 5.5 6c-.6 .6 -.6 1.2 -.5 2v3.5" />
</svg><span>tb1337/paperless-api auf GitHub besuchen</span>
    </a>
  </div>

Dieses Modul bildet die Grundlage für mein Projekt **Paperless Secretary**, also einen virtuellen Sekretär, der wiederkehrende Aufgaben für mich erledigt. Meine Aufgabe besteht darin, diese wiederkehrenden Aufgaben zu identifizieren und entsprechende Bearbeitungsregeln zu erstellen.

> [!warning]
> **Bitte beachte,** dass ich den Secretary zur Zeit nicht öffentlich zur Verfügung stellen kann. Es handelt sich hierbei um eine experimentelle Software in Python, die insbesondere meinen Bedarf abdeckt und bei dir möglicherweise gar nicht funktioniert, schlimmstenfalls deine Daten in *Paperless-ngx* durcheinanderbringen könnte.

### Regel für Kontoauszüge

Kommen wir auf mein oben genanntes Beispiel zurück, welches ich wie folgt gelöst habe:

```python
"""N26; Kontoauszüge; Titel bearbeiten."""

import re

from pypaperless.models import Document

from .base import Rule
from .const import CORRESPONDENT_N26, DOCUMENT_TYPE_KONTOAUSZUG

# 1)
_rule = Rule("N26; Kontoauszüge; Titel => yyyy/MM (Sparkonto)")


# 2)
@_rule.condition(name="correspondent == N26")
async def check_correspondent(item: Document) -> bool:
    return item.correspondent == CORRESPONDENT_N26


# 3)
@_rule.condition(name="document_type == Kontoauszug")
async def check_document_type(item: Document) -> bool:
    return item.document_type == DOCUMENT_TYPE_KONTOAUSZUG


# 4)
@_rule.condition(name="title !~ yyyy/MM")
async def check_document(item: Document) -> bool:
    content = item.content or ""

    if "Sparkonto" in content:
        pattern = r"^\d{4}/(0[1-9]|1[0-2])\sSparkonto*$"
    else:
        pattern = r"^\d{4}/(0[1-9]|1[0-2])*$"

    return not bool(re.match(pattern, item.title))


# 5)
@_rule.action(name="Apply new document title.")
async def apply_title(*, rule: Rule, item: Document) -> bool:
    content = item.content or ""

    match = re.search(r"\d{2}\.(\d{2})\.(\d{4}) bis \d{2}\.\1\.\2", content)
    if match:
        year = match.group(2)
        month = match.group(1)

        item.title = f"{year}/{month}"
        if "Sparkonto" in content:
            item.title += " Sparkonto"

        # 6)
        if not rule.dry_run:
            return await item.update()

    return False
```

Keine Sorge, sieht schlimmer aus, als es wirklich ist :smile:. Ich habe ein paar Stellen im Code nummeriert und gehe nun einmal kurz darauf ein.

1. Es wird eine neue `Rule` erstellt. Hierbei handelt es sich um eine Klasse, die zum Core des Secretary gehört. Der Secretary verarbeitet solche Regeln dann, indem er sie auf Dokumente in *Paperless-ngx* anwendet.
2. Der Regel wird eine `Condition` angeheftet. Das bedeutet, dass die Regel nur ausgeführt wird, wenn die Bedingung erfüllt wird. Konkret prüft sie also, ob das Dokument von meiner Bank ist.
3. Wieder eine `Condition`. Diesmal wird überprüft, ob das Dokument ein Kontoauszug ist.
4. Die letzte `Condition` überprüft, ob der Kontoauszug einen Titel in dem Format `yyyy/MM` oder `yyyy/MM Sparkonto` hat, so wie ich mir das wünsche. Besser gesagt: ob genau dieser Fall hier nicht zutrifft.
5. Jetzt wird die `Action` implementiert. Das ist die Aktion, die ausgeführt wird, wenn alle Bedingungen zutreffen. Im Dokument-Inhalt ist bei meiner Bank ein Hinweis enthalten, ob der Kontoauszug für mein Girokonto oder Tagesgeldkonto ist. Deswegen überprüft die Aktion das kurz und bildet dann aus dem Inhalt des Dokuments den korrekten Titel.
6. Wenn der Secretary gerade nicht im Testmodus ausgeführt wird, wird der neue Titel dann letztendlich in *Paperless-ngx* festgelegt.

Bei der Klasse `Document` handelt es sich um ein Objekt von **pypaperless**. Dieses Objekt stellt nicht nur sämtliche Daten aus *Paperless-ngx* bereit, sondern auch Funktionen zum Ändern und Löschen von eben solchen Daten.

## Custom Fields

*Custom Fields* sind selbst definierte Zusatzfelder, die du an Dokumente hängst - über Dokumenttyp, Korrespondent und Tags hinaus. Praktisch für alles, was strukturiert sein soll: ein Betrag, ein Ablaufdatum, eine Vertrags- oder Rechnungsnummer.

So legst du eins an: über den Menüpunkt **Benutzerdefinierte Felder** ein neues Feld anlegen, einen Namen vergeben und einen Datentyp wählen (Text, Zahl, Datum, Währung, Boolean, Auswahl, …). Am Dokument taucht das Feld danach in der Seitenleiste auf und lässt sich befüllen.

Ein kleines Beispiel: an jeden *Vertrag* hänge ich ein Datumsfeld **Kündigungsfrist**. So sehe ich auf einen Blick, bis wann ich spätestens kündigen müsste.

> [!tip]
> *Custom Fields* lassen sich nicht nur von Hand, sondern auch per API automatisch befüllen. Genau das erledigt bei mir die oben beschriebene Middleware auf Basis von [pypaperless](/tags/pypaperless/).

## Weitere Use-Cases

Es gibt zahlreiche Anwendungsmöglichkeiten für diese Regeln. In meinem Fall haben sich folgende Use-Cases herauskristallisiert:

1. Titel von Dokumenten anpassen
2. Custom Fields von Dokumenten automatisch befüllen
3. Reminder für Wiedervorlagen/Todos
4. Verschiedene Validierungen

Ich bin gespannt, welche Herausforderungen du mit diesen Regeln angehen würdest. Teile mir gerne deine Ideen mit!

---

Das Titel-/Hintergrundbild stammt von [Chris Ried](https://unsplash.com/de/@cdr6934?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash) auf [Unsplash](https://unsplash.com/de/fotos/ein-computerbildschirm-mit-einem-haufen-code-darauf-ieic5Tq8YMk?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash).

