Export-Services
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:
ArchiveDer Kurzname des Archivs, aus dem exportiert wird.
ModuleDer Name des Python-Modules (ohne
.py-Endung) im Verzeichniscustomisation\exports.
Alle zusätzlichen Parameter (hier im Beispiel Directory) können anschließend im Export-Service
angefragt werden.
ExportDer 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.
StatusFromundStatusToBei der Verwendung eines Ad-hoc-Workflows im Archiv kann auch anhand des Status exportiert werden. In
StatusFromundStatusTomuss 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 ausStatusFromgesucht und nach der Verarbeitung in den Status ausStatusTogesetzt.
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.recordEin
dict, das die JSON-serialisierte Darstellung des Vorgangs enthält.context.archiveEin
dict, das die JSON-serialisierte Darstellung des Archivs enthält.context.transitionsEine 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:
self.write_attachment(attachment, path, on_duplicate="enumerate")
attachmentDas Dokument, das exportiert werden soll, übergeben als
dictausrecord['attachments'][n], wobeinin Listen-Index ist.pathWahlweise 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_duplicateGibt an, was passieren soll, wenn die in
pathübergebene Datei bereits existiert. Mögliche Werte sindenumerate(es wird ein Zähler an die Datei angehängt, z.B.datei_01.pdf),overwrite(die Datei wird überschrieben) undskip(die Datei wird übersprungen und nicht exportiert). Vorgabe istenumerate.
Die Funktion liefert den absoluten Pfad zurück, unter dem die Datei abgespeichert wurde.
Beispiel:
# Exportiert das erste Dokument des Vorgangs in das Verzeichnis, das in der Sektion
# der export.ini unter `Directory` konfiguriert ist.
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:
self.run_export(
context,
export_type,
path,
export_options={"attachments_first": True, "with_history": False},
on_duplicate="enumerate"
)
contextDie
context-Variable der Haupt-Methode, die durchgereicht werden muss.export_typeHier 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
pathWahlweise 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_optionsEin
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_duplicateGibt an, was passieren soll, wenn die in
pathübergebene Datei bereits existiert. Mögliche Werte sindenumerate(es wird ein Zähler an die Datei angehängt, z.B.datei_01.pdf),overwrite(die Datei wird überschrieben) undskip(die Datei wird übersprungen und nicht exportiert). Vorgabe istenumerate.
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:
class ExportService:
def export(self, context):
# Enumerates "test.xlsx" (_01, _02, ...) if more than one document is exported
filepath = self.run_export(context, "xlsx", "test.xlsx")
if filepath is not None:
_logger.info(f"Export XLSX: {filepath}")
return 1
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:
[Database]
DSN = MY_DSN
UID = user
PWD = password
Anschließend kann über self.database mithilfe des with-Statements eine Verbindung aufgebaut
werden:
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.
Wichtig: Da PHOENIX Archiv eine 64-Bit-Anwendung ist, werden aktuell 64-Bit-ODBC-Treiber vorausgesetzt.