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_firstfür die Reihenfolge der PDF-Inhalte beim Export-Type"pdf_with_attachments". BeiTruewerden die Dokumente vor den Vorgangsinformationen in die PDF-Datei geschrieben. Vorgabe istFalse.with_historyfü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.
