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

@ -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
* *
@ -33,176 +32,173 @@ import jakarta.inject.Named;
@ConversationScoped @ConversationScoped
public class AGLAnalyticControllerOPListe implements Serializable { public class AGLAnalyticControllerOPListe implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private static Logger logger = Logger.getLogger(AnalyticController.class.getName()); private static Logger logger = Logger.getLogger(AnalyticController.class.getName());
@Inject @Inject
protected DocumentService documentService; protected DocumentService documentService;
@Inject @Inject
protected AGLConfigController aglConfigController; protected AGLConfigController aglConfigController;
@Inject @Inject
protected WorkflowController workflowController; protected WorkflowController workflowController;
@Inject @Inject
TeamService teamService; TeamService teamService;
@Inject @Inject
ResourceBundleHandler resourceBundleHandler; ResourceBundleHandler resourceBundleHandler;
TreeMap<String, DebitorStatistikData> stats = null; TreeMap<String, DebitorStatistikData> stats = null;
String departmentID; String departmentID;
String departmentIDLast = null; String departmentIDLast = null;
// String departmentName; // String departmentName;
ItemCollection department; ItemCollection department;
int countAll = 0; int countAll = 0;
int countOpen = 0; int countOpen = 0;
int countDue = 0; int countDue = 0;
int countDunning = 0; int countDunning = 0;
boolean refreshCalculation = false; boolean refreshCalculation = false;
public void onEvent(@Observes AnalyticEvent event) { public void onEvent(@Observes AnalyticEvent event) {
if (!event.getWorkitem().getModelVersion().contains("analyse-opliste-")) { if (!event.getWorkitem().getModelVersion().contains("analyse-opliste-")) {
// no op // no op
return; return;
} }
department = null; department = null;
departmentID = event.getWorkitem().getItemValueString("space.ref"); departmentID = event.getWorkitem().getItemValueString("space.ref");
if (departmentIDLast == null || !departmentID.equals(departmentIDLast)) { if (departmentIDLast == null || !departmentID.equals(departmentIDLast)) {
department = documentService.load(departmentID); department = documentService.load(departmentID);
if (department != null) { if (department != null) {
event.getWorkitem().setItemValue("space.name", department.getItemValueString("name")); event.getWorkitem().setItemValue("space.name", department.getItemValueString("name"));
} }
departmentIDLast = departmentID; departmentIDLast = departmentID;
resetStats(event); resetStats(event);
calculateStats(); calculateStats();
refreshCalculation = true; refreshCalculation = true;
} }
// use cache? // use cache?
if (event.getWorkitem().hasItem(event.getKey())) { if (event.getWorkitem().hasItem(event.getKey())) {
// logger.info(" use cache for " + event.getKey()); // logger.info(" use cache for " + event.getKey());
// no op // no op
return; return;
} }
String link = "/pages/workitems/worklist.xhtml"; String link = "/pages/workitems/worklist.xhtml";
logger.fine("process ref=" + workflowController.getWorkitem().getItemValueString("process.ref")); logger.fine("process ref=" + workflowController.getWorkitem().getItemValueString("process.ref"));
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() // + "&modelversion=rechnungsausgang*" link = link + "?processref=" + process.getUniqueID() // + "&modelversion=rechnungsausgang*"
+ "&spaceref=" + departmentID; + "&spaceref=" + departmentID;
} }
if ("analytic.invoices.count.all".equals(event.getKey())) { if ("analytic.invoices.count.all".equals(event.getKey())) {
event.setValue("" + countAll); event.setValue("" + countAll);
event.setLabel(resourceBundleHandler.findMessage("oplist.invoices")); event.setLabel(resourceBundleHandler.findMessage("oplist.invoices"));
event.setDescription(resourceBundleHandler.findMessage("oplist.invoices.description.all")); event.setDescription(resourceBundleHandler.findMessage("oplist.invoices.description.all"));
event.getWorkitem().setItemValue("invoices.total", event.getValue()); event.getWorkitem().setItemValue("invoices.total", event.getValue());
event.setLink(link); event.setLink(link);
} }
if ("analytic.invoices.count.open".equals(event.getKey())) { if ("analytic.invoices.count.open".equals(event.getKey())) {
event.setValue("" + (countOpen)); event.setValue("" + (countOpen));
event.setLabel(resourceBundleHandler.findMessage("oplist.invoices")); event.setLabel(resourceBundleHandler.findMessage("oplist.invoices"));
event.setDescription(resourceBundleHandler.findMessage("oplist.invoices.description.open")); event.setDescription(resourceBundleHandler.findMessage("oplist.invoices.description.open"));
event.getWorkitem().setItemValue("invoices.total.open", event.getValue()); event.getWorkitem().setItemValue("invoices.total.open", event.getValue());
event.setLink(link); event.setLink(link);
} }
if ("analytic.invoices.count.due".equals(event.getKey())) { if ("analytic.invoices.count.due".equals(event.getKey())) {
event.setValue("" + countDue); event.setValue("" + countDue);
event.setLabel(resourceBundleHandler.findMessage("oplist.invoices")); event.setLabel(resourceBundleHandler.findMessage("oplist.invoices"));
event.setDescription(resourceBundleHandler.findMessage("oplist.invoices.description.due")); event.setDescription(resourceBundleHandler.findMessage("oplist.invoices.description.due"));
event.getWorkitem().setItemValue("invoices.total.due", event.getValue()); event.getWorkitem().setItemValue("invoices.total.due", event.getValue());
event.setLink(link); event.setLink(link);
} }
if ("analytic.invoices.count.dunning".equals(event.getKey())) { if ("analytic.invoices.count.dunning".equals(event.getKey())) {
event.setValue("" + countDunning); event.setValue("" + countDunning);
event.setLabel(resourceBundleHandler.findMessage("oplist.invoices")); event.setLabel(resourceBundleHandler.findMessage("oplist.invoices"));
event.setDescription(resourceBundleHandler.findMessage("oplist.invoices.description.dunning")); event.setDescription(resourceBundleHandler.findMessage("oplist.invoices.description.dunning"));
event.getWorkitem().setItemValue("invoices.total.dunning", event.getValue()); event.getWorkitem().setItemValue("invoices.total.dunning", event.getValue());
event.setLink(link); event.setLink(link);
} }
} }
/** /**
* Reset cached analytic keys * Reset cached analytic keys
*/ */
private void resetStats(AnalyticEvent event) { private void resetStats(AnalyticEvent event) {
event.getWorkitem().removeItem("analytic.invoices.count.all"); event.getWorkitem().removeItem("analytic.invoices.count.all");
event.getWorkitem().removeItem("analytic.invoices.count.open"); event.getWorkitem().removeItem("analytic.invoices.count.open");
event.getWorkitem().removeItem("analytic.invoices.count.due"); event.getWorkitem().removeItem("analytic.invoices.count.due");
event.getWorkitem().removeItem("analytic.invoices.count.dunning"); event.getWorkitem().removeItem("analytic.invoices.count.dunning");
} }
/** /**
* 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() {
* long l = System.currentTimeMillis();
*/
private void calculateStats() {
long l = System.currentTimeMillis();
countAll = 0; countAll = 0;
countDue = 0; countDue = 0;
countDunning = 0; countDunning = 0;
logger.info(" ├──calculate stats for " + departmentID + "...."); logger.info(" ├──calculate stats for " + departmentID + "....");
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]");
countDunning = documentService.count("(type:workitem) AND $uniqueidref:" + departmentID countDunning = documentService.count("(type:workitem) AND $uniqueidref:" + departmentID
+ " AND $modelversion:rechnungsausgang-* AND $taskid:[5200 TO 5399]"); + " AND $modelversion:rechnungsausgang-* AND $taskid:[5200 TO 5399]");
countOpen = countAll - countDue - countDunning; countOpen = countAll - countDue - countDunning;
} else { } else {
// countAll=documentService.count("(type:workitem OR type:workitemarchive) AND // countAll=documentService.count("(type:workitem OR type:workitemarchive) AND
// $modelversion:rechnungsausgang-*") // $modelversion:rechnungsausgang-*")
countAll = documentService.count("(type:workitem) AND $modelversion:rechnungsausgang-*"); countAll = documentService.count("(type:workitem) AND $modelversion:rechnungsausgang-*");
countDue = documentService countDue = documentService
.count("(type:workitem) AND $modelversion:rechnungsausgang-* AND $taskid:[5100 TO 5199]"); .count("(type:workitem) AND $modelversion:rechnungsausgang-* AND $taskid:[5100 TO 5199]");
countDunning = documentService countDunning = documentService
.count("(type:workitem) AND $modelversion:rechnungsausgang-* AND $taskid:[5200 TO 5399]"); .count("(type:workitem) AND $modelversion:rechnungsausgang-* AND $taskid:[5200 TO 5399]");
countOpen = countAll - countDue - countDunning; countOpen = countAll - countDue - countDunning;
} }
} catch (QueryException e) { } catch (QueryException e) {
logger.warning("Failed to query invoices: " + e.getMessage()); logger.warning("Failed to query invoices: " + e.getMessage());
} }
logger.info(" ├──calculate stats took " + (System.currentTimeMillis() - l) + "ms"); logger.info(" ├──calculate stats took " + (System.currentTimeMillis() - l) + "ms");
} }
private String formatCurrency(Double value) { private String formatCurrency(Double value) {
DecimalFormatSymbols symbols = new DecimalFormatSymbols(Locale.getDefault()); DecimalFormatSymbols symbols = new DecimalFormatSymbols(Locale.getDefault());
symbols.setGroupingSeparator('.'); symbols.setGroupingSeparator('.');
symbols.setDecimalSeparator(','); symbols.setDecimalSeparator(',');
DecimalFormat formatter = new DecimalFormat("#,##0.00", symbols); DecimalFormat formatter = new DecimalFormat("#,##0.00", symbols);
return formatter.format(value); return formatter.format(value);
} }
} }

View file

