Zum Hauptinhalt springen

Export-Services

Heute aktualisiert

Einleitung

In einem regelbasierten Workflow können Vorgänge durch Workflow-Aktionen für einen Export markiert werden. Die so markierten Vorgänge können dann von Export-Clients abgeholt und durch in Python programmierte Export-Services verarbeitet werden.

Damit auch mehrere Clients aus dem gleichen Archiv exportieren können, ohne sich gegenseitig zu stören, müssen der Client und die Workflow-Aktion zunächst miteinander verbunden werden. Dafür muss im Bereich “Exporte” zunächst ein neuer Export konfiguriert werden, der dann in der Workflow-Aktion “Vorgang exportieren” zur Auswahl steht. Beim Ausführen der Aktion wird der Vorgang dann für diesen Export markiert.

Die Verbindung von Export und Archiv findet dann erst im Client statt, sodass ein Export auch für mehrere Archive (die z.B. den gleichen Workflow verwenden) wiederverwendet werden kann.


Konfiguration der export.ini

In der Datei export.ini im config-Verzeichnis werden die einzelnen Exports im Client konfiguriert. Der Client greift per Dienstbenutzer über die HTTP-API auf den Server zu und benötigt daher eine konfigurierte Rolle.

Eine minimale export.ini sieht folgendermaßen aus:

[DEFAULT]
URL = http://localhost:8551/
Token = tyNd3LSo8tJBtkzru5N3jiaIijmuALo4

[Example]
Archive = rechnungen
Export = foo
Module = myexport
Directory = C:\Export

In der Sektion [DEFAULT] können die URL und das Token des Dienstbenutzers global für alle folgenden Sektionen voreingestellt werden, diese Parameter können jedoch auch in jeder Sektion einzeln überschrieben werden.

In jeder Sektion gibt es folgende Pflicht-Parameter:

Archive

Der Kurzname des Archivs, aus dem exportiert wird.

Module

Der Name des Python-Modules (ohne .py-Endung) im Verzeichnis

customisation\exports.

Alle zusätzlichen Parameter (hier im Beispiel Directory) können anschließend im Export-Service angefragt werden.

Export

Der Kurzname des Exports, der in der Administrationsoberfläche konfiguriert und in der Workflow-Aktion eingestellt ist. Diese Option muss angegeben werden, wenn man über einen regelbasierten Workflow exportiert.

StatusFrom und StatusTo

Bei der Verwendung eines Ad-hoc-Workflows im Archiv kann auch anhand des Status exportiert werden. In StatusFrom und StatusTo muss jeweils der Name eines Status im Workflow angegeben werden, dabei müssen beide Optionen einen unterschiedlichen Status enthalten. Es werden dann alle Vorgänge mit dem Status aus StatusFrom gesucht und nach der Verarbeitung in den Status aus StatusTo gesetzt.


Aufruf der Exporte

Über pa_client.exe export werden alle in der export.ini konfigurierten Exporte durchgeführt.


Aufbau eines Export-Services

Das folgende Beispiel ist ein minimaler Export-Service. Das Python-Modul muss eine Klasse mit dem Namen ExportService mit einer Methode export() enthalten.

class ExportService:

def export(self, context):
dst = self.root_config["directory"]

# Speichert das erste Dokument ins Zielverzeichnis "dst". Der zweite Parameter kann auch
# ein Dateiname sein, ansonsten wird die Datei unter dem archivierten Dateinamen ins
# Zielverzeichnis geschrieben.
self.write_attachment(context.record['attachments'][0], dst)

# Vorgang wurde erfolgreich exportiert.
return True

Die ExportService-Klasse wird für die Sektion in der export.ini einmal instanziiert und anschließend wird die Methode export() für jeden zu exporierenden Vorgang einmal aufgerufen.

Wenn diese Methode True zurückgibt, gilt der Vorgang als erfolgreich exportiert, andernfalls wird der Export für diesen Vorgang bei einem zukünftigen Aufruf noch einmal durchgeführt.

Der Parameter context enthält in Attributen Informationen zum aktuellen Vorgang:

context.record

Ein dict, das die JSON-serialisierte Darstellung des Vorgangs enthält.

context.archive

Ein dict, das die JSON-serialisierte Darstellung des Archivs enthält.

context.transitions

Eine Liste der möglichen Workflow-Übergänge (als IDs).

Den Export-Service kann man außerdem über weitere Methoden genauer steuern:

def setUp(self):
pass

def tearDown(self):
pass

Mit setUp und tearDown können einmalige Aktion vor und nach dem Export durchgeführt werden.


Datei-Export

Um ein Dokument zu exportieren, muss die Methode self.write_attachment() verwendet werden, die mit folgenden Parametern aufgerufen werden kann:

python

self.write_attachment(attachment, path, on_duplicate="enumerate")

attachment

Das Dokument, das exportiert werden soll, übergeben als dict aus record['attachments'][n], wobei n in Listen-Index ist.

path

Wahlweise ein Verzeichnis oder ein vollständiger Dateipfad. Dieser Pfad muss absolut sein. Wenn als Pfad ein Verzeichnis angegeben wird, wird das Dokument unter dem im Archiv hinterlegten Dateinamen in diesem Verzeichnis abgespeichert.

on_duplicate

Gibt an, was passieren soll, wenn die in path übergebene Datei bereits existiert.

Mögliche Werte sind enumerate (es wird ein Zähler an die Datei angehängt, z.B. datei_01.pdf), overwrite (die Datei wird überschrieben) und skip (die Datei wird übersprungen und nicht exportiert). Vorgabe ist enumerate.

Die Funktion liefert den absoluten Pfad zurück, unter dem die Datei abgespeichert wurde.

Beispiel:

python

# Exportiert das erste Dokument des Vorgangs in das konfigurierte Verzeichnis.
self.write_attachment(record['attachments'][0], self.root_config["directory"])

Datei-Export mit serverseitigen Export-Funktionen

Das Exportieren in Formaten wird durch die Methode self.run_export() ermöglicht, die mit folgenden Parametern aufgerufen werden kann:

python

self.run_export(
context,
export_type,
path,
export_options={"attachments_first": True, "with_history": False},
on_duplicate="enumerate"
)

context

Die context-Variable der Haupt-Methode, die durchgereicht werden muss.

export_type

Hier muss einer der folgenden Werte übergeben werden, um das gewünschte Export-Format auszuwählen:

  • "attachments" für „Dokumente zusammenfassen“

  • "csv" für CSV

  • "csv_with_attachments" für CSV mit Dokumenten

  • "accounting_records" für Buchungssätze

  • "pdf" für PDF

  • "pdf_with_attachments" für PDF mit Dokumenten

  • "xlsx" für Excel

path

Wahlweise ein Verzeichnis oder ein vollständiger Dateipfad. Dieser Pfad muss absolut sein. Wenn als Pfad ein Verzeichnis angegeben wird, wird das Dokument unter einem vom Export erzeugten Dateinamen in diesem Verzeichnis abgespeichert.

export_options

Ein dict, mit dem sich Optionen beim Export steuern lassen. Aktuell werden folgende Optionen unterstützt:

  • attachments_first für die Reihenfolge der PDF-Inhalte beim Export-Type "pdf_with_attachments". Bei True werden die Dokumente vor den Vorgangsinformationen in die PDF-Datei geschrieben. Vorgabe ist False.

  • with_history für das Exportieren der Historie. Standardmäßig wird sie exportiert.

on_duplicate

Gibt an, was passieren soll, wenn die in path übergebene Datei bereits existiert. Mögliche Werte sind enumerate (es wird ein Zähler an die Datei angehängt, z.B. datei_01.pdf), overwrite (die Datei wird überschrieben) und skip (die Datei wird übersprungen und nicht exportiert). Vorgabe ist enumerate.

Die Funktion liefert den absoluten Pfad zurück, unter dem die Datei abgespeichert wurde oder None, wenn der Export nicht erfolgreich war und eine Warnung geloggt wurde.

Im folgenden Beispiel werden alle „Export“-markierten Dateien im XLSX-Format aus einem vorkonfigurierten Archiv exportiert:

python

class ExportService:
def export(self, context):
# Exportiert im XLSX-Format, nummeriert Duplikate (_01, _02, ...)
filepath = self.run_export(context, "xlsx", "test.xlsx")

if filepath is not None:
_logger.info(f"Export XLSX: {filepath}")

return 1


Vorgang aktualisieren

Die Methode update_record aktualisiert den Vorgang des Kontextes basierend auf den übergebenen Daten und ändert dabei auch automatisch den im context hinterlegten record, sodass stets die aktuellste Vorgangs-ID zur Verfügung steht.

python

def update_record(self, context, *, data)

context

Die context-Variable der Haupt-Methode, die durchgereicht werden muss.

data

Ein Dictionary mit den zu aktualisierenden Feldern und ihren neuen Werten.

Liefert den aktualisierten Datensatz, bzw. Vorgang als Dictionary zurück.

Beispiel:

python

record = self.update_record(context, data={"_field": 100})

# Alternativ: Ohne Zuweisung wird context.record ebenfalls aktualisiert.
self.update_record(context, data={"_field": 100})


Workflow-Übergänge ausführen

Es ist möglich, beim Exportieren eines Vorgangs automatisch den nächsten Status-Übergang durchzuführen. Der Übergang darf dabei keine Benutzer-Rückfragen machen.

Der Export-Service bietet dafür eine Methode mit folgender Signatur an.

def execute_transition(self, context, *, id=None, name=None)

Der context-Parameter aus der process-Methode muss durchgereicht werden, die üblichen Parameter sind optional, solang es exakt einen Übergang gibt.

Bei mehreren möglichen Übergängen ist es sinnvoll, den nächsten Übergang anhand des Names im Parameter name anzugeben.

Beispiel:

self.execute_transition(context, name="Gebucht")


Datenbankzugang

Es ist möglich, innerhalb der process-Methode über ODBC auf externe Datenbanken zuzugreifen. Eine ODBC-Datenquelle kann über die Sektion Database in der zum Modul gehörenden INI-Datei einfach konfiguriert werden:

ini

[Database]
DSN = MY_DSN
UID = user
PWD = password

Anschließend kann über self.database mithilfe des with-Statements eine Verbindung aufgebaut werden:

python

with self.database() as conn:
sql = 'SELECT data FROM mydata WHERE ID = {}'.format(documentId)
cursor = conn.cursor()
cursor.execute(sql)
value = cursor.fetchone()[0]
# ...

Sollten Daten verändert werden, muss die Transaktion mit conn.commit() abgeschlossen werden. Beim Verlassen des with-Blocks wird die Datenbankverbindung automatisch geschlossen.

Intern wird für die Datenbankverbindungen PyODBC benutzt.

Sollte innerhalb eines Archivierungsvorgangs Zugriff auf mehr als eine externe Datenbank benötigt werden, muss PyODBC direkt verwendet werden.

Hat dies deine Frage beantwortet?