neue Code Formatierung

This commit is contained in:
Ralph Soika 2025-05-07 11:37:31 +02:00
parent be064fb965
commit 0f7627419a
93 changed files with 6864 additions and 6078 deletions

210
BPMN-KI-Test/prompt1.txt Normal file
View file

@ -0,0 +1,210 @@
Du bist Prozessberater und Spezialisiert auf die Analyse von BPMN Modellen.
Bitte beschreibe den im folgenden BPMN 2.0 Modell definierten Prozess und erstelle eine kurze Zusammenfassung:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<bpmn2:definitions xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
xmlns:dc="http://www.omg.org/spec/DD/20100524/DC"
xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:open-bpmn="http://open-bpmn.org/XMLSchema"
xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
exporter="org.openbpmn" exporterVersion="1.0.0" targetNamespace="http://open-bpmn.org"
xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL">
<bpmn2:collaboration id="collaboration_1" name="Default Collaboration">
<bpmn2:participant id="participant_TgW2JA" name="Default Process" processRef="process_1" />
<bpmn2:participant id="participant_tgRhJQ" name="Rechnungseingang" processRef="process_lYaFpg">
<bpmn2:documentation id="documentation_wSfXcQ"><![CDATA[Dieser Workflow dient dazu eingehende Rechnungen zu prüfen und zur Bezahlung weiterzuleiten. Es wird unterschieden von Sachrechnungen, die vom Sekretariat bearbeitet werden können und Cargo-Rechnungen die durch die Logistikabteilung anhand von Logistikaufträgen geprüft werden.
Nachdem die Rechnung bezahlt wurde wird der Vorgang archiviert. ]]></bpmn2:documentation>
</bpmn2:participant>
</bpmn2:collaboration>
<bpmn2:extensionElements>
<open-bpmn:auto-align>true</open-bpmn:auto-align>
</bpmn2:extensionElements>
<bpmn2:process definitionalCollaborationRef="collaboration_1" id="process_1"
name="Default Process" processType="Public">
<bpmn2:documentation id="documentation_wXazVg" />
</bpmn2:process>
<bpmn2:process definitionalCollaborationRef="collaboration_1" id="process_lYaFpg"
isExecutable="true" name="Rechnungseingang" processType="Private">
<bpmn2:laneSet id="laneset_fDygyw" name="Lane Set">
<bpmn2:lane id="lane_BgoDFg" name="Sekretariat">
<bpmn2:documentation id="documentation_hIztzA" />
<bpmn2:flowNodeRef>task_7Y3UUA</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>event_bH7Wag</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>event_oNXxgg</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>event_GEqHhQ</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>event_VJXKGg</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>task_SP75zg</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>gateway_ASbu1A</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>gateway_Vyo0Zw</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>task_xAzhzw</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>event_YH3bnw</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>event_1qR49w</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>task_fHgZBg</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>event_YnFi7A</bpmn2:flowNodeRef>
</bpmn2:lane>
<bpmn2:lane id="lane_eMP9Xw" name="Fachbereich">
<bpmn2:documentation id="documentation_6ihnmQ" />
<bpmn2:flowNodeRef>event_A1J0OA</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>task_5lS3mw</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>gateway_Hn5ZhQ</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>event_dJ89xQ</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>event_30uUNA</bpmn2:flowNodeRef>
</bpmn2:lane>
</bpmn2:laneSet>
<bpmn2:startEvent id="event_bH7Wag" name="Start">
<bpmn2:documentation id="documentation_pguolw" />
<bpmn2:outgoing>sequenceFlow_8yitUQ</bpmn2:outgoing>
</bpmn2:startEvent>
<bpmn2:task id="task_7Y3UUA" name="Rechnungsprüfung">
<bpmn2:documentation id="documentation_oKsT5Q"><![CDATA[Inhaltliche Prüufng der Rechnung]]></bpmn2:documentation>
<bpmn2:incoming>sequenceFlow_BGVbSA</bpmn2:incoming>
<bpmn2:outgoing>sequenceFlow_FyAf3g</bpmn2:outgoing>
<bpmn2:incoming>sequenceFlow_hvQWmg</bpmn2:incoming>
<bpmn2:incoming>sequenceFlow_j4GDgg</bpmn2:incoming>
</bpmn2:task>
<bpmn2:endEvent id="event_oNXxgg" name="Ende">
<bpmn2:documentation id="documentation_1heF3g" />
<bpmn2:incoming>sequenceFlow_iVpj0g</bpmn2:incoming>
</bpmn2:endEvent>
<bpmn2:intermediateCatchEvent id="event_GEqHhQ" name="Import">
<bpmn2:documentation id="documentation_gR2xbw" />
<bpmn2:incoming>sequenceFlow_8yitUQ</bpmn2:incoming>
<bpmn2:outgoing>sequenceFlow_BGVbSA</bpmn2:outgoing>
</bpmn2:intermediateCatchEvent>
<bpmn2:sequenceFlow id="sequenceFlow_8yitUQ" sourceRef="event_bH7Wag" targetRef="event_GEqHhQ">
<bpmn2:documentation id="documentation_2Zd0Aw" />
</bpmn2:sequenceFlow>
<bpmn2:sequenceFlow id="sequenceFlow_BGVbSA" sourceRef="event_GEqHhQ" targetRef="task_7Y3UUA">
<bpmn2:documentation id="documentation_6k088A" />
</bpmn2:sequenceFlow>
<bpmn2:intermediateCatchEvent id="event_VJXKGg" name="Sachrechnung">
<bpmn2:documentation id="documentation_DaDN3w"><![CDATA[Weiterleiten zur Sachprüfung]]></bpmn2:documentation>
<bpmn2:incoming>sequenceFlow_rZXh6g</bpmn2:incoming>
<bpmn2:outgoing>sequenceFlow_sNlWkQ</bpmn2:outgoing>
</bpmn2:intermediateCatchEvent>
<bpmn2:task id="task_SP75zg" name="Budgetprüfung">
<bpmn2:documentation id="documentation_qP2Sew" />
<bpmn2:incoming>sequenceFlow_sNlWkQ</bpmn2:incoming>
<bpmn2:outgoing>sequenceFlow_nxZ9bA</bpmn2:outgoing>
</bpmn2:task>
<bpmn2:task id="task_5lS3mw" name="Prüfung Logistik Auftrag">
<bpmn2:documentation id="documentation_zweAkw"><![CDATA[Prüfung der Rechnung und Abgleich mit Logistikauftrag]]></bpmn2:documentation>
<bpmn2:incoming>sequenceFlow_DXGQrw</bpmn2:incoming>
<bpmn2:outgoing>sequenceFlow_UebylQ</bpmn2:outgoing>
</bpmn2:task>
<bpmn2:eventBasedGateway gatewayDirection="Diverging" id="gateway_ASbu1A" name="">
<bpmn2:documentation id="documentation_09gukg" />
<bpmn2:incoming>sequenceFlow_FyAf3g</bpmn2:incoming>
<bpmn2:outgoing>sequenceFlow_rZXh6g</bpmn2:outgoing>
<bpmn2:outgoing>sequenceFlow_i7cH2A</bpmn2:outgoing>
</bpmn2:eventBasedGateway>
<bpmn2:sequenceFlow id="sequenceFlow_FyAf3g" sourceRef="task_7Y3UUA" targetRef="gateway_ASbu1A">
<bpmn2:documentation id="documentation_tv00MQ" />
</bpmn2:sequenceFlow>
<bpmn2:sequenceFlow id="sequenceFlow_rZXh6g" sourceRef="gateway_ASbu1A" targetRef="event_VJXKGg">
<bpmn2:documentation id="documentation_EPAmhQ" />
</bpmn2:sequenceFlow>
<bpmn2:sequenceFlow id="sequenceFlow_sNlWkQ" sourceRef="event_VJXKGg" targetRef="task_SP75zg">
<bpmn2:documentation id="documentation_iXrfpg" />
</bpmn2:sequenceFlow>
<bpmn2:intermediateCatchEvent id="event_A1J0OA" name="Cargo Rechnung">
<bpmn2:documentation id="documentation_RWi6LA"><![CDATA[Weiterleitung zur Prüfung Logistikauftrag]]></bpmn2:documentation>
<bpmn2:incoming>sequenceFlow_i7cH2A</bpmn2:incoming>
<bpmn2:outgoing>sequenceFlow_DXGQrw</bpmn2:outgoing>
</bpmn2:intermediateCatchEvent>
<bpmn2:sequenceFlow id="sequenceFlow_i7cH2A" sourceRef="gateway_ASbu1A" targetRef="event_A1J0OA">
<bpmn2:documentation id="documentation_pz6J4g" />
</bpmn2:sequenceFlow>
<bpmn2:sequenceFlow id="sequenceFlow_DXGQrw" sourceRef="event_A1J0OA" targetRef="task_5lS3mw">
<bpmn2:documentation id="documentation_ngSLdQ" />
</bpmn2:sequenceFlow>
<bpmn2:exclusiveGateway gatewayDirection="Diverging" id="gateway_Vyo0Zw" name="">
<bpmn2:documentation id="documentation_Pm90XA" />
<bpmn2:incoming>sequenceFlow_nxZ9bA</bpmn2:incoming>
<bpmn2:outgoing>sequenceFlow_9tp42Q</bpmn2:outgoing>
<bpmn2:outgoing>sequenceFlow_h4SfiQ</bpmn2:outgoing>
</bpmn2:exclusiveGateway>
<bpmn2:intermediateCatchEvent id="event_dJ89xQ" name="Freigabe">
<bpmn2:documentation id="documentation_sB06Ag"><![CDATA[Rechnung in Ordnung und freigegeben zur Bezahlung]]></bpmn2:documentation>
<bpmn2:incoming>sequenceFlow_PLk0RA</bpmn2:incoming>
<bpmn2:outgoing>sequenceFlow_A0FQoA</bpmn2:outgoing>
</bpmn2:intermediateCatchEvent>
<bpmn2:exclusiveGateway gatewayDirection="Diverging" id="gateway_Hn5ZhQ" name="">
<bpmn2:documentation id="documentation_aLEQqQ" />
<bpmn2:incoming>sequenceFlow_UebylQ</bpmn2:incoming>
<bpmn2:outgoing>sequenceFlow_PLk0RA</bpmn2:outgoing>
<bpmn2:outgoing>sequenceFlow_xVEjcA</bpmn2:outgoing>
</bpmn2:exclusiveGateway>
<bpmn2:sequenceFlow id="sequenceFlow_UebylQ" sourceRef="task_5lS3mw" targetRef="gateway_Hn5ZhQ">
<bpmn2:documentation id="documentation_MZdr7Q" />
</bpmn2:sequenceFlow>
<bpmn2:task id="task_xAzhzw" name="Bezahlung">
<bpmn2:documentation id="documentation_zO3SRw" />
<bpmn2:incoming>sequenceFlow_9OrwEg</bpmn2:incoming>
<bpmn2:incoming>sequenceFlow_A0FQoA</bpmn2:incoming>
<bpmn2:outgoing>sequenceFlow_OfKNWA</bpmn2:outgoing>
</bpmn2:task>
<bpmn2:sequenceFlow id="sequenceFlow_nxZ9bA" sourceRef="task_SP75zg" targetRef="gateway_Vyo0Zw">
<bpmn2:documentation id="documentation_G4qmmA" />
</bpmn2:sequenceFlow>
<bpmn2:sequenceFlow id="sequenceFlow_PLk0RA" sourceRef="gateway_Hn5ZhQ" targetRef="event_dJ89xQ">
<bpmn2:documentation id="documentation_Y5qwSA" />
</bpmn2:sequenceFlow>
<bpmn2:intermediateCatchEvent id="event_YH3bnw" name="Freigeben">
<bpmn2:documentation id="documentation_C05Bhw"><![CDATA[Rechnung in Ordnung und freigegeben zur Bezahlung]]></bpmn2:documentation>
<bpmn2:incoming>sequenceFlow_9tp42Q</bpmn2:incoming>
<bpmn2:outgoing>sequenceFlow_9OrwEg</bpmn2:outgoing>
</bpmn2:intermediateCatchEvent>
<bpmn2:sequenceFlow id="sequenceFlow_9tp42Q" sourceRef="gateway_Vyo0Zw" targetRef="event_YH3bnw">
<bpmn2:documentation id="documentation_PIQaSg" />
</bpmn2:sequenceFlow>
<bpmn2:sequenceFlow id="sequenceFlow_9OrwEg" sourceRef="event_YH3bnw" targetRef="task_xAzhzw">
<bpmn2:documentation id="documentation_0eagTw" />
</bpmn2:sequenceFlow>
<bpmn2:intermediateCatchEvent id="event_1qR49w" name="Ablehnen">
<bpmn2:documentation id="documentation_cdpcXA" />
<bpmn2:incoming>sequenceFlow_h4SfiQ</bpmn2:incoming>
<bpmn2:outgoing>sequenceFlow_hvQWmg</bpmn2:outgoing>
</bpmn2:intermediateCatchEvent>
<bpmn2:intermediateCatchEvent id="event_30uUNA" name="Ablehnen">
<bpmn2:documentation id="documentation_VazNUA" />
<bpmn2:incoming>sequenceFlow_xVEjcA</bpmn2:incoming>
<bpmn2:outgoing>sequenceFlow_j4GDgg</bpmn2:outgoing>
</bpmn2:intermediateCatchEvent>
<bpmn2:sequenceFlow id="sequenceFlow_h4SfiQ" sourceRef="gateway_Vyo0Zw" targetRef="event_1qR49w">
<bpmn2:documentation id="documentation_Q2yPDw" />
</bpmn2:sequenceFlow>
<bpmn2:sequenceFlow id="sequenceFlow_hvQWmg" sourceRef="event_1qR49w" targetRef="task_7Y3UUA">
<bpmn2:documentation id="documentation_qkneSw" />
</bpmn2:sequenceFlow>
<bpmn2:sequenceFlow id="sequenceFlow_xVEjcA" sourceRef="gateway_Hn5ZhQ" targetRef="event_30uUNA">
<bpmn2:documentation id="documentation_l9waIg" />
</bpmn2:sequenceFlow>
<bpmn2:sequenceFlow id="sequenceFlow_j4GDgg" sourceRef="event_30uUNA" targetRef="task_7Y3UUA">
<bpmn2:documentation id="documentation_4vaLww" />
</bpmn2:sequenceFlow>
<bpmn2:sequenceFlow id="sequenceFlow_A0FQoA" sourceRef="event_dJ89xQ" targetRef="task_xAzhzw">
<bpmn2:documentation id="documentation_fAB90Q" />
</bpmn2:sequenceFlow>
<bpmn2:task id="task_fHgZBg" name="Archiviert">
<bpmn2:documentation id="documentation_Idessw"><![CDATA[Rechnungsprüfung abgeschlossen und Rechnung archiveirt.]]></bpmn2:documentation>
<bpmn2:incoming>sequenceFlow_A0FQoA</bpmn2:incoming>
<bpmn2:incoming>sequenceFlow_sUEtcA</bpmn2:incoming>
<bpmn2:outgoing>sequenceFlow_iVpj0g</bpmn2:outgoing>
</bpmn2:task>
<bpmn2:intermediateCatchEvent id="event_YnFi7A" name="Bezahlt">
<bpmn2:documentation id="documentation_Jz0LKg"><![CDATA[Rechnungsbetrag wurde überwiesen]]></bpmn2:documentation>
<bpmn2:incoming>sequenceFlow_OfKNWA</bpmn2:incoming>
<bpmn2:outgoing>sequenceFlow_sUEtcA</bpmn2:outgoing>
</bpmn2:intermediateCatchEvent>
<bpmn2:sequenceFlow id="sequenceFlow_OfKNWA" sourceRef="task_xAzhzw" targetRef="event_YnFi7A">
<bpmn2:documentation id="documentation_DdJpCg" />
</bpmn2:sequenceFlow>
<bpmn2:sequenceFlow id="sequenceFlow_sUEtcA" sourceRef="event_YnFi7A" targetRef="task_fHgZBg">
<bpmn2:documentation id="documentation_uG0SGg" />
</bpmn2:sequenceFlow>
<bpmn2:sequenceFlow id="sequenceFlow_iVpj0g" sourceRef="task_fHgZBg" targetRef="event_oNXxgg">
<bpmn2:documentation id="documentation_Is0LqQ" />
</bpmn2:sequenceFlow>
</bpmn2:process>
</bpmn2:definitions>

11
BPMN-KI-Test/prompt2.txt Normal file
View file

@ -0,0 +1,11 @@
Wir haben einen Geschäftsprozess für die Eingangsrechnungsprüfung in einem Unternehmen.
Rechnungen werden importiert und dann zur "Rechnungsprüfung" geleitet. Dieser Zusstand ist in einem BPMN 2.0 Diagram als Task Element abgebildet.
Der Mitarbeiter hat nun über die beiden Aktionen "Sachrechnung" und "Cargorechnung" die Möglichkeit die Rechnung zu klassifizieren. Diese beiden Aktionen sind in einem
BPMN 2.0 Diagram als Event Elemente dargestellt. Das Event 'Sachrechnung' führt zum Status (Task) "Budgetprüfung" (die vom Sekretariat durchgeführt wird)
und das Event 'Cargorechnung' führt zum Status 'Prüfung Logistikauftrag' (die vom Fachbereich durchgeführt werden kann). In beiden Zuständen kann nun der Mitarbeiter die Rechnung
über das Event "Freigeben" zur Bezahlung freigeben (Task) oder mit "Ablehnen" zurück an die initiale "Rechnungsprüfung" zurücksenden. Nach der Bezahlung kann der Mitarbeiter
die Rechnung über die Aktion (Event) "Bezahlt" in den finalen Status (Task) 'Archiviert' senden.
In einem BPMN Diagram sind diese Verzweigungen bei den Entscheidungen mit Gateways dargestellt.
Erstelle für diesen Prozessablauf eine sehr kompakte Darstellungsform, die für ein KI Prompt Template geeignet ist.

View file