@ -36,497 +36,477 @@ import jakarta.json.JsonObjectBuilder;
@RequestScoped @RequestScoped
public class AGLAnalyticControllerSteuer implements Serializable { public class AGLAnalyticControllerSteuer implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private static Logger logger = Logger.getLogger(AnalyticController.class.getName()); private static Logger logger = Logger.getLogger(AnalyticController.class.getName());
@Inject @Inject
protected DocumentService documentService; protected DocumentService documentService;
@Inject @Inject
TeamService teamService; TeamService teamService;
@Inject @Inject
protected WorkflowController workflowController; protected WorkflowController workflowController;
@Inject @Inject
protected DebitorStatistikController debitorStatistikController; protected DebitorStatistikController debitorStatistikController;
List<ItemCollection> steuerbescheide = null; List<ItemCollection> steuerbescheide = null;
public void onEvent(@Observes AnalyticEvent event) { public void onEvent(@Observes AnalyticEvent event) {
if (!"workitem".equals(event.getWorkitem().getType()) if (!"workitem".equals(event.getWorkitem().getType())
|| !event.getWorkitem().getModelVersion().startsWith("analyse-steuer")) { || !event.getWorkitem().getModelVersion().startsWith("analyse-steuer")) {
// no op // no op
return; return;
} }
ItemCollection process = documentService ItemCollection process = documentService
.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()); event.setLabel("EUR");
event.setLabel("EUR"); event.setDescription("Gesamt Einfuhrumsatzsteuer (EUSt)");
event.setDescription("Gesamt Einfuhrumsatzsteuer (EUSt)"); event.getWorkitem().setItemValue("eust.total", event.getValue());
event.getWorkitem().setItemValue("eust.total", event.getValue()); event.setLink(link);
event.setLink(link); }
} if ("analytic.steuer.zoll".equals(event.getKey())) {
if ("analytic.steuer.zoll".equals(event.getKey())) { event.setValue(computeSteuerbescheideZoll());
event.setValue(computeSteuerbescheideZoll()); event.setLabel("EUR");
event.setLabel("EUR"); event.setDescription("Gesamt Zölle (ZOLLEU)");
event.setDescription("Gesamt Zölle (ZOLLEU)"); event.setLink(link);
event.setLink(link); }
}
if ("analytic.steuer.total".equals(event.getKey())) {
if ("analytic.steuer.total".equals(event.getKey())) { event.setValue(computeSteuerbescheideTotal());
event.setValue(computeSteuerbescheideTotal()); event.setLabel("EUR");
event.setLabel("EUR"); event.setDescription("Alle Steuerbescheide die noch nicht abgerechnet wurden.");
event.setDescription("Alle Steuerbescheide die noch nicht abgerechnet wurden."); event.setLink(link);
event.setLink(link); }
}
if ("analytic.steuer.saldo".equals(event.getKey())) {
if ("analytic.steuer.saldo".equals(event.getKey())) { event.setValue(computeSteuerbescheideSaldo());
event.setValue(computeSteuerbescheideSaldo()); event.setLabel("EUR");
event.setLabel("EUR"); event.setDescription("Saldo aller Steuerbescheide die noch nicht abgerechnet wurden.");
event.setDescription("Saldo aller Steuerbescheide die noch nicht abgerechnet wurden."); event.setLink(link);
event.setLink(link); }
}
if ("analytic.steuer.due".equals(event.getKey())) {
if ("analytic.steuer.due".equals(event.getKey())) { event.setValue(computeSteuerbescheideSaldoDue());
event.setValue(computeSteuerbescheideSaldoDue()); event.setLabel("EUR");
event.setLabel("EUR"); event.setDescription("Saldo aller überfälligen Steuerbescheide die noch nicht abgerechnet wurden.");
event.setDescription("Saldo aller überfälligen Steuerbescheide die noch nicht abgerechnet wurden."); // event.setLink(link + "&task=Überfällig");
// event.setLink(link + "&task=Überfällig"); event.setLink(link + "&task=1200");
event.setLink(link + "&task=1200"); }
}
// Charts
// Charts
if ("analytic.steuer.trend".equals(event.getKey())) {
if ("analytic.steuer.trend".equals(event.getKey())) { event.setValue(accumulateSteuerbescheideByWeek());
event.setValue(accumulateSteuerbescheideByWeek()); event.setLabel("Zoll & EUST");
event.setLabel("Zoll & EUST"); event.setDescription("Fälligkeiten nach KW");
event.setDescription("Fälligkeiten nach KW"); }
}
if ("analytic.steuer.trend.abteilung".equals(event.getKey())) {
if ("analytic.steuer.trend.abteilung".equals(event.getKey())) { event.setValue(accumulateSteuerbescheideAbteilungByWeek());
event.setValue(accumulateSteuerbescheideAbteilungByWeek()); event.setLabel("Nach Abteilungen");
event.setLabel("Nach Abteilungen"); event.setDescription("Offene Salden nach KW pro Abteilung");
event.setDescription("Offene Salden nach KW pro Abteilung"); }
}
}
}
/**
/** * Berechnet die Summe aller offenen EUST Beträge // tax.code=[0],
* Berechnet die Summe aller offenen EUST Beträge * amount=[3885.16], activity.type=[EUST], // filenumber=[LA-PAP-2408-024]}
* // tax.code=[0], amount=[3885.16], activity.type=[EUST], *
* // filenumber=[LA-PAP-2408-024]} * @return
* */
* @return private double computeSteuerbescheideEUST() {
*/ logger.fine("compute EUST...");
private double computeSteuerbescheideEUST() { List<ItemCollection> list = getSteuerbescheide();
logger.fine("compute EUST..."); double result = 0;
List<ItemCollection> list = getSteuerbescheide();
double result = 0; for (ItemCollection steuerItemCol : list) {
result = result + getZollBySteuerBeleg(steuerItemCol, "EUST");
for (ItemCollection steuerItemCol : list) {
result = result + getZollBySteuerBeleg(steuerItemCol, "EUST"); }
return InvoiceUtil.round(result);
} }
return InvoiceUtil.round(result);
} /**
* Berechnet die Summe aller offenen Zoll Beträge
/** *
* Berechnet die Summe aller offenen Zoll Beträge * @return
* */
* @return private double computeSteuerbescheideZoll() {
*/ logger.fine("compute Zoll...");
private double computeSteuerbescheideZoll() { List<ItemCollection> list = getSteuerbescheide();
logger.fine("compute Zoll..."); double result = 0;
List<ItemCollection> list = getSteuerbescheide(); for (ItemCollection steuerItemCol : list) {
double result = 0; result = result + getZollBySteuerBeleg(steuerItemCol, "ZOLL");
for (ItemCollection steuerItemCol : list) { }
result = result + getZollBySteuerBeleg(steuerItemCol, "ZOLL"); return InvoiceUtil.round(result);
} }
return InvoiceUtil.round(result);
} /**
* Berechnet die Summe aller überfälligen steuer Beträge $taskID=1200
/** *
* Berechnet die Summe aller überfälligen steuer Beträge $taskID=1200 * @return
* */
* @return private double computeSteuerbescheideSaldo() {
*/ logger.fine("compute saldo...");
private double computeSteuerbescheideSaldo() { List<ItemCollection> list = getSteuerbescheide();
logger.fine("compute saldo..."); double result = 0;
List<ItemCollection> list = getSteuerbescheide(); for (ItemCollection steuerItemCol : list) {
double result = 0; result = result + steuerItemCol.getItemValueDouble("invoice.saldo");
for (ItemCollection steuerItemCol : list) { }
result = result + steuerItemCol.getItemValueDouble("invoice.saldo"); return InvoiceUtil.round(result);
}
return InvoiceUtil.round(result); }
} /**
* Berechnet die Summe aller überfälligen steuer Beträge $taskID=1200
/** *
* Berechnet die Summe aller überfälligen steuer Beträge $taskID=1200 * @return
* */
* @return private double computeSteuerbescheideTotal() {
*/ logger.fine("compute total...");
private double computeSteuerbescheideTotal() { List<ItemCollection> list = getSteuerbescheide();
logger.fine("compute total..."); double result = 0;
List<ItemCollection> list = getSteuerbescheide(); for (ItemCollection steuerItemCol : list) {
double result = 0; result = result + steuerItemCol.getItemValueDouble("invoice.total.net");
for (ItemCollection steuerItemCol : list) { }
result = result + steuerItemCol.getItemValueDouble("invoice.total.net"); return InvoiceUtil.round(result);
} }
return InvoiceUtil.round(result);
} /**
* Berechnet die Summe aller überfälligen steuer Beträge $taskID=1200
/** *
* Berechnet die Summe aller überfälligen steuer Beträge $taskID=1200 * @return
* */
* @return private double computeSteuerbescheideSaldoDue() {
*/ logger.fine("compute EUST...");
private double computeSteuerbescheideSaldoDue() { List<ItemCollection> list = getSteuerbescheide();
logger.fine("compute EUST..."); double result = 0;
List<ItemCollection> list = getSteuerbescheide(); for (ItemCollection steuerItemCol : list) {
double result = 0; if (steuerItemCol.getTaskID() == 1200) {
for (ItemCollection steuerItemCol : list) { result = result + steuerItemCol.getItemValueDouble("invoice.saldo");
if (steuerItemCol.getTaskID() == 1200) { }
result = result + steuerItemCol.getItemValueDouble("invoice.saldo"); }
} return InvoiceUtil.round(result);
}
return InvoiceUtil.round(result); }
} /**
* Berechnet die summ zoll/eust für einen Beleg
/** *
* Berechnet die summ zoll/eust für einen Beleg * @param steuerItemCol
* * @return
* @param steuerItemCol */
* @return private double getZollBySteuerBeleg(ItemCollection steuerItemCol, String type) {
*/ double result = 0;
private double getZollBySteuerBeleg(ItemCollection steuerItemCol, String type) { List<ItemCollection> positionsTabelle = InvoiceUtil.explodeChildList(steuerItemCol);
double result = 0; for (ItemCollection pos : positionsTabelle) {
List<ItemCollection> positionsTabelle = InvoiceUtil.explodeChildList(steuerItemCol); if (type.equalsIgnoreCase(pos.getItemValueString("activity.type"))) {
for (ItemCollection pos : positionsTabelle) { result = result + pos.getItemValueDouble("amount");
if (type.equalsIgnoreCase(pos.getItemValueString("activity.type"))) { }
result = result + pos.getItemValueDouble("amount"); }
} return result;
} }
return result;
} private List<ItemCollection> getSteuerbescheide() {
if (steuerbescheide != null) {
private List<ItemCollection> getSteuerbescheide() { return steuerbescheide;
if (steuerbescheide != null) { }
return steuerbescheide; // compute steuerbescheide....
} logger.info("Load Steuerbescheide for analytics....");
// compute steuerbescheide.... String query = "(type:workitem ) AND ($workflowgroup:\"Steuerbescheid\")";
logger.info("Load Steuerbescheide for analytics...."); try {
String query = "(type:workitem ) AND ($workflowgroup:\"Steuerbescheid\")"; steuerbescheide = documentService.find(query, 999, 0, "$created", false);
try { return steuerbescheide;
steuerbescheide = documentService.find(query, 999, 0, "$created", false); } catch (QueryException e) {
return steuerbescheide; logger.severe("Failed to get invoices: " + e.getMessage());
} catch (QueryException e) { }
logger.severe("Failed to get invoices: " + e.getMessage());
} return null;
}
return null;
} /**
* Berechnet Chart daten aller Steuerbescheide bei KW
/** *
* Berechnet Chart daten aller Steuerbescheide bei KW * <pre>
* *
* <pre> chartCfg = {
* type: 'bar',
chartCfg = { data: {
type: 'bar', datasets: [{
data: { data: [20, 10],
datasets: [{ }],
data: [20, 10], labels: ['1', '2']
}], }
labels: ['1', '2'] }
} *
} * </pre>
* *
* </pre> * @return
* */
* @return private String accumulateSteuerbescheideByWeek() {
*/ logger.fine("accumulate steuer by week...");
private String accumulateSteuerbescheideByWeek() {
logger.fine("accumulate steuer by week..."); List<ItemCollection> list = getSteuerbescheide();
Map<Integer, SteuerData> saldoByWeek = new HashMap<>();
List<ItemCollection> list = getSteuerbescheide(); Calendar calendar = Calendar.getInstance();
Map<Integer, SteuerData> saldoByWeek = new HashMap<>(); int earliestWeek = Integer.MAX_VALUE;
Calendar calendar = Calendar.getInstance(); int latestWeek = Integer.MIN_VALUE;
int earliestWeek = Integer.MAX_VALUE;
int latestWeek = Integer.MIN_VALUE; // Step 1: Group saldo by week
for (ItemCollection steuerItemCol : list) {
// Step 1: Group saldo by week double saldo = steuerItemCol.getItemValueDouble("invoice.saldo");
for (ItemCollection steuerItemCol : list) { double total = steuerItemCol.getItemValueDouble("invoice.total.net");
double saldo = steuerItemCol.getItemValueDouble("invoice.saldo"); Date date = steuerItemCol.getItemValueDate("invoice.date");
double total = steuerItemCol.getItemValueDouble("invoice.total.net");
Date date = steuerItemCol.getItemValueDate("invoice.date"); // Get the week number and year from the date
calendar.setTime(date);
// Get the week number and year from the date int weekNumber = calendar.get(Calendar.WEEK_OF_YEAR);
calendar.setTime(date); int year = calendar.get(Calendar.YEAR);
int weekNumber = calendar.get(Calendar.WEEK_OF_YEAR); int yearWeek = year * 100 + weekNumber; // Unique key for each year + week combination
int year = calendar.get(Calendar.YEAR);
int yearWeek = year * 100 + weekNumber; // Unique key for each year + week combination // Accumulate the saldo per week
SteuerData steuerDataByWeek = saldoByWeek.getOrDefault(yearWeek, new SteuerData());
// Accumulate the saldo per week double eust = getZollBySteuerBeleg(steuerItemCol, "EUST");
SteuerData steuerDataByWeek = saldoByWeek.getOrDefault(yearWeek, new SteuerData()); double zoll = getZollBySteuerBeleg(steuerItemCol, "ZOLL");
double eust = getZollBySteuerBeleg(steuerItemCol, "EUST"); steuerDataByWeek.add(eust, zoll, total, saldo, "");
double zoll = getZollBySteuerBeleg(steuerItemCol, "ZOLL");
steuerDataByWeek.add(eust, zoll, total, saldo, ""); saldoByWeek.put(yearWeek, steuerDataByWeek);
saldoByWeek.put(yearWeek, steuerDataByWeek); // Track the earliest and latest weeks
earliestWeek = Math.min(earliestWeek, yearWeek);
// Track the earliest and latest weeks latestWeek = Math.max(latestWeek, yearWeek);
earliestWeek = Math.min(earliestWeek, yearWeek); }
latestWeek = Math.max(latestWeek, yearWeek);
} // Step 2: Prepare the JSON structure and ensure we have all weeks from earliest
// to latest
// Step 2: Prepare the JSON structure and ensure we have all weeks from earliest JsonArrayBuilder dataBuilderSaldo = Json.createArrayBuilder();
// to latest JsonArrayBuilder dataBuilderEust = Json.createArrayBuilder();
JsonArrayBuilder dataBuilderSaldo = Json.createArrayBuilder(); JsonArrayBuilder dataBuilderZoll = Json.createArrayBuilder();
JsonArrayBuilder dataBuilderEust = Json.createArrayBuilder(); JsonArrayBuilder labelsBuilder = Json.createArrayBuilder();
JsonArrayBuilder dataBuilderZoll = Json.createArrayBuilder();
JsonArrayBuilder labelsBuilder = Json.createArrayBuilder(); Calendar startCal = Calendar.getInstance();
startCal.set(Calendar.YEAR, earliestWeek / 100);
Calendar startCal = Calendar.getInstance(); startCal.set(Calendar.WEEK_OF_YEAR, earliestWeek % 100);
startCal.set(Calendar.YEAR, earliestWeek / 100);
startCal.set(Calendar.WEEK_OF_YEAR, earliestWeek % 100); Calendar endCal = Calendar.getInstance();
endCal.set(Calendar.YEAR, latestWeek / 100);
Calendar endCal = Calendar.getInstance(); endCal.set(Calendar.WEEK_OF_YEAR, latestWeek % 100);
endCal.set(Calendar.YEAR, latestWeek / 100);
endCal.set(Calendar.WEEK_OF_YEAR, latestWeek % 100); while (!startCal.after(endCal)) {
int weekNumber = startCal.get(Calendar.WEEK_OF_YEAR);
while (!startCal.after(endCal)) { int year = startCal.get(Calendar.YEAR);
int weekNumber = startCal.get(Calendar.WEEK_OF_YEAR); int yearWeek = year * 100 + weekNumber;
int year = startCal.get(Calendar.YEAR);
int yearWeek = year * 100 + weekNumber; // Add the saldo for the week or 0.0 if no data
SteuerData steuerData = saldoByWeek.getOrDefault(yearWeek, new SteuerData());
// Add the saldo for the week or 0.0 if no data dataBuilderSaldo.add(steuerData.saldo);
SteuerData steuerData = saldoByWeek.getOrDefault(yearWeek, new SteuerData()); dataBuilderEust.add(steuerData.eust);
dataBuilderSaldo.add(steuerData.saldo); dataBuilderZoll.add(steuerData.zoll);
dataBuilderEust.add(steuerData.eust); labelsBuilder.add(weekNumber + "/" + year); // Label as 'week/year'
dataBuilderZoll.add(steuerData.zoll);
labelsBuilder.add(weekNumber + "/" + year); // Label as 'week/year' // Move to the next week
startCal.add(Calendar.WEEK_OF_YEAR, 1);
// Move to the next week }
startCal.add(Calendar.WEEK_OF_YEAR, 1);
} // Step 3: Prepare the JSON structure
JsonObjectBuilder jsonBuilder = Json
// Step 3: Prepare the JSON structure .createObjectBuilder().add("type", "bar").add("data", Json
JsonObjectBuilder jsonBuilder = Json.createObjectBuilder() .createObjectBuilder().add("datasets", Json.createArrayBuilder()
.add("type", "bar")
.add("data", Json.createObjectBuilder() .add(Json.createObjectBuilder() // First dataset for 'tax'
.add("datasets", Json.createArrayBuilder() .add("label", "Zoll").add("backgroundColor", "#96f").add("borderWidth", 1)
.add("data", dataBuilderZoll))
.add(Json.createObjectBuilder() // First dataset for 'tax' .add(Json.createObjectBuilder() // Second dataset for 'eust'
.add("label", "Zoll") .add("label", "EUSt").add("backgroundColor", "#36a2eb").add("borderWidth", 1)
.add("backgroundColor", "#96f") .add("data", dataBuilderEust))
.add("borderWidth", 1) .add(Json.createObjectBuilder() // Third dataset for 'Saldo'
.add("data", dataBuilderZoll)) .add("label", "Saldo").add("backgroundColor", "#ff6384").add("borderWidth", 1)
.add(Json.createObjectBuilder() // Second dataset for 'eust' .add("data", dataBuilderSaldo)))
.add("label", "EUSt")
.add("backgroundColor", "#36a2eb") .add("labels", labelsBuilder));
.add("borderWidth", 1)
.add("data", dataBuilderEust)) // Step 4: Return the JSON structure as a string
.add(Json.createObjectBuilder() // Third dataset for 'Saldo' JsonObject chartCfg = jsonBuilder.build();
.add("label", "Saldo") return chartCfg.toString();
.add("backgroundColor", "#ff6384") }
.add("borderWidth", 1)
.add("data", dataBuilderSaldo))) /**
* Berechnet Chart daten aller Steuerbescheide bei KW
.add("labels", labelsBuilder)); *
* <pre>
// Step 4: Return the JSON structure as a string *
JsonObject chartCfg = jsonBuilder.build(); chartCfg = {
return chartCfg.toString(); type: 'bar',
} data: {
datasets: [{
/** data: [20, 10],
* Berechnet Chart daten aller Steuerbescheide bei KW }],
* labels: ['1', '2']
* <pre> }
* }
chartCfg = { *
type: 'bar', * </pre>
data: { *
datasets: [{ * @return
data: [20, 10], */
}], private String accumulateSteuerbescheideAbteilungByWeek() {
labels: ['1', '2'] logger.fine("accumulate steuer by Abteilung week...");
}
} String[] colors = { "#36a2eb", "#ff6384", "#4bc0c0", "#ff9f40", "#96f", "#ffcd56", "#c9cbcf" };
*
* </pre> List<ItemCollection> list = getSteuerbescheide();
* Map<String, SteuerData> totalByWeek = new HashMap<>();
* @return Calendar calendar = Calendar.getInstance();
*/ int earliestWeek = Integer.MAX_VALUE;
private String accumulateSteuerbescheideAbteilungByWeek() { int latestWeek = Integer.MIN_VALUE;
logger.fine("accumulate steuer by Abteilung week...");
List<String> abteilungsListe = new ArrayList<>();
String[] colors = {
"#36a2eb", // Step 1: Group total by week
"#ff6384", for (ItemCollection steuerItemCol : list) {
"#4bc0c0", String abteilung = steuerItemCol.getItemValueString("space.name");
"#ff9f40", if (abteilung.isEmpty()) {
"#96f", abteilung = "Keine Zuordnung";
"#ffcd56", }
"#c9cbcf" if (!abteilungsListe.contains(abteilung)) {
}; abteilungsListe.add(abteilung);
}
List<ItemCollection> list = getSteuerbescheide(); double saldo = steuerItemCol.getItemValueDouble("invoice.saldo");
Map<String, SteuerData> totalByWeek = new HashMap<>(); double total = steuerItemCol.getItemValueDouble("invoice.total.net");
Calendar calendar = Calendar.getInstance(); Date date = steuerItemCol.getItemValueDate("invoice.date");
int earliestWeek = Integer.MAX_VALUE;
int latestWeek = Integer.MIN_VALUE; logger.fine(".... " + steuerItemCol.getItemValueString("$workflowSummary") + " = " + total);
// Get the week number and year from the date
List<String> abteilungsListe = new ArrayList<>(); calendar.setTime(date);
int weekNumber = calendar.get(Calendar.WEEK_OF_YEAR);
// Step 1: Group total by week int year = calendar.get(Calendar.YEAR);
for (ItemCollection steuerItemCol : list) { int yearWeek = year * 100 + weekNumber;
String abteilung = steuerItemCol.getItemValueString("space.name"); String key = abteilung + "~" + yearWeek;// Unique key Import Forest~202405
if (abteilung.isEmpty()) {
abteilung = "Keine Zuordnung"; // Accumulate the total per week
} SteuerData steuerDataByWeek = totalByWeek.getOrDefault(key, new SteuerData());
if (!abteilungsListe.contains(abteilung)) { double eust = getZollBySteuerBeleg(steuerItemCol, "EUST");
abteilungsListe.add(abteilung); double zoll = getZollBySteuerBeleg(steuerItemCol, "ZOLL");
} steuerDataByWeek.add(eust, zoll, total, saldo, abteilung);
double saldo = steuerItemCol.getItemValueDouble("invoice.saldo");
double total = steuerItemCol.getItemValueDouble("invoice.total.net"); totalByWeek.put(key, steuerDataByWeek);
Date date = steuerItemCol.getItemValueDate("invoice.date");
// Track the earliest and latest weeks
logger.fine(".... " + steuerItemCol.getItemValueString("$workflowSummary") + " = " + total); earliestWeek = Math.min(earliestWeek, yearWeek);
// Get the week number and year from the date latestWeek = Math.max(latestWeek, yearWeek);
calendar.setTime(date); }
int weekNumber = calendar.get(Calendar.WEEK_OF_YEAR);
int year = calendar.get(Calendar.YEAR); // Step 2: Prepare the JSON structure and ensure we have all weeks from earliest
int yearWeek = year * 100 + weekNumber; // to latest
String key = abteilung + "~" + yearWeek;// Unique key Import Forest~202405 JsonArrayBuilder labelsBuilder = Json.createArrayBuilder();
Calendar startCal = Calendar.getInstance();
// Accumulate the total per week startCal.set(Calendar.YEAR, earliestWeek / 100);
SteuerData steuerDataByWeek = totalByWeek.getOrDefault(key, new SteuerData()); startCal.set(Calendar.WEEK_OF_YEAR, earliestWeek % 100);
double eust = getZollBySteuerBeleg(steuerItemCol, "EUST");
double zoll = getZollBySteuerBeleg(steuerItemCol, "ZOLL"); Calendar endCal = Calendar.getInstance();
steuerDataByWeek.add(eust, zoll, total, saldo, abteilung); endCal.set(Calendar.YEAR, latestWeek / 100);
endCal.set(Calendar.WEEK_OF_YEAR, latestWeek % 100);
totalByWeek.put(key, steuerDataByWeek);
// Build labels - je eines pro woche
// Track the earliest and latest weeks while (!startCal.after(endCal)) {
earliestWeek = Math.min(earliestWeek, yearWeek); int weekNumber = startCal.get(Calendar.WEEK_OF_YEAR);
latestWeek = Math.max(latestWeek, yearWeek); int year = startCal.get(Calendar.YEAR);
} labelsBuilder.add(weekNumber + "/" + year); // Label as 'week/year'
// Move to the next week
// Step 2: Prepare the JSON structure and ensure we have all weeks from earliest startCal.add(Calendar.WEEK_OF_YEAR, 1);
// to latest }
JsonArrayBuilder labelsBuilder = Json.createArrayBuilder();
Calendar startCal = Calendar.getInstance(); // Step 3: Baue die Balken pro abrteilung
startCal.set(Calendar.YEAR, earliestWeek / 100); JsonArrayBuilder datasets = Json.createArrayBuilder();
startCal.set(Calendar.WEEK_OF_YEAR, earliestWeek % 100);
JsonObjectBuilder jsonBuilder = Json.createObjectBuilder().add("type", "bar");
Calendar endCal = Calendar.getInstance();
endCal.set(Calendar.YEAR, latestWeek / 100); JsonObjectBuilder dataDing = Json.createObjectBuilder();
endCal.set(Calendar.WEEK_OF_YEAR, latestWeek % 100); int iColor = 0;
for (String _abteilung : abteilungsListe) {
// Build labels - je eines pro woche JsonArrayBuilder dataBuilderTotals = Json.createArrayBuilder();
while (!startCal.after(endCal)) {
int weekNumber = startCal.get(Calendar.WEEK_OF_YEAR); startCal = Calendar.getInstance();
int year = startCal.get(Calendar.YEAR); startCal.set(Calendar.YEAR, earliestWeek / 100);
labelsBuilder.add(weekNumber + "/" + year); // Label as 'week/year' startCal.set(Calendar.WEEK_OF_YEAR, earliestWeek % 100);
// Move to the next week
startCal.add(Calendar.WEEK_OF_YEAR, 1); endCal = Calendar.getInstance();
} endCal.set(Calendar.YEAR, latestWeek / 100);
endCal.set(Calendar.WEEK_OF_YEAR, latestWeek % 100);
// Step 3: Baue die Balken pro abrteilung
JsonArrayBuilder datasets = Json.createArrayBuilder(); while (!startCal.after(endCal)) {
int weekNumber = startCal.get(Calendar.WEEK_OF_YEAR);
JsonObjectBuilder jsonBuilder = Json.createObjectBuilder() int year = startCal.get(Calendar.YEAR);
.add("type", "bar"); int yearWeek = year * 100 + weekNumber;
String key = _abteilung + "~" + yearWeek;// Unique key Import Forest~202405
JsonObjectBuilder dataDing = Json.createObjectBuilder(); // Add the total for the week or 0.0 if no data
int iColor = 0; SteuerData steuerData = totalByWeek.getOrDefault(key, new SteuerData());
for (String _abteilung : abteilungsListe) { logger.fine(" " + yearWeek + "=" + steuerData.total);
JsonArrayBuilder dataBuilderTotals = Json.createArrayBuilder(); dataBuilderTotals.add(steuerData.total);
// Move to the next week
startCal = Calendar.getInstance(); startCal.add(Calendar.WEEK_OF_YEAR, 1);
startCal.set(Calendar.YEAR, earliestWeek / 100); }
startCal.set(Calendar.WEEK_OF_YEAR, earliestWeek % 100);
datasets.add(Json.createObjectBuilder() // First dataset for 'tax'
endCal = Calendar.getInstance(); .add("label", _abteilung).add("backgroundColor", colors[iColor]).add("borderWidth", 1)
endCal.set(Calendar.YEAR, latestWeek / 100); .add("data", dataBuilderTotals));
endCal.set(Calendar.WEEK_OF_YEAR, latestWeek % 100);
iColor++;
while (!startCal.after(endCal)) { if (iColor > colors.length) {
int weekNumber = startCal.get(Calendar.WEEK_OF_YEAR); iColor = 0;
int year = startCal.get(Calendar.YEAR); }
int yearWeek = year * 100 + weekNumber; }
String key = _abteilung + "~" + yearWeek;// Unique key Import Forest~202405
// Add the total for the week or 0.0 if no data dataDing.add("datasets", datasets);
SteuerData steuerData = totalByWeek.getOrDefault(key, new SteuerData()); dataDing.add("labels", labelsBuilder);
logger.fine(" " + yearWeek + "=" + steuerData.total);
dataBuilderTotals.add(steuerData.total); jsonBuilder.add("data", dataDing);
// Move to the next week
startCal.add(Calendar.WEEK_OF_YEAR, 1); // Step 4: Return the JSON structure as a string
} JsonObject chartCfg = jsonBuilder.build();
return chartCfg.toString();
datasets.add(Json.createObjectBuilder() // First dataset for 'tax' }
.add("label", _abteilung)
.add("backgroundColor", colors[iColor]) /**
.add("borderWidth", 1) * Helper method to hold zoll and eust totals in an object. Needed to build
.add("data", dataBuilderTotals)); * totals per week.
*/
iColor++; class SteuerData {
if (iColor > colors.length) { double total = 0.0;
iColor = 0; double zoll = 0.0;
} double eust = 0.0;
} double saldo = 0.0;
String abteilung = "";
dataDing.add("datasets", datasets);
dataDing.add("labels", labelsBuilder); public SteuerData() {
// default constructor
jsonBuilder.add("data", dataDing); }
// Step 4: Return the JSON structure as a string /**
JsonObject chartCfg = jsonBuilder.build(); * Adds new zoll and eust values and updates the total
return chartCfg.toString(); *
} * @param eustNew eust value to be added
* @param zollNew zoll value to be added
/** */
* Helper method to hold zoll and eust totals in an object. Needed to build public void add(double eustNew, double zollNew, double totalNew, double saldoNew, String abteilungNew) {
* totals per week. eust = eust + eustNew;
*/ zoll = zoll + zollNew;
class SteuerData { saldo = saldo + saldoNew;
double total = 0.0; total = total + totalNew;
double zoll = 0.0; abteilung = abteilungNew;
double eust = 0.0; }
double saldo = 0.0;
String abteilung = ""; }
public SteuerData() {
// default constructor
}
/**
* Adds new zoll and eust values and updates the total
*
* @param eustNew eust value to be added
* @param zollNew zoll value to be added
*/
public void add(double eustNew, double zollNew, double totalNew, double saldoNew, String abteilungNew) {
eust = eust + eustNew;
zoll = zoll + zollNew;
saldo = saldo + saldoNew;
total = total + totalNew;
abteilung = abteilungNew;
}
}
} }

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.
@ -61,245 +60,232 @@ import jakarta.inject.Inject;
*/ */
public class AGLAnalyticExcelAdapter extends POIFindReplaceAdapter { public class AGLAnalyticExcelAdapter extends POIFindReplaceAdapter {
private static Logger logger = Logger.getLogger(AGLAnalyticExcelAdapter.class.getName()); private static Logger logger = Logger.getLogger(AGLAnalyticExcelAdapter.class.getName());
@Inject @Inject
WorkflowService workflowService; WorkflowService workflowService;
@Inject @Inject
InvoiceService opListExportService; InvoiceService opListExportService;
@Inject @Inject
DocumentService documentService; DocumentService documentService;
@Inject @Inject
ConfigService configService; ConfigService configService;
/** /**
* This method finds or create the Zahlungsavis and adds a reference * This method finds or create the Zahlungsavis and adds a reference
* ($workitemref) to the current invoice. * ($workitemref) to the current invoice.
* *
* @throws PluginException * @throws PluginException
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public ItemCollection execute(ItemCollection document, ItemCollection event) public ItemCollection execute(ItemCollection document, ItemCollection event)
throws AdapterException, PluginException { throws AdapterException, PluginException {
// ermittle die Hauptwährungen // ermittle die Hauptwährungen
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
ItemCollection steuerbescheideConfig = workflowService.evalWorkflowResult(event, "steuerbescheide",
document,
false);
if (steuerbescheideConfig == null || !steuerbescheideConfig.hasItem("excel-export")) {
throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), CONFIG_ERROR,
"missing Steuerbescheide configuration in model event - please check model configuration");
}
List<String> excelDefList = steuerbescheideConfig.getItemValue("excel-export");
ItemCollection excelDefCollection = XMLParser.parseItemStructure(excelDefList.get(0));
String textblock = excelDefCollection.getItemValueString("textblock");
String template = excelDefCollection.getItemValueString("template");
String targetName = excelDefCollection.getItemValueString("target-name");
// adapt text....
targetName = workflowService.adaptText(targetName, document);
try {
appendExcelTemplate(document, textblock, template, targetName);
insertInvoiceRows(document, targetName);
logger.info("... loading poi configuration..");
// Process POI instructions
// read the config
ItemCollection poiConfig = workflowService.evalWorkflowResult(event, "poi-update", document,
false);
if (poiConfig == null || !poiConfig.hasItem("findreplace")) {
throw new PluginException(POIFindReplaceAdapter.class.getSimpleName(), CONFIG_ERROR,
"missing poi configuration");
}
List<String> replaceDevList = poiConfig.getItemValue("findreplace");
String eval = poiConfig.getItemValueString("eval");
logger.info("... update template with normal poi information..");
this.updateFileData(document.getFileData(targetName), document, replaceDevList, eval);
} catch (PluginException | IOException | QueryException e) {
throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(),
InvoiceService.ERROR_CONFIG,
"failed to update steuerbescheide: " + e.getMessage());
}
logger.info("... completed!");
return document;
} }
/** // read the Steuerbescheide options
* This helper method inserts a row for each invoice at row 16 ItemCollection steuerbescheideConfig = workflowService.evalWorkflowResult(event, "steuerbescheide", document,
* into the excel template file false);
* <p> if (steuerbescheideConfig == null || !steuerbescheideConfig.hasItem("excel-export")) {
* The method copies the Row A16 as a reference row throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), CONFIG_ERROR,
* <p> "missing Steuerbescheide configuration in model event - please check model configuration");
* The named cell 'TOTAL' should contain the summary formula. It will be }
* evaluated at the end. List<String> excelDefList = steuerbescheideConfig.getItemValue("excel-export");
* ItemCollection excelDefCollection = XMLParser.parseItemStructure(excelDefList.get(0));
* @throws PluginException
* @throws QueryException
*/
private void insertInvoiceRows(ItemCollection document, String fileName)
throws PluginException, QueryException {
// load dummy rechnungen String textblock = excelDefCollection.getItemValueString("textblock");
List<ItemCollection> invoices = loadSteuerbescheide(); String template = excelDefCollection.getItemValueString("template");
String targetName = excelDefCollection.getItemValueString("target-name");
FileData fileData = document.getFileData(fileName); // adapt text....
targetName = workflowService.adaptText(targetName, document);
// load XSSFWorkbook try {
XSSFWorkbook doc = null; appendExcelTemplate(document, textblock, template, targetName);
try (InputStream imputStream = new ByteArrayInputStream(fileData.getContent())) { insertInvoiceRows(document, targetName);
doc = new XSSFWorkbook(imputStream);
// NOTE: we only take the first sheet !
XSSFSheet sheet = doc.getSheetAt(0);
CellReference invoiceRefCell = new CellReference("A11");
XSSFRow referenceRowInvoice = sheet.getRow(invoiceRefCell.getRow());
int referenceRowPos = 10;
int rowPos = referenceRowPos;
int lastRow = 2999;
logger.finest("Last rownum=" + lastRow);
// jetzt füge alle Rechnungen an. logger.info("... loading poi configuration..");
int totalRowCount = invoices.size(); // Process POI instructions
sheet.shiftRows(referenceRowPos, lastRow, totalRowCount, true, true); // read the config
for (ItemCollection invoice : invoices) { ItemCollection poiConfig = workflowService.evalWorkflowResult(event, "poi-update", document, false);
logger.fine("......add invoice " + invoice.getUniqueID()); if (poiConfig == null || !poiConfig.hasItem("findreplace")) {
// now create a new line.. throw new PluginException(POIFindReplaceAdapter.class.getSimpleName(), CONFIG_ERROR,
XSSFRow row = sheet.createRow(rowPos); "missing poi configuration");
row.copyRowFrom(referenceRowInvoice, new CellCopyPolicy()); }
// insert values List<String> replaceDevList = poiConfig.getItemValue("findreplace");
row.getCell(0) String eval = poiConfig.getItemValueString("eval");
.setCellValue(invoice.getItemValueDate("$created"));
row.getCell(1, MissingCellPolicy.CREATE_NULL_AS_BLANK)
.setCellValue(invoice.getItemValueString("invoice.number"));
row.getCell(2, MissingCellPolicy.CREATE_NULL_AS_BLANK)
.setCellValue(invoice.getItemValueDate("invoice.date"));
row.getCell(3, MissingCellPolicy.CREATE_NULL_AS_BLANK)
.setCellValue(invoice.getItemValueDate("invoice.booking_date"));
// Änderung Frau Mwangi am 5.12.2025 logger.info("... update template with normal poi information..");
// Nun soll anstatt des Departments der Anmelder ausgegeben werden this.updateFileData(document.getFileData(targetName), document, replaceDevList, eval);
// row.getCell(4, MissingCellPolicy.CREATE_NULL_AS_BLANK)
// .setCellValue(invoice.getItemValueString("space.name"));
row.getCell(4, MissingCellPolicy.CREATE_NULL_AS_BLANK)
.setCellValue(invoice.getItemValueString("steuer.anmelder"));
row.getCell(5, MissingCellPolicy.CREATE_NULL_AS_BLANK) } catch (PluginException | IOException | QueryException e) {
.setCellValue(invoice.getItemValueString("invoice.text")); throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), InvoiceService.ERROR_CONFIG,
row.getCell(6, MissingCellPolicy.CREATE_NULL_AS_BLANK) "failed to update steuerbescheide: " + e.getMessage());
.setCellValue(invoice.getItemValueString("invoice.atc.number")); }
row.getCell(7, MissingCellPolicy.CREATE_NULL_AS_BLANK) logger.info("... completed!");
.setCellValue(invoice.getItemValueDouble("invoice.total.net")); return document;
row.getCell(8, MissingCellPolicy.CREATE_NULL_AS_BLANK) }
.setCellValue(invoice.getItemValueDouble("invoice.saldo"));
row.getCell(9, MissingCellPolicy.CREATE_NULL_AS_BLANK)
.setCellValue(invoice.getItemValueDouble("steuer.eust"));
row.getCell(10, MissingCellPolicy.CREATE_NULL_AS_BLANK)
.setCellValue(invoice.getItemValueDate("steuer.eust.due"));
row.getCell(11, MissingCellPolicy.CREATE_NULL_AS_BLANK)
.setCellValue(invoice.getItemValueDouble("steuer.zoll"));
row.getCell(12, MissingCellPolicy.CREATE_NULL_AS_BLANK)
.setCellValue(invoice.getItemValueDate("steuer.zoll.due"));
rowPos++; /**
} * This helper method inserts a row for each invoice at row 16 into the excel
* template file
* <p>
* The method copies the Row A16 as a reference row
* <p>
* The named cell 'TOTAL' should contain the summary formula. It will be
* evaluated at the end.
*
* @throws PluginException
* @throws QueryException
*/
private void insertInvoiceRows(ItemCollection document, String fileName) throws PluginException, QueryException {
// Leerzeile // load dummy rechnungen
// categoryRow = sheet.createRow(rowPos); List<ItemCollection> invoices = loadSteuerbescheide();
rowPos++;
// write back the file FileData fileData = document.getFileData(fileName);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
doc.write(byteArrayOutputStream);
byte[] newContent = byteArrayOutputStream.toByteArray(); // load XSSFWorkbook
FileData fileDataNew = new FileData(fileData.getName(), newContent, fileData.getContentType(), XSSFWorkbook doc = null;
null); try (InputStream imputStream = new ByteArrayInputStream(fileData.getContent())) {
// update the fileData doc = new XSSFWorkbook(imputStream);
document.addFileData(fileDataNew); // NOTE: we only take the first sheet !
XSSFSheet sheet = doc.getSheetAt(0);
CellReference invoiceRefCell = new CellReference("A11");
XSSFRow referenceRowInvoice = sheet.getRow(invoiceRefCell.getRow());
int referenceRowPos = 10;
int rowPos = referenceRowPos;
int lastRow = 2999;
logger.finest("Last rownum=" + lastRow);
logger.finest("......new document added"); // jetzt füge alle Rechnungen an.
int totalRowCount = invoices.size();
sheet.shiftRows(referenceRowPos, lastRow, totalRowCount, true, true);
for (ItemCollection invoice : invoices) {
logger.fine("......add invoice " + invoice.getUniqueID());
// now create a new line..
XSSFRow row = sheet.createRow(rowPos);
row.copyRowFrom(referenceRowInvoice, new CellCopyPolicy());
// insert values
row.getCell(0).setCellValue(invoice.getItemValueDate("$created"));
row.getCell(1, MissingCellPolicy.CREATE_NULL_AS_BLANK)
.setCellValue(invoice.getItemValueString("invoice.number"));
row.getCell(2, MissingCellPolicy.CREATE_NULL_AS_BLANK)
.setCellValue(invoice.getItemValueDate("invoice.date"));
row.getCell(3, MissingCellPolicy.CREATE_NULL_AS_BLANK)
.setCellValue(invoice.getItemValueDate("invoice.booking_date"));
// Änderung Frau Mwangi am 5.12.2025
// Nun soll anstatt des Departments der Anmelder ausgegeben werden
// row.getCell(4, MissingCellPolicy.CREATE_NULL_AS_BLANK)
// .setCellValue(invoice.getItemValueString("space.name"));
row.getCell(4, MissingCellPolicy.CREATE_NULL_AS_BLANK)
.setCellValue(invoice.getItemValueString("steuer.anmelder"));
row.getCell(5, MissingCellPolicy.CREATE_NULL_AS_BLANK)
.setCellValue(invoice.getItemValueString("invoice.text"));
row.getCell(6, MissingCellPolicy.CREATE_NULL_AS_BLANK)
.setCellValue(invoice.getItemValueString("invoice.atc.number"));
row.getCell(7, MissingCellPolicy.CREATE_NULL_AS_BLANK)
.setCellValue(invoice.getItemValueDouble("invoice.total.net"));
row.getCell(8, MissingCellPolicy.CREATE_NULL_AS_BLANK)
.setCellValue(invoice.getItemValueDouble("invoice.saldo"));
row.getCell(9, MissingCellPolicy.CREATE_NULL_AS_BLANK)
.setCellValue(invoice.getItemValueDouble("steuer.eust"));
row.getCell(10, MissingCellPolicy.CREATE_NULL_AS_BLANK)
.setCellValue(invoice.getItemValueDate("steuer.eust.due"));
row.getCell(11, MissingCellPolicy.CREATE_NULL_AS_BLANK)
.setCellValue(invoice.getItemValueDouble("steuer.zoll"));
row.getCell(12, MissingCellPolicy.CREATE_NULL_AS_BLANK)
.setCellValue(invoice.getItemValueDate("steuer.zoll.due"));
rowPos++;
}
// Leerzeile
// categoryRow = sheet.createRow(rowPos);
rowPos++;
// write back the file
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
doc.write(byteArrayOutputStream);
byte[] newContent = byteArrayOutputStream.toByteArray();
FileData fileDataNew = new FileData(fileData.getName(), newContent, fileData.getContentType(), null);
// update the fileData
document.addFileData(fileDataNew);
logger.finest("......new document added");
} catch (IOException e) {
throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), InvoiceService.ERROR_CONFIG,
"failed to update opliste: " + e.getMessage());
} finally {
if (doc != null) {
try {
doc.close();
} catch (IOException e) { } catch (IOException e) {
throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), logger.severe("Failed to close workbook: " + e.getMessage());
InvoiceService.ERROR_CONFIG, e.printStackTrace();
"failed to update opliste: " + e.getMessage());
} finally {
if (doc != null) {
try {
doc.close();
} catch (IOException e) {
logger.severe("Failed to close workbook: " + e.getMessage());
e.printStackTrace();
}
}
} }
}
}
}
/**
* This method loads a text-block for a specified ref and appends the named
* fileData object of this document.
*
* @param document
* @throws PluginException
*/
private void appendExcelTemplate(ItemCollection document, String textblockRef, String template, String targetName)
throws PluginException {
if ((template == null || template.isEmpty()) || (textblockRef == null || textblockRef.isEmpty())) {
throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), InvoiceService.ERROR_CONFIG,
"invalid steuerbescheide configuration in model event - textblock/template reference not defined!");
} }
/** // load the text block
* This method loads a text-block for a specified ref and appends the named FileData fileData = opListExportService.loadTextBlockFileData(textblockRef, template);
* fileData object of this document.
*
* @param document
* @throws PluginException
*/
private void appendExcelTemplate(ItemCollection document, String textblockRef, String template,
String targetName)
throws PluginException {
if ((template == null || template.isEmpty()) || (textblockRef == null || textblockRef.isEmpty())) {
throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(),
InvoiceService.ERROR_CONFIG,
"invalid steuerbescheide configuration in model event - textblock/template reference not defined!");
}
// load the text block
FileData fileData = opListExportService.loadTextBlockFileData(textblockRef, template);
// do we found the document?
if (fileData == null) {
throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(),
InvoiceService.ERROR_CONFIG,
"invalid steuerbescheide configuration in model event - template=" + template
+ " not found!");
}
fileData.setName(targetName);
// append document
logger.info("...append new steuerbescheide Template: " + targetName);
document.addFileData(fileData);
// do we found the document?
if (fileData == null) {
throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), InvoiceService.ERROR_CONFIG,
"invalid steuerbescheide configuration in model event - template=" + template + " not found!");
} }
fileData.setName(targetName);
// append document
logger.info("...append new steuerbescheide Template: " + targetName);
document.addFileData(fileData);
/** }
* Diese Methode läd alle offenen Steuerbescheide
* /**
* @return * Diese Methode läd alle offenen Steuerbescheide
*/ *
private List<ItemCollection> loadSteuerbescheide() { * @return
List<ItemCollection> result = new ArrayList<>(); */
try { private List<ItemCollection> loadSteuerbescheide() {
result = documentService.find( List<ItemCollection> result = new ArrayList<>();
"$modelversion:steuerbescheid-* AND type:workitem", 9999, 0, try {
"invoice.number", false); result = documentService.find("$modelversion:steuerbescheid-* AND type:workitem", 9999, 0, "invoice.number",
} catch (QueryException e) { false);
e.printStackTrace(); } catch (QueryException e) {
} e.printStackTrace();
return result;
} }
return result;
}
} }

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

@ -11,38 +11,38 @@ package com.alexanderlogistics;
*/ */
public class BusinessPartnerSearchEntry { public class BusinessPartnerSearchEntry {
private String key; private String key;
private String display; private String display;
private String data; private String data;
public BusinessPartnerSearchEntry(String key, String display, String data) { public BusinessPartnerSearchEntry(String key, String display, String data) {
super(); super();
this.key = key; this.key = key;
this.display = display; this.display = display;
this.data = data; this.data = data;
} }
public String getKey() { public String getKey() {
return key; return key;
} }
public void setKey(String key) { public void setKey(String key) {
this.key = key; this.key = key;
} }
public String getDisplay() { public String getDisplay() {
return display; return display;
} }
public void setDisplay(String display) { public void setDisplay(String display) {
this.display = display; this.display = display;
} }
public String getData() { public String getData() {
return data; return data;
} }
public void setData(String data) { public void setData(String data) {
this.data = data; this.data = data;
} }
} }

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

@ -30,138 +30,138 @@ import jakarta.inject.Named;
@ConversationScoped @ConversationScoped
public class InvoiceAnalyseController implements Serializable { public class InvoiceAnalyseController implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private static Logger logger = Logger.getLogger(InvoiceAnalyseController.class.getName()); private static Logger logger = Logger.getLogger(InvoiceAnalyseController.class.getName());
@Inject @Inject
protected DocumentService documentService; protected DocumentService documentService;
@Inject @Inject
SearchService searchService; SearchService searchService;
Category sachpruefung; Category sachpruefung;
Category verteilung; Category verteilung;
Category buchhaltung; Category buchhaltung;
private ItemCollection filter; private ItemCollection filter;
public ItemCollection getFilter() { public ItemCollection getFilter() {
if (filter == null) { if (filter == null) {
filter = new ItemCollection(); filter = new ItemCollection();
// compute start stop based on current month // compute start stop based on current month
YearMonth startYearMonth = YearMonth.now(); YearMonth startYearMonth = YearMonth.now();
java.time.LocalDate startOfMonthDate = startYearMonth.atDay(1); java.time.LocalDate startOfMonthDate = startYearMonth.atDay(1);
java.time.LocalDate endOfMonthDate = startYearMonth.atEndOfMonth(); java.time.LocalDate endOfMonthDate = startYearMonth.atEndOfMonth();
filter.setItemValue("start", filter.setItemValue("start",
java.util.Date.from(startOfMonthDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant())); java.util.Date.from(startOfMonthDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant()));
filter.setItemValue("stop", filter.setItemValue("stop",
java.util.Date.from(endOfMonthDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant())); java.util.Date.from(endOfMonthDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant()));
} }
return filter; return filter;
} }
public void setFilter(ItemCollection filter) { public void setFilter(ItemCollection filter) {
this.filter = filter; this.filter = filter;
} }
public Category getSachpruefung() { public Category getSachpruefung() {
return sachpruefung; return sachpruefung;
} }
public Category getVerteilung() { public Category getVerteilung() {
return verteilung; return verteilung;
} }
public Category getBuchhaltung() { public Category getBuchhaltung() {
return buchhaltung; return buchhaltung;
} }
public int totalCountByCategory(Category cat) { public int totalCountByCategory(Category cat) {
int result = 0; int result = 0;
Collection<Integer> allCounts = cat.getLabels().values(); Collection<Integer> allCounts = cat.getLabels().values();
result = allCounts.stream().reduce(0, Integer::sum); result = allCounts.stream().reduce(0, Integer::sum);
return result; return result;
} }
/** /**
* Fuert verschiedene Queries aus um eine Analyse der Sachprufung durchzuführen. * Fuert verschiedene Queries aus um eine Analyse der Sachprufung durchzuführen.
*/ */
public void analyse() { public void analyse() {
logger.info("start analyse...."); logger.info("start analyse....");
verteilung = null; verteilung = null;
sachpruefung = null; sachpruefung = null;
Date start = filter.getItemValueDate("start"); Date start = filter.getItemValueDate("start");
Date stop = filter.getItemValueDate("stop"); Date stop = filter.getItemValueDate("stop");
logger.info("...daterange=" + start + " - " + stop); logger.info("...daterange=" + start + " - " + stop);
// serach date range? // serach date range?
String sDateFrom = "191401070000"; // because * did not work here String sDateFrom = "191401070000"; // because * did not work here
String sDateTo = "211401070000"; String sDateTo = "211401070000";
SimpleDateFormat dateformat = new SimpleDateFormat("yyyyMMddHHmm"); SimpleDateFormat dateformat = new SimpleDateFormat("yyyyMMddHHmm");
if (start != null) { if (start != null) {
Calendar cal = Calendar.getInstance(); Calendar cal = Calendar.getInstance();
cal.setTime(start); cal.setTime(start);
sDateFrom = dateformat.format(cal.getTime()); sDateFrom = dateformat.format(cal.getTime());
} }
if (stop != null) { if (stop != null) {
Calendar cal = Calendar.getInstance(); Calendar cal = Calendar.getInstance();
cal.setTime(stop); cal.setTime(stop);
cal.add(Calendar.DATE, 1); cal.add(Calendar.DATE, 1);
sDateTo = dateformat.format(cal.getTime()); sDateTo = dateformat.format(cal.getTime());
} }
// query Verteilung // query Verteilung
String query = "(type:workitem OR type:workitemarchive) "; String query = "(type:workitem OR type:workitemarchive) ";
if (start != null || stop != null) { if (start != null || stop != null) {
query += "AND (taxonomy.verteilung.stop:[" + sDateFrom + " TO " + sDateTo + "]) "; query += "AND (taxonomy.verteilung.stop:[" + sDateFrom + " TO " + sDateTo + "]) ";
} }
logger.info(query); logger.info(query);
List<Category> taxResult = searchService.getTaxonomyByQuery(query, "taxonomy.verteilung.stop.by"); List<Category> taxResult = searchService.getTaxonomyByQuery(query, "taxonomy.verteilung.stop.by");
if (taxResult.size() > 0) { if (taxResult.size() > 0) {
verteilung = taxResult.get(0); verteilung = taxResult.get(0);
} }
// query Sachprüfung // query Sachprüfung
query = "(type:workitem OR type:workitemarchive) "; query = "(type:workitem OR type:workitemarchive) ";
if (start != null || stop != null) { if (start != null || stop != null) {
query += "AND (taxonomy.sachpruefung.stop:[" + sDateFrom + " TO " + sDateTo + "]) "; query += "AND (taxonomy.sachpruefung.stop:[" + sDateFrom + " TO " + sDateTo + "]) ";
} }
logger.info(query); logger.info(query);
taxResult = searchService.getTaxonomyByQuery(query, "taxonomy.sachpruefung.stop.by"); taxResult = searchService.getTaxonomyByQuery(query, "taxonomy.sachpruefung.stop.by");
if (taxResult.size() > 0) { if (taxResult.size() > 0) {
sachpruefung = taxResult.get(0); sachpruefung = taxResult.get(0);
} }
// query Buchhaltung // query Buchhaltung
query = "(type:workitem OR type:workitemarchive) "; query = "(type:workitem OR type:workitemarchive) ";
if (start != null || stop != null) { if (start != null || stop != null) {
query += "AND (taxonomy.buchhaltung.stop:[" + sDateFrom + " TO " + sDateTo + "]) "; query += "AND (taxonomy.buchhaltung.stop:[" + sDateFrom + " TO " + sDateTo + "]) ";
} }
logger.info(query); logger.info(query);
taxResult = searchService.getTaxonomyByQuery(query, "taxonomy.buchhaltung.stop.by"); taxResult = searchService.getTaxonomyByQuery(query, "taxonomy.buchhaltung.stop.by");
if (taxResult.size() > 0) { if (taxResult.size() > 0) {
buchhaltung = taxResult.get(0); buchhaltung = taxResult.get(0);
} }
} }
/** /**
* This method reset the search and input state. * This method reset the search and input state.
*/ */
public void reset() { public void reset() {
filter = new ItemCollection(); filter = new ItemCollection();
sachpruefung = null; sachpruefung = null;
verteilung = null; verteilung = null;
buchhaltung = null; buchhaltung = null;
logger.fine("reset"); logger.fine("reset");
} }
} }

View file

@ -17,31 +17,31 @@ import jakarta.inject.Inject;
*/ */
public class InvoiceOutgoingPlugin extends AbstractPlugin { public class InvoiceOutgoingPlugin extends AbstractPlugin {
@Inject @Inject
BusinessPartnerService businessPartnerService; BusinessPartnerService businessPartnerService;
/** /**
* *
* @throws PluginException - if data is missing * @throws PluginException - if data is missing
* *
**/ **/
@Override @Override
public ItemCollection run(ItemCollection workitem, ItemCollection event) throws PluginException { public ItemCollection run(ItemCollection workitem, ItemCollection event) throws PluginException {
// Update BUsiness Partner Data // Update BUsiness Partner Data
businessPartnerService.updateBusinessPartnerData(workitem); businessPartnerService.updateBusinessPartnerData(workitem);
// Update Invoice.positions // Update Invoice.positions
InvoiceUtil.updateInvoicePositions(workitem); InvoiceUtil.updateInvoicePositions(workitem);
String img = ""; String img = "";
if (workitem.getItemValueBoolean("invoice.protest")) { if (workitem.getItemValueBoolean("invoice.protest")) {
img = img + "<img title='Reklamiert' src=\"/layout/icons/icon106.png\">"; img = img + "<img title='Reklamiert' src=\"/layout/icons/icon106.png\">";
} }
workitem.setItemValue("_img", img); workitem.setItemValue("_img", img);
return workitem; return workitem;
} }
} }

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
@ -45,393 +45,392 @@ import jakarta.inject.Inject;
*/ */
public class InvoicePlugin extends AbstractPlugin { public class InvoicePlugin extends AbstractPlugin {
public static final int TASK_ERFASSUNG = 5001; public static final int TASK_ERFASSUNG = 5001;
public static final int TASK_SACHPRUEFUNG = 5200; public static final int TASK_SACHPRUEFUNG = 5200;
public static final int TASK_SACHPRUEFUNG_DELEGATION = 5210; public static final int TASK_SACHPRUEFUNG_DELEGATION = 5210;
public static final int EVENT_FREIGEBEN = 20; public static final int EVENT_FREIGEBEN = 20;
public static final String ERROR_MISSING_DATA = "MISSING_DATA"; public static final String ERROR_MISSING_DATA = "MISSING_DATA";
public static final String ERROR_DUPPLICATE_INVOICE_NUMBER = "DUPPLICATE_INVOICE_NUMBER"; public static final String ERROR_DUPPLICATE_INVOICE_NUMBER = "DUPPLICATE_INVOICE_NUMBER";
public static final String ERROR_NEW_IBANBIC = "NEW_IBANBIC"; public static final String ERROR_NEW_IBANBIC = "NEW_IBANBIC";
private static Logger logger = Logger.getLogger(InvoicePlugin.class.getName()); private static Logger logger = Logger.getLogger(InvoicePlugin.class.getName());
// Positions Pattern - kann überschrieben werden // Positions Pattern - kann überschrieben werden
// XX-YYY-####-### oder XX-YYYZZZ-####-### // XX-YYY-####-### oder XX-YYYZZZ-####-###
// Default = ([A-Z]{2}-[A-Z]{3}-[0-9]{4}-[0-9]{3}) // Default = ([A-Z]{2}-[A-Z]{3}-[0-9]{4}-[0-9]{3})
// Optional = ([A-Z]{2}-(?:[A-Z]{3}|[A-Z]{6})-[0-9]{4}-[0-9]{3}) // Optional = ([A-Z]{2}-(?:[A-Z]{3}|[A-Z]{6})-[0-9]{4}-[0-9]{3})
// Nur 6- Stellig = ([A-Z]{2}-(?:[A-Z]{6})-[0-9]{4}-[0-9]{3}) // Nur 6- Stellig = ([A-Z]{2}-(?:[A-Z]{6})-[0-9]{4}-[0-9]{3})
public final static String INVOICE_POSITIONS_PATTERN_DEFAULT = "([A-Z]{2}-(?:[A-Z]{3}|[A-Z]{6})-[0-9]{4}-[0-9]{3})"; public final static String INVOICE_POSITIONS_PATTERN_DEFAULT = "([A-Z]{2}-(?:[A-Z]{3}|[A-Z]{6})-[0-9]{4}-[0-9]{3})";
@Inject @Inject
@ConfigProperty(name = "invoice.position.pattern", defaultValue = INVOICE_POSITIONS_PATTERN_DEFAULT) @ConfigProperty(name = "invoice.position.pattern", defaultValue = INVOICE_POSITIONS_PATTERN_DEFAULT)
String invoicePositionsPattern; String invoicePositionsPattern;
@Inject @Inject
BusinessPartnerService businessPartnerService; BusinessPartnerService businessPartnerService;
@Inject @Inject
ResourceBundleHandler resourceBundleHandler; ResourceBundleHandler resourceBundleHandler;
/** /**
* Test childworkitems for empty lines * Test childworkitems for empty lines
* *
* @throws PluginException - if data is missing * @throws PluginException - if data is missing
* *
**/ **/
@Override @Override
public ItemCollection run(ItemCollection workitem, ItemCollection event) throws PluginException { public ItemCollection run(ItemCollection workitem, ItemCollection event) throws PluginException {
// Update BUsiness Partner Data // Update BUsiness Partner Data
businessPartnerService.updateBusinessPartnerData(workitem); businessPartnerService.updateBusinessPartnerData(workitem);
updateImg(workitem); updateImg(workitem);
// Update Invoice.positions // Update Invoice.positions
InvoiceUtil.updateInvoicePositions(workitem); InvoiceUtil.updateInvoicePositions(workitem);
// skip if validaten tag is required=false // skip if validaten tag is required=false
ItemCollection evalItemCollection = this.getWorkflowService().evalWorkflowResult(event, "validation", workitem); ItemCollection evalItemCollection = this.getWorkflowService().evalWorkflowResult(event, "validation", workitem);
if (evalItemCollection != null) { if (evalItemCollection != null) {
// evaluate the validation rules... // evaluate the validation rules...
if ("false".equalsIgnoreCase(evalItemCollection.getItemValueString("required"))) { if ("false".equalsIgnoreCase(evalItemCollection.getItemValueString("required"))) {
return workitem; return workitem;
} }
} }
boolean isPublicEvent = !("0".equals(event.getItemValueString("keypublicresult"))); boolean isPublicEvent = !("0".equals(event.getItemValueString("keypublicresult")));
// Payment.type muss immer eingetragne werden! // Payment.type muss immer eingetragne werden!
if (isPublicEvent && workitem.getTaskID() >= TASK_ERFASSUNG && workitem.getEventID() < 900) { if (isPublicEvent && workitem.getTaskID() >= TASK_ERFASSUNG && workitem.getEventID() < 900) {
if (workitem.getItemValueString("payment.type").trim().isEmpty()) { if (workitem.getItemValueString("payment.type").trim().isEmpty()) {
// throw a plugin exception! // throw a plugin exception!
String message = resourceBundleHandler.findMessage("ERROR_MISSING_PAYMENT"); String message = resourceBundleHandler.findMessage("ERROR_MISSING_PAYMENT");
throw new PluginException(InvoicePlugin.class.getName(), ERROR_MISSING_DATA, message); throw new PluginException(InvoicePlugin.class.getName(), ERROR_MISSING_DATA, message);
} }
// doppelte Rechnungsnummer prüfen // doppelte Rechnungsnummer prüfen
if (workitem.getTaskID() == TASK_ERFASSUNG) { if (workitem.getTaskID() == TASK_ERFASSUNG) {
validateInvoiceNumber(workitem); validateInvoiceNumber(workitem);
checkIBANNumber(workitem); checkIBANNumber(workitem);
} }
if (workitem.getTaskID() == TASK_ERFASSUNG || ((workitem.getTaskID() == TASK_SACHPRUEFUNG if (workitem.getTaskID() == TASK_ERFASSUNG || ((workitem.getTaskID() == TASK_SACHPRUEFUNG
|| workitem.getTaskID() == TASK_SACHPRUEFUNG_DELEGATION) || workitem.getTaskID() == TASK_SACHPRUEFUNG_DELEGATION)
&& workitem.getEventID() == EVENT_FREIGEBEN)) { && workitem.getEventID() == EVENT_FREIGEBEN)) {
// Buchungsperiode auf plausi prüfen // Buchungsperiode auf plausi prüfen
if (isCargoRechnung(workitem) && "workitem".equals(workitem.getType())) { if (isCargoRechnung(workitem) && "workitem".equals(workitem.getType())) {
validateBuchungsperiode(workitem.getItemValueString(InvoiceUtil.ITEM_INVOICE_PERIOD)); validateBuchungsperiode(workitem.getItemValueString(InvoiceUtil.ITEM_INVOICE_PERIOD));
} }
// Cargosoft Kreditorennnummer prüfen // Cargosoft Kreditorennnummer prüfen
if (isCargoRechnung(workitem)) { if (isCargoRechnung(workitem)) {
validateCargosoftCdtrNumber(workitem); validateCargosoftCdtrNumber(workitem);
} }
} }
} }
// die prüfung der positionsnummern und Category erfolgt nur im Status // die prüfung der positionsnummern und Category erfolgt nur im Status
// Sachprüfung (5200) und nur beim Freigeben (20)! // Sachprüfung (5200) und nur beim Freigeben (20)!
if (isCargoRechnung(workitem) if (isCargoRechnung(workitem)
&& ((workitem.getTaskID() == TASK_SACHPRUEFUNG || workitem.getTaskID() == TASK_SACHPRUEFUNG_DELEGATION) && ((workitem.getTaskID() == TASK_SACHPRUEFUNG || workitem.getTaskID() == TASK_SACHPRUEFUNG_DELEGATION)
&& workitem.getEventID() == EVENT_FREIGEBEN)) { && workitem.getEventID() == EVENT_FREIGEBEN)) {
List<ItemCollection> childs = InvoiceUtil.explodeChildList(workitem); List<ItemCollection> childs = InvoiceUtil.explodeChildList(workitem);
for (ItemCollection posItem : childs) { for (ItemCollection posItem : childs) {
if (posItem.getItemValueString("name").trim().isEmpty()) { if (posItem.getItemValueString("name").trim().isEmpty()) {
// throw a plugin exception - because name is missing! // throw a plugin exception - because name is missing!
String message = resourceBundleHandler.findMessage("ERROR_MISSING_POSNO"); String message = resourceBundleHandler.findMessage("ERROR_MISSING_POSNO");
message = message.replace("{1}", posItem.getItemValueString("numpos")); message = message.replace("{1}", posItem.getItemValueString("numpos"));
throw new PluginException(InvoicePlugin.class.getName(), ERROR_MISSING_DATA, message); throw new PluginException(InvoicePlugin.class.getName(), ERROR_MISSING_DATA, message);
} }
// validate pos for regex pattern 'XX-YYY-####-###' // validate pos for regex pattern 'XX-YYY-####-###'
if (!posItem.getItemValueString("name").matches(invoicePositionsPattern)) { if (!posItem.getItemValueString("name").matches(invoicePositionsPattern)) {
// throw a plugin exception - because name is missing! // throw a plugin exception - because name is missing!
String message = resourceBundleHandler.findMessage("ERROR_FORMAT_POSNO"); String message = resourceBundleHandler.findMessage("ERROR_FORMAT_POSNO");
message = message.replace("{1}", posItem.getItemValueString("numpos")); message = message.replace("{1}", posItem.getItemValueString("numpos"));
throw new PluginException(InvoicePlugin.class.getName(), ERROR_MISSING_DATA, message); throw new PluginException(InvoicePlugin.class.getName(), ERROR_MISSING_DATA, message);
} }
if (posItem.getItemValueString("category").trim().isEmpty()) { if (posItem.getItemValueString("category").trim().isEmpty()) {
// throw a plugin exception - because name is missing! // throw a plugin exception - because name is missing!
String message = resourceBundleHandler.findMessage("ERROR_MISSING_CATEGORY"); String message = resourceBundleHandler.findMessage("ERROR_MISSING_CATEGORY");
message = message.replace("{1}", posItem.getItemValueString("numpos")); message = message.replace("{1}", posItem.getItemValueString("numpos"));
throw new PluginException(InvoicePlugin.class.getName(), ERROR_MISSING_DATA, throw new PluginException(InvoicePlugin.class.getName(), ERROR_MISSING_DATA,
"Eingabefehler - Die Leistungsart in Zeile " + posItem.getItemValueString("numpos") "Eingabefehler - Die Leistungsart in Zeile " + posItem.getItemValueString("numpos")
+ " muss ausgefüllt sein!"); + " muss ausgefüllt sein!");
} }
if (posItem.getItemValueString("tax").trim().isEmpty()) { if (posItem.getItemValueString("tax").trim().isEmpty()) {
// throw a plugin exception - because name is missing! // throw a plugin exception - because name is missing!
String message = resourceBundleHandler.findMessage("ERROR_MISSING_TAX"); String message = resourceBundleHandler.findMessage("ERROR_MISSING_TAX");
message = message.replace("{1}", posItem.getItemValueString("numpos")); message = message.replace("{1}", posItem.getItemValueString("numpos"));
throw new PluginException(InvoicePlugin.class.getName(), ERROR_MISSING_DATA, message); throw new PluginException(InvoicePlugin.class.getName(), ERROR_MISSING_DATA, message);
} }
if (posItem.getItemValueFloat("amount") == 0) { if (posItem.getItemValueFloat("amount") == 0) {
// throw a plugin exception - because name is missing! // throw a plugin exception - because name is missing!
String message = resourceBundleHandler.findMessage("ERROR_MISSING_NET"); String message = resourceBundleHandler.findMessage("ERROR_MISSING_NET");
message = message.replace("{1}", posItem.getItemValueString("numpos")); message = message.replace("{1}", posItem.getItemValueString("numpos"));
throw new PluginException(InvoicePlugin.class.getName(), ERROR_MISSING_DATA, message); throw new PluginException(InvoicePlugin.class.getName(), ERROR_MISSING_DATA, message);
} }
// Buchunsperiode // Buchunsperiode
if (!posItem.getItemValueString(InvoiceUtil.ITEM_INVOICE_PERIOD).trim().isEmpty()) { if (!posItem.getItemValueString(InvoiceUtil.ITEM_INVOICE_PERIOD).trim().isEmpty()) {
validateBuchungsperiode(posItem.getItemValueString(InvoiceUtil.ITEM_INVOICE_PERIOD)); validateBuchungsperiode(posItem.getItemValueString(InvoiceUtil.ITEM_INVOICE_PERIOD));
} }
} }
// Ergaenzung 18.05.2021: // Ergaenzung 18.05.2021:
// es kann vorkommen, das die Anwender abweichende Buchungsperioden eingeben so // es kann vorkommen, das die Anwender abweichende Buchungsperioden eingeben so
// das die Hauptbuchungsperiode gar ncht merh vorkommt. Das darf aber nicht der // das die Hauptbuchungsperiode gar ncht merh vorkommt. Das darf aber nicht der
// fall sein. // fall sein.
// Im folgenden überprüfen wir ob eine Buchungszeile vorkommt in der keine oder // Im folgenden überprüfen wir ob eine Buchungszeile vorkommt in der keine oder
// die Hauptbuchunsperiode ausgewählt wurde. Ist das nicht der Fal gibt es eine // die Hauptbuchunsperiode ausgewählt wurde. Ist das nicht der Fal gibt es eine
// Fehlermeldung für den Anwendere // Fehlermeldung für den Anwendere
if (isCargoRechnung(workitem) && childs.size() > 0) { if (isCargoRechnung(workitem) && childs.size() > 0) {
String hauptBuchungsperiode = workitem.getItemValueString("invoice.period"); String hauptBuchungsperiode = workitem.getItemValueString("invoice.period");
boolean buchungsperiodenValid = false; boolean buchungsperiodenValid = false;
for (ItemCollection posItem : childs) { for (ItemCollection posItem : childs) {
String posBuchungsperiode = posItem.getItemValueString("invoice.period"); String posBuchungsperiode = posItem.getItemValueString("invoice.period");
if (posBuchungsperiode.isEmpty() || posBuchungsperiode.equals(hauptBuchungsperiode)) { if (posBuchungsperiode.isEmpty() || posBuchungsperiode.equals(hauptBuchungsperiode)) {
// alles fein! // alles fein!
buchungsperiodenValid = true; buchungsperiodenValid = true;
} }
} }
if (buchungsperiodenValid == false) { if (buchungsperiodenValid == false) {
// fehlerhafte Buchungsperioden. // fehlerhafte Buchungsperioden.
String message = resourceBundleHandler.findMessage("ERROR_MAIN_BOOKING_PERIOD"); String message = resourceBundleHandler.findMessage("ERROR_MAIN_BOOKING_PERIOD");
throw new PluginException(InvoicePlugin.class.getName(), ERROR_MISSING_DATA, message); throw new PluginException(InvoicePlugin.class.getName(), ERROR_MISSING_DATA, message);
} }
} }
} }
return workitem; return workitem;
} }
/** /**
* Hilfsroutine die das _img item aktualisiert. * Hilfsroutine die das _img item aktualisiert.
*/ */
private void updateImg(ItemCollection workitem) { private void updateImg(ItemCollection workitem) {
// Update _img icon list // Update _img icon list
String img = workitem.getItemValueString("_img"); String img = workitem.getItemValueString("_img");
// 031=ablehnen // 031=ablehnen
// 100=sofortüberweichung // 100=sofortüberweichung
// 074=mahnen // 074=mahnen
// sofortüberweisung // sofortüberweisung
if (workitem.getEventID() == 96 && !img.contains("100.png")) { if (workitem.getEventID() == 96 && !img.contains("100.png")) {
img = img + "<img title='Sofortüberweisung' src=\"/layout/icons/icon100.png\">"; img = img + "<img title='Sofortüberweisung' src=\"/layout/icons/icon100.png\">";
} }
// mahnen // mahnen
if (workitem.getEventID() == 95 && !img.contains("103.png")) { if (workitem.getEventID() == 95 && !img.contains("103.png")) {
img = img + "<img title='Mahnen' src=\"/layout/icons/icon103.png\">"; img = img + "<img title='Mahnen' src=\"/layout/icons/icon103.png\">";
} }
// ablehnen // ablehnen
if (workitem.getTaskID() == 5100 && workitem.getEventID() == 90 && !img.contains("028.png")) { if (workitem.getTaskID() == 5100 && workitem.getEventID() == 90 && !img.contains("028.png")) {
img = img + "<img title='Abgelehnt' src=\"/layout/icons/icon028.png\">"; img = img + "<img title='Abgelehnt' src=\"/layout/icons/icon028.png\">";
} }
if (workitem.getTaskID() == 5200 && workitem.getEventID() == 90 && !img.contains("028.png")) { if (workitem.getTaskID() == 5200 && workitem.getEventID() == 90 && !img.contains("028.png")) {
img = img + "<img title='Abgelehnt' src=\"/layout/icons/icon028.png\">"; img = img + "<img title='Abgelehnt' src=\"/layout/icons/icon028.png\">";
} }
workitem.setItemValue("_img", img); workitem.setItemValue("_img", img);
} }
/** /**
* Diese Method prüft die Buchungsperiode auf Plausibilität * Diese Method prüft die Buchungsperiode auf Plausibilität
* *
* YYYY(+1)01-12 * YYYY(+1)01-12
* *
* z.b. 202110 oder 202107 oder 202201 * z.b. 202110 oder 202107 oder 202201
* *
* @param workitem * @param workitem
* @throws PluginException * @throws PluginException
*/ */
private void validateBuchungsperiode(String period) throws PluginException { private void validateBuchungsperiode(String period) throws PluginException {
// buchungsperionde nur prüfen wenn noch nicht archiviert // buchungsperionde nur prüfen wenn noch nicht archiviert
if (!period.isEmpty()) { if (!period.isEmpty()) {
LocalDate localDate = LocalDate.now(); LocalDate localDate = LocalDate.now();
int year = localDate.getYear(); int year = localDate.getYear();
// build regex.... // build regex....
String regex = "(" + year + "|" + (year + 1) + "|" + (year - 1) + ")(1[0-2]|0[1-9])"; String regex = "(" + year + "|" + (year + 1) + "|" + (year - 1) + ")(1[0-2]|0[1-9])";
if (!period.matches(regex)) { if (!period.matches(regex)) {
// throw a plugin exception - because name is missing! // throw a plugin exception - because name is missing!
String message = resourceBundleHandler.findMessage("ERROR_BOOKING_PERIOD"); String message = resourceBundleHandler.findMessage("ERROR_BOOKING_PERIOD");
throw new PluginException(InvoicePlugin.class.getName(), ERROR_MISSING_DATA, message); throw new PluginException(InvoicePlugin.class.getName(), ERROR_MISSING_DATA, message);
} }
} }
} }
/** /**
* Diese Method prüft ob die "invoice.number" bereits einmal im * Diese Method prüft ob die "invoice.number" bereits einmal im
* Rechnugnsworkflow vergeben wurde. Falls ja wird eine PluginExcpetion * Rechnugnsworkflow vergeben wurde. Falls ja wird eine PluginExcpetion
* ausgelöst. * ausgelöst.
* <p> * <p>
* Query Example: * Query Example:
* <p> * <p>
* <code>NOT $uniqueid:"9f72fa50-4845-41ea-b6b9-ccd518c353be" AND * <code>NOT $uniqueid:"9f72fa50-4845-41ea-b6b9-ccd518c353be" AND
txtcooperatespace:"9dba107e-f8ef-4150-a832-040d75a6eda7" AND invoice.number:"45"</code> txtcooperatespace:"9dba107e-f8ef-4150-a832-040d75a6eda7" AND invoice.number:"45"</code>
* *
* <p> * <p>
* in case a duplicate invoice was detected the item invoice.number.duplicate is * in case a duplicate invoice was detected the item invoice.number.duplicate is
* filled. This item is used for a conditional event. The case is displayed as a * filled. This item is used for a conditional event. The case is displayed as a
* warning in the form. * warning in the form.
* *
* @throws PluginException * @throws PluginException
* @throws QueryException * @throws QueryException
* *
*/ */
private void validateInvoiceNumber(ItemCollection workitem) throws PluginException { private void validateInvoiceNumber(ItemCollection workitem) throws PluginException {
String invoiceNumber = workitem.getItemValueString("invoice.number"); String invoiceNumber = workitem.getItemValueString("invoice.number");
// strip // strip
String invoiceNumberStripped = invoiceNumber.replace(" ", ""); String invoiceNumberStripped = invoiceNumber.replace(" ", "");
workitem.setItemValue("invoice.number.stripped", invoiceNumberStripped); workitem.setItemValue("invoice.number.stripped", invoiceNumberStripped);
String invoiceNumberDuplicate = workitem.getItemValueString("invoice.number.duplicate"); String invoiceNumberDuplicate = workitem.getItemValueString("invoice.number.duplicate");
// wenn keine Rechnungsnummer eingegeben wurde gehts weiter! // wenn keine Rechnungsnummer eingegeben wurde gehts weiter!
if (invoiceNumber.isEmpty()) { if (invoiceNumber.isEmpty()) {
return; return;
} }
// rechnungseingang only workitems... // rechnungseingang only workitems...
// Change 11.4.22 - we have now an additional field: invoice.number.stripped // Change 11.4.22 - we have now an additional field: invoice.number.stripped
String query = "(type:workitem OR type:workitemarchive) AND ($modelversion:rechnungseingang*) AND NOT ($uniqueid:\"" String query = "(type:workitem OR type:workitemarchive) AND ($modelversion:rechnungseingang*) AND NOT ($uniqueid:\""
+ workitem.getUniqueID() + "\") AND ((invoice.number:\"" + invoiceNumberStripped + workitem.getUniqueID() + "\") AND ((invoice.number:\"" + invoiceNumberStripped
+ "\") OR (invoice.number.stripped:\"" + invoiceNumberStripped + "\"))"; + "\") OR (invoice.number.stripped:\"" + invoiceNumberStripped + "\"))";
try { try {
int result = this.getWorkflowService().getDocumentService().count(query, 1); int result = this.getWorkflowService().getDocumentService().count(query, 1);
if (result > 0) { if (result > 0) {
// wenn _invoicenumber_duplicate bereits gesetzt ist - dann geht es ohne prüfung // wenn _invoicenumber_duplicate bereits gesetzt ist - dann geht es ohne prüfung
// weiter // weiter
if (!invoiceNumberDuplicate.isEmpty()) { if (!invoiceNumberDuplicate.isEmpty()) {
logger.warning("...validateion skipped by user with duplicate invoice number: " + invoiceNumber); logger.warning("...validateion skipped by user with duplicate invoice number: " + invoiceNumber);
} else { } else {
// set _invoicenumber_duplicate - dadurch wird die warnmeldung ausgegeben und // set _invoicenumber_duplicate - dadurch wird die warnmeldung ausgegeben und
// der Vorgang nicht weitergeleitet // der Vorgang nicht weitergeleitet
workitem.replaceItemValue("invoice.number.duplicate", invoiceNumber); workitem.replaceItemValue("invoice.number.duplicate", invoiceNumber);
String message = resourceBundleHandler.findMessage("ERROR_INVOICENO"); String message = resourceBundleHandler.findMessage("ERROR_INVOICENO");
throw new PluginException(InvoicePlugin.class.getName(), ERROR_DUPPLICATE_INVOICE_NUMBER, message); throw new PluginException(InvoicePlugin.class.getName(), ERROR_DUPPLICATE_INVOICE_NUMBER, message);
} }
} else { } else {
// clear ! // clear !
workitem.appendItemValue("invoice.number.duplicate.history", workitem.appendItemValue("invoice.number.duplicate.history",
workitem.getItemValueString("invoice.number.duplicate")); workitem.getItemValueString("invoice.number.duplicate"));
workitem.replaceItemValue("invoice.number.duplicate", ""); workitem.replaceItemValue("invoice.number.duplicate", "");
} }
} catch (QueryException e) { } catch (QueryException e) {
throw new PluginException(PluginException.class.getName(), "QUERY ERROR", e.getMessage(), e); throw new PluginException(PluginException.class.getName(), "QUERY ERROR", e.getMessage(), e);
} }
} }
/** /**
* Diese Method prüft ob die eingegebene IBAN nummer bereits in dem ausgewälten * Diese Method prüft ob die eingegebene IBAN nummer bereits in dem ausgewälten
* Kreditor bekannt ist. Fallst nicht wird diese schnell mal in den Kreditoren * Kreditor bekannt ist. Fallst nicht wird diese schnell mal in den Kreditoren
* Datensatz übertragen. Dadurch muss man nicht extra die Kreditoren Verwaltung * Datensatz übertragen. Dadurch muss man nicht extra die Kreditoren Verwaltung
* aufrufen. Es ist ein art selbst-lernendes System. * aufrufen. Es ist ein art selbst-lernendes System.
* <p> * <p>
* Die Mehtode ruft eine Plugin Exception aus um den User zu fragen ob er das * Die Mehtode ruft eine Plugin Exception aus um den User zu fragen ob er das
* möchte. * möchte.
* *
* @throws PluginException * @throws PluginException
* @throws QueryException * @throws QueryException
* *
*/ */
private void checkIBANNumber(ItemCollection workitem) throws PluginException { private void checkIBANNumber(ItemCollection workitem) throws PluginException {
String cdtrNumber = workitem.getItemValueString("cdtr.number"); String cdtrNumber = workitem.getItemValueString("cdtr.number");
String iban = workitem.getItemValueString("cdtr.iban"); String iban = workitem.getItemValueString("cdtr.iban");
String bic = workitem.getItemValueString("cdtr.bic"); String bic = workitem.getItemValueString("cdtr.bic");
String overtakeIBAN = workitem.getItemValueString("ibanbic.overtake"); String overtakeIBAN = workitem.getItemValueString("ibanbic.overtake");
if (cdtrNumber.isEmpty() || iban.isEmpty() || bic.isEmpty()) { if (cdtrNumber.isEmpty() || iban.isEmpty() || bic.isEmpty()) {
// no op // no op
return; return;
} }
ItemCollection businessPartner = businessPartnerService ItemCollection businessPartner = businessPartnerService
.getBusinessPartnerByID(InvoiceUtil.buildBPID(cdtrNumber)); .getBusinessPartnerByID(InvoiceUtil.buildBPID(cdtrNumber));
if (businessPartner != null) { if (businessPartner != null) {
// update country // update country
workitem.setItemValue("invoice.country", businessPartner.getItemValueString("partner.country")); workitem.setItemValue("invoice.country", businessPartner.getItemValueString("partner.country"));
// wenn bereits eine Übernahme angedroht wurde, dann übernehmen wir die neue // wenn bereits eine Übernahme angedroht wurde, dann übernehmen wir die neue
// IBAN/BIC! // IBAN/BIC!
if (!overtakeIBAN.isEmpty()) { if (!overtakeIBAN.isEmpty()) {
businessPartnerService.addNewIBANBIC(businessPartner, iban, bic); businessPartnerService.addNewIBANBIC(businessPartner, iban, bic);
workitem.setItemValue("ibanbic.overtake", ""); workitem.setItemValue("ibanbic.overtake", "");
return; return;
} }
// Prüfen ob wir die IBAN schon kennen // Prüfen ob wir die IBAN schon kennen
if (!businessPartnerService.isIBANBICKnown(businessPartner, iban, bic)) { if (!businessPartnerService.isIBANBICKnown(businessPartner, iban, bic)) {
// OK - scheinbar ist diese IBAN/BIC nicht bekannt. Also fragen wir mal nach.... // OK - scheinbar ist diese IBAN/BIC nicht bekannt. Also fragen wir mal nach....
workitem.setItemValue("ibanbic.overtake", iban + bic); workitem.setItemValue("ibanbic.overtake", iban + bic);
String message = resourceBundleHandler.findMessage("ERROR_IBAN_UNKNOWN"); String message = resourceBundleHandler.findMessage("ERROR_IBAN_UNKNOWN");
throw new PluginException(InvoicePlugin.class.getName(), ERROR_NEW_IBANBIC, message); throw new PluginException(InvoicePlugin.class.getName(), ERROR_NEW_IBANBIC, message);
} }
} }
} }
/** /**
* Hilfsmethod prüft ob die cdtr.nummer mit einer nummer aus der Cargosoft * Hilfsmethod prüft ob die cdtr.nummer mit einer nummer aus der Cargosoft
* Import Datei übereinstimmt. * Import Datei übereinstimmt.
* *
* @param workitem * @param workitem
* @throws PluginException * @throws PluginException
*/ */
private void validateCargosoftCdtrNumber(ItemCollection workitem) throws PluginException { private void validateCargosoftCdtrNumber(ItemCollection workitem) throws PluginException {
String crdtrNumber = workitem.getItemValueString("cdtr.number"); String crdtrNumber = workitem.getItemValueString("cdtr.number");
// wenn keine Nummer eingegeben wurde gehts weiter! // wenn keine Nummer eingegeben wurde gehts weiter!
if (crdtrNumber.isEmpty()) { if (crdtrNumber.isEmpty()) {
return; return;
} }
// search creditor number in cargosoft... // search creditor number in cargosoft...
// die cargosoft Kreditornummer beginnt seltsamerweise mit einem K..... // die cargosoft Kreditornummer beginnt seltsamerweise mit einem K.....
try { try {
String query = "(type:cargosoftkreditor) AND (name:K" + crdtrNumber + " OR name:" + crdtrNumber + ")"; String query = "(type:cargosoftkreditor) AND (name:K" + crdtrNumber + " OR name:" + crdtrNumber + ")";
List<ItemCollection> result = this.getWorkflowService().getDocumentService().find(query, 1, 0); List<ItemCollection> result = this.getWorkflowService().getDocumentService().find(query, 1, 0);
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
ItemCollection cargo = result.get(0); ItemCollection cargo = result.get(0);
workitem.setItemValue("cdtr.name.cargosoft", cargo.getItemValueString("_VENDOR_Name")); workitem.setItemValue("cdtr.name.cargosoft", cargo.getItemValueString("_VENDOR_Name"));
} }
} catch (QueryException e) { } catch (QueryException e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
/** /**
* This method returns true if the current workitem is a Cargosoft Invoice. * This method returns true if the current workitem is a Cargosoft Invoice.
* *
* This can be based on different model versions e.g. rechnungseingang-de-1.2, * This can be based on different model versions e.g. rechnungseingang-de-1.2,
* rechnungseingang-pl-1.0, rechnungseingang-dwc-1.0, ... * rechnungseingang-pl-1.0, rechnungseingang-dwc-1.0, ...
* *
* @param workitem * @param workitem
* @return * @return
*/ */
public static boolean isCargoRechnung(ItemCollection workitem) { public static boolean isCargoRechnung(ItemCollection workitem) {
String REGEX_PATTERN = "rechnungseingang-([a-z]{2}|[a-z]{3})-\\d.\\d"; String REGEX_PATTERN = "rechnungseingang-([a-z]{2}|[a-z]{3})-\\d.\\d";
// Erstellen Sie ein Pattern-Objekt // Erstellen Sie ein Pattern-Objekt
Pattern pattern = Pattern.compile(REGEX_PATTERN); Pattern pattern = Pattern.compile(REGEX_PATTERN);
Matcher matcher = pattern.matcher(workitem.getModelVersion()); Matcher matcher = pattern.matcher(workitem.getModelVersion());
// Überprüfen, ob das Muster übereinstimmt // Überprüfen, ob das Muster übereinstimmt
return matcher.matches(); return matcher.matches();
} }
} }

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".
@ -48,30 +47,30 @@ import jakarta.inject.Named;
@RequestScoped @RequestScoped
public class OPListExportController extends SchedulerController { public class OPListExportController extends SchedulerController {
public static final String OPLIST_EXPORT_CONFIGURATION = "OPLIST_EXPORT_CONFIGURATION"; public static final String OPLIST_EXPORT_CONFIGURATION = "OPLIST_EXPORT_CONFIGURATION";
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private static Logger logger = Logger.getLogger(OPListExportController.class.getName()); private static Logger logger = Logger.getLogger(OPListExportController.class.getName());
@Inject @Inject
OPListExportScheduler opListExportScheduler; OPListExportScheduler opListExportScheduler;
@Override @Override
public String getName() { public String getName() {
return OPLIST_EXPORT_CONFIGURATION; return OPLIST_EXPORT_CONFIGURATION;
} }
/** /**
* Returns the sepa scheduler class name. This name depends on the _export_type. * Returns the sepa scheduler class name. This name depends on the _export_type.
* *
* There are two export interfaces available - csv and XML * There are two export interfaces available - csv and XML
* *
*/ */
@Override @Override
public String getSchedulerClass() { public String getSchedulerClass() {
String schedulerClass = OPListExportScheduler.class.getName(); String schedulerClass = OPListExportScheduler.class.getName();
logger.finest("...... scheduler: " + schedulerClass); logger.finest("...... scheduler: " + schedulerClass);
return schedulerClass; return schedulerClass;
} }
} }

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.
* *
* *
* *
@ -39,101 +39,100 @@ import jakarta.inject.Inject;
*/ */
public class RenameFilenamesAdapter implements SignalAdapter { public class RenameFilenamesAdapter implements SignalAdapter {
private static Logger logger = Logger.getLogger(RenameFilenamesAdapter.class.getName()); private static Logger logger = Logger.getLogger(RenameFilenamesAdapter.class.getName());
@Inject @Inject
SnapshotService snapshotService; SnapshotService snapshotService;
/** /**
* Rename Filenames * Rename Filenames
* *
* @throws PluginException * @throws PluginException
* *
**/ **/
@Override @Override
public ItemCollection execute(ItemCollection workitem, ItemCollection event) public ItemCollection execute(ItemCollection workitem, ItemCollection event)
throws AdapterException, PluginException { throws AdapterException, PluginException {
int fileNameCounter = 0; // for multiple files int fileNameCounter = 0; // for multiple files
List<FileData> fileDataSet = workitem.getFileData(); List<FileData> fileDataSet = workitem.getFileData();
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);
if (!newFileName.equals(fileName)) { if (!newFileName.equals(fileName)) {
logger.info("Rename file name " + fileName + " -> " + newFileName); logger.info("Rename file name " + fileName + " -> " + newFileName);
// do we have content // do we have content
if (fileData.getContent().length < 10) { if (fileData.getContent().length < 10) {
// fetch snaphot data // fetch snaphot data
FileData snapShotFileData = snapshotService.getWorkItemFile(workitem.getUniqueID(), fileName); FileData snapShotFileData = snapshotService.getWorkItemFile(workitem.getUniqueID(), fileName);
// remove old data // remove old data
workitem.removeFile(fileName); workitem.removeFile(fileName);
snapShotFileData.setName(newFileName); snapShotFileData.setName(newFileName);
workitem.addFileData(snapShotFileData); workitem.addFileData(snapShotFileData);
} else { } else {
fileData.setName(newFileName); fileData.setName(newFileName);
} }
fileNameCounter++; fileNameCounter++;
} }
// Update FTP Target path information // Update FTP Target path information
String targetPath = ""; String targetPath = "";
if (InvoiceUtil.isCreditorInvoice(workitem)) { if (InvoiceUtil.isCreditorInvoice(workitem)) {
targetPath = "invoice-in/"; targetPath = "invoice-in/";
String bookingPath = workitem.getItemValueString("invoice.period"); String bookingPath = workitem.getItemValueString("invoice.period");
// Sachrechnung // Sachrechnung
if (bookingPath.isEmpty()) { if (bookingPath.isEmpty()) {
// build period from invoice.date // build period from invoice.date
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMM"); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMM");
LocalDate invoiceDate = workitem.getItemValueLocalDate("invoice.date"); LocalDate invoiceDate = workitem.getItemValueLocalDate("invoice.date");
bookingPath = invoiceDate.format(formatter); bookingPath = invoiceDate.format(formatter);
} }
if (bookingPath.length() > 4) { if (bookingPath.length() > 4) {
targetPath = targetPath + bookingPath.substring(0, 4) + "/" + bookingPath.substring(4); targetPath = targetPath + bookingPath.substring(0, 4) + "/" + bookingPath.substring(4);
} }
} else { } else {
targetPath = "invoice-out/"; targetPath = "invoice-out/";
String bookingPath = workitem.getItemValueString("invoice.bookingperiod"); String bookingPath = workitem.getItemValueString("invoice.bookingperiod");
targetPath = targetPath + bookingPath.substring(0, 4) + "/" + bookingPath.substring(4); targetPath = targetPath + bookingPath.substring(0, 4) + "/" + bookingPath.substring(4);
} }
workitem.setItemValue("ftp.target.path", targetPath + "/"); workitem.setItemValue("ftp.target.path", targetPath + "/");
} }
} }
return workitem; return workitem;
} }
/** /**
* Baut einen Filenamen aus verschiedenen Attributen zusammen. * Baut einen Filenamen aus verschiedenen Attributen zusammen.
* *
* *
* @param workitem * @param workitem
* @return * @return
*/ */
private String buildFileName(String fileName, ItemCollection workitem, int counter) { private String buildFileName(String fileName, ItemCollection workitem, int counter) {
String result = workitem.getItemValueString("sequencenumber") + "_"; String result = workitem.getItemValueString("sequencenumber") + "_";
if (InvoiceUtil.isSachrechnung(workitem)) { if (InvoiceUtil.isSachrechnung(workitem)) {
result = result + workitem.getItemValueString("invoice.number.stripped"); result = result + workitem.getItemValueString("invoice.number.stripped");
} else { } else {
// für ausgangsrechnungen skippen wir die Cargosoft XML Datei // für ausgangsrechnungen skippen wir die Cargosoft XML Datei
if (fileName.toLowerCase().endsWith(".xml")) { if (fileName.toLowerCase().endsWith(".xml")) {
return fileName; return fileName;
} }
// für die PDF Datei bauen wir einen neuen Namen // für die PDF Datei bauen wir einen neuen Namen
result = result + workitem.getItemValueString("invoice.positions"); result = result + workitem.getItemValueString("invoice.positions");
} }
if (counter > 0) { if (counter > 0) {
result = result + "_" + counter; result = result + "_" + counter;
} }
result = result + fileName.substring(fileName.lastIndexOf(".")); result = result + fileName.substring(fileName.lastIndexOf("."));
return result; return result;
} }
} }

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
@ -30,150 +27,149 @@ import jakarta.inject.Inject;
*/ */
public class SteuerbescheidPlugin extends AbstractPlugin { public class SteuerbescheidPlugin extends AbstractPlugin {
private static Logger logger = Logger.getLogger(SteuerbescheidPlugin.class.getName()); private static Logger logger = Logger.getLogger(SteuerbescheidPlugin.class.getName());
@Inject @Inject
protected DocumentService documentService; protected DocumentService documentService;
/** /**
* Prüft ob der Steuerbetrag abgerechnet ist. * Prüft ob der Steuerbetrag abgerechnet ist.
* *
* *
**/ **/
@Override @Override
public ItemCollection run(ItemCollection workitem, ItemCollection event) throws PluginException { public ItemCollection run(ItemCollection workitem, ItemCollection event) throws PluginException {
// Update Fälligkeit // Update Fälligkeit
updateFaelligkeit(workitem); updateFaelligkeit(workitem);
// Update Salden // Update Salden
updateTotalEustZoll(workitem); updateTotalEustZoll(workitem);
updateInvoiceSaldo(workitem); updateInvoiceSaldo(workitem);
return workitem; return workitem;
} }
/** /**
* Aktualisiert die EUST/ZOLL Summen * Aktualisiert die EUST/ZOLL Summen
* *
*/ */
protected void updateTotalEustZoll(ItemCollection workitem) { protected void updateTotalEustZoll(ItemCollection workitem) {
double saldoEust = 0; double saldoEust = 0;
double saldoZoll = 0; double saldoZoll = 0;
List<ItemCollection> positionsTabelle = InvoiceUtil.explodeChildList(workitem); List<ItemCollection> positionsTabelle = InvoiceUtil.explodeChildList(workitem);
for (ItemCollection pos : positionsTabelle) { for (ItemCollection pos : positionsTabelle) {
if ("EUST".equalsIgnoreCase(pos.getItemValueString("activity.type"))) { if ("EUST".equalsIgnoreCase(pos.getItemValueString("activity.type"))) {
saldoEust = saldoEust + pos.getItemValueDouble("amount"); saldoEust = saldoEust + pos.getItemValueDouble("amount");
} }
if ("ZOLL".equalsIgnoreCase(pos.getItemValueString("activity.type"))) { if ("ZOLL".equalsIgnoreCase(pos.getItemValueString("activity.type"))) {
saldoZoll = saldoZoll + pos.getItemValueDouble("amount"); saldoZoll = saldoZoll + pos.getItemValueDouble("amount");
} }
} }
workitem.setItemValue("steuer.eust", saldoEust); workitem.setItemValue("steuer.eust", saldoEust);
workitem.setItemValue("steuer.zoll", saldoZoll); workitem.setItemValue("steuer.zoll", saldoZoll);
} }
/** /**
* 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) {
boolean foundZoll = false; boolean foundZoll = false;
boolean foundEust = false; boolean foundEust = false;
Date datEust = null; Date datEust = null;
Date datZoll = null; Date datZoll = null;
// resolve datEust und datZoll from steuerbescheid.positionen ChildItems.... // resolve datEust und datZoll from steuerbescheid.positionen ChildItems....
List<ItemCollection> steuerPositionen = InvoiceUtil.explodeChildList(workitem, "steuerbescheid.position"); List<ItemCollection> steuerPositionen = InvoiceUtil.explodeChildList(workitem, "steuerbescheid.position");
for (ItemCollection steuerPos : steuerPositionen) { for (ItemCollection steuerPos : steuerPositionen) {
if (steuerPos.getItemValueString("key").startsWith("A")) { if (steuerPos.getItemValueString("key").startsWith("A")) {
datZoll = steuerPos.getItemValueDate("duedate"); datZoll = steuerPos.getItemValueDate("duedate");
} }
if (steuerPos.getItemValueString("key").startsWith("B")) { if (steuerPos.getItemValueString("key").startsWith("B")) {
datEust = steuerPos.getItemValueDate("duedate"); datEust = steuerPos.getItemValueDate("duedate");
} }
} }
// Map fälligkeit auf die Cargosoft Positionen // Map fälligkeit auf die Cargosoft Positionen
List<ItemCollection> rows = InvoiceUtil.explodeChildList(workitem); List<ItemCollection> rows = InvoiceUtil.explodeChildList(workitem);
for (ItemCollection pos : rows) { for (ItemCollection pos : rows) {
if (datZoll != null && "ZOLL".equalsIgnoreCase(pos.getItemValueString("activity.type"))) { if (datZoll != null && "ZOLL".equalsIgnoreCase(pos.getItemValueString("activity.type"))) {
pos.setItemValue("duedate", datZoll); pos.setItemValue("duedate", datZoll);
foundZoll = true; foundZoll = true;
} }
if (datEust != null && "EUST".equalsIgnoreCase(pos.getItemValueString("activity.type"))) { if (datEust != null && "EUST".equalsIgnoreCase(pos.getItemValueString("activity.type"))) {
pos.setItemValue("duedate", datEust); pos.setItemValue("duedate", datEust);
foundEust = true; foundEust = true;
} }
} }
if (foundEust) { if (foundEust) {
workitem.setItemValue("steuer.eust.due", datEust); workitem.setItemValue("steuer.eust.due", datEust);
} else { } else {
workitem.removeItem("steuer.eust.due"); workitem.removeItem("steuer.eust.due");
} }
if (foundZoll) { if (foundZoll) {
workitem.setItemValue("steuer.zoll.due", datZoll); workitem.setItemValue("steuer.zoll.due", datZoll);
} else { } else {
workitem.removeItem("steuer.zoll.due"); workitem.removeItem("steuer.zoll.due");
} }
InvoiceUtil.implodeChildList(workitem, rows); InvoiceUtil.implodeChildList(workitem, rows);
} }
/** /**
* Aktualisiert den invoice.saldo anhand der ausgehenden Rechnungen zu einer ATC * Aktualisiert den invoice.saldo anhand der ausgehenden Rechnungen zu einer ATC
* Nummer * Nummer
* *
*/ */
protected void updateInvoiceSaldo(ItemCollection workitem) { protected void updateInvoiceSaldo(ItemCollection workitem) {
String atcNummer = workitem.getItemValueString("invoice.atc.number"); String atcNummer = workitem.getItemValueString("invoice.atc.number");
logger.fine("Verify ATC-Nr.: " + atcNummer); logger.fine("Verify ATC-Nr.: " + atcNummer);
double amount = workitem.getItemValueDouble("invoice.total.net"); double amount = workitem.getItemValueDouble("invoice.total.net");
List<ItemCollection> invoices = findInvoices(atcNummer); List<ItemCollection> invoices = findInvoices(atcNummer);
// Beträge subtrahieren.... // Beträge subtrahieren....
double saldo = amount; double saldo = amount;
logger.info("Found " + invoices.size() + " matching invoices..."); logger.info("Found " + invoices.size() + " matching invoices...");
for (ItemCollection invoice : invoices) { for (ItemCollection invoice : invoices) {
workitem.setItemValueUnique("$workitemRef", invoice.getUniqueID()); workitem.setItemValueUnique("$workitemRef", invoice.getUniqueID());
// suche die Zeile in der die ATC Nummer vorkommt. // suche die Zeile in der die ATC Nummer vorkommt.
List<ItemCollection> positionen = InvoiceUtil.explodeChildList(invoice); List<ItemCollection> positionen = InvoiceUtil.explodeChildList(invoice);
for (ItemCollection pos : positionen) { for (ItemCollection pos : positionen) {
if (atcNummer.equals(pos.getItemValueString("atc.number"))) { if (atcNummer.equals(pos.getItemValueString("atc.number"))) {
double umsatz = pos.getItemValueDouble("datev.umsatz"); double umsatz = pos.getItemValueDouble("datev.umsatz");
logger.info("...Umsatz=" + umsatz); logger.info("...Umsatz=" + umsatz);
saldo = InvoiceUtil.round(saldo - umsatz); saldo = InvoiceUtil.round(saldo - umsatz);
} }
} }
} }
// Update Saldo // Update Saldo
workitem.setItemValue("invoice.saldo", InvoiceUtil.round(saldo)); workitem.setItemValue("invoice.saldo", InvoiceUtil.round(saldo));
} }
/* /*
* Sucht alle Ausgansrechnungen zu einer ATC Nummer * Sucht alle Ausgansrechnungen zu einer ATC Nummer
*/ */
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; } catch (QueryException e) {
} catch (QueryException e) { logger.severe("Failed to get invoices: " + e.getMessage());
logger.severe("Failed to get invoices: " + e.getMessage()); }
}
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,14 +125,12 @@ 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")
+ "</invoice.total>\n"); + "</invoice.total>\n");
@ -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

@ -55,27 +55,27 @@ import jakarta.inject.Named;
@RequestScoped @RequestScoped
public class CargosoftExportController extends SchedulerController { public class CargosoftExportController extends SchedulerController {
public static final String CARGOSOFT_EXPORT_CONFIGURATION = "CARGOSOFT_EXPORT_CONFIGURATION"; public static final String CARGOSOFT_EXPORT_CONFIGURATION = "CARGOSOFT_EXPORT_CONFIGURATION";
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private static Logger logger = Logger.getLogger(CargosoftExportController.class.getName()); private static Logger logger = Logger.getLogger(CargosoftExportController.class.getName());
@Override @Override
public String getName() { public String getName() {
return CARGOSOFT_EXPORT_CONFIGURATION; return CARGOSOFT_EXPORT_CONFIGURATION;
} }
/** /**
* Returns the sepa scheduler class name. This name depends on the _export_type. * Returns the sepa scheduler class name. This name depends on the _export_type.
* *
* There are two export interfaces available - csv and XML * There are two export interfaces available - csv and XML
* *
*/ */
@Override @Override
public String getSchedulerClass() { public String getSchedulerClass() {
String schedulerClass = CargosoftExportScheduler.class.getName(); String schedulerClass = CargosoftExportScheduler.class.getName();
logger.finest("...... scheduler: " + schedulerClass); logger.finest("...... scheduler: " + schedulerClass);
return schedulerClass; return schedulerClass;
} }
} }

View file

@ -54,291 +54,290 @@ import jakarta.inject.Named;
@ConversationScoped @ConversationScoped
public class ExcelExportController implements Serializable { public class ExcelExportController implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private static Logger logger = Logger.getLogger(ExcelExportController.class.getName()); private static Logger logger = Logger.getLogger(ExcelExportController.class.getName());
public static final String ERROR_CONFIG = "CONFIG_ERROR"; public static final String ERROR_CONFIG = "CONFIG_ERROR";
final String TYPE_TEXTBLOCK = "textblock"; final String TYPE_TEXTBLOCK = "textblock";
public static final int MAX_ROWS = 3000; public static final int MAX_ROWS = 3000;
@Inject @Inject
protected DocumentService documentService; protected DocumentService documentService;
@Inject @Inject
WorkflowController workflowController; WorkflowController workflowController;
@Inject @Inject
DocumentController documentController; DocumentController documentController;
@Inject @Inject
SnapshotService snapshotService; SnapshotService snapshotService;
private ItemCollection filter; private ItemCollection filter;
private CustomFormItem dbtrItem; private CustomFormItem dbtrItem;
public ExcelExportController() { public ExcelExportController() {
} }
@PostConstruct @PostConstruct
public void init() { public void init() {
reset(); reset();
} }
public ItemCollection getFilter() { public ItemCollection getFilter() {
return filter; return filter;
} }
public void setFilter(ItemCollection filter) { public void setFilter(ItemCollection filter) {
this.filter = filter; this.filter = filter;
} }
/** /**
* Fuert verschiedene Queries aus um eine Analyse der Sachprufung durchzuführen. * Fuert verschiedene Queries aus um eine Analyse der Sachprufung durchzuführen.
* *
* @throws PluginException * @throws PluginException
*/ */
public String export() throws PluginException { public String export() throws PluginException {
logger.info("start export...."); logger.info("start export....");
Date start = filter.getItemValueDate("start"); Date start = filter.getItemValueDate("start");
Date stop = filter.getItemValueDate("stop"); Date stop = filter.getItemValueDate("stop");
logger.info("...daterange=" + start + " - " + stop); logger.info("...daterange=" + start + " - " + stop);
// serach date range? // serach date range?
String sDateFrom = "191401070000"; // because * did not work here String sDateFrom = "191401070000"; // because * did not work here
String sDateTo = "211401070000"; String sDateTo = "211401070000";
SimpleDateFormat dateformat = new SimpleDateFormat("yyyyMMddHHmm"); SimpleDateFormat dateformat = new SimpleDateFormat("yyyyMMddHHmm");
if (start != null) { if (start != null) {
Calendar cal = Calendar.getInstance(); Calendar cal = Calendar.getInstance();
cal.setTime(start); cal.setTime(start);
sDateFrom = dateformat.format(cal.getTime()); sDateFrom = dateformat.format(cal.getTime());
} }
if (stop != null) { if (stop != null) {
Calendar cal = Calendar.getInstance(); Calendar cal = Calendar.getInstance();
cal.setTime(stop); cal.setTime(stop);
cal.add(Calendar.DATE, 1); cal.add(Calendar.DATE, 1);
sDateTo = dateformat.format(cal.getTime()); sDateTo = dateformat.format(cal.getTime());
} }
// query zeit // query zeit
String query = "(type:workitem OR type:workitemarchive) AND ($workflowgroup:Rechnungsausgang) "; String query = "(type:workitem OR type:workitemarchive) AND ($workflowgroup:Rechnungsausgang) ";
if (start != null && stop != null) { if (start != null && stop != null) {
query += "AND (invoice.date:[" + sDateFrom + " TO " + sDateTo + "]) "; query += "AND (invoice.date:[" + sDateFrom + " TO " + sDateTo + "]) ";
} else { } else {
throw new PluginException("ERROR", "Bitte geben Sie einen Zeitraum an", null); throw new PluginException("ERROR", "Bitte geben Sie einen Zeitraum an", null);
} }
logger.info(query); logger.info(query);
// 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 && !workflowController.getWorkitem().getItemValueString("dbtr.number").isEmpty()) {
&& !workflowController.getWorkitem().getItemValueString("dbtr.number").isEmpty()) { query += " AND (dbtr.number:" + workflowController.getWorkitem().getItemValueString("dbtr.number") + ")";
query += " AND (dbtr.number:" + workflowController.getWorkitem().getItemValueString("dbtr.number") + ")"; }
} if (!filter.getItemValueString("invoice.text").isEmpty()) {
if (!filter.getItemValueString("invoice.text").isEmpty()) { query += " AND (invoice.positions:" + filter.getItemValueString("invoice.text") + ")";
query += " AND (invoice.positions:" + filter.getItemValueString("invoice.text") + ")"; }
}
// load the text block // load the text block
String template = "ausgangsrechnungen-export_template.xlsx"; String template = "ausgangsrechnungen-export_template.xlsx";
FileData fileData = loadTextBlockFileData("Excel-Export Template", template); FileData fileData = loadTextBlockFileData("Excel-Export Template", template);
// do we found the document? // do we found the document?
if (fileData == null) { if (fileData == null) {
throw new PluginException(InkassoExportAdapter.class.getSimpleName(), ERROR_CONFIG, throw new PluginException(InkassoExportAdapter.class.getSimpleName(), ERROR_CONFIG,
"Missing Excel Export Template - template=" + template + " not found!"); "Missing Excel Export Template - template=" + template + " not found!");
} }
String targetName = "ausgangsrechnungen-export_" + dateformat.format(new Date()) + ".xlsx"; String targetName = "ausgangsrechnungen-export_" + dateformat.format(new Date()) + ".xlsx";
try { try {
List<ItemCollection> invoices = documentService.find(query, MAX_ROWS, 0, "invoice.date", true); List<ItemCollection> invoices = documentService.find(query, MAX_ROWS, 0, "invoice.date", true);
if (invoices.size() > 0) { if (invoices.size() > 0) {
insertInvoiceRows(invoices, fileData); insertInvoiceRows(invoices, fileData);
} }
fileData.setName(targetName); fileData.setName(targetName);
// See: // See:
// https://stackoverflow.com/questions/9391838/how-to-provide-a-file-download-from-a-jsf-backing-bean // https://stackoverflow.com/questions/9391838/how-to-provide-a-file-download-from-a-jsf-backing-bean
download(fileData); download(fileData);
} catch (IOException | QueryException e) { } catch (IOException | QueryException e) {
throw new PluginException(InkassoExportAdapter.class.getSimpleName(), ERROR_CONFIG, throw new PluginException(InkassoExportAdapter.class.getSimpleName(), ERROR_CONFIG,
"Failed to generate Excel Export: " + e.getMessage()); "Failed to generate Excel Export: " + e.getMessage());
} }
logger.info("Query=" + query); logger.info("Query=" + query);
return "/pages/admin/excel_export_rechnungsausgang.jsf?faces-redirect=true"; return "/pages/admin/excel_export_rechnungsausgang.jsf?faces-redirect=true";
} }
/** /**
* Helper method to initialize a download * Helper method to initialize a download
* *
* @throws IOException * @throws IOException
*/ */
public void download(FileData fileData) throws IOException { public void download(FileData fileData) throws IOException {
FacesContext facesContext = FacesContext.getCurrentInstance(); FacesContext facesContext = FacesContext.getCurrentInstance();
ExternalContext externalContext = facesContext.getExternalContext(); ExternalContext externalContext = facesContext.getExternalContext();
externalContext.responseReset(); externalContext.responseReset();
externalContext.setResponseContentType("application/vnd.ms-excel"); externalContext.setResponseContentType("application/vnd.ms-excel");
externalContext.setResponseContentLength(fileData.getContent().length); externalContext.setResponseContentLength(fileData.getContent().length);
externalContext.setResponseHeader("Content-Disposition", "attachment; filename=\"" + fileData.getName() + "\""); externalContext.setResponseHeader("Content-Disposition", "attachment; filename=\"" + fileData.getName() + "\"");
OutputStream output = externalContext.getResponseOutputStream(); OutputStream output = externalContext.getResponseOutputStream();
// Now you can write the InputStream of the file to the above OutputStream the // Now you can write the InputStream of the file to the above OutputStream the
// usual way. // usual way.
output.write(fileData.getContent()); output.write(fileData.getContent());
facesContext.responseComplete(); // Important! Otherwise Faces will attempt to render the response which facesContext.responseComplete(); // Important! Otherwise Faces will attempt to render the response which
// obviously will fail since it's already written with a file and closed. // obviously will fail since it's already written with a file and closed.
} }
/** /**
* This method returns a text-block ItemCollection for a specified name. * This method returns a text-block ItemCollection for a specified name.
* *
* @param name in attribute txtname * @param name in attribute txtname
* *
* *
*/ */
private FileData loadTextBlockFileData(String name, String fileName) { private FileData loadTextBlockFileData(String name, String fileName) {
ItemCollection textBlockItemCollection = null; ItemCollection textBlockItemCollection = null;
// load text-block by name.... // load text-block by name....
String sQuery = "(type:\"" + TYPE_TEXTBLOCK + "\" AND txtname:\"" + name + "\")"; String sQuery = "(type:\"" + TYPE_TEXTBLOCK + "\" AND txtname:\"" + name + "\")";
Collection<ItemCollection> col; Collection<ItemCollection> col;
try { try {
// find the textblock... // find the textblock...
col = documentService.find(sQuery, 1, 0); col = documentService.find(sQuery, 1, 0);
if (col.size() > 0) { if (col.size() > 0) {
textBlockItemCollection = col.iterator().next(); textBlockItemCollection = col.iterator().next();
// fetch the fileData... // fetch the fileData...
return snapshotService.getWorkItemFile(textBlockItemCollection.getUniqueID(), fileName); return snapshotService.getWorkItemFile(textBlockItemCollection.getUniqueID(), fileName);
} else { } else {
logger.warning("Missing text-block : '" + name + "'"); logger.warning("Missing text-block : '" + name + "'");
} }
} catch (QueryException e) { } catch (QueryException e) {
logger.warning("getTextBlock - invalid query: " + e.getMessage()); logger.warning("getTextBlock - invalid query: " + e.getMessage());
} }
return null; return null;
} }
/** /**
* This method reset the search and input state. * This method reset the search and input state.
*/ */
public void reset() { public void reset() {
filter = new ItemCollection(); filter = new ItemCollection();
// compute start stop based on current month // compute start stop based on current month
YearMonth startYearMonth = YearMonth.now(); YearMonth startYearMonth = YearMonth.now();
java.time.LocalDate startOfMonthDate = startYearMonth.atDay(1); java.time.LocalDate startOfMonthDate = startYearMonth.atDay(1);
java.time.LocalDate endOfMonthDate = startYearMonth.atEndOfMonth(); java.time.LocalDate endOfMonthDate = startYearMonth.atEndOfMonth();
filter.setItemValue("start", filter.setItemValue("start",
java.util.Date.from(startOfMonthDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant())); java.util.Date.from(startOfMonthDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant()));
filter.setItemValue("stop", filter.setItemValue("stop",
java.util.Date.from(endOfMonthDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant())); java.util.Date.from(endOfMonthDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant()));
workflowController.setWorkitem(new ItemCollection()); workflowController.setWorkitem(new ItemCollection());
// debtr item // debtr item
dbtrItem = new CustomFormItem("dbtr.number", "text", "", false, false, false, "", null, false, 0); dbtrItem = new CustomFormItem("dbtr.number", "text", "", false, false, false, "", null, false, 0);
} }
public CustomFormItem getItem() { public CustomFormItem getItem() {
// dbtrItem.setName("dbtr.number"); // dbtrItem.setName("dbtr.number");
return dbtrItem; return dbtrItem;
} }
/** /**
* This helper method inserts a row for each invoice * This helper method inserts a row for each invoice
* *
* @throws PluginException * @throws PluginException
*/ */
private void insertInvoiceRows(List<ItemCollection> invoices, FileData fileData) throws PluginException { private void insertInvoiceRows(List<ItemCollection> invoices, FileData fileData) throws PluginException {
// load XSSFWorkbook // load XSSFWorkbook
try (InputStream imputStream = new ByteArrayInputStream(fileData.getContent())) { try (InputStream imputStream = new ByteArrayInputStream(fileData.getContent())) {
XSSFWorkbook doc = new XSSFWorkbook(imputStream); XSSFWorkbook doc = new XSSFWorkbook(imputStream);
// NOTE: we only take the first sheet ! // NOTE: we only take the first sheet !
XSSFSheet sheet = doc.getSheetAt(0); XSSFSheet sheet = doc.getSheetAt(0);
XSSFCell standCell = getCellByRef(doc, sheet, "F6"); XSSFCell standCell = getCellByRef(doc, sheet, "F6");
standCell.setCellValue(new Date()); standCell.setCellValue(new Date());
CellReference cr = new CellReference("A11"); CellReference cr = new CellReference("A11");
XSSFRow referenceRow = sheet.getRow(cr.getRow()); XSSFRow referenceRow = sheet.getRow(cr.getRow());
int referenceRowPos = referenceRow.getRowNum() + 1; int referenceRowPos = referenceRow.getRowNum() + 1;
int rowPos = referenceRowPos; int rowPos = referenceRowPos;
// int lastRow = sheet.getLastRowNum(); // int lastRow = sheet.getLastRowNum();
int lastRow = 999; int lastRow = 999;
logger.finest("Last rownum=" + lastRow); logger.finest("Last rownum=" + lastRow);
sheet.shiftRows(rowPos, lastRow, invoices.size(), true, true); sheet.shiftRows(rowPos, lastRow, invoices.size(), true, true);
for (ItemCollection invoice : invoices) { for (ItemCollection invoice : invoices) {
logger.finest("......copy row..."); logger.finest("......copy row...");
// now create a new line.. // now create a new line..
XSSFRow row = sheet.createRow(rowPos); XSSFRow row = sheet.createRow(rowPos);
row.copyRowFrom(referenceRow, new CellCopyPolicy()); row.copyRowFrom(referenceRow, new CellCopyPolicy());
// insert values // insert values
row.getCell(0).setCellValue(invoice.getItemValueString("invoice.number")); row.getCell(0).setCellValue(invoice.getItemValueString("invoice.number"));
row.getCell(1).setCellValue(invoice.getItemValueString("dbtr.number")); row.getCell(1).setCellValue(invoice.getItemValueString("dbtr.number"));
row.getCell(2).setCellValue(invoice.getItemValueString("space.name")); row.getCell(2).setCellValue(invoice.getItemValueString("space.name"));
row.getCell(3).setCellValue(invoice.getItemValueString("invoice.text")); row.getCell(3).setCellValue(invoice.getItemValueString("invoice.text"));
row.getCell(4).setCellValue(invoice.getItemValueDate("invoice.date")); row.getCell(4).setCellValue(invoice.getItemValueDate("invoice.date"));
row.getCell(5).setCellValue(invoice.getItemValueDate("invoice.duedate")); row.getCell(5).setCellValue(invoice.getItemValueDate("invoice.duedate"));
row.getCell(6).setCellValue(invoice.getItemValueDouble("invoice.total")); row.getCell(6).setCellValue(invoice.getItemValueDouble("invoice.total"));
rowPos++; rowPos++;
} }
// delete reference row // delete reference row
sheet.shiftRows(referenceRowPos, lastRow + invoices.size(), -1, true, true); sheet.shiftRows(referenceRowPos, lastRow + invoices.size(), -1, true, true);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
// write back the file // write back the file
doc.write(byteArrayOutputStream); doc.write(byteArrayOutputStream);
doc.close(); doc.close();
byte[] newContent = byteArrayOutputStream.toByteArray(); byte[] newContent = byteArrayOutputStream.toByteArray();
fileData.setContent(newContent); fileData.setContent(newContent);
} catch (IOException e) { } catch (IOException e) {
throw new PluginException(ZahlungsavisExportAdapter.class.getSimpleName(), ERROR_CONFIG, throw new PluginException(ZahlungsavisExportAdapter.class.getSimpleName(), ERROR_CONFIG,
"failed to update excel export: " + e.getMessage()); "failed to update excel export: " + e.getMessage());
} }
} }
/** /**
* Returns a Cell by name or an optional absolute cell postion * Returns a Cell by name or an optional absolute cell postion
* <p> * <p>
* Examples for refs are 'A1', 'AB3', 'MyCell' where 'MyCell' is a named cell. * Examples for refs are 'A1', 'AB3', 'MyCell' where 'MyCell' is a named cell.
* <p> * <p>
* *
*/ */
public static XSSFCell getCellByRef(XSSFWorkbook doc, XSSFSheet sheet, String cellReference) { public static XSSFCell getCellByRef(XSSFWorkbook doc, XSSFSheet sheet, String cellReference) {
XSSFCell cell = null; XSSFCell cell = null;
// first we test if the cellName is a named cell // first we test if the cellName is a named cell
Name aNamedCell = doc.getName(cellReference); Name aNamedCell = doc.getName(cellReference);
if (aNamedCell != null) { if (aNamedCell != null) {
// yes its a named cell so we need to get the referrer Formula // yes its a named cell so we need to get the referrer Formula
logger.finest("...resolving named cell = " + aNamedCell.getNameName()); logger.finest("...resolving named cell = " + aNamedCell.getNameName());
cellReference = aNamedCell.getRefersToFormula(); cellReference = aNamedCell.getRefersToFormula();
// now we can find the cell by its ref // now we can find the cell by its ref
} }
CellReference cr = new CellReference(cellReference); CellReference cr = new CellReference(cellReference);
XSSFRow row = sheet.getRow(cr.getRow()); XSSFRow row = sheet.getRow(cr.getRow());
if (row == null) { if (row == null) {
logger.severe("Unable to resolve cell ref '" + cellReference + "'!"); logger.severe("Unable to resolve cell ref '" + cellReference + "'!");
return null; return null;
} }
cell = row.getCell(cr.getCol()); cell = row.getCell(cr.getCol());
return cell; return cell;
} }
} }

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
@ -125,24 +125,24 @@ public class MahnlaufExecuteAdapter implements SignalAdapter {
} }
switch (invoice.getTaskID()) { switch (invoice.getTaskID()) {
case 5200: case 5200:
case 5201: case 5201:
if (dunningLevel < 1) if (dunningLevel < 1)
dunningLevel = 1; dunningLevel = 1;
break; break;
case 5210: case 5210:
case 5211: case 5211:
if (dunningLevel < 2) if (dunningLevel < 2)
dunningLevel = 2; dunningLevel = 2;
break; break;
case 5220: case 5220:
if (dunningLevel < 3) if (dunningLevel < 3)
dunningLevel = 3; dunningLevel = 3;
break; break;
default: default:
if (dunningLevel < 1) if (dunningLevel < 1)
dunningLevel = 1; dunningLevel = 1;
break; break;
} }
// create a workitem stub for the mail message // create a workitem stub for the mail message
@ -179,18 +179,18 @@ public class MahnlaufExecuteAdapter implements SignalAdapter {
// compute subject // compute subject
switch (dunningLevel) { switch (dunningLevel) {
case 2: case 2:
mahnLaufWorkitem.setItemValue("mail.subject_de", "2. MAHNUNG"); mahnLaufWorkitem.setItemValue("mail.subject_de", "2. MAHNUNG");
mahnLaufWorkitem.setItemValue("mail.subject_en", "2. REMINDER"); mahnLaufWorkitem.setItemValue("mail.subject_en", "2. REMINDER");
break; break;
case 3: case 3:
mahnLaufWorkitem.setItemValue("mail.subject_de", "LETZTE AUSSERGERICHTLICHE MAHNUNG"); mahnLaufWorkitem.setItemValue("mail.subject_de", "LETZTE AUSSERGERICHTLICHE MAHNUNG");
mahnLaufWorkitem.setItemValue("mail.subject_en", "LAST REMINDER"); mahnLaufWorkitem.setItemValue("mail.subject_en", "LAST REMINDER");
break; break;
default: default:
mahnLaufWorkitem.setItemValue("mail.subject_de", "1. MAHNUNG"); mahnLaufWorkitem.setItemValue("mail.subject_de", "1. MAHNUNG");
mahnLaufWorkitem.setItemValue("mail.subject_en", "1. REMINDER"); mahnLaufWorkitem.setItemValue("mail.subject_en", "1. REMINDER");
break; break;
} }
// Verify the dbtr.mail item (split into multi value) // Verify the dbtr.mail item (split into multi value)
@ -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

@ -36,146 +36,145 @@ import jakarta.inject.Inject;
*/ */
public class MahnlaufRefAddAdapter implements SignalAdapter { public class MahnlaufRefAddAdapter implements SignalAdapter {
private static Logger logger = Logger.getLogger(MahnlaufRefAddAdapter.class.getName()); private static Logger logger = Logger.getLogger(MahnlaufRefAddAdapter.class.getName());
@EJB @EJB
ModelService modelService; ModelService modelService;
@EJB @EJB
WorkflowService workflowService = null; WorkflowService workflowService = null;
@Inject @Inject
SepaWorkflowService sepaWorkflowService; SepaWorkflowService sepaWorkflowService;
@Inject @Inject
BusinessPartnerService businessPartnerService; BusinessPartnerService businessPartnerService;
/** /**
* This method finds or create the Mahnlauf and adds a reference ($workitemref) * This method finds or create the Mahnlauf and adds a reference ($workitemref)
* to the current invoice. * to the current invoice.
* *
* @throws PluginException * @throws PluginException
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public ItemCollection execute(ItemCollection invoice, ItemCollection event) public ItemCollection execute(ItemCollection invoice, ItemCollection event)
throws AdapterException, PluginException { throws AdapterException, PluginException {
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."); }
}
String dbtrNumber = invoice.getItemValueString(MahnlaufService.ITEM_DBTR_NUMBER); String dbtrNumber = invoice.getItemValueString(MahnlaufService.ITEM_DBTR_NUMBER);
ItemCollection mahnLaufWorkitem; ItemCollection mahnLaufWorkitem;
try { try {
mahnLaufWorkitem = findMahnlauf(dbtrNumber); mahnLaufWorkitem = findMahnlauf(dbtrNumber);
if (mahnLaufWorkitem == null) { if (mahnLaufWorkitem == null) {
// create one... // create one...
mahnLaufWorkitem = createNewMahnlauf(dbtrNumber, invoice, event); mahnLaufWorkitem = createNewMahnlauf(dbtrNumber, invoice, event);
} }
// add Invoice to Mahnlauf // add Invoice to Mahnlauf
List<String> refList = mahnLaufWorkitem.getItemValue("$workitemref"); List<String> refList = mahnLaufWorkitem.getItemValue("$workitemref");
if (!refList.contains(invoice.getUniqueID())) { if (!refList.contains(invoice.getUniqueID())) {
mahnLaufWorkitem.appendItemValueUnique("$workitemref", invoice.getUniqueID()); mahnLaufWorkitem.appendItemValueUnique("$workitemref", invoice.getUniqueID());
} }
// set event 100 and process // set event 100 and process
ItemCollection mahnlaufConfig = workflowService.evalWorkflowResult(event, "mahnlauf", invoice, true); ItemCollection mahnlaufConfig = workflowService.evalWorkflowResult(event, "mahnlauf", invoice, true);
int eventID = mahnlaufConfig.getItemValueInteger("event"); int eventID = mahnlaufConfig.getItemValueInteger("event");
mahnLaufWorkitem.event(eventID); mahnLaufWorkitem.event(eventID);
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
invoice.setItemValue("dunning.ref", mahnLaufWorkitem.getUniqueID()); invoice.setItemValue("dunning.ref", mahnLaufWorkitem.getUniqueID());
return invoice; return invoice;
} }
/** /**
* Helper method finds an open mahnlauf for a debtr.numbre (name) * Helper method finds an open mahnlauf for a debtr.numbre (name)
* *
* @param key * @param key
* @return * @return
* @throws QueryException * @throws QueryException
*/ */
public ItemCollection findMahnlauf(String key) throws QueryException { public ItemCollection findMahnlauf(String key) throws QueryException {
String query = "(type:workitem) AND ($modelversion:mahnlauf*) AND (name:\"" + key + "\")"; String query = "(type:workitem) AND ($modelversion:mahnlauf*) AND (name:\"" + key + "\")";
List<ItemCollection> resultList = workflowService.getDocumentService().find(query, 1, 0, "$modified", true); List<ItemCollection> resultList = workflowService.getDocumentService().find(query, 1, 0, "$modified", true);
if (resultList.size() > 0) { if (resultList.size() > 0) {
return resultList.get(0); return resultList.get(0);
} }
// no mahnaluffound // no mahnaluffound
return null; return null;
} }
/** /**
* Helper method to create a new Mahnlauf workitem * Helper method to create a new Mahnlauf workitem
* *
* @param key * @param key
* @param invoice * @param invoice
* @return * @return
* @throws ModelException * @throws ModelException
* @throws PluginException * @throws PluginException
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")
public ItemCollection createNewMahnlauf(String key, ItemCollection invoice, ItemCollection event) public ItemCollection createNewMahnlauf(String key, ItemCollection invoice, ItemCollection event)
throws ModelException, PluginException { throws ModelException, PluginException {
String modelVersion = null; String modelVersion = null;
int taskID = -1; int taskID = -1;
int eventID = -1; int eventID = -1;
// test if the event provides a sepa export configuration // test if the event provides a sepa export configuration
ItemCollection mahnlaufConfig = workflowService.evalWorkflowResult(event, "mahnlauf", invoice, true); ItemCollection mahnlaufConfig = workflowService.evalWorkflowResult(event, "mahnlauf", invoice, true);
if (mahnlaufConfig != null && mahnlaufConfig.hasItem("modelversion") && mahnlaufConfig.hasItem("task")) { if (mahnlaufConfig != null && mahnlaufConfig.hasItem("modelversion") && mahnlaufConfig.hasItem("task")) {
logger.fine("read model information from event"); logger.fine("read model information from event");
modelVersion = mahnlaufConfig.getItemValueString("modelVersion"); modelVersion = mahnlaufConfig.getItemValueString("modelVersion");
taskID = mahnlaufConfig.getItemValueInteger("task"); taskID = mahnlaufConfig.getItemValueInteger("task");
eventID = mahnlaufConfig.getItemValueInteger("event"); eventID = mahnlaufConfig.getItemValueInteger("event");
} }
// build the sepa export workitem.... // build the sepa export workitem....
ItemCollection mahnlaufWorkitem = new ItemCollection().model(modelVersion).task(taskID); ItemCollection mahnlaufWorkitem = new ItemCollection().model(modelVersion).task(taskID);
mahnlaufWorkitem.replaceItemValue("name", key); mahnlaufWorkitem.replaceItemValue("name", key);
mahnlaufWorkitem.replaceItemValue(WorkflowKernel.CREATED, new Date()); mahnlaufWorkitem.replaceItemValue(WorkflowKernel.CREATED, new Date());
mahnlaufWorkitem.replaceItemValue(WorkflowKernel.MODIFIED, new Date()); mahnlaufWorkitem.replaceItemValue(WorkflowKernel.MODIFIED, new Date());
// set unqiueid // set unqiueid
mahnlaufWorkitem.setItemValue(WorkflowKernel.UNIQUEID, WorkflowKernel.generateUniqueID()); mahnlaufWorkitem.setItemValue(WorkflowKernel.UNIQUEID, WorkflowKernel.generateUniqueID());
// copy dbtr_iban // copy dbtr_iban
mahnlaufWorkitem.setItemValue(MahnlaufService.ITEM_DBTR_NAME, mahnlaufWorkitem.setItemValue(MahnlaufService.ITEM_DBTR_NAME,
invoice.getItemValue(MahnlaufService.ITEM_DBTR_NAME)); invoice.getItemValue(MahnlaufService.ITEM_DBTR_NAME));
// lookup email.... // lookup email....
String dbtrNumber = invoice.getItemValueString("dbtr.number"); String dbtrNumber = invoice.getItemValueString("dbtr.number");
ItemCollection businessPartner = businessPartnerService ItemCollection businessPartner = businessPartnerService
.getBusinessPartnerByID(InvoiceUtil.buildBPID(dbtrNumber)); .getBusinessPartnerByID(InvoiceUtil.buildBPID(dbtrNumber));
if (businessPartner != null) { if (businessPartner != null) {
mahnlaufWorkitem.setItemValue(MahnlaufService.ITEM_DBTR_EMAIL, businessPartner.getItemValue("dbtr.mail"));// dbtr.mail mahnlaufWorkitem.setItemValue(MahnlaufService.ITEM_DBTR_EMAIL, businessPartner.getItemValue("dbtr.mail"));// dbtr.mail
} }
mahnlaufWorkitem.setItemValue(MahnlaufService.ITEM_DBTR_NUMBER, mahnlaufWorkitem.setItemValue(MahnlaufService.ITEM_DBTR_NUMBER,
invoice.getItemValue(MahnlaufService.ITEM_DBTR_NUMBER)); invoice.getItemValue(MahnlaufService.ITEM_DBTR_NUMBER));
mahnlaufWorkitem.setItemValue("invoice.language", invoice.getItemValue("invoice.language")); mahnlaufWorkitem.setItemValue("invoice.language", invoice.getItemValue("invoice.language"));
// set workflow group name from the Task Element to identify document in xslt // set workflow group name from the Task Element to identify document in xslt
Model model = modelService.getModel(modelVersion); Model model = modelService.getModel(modelVersion);
ItemCollection task = model.getTask(taskID); ItemCollection task = model.getTask(taskID);
String modelTaskGroupName = task.getItemValueString("txtworkflowgroup"); // DO NOT CHANGE! String modelTaskGroupName = task.getItemValueString("txtworkflowgroup"); // DO NOT CHANGE!
mahnlaufWorkitem.setItemValue(WorkflowKernel.WORKFLOWGROUP, modelTaskGroupName); mahnlaufWorkitem.setItemValue(WorkflowKernel.WORKFLOWGROUP, modelTaskGroupName);
logger.info("...created new mahnlauf " + key + "..."); logger.info("...created new mahnlauf " + key + "...");
return mahnlaufWorkitem; return mahnlaufWorkitem;
} }
} }

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

@ -42,336 +42,306 @@ import jakarta.json.bind.JsonbConfig;
* Der ZohoAPIService stellt methoden für den Zugriff auf die Zoho API bereit. * Der ZohoAPIService stellt methoden für den Zugriff auf die Zoho API bereit.
*/ */
@DeclareRoles({ "org.imixs.ACCESSLEVEL.NOACCESS", "org.imixs.ACCESSLEVEL.READERACCESS", @DeclareRoles({ "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" })
@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 @Singleton
@RunAs("org.imixs.ACCESSLEVEL.MANAGERACCESS") @RunAs("org.imixs.ACCESSLEVEL.MANAGERACCESS")
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;
private HttpClient httpClient; private HttpClient httpClient;
private Jsonb jsonb; private Jsonb jsonb;
@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); }
public String findContactIDByCompanyName(String companyName, String accessToken) throws PluginException {
// Print JSON to console for verification
logger.info("├── Zoho lookup contact '" + companyName + "'...");
try {
// https://www.zohoapis.com/books/v3/contacts?organization_id=10234695&company_name=xxx'
String uri = zohoOAuthManager.getBaseURI() + "contacts?organization_id="
+ zohoOAuthManager.getOrganization() + "&company_name="
+ URLEncoder.encode(companyName, StandardCharsets.UTF_8);
logger.fine("│ ├── uri= " + uri);
logger.fine("│ ├── accessToken=" + accessToken);
HttpRequest request = HttpRequest.newBuilder().uri(URI.create(uri))
.header("Authorization", "Zoho-oauthtoken " + accessToken)
.header("Content-Type", "application/json").GET().build();
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
// Print response details to console
logger.fine("├── Zoho Response:");
logger.fine("│ ├── Status Code: " + response.statusCode());
logger.fine("│ ├── Response Body: " + response.body());
if (response.statusCode() >= 300) {
throw new PluginException(this.getClass().getName(), "Zoho API error",
"Failed to find Contacts - Status code: " + response.statusCode() + ", Response: "
+ response.body());
}
ZohoContactFindResponse contactsResponse = jsonb.fromJson(response.body(), ZohoContactFindResponse.class);
if (contactsResponse != null && contactsResponse.getContacts() != null) {
// Durchsuche die Ergebnisse nach exakter Übereinstimmung
for (ZohoContact contact : contactsResponse.getContacts()) {
if (companyName.equalsIgnoreCase(contact.getCompanyName())) {
logger.info("│ ├── contactID= " + contact.getContactId());
return contact.getContactId();
}
}
}
return null;
} catch (Exception e) {
throw new PluginException(this.getClass().getName(), "Zoho API error", "Failed to find contacts", e);
} }
public String findContactIDByCompanyName(String companyName, String accessToken) throws PluginException { }
// Print JSON to console for verification /**
logger.info("├── Zoho lookup contact '" + companyName + "'..."); * Exports an invocie document
try { *
// https://www.zohoapis.com/books/v3/contacts?organization_id=10234695&company_name=xxx' * @see https://www.zoho.com/books/api/v3/invoices/#create-an-invoice
String uri = ZOHO_API_BASE_URL + "contacts?organization_id=" *
+ zohoOAuthManager.getOrganization() + "&company_name=" * @param invoice
+ URLEncoder.encode(companyName, StandardCharsets.UTF_8); * @throws PluginException
logger.fine("│ ├── uri= " + uri); */
logger.fine("│ ├── accessToken=" + accessToken); public ZohoInvoice exportInvoice(ItemCollection invoice, List<FileData> files) throws PluginException {
HttpRequest request = HttpRequest.newBuilder() String accessToken = zohoOAuthManager.getValidAccessToken();
.uri(URI.create(uri)) logger.info("├── Zoho Export Invoice " + invoice.getItemValueString("invoice.number") + "...");
.header("Authorization", "Zoho-oauthtoken " + accessToken)
.header("Content-Type", "application/json")
.GET()
.build();
HttpResponse<String> response = httpClient.send(
request, HttpResponse.BodyHandlers.ofString());
// Print response details to console
logger.fine("├── Zoho Response:");
logger.fine("│ ├── Status Code: " + response.statusCode());
logger.fine("│ ├── Response Body: " + response.body());
if (response.statusCode() >= 300) {
throw new PluginException(this.getClass().getName(), "Zoho API error",
"Failed to find Contacts - Status code: " + response.statusCode() +
", Response: " + response.body());
}
ZohoContactFindResponse contactsResponse = jsonb.fromJson(response.body(),
ZohoContactFindResponse.class);
if (contactsResponse != null && contactsResponse.getContacts() != null) {
// Durchsuche die Ergebnisse nach exakter Übereinstimmung
for (ZohoContact contact : contactsResponse.getContacts()) {
if (companyName.equalsIgnoreCase(contact.getCompanyName())) {
logger.info("│ ├── contactID= " + contact.getContactId());
return contact.getContactId();
}
}
}
return null;
} catch (Exception e) {
throw new PluginException(this.getClass().getName(), "Zoho API error",
"Failed to find contacts", e);
}
// first lookup contact!
String contactID = "";
contactID = findContactIDByCompanyName(invoice.getItemValueString("dbtr.name"), accessToken);
if (contactID == null) {
logger.info("│ ├── contact not found");
ZohoContact contact = createContact(invoice.getItemValueString("dbtr.name"), accessToken);
contactID = contact.getContactId();
} }
/** // Konvertiere ItemCollection zu ZohoInvoiceDTO
* Exports an invocie document ZohoInvoice zohoInvoice = convertToZohoInvoice(invoice, contactID, accessToken);
*
* @see https://www.zoho.com/books/api/v3/invoices/#create-an-invoice
*
* @param invoice
* @throws PluginException
*/
public ZohoInvoice exportInvoice(ItemCollection invoice, List<FileData> files) throws PluginException {
String accessToken = zohoOAuthManager.getValidAccessToken();
logger.info("├── Zoho Export Invoice " + invoice.getItemValueString("invoice.number") + "...");
// first lookup contact! try {
String contactID = ""; String requestBody = jsonb.toJson(zohoInvoice);
contactID = findContactIDByCompanyName(invoice.getItemValueString("dbtr.name"), accessToken);
if (contactID == null) { // Print JSON to console for verification
logger.info("│ ├── contact not found"); logger.fine("├── Zoho Invoice JSON Request...");
ZohoContact contact = createContact(invoice.getItemValueString("dbtr.name"), accessToken); logger.fine("│ ├── " + requestBody);
contactID = contact.getContactId();
// https://www.zohoapis.com/books/v3/invoices?organization_id=10234695'
String uri = zohoOAuthManager.getBaseURI() + "invoices?organization_id="
+ zohoOAuthManager.getOrganization();
logger.fine("│ ├── uri= " + uri);
logger.fine("│ ├── accessToken=" + accessToken);
HttpRequest request = HttpRequest.newBuilder().uri(URI.create(uri))
.header("Authorization", "Zoho-oauthtoken " + accessToken)
.header("Content-Type", "application/json").POST(HttpRequest.BodyPublishers.ofString(requestBody))
.build();
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
// Print response details to console
logger.fine("├── Zoho Response:");
logger.fine("│ ├── Status Code: " + response.statusCode());
if (response.statusCode() >= 300) {
throw new PluginException(this.getClass().getName(), "Zoho API error",
"Failed to create invoice. Status code: " + response.statusCode() + ", Response: "
+ response.body());
}
// Erfolgreiche Erstellung
String bodyContent = response.body();
logger.fine("│ ├── Response Body: " + bodyContent);
System.out.print(bodyContent);
ZohoInvoiceCreateResponse createResponse = jsonb.fromJson(bodyContent, ZohoInvoiceCreateResponse.class);
logger.fine("│ ├── OK - customerID=" + createResponse.getInvoice().getCustomerId());
logger.info("│ ├── OK - invoiceID=" + createResponse.getInvoice().getInvoiceId());
if (createResponse != null) {
zohoInvoice = createResponse.getInvoice();
// now add attachments (optional)
if (files != null) {
for (FileData file : files) {
attacheFileData(zohoInvoice.getInvoiceId(), file, "invoices", accessToken);
}
} }
// Konvertiere ItemCollection zu ZohoInvoiceDTO logger.info("│ ├── Invoice Export successful.");
ZohoInvoice zohoInvoice = convertToZohoInvoice(invoice, contactID, accessToken);
try {
String requestBody = jsonb.toJson(zohoInvoice);
// Print JSON to console for verification
logger.fine("├── Zoho Invoice JSON Request...");
logger.fine("│ ├── " + requestBody);
// https://www.zohoapis.com/books/v3/invoices?organization_id=10234695'
String uri = ZOHO_API_BASE_URL + "invoices?organization_id="
+ zohoOAuthManager.getOrganization();
logger.fine("│ ├── uri= " + uri);
logger.fine("│ ├── accessToken=" + accessToken);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(uri))
.header("Authorization", "Zoho-oauthtoken " + accessToken)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(requestBody))
.build();
HttpResponse<String> response = httpClient.send(
request, HttpResponse.BodyHandlers.ofString());
// Print response details to console
logger.fine("├── Zoho Response:");
logger.fine("│ ├── Status Code: " + response.statusCode());
if (response.statusCode() >= 300) {
throw new PluginException(this.getClass().getName(), "Zoho API error",
"Failed to create invoice. Status code: " + response.statusCode() +
", Response: " + response.body());
}
// Erfolgreiche Erstellung
String bodyContent = response.body();
logger.fine("│ ├── Response Body: " + bodyContent);
System.out.print(bodyContent);
ZohoInvoiceCreateResponse createResponse = jsonb.fromJson(bodyContent,
ZohoInvoiceCreateResponse.class);
logger.fine("│ ├── OK - customerID=" + createResponse.getInvoice().getCustomerId());
logger.info("│ ├── OK - invoiceID=" + createResponse.getInvoice().getInvoiceId());
if (createResponse != null) {
zohoInvoice = createResponse.getInvoice();
// now add attachments (optional)
if (files != null) {
for (FileData file : files) {
attacheFileData(zohoInvoice.getInvoiceId(), file, "invoices",
accessToken);
}
}
logger.info("│ ├── Invoice Export successful.");
return zohoInvoice;
} else {
throw new PluginException(this.getClass().getName(), "Zoho API error",
"Failed to export invoice to Zoho: unable to resolve response");
}
} catch (IOException | InterruptedException e) {
throw new PluginException(this.getClass().getName(), "Zoho API error",
"Failed to export invoice to Zoho: " + e.getMessage(), e);
}
}
/**
* Erstellt einen neuen Contact in Zoho Books
*
* @param companyName Der Firmenname für den neuen Contact
* @return Der erstellte ZohoContact mit contact_id
* @throws PluginException Wenn der API-Aufruf fehlschlägt
*/
public ZohoContact createContact(String companyName, String accessToken) throws PluginException {
logger.info("├── Zoho create contact '" + companyName + "'...");
try {
// Contact-DTO vorbereiten
ZohoContact newContact = new ZohoContact();
newContact.setContactName(companyName); // Wir verwenden den Firmennamen auch als Contact Name
newContact.setCompanyName(companyName);
// JSON serialisieren
String requestBody = jsonb.toJson(newContact);
logger.fine("│ ├── " + requestBody);
String uri = ZOHO_API_BASE_URL + "contacts?organization_id="
+ zohoOAuthManager.getOrganization();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(uri))
.header("Authorization", "Zoho-oauthtoken " + accessToken)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(requestBody))
.build();
HttpResponse<String> response = httpClient.send(
request, HttpResponse.BodyHandlers.ofString());
logger.fine("├── Zoho Response:");
logger.fine("│ ├── Status Code: " + response.statusCode());
logger.fine("│ ├── Response Body: " + response.body());
if (response.statusCode() >= 300) {
throw new PluginException(this.getClass().getName(), "Zoho API error",
"Failed to create invoice. Status code: " + response.statusCode() +
", Response: " + response.body());
}
// Erfolgreiche Erstellung
ZohoContactCreateResponse createResponse = jsonb.fromJson(response.body(),
ZohoContactCreateResponse.class);
if (createResponse != null) {
logger.info("│ ├── OK - contactID=" + createResponse.getContact().getContactId());
return createResponse.getContact();
} else {
throw new PluginException(this.getClass().getName(), "Zoho API error",
"Response could not be resolved!");
}
} catch (IOException | InterruptedException e) {
throw new PluginException(this.getClass().getName(), "Zoho API error",
"Failed to create contact: " + e.getMessage(), e);
}
}
private ZohoInvoice convertToZohoInvoice(ItemCollection invoice, String contactID, String accessToken)
throws PluginException {
ZohoInvoice zohoInvoice = new ZohoInvoice();
// Basisinformationen
logger.fine("invoice.date=" + invoice.getItemValueDate("invoice.date"));
logger.fine("invoice.duedate=" + invoice.getItemValueDate("invoice.duedate"));
// LocalDate.ofInstant(date.toInstant(), ZoneId.systemDefault())
zohoInvoice.setDate(
LocalDate.ofInstant(invoice.getItemValueDate("invoice.date").toInstant(),
ZoneId.systemDefault()));
zohoInvoice.setDueDate(
LocalDate.ofInstant(invoice.getItemValueDate("invoice.duedate").toInstant(),
ZoneId.systemDefault()));
zohoInvoice.setTotal(invoice.getItemValueDouble("invoice.total"));
// Kunden-ID (muss angepasst werden je nachdem wie du sie speicherst)
zohoInvoice.setCustomerId(contactID);
List<ItemCollection> positionen = InvoiceUtil.explodeChildList(invoice);
List<ZohoInvoiceItem> lineItems = positionen.stream()
.map(item -> {
ItemCollection itemCol = (ItemCollection) item;
ZohoInvoiceItem lineItem = new ZohoInvoiceItem();
lineItem.setName(itemCol.getItemValueString("datev.text"));
lineItem.setDescription(itemCol.getItemValueString("billingtext"));
lineItem.setRate(itemCol.getItemValueDouble("datev.umsatz"));
lineItem.setQuantity(1);
return lineItem;
})
.collect(Collectors.toList());
zohoInvoice.setLineItems(lineItems);
return zohoInvoice; return zohoInvoice;
} else {
throw new PluginException(this.getClass().getName(), "Zoho API error",
"Failed to export invoice to Zoho: unable to resolve response");
}
} catch (IOException | InterruptedException e) {
throw new PluginException(this.getClass().getName(), "Zoho API error",
"Failed to export invoice to Zoho: " + e.getMessage(), e);
} }
/** }
* This method adds an attachment to an invoice
*
* @param invoiceId The Zoho invoice ID
* @param file The FileData object containing the attachment
* @param uriPattern The API endpoint pattern (e.g., "invoices")
* @param accessToken The OAuth access token
* @throws PluginException If the API call fails
*/
public void attacheFileData(String invoiceId, FileData file, String uriPattern, String accessToken)
throws PluginException {
String uri = ZOHO_API_BASE_URL + uriPattern + "/" + invoiceId + "/attachment" +
"?organization_id=" + zohoOAuthManager.getOrganization();
logger.info("│ ├── add attachment '" + file.getName() + "'..."); /**
logger.fine("│ ├── ...uri: " + uri + "..."); * Erstellt einen neuen Contact in Zoho Books
*
* @param companyName Der Firmenname für den neuen Contact
* @return Der erstellte ZohoContact mit contact_id
* @throws PluginException Wenn der API-Aufruf fehlschlägt
*/
public ZohoContact createContact(String companyName, String accessToken) throws PluginException {
logger.info("├── Zoho create contact '" + companyName + "'...");
try {
// Contact-DTO vorbereiten
ZohoContact newContact = new ZohoContact();
newContact.setContactName(companyName); // Wir verwenden den Firmennamen auch als Contact Name
newContact.setCompanyName(companyName);
try { // JSON serialisieren
// Generate a unique boundary String requestBody = jsonb.toJson(newContact);
String boundary = "----WebKitFormBoundary" + System.currentTimeMillis(); logger.fine("│ ├── " + requestBody);
String uri = zohoOAuthManager.getBaseURI() + "contacts?organization_id="
+ zohoOAuthManager.getOrganization();
// Build multipart request body HttpRequest request = HttpRequest.newBuilder().uri(URI.create(uri))
byte[] header = ("--" + boundary + "\r\n" + .header("Authorization", "Zoho-oauthtoken " + accessToken)
"Content-Disposition: form-data; name=\"attachment\"; filename=\"" .header("Content-Type", "application/json").POST(HttpRequest.BodyPublishers.ofString(requestBody))
+ file.getName() + "\"\r\n" + .build();
"Content-Type: " + file.getContentType() + "\r\n\r\n")
.getBytes(StandardCharsets.UTF_8);
byte[] footer = ("\r\n--" + boundary + "--\r\n").getBytes(StandardCharsets.UTF_8); HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
logger.fine("├── Zoho Response:");
logger.fine("│ ├── Status Code: " + response.statusCode());
logger.fine("│ ├── Response Body: " + response.body());
if (response.statusCode() >= 300) {
throw new PluginException(this.getClass().getName(), "Zoho API error",
"Failed to create invoice. Status code: " + response.statusCode() + ", Response: "
+ response.body());
}
logger.fine("│ ├── content size=" + file.getContent().length); // Erfolgreiche Erstellung
HttpRequest.BodyPublisher bodyPublisher = HttpRequest.BodyPublishers.ofByteArrays( ZohoContactCreateResponse createResponse = jsonb.fromJson(response.body(), ZohoContactCreateResponse.class);
Arrays.asList(header, file.getContent(), footer)); if (createResponse != null) {
logger.info("│ ├── OK - contactID=" + createResponse.getContact().getContactId());
return createResponse.getContact();
} else {
throw new PluginException(this.getClass().getName(), "Zoho API error",
"Response could not be resolved!");
}
// Create and send the request } catch (IOException | InterruptedException e) {
HttpRequest request = HttpRequest.newBuilder() throw new PluginException(this.getClass().getName(), "Zoho API error",
.uri(URI.create(uri)) "Failed to create contact: " + e.getMessage(), e);
.header("Authorization", "Zoho-oauthtoken " + accessToken)
.header("Content-Type", "multipart/form-data; boundary=" + boundary)
.POST(bodyPublisher)
.build();
HttpResponse<String> response = httpClient.send(
request,
HttpResponse.BodyHandlers.ofString());
logger.fine("├── Zoho Response:");
logger.fine("│ ├── Status Code: " + response.statusCode());
logger.fine("│ ├── Response Body: " + response.body());
if (response.statusCode() >= 300) {
throw new PluginException(this.getClass().getName(), "Zoho API error",
"Failed to add attachment. Status code: " + response.statusCode() +
", Response: " + response.body());
}
logger.info("│ ├── OK - Attachment added successfully");
} catch (IOException | InterruptedException e) {
throw new PluginException(this.getClass().getName(), "Zoho API error",
"Failed to add attachment: " + e.getMessage(), e);
}
} }
}
private ZohoInvoice convertToZohoInvoice(ItemCollection invoice, String contactID, String accessToken)
throws PluginException {
ZohoInvoice zohoInvoice = new ZohoInvoice();
// Basisinformationen
zohoInvoice.setInvoiceNumber(invoice.getItemValueString("invoice.number"));
zohoInvoice.setDate(
LocalDate.ofInstant(invoice.getItemValueDate("invoice.date").toInstant(), ZoneId.systemDefault()));
zohoInvoice.setDueDate(
LocalDate.ofInstant(invoice.getItemValueDate("invoice.duedate").toInstant(), ZoneId.systemDefault()));
zohoInvoice.setTotal(invoice.getItemValueDouble("invoice.total"));
// Kunden-ID (muss angepasst werden je nachdem wie du sie speicherst)
zohoInvoice.setCustomerId(contactID);
List<ItemCollection> positionen = InvoiceUtil.explodeChildList(invoice);
List<ZohoInvoiceItem> lineItems = positionen.stream().map(item -> {
ItemCollection itemCol = (ItemCollection) item;
ZohoInvoiceItem lineItem = new ZohoInvoiceItem();
lineItem.setName(itemCol.getItemValueString("datev.text"));
lineItem.setDescription(itemCol.getItemValueString("billingtext"));
lineItem.setRate(itemCol.getItemValueDouble("datev.umsatz"));
lineItem.setQuantity(1);
return lineItem;
}).collect(Collectors.toList());
zohoInvoice.setLineItems(lineItems);
return zohoInvoice;
}
/**
* This method adds an attachment to an invoice
*
* @param invoiceId The Zoho invoice ID
* @param file The FileData object containing the attachment
* @param uriPattern The API endpoint pattern (e.g., "invoices")
* @param accessToken The OAuth access token
* @throws PluginException If the API call fails
*/
public void attacheFileData(String invoiceId, FileData file, String uriPattern, String accessToken)
throws PluginException {
String uri = zohoOAuthManager.getBaseURI() + uriPattern + "/" + //
invoiceId + //
"/attachment" + //
"?organization_id=" + zohoOAuthManager.getOrganization();
logger.info("│ ├── add attachment '" + file.getName() + "'...");
logger.fine("│ ├── ...uri: " + uri + "...");
try {
// Generate a unique boundary
String boundary = "----WebKitFormBoundary" + System.currentTimeMillis();
// Build multipart request body
byte[] header = ("--" + boundary + "\r\n"
+ "Content-Disposition: form-data; name=\"attachment\"; filename=\"" + file.getName() + "\"\r\n"
+ "Content-Type: " + file.getContentType() + "\r\n\r\n").getBytes(StandardCharsets.UTF_8);
byte[] footer = ("\r\n--" + boundary + "--\r\n").getBytes(StandardCharsets.UTF_8);
logger.fine("│ ├── content size=" + file.getContent().length);
HttpRequest.BodyPublisher bodyPublisher = HttpRequest.BodyPublishers
.ofByteArrays(Arrays.asList(header, file.getContent(), footer));
// Create and send the request
HttpRequest request = HttpRequest.newBuilder().uri(URI.create(uri))
.header("Authorization", "Zoho-oauthtoken " + accessToken)
.header("Content-Type", "multipart/form-data; boundary=" + boundary).POST(bodyPublisher).build();
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
logger.fine("├── Zoho Response:");
logger.fine("│ ├── Status Code: " + response.statusCode());
logger.fine("│ ├── Response Body: " + response.body());
if (response.statusCode() >= 300) {
throw new PluginException(this.getClass().getName(), "Zoho API error",
"Failed to add attachment. Status code: " + response.statusCode() + ", Response: "
+ response.body());
}
logger.info("│ ├── OK - Attachment added successfully");
} catch (IOException | InterruptedException e) {
throw new PluginException(this.getClass().getName(), "Zoho API error",
"Failed to add attachment: " + e.getMessage(), e);
}
}
} }

View file

@ -45,44 +45,44 @@ import jakarta.inject.Named;
@ApplicationScoped @ApplicationScoped
public class ZohoController extends ConfigController { public class ZohoController extends ConfigController {
public static final String ZOHO_CONFIGURATION = "ZOHO_CONFIGURATION"; public static final String ZOHO_CONFIGURATION = "ZOHO_CONFIGURATION";
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private static Logger logger = Logger.getLogger(ZohoController.class.getName()); private static Logger logger = Logger.getLogger(ZohoController.class.getName());
@Inject @Inject
ZohoOAuthManager zohoOAuthManager; ZohoOAuthManager zohoOAuthManager;
public ZohoController() { public ZohoController() {
super(); super();
setName(ZOHO_CONFIGURATION); setName(ZOHO_CONFIGURATION);
} }
/** /**
* Ruft zohoOAuhtManager.updateToken auf * Ruft zohoOAuhtManager.updateToken auf
*/ */
public void updateAccessToken() { public void updateAccessToken() {
// //
try { try {
zohoOAuthManager.updateTokens(this.getWorkitem().getItemValueString("zoho.code")); zohoOAuthManager.updateTokens(this.getWorkitem().getItemValueString("zoho.code"));
} catch (PluginException e) { } catch (PluginException e) {
String message = "Failed to generate token: " + e.getMessage(); String message = "Failed to generate token: " + e.getMessage();
logger.warning(message); logger.warning(message);
this.getWorkitem().setItemValue("message", message); this.getWorkitem().setItemValue("message", message);
} }
} }
/** /**
* Ruft zohoOAuhtManager.refreshToken auf * Ruft zohoOAuhtManager.refreshToken auf
*/ */
public void refreshAccessToken() { public void refreshAccessToken() {
// //
try { try {
zohoOAuthManager.refreshAccessToken(); zohoOAuthManager.refreshAccessToken();
} catch (PluginException e) { } catch (PluginException e) {
String message = "Failed to refresh token: " + e.getMessage(); String message = "Failed to refresh token: " + e.getMessage();
logger.warning(message); logger.warning(message);
this.getWorkitem().setItemValue("message", message); this.getWorkitem().setItemValue("message", message);
} }
} }
} }

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

@ -1,42 +1,42 @@
package com.alexanderlogistics.zoho.dto; package com.alexanderlogistics.zoho.dto;
public class ZohoInvoiceItem { public class ZohoInvoiceItem {
private String name; private String name;
private String description; private String description;
private double rate; private double rate;
private int quantity; private int quantity;
// Getter und Setter // Getter und Setter
public String getName() { public String getName() {
return name; return name;
} }
public void setName(String name) { public void setName(String name) {
this.name = name; this.name = name;
} }
public String getDescription() { public String getDescription() {
return description; return description;
} }
public void setDescription(String description) { public void setDescription(String description) {
this.description = description; this.description = description;
} }
public double getRate() { public double getRate() {
return rate; return rate;
} }
public void setRate(double rate) { public void setRate(double rate) {
this.rate = rate; this.rate = rate;
} }
public int getQuantity() { public int getQuantity() {
return quantity; return quantity;
} }
public void setQuantity(int quantity) { public void setQuantity(int quantity) {
this.quantity = quantity; this.quantity = quantity;
} }
} }

View file

@ -33,150 +33,149 @@ import jakarta.inject.Named;
@ConversationScoped @ConversationScoped
public class AnalyticController implements Serializable { public class AnalyticController implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private static Logger logger = Logger.getLogger(AnalyticController.class.getName()); private static Logger logger = Logger.getLogger(AnalyticController.class.getName());
@Inject @Inject
protected Event<AnalyticEvent> analyticEvents; protected Event<AnalyticEvent> analyticEvents;
@Inject @Inject
protected WorkflowController workflowController; protected WorkflowController workflowController;
/** /**
* Returns a analytic value as a String for a given key. * Returns a analytic value as a String for a given key.
* *
* @param key * @param key
* @return * @return
*/ */
public String getAsString(String key) { public String getAsString(String key) {
ItemCollection analyticData = computeValue(key); ItemCollection analyticData = computeValue(key);
return analyticData.getItemValueString("value"); return analyticData.getItemValueString("value");
} }
/** /**
* Returns a analytic value as a Json String for a given key. * Returns a analytic value as a Json String for a given key.
* *
* @param key * @param key
* @return * @return
*/ */
public String getAsJson(String key) { public String getAsJson(String key) {
ItemCollection analyticData = computeValue(key); ItemCollection analyticData = computeValue(key);
String jsonval = analyticData.getItemValueString("value"); String jsonval = analyticData.getItemValueString("value");
if (jsonval == null || jsonval.isEmpty()) { if (jsonval == null || jsonval.isEmpty()) {
return "null"; return "null";
} else { } else {
return jsonval; return jsonval;
} }
} }
/** /**
* Returns a analytic value as a Double for a given key. * Returns a analytic value as a Double for a given key.
* *
* @param key * @param key
* @return * @return
*/ */
public double getAsDouble(String key) { public double getAsDouble(String key) {
ItemCollection analyticData = computeValue(key); ItemCollection analyticData = computeValue(key);
return analyticData.getItemValueDouble("value"); return analyticData.getItemValueDouble("value");
} }
/** /**
* Returns the analytic label for a given key * Returns the analytic label for a given key
* *
* @param key * @param key
* @return * @return
*/ */
public String getLabel(String key) { public String getLabel(String key) {
ItemCollection analyticData = computeValue(key); ItemCollection analyticData = computeValue(key);
return analyticData.getItemValueString("label"); return analyticData.getItemValueString("label");
} }
/** /**
* Returns the analytic optional link for a given key * Returns the analytic optional link for a given key
* *
* @param key * @param key
* @return * @return
*/ */
public String getLink(String key) { public String getLink(String key) {
ItemCollection analyticData = computeValue(key); ItemCollection analyticData = computeValue(key);
return analyticData.getItemValueString("link"); return analyticData.getItemValueString("link");
} }
/** /**
* Returns the analytic description for a given key * Returns the analytic description for a given key
* *
* @param key * @param key
* @return * @return
*/ */
public String getDescription(String key) { public String getDescription(String key) {
ItemCollection analyticData = computeValue(key); ItemCollection analyticData = computeValue(key);
return analyticData.getItemValueString("description"); return analyticData.getItemValueString("description");
} }
/** /**
* 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. *
* * @param key
* @param key * @return
* @return */
*/ protected ItemCollection computeValue(String key) {
protected ItemCollection computeValue(String key) { if (workflowController.getWorkitem() != null) {
if (workflowController.getWorkitem() != null) { logger.fine("fire analytic event for key '" + key + "'");
logger.fine("fire analytic event for key '" + key + "'"); // Fire the Analytics Event for this key
// Fire the Analytics Event for this key AnalyticEvent event = new AnalyticEvent(key, workflowController.getWorkitem());
AnalyticEvent event = new AnalyticEvent(key, workflowController.getWorkitem()); if (analyticEvents != null) {
if (analyticEvents != null) { analyticEvents.fire(event);
analyticEvents.fire(event); if (event.getValue() != null) {
if (event.getValue() != null) { ItemCollection details = new ItemCollection();
ItemCollection details = new ItemCollection(); details.setItemValue("value", event.getValue());
details.setItemValue("value", event.getValue()); details.setItemValue("label", event.getLabel());
details.setItemValue("label", event.getLabel()); details.setItemValue("description", event.getDescription());
details.setItemValue("description", event.getDescription()); details.setItemValue("link", event.getLink());
details.setItemValue("link", event.getLink()); // cache result
// cache result implodeDetails(key, details);
implodeDetails(key, details); }
} }
} }
}
// analytic value is now already cached! // analytic value is now already cached!
return explodeDetails(key); return explodeDetails(key);
} }
/** /**
* Convert the List of ItemCollections back into a List of Map elements * Convert the List of ItemCollections back into a List of Map elements
* *
* @param workitem * @param workitem
*/ */
@SuppressWarnings({ "rawtypes" }) @SuppressWarnings({ "rawtypes" })
private void implodeDetails(String key, ItemCollection details) { private void implodeDetails(String key, ItemCollection details) {
// convert the child ItemCollection elements into a List of Map // convert the child ItemCollection elements into a List of Map
List<Map> detailsList = new ArrayList<Map>(); List<Map> detailsList = new ArrayList<Map>();
detailsList.add(details.getAllItems()); detailsList.add(details.getAllItems());
workflowController.getWorkitem().replaceItemValue(key, detailsList); workflowController.getWorkitem().replaceItemValue(key, detailsList);
} }
/** /**
* converts the Map List of a workitem into a List of ItemCollectons * converts the Map List of a workitem into a List of ItemCollectons
*/ */
@SuppressWarnings({ "rawtypes", "unchecked" }) @SuppressWarnings({ "rawtypes", "unchecked" })
private ItemCollection explodeDetails(String key) { private ItemCollection explodeDetails(String key) {
// convert current list of childItems into ItemCollection elements // convert current list of childItems into ItemCollection elements
List<Object> mapOrderItems = workflowController.getWorkitem().getItemValue(key); List<Object> mapOrderItems = workflowController.getWorkitem().getItemValue(key);
if (mapOrderItems != null && mapOrderItems.size() > 0) { if (mapOrderItems != null && mapOrderItems.size() > 0) {
ItemCollection itemCol = new ItemCollection((Map) mapOrderItems.get(0)); ItemCollection itemCol = new ItemCollection((Map) mapOrderItems.get(0));
return itemCol; return itemCol;
} }
// return empty collection // return empty collection
ItemCollection dummy = new ItemCollection(); ItemCollection dummy = new ItemCollection();
dummy.setItemValue("value", ""); dummy.setItemValue("value", "");
dummy.setItemValue("label", ""); dummy.setItemValue("label", "");
dummy.setItemValue("description", "No data available"); dummy.setItemValue("description", "No data available");
return dummy; return dummy;
} }
} }

View file

@ -10,12 +10,12 @@
<ui:define name="scripts"> <ui:define name="scripts">
<script type="text/javascript"> <script type="text/javascript">
/*<![CDATA[*/ /*<![CDATA[*/
function handleSubmit(event) { function handleSubmit(event) {
// remove ajax kringel // remove ajax kringel
setTimeout(function() { setTimeout(function () {
document.body.classList.remove('loading'); document.body.classList.remove('loading');
}, 5000); }, 5000);
} }
/*]]>*/ /*]]>*/
</script> </script>
</ui:define> </ui:define>
@ -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">
@ -65,12 +66,14 @@
Abteilung: Abteilung:
</dt> </dt>
<dd> <dd>
<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,10 +85,10 @@
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>
</dd> </dd>
</dl> </dl>
@ -94,15 +97,14 @@
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>
</div> </div>
<h:commandButton actionListener="#{excelExportController.export()}" <h:commandButton actionListener="#{excelExportController.export()}"
value="Excel Export starten"> value="Excel Export starten">
</h:commandButton> </h:commandButton>
<h:commandButton value="#{message.close}" action="notes" /> <h:commandButton value="#{message.close}" action="notes" />
</div> </div>

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

@ -19,40 +19,40 @@ import org.junit.jupiter.api.Test;
*/ */
public class TestFTP { public class TestFTP {
final static String FILE = "/DTVF_SKBeschrift_21010.csv"; final static String FILE = "/DTVF_SKBeschrift_21010.csv";
private final static Logger logger = Logger.getLogger(TestFTP.class.getName()); private final static Logger logger = Logger.getLogger(TestFTP.class.getName());
@BeforeEach @BeforeEach
public void setUp() throws Exception { public void setUp() throws Exception {
} }
/** /**
* Test Kreditoren import * Test Kreditoren import
*/ */
@Disabled @Disabled
@Test @Test
public void testPutFileToFTP() { public void testPutFileToFTP() {
FTPConnector ftpc = new FTPConnector(); FTPConnector ftpc = new FTPConnector();
ftpc.ftpServer = Optional.ofNullable("u248962.your-storagebox.de"); ftpc.ftpServer = Optional.ofNullable("u248962.your-storagebox.de");
ftpc.ftpPort = Optional.ofNullable(21); ftpc.ftpPort = Optional.ofNullable(21);
ftpc.ftpUser = Optional.ofNullable("u248962-sub2"); ftpc.ftpUser = Optional.ofNullable("u248962-sub2");
ftpc.ftpPassword = Optional.ofNullable("aXa61n9Un3jDNQlL"); ftpc.ftpPassword = Optional.ofNullable("aXa61n9Un3jDNQlL");
ftpc.ftpPath = Optional.ofNullable("/"); ftpc.ftpPath = Optional.ofNullable("/");
FileData fileData = new FileData("test.txt", "some data...".getBytes(), "text", null); FileData fileData = new FileData("test.txt", "some data...".getBytes(), "text", null);
try { try {
ftpc.put(fileData); ftpc.put(fileData);
logger.info("test finished..."); logger.info("test finished...");
} catch (PluginException e) { } catch (PluginException e) {
e.printStackTrace(); e.printStackTrace();
fail(); fail();
} }
} }
} }

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

@ -13,18 +13,18 @@ import org.junit.jupiter.api.Test;
*/ */
public class TestPostionsnummernRegex { public class TestPostionsnummernRegex {
/** /**
* Test positionnummern 'XX-YYY-####-###' * Test positionnummern 'XX-YYY-####-###'
*/ */
@Test @Test
public void testPutFileToFTP() { public void testPutFileToFTP() {
assertTrue("AA-BBC-1234-991".matches(InvoicePlugin.INVOICE_POSITIONS_PATTERN_DEFAULT)); assertTrue("AA-BBC-1234-991".matches(InvoicePlugin.INVOICE_POSITIONS_PATTERN_DEFAULT));
assertFalse("aa-BBC-1234-991".matches(InvoicePlugin.INVOICE_POSITIONS_PATTERN_DEFAULT)); assertFalse("aa-BBC-1234-991".matches(InvoicePlugin.INVOICE_POSITIONS_PATTERN_DEFAULT));
assertFalse("CC-BBC-1234-991 ".matches(InvoicePlugin.INVOICE_POSITIONS_PATTERN_DEFAULT)); assertFalse("CC-BBC-1234-991 ".matches(InvoicePlugin.INVOICE_POSITIONS_PATTERN_DEFAULT));
assertTrue("IM-ZEL-2101-005".matches(InvoicePlugin.INVOICE_POSITIONS_PATTERN_DEFAULT)); assertTrue("IM-ZEL-2101-005".matches(InvoicePlugin.INVOICE_POSITIONS_PATTERN_DEFAULT));
} }
} }

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

@ -18,65 +18,65 @@ import org.junit.jupiter.api.Test;
*/ */
public class TestSEPATransform { public class TestSEPATransform {
final static String ENCODING = "UTF-8"; final static String ENCODING = "UTF-8";
private static Logger logger = Logger.getLogger(TestSEPATransform.class.getName()); private static Logger logger = Logger.getLogger(TestSEPATransform.class.getName());
@BeforeEach @BeforeEach
public void setup() throws PluginException, ModelException { public void setup() throws PluginException, ModelException {
logger.info("setup test environment ..."); logger.info("setup test environment ...");
} }
/** /**
* XSL Transformation for SEPA Standard file * XSL Transformation for SEPA Standard file
* *
* @throws IOException * @throws IOException
*/ */
@Test @Test
public void testDefault() throws IOException { public void testDefault() throws IOException {
String MODEL_PATH_XML = "sepa/beispiel-daten.xml"; String MODEL_PATH_XML = "sepa/beispiel-daten.xml";
String MODEL_PATH_XSL = "sepa/sepa-2.0.5.xsl"; String MODEL_PATH_XSL = "sepa/sepa-2.0.5.xsl";
try { try {
XSLTester.transform(MODEL_PATH_XML, MODEL_PATH_XSL, "../reports/sepa/result_sepa01.xml"); XSLTester.transform(MODEL_PATH_XML, MODEL_PATH_XSL, "../reports/sepa/result_sepa01.xml");
} catch (Exception e) { } catch (Exception e) {
fail(); fail();
} }
} }
/** /**
* XSL Transformation for SEPA file for Bank Polski * XSL Transformation for SEPA file for Bank Polski
* *
* We also test the two-digits case. 10,5 need to become 10,50 * We also test the two-digits case. 10,5 need to become 10,50
* *
* @throws IOException * @throws IOException
*/ */
@Test @Test
public void testBankPolski() throws IOException { public void testBankPolski() throws IOException {
String MODEL_PATH_XML = "sepa/beispiel-daten.xml"; String MODEL_PATH_XML = "sepa/beispiel-daten.xml";
String MODEL_PATH_XSL = "sepa/sepa-2.0.11-pl.xsl"; String MODEL_PATH_XSL = "sepa/sepa-2.0.11-pl.xsl";
try { try {
XSLTester.transform(MODEL_PATH_XML, MODEL_PATH_XSL, "../reports/sepa/result_sepa-pl.xml"); XSLTester.transform(MODEL_PATH_XML, MODEL_PATH_XSL, "../reports/sepa/result_sepa-pl.xml");
} catch (Exception e) { } catch (Exception e) {
fail(); fail();
} }
} }
/** /**
* XSL Transformation for SEPA file for Bank Polski * XSL Transformation for SEPA file for Bank Polski
* *
* Neue Bank in Polen - bankmillennium * Neue Bank in Polen - bankmillennium
* *
* @throws IOException * @throws IOException
*/ */
@Test @Test
public void testBankPolskibankmillennium() throws IOException { public void testBankPolskibankmillennium() throws IOException {
String MODEL_PATH_XML = "sepa/beispiel-daten.xml"; String MODEL_PATH_XML = "sepa/beispiel-daten.xml";
String MODEL_PATH_XSL = "sepa/sepa-2.0.11-pl_bankmillennium.xsl"; String MODEL_PATH_XSL = "sepa/sepa-2.0.11-pl_bankmillennium.xsl";
try { try {
XSLTester.transform(MODEL_PATH_XML, MODEL_PATH_XSL, "../reports/sepa/result_sepa-pl_bankmillennium.xml"); XSLTester.transform(MODEL_PATH_XML, MODEL_PATH_XSL, "../reports/sepa/result_sepa-pl_bankmillennium.xml");
} catch (Exception e) { } catch (Exception e) {
fail(); fail();
} }
} }
} }

View file

@ -34,59 +34,59 @@ import org.imixs.workflow.xml.XSLHandler;
*/ */
public class XSLTester { public class XSLTester {
final static String ENCODING = "UTF-8"; final static String ENCODING = "UTF-8";
/** /**
* XSL Transformation * XSL Transformation
* *
* @throws IOException * @throws IOException
* @throws TransformerException * @throws TransformerException
*/ */
public static void transform(String xmlSourcePath, String xslSourcePath, String outputPath) public static void transform(String xmlSourcePath, String xslSourcePath, String outputPath)
throws IOException, TransformerException { throws IOException, TransformerException {
OutputStream outputStream = new FileOutputStream(outputPath); OutputStream outputStream = new FileOutputStream(outputPath);
// Use the class loader to load the XML file as a resource // Use the class loader to load the XML file as a resource
ClassLoader classLoader = XSLTester.class.getClassLoader(); ClassLoader classLoader = XSLTester.class.getClassLoader();
InputStream xmlInputStream = classLoader.getResourceAsStream(xmlSourcePath); InputStream xmlInputStream = classLoader.getResourceAsStream(xmlSourcePath);
InputStream xslInputStream = classLoader.getResourceAsStream(xslSourcePath); InputStream xslInputStream = classLoader.getResourceAsStream(xslSourcePath);
try { try {
if (xmlInputStream == null) { if (xmlInputStream == null) {
throw new RuntimeException("XML file not found: " + xmlSourcePath); throw new RuntimeException("XML file not found: " + xmlSourcePath);
} }
if (xslInputStream == null) { if (xslInputStream == null) {
throw new RuntimeException("XSL file not found: " + xslSourcePath); throw new RuntimeException("XSL file not found: " + xslSourcePath);
} }
String xmlSource = readXml(xmlInputStream); String xmlSource = readXml(xmlInputStream);
String xslSource = readXml(xslInputStream); String xslSource = readXml(xslInputStream);
XSLHandler.transform(xmlSource, xslSource, ENCODING, outputStream); XSLHandler.transform(xmlSource, xslSource, ENCODING, outputStream);
} finally { } finally {
if (xmlInputStream != null) if (xmlInputStream != null)
xmlInputStream.close(); xmlInputStream.close();
if (xslInputStream != null) if (xslInputStream != null)
xslInputStream.close(); xslInputStream.close();
if (outputStream != null) if (outputStream != null)
outputStream.close(); outputStream.close();
} }
} }
/** /**
* read and return the XML content as a String * read and return the XML content as a String
* *
*/ */
private static String readXml(InputStream inputStream) { private static String readXml(InputStream inputStream) {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) { try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
StringBuilder xmlContent = new StringBuilder(); StringBuilder xmlContent = new StringBuilder();
String line; String line;
while ((line = reader.readLine()) != null) { while ((line = reader.readLine()) != null) {
xmlContent.append(line).append("\n"); xmlContent.append(line).append("\n");
} }
return xmlContent.toString(); return xmlContent.toString();
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException("Error reading XML file", e); throw new RuntimeException("Error reading XML file", e);
} }
} }
} }

View file

@ -43,77 +43,77 @@ import com.alexanderlogistics.mahnlauf.MahnlaufService;
@MockitoSettings(strictness = Strictness.WARN) @MockitoSettings(strictness = Strictness.WARN)
public class EAkteImportTester { public class EAkteImportTester {
@Spy @Spy
@InjectMocks @InjectMocks
private CargosoftXMLEAkteImportService service; private CargosoftXMLEAkteImportService service;
@Mock @Mock
private DocumentService documentService; private DocumentService documentService;
@Mock @Mock
private BusinessPartnerService businessPartnerService; private BusinessPartnerService businessPartnerService;
@Mock @Mock
private MahnlaufService mahnlaufService; private MahnlaufService mahnlaufService;
@BeforeEach @BeforeEach
public void setUp() { public void setUp() {
// Mock protected Methode 'alreadyImported' -> always false // Mock protected Methode 'alreadyImported' -> always false
Mockito.when(service.alreadyImported(Mockito.anyString())).thenReturn(false); Mockito.when(service.alreadyImported(Mockito.anyString())).thenReturn(false);
} }
/** /**
* Ein Einfacher test * Ein Einfacher test
* *
* @throws TransformerException * @throws TransformerException
* @throws XPathExpressionException * @throws XPathExpressionException
* @throws ModelException * @throws ModelException
* @throws PluginException * @throws PluginException
* @throws ProcessingErrorException * @throws ProcessingErrorException
* @throws AccessDeniedException * @throws AccessDeniedException
* *
* @throws Exception * @throws Exception
*/ */
@Test @Test
public void testEAkteBasic() throws Exception { public void testEAkteBasic() throws Exception {
ItemCollection source = new ItemCollection(); ItemCollection source = new ItemCollection();
String fileName = "Abgabenbescheid-IM-GCA-2408-116N-001-ATC400012650820242452-_20240806164256864_DEBUG.xml"; String fileName = "Abgabenbescheid-IM-GCA-2408-116N-001-ATC400012650820242452-_20240806164256864_DEBUG.xml";
byte[] xmlContent = readTestFile("cargosoft/" + fileName); byte[] xmlContent = readTestFile("cargosoft/" + fileName);
assertNotNull(xmlContent); assertNotNull(xmlContent);
// Act // Act
ItemCollection result = service.createWorkitem(source, fileName, xmlContent, new HashMap<>()); ItemCollection result = service.createWorkitem(source, fileName, xmlContent, new HashMap<>());
// Assert // Assert
assertNotNull(result); assertNotNull(result);
assertEquals("001", result.getItemValueString("mandant.id")); assertEquals("001", result.getItemValueString("mandant.id"));
// test Rows // test Rows
List<ItemCollection> rows = InvoiceUtil.explodeChildList(result); List<ItemCollection> rows = InvoiceUtil.explodeChildList(result);
assertNotNull(rows); assertNotNull(rows);
assertEquals(2, rows.size()); assertEquals(2, rows.size());
ItemCollection row1 = rows.get(0); ItemCollection row1 = rows.get(0);
ItemCollection row2 = rows.get(1); ItemCollection row2 = rows.get(1);
assertEquals("LA-ZEL-2403-004", row1.getItemValueString("filenumber")); assertEquals("LA-ZEL-2403-004", row1.getItemValueString("filenumber"));
assertEquals("LA-ZEL-2403-00", row2.getItemValueString("filenumber")); assertEquals("LA-ZEL-2403-00", row2.getItemValueString("filenumber"));
assertEquals("ZOLL", row1.getItemValueString("activity.type")); assertEquals("ZOLL", row1.getItemValueString("activity.type"));
assertEquals("EUST", row2.getItemValueString("activity.type")); assertEquals("EUST", row2.getItemValueString("activity.type"));
} }
/** /**
* Hilfsmethode zum einlesen einer xml test datei * Hilfsmethode zum einlesen einer xml test datei
* *
* @return * @return
* @throws IOException * @throws IOException
*/ */
private byte[] readTestFile(String filename) throws IOException { private byte[] readTestFile(String filename) throws IOException {
// Load the file from the resources folder // Load the file from the resources folder
ClassLoader classLoader = getClass().getClassLoader(); ClassLoader classLoader = getClass().getClassLoader();
InputStream inputStream = classLoader.getResourceAsStream(filename); InputStream inputStream = classLoader.getResourceAsStream(filename);
byte[] data = inputStream.readAllBytes(); byte[] data = inputStream.readAllBytes();
inputStream.close(); inputStream.close();
return data; return data;
} }
} }

View file

@ -43,122 +43,122 @@ import com.alexanderlogistics.mahnlauf.MahnlaufService;
@MockitoSettings(strictness = Strictness.WARN) @MockitoSettings(strictness = Strictness.WARN)
public class InvoiceImportTester { public class InvoiceImportTester {
@Spy @Spy
@InjectMocks @InjectMocks
private CargosoftXMLInvoiceImportService service; private CargosoftXMLInvoiceImportService service;
@Mock @Mock
private DocumentService documentService; private DocumentService documentService;
@Mock @Mock
private MahnlaufService mahnlaufService; private MahnlaufService mahnlaufService;
@Mock @Mock
private BusinessPartnerService businessPartnerService; private BusinessPartnerService businessPartnerService;
@BeforeEach @BeforeEach
public void setUp() { public void setUp() {
// Mock protected Methode 'alreadyImported' -> always false // Mock protected Methode 'alreadyImported' -> always false
Mockito.when(service.alreadyImported(Mockito.anyString())).thenReturn(false); Mockito.when(service.alreadyImported(Mockito.anyString())).thenReturn(false);
} }
/** /**
* Ein Einfacher test * Ein Einfacher test
* *
* @throws TransformerException * @throws TransformerException
* @throws XPathExpressionException * @throws XPathExpressionException
* @throws ModelException * @throws ModelException
* @throws PluginException * @throws PluginException
* @throws ProcessingErrorException * @throws ProcessingErrorException
* @throws AccessDeniedException * @throws AccessDeniedException
* *
* @throws Exception * @throws Exception
*/ */
@Test @Test
public void testInvoiceBasic() throws Exception { public void testInvoiceBasic() throws Exception {
ItemCollection source = new ItemCollection(); ItemCollection source = new ItemCollection();
String fileName = "001_R_224380_110136787.xml"; String fileName = "001_R_224380_110136787.xml";
byte[] xmlContent = readTestFile("cargosoft/" + fileName); byte[] xmlContent = readTestFile("cargosoft/" + fileName);
assertNotNull(xmlContent); assertNotNull(xmlContent);
// Act // Act
ItemCollection result = service.createWorkitem(source, fileName, xmlContent, new HashMap<>()); ItemCollection result = service.createWorkitem(source, fileName, xmlContent, new HashMap<>());
// Assert // Assert
assertNotNull(result); assertNotNull(result);
assertEquals("001", result.getItemValueString("mandant.id")); assertEquals("001", result.getItemValueString("mandant.id"));
// test Rows // test Rows
List<ItemCollection> rows = InvoiceUtil.explodeChildList(result); List<ItemCollection> rows = InvoiceUtil.explodeChildList(result);
assertNotNull(rows); assertNotNull(rows);
assertEquals(2, rows.size()); assertEquals(2, rows.size());
ItemCollection row1 = rows.get(0); ItemCollection row1 = rows.get(0);
ItemCollection row2 = rows.get(1); ItemCollection row2 = rows.get(1);
assertEquals("EX-GCA-2408-042", row1.getItemValueString("datev.text")); assertEquals("EX-GCA-2408-042", row1.getItemValueString("datev.text"));
assertEquals("EX-GCA-2408-042", row2.getItemValueString("datev.text")); assertEquals("EX-GCA-2408-042", row2.getItemValueString("datev.text"));
} }
/** /**
* Ein ATC test * Ein ATC test
* *
* @throws IOException * @throws IOException
* *
* @throws Exception * @throws Exception
*/ */
@Test @Test
public void testInvoiceATC() throws Exception { public void testInvoiceATC() throws Exception {
ItemCollection source = new ItemCollection(); ItemCollection source = new ItemCollection();
String fileName = "001_R_224225_115103063.xml"; String fileName = "001_R_224225_115103063.xml";
byte[] xmlContent = readTestFile("cargosoft/" + fileName); byte[] xmlContent = readTestFile("cargosoft/" + fileName);
assertNotNull(xmlContent); assertNotNull(xmlContent);
// Act // Act
ItemCollection result = service.createWorkitem(source, fileName, xmlContent, new HashMap<>()); ItemCollection result = service.createWorkitem(source, fileName, xmlContent, new HashMap<>());
// Assert // Assert
assertNotNull(result); assertNotNull(result);
assertEquals("001", result.getItemValueString("mandant.id")); assertEquals("001", result.getItemValueString("mandant.id"));
assertEquals("ATC400012650820242452", result.getItemValueString("invoice.atc.number")); assertEquals("ATC400012650820242452", result.getItemValueString("invoice.atc.number"));
assertEquals(3752.95, result.getItemValueDouble("invoice.total"), 0.0); assertEquals(3752.95, result.getItemValueDouble("invoice.total"), 0.0);
// test Rows // test Rows
List<ItemCollection> rows = InvoiceUtil.explodeChildList(result); List<ItemCollection> rows = InvoiceUtil.explodeChildList(result);
assertNotNull(rows); assertNotNull(rows);
assertEquals(2, rows.size()); assertEquals(2, rows.size());
ItemCollection row1 = rows.get(0); ItemCollection row1 = rows.get(0);
assertEquals("IM-GCA-2408-116", row1.getItemValueString("datev.text")); assertEquals("IM-GCA-2408-116", row1.getItemValueString("datev.text"));
assertEquals("EUST", row1.getItemValueString("category")); assertEquals("EUST", row1.getItemValueString("category"));
assertEquals("EUST", row1.getItemValueString("BillingCode")); assertEquals("EUST", row1.getItemValueString("BillingCode"));
// test BillingText // test BillingText
List<String> billingTextList = row1.getItemValue("BillingText"); List<String> billingTextList = row1.getItemValue("BillingText");
assertEquals(2, billingTextList.size()); assertEquals(2, billingTextList.size());
// test ATC Number // test ATC Number
assertEquals("ATC400012650820242452", row1.getItemValueString("atc.number")); assertEquals("ATC400012650820242452", row1.getItemValueString("atc.number"));
// Umsatz // Umsatz
assertEquals(3591.34, row1.getItemValueDouble("datev.umsatz"), 0.0); assertEquals(3591.34, row1.getItemValueDouble("datev.umsatz"), 0.0);
ItemCollection row2 = rows.get(1); ItemCollection row2 = rows.get(1);
assertEquals("IM-GCA-2408-116", row2.getItemValueString("datev.text")); assertEquals("IM-GCA-2408-116", row2.getItemValueString("datev.text"));
assertEquals("SONST", row2.getItemValueString("category")); assertEquals("SONST", row2.getItemValueString("category"));
// Umsatz // Umsatz
assertEquals(161.61, row2.getItemValueDouble("datev.umsatz"), 0.0); assertEquals(161.61, row2.getItemValueDouble("datev.umsatz"), 0.0);
} }
/** /**
* Hilfsmethode zum einlesen einer xml test datei * Hilfsmethode zum einlesen einer xml test datei
* *
* @return * @return
* @throws IOException * @throws IOException
*/ */
private byte[] readTestFile(String filename) throws IOException { private byte[] readTestFile(String filename) throws IOException {
// Load the file from the resources folder // Load the file from the resources folder
ClassLoader classLoader = getClass().getClassLoader(); ClassLoader classLoader = getClass().getClassLoader();
InputStream inputStream = classLoader.getResourceAsStream(filename); InputStream inputStream = classLoader.getResourceAsStream(filename);
byte[] data = inputStream.readAllBytes(); byte[] data = inputStream.readAllBytes();
inputStream.close(); inputStream.close();
return data; return data;
} }
} }

View file

@ -33,240 +33,218 @@ import jakarta.json.bind.JsonbBuilder;
@Disabled @Disabled
public class TestUpdateTokens { public class TestUpdateTokens {
final static String ENCODING = "UTF-8"; final static String ENCODING = "UTF-8";
private static Logger logger = Logger.getLogger(TestUpdateTokens.class.getName()); private static Logger logger = Logger.getLogger(TestUpdateTokens.class.getName());
private HttpClient httpClient; private HttpClient httpClient;
private Jsonb jsonb; private Jsonb jsonb;
private String redirectURI = "https://alexander-logistics-dwc.office-workflow.de/api/zoho/grant"; private String redirectURI = "https://alexander-logistics-dwc.office-workflow.de/api/zoho/grant";
private String clientID = "1000.GR5FTPZYB06HEWSFSSX4DJU3OBD4EJ"; private String clientID = "1000.GR5FTPZYB06HEWSFSSX4DJU3OBD4EJ";
private String clientSecret = "3f3e74c5212bbc924a7ebf77f887c6296db1106c47"; private String clientSecret = "3f3e74c5212bbc924a7ebf77f887c6296db1106c47";
private String code = null; private String code = null;
private String accessToken = null; private String accessToken = null;
private String refreshToken = null; private String refreshToken = null;
@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)) this.jsonb = JsonbBuilder.create();
.build(); }
this.jsonb = JsonbBuilder.create();
}
/** /**
* Testet den Vorgang einen Access und Refresh Token zu generieren * Testet den Vorgang einen Access und Refresh Token zu generieren
* *
* @throws IOException * @throws IOException
*/ */
@Test @Test
public void testUpdateTokens() throws IOException { public void testUpdateTokens() throws IOException {
logger.info("├── update Zoho tokens"); logger.info("├── update Zoho tokens");
refreshToken = null; refreshToken = null;
accessToken = null; accessToken = null;
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) {
// 1. Response-Logging für Debugging // 1. Response-Logging für Debugging
logger.info("│ ├── Response Body=" + response.body()); logger.info("│ ├── Response Body=" + response.body());
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);
logger.info("│ ├── accessToken=" + accessToken); logger.info("│ ├── accessToken=" + accessToken);
refreshToken = tokenResponse.getRefresh_token(); refreshToken = tokenResponse.getRefresh_token();
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) {
throw new InvalidAccessException("Unable to resolve access tokens: " + e.getMessage()); throw new InvalidAccessException("Unable to resolve access tokens: " + e.getMessage());
} }
} }
/** /**
* Testet den Refresh Token * Testet den Refresh Token
* *
* POST: * POST:
* {Accounts_URL}/oauth/v2/token?refresh_token={refresh_token}&client_id={client_id}&client_secret={client_secret}&grant_type=refresh_token * {Accounts_URL}/oauth/v2/token?refresh_token={refresh_token}&client_id={client_id}&client_secret={client_secret}&grant_type=refresh_token
* *
* @throws IOException * @throws IOException
*/ */
@Test @Test
public void testRefreshAccessToken() throws IOException { public void testRefreshAccessToken() throws IOException {
logger.info("├── refresh Zoho access token"); logger.info("├── refresh Zoho access token");
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) {
// 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) {
throw new InvalidAccessException("Unable to resolve access tokens: " + e.getMessage()); throw new InvalidAccessException("Unable to resolve access tokens: " + e.getMessage());
} }
} }
/** /**
* Testet den eigentlichen API Zugriff mit Hilfe des zuvor generieren Access * Testet den eigentlichen API Zugriff mit Hilfe des zuvor generieren Access
* Token * Token
* *
* GET: https://{zohoapis_domain}/writer/api/v1/documents * GET: https://{zohoapis_domain}/writer/api/v1/documents
* *
* @see https://www.zoho.com/books/api/v3/introduction/#overview * @see https://www.zoho.com/books/api/v3/introduction/#overview
* @throws IOException * @throws IOException
*/ */
@Test @Test
public void testGetOrganizations() throws IOException { public void testGetOrganizations() throws IOException {
logger.info("├── get Zoho organizations"); logger.info("├── get Zoho organizations");
accessToken = "1000.21912dc6af0906707f51d336fd98ab61.eb153952d0d86a828fcb7e18426c2c1a"; accessToken = "1000.21912dc6af0906707f51d336fd98ab61.eb153952d0d86a828fcb7e18426c2c1a";
try { try {
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());
// 1. Response-Logging für Debugging // 1. Response-Logging für Debugging
logger.info("│ ├── Response Body=" + response.body()); logger.info("│ ├── Response Body=" + response.body());
if (response.statusCode() >= 300) { if (response.statusCode() >= 300) {
throw new RuntimeException("API request failed with status code: " + response.statusCode()); throw new RuntimeException("API request failed with status code: " + response.statusCode());
} }
} catch (IOException | InterruptedException e) { } catch (IOException | InterruptedException e) {
throw new InvalidAccessException("Unable to resolve access tokens: " + e.getMessage()); throw new InvalidAccessException("Unable to resolve access tokens: " + e.getMessage());
} }
} }
/** /**
* Testet den eigentlichen API Zugriff mit Hilfe des zuvor generieren Access * Testet den eigentlichen API Zugriff mit Hilfe des zuvor generieren Access
* 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'
* *
* *
* @throws IOException * @throws IOException
*/ */
@Test @Test
public void testGetInvoices() throws IOException { public void testGetInvoices() throws IOException {
logger.info("├── get Zoho documents"); logger.info("├── get Zoho documents");
accessToken = "1000.21912dc6af0906707f51d336fd98ab61.eb153952d0d86a828fcb7e18426c2c1a"; accessToken = "1000.21912dc6af0906707f51d336fd98ab61.eb153952d0d86a828fcb7e18426c2c1a";
try { try {
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());
// 1. Response-Logging für Debugging // 1. Response-Logging für Debugging
logger.info("│ ├── Response Body=" + response.body()); logger.info("│ ├── Response Body=" + response.body());
if (response.statusCode() >= 300) { if (response.statusCode() >= 300) {
throw new RuntimeException("API request failed with status code: " + response.statusCode()); throw new RuntimeException("API request failed with status code: " + response.statusCode());
} }
} catch (IOException | InterruptedException e) { } catch (IOException | InterruptedException e) {
throw new InvalidAccessException("Unable to resolve access tokens: " + e.getMessage()); throw new InvalidAccessException("Unable to resolve access tokens: " + e.getMessage());
} }
} }
} }

View file

@ -1,97 +1,97 @@
{ {
"code": 0, "code" : 0,
"message": "success", "message" : "success",
"invoices": [ "invoices" : [
{ {
"ach_payment_initiated": false, "ach_payment_initiated" : false,
"invoice_id": "777249000000055583", "invoice_id" : "777249000000055583",
"zcrm_potential_id": "", "zcrm_potential_id" : "",
"customer_id": "777249000000055569", "customer_id" : "777249000000055569",
"zcrm_potential_name": "", "zcrm_potential_name" : "",
"customer_name": "Mr. Foo", "customer_name" : "Mr. Foo",
"company_name": "", "company_name" : "",
"status": "overdue", "status" : "overdue",
"invoice_number": "INV-000001", "invoice_number" : "INV-000001",
"reference_number": "23452345", "reference_number" : "23452345",
"date": "2025-04-21", "date" : "2025-04-21",
"due_date": "2025-04-21", "due_date" : "2025-04-21",
"due_days": "Overdue by 3 days", "due_days" : "Overdue by 3 days",
"email": "ralph.soika@imixs.com", "email" : "ralph.soika@imixs.com",
"project_name": "", "project_name" : "",
"billing_address": { "billing_address" : {
"address": "", "address" : "",
"street2": "", "street2" : "",
"city": "", "city" : "",
"state": "", "state" : "",
"zipcode": "", "zipcode" : "",
"country": "", "country" : "",
"phone": "", "phone" : "",
"fax": "", "fax" : "",
"attention": "" "attention" : ""
}, },
"shipping_address": { "shipping_address" : {
"address": "", "address" : "",
"street2": "", "street2" : "",
"city": "", "city" : "",
"state": "", "state" : "",
"zipcode": "", "zipcode" : "",
"country": "", "country" : "",
"phone": "", "phone" : "",
"fax": "", "fax" : "",
"attention": "" "attention" : ""
}, },
"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",
"is_viewed_by_client": false, "is_viewed_by_client" : false,
"has_attachment": true, "has_attachment" : true,
"client_viewed_time": "", "client_viewed_time" : "",
"is_viewed_in_mail": false, "is_viewed_in_mail" : false,
"is_emailed": true, "is_emailed" : true,
"mail_first_viewed_time": "", "mail_first_viewed_time" : "",
"mail_last_viewed_time": "", "mail_last_viewed_time" : "",
"is_correction_invoice": false, "is_correction_invoice" : false,
"color_code": "", "color_code" : "",
"current_sub_status_id": "", "current_sub_status_id" : "",
"current_sub_status": "overdue", "current_sub_status" : "overdue",
"currency_id": "777249000000000071", "currency_id" : "777249000000000071",
"schedule_time": "", "schedule_time" : "",
"currency_code": "EUR", "currency_code" : "EUR",
"currency_symbol": "€", "currency_symbol" : "€",
"template_type": "standard", "template_type" : "standard",
"invoice_url": "https://zohosecurepay.eu/books/imixs/secure?CInvoiceID=2-64277fbd3cd8bc86f8e581a3a7e1544618c191c094de1ae938827291b0828c92b5f796a1ec94bfb1fdcce7621ddf18bf61770563d9fd283207b7fa0e24d232a416b0b5e010c61cd7 ", "invoice_url" : "https://zohosecurepay.eu/books/imixs/secure?CInvoiceID=2-64277fbd3cd8bc86f8e581a3a7e1544618c191c094de1ae938827291b0828c92b5f796a1ec94bfb1fdcce7621ddf18bf61770563d9fd283207b7fa0e24d232a416b0b5e010c61cd7 ",
"transaction_type": "renewal", "transaction_type" : "renewal",
"reminders_sent": 0, "reminders_sent" : 0,
"last_reminder_sent_date": "", "last_reminder_sent_date" : "",
"last_payment_date": "", "last_payment_date" : "",
"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" : {
"page": 1, "page" : 1,
"per_page": 200, "per_page" : 200,
"has_more_page": false, "has_more_page" : false,
"report_name": "Invoices", "report_name" : "Invoices",
"applied_filter": "Status.All", "applied_filter" : "Status.All",
"custom_fields": [], "custom_fields" : [ ],
"sort_column": "created_time", "sort_column" : "created_time",
"sort_order": "D" "sort_order" : "D"
} }
} }

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>