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
```
OrganizationID=20105697367
client ID= 1000.NLTM2KUEHI696MSLGEIANATVGGX7IN
client secret= ee9ba49f16e77c64abb89cea13542d2326d8d03ce8
zoho.accesstoken 1000.18f5fbeaf572425b8062d015d541aad5.a704efa994774ef33d7af75106468a13
zoho.apidomain https://www.zohoapis.eu
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
@ -86,3 +92,16 @@ Danach den Code token kopieren:
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!
# 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
* Rechnungen
* und bietet im Workflow noch eine Excel-Export Funktion
* Rechnungen und bietet im Workflow noch eine Excel-Export Funktion
*
* @author rsoika
*
@ -33,176 +32,173 @@ import jakarta.inject.Named;
@ConversationScoped
public class AGLAnalyticControllerOPListe implements Serializable {
private static final long serialVersionUID = 1L;
private static Logger logger = Logger.getLogger(AnalyticController.class.getName());
private static final long serialVersionUID = 1L;
private static Logger logger = Logger.getLogger(AnalyticController.class.getName());
@Inject
protected DocumentService documentService;
@Inject
protected DocumentService documentService;
@Inject
protected AGLConfigController aglConfigController;
@Inject
protected AGLConfigController aglConfigController;
@Inject
protected WorkflowController workflowController;
@Inject
protected WorkflowController workflowController;
@Inject
TeamService teamService;
@Inject
TeamService teamService;
@Inject
ResourceBundleHandler resourceBundleHandler;
@Inject
ResourceBundleHandler resourceBundleHandler;
TreeMap<String, DebitorStatistikData> stats = null;
TreeMap<String, DebitorStatistikData> stats = null;
String departmentID;
String departmentIDLast = null;
// String departmentName;
ItemCollection department;
String departmentID;
String departmentIDLast = null;
// String departmentName;
ItemCollection department;
int countAll = 0;
int countOpen = 0;
int countDue = 0;
int countDunning = 0;
boolean refreshCalculation = false;
int countAll = 0;
int countOpen = 0;
int countDue = 0;
int countDunning = 0;
boolean refreshCalculation = false;
public void onEvent(@Observes AnalyticEvent event) {
public void onEvent(@Observes AnalyticEvent event) {
if (!event.getWorkitem().getModelVersion().contains("analyse-opliste-")) {
// no op
return;
}
if (!event.getWorkitem().getModelVersion().contains("analyse-opliste-")) {
// no op
return;
}
department = null;
departmentID = event.getWorkitem().getItemValueString("space.ref");
if (departmentIDLast == null || !departmentID.equals(departmentIDLast)) {
department = documentService.load(departmentID);
if (department != null) {
event.getWorkitem().setItemValue("space.name", department.getItemValueString("name"));
}
departmentIDLast = departmentID;
resetStats(event);
calculateStats();
refreshCalculation = true;
}
department = null;
departmentID = event.getWorkitem().getItemValueString("space.ref");
if (departmentIDLast == null || !departmentID.equals(departmentIDLast)) {
department = documentService.load(departmentID);
if (department != null) {
event.getWorkitem().setItemValue("space.name", department.getItemValueString("name"));
}
departmentIDLast = departmentID;
resetStats(event);
calculateStats();
refreshCalculation = true;
}
// use cache?
if (event.getWorkitem().hasItem(event.getKey())) {
// logger.info(" use cache for " + event.getKey());
// no op
return;
}
String link = "/pages/workitems/worklist.xhtml";
logger.fine("process ref=" + workflowController.getWorkitem().getItemValueString("process.ref"));
ItemCollection process = documentService
.load(workflowController.getWorkitem().getItemValueString("process.ref"));
if (process != null) {
link = link + "?processref=" + process.getUniqueID() // + "&modelversion=rechnungsausgang*"
+ "&spaceref=" + departmentID;
}
if ("analytic.invoices.count.all".equals(event.getKey())) {
event.setValue("" + countAll);
event.setLabel(resourceBundleHandler.findMessage("oplist.invoices"));
event.setDescription(resourceBundleHandler.findMessage("oplist.invoices.description.all"));
event.getWorkitem().setItemValue("invoices.total", event.getValue());
event.setLink(link);
}
// use cache?
if (event.getWorkitem().hasItem(event.getKey())) {
// logger.info(" use cache for " + event.getKey());
// no op
return;
}
String link = "/pages/workitems/worklist.xhtml";
logger.fine("process ref=" + workflowController.getWorkitem().getItemValueString("process.ref"));
ItemCollection process = documentService
.load(workflowController.getWorkitem().getItemValueString("process.ref"));
if (process != null) {
link = link + "?processref=" + process.getUniqueID() // + "&modelversion=rechnungsausgang*"
+ "&spaceref=" + departmentID;
}
if ("analytic.invoices.count.all".equals(event.getKey())) {
event.setValue("" + countAll);
event.setLabel(resourceBundleHandler.findMessage("oplist.invoices"));
event.setDescription(resourceBundleHandler.findMessage("oplist.invoices.description.all"));
event.getWorkitem().setItemValue("invoices.total", event.getValue());
event.setLink(link);
}
if ("analytic.invoices.count.open".equals(event.getKey())) {
event.setValue("" + (countOpen));
event.setLabel(resourceBundleHandler.findMessage("oplist.invoices"));
event.setDescription(resourceBundleHandler.findMessage("oplist.invoices.description.open"));
event.getWorkitem().setItemValue("invoices.total.open", event.getValue());
event.setLink(link);
}
if ("analytic.invoices.count.open".equals(event.getKey())) {
event.setValue("" + (countOpen));
event.setLabel(resourceBundleHandler.findMessage("oplist.invoices"));
event.setDescription(resourceBundleHandler.findMessage("oplist.invoices.description.open"));
event.getWorkitem().setItemValue("invoices.total.open", event.getValue());
event.setLink(link);
}
if ("analytic.invoices.count.due".equals(event.getKey())) {
event.setValue("" + countDue);
event.setLabel(resourceBundleHandler.findMessage("oplist.invoices"));
event.setDescription(resourceBundleHandler.findMessage("oplist.invoices.description.due"));
event.getWorkitem().setItemValue("invoices.total.due", event.getValue());
event.setLink(link);
}
if ("analytic.invoices.count.dunning".equals(event.getKey())) {
event.setValue("" + countDunning);
event.setLabel(resourceBundleHandler.findMessage("oplist.invoices"));
event.setDescription(resourceBundleHandler.findMessage("oplist.invoices.description.dunning"));
event.getWorkitem().setItemValue("invoices.total.dunning", event.getValue());
event.setLink(link);
}
if ("analytic.invoices.count.due".equals(event.getKey())) {
event.setValue("" + countDue);
event.setLabel(resourceBundleHandler.findMessage("oplist.invoices"));
event.setDescription(resourceBundleHandler.findMessage("oplist.invoices.description.due"));
event.getWorkitem().setItemValue("invoices.total.due", event.getValue());
event.setLink(link);
}
if ("analytic.invoices.count.dunning".equals(event.getKey())) {
event.setValue("" + countDunning);
event.setLabel(resourceBundleHandler.findMessage("oplist.invoices"));
event.setDescription(resourceBundleHandler.findMessage("oplist.invoices.description.dunning"));
event.getWorkitem().setItemValue("invoices.total.dunning", event.getValue());
event.setLink(link);
}
}
}
/**
* Reset cached analytic keys
*/
private void resetStats(AnalyticEvent event) {
event.getWorkitem().removeItem("analytic.invoices.count.all");
event.getWorkitem().removeItem("analytic.invoices.count.open");
event.getWorkitem().removeItem("analytic.invoices.count.due");
event.getWorkitem().removeItem("analytic.invoices.count.dunning");
/**
* Reset cached analytic keys
*/
private void resetStats(AnalyticEvent event) {
event.getWorkitem().removeItem("analytic.invoices.count.all");
event.getWorkitem().removeItem("analytic.invoices.count.open");
event.getWorkitem().removeItem("analytic.invoices.count.due");
event.getWorkitem().removeItem("analytic.invoices.count.dunning");
}
}
/**
* Läd die statistik daten zu einem department die Anzahl an aktuellen
* Rechnungen zu ermitteln
*
* int countAll = 0;
* int countOpen = 0;
* int countDue = 0;
* int countDunning = 0;
*
*/
private void calculateStats() {
long l = System.currentTimeMillis();
/**
* Läd die statistik daten zu einem department die Anzahl an aktuellen
* Rechnungen zu ermitteln
*
* int countAll = 0; int countOpen = 0; int countDue = 0; int countDunning = 0;
*
*/
private void calculateStats() {
long l = System.currentTimeMillis();
countAll = 0;
countDue = 0;
countDunning = 0;
logger.info(" ├──calculate stats for " + departmentID + "....");
countAll = 0;
countDue = 0;
countDunning = 0;
logger.info(" ├──calculate stats for " + departmentID + "....");
try {
try {
if (departmentID == null || !departmentID.isEmpty()) {
countAll = documentService.count("(type:workitem) AND $uniqueidref:" + departmentID
+ " AND $modelversion:rechnungsausgang-*");
if (departmentID == null || !departmentID.isEmpty()) {
countAll = documentService.count(
"(type:workitem) AND $uniqueidref:" + departmentID + " AND $modelversion:rechnungsausgang-*");
countDue = documentService.count("(type:workitem) AND $uniqueidref:" + departmentID
+ " AND $modelversion:rechnungsausgang-* AND $taskid:[5100 TO 5199]");
countDunning = documentService.count("(type:workitem) AND $uniqueidref:" + departmentID
+ " AND $modelversion:rechnungsausgang-* AND $taskid:[5200 TO 5399]");
countDue = documentService.count("(type:workitem) AND $uniqueidref:" + departmentID
+ " AND $modelversion:rechnungsausgang-* AND $taskid:[5100 TO 5199]");
countDunning = documentService.count("(type:workitem) AND $uniqueidref:" + departmentID
+ " AND $modelversion:rechnungsausgang-* AND $taskid:[5200 TO 5399]");
countOpen = countAll - countDue - countDunning;
} else {
countOpen = countAll - countDue - countDunning;
} else {
// countAll=documentService.count("(type:workitem OR type:workitemarchive) AND
// $modelversion:rechnungsausgang-*")
// countAll=documentService.count("(type:workitem OR type:workitemarchive) AND
// $modelversion:rechnungsausgang-*")
countAll = documentService.count("(type:workitem) AND $modelversion:rechnungsausgang-*");
countAll = documentService.count("(type:workitem) AND $modelversion:rechnungsausgang-*");
countDue = documentService
.count("(type:workitem) AND $modelversion:rechnungsausgang-* AND $taskid:[5100 TO 5199]");
countDue = documentService
.count("(type:workitem) AND $modelversion:rechnungsausgang-* AND $taskid:[5100 TO 5199]");
countDunning = documentService
.count("(type:workitem) AND $modelversion:rechnungsausgang-* AND $taskid:[5200 TO 5399]");
countOpen = countAll - countDue - countDunning;
}
countDunning = documentService
.count("(type:workitem) AND $modelversion:rechnungsausgang-* AND $taskid:[5200 TO 5399]");
countOpen = countAll - countDue - countDunning;
}
} catch (QueryException e) {
logger.warning("Failed to query invoices: " + e.getMessage());
} catch (QueryException e) {
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());
symbols.setGroupingSeparator('.');
symbols.setDecimalSeparator(',');
DecimalFormat formatter = new DecimalFormat("#,##0.00", symbols);
DecimalFormatSymbols symbols = new DecimalFormatSymbols(Locale.getDefault());
symbols.setGroupingSeparator('.');
symbols.setDecimalSeparator(',');
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
public class AGLAnalyticControllerSteuer implements Serializable {
private static final long serialVersionUID = 1L;
private static Logger logger = Logger.getLogger(AnalyticController.class.getName());
@Inject
protected DocumentService documentService;
@Inject
TeamService teamService;
@Inject
protected WorkflowController workflowController;
@Inject
protected DebitorStatistikController debitorStatistikController;
List<ItemCollection> steuerbescheide = null;
public void onEvent(@Observes AnalyticEvent event) {
if (!"workitem".equals(event.getWorkitem().getType())
|| !event.getWorkitem().getModelVersion().startsWith("analyse-steuer")) {
// no op
return;
}
ItemCollection process = documentService
.load(workflowController.getWorkitem().getItemValueString("process.ref"));
String link = "/pages/workitems/worklist.xhtml";
if (process != null) {
link = link + "?processref=" + process.getUniqueID()
+ "&workflowgroup=Steuerbescheid";
}
if ("analytic.steuer.eust".equals(event.getKey())) {
event.setValue(computeSteuerbescheideEUST());
event.setLabel("EUR");
event.setDescription("Gesamt Einfuhrumsatzsteuer (EUSt)");
event.getWorkitem().setItemValue("eust.total", event.getValue());
event.setLink(link);
}
if ("analytic.steuer.zoll".equals(event.getKey())) {
event.setValue(computeSteuerbescheideZoll());
event.setLabel("EUR");
event.setDescription("Gesamt Zölle (ZOLLEU)");
event.setLink(link);
}
if ("analytic.steuer.total".equals(event.getKey())) {
event.setValue(computeSteuerbescheideTotal());
event.setLabel("EUR");
event.setDescription("Alle Steuerbescheide die noch nicht abgerechnet wurden.");
event.setLink(link);
}
if ("analytic.steuer.saldo".equals(event.getKey())) {
event.setValue(computeSteuerbescheideSaldo());
event.setLabel("EUR");
event.setDescription("Saldo aller Steuerbescheide die noch nicht abgerechnet wurden.");
event.setLink(link);
}
if ("analytic.steuer.due".equals(event.getKey())) {
event.setValue(computeSteuerbescheideSaldoDue());
event.setLabel("EUR");
event.setDescription("Saldo aller überfälligen Steuerbescheide die noch nicht abgerechnet wurden.");
// event.setLink(link + "&task=Überfällig");
event.setLink(link + "&task=1200");
}
// Charts
if ("analytic.steuer.trend".equals(event.getKey())) {
event.setValue(accumulateSteuerbescheideByWeek());
event.setLabel("Zoll & EUST");
event.setDescription("Fälligkeiten nach KW");
}
if ("analytic.steuer.trend.abteilung".equals(event.getKey())) {
event.setValue(accumulateSteuerbescheideAbteilungByWeek());
event.setLabel("Nach Abteilungen");
event.setDescription("Offene Salden nach KW pro Abteilung");
}
}
/**
* Berechnet die Summe aller offenen EUST Beträge
* // tax.code=[0], amount=[3885.16], activity.type=[EUST],
* // filenumber=[LA-PAP-2408-024]}
*
* @return
*/
private double computeSteuerbescheideEUST() {
logger.fine("compute EUST...");
List<ItemCollection> list = getSteuerbescheide();
double result = 0;
for (ItemCollection steuerItemCol : list) {
result = result + getZollBySteuerBeleg(steuerItemCol, "EUST");
}
return InvoiceUtil.round(result);
}
/**
* Berechnet die Summe aller offenen Zoll Beträge
*
* @return
*/
private double computeSteuerbescheideZoll() {
logger.fine("compute Zoll...");
List<ItemCollection> list = getSteuerbescheide();
double result = 0;
for (ItemCollection steuerItemCol : list) {
result = result + getZollBySteuerBeleg(steuerItemCol, "ZOLL");
}
return InvoiceUtil.round(result);
}
/**
* Berechnet die Summe aller überfälligen steuer Beträge $taskID=1200
*
* @return
*/
private double computeSteuerbescheideSaldo() {
logger.fine("compute saldo...");
List<ItemCollection> list = getSteuerbescheide();
double result = 0;
for (ItemCollection steuerItemCol : list) {
result = result + steuerItemCol.getItemValueDouble("invoice.saldo");
}
return InvoiceUtil.round(result);
}
/**
* Berechnet die Summe aller überfälligen steuer Beträge $taskID=1200
*
* @return
*/
private double computeSteuerbescheideTotal() {
logger.fine("compute total...");
List<ItemCollection> list = getSteuerbescheide();
double result = 0;
for (ItemCollection steuerItemCol : list) {
result = result + steuerItemCol.getItemValueDouble("invoice.total.net");
}
return InvoiceUtil.round(result);
}
/**
* Berechnet die Summe aller überfälligen steuer Beträge $taskID=1200
*
* @return
*/
private double computeSteuerbescheideSaldoDue() {
logger.fine("compute EUST...");
List<ItemCollection> list = getSteuerbescheide();
double result = 0;
for (ItemCollection steuerItemCol : list) {
if (steuerItemCol.getTaskID() == 1200) {
result = result + steuerItemCol.getItemValueDouble("invoice.saldo");
}
}
return InvoiceUtil.round(result);
}
/**
* Berechnet die summ zoll/eust für einen Beleg
*
* @param steuerItemCol
* @return
*/
private double getZollBySteuerBeleg(ItemCollection steuerItemCol, String type) {
double result = 0;
List<ItemCollection> positionsTabelle = InvoiceUtil.explodeChildList(steuerItemCol);
for (ItemCollection pos : positionsTabelle) {
if (type.equalsIgnoreCase(pos.getItemValueString("activity.type"))) {
result = result + pos.getItemValueDouble("amount");
}
}
return result;
}
private List<ItemCollection> getSteuerbescheide() {
if (steuerbescheide != null) {
return steuerbescheide;
}
// compute steuerbescheide....
logger.info("Load Steuerbescheide for analytics....");
String query = "(type:workitem ) AND ($workflowgroup:\"Steuerbescheid\")";
try {
steuerbescheide = documentService.find(query, 999, 0, "$created", false);
return steuerbescheide;
} catch (QueryException e) {
logger.severe("Failed to get invoices: " + e.getMessage());
}
return null;
}
/**
* Berechnet Chart daten aller Steuerbescheide bei KW
*
* <pre>
*
chartCfg = {
type: 'bar',
data: {
datasets: [{
data: [20, 10],
}],
labels: ['1', '2']
}
}
*
* </pre>
*
* @return
*/
private String accumulateSteuerbescheideByWeek() {
logger.fine("accumulate steuer by week...");
List<ItemCollection> list = getSteuerbescheide();
Map<Integer, SteuerData> saldoByWeek = new HashMap<>();
Calendar calendar = Calendar.getInstance();
int earliestWeek = Integer.MAX_VALUE;
int latestWeek = Integer.MIN_VALUE;
// Step 1: Group saldo by week
for (ItemCollection steuerItemCol : list) {
double saldo = steuerItemCol.getItemValueDouble("invoice.saldo");
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);
int weekNumber = calendar.get(Calendar.WEEK_OF_YEAR);
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());
double eust = getZollBySteuerBeleg(steuerItemCol, "EUST");
double zoll = getZollBySteuerBeleg(steuerItemCol, "ZOLL");
steuerDataByWeek.add(eust, zoll, total, saldo, "");
saldoByWeek.put(yearWeek, steuerDataByWeek);
// Track the earliest and latest weeks
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
JsonArrayBuilder dataBuilderSaldo = Json.createArrayBuilder();
JsonArrayBuilder dataBuilderEust = Json.createArrayBuilder();
JsonArrayBuilder dataBuilderZoll = Json.createArrayBuilder();
JsonArrayBuilder labelsBuilder = Json.createArrayBuilder();
Calendar startCal = Calendar.getInstance();
startCal.set(Calendar.YEAR, earliestWeek / 100);
startCal.set(Calendar.WEEK_OF_YEAR, earliestWeek % 100);
Calendar endCal = Calendar.getInstance();
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);
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());
dataBuilderSaldo.add(steuerData.saldo);
dataBuilderEust.add(steuerData.eust);
dataBuilderZoll.add(steuerData.zoll);
labelsBuilder.add(weekNumber + "/" + year); // Label as 'week/year'
// Move to the next week
startCal.add(Calendar.WEEK_OF_YEAR, 1);
}
// Step 3: Prepare the JSON structure
JsonObjectBuilder jsonBuilder = Json.createObjectBuilder()
.add("type", "bar")
.add("data", Json.createObjectBuilder()
.add("datasets", Json.createArrayBuilder()
.add(Json.createObjectBuilder() // First dataset for 'tax'
.add("label", "Zoll")
.add("backgroundColor", "#96f")
.add("borderWidth", 1)
.add("data", dataBuilderZoll))
.add(Json.createObjectBuilder() // Second dataset for 'eust'
.add("label", "EUSt")
.add("backgroundColor", "#36a2eb")
.add("borderWidth", 1)
.add("data", dataBuilderEust))
.add(Json.createObjectBuilder() // Third dataset for 'Saldo'
.add("label", "Saldo")
.add("backgroundColor", "#ff6384")
.add("borderWidth", 1)
.add("data", dataBuilderSaldo)))
.add("labels", labelsBuilder));
// Step 4: Return the JSON structure as a string
JsonObject chartCfg = jsonBuilder.build();
return chartCfg.toString();
}
/**
* Berechnet Chart daten aller Steuerbescheide bei KW
*
* <pre>
*
chartCfg = {
type: 'bar',
data: {
datasets: [{
data: [20, 10],
}],
labels: ['1', '2']
}
}
*
* </pre>
*
* @return
*/
private String accumulateSteuerbescheideAbteilungByWeek() {
logger.fine("accumulate steuer by Abteilung week...");
String[] colors = {
"#36a2eb",
"#ff6384",
"#4bc0c0",
"#ff9f40",
"#96f",
"#ffcd56",
"#c9cbcf"
};
List<ItemCollection> list = getSteuerbescheide();
Map<String, SteuerData> totalByWeek = new HashMap<>();
Calendar calendar = Calendar.getInstance();
int earliestWeek = Integer.MAX_VALUE;
int latestWeek = Integer.MIN_VALUE;
List<String> abteilungsListe = new ArrayList<>();
// Step 1: Group total by week
for (ItemCollection steuerItemCol : list) {
String abteilung = steuerItemCol.getItemValueString("space.name");
if (abteilung.isEmpty()) {
abteilung = "Keine Zuordnung";
}
if (!abteilungsListe.contains(abteilung)) {
abteilungsListe.add(abteilung);
}
double saldo = steuerItemCol.getItemValueDouble("invoice.saldo");
double total = steuerItemCol.getItemValueDouble("invoice.total.net");
Date date = steuerItemCol.getItemValueDate("invoice.date");
logger.fine(".... " + steuerItemCol.getItemValueString("$workflowSummary") + " = " + total);
// Get the week number and year from the date
calendar.setTime(date);
int weekNumber = calendar.get(Calendar.WEEK_OF_YEAR);
int year = calendar.get(Calendar.YEAR);
int yearWeek = year * 100 + weekNumber;
String key = abteilung + "~" + yearWeek;// Unique key Import Forest~202405
// Accumulate the total per week
SteuerData steuerDataByWeek = totalByWeek.getOrDefault(key, new SteuerData());
double eust = getZollBySteuerBeleg(steuerItemCol, "EUST");
double zoll = getZollBySteuerBeleg(steuerItemCol, "ZOLL");
steuerDataByWeek.add(eust, zoll, total, saldo, abteilung);
totalByWeek.put(key, steuerDataByWeek);
// Track the earliest and latest weeks
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
JsonArrayBuilder labelsBuilder = Json.createArrayBuilder();
Calendar startCal = Calendar.getInstance();
startCal.set(Calendar.YEAR, earliestWeek / 100);
startCal.set(Calendar.WEEK_OF_YEAR, earliestWeek % 100);
Calendar endCal = Calendar.getInstance();
endCal.set(Calendar.YEAR, latestWeek / 100);
endCal.set(Calendar.WEEK_OF_YEAR, latestWeek % 100);
// Build labels - je eines pro woche
while (!startCal.after(endCal)) {
int weekNumber = startCal.get(Calendar.WEEK_OF_YEAR);
int year = startCal.get(Calendar.YEAR);
labelsBuilder.add(weekNumber + "/" + year); // Label as 'week/year'
// Move to the next week
startCal.add(Calendar.WEEK_OF_YEAR, 1);
}
// Step 3: Baue die Balken pro abrteilung
JsonArrayBuilder datasets = Json.createArrayBuilder();
JsonObjectBuilder jsonBuilder = Json.createObjectBuilder()
.add("type", "bar");
JsonObjectBuilder dataDing = Json.createObjectBuilder();
int iColor = 0;
for (String _abteilung : abteilungsListe) {
JsonArrayBuilder dataBuilderTotals = Json.createArrayBuilder();
startCal = Calendar.getInstance();
startCal.set(Calendar.YEAR, earliestWeek / 100);
startCal.set(Calendar.WEEK_OF_YEAR, earliestWeek % 100);
endCal = Calendar.getInstance();
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);
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
SteuerData steuerData = totalByWeek.getOrDefault(key, new SteuerData());
logger.fine(" " + yearWeek + "=" + steuerData.total);
dataBuilderTotals.add(steuerData.total);
// Move to the next week
startCal.add(Calendar.WEEK_OF_YEAR, 1);
}
datasets.add(Json.createObjectBuilder() // First dataset for 'tax'
.add("label", _abteilung)
.add("backgroundColor", colors[iColor])
.add("borderWidth", 1)
.add("data", dataBuilderTotals));
iColor++;
if (iColor > colors.length) {
iColor = 0;
}
}
dataDing.add("datasets", datasets);
dataDing.add("labels", labelsBuilder);
jsonBuilder.add("data", dataDing);
// Step 4: Return the JSON structure as a string
JsonObject chartCfg = jsonBuilder.build();
return chartCfg.toString();
}
/**
* Helper method to hold zoll and eust totals in an object. Needed to build
* totals per week.
*/
class SteuerData {
double total = 0.0;
double zoll = 0.0;
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;
}
}
private static final long serialVersionUID = 1L;
private static Logger logger = Logger.getLogger(AnalyticController.class.getName());
@Inject
protected DocumentService documentService;
@Inject
TeamService teamService;
@Inject
protected WorkflowController workflowController;
@Inject
protected DebitorStatistikController debitorStatistikController;
List<ItemCollection> steuerbescheide = null;
public void onEvent(@Observes AnalyticEvent event) {
if (!"workitem".equals(event.getWorkitem().getType())
|| !event.getWorkitem().getModelVersion().startsWith("analyse-steuer")) {
// no op
return;
}
ItemCollection process = documentService
.load(workflowController.getWorkitem().getItemValueString("process.ref"));
String link = "/pages/workitems/worklist.xhtml";
if (process != null) {
link = link + "?processref=" + process.getUniqueID() + "&workflowgroup=Steuerbescheid";
}
if ("analytic.steuer.eust".equals(event.getKey())) {
event.setValue(computeSteuerbescheideEUST());
event.setLabel("EUR");
event.setDescription("Gesamt Einfuhrumsatzsteuer (EUSt)");
event.getWorkitem().setItemValue("eust.total", event.getValue());
event.setLink(link);
}
if ("analytic.steuer.zoll".equals(event.getKey())) {
event.setValue(computeSteuerbescheideZoll());
event.setLabel("EUR");
event.setDescription("Gesamt Zölle (ZOLLEU)");
event.setLink(link);
}
if ("analytic.steuer.total".equals(event.getKey())) {
event.setValue(computeSteuerbescheideTotal());
event.setLabel("EUR");
event.setDescription("Alle Steuerbescheide die noch nicht abgerechnet wurden.");
event.setLink(link);
}
if ("analytic.steuer.saldo".equals(event.getKey())) {
event.setValue(computeSteuerbescheideSaldo());
event.setLabel("EUR");
event.setDescription("Saldo aller Steuerbescheide die noch nicht abgerechnet wurden.");
event.setLink(link);
}
if ("analytic.steuer.due".equals(event.getKey())) {
event.setValue(computeSteuerbescheideSaldoDue());
event.setLabel("EUR");
event.setDescription("Saldo aller überfälligen Steuerbescheide die noch nicht abgerechnet wurden.");
// event.setLink(link + "&task=Überfällig");
event.setLink(link + "&task=1200");
}
// Charts
if ("analytic.steuer.trend".equals(event.getKey())) {
event.setValue(accumulateSteuerbescheideByWeek());
event.setLabel("Zoll & EUST");
event.setDescription("Fälligkeiten nach KW");
}
if ("analytic.steuer.trend.abteilung".equals(event.getKey())) {
event.setValue(accumulateSteuerbescheideAbteilungByWeek());
event.setLabel("Nach Abteilungen");
event.setDescription("Offene Salden nach KW pro Abteilung");
}
}
/**
* Berechnet die Summe aller offenen EUST Beträge // tax.code=[0],
* amount=[3885.16], activity.type=[EUST], // filenumber=[LA-PAP-2408-024]}
*
* @return
*/
private double computeSteuerbescheideEUST() {
logger.fine("compute EUST...");
List<ItemCollection> list = getSteuerbescheide();
double result = 0;
for (ItemCollection steuerItemCol : list) {
result = result + getZollBySteuerBeleg(steuerItemCol, "EUST");
}
return InvoiceUtil.round(result);
}
/**
* Berechnet die Summe aller offenen Zoll Beträge
*
* @return
*/
private double computeSteuerbescheideZoll() {
logger.fine("compute Zoll...");
List<ItemCollection> list = getSteuerbescheide();
double result = 0;
for (ItemCollection steuerItemCol : list) {
result = result + getZollBySteuerBeleg(steuerItemCol, "ZOLL");
}
return InvoiceUtil.round(result);
}
/**
* Berechnet die Summe aller überfälligen steuer Beträge $taskID=1200
*
* @return
*/
private double computeSteuerbescheideSaldo() {
logger.fine("compute saldo...");
List<ItemCollection> list = getSteuerbescheide();
double result = 0;
for (ItemCollection steuerItemCol : list) {
result = result + steuerItemCol.getItemValueDouble("invoice.saldo");
}
return InvoiceUtil.round(result);
}
/**
* Berechnet die Summe aller überfälligen steuer Beträge $taskID=1200
*
* @return
*/
private double computeSteuerbescheideTotal() {
logger.fine("compute total...");
List<ItemCollection> list = getSteuerbescheide();
double result = 0;
for (ItemCollection steuerItemCol : list) {
result = result + steuerItemCol.getItemValueDouble("invoice.total.net");
}
return InvoiceUtil.round(result);
}
/**
* Berechnet die Summe aller überfälligen steuer Beträge $taskID=1200
*
* @return
*/
private double computeSteuerbescheideSaldoDue() {
logger.fine("compute EUST...");
List<ItemCollection> list = getSteuerbescheide();
double result = 0;
for (ItemCollection steuerItemCol : list) {
if (steuerItemCol.getTaskID() == 1200) {
result = result + steuerItemCol.getItemValueDouble("invoice.saldo");
}
}
return InvoiceUtil.round(result);
}
/**
* Berechnet die summ zoll/eust für einen Beleg
*
* @param steuerItemCol
* @return
*/
private double getZollBySteuerBeleg(ItemCollection steuerItemCol, String type) {
double result = 0;
List<ItemCollection> positionsTabelle = InvoiceUtil.explodeChildList(steuerItemCol);
for (ItemCollection pos : positionsTabelle) {
if (type.equalsIgnoreCase(pos.getItemValueString("activity.type"))) {
result = result + pos.getItemValueDouble("amount");
}
}
return result;
}
private List<ItemCollection> getSteuerbescheide() {
if (steuerbescheide != null) {
return steuerbescheide;
}
// compute steuerbescheide....
logger.info("Load Steuerbescheide for analytics....");
String query = "(type:workitem ) AND ($workflowgroup:\"Steuerbescheid\")";
try {
steuerbescheide = documentService.find(query, 999, 0, "$created", false);
return steuerbescheide;
} catch (QueryException e) {
logger.severe("Failed to get invoices: " + e.getMessage());
}
return null;
}
/**
* Berechnet Chart daten aller Steuerbescheide bei KW
*
* <pre>
*
chartCfg = {
type: 'bar',
data: {
datasets: [{
data: [20, 10],
}],
labels: ['1', '2']
}
}
*
* </pre>
*
* @return
*/
private String accumulateSteuerbescheideByWeek() {
logger.fine("accumulate steuer by week...");
List<ItemCollection> list = getSteuerbescheide();
Map<Integer, SteuerData> saldoByWeek = new HashMap<>();
Calendar calendar = Calendar.getInstance();
int earliestWeek = Integer.MAX_VALUE;
int latestWeek = Integer.MIN_VALUE;
// Step 1: Group saldo by week
for (ItemCollection steuerItemCol : list) {
double saldo = steuerItemCol.getItemValueDouble("invoice.saldo");
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);
int weekNumber = calendar.get(Calendar.WEEK_OF_YEAR);
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());
double eust = getZollBySteuerBeleg(steuerItemCol, "EUST");
double zoll = getZollBySteuerBeleg(steuerItemCol, "ZOLL");
steuerDataByWeek.add(eust, zoll, total, saldo, "");
saldoByWeek.put(yearWeek, steuerDataByWeek);
// Track the earliest and latest weeks
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
JsonArrayBuilder dataBuilderSaldo = Json.createArrayBuilder();
JsonArrayBuilder dataBuilderEust = Json.createArrayBuilder();
JsonArrayBuilder dataBuilderZoll = Json.createArrayBuilder();
JsonArrayBuilder labelsBuilder = Json.createArrayBuilder();
Calendar startCal = Calendar.getInstance();
startCal.set(Calendar.YEAR, earliestWeek / 100);
startCal.set(Calendar.WEEK_OF_YEAR, earliestWeek % 100);
Calendar endCal = Calendar.getInstance();
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);
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());
dataBuilderSaldo.add(steuerData.saldo);
dataBuilderEust.add(steuerData.eust);
dataBuilderZoll.add(steuerData.zoll);
labelsBuilder.add(weekNumber + "/" + year); // Label as 'week/year'
// Move to the next week
startCal.add(Calendar.WEEK_OF_YEAR, 1);
}
// Step 3: Prepare the JSON structure
JsonObjectBuilder jsonBuilder = Json
.createObjectBuilder().add("type", "bar").add("data", Json
.createObjectBuilder().add("datasets", Json.createArrayBuilder()
.add(Json.createObjectBuilder() // First dataset for 'tax'
.add("label", "Zoll").add("backgroundColor", "#96f").add("borderWidth", 1)
.add("data", dataBuilderZoll))
.add(Json.createObjectBuilder() // Second dataset for 'eust'
.add("label", "EUSt").add("backgroundColor", "#36a2eb").add("borderWidth", 1)
.add("data", dataBuilderEust))
.add(Json.createObjectBuilder() // Third dataset for 'Saldo'
.add("label", "Saldo").add("backgroundColor", "#ff6384").add("borderWidth", 1)
.add("data", dataBuilderSaldo)))
.add("labels", labelsBuilder));
// Step 4: Return the JSON structure as a string
JsonObject chartCfg = jsonBuilder.build();
return chartCfg.toString();
}
/**
* Berechnet Chart daten aller Steuerbescheide bei KW
*
* <pre>
*
chartCfg = {
type: 'bar',
data: {
datasets: [{
data: [20, 10],
}],
labels: ['1', '2']
}
}
*
* </pre>
*
* @return
*/
private String accumulateSteuerbescheideAbteilungByWeek() {
logger.fine("accumulate steuer by Abteilung week...");
String[] colors = { "#36a2eb", "#ff6384", "#4bc0c0", "#ff9f40", "#96f", "#ffcd56", "#c9cbcf" };
List<ItemCollection> list = getSteuerbescheide();
Map<String, SteuerData> totalByWeek = new HashMap<>();
Calendar calendar = Calendar.getInstance();
int earliestWeek = Integer.MAX_VALUE;
int latestWeek = Integer.MIN_VALUE;
List<String> abteilungsListe = new ArrayList<>();
// Step 1: Group total by week
for (ItemCollection steuerItemCol : list) {
String abteilung = steuerItemCol.getItemValueString("space.name");
if (abteilung.isEmpty()) {
abteilung = "Keine Zuordnung";
}
if (!abteilungsListe.contains(abteilung)) {
abteilungsListe.add(abteilung);
}
double saldo = steuerItemCol.getItemValueDouble("invoice.saldo");
double total = steuerItemCol.getItemValueDouble("invoice.total.net");
Date date = steuerItemCol.getItemValueDate("invoice.date");
logger.fine(".... " + steuerItemCol.getItemValueString("$workflowSummary") + " = " + total);
// Get the week number and year from the date
calendar.setTime(date);
int weekNumber = calendar.get(Calendar.WEEK_OF_YEAR);
int year = calendar.get(Calendar.YEAR);
int yearWeek = year * 100 + weekNumber;
String key = abteilung + "~" + yearWeek;// Unique key Import Forest~202405
// Accumulate the total per week
SteuerData steuerDataByWeek = totalByWeek.getOrDefault(key, new SteuerData());
double eust = getZollBySteuerBeleg(steuerItemCol, "EUST");
double zoll = getZollBySteuerBeleg(steuerItemCol, "ZOLL");
steuerDataByWeek.add(eust, zoll, total, saldo, abteilung);
totalByWeek.put(key, steuerDataByWeek);
// Track the earliest and latest weeks
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
JsonArrayBuilder labelsBuilder = Json.createArrayBuilder();
Calendar startCal = Calendar.getInstance();
startCal.set(Calendar.YEAR, earliestWeek / 100);
startCal.set(Calendar.WEEK_OF_YEAR, earliestWeek % 100);
Calendar endCal = Calendar.getInstance();
endCal.set(Calendar.YEAR, latestWeek / 100);
endCal.set(Calendar.WEEK_OF_YEAR, latestWeek % 100);
// Build labels - je eines pro woche
while (!startCal.after(endCal)) {
int weekNumber = startCal.get(Calendar.WEEK_OF_YEAR);
int year = startCal.get(Calendar.YEAR);
labelsBuilder.add(weekNumber + "/" + year); // Label as 'week/year'
// Move to the next week
startCal.add(Calendar.WEEK_OF_YEAR, 1);
}
// Step 3: Baue die Balken pro abrteilung
JsonArrayBuilder datasets = Json.createArrayBuilder();
JsonObjectBuilder jsonBuilder = Json.createObjectBuilder().add("type", "bar");
JsonObjectBuilder dataDing = Json.createObjectBuilder();
int iColor = 0;
for (String _abteilung : abteilungsListe) {
JsonArrayBuilder dataBuilderTotals = Json.createArrayBuilder();
startCal = Calendar.getInstance();
startCal.set(Calendar.YEAR, earliestWeek / 100);
startCal.set(Calendar.WEEK_OF_YEAR, earliestWeek % 100);
endCal = Calendar.getInstance();
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);
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
SteuerData steuerData = totalByWeek.getOrDefault(key, new SteuerData());
logger.fine(" " + yearWeek + "=" + steuerData.total);
dataBuilderTotals.add(steuerData.total);
// Move to the next week
startCal.add(Calendar.WEEK_OF_YEAR, 1);
}
datasets.add(Json.createObjectBuilder() // First dataset for 'tax'
.add("label", _abteilung).add("backgroundColor", colors[iColor]).add("borderWidth", 1)
.add("data", dataBuilderTotals));
iColor++;
if (iColor > colors.length) {
iColor = 0;
}
}
dataDing.add("datasets", datasets);
dataDing.add("labels", labelsBuilder);
jsonBuilder.add("data", dataDing);
// Step 4: Return the JSON structure as a string
JsonObject chartCfg = jsonBuilder.build();
return chartCfg.toString();
}
/**
* Helper method to hold zoll and eust totals in an object. Needed to build
* totals per week.
*/
class SteuerData {
double total = 0.0;
double zoll = 0.0;
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>
* Der Adapter kopiert die Steuerbescheiddaten in neue Zeilen, welche ab
* Zeilennummer
* 16 eingefügt werden.
* Zeilennummer 16 eingefügt werden.
* <p>
*
* Abschliessend wird die Zelle 'TOTAL' noch aktualisiert.
@ -61,245 +60,232 @@ import jakarta.inject.Inject;
*/
public class AGLAnalyticExcelAdapter extends POIFindReplaceAdapter {
private static Logger logger = Logger.getLogger(AGLAnalyticExcelAdapter.class.getName());
private static Logger logger = Logger.getLogger(AGLAnalyticExcelAdapter.class.getName());
@Inject
WorkflowService workflowService;
@Inject
WorkflowService workflowService;
@Inject
InvoiceService opListExportService;
@Inject
InvoiceService opListExportService;
@Inject
DocumentService documentService;
@Inject
DocumentService documentService;
@Inject
ConfigService configService;
@Inject
ConfigService configService;
/**
* This method finds or create the Zahlungsavis and adds a reference
* ($workitemref) to the current invoice.
*
* @throws PluginException
*/
@SuppressWarnings("unchecked")
@Override
public ItemCollection execute(ItemCollection document, ItemCollection event)
throws AdapterException, PluginException {
/**
* This method finds or create the Zahlungsavis and adds a reference
* ($workitemref) to the current invoice.
*
* @throws PluginException
*/
@SuppressWarnings("unchecked")
@Override
public ItemCollection execute(ItemCollection document, ItemCollection event)
throws AdapterException, PluginException {
// ermittle die Hauptwährungen
ItemCollection configItemCollection = configService.loadConfiguration("AGL_CONFIGURATION");
List<String> currencyList = configItemCollection.getItemValue("currency.out");
if (currencyList == null || currencyList.size() < 2) {
throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(),
InvoiceService.ERROR_CONFIG,
"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;
// ermittle die Hauptwährungen
ItemCollection configItemCollection = configService.loadConfiguration("AGL_CONFIGURATION");
List<String> currencyList = configItemCollection.getItemValue("currency.out");
if (currencyList == null || currencyList.size() < 2) {
throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), InvoiceService.ERROR_CONFIG,
"missing Currency configuration - please check parameters");
}
/**
* 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 {
// 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));
// load dummy rechnungen
List<ItemCollection> invoices = loadSteuerbescheide();
String textblock = excelDefCollection.getItemValueString("textblock");
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
XSSFWorkbook doc = null;
try (InputStream imputStream = new ByteArrayInputStream(fileData.getContent())) {
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);
try {
appendExcelTemplate(document, textblock, template, targetName);
insertInvoiceRows(document, targetName);
// 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"));
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");
// Ä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"));
logger.info("... update template with normal poi information..");
this.updateFileData(document.getFileData(targetName), document, replaceDevList, eval);
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"));
} 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;
}
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
// categoryRow = sheet.createRow(rowPos);
rowPos++;
// load dummy rechnungen
List<ItemCollection> invoices = loadSteuerbescheide();
// write back the file
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
doc.write(byteArrayOutputStream);
FileData fileData = document.getFileData(fileName);
byte[] newContent = byteArrayOutputStream.toByteArray();
FileData fileDataNew = new FileData(fileData.getName(), newContent, fileData.getContentType(),
null);
// update the fileData
document.addFileData(fileDataNew);
// load XSSFWorkbook
XSSFWorkbook doc = null;
try (InputStream imputStream = new ByteArrayInputStream(fileData.getContent())) {
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);
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) {
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) {
logger.severe("Failed to close workbook: " + e.getMessage());
e.printStackTrace();
}
}
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!");
}
/**
* 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
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);
// 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);
/**
* Diese Methode läd alle offenen Steuerbescheide
*
* @return
*/
private List<ItemCollection> loadSteuerbescheide() {
List<ItemCollection> result = new ArrayList<>();
try {
result = documentService.find(
"$modelversion:steuerbescheid-* AND type:workitem", 9999, 0,
"invoice.number", false);
} catch (QueryException e) {
e.printStackTrace();
}
return result;
}
/**
* Diese Methode läd alle offenen Steuerbescheide
*
* @return
*/
private List<ItemCollection> loadSteuerbescheide() {
List<ItemCollection> result = new ArrayList<>();
try {
result = documentService.find("$modelversion:steuerbescheid-* AND type:workitem", 9999, 0, "invoice.number",
false);
} catch (QueryException e) {
e.printStackTrace();
}
return result;
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -89,8 +89,7 @@ public class InvoiceService {
ItemCollection configItemCollection = configService.loadConfiguration("AGL_CONFIGURATION");
List<String> currencyList = configItemCollection.getItemValue("currency.out");
if (currencyList == null || currencyList.size() < 1) {
throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(),
InvoiceService.ERROR_CONFIG,
throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), InvoiceService.ERROR_CONFIG,
"missing Currency configuration - please check parameters");
}
return currencyList;
@ -108,8 +107,7 @@ public class InvoiceService {
ItemCollection configItemCollection = configService.loadConfiguration("AGL_CONFIGURATION");
List<String> currencyList = configItemCollection.getItemValue("currency.out");
if (currencyList == null || currencyList.size() < 1) {
throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(),
InvoiceService.ERROR_CONFIG,
throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), InvoiceService.ERROR_CONFIG,
"missing Currency configuration - please check parameters");
}
return currencyList.get(0);
@ -127,16 +125,14 @@ public class InvoiceService {
ItemCollection configItemCollection = configService.loadConfiguration("AGL_CONFIGURATION");
List<String> currencyList = configItemCollection.getItemValue("currency.in");
if (currencyList == null || currencyList.size() < 1) {
throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(),
InvoiceService.ERROR_CONFIG,
throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), InvoiceService.ERROR_CONFIG,
"missing Currency configuration - please check parameters");
}
return currencyList;
}
/**
* Prüft ob der Vorgang überflällig ist.
* Default ist 21 tage.
* Prüft ob der Vorgang überflällig ist. Default ist 21 tage.
*/
public boolean isOverdue(Date date, int taskid) {
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
* Ausgangsrechnugnen
*
* - Rundungs Methode
* - Berechnung der Positionsnummern
* - Explode Childitems
* - Rundungs Methode - Berechnung der Positionsnummern - Explode Childitems
*
* See:
* 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_-]+)*@"
+ "[^-][A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(\\.[A-Za-z]{2,})$";
return Pattern.compile(regexPattern)
.matcher(emailAddress)
.matches();
return Pattern.compile(regexPattern).matcher(emailAddress).matches();
}
// @Deprecated
@ -130,10 +126,9 @@ public class InvoiceUtil {
/**
* Diese Methode berechnet das feld 'invoice.positions' welches eine Valueliste
* mit allen Positionsnummern aus dem Childliste enthält.
* Es wird sowohl für Ausgangs wie Eingangsrechnungen genutzt.
* Das feld wird indiziert, so dass eine gezielte Suche nach positiosnummern
* möglich ist.
* mit allen Positionsnummern aus dem Childliste enthält. Es wird sowohl für
* Ausgangs wie Eingangsrechnungen genutzt. Das feld wird indiziert, so dass
* eine gezielte Suche nach positiosnummern möglich ist.
*
* @param workitem
*/

View file

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

View file

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

View file

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

View file

@ -34,8 +34,7 @@ import jakarta.inject.Named;
/**
* The OPListExportController is used to configure the OpListeExporterService.
* This service is
* used to export workitems.
* This service is used to export workitems.
* <p>
* The Controller creates a configuration entity "type=configuration;
* txtname=OPLIST_EXPORT_CONFIGURATION".
@ -48,30 +47,30 @@ import jakarta.inject.Named;
@RequestScoped
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 Logger logger = Logger.getLogger(OPListExportController.class.getName());
private static final long serialVersionUID = 1L;
private static Logger logger = Logger.getLogger(OPListExportController.class.getName());
@Inject
OPListExportScheduler opListExportScheduler;
@Inject
OPListExportScheduler opListExportScheduler;
@Override
public String getName() {
return OPLIST_EXPORT_CONFIGURATION;
}
@Override
public String getName() {
return OPLIST_EXPORT_CONFIGURATION;
}
/**
* Returns the sepa scheduler class name. This name depends on the _export_type.
*
* There are two export interfaces available - csv and XML
*
*/
@Override
public String getSchedulerClass() {
String schedulerClass = OPListExportScheduler.class.getName();
logger.finest("...... scheduler: " + schedulerClass);
return schedulerClass;
}
/**
* Returns the sepa scheduler class name. This name depends on the _export_type.
*
* There are two export interfaces available - csv and XML
*
*/
@Override
public String getSchedulerClass() {
String schedulerClass = OPListExportScheduler.class.getName();
logger.finest("...... scheduler: " + schedulerClass);
return schedulerClass;
}
}

View file

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

View file

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

View file

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

View file

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

View file

@ -17,11 +17,8 @@ import jakarta.inject.Inject;
* Das SteuerbescheidPlugin prüft ob
*
* - die ATC Nummer aus dem Steuerbescheid in einer Ausgangsrechnung gefunden
* wird ($taskID<5990)
* - invoice.atc.number
* - der Betrag "invoice.total.net"
* - die Währung???
* - die Positionsnummer ???
* wird ($taskID<5990) - invoice.atc.number - der Betrag "invoice.total.net" -
* die Währung??? - die Positionsnummer ???
*
*
* @author rsoika
@ -30,150 +27,149 @@ import jakarta.inject.Inject;
*/
public class SteuerbescheidPlugin extends AbstractPlugin {
private static Logger logger = Logger.getLogger(SteuerbescheidPlugin.class.getName());
private static Logger logger = Logger.getLogger(SteuerbescheidPlugin.class.getName());
@Inject
protected DocumentService documentService;
@Inject
protected DocumentService documentService;
/**
* Prüft ob der Steuerbetrag abgerechnet ist.
*
*
**/
@Override
public ItemCollection run(ItemCollection workitem, ItemCollection event) throws PluginException {
/**
* Prüft ob der Steuerbetrag abgerechnet ist.
*
*
**/
@Override
public ItemCollection run(ItemCollection workitem, ItemCollection event) throws PluginException {
// Update Fälligkeit
updateFaelligkeit(workitem);
// Update Fälligkeit
updateFaelligkeit(workitem);
// Update Salden
updateTotalEustZoll(workitem);
updateInvoiceSaldo(workitem);
// Update Salden
updateTotalEustZoll(workitem);
updateInvoiceSaldo(workitem);
return workitem;
}
return workitem;
}
/**
* Aktualisiert die EUST/ZOLL Summen
*
*/
protected void updateTotalEustZoll(ItemCollection workitem) {
/**
* Aktualisiert die EUST/ZOLL Summen
*
*/
protected void updateTotalEustZoll(ItemCollection workitem) {
double saldoEust = 0;
double saldoZoll = 0;
List<ItemCollection> positionsTabelle = InvoiceUtil.explodeChildList(workitem);
for (ItemCollection pos : positionsTabelle) {
if ("EUST".equalsIgnoreCase(pos.getItemValueString("activity.type"))) {
saldoEust = saldoEust + pos.getItemValueDouble("amount");
}
if ("ZOLL".equalsIgnoreCase(pos.getItemValueString("activity.type"))) {
saldoZoll = saldoZoll + pos.getItemValueDouble("amount");
}
}
double saldoEust = 0;
double saldoZoll = 0;
List<ItemCollection> positionsTabelle = InvoiceUtil.explodeChildList(workitem);
for (ItemCollection pos : positionsTabelle) {
if ("EUST".equalsIgnoreCase(pos.getItemValueString("activity.type"))) {
saldoEust = saldoEust + pos.getItemValueDouble("amount");
}
if ("ZOLL".equalsIgnoreCase(pos.getItemValueString("activity.type"))) {
saldoZoll = saldoZoll + pos.getItemValueDouble("amount");
}
}
workitem.setItemValue("steuer.eust", saldoEust);
workitem.setItemValue("steuer.zoll", saldoZoll);
workitem.setItemValue("steuer.eust", saldoEust);
workitem.setItemValue("steuer.zoll", saldoZoll);
}
}
/**
* Aktualisiert die EUST/ZOLL Fälligkeiten in den Detailtabellen
* Die Daten kommen von der KI und werden auf die Positionsnummern gemappt.
*
*/
protected void updateFaelligkeit(ItemCollection workitem) {
/**
* Aktualisiert die EUST/ZOLL Fälligkeiten in den Detailtabellen Die Daten
* kommen von der KI und werden auf die Positionsnummern gemappt.
*
*/
protected void updateFaelligkeit(ItemCollection workitem) {
boolean foundZoll = false;
boolean foundEust = false;
Date datEust = null;
Date datZoll = null;
// resolve datEust und datZoll from steuerbescheid.positionen ChildItems....
List<ItemCollection> steuerPositionen = InvoiceUtil.explodeChildList(workitem, "steuerbescheid.position");
for (ItemCollection steuerPos : steuerPositionen) {
if (steuerPos.getItemValueString("key").startsWith("A")) {
datZoll = steuerPos.getItemValueDate("duedate");
}
if (steuerPos.getItemValueString("key").startsWith("B")) {
datEust = steuerPos.getItemValueDate("duedate");
}
}
boolean foundZoll = false;
boolean foundEust = false;
Date datEust = null;
Date datZoll = null;
// resolve datEust und datZoll from steuerbescheid.positionen ChildItems....
List<ItemCollection> steuerPositionen = InvoiceUtil.explodeChildList(workitem, "steuerbescheid.position");
for (ItemCollection steuerPos : steuerPositionen) {
if (steuerPos.getItemValueString("key").startsWith("A")) {
datZoll = steuerPos.getItemValueDate("duedate");
}
if (steuerPos.getItemValueString("key").startsWith("B")) {
datEust = steuerPos.getItemValueDate("duedate");
}
}
// Map fälligkeit auf die Cargosoft Positionen
List<ItemCollection> rows = InvoiceUtil.explodeChildList(workitem);
for (ItemCollection pos : rows) {
if (datZoll != null && "ZOLL".equalsIgnoreCase(pos.getItemValueString("activity.type"))) {
pos.setItemValue("duedate", datZoll);
foundZoll = true;
}
if (datEust != null && "EUST".equalsIgnoreCase(pos.getItemValueString("activity.type"))) {
pos.setItemValue("duedate", datEust);
foundEust = true;
}
}
// Map fälligkeit auf die Cargosoft Positionen
List<ItemCollection> rows = InvoiceUtil.explodeChildList(workitem);
for (ItemCollection pos : rows) {
if (datZoll != null && "ZOLL".equalsIgnoreCase(pos.getItemValueString("activity.type"))) {
pos.setItemValue("duedate", datZoll);
foundZoll = true;
}
if (datEust != null && "EUST".equalsIgnoreCase(pos.getItemValueString("activity.type"))) {
pos.setItemValue("duedate", datEust);
foundEust = true;
}
}
if (foundEust) {
workitem.setItemValue("steuer.eust.due", datEust);
} else {
workitem.removeItem("steuer.eust.due");
}
if (foundZoll) {
workitem.setItemValue("steuer.zoll.due", datZoll);
} else {
workitem.removeItem("steuer.zoll.due");
}
InvoiceUtil.implodeChildList(workitem, rows);
}
if (foundEust) {
workitem.setItemValue("steuer.eust.due", datEust);
} else {
workitem.removeItem("steuer.eust.due");
}
if (foundZoll) {
workitem.setItemValue("steuer.zoll.due", datZoll);
} else {
workitem.removeItem("steuer.zoll.due");
}
InvoiceUtil.implodeChildList(workitem, rows);
}
/**
* Aktualisiert den invoice.saldo anhand der ausgehenden Rechnungen zu einer ATC
* Nummer
*
*/
protected void updateInvoiceSaldo(ItemCollection workitem) {
/**
* Aktualisiert den invoice.saldo anhand der ausgehenden Rechnungen zu einer ATC
* Nummer
*
*/
protected void updateInvoiceSaldo(ItemCollection workitem) {
String atcNummer = workitem.getItemValueString("invoice.atc.number");
logger.fine("Verify ATC-Nr.: " + atcNummer);
double amount = workitem.getItemValueDouble("invoice.total.net");
List<ItemCollection> invoices = findInvoices(atcNummer);
String atcNummer = workitem.getItemValueString("invoice.atc.number");
logger.fine("Verify ATC-Nr.: " + atcNummer);
double amount = workitem.getItemValueDouble("invoice.total.net");
List<ItemCollection> invoices = findInvoices(atcNummer);
// Beträge subtrahieren....
double saldo = amount;
logger.info("Found " + invoices.size() + " matching invoices...");
for (ItemCollection invoice : invoices) {
workitem.setItemValueUnique("$workitemRef", invoice.getUniqueID());
// Beträge subtrahieren....
double saldo = amount;
logger.info("Found " + invoices.size() + " matching invoices...");
for (ItemCollection invoice : invoices) {
workitem.setItemValueUnique("$workitemRef", invoice.getUniqueID());
// suche die Zeile in der die ATC Nummer vorkommt.
List<ItemCollection> positionen = InvoiceUtil.explodeChildList(invoice);
for (ItemCollection pos : positionen) {
if (atcNummer.equals(pos.getItemValueString("atc.number"))) {
double umsatz = pos.getItemValueDouble("datev.umsatz");
logger.info("...Umsatz=" + umsatz);
saldo = InvoiceUtil.round(saldo - umsatz);
}
}
}
// suche die Zeile in der die ATC Nummer vorkommt.
List<ItemCollection> positionen = InvoiceUtil.explodeChildList(invoice);
for (ItemCollection pos : positionen) {
if (atcNummer.equals(pos.getItemValueString("atc.number"))) {
double umsatz = pos.getItemValueDouble("datev.umsatz");
logger.info("...Umsatz=" + umsatz);
saldo = InvoiceUtil.round(saldo - umsatz);
}
}
}
// Update Saldo
workitem.setItemValue("invoice.saldo", InvoiceUtil.round(saldo));
// Update Saldo
workitem.setItemValue("invoice.saldo", InvoiceUtil.round(saldo));
}
}
/*
* Sucht alle Ausgansrechnungen zu einer ATC Nummer
*/
protected List<ItemCollection> findInvoices(String atcNumber) {
List<ItemCollection> result = new ArrayList<ItemCollection>();
String query = "(type:workitem OR type:workitemarchive) AND ($taskid:[5000 TO 5990])"
+ " AND invoice.atc.number:\"" + atcNumber + "\" "
+ " AND $workflowgroup:\"Rechnungsausgang\" ";
try {
result = documentService.find(query, 999, 0, "$created", false);
return result;
} catch (QueryException e) {
logger.severe("Failed to get invoices: " + e.getMessage());
}
/*
* Sucht alle Ausgansrechnungen zu einer ATC Nummer
*/
protected List<ItemCollection> findInvoices(String atcNumber) {
List<ItemCollection> result = new ArrayList<ItemCollection>();
String query = "(type:workitem OR type:workitemarchive) AND ($taskid:[5000 TO 5990])"
+ " AND invoice.atc.number:\"" + atcNumber + "\" " + " AND $workflowgroup:\"Rechnungsausgang\" ";
try {
result = documentService.find(query, 999, 0, "$created", false);
return result;
} catch (QueryException e) {
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();
if ((!invoiceCurrency.equals(paymentCurrency))
&& (!invoiceCurrency.equals(mainCurrency) &&
!paymentCurrency.equals(mainCurrency))) {
&& (!invoiceCurrency.equals(mainCurrency) && !paymentCurrency.equals(mainCurrency))) {
String message = resourceBundleHandler.findMessage("ERROR_PAYMENT4");
message = message.replace("{1}", mainCurrency);
throw new PluginException(PluginException.class.getName(),
ZahlungseingangService.ERROR_PAYMENT_BOOKING_FAILED,
message);
ZahlungseingangService.ERROR_PAYMENT_BOOKING_FAILED, message);
}
if (!invoiceCurrency.equals(paymentCurrency)) {
@ -142,8 +140,7 @@ public class ZahlungseingangSaldoAdapter implements SignalAdapter {
message = message.replace("{1}", paymentCurrency);
message = message.replace("{2}", invoiceNumber);
throw new PluginException(PluginException.class.getName(),
ZahlungseingangService.ERROR_PAYMENT_BOOKING_FAILED,
message);
ZahlungseingangService.ERROR_PAYMENT_BOOKING_FAILED, message);
}
}
@ -151,8 +148,7 @@ public class ZahlungseingangSaldoAdapter implements SignalAdapter {
* Falls die Währung der Rechnung von der Währung der Zahlung abweicht, dann
* rechnen wir den PaymentAmmout mit der Rate aus.
*/
if ("EUR".equals(paymentCurrency) &&
!invoiceCurrency.equals(paymentCurrency) && invoiceRate != 0) {
if ("EUR".equals(paymentCurrency) && !invoiceCurrency.equals(paymentCurrency) && invoiceRate != 0) {
invoicePaymentAmount = invoicePaymentAmount * invoiceRate;
// runde auf 2 Stellen
invoicePaymentAmount = (float) InvoiceUtil.round(invoicePaymentAmount);

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -15,8 +15,8 @@ import jakarta.inject.Inject;
import jakarta.inject.Named;
/**
* Der InkassoController dient dazu eine bereits zugewiesene Rechnung
* wieder aus dem Inkasso zu entfernen.
* Der InkassoController dient dazu eine bereits zugewiesene Rechnung wieder aus
* dem Inkasso zu entfernen.
* <p>
* Zusätzlich bietet er die Summenberechnung an, bei der die Gutschriften
* 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
* HTML Table wen sending the mail.
* <p>
* The Adapter also splits the item 'dbtr.mail' into a multi value field if
* the value contains email addresses separated with new lines.
* The Adapter also splits the item 'dbtr.mail' into a multi value field if the
* value contains email addresses separated with new lines.
*
*
* @version 1.0
@ -125,24 +125,24 @@ public class MahnlaufExecuteAdapter implements SignalAdapter {
}
switch (invoice.getTaskID()) {
case 5200:
case 5201:
if (dunningLevel < 1)
dunningLevel = 1;
break;
case 5210:
case 5211:
if (dunningLevel < 2)
dunningLevel = 2;
break;
case 5220:
if (dunningLevel < 3)
dunningLevel = 3;
break;
default:
if (dunningLevel < 1)
dunningLevel = 1;
break;
case 5200:
case 5201:
if (dunningLevel < 1)
dunningLevel = 1;
break;
case 5210:
case 5211:
if (dunningLevel < 2)
dunningLevel = 2;
break;
case 5220:
if (dunningLevel < 3)
dunningLevel = 3;
break;
default:
if (dunningLevel < 1)
dunningLevel = 1;
break;
}
// create a workitem stub for the mail message
@ -179,18 +179,18 @@ public class MahnlaufExecuteAdapter implements SignalAdapter {
// compute subject
switch (dunningLevel) {
case 2:
mahnLaufWorkitem.setItemValue("mail.subject_de", "2. MAHNUNG");
mahnLaufWorkitem.setItemValue("mail.subject_en", "2. REMINDER");
break;
case 3:
mahnLaufWorkitem.setItemValue("mail.subject_de", "LETZTE AUSSERGERICHTLICHE MAHNUNG");
mahnLaufWorkitem.setItemValue("mail.subject_en", "LAST REMINDER");
break;
default:
mahnLaufWorkitem.setItemValue("mail.subject_de", "1. MAHNUNG");
mahnLaufWorkitem.setItemValue("mail.subject_en", "1. REMINDER");
break;
case 2:
mahnLaufWorkitem.setItemValue("mail.subject_de", "2. MAHNUNG");
mahnLaufWorkitem.setItemValue("mail.subject_en", "2. REMINDER");
break;
case 3:
mahnLaufWorkitem.setItemValue("mail.subject_de", "LETZTE AUSSERGERICHTLICHE MAHNUNG");
mahnLaufWorkitem.setItemValue("mail.subject_en", "LAST REMINDER");
break;
default:
mahnLaufWorkitem.setItemValue("mail.subject_de", "1. MAHNUNG");
mahnLaufWorkitem.setItemValue("mail.subject_en", "1. REMINDER");
break;
}
// Verify the dbtr.mail item (split into multi value)
@ -198,8 +198,7 @@ public class MahnlaufExecuteAdapter implements SignalAdapter {
for (String mail : mailListe) {
logger.fine(" test mail address:'" + mail + "'");
if (!InvoiceUtil.validateEmail(mail)) {
throw new PluginException(MahnlaufExecuteAdapter.class.getName(),
"INVALID_EMAIL",
throw new PluginException(MahnlaufExecuteAdapter.class.getName(), "INVALID_EMAIL",
"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 {
private static Logger logger = Logger.getLogger(MahnlaufRefAddAdapter.class.getName());
private static Logger logger = Logger.getLogger(MahnlaufRefAddAdapter.class.getName());
@EJB
ModelService modelService;
@EJB
ModelService modelService;
@EJB
WorkflowService workflowService = null;
@EJB
WorkflowService workflowService = null;
@Inject
SepaWorkflowService sepaWorkflowService;
@Inject
SepaWorkflowService sepaWorkflowService;
@Inject
BusinessPartnerService businessPartnerService;
@Inject
BusinessPartnerService businessPartnerService;
/**
* This method finds or create the Mahnlauf and adds a reference ($workitemref)
* to the current invoice.
*
* @throws PluginException
*/
@SuppressWarnings("unchecked")
@Override
public ItemCollection execute(ItemCollection invoice, ItemCollection event)
throws AdapterException, PluginException {
/**
* This method finds or create the Mahnlauf and adds a reference ($workitemref)
* to the current invoice.
*
* @throws PluginException
*/
@SuppressWarnings("unchecked")
@Override
public ItemCollection execute(ItemCollection invoice, ItemCollection event)
throws AdapterException, PluginException {
if (invoice.getTaskID() < MahnlaufService.TASK_MAHNLAUF_START
|| invoice.getTaskID() > MahnlaufService.TASK_MAHNLAUF_END) {
throw new PluginException(MahnlaufRefAddAdapter.class.getName(),
MahnlaufService.ERROR_CONFIG,
"Der Mahnlauf kann nur für Rechnungen erstellt werden die bereits die Fälligkeit erreicht haben.");
}
if (invoice.getTaskID() < MahnlaufService.TASK_MAHNLAUF_START
|| invoice.getTaskID() > MahnlaufService.TASK_MAHNLAUF_END) {
throw new PluginException(MahnlaufRefAddAdapter.class.getName(), MahnlaufService.ERROR_CONFIG,
"Der Mahnlauf kann nur für Rechnungen erstellt werden die bereits die Fälligkeit erreicht haben.");
}
String dbtrNumber = invoice.getItemValueString(MahnlaufService.ITEM_DBTR_NUMBER);
ItemCollection mahnLaufWorkitem;
try {
mahnLaufWorkitem = findMahnlauf(dbtrNumber);
String dbtrNumber = invoice.getItemValueString(MahnlaufService.ITEM_DBTR_NUMBER);
ItemCollection mahnLaufWorkitem;
try {
mahnLaufWorkitem = findMahnlauf(dbtrNumber);
if (mahnLaufWorkitem == null) {
// create one...
mahnLaufWorkitem = createNewMahnlauf(dbtrNumber, invoice, event);
}
if (mahnLaufWorkitem == null) {
// create one...
mahnLaufWorkitem = createNewMahnlauf(dbtrNumber, invoice, event);
}
// add Invoice to Mahnlauf
List<String> refList = mahnLaufWorkitem.getItemValue("$workitemref");
if (!refList.contains(invoice.getUniqueID())) {
mahnLaufWorkitem.appendItemValueUnique("$workitemref", invoice.getUniqueID());
}
// add Invoice to Mahnlauf
List<String> refList = mahnLaufWorkitem.getItemValue("$workitemref");
if (!refList.contains(invoice.getUniqueID())) {
mahnLaufWorkitem.appendItemValueUnique("$workitemref", invoice.getUniqueID());
}
// set event 100 and process
ItemCollection mahnlaufConfig = workflowService.evalWorkflowResult(event, "mahnlauf", invoice, true);
int eventID = mahnlaufConfig.getItemValueInteger("event");
// set event 100 and process
ItemCollection mahnlaufConfig = workflowService.evalWorkflowResult(event, "mahnlauf", invoice, true);
int eventID = mahnlaufConfig.getItemValueInteger("event");
mahnLaufWorkitem.event(eventID);
workflowService.processWorkItem(mahnLaufWorkitem);
mahnLaufWorkitem.event(eventID);
workflowService.processWorkItem(mahnLaufWorkitem);
} catch (QueryException | ModelException e) {
throw new PluginException(MahnlaufRefAddAdapter.class.getName(),
MahnlaufService.ERROR_CONFIG, "Unable to create Mahnlauf", e);
}
} catch (QueryException | ModelException e) {
throw new PluginException(MahnlaufRefAddAdapter.class.getName(), MahnlaufService.ERROR_CONFIG,
"Unable to create Mahnlauf", e);
}
// set mahnlauf ref
invoice.setItemValue("dunning.ref", mahnLaufWorkitem.getUniqueID());
return invoice;
}
// set mahnlauf ref
invoice.setItemValue("dunning.ref", mahnLaufWorkitem.getUniqueID());
return invoice;
}
/**
* Helper method finds an open mahnlauf for a debtr.numbre (name)
*
* @param key
* @return
* @throws QueryException
*/
public ItemCollection findMahnlauf(String key) throws QueryException {
String query = "(type:workitem) AND ($modelversion:mahnlauf*) AND (name:\"" + key + "\")";
List<ItemCollection> resultList = workflowService.getDocumentService().find(query, 1, 0, "$modified", true);
/**
* Helper method finds an open mahnlauf for a debtr.numbre (name)
*
* @param key
* @return
* @throws QueryException
*/
public ItemCollection findMahnlauf(String key) throws QueryException {
String query = "(type:workitem) AND ($modelversion:mahnlauf*) AND (name:\"" + key + "\")";
List<ItemCollection> resultList = workflowService.getDocumentService().find(query, 1, 0, "$modified", true);
if (resultList.size() > 0) {
return resultList.get(0);
}
// no mahnaluffound
return null;
}
if (resultList.size() > 0) {
return resultList.get(0);
}
// no mahnaluffound
return null;
}
/**
* Helper method to create a new Mahnlauf workitem
*
* @param key
* @param invoice
* @return
* @throws ModelException
* @throws PluginException
*/
@SuppressWarnings("unused")
public ItemCollection createNewMahnlauf(String key, ItemCollection invoice, ItemCollection event)
throws ModelException, PluginException {
String modelVersion = null;
int taskID = -1;
int eventID = -1;
// test if the event provides a sepa export configuration
ItemCollection mahnlaufConfig = workflowService.evalWorkflowResult(event, "mahnlauf", invoice, true);
if (mahnlaufConfig != null && mahnlaufConfig.hasItem("modelversion") && mahnlaufConfig.hasItem("task")) {
logger.fine("read model information from event");
modelVersion = mahnlaufConfig.getItemValueString("modelVersion");
taskID = mahnlaufConfig.getItemValueInteger("task");
eventID = mahnlaufConfig.getItemValueInteger("event");
}
/**
* Helper method to create a new Mahnlauf workitem
*
* @param key
* @param invoice
* @return
* @throws ModelException
* @throws PluginException
*/
@SuppressWarnings("unused")
public ItemCollection createNewMahnlauf(String key, ItemCollection invoice, ItemCollection event)
throws ModelException, PluginException {
String modelVersion = null;
int taskID = -1;
int eventID = -1;
// test if the event provides a sepa export configuration
ItemCollection mahnlaufConfig = workflowService.evalWorkflowResult(event, "mahnlauf", invoice, true);
if (mahnlaufConfig != null && mahnlaufConfig.hasItem("modelversion") && mahnlaufConfig.hasItem("task")) {
logger.fine("read model information from event");
modelVersion = mahnlaufConfig.getItemValueString("modelVersion");
taskID = mahnlaufConfig.getItemValueInteger("task");
eventID = mahnlaufConfig.getItemValueInteger("event");
}
// build the sepa export workitem....
ItemCollection mahnlaufWorkitem = new ItemCollection().model(modelVersion).task(taskID);
mahnlaufWorkitem.replaceItemValue("name", key);
mahnlaufWorkitem.replaceItemValue(WorkflowKernel.CREATED, new Date());
mahnlaufWorkitem.replaceItemValue(WorkflowKernel.MODIFIED, new Date());
// set unqiueid
mahnlaufWorkitem.setItemValue(WorkflowKernel.UNIQUEID, WorkflowKernel.generateUniqueID());
// build the sepa export workitem....
ItemCollection mahnlaufWorkitem = new ItemCollection().model(modelVersion).task(taskID);
mahnlaufWorkitem.replaceItemValue("name", key);
mahnlaufWorkitem.replaceItemValue(WorkflowKernel.CREATED, new Date());
mahnlaufWorkitem.replaceItemValue(WorkflowKernel.MODIFIED, new Date());
// set unqiueid
mahnlaufWorkitem.setItemValue(WorkflowKernel.UNIQUEID, WorkflowKernel.generateUniqueID());
// copy dbtr_iban
mahnlaufWorkitem.setItemValue(MahnlaufService.ITEM_DBTR_NAME,
invoice.getItemValue(MahnlaufService.ITEM_DBTR_NAME));
// copy dbtr_iban
mahnlaufWorkitem.setItemValue(MahnlaufService.ITEM_DBTR_NAME,
invoice.getItemValue(MahnlaufService.ITEM_DBTR_NAME));
// lookup email....
String dbtrNumber = invoice.getItemValueString("dbtr.number");
ItemCollection businessPartner = businessPartnerService
.getBusinessPartnerByID(InvoiceUtil.buildBPID(dbtrNumber));
if (businessPartner != null) {
mahnlaufWorkitem.setItemValue(MahnlaufService.ITEM_DBTR_EMAIL, businessPartner.getItemValue("dbtr.mail"));// dbtr.mail
}
// lookup email....
String dbtrNumber = invoice.getItemValueString("dbtr.number");
ItemCollection businessPartner = businessPartnerService
.getBusinessPartnerByID(InvoiceUtil.buildBPID(dbtrNumber));
if (businessPartner != null) {
mahnlaufWorkitem.setItemValue(MahnlaufService.ITEM_DBTR_EMAIL, businessPartner.getItemValue("dbtr.mail"));// dbtr.mail
}
mahnlaufWorkitem.setItemValue(MahnlaufService.ITEM_DBTR_NUMBER,
invoice.getItemValue(MahnlaufService.ITEM_DBTR_NUMBER));
mahnlaufWorkitem.setItemValue("invoice.language", invoice.getItemValue("invoice.language"));
mahnlaufWorkitem.setItemValue(MahnlaufService.ITEM_DBTR_NUMBER,
invoice.getItemValue(MahnlaufService.ITEM_DBTR_NUMBER));
mahnlaufWorkitem.setItemValue("invoice.language", invoice.getItemValue("invoice.language"));
// set workflow group name from the Task Element to identify document in xslt
Model model = modelService.getModel(modelVersion);
ItemCollection task = model.getTask(taskID);
String modelTaskGroupName = task.getItemValueString("txtworkflowgroup"); // DO NOT CHANGE!
mahnlaufWorkitem.setItemValue(WorkflowKernel.WORKFLOWGROUP, modelTaskGroupName);
// set workflow group name from the Task Element to identify document in xslt
Model model = modelService.getModel(modelVersion);
ItemCollection task = model.getTask(taskID);
String modelTaskGroupName = task.getItemValueString("txtworkflowgroup"); // DO NOT CHANGE!
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;
/**
* Der MahnlaufService wird vom MahnlaufExecuteAdapter um
* Ausgangsrechnungen zu akutalisieren.
* Der MahnlaufService wird vom MahnlaufExecuteAdapter um Ausgangsrechnungen zu
* akutalisieren.
*
* @author rsoika
*

View file

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

View file

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

View file

@ -89,19 +89,18 @@ import jakarta.inject.Inject;
/**
* Der CargosoftXMLInvoiceImportImportService erweitert den FTPImportService und
* kann XML Invoice Dokumente von Cargosoft importieren.
* Der Service reagiert auf DocumentImportEvents und kann über das Options Feld
* konfiguriert werden.
* kann XML Invoice Dokumente von Cargosoft importieren. Der Service reagiert
* auf DocumentImportEvents und kann über das Options Feld konfiguriert werden.
* <p>
* Im Gegensatz zum FTPImportService werden die XML Dateien nach einem
* speziellen Verfahren geparsed und dann als Ausgangsrechnungen importiert.
* Der Service ersetzt den alten DATEV Import. Falls eine Rechnung schon
* existiert (Rechnungsnummer) wird der Datensatz vom FTP Laufwerk gelöscht und
* nicht importiert.
* speziellen Verfahren geparsed und dann als Ausgangsrechnungen importiert. Der
* Service ersetzt den alten DATEV Import. Falls eine Rechnung schon existiert
* (Rechnungsnummer) wird der Datensatz vom FTP Laufwerk gelöscht und nicht
* importiert.
* <p>
* In den Optionen muss die option "mandant.id" hinterlegt sein. Stimmt
* diese beim Import nicht mit der Mandanten Nummer in der XML Datei überein,
* wird das XML Dokument gelöscht und nicht importiert.
* In den Optionen muss die option "mandant.id" hinterlegt sein. Stimmt diese
* beim Import nicht mit der Mandanten Nummer in der XML Datei überein, wird das
* XML Dokument gelöscht und nicht importiert.
*
*
*
@ -164,8 +163,7 @@ public class CargosoftXMLInvoiceImportService {
return;
}
documentImportService.logMessage("...CARGOSOFT_INVOICE_XML Import started: mandant.id=" + mandantID,
event);
documentImportService.logMessage("...CARGOSOFT_INVOICE_XML Import started: mandant.id=" + mandantID, event);
String ftpServer = event.getSource().getItemValueString(DocumentImportService.SOURCE_ITEM_SERVER);
String ftpPort = event.getSource().getItemValueString(DocumentImportService.SOURCE_ITEM_PORT);
String ftpUser = event.getSource().getItemValueString(DocumentImportService.SOURCE_ITEM_USER);
@ -218,8 +216,8 @@ public class CargosoftXMLInvoiceImportService {
ftpClient.connect(ftpServer, ftpPort);
if (ftpClient.login(ftpUser, ftpPassword) == false) {
throw new PluginException(CargosoftXMLInvoiceImportService.class.getName(),
"FTP_ERROR", "FTP file transfer failed: login failed!");
throw new PluginException(CargosoftXMLInvoiceImportService.class.getName(), "FTP_ERROR",
"FTP file transfer failed: login failed!");
}
ftpClient.enterLocalPassiveMode();
logger.finest("...... FileType=" + FTP.BINARY_FILE_TYPE);
@ -227,8 +225,7 @@ public class CargosoftXMLInvoiceImportService {
ftpClient.setControlEncoding("UTF-8");
} catch (IOException e) {
throw new PluginException(CargosoftXMLInvoiceImportService.class.getName(),
"FTP_ERROR", e.getMessage());
throw new PluginException(CargosoftXMLInvoiceImportService.class.getName(), "FTP_ERROR", e.getMessage());
}
return ftpClient;
@ -252,8 +249,8 @@ public class CargosoftXMLInvoiceImportService {
// try to enter the working directory and read all files...
boolean bWorkingDir = ftpClient.changeWorkingDirectory(ftpPath);
if (bWorkingDir == false) {
throw new PluginException(CargosoftXMLInvoiceImportService.class.getName(),
"FTP_ERROR", "...failed to change into working directory: ");
throw new PluginException(CargosoftXMLInvoiceImportService.class.getName(), "FTP_ERROR",
"...failed to change into working directory: ");
}
FTPFile[] allFiles = ftpClient.listFiles(ftpPath);
@ -301,10 +298,10 @@ public class CargosoftXMLInvoiceImportService {
// "processed/");
// }
} catch (AccessDeniedException | ProcessingErrorException | PluginException
| ModelException | XPathExpressionException | TransformerException e) {
throw new PluginException(CargosoftXMLInvoiceImportService.class.getName(),
"FTP_ERROR", e.getMessage());
} catch (AccessDeniedException | ProcessingErrorException | PluginException | ModelException
| XPathExpressionException | TransformerException e) {
throw new PluginException(CargosoftXMLInvoiceImportService.class.getName(), "FTP_ERROR",
e.getMessage());
}
}
documentImportService.logMessage("..." + count + " new files imported.", event);
@ -315,8 +312,7 @@ public class CargosoftXMLInvoiceImportService {
int r = ftpClient.getReplyCode();
logger.severe("FTP ReplyCode=" + r);
throw new PluginException(CargosoftXMLInvoiceImportService.class.getName(),
"FTP_ERROR", e.getMessage());
throw new PluginException(CargosoftXMLInvoiceImportService.class.getName(), "FTP_ERROR", e.getMessage());
} finally {
// do logout....
@ -326,8 +322,8 @@ public class CargosoftXMLInvoiceImportService {
ftpClient.disconnect();
} catch (IOException e) {
documentImportService.logMessage("...FTP file transfer failed: " + e.getMessage(), event);
throw new PluginException(CargosoftXMLInvoiceImportService.class.getName(),
"FTP_ERROR", e.getMessage());
throw new PluginException(CargosoftXMLInvoiceImportService.class.getName(), "FTP_ERROR",
e.getMessage());
}
}
}
@ -346,9 +342,8 @@ public class CargosoftXMLInvoiceImportService {
* @throws XPathExpressionException
*/
public ItemCollection createWorkitem(ItemCollection source, String fileName, byte[] rawData,
Map<String, List<String>> spacePosMappings)
throws AccessDeniedException, ProcessingErrorException, PluginException, ModelException,
XPathExpressionException, TransformerException {
Map<String, List<String>> spacePosMappings) throws AccessDeniedException, ProcessingErrorException,
PluginException, ModelException, XPathExpressionException, TransformerException {
ItemCollection workitem = new ItemCollection();
workitem.model(source.getItemValueString(DocumentImportService.SOURCE_ITEM_MODELVERSION));
workitem.task(source.getItemValueInteger(DocumentImportService.SOURCE_ITEM_TASK));
@ -377,11 +372,11 @@ public class CargosoftXMLInvoiceImportService {
// invoice.rate
// invoice.base.amount
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/Client/Codes/Code", workitem,
"mandant.id", String.class);
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/Client/Codes/Code", workitem, "mandant.id",
String.class);
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceNumber",
workitem, "invoice.number", String.class);
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceNumber", workitem, "invoice.number",
String.class);
// verify if invoice is already imported.
if (alreadyImported(workitem.getItemValueString("invoice.number"))) {
@ -389,14 +384,14 @@ public class CargosoftXMLInvoiceImportService {
return null;
}
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceType/Codes/Code",
workitem, "invoice.type", String.class);
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceType/Description",
workitem, "invoice.type.description", String.class);
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceType/Codes/Code", workitem, "invoice.type",
String.class);
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceType/Description", workitem,
"invoice.type.description", String.class);
// Rechnungssumme errechnen.....
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceAmount/NetAmount/Amount/Value",
workitem, "invoice.total.net", Double.class);
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceAmount/NetAmount/Amount/Value", workitem,
"invoice.total.net", Double.class);
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceAmount/VATInformation/VATAmount/Amount/Value",
workitem, "invoice.total.tax", Double.class);
if (!workitem.hasItem("invoice.total.tax")) {
@ -416,34 +411,33 @@ public class CargosoftXMLInvoiceImportService {
}
// Steuer
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceAmount/VATInformation/VAT/VATRate",
workitem, "invoice.vatrate", Double.class);
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceAmount/VATInformation/VAT/VATRate", workitem,
"invoice.vatrate", Double.class);
// Booking Period
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/BookingPeriod",
workitem, "invoice.bookingperiod", String.class);
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/BookingPeriod", workitem, "invoice.bookingperiod",
String.class);
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceCurrency/Codes/Code",
workitem, "invoice.currency", String.class);
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceCurrency/Codes/Code", workitem,
"invoice.currency", String.class);
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceDate",
workitem, "invoice.date", Date.class);
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceDate", workitem, "invoice.date", Date.class);
readXMLValue(doc,
"/Invoices/Invoice/InvoiceHeader/PaymentConditions/PaymentCondition/Codes/Code[@Type='cs']",
workitem, "payment.term", String.class);
// kostenstelle
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/CostUnit/Description",
workitem, "invoice.CostUnit", String.class);
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/CostUnit/Description", workitem, "invoice.CostUnit",
String.class);
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/PaymentConditions/DueDate",
workitem, "invoice.duedate", Date.class);
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/PaymentConditions/DueDate",
workitem, "invoice.reminder", Date.class);
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/PaymentConditions/DueDate", workitem, "invoice.duedate",
Date.class);
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/PaymentConditions/DueDate", workitem, "invoice.reminder",
Date.class);
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceAddress/Codes/Code",
workitem, "dbtr.number", String.class);
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceAddress/Codes/Code", workitem, "dbtr.number",
String.class);
String dbtrNumber = workitem.getItemValueString("dbtr.number");
if (dbtrNumber.startsWith("K") || dbtrNumber.startsWith("D")) {
workitem.setItemValue("dbtr.number", dbtrNumber.substring(1));
@ -487,8 +481,7 @@ public class CargosoftXMLInvoiceImportService {
attacheFiles(doc, workitem);
} catch (ParserConfigurationException | SAXException | IOException e) {
throw new PluginException(CargosoftXMLInvoiceImportService.class.getName(),
"XML_ERROR", e.getMessage());
throw new PluginException(CargosoftXMLInvoiceImportService.class.getName(), "XML_ERROR", e.getMessage());
}
return workitem;
}
@ -512,8 +505,7 @@ public class CargosoftXMLInvoiceImportService {
*
* /Invoices/Invoice/InvoiceHeader/Client/Code
*
* Beispiel Datum:
* <InvoiceDate>2024-05-13T00:00:00+02:00</InvoiceDate>
* Beispiel Datum: <InvoiceDate>2024-05-13T00:00:00+02:00</InvoiceDate>
*
* @param doc - xml doc
* @param expression - xpath expression
@ -566,11 +558,9 @@ public class CargosoftXMLInvoiceImportService {
/**
* Hilfsmethode die die ATC Nummer sucht und in das workitem feld
* invoice.atc.number einträgt.
* Wir gehen davon aus, das wir über die ChildItems diese aus dem billingtexts
* schon ermittel haben.
* Es können mehrere ATC nummern existieren. Diese werden in einer Liste
* gespeichert
* invoice.atc.number einträgt. Wir gehen davon aus, das wir über die ChildItems
* diese aus dem billingtexts schon ermittel haben. Es können mehrere ATC
* nummern existieren. Diese werden in einer Liste gespeichert
*
* @param doc
* @param workitem
@ -595,15 +585,9 @@ public class CargosoftXMLInvoiceImportService {
*
* InvoiceRows/InvoiceRow
*
* Each row has the following items:
* _childitems {
* datev.shzeichen=[H],
* 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]}}
* Each row has the following items: _childitems { datev.shzeichen=[H],
* 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 workitem
@ -622,8 +606,7 @@ public class CargosoftXMLInvoiceImportService {
XPathExpression netAmountExchangeRateExpr = xPath
.compile("InvoiceAmount/NetAmount/Amount/ExchangeRate/text()");
XPathExpression netActivityTypeExpr = xPath
.compile("ActivityType/Codes/Code[@Type='cs']/text()");
XPathExpression netActivityTypeExpr = xPath.compile("ActivityType/Codes/Code[@Type='cs']/text()");
NodeList rowList = doc.getElementsByTagName("InvoiceRow");
double _kurs = 0.0;
@ -732,13 +715,10 @@ public class CargosoftXMLInvoiceImportService {
}
/**
* G = Gutschrift
* SR = Stornorechnung
* GK = Sammelgutschrift
* SS = Sammelrechnung Storno
* G = Gutschrift SR = Stornorechnung GK = Sammelgutschrift SS = Sammelrechnung
* Storno
*
* müssen wir das vorzeichen ändern
* dies wird über den Invoice.Type geprüft
* müssen wir das vorzeichen ändern dies wird über den Invoice.Type geprüft
*
* @return
*/
@ -748,9 +728,9 @@ public class CargosoftXMLInvoiceImportService {
}
/**
* This method attache the pdf file and the XML file to the workitem.
* The method removes the file content first form the xml tree and attache the
* XML without the pdf data.
* This method attache the pdf file and the XML file to the workitem. The method
* removes the file content first form the xml tree and attache the XML without
* the pdf data.
*
* @throws XPathExpressionException
* @throws TransformerException
@ -761,16 +741,16 @@ public class CargosoftXMLInvoiceImportService {
XPathFactory xpathFactory = XPathFactory.newInstance();
XPath xpath = xpathFactory.newXPath();
// read filename and content....
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/Attachments/Attachment/Filename",
workitem, "import.filename", String.class);
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/Attachments/Attachment/Content",
workitem, "import.filedata", String.class);
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/Attachments/Attachment/Filename", workitem,
"import.filename", String.class);
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/Attachments/Attachment/Content", workitem, "import.filedata",
String.class);
// attache the PDF file content
String fileDataString = workitem.getItemValueString("import.filedata");
byte[] decodedPDFData = java.util.Base64.getDecoder().decode(fileDataString);
FileData fileData = new FileData(workitem.getItemValueString("import.filename"),
decodedPDFData, "application/pdf", null);
FileData fileData = new FileData(workitem.getItemValueString("import.filename"), decodedPDFData,
"application/pdf", null);
workitem.addFileData(fileData);
// remove the temp items..
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.
*/
@DeclareRoles({ "org.imixs.ACCESSLEVEL.NOACCESS", "org.imixs.ACCESSLEVEL.READERACCESS",
"org.imixs.ACCESSLEVEL.AUTHORACCESS", "org.imixs.ACCESSLEVEL.EDITORACCESS",
"org.imixs.ACCESSLEVEL.MANAGERACCESS" })
"org.imixs.ACCESSLEVEL.AUTHORACCESS", "org.imixs.ACCESSLEVEL.EDITORACCESS",
"org.imixs.ACCESSLEVEL.MANAGERACCESS" })
@RolesAllowed({ "org.imixs.ACCESSLEVEL.NOACCESS", "org.imixs.ACCESSLEVEL.READERACCESS",
"org.imixs.ACCESSLEVEL.AUTHORACCESS", "org.imixs.ACCESSLEVEL.EDITORACCESS",
"org.imixs.ACCESSLEVEL.MANAGERACCESS" })
"org.imixs.ACCESSLEVEL.AUTHORACCESS", "org.imixs.ACCESSLEVEL.EDITORACCESS",
"org.imixs.ACCESSLEVEL.MANAGERACCESS" })
@Singleton
@RunAs("org.imixs.ACCESSLEVEL.MANAGERACCESS")
public class ZohoAPIService {
private static Logger logger = Logger.getLogger(ZohoAPIService.class.getName());
private static final String ZOHO_API_BASE_URL = "https://www.zohoapis.eu/books/v3/";
private static Logger logger = Logger.getLogger(ZohoAPIService.class.getName());
@Inject
ZohoOAuthManager zohoOAuthManager;
@Inject
ZohoOAuthManager zohoOAuthManager;
private HttpClient httpClient;
private Jsonb jsonb;
private HttpClient httpClient;
private Jsonb jsonb;
@PostConstruct
void init() {
this.httpClient = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10))
.build();
@PostConstruct
void init() {
this.httpClient = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10)).build();
JsonbConfig config = new JsonbConfig()
.withDateFormat("yyyy-MM-dd", Locale.getDefault())
.setProperty("jsonb.fail-on-unknown-properties", false); // Ignoriert unbekannte Felder
this.jsonb = JsonbBuilder.create(config);
JsonbConfig config = new JsonbConfig().withDateFormat("yyyy-MM-dd", Locale.getDefault())
.setProperty("jsonb.fail-on-unknown-properties", false); // Ignoriert unbekannte Felder
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 + "'...");
try {
// https://www.zohoapis.com/books/v3/contacts?organization_id=10234695&company_name=xxx'
String uri = ZOHO_API_BASE_URL + "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);
}
/**
* Exports an invocie document
*
* @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!
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();
}
/**
* Exports an invocie document
*
* @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") + "...");
// Konvertiere ItemCollection zu ZohoInvoiceDTO
ZohoInvoice zohoInvoice = convertToZohoInvoice(invoice, contactID, accessToken);
// 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();
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 = 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
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);
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);
}
/**
* 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 {
// Generate a unique boundary
String boundary = "----WebKitFormBoundary" + System.currentTimeMillis();
// JSON serialisieren
String requestBody = jsonb.toJson(newContact);
logger.fine("│ ├── " + requestBody);
String uri = zohoOAuthManager.getBaseURI() + "contacts?organization_id="
+ zohoOAuthManager.getOrganization();
// 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);
HttpRequest request = HttpRequest.newBuilder().uri(URI.create(uri))
.header("Authorization", "Zoho-oauthtoken " + accessToken)
.header("Content-Type", "application/json").POST(HttpRequest.BodyPublishers.ofString(requestBody))
.build();
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);
HttpRequest.BodyPublisher bodyPublisher = HttpRequest.BodyPublishers.ofByteArrays(
Arrays.asList(header, file.getContent(), footer));
// 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!");
}
// 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);
}
} 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
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
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 Logger logger = Logger.getLogger(ZohoController.class.getName());
private static final long serialVersionUID = 1L;
private static Logger logger = Logger.getLogger(ZohoController.class.getName());
@Inject
ZohoOAuthManager zohoOAuthManager;
@Inject
ZohoOAuthManager zohoOAuthManager;
public ZohoController() {
super();
setName(ZOHO_CONFIGURATION);
}
public ZohoController() {
super();
setName(ZOHO_CONFIGURATION);
}
/**
* Ruft zohoOAuhtManager.updateToken auf
*/
public void updateAccessToken() {
//
try {
zohoOAuthManager.updateTokens(this.getWorkitem().getItemValueString("zoho.code"));
} catch (PluginException e) {
String message = "Failed to generate token: " + e.getMessage();
logger.warning(message);
this.getWorkitem().setItemValue("message", message);
}
}
/**
* Ruft zohoOAuhtManager.updateToken auf
*/
public void updateAccessToken() {
//
try {
zohoOAuthManager.updateTokens(this.getWorkitem().getItemValueString("zoho.code"));
} catch (PluginException e) {
String message = "Failed to generate token: " + e.getMessage();
logger.warning(message);
this.getWorkitem().setItemValue("message", message);
}
}
/**
* Ruft zohoOAuhtManager.refreshToken auf
*/
public void refreshAccessToken() {
//
try {
zohoOAuthManager.refreshAccessToken();
} catch (PluginException e) {
String message = "Failed to refresh token: " + e.getMessage();
logger.warning(message);
this.getWorkitem().setItemValue("message", message);
}
}
/**
* Ruft zohoOAuhtManager.refreshToken auf
*/
public void refreshAccessToken() {
//
try {
zohoOAuthManager.refreshAccessToken();
} catch (PluginException e) {
String message = "Failed to refresh token: " + e.getMessage();
logger.warning(message);
this.getWorkitem().setItemValue("message", message);
}
}
}

View file

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

View file

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

View file

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

View file

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

View file

@ -10,12 +10,12 @@
<ui:define name="scripts">
<script type="text/javascript">
/*<![CDATA[*/
function handleSubmit(event) {
// remove ajax kringel
setTimeout(function() {
document.body.classList.remove('loading');
}, 5000);
}
function handleSubmit(event) {
// remove ajax kringel
setTimeout(function () {
document.body.classList.remove('loading');
}, 5000);
}
/*]]>*/
</script>
</ui:define>
@ -32,7 +32,8 @@
<h:panelGroup layout="block" styleClass="imixs-body" id="analyse_panel">
<div class="imixs-form-panel">
<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>
</div>
<div class="imixs-form-section-2">
@ -65,12 +66,14 @@
Abteilung:
</dt>
<dd>
<h:selectOneMenu noSelectionOption="true"
<h:selectOneMenu noSelectionOption="true"
value="#{excelExportController.filter.item['space.ref']}">
<f:selectItem itemLabel=" - " itemValue="-" ></f:selectItem>
<c:forEach items="#{spaceRefFormController.getSpaces(workflowController.workitem, options)}"
<f:selectItem itemLabel=" - " itemValue="-"></f:selectItem>
<c:forEach
items="#{spaceRefFormController.getSpaces(workflowController.workitem, options)}"
var="space">
<f:selectItem itemLabel="#{space.item['name']}" itemValue="#{space.item['$UniqueID']}">
<f:selectItem itemLabel="#{space.item['name']}"
itemValue="#{space.item['$UniqueID']}">
</f:selectItem>
</c:forEach>
</h:selectOneMenu>
@ -82,10 +85,10 @@
Debitor:
</dt>
<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="item" value="#{excelExportController.item}" />
</ui:include>
</ui:include>
</dd>
</dl>
@ -94,15 +97,14 @@
Positionsnummer:
</dt>
<dd>
<h:inputText
value="#{excelExportController.filter.item['invoice.text']}">
<h:inputText value="#{excelExportController.filter.item['invoice.text']}">
</h:inputText>
</dd>
</dl>
</div>
<h:commandButton actionListener="#{excelExportController.export()}"
value="Excel Export starten">
value="Excel Export starten">
</h:commandButton>
<h:commandButton value="#{message.close}" action="notes" />
</div>

View file

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

View file

@ -19,40 +19,40 @@ import org.junit.jupiter.api.Test;
*/
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
public void setUp() throws Exception {
@BeforeEach
public void setUp() throws Exception {
}
}
/**
* Test Kreditoren import
*/
@Disabled
@Test
public void testPutFileToFTP() {
/**
* Test Kreditoren import
*/
@Disabled
@Test
public void testPutFileToFTP() {
FTPConnector ftpc = new FTPConnector();
FTPConnector ftpc = new FTPConnector();
ftpc.ftpServer = Optional.ofNullable("u248962.your-storagebox.de");
ftpc.ftpPort = Optional.ofNullable(21);
ftpc.ftpUser = Optional.ofNullable("u248962-sub2");
ftpc.ftpPassword = Optional.ofNullable("aXa61n9Un3jDNQlL");
ftpc.ftpPath = Optional.ofNullable("/");
ftpc.ftpServer = Optional.ofNullable("u248962.your-storagebox.de");
ftpc.ftpPort = Optional.ofNullable(21);
ftpc.ftpUser = Optional.ofNullable("u248962-sub2");
ftpc.ftpPassword = Optional.ofNullable("aXa61n9Un3jDNQlL");
ftpc.ftpPath = Optional.ofNullable("/");
FileData fileData = new FileData("test.txt", "some data...".getBytes(), "text", null);
try {
ftpc.put(fileData);
logger.info("test finished...");
} catch (PluginException e) {
FileData fileData = new FileData("test.txt", "some data...".getBytes(), "text", null);
try {
ftpc.put(fileData);
logger.info("test finished...");
} catch (PluginException e) {
e.printStackTrace();
fail();
}
e.printStackTrace();
fail();
}
}
}
}

View file

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

View file

@ -13,18 +13,18 @@ import org.junit.jupiter.api.Test;
*/
public class TestPostionsnummernRegex {
/**
* Test positionnummern 'XX-YYY-####-###'
*/
@Test
public void testPutFileToFTP() {
/**
* Test positionnummern 'XX-YYY-####-###'
*/
@Test
public void testPutFileToFTP() {
assertTrue("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));
assertTrue("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));
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
*
*
* EXTF_202301_202302030602_1295
* EXTF_202301_202302060601_1296
* EXTF_202301_202302070603_1297
* EXTF_202301_202302080603_1298
* EXTF_202301_202302090604_1299
* EXTF_202301_202302100603_1300
* EXTF_202301_202302130601_1301
* EXTF_202301_202302140604_1302
* EXTF_202301_202302141840_1303
* EXTF_202301_202302160944_1304
* EXTF_202302_202302020601_1294
* EXTF_202302_202302030602_1295
* EXTF_202302_202302060601_1296
* EXTF_202302_202302070603_1297
* 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_202301_202302030602_1295 EXTF_202301_202302060601_1296
* EXTF_202301_202302070603_1297 EXTF_202301_202302080603_1298
* EXTF_202301_202302090604_1299 EXTF_202301_202302100603_1300
* EXTF_202301_202302130601_1301 EXTF_202301_202302140604_1302
* EXTF_202301_202302141840_1303 EXTF_202301_202302160944_1304
* EXTF_202302_202302020601_1294 EXTF_202302_202302030602_1295
* EXTF_202302_202302060601_1296 EXTF_202302_202302070603_1297
* 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
*
*/

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -33,240 +33,218 @@ import jakarta.json.bind.JsonbBuilder;
@Disabled
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 Jsonb jsonb;
private HttpClient httpClient;
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 clientSecret = "3f3e74c5212bbc924a7ebf77f887c6296db1106c47";
private String clientID = "1000.GR5FTPZYB06HEWSFSSX4DJU3OBD4EJ";
private String clientSecret = "3f3e74c5212bbc924a7ebf77f887c6296db1106c47";
private String code = null;
private String accessToken = null;
private String refreshToken = null;
private String code = null;
private String accessToken = null;
private String refreshToken = null;
@BeforeEach
public void setup() throws PluginException, ModelException {
logger.info("setup test environment ...");
this.httpClient = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10))
.build();
this.jsonb = JsonbBuilder.create();
}
@BeforeEach
public void setup() throws PluginException, ModelException {
logger.info("setup test environment ...");
this.httpClient = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10)).build();
this.jsonb = JsonbBuilder.create();
}
/**
* Testet den Vorgang einen Access und Refresh Token zu generieren
*
* @throws IOException
*/
@Test
public void testUpdateTokens() throws IOException {
/**
* Testet den Vorgang einen Access und Refresh Token zu generieren
*
* @throws IOException
*/
@Test
public void testUpdateTokens() throws IOException {
logger.info("├── update Zoho tokens");
refreshToken = null;
accessToken = null;
code = "1000.48bd85a3965cf724f4691757cfd8d93b.8ae5e3ee7d70f90d307da4d289a9b067";
try {
logger.info("├── update Zoho tokens");
refreshToken = null;
accessToken = null;
code = "1000.48bd85a3965cf724f4691757cfd8d93b.8ae5e3ee7d70f90d307da4d289a9b067";
try {
String requestUrl = "https://accounts.zoho.eu/oauth/v2/token" +
"?code=" + URLEncoder.encode(code, StandardCharsets.UTF_8) +
"&client_id=" + URLEncoder.encode(clientID, StandardCharsets.UTF_8) +
"&client_secret=" + URLEncoder.encode(clientSecret, StandardCharsets.UTF_8) +
"&redirect_uri=" + URLEncoder.encode(redirectURI, StandardCharsets.UTF_8) +
"&grant_type=authorization_code";
String requestUrl = "https://accounts.zoho.eu/oauth/v2/token" + "?code="
+ URLEncoder.encode(code, StandardCharsets.UTF_8) + "&client_id="
+ URLEncoder.encode(clientID, StandardCharsets.UTF_8) + "&client_secret="
+ URLEncoder.encode(clientSecret, StandardCharsets.UTF_8) + "&redirect_uri="
+ URLEncoder.encode(redirectURI, StandardCharsets.UTF_8) + "&grant_type=authorization_code";
logger.info("│ ├── Request URI=" + requestUrl);
logger.info("│ ├── Request URI=" + requestUrl);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(requestUrl))
.header("Content-Type", "application/x-www-form-urlencoded")
.POST(HttpRequest.BodyPublishers.noBody()) // Wichtig: Kein Body!
.build();
HttpRequest request = HttpRequest.newBuilder().uri(URI.create(requestUrl))
.header("Content-Type", "application/x-www-form-urlencoded")
.POST(HttpRequest.BodyPublishers.noBody()) // Wichtig: Kein Body!
.build();
HttpResponse<String> response;
HttpResponse<String> response;
response = httpClient.send(request,
HttpResponse.BodyHandlers.ofString());
response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
logger.info("│ ├── Response statusCode=" + response.statusCode());
if (response.statusCode() == 200) {
logger.info("│ ├── Response statusCode=" + response.statusCode());
if (response.statusCode() == 200) {
// 1. Response-Logging für Debugging
logger.info("│ ├── Response Body=" + response.body());
// 1. Response-Logging für Debugging
logger.info("│ ├── Response Body=" + response.body());
assertNotNull(response.body());
assertFalse(response.body().contains("error"));
assertNotNull(response.body());
assertFalse(response.body().contains("error"));
TokenResponse tokenResponse = jsonb.fromJson(response.body(),
TokenResponse.class);
TokenResponse tokenResponse = jsonb.fromJson(response.body(), TokenResponse.class);
accessToken = tokenResponse.getAccess_token();
assertNotNull(accessToken);
logger.info("│ ├── accessToken=" + accessToken);
refreshToken = tokenResponse.getRefresh_token();
logger.info("│ ├── refreshToken=" + refreshToken);
logger.info("│ ├── expires_in=" + tokenResponse.getExpires_in());
} else {
throw new RuntimeException("Failed to get tokens: " +
response.statusCode() + " - " + response.body());
}
accessToken = tokenResponse.getAccess_token();
assertNotNull(accessToken);
logger.info("│ ├── accessToken=" + accessToken);
refreshToken = tokenResponse.getRefresh_token();
logger.info("│ ├── refreshToken=" + refreshToken);
logger.info("│ ├── expires_in=" + tokenResponse.getExpires_in());
} else {
throw new RuntimeException("Failed to get tokens: " + response.statusCode() + " - " + response.body());
}
} catch (IOException | InterruptedException e) {
throw new InvalidAccessException("Unable to resolve access tokens: " + e.getMessage());
}
} catch (IOException | InterruptedException e) {
throw new InvalidAccessException("Unable to resolve access tokens: " + e.getMessage());
}
}
}
/**
* Testet den Refresh Token
*
* POST:
* {Accounts_URL}/oauth/v2/token?refresh_token={refresh_token}&client_id={client_id}&client_secret={client_secret}&grant_type=refresh_token
*
* @throws IOException
*/
@Test
public void testRefreshAccessToken() throws IOException {
/**
* Testet den Refresh Token
*
* POST:
* {Accounts_URL}/oauth/v2/token?refresh_token={refresh_token}&client_id={client_id}&client_secret={client_secret}&grant_type=refresh_token
*
* @throws IOException
*/
@Test
public void testRefreshAccessToken() throws IOException {
logger.info("├── refresh Zoho access token");
refreshToken = "1000.cf458816e97f848df8134ce1c7e923a1.5b9dbdee317e35d7f0c619e7648d55ff";
try {
logger.info("├── refresh Zoho access token");
refreshToken = "1000.cf458816e97f848df8134ce1c7e923a1.5b9dbdee317e35d7f0c619e7648d55ff";
try {
String requestUrl = "https://accounts.zoho.eu/oauth/v2/token" +
"?refresh_token=" + URLEncoder.encode(refreshToken, StandardCharsets.UTF_8) +
"&client_id=" + URLEncoder.encode(clientID, StandardCharsets.UTF_8) +
"&client_secret=" + URLEncoder.encode(clientSecret, StandardCharsets.UTF_8) +
"&grant_type=refresh_token";
String requestUrl = "https://accounts.zoho.eu/oauth/v2/token" + "?refresh_token="
+ URLEncoder.encode(refreshToken, StandardCharsets.UTF_8) + "&client_id="
+ URLEncoder.encode(clientID, StandardCharsets.UTF_8) + "&client_secret="
+ URLEncoder.encode(clientSecret, StandardCharsets.UTF_8) + "&grant_type=refresh_token";
logger.info("│ ├── Request URI=" + requestUrl);
logger.info("│ ├── Request URI=" + requestUrl);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(requestUrl))
.header("Content-Type", "application/x-www-form-urlencoded")
.POST(HttpRequest.BodyPublishers.noBody()) // Wichtig: Kein Body!
.build();
HttpRequest request = HttpRequest.newBuilder().uri(URI.create(requestUrl))
.header("Content-Type", "application/x-www-form-urlencoded")
.POST(HttpRequest.BodyPublishers.noBody()) // Wichtig: Kein Body!
.build();
HttpResponse<String> response;
HttpResponse<String> response;
response = httpClient.send(request,
HttpResponse.BodyHandlers.ofString());
response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
logger.info("│ ├── Response statusCode=" + response.statusCode());
if (response.statusCode() == 200) {
logger.info("│ ├── Response statusCode=" + response.statusCode());
if (response.statusCode() == 200) {
// 1. Response-Logging für Debugging
logger.info("│ ├── Response Body=" + response.body());
// 1. Response-Logging für Debugging
logger.info("│ ├── Response Body=" + response.body());
TokenResponse tokenResponse = jsonb.fromJson(response.body(),
TokenResponse.class);
TokenResponse tokenResponse = jsonb.fromJson(response.body(), TokenResponse.class);
String accessToken = tokenResponse.getAccess_token();
logger.info("│ ├── accessToken=" + accessToken);
logger.info("│ ├── expires_in=" + tokenResponse.getExpires_in());
} else {
throw new RuntimeException("Failed to get tokens: " +
response.statusCode() + " - " + response.body());
}
String accessToken = tokenResponse.getAccess_token();
logger.info("│ ├── accessToken=" + accessToken);
logger.info("│ ├── expires_in=" + tokenResponse.getExpires_in());
} else {
throw new RuntimeException("Failed to get tokens: " + response.statusCode() + " - " + response.body());
}
} catch (IOException | InterruptedException e) {
throw new InvalidAccessException("Unable to resolve access tokens: " + e.getMessage());
}
} catch (IOException | InterruptedException e) {
throw new InvalidAccessException("Unable to resolve access tokens: " + e.getMessage());
}
}
}
/**
* Testet den eigentlichen API Zugriff mit Hilfe des zuvor generieren Access
* Token
*
* GET: https://{zohoapis_domain}/writer/api/v1/documents
*
* @see https://www.zoho.com/books/api/v3/introduction/#overview
* @throws IOException
*/
@Test
public void testGetOrganizations() throws IOException {
/**
* Testet den eigentlichen API Zugriff mit Hilfe des zuvor generieren Access
* Token
*
* GET: https://{zohoapis_domain}/writer/api/v1/documents
*
* @see https://www.zoho.com/books/api/v3/introduction/#overview
* @throws IOException
*/
@Test
public void testGetOrganizations() throws IOException {
logger.info("├── get Zoho organizations");
accessToken = "1000.21912dc6af0906707f51d336fd98ab61.eb153952d0d86a828fcb7e18426c2c1a";
try {
logger.info("├── get Zoho organizations");
accessToken = "1000.21912dc6af0906707f51d336fd98ab61.eb153952d0d86a828fcb7e18426c2c1a";
try {
String url = "https://www.zohoapis.eu/books/v3/organizations";
String url = "https://www.zohoapis.eu/books/v3/organizations";
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.GET()
.header("Authorization", "Zoho-oauthtoken " + accessToken)
.build();
HttpRequest request = HttpRequest.newBuilder().uri(URI.create(url)).GET()
.header("Authorization", "Zoho-oauthtoken " + accessToken).build();
HttpResponse<String> response = httpClient.send(
request,
HttpResponse.BodyHandlers.ofString());
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
logger.info("│ ├── Response statusCode=" + response.statusCode());
logger.info("│ ├── Response statusCode=" + response.statusCode());
// 1. Response-Logging für Debugging
logger.info("│ ├── Response Body=" + response.body());
// 1. Response-Logging für Debugging
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) {
throw new InvalidAccessException("Unable to resolve access tokens: " + e.getMessage());
}
} catch (IOException | InterruptedException e) {
throw new InvalidAccessException("Unable to resolve access tokens: " + e.getMessage());
}
}
}
/**
* Testet den eigentlichen API Zugriff mit Hilfe des zuvor generieren Access
* Token
*
*
* curl --request GET \
* --url 'https://www.zohoapis.com/books/v3/invoices?organization_id=10234695' \
* --header 'Authorization: Zoho-oauthtoken
* 1000.41d9xxxxxxxxxxxxxxxxxxxxxxxxc2d1.8fccxxxxxxxxxxxxxxxxxxxxxxxx125f'
*
*
* @throws IOException
*/
@Test
public void testGetInvoices() throws IOException {
/**
* Testet den eigentlichen API Zugriff mit Hilfe des zuvor generieren Access
* Token
*
*
* curl --request GET \ --url
* 'https://www.zohoapis.com/books/v3/invoices?organization_id=10234695' \
* --header 'Authorization: Zoho-oauthtoken
* 1000.41d9xxxxxxxxxxxxxxxxxxxxxxxxc2d1.8fccxxxxxxxxxxxxxxxxxxxxxxxx125f'
*
*
* @throws IOException
*/
@Test
public void testGetInvoices() throws IOException {
logger.info("├── get Zoho documents");
accessToken = "1000.21912dc6af0906707f51d336fd98ab61.eb153952d0d86a828fcb7e18426c2c1a";
try {
logger.info("├── get Zoho documents");
accessToken = "1000.21912dc6af0906707f51d336fd98ab61.eb153952d0d86a828fcb7e18426c2c1a";
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()
.uri(URI.create(url))
.GET()
.header("Authorization", "Zoho-oauthtoken " + accessToken)
.build();
HttpRequest request = HttpRequest.newBuilder().uri(URI.create(url)).GET()
.header("Authorization", "Zoho-oauthtoken " + accessToken).build();
HttpResponse<String> response = httpClient.send(
request,
HttpResponse.BodyHandlers.ofString());
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
logger.info("│ ├── Response statusCode=" + response.statusCode());
logger.info("│ ├── Response statusCode=" + response.statusCode());
// 1. Response-Logging für Debugging
logger.info("│ ├── Response Body=" + response.body());
// 1. Response-Logging für Debugging
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) {
throw new InvalidAccessException("Unable to resolve access tokens: " + e.getMessage());
}
} catch (IOException | InterruptedException e) {
throw new InvalidAccessException("Unable to resolve access tokens: " + e.getMessage());
}
}
}
}

View file

@ -1,97 +1,97 @@
{
"code": 0,
"message": "success",
"invoices": [
"code" : 0,
"message" : "success",
"invoices" : [
{
"ach_payment_initiated": false,
"invoice_id": "777249000000055583",
"zcrm_potential_id": "",
"customer_id": "777249000000055569",
"zcrm_potential_name": "",
"customer_name": "Mr. Foo",
"company_name": "",
"status": "overdue",
"invoice_number": "INV-000001",
"reference_number": "23452345",
"date": "2025-04-21",
"due_date": "2025-04-21",
"due_days": "Overdue by 3 days",
"email": "ralph.soika@imixs.com",
"project_name": "",
"billing_address": {
"address": "",
"street2": "",
"city": "",
"state": "",
"zipcode": "",
"country": "",
"phone": "",
"fax": "",
"attention": ""
"ach_payment_initiated" : false,
"invoice_id" : "777249000000055583",
"zcrm_potential_id" : "",
"customer_id" : "777249000000055569",
"zcrm_potential_name" : "",
"customer_name" : "Mr. Foo",
"company_name" : "",
"status" : "overdue",
"invoice_number" : "INV-000001",
"reference_number" : "23452345",
"date" : "2025-04-21",
"due_date" : "2025-04-21",
"due_days" : "Overdue by 3 days",
"email" : "ralph.soika@imixs.com",
"project_name" : "",
"billing_address" : {
"address" : "",
"street2" : "",
"city" : "",
"state" : "",
"zipcode" : "",
"country" : "",
"phone" : "",
"fax" : "",
"attention" : ""
},
"shipping_address": {
"address": "",
"street2": "",
"city": "",
"state": "",
"zipcode": "",
"country": "",
"phone": "",
"fax": "",
"attention": ""
"shipping_address" : {
"address" : "",
"street2" : "",
"city" : "",
"state" : "",
"zipcode" : "",
"country" : "",
"phone" : "",
"fax" : "",
"attention" : ""
},
"country": "",
"phone": "",
"created_by": "Ralph Soika",
"total": 50.00,
"balance": 50.00,
"payment_expected_date": "",
"custom_fields": [],
"custom_field_hash": {},
"tags": [],
"salesperson_name": "",
"shipping_charge": 0.00,
"adjustment": 0.00,
"created_time": "2025-04-21T13:36:26+0200",
"last_modified_time": "2025-04-21T13:42:56+0200",
"updated_time": "2025-04-21T13:42:56+0200",
"is_viewed_by_client": false,
"has_attachment": true,
"client_viewed_time": "",
"is_viewed_in_mail": false,
"is_emailed": true,
"mail_first_viewed_time": "",
"mail_last_viewed_time": "",
"is_correction_invoice": false,
"color_code": "",
"current_sub_status_id": "",
"current_sub_status": "overdue",
"currency_id": "777249000000000071",
"schedule_time": "",
"currency_code": "EUR",
"currency_symbol": "€",
"template_type": "standard",
"invoice_url": "https://zohosecurepay.eu/books/imixs/secure?CInvoiceID=2-64277fbd3cd8bc86f8e581a3a7e1544618c191c094de1ae938827291b0828c92b5f796a1ec94bfb1fdcce7621ddf18bf61770563d9fd283207b7fa0e24d232a416b0b5e010c61cd7 ",
"transaction_type": "renewal",
"reminders_sent": 0,
"last_reminder_sent_date": "",
"last_payment_date": "",
"template_id": "777249000000000103",
"documents": "",
"salesperson_id": "",
"write_off_amount": 0.00,
"exchange_rate": 1.00,
"unprocessed_payment_amount": 0.00
"country" : "",
"phone" : "",
"created_by" : "Ralph Soika",
"total" : 50.0,
"balance" : 50.0,
"payment_expected_date" : "",
"custom_fields" : [ ],
"custom_field_hash" : { },
"tags" : [ ],
"salesperson_name" : "",
"shipping_charge" : 0.0,
"adjustment" : 0.0,
"created_time" : "2025-04-21T13:36:26+0200",
"last_modified_time" : "2025-04-21T13:42:56+0200",
"updated_time" : "2025-04-21T13:42:56+0200",
"is_viewed_by_client" : false,
"has_attachment" : true,
"client_viewed_time" : "",
"is_viewed_in_mail" : false,
"is_emailed" : true,
"mail_first_viewed_time" : "",
"mail_last_viewed_time" : "",
"is_correction_invoice" : false,
"color_code" : "",
"current_sub_status_id" : "",
"current_sub_status" : "overdue",
"currency_id" : "777249000000000071",
"schedule_time" : "",
"currency_code" : "EUR",
"currency_symbol" : "€",
"template_type" : "standard",
"invoice_url" : "https://zohosecurepay.eu/books/imixs/secure?CInvoiceID=2-64277fbd3cd8bc86f8e581a3a7e1544618c191c094de1ae938827291b0828c92b5f796a1ec94bfb1fdcce7621ddf18bf61770563d9fd283207b7fa0e24d232a416b0b5e010c61cd7 ",
"transaction_type" : "renewal",
"reminders_sent" : 0,
"last_reminder_sent_date" : "",
"last_payment_date" : "",
"template_id" : "777249000000000103",
"documents" : "",
"salesperson_id" : "",
"write_off_amount" : 0.0,
"exchange_rate" : 1.0,
"unprocessed_payment_amount" : 0.0
}
],
"page_context": {
"page": 1,
"per_page": 200,
"has_more_page": false,
"report_name": "Invoices",
"applied_filter": "Status.All",
"custom_fields": [],
"sort_column": "created_time",
"sort_order": "D"
"page_context" : {
"page" : 1,
"per_page" : 200,
"has_more_page" : false,
"report_name" : "Invoices",
"applied_filter" : "Status.All",
"custom_fields" : [ ],
"sort_column" : "created_time",
"sort_order" : "D"
}
}

39
pom.xml
View file

@ -128,8 +128,45 @@
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
</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>
</build>
<dependencyManagement>

View file

@ -74,6 +74,7 @@
<bpmn2:flowNodeRef>event_kT136g</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>event_jkHxJA</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>event_zzWQSA</bpmn2:flowNodeRef>
<bpmn2:flowNodeRef>event_k4Ms1Q</bpmn2:flowNodeRef>
</bpmn2:lane>
</bpmn2:laneSet>
<bpmn2:task id="task_ToMZaQ" imixs:processid="1000" name="Neuanlage">
@ -155,6 +156,7 @@
<bpmn2:outgoing>sequenceFlow_03b60w</bpmn2:outgoing>
<bpmn2:incoming>sequenceFlow_u8PLGw</bpmn2:incoming>
<bpmn2:incoming>sequenceFlow_ZTiO7w</bpmn2:incoming>
<bpmn2:incoming>sequenceFlow_gIdPow</bpmn2:incoming>
</bpmn2:task>
<bpmn2:task id="task_B8Pi7A" imixs:processid="1800" name="Locked">
<bpmn2:extensionElements>
@ -719,7 +721,40 @@
<bpmn2:sequenceFlow id="sequenceFlow_ZTiO7w" sourceRef="event_zzWQSA" targetRef="task_Q80A1Q">
<bpmn2:documentation id="documentation_S2i1Tg"/>
</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:signal id="signal_1" name="com.alexanderlogistics.AGLAnalyticExcelAdapterDebitor"/>
<bpmndi:BPMNDiagram id="BPMNDiagram_1" name="OpenBPMN Diagram">
<bpmndi:BPMNPlane bpmnElement="collaboration_1" id="BPMNPlane_1">
<bpmndi:BPMNShape bpmnElement="event_WmkBvw" id="BPMNShape_heefeQ">
@ -1070,6 +1105,18 @@
<di:waypoint x="714.0" y="465.0"/>
<di:waypoint x="690.0" y="465.0"/>
</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:BPMNDiagram>
</bpmn2:definitions>