@ -0,0 +1,389 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<bpmn2:definitions xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:open-bpmn="http://open-bpmn.org/XMLSchema" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" exporter="org.openbpmn" exporterVersion="1.0.0" targetNamespace="http://open-bpmn.org" xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL">
<bpmn2:collaboration id="collaboration_1" name="Default Collaboration">
<bpmn2:participant id="participant_TgW2JA" name="Default Process" processRef="process_1"/>
<bpmn2:participant id="participant_tgRhJQ" name="Rechnungseingang" processRef="process_lYaFpg">
<bpmn2:documentation id="documentation_wSfXcQ"><![CDATA[Dieser Workflow dient dazu eingehende Rechnungen zu prüfen und zur Bezahlung weiterzuleiten. Es wird unterschieden von Sachrechnungen, die vom Sekretariat bearbeitet werden können und Cargo-Rechnungen die durch die Logistikabteilung anhand von Logistikaufträgen geprüft werden.
Nachdem die Rechnung bezahlt wurde wird der Vorgang archiviert. ]]></bpmn2:documentation>
</bpmn2:participant>
</bpmn2:collaboration>
<bpmn2:extensionElements>
<open-bpmn:auto-align>true</open-bpmn:auto-align>
</bpmn2:extensionElements>
<bpmn2:process definitionalCollaborationRef="collaboration_1" id="process_1" name="Default Process" processType="Public">
<bpmn2:documentation id="documentation_wXazVg"/>
</bpmn2:process>
<bpmn2:process definitionalCollaborationRef="collaboration_1" id="process_lYaFpg" isExecutable="true" name="Rechnungseingang" processType="Private">
<bpmn2:laneSet id="laneset_fDygyw" name="Lane Set">
<bpmn2:lane id="lane_BgoDFg" name="Sekretariat">
<bpmn2:documentation id="documentation_hIztzA"/>
<bpmn2:flowNodeRef>task_7Y3UUA</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>event_bH7Wag</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>event_oNXxgg</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>event_GEqHhQ</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>event_VJXKGg</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>task_SP75zg</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>gateway_ASbu1A</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>gateway_Vyo0Zw</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>task_xAzhzw</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>event_YH3bnw</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>event_1qR49w</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>task_fHgZBg</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>event_YnFi7A</bpmn2:flowNodeRef>
</bpmn2:lane>
<bpmn2:lane id="lane_eMP9Xw" name="Fachbereich">
<bpmn2:documentation id="documentation_6ihnmQ"/>
<bpmn2:flowNodeRef>event_A1J0OA</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>task_5lS3mw</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>gateway_Hn5ZhQ</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>event_dJ89xQ</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>event_30uUNA</bpmn2:flowNodeRef>
</bpmn2:lane>
</bpmn2:laneSet>
<bpmn2:startEvent id="event_bH7Wag" name="Start">
<bpmn2:documentation id="documentation_pguolw"/>
<bpmn2:outgoing>sequenceFlow_8yitUQ</bpmn2:outgoing>
</bpmn2:startEvent>
<bpmn2:task id="task_7Y3UUA" name="Rechnungsprüfung">
<bpmn2:documentation id="documentation_oKsT5Q"><![CDATA[Inhaltliche Prüufng der Rechnung]]></bpmn2:documentation>
<bpmn2:incoming>sequenceFlow_BGVbSA</bpmn2:incoming>
<bpmn2:outgoing>sequenceFlow_FyAf3g</bpmn2:outgoing>
<bpmn2:incoming>sequenceFlow_hvQWmg</bpmn2:incoming>
<bpmn2:incoming>sequenceFlow_j4GDgg</bpmn2:incoming>
</bpmn2:task>
<bpmn2:endEvent id="event_oNXxgg" name="Ende">
<bpmn2:documentation id="documentation_1heF3g"/>
<bpmn2:incoming>sequenceFlow_iVpj0g</bpmn2:incoming>
</bpmn2:endEvent>
<bpmn2:intermediateCatchEvent id="event_GEqHhQ" name="Import">
<bpmn2:documentation id="documentation_gR2xbw"/>
<bpmn2:incoming>sequenceFlow_8yitUQ</bpmn2:incoming>
<bpmn2:outgoing>sequenceFlow_BGVbSA</bpmn2:outgoing>
</bpmn2:intermediateCatchEvent>
<bpmn2:sequenceFlow id="sequenceFlow_8yitUQ" sourceRef="event_bH7Wag" targetRef="event_GEqHhQ">
<bpmn2:documentation id="documentation_2Zd0Aw"/>
</bpmn2:sequenceFlow>
<bpmn2:sequenceFlow id="sequenceFlow_BGVbSA" sourceRef="event_GEqHhQ" targetRef="task_7Y3UUA">
<bpmn2:documentation id="documentation_6k088A"/>
</bpmn2:sequenceFlow>
<bpmn2:intermediateCatchEvent id="event_VJXKGg" name="Sachrechnung">
<bpmn2:documentation id="documentation_DaDN3w"><![CDATA[Weiterleiten zur Sachprüfung]]></bpmn2:documentation>
<bpmn2:incoming>sequenceFlow_rZXh6g</bpmn2:incoming>
<bpmn2:outgoing>sequenceFlow_sNlWkQ</bpmn2:outgoing>
</bpmn2:intermediateCatchEvent>
<bpmn2:task id="task_SP75zg" name="Budgetprüfung">
<bpmn2:documentation id="documentation_qP2Sew"/>
<bpmn2:incoming>sequenceFlow_sNlWkQ</bpmn2:incoming>
<bpmn2:outgoing>sequenceFlow_nxZ9bA</bpmn2:outgoing>
</bpmn2:task>
<bpmn2:task id="task_5lS3mw" name="Prüfung Logistik Auftrag">
<bpmn2:documentation id="documentation_zweAkw"><![CDATA[Prüfung der Rechnung und Abgleich mit Logistikauftrag]]></bpmn2:documentation>
<bpmn2:incoming>sequenceFlow_DXGQrw</bpmn2:incoming>
<bpmn2:outgoing>sequenceFlow_UebylQ</bpmn2:outgoing>
</bpmn2:task>
<bpmn2:eventBasedGateway gatewayDirection="Diverging" id="gateway_ASbu1A" name="">
<bpmn2:documentation id="documentation_09gukg"/>
<bpmn2:incoming>sequenceFlow_FyAf3g</bpmn2:incoming>
<bpmn2:outgoing>sequenceFlow_rZXh6g</bpmn2:outgoing>
<bpmn2:outgoing>sequenceFlow_i7cH2A</bpmn2:outgoing>
</bpmn2:eventBasedGateway>
<bpmn2:sequenceFlow id="sequenceFlow_FyAf3g" sourceRef="task_7Y3UUA" targetRef="gateway_ASbu1A">
<bpmn2:documentation id="documentation_tv00MQ"/>
</bpmn2:sequenceFlow>
<bpmn2:sequenceFlow id="sequenceFlow_rZXh6g" sourceRef="gateway_ASbu1A" targetRef="event_VJXKGg">
<bpmn2:documentation id="documentation_EPAmhQ"/>
</bpmn2:sequenceFlow>
<bpmn2:sequenceFlow id="sequenceFlow_sNlWkQ" sourceRef="event_VJXKGg" targetRef="task_SP75zg">
<bpmn2:documentation id="documentation_iXrfpg"/>
</bpmn2:sequenceFlow>
<bpmn2:intermediateCatchEvent id="event_A1J0OA" name="Cargo Rechnung">
<bpmn2:documentation id="documentation_RWi6LA"><![CDATA[Weiterleitung zur Prüfung Logistikauftrag]]></bpmn2:documentation>
<bpmn2:incoming>sequenceFlow_i7cH2A</bpmn2:incoming>
<bpmn2:outgoing>sequenceFlow_DXGQrw</bpmn2:outgoing>
</bpmn2:intermediateCatchEvent>
<bpmn2:sequenceFlow id="sequenceFlow_i7cH2A" sourceRef="gateway_ASbu1A" targetRef="event_A1J0OA">
<bpmn2:documentation id="documentation_pz6J4g"/>
</bpmn2:sequenceFlow>
<bpmn2:sequenceFlow id="sequenceFlow_DXGQrw" sourceRef="event_A1J0OA" targetRef="task_5lS3mw">
<bpmn2:documentation id="documentation_ngSLdQ"/>
</bpmn2:sequenceFlow>
<bpmn2:exclusiveGateway gatewayDirection="Diverging" id="gateway_Vyo0Zw" name="">
<bpmn2:documentation id="documentation_Pm90XA"/>
<bpmn2:incoming>sequenceFlow_nxZ9bA</bpmn2:incoming>
<bpmn2:outgoing>sequenceFlow_9tp42Q</bpmn2:outgoing>
<bpmn2:outgoing>sequenceFlow_h4SfiQ</bpmn2:outgoing>
</bpmn2:exclusiveGateway>
<bpmn2:intermediateCatchEvent id="event_dJ89xQ" name="Freigabe">
<bpmn2:documentation id="documentation_sB06Ag"><![CDATA[Rechnung in Ordnung und freigegeben zur Bezahlung]]></bpmn2:documentation>
<bpmn2:incoming>sequenceFlow_PLk0RA</bpmn2:incoming>
<bpmn2:outgoing>sequenceFlow_A0FQoA</bpmn2:outgoing>
</bpmn2:intermediateCatchEvent>
<bpmn2:exclusiveGateway gatewayDirection="Diverging" id="gateway_Hn5ZhQ" name="">
<bpmn2:documentation id="documentation_aLEQqQ"/>
<bpmn2:incoming>sequenceFlow_UebylQ</bpmn2:incoming>
<bpmn2:outgoing>sequenceFlow_PLk0RA</bpmn2:outgoing>
<bpmn2:outgoing>sequenceFlow_xVEjcA</bpmn2:outgoing>
</bpmn2:exclusiveGateway>
<bpmn2:sequenceFlow id="sequenceFlow_UebylQ" sourceRef="task_5lS3mw" targetRef="gateway_Hn5ZhQ">
<bpmn2:documentation id="documentation_MZdr7Q"/>
</bpmn2:sequenceFlow>
<bpmn2:task id="task_xAzhzw" name="Bezahlung">
<bpmn2:documentation id="documentation_zO3SRw"/>
<bpmn2:incoming>sequenceFlow_9OrwEg</bpmn2:incoming>
<bpmn2:incoming>sequenceFlow_A0FQoA</bpmn2:incoming>
<bpmn2:outgoing>sequenceFlow_OfKNWA</bpmn2:outgoing>
</bpmn2:task>
<bpmn2:sequenceFlow id="sequenceFlow_nxZ9bA" sourceRef="task_SP75zg" targetRef="gateway_Vyo0Zw">
<bpmn2:documentation id="documentation_G4qmmA"/>
</bpmn2:sequenceFlow>
<bpmn2:sequenceFlow id="sequenceFlow_PLk0RA" sourceRef="gateway_Hn5ZhQ" targetRef="event_dJ89xQ">
<bpmn2:documentation id="documentation_Y5qwSA"/>
</bpmn2:sequenceFlow>
<bpmn2:intermediateCatchEvent id="event_YH3bnw" name="Freigeben">
<bpmn2:documentation id="documentation_C05Bhw"><![CDATA[Rechnung in Ordnung und freigegeben zur Bezahlung]]></bpmn2:documentation>
<bpmn2:incoming>sequenceFlow_9tp42Q</bpmn2:incoming>
<bpmn2:outgoing>sequenceFlow_9OrwEg</bpmn2:outgoing>
</bpmn2:intermediateCatchEvent>
<bpmn2:sequenceFlow id="sequenceFlow_9tp42Q" sourceRef="gateway_Vyo0Zw" targetRef="event_YH3bnw">
<bpmn2:documentation id="documentation_PIQaSg"/>
</bpmn2:sequenceFlow>
<bpmn2:sequenceFlow id="sequenceFlow_9OrwEg" sourceRef="event_YH3bnw" targetRef="task_xAzhzw">
<bpmn2:documentation id="documentation_0eagTw"/>
</bpmn2:sequenceFlow>
<bpmn2:intermediateCatchEvent id="event_1qR49w" name="Ablehnen">
<bpmn2:documentation id="documentation_cdpcXA"/>
<bpmn2:incoming>sequenceFlow_h4SfiQ</bpmn2:incoming>
<bpmn2:outgoing>sequenceFlow_hvQWmg</bpmn2:outgoing>
</bpmn2:intermediateCatchEvent>
<bpmn2:intermediateCatchEvent id="event_30uUNA" name="Ablehnen">
<bpmn2:documentation id="documentation_VazNUA"/>
<bpmn2:incoming>sequenceFlow_xVEjcA</bpmn2:incoming>
<bpmn2:outgoing>sequenceFlow_j4GDgg</bpmn2:outgoing>
</bpmn2:intermediateCatchEvent>
<bpmn2:sequenceFlow id="sequenceFlow_h4SfiQ" sourceRef="gateway_Vyo0Zw" targetRef="event_1qR49w">
<bpmn2:documentation id="documentation_Q2yPDw"/>
</bpmn2:sequenceFlow>
<bpmn2:sequenceFlow id="sequenceFlow_hvQWmg" sourceRef="event_1qR49w" targetRef="task_7Y3UUA">
<bpmn2:documentation id="documentation_qkneSw"/>
</bpmn2:sequenceFlow>
<bpmn2:sequenceFlow id="sequenceFlow_xVEjcA" sourceRef="gateway_Hn5ZhQ" targetRef="event_30uUNA">
<bpmn2:documentation id="documentation_l9waIg"/>
</bpmn2:sequenceFlow>
<bpmn2:sequenceFlow id="sequenceFlow_j4GDgg" sourceRef="event_30uUNA" targetRef="task_7Y3UUA">
<bpmn2:documentation id="documentation_4vaLww"/>
</bpmn2:sequenceFlow>
<bpmn2:sequenceFlow id="sequenceFlow_A0FQoA" sourceRef="event_dJ89xQ" targetRef="task_xAzhzw">
<bpmn2:documentation id="documentation_fAB90Q"/>
</bpmn2:sequenceFlow>
<bpmn2:task id="task_fHgZBg" name="Archiviert">
<bpmn2:documentation id="documentation_Idessw"><![CDATA[Rechnungsprüfung abgeschlossen und Rechnung archiveirt.]]></bpmn2:documentation>
<bpmn2:incoming>sequenceFlow_A0FQoA</bpmn2:incoming>
<bpmn2:incoming>sequenceFlow_sUEtcA</bpmn2:incoming>
<bpmn2:outgoing>sequenceFlow_iVpj0g</bpmn2:outgoing>
</bpmn2:task>
<bpmn2:intermediateCatchEvent id="event_YnFi7A" name="Bezahlt">
<bpmn2:documentation id="documentation_Jz0LKg"><![CDATA[Rechnungsbetrag wurde überwiesen]]></bpmn2:documentation>
<bpmn2:incoming>sequenceFlow_OfKNWA</bpmn2:incoming>
<bpmn2:outgoing>sequenceFlow_sUEtcA</bpmn2:outgoing>
</bpmn2:intermediateCatchEvent>
<bpmn2:sequenceFlow id="sequenceFlow_OfKNWA" sourceRef="task_xAzhzw" targetRef="event_YnFi7A">
<bpmn2:documentation id="documentation_DdJpCg"/>
</bpmn2:sequenceFlow>
<bpmn2:sequenceFlow id="sequenceFlow_sUEtcA" sourceRef="event_YnFi7A" targetRef="task_fHgZBg">
<bpmn2:documentation id="documentation_uG0SGg"/>
</bpmn2:sequenceFlow>
<bpmn2:sequenceFlow id="sequenceFlow_iVpj0g" sourceRef="task_fHgZBg" targetRef="event_oNXxgg">
<bpmn2:documentation id="documentation_Is0LqQ"/>
</bpmn2:sequenceFlow>
</bpmn2:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1" name="OpenBPMN Diagram">
<bpmndi:BPMNPlane bpmnElement="collaboration_1" id="BPMNPlane_1">
<bpmndi:BPMNShape bpmnElement="event_bH7Wag" id="BPMNShape_uh055A">
<dc:Bounds height="36.0" width="36.0" x="307.0" y="-233.0"/>
<bpmndi:BPMNLabel id="BPMNLabel_iQJHWQ">
<dc:Bounds height="20.0" width="100.0" x="275.0" y="-194.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="event_oNXxgg" id="BPMNShape_UInW7g">
<dc:Bounds height="36.0" width="36.0" x="1777.0" y="-233.0"/>
<bpmndi:BPMNLabel id="BPMNLabel_34qp2g">
<dc:Bounds height="20.0" width="100.0" x="1745.0" y="-194.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="task_7Y3UUA" id="BPMNShape_6O8H3A">
<dc:Bounds height="50.0" width="110.0" x="520.0" y="-240.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="participant_tgRhJQ" id="BPMNShape_nrps0Q">
<dc:Bounds height="630.0" width="1650.0" x="230.0" y="-370.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="lane_BgoDFg" id="BPMNShape_Lane_D1gWUg">
<dc:Bounds height="258.0" width="1620.0" x="260.0" y="-370.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="lane_eMP9Xw" id="BPMNShape_Lane_j8rKkg">
<dc:Bounds height="372.0" width="1620.0" x="260.0" y="-112.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="event_GEqHhQ" id="BPMNShape_NYeOlw">
<dc:Bounds height="36.0" width="36.0" x="397.0" y="-233.0"/>
<bpmndi:BPMNLabel id="BPMNLabel_u0gAdw">
<dc:Bounds height="20.0" width="100.0" x="365.0" y="-194.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_8yitUQ" id="BPMNEdge_SKFqeQ" sourceElement="BPMNShape_uh055A" targetElement="BPMNShape_NYeOlw">
<di:waypoint x="343.0" y="-215.0"/>
<di:waypoint x="397.0" y="-215.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_BGVbSA" id="BPMNEdge_qcdmuA" sourceElement="BPMNShape_NYeOlw" targetElement="BPMNShape_6O8H3A">
<di:waypoint x="433.0" y="-215.0"/>
<di:waypoint x="520.0" y="-215.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape bpmnElement="event_VJXKGg" id="BPMNShape_QCvdOw">
<dc:Bounds height="36.0" width="36.0" x="797.0" y="-233.0"/>
<bpmndi:BPMNLabel id="BPMNLabel_6j9aVg">
<dc:Bounds height="20.0" width="100.0" x="765.0" y="-194.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="task_SP75zg" id="BPMNShape_dnAc8g">
<dc:Bounds height="50.0" width="110.0" x="930.0" y="-240.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="task_5lS3mw" id="BPMNShape_VFDbeg">
<dc:Bounds height="50.0" width="110.0" x="920.0" y="0.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="gateway_ASbu1A" id="BPMNShape_LIkiBA">
<dc:Bounds height="50.0" width="50.0" x="680.0" y="-240.0"/>
<bpmndi:BPMNLabel id="BPMNLabel_uZQpOQ">
<dc:Bounds height="20.0" width="100.0" x="655.0" y="-187.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_FyAf3g" id="BPMNEdge_F0Y1Tw" sourceElement="BPMNShape_6O8H3A" targetElement="BPMNShape_LIkiBA">
<di:waypoint x="630.0" y="-215.0"/>
<di:waypoint x="680.0" y="-215.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_rZXh6g" id="BPMNEdge_luL2IA" sourceElement="BPMNShape_LIkiBA" targetElement="BPMNShape_QCvdOw">
<di:waypoint x="730.0" y="-215.0"/>
<di:waypoint x="797.0" y="-215.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_sNlWkQ" id="BPMNEdge_vB8usw" sourceElement="BPMNShape_QCvdOw" targetElement="BPMNShape_dnAc8g">
<di:waypoint x="833.0" y="-215.0"/>
<di:waypoint x="930.0" y="-215.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape bpmnElement="event_A1J0OA" id="BPMNShape_KJbgeQ">
<dc:Bounds height="36.0" width="36.0" x="787.0" y="7.0"/>
<bpmndi:BPMNLabel id="BPMNLabel_Rmen0A">
<dc:Bounds height="20.0" width="100.0" x="755.0" y="46.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_i7cH2A" id="BPMNEdge_HBLd5A" sourceElement="BPMNShape_LIkiBA" targetElement="BPMNShape_KJbgeQ">
<di:waypoint x="703.0" y="-192.0"/>
<di:waypoint x="703.0" y="25.0"/>
<di:waypoint x="787.0" y="25.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_DXGQrw" id="BPMNEdge_s15wBA" sourceElement="BPMNShape_KJbgeQ" targetElement="BPMNShape_VFDbeg">
<di:waypoint x="823.0" y="25.0"/>
<di:waypoint x="920.0" y="25.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape bpmnElement="gateway_Vyo0Zw" id="BPMNShape_QihA6g">
<dc:Bounds height="50.0" width="50.0" x="1090.0" y="-240.0"/>
<bpmndi:BPMNLabel id="BPMNLabel_cyvcMA">
<dc:Bounds height="20.0" width="100.0" x="1065.0" y="-187.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="event_dJ89xQ" id="BPMNShape_4M1lNg">
<dc:Bounds height="36.0" width="36.0" x="1187.0" y="7.0"/>
<bpmndi:BPMNLabel id="BPMNLabel_gdYGpg">
<dc:Bounds height="20.0" width="100.0" x="1155.0" y="46.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="gateway_Hn5ZhQ" id="BPMNShape_mr0ibw">
<dc:Bounds height="50.0" width="50.0" x="1080.0" y="0.0"/>
<bpmndi:BPMNLabel id="BPMNLabel_5l4dRA">
<dc:Bounds height="20.0" width="100.0" x="1055.0" y="53.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_UebylQ" id="BPMNEdge_U0wIKg" sourceElement="BPMNShape_VFDbeg" targetElement="BPMNShape_mr0ibw">
<di:waypoint x="1030.0" y="25.0"/>
<di:waypoint x="1080.0" y="25.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape bpmnElement="task_xAzhzw" id="BPMNShape_JrIJDQ">
<dc:Bounds height="50.0" width="110.0" x="1290.0" y="-240.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_nxZ9bA" id="BPMNEdge_KKXvFg" sourceElement="BPMNShape_dnAc8g" targetElement="BPMNShape_QihA6g">
<di:waypoint x="1040.0" y="-215.0"/>
<di:waypoint x="1090.0" y="-215.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_PLk0RA" id="BPMNEdge_8UEsHA" sourceElement="BPMNShape_mr0ibw" targetElement="BPMNShape_4M1lNg">
<di:waypoint x="1130.0" y="25.0"/>
<di:waypoint x="1187.0" y="25.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape bpmnElement="event_YH3bnw" id="BPMNShape_GUBmSg">
<dc:Bounds height="36.0" width="36.0" x="1197.0" y="-233.0"/>
<bpmndi:BPMNLabel id="BPMNLabel_c48Mng">
<dc:Bounds height="20.0" width="100.0" x="1165.0" y="-194.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_9tp42Q" id="BPMNEdge_0AkLow" sourceElement="BPMNShape_QihA6g" targetElement="BPMNShape_GUBmSg">
<di:waypoint x="1140.0" y="-215.0"/>
<di:waypoint x="1197.0" y="-215.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_9OrwEg" id="BPMNEdge_eqL00Q" sourceElement="BPMNShape_GUBmSg" targetElement="BPMNShape_JrIJDQ">
<di:waypoint x="1233.0" y="-215.0"/>
<di:waypoint x="1290.0" y="-215.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape bpmnElement="event_1qR49w" id="BPMNShape_rbG2Zg">
<dc:Bounds height="36.0" width="36.0" x="1097.0" y="-333.0"/>
<bpmndi:BPMNLabel id="BPMNLabel_2qmYIA">
<dc:Bounds height="20.0" width="100.0" x="1065.0" y="-294.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="event_30uUNA" id="BPMNShape_xUycJQ">
<dc:Bounds height="36.0" width="36.0" x="1087.0" y="127.0"/>
<bpmndi:BPMNLabel id="BPMNLabel_40xf3g">
<dc:Bounds height="20.0" width="100.0" x="1055.0" y="166.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_h4SfiQ" id="BPMNEdge_7m0QHg" sourceElement="BPMNShape_QihA6g" targetElement="BPMNShape_rbG2Zg">
<di:waypoint x="1115.0" y="-240.0"/>
<di:waypoint x="1115.0" y="-297.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_hvQWmg" id="BPMNEdge_xx0UyQ" sourceElement="BPMNShape_rbG2Zg" targetElement="BPMNShape_6O8H3A">
<di:waypoint x="1097.0" y="-315.0"/>
<di:waypoint x="575.0" y="-315.0"/>
<di:waypoint x="575.0" y="-240.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_xVEjcA" id="BPMNEdge_5276Pw" sourceElement="BPMNShape_mr0ibw" targetElement="BPMNShape_xUycJQ">
<di:waypoint x="1105.0" y="50.0"/>
<di:waypoint x="1105.0" y="127.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_j4GDgg" id="BPMNEdge_3xc00w" sourceElement="BPMNShape_xUycJQ" targetElement="BPMNShape_6O8H3A">
<di:waypoint x="1087.0" y="145.0"/>
<di:waypoint x="575.0" y="145.0"/>
<di:waypoint x="575.0" y="-190.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_A0FQoA" id="BPMNEdge_vgxzvQ" sourceElement="BPMNShape_4M1lNg" targetElement="BPMNShape_JrIJDQ">
<di:waypoint x="1223.0" y="25.0"/>
<di:waypoint x="1345.0" y="25.0"/>
<di:waypoint x="1345.0" y="-190.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape bpmnElement="task_fHgZBg" id="BPMNShape_79up9g">
<dc:Bounds height="50.0" width="110.0" x="1600.0" y="-240.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="event_YnFi7A" id="BPMNShape_THdIqQ">
<dc:Bounds height="36.0" width="36.0" x="1457.0" y="-233.0"/>
<bpmndi:BPMNLabel id="BPMNLabel_P4dNKg">
<dc:Bounds height="20.0" width="100.0" x="1425.0" y="-194.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_OfKNWA" id="BPMNEdge_p2wDpg" sourceElement="BPMNShape_JrIJDQ" targetElement="BPMNShape_THdIqQ">
<di:waypoint x="1400.0" y="-215.0"/>
<di:waypoint x="1457.0" y="-215.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_sUEtcA" id="BPMNEdge_lOlEPw" sourceElement="BPMNShape_THdIqQ" targetElement="BPMNShape_79up9g">
<di:waypoint x="1493.0" y="-215.0"/>
<di:waypoint x="1600.0" y="-215.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_iVpj0g" id="BPMNEdge_XmQNXg" sourceElement="BPMNShape_79up9g" targetElement="BPMNShape_UInW7g">
<di:waypoint x="1710.0" y="-215.0"/>
<di:waypoint x="1777.0" y="-215.0"/>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn2:definitions>

View file

@ -0,0 +1,207 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<bpmn2:definitions xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
xmlns:dc="http://www.omg.org/spec/DD/20100524/DC"
xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:open-bpmn="http://open-bpmn.org/XMLSchema"
xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
exporter="org.openbpmn" exporterVersion="1.0.0" targetNamespace="http://open-bpmn.org"
xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL">
<bpmn2:collaboration id="collaboration_1" name="Default Collaboration">
<bpmn2:participant id="participant_TgW2JA" name="Default Process" processRef="process_1" />
<bpmn2:participant id="participant_tgRhJQ" name="Rechnungseingang" processRef="process_lYaFpg">
<bpmn2:documentation id="documentation_wSfXcQ"><![CDATA[Dieser Workflow dient dazu eingehende Rechnungen zu prüfen und zur Bezahlung weiterzuleiten. Es wird unterschieden von Sachrechnungen, die vom Sekretariat bearbeitet werden können und Cargo-Rechnungen die durch die Logistikabteilung anhand von Logistikaufträgen geprüft werden.
Nachdem die Rechnung bezahlt wurde wird der Vorgang archiviert. ]]></bpmn2:documentation>
</bpmn2:participant>
</bpmn2:collaboration>
<bpmn2:extensionElements>
<open-bpmn:auto-align>true</open-bpmn:auto-align>
</bpmn2:extensionElements>
<bpmn2:process definitionalCollaborationRef="collaboration_1" id="process_1"
name="Default Process" processType="Public">
<bpmn2:documentation id="documentation_wXazVg" />
</bpmn2:process>
<bpmn2:process definitionalCollaborationRef="collaboration_1" id="process_lYaFpg"
isExecutable="true" name="Rechnungseingang" processType="Private">
<bpmn2:laneSet id="laneset_fDygyw" name="Lane Set">
<bpmn2:lane id="lane_BgoDFg" name="Sekretariat">
<bpmn2:documentation id="documentation_hIztzA" />
<bpmn2:flowNodeRef>task_7Y3UUA</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>event_bH7Wag</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>event_oNXxgg</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>event_GEqHhQ</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>event_VJXKGg</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>task_SP75zg</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>gateway_ASbu1A</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>gateway_Vyo0Zw</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>task_xAzhzw</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>event_YH3bnw</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>event_1qR49w</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>task_fHgZBg</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>event_YnFi7A</bpmn2:flowNodeRef>
</bpmn2:lane>
<bpmn2:lane id="lane_eMP9Xw" name="Fachbereich">
<bpmn2:documentation id="documentation_6ihnmQ" />
<bpmn2:flowNodeRef>event_A1J0OA</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>task_5lS3mw</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>gateway_Hn5ZhQ</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>event_dJ89xQ</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>event_30uUNA</bpmn2:flowNodeRef>
</bpmn2:lane>
</bpmn2:laneSet>
<bpmn2:startEvent id="event_bH7Wag" name="Start">
<bpmn2:documentation id="documentation_pguolw" />
<bpmn2:outgoing>sequenceFlow_8yitUQ</bpmn2:outgoing>
</bpmn2:startEvent>
<bpmn2:task id="task_7Y3UUA" name="Rechnungsprüfung">
<bpmn2:documentation id="documentation_oKsT5Q"><![CDATA[Inhaltliche Prüufng der Rechnung]]></bpmn2:documentation>
<bpmn2:incoming>sequenceFlow_BGVbSA</bpmn2:incoming>
<bpmn2:outgoing>sequenceFlow_FyAf3g</bpmn2:outgoing>
<bpmn2:incoming>sequenceFlow_hvQWmg</bpmn2:incoming>
<bpmn2:incoming>sequenceFlow_j4GDgg</bpmn2:incoming>
</bpmn2:task>
<bpmn2:endEvent id="event_oNXxgg" name="Ende">
<bpmn2:documentation id="documentation_1heF3g" />
<bpmn2:incoming>sequenceFlow_iVpj0g</bpmn2:incoming>
</bpmn2:endEvent>
<bpmn2:intermediateCatchEvent id="event_GEqHhQ" name="Import">
<bpmn2:documentation id="documentation_gR2xbw" />
<bpmn2:incoming>sequenceFlow_8yitUQ</bpmn2:incoming>
<bpmn2:outgoing>sequenceFlow_BGVbSA</bpmn2:outgoing>
</bpmn2:intermediateCatchEvent>
<bpmn2:sequenceFlow id="sequenceFlow_8yitUQ" sourceRef="event_bH7Wag" targetRef="event_GEqHhQ">
<bpmn2:documentation id="documentation_2Zd0Aw" />
</bpmn2:sequenceFlow>
<bpmn2:sequenceFlow id="sequenceFlow_BGVbSA" sourceRef="event_GEqHhQ" targetRef="task_7Y3UUA">
<bpmn2:documentation id="documentation_6k088A" />
</bpmn2:sequenceFlow>
<bpmn2:intermediateCatchEvent id="event_VJXKGg" name="Sachrechnung">
<bpmn2:documentation id="documentation_DaDN3w"><![CDATA[Weiterleiten zur Sachprüfung]]></bpmn2:documentation>
<bpmn2:incoming>sequenceFlow_rZXh6g</bpmn2:incoming>
<bpmn2:outgoing>sequenceFlow_sNlWkQ</bpmn2:outgoing>
</bpmn2:intermediateCatchEvent>
<bpmn2:task id="task_SP75zg" name="Budgetprüfung">
<bpmn2:documentation id="documentation_qP2Sew" />
<bpmn2:incoming>sequenceFlow_sNlWkQ</bpmn2:incoming>
<bpmn2:outgoing>sequenceFlow_nxZ9bA</bpmn2:outgoing>
</bpmn2:task>
<bpmn2:task id="task_5lS3mw" name="Prüfung Logistik Auftrag">
<bpmn2:documentation id="documentation_zweAkw"><![CDATA[Prüfung der Rechnung und Abgleich mit Logistikauftrag]]></bpmn2:documentation>
<bpmn2:incoming>sequenceFlow_DXGQrw</bpmn2:incoming>
<bpmn2:outgoing>sequenceFlow_UebylQ</bpmn2:outgoing>
</bpmn2:task>
<bpmn2:eventBasedGateway gatewayDirection="Diverging" id="gateway_ASbu1A" name="">
<bpmn2:documentation id="documentation_09gukg" />
<bpmn2:incoming>sequenceFlow_FyAf3g</bpmn2:incoming>
<bpmn2:outgoing>sequenceFlow_rZXh6g</bpmn2:outgoing>
<bpmn2:outgoing>sequenceFlow_i7cH2A</bpmn2:outgoing>
</bpmn2:eventBasedGateway>
<bpmn2:sequenceFlow id="sequenceFlow_FyAf3g" sourceRef="task_7Y3UUA" targetRef="gateway_ASbu1A">
<bpmn2:documentation id="documentation_tv00MQ" />
</bpmn2:sequenceFlow>
<bpmn2:sequenceFlow id="sequenceFlow_rZXh6g" sourceRef="gateway_ASbu1A" targetRef="event_VJXKGg">
<bpmn2:documentation id="documentation_EPAmhQ" />
</bpmn2:sequenceFlow>
<bpmn2:sequenceFlow id="sequenceFlow_sNlWkQ" sourceRef="event_VJXKGg" targetRef="task_SP75zg">
<bpmn2:documentation id="documentation_iXrfpg" />
</bpmn2:sequenceFlow>
<bpmn2:intermediateCatchEvent id="event_A1J0OA" name="Cargo Rechnung">
<bpmn2:documentation id="documentation_RWi6LA"><![CDATA[Weiterleitung zur Prüfung Logistikauftrag]]></bpmn2:documentation>
<bpmn2:incoming>sequenceFlow_i7cH2A</bpmn2:incoming>
<bpmn2:outgoing>sequenceFlow_DXGQrw</bpmn2:outgoing>
</bpmn2:intermediateCatchEvent>
<bpmn2:sequenceFlow id="sequenceFlow_i7cH2A" sourceRef="gateway_ASbu1A" targetRef="event_A1J0OA">
<bpmn2:documentation id="documentation_pz6J4g" />
</bpmn2:sequenceFlow>
<bpmn2:sequenceFlow id="sequenceFlow_DXGQrw" sourceRef="event_A1J0OA" targetRef="task_5lS3mw">
<bpmn2:documentation id="documentation_ngSLdQ" />
</bpmn2:sequenceFlow>
<bpmn2:exclusiveGateway gatewayDirection="Diverging" id="gateway_Vyo0Zw" name="">
<bpmn2:documentation id="documentation_Pm90XA" />
<bpmn2:incoming>sequenceFlow_nxZ9bA</bpmn2:incoming>
<bpmn2:outgoing>sequenceFlow_9tp42Q</bpmn2:outgoing>
<bpmn2:outgoing>sequenceFlow_h4SfiQ</bpmn2:outgoing>
</bpmn2:exclusiveGateway>
<bpmn2:intermediateCatchEvent id="event_dJ89xQ" name="Freigabe">
<bpmn2:documentation id="documentation_sB06Ag"><![CDATA[Rechnung in Ordnung und freigegeben zur Bezahlung]]></bpmn2:documentation>
<bpmn2:incoming>sequenceFlow_PLk0RA</bpmn2:incoming>
<bpmn2:outgoing>sequenceFlow_A0FQoA</bpmn2:outgoing>
</bpmn2:intermediateCatchEvent>
<bpmn2:exclusiveGateway gatewayDirection="Diverging" id="gateway_Hn5ZhQ" name="">
<bpmn2:documentation id="documentation_aLEQqQ" />
<bpmn2:incoming>sequenceFlow_UebylQ</bpmn2:incoming>
<bpmn2:outgoing>sequenceFlow_PLk0RA</bpmn2:outgoing>
<bpmn2:outgoing>sequenceFlow_xVEjcA</bpmn2:outgoing>
</bpmn2:exclusiveGateway>
<bpmn2:sequenceFlow id="sequenceFlow_UebylQ" sourceRef="task_5lS3mw" targetRef="gateway_Hn5ZhQ">
<bpmn2:documentation id="documentation_MZdr7Q" />
</bpmn2:sequenceFlow>
<bpmn2:task id="task_xAzhzw" name="Bezahlung">
<bpmn2:documentation id="documentation_zO3SRw" />
<bpmn2:incoming>sequenceFlow_9OrwEg</bpmn2:incoming>
<bpmn2:incoming>sequenceFlow_A0FQoA</bpmn2:incoming>
<bpmn2:outgoing>sequenceFlow_OfKNWA</bpmn2:outgoing>
</bpmn2:task>
<bpmn2:sequenceFlow id="sequenceFlow_nxZ9bA" sourceRef="task_SP75zg" targetRef="gateway_Vyo0Zw">
<bpmn2:documentation id="documentation_G4qmmA" />
</bpmn2:sequenceFlow>
<bpmn2:sequenceFlow id="sequenceFlow_PLk0RA" sourceRef="gateway_Hn5ZhQ" targetRef="event_dJ89xQ">
<bpmn2:documentation id="documentation_Y5qwSA" />
</bpmn2:sequenceFlow>
<bpmn2:intermediateCatchEvent id="event_YH3bnw" name="Freigeben">
<bpmn2:documentation id="documentation_C05Bhw"><![CDATA[Rechnung in Ordnung und freigegeben zur Bezahlung]]></bpmn2:documentation>
<bpmn2:incoming>sequenceFlow_9tp42Q</bpmn2:incoming>
<bpmn2:outgoing>sequenceFlow_9OrwEg</bpmn2:outgoing>
</bpmn2:intermediateCatchEvent>
<bpmn2:sequenceFlow id="sequenceFlow_9tp42Q" sourceRef="gateway_Vyo0Zw" targetRef="event_YH3bnw">
<bpmn2:documentation id="documentation_PIQaSg" />
</bpmn2:sequenceFlow>
<bpmn2:sequenceFlow id="sequenceFlow_9OrwEg" sourceRef="event_YH3bnw" targetRef="task_xAzhzw">
<bpmn2:documentation id="documentation_0eagTw" />
</bpmn2:sequenceFlow>
<bpmn2:intermediateCatchEvent id="event_1qR49w" name="Ablehnen">
<bpmn2:documentation id="documentation_cdpcXA" />
<bpmn2:incoming>sequenceFlow_h4SfiQ</bpmn2:incoming>
<bpmn2:outgoing>sequenceFlow_hvQWmg</bpmn2:outgoing>
</bpmn2:intermediateCatchEvent>
<bpmn2:intermediateCatchEvent id="event_30uUNA" name="Ablehnen">
<bpmn2:documentation id="documentation_VazNUA" />
<bpmn2:incoming>sequenceFlow_xVEjcA</bpmn2:incoming>
<bpmn2:outgoing>sequenceFlow_j4GDgg</bpmn2:outgoing>
</bpmn2:intermediateCatchEvent>
<bpmn2:sequenceFlow id="sequenceFlow_h4SfiQ" sourceRef="gateway_Vyo0Zw" targetRef="event_1qR49w">
<bpmn2:documentation id="documentation_Q2yPDw" />
</bpmn2:sequenceFlow>
<bpmn2:sequenceFlow id="sequenceFlow_hvQWmg" sourceRef="event_1qR49w" targetRef="task_7Y3UUA">
<bpmn2:documentation id="documentation_qkneSw" />
</bpmn2:sequenceFlow>
<bpmn2:sequenceFlow id="sequenceFlow_xVEjcA" sourceRef="gateway_Hn5ZhQ" targetRef="event_30uUNA">
<bpmn2:documentation id="documentation_l9waIg" />
</bpmn2:sequenceFlow>
<bpmn2:sequenceFlow id="sequenceFlow_j4GDgg" sourceRef="event_30uUNA" targetRef="task_7Y3UUA">
<bpmn2:documentation id="documentation_4vaLww" />
</bpmn2:sequenceFlow>
<bpmn2:sequenceFlow id="sequenceFlow_A0FQoA" sourceRef="event_dJ89xQ" targetRef="task_xAzhzw">
<bpmn2:documentation id="documentation_fAB90Q" />
</bpmn2:sequenceFlow>
<bpmn2:task id="task_fHgZBg" name="Archiviert">
<bpmn2:documentation id="documentation_Idessw"><![CDATA[Rechnungsprüfung abgeschlossen und Rechnung archiveirt.]]></bpmn2:documentation>
<bpmn2:incoming>sequenceFlow_A0FQoA</bpmn2:incoming>
<bpmn2:incoming>sequenceFlow_sUEtcA</bpmn2:incoming>
<bpmn2:outgoing>sequenceFlow_iVpj0g</bpmn2:outgoing>
</bpmn2:task>
<bpmn2:intermediateCatchEvent id="event_YnFi7A" name="Bezahlt">
<bpmn2:documentation id="documentation_Jz0LKg"><![CDATA[Rechnungsbetrag wurde überwiesen]]></bpmn2:documentation>
<bpmn2:incoming>sequenceFlow_OfKNWA</bpmn2:incoming>
<bpmn2:outgoing>sequenceFlow_sUEtcA</bpmn2:outgoing>
</bpmn2:intermediateCatchEvent>
<bpmn2:sequenceFlow id="sequenceFlow_OfKNWA" sourceRef="task_xAzhzw" targetRef="event_YnFi7A">
<bpmn2:documentation id="documentation_DdJpCg" />
</bpmn2:sequenceFlow>
<bpmn2:sequenceFlow id="sequenceFlow_sUEtcA" sourceRef="event_YnFi7A" targetRef="task_fHgZBg">
<bpmn2:documentation id="documentation_uG0SGg" />
</bpmn2:sequenceFlow>
<bpmn2:sequenceFlow id="sequenceFlow_iVpj0g" sourceRef="task_fHgZBg" targetRef="event_oNXxgg">
<bpmn2:documentation id="documentation_Is0LqQ" />
</bpmn2:sequenceFlow>
</bpmn2:process>
</bpmn2:definitions>

17
BPMN-KI-Test/schema-1.txt Normal file
View file

@ -0,0 +1,17 @@
Prozess: Eingangsrechnungsprüfung
START -> [Task] Rechnungsprüfung
|
|-- [Event] Sachrechnung --> [Task] Budgetprüfung (Sekretariat)
| |
| |-- [Event] Freigeben --> [Task] Bezahlung
| |-- [Event] Ablehnen --> [Task] Rechnungsprüfung
|
|-- [Event] Cargorechnung --> [Task] Prüfung Logistikauftrag (Fachbereich)
|
|-- [Event] Freigeben --> [Task] Bezahlung
|-- [Event] Ablehnen --> [Task] Rechnungsprüfung
[Task] Bezahlung
|
|-- [Event] Bezahlt --> [Task] Archiviert (ENDE)

46
BPMN-KI-Test/schema-2.txt Normal file
View file

@ -0,0 +1,46 @@
prozess: Eingangsrechnungsprüfung
start_event: Rechnung importiert
tasks:
- name: Rechnungsprüfung
type: user_task
gateway:
name: Klassifikation
type: exclusive
paths:
- event: Sachrechnung
next_task: Budgetprüfung
role: Sekretariat
- event: Cargorechnung
next_task: Prüfung Logistikauftrag
role: Fachbereich
- name: Budgetprüfung
type: user_task
gateway:
name: Entscheidung
type: exclusive
paths:
- event: Freigeben
next_task: Bezahlung
- event: Ablehnen
next_task: Rechnungsprüfung
- name: Prüfung Logistikauftrag
type: user_task
gateway:
name: Entscheidung
type: exclusive
paths:
- event: Freigeben
next_task: Bezahlung
- event: Ablehnen
next_task: Rechnungsprüfung
- name: Bezahlung
type: service_task
event:
name: Bezahlt
next_task: Archiviert
- name: Archiviert
type: end_event

View file

@ -8,9 +8,15 @@ https://api-console.zoho.eu/
https://api-console.zoho.eu/client/1000.NLTM2KUEHI696MSLGEIANATVGGX7IN https://api-console.zoho.eu/client/1000.NLTM2KUEHI696MSLGEIANATVGGX7IN
``` ```
OrganizationID=20105697367 zoho.accesstoken 1000.18f5fbeaf572425b8062d015d541aad5.a704efa994774ef33d7af75106468a13
client ID= 1000.NLTM2KUEHI696MSLGEIANATVGGX7IN zoho.apidomain https://www.zohoapis.eu
client secret= ee9ba49f16e77c64abb89cea13542d2326d8d03ce8 zoho.clientid 1000.GR5FTPZYB06HEWSFSSX4DJU3OBD4EJ
zoho.clientsecret 3f3e74c5212bbc924a7ebf77f887c6296db1106c47
zoho.code 1000.c184c7592a218fee58f7b7855b56eb77.7b533ff9f570fd6ab3296784caeec072
zoho.organization 20105697367
zoho.refreshtoken 1000.98fe4d0c4f676237b400bf8a3dc219f3.03d6bacf5b988d339eb788c4ecff440e
zoho.scope ZohoBooks.contacts.ALL ZohoBooks.invoices.CREATE ZohoBooks.invoices.READ ZohoBooks.invoices.UPDATE
``` ```
# API Dokumentation # API Dokumentation
@ -86,3 +92,16 @@ Danach den Code token kopieren:
1000.7fe6315e8848b92a2f281b885677d37a.428c02dc2309606c86271842f1f67de4 1000.7fe6315e8848b92a2f281b885677d37a.428c02dc2309606c86271842f1f67de4
**Note:** Der Code muss für Test immer wieder neu erstellt werden, da er nur für einige Minuten gültig ist! **Note:** Der Code muss für Test immer wieder neu erstellt werden, da er nur für einige Minuten gültig ist!
# Disable auto-generated invoice number
Um die Automatische Vergabe von Rechnungsnummern in Zoho zu deaktivieren kann wie folgt vorgegangen werden:
1. Unter 'Invoices' eine neue Rechnung anlegen#
2. Im Feld 'Invoice#' das Zahnrad anklicken:
<img src="disable-auto-generated-number-01.png" />
3. Und dort die Option "Enter invoice numbers manually" auswählen
<img src="disable-auto-generated-number-02.png" />

26
doc/zoho/SECRETS.md Normal file
View file

@ -0,0 +1,26 @@
# Zoho Web API
AGL Dubai nutzt Zoho als Platform zusammen mit einer lokalen Steuerkanzlei
## Test/Prod Account Imixs
https://api-console.zoho.eu/
https://api-console.zoho.eu/client/1000.NLTM2KUEHI696MSLGEIANATVGGX7IN
```
OrganizationID=20105697367
client ID= 1000.NLTM2KUEHI696MSLGEIANATVGGX7IN
client secret= ee9ba49f16e77c64abb89cea13542d2326d8d03ce8
```
# API Dokumentation
- API Docu: https://www.zoho.com/books/api/v3/introduction/
- Authentication: https://www.zoho.com/books/api/v3/oauth/
# Testumgebung
https://books.zoho.eu/app/20105697367
https://accounts.zoho.eu/home#
Base API URI= https://accounts.zoho.eu/

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View file

@ -117,8 +117,7 @@ public class AGLAnalyticControllerDebitor implements Serializable {
ItemCollection process = documentService ItemCollection process = documentService
.load(workflowController.getWorkitem().getItemValueString("process.ref")); .load(workflowController.getWorkitem().getItemValueString("process.ref"));
if (process != null) { if (process != null) {
link = link + "&processref=" + process.getUniqueID() link = link + "&processref=" + process.getUniqueID() + "&phrase=" + getDbtNr();
+ "&phrase=" + getDbtNr();
} }
if ("analytic.invoices.count.all".equals(event.getKey())) { if ("analytic.invoices.count.all".equals(event.getKey())) {
event.setValue("" + countAll); event.setValue("" + countAll);
@ -188,8 +187,8 @@ public class AGLAnalyticControllerDebitor implements Serializable {
} }
String label = "<span class=\"pull-right\">" + formatCurrency(totalCurrency1) + " " + currencies.get(0) String label = "<span class=\"pull-right\">" + formatCurrency(totalCurrency1) + " " + currencies.get(0)
+ "</span>"; + "</span>";
label = label + "</br><span class=\"pull-right\">" + label = label + "</br><span class=\"pull-right\">" + formatCurrency(totalCurrency2) + " " + currencies.get(1)
formatCurrency(totalCurrency2) + " " + currencies.get(1) + "</span>"; + "</span>";
return label; return label;
} }
@ -200,8 +199,7 @@ public class AGLAnalyticControllerDebitor implements Serializable {
stats = null; stats = null;
} else { } else {
logger.info(" ├──load invoices for " + getDbtNr() + "...."); logger.info(" ├──load invoices for " + getDbtNr() + "....");
String query = "(type:workitem) AND dbtr.number:" + getDbtNr() String query = "(type:workitem) AND dbtr.number:" + getDbtNr() + " AND $modelversion:rechnungsausgang-*";
+ " AND $modelversion:rechnungsausgang-*";
try { try {
logger.info(" ├──refresh invoice stats for " + getDbtNr() + "...."); logger.info(" ├──refresh invoice stats for " + getDbtNr() + "....");
invoices = documentService.find(query, 999, 0, "$created", false); invoices = documentService.find(query, 999, 0, "$created", false);
@ -404,15 +402,12 @@ public class AGLAnalyticControllerDebitor implements Serializable {
String startDateStr = startDate.format(formatter); String startDateStr = startDate.format(formatter);
String endDateStr = endDate.format(formatter); String endDateStr = endDate.format(formatter);
String query = "(type:workitemarchive) AND " String query = "(type:workitemarchive) AND " + " invoice.date:[" + startDateStr + " TO " + endDateStr + "] AND "
+ " invoice.date:[" + startDateStr + " TO " + endDateStr + "] AND " + "dbtr.number:" + getDbtNr() + " AND $modelversion:rechnungsausgang-* AND ($taskid:[5900 TO 5999])";
+ "dbtr.number:" + getDbtNr()
+ " AND $modelversion:rechnungsausgang-* AND ($taskid:[5900 TO 5999])";
logger.fine("query = " + query); logger.fine("query = " + query);
try { try {
List<ItemCollection> invoices = documentService List<ItemCollection> invoices = documentService.findStubs(query, 9999, 0, "$created", false);
.findStubs(query, 9999, 0, "$created", false);
for (ItemCollection invoice : invoices) { for (ItemCollection invoice : invoices) {
try { try {
@ -477,14 +472,12 @@ public class AGLAnalyticControllerDebitor implements Serializable {
// Wir selektieren alle Zahlungseingänge interessieren uns aber nur für den // Wir selektieren alle Zahlungseingänge interessieren uns aber nur für den
// letzten // letzten
String sQuery = " (type:\"workitem\" OR type:\"workitemarchive\") " + // String sQuery = " (type:\"workitem\" OR type:\"workitemarchive\") " + //
" AND ($modelversion:zahlungseingang-*) AND ($workitemref:\"" " AND ($modelversion:zahlungseingang-*) AND ($workitemref:\"" + invoice.getUniqueID() + "\" )";
+ invoice.getUniqueID() + "\" )";
List<ItemCollection> workitems = null; List<ItemCollection> workitems = null;
try { try {
workitems = documentService.findStubs(sQuery, 99, 0, workitems = documentService.findStubs(sQuery, 99, 0, "payment.date", true);
"payment.date", true);
if (workitems.size() > 0) { if (workitems.size() > 0) {
return workitems.get(0).getItemValueDate("payment.date"); return workitems.get(0).getItemValueDate("payment.date");
} }

View file

@ -23,8 +23,7 @@ import jakarta.inject.Named;
/** /**
* Der AGLAnalyticControllerOPListe berechnet Analyse Daten über alle offenen * Der AGLAnalyticControllerOPListe berechnet Analyse Daten über alle offenen
* Rechnungen * Rechnungen und bietet im Workflow noch eine Excel-Export Funktion
* und bietet im Workflow noch eine Excel-Export Funktion
* *
* @author rsoika * @author rsoika
* *
@ -146,10 +145,7 @@ public class AGLAnalyticControllerOPListe implements Serializable {
* Läd die statistik daten zu einem department die Anzahl an aktuellen * Läd die statistik daten zu einem department die Anzahl an aktuellen
* Rechnungen zu ermitteln * Rechnungen zu ermitteln
* *
* int countAll = 0; * int countAll = 0; int countOpen = 0; int countDue = 0; int countDunning = 0;
* int countOpen = 0;
* int countDue = 0;
* int countDunning = 0;
* *
*/ */
private void calculateStats() { private void calculateStats() {
@ -163,8 +159,8 @@ public class AGLAnalyticControllerOPListe implements Serializable {
try { try {
if (departmentID == null || !departmentID.isEmpty()) { if (departmentID == null || !departmentID.isEmpty()) {
countAll = documentService.count("(type:workitem) AND $uniqueidref:" + departmentID countAll = documentService.count(
+ " AND $modelversion:rechnungsausgang-*"); "(type:workitem) AND $uniqueidref:" + departmentID + " AND $modelversion:rechnungsausgang-*");
countDue = documentService.count("(type:workitem) AND $uniqueidref:" + departmentID countDue = documentService.count("(type:workitem) AND $uniqueidref:" + departmentID
+ " AND $modelversion:rechnungsausgang-* AND $taskid:[5100 TO 5199]"); + " AND $modelversion:rechnungsausgang-* AND $taskid:[5100 TO 5199]");

View file

@ -66,8 +66,7 @@ public class AGLAnalyticControllerSteuer implements Serializable {
.load(workflowController.getWorkitem().getItemValueString("process.ref")); .load(workflowController.getWorkitem().getItemValueString("process.ref"));
String link = "/pages/workitems/worklist.xhtml"; String link = "/pages/workitems/worklist.xhtml";
if (process != null) { if (process != null) {
link = link + "?processref=" + process.getUniqueID() link = link + "?processref=" + process.getUniqueID() + "&workflowgroup=Steuerbescheid";
+ "&workflowgroup=Steuerbescheid";
} }
if ("analytic.steuer.eust".equals(event.getKey())) { if ("analytic.steuer.eust".equals(event.getKey())) {
event.setValue(computeSteuerbescheideEUST()); event.setValue(computeSteuerbescheideEUST());
@ -122,9 +121,8 @@ public class AGLAnalyticControllerSteuer implements Serializable {
} }
/** /**
* Berechnet die Summe aller offenen EUST Beträge * Berechnet die Summe aller offenen EUST Beträge // tax.code=[0],
* // tax.code=[0], amount=[3885.16], activity.type=[EUST], * amount=[3885.16], activity.type=[EUST], // filenumber=[LA-PAP-2408-024]}
* // filenumber=[LA-PAP-2408-024]}
* *
* @return * @return
*/ */
@ -323,25 +321,18 @@ public class AGLAnalyticControllerSteuer implements Serializable {
} }
// Step 3: Prepare the JSON structure // Step 3: Prepare the JSON structure
JsonObjectBuilder jsonBuilder = Json.createObjectBuilder() JsonObjectBuilder jsonBuilder = Json
.add("type", "bar") .createObjectBuilder().add("type", "bar").add("data", Json
.add("data", Json.createObjectBuilder() .createObjectBuilder().add("datasets", Json.createArrayBuilder()
.add("datasets", Json.createArrayBuilder()
.add(Json.createObjectBuilder() // First dataset for 'tax' .add(Json.createObjectBuilder() // First dataset for 'tax'
.add("label", "Zoll") .add("label", "Zoll").add("backgroundColor", "#96f").add("borderWidth", 1)
.add("backgroundColor", "#96f")
.add("borderWidth", 1)
.add("data", dataBuilderZoll)) .add("data", dataBuilderZoll))
.add(Json.createObjectBuilder() // Second dataset for 'eust' .add(Json.createObjectBuilder() // Second dataset for 'eust'
.add("label", "EUSt") .add("label", "EUSt").add("backgroundColor", "#36a2eb").add("borderWidth", 1)
.add("backgroundColor", "#36a2eb")
.add("borderWidth", 1)
.add("data", dataBuilderEust)) .add("data", dataBuilderEust))
.add(Json.createObjectBuilder() // Third dataset for 'Saldo' .add(Json.createObjectBuilder() // Third dataset for 'Saldo'
.add("label", "Saldo") .add("label", "Saldo").add("backgroundColor", "#ff6384").add("borderWidth", 1)
.add("backgroundColor", "#ff6384")
.add("borderWidth", 1)
.add("data", dataBuilderSaldo))) .add("data", dataBuilderSaldo)))
.add("labels", labelsBuilder)); .add("labels", labelsBuilder));
@ -373,15 +364,7 @@ public class AGLAnalyticControllerSteuer implements Serializable {
private String accumulateSteuerbescheideAbteilungByWeek() { private String accumulateSteuerbescheideAbteilungByWeek() {
logger.fine("accumulate steuer by Abteilung week..."); logger.fine("accumulate steuer by Abteilung week...");
String[] colors = { String[] colors = { "#36a2eb", "#ff6384", "#4bc0c0", "#ff9f40", "#96f", "#ffcd56", "#c9cbcf" };
"#36a2eb",
"#ff6384",
"#4bc0c0",
"#ff9f40",
"#96f",
"#ffcd56",
"#c9cbcf"
};
List<ItemCollection> list = getSteuerbescheide(); List<ItemCollection> list = getSteuerbescheide();
Map<String, SteuerData> totalByWeek = new HashMap<>(); Map<String, SteuerData> totalByWeek = new HashMap<>();
@ -448,8 +431,7 @@ public class AGLAnalyticControllerSteuer implements Serializable {
// Step 3: Baue die Balken pro abrteilung // Step 3: Baue die Balken pro abrteilung
JsonArrayBuilder datasets = Json.createArrayBuilder(); JsonArrayBuilder datasets = Json.createArrayBuilder();
JsonObjectBuilder jsonBuilder = Json.createObjectBuilder() JsonObjectBuilder jsonBuilder = Json.createObjectBuilder().add("type", "bar");
.add("type", "bar");
JsonObjectBuilder dataDing = Json.createObjectBuilder(); JsonObjectBuilder dataDing = Json.createObjectBuilder();
int iColor = 0; int iColor = 0;
@ -478,9 +460,7 @@ public class AGLAnalyticControllerSteuer implements Serializable {
} }
datasets.add(Json.createObjectBuilder() // First dataset for 'tax' datasets.add(Json.createObjectBuilder() // First dataset for 'tax'
.add("label", _abteilung) .add("label", _abteilung).add("backgroundColor", colors[iColor]).add("borderWidth", 1)
.add("backgroundColor", colors[iColor])
.add("borderWidth", 1)
.add("data", dataBuilderTotals)); .add("data", dataBuilderTotals));
iColor++; iColor++;

View file

@ -49,8 +49,7 @@ import jakarta.inject.Inject;
* *
* <p> * <p>
* Der Adapter kopiert die Steuerbescheiddaten in neue Zeilen, welche ab * Der Adapter kopiert die Steuerbescheiddaten in neue Zeilen, welche ab
* Zeilennummer * Zeilennummer 16 eingefügt werden.
* 16 eingefügt werden.
* <p> * <p>
* *
* Abschliessend wird die Zelle 'TOTAL' noch aktualisiert. * Abschliessend wird die Zelle 'TOTAL' noch aktualisiert.
@ -90,14 +89,12 @@ public class AGLAnalyticExcelAdapter extends POIFindReplaceAdapter {
ItemCollection configItemCollection = configService.loadConfiguration("AGL_CONFIGURATION"); ItemCollection configItemCollection = configService.loadConfiguration("AGL_CONFIGURATION");
List<String> currencyList = configItemCollection.getItemValue("currency.out"); List<String> currencyList = configItemCollection.getItemValue("currency.out");
if (currencyList == null || currencyList.size() < 2) { if (currencyList == null || currencyList.size() < 2) {
throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), InvoiceService.ERROR_CONFIG,
InvoiceService.ERROR_CONFIG,
"missing Currency configuration - please check parameters"); "missing Currency configuration - please check parameters");
} }
// read the Steuerbescheide options // read the Steuerbescheide options
ItemCollection steuerbescheideConfig = workflowService.evalWorkflowResult(event, "steuerbescheide", ItemCollection steuerbescheideConfig = workflowService.evalWorkflowResult(event, "steuerbescheide", document,
document,
false); false);
if (steuerbescheideConfig == null || !steuerbescheideConfig.hasItem("excel-export")) { if (steuerbescheideConfig == null || !steuerbescheideConfig.hasItem("excel-export")) {
throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), CONFIG_ERROR, throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), CONFIG_ERROR,
@ -120,8 +117,7 @@ public class AGLAnalyticExcelAdapter extends POIFindReplaceAdapter {
logger.info("... loading poi configuration.."); logger.info("... loading poi configuration..");
// Process POI instructions // Process POI instructions
// read the config // read the config
ItemCollection poiConfig = workflowService.evalWorkflowResult(event, "poi-update", document, ItemCollection poiConfig = workflowService.evalWorkflowResult(event, "poi-update", document, false);
false);
if (poiConfig == null || !poiConfig.hasItem("findreplace")) { if (poiConfig == null || !poiConfig.hasItem("findreplace")) {
throw new PluginException(POIFindReplaceAdapter.class.getSimpleName(), CONFIG_ERROR, throw new PluginException(POIFindReplaceAdapter.class.getSimpleName(), CONFIG_ERROR,
"missing poi configuration"); "missing poi configuration");
@ -133,8 +129,7 @@ public class AGLAnalyticExcelAdapter extends POIFindReplaceAdapter {
this.updateFileData(document.getFileData(targetName), document, replaceDevList, eval); this.updateFileData(document.getFileData(targetName), document, replaceDevList, eval);
} catch (PluginException | IOException | QueryException e) { } catch (PluginException | IOException | QueryException e) {
throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), InvoiceService.ERROR_CONFIG,
InvoiceService.ERROR_CONFIG,
"failed to update steuerbescheide: " + e.getMessage()); "failed to update steuerbescheide: " + e.getMessage());
} }
logger.info("... completed!"); logger.info("... completed!");
@ -142,8 +137,8 @@ public class AGLAnalyticExcelAdapter extends POIFindReplaceAdapter {
} }
/** /**
* This helper method inserts a row for each invoice at row 16 * This helper method inserts a row for each invoice at row 16 into the excel
* into the excel template file * template file
* <p> * <p>
* The method copies the Row A16 as a reference row * The method copies the Row A16 as a reference row
* <p> * <p>
@ -153,8 +148,7 @@ public class AGLAnalyticExcelAdapter extends POIFindReplaceAdapter {
* @throws PluginException * @throws PluginException
* @throws QueryException * @throws QueryException
*/ */
private void insertInvoiceRows(ItemCollection document, String fileName) private void insertInvoiceRows(ItemCollection document, String fileName) throws PluginException, QueryException {
throws PluginException, QueryException {
// load dummy rechnungen // load dummy rechnungen
List<ItemCollection> invoices = loadSteuerbescheide(); List<ItemCollection> invoices = loadSteuerbescheide();
@ -183,8 +177,7 @@ public class AGLAnalyticExcelAdapter extends POIFindReplaceAdapter {
XSSFRow row = sheet.createRow(rowPos); XSSFRow row = sheet.createRow(rowPos);
row.copyRowFrom(referenceRowInvoice, new CellCopyPolicy()); row.copyRowFrom(referenceRowInvoice, new CellCopyPolicy());
// insert values // insert values
row.getCell(0) row.getCell(0).setCellValue(invoice.getItemValueDate("$created"));
.setCellValue(invoice.getItemValueDate("$created"));
row.getCell(1, MissingCellPolicy.CREATE_NULL_AS_BLANK) row.getCell(1, MissingCellPolicy.CREATE_NULL_AS_BLANK)
.setCellValue(invoice.getItemValueString("invoice.number")); .setCellValue(invoice.getItemValueString("invoice.number"));
row.getCell(2, MissingCellPolicy.CREATE_NULL_AS_BLANK) row.getCell(2, MissingCellPolicy.CREATE_NULL_AS_BLANK)
@ -228,16 +221,14 @@ public class AGLAnalyticExcelAdapter extends POIFindReplaceAdapter {
doc.write(byteArrayOutputStream); doc.write(byteArrayOutputStream);
byte[] newContent = byteArrayOutputStream.toByteArray(); byte[] newContent = byteArrayOutputStream.toByteArray();
FileData fileDataNew = new FileData(fileData.getName(), newContent, fileData.getContentType(), FileData fileDataNew = new FileData(fileData.getName(), newContent, fileData.getContentType(), null);
null);
// update the fileData // update the fileData
document.addFileData(fileDataNew); document.addFileData(fileDataNew);
logger.finest("......new document added"); logger.finest("......new document added");
} catch (IOException e) { } catch (IOException e) {
throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), InvoiceService.ERROR_CONFIG,
InvoiceService.ERROR_CONFIG,
"failed to update opliste: " + e.getMessage()); "failed to update opliste: " + e.getMessage());
} finally { } finally {
if (doc != null) { if (doc != null) {
@ -258,13 +249,11 @@ public class AGLAnalyticExcelAdapter extends POIFindReplaceAdapter {
* @param document * @param document
* @throws PluginException * @throws PluginException
*/ */
private void appendExcelTemplate(ItemCollection document, String textblockRef, String template, private void appendExcelTemplate(ItemCollection document, String textblockRef, String template, String targetName)
String targetName)
throws PluginException { throws PluginException {
if ((template == null || template.isEmpty()) || (textblockRef == null || textblockRef.isEmpty())) { if ((template == null || template.isEmpty()) || (textblockRef == null || textblockRef.isEmpty())) {
throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), InvoiceService.ERROR_CONFIG,
InvoiceService.ERROR_CONFIG,
"invalid steuerbescheide configuration in model event - textblock/template reference not defined!"); "invalid steuerbescheide configuration in model event - textblock/template reference not defined!");
} }
@ -273,10 +262,8 @@ public class AGLAnalyticExcelAdapter extends POIFindReplaceAdapter {
// do we found the document? // do we found the document?
if (fileData == null) { if (fileData == null) {
throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), InvoiceService.ERROR_CONFIG,
InvoiceService.ERROR_CONFIG, "invalid steuerbescheide configuration in model event - template=" + template + " not found!");
"invalid steuerbescheide configuration in model event - template=" + template
+ " not found!");
} }
fileData.setName(targetName); fileData.setName(targetName);
// append document // append document
@ -293,9 +280,8 @@ public class AGLAnalyticExcelAdapter extends POIFindReplaceAdapter {
private List<ItemCollection> loadSteuerbescheide() { private List<ItemCollection> loadSteuerbescheide() {
List<ItemCollection> result = new ArrayList<>(); List<ItemCollection> result = new ArrayList<>();
try { try {
result = documentService.find( result = documentService.find("$modelversion:steuerbescheid-* AND type:workitem", 9999, 0, "invoice.number",
"$modelversion:steuerbescheid-* AND type:workitem", 9999, 0, false);
"invoice.number", false);
} catch (QueryException e) { } catch (QueryException e) {
e.printStackTrace(); e.printStackTrace();
} }

View file

@ -52,8 +52,7 @@ import jakarta.inject.Inject;
* *
* <p> * <p>
* Der Adapter kopiert die ausgangsrechnungen in neue Zeilen, welche ab * Der Adapter kopiert die ausgangsrechnungen in neue Zeilen, welche ab
* Zeilennummer * Zeilennummer 16 eingefügt werden.
* 16 eingefügt werden.
* <p> * <p>
* *
* Abschliessend wird die Zelle 'TOTAL' noch aktualisiert. * Abschliessend wird die Zelle 'TOTAL' noch aktualisiert.
@ -92,9 +91,7 @@ public class AGLAnalyticExcelAdapterDebitor extends POIFindReplaceAdapter {
List<String> currencyList = invoiceService.getCurrenciesOut(); List<String> currencyList = invoiceService.getCurrenciesOut();
// read the options // read the options
ItemCollection ausgangsrechnungConfig = workflowService.evalWorkflowResult(event, "soa", ItemCollection ausgangsrechnungConfig = workflowService.evalWorkflowResult(event, "soa", document, false);
document,
false);
if (ausgangsrechnungConfig == null || !ausgangsrechnungConfig.hasItem("excel-export")) { if (ausgangsrechnungConfig == null || !ausgangsrechnungConfig.hasItem("excel-export")) {
throw new PluginException(AGLAnalyticExcelAdapterDebitor.class.getSimpleName(), CONFIG_ERROR, throw new PluginException(AGLAnalyticExcelAdapterDebitor.class.getSimpleName(), CONFIG_ERROR,
"missing soa configuration in model event - please check model configuration"); "missing soa configuration in model event - please check model configuration");
@ -129,14 +126,12 @@ public class AGLAnalyticExcelAdapterDebitor extends POIFindReplaceAdapter {
doc.write(byteArrayOutputStream); doc.write(byteArrayOutputStream);
byte[] newContent = byteArrayOutputStream.toByteArray(); byte[] newContent = byteArrayOutputStream.toByteArray();
FileData fileDataNew = new FileData(fileData.getName(), newContent, fileData.getContentType(), FileData fileDataNew = new FileData(fileData.getName(), newContent, fileData.getContentType(), null);
null);
document.addFileData(fileDataNew); document.addFileData(fileDataNew);
logger.finest("......new document added"); logger.finest("......new document added");
} catch (IOException | QueryException e) { } catch (IOException | QueryException e) {
throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), InvoiceService.ERROR_CONFIG,
InvoiceService.ERROR_CONFIG,
"failed to update opliste: " + e.getMessage()); "failed to update opliste: " + e.getMessage());
} finally { } finally {
if (doc != null) { if (doc != null) {
@ -153,8 +148,7 @@ public class AGLAnalyticExcelAdapterDebitor extends POIFindReplaceAdapter {
processPOIUpdate(document, event, targetName); processPOIUpdate(document, event, targetName);
} catch (PluginException e) { } catch (PluginException e) {
throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), InvoiceService.ERROR_CONFIG,
InvoiceService.ERROR_CONFIG,
"failed to update steuerbescheide: " + e.getMessage()); "failed to update steuerbescheide: " + e.getMessage());
} }
logger.info("... completed!"); logger.info("... completed!");
@ -213,8 +207,8 @@ public class AGLAnalyticExcelAdapterDebitor extends POIFindReplaceAdapter {
} }
/** /**
* This helper method inserts a row for each invoice at row 16 * This helper method inserts a row for each invoice at row 16 into the excel
* into the excel template file * template file
* <p> * <p>
* The method copies the Row A16 as a reference row * The method copies the Row A16 as a reference row
* <p> * <p>
@ -249,8 +243,7 @@ public class AGLAnalyticExcelAdapterDebitor extends POIFindReplaceAdapter {
XSSFRow row = sheet.createRow(rowPos); XSSFRow row = sheet.createRow(rowPos);
row.copyRowFrom(referenceRowInvoice, new CellCopyPolicy()); row.copyRowFrom(referenceRowInvoice, new CellCopyPolicy());
// insert values // insert values
row.getCell(0) row.getCell(0).setCellValue(invoice.getItemValueDate("$created"));
.setCellValue(invoice.getItemValueDate("$created"));
row.getCell(1, MissingCellPolicy.CREATE_NULL_AS_BLANK) row.getCell(1, MissingCellPolicy.CREATE_NULL_AS_BLANK)
.setCellValue(invoice.getItemValueString("invoice.number")); .setCellValue(invoice.getItemValueString("invoice.number"));
row.getCell(2, MissingCellPolicy.CREATE_NULL_AS_BLANK) row.getCell(2, MissingCellPolicy.CREATE_NULL_AS_BLANK)
@ -318,12 +311,10 @@ public class AGLAnalyticExcelAdapterDebitor extends POIFindReplaceAdapter {
* @throws PluginException * @throws PluginException
*/ */
private FileData appendExcelTemplate(ItemCollection document, String textblockRef, String template, private FileData appendExcelTemplate(ItemCollection document, String textblockRef, String template,
String targetName) String targetName) throws PluginException {
throws PluginException {
if ((template == null || template.isEmpty()) || (textblockRef == null || textblockRef.isEmpty())) { if ((template == null || template.isEmpty()) || (textblockRef == null || textblockRef.isEmpty())) {
throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), InvoiceService.ERROR_CONFIG,
InvoiceService.ERROR_CONFIG,
"invalid SOA configuration in model event - textblock/template reference not defined!"); "invalid SOA configuration in model event - textblock/template reference not defined!");
} }
@ -332,10 +323,8 @@ public class AGLAnalyticExcelAdapterDebitor extends POIFindReplaceAdapter {
// do we found the document? // do we found the document?
if (fileData == null) { if (fileData == null) {
throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), InvoiceService.ERROR_CONFIG,
InvoiceService.ERROR_CONFIG, "invalid SOA configuration in model event - template=" + template + " not found!");
"invalid SOA configuration in model event - template=" + template
+ " not found!");
} }
fileData.setName(targetName); fileData.setName(targetName);
// append document // append document
@ -352,12 +341,9 @@ public class AGLAnalyticExcelAdapterDebitor extends POIFindReplaceAdapter {
private List<ItemCollection> loadInvoices(String dbtrNumber) { private List<ItemCollection> loadInvoices(String dbtrNumber) {
List<ItemCollection> result = new ArrayList<>(); List<ItemCollection> result = new ArrayList<>();
String query = "(type:workitem) AND dbtr.number:" + dbtrNumber String query = "(type:workitem) AND dbtr.number:" + dbtrNumber + " AND $modelversion:rechnungsausgang-*";
+ " AND $modelversion:rechnungsausgang-*";
try { try {
result = documentService.find( result = documentService.find(query, 9999, 0, "invoice.number", false);
query, 9999, 0,
"invoice.number", false);
} catch (QueryException e) { } catch (QueryException e) {
e.printStackTrace(); e.printStackTrace();
} }

View file

@ -109,9 +109,8 @@ public class BusinessPartnerController implements Serializable {
* *
* {@code * {@code
<h:commandScript name="datevSearch" action= <h:commandScript name="datevSearch" action=
* "#{datevSearchController.searchCdtr()}" * "#{datevSearchController.searchCdtr()}" render=
* render="autocomplete-resultlist-datev" onevent="autocompleteShowResult" /> * "autocomplete-resultlist-datev" onevent="autocompleteShowResult" /> }
* }
*/ */
public void search(String options) { public void search(String options) {
List<ItemCollection> resultList = null; List<ItemCollection> resultList = null;
@ -160,10 +159,9 @@ public class BusinessPartnerController implements Serializable {
/** /**
* Diese Helper Method liefert true wenn in den Options von Form Part 'reRender' * Diese Helper Method liefert true wenn in den Options von Form Part 'reRender'
* angegeben wurde. * angegeben wurde. Damit löst die Suchmaske businesspartner_search.xhtml eine
* Damit löst die Suchmaske businesspartner_search.xhtml eine komplette rerender * komplette rerender Phase der Form aus. Dies ist z.b. für den workflow
* Phase der Form aus. * 'analyse-debitor.bpmn' nützlich
* Dies ist z.b. für den workflow 'analyse-debitor.bpmn' nützlich
* *
* @param options * @param options
* @return * @return
@ -277,16 +275,16 @@ public class BusinessPartnerController implements Serializable {
if (!dbtrNumber.isEmpty()) { if (!dbtrNumber.isEmpty()) {
invoicesOut = businessPartnerService.findOutgoingInvoices(dbtrNumber); invoicesOut = businessPartnerService.findOutgoingInvoices(dbtrNumber);
// Mahnungen // Mahnungen
String query = "(type:workitem) AND ($modelversion:mahnlauf-*) " String query = "(type:workitem) AND ($modelversion:mahnlauf-*) " + " AND (dbtr.number:" + dbtrNumber
+ " AND (dbtr.number:" + dbtrNumber + ")"; + ")";
dunnings = documentService.findStubs(query, 999, 0, "invoice.number", false); dunnings = documentService.findStubs(query, 999, 0, "invoice.number", false);
} }
// Eingangsrechnungen // Eingangsrechnungen
String cdtrNumber = workitem.getItemValueString("cdtr.number"); String cdtrNumber = workitem.getItemValueString("cdtr.number");
if (!cdtrNumber.isEmpty()) { if (!cdtrNumber.isEmpty()) {
String query = "(type:workitem) AND ($modelversion:rechnungseingang-*) " String query = "(type:workitem) AND ($modelversion:rechnungseingang-*) " + " AND (cdtr.number:"
+ " AND (cdtr.number:" + cdtrNumber + ")"; + cdtrNumber + ")";
invoicesIn = documentService.findStubs(query, 999, 0, "invoice.number", false); invoicesIn = documentService.findStubs(query, 999, 0, "invoice.number", false);
} }
@ -329,8 +327,8 @@ public class BusinessPartnerController implements Serializable {
* user has selected a new businesspartner. * user has selected a new businesspartner.
* <p> * <p>
* The method lookups the given BusinessPartner and updates the metadata for * The method lookups the given BusinessPartner and updates the metadata for
* dbtr/cdtr depending on the workflow type. * dbtr/cdtr depending on the workflow type. A form will than rerender the
* A form will than rerender the opList section. * opList section.
*/ */
public void updateMetaData() { public void updateMetaData() {
FacesContext fc = FacesContext.getCurrentInstance(); FacesContext fc = FacesContext.getCurrentInstance();
@ -345,8 +343,7 @@ public class BusinessPartnerController implements Serializable {
businessPartner.getItemValueString("cdtr.number")); businessPartner.getItemValueString("cdtr.number"));
workflowController.getWorkitem().setItemValue("cdtr.name", workflowController.getWorkitem().setItemValue("cdtr.name",
businessPartner.getItemValueString("partner.name")); businessPartner.getItemValueString("partner.name"));
workflowController.getWorkitem().setItemValue("cdtr.mail", workflowController.getWorkitem().setItemValue("cdtr.mail", businessPartner.getItemValue("cdtr.mail"));
businessPartner.getItemValue("cdtr.mail"));
} }
// Ausgangsrechnung // Ausgangsrechnung
@ -355,8 +352,7 @@ public class BusinessPartnerController implements Serializable {
businessPartner.getItemValueString("dbtr.number")); businessPartner.getItemValueString("dbtr.number"));
workflowController.getWorkitem().setItemValue("dbtr.name", workflowController.getWorkitem().setItemValue("dbtr.name",
businessPartner.getItemValueString("partner.name")); businessPartner.getItemValueString("partner.name"));
workflowController.getWorkitem().setItemValue("dbtr.mail", workflowController.getWorkitem().setItemValue("dbtr.mail", businessPartner.getItemValue("dbtr.mail"));
businessPartner.getItemValue("dbtr.mail"));
} }
// Zahlungseingang // Zahlungseingang
@ -365,8 +361,7 @@ public class BusinessPartnerController implements Serializable {
businessPartner.getItemValueString("dbtr.number")); businessPartner.getItemValueString("dbtr.number"));
workflowController.getWorkitem().setItemValue("dbtr.name", workflowController.getWorkitem().setItemValue("dbtr.name",
businessPartner.getItemValueString("partner.name")); businessPartner.getItemValueString("partner.name"));
workflowController.getWorkitem().setItemValue("dbtr.mail", workflowController.getWorkitem().setItemValue("dbtr.mail", businessPartner.getItemValue("dbtr.mail"));
businessPartner.getItemValue("dbtr.mail"));
} }
// Analyse Debitor // Analyse Debitor
@ -375,8 +370,7 @@ public class BusinessPartnerController implements Serializable {
businessPartner.getItemValueString("dbtr.number")); businessPartner.getItemValueString("dbtr.number"));
workflowController.getWorkitem().setItemValue("dbtr.name", workflowController.getWorkitem().setItemValue("dbtr.name",
businessPartner.getItemValueString("partner.name")); businessPartner.getItemValueString("partner.name"));
workflowController.getWorkitem().setItemValue("dbtr.mail", workflowController.getWorkitem().setItemValue("dbtr.mail", businessPartner.getItemValue("dbtr.mail"));
businessPartner.getItemValue("dbtr.mail"));
} }
} }

View file

@ -51,7 +51,7 @@ import jakarta.annotation.PostConstruct;
import jakarta.annotation.security.DeclareRoles; import jakarta.annotation.security.DeclareRoles;
import jakarta.annotation.security.RolesAllowed; import jakarta.annotation.security.RolesAllowed;
import jakarta.annotation.security.RunAs; import jakarta.annotation.security.RunAs;
import jakarta.ejb.Singleton; import jakarta.ejb.Stateless;
import jakarta.inject.Inject; import jakarta.inject.Inject;
/** /**
@ -68,7 +68,7 @@ import jakarta.inject.Inject;
@RolesAllowed({ "org.imixs.ACCESSLEVEL.NOACCESS", "org.imixs.ACCESSLEVEL.READERACCESS", @RolesAllowed({ "org.imixs.ACCESSLEVEL.NOACCESS", "org.imixs.ACCESSLEVEL.READERACCESS",
"org.imixs.ACCESSLEVEL.AUTHORACCESS", "org.imixs.ACCESSLEVEL.EDITORACCESS", "org.imixs.ACCESSLEVEL.AUTHORACCESS", "org.imixs.ACCESSLEVEL.EDITORACCESS",
"org.imixs.ACCESSLEVEL.MANAGERACCESS" }) "org.imixs.ACCESSLEVEL.MANAGERACCESS" })
@Singleton @Stateless
@RunAs("org.imixs.ACCESSLEVEL.MANAGERACCESS") @RunAs("org.imixs.ACCESSLEVEL.MANAGERACCESS")
public class BusinessPartnerService { public class BusinessPartnerService {
@ -121,9 +121,8 @@ public class BusinessPartnerService {
/** /**
* Diese Methode aktualisiert das business Partner object. Sie wird von den * Diese Methode aktualisiert das business Partner object. Sie wird von den
* Invoice Plugins genutzt. * Invoice Plugins genutzt. Die Methode wirft eine PluginException falls das
* Die Methode wirft eine PluginException falls das BusinessPartner Objekt * BusinessPartner Objekt gesperrt oder in verification ist!
* gesperrt oder in verification ist!
* *
* Gleichzeitig wird bei Rechnungen in Abhängigkeit der Rechnungsart die * Gleichzeitig wird bei Rechnungen in Abhängigkeit der Rechnungsart die
* dbtr./cdtr. items sowie mail und country aktualisiert. * dbtr./cdtr. items sowie mail und country aktualisiert.
@ -145,8 +144,18 @@ public class BusinessPartnerService {
workitem.setItemValue("partner.id", partnerID); workitem.setItemValue("partner.id", partnerID);
} }
if (partnerID == null || partnerID.isEmpty()) {
return;
}
// load Business Partner // load Business Partner
ItemCollection businessPartner = getBusinessPartnerByID(partnerID); ItemCollection businessPartner = getBusinessPartnerByID(partnerID);
if (businessPartner == null) {
return;
}
// because the method can be called multiple times within one worklfow
// processing cycle
// we need to load the object within the current transaction context
businessPartner = documentService.load(businessPartner.getUniqueID());
if (businessPartner != null) { if (businessPartner != null) {
workitem.setItemValue("partner.name", businessPartner.getItemValueString("partner.name")); workitem.setItemValue("partner.name", businessPartner.getItemValueString("partner.name"));
@ -166,13 +175,11 @@ public class BusinessPartnerService {
// Validate Business Partner Status! // Validate Business Partner Status!
if (businessPartner.getTaskID() == TASK_VERIFICATION) { if (businessPartner.getTaskID() == TASK_VERIFICATION) {
throw new PluginException(InvoicePlugin.class.getName(), throw new PluginException(InvoicePlugin.class.getName(), ERROR_BUSINESSPARTNER_VERIFICATION,
ERROR_BUSINESSPARTNER_VERIFICATION,
"Businesspartner is in verification. Invoice can't be processed!"); "Businesspartner is in verification. Invoice can't be processed!");
} }
if (businessPartner.getTaskID() == TASK_LOCKED) { if (businessPartner.getTaskID() == TASK_LOCKED) {
throw new PluginException(InvoicePlugin.class.getName(), throw new PluginException(InvoicePlugin.class.getName(), ERROR_BUSINESSPARTNER_LOCKED,
ERROR_BUSINESSPARTNER_LOCKED,
"Businesspartner is locked. Invoice can't be processed!"); "Businesspartner is locked. Invoice can't be processed!");
} }
// update business partner status... // update business partner status...
@ -225,8 +232,7 @@ public class BusinessPartnerService {
/** /**
* Diese Methode sucht einen Business Partner anhand seiner BPID * Diese Methode sucht einen Business Partner anhand seiner BPID
* *
* @param bpid * @param bpid - business partner id phrase
* - business partner id phrase
* @return - matching business partner * @return - matching business partner
*/ */
public ItemCollection getBusinessPartnerByID(String bpid) { public ItemCollection getBusinessPartnerByID(String bpid) {
@ -251,8 +257,7 @@ public class BusinessPartnerService {
/** /**
* Diese Methode sucht Business Partner anhand einer Suchphrase * Diese Methode sucht Business Partner anhand einer Suchphrase
* *
* @param phrase * @param phrase - search phrase
* - search phrase
* @return - list of matching business partners * @return - list of matching business partners
*/ */
public List<ItemCollection> search(String phrase) { public List<ItemCollection> search(String phrase) {
@ -386,8 +391,7 @@ public class BusinessPartnerService {
List<ItemCollection> bankenListe = explodeBanks(businessPartner); List<ItemCollection> bankenListe = explodeBanks(businessPartner);
for (ItemCollection bank : bankenListe) { for (ItemCollection bank : bankenListe) {
if (iban.equals(bank.getItemValueString("iban")) if (iban.equals(bank.getItemValueString("iban")) && bic.equals(bank.getItemValueString("bic"))) {
&& bic.equals(bank.getItemValueString("bic"))) {
// known! // known!
return true; return true;
} }

View file

@ -113,8 +113,8 @@ public class CargosoftExportAdapter implements SignalAdapter {
// finally append the success event id // finally append the success event id
document.event(EVENT_SUCCESS); document.event(EVENT_SUCCESS);
} catch (PluginException | IOException } catch (PluginException | IOException | jakarta.xml.bind.JAXBException
| jakarta.xml.bind.JAXBException | javax.xml.transform.TransformerException e) { | javax.xml.transform.TransformerException e) {
logger.severe("cargosoft export failed: " + e.getMessage()); logger.severe("cargosoft export failed: " + e.getMessage());
document.setItemValue("cargosoft.error", e.getMessage()); document.setItemValue("cargosoft.error", e.getMessage());
document.event(EVENT_FAILURE); document.event(EVENT_FAILURE);

View file

@ -246,8 +246,7 @@ public class CustomSearchController implements Serializable {
Date endDate = endCalendar.getTime(); Date endDate = endCalendar.getTime();
SimpleDateFormat dateformat = new SimpleDateFormat("yyyyMMddHHmm"); SimpleDateFormat dateformat = new SimpleDateFormat("yyyyMMddHHmm");
String dateRange = "[" + dateformat.format(startDate) + " TO " String dateRange = "[" + dateformat.format(startDate) + " TO " + dateformat.format(endDate) + "]";
+ dateformat.format(endDate) + "]";
return dateRange; return dateRange;
} }
@ -277,8 +276,7 @@ public class CustomSearchController implements Serializable {
Date endDate = endCalendar.getTime(); Date endDate = endCalendar.getTime();
SimpleDateFormat dateformat = new SimpleDateFormat("yyyyMMddHHmm"); SimpleDateFormat dateformat = new SimpleDateFormat("yyyyMMddHHmm");
String dateRange = "[" + dateformat.format(startDate) + " TO " String dateRange = "[" + dateformat.format(startDate) + " TO " + dateformat.format(endDate) + "]";
+ dateformat.format(endDate) + "]";
return dateRange; return dateRange;
} }

View file

@ -91,8 +91,8 @@ public class DebitorStatistikController implements Serializable {
} }
/** /**
* Gibt eine bookmarkable url zurück. * Gibt eine bookmarkable url zurück. Wir vom Aktualisieren button in der Page
* Wir vom Aktualisieren button in der Page genutzt * genutzt
* *
* @return * @return
*/ */
@ -115,8 +115,7 @@ public class DebitorStatistikController implements Serializable {
return 0; return 0;
} }
try { try {
result = documentService result = documentService.count("(type:workitem OR type:workitemarchive) AND dbtr.number:" + getDbtNr()
.count("(type:workitem OR type:workitemarchive) AND dbtr.number:" + getDbtNr()
+ " AND $workflowgroup:\"Rechnungsausgang\""); + " AND $workflowgroup:\"Rechnungsausgang\"");
} catch (QueryException e) { } catch (QueryException e) {
logger.severe("Failed to get statistic: " + e.getMessage()); logger.severe("Failed to get statistic: " + e.getMessage());
@ -139,8 +138,7 @@ public class DebitorStatistikController implements Serializable {
return 0; return 0;
} }
try { try {
result = documentService result = documentService.count("(type:workitem OR type:workitemarchive) AND dbtr.number:" + getDbtNr()
.count("(type:workitem OR type:workitemarchive) AND dbtr.number:" + getDbtNr()
+ " AND $workflowgroup:\"Rechnungsausgang\" AND ($taskid:[5900 TO 5999])"); + " AND $workflowgroup:\"Rechnungsausgang\" AND ($taskid:[5900 TO 5999])");
} catch (QueryException e) { } catch (QueryException e) {
logger.severe("Failed to get statistic: " + e.getMessage()); logger.severe("Failed to get statistic: " + e.getMessage());
@ -163,8 +161,7 @@ public class DebitorStatistikController implements Serializable {
return 0; return 0;
} }
try { try {
result = documentService result = documentService.count("(type:workitem) AND dbtr.number:" + getDbtNr()
.count("(type:workitem) AND dbtr.number:" + getDbtNr()
+ " AND $workflowgroup:\"Rechnungsausgang\" AND ($taskid:[5000 TO 5099])"); + " AND $workflowgroup:\"Rechnungsausgang\" AND ($taskid:[5000 TO 5099])");
} catch (QueryException e) { } catch (QueryException e) {
logger.severe("Failed to get statistic: " + e.getMessage()); logger.severe("Failed to get statistic: " + e.getMessage());
@ -187,8 +184,7 @@ public class DebitorStatistikController implements Serializable {
return 0; return 0;
} }
try { try {
result = documentService result = documentService.count("(type:workitem) AND dbtr.number:" + getDbtNr()
.count("(type:workitem) AND dbtr.number:" + getDbtNr()
+ " AND $workflowgroup:\"Rechnungsausgang\" AND ($taskid:[5100 TO 5199])"); + " AND $workflowgroup:\"Rechnungsausgang\" AND ($taskid:[5100 TO 5199])");
} catch (QueryException e) { } catch (QueryException e) {
logger.severe("Failed to get statistic: " + e.getMessage()); logger.severe("Failed to get statistic: " + e.getMessage());
@ -211,8 +207,7 @@ public class DebitorStatistikController implements Serializable {
return 0; return 0;
} }
try { try {
result = documentService result = documentService.count("(type:workitem ) AND dbtr.number:" + getDbtNr()
.count("(type:workitem ) AND dbtr.number:" + getDbtNr()
+ " AND $workflowgroup:\"Rechnungsausgang\" AND ($taskid:[5200 TO 5299])"); + " AND $workflowgroup:\"Rechnungsausgang\" AND ($taskid:[5200 TO 5299])");
} catch (QueryException e) { } catch (QueryException e) {
logger.severe("Failed to get statistic: " + e.getMessage()); logger.severe("Failed to get statistic: " + e.getMessage());
@ -235,8 +230,7 @@ public class DebitorStatistikController implements Serializable {
return 0; return 0;
} }
try { try {
result = documentService result = documentService.count("(type:workitem OR type:workitemarchive) AND dbtr.number:" + getDbtNr()
.count("(type:workitem OR type:workitemarchive) AND dbtr.number:" + getDbtNr()
+ " AND $workflowgroup:\"Rechnungsausgang\" AND ($taskid:[5300 TO 5399])"); + " AND $workflowgroup:\"Rechnungsausgang\" AND ($taskid:[5300 TO 5399])");
} catch (QueryException e) { } catch (QueryException e) {
logger.severe("Failed to get statistic: " + e.getMessage()); logger.severe("Failed to get statistic: " + e.getMessage());
@ -311,8 +305,7 @@ public class DebitorStatistikController implements Serializable {
// df.format(to) + "])"; // df.format(to) + "])";
logger.fine("query = " + query); logger.fine("query = " + query);
try { try {
List<ItemCollection> invoices = documentService List<ItemCollection> invoices = documentService.findStubs(query, 9999, 0, "$created", false);
.findStubs(query, 9999, 0, "$created", false);
for (ItemCollection invoice : invoices) { for (ItemCollection invoice : invoices) {
try { try {
@ -435,14 +428,12 @@ public class DebitorStatistikController implements Serializable {
// Wir selektieren alle Zahlungseingänge interessieren usn aber nur für den // Wir selektieren alle Zahlungseingänge interessieren usn aber nur für den
// letzten // letzten
String sQuery = " (type:\"workitem\" OR type:\"workitemarchive\") " + // String sQuery = " (type:\"workitem\" OR type:\"workitemarchive\") " + //
" AND ($workflowgroup:Zahlungseingang) AND ($workitemref:\"" " AND ($workflowgroup:Zahlungseingang) AND ($workitemref:\"" + invoice.getUniqueID() + "\" )";
+ invoice.getUniqueID() + "\" )";
List<ItemCollection> workitems = null; List<ItemCollection> workitems = null;
try { try {
workitems = documentService.findStubs(sQuery, 99, 0, workitems = documentService.findStubs(sQuery, 99, 0, "payment.date", true);
"payment.date", true);
if (workitems.size() > 0) { if (workitems.size() > 0) {
return workitems.get(0).getItemValueDate("payment.date"); return workitems.get(0).getItemValueDate("payment.date");
} }

View file

@ -36,8 +36,8 @@ import jakarta.inject.Inject;
* 2.2.22 - Das Plugin erzeugt auch das Feld _img welches die Images von * 2.2.22 - Das Plugin erzeugt auch das Feld _img welches die Images von
* Sofortüberweisung, Mahnung und Ablehnung anzeigt. * Sofortüberweisung, Mahnung und Ablehnung anzeigt.
* *
* 26.4.25 - Das Plugin setzt nun auch partner.name und partner.id * 26.4.25 - Das Plugin setzt nun auch partner.name und partner.id zusätzlich
* zusätzlich processed es das BP Workitem * processed es das BP Workitem
* *
* @author rsoika * @author rsoika
* @version 1.0 * @version 1.0
@ -403,8 +403,7 @@ public class InvoicePlugin extends AbstractPlugin {
if (result == null || result.size() == 0) { if (result == null || result.size() == 0) {
String message = resourceBundleHandler.findMessage("ERROR_CDTR_INVALID"); String message = resourceBundleHandler.findMessage("ERROR_CDTR_INVALID");
throw new PluginException(InvoicePlugin.class.getName(), ERROR_MISSING_DATA, throw new PluginException(InvoicePlugin.class.getName(), ERROR_MISSING_DATA, message);
message);
} else { } else {
// update cargosoft crediotr name // update cargosoft crediotr name

View file

@ -89,8 +89,7 @@ public class InvoiceService {
ItemCollection configItemCollection = configService.loadConfiguration("AGL_CONFIGURATION"); ItemCollection configItemCollection = configService.loadConfiguration("AGL_CONFIGURATION");
List<String> currencyList = configItemCollection.getItemValue("currency.out"); List<String> currencyList = configItemCollection.getItemValue("currency.out");
if (currencyList == null || currencyList.size() < 1) { if (currencyList == null || currencyList.size() < 1) {
throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), InvoiceService.ERROR_CONFIG,
InvoiceService.ERROR_CONFIG,
"missing Currency configuration - please check parameters"); "missing Currency configuration - please check parameters");
} }
return currencyList; return currencyList;
@ -108,8 +107,7 @@ public class InvoiceService {
ItemCollection configItemCollection = configService.loadConfiguration("AGL_CONFIGURATION"); ItemCollection configItemCollection = configService.loadConfiguration("AGL_CONFIGURATION");
List<String> currencyList = configItemCollection.getItemValue("currency.out"); List<String> currencyList = configItemCollection.getItemValue("currency.out");
if (currencyList == null || currencyList.size() < 1) { if (currencyList == null || currencyList.size() < 1) {
throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), InvoiceService.ERROR_CONFIG,
InvoiceService.ERROR_CONFIG,
"missing Currency configuration - please check parameters"); "missing Currency configuration - please check parameters");
} }
return currencyList.get(0); return currencyList.get(0);
@ -127,16 +125,14 @@ public class InvoiceService {
ItemCollection configItemCollection = configService.loadConfiguration("AGL_CONFIGURATION"); ItemCollection configItemCollection = configService.loadConfiguration("AGL_CONFIGURATION");
List<String> currencyList = configItemCollection.getItemValue("currency.in"); List<String> currencyList = configItemCollection.getItemValue("currency.in");
if (currencyList == null || currencyList.size() < 1) { if (currencyList == null || currencyList.size() < 1) {
throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), InvoiceService.ERROR_CONFIG,
InvoiceService.ERROR_CONFIG,
"missing Currency configuration - please check parameters"); "missing Currency configuration - please check parameters");
} }
return currencyList; return currencyList;
} }
/** /**
* Prüft ob der Vorgang überflällig ist. * Prüft ob der Vorgang überflällig ist. Default ist 21 tage.
* Default ist 21 tage.
*/ */
public boolean isOverdue(Date date, int taskid) { public boolean isOverdue(Date date, int taskid) {
Date now = new Date(); Date now = new Date();

View file

@ -13,9 +13,7 @@ import org.imixs.workflow.ItemCollection;
* Hilfsmethoden für die Berechnung und Validierung von Ein und * Hilfsmethoden für die Berechnung und Validierung von Ein und
* Ausgangsrechnugnen * Ausgangsrechnugnen
* *
* - Rundungs Methode * - Rundungs Methode - Berechnung der Positionsnummern - Explode Childitems
* - Berechnung der Positionsnummern
* - Explode Childitems
* *
* See: * See:
* https://stackoverflow.com/questions/2808535/round-a-double-to-2-decimal-places * https://stackoverflow.com/questions/2808535/round-a-double-to-2-decimal-places
@ -63,9 +61,7 @@ public class InvoiceUtil {
String regexPattern = "^(?=.{1,64}@)[A-Za-z0-9_-]+(\\.[A-Za-z0-9_-]+)*@" String regexPattern = "^(?=.{1,64}@)[A-Za-z0-9_-]+(\\.[A-Za-z0-9_-]+)*@"
+ "[^-][A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(\\.[A-Za-z]{2,})$"; + "[^-][A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(\\.[A-Za-z]{2,})$";
return Pattern.compile(regexPattern) return Pattern.compile(regexPattern).matcher(emailAddress).matches();
.matcher(emailAddress)
.matches();
} }
// @Deprecated // @Deprecated
@ -130,10 +126,9 @@ public class InvoiceUtil {
/** /**
* Diese Methode berechnet das feld 'invoice.positions' welches eine Valueliste * Diese Methode berechnet das feld 'invoice.positions' welches eine Valueliste
* mit allen Positionsnummern aus dem Childliste enthält. * mit allen Positionsnummern aus dem Childliste enthält. Es wird sowohl für
* Es wird sowohl für Ausgangs wie Eingangsrechnungen genutzt. * Ausgangs wie Eingangsrechnungen genutzt. Das feld wird indiziert, so dass
* Das feld wird indiziert, so dass eine gezielte Suche nach positiosnummern * eine gezielte Suche nach positiosnummern möglich ist.
* möglich ist.
* *
* @param workitem * @param workitem
*/ */

View file

@ -74,8 +74,7 @@ public class OPListController implements Serializable {
* Diese Methode wird für Zahlungseingänge und Mahnläufe verwendet. * Diese Methode wird für Zahlungseingänge und Mahnläufe verwendet.
* <p> * <p>
* Die Methode liefert die Rechnungen zur gegebenen dbtrNumber. Die Methode * Die Methode liefert die Rechnungen zur gegebenen dbtrNumber. Die Methode
* nutzt * nutzt einen lokalen cache um die Zugriffe zu beschleunigen.
* einen lokalen cache um die Zugriffe zu beschleunigen.
* <p> * <p>
* Befindet sich ein Zahlungseingang in Erstellung (Task=1000) werden alle * Befindet sich ein Zahlungseingang in Erstellung (Task=1000) werden alle
* offenen Rechnungen geholt. * offenen Rechnungen geholt.
@ -185,8 +184,7 @@ public class OPListController implements Serializable {
// einer Rechnung mit dem Event 100 (Mahnlauf erstellen) kommen, oder // einer Rechnung mit dem Event 100 (Mahnlauf erstellen) kommen, oder
// wenn der Mahnlauf von außen gerate eben neu erzeugt wurde // wenn der Mahnlauf von außen gerate eben neu erzeugt wurde
if (workflowController.getWorkitem().getItemValueInteger("$lastevent") == 100 if (workflowController.getWorkitem().getItemValueInteger("$lastevent") == 100
|| || workflowController.getWorkitem().getItemValueInteger("$lastevent") == 0) {
workflowController.getWorkitem().getItemValueInteger("$lastevent") == 0) {
List<String> intialSelection = new ArrayList<String>(); List<String> intialSelection = new ArrayList<String>();
for (ItemCollection invoice : invoiceList) { for (ItemCollection invoice : invoiceList) {
// ist die Rechnung fällig? // ist die Rechnung fällig?
@ -323,9 +321,9 @@ public class OPListController implements Serializable {
} }
/** /**
* Berechnet die Summer aller Rechnungen zu einer Währung. * Berechnet die Summer aller Rechnungen zu einer Währung. Es werden nur
* Es werden nur Rechnungen berücksichtigt, deren Haupt Währung der übergebenen * Rechnungen berücksichtigt, deren Haupt Währung der übergebenen Währung
* Währung entspricht. * entspricht.
* *
* @param currency * @param currency
* @return * @return

View file

@ -95,9 +95,7 @@ public class OPListExportAdapter extends POIFindReplaceAdapter {
} }
// read the template options // read the template options
ItemCollection evalItemCollection = workflowService.evalWorkflowResult(event, "opliste", ItemCollection evalItemCollection = workflowService.evalWorkflowResult(event, "opliste", document, false);
document,
false);
if (evalItemCollection == null || !evalItemCollection.hasItem("excel-export")) { if (evalItemCollection == null || !evalItemCollection.hasItem("excel-export")) {
throw new PluginException(OPListExportAdapter.class.getSimpleName(), CONFIG_ERROR, throw new PluginException(OPListExportAdapter.class.getSimpleName(), CONFIG_ERROR,
"missing opliste configuration in model event - please check model configuration"); "missing opliste configuration in model event - please check model configuration");
@ -132,13 +130,11 @@ public class OPListExportAdapter extends POIFindReplaceAdapter {
doc.write(byteArrayOutputStream); doc.write(byteArrayOutputStream);
byte[] newContent = byteArrayOutputStream.toByteArray(); byte[] newContent = byteArrayOutputStream.toByteArray();
FileData fileDataNew = new FileData(fileData.getName(), newContent, fileData.getContentType(), FileData fileDataNew = new FileData(fileData.getName(), newContent, fileData.getContentType(), null);
null);
document.addFileData(fileDataNew); document.addFileData(fileDataNew);
logger.finest("......new document added"); logger.finest("......new document added");
} catch (IOException | QueryException e) { } catch (IOException | QueryException e) {
throw new PluginException(OPListExportAdapter.class.getSimpleName(), throw new PluginException(OPListExportAdapter.class.getSimpleName(), InvoiceService.ERROR_CONFIG,
InvoiceService.ERROR_CONFIG,
"failed to update opliste: " + e.getMessage()); "failed to update opliste: " + e.getMessage());
} finally { } finally {
if (doc != null) { if (doc != null) {
@ -157,8 +153,7 @@ public class OPListExportAdapter extends POIFindReplaceAdapter {
} catch ( } catch (
PluginException e) { PluginException e) {
throw new PluginException(OPListExportAdapter.class.getSimpleName(), throw new PluginException(OPListExportAdapter.class.getSimpleName(), InvoiceService.ERROR_CONFIG,
InvoiceService.ERROR_CONFIG,
"failed to update op-liste: " + e.getMessage()); "failed to update op-liste: " + e.getMessage());
} }
logger.fine("... completed!"); logger.fine("... completed!");
@ -211,8 +206,7 @@ public class OPListExportAdapter extends POIFindReplaceAdapter {
* @throws QueryException * @throws QueryException
*/ */
private void insertInvoiceRows(XSSFSheet sheet, ItemCollection document, String fileName, boolean filter, private void insertInvoiceRows(XSSFSheet sheet, ItemCollection document, String fileName, boolean filter,
List<String> currencyList) List<String> currencyList) throws PluginException, QueryException {
throws PluginException, QueryException {
String spaceID = document.getItemValueString("space.ref"); String spaceID = document.getItemValueString("space.ref");
logger.fine("... space.ref=" + spaceID + " ...grouping invoices by spaceid...."); logger.fine("... space.ref=" + spaceID + " ...grouping invoices by spaceid....");
@ -349,12 +343,10 @@ public class OPListExportAdapter extends POIFindReplaceAdapter {
* @throws PluginException * @throws PluginException
*/ */
private FileData appendExcelTemplate(ItemCollection document, String textblockRef, String template, private FileData appendExcelTemplate(ItemCollection document, String textblockRef, String template,
String targetName) String targetName) throws PluginException {
throws PluginException {
if ((template == null || template.isEmpty()) || (textblockRef == null || textblockRef.isEmpty())) { if ((template == null || template.isEmpty()) || (textblockRef == null || textblockRef.isEmpty())) {
throw new PluginException(OPListExportAdapter.class.getSimpleName(), throw new PluginException(OPListExportAdapter.class.getSimpleName(), InvoiceService.ERROR_CONFIG,
InvoiceService.ERROR_CONFIG,
"invalid POI configuration in model event - textblock/template reference not defined!"); "invalid POI configuration in model event - textblock/template reference not defined!");
} }
@ -363,10 +355,8 @@ public class OPListExportAdapter extends POIFindReplaceAdapter {
// do we found the document? // do we found the document?
if (fileData == null) { if (fileData == null) {
throw new PluginException(OPListExportAdapter.class.getSimpleName(), throw new PluginException(OPListExportAdapter.class.getSimpleName(), InvoiceService.ERROR_CONFIG,
InvoiceService.ERROR_CONFIG, "invalid POI configuration in model event - template=" + template + " not found!");
"invalid POI configuration in model event - template=" + template
+ " not found!");
} }
fileData.setName(targetName); fileData.setName(targetName);
// append document // append document

View file

@ -119,9 +119,7 @@ public class OPListExportAdapterByWeek extends POIFindReplaceAdapter {
Collections.sort(spaces, new ItemCollectionComparator("space.name", true)); Collections.sort(spaces, new ItemCollectionComparator("space.name", true));
// read the template options // read the template options
ItemCollection evalItemCollection = workflowService.evalWorkflowResult(event, "opliste", ItemCollection evalItemCollection = workflowService.evalWorkflowResult(event, "opliste", document, false);
document,
false);
if (evalItemCollection == null || !evalItemCollection.hasItem("excel-export")) { if (evalItemCollection == null || !evalItemCollection.hasItem("excel-export")) {
throw new PluginException(OPListExportAdapter.class.getSimpleName(), CONFIG_ERROR, throw new PluginException(OPListExportAdapter.class.getSimpleName(), CONFIG_ERROR,
"missing opliste configuration in model event - please check model configuration"); "missing opliste configuration in model event - please check model configuration");
@ -155,14 +153,12 @@ public class OPListExportAdapterByWeek extends POIFindReplaceAdapter {
doc.write(byteArrayOutputStream); doc.write(byteArrayOutputStream);
byte[] newContent = byteArrayOutputStream.toByteArray(); byte[] newContent = byteArrayOutputStream.toByteArray();
FileData fileDataNew = new FileData(fileData.getName(), newContent, fileData.getContentType(), FileData fileDataNew = new FileData(fileData.getName(), newContent, fileData.getContentType(), null);
null);
document.addFileData(fileDataNew); document.addFileData(fileDataNew);
logger.finest("......new document added"); logger.finest("......new document added");
} catch (IOException | QueryException e) { } catch (IOException | QueryException e) {
throw new PluginException(OPListExportAdapter.class.getSimpleName(), throw new PluginException(OPListExportAdapter.class.getSimpleName(), InvoiceService.ERROR_CONFIG,
InvoiceService.ERROR_CONFIG,
"failed to update opliste: " + e.getMessage()); "failed to update opliste: " + e.getMessage());
} finally { } finally {
if (doc != null) { if (doc != null) {
@ -180,8 +176,7 @@ public class OPListExportAdapterByWeek extends POIFindReplaceAdapter {
} catch ( } catch (
PluginException e) { PluginException e) {
throw new PluginException(OPListExportAdapter.class.getSimpleName(), throw new PluginException(OPListExportAdapter.class.getSimpleName(), InvoiceService.ERROR_CONFIG,
InvoiceService.ERROR_CONFIG,
"failed to update op-liste: " + e.getMessage()); "failed to update op-liste: " + e.getMessage());
} }
logger.fine("... completed!"); logger.fine("... completed!");
@ -254,8 +249,7 @@ public class OPListExportAdapterByWeek extends POIFindReplaceAdapter {
* @throws QueryException * @throws QueryException
*/ */
private void insertInvoiceRows(XSSFWorkbook doc, XSSFSheet sheet, List<String> currencyList, private void insertInvoiceRows(XSSFWorkbook doc, XSSFSheet sheet, List<String> currencyList,
List<ItemCollection> spaces) List<ItemCollection> spaces) throws PluginException, QueryException {
throws PluginException, QueryException {
List<String> spaceNames = new ArrayList<String>(); List<String> spaceNames = new ArrayList<String>();
@ -285,8 +279,8 @@ public class OPListExportAdapterByWeek extends POIFindReplaceAdapter {
} }
/* /*
* --- Phase 1 ------------------------------------------------------- * --- Phase 1 ------------------------------------------------------- Im
* Im folgenden gruppieren wir die Rechnungen nach Woche * folgenden gruppieren wir die Rechnungen nach Woche
*/ */
// XSSFCell cellKWTotalReference = referenceRowTotal.getCell(14); // XSSFCell cellKWTotalReference = referenceRowTotal.getCell(14);
@ -306,10 +300,10 @@ public class OPListExportAdapterByWeek extends POIFindReplaceAdapter {
totalDataMapUeberFaellig.put("TOTAL", new TotalInvoiceData("TOTAL")); totalDataMapUeberFaellig.put("TOTAL", new TotalInvoiceData("TOTAL"));
/* /*
* --- Phase 3 ------------------------------------------------------- * --- Phase 3 ------------------------------------------------------- now build
* now build a new row for each week and print out the collected invoice data * a new row for each week and print out the collected invoice data first
* first iterate over all objects and collect all known weeks and sort them into * iterate over all objects and collect all known weeks and sort them into a
* a ordered list.... * ordered list....
*/ */
List<String> sortedWeekList = new ArrayList<String>(); List<String> sortedWeekList = new ArrayList<String>();
Iterator<Map.Entry<String, WeekInvoiceData>> iterator = weekDataMap.entrySet().iterator(); Iterator<Map.Entry<String, WeekInvoiceData>> iterator = weekDataMap.entrySet().iterator();
@ -323,9 +317,9 @@ public class OPListExportAdapterByWeek extends POIFindReplaceAdapter {
Collections.sort(sortedWeekList); Collections.sort(sortedWeekList);
/* /*
* --- Phase 4 ------------------------------------------------------- * --- Phase 4 ------------------------------------------------------- Nun
* Nun müssen wir so viele Zeilen nach unten schieben wie wir gleich einfügen. * müssen wir so viele Zeilen nach unten schieben wie wir gleich einfügen. Dazu
* Dazu müssen wir kurz einen Probelauf machen um die geplante Anzahl zu * müssen wir kurz einen Probelauf machen um die geplante Anzahl zu
* errechnen.... * errechnen....
*/ */
int rowPos = 11; int rowPos = 11;
@ -333,8 +327,8 @@ public class OPListExportAdapterByWeek extends POIFindReplaceAdapter {
sheet.shiftRows(rowPos + 1, 2999, insertCount, true, true); sheet.shiftRows(rowPos + 1, 2999, insertCount, true, true);
/* /*
* --- Phase 5 ------------------------------------------------------- * --- Phase 5 ------------------------------------------------------- Jetzt
* Jetzt Zahlen Zeile für Zeile einfügen.... * Zahlen Zeile für Zeile einfügen....
*/ */
boolean ueberFaellig = true; boolean ueberFaellig = true;
for (String week : sortedWeekList) { for (String week : sortedWeekList) {
@ -399,9 +393,8 @@ public class OPListExportAdapterByWeek extends POIFindReplaceAdapter {
} }
/* /*
* --- Phase 6 ------------------------------------------------------- * --- Phase 6 ------------------------------------------------------- now lets
* now lets update the totals... * update the totals... Überfällig
* Überfällig
*/ */
int _rowNo = sortedWeekList.size() + 14; int _rowNo = sortedWeekList.size() + 14;
XSSFRow rowUeberFaellig = sheet.getRow(_rowNo); XSSFRow rowUeberFaellig = sheet.getRow(_rowNo);
@ -462,14 +455,12 @@ public class OPListExportAdapterByWeek extends POIFindReplaceAdapter {
* Diese Methode gruppiert eine Rechnungsliste nach Kalenderwoche * Diese Methode gruppiert eine Rechnungsliste nach Kalenderwoche
* *
* @param spaceID - Space Ref to select a list of invoices associated with * @param spaceID - Space Ref to select a list of invoices associated with
* a * a space
* space
* @param weekDataCache - a local cache storing all invoices by week * @param weekDataCache - a local cache storing all invoices by week
* *
* *
*/ */
private void groupInvoicesByWeek(String spaceID, String spaceName, private void groupInvoicesByWeek(String spaceID, String spaceName, Map<String, WeekInvoiceData> weekDataCache) {
Map<String, WeekInvoiceData> weekDataCache) {
logger.info("...group invoices for " + spaceName + "/" + spaceID); logger.info("...group invoices for " + spaceName + "/" + spaceID);
try { try {
@ -482,9 +473,7 @@ public class OPListExportAdapterByWeek extends POIFindReplaceAdapter {
// compute Week // compute Week
Date dueDate = invoice.getItemValueDate("invoice.duedate"); Date dueDate = invoice.getItemValueDate("invoice.duedate");
LocalDate localDate = dueDate.toInstant() LocalDate localDate = dueDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
.atZone(ZoneId.systemDefault())
.toLocalDate();
int weekNumber = localDate.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR); int weekNumber = localDate.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR);
int weekBasedYear = localDate.get(IsoFields.WEEK_BASED_YEAR); int weekBasedYear = localDate.get(IsoFields.WEEK_BASED_YEAR);
String weekCategory = weekBasedYear + "/" + String.format("%02d", weekNumber); String weekCategory = weekBasedYear + "/" + String.format("%02d", weekNumber);
@ -526,12 +515,10 @@ public class OPListExportAdapterByWeek extends POIFindReplaceAdapter {
* @throws PluginException * @throws PluginException
*/ */
private FileData appendExcelTemplate(ItemCollection document, String textblockRef, String template, private FileData appendExcelTemplate(ItemCollection document, String textblockRef, String template,
String targetName) String targetName) throws PluginException {
throws PluginException {
if ((template == null || template.isEmpty()) || (textblockRef == null || textblockRef.isEmpty())) { if ((template == null || template.isEmpty()) || (textblockRef == null || textblockRef.isEmpty())) {
throw new PluginException(OPListExportAdapter.class.getSimpleName(), throw new PluginException(OPListExportAdapter.class.getSimpleName(), InvoiceService.ERROR_CONFIG,
InvoiceService.ERROR_CONFIG,
"invalid POI configuration in model event - textblock/template reference not defined!"); "invalid POI configuration in model event - textblock/template reference not defined!");
} }
@ -540,10 +527,8 @@ public class OPListExportAdapterByWeek extends POIFindReplaceAdapter {
// do we found the document? // do we found the document?
if (fileData == null) { if (fileData == null) {
throw new PluginException(OPListExportAdapter.class.getSimpleName(), throw new PluginException(OPListExportAdapter.class.getSimpleName(), InvoiceService.ERROR_CONFIG,
InvoiceService.ERROR_CONFIG, "invalid POI configuration in model event - template=" + template + " not found!");
"invalid POI configuration in model event - template=" + template
+ " not found!");
} }
fileData.setName(targetName); fileData.setName(targetName);
// append document // append document
@ -555,8 +540,8 @@ public class OPListExportAdapterByWeek extends POIFindReplaceAdapter {
} }
/** /**
* Data Element for invoice totals per week and space. * Data Element for invoice totals per week and space. The object holds multiple
* The object holds multiple currencies. * currencies.
*/ */
class WeekInvoiceData { class WeekInvoiceData {
@ -601,8 +586,8 @@ class WeekInvoiceData {
} }
/** /**
* Data Element for invoice totals per space. * Data Element for invoice totals per space. The object holds multiple
* The object holds multiple currencies. * currencies.
*/ */
class TotalInvoiceData { class TotalInvoiceData {

View file

@ -34,8 +34,7 @@ import jakarta.inject.Named;
/** /**
* The OPListExportController is used to configure the OpListeExporterService. * The OPListExportController is used to configure the OpListeExporterService.
* This service is * This service is used to export workitems.
* used to export workitems.
* <p> * <p>
* The Controller creates a configuration entity "type=configuration; * The Controller creates a configuration entity "type=configuration;
* txtname=OPLIST_EXPORT_CONFIGURATION". * txtname=OPLIST_EXPORT_CONFIGURATION".

View file

@ -139,8 +139,8 @@ public class OPListExportScheduler implements Scheduler {
List<ItemCollection> invoices = new ArrayList<ItemCollection>(); List<ItemCollection> invoices = new ArrayList<ItemCollection>();
try { try {
invoices = documentService.find( invoices = documentService.find(
"$modelversion:rechnungsausgang-* AND type:workitem AND $uniqueidref:" + space.getUniqueID(), "$modelversion:rechnungsausgang-* AND type:workitem AND $uniqueidref:" + space.getUniqueID(), 999,
999, 0); 0);
} catch (QueryException e) { } catch (QueryException e) {
logger.warning("Failed to compute invoice list: " + e.getMessage()); logger.warning("Failed to compute invoice list: " + e.getMessage());
e.printStackTrace(); e.printStackTrace();

View file

@ -28,8 +28,8 @@ import jakarta.inject.Inject;
* *
* Bei Sachrechnungen * Bei Sachrechnungen
* *
* sequencenumber + "_" + invoice.number.stripped + ".pdf" * sequencenumber + "_" + invoice.number.stripped + ".pdf" wird das
* wird das Rechnungsdatum verwendet um die Buchungsperiode zu berechnen. * Rechnungsdatum verwendet um die Buchungsperiode zu berechnen.
* *
* *
* *
@ -59,8 +59,7 @@ public class RenameFilenamesAdapter implements SignalAdapter {
for (FileData fileData : fileDataSet) { for (FileData fileData : fileDataSet) {
String fileName = fileData.getName(); String fileName = fileData.getName();
// only PDF files and X-Rechnung // only PDF files and X-Rechnung
if ((fileName.toLowerCase().endsWith(".pdf") if ((fileName.toLowerCase().endsWith(".pdf") || fileName.toLowerCase().endsWith(".xml"))) {
|| fileName.toLowerCase().endsWith(".xml"))) {
// Build new Filename // Build new Filename
String newFileName = buildFileName(fileName, workitem, fileNameCounter); String newFileName = buildFileName(fileName, workitem, fileNameCounter);

View file

@ -14,9 +14,9 @@ import jakarta.inject.Inject;
import jakarta.inject.Named; import jakarta.inject.Named;
/** /**
* Der SEPAExportControllerAGL dient dazu die IBAN/BIC einstellungen in * Der SEPAExportControllerAGL dient dazu die IBAN/BIC einstellungen in einem
* einem SEPA Export zu aktualiseren. Er wird von der Form * SEPA Export zu aktualiseren. Er wird von der Form sepa_export.xhtml aus per
* sepa_export.xhtml aus per Ajax angesteuert. * Ajax angesteuert.
* *
* @author rsoika * @author rsoika
* *

View file

@ -12,8 +12,8 @@ import jakarta.inject.Inject;
import jakarta.inject.Named; import jakarta.inject.Named;
/** /**
* Der SteuerListController dient dazu in einem Stuerbescheid * Der SteuerListController dient dazu in einem Stuerbescheid die einzelnen
* die einzelnen Zeilen auszugeben. * Zeilen auszugeben.
* *
* @author rsoika * @author rsoika
* *

View file

@ -17,11 +17,8 @@ import jakarta.inject.Inject;
* Das SteuerbescheidPlugin prüft ob * Das SteuerbescheidPlugin prüft ob
* *
* - die ATC Nummer aus dem Steuerbescheid in einer Ausgangsrechnung gefunden * - die ATC Nummer aus dem Steuerbescheid in einer Ausgangsrechnung gefunden
* wird ($taskID<5990) * wird ($taskID<5990) - invoice.atc.number - der Betrag "invoice.total.net" -
* - invoice.atc.number * die Währung??? - die Positionsnummer ???
* - der Betrag "invoice.total.net"
* - die Währung???
* - die Positionsnummer ???
* *
* *
* @author rsoika * @author rsoika
@ -77,8 +74,8 @@ public class SteuerbescheidPlugin extends AbstractPlugin {
} }
/** /**
* Aktualisiert die EUST/ZOLL Fälligkeiten in den Detailtabellen * Aktualisiert die EUST/ZOLL Fälligkeiten in den Detailtabellen Die Daten
* Die Daten kommen von der KI und werden auf die Positionsnummern gemappt. * kommen von der KI und werden auf die Positionsnummern gemappt.
* *
*/ */
protected void updateFaelligkeit(ItemCollection workitem) { protected void updateFaelligkeit(ItemCollection workitem) {
@ -164,8 +161,7 @@ public class SteuerbescheidPlugin extends AbstractPlugin {
protected List<ItemCollection> findInvoices(String atcNumber) { protected List<ItemCollection> findInvoices(String atcNumber) {
List<ItemCollection> result = new ArrayList<ItemCollection>(); List<ItemCollection> result = new ArrayList<ItemCollection>();
String query = "(type:workitem OR type:workitemarchive) AND ($taskid:[5000 TO 5990])" String query = "(type:workitem OR type:workitemarchive) AND ($taskid:[5000 TO 5990])"
+ " AND invoice.atc.number:\"" + atcNumber + "\" " + " AND invoice.atc.number:\"" + atcNumber + "\" " + " AND $workflowgroup:\"Rechnungsausgang\" ";
+ " AND $workflowgroup:\"Rechnungsausgang\" ";
try { try {
result = documentService.find(query, 999, 0, "$created", false); result = documentService.find(query, 999, 0, "$created", false);
return result; return result;

View file

@ -127,13 +127,11 @@ public class ZahlungseingangSaldoAdapter implements SignalAdapter {
String mainCurrency = invoiceService.getCurrenciesOutMain(); String mainCurrency = invoiceService.getCurrenciesOutMain();
if ((!invoiceCurrency.equals(paymentCurrency)) if ((!invoiceCurrency.equals(paymentCurrency))
&& (!invoiceCurrency.equals(mainCurrency) && && (!invoiceCurrency.equals(mainCurrency) && !paymentCurrency.equals(mainCurrency))) {
!paymentCurrency.equals(mainCurrency))) {
String message = resourceBundleHandler.findMessage("ERROR_PAYMENT4"); String message = resourceBundleHandler.findMessage("ERROR_PAYMENT4");
message = message.replace("{1}", mainCurrency); message = message.replace("{1}", mainCurrency);
throw new PluginException(PluginException.class.getName(), throw new PluginException(PluginException.class.getName(),
ZahlungseingangService.ERROR_PAYMENT_BOOKING_FAILED, ZahlungseingangService.ERROR_PAYMENT_BOOKING_FAILED, message);
message);
} }
if (!invoiceCurrency.equals(paymentCurrency)) { if (!invoiceCurrency.equals(paymentCurrency)) {
@ -142,8 +140,7 @@ public class ZahlungseingangSaldoAdapter implements SignalAdapter {
message = message.replace("{1}", paymentCurrency); message = message.replace("{1}", paymentCurrency);
message = message.replace("{2}", invoiceNumber); message = message.replace("{2}", invoiceNumber);
throw new PluginException(PluginException.class.getName(), throw new PluginException(PluginException.class.getName(),
ZahlungseingangService.ERROR_PAYMENT_BOOKING_FAILED, ZahlungseingangService.ERROR_PAYMENT_BOOKING_FAILED, message);
message);
} }
} }
@ -151,8 +148,7 @@ public class ZahlungseingangSaldoAdapter implements SignalAdapter {
* Falls die Währung der Rechnung von der Währung der Zahlung abweicht, dann * Falls die Währung der Rechnung von der Währung der Zahlung abweicht, dann
* rechnen wir den PaymentAmmout mit der Rate aus. * rechnen wir den PaymentAmmout mit der Rate aus.
*/ */
if ("EUR".equals(paymentCurrency) && if ("EUR".equals(paymentCurrency) && !invoiceCurrency.equals(paymentCurrency) && invoiceRate != 0) {
!invoiceCurrency.equals(paymentCurrency) && invoiceRate != 0) {
invoicePaymentAmount = invoicePaymentAmount * invoiceRate; invoicePaymentAmount = invoicePaymentAmount * invoiceRate;
// runde auf 2 Stellen // runde auf 2 Stellen
invoicePaymentAmount = (float) InvoiceUtil.round(invoicePaymentAmount); invoicePaymentAmount = (float) InvoiceUtil.round(invoicePaymentAmount);

View file

@ -15,8 +15,8 @@ import jakarta.enterprise.event.Observes;
import jakarta.inject.Inject; import jakarta.inject.Inject;
/** /**
* The PromptExampleAdapterXML is a CDI bean that creates XML examples based * The PromptExampleAdapterXML is a CDI bean that creates XML examples based on
* on the last 3 invoices. * the last 3 invoices.
* *
* The adapter only runs if the prompt template contains * The adapter only runs if the prompt template contains
* *
@ -79,8 +79,7 @@ public class PromptExampleAdapterXML {
if (invoices != null && invoices.size() > 0) { if (invoices != null && invoices.size() > 0) {
// Build an example for each invoice // Build an example for each invoice
for (ItemCollection invoice : invoices) { for (ItemCollection invoice : invoices) {
promptExamples = promptExamples + buildExampleDE(invoice, exampleHeaderData, promptExamples = promptExamples + buildExampleDE(invoice, exampleHeaderData, exampleHeaderXML);
exampleHeaderXML);
} }
} }
prompt = prompt.replace("<<EXAMPLES_XML>>", "" + promptExamples); prompt = prompt.replace("<<EXAMPLES_XML>>", "" + promptExamples);
@ -126,13 +125,11 @@ public class PromptExampleAdapterXML {
example.append( example.append(
" <invoice.number>" + invoice.getItemValueString("invoice.number") + "</invoice.number>\n"); " <invoice.number>" + invoice.getItemValueString("invoice.number") + "</invoice.number>\n");
if (invoice.getItemValueDate("invoice.date") != null) { if (invoice.getItemValueDate("invoice.date") != null) {
example.append( example.append(" <invoice.date>" + dateFormat.format(invoice.getItemValueDate("invoice.date"))
" <invoice.date>" + dateFormat.format(invoice.getItemValueDate("invoice.date"))
+ "</invoice.date>\n"); + "</invoice.date>\n");
} }
if (invoice.getItemValueDate("invoice.duedate") != null) { if (invoice.getItemValueDate("invoice.duedate") != null) {
example.append( example.append(" <invoice.duedate>" + dateFormat.format(invoice.getItemValueDate("invoice.duedate"))
" <invoice.duedate>" + dateFormat.format(invoice.getItemValueDate("invoice.duedate"))
+ "</invoice.duedate>\n"); + "</invoice.duedate>\n");
} }
example.append(" <invoice.total type=\"double\">" + invoice.getItemValueDouble("invoice.total") example.append(" <invoice.total type=\"double\">" + invoice.getItemValueDouble("invoice.total")
@ -154,8 +151,7 @@ public class PromptExampleAdapterXML {
List<ItemCollection> result = new ArrayList<>(); List<ItemCollection> result = new ArrayList<>();
String searchTerm = "(type:workitemarchive) AND ($modelversion:rechnungseingang*) AND ($taskid:5900) AND NOT ($uniqueid:" String searchTerm = "(type:workitemarchive) AND ($modelversion:rechnungseingang*) AND ($taskid:5900) AND NOT ($uniqueid:"
+ currentID + ") AND (document.company:\"" + currentID + ") AND (document.company:\"" + companyName + "\")";
+ companyName + "\")";
try { try {
result = documentService.find(searchTerm, 3, 0, "$modified", true); result = documentService.find(searchTerm, 3, 0, "$modified", true);
} catch (QueryException e) { } catch (QueryException e) {

View file

@ -145,8 +145,7 @@ public class CargosoftMigrationRestService implements Serializable {
isRunning = true; isRunning = true;
int totalCount = documentService.count(query); int totalCount = documentService.count(query);
log("│   ├── found " + totalCount + " cargosoft entries", log("│   ├── found " + totalCount + " cargosoft entries", messageBuffer);
messageBuffer);
// Berechne Anzahl der benötigten Pages // Berechne Anzahl der benötigten Pages
int totalPages = (int) Math.ceil((double) totalCount / batchSize); int totalPages = (int) Math.ceil((double) totalCount / batchSize);
@ -169,8 +168,8 @@ public class CargosoftMigrationRestService implements Serializable {
} }
// Fortschritt loggen // Fortschritt loggen
log("│ ├── Processed page " + (pageIndex + 1) + " of " + totalPages + log("│ ├── Processed page " + (pageIndex + 1) + " of " + totalPages + " (" + totalObjects
" (" + totalObjects + " objects read, " + syncs + " total syncs)", messageBuffer); + " objects read, " + syncs + " total syncs)", messageBuffer);
// Optional: Kurze Pause nach jedem 5. Batch // Optional: Kurze Pause nach jedem 5. Batch
try { try {
Thread.sleep(100); Thread.sleep(100);
@ -185,8 +184,8 @@ public class CargosoftMigrationRestService implements Serializable {
} }
long duration = System.currentTimeMillis() - l; long duration = System.currentTimeMillis() - l;
double objectsPerSecond = totalObjects / (duration / 1000.0); double objectsPerSecond = totalObjects / (duration / 1000.0);
log("├── Successfully " + syncs + " business partner objects synced in " + log("├── Successfully " + syncs + " business partner objects synced in " + duration + "ms ("
duration + "ms (" + String.format("%.1f", objectsPerSecond) + " objects/sec)", messageBuffer); + String.format("%.1f", objectsPerSecond) + " objects/sec)", messageBuffer);
isRunning = false; isRunning = false;
return messageBuffer.toString(); return messageBuffer.toString();
@ -273,8 +272,8 @@ public class CargosoftMigrationRestService implements Serializable {
// Fortschritt loggen // Fortschritt loggen
iterations++; iterations++;
log("│ ├── Processed page " + (iterations) + " of " + totalPages + log("│ ├── Processed page " + (iterations) + " of " + totalPages + " (" + deletions + " total deletions)",
" (" + deletions + " total deletions)", messageBuffer); messageBuffer);
// Optional: Kurze Pause nach jedem 5. Batch // Optional: Kurze Pause nach jedem 5. Batch
try { try {
@ -290,8 +289,8 @@ public class CargosoftMigrationRestService implements Serializable {
} }
long duration = System.currentTimeMillis() - l; long duration = System.currentTimeMillis() - l;
double objectsPerSecond = totalObjects / (duration / 1000.0); double objectsPerSecond = totalObjects / (duration / 1000.0);
log("├── Successfully " + deletions + " business partner objects deleted in " + log("├── Successfully " + deletions + " business partner objects deleted in " + duration + "ms ("
duration + "ms (" + String.format("%.1f", objectsPerSecond) + " objects/sec)", messageBuffer); + String.format("%.1f", objectsPerSecond) + " objects/sec)", messageBuffer);
isRunning = false; isRunning = false;
return messageBuffer.toString(); return messageBuffer.toString();
@ -373,8 +372,8 @@ public class CargosoftMigrationRestService implements Serializable {
// Fortschritt loggen // Fortschritt loggen
// Fortschritt loggen // Fortschritt loggen
log("│ ├── Processed page " + (pageIndex + 1) + " of " + totalPages + log("│ ├── Processed page " + (pageIndex + 1) + " of " + totalPages + " (" + deletions
" (" + deletions + " total deletions)", messageBuffer); + " total deletions)", messageBuffer);
// Optional: Kurze Pause nach jedem 5. Batch // Optional: Kurze Pause nach jedem 5. Batch
try { try {
Thread.sleep(100); Thread.sleep(100);
@ -389,8 +388,8 @@ public class CargosoftMigrationRestService implements Serializable {
} }
long duration = System.currentTimeMillis() - l; long duration = System.currentTimeMillis() - l;
double objectsPerSecond = totalObjects / (duration / 1000.0); double objectsPerSecond = totalObjects / (duration / 1000.0);
log("├── Successfully " + deletions + " duplicated cargosoft objects deleted in " + log("├── Successfully " + deletions + " duplicated cargosoft objects deleted in " + duration + "ms ("
duration + "ms (" + String.format("%.1f", objectsPerSecond) + " objects/sec)", messageBuffer); + String.format("%.1f", objectsPerSecond) + " objects/sec)", messageBuffer);
isRunning = false; isRunning = false;
return messageBuffer.toString(); return messageBuffer.toString();

View file

@ -139,9 +139,7 @@ public class DataRestService {
protected Client createClient(String serviceAPI, String sessionID) { protected Client createClient(String serviceAPI, String sessionID) {
// Create a Cookie object with a name and a value // Create a Cookie object with a name and a value
Cookie cookie = new Cookie.Builder("JSESSIONID") Cookie cookie = new Cookie.Builder("JSESSIONID").value(sessionID).path("/")
.value(sessionID)
.path("/")
// .domain("domain.com") // .domain("domain.com")
.build(); .build();

View file

@ -132,8 +132,7 @@ public class ExcelExportController implements Serializable {
// query Sachprüfung // query Sachprüfung
if (!filter.getItemValueString("space.ref").isEmpty() if (!filter.getItemValueString("space.ref").isEmpty() && !"-".equals(filter.getItemValueString("space.ref"))) {
&& !"-".equals(filter.getItemValueString("space.ref"))) {
query += " AND ($uniqueidref:" + filter.getItemValueString("space.ref") + ")"; query += " AND ($uniqueidref:" + filter.getItemValueString("space.ref") + ")";
} }
if (workflowController.getWorkitem() != null if (workflowController.getWorkitem() != null

View file

@ -17,8 +17,8 @@ import jakarta.inject.Inject;
/** /**
* Der InkassoAppendAdapter verknüpft eine Rechnung mit einem Inkasso Workflow * Der InkassoAppendAdapter verknüpft eine Rechnung mit einem Inkasso Workflow
* zum aktuellen Debitor. Existiert aktuell kein Inkasso für diesen * zum aktuellen Debitor. Existiert aktuell kein Inkasso für diesen Debitor
* Debitor erzeugt der Adapter automatisch eine neue Prozessinstanz. * erzeugt der Adapter automatisch eine neue Prozessinstanz.
* <p> * <p>
* Wurde die Rechnung bereits einem Inkasso zugeordnet passiert nichts. * Wurde die Rechnung bereits einem Inkasso zugeordnet passiert nichts.
* *
@ -41,8 +41,8 @@ public class InkassoAppendAdapter implements SignalAdapter {
WorkflowService workflowService; WorkflowService workflowService;
/** /**
* This method finds or create the Inkasso and adds a reference * This method finds or create the Inkasso and adds a reference ($workitemref)
* ($workitemref) to the current invoice. * to the current invoice.
* *
* @throws PluginException * @throws PluginException
*/ */
@ -59,8 +59,8 @@ public class InkassoAppendAdapter implements SignalAdapter {
/** /**
* Diese method hängt eine referenz der aktuellen Rechnung an den Inkasso * Diese method hängt eine referenz der aktuellen Rechnung an den Inkasso
* Workflow an. Gibt es noch keinen, wird einer erzeugt. * Workflow an. Gibt es noch keinen, wird einer erzeugt. Die method liefert das
* Die method liefert das Inkasso Workitem zurück. * Inkasso Workitem zurück.
* *
* @param document * @param document
* @throws PluginException * @throws PluginException

View file

@ -15,8 +15,8 @@ import jakarta.inject.Inject;
import jakarta.inject.Named; import jakarta.inject.Named;
/** /**
* Der InkassoController dient dazu eine bereits zugewiesene Rechnung * Der InkassoController dient dazu eine bereits zugewiesene Rechnung wieder aus
* wieder aus dem Inkasso zu entfernen. * dem Inkasso zu entfernen.
* <p> * <p>
* Zusätzlich bietet er die Summenberechnung an, bei der die Gutschriften * Zusätzlich bietet er die Summenberechnung an, bei der die Gutschriften
* abgezogen werden. * abgezogen werden.

View file

@ -29,8 +29,8 @@ import jakarta.inject.Inject;
* processed invoices. This item is used in the Workflow Model to construct a * processed invoices. This item is used in the Workflow Model to construct a
* HTML Table wen sending the mail. * HTML Table wen sending the mail.
* <p> * <p>
* The Adapter also splits the item 'dbtr.mail' into a multi value field if * The Adapter also splits the item 'dbtr.mail' into a multi value field if the
* the value contains email addresses separated with new lines. * value contains email addresses separated with new lines.
* *
* *
* @version 1.0 * @version 1.0
@ -198,8 +198,7 @@ public class MahnlaufExecuteAdapter implements SignalAdapter {
for (String mail : mailListe) { for (String mail : mailListe) {
logger.fine(" test mail address:'" + mail + "'"); logger.fine(" test mail address:'" + mail + "'");
if (!InvoiceUtil.validateEmail(mail)) { if (!InvoiceUtil.validateEmail(mail)) {
throw new PluginException(MahnlaufExecuteAdapter.class.getName(), throw new PluginException(MahnlaufExecuteAdapter.class.getName(), "INVALID_EMAIL",
"INVALID_EMAIL",
"Die Daten konnten nicht aktualisiert werden! Bitte geben Sie eine gültige E-Mail Adresse ein!"); "Die Daten konnten nicht aktualisiert werden! Bitte geben Sie eine gültige E-Mail Adresse ein!");
} }
} }

View file

@ -63,8 +63,7 @@ public class MahnlaufRefAddAdapter implements SignalAdapter {
if (invoice.getTaskID() < MahnlaufService.TASK_MAHNLAUF_START if (invoice.getTaskID() < MahnlaufService.TASK_MAHNLAUF_START
|| invoice.getTaskID() > MahnlaufService.TASK_MAHNLAUF_END) { || invoice.getTaskID() > MahnlaufService.TASK_MAHNLAUF_END) {
throw new PluginException(MahnlaufRefAddAdapter.class.getName(), throw new PluginException(MahnlaufRefAddAdapter.class.getName(), MahnlaufService.ERROR_CONFIG,
MahnlaufService.ERROR_CONFIG,
"Der Mahnlauf kann nur für Rechnungen erstellt werden die bereits die Fälligkeit erreicht haben."); "Der Mahnlauf kann nur für Rechnungen erstellt werden die bereits die Fälligkeit erreicht haben.");
} }
@ -92,8 +91,8 @@ public class MahnlaufRefAddAdapter implements SignalAdapter {
workflowService.processWorkItem(mahnLaufWorkitem); workflowService.processWorkItem(mahnLaufWorkitem);
} catch (QueryException | ModelException e) { } catch (QueryException | ModelException e) {
throw new PluginException(MahnlaufRefAddAdapter.class.getName(), throw new PluginException(MahnlaufRefAddAdapter.class.getName(), MahnlaufService.ERROR_CONFIG,
MahnlaufService.ERROR_CONFIG, "Unable to create Mahnlauf", e); "Unable to create Mahnlauf", e);
} }
// set mahnlauf ref // set mahnlauf ref

View file

@ -49,8 +49,8 @@ import jakarta.ejb.Singleton;
import jakarta.inject.Inject; import jakarta.inject.Inject;
/** /**
* Der MahnlaufService wird vom MahnlaufExecuteAdapter um * Der MahnlaufService wird vom MahnlaufExecuteAdapter um Ausgangsrechnungen zu
* Ausgangsrechnungen zu akutalisieren. * akutalisieren.
* *
* @author rsoika * @author rsoika
* *

View file

@ -59,11 +59,10 @@ import jakarta.enterprise.event.Observes;
/** /**
* Der BusinessPartnerImportService hängt sich an den standard CSVImport Service * Der BusinessPartnerImportService hängt sich an den standard CSVImport Service
* der die Cargosoft Stammdaten aktualisiert. * der die Cargosoft Stammdaten aktualisiert. Es ist ein simples Observer
* Es ist ein simples Observer Pattern das auf das Document Event * Pattern das auf das Document Event "ON_DOCUMENT_SAVE" reagiert. Hier hängt
* "ON_DOCUMENT_SAVE" reagiert. Hier hängt sich der BusinessPartnerImportService * sich der BusinessPartnerImportService ein und prüft ob der Workflow schon
* ein und prüft ob der Workflow schon existiert oder ggf. aktualisiert werden * existiert oder ggf. aktualisiert werden muss.
* muss.
* <p> * <p>
* Der Service migriert auch die alten zusätzlichen IBAN/BIC felder wenn der * Der Service migriert auch die alten zusätzlichen IBAN/BIC felder wenn der
* Business partner erstmals neu angelegt wird. * Business partner erstmals neu angelegt wird.

View file

@ -89,17 +89,16 @@ import jakarta.enterprise.event.Observes;
import jakarta.inject.Inject; import jakarta.inject.Inject;
/** /**
* Der CargosoftXMLEAkteImportService erweitert den FTPImportService und * Der CargosoftXMLEAkteImportService erweitert den FTPImportService und kann
* kann XML EAkten (Steuerbescheide) von Cargosoft importieren. * XML EAkten (Steuerbescheide) von Cargosoft importieren. Der Service reagiert
* Der Service reagiert auf DocumentImportEvents und kann über das Options Feld * auf DocumentImportEvents und kann über das Options Feld konfiguriert werden.
* konfiguriert werden.
* <p> * <p>
* Im Gegensatz zum FTPImportService werden die XML Dateien nach einem * Im Gegensatz zum FTPImportService werden die XML Dateien nach einem
* speziellen Verfahren geparsed und dann als Steuerbescheid importiert. * speziellen Verfahren geparsed und dann als Steuerbescheid importiert.
* <p> * <p>
* In den Optionen muss die option "mandant.id" hinterlegt sein. Stimmt * In den Optionen muss die option "mandant.id" hinterlegt sein. Stimmt diese
* diese beim Import nicht mit der Mandanten Nummer in der XML Datei überein, * beim Import nicht mit der Mandanten Nummer in der XML Datei überein, wird das
* wird das XML Dokument gelöscht und nicht importiert. * XML Dokument gelöscht und nicht importiert.
* *
* *
* *
@ -162,8 +161,7 @@ public class CargosoftXMLEAkteImportService {
return; return;
} }
documentImportService.logMessage("...CARGOSOFT_EAKTE_XML Import started: mandant.id=" + mandantID, documentImportService.logMessage("...CARGOSOFT_EAKTE_XML Import started: mandant.id=" + mandantID, event);
event);
String ftpServer = event.getSource().getItemValueString(DocumentImportService.SOURCE_ITEM_SERVER); String ftpServer = event.getSource().getItemValueString(DocumentImportService.SOURCE_ITEM_SERVER);
String ftpPort = event.getSource().getItemValueString(DocumentImportService.SOURCE_ITEM_PORT); String ftpPort = event.getSource().getItemValueString(DocumentImportService.SOURCE_ITEM_PORT);
String ftpUser = event.getSource().getItemValueString(DocumentImportService.SOURCE_ITEM_USER); String ftpUser = event.getSource().getItemValueString(DocumentImportService.SOURCE_ITEM_USER);
@ -216,8 +214,8 @@ public class CargosoftXMLEAkteImportService {
ftpClient.connect(ftpServer, ftpPort); ftpClient.connect(ftpServer, ftpPort);
if (ftpClient.login(ftpUser, ftpPassword) == false) { if (ftpClient.login(ftpUser, ftpPassword) == false) {
throw new PluginException(CargosoftXMLEAkteImportService.class.getName(), throw new PluginException(CargosoftXMLEAkteImportService.class.getName(), "FTP_ERROR",
"FTP_ERROR", "FTP file transfer failed: login failed!"); "FTP file transfer failed: login failed!");
} }
ftpClient.enterLocalPassiveMode(); ftpClient.enterLocalPassiveMode();
logger.finest("...... FileType=" + FTP.BINARY_FILE_TYPE); logger.finest("...... FileType=" + FTP.BINARY_FILE_TYPE);
@ -225,8 +223,7 @@ public class CargosoftXMLEAkteImportService {
ftpClient.setControlEncoding("UTF-8"); ftpClient.setControlEncoding("UTF-8");
} catch (IOException e) { } catch (IOException e) {
throw new PluginException(CargosoftXMLEAkteImportService.class.getName(), throw new PluginException(CargosoftXMLEAkteImportService.class.getName(), "FTP_ERROR", e.getMessage());
"FTP_ERROR", e.getMessage());
} }
return ftpClient; return ftpClient;
@ -250,8 +247,8 @@ public class CargosoftXMLEAkteImportService {
// try to enter the working directory and read all files... // try to enter the working directory and read all files...
boolean bWorkingDir = ftpClient.changeWorkingDirectory(ftpPath); boolean bWorkingDir = ftpClient.changeWorkingDirectory(ftpPath);
if (bWorkingDir == false) { if (bWorkingDir == false) {
throw new PluginException(CargosoftXMLEAkteImportService.class.getName(), throw new PluginException(CargosoftXMLEAkteImportService.class.getName(), "FTP_ERROR",
"FTP_ERROR", "...failed to change into working directory: "); "...failed to change into working directory: ");
} }
FTPFile[] allFiles = ftpClient.listFiles(ftpPath); FTPFile[] allFiles = ftpClient.listFiles(ftpPath);
@ -299,10 +296,10 @@ public class CargosoftXMLEAkteImportService {
// "processed/"); // "processed/");
// } // }
} catch (AccessDeniedException | ProcessingErrorException | PluginException } catch (AccessDeniedException | ProcessingErrorException | PluginException | ModelException
| ModelException | XPathExpressionException | TransformerException e) { | XPathExpressionException | TransformerException e) {
throw new PluginException(CargosoftXMLEAkteImportService.class.getName(), throw new PluginException(CargosoftXMLEAkteImportService.class.getName(), "FTP_ERROR",
"FTP_ERROR", e.getMessage()); e.getMessage());
} }
} }
documentImportService.logMessage("..." + count + " new files imported.", event); documentImportService.logMessage("..." + count + " new files imported.", event);
@ -313,8 +310,7 @@ public class CargosoftXMLEAkteImportService {
int r = ftpClient.getReplyCode(); int r = ftpClient.getReplyCode();
logger.severe("FTP ReplyCode=" + r); logger.severe("FTP ReplyCode=" + r);
throw new PluginException(CargosoftXMLEAkteImportService.class.getName(), throw new PluginException(CargosoftXMLEAkteImportService.class.getName(), "FTP_ERROR", e.getMessage());
"FTP_ERROR", e.getMessage());
} finally { } finally {
// do logout.... // do logout....
@ -324,8 +320,7 @@ public class CargosoftXMLEAkteImportService {
ftpClient.disconnect(); ftpClient.disconnect();
} catch (IOException e) { } catch (IOException e) {
documentImportService.logMessage("...FTP file transfer failed: " + e.getMessage(), event); documentImportService.logMessage("...FTP file transfer failed: " + e.getMessage(), event);
throw new PluginException(CargosoftXMLEAkteImportService.class.getName(), throw new PluginException(CargosoftXMLEAkteImportService.class.getName(), "FTP_ERROR", e.getMessage());
"FTP_ERROR", e.getMessage());
} }
} }
} }
@ -344,9 +339,8 @@ public class CargosoftXMLEAkteImportService {
* @throws XPathExpressionException * @throws XPathExpressionException
*/ */
public ItemCollection createWorkitem(ItemCollection source, String fileName, byte[] rawData, public ItemCollection createWorkitem(ItemCollection source, String fileName, byte[] rawData,
Map<String, List<String>> spacePosMappings) Map<String, List<String>> spacePosMappings) throws AccessDeniedException, ProcessingErrorException,
throws AccessDeniedException, ProcessingErrorException, PluginException, ModelException, PluginException, ModelException, XPathExpressionException, TransformerException {
XPathExpressionException, TransformerException {
ItemCollection workitem = new ItemCollection(); ItemCollection workitem = new ItemCollection();
workitem.model(source.getItemValueString(DocumentImportService.SOURCE_ITEM_MODELVERSION)); workitem.model(source.getItemValueString(DocumentImportService.SOURCE_ITEM_MODELVERSION));
workitem.task(source.getItemValueInteger(DocumentImportService.SOURCE_ITEM_TASK)); workitem.task(source.getItemValueInteger(DocumentImportService.SOURCE_ITEM_TASK));
@ -375,37 +369,37 @@ public class CargosoftXMLEAkteImportService {
// invoice.rate // invoice.rate
// invoice.base.amount // invoice.base.amount
readXMLValue(doc, "/CargoSoftEFile/EFile/References/Reference[@type='client']", workitem, readXMLValue(doc, "/CargoSoftEFile/EFile/References/Reference[@type='client']", workitem, "mandant.id",
"mandant.id", String.class); String.class);
readXMLValue(doc, "/CargoSoftEFile/EFile/References/Reference[@type='cs_voucher_number']", readXMLValue(doc, "/CargoSoftEFile/EFile/References/Reference[@type='cs_voucher_number']", workitem,
workitem, "invoice.number", String.class); "invoice.number", String.class);
readXMLValue(doc, "/CargoSoftEFile/EFile/References/Reference[@type='cs_address_number']", readXMLValue(doc, "/CargoSoftEFile/EFile/References/Reference[@type='cs_address_number']", workitem,
workitem, "cdtr.number", String.class); "cdtr.number", String.class);
readXMLValue(doc, "/CargoSoftEFile/EFile/References/Reference[@type='voucherdate']", readXMLValue(doc, "/CargoSoftEFile/EFile/References/Reference[@type='voucherdate']", workitem,
workitem, "invoice.date", Date.class); "invoice.date", Date.class);
readXMLValue(doc, "/CargoSoftEFile/EFile/References/Reference[@type='currency']", readXMLValue(doc, "/CargoSoftEFile/EFile/References/Reference[@type='currency']", workitem,
workitem, "invoice.currency", String.class); "invoice.currency", String.class);
readXMLValue(doc, "/CargoSoftEFile/EFile/References/Reference[@type='currency_rate']", readXMLValue(doc, "/CargoSoftEFile/EFile/References/Reference[@type='currency_rate']", workitem,
workitem, "invoice.rate", Double.class); "invoice.rate", Double.class);
readXMLValue(doc, "/CargoSoftEFile/EFile/References/Reference[@type='total_net_amount']", readXMLValue(doc, "/CargoSoftEFile/EFile/References/Reference[@type='total_net_amount']", workitem,
workitem, "invoice.total.net", Double.class); "invoice.total.net", Double.class);
readXMLValue(doc, "/CargoSoftEFile/EFile/References/Reference[@type='total_tax_amount']", readXMLValue(doc, "/CargoSoftEFile/EFile/References/Reference[@type='total_tax_amount']", workitem,
workitem, "invoice.total.tax", Double.class); "invoice.total.tax", Double.class);
readXMLValue(doc, "/CargoSoftEFile/EFile/References/Reference[@type='reference']", readXMLValue(doc, "/CargoSoftEFile/EFile/References/Reference[@type='reference']", workitem,
workitem, "invoice.atc.number", String.class); "invoice.atc.number", String.class);
readXMLValue(doc, "/CargoSoftEFile/EFile/References/Reference[@type='booking_period']", readXMLValue(doc, "/CargoSoftEFile/EFile/References/Reference[@type='booking_period']", workitem,
workitem, "invoice.booking_period", String.class); "invoice.booking_period", String.class);
readXMLValue(doc, "/CargoSoftEFile/EFile/References/Reference[@type='booking_date']", readXMLValue(doc, "/CargoSoftEFile/EFile/References/Reference[@type='booking_date']", workitem,
workitem, "invoice.booking_date", Date.class); "invoice.booking_date", Date.class);
readXMLValue(doc, "/CargoSoftEFile/EFile/References/Reference[@type='booking_text']", readXMLValue(doc, "/CargoSoftEFile/EFile/References/Reference[@type='booking_text']", workitem,
workitem, "invoice.booking_text", String.class); "invoice.booking_text", String.class);
// verify if invoice is already imported. // verify if invoice is already imported.
if (alreadyImported(workitem.getItemValueString("invoice.number"))) { if (alreadyImported(workitem.getItemValueString("invoice.number"))) {
@ -440,8 +434,7 @@ public class CargosoftXMLEAkteImportService {
attacheFiles(doc, workitem); attacheFiles(doc, workitem);
} }
} catch (ParserConfigurationException | SAXException | IOException e) { } catch (ParserConfigurationException | SAXException | IOException e) {
throw new PluginException(CargosoftXMLEAkteImportService.class.getName(), throw new PluginException(CargosoftXMLEAkteImportService.class.getName(), "XML_ERROR", e.getMessage());
"XML_ERROR", e.getMessage());
} }
return workitem; return workitem;
} }
@ -452,8 +445,7 @@ public class CargosoftXMLEAkteImportService {
* *
* /Invoices/Invoice/InvoiceHeader/Client/Code * /Invoices/Invoice/InvoiceHeader/Client/Code
* *
* Beispiel Datum: * Beispiel Datum: <InvoiceDate>2024-05-13T00:00:00+02:00</InvoiceDate>
* <InvoiceDate>2024-05-13T00:00:00+02:00</InvoiceDate>
* *
* @param doc - xml doc * @param doc - xml doc
* @param expression - xpath expression * @param expression - xpath expression
@ -513,9 +505,9 @@ public class CargosoftXMLEAkteImportService {
} }
/** /**
* This method attache the pdf file and the XML file to the workitem. * This method attache the pdf file and the XML file to the workitem. The method
* The method removes the file content first form the xml tree and attache the * removes the file content first form the xml tree and attache the XML without
* XML without the pdf data. * the pdf data.
* *
* @throws XPathExpressionException * @throws XPathExpressionException
* @throws TransformerException * @throws TransformerException
@ -526,24 +518,23 @@ public class CargosoftXMLEAkteImportService {
XPathFactory xpathFactory = XPathFactory.newInstance(); XPathFactory xpathFactory = XPathFactory.newInstance();
XPath xpath = xpathFactory.newXPath(); XPath xpath = xpathFactory.newXPath();
// read filename and content.... // read filename and content....
readXMLValue(doc, "/CargoSoftEFile/EFile/Attachments/Attachment/Filename", readXMLValue(doc, "/CargoSoftEFile/EFile/Attachments/Attachment/Filename", workitem, "import.filename",
workitem, "import.filename", String.class); String.class);
readXMLValue(doc, "/CargoSoftEFile/EFile/Attachments/Attachment/Content", readXMLValue(doc, "/CargoSoftEFile/EFile/Attachments/Attachment/Content", workitem, "import.filedata",
workitem, "import.filedata", String.class); String.class);
// attache the PDF file content // attache the PDF file content
String fileDataString = workitem.getItemValueString("import.filedata"); String fileDataString = workitem.getItemValueString("import.filedata");
byte[] decodedPDFData = java.util.Base64.getDecoder().decode(fileDataString); byte[] decodedPDFData = java.util.Base64.getDecoder().decode(fileDataString);
FileData fileData = new FileData(workitem.getItemValueString("import.filename"), FileData fileData = new FileData(workitem.getItemValueString("import.filename"), decodedPDFData,
decodedPDFData, "application/pdf", null); "application/pdf", null);
workitem.addFileData(fileData); workitem.addFileData(fileData);
// remove the temp items.. // remove the temp items..
workitem.removeItem("import.filename"); workitem.removeItem("import.filename");
workitem.removeItem("import.filedata"); workitem.removeItem("import.filedata");
// next remove the Attachments/AttachmentContent from the dom tree // next remove the Attachments/AttachmentContent from the dom tree
XPathExpression attachmentsExpr = xpath XPathExpression attachmentsExpr = xpath.compile("/CargoSoftEFile/EFile/Attachments/Attachment/Content");
.compile("/CargoSoftEFile/EFile/Attachments/Attachment/Content");
NodeList contentNodes = (NodeList) attachmentsExpr.evaluate(doc, XPathConstants.NODESET); NodeList contentNodes = (NodeList) attachmentsExpr.evaluate(doc, XPathConstants.NODESET);
// Remove the matching nodes // Remove the matching nodes
for (int i = 0; i < contentNodes.getLength(); i++) { for (int i = 0; i < contentNodes.getLength(); i++) {
@ -594,8 +585,7 @@ public class CargosoftXMLEAkteImportService {
* child Workitem for each row. * child Workitem for each row.
* *
* The method assumes that the rows are starting with the type 'row_n_' where * The method assumes that the rows are starting with the type 'row_n_' where
* 'n' is the row number. * 'n' is the row number. We start with row 1 and read until we found more rows.
* We start with row 1 and read until we found more rows.
* *
* Example: * Example:
* *

View file

@ -89,19 +89,18 @@ import jakarta.inject.Inject;
/** /**
* Der CargosoftXMLInvoiceImportImportService erweitert den FTPImportService und * Der CargosoftXMLInvoiceImportImportService erweitert den FTPImportService und
* kann XML Invoice Dokumente von Cargosoft importieren. * kann XML Invoice Dokumente von Cargosoft importieren. Der Service reagiert
* Der Service reagiert auf DocumentImportEvents und kann über das Options Feld * auf DocumentImportEvents und kann über das Options Feld konfiguriert werden.
* konfiguriert werden.
* <p> * <p>
* Im Gegensatz zum FTPImportService werden die XML Dateien nach einem * Im Gegensatz zum FTPImportService werden die XML Dateien nach einem
* speziellen Verfahren geparsed und dann als Ausgangsrechnungen importiert. * speziellen Verfahren geparsed und dann als Ausgangsrechnungen importiert. Der
* Der Service ersetzt den alten DATEV Import. Falls eine Rechnung schon * Service ersetzt den alten DATEV Import. Falls eine Rechnung schon existiert
* existiert (Rechnungsnummer) wird der Datensatz vom FTP Laufwerk gelöscht und * (Rechnungsnummer) wird der Datensatz vom FTP Laufwerk gelöscht und nicht
* nicht importiert. * importiert.
* <p> * <p>
* In den Optionen muss die option "mandant.id" hinterlegt sein. Stimmt * In den Optionen muss die option "mandant.id" hinterlegt sein. Stimmt diese
* diese beim Import nicht mit der Mandanten Nummer in der XML Datei überein, * beim Import nicht mit der Mandanten Nummer in der XML Datei überein, wird das
* wird das XML Dokument gelöscht und nicht importiert. * XML Dokument gelöscht und nicht importiert.
* *
* *
* *
@ -164,8 +163,7 @@ public class CargosoftXMLInvoiceImportService {
return; return;
} }
documentImportService.logMessage("...CARGOSOFT_INVOICE_XML Import started: mandant.id=" + mandantID, documentImportService.logMessage("...CARGOSOFT_INVOICE_XML Import started: mandant.id=" + mandantID, event);
event);
String ftpServer = event.getSource().getItemValueString(DocumentImportService.SOURCE_ITEM_SERVER); String ftpServer = event.getSource().getItemValueString(DocumentImportService.SOURCE_ITEM_SERVER);
String ftpPort = event.getSource().getItemValueString(DocumentImportService.SOURCE_ITEM_PORT); String ftpPort = event.getSource().getItemValueString(DocumentImportService.SOURCE_ITEM_PORT);
String ftpUser = event.getSource().getItemValueString(DocumentImportService.SOURCE_ITEM_USER); String ftpUser = event.getSource().getItemValueString(DocumentImportService.SOURCE_ITEM_USER);
@ -218,8 +216,8 @@ public class CargosoftXMLInvoiceImportService {
ftpClient.connect(ftpServer, ftpPort); ftpClient.connect(ftpServer, ftpPort);
if (ftpClient.login(ftpUser, ftpPassword) == false) { if (ftpClient.login(ftpUser, ftpPassword) == false) {
throw new PluginException(CargosoftXMLInvoiceImportService.class.getName(), throw new PluginException(CargosoftXMLInvoiceImportService.class.getName(), "FTP_ERROR",
"FTP_ERROR", "FTP file transfer failed: login failed!"); "FTP file transfer failed: login failed!");
} }
ftpClient.enterLocalPassiveMode(); ftpClient.enterLocalPassiveMode();
logger.finest("...... FileType=" + FTP.BINARY_FILE_TYPE); logger.finest("...... FileType=" + FTP.BINARY_FILE_TYPE);
@ -227,8 +225,7 @@ public class CargosoftXMLInvoiceImportService {
ftpClient.setControlEncoding("UTF-8"); ftpClient.setControlEncoding("UTF-8");
} catch (IOException e) { } catch (IOException e) {
throw new PluginException(CargosoftXMLInvoiceImportService.class.getName(), throw new PluginException(CargosoftXMLInvoiceImportService.class.getName(), "FTP_ERROR", e.getMessage());
"FTP_ERROR", e.getMessage());
} }
return ftpClient; return ftpClient;
@ -252,8 +249,8 @@ public class CargosoftXMLInvoiceImportService {
// try to enter the working directory and read all files... // try to enter the working directory and read all files...
boolean bWorkingDir = ftpClient.changeWorkingDirectory(ftpPath); boolean bWorkingDir = ftpClient.changeWorkingDirectory(ftpPath);
if (bWorkingDir == false) { if (bWorkingDir == false) {
throw new PluginException(CargosoftXMLInvoiceImportService.class.getName(), throw new PluginException(CargosoftXMLInvoiceImportService.class.getName(), "FTP_ERROR",
"FTP_ERROR", "...failed to change into working directory: "); "...failed to change into working directory: ");
} }
FTPFile[] allFiles = ftpClient.listFiles(ftpPath); FTPFile[] allFiles = ftpClient.listFiles(ftpPath);
@ -301,10 +298,10 @@ public class CargosoftXMLInvoiceImportService {
// "processed/"); // "processed/");
// } // }
} catch (AccessDeniedException | ProcessingErrorException | PluginException } catch (AccessDeniedException | ProcessingErrorException | PluginException | ModelException
| ModelException | XPathExpressionException | TransformerException e) { | XPathExpressionException | TransformerException e) {
throw new PluginException(CargosoftXMLInvoiceImportService.class.getName(), throw new PluginException(CargosoftXMLInvoiceImportService.class.getName(), "FTP_ERROR",
"FTP_ERROR", e.getMessage()); e.getMessage());
} }
} }
documentImportService.logMessage("..." + count + " new files imported.", event); documentImportService.logMessage("..." + count + " new files imported.", event);
@ -315,8 +312,7 @@ public class CargosoftXMLInvoiceImportService {
int r = ftpClient.getReplyCode(); int r = ftpClient.getReplyCode();
logger.severe("FTP ReplyCode=" + r); logger.severe("FTP ReplyCode=" + r);
throw new PluginException(CargosoftXMLInvoiceImportService.class.getName(), throw new PluginException(CargosoftXMLInvoiceImportService.class.getName(), "FTP_ERROR", e.getMessage());
"FTP_ERROR", e.getMessage());
} finally { } finally {
// do logout.... // do logout....
@ -326,8 +322,8 @@ public class CargosoftXMLInvoiceImportService {
ftpClient.disconnect(); ftpClient.disconnect();
} catch (IOException e) { } catch (IOException e) {
documentImportService.logMessage("...FTP file transfer failed: " + e.getMessage(), event); documentImportService.logMessage("...FTP file transfer failed: " + e.getMessage(), event);
throw new PluginException(CargosoftXMLInvoiceImportService.class.getName(), throw new PluginException(CargosoftXMLInvoiceImportService.class.getName(), "FTP_ERROR",
"FTP_ERROR", e.getMessage()); e.getMessage());
} }
} }
} }
@ -346,9 +342,8 @@ public class CargosoftXMLInvoiceImportService {
* @throws XPathExpressionException * @throws XPathExpressionException
*/ */
public ItemCollection createWorkitem(ItemCollection source, String fileName, byte[] rawData, public ItemCollection createWorkitem(ItemCollection source, String fileName, byte[] rawData,
Map<String, List<String>> spacePosMappings) Map<String, List<String>> spacePosMappings) throws AccessDeniedException, ProcessingErrorException,
throws AccessDeniedException, ProcessingErrorException, PluginException, ModelException, PluginException, ModelException, XPathExpressionException, TransformerException {
XPathExpressionException, TransformerException {
ItemCollection workitem = new ItemCollection(); ItemCollection workitem = new ItemCollection();
workitem.model(source.getItemValueString(DocumentImportService.SOURCE_ITEM_MODELVERSION)); workitem.model(source.getItemValueString(DocumentImportService.SOURCE_ITEM_MODELVERSION));
workitem.task(source.getItemValueInteger(DocumentImportService.SOURCE_ITEM_TASK)); workitem.task(source.getItemValueInteger(DocumentImportService.SOURCE_ITEM_TASK));
@ -377,11 +372,11 @@ public class CargosoftXMLInvoiceImportService {
// invoice.rate // invoice.rate
// invoice.base.amount // invoice.base.amount
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/Client/Codes/Code", workitem, readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/Client/Codes/Code", workitem, "mandant.id",
"mandant.id", String.class); String.class);
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceNumber", readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceNumber", workitem, "invoice.number",
workitem, "invoice.number", String.class); String.class);
// verify if invoice is already imported. // verify if invoice is already imported.
if (alreadyImported(workitem.getItemValueString("invoice.number"))) { if (alreadyImported(workitem.getItemValueString("invoice.number"))) {
@ -389,14 +384,14 @@ public class CargosoftXMLInvoiceImportService {
return null; return null;
} }
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceType/Codes/Code", readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceType/Codes/Code", workitem, "invoice.type",
workitem, "invoice.type", String.class); String.class);
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceType/Description", readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceType/Description", workitem,
workitem, "invoice.type.description", String.class); "invoice.type.description", String.class);
// Rechnungssumme errechnen..... // Rechnungssumme errechnen.....
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceAmount/NetAmount/Amount/Value", readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceAmount/NetAmount/Amount/Value", workitem,
workitem, "invoice.total.net", Double.class); "invoice.total.net", Double.class);
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceAmount/VATInformation/VATAmount/Amount/Value", readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceAmount/VATInformation/VATAmount/Amount/Value",
workitem, "invoice.total.tax", Double.class); workitem, "invoice.total.tax", Double.class);
if (!workitem.hasItem("invoice.total.tax")) { if (!workitem.hasItem("invoice.total.tax")) {
@ -416,34 +411,33 @@ public class CargosoftXMLInvoiceImportService {
} }
// Steuer // Steuer
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceAmount/VATInformation/VAT/VATRate", readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceAmount/VATInformation/VAT/VATRate", workitem,
workitem, "invoice.vatrate", Double.class); "invoice.vatrate", Double.class);
// Booking Period // Booking Period
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/BookingPeriod", readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/BookingPeriod", workitem, "invoice.bookingperiod",
workitem, "invoice.bookingperiod", String.class); String.class);
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceCurrency/Codes/Code", readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceCurrency/Codes/Code", workitem,
workitem, "invoice.currency", String.class); "invoice.currency", String.class);
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceDate", readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceDate", workitem, "invoice.date", Date.class);
workitem, "invoice.date", Date.class);
readXMLValue(doc, readXMLValue(doc,
"/Invoices/Invoice/InvoiceHeader/PaymentConditions/PaymentCondition/Codes/Code[@Type='cs']", "/Invoices/Invoice/InvoiceHeader/PaymentConditions/PaymentCondition/Codes/Code[@Type='cs']",
workitem, "payment.term", String.class); workitem, "payment.term", String.class);
// kostenstelle // kostenstelle
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/CostUnit/Description", readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/CostUnit/Description", workitem, "invoice.CostUnit",
workitem, "invoice.CostUnit", String.class); String.class);
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/PaymentConditions/DueDate", readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/PaymentConditions/DueDate", workitem, "invoice.duedate",
workitem, "invoice.duedate", Date.class); Date.class);
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/PaymentConditions/DueDate", readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/PaymentConditions/DueDate", workitem, "invoice.reminder",
workitem, "invoice.reminder", Date.class); Date.class);
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceAddress/Codes/Code", readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceAddress/Codes/Code", workitem, "dbtr.number",
workitem, "dbtr.number", String.class); String.class);
String dbtrNumber = workitem.getItemValueString("dbtr.number"); String dbtrNumber = workitem.getItemValueString("dbtr.number");
if (dbtrNumber.startsWith("K") || dbtrNumber.startsWith("D")) { if (dbtrNumber.startsWith("K") || dbtrNumber.startsWith("D")) {
workitem.setItemValue("dbtr.number", dbtrNumber.substring(1)); workitem.setItemValue("dbtr.number", dbtrNumber.substring(1));
@ -487,8 +481,7 @@ public class CargosoftXMLInvoiceImportService {
attacheFiles(doc, workitem); attacheFiles(doc, workitem);
} catch (ParserConfigurationException | SAXException | IOException e) { } catch (ParserConfigurationException | SAXException | IOException e) {
throw new PluginException(CargosoftXMLInvoiceImportService.class.getName(), throw new PluginException(CargosoftXMLInvoiceImportService.class.getName(), "XML_ERROR", e.getMessage());
"XML_ERROR", e.getMessage());
} }
return workitem; return workitem;
} }
@ -512,8 +505,7 @@ public class CargosoftXMLInvoiceImportService {
* *
* /Invoices/Invoice/InvoiceHeader/Client/Code * /Invoices/Invoice/InvoiceHeader/Client/Code
* *
* Beispiel Datum: * Beispiel Datum: <InvoiceDate>2024-05-13T00:00:00+02:00</InvoiceDate>
* <InvoiceDate>2024-05-13T00:00:00+02:00</InvoiceDate>
* *
* @param doc - xml doc * @param doc - xml doc
* @param expression - xpath expression * @param expression - xpath expression
@ -566,11 +558,9 @@ public class CargosoftXMLInvoiceImportService {
/** /**
* Hilfsmethode die die ATC Nummer sucht und in das workitem feld * Hilfsmethode die die ATC Nummer sucht und in das workitem feld
* invoice.atc.number einträgt. * invoice.atc.number einträgt. Wir gehen davon aus, das wir über die ChildItems
* Wir gehen davon aus, das wir über die ChildItems diese aus dem billingtexts * diese aus dem billingtexts schon ermittel haben. Es können mehrere ATC
* schon ermittel haben. * nummern existieren. Diese werden in einer Liste gespeichert
* Es können mehrere ATC nummern existieren. Diese werden in einer Liste
* gespeichert
* *
* @param doc * @param doc
* @param workitem * @param workitem
@ -595,15 +585,9 @@ public class CargosoftXMLInvoiceImportService {
* *
* InvoiceRows/InvoiceRow * InvoiceRows/InvoiceRow
* *
* Each row has the following items: * Each row has the following items: _childitems { datev.shzeichen=[H],
* _childitems { * datev.kurs=[0.0], datev.basisumsatz=[0.0], datev.text=[R LA-PAP-2405-051],
* datev.shzeichen=[H], * datev.konto=[3851], datev.umsatz=[3307.81], datev.wkz=[EUR]}}
* datev.kurs=[0.0],
* datev.basisumsatz=[0.0],
* datev.text=[R LA-PAP-2405-051],
* datev.konto=[3851],
* datev.umsatz=[3307.81],
* datev.wkz=[EUR]}}
* *
* @param doc - xml doc * @param doc - xml doc
* @param workitem * @param workitem
@ -622,8 +606,7 @@ public class CargosoftXMLInvoiceImportService {
XPathExpression netAmountExchangeRateExpr = xPath XPathExpression netAmountExchangeRateExpr = xPath
.compile("InvoiceAmount/NetAmount/Amount/ExchangeRate/text()"); .compile("InvoiceAmount/NetAmount/Amount/ExchangeRate/text()");
XPathExpression netActivityTypeExpr = xPath XPathExpression netActivityTypeExpr = xPath.compile("ActivityType/Codes/Code[@Type='cs']/text()");
.compile("ActivityType/Codes/Code[@Type='cs']/text()");
NodeList rowList = doc.getElementsByTagName("InvoiceRow"); NodeList rowList = doc.getElementsByTagName("InvoiceRow");
double _kurs = 0.0; double _kurs = 0.0;
@ -732,13 +715,10 @@ public class CargosoftXMLInvoiceImportService {
} }
/** /**
* G = Gutschrift * G = Gutschrift SR = Stornorechnung GK = Sammelgutschrift SS = Sammelrechnung
* SR = Stornorechnung * Storno
* GK = Sammelgutschrift
* SS = Sammelrechnung Storno
* *
* müssen wir das vorzeichen ändern * müssen wir das vorzeichen ändern dies wird über den Invoice.Type geprüft
* dies wird über den Invoice.Type geprüft
* *
* @return * @return
*/ */
@ -748,9 +728,9 @@ public class CargosoftXMLInvoiceImportService {
} }
/** /**
* This method attache the pdf file and the XML file to the workitem. * This method attache the pdf file and the XML file to the workitem. The method
* The method removes the file content first form the xml tree and attache the * removes the file content first form the xml tree and attache the XML without
* XML without the pdf data. * the pdf data.
* *
* @throws XPathExpressionException * @throws XPathExpressionException
* @throws TransformerException * @throws TransformerException
@ -761,16 +741,16 @@ public class CargosoftXMLInvoiceImportService {
XPathFactory xpathFactory = XPathFactory.newInstance(); XPathFactory xpathFactory = XPathFactory.newInstance();
XPath xpath = xpathFactory.newXPath(); XPath xpath = xpathFactory.newXPath();
// read filename and content.... // read filename and content....
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/Attachments/Attachment/Filename", readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/Attachments/Attachment/Filename", workitem,
workitem, "import.filename", String.class); "import.filename", String.class);
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/Attachments/Attachment/Content", readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/Attachments/Attachment/Content", workitem, "import.filedata",
workitem, "import.filedata", String.class); String.class);
// attache the PDF file content // attache the PDF file content
String fileDataString = workitem.getItemValueString("import.filedata"); String fileDataString = workitem.getItemValueString("import.filedata");
byte[] decodedPDFData = java.util.Base64.getDecoder().decode(fileDataString); byte[] decodedPDFData = java.util.Base64.getDecoder().decode(fileDataString);
FileData fileData = new FileData(workitem.getItemValueString("import.filename"), FileData fileData = new FileData(workitem.getItemValueString("import.filename"), decodedPDFData,
decodedPDFData, "application/pdf", null); "application/pdf", null);
workitem.addFileData(fileData); workitem.addFileData(fileData);
// remove the temp items.. // remove the temp items..
workitem.removeItem("import.filename"); workitem.removeItem("import.filename");

View file

@ -52,7 +52,6 @@ import jakarta.json.bind.JsonbConfig;
public class ZohoAPIService { public class ZohoAPIService {
private static Logger logger = Logger.getLogger(ZohoAPIService.class.getName()); private static Logger logger = Logger.getLogger(ZohoAPIService.class.getName());
private static final String ZOHO_API_BASE_URL = "https://www.zohoapis.eu/books/v3/";
@Inject @Inject
ZohoOAuthManager zohoOAuthManager; ZohoOAuthManager zohoOAuthManager;
@ -62,12 +61,9 @@ public class ZohoAPIService {
@PostConstruct @PostConstruct
void init() { void init() {
this.httpClient = HttpClient.newBuilder() this.httpClient = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10)).build();
.connectTimeout(Duration.ofSeconds(10))
.build();
JsonbConfig config = new JsonbConfig() JsonbConfig config = new JsonbConfig().withDateFormat("yyyy-MM-dd", Locale.getDefault())
.withDateFormat("yyyy-MM-dd", Locale.getDefault())
.setProperty("jsonb.fail-on-unknown-properties", false); // Ignoriert unbekannte Felder .setProperty("jsonb.fail-on-unknown-properties", false); // Ignoriert unbekannte Felder
this.jsonb = JsonbBuilder.create(config); this.jsonb = JsonbBuilder.create(config);
} }
@ -78,20 +74,16 @@ public class ZohoAPIService {
logger.info("├── Zoho lookup contact '" + companyName + "'..."); logger.info("├── Zoho lookup contact '" + companyName + "'...");
try { try {
// https://www.zohoapis.com/books/v3/contacts?organization_id=10234695&company_name=xxx' // https://www.zohoapis.com/books/v3/contacts?organization_id=10234695&company_name=xxx'
String uri = ZOHO_API_BASE_URL + "contacts?organization_id=" String uri = zohoOAuthManager.getBaseURI() + "contacts?organization_id="
+ zohoOAuthManager.getOrganization() + "&company_name=" + zohoOAuthManager.getOrganization() + "&company_name="
+ URLEncoder.encode(companyName, StandardCharsets.UTF_8); + URLEncoder.encode(companyName, StandardCharsets.UTF_8);
logger.fine("│ ├── uri= " + uri); logger.fine("│ ├── uri= " + uri);
logger.fine("│ ├── accessToken=" + accessToken); logger.fine("│ ├── accessToken=" + accessToken);
HttpRequest request = HttpRequest.newBuilder() HttpRequest request = HttpRequest.newBuilder().uri(URI.create(uri))
.uri(URI.create(uri))
.header("Authorization", "Zoho-oauthtoken " + accessToken) .header("Authorization", "Zoho-oauthtoken " + accessToken)
.header("Content-Type", "application/json") .header("Content-Type", "application/json").GET().build();
.GET()
.build();
HttpResponse<String> response = httpClient.send( HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
request, HttpResponse.BodyHandlers.ofString());
// Print response details to console // Print response details to console
logger.fine("├── Zoho Response:"); logger.fine("├── Zoho Response:");
@ -99,12 +91,11 @@ public class ZohoAPIService {
logger.fine("│ ├── Response Body: " + response.body()); logger.fine("│ ├── Response Body: " + response.body());
if (response.statusCode() >= 300) { if (response.statusCode() >= 300) {
throw new PluginException(this.getClass().getName(), "Zoho API error", throw new PluginException(this.getClass().getName(), "Zoho API error",
"Failed to find Contacts - Status code: " + response.statusCode() + "Failed to find Contacts - Status code: " + response.statusCode() + ", Response: "
", Response: " + response.body()); + response.body());
} }
ZohoContactFindResponse contactsResponse = jsonb.fromJson(response.body(), ZohoContactFindResponse contactsResponse = jsonb.fromJson(response.body(), ZohoContactFindResponse.class);
ZohoContactFindResponse.class);
if (contactsResponse != null && contactsResponse.getContacts() != null) { if (contactsResponse != null && contactsResponse.getContacts() != null) {
// Durchsuche die Ergebnisse nach exakter Übereinstimmung // Durchsuche die Ergebnisse nach exakter Übereinstimmung
@ -117,8 +108,7 @@ public class ZohoAPIService {
} }
return null; return null;
} catch (Exception e) { } catch (Exception e) {
throw new PluginException(this.getClass().getName(), "Zoho API error", throw new PluginException(this.getClass().getName(), "Zoho API error", "Failed to find contacts", e);
"Failed to find contacts", e);
} }
} }
@ -155,19 +145,16 @@ public class ZohoAPIService {
logger.fine("│ ├── " + requestBody); logger.fine("│ ├── " + requestBody);
// https://www.zohoapis.com/books/v3/invoices?organization_id=10234695' // https://www.zohoapis.com/books/v3/invoices?organization_id=10234695'
String uri = ZOHO_API_BASE_URL + "invoices?organization_id=" String uri = zohoOAuthManager.getBaseURI() + "invoices?organization_id="
+ zohoOAuthManager.getOrganization(); + zohoOAuthManager.getOrganization();
logger.fine("│ ├── uri= " + uri); logger.fine("│ ├── uri= " + uri);
logger.fine("│ ├── accessToken=" + accessToken); logger.fine("│ ├── accessToken=" + accessToken);
HttpRequest request = HttpRequest.newBuilder() HttpRequest request = HttpRequest.newBuilder().uri(URI.create(uri))
.uri(URI.create(uri))
.header("Authorization", "Zoho-oauthtoken " + accessToken) .header("Authorization", "Zoho-oauthtoken " + accessToken)
.header("Content-Type", "application/json") .header("Content-Type", "application/json").POST(HttpRequest.BodyPublishers.ofString(requestBody))
.POST(HttpRequest.BodyPublishers.ofString(requestBody))
.build(); .build();
HttpResponse<String> response = httpClient.send( HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
request, HttpResponse.BodyHandlers.ofString());
// Print response details to console // Print response details to console
logger.fine("├── Zoho Response:"); logger.fine("├── Zoho Response:");
@ -175,8 +162,8 @@ public class ZohoAPIService {
if (response.statusCode() >= 300) { if (response.statusCode() >= 300) {
throw new PluginException(this.getClass().getName(), "Zoho API error", throw new PluginException(this.getClass().getName(), "Zoho API error",
"Failed to create invoice. Status code: " + response.statusCode() + "Failed to create invoice. Status code: " + response.statusCode() + ", Response: "
", Response: " + response.body()); + response.body());
} }
// Erfolgreiche Erstellung // Erfolgreiche Erstellung
@ -184,8 +171,7 @@ public class ZohoAPIService {
logger.fine("│ ├── Response Body: " + bodyContent); logger.fine("│ ├── Response Body: " + bodyContent);
System.out.print(bodyContent); System.out.print(bodyContent);
ZohoInvoiceCreateResponse createResponse = jsonb.fromJson(bodyContent, ZohoInvoiceCreateResponse createResponse = jsonb.fromJson(bodyContent, ZohoInvoiceCreateResponse.class);
ZohoInvoiceCreateResponse.class);
logger.fine("│ ├── OK - customerID=" + createResponse.getInvoice().getCustomerId()); logger.fine("│ ├── OK - customerID=" + createResponse.getInvoice().getCustomerId());
logger.info("│ ├── OK - invoiceID=" + createResponse.getInvoice().getInvoiceId()); logger.info("│ ├── OK - invoiceID=" + createResponse.getInvoice().getInvoiceId());
@ -196,8 +182,7 @@ public class ZohoAPIService {
// now add attachments (optional) // now add attachments (optional)
if (files != null) { if (files != null) {
for (FileData file : files) { for (FileData file : files) {
attacheFileData(zohoInvoice.getInvoiceId(), file, "invoices", attacheFileData(zohoInvoice.getInvoiceId(), file, "invoices", accessToken);
accessToken);
} }
} }
@ -233,30 +218,26 @@ public class ZohoAPIService {
// JSON serialisieren // JSON serialisieren
String requestBody = jsonb.toJson(newContact); String requestBody = jsonb.toJson(newContact);
logger.fine("│ ├── " + requestBody); logger.fine("│ ├── " + requestBody);
String uri = ZOHO_API_BASE_URL + "contacts?organization_id=" String uri = zohoOAuthManager.getBaseURI() + "contacts?organization_id="
+ zohoOAuthManager.getOrganization(); + zohoOAuthManager.getOrganization();
HttpRequest request = HttpRequest.newBuilder() HttpRequest request = HttpRequest.newBuilder().uri(URI.create(uri))
.uri(URI.create(uri))
.header("Authorization", "Zoho-oauthtoken " + accessToken) .header("Authorization", "Zoho-oauthtoken " + accessToken)
.header("Content-Type", "application/json") .header("Content-Type", "application/json").POST(HttpRequest.BodyPublishers.ofString(requestBody))
.POST(HttpRequest.BodyPublishers.ofString(requestBody))
.build(); .build();
HttpResponse<String> response = httpClient.send( HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
request, HttpResponse.BodyHandlers.ofString());
logger.fine("├── Zoho Response:"); logger.fine("├── Zoho Response:");
logger.fine("│ ├── Status Code: " + response.statusCode()); logger.fine("│ ├── Status Code: " + response.statusCode());
logger.fine("│ ├── Response Body: " + response.body()); logger.fine("│ ├── Response Body: " + response.body());
if (response.statusCode() >= 300) { if (response.statusCode() >= 300) {
throw new PluginException(this.getClass().getName(), "Zoho API error", throw new PluginException(this.getClass().getName(), "Zoho API error",
"Failed to create invoice. Status code: " + response.statusCode() + "Failed to create invoice. Status code: " + response.statusCode() + ", Response: "
", Response: " + response.body()); + response.body());
} }
// Erfolgreiche Erstellung // Erfolgreiche Erstellung
ZohoContactCreateResponse createResponse = jsonb.fromJson(response.body(), ZohoContactCreateResponse createResponse = jsonb.fromJson(response.body(), ZohoContactCreateResponse.class);
ZohoContactCreateResponse.class);
if (createResponse != null) { if (createResponse != null) {
logger.info("│ ├── OK - contactID=" + createResponse.getContact().getContactId()); logger.info("│ ├── OK - contactID=" + createResponse.getContact().getContactId());
return createResponse.getContact(); return createResponse.getContact();
@ -277,25 +258,20 @@ public class ZohoAPIService {
ZohoInvoice zohoInvoice = new ZohoInvoice(); ZohoInvoice zohoInvoice = new ZohoInvoice();
// Basisinformationen // Basisinformationen
logger.fine("invoice.date=" + invoice.getItemValueDate("invoice.date"));
logger.fine("invoice.duedate=" + invoice.getItemValueDate("invoice.duedate"));
// LocalDate.ofInstant(date.toInstant(), ZoneId.systemDefault()) zohoInvoice.setInvoiceNumber(invoice.getItemValueString("invoice.number"));
zohoInvoice.setDate( zohoInvoice.setDate(
LocalDate.ofInstant(invoice.getItemValueDate("invoice.date").toInstant(), LocalDate.ofInstant(invoice.getItemValueDate("invoice.date").toInstant(), ZoneId.systemDefault()));
ZoneId.systemDefault()));
zohoInvoice.setDueDate( zohoInvoice.setDueDate(
LocalDate.ofInstant(invoice.getItemValueDate("invoice.duedate").toInstant(), LocalDate.ofInstant(invoice.getItemValueDate("invoice.duedate").toInstant(), ZoneId.systemDefault()));
ZoneId.systemDefault()));
zohoInvoice.setTotal(invoice.getItemValueDouble("invoice.total")); zohoInvoice.setTotal(invoice.getItemValueDouble("invoice.total"));
// Kunden-ID (muss angepasst werden je nachdem wie du sie speicherst) // Kunden-ID (muss angepasst werden je nachdem wie du sie speicherst)
zohoInvoice.setCustomerId(contactID); zohoInvoice.setCustomerId(contactID);
List<ItemCollection> positionen = InvoiceUtil.explodeChildList(invoice); List<ItemCollection> positionen = InvoiceUtil.explodeChildList(invoice);
List<ZohoInvoiceItem> lineItems = positionen.stream() List<ZohoInvoiceItem> lineItems = positionen.stream().map(item -> {
.map(item -> {
ItemCollection itemCol = (ItemCollection) item; ItemCollection itemCol = (ItemCollection) item;
ZohoInvoiceItem lineItem = new ZohoInvoiceItem(); ZohoInvoiceItem lineItem = new ZohoInvoiceItem();
lineItem.setName(itemCol.getItemValueString("datev.text")); lineItem.setName(itemCol.getItemValueString("datev.text"));
@ -303,8 +279,7 @@ public class ZohoAPIService {
lineItem.setRate(itemCol.getItemValueDouble("datev.umsatz")); lineItem.setRate(itemCol.getItemValueDouble("datev.umsatz"));
lineItem.setQuantity(1); lineItem.setQuantity(1);
return lineItem; return lineItem;
}) }).collect(Collectors.toList());
.collect(Collectors.toList());
zohoInvoice.setLineItems(lineItems); zohoInvoice.setLineItems(lineItems);
return zohoInvoice; return zohoInvoice;
@ -321,7 +296,9 @@ public class ZohoAPIService {
*/ */
public void attacheFileData(String invoiceId, FileData file, String uriPattern, String accessToken) public void attacheFileData(String invoiceId, FileData file, String uriPattern, String accessToken)
throws PluginException { throws PluginException {
String uri = ZOHO_API_BASE_URL + uriPattern + "/" + invoiceId + "/attachment" + String uri = zohoOAuthManager.getBaseURI() + uriPattern + "/" + //
invoiceId + //
"/attachment" + //
"?organization_id=" + zohoOAuthManager.getOrganization(); "?organization_id=" + zohoOAuthManager.getOrganization();
logger.info("│ ├── add attachment '" + file.getName() + "'..."); logger.info("│ ├── add attachment '" + file.getName() + "'...");
@ -332,29 +309,22 @@ public class ZohoAPIService {
String boundary = "----WebKitFormBoundary" + System.currentTimeMillis(); String boundary = "----WebKitFormBoundary" + System.currentTimeMillis();
// Build multipart request body // Build multipart request body
byte[] header = ("--" + boundary + "\r\n" + byte[] header = ("--" + boundary + "\r\n"
"Content-Disposition: form-data; name=\"attachment\"; filename=\"" + "Content-Disposition: form-data; name=\"attachment\"; filename=\"" + file.getName() + "\"\r\n"
+ file.getName() + "\"\r\n" + + "Content-Type: " + file.getContentType() + "\r\n\r\n").getBytes(StandardCharsets.UTF_8);
"Content-Type: " + file.getContentType() + "\r\n\r\n")
.getBytes(StandardCharsets.UTF_8);
byte[] footer = ("\r\n--" + boundary + "--\r\n").getBytes(StandardCharsets.UTF_8); byte[] footer = ("\r\n--" + boundary + "--\r\n").getBytes(StandardCharsets.UTF_8);
logger.fine("│ ├── content size=" + file.getContent().length); logger.fine("│ ├── content size=" + file.getContent().length);
HttpRequest.BodyPublisher bodyPublisher = HttpRequest.BodyPublishers.ofByteArrays( HttpRequest.BodyPublisher bodyPublisher = HttpRequest.BodyPublishers
Arrays.asList(header, file.getContent(), footer)); .ofByteArrays(Arrays.asList(header, file.getContent(), footer));
// Create and send the request // Create and send the request
HttpRequest request = HttpRequest.newBuilder() HttpRequest request = HttpRequest.newBuilder().uri(URI.create(uri))
.uri(URI.create(uri))
.header("Authorization", "Zoho-oauthtoken " + accessToken) .header("Authorization", "Zoho-oauthtoken " + accessToken)
.header("Content-Type", "multipart/form-data; boundary=" + boundary) .header("Content-Type", "multipart/form-data; boundary=" + boundary).POST(bodyPublisher).build();
.POST(bodyPublisher)
.build();
HttpResponse<String> response = httpClient.send( HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
request,
HttpResponse.BodyHandlers.ofString());
logger.fine("├── Zoho Response:"); logger.fine("├── Zoho Response:");
logger.fine("│ ├── Status Code: " + response.statusCode()); logger.fine("│ ├── Status Code: " + response.statusCode());
@ -362,8 +332,8 @@ public class ZohoAPIService {
if (response.statusCode() >= 300) { if (response.statusCode() >= 300) {
throw new PluginException(this.getClass().getName(), "Zoho API error", throw new PluginException(this.getClass().getName(), "Zoho API error",
"Failed to add attachment. Status code: " + response.statusCode() + "Failed to add attachment. Status code: " + response.statusCode() + ", Response: "
", Response: " + response.body()); + response.body());
} }
logger.info("│ ├── OK - Attachment added successfully"); logger.info("│ ├── OK - Attachment added successfully");

View file

@ -52,8 +52,8 @@ import jakarta.ws.rs.core.MediaType;
* verwenden! * verwenden!
* *
* Der Service stellt im Grunde nur eine Callback URL für einen normalen Zoho * Der Service stellt im Grunde nur eine Callback URL für einen normalen Zoho
* Client ein. Also wenn jemand einen solchen Token über den Browser * Client ein. Also wenn jemand einen solchen Token über den Browser anfordert.
* anfordert. Der URL muss dann odrt als CallBack URL angegeben sein! * Der URL muss dann odrt als CallBack URL angegeben sein!
* *
* Beispiel: * Beispiel:
* *
@ -90,8 +90,7 @@ public class ZohoOAuthCallbackService {
* *
* state=testing * state=testing
* code=1000.c27802b99c007ad8db93a5a7291cb15a.8cb60b6a51df13ea627ef3ac7c2e4791 * code=1000.c27802b99c007ad8db93a5a7291cb15a.8cb60b6a51df13ea627ef3ac7c2e4791
* location=eu * location=eu accounts-server=https%3A%2F%2Faccounts.zoho.eu
* accounts-server=https%3A%2F%2Faccounts.zoho.eu
* *
* *
* @param workflowgroup * @param workflowgroup
@ -102,11 +101,8 @@ public class ZohoOAuthCallbackService {
@GET @GET
@Path("/grant") @Path("/grant")
@Produces({ MediaType.TEXT_HTML }) @Produces({ MediaType.TEXT_HTML })
public String getGrantToken( public String getGrantToken(@QueryParam("state") String state, @QueryParam("code") String code,
@QueryParam("state") String state, @QueryParam("location") String location, @QueryParam("accounts-server") String accountsServer)
@QueryParam("code") String code,
@QueryParam("location") String location,
@QueryParam("accounts-server") String accountsServer)
throws QueryException { throws QueryException {
logger.info("├── Receive Grant Token from Zoho"); logger.info("├── Receive Grant Token from Zoho");

View file

@ -47,9 +47,7 @@ public class ZohoOAuthManager {
@PostConstruct @PostConstruct
void init() { void init() {
this.httpClient = HttpClient.newBuilder() this.httpClient = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10)).build();
.connectTimeout(Duration.ofSeconds(10))
.build();
this.jsonb = JsonbBuilder.create(); this.jsonb = JsonbBuilder.create();
} }
@ -87,6 +85,18 @@ public class ZohoOAuthManager {
return null; return null;
} }
public String getBaseURI() throws PluginException {
String uri = "";
ItemCollection zohoConfig = configService.loadConfiguration(ZohoController.ZOHO_CONFIGURATION, false);
if (zohoConfig != null) {
uri = zohoConfig.getItemValueString("zoho.uri");
if (!uri.endsWith("/")) {
uri = uri + "/";
}
}
return uri;
}
/** /**
* Step 3: Generate Access and Refresh Token * Step 3: Generate Access and Refresh Token
* *
@ -107,24 +117,21 @@ public class ZohoOAuthManager {
String clientID = zohoConfig.getItemValueString("zoho.clientid"); String clientID = zohoConfig.getItemValueString("zoho.clientid");
String clientSecret = zohoConfig.getItemValueString("zoho.clientsecret"); String clientSecret = zohoConfig.getItemValueString("zoho.clientsecret");
String requestUrl = "https://accounts.zoho.eu/oauth/v2/token" + String requestUrl = "https://accounts.zoho.eu/oauth/v2/token" + "?code="
"?code=" + URLEncoder.encode(code, StandardCharsets.UTF_8) + + URLEncoder.encode(code, StandardCharsets.UTF_8) + "&client_id="
"&client_id=" + URLEncoder.encode(clientID, StandardCharsets.UTF_8) + + URLEncoder.encode(clientID, StandardCharsets.UTF_8) + "&client_secret="
"&client_secret=" + URLEncoder.encode(clientSecret, StandardCharsets.UTF_8) + + URLEncoder.encode(clientSecret, StandardCharsets.UTF_8) + "&redirect_uri="
"&redirect_uri=" + URLEncoder.encode(redirectURI, StandardCharsets.UTF_8) + + URLEncoder.encode(redirectURI, StandardCharsets.UTF_8) + "&grant_type=authorization_code";
"&grant_type=authorization_code";
logger.fine("│ ├── Request URI=" + requestUrl); logger.fine("│ ├── Request URI=" + requestUrl);
HttpRequest request = HttpRequest.newBuilder() HttpRequest request = HttpRequest.newBuilder().uri(URI.create(requestUrl))
.uri(URI.create(requestUrl))
.header("Content-Type", "application/x-www-form-urlencoded") .header("Content-Type", "application/x-www-form-urlencoded")
.POST(HttpRequest.BodyPublishers.noBody()) // Wichtig: Kein Body! .POST(HttpRequest.BodyPublishers.noBody()) // Wichtig: Kein Body!
.build(); .build();
HttpResponse<String> response; HttpResponse<String> response;
response = httpClient.send(request, response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
HttpResponse.BodyHandlers.ofString());
logger.fine("│ ├── Response statusCode=" + response.statusCode()); logger.fine("│ ├── Response statusCode=" + response.statusCode());
if (response.statusCode() == 200) { if (response.statusCode() == 200) {
@ -132,23 +139,20 @@ public class ZohoOAuthManager {
logger.fine("│ ├── Response Body=" + response.body()); logger.fine("│ ├── Response Body=" + response.body());
if (response.body().contains("error")) { if (response.body().contains("error")) {
throw new PluginException(ZohoOAuthManager.class.getSimpleName(), throw new PluginException(ZohoOAuthManager.class.getSimpleName(), ZohoOAuthManager.ERROR_CONFIG,
ZohoOAuthManager.ERROR_CONFIG,
"Ungültiger Access Code!"); "Ungültiger Access Code!");
} }
TokenResponse tokenResponse = jsonb.fromJson(response.body(), TokenResponse tokenResponse = jsonb.fromJson(response.body(), TokenResponse.class);
TokenResponse.class);
saveTokenData(zohoConfig, tokenResponse); saveTokenData(zohoConfig, tokenResponse);
} else { } else {
throw new RuntimeException("Failed to get tokens: " + throw new RuntimeException("Failed to get tokens: " + response.statusCode() + " - " + response.body());
response.statusCode() + " - " + response.body());
} }
} catch (IOException | InterruptedException e) { } catch (IOException | InterruptedException e) {
throw new PluginException(ZohoOAuthManager.class.getSimpleName(), throw new PluginException(ZohoOAuthManager.class.getSimpleName(), ZohoOAuthManager.ERROR_CONFIG,
ZohoOAuthManager.ERROR_CONFIG, "Unable to resolve access tokens: " + e.getMessage()); "Unable to resolve access tokens: " + e.getMessage());
} }
} }
@ -168,26 +172,21 @@ public class ZohoOAuthManager {
String refreshToken = zohoConfig.getItemValueString("zoho.refreshToken"); String refreshToken = zohoConfig.getItemValueString("zoho.refreshToken");
try { try {
String requestUrl = "https://accounts.zoho.eu/oauth/v2/token" + String requestUrl = "https://accounts.zoho.eu/oauth/v2/token" + "?refresh_token="
"?refresh_token=" + URLEncoder.encode(refreshToken, StandardCharsets.UTF_8) + + URLEncoder.encode(refreshToken, StandardCharsets.UTF_8) + "&client_id="
"&client_id=" + URLEncoder.encode(clientID, StandardCharsets.UTF_8) + + URLEncoder.encode(clientID, StandardCharsets.UTF_8) + "&client_secret="
"&client_secret=" + URLEncoder.encode(clientSecret, + URLEncoder.encode(clientSecret, StandardCharsets.UTF_8) + "&grant_type=refresh_token";
StandardCharsets.UTF_8)
+
"&grant_type=refresh_token";
logger.fine("│ ├── Request URI=" + requestUrl); logger.fine("│ ├── Request URI=" + requestUrl);
HttpRequest request = HttpRequest.newBuilder() HttpRequest request = HttpRequest.newBuilder().uri(URI.create(requestUrl))
.uri(URI.create(requestUrl))
.header("Content-Type", "application/x-www-form-urlencoded") .header("Content-Type", "application/x-www-form-urlencoded")
.POST(HttpRequest.BodyPublishers.noBody()) // Wichtig: Kein Body! .POST(HttpRequest.BodyPublishers.noBody()) // Wichtig: Kein Body!
.build(); .build();
HttpResponse<String> response; HttpResponse<String> response;
response = httpClient.send(request, response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
HttpResponse.BodyHandlers.ofString());
logger.fine("│ ├── Response statusCode=" + response.statusCode()); logger.fine("│ ├── Response statusCode=" + response.statusCode());
if (response.statusCode() == 200) { if (response.statusCode() == 200) {
@ -195,19 +194,17 @@ public class ZohoOAuthManager {
// 1. Response-Logging für Debugging // 1. Response-Logging für Debugging
logger.fine("│ ├── Response Body=" + response.body()); logger.fine("│ ├── Response Body=" + response.body());
TokenResponse tokenResponse = jsonb.fromJson(response.body(), TokenResponse tokenResponse = jsonb.fromJson(response.body(), TokenResponse.class);
TokenResponse.class);
saveTokenData(zohoConfig, tokenResponse); saveTokenData(zohoConfig, tokenResponse);
return zohoConfig.getItemValueString("zoho.accessToken"); return zohoConfig.getItemValueString("zoho.accessToken");
} else { } else {
throw new RuntimeException("Failed to get tokens: " + throw new RuntimeException("Failed to get tokens: " + response.statusCode() + " - " + response.body());
response.statusCode() + " - " + response.body());
} }
} catch (IOException | InterruptedException e) { } catch (IOException | InterruptedException e) {
throw new PluginException(ZohoOAuthManager.class.getSimpleName(), throw new PluginException(ZohoOAuthManager.class.getSimpleName(), ZohoOAuthManager.ERROR_CONFIG,
ZohoOAuthManager.ERROR_CONFIG, "Unable to refresh access tokens: " + e.getMessage()); "Unable to refresh access tokens: " + e.getMessage());
} }
} }

View file

@ -114,8 +114,7 @@ public class AnalyticController implements Serializable {
} }
/** /**
* Computes an analytic value. The method cache the value in the * Computes an analytic value. The method cache the value in the item key.
* item key.
* *
* An observer controller is responsible to cache or reset the cached values if * An observer controller is responsible to cache or reset the cached values if
* needed. * needed.

View file

@ -32,7 +32,8 @@
<h:panelGroup layout="block" styleClass="imixs-body" id="analyse_panel"> <h:panelGroup layout="block" styleClass="imixs-body" id="analyse_panel">
<div class="imixs-form-panel"> <div class="imixs-form-panel">
<div class="ui-state-highlight ui-corner-all" style="margin-bottom: 10px; padding: .5em;"> <div class="ui-state-highlight ui-corner-all" style="margin-bottom: 10px; padding: .5em;">
<p>Der folgende Report exportiert <i>Ausgangsrechnungen</i>, in eine Excel Datei. Der Export ist auf maximal 3000 Einträge pro Tabelle beschränkt <p>Der folgende Report exportiert <i>Ausgangsrechnungen</i>, in eine Excel Datei. Der
Export ist auf maximal 3000 Einträge pro Tabelle beschränkt
</p> </p>
</div> </div>
<div class="imixs-form-section-2"> <div class="imixs-form-section-2">
@ -68,9 +69,11 @@
<h:selectOneMenu noSelectionOption="true" <h:selectOneMenu noSelectionOption="true"
value="#{excelExportController.filter.item['space.ref']}"> value="#{excelExportController.filter.item['space.ref']}">
<f:selectItem itemLabel=" - " itemValue="-"></f:selectItem> <f:selectItem itemLabel=" - " itemValue="-"></f:selectItem>
<c:forEach items="#{spaceRefFormController.getSpaces(workflowController.workitem, options)}" <c:forEach
items="#{spaceRefFormController.getSpaces(workflowController.workitem, options)}"
var="space"> var="space">
<f:selectItem itemLabel="#{space.item['name']}" itemValue="#{space.item['$UniqueID']}"> <f:selectItem itemLabel="#{space.item['name']}"
itemValue="#{space.item['$UniqueID']}">
</f:selectItem> </f:selectItem>
</c:forEach> </c:forEach>
</h:selectOneMenu> </h:selectOneMenu>
@ -82,7 +85,7 @@
Debitor: Debitor:
</dt> </dt>
<dd> <dd>
<ui:include src="/pages/workitems/parts/alexander/debitor_search.xhtml" > <ui:include src="/pages/workitems/parts/alexander/businesspartner_search.xhtml">
<ui:param name="workitem" value="#{workflowController.workitem}" /> <ui:param name="workitem" value="#{workflowController.workitem}" />
<ui:param name="item" value="#{excelExportController.item}" /> <ui:param name="item" value="#{excelExportController.item}" />
</ui:include> </ui:include>
@ -94,8 +97,7 @@
Positionsnummer: Positionsnummer:
</dt> </dt>
<dd> <dd>
<h:inputText <h:inputText value="#{excelExportController.filter.item['invoice.text']}">
value="#{excelExportController.filter.item['invoice.text']}">
</h:inputText> </h:inputText>
</dd> </dd>
</dl> </dl>

View file

@ -19,13 +19,13 @@
/*]]>*/ /*]]>*/
</script> </script>
<h:form id="import_form_id"> <h:form id="import_form_id" autocomplete="off">
<div class="imixs-form"> <div class="imixs-form">
<div class="imixs-header"> <div class="imixs-header">
<h1>ZOHO - API </h1> <h1>ZOHO - API </h1>
<!-- ########## Error ########## --> <!-- ########## Error ########## -->
<ui:include src="/error_message.xhtml" />
</div> </div>
<div class="imixs-body"> <div class="imixs-body">
@ -41,8 +41,27 @@
<!-- API Tokens --> <!-- API Tokens -->
<div class="imixs-form-section-2"> <div class="imixs-form-section-2">
<dl> <dl>
<dt>Client ID</dt> <dt>API Base URL <span class="imixs-required">*</span></dt>
<dd>
<h:inputText required="true"
value="#{zohoController.workitem.item['zoho.uri']}">
</h:inputText>
</dd>
</dl>
<dl>
<dt>Organization <span class="imixs-required">*</span></dt>
<dd>
<h:inputText required="true"
value="#{zohoController.workitem.item['zoho.organization']}">
</h:inputText>
</dd>
</dl>
<dl>
<dt>Client ID <span class="imixs-required">*</span></dt>
<dd> <dd>
<h:inputText required="false" <h:inputText required="false"
value="#{zohoController.workitem.item['zoho.clientid']}"> value="#{zohoController.workitem.item['zoho.clientid']}">
@ -51,27 +70,20 @@
</dd> </dd>
</dl> </dl>
<dl> <dl>
<dt>Client Secret</dt> <dt>Client Secret <span class="imixs-required">*</span></dt>
<dd> <dd>
<h:inputSecret required="false" redisplay="true" <h:inputSecret required="false" redisplay="true"
value="#{zohoController.workitem.item['zoho.clientsecret']}"> value="#{zohoController.workitem.item['zoho.clientsecret']}">
</h:inputSecret> </h:inputSecret>
</dd> </dd>
</dl> </dl>
<dl>
<dt>Organization</dt>
<dd>
<h:inputText required="false"
value="#{zohoController.workitem.item['zoho.organization']}">
</h:inputText>
</dd>
</dl>
</div> </div>
<!-- Grant Token --> <!-- Grant Token -->
<div class="imixs-form-section-2"> <div class="imixs-form-section-2">
<dl> <dl>
<dt>Access Code</dt> <dt>Access Code <span class="imixs-required">*</span> </dt>
<dd> <dd>
<h:inputSecret required="false" redisplay="true" <h:inputSecret required="false" redisplay="true"
value="#{zohoController.workitem.item['zoho.code']}"> value="#{zohoController.workitem.item['zoho.code']}">

View file

@ -59,8 +59,8 @@ public class TestIMAPOutlookOAuth {
/* /*
* Neue Daten * Neue Daten
* *
* Wert: zDL8Q~iEy8AX~dEmIOHrVv3aBTCZLrHIFmShibZ_ * Wert: zDL8Q~iEy8AX~dEmIOHrVv3aBTCZLrHIFmShibZ_ Geheime ID:
* Geheime ID: bcaa558a-3300-4498-94e1-2267f418afd5 * bcaa558a-3300-4498-94e1-2267f418afd5
*/ */
@Test @Test

View file

@ -66,34 +66,20 @@ public class DueDateBugfix {
* Erledigte * Erledigte
* *
* *
* EXTF_202301_202302030602_1295 * EXTF_202301_202302030602_1295 EXTF_202301_202302060601_1296
* EXTF_202301_202302060601_1296 * EXTF_202301_202302070603_1297 EXTF_202301_202302080603_1298
* EXTF_202301_202302070603_1297 * EXTF_202301_202302090604_1299 EXTF_202301_202302100603_1300
* EXTF_202301_202302080603_1298 * EXTF_202301_202302130601_1301 EXTF_202301_202302140604_1302
* EXTF_202301_202302090604_1299 * EXTF_202301_202302141840_1303 EXTF_202301_202302160944_1304
* EXTF_202301_202302100603_1300 * EXTF_202302_202302020601_1294 EXTF_202302_202302030602_1295
* EXTF_202301_202302130601_1301 * EXTF_202302_202302060601_1296 EXTF_202302_202302070603_1297
* EXTF_202301_202302140604_1302 * EXTF_202302_202302080603_1298 EXTF_202302_202302090604_1299
* EXTF_202301_202302141840_1303 * EXTF_202302_202302100604_1300 EXTF_202302_202302130601_1301
* EXTF_202301_202302160944_1304 * EXTF_202302_202302140604_1302 EXTF_202302_202302141840_1303
* EXTF_202302_202302020601_1294 * EXTF_202302_202302160944_1304 EXTF_202303_202302070603_1297
* EXTF_202302_202302030602_1295 * EXTF_202303_202302080603_1298 EXTF_202303_202302090604_1299
* EXTF_202302_202302060601_1296 * EXTF_202303_202302100603_1300 EXTF_202303_202302130601_1301
* EXTF_202302_202302070603_1297 * EXTF_202303_202302140604_1302 EXTF_202303_202302141840_1303
* EXTF_202302_202302080603_1298
* EXTF_202302_202302090604_1299
* EXTF_202302_202302100604_1300
* EXTF_202302_202302130601_1301
* EXTF_202302_202302140604_1302
* EXTF_202302_202302141840_1303
* EXTF_202302_202302160944_1304
* EXTF_202303_202302070603_1297
* EXTF_202303_202302080603_1298
* EXTF_202303_202302090604_1299
* EXTF_202303_202302100603_1300
* EXTF_202303_202302130601_1301
* EXTF_202303_202302140604_1302
* EXTF_202303_202302141840_1303
* EXTF_202303_202302160944_1304 * EXTF_202303_202302160944_1304
* *
*/ */

View file

@ -17,8 +17,8 @@ import jakarta.ws.rs.core.Cookie;
import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.Response;
/** /**
* Test to request data via a JSESSIONID only.... * Test to request data via a JSESSIONID only.... This technique is used by the
* This technique is used by the DataRestService * DataRestService
* *
*/ */
@Ignore @Ignore
@ -36,11 +36,8 @@ public class TestDataClient {
@BeforeEach @BeforeEach
public void setup() throws PluginException { public void setup() throws PluginException {
// Create a Cookie object with a name and a value // Create a Cookie object with a name and a value
Cookie cookie = new Cookie.Builder("JSESSIONID") Cookie cookie = new Cookie.Builder("JSESSIONID").value(sessionID).path("/")
.value(sessionID) .domain("alexander-logistics.office-workflow.de").build();
.path("/")
.domain("alexander-logistics.office-workflow.de")
.build();
client = new DocumentClient(restAPI.trim()); client = new DocumentClient(restAPI.trim());
CookieAuthenticator aut = new CookieAuthenticator(cookie); CookieAuthenticator aut = new CookieAuthenticator(cookie);

View file

@ -52,9 +52,7 @@ public class TestUpdateTokens {
@BeforeEach @BeforeEach
public void setup() throws PluginException, ModelException { public void setup() throws PluginException, ModelException {
logger.info("setup test environment ..."); logger.info("setup test environment ...");
this.httpClient = HttpClient.newBuilder() this.httpClient = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10)).build();
.connectTimeout(Duration.ofSeconds(10))
.build();
this.jsonb = JsonbBuilder.create(); this.jsonb = JsonbBuilder.create();
} }
@ -72,25 +70,22 @@ public class TestUpdateTokens {
code = "1000.48bd85a3965cf724f4691757cfd8d93b.8ae5e3ee7d70f90d307da4d289a9b067"; code = "1000.48bd85a3965cf724f4691757cfd8d93b.8ae5e3ee7d70f90d307da4d289a9b067";
try { try {
String requestUrl = "https://accounts.zoho.eu/oauth/v2/token" + String requestUrl = "https://accounts.zoho.eu/oauth/v2/token" + "?code="
"?code=" + URLEncoder.encode(code, StandardCharsets.UTF_8) + + URLEncoder.encode(code, StandardCharsets.UTF_8) + "&client_id="
"&client_id=" + URLEncoder.encode(clientID, StandardCharsets.UTF_8) + + URLEncoder.encode(clientID, StandardCharsets.UTF_8) + "&client_secret="
"&client_secret=" + URLEncoder.encode(clientSecret, StandardCharsets.UTF_8) + + URLEncoder.encode(clientSecret, StandardCharsets.UTF_8) + "&redirect_uri="
"&redirect_uri=" + URLEncoder.encode(redirectURI, StandardCharsets.UTF_8) + + URLEncoder.encode(redirectURI, StandardCharsets.UTF_8) + "&grant_type=authorization_code";
"&grant_type=authorization_code";
logger.info("│ ├── Request URI=" + requestUrl); logger.info("│ ├── Request URI=" + requestUrl);
HttpRequest request = HttpRequest.newBuilder() HttpRequest request = HttpRequest.newBuilder().uri(URI.create(requestUrl))
.uri(URI.create(requestUrl))
.header("Content-Type", "application/x-www-form-urlencoded") .header("Content-Type", "application/x-www-form-urlencoded")
.POST(HttpRequest.BodyPublishers.noBody()) // Wichtig: Kein Body! .POST(HttpRequest.BodyPublishers.noBody()) // Wichtig: Kein Body!
.build(); .build();
HttpResponse<String> response; HttpResponse<String> response;
response = httpClient.send(request, response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
HttpResponse.BodyHandlers.ofString());
logger.info("│ ├── Response statusCode=" + response.statusCode()); logger.info("│ ├── Response statusCode=" + response.statusCode());
if (response.statusCode() == 200) { if (response.statusCode() == 200) {
@ -101,8 +96,7 @@ public class TestUpdateTokens {
assertNotNull(response.body()); assertNotNull(response.body());
assertFalse(response.body().contains("error")); assertFalse(response.body().contains("error"));
TokenResponse tokenResponse = jsonb.fromJson(response.body(), TokenResponse tokenResponse = jsonb.fromJson(response.body(), TokenResponse.class);
TokenResponse.class);
accessToken = tokenResponse.getAccess_token(); accessToken = tokenResponse.getAccess_token();
assertNotNull(accessToken); assertNotNull(accessToken);
@ -111,8 +105,7 @@ public class TestUpdateTokens {
logger.info("│ ├── refreshToken=" + refreshToken); logger.info("│ ├── refreshToken=" + refreshToken);
logger.info("│ ├── expires_in=" + tokenResponse.getExpires_in()); logger.info("│ ├── expires_in=" + tokenResponse.getExpires_in());
} else { } else {
throw new RuntimeException("Failed to get tokens: " + throw new RuntimeException("Failed to get tokens: " + response.statusCode() + " - " + response.body());
response.statusCode() + " - " + response.body());
} }
} catch (IOException | InterruptedException e) { } catch (IOException | InterruptedException e) {
@ -136,24 +129,21 @@ public class TestUpdateTokens {
refreshToken = "1000.cf458816e97f848df8134ce1c7e923a1.5b9dbdee317e35d7f0c619e7648d55ff"; refreshToken = "1000.cf458816e97f848df8134ce1c7e923a1.5b9dbdee317e35d7f0c619e7648d55ff";
try { try {
String requestUrl = "https://accounts.zoho.eu/oauth/v2/token" + String requestUrl = "https://accounts.zoho.eu/oauth/v2/token" + "?refresh_token="
"?refresh_token=" + URLEncoder.encode(refreshToken, StandardCharsets.UTF_8) + + URLEncoder.encode(refreshToken, StandardCharsets.UTF_8) + "&client_id="
"&client_id=" + URLEncoder.encode(clientID, StandardCharsets.UTF_8) + + URLEncoder.encode(clientID, StandardCharsets.UTF_8) + "&client_secret="
"&client_secret=" + URLEncoder.encode(clientSecret, StandardCharsets.UTF_8) + + URLEncoder.encode(clientSecret, StandardCharsets.UTF_8) + "&grant_type=refresh_token";
"&grant_type=refresh_token";
logger.info("│ ├── Request URI=" + requestUrl); logger.info("│ ├── Request URI=" + requestUrl);
HttpRequest request = HttpRequest.newBuilder() HttpRequest request = HttpRequest.newBuilder().uri(URI.create(requestUrl))
.uri(URI.create(requestUrl))
.header("Content-Type", "application/x-www-form-urlencoded") .header("Content-Type", "application/x-www-form-urlencoded")
.POST(HttpRequest.BodyPublishers.noBody()) // Wichtig: Kein Body! .POST(HttpRequest.BodyPublishers.noBody()) // Wichtig: Kein Body!
.build(); .build();
HttpResponse<String> response; HttpResponse<String> response;
response = httpClient.send(request, response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
HttpResponse.BodyHandlers.ofString());
logger.info("│ ├── Response statusCode=" + response.statusCode()); logger.info("│ ├── Response statusCode=" + response.statusCode());
if (response.statusCode() == 200) { if (response.statusCode() == 200) {
@ -161,15 +151,13 @@ public class TestUpdateTokens {
// 1. Response-Logging für Debugging // 1. Response-Logging für Debugging
logger.info("│ ├── Response Body=" + response.body()); logger.info("│ ├── Response Body=" + response.body());
TokenResponse tokenResponse = jsonb.fromJson(response.body(), TokenResponse tokenResponse = jsonb.fromJson(response.body(), TokenResponse.class);
TokenResponse.class);
String accessToken = tokenResponse.getAccess_token(); String accessToken = tokenResponse.getAccess_token();
logger.info("│ ├── accessToken=" + accessToken); logger.info("│ ├── accessToken=" + accessToken);
logger.info("│ ├── expires_in=" + tokenResponse.getExpires_in()); logger.info("│ ├── expires_in=" + tokenResponse.getExpires_in());
} else { } else {
throw new RuntimeException("Failed to get tokens: " + throw new RuntimeException("Failed to get tokens: " + response.statusCode() + " - " + response.body());
response.statusCode() + " - " + response.body());
} }
} catch (IOException | InterruptedException e) { } catch (IOException | InterruptedException e) {
@ -196,15 +184,10 @@ public class TestUpdateTokens {
String url = "https://www.zohoapis.eu/books/v3/organizations"; String url = "https://www.zohoapis.eu/books/v3/organizations";
HttpRequest request = HttpRequest.newBuilder() HttpRequest request = HttpRequest.newBuilder().uri(URI.create(url)).GET()
.uri(URI.create(url)) .header("Authorization", "Zoho-oauthtoken " + accessToken).build();
.GET()
.header("Authorization", "Zoho-oauthtoken " + accessToken)
.build();
HttpResponse<String> response = httpClient.send( HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
request,
HttpResponse.BodyHandlers.ofString());
logger.info("│ ├── Response statusCode=" + response.statusCode()); logger.info("│ ├── Response statusCode=" + response.statusCode());
@ -227,8 +210,8 @@ public class TestUpdateTokens {
* Token * Token
* *
* *
* curl --request GET \ * curl --request GET \ --url
* --url 'https://www.zohoapis.com/books/v3/invoices?organization_id=10234695' \ * 'https://www.zohoapis.com/books/v3/invoices?organization_id=10234695' \
* --header 'Authorization: Zoho-oauthtoken * --header 'Authorization: Zoho-oauthtoken
* 1000.41d9xxxxxxxxxxxxxxxxxxxxxxxxc2d1.8fccxxxxxxxxxxxxxxxxxxxxxxxx125f' * 1000.41d9xxxxxxxxxxxxxxxxxxxxxxxxc2d1.8fccxxxxxxxxxxxxxxxxxxxxxxxx125f'
* *
@ -244,15 +227,10 @@ public class TestUpdateTokens {
String url = "https://www.zohoapis.eu/books/v3/invoices?organization_id=20105697367"; String url = "https://www.zohoapis.eu/books/v3/invoices?organization_id=20105697367";
HttpRequest request = HttpRequest.newBuilder() HttpRequest request = HttpRequest.newBuilder().uri(URI.create(url)).GET()
.uri(URI.create(url)) .header("Authorization", "Zoho-oauthtoken " + accessToken).build();
.GET()
.header("Authorization", "Zoho-oauthtoken " + accessToken)
.build();
HttpResponse<String> response = httpClient.send( HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
request,
HttpResponse.BodyHandlers.ofString());
logger.info("│ ├── Response statusCode=" + response.statusCode()); logger.info("│ ├── Response statusCode=" + response.statusCode());

View file

@ -43,15 +43,15 @@
"country" : "", "country" : "",
"phone" : "", "phone" : "",
"created_by" : "Ralph Soika", "created_by" : "Ralph Soika",
"total": 50.00, "total" : 50.0,
"balance": 50.00, "balance" : 50.0,
"payment_expected_date" : "", "payment_expected_date" : "",
"custom_fields" : [ ], "custom_fields" : [ ],
"custom_field_hash" : { }, "custom_field_hash" : { },
"tags" : [ ], "tags" : [ ],
"salesperson_name" : "", "salesperson_name" : "",
"shipping_charge": 0.00, "shipping_charge" : 0.0,
"adjustment": 0.00, "adjustment" : 0.0,
"created_time" : "2025-04-21T13:36:26+0200", "created_time" : "2025-04-21T13:36:26+0200",
"last_modified_time" : "2025-04-21T13:42:56+0200", "last_modified_time" : "2025-04-21T13:42:56+0200",
"updated_time" : "2025-04-21T13:42:56+0200", "updated_time" : "2025-04-21T13:42:56+0200",
@ -79,9 +79,9 @@
"template_id" : "777249000000000103", "template_id" : "777249000000000103",
"documents" : "", "documents" : "",
"salesperson_id" : "", "salesperson_id" : "",
"write_off_amount": 0.00, "write_off_amount" : 0.0,
"exchange_rate": 1.00, "exchange_rate" : 1.0,
"unprocessed_payment_amount": 0.00 "unprocessed_payment_amount" : 0.0
} }
], ],
"page_context" : { "page_context" : {

39
pom.xml
View file

@ -128,8 +128,45 @@
<encoding>${project.build.sourceEncoding}</encoding> <encoding>${project.build.sourceEncoding}</encoding>
</configuration> </configuration>
</plugin> </plugin>
</plugins>
<!-- Imixs Code Formatter and Checkstyle -->
<plugin>
<groupId>net.revelc.code.formatter</groupId>
<artifactId>formatter-maven-plugin</artifactId>
<version>2.26.0</version>
<configuration>
<configFile>
https://raw.githubusercontent.com/imixs/imixs-workflow/refs/heads/master/imixs-code-style.xml</configFile>
<lineEnding>LF</lineEnding>
</configuration>
<executions>
<execution>
<phase>validate</phase>
<goals>
<goal>format</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<configLocation>
https://raw.githubusercontent.com/imixs/imixs-workflow/master/imixs-checkstyle-8.44.xml</configLocation>
<failOnViolation>true</failOnViolation>
</configuration>
<executions>
<execution>
<phase>verify</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<finalName>office-alexander-logistics</finalName> <finalName>office-alexander-logistics</finalName>
</build> </build>
<dependencyManagement> <dependencyManagement>

View file

@ -74,6 +74,7 @@
<bpmn2:flowNodeRef>event_kT136g</bpmn2:flowNodeRef> <bpmn2:flowNodeRef>event_kT136g</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>event_jkHxJA</bpmn2:flowNodeRef> <bpmn2:flowNodeRef>event_jkHxJA</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>event_zzWQSA</bpmn2:flowNodeRef> <bpmn2:flowNodeRef>event_zzWQSA</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>event_k4Ms1Q</bpmn2:flowNodeRef>
</bpmn2:lane> </bpmn2:lane>
</bpmn2:laneSet> </bpmn2:laneSet>
<bpmn2:task id="task_ToMZaQ" imixs:processid="1000" name="Neuanlage"> <bpmn2:task id="task_ToMZaQ" imixs:processid="1000" name="Neuanlage">
@ -155,6 +156,7 @@
<bpmn2:outgoing>sequenceFlow_03b60w</bpmn2:outgoing> <bpmn2:outgoing>sequenceFlow_03b60w</bpmn2:outgoing>
<bpmn2:incoming>sequenceFlow_u8PLGw</bpmn2:incoming> <bpmn2:incoming>sequenceFlow_u8PLGw</bpmn2:incoming>
<bpmn2:incoming>sequenceFlow_ZTiO7w</bpmn2:incoming> <bpmn2:incoming>sequenceFlow_ZTiO7w</bpmn2:incoming>
<bpmn2:incoming>sequenceFlow_gIdPow</bpmn2:incoming>
</bpmn2:task> </bpmn2:task>
<bpmn2:task id="task_B8Pi7A" imixs:processid="1800" name="Locked"> <bpmn2:task id="task_B8Pi7A" imixs:processid="1800" name="Locked">
<bpmn2:extensionElements> <bpmn2:extensionElements>
@ -719,7 +721,40 @@
<bpmn2:sequenceFlow id="sequenceFlow_ZTiO7w" sourceRef="event_zzWQSA" targetRef="task_Q80A1Q"> <bpmn2:sequenceFlow id="sequenceFlow_ZTiO7w" sourceRef="event_zzWQSA" targetRef="task_Q80A1Q">
<bpmn2:documentation id="documentation_S2i1Tg"/> <bpmn2:documentation id="documentation_S2i1Tg"/>
</bpmn2:sequenceFlow> </bpmn2:sequenceFlow>
<bpmn2:intermediateCatchEvent id="event_k4Ms1Q" imixs:activityid="30" name="Excel Export">
<bpmn2:extensionElements>
<imixs:item name="rtfresultlog" type="xs:string">
<imixs:value><![CDATA[Analysis data archived and Excel report generated]]></imixs:value>
</imixs:item>
<imixs:item name="keypublicresult" type="xs:string">
<imixs:value><![CDATA[1]]></imixs:value>
</imixs:item>
<imixs:item name="txtactivityresult" type="xs:string">
<imixs:value><![CDATA[<soa name="excel-export">
<textblock>SOA Template</textblock>
<template>soa_template.xlsx</template>
<target-name>soa_<itemvalue>dbtr.number</itemvalue>_<itemvalue format="yyyy-MM-dd">$lasteventdate</itemvalue>.xlsx</target-name>
</soa>
<poi-update name="findreplace">
<find>F6</find>
<replace><itemvalue format="dd.MM.yyyy">$lasteventdate</itemvalue></replace>
<type>date</type>
</poi-update>
<poi-update name="findreplace">
<find>A7</find>
<replace><itemvalue>dbtr.name</itemvalue></replace>
</poi-update>]]></imixs:value>
</imixs:item>
</bpmn2:extensionElements>
<bpmn2:documentation id="documentation_r37NEQ"/>
<bpmn2:signalEventDefinition id="signalEventDefinition_rRKGpw" signalRef="signal_1"/>
<bpmn2:outgoing>sequenceFlow_gIdPow</bpmn2:outgoing>
</bpmn2:intermediateCatchEvent>
<bpmn2:sequenceFlow id="sequenceFlow_gIdPow" sourceRef="event_k4Ms1Q" targetRef="task_Q80A1Q">
<bpmn2:documentation id="documentation_q0cGQQ"/>
</bpmn2:sequenceFlow>
</bpmn2:process> </bpmn2:process>
<bpmn2:signal id="signal_1" name="com.alexanderlogistics.AGLAnalyticExcelAdapterDebitor"/>
<bpmndi:BPMNDiagram id="BPMNDiagram_1" name="OpenBPMN Diagram"> <bpmndi:BPMNDiagram id="BPMNDiagram_1" name="OpenBPMN Diagram">
<bpmndi:BPMNPlane bpmnElement="collaboration_1" id="BPMNPlane_1"> <bpmndi:BPMNPlane bpmnElement="collaboration_1" id="BPMNPlane_1">
<bpmndi:BPMNShape bpmnElement="event_WmkBvw" id="BPMNShape_heefeQ"> <bpmndi:BPMNShape bpmnElement="event_WmkBvw" id="BPMNShape_heefeQ">
@ -1070,6 +1105,18 @@
<di:waypoint x="714.0" y="465.0"/> <di:waypoint x="714.0" y="465.0"/>
<di:waypoint x="690.0" y="465.0"/> <di:waypoint x="690.0" y="465.0"/>
</bpmndi:BPMNEdge> </bpmndi:BPMNEdge>
<bpmndi:BPMNShape bpmnElement="event_k4Ms1Q" id="BPMNShape_m0PeLw">
<dc:Bounds height="36.0" width="36.0" x="827.0" y="357.0"/>
<bpmndi:BPMNLabel id="BPMNLabel_fOaAtQ">
<dc:Bounds height="20.0" width="100.0" x="795.0" y="396.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="sequenceFlow_gIdPow" id="BPMNEdge_QW8gQQ" sourceElement="BPMNShape_m0PeLw" targetElement="BPMNShape_7JA0nw">
<di:waypoint x="827.0" y="375.0"/>
<di:waypoint x="759.0" y="375.0"/>
<di:waypoint x="759.0" y="465.0"/>
<di:waypoint x="690.0" y="465.0"/>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane> </bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram> </bpmndi:BPMNDiagram>
</bpmn2:definitions> </bpmn2:definitions>