neue Code Formatierung

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

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

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

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

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

View file

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

View file

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

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

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

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

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

View file

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

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

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View file

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

View file

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

View file

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

View file

@ -35,30 +35,29 @@ import jakarta.inject.Inject;
* Textblock und fügt die offenen Recnungen ein * Textblock und fügt die offenen Recnungen ein
* *
* Der Adapter wird im Modell wie folgt konfiguriert * Der Adapter wird im Modell wie folgt konfiguriert
* *
* <pre> * <pre>
* {@code * {@code
<soa name="excel-export"> <soa name="excel-export">
<textblock>textblock-ref</textblock> <textblock>textblock-ref</textblock>
<template>filename</template> <template>filename</template>
<targetname>filename</targetname> <targetname>filename</targetname>
</soa> </soa>
} }
* </pre> * </pre>
* *
* <p> * <p>
* Der Adapter erweitert den POIAdapter somit können Felder aktualisiert werden * Der Adapter erweitert den POIAdapter somit können Felder aktualisiert werden
* (siehe POIFineReplaceAdapter). * (siehe POIFineReplaceAdapter).
* *
* <p> * <p>
* Der Adapter kopiert die ausgangsrechnungen in neue Zeilen, welche ab * Der Adapter kopiert die ausgangsrechnungen in neue Zeilen, welche ab
* Zeilennummer * Zeilennummer 16 eingefügt werden.
* 16 eingefügt werden.
* <p> * <p>
* *
* Abschliessend wird die Zelle 'TOTAL' noch aktualisiert. * Abschliessend wird die Zelle 'TOTAL' noch aktualisiert.
* *
* *
* @version 1.0 * @version 1.0
* @author rsoika * @author rsoika
*/ */
@ -80,7 +79,7 @@ public class AGLAnalyticExcelAdapterDebitor extends POIFindReplaceAdapter {
/** /**
* This method * This method
* *
* @throws PluginException * @throws PluginException
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -92,9 +91,7 @@ public class AGLAnalyticExcelAdapterDebitor extends POIFindReplaceAdapter {
List<String> currencyList = invoiceService.getCurrenciesOut(); List<String> currencyList = invoiceService.getCurrenciesOut();
// read the options // read the options
ItemCollection ausgangsrechnungConfig = workflowService.evalWorkflowResult(event, "soa", ItemCollection ausgangsrechnungConfig = workflowService.evalWorkflowResult(event, "soa", document, false);
document,
false);
if (ausgangsrechnungConfig == null || !ausgangsrechnungConfig.hasItem("excel-export")) { if (ausgangsrechnungConfig == null || !ausgangsrechnungConfig.hasItem("excel-export")) {
throw new PluginException(AGLAnalyticExcelAdapterDebitor.class.getSimpleName(), CONFIG_ERROR, throw new PluginException(AGLAnalyticExcelAdapterDebitor.class.getSimpleName(), CONFIG_ERROR,
"missing soa configuration in model event - please check model configuration"); "missing soa configuration in model event - please check model configuration");
@ -129,14 +126,12 @@ public class AGLAnalyticExcelAdapterDebitor extends POIFindReplaceAdapter {
doc.write(byteArrayOutputStream); doc.write(byteArrayOutputStream);
byte[] newContent = byteArrayOutputStream.toByteArray(); byte[] newContent = byteArrayOutputStream.toByteArray();
FileData fileDataNew = new FileData(fileData.getName(), newContent, fileData.getContentType(), FileData fileDataNew = new FileData(fileData.getName(), newContent, fileData.getContentType(), null);
null);
document.addFileData(fileDataNew); document.addFileData(fileDataNew);
logger.finest("......new document added"); logger.finest("......new document added");
} catch (IOException | QueryException e) { } catch (IOException | QueryException e) {
throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), InvoiceService.ERROR_CONFIG,
InvoiceService.ERROR_CONFIG,
"failed to update opliste: " + e.getMessage()); "failed to update opliste: " + e.getMessage());
} finally { } finally {
if (doc != null) { if (doc != null) {
@ -153,8 +148,7 @@ public class AGLAnalyticExcelAdapterDebitor extends POIFindReplaceAdapter {
processPOIUpdate(document, event, targetName); processPOIUpdate(document, event, targetName);
} catch (PluginException e) { } catch (PluginException e) {
throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), InvoiceService.ERROR_CONFIG,
InvoiceService.ERROR_CONFIG,
"failed to update steuerbescheide: " + e.getMessage()); "failed to update steuerbescheide: " + e.getMessage());
} }
logger.info("... completed!"); logger.info("... completed!");
@ -166,7 +160,7 @@ public class AGLAnalyticExcelAdapterDebitor extends POIFindReplaceAdapter {
* the Total and Saldo Colum for each currency * the Total and Saldo Colum for each currency
* *
* The template provided only the columns for the first currency. * The template provided only the columns for the first currency.
* *
* @param sheet * @param sheet
* @param currencyList * @param currencyList
*/ */
@ -201,7 +195,7 @@ public class AGLAnalyticExcelAdapterDebitor extends POIFindReplaceAdapter {
/** /**
* Hilfsmethode die das fuehrende K/D aus der Debitorennummer entfernt * Hilfsmethode die das fuehrende K/D aus der Debitorennummer entfernt
* *
* @return * @return
*/ */
private String getDbtNr(ItemCollection workitem) { private String getDbtNr(ItemCollection workitem) {
@ -213,14 +207,14 @@ public class AGLAnalyticExcelAdapterDebitor extends POIFindReplaceAdapter {
} }
/** /**
* This helper method inserts a row for each invoice at row 16 * This helper method inserts a row for each invoice at row 16 into the excel
* into the excel template file * template file
* <p> * <p>
* The method copies the Row A16 as a reference row * The method copies the Row A16 as a reference row
* <p> * <p>
* The named cell 'TOTAL' should contain the summary formula. It will be * The named cell 'TOTAL' should contain the summary formula. It will be
* evaluated at the end. * evaluated at the end.
* *
* @throws PluginException * @throws PluginException
* @throws QueryException * @throws QueryException
*/ */
@ -249,8 +243,7 @@ public class AGLAnalyticExcelAdapterDebitor extends POIFindReplaceAdapter {
XSSFRow row = sheet.createRow(rowPos); XSSFRow row = sheet.createRow(rowPos);
row.copyRowFrom(referenceRowInvoice, new CellCopyPolicy()); row.copyRowFrom(referenceRowInvoice, new CellCopyPolicy());
// insert values // insert values
row.getCell(0) row.getCell(0).setCellValue(invoice.getItemValueDate("$created"));
.setCellValue(invoice.getItemValueDate("$created"));
row.getCell(1, MissingCellPolicy.CREATE_NULL_AS_BLANK) row.getCell(1, MissingCellPolicy.CREATE_NULL_AS_BLANK)
.setCellValue(invoice.getItemValueString("invoice.number")); .setCellValue(invoice.getItemValueString("invoice.number"));
row.getCell(2, MissingCellPolicy.CREATE_NULL_AS_BLANK) row.getCell(2, MissingCellPolicy.CREATE_NULL_AS_BLANK)
@ -293,7 +286,7 @@ public class AGLAnalyticExcelAdapterDebitor extends POIFindReplaceAdapter {
/** /**
* Findet die Total Spalte für eine Währung * Findet die Total Spalte für eine Währung
* *
* @param currency * @param currency
* @return * @return
*/ */
@ -313,17 +306,15 @@ public class AGLAnalyticExcelAdapterDebitor extends POIFindReplaceAdapter {
/** /**
* This method loads a text-block for a specified ref and appends the named * This method loads a text-block for a specified ref and appends the named
* fileData object of this document. * fileData object of this document.
* *
* @param document * @param document
* @throws PluginException * @throws PluginException
*/ */
private FileData appendExcelTemplate(ItemCollection document, String textblockRef, String template, private FileData appendExcelTemplate(ItemCollection document, String textblockRef, String template,
String targetName) String targetName) throws PluginException {
throws PluginException {
if ((template == null || template.isEmpty()) || (textblockRef == null || textblockRef.isEmpty())) { if ((template == null || template.isEmpty()) || (textblockRef == null || textblockRef.isEmpty())) {
throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), InvoiceService.ERROR_CONFIG,
InvoiceService.ERROR_CONFIG,
"invalid SOA configuration in model event - textblock/template reference not defined!"); "invalid SOA configuration in model event - textblock/template reference not defined!");
} }
@ -332,10 +323,8 @@ public class AGLAnalyticExcelAdapterDebitor extends POIFindReplaceAdapter {
// do we found the document? // do we found the document?
if (fileData == null) { if (fileData == null) {
throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), InvoiceService.ERROR_CONFIG,
InvoiceService.ERROR_CONFIG, "invalid SOA configuration in model event - template=" + template + " not found!");
"invalid SOA configuration in model event - template=" + template
+ " not found!");
} }
fileData.setName(targetName); fileData.setName(targetName);
// append document // append document
@ -346,18 +335,15 @@ public class AGLAnalyticExcelAdapterDebitor extends POIFindReplaceAdapter {
/** /**
* Diese Methode läd alle offenen Steuerbescheide * Diese Methode läd alle offenen Steuerbescheide
* *
* @return * @return
*/ */
private List<ItemCollection> loadInvoices(String dbtrNumber) { private List<ItemCollection> loadInvoices(String dbtrNumber) {
List<ItemCollection> result = new ArrayList<>(); List<ItemCollection> result = new ArrayList<>();
String query = "(type:workitem) AND dbtr.number:" + dbtrNumber String query = "(type:workitem) AND dbtr.number:" + dbtrNumber + " AND $modelversion:rechnungsausgang-*";
+ " AND $modelversion:rechnungsausgang-*";
try { try {
result = documentService.find( result = documentService.find(query, 9999, 0, "invoice.number", false);
query, 9999, 0,
"invoice.number", false);
} catch (QueryException e) { } catch (QueryException e) {
e.printStackTrace(); e.printStackTrace();
} }

View file

@ -1,51 +1,51 @@
/******************************************************************************* /*******************************************************************************
* Imixs Workflow Technology * Imixs Workflow Technology
* Copyright (C) 2003, 2008 Imixs Software Solutions GmbH, * Copyright (C) 2003, 2008 Imixs Software Solutions GmbH,
* http://www.imixs.com * http://www.imixs.com
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2 * as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version. * of the License, or (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details. * General Public License for more details.
* *
* You can receive a copy of the GNU General Public * You can receive a copy of the GNU General Public
* License at http://www.gnu.org/licenses/gpl.html * License at http://www.gnu.org/licenses/gpl.html
* *
* Contributors: * Contributors:
* Imixs Software Solutions GmbH - initial API and implementation * Imixs Software Solutions GmbH - initial API and implementation
* Ralph Soika * Ralph Soika
* *
*******************************************************************************/ *******************************************************************************/
package com.alexanderlogistics; package com.alexanderlogistics;
import org.imixs.workflow.office.config.ConfigController; import org.imixs.workflow.office.config.ConfigController;
import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Named; import jakarta.inject.Named;
/** /**
* The Custom Config Controller for TAXES and Invoice typs * The Custom Config Controller for TAXES and Invoice typs
* *
* *
* @author rsoika * @author rsoika
* @version 1.0 * @version 1.0
*/ */
@Named("aglConfigController") @Named("aglConfigController")
// @RequestScoped // @RequestScoped
@ApplicationScoped @ApplicationScoped
public class AGLConfigController extends ConfigController { public class AGLConfigController extends ConfigController {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
public AGLConfigController() { public AGLConfigController() {
super(); super();
setName("AGL_CONFIGURATION"); setName("AGL_CONFIGURATION");
} }
} }

View file

@ -1,433 +1,427 @@
/******************************************************************************* /*******************************************************************************
* Imixs Workflow Technology * Imixs Workflow Technology
* Copyright (C) 2003, 2008 Imixs Software Solutions GmbH, * Copyright (C) 2003, 2008 Imixs Software Solutions GmbH,
* http://www.imixs.com * http://www.imixs.com
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2 * as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version. * of the License, or (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details. * General Public License for more details.
* *
* You can receive a copy of the GNU General Public * You can receive a copy of the GNU General Public
* License at http://www.gnu.org/licenses/gpl.html * License at http://www.gnu.org/licenses/gpl.html
* *
* Contributors: * Contributors:
* Imixs Software Solutions GmbH - initial API and implementation * Imixs Software Solutions GmbH - initial API and implementation
* Ralph Soika * Ralph Soika
* *
*******************************************************************************/ *******************************************************************************/
package com.alexanderlogistics; package com.alexanderlogistics;
import java.io.IOException; import java.io.IOException;
import java.io.Serializable; import java.io.Serializable;
import java.io.StringWriter; import java.io.StringWriter;
import java.io.Writer; import java.io.Writer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.logging.Logger; import java.util.logging.Logger;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.imixs.workflow.ItemCollection; import org.imixs.workflow.ItemCollection;
import org.imixs.workflow.engine.DocumentService; import org.imixs.workflow.engine.DocumentService;
import org.imixs.workflow.exceptions.AccessDeniedException; import org.imixs.workflow.exceptions.AccessDeniedException;
import org.imixs.workflow.exceptions.QueryException; import org.imixs.workflow.exceptions.QueryException;
import org.imixs.workflow.faces.data.WorkflowController; import org.imixs.workflow.faces.data.WorkflowController;
import org.imixs.workflow.faces.data.WorkflowEvent; import org.imixs.workflow.faces.data.WorkflowEvent;
import jakarta.enterprise.context.ConversationScoped; import jakarta.enterprise.context.ConversationScoped;
import jakarta.enterprise.event.Observes; import jakarta.enterprise.event.Observes;
import jakarta.faces.context.FacesContext; import jakarta.faces.context.FacesContext;
import jakarta.inject.Inject; import jakarta.inject.Inject;
import jakarta.inject.Named; import jakarta.inject.Named;
import jakarta.json.Json; import jakarta.json.Json;
import jakarta.json.JsonObject; import jakarta.json.JsonObject;
import jakarta.json.JsonObjectBuilder; import jakarta.json.JsonObjectBuilder;
/** /**
* Der BusinessPartnerController stellt Methoden für die BusinessPartner Forms * Der BusinessPartnerController stellt Methoden für die BusinessPartner Forms
* sowie für die das Suche-Widget 'businesspartner_search' bereit. * sowie für die das Suche-Widget 'businesspartner_search' bereit.
* *
* *
* @author rsoika * @author rsoika
* @version 1.0 * @version 1.0
*/ */
@Named @Named
@ConversationScoped @ConversationScoped
public class BusinessPartnerController implements Serializable { public class BusinessPartnerController implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
protected List<ItemCollection> ibanList = null; protected List<ItemCollection> ibanList = null;
protected List<ItemCollection> invoicesOut = null; protected List<ItemCollection> invoicesOut = null;
protected List<ItemCollection> invoicesIn = null; protected List<ItemCollection> invoicesIn = null;
protected List<ItemCollection> dunnings = null; protected List<ItemCollection> dunnings = null;
@Inject @Inject
protected WorkflowController workflowController; protected WorkflowController workflowController;
@Inject @Inject
DocumentService documentService; DocumentService documentService;
@Inject @Inject
BusinessPartnerService businessPartnerService; BusinessPartnerService businessPartnerService;
private List<BusinessPartnerSearchEntry> searchResult = null; private List<BusinessPartnerSearchEntry> searchResult = null;
private static Logger logger = Logger.getLogger(BusinessPartnerController.class.getName()); private static Logger logger = Logger.getLogger(BusinessPartnerController.class.getName());
public List<ItemCollection> getInvoicesOut() { public List<ItemCollection> getInvoicesOut() {
if (invoicesOut == null) { if (invoicesOut == null) {
invoicesOut = new ArrayList<>(); invoicesOut = new ArrayList<>();
} }
return invoicesOut; return invoicesOut;
} }
public List<ItemCollection> getInvoicesIn() { public List<ItemCollection> getInvoicesIn() {
if (invoicesIn == null) { if (invoicesIn == null) {
invoicesIn = new ArrayList<>(); invoicesIn = new ArrayList<>();
} }
return invoicesIn; return invoicesIn;
} }
public List<ItemCollection> getDunnings() { public List<ItemCollection> getDunnings() {
if (dunnings == null) { if (dunnings == null) {
dunnings = new ArrayList<>(); dunnings = new ArrayList<>();
} }
return dunnings; return dunnings;
} }
/** /**
* This method searches a text phrase within the list of DATEV kreditoren * This method searches a text phrase within the list of DATEV kreditoren
* <p> * <p>
* JSF Integration: * JSF Integration:
* *
* {@code * {@code
<h:commandScript name="datevSearch" action= <h:commandScript name="datevSearch" action=
* "#{datevSearchController.searchCdtr()}" * "#{datevSearchController.searchCdtr()}" render=
* render="autocomplete-resultlist-datev" onevent="autocompleteShowResult" /> * "autocomplete-resultlist-datev" onevent="autocompleteShowResult" /> }
* } */
*/ public void search(String options) {
public void search(String options) { List<ItemCollection> resultList = null;
List<ItemCollection> resultList = null; searchResult = new ArrayList<BusinessPartnerSearchEntry>();
searchResult = new ArrayList<BusinessPartnerSearchEntry>(); // get the param from faces context....
// get the param from faces context.... FacesContext fc = FacesContext.getCurrentInstance();
FacesContext fc = FacesContext.getCurrentInstance(); String phrase = fc.getExternalContext().getRequestParameterMap().get("phrase");
String phrase = fc.getExternalContext().getRequestParameterMap().get("phrase"); if (phrase == null) {
if (phrase == null) { return;
return; }
}
logger.fine("search prase '" + phrase + "'");
logger.fine("search prase '" + phrase + "'"); if (phrase == null || phrase.length() < 2) {
if (phrase == null || phrase.length() < 2) { return;
return; }
} logger.fine("search for=" + phrase);
logger.fine("search for=" + phrase); Pattern pattern = null;
Pattern pattern = null; resultList = businessPartnerService.search(phrase);
resultList = businessPartnerService.search(phrase); // Compile the regex pattern if available
// Compile the regex pattern if available if (options.contains("regexPattern=")) {
if (options.contains("regexPattern=")) { String regexPattern = options.substring(options.indexOf("regexPattern=") + 13);
String regexPattern = options.substring(options.indexOf("regexPattern=") + 13); logger.fine("regex=" + regexPattern);
logger.fine("regex=" + regexPattern); pattern = Pattern.compile(regexPattern);
pattern = Pattern.compile(regexPattern); }
}
// Filter by Regex and convert ItemCollection into a BusinessPartnerSearchEntry
// Filter by Regex and convert ItemCollection into a BusinessPartnerSearchEntry for (ItemCollection businessPartner : resultList) {
for (ItemCollection businessPartner : resultList) { String name = businessPartner.getItemValueString("name");
String name = businessPartner.getItemValueString("name"); // Prüfen, ob der name der Regex entspricht
// Prüfen, ob der name der Regex entspricht if (pattern != null) {
if (pattern != null) { Matcher matcher = pattern.matcher(name);
Matcher matcher = pattern.matcher(name); if (!matcher.matches()) {
if (!matcher.matches()) { // Kein passendes Konto
// Kein passendes Konto continue;
continue; }
} }
} String display = businessPartner.getItemValueString("$workflowsummary");
String display = businessPartner.getItemValueString("$workflowsummary");
display = display.replace("\"", "");
display = display.replace("\"", ""); display = display.replace("'", "");
display = display.replace("'", "");
searchResult.add(new BusinessPartnerSearchEntry(name, display, buildJsonData(businessPartner)));
searchResult.add(new BusinessPartnerSearchEntry(name, display, buildJsonData(businessPartner)));
}
} }
}
/**
/** * Diese Helper Method liefert true wenn in den Options von Form Part 'reRender'
* Diese Helper Method liefert true wenn in den Options von Form Part 'reRender' * angegeben wurde. Damit löst die Suchmaske businesspartner_search.xhtml eine
* angegeben wurde. * komplette rerender Phase der Form aus. Dies ist z.b. für den workflow
* Damit löst die Suchmaske businesspartner_search.xhtml eine komplette rerender * 'analyse-debitor.bpmn' nützlich
* Phase der Form aus. *
* Dies ist z.b. für den workflow 'analyse-debitor.bpmn' nützlich * @param options
* * @return
* @param options */
* @return public boolean isRerenderMode(String options) {
*/ return options.toLowerCase().contains("rerender");
public boolean isRerenderMode(String options) { }
return options.toLowerCase().contains("rerender");
} /**
* Die Resultliste wird als eine Liste einen Arrays zurückgegeben. Der Erste
/** * Eintrag
* Die Resultliste wird als eine Liste einen Arrays zurückgegeben. Der Erste *
* Eintrag * @return
* */
* @return public List<BusinessPartnerSearchEntry> getSearchResult() {
*/ return searchResult;
public List<BusinessPartnerSearchEntry> getSearchResult() { }
return searchResult;
} public ItemCollection getBusinessPartnerByID(String bpid) {
ItemCollection partner = businessPartnerService.getBusinessPartnerByID(bpid);
public ItemCollection getBusinessPartnerByID(String bpid) { return partner;
ItemCollection partner = businessPartnerService.getBusinessPartnerByID(bpid); }
return partner;
} /**
* WorkflowEvent listener resolved additional data.
/** *
* WorkflowEvent listener resolved additional data. * For businesspartner the method converts the IBAN child list embeded HashMaps
* * into ItemCollections and reconvert them before processing
* For businesspartner the method converts the IBAN child list embeded HashMaps *
* into ItemCollections and reconvert them before processing * for invoices the method resolve the partner.id and partner.name
* *
* for invoices the method resolve the partner.id and partner.name * @param workflowEvent
* * @throws AccessDeniedException
* @param workflowEvent */
* @throws AccessDeniedException public void onWorkflowEvent(@Observes WorkflowEvent workflowEvent) throws AccessDeniedException {
*/
public void onWorkflowEvent(@Observes WorkflowEvent workflowEvent) throws AccessDeniedException { int eventType = workflowEvent.getEventType();
ItemCollection workitem = workflowEvent.getWorkitem();
int eventType = workflowEvent.getEventType(); if (workitem == null) {
ItemCollection workitem = workflowEvent.getWorkitem(); return;
if (workitem == null) { }
return; if (workitem.getModelVersion().startsWith("businesspartner-")) {
} // reset orderItems if workItem has changed
if (workitem.getModelVersion().startsWith("businesspartner-")) { if (WorkflowEvent.WORKITEM_CHANGED == eventType || WorkflowEvent.WORKITEM_CREATED == eventType) {
// reset orderItems if workItem has changed // reset state
if (WorkflowEvent.WORKITEM_CHANGED == eventType || WorkflowEvent.WORKITEM_CREATED == eventType) { ibanList = BusinessPartnerService.explodeBanks(workitem);
// reset state loadStats(workitem);
ibanList = BusinessPartnerService.explodeBanks(workitem); }
loadStats(workitem);
} // before the workitem is saved we update the field txtOrderItems
if (WorkflowEvent.WORKITEM_BEFORE_PROCESS == eventType) {
// before the workitem is saved we update the field txtOrderItems BusinessPartnerService.implodeBanks(workitem, ibanList);
if (WorkflowEvent.WORKITEM_BEFORE_PROCESS == eventType) { }
BusinessPartnerService.implodeBanks(workitem, ibanList);
} if (WorkflowEvent.WORKITEM_AFTER_PROCESS == eventType) {
// reset state
if (WorkflowEvent.WORKITEM_AFTER_PROCESS == eventType) { ibanList = BusinessPartnerService.explodeBanks(workitem);
// reset state }
ibanList = BusinessPartnerService.explodeBanks(workitem); }
}
} if (workitem.getModelVersion().startsWith("rechnungsausgang-")) {
// resolve partner.id and partner.name
if (workitem.getModelVersion().startsWith("rechnungsausgang-")) { if (workitem.getItemValueString("partner.id").isEmpty()) {
// resolve partner.id and partner.name // try to resolve the data
if (workitem.getItemValueString("partner.id").isEmpty()) { ItemCollection bpWorkitem = getBusinessPartnerByID(InvoiceUtil.getBPID(workitem));
// try to resolve the data if (bpWorkitem != null) {
ItemCollection bpWorkitem = getBusinessPartnerByID(InvoiceUtil.getBPID(workitem)); workitem.setItemValue("partner.id", bpWorkitem.getItemValueString("partner.id"));
if (bpWorkitem != null) { workitem.setItemValue("partner.name", bpWorkitem.getItemValueString("partner.name"));
workitem.setItemValue("partner.id", bpWorkitem.getItemValueString("partner.id")); }
workitem.setItemValue("partner.name", bpWorkitem.getItemValueString("partner.name")); }
} }
}
} if (workitem.getModelVersion().startsWith("rechnungseingang-")) {
// resolve partner.id and partner.name
if (workitem.getModelVersion().startsWith("rechnungseingang-")) { if (workitem.getItemValueString("partner.id").isEmpty()) {
// resolve partner.id and partner.name // try to resolve the data
if (workitem.getItemValueString("partner.id").isEmpty()) { ItemCollection bpWorkitem = getBusinessPartnerByID(InvoiceUtil.getBPID(workitem));
// try to resolve the data if (bpWorkitem != null) {
ItemCollection bpWorkitem = getBusinessPartnerByID(InvoiceUtil.getBPID(workitem)); workitem.setItemValue("partner.id", bpWorkitem.getItemValueString("partner.id"));
if (bpWorkitem != null) { workitem.setItemValue("partner.name", bpWorkitem.getItemValueString("partner.name"));
workitem.setItemValue("partner.id", bpWorkitem.getItemValueString("partner.id")); }
workitem.setItemValue("partner.name", bpWorkitem.getItemValueString("partner.name")); }
} }
}
} if (workitem.getModelVersion().startsWith("zahlungseingang-")) {
// resolve partner.id and partner.name
if (workitem.getModelVersion().startsWith("zahlungseingang-")) { if (workitem.getItemValueString("partner.id").isEmpty()) {
// resolve partner.id and partner.name // try to resolve the data
if (workitem.getItemValueString("partner.id").isEmpty()) { ItemCollection bpWorkitem = getBusinessPartnerByID(
// try to resolve the data InvoiceUtil.buildBPID(workitem.getItemValueString("dbtr.number")));
ItemCollection bpWorkitem = getBusinessPartnerByID( if (bpWorkitem != null) {
InvoiceUtil.buildBPID(workitem.getItemValueString("dbtr.number"))); workitem.setItemValue("partner.id", bpWorkitem.getItemValueString("partner.id"));
if (bpWorkitem != null) { workitem.setItemValue("partner.name", bpWorkitem.getItemValueString("partner.name"));
workitem.setItemValue("partner.id", bpWorkitem.getItemValueString("partner.id")); }
workitem.setItemValue("partner.name", bpWorkitem.getItemValueString("partner.name")); }
} }
}
} }
} /**
* Hilfsmethode die die offenen Rechnungen läd. Wird von WorktiemChanged Event
/** * aufgerufen.
* Hilfsmethode die die offenen Rechnungen läd. Wird von WorktiemChanged Event *
* aufgerufen. * @param workitem
* */
* @param workitem private void loadStats(ItemCollection workitem) {
*/
private void loadStats(ItemCollection workitem) { try {
// Ausgangsrechnungen
try { String dbtrNumber = workitem.getItemValueString("dbtr.number");
// Ausgangsrechnungen if (!dbtrNumber.isEmpty()) {
String dbtrNumber = workitem.getItemValueString("dbtr.number"); invoicesOut = businessPartnerService.findOutgoingInvoices(dbtrNumber);
if (!dbtrNumber.isEmpty()) { // Mahnungen
invoicesOut = businessPartnerService.findOutgoingInvoices(dbtrNumber); String query = "(type:workitem) AND ($modelversion:mahnlauf-*) " + " AND (dbtr.number:" + dbtrNumber
// Mahnungen + ")";
String query = "(type:workitem) AND ($modelversion:mahnlauf-*) " dunnings = documentService.findStubs(query, 999, 0, "invoice.number", false);
+ " AND (dbtr.number:" + dbtrNumber + ")"; }
dunnings = documentService.findStubs(query, 999, 0, "invoice.number", false);
} // Eingangsrechnungen
String cdtrNumber = workitem.getItemValueString("cdtr.number");
// Eingangsrechnungen if (!cdtrNumber.isEmpty()) {
String cdtrNumber = workitem.getItemValueString("cdtr.number"); String query = "(type:workitem) AND ($modelversion:rechnungseingang-*) " + " AND (cdtr.number:"
if (!cdtrNumber.isEmpty()) { + cdtrNumber + ")";
String query = "(type:workitem) AND ($modelversion:rechnungseingang-*) " invoicesIn = documentService.findStubs(query, 999, 0, "invoice.number", false);
+ " AND (cdtr.number:" + cdtrNumber + ")"; }
invoicesIn = documentService.findStubs(query, 999, 0, "invoice.number", false);
} } catch (QueryException e) {
logger.severe("failed to load stats: " + e.getMessage());
} catch (QueryException e) { }
logger.severe("failed to load stats: " + e.getMessage());
} }
} public List<ItemCollection> getIbanList() {
return ibanList;
public List<ItemCollection> getIbanList() { }
return ibanList;
} public void addIBAN() {
if (ibanList == null) {
public void addIBAN() { ibanList = new ArrayList<ItemCollection>();
if (ibanList == null) { }
ibanList = new ArrayList<ItemCollection>(); ItemCollection source = new ItemCollection();
} ibanList.add(source);
ItemCollection source = new ItemCollection(); }
ibanList.add(source);
} /**
* Removes an dbtr item from the list
/** *
* Removes an dbtr item from the list * @param name - name of dbtr
* */
* @param name - name of dbtr public void removeIBAN(String id) {
*/ if (id != null && ibanList != null) {
public void removeIBAN(String id) { for (ItemCollection cdtr : ibanList) {
if (id != null && ibanList != null) { if (id.equals(cdtr.getItemValueString("id"))) {
for (ItemCollection cdtr : ibanList) { ibanList.remove(cdtr);
if (id.equals(cdtr.getItemValueString("id"))) { break;
ibanList.remove(cdtr); }
break; }
} }
} }
}
} /**
* This JSF backing method is called by businesspartner_serach.xhtml after the
/** * user has selected a new businesspartner.
* This JSF backing method is called by businesspartner_serach.xhtml after the * <p>
* user has selected a new businesspartner. * The method lookups the given BusinessPartner and updates the metadata for
* <p> * dbtr/cdtr depending on the workflow type. A form will than rerender the
* The method lookups the given BusinessPartner and updates the metadata for * opList section.
* dbtr/cdtr depending on the workflow type. */
* A form will than rerender the opList section. public void updateMetaData() {
*/ FacesContext fc = FacesContext.getCurrentInstance();
public void updateMetaData() { String bpid = fc.getExternalContext().getRequestParameterMap().get("partnerID");
FacesContext fc = FacesContext.getCurrentInstance(); logger.info("....update metadata.....bpid = " + bpid);
String bpid = fc.getExternalContext().getRequestParameterMap().get("partnerID"); ItemCollection businessPartner = businessPartnerService.getBusinessPartnerByID(bpid);
logger.info("....update metadata.....bpid = " + bpid); if (businessPartner != null) {
ItemCollection businessPartner = businessPartnerService.getBusinessPartnerByID(bpid);
if (businessPartner != null) { // Eingangsrechnung
if (InvoiceUtil.isCreditorInvoice(workflowController.getWorkitem())) {
// Eingangsrechnung workflowController.getWorkitem().setItemValue("cdtr.number",
if (InvoiceUtil.isCreditorInvoice(workflowController.getWorkitem())) { businessPartner.getItemValueString("cdtr.number"));
workflowController.getWorkitem().setItemValue("cdtr.number", workflowController.getWorkitem().setItemValue("cdtr.name",
businessPartner.getItemValueString("cdtr.number")); businessPartner.getItemValueString("partner.name"));
workflowController.getWorkitem().setItemValue("cdtr.name", workflowController.getWorkitem().setItemValue("cdtr.mail", businessPartner.getItemValue("cdtr.mail"));
businessPartner.getItemValueString("partner.name")); }
workflowController.getWorkitem().setItemValue("cdtr.mail",
businessPartner.getItemValue("cdtr.mail")); // Ausgangsrechnung
} if (InvoiceUtil.isDebitorInvoice(workflowController.getWorkitem())) {
workflowController.getWorkitem().setItemValue("dbtr.number",
// Ausgangsrechnung businessPartner.getItemValueString("dbtr.number"));
if (InvoiceUtil.isDebitorInvoice(workflowController.getWorkitem())) { workflowController.getWorkitem().setItemValue("dbtr.name",
workflowController.getWorkitem().setItemValue("dbtr.number", businessPartner.getItemValueString("partner.name"));
businessPartner.getItemValueString("dbtr.number")); workflowController.getWorkitem().setItemValue("dbtr.mail", businessPartner.getItemValue("dbtr.mail"));
workflowController.getWorkitem().setItemValue("dbtr.name", }
businessPartner.getItemValueString("partner.name"));
workflowController.getWorkitem().setItemValue("dbtr.mail", // Zahlungseingang
businessPartner.getItemValue("dbtr.mail")); if (workflowController.getWorkitem().getModelVersion().startsWith("zahlungseingang")) {
} workflowController.getWorkitem().setItemValue("dbtr.number",
businessPartner.getItemValueString("dbtr.number"));
// Zahlungseingang workflowController.getWorkitem().setItemValue("dbtr.name",
if (workflowController.getWorkitem().getModelVersion().startsWith("zahlungseingang")) { businessPartner.getItemValueString("partner.name"));
workflowController.getWorkitem().setItemValue("dbtr.number", workflowController.getWorkitem().setItemValue("dbtr.mail", businessPartner.getItemValue("dbtr.mail"));
businessPartner.getItemValueString("dbtr.number")); }
workflowController.getWorkitem().setItemValue("dbtr.name",
businessPartner.getItemValueString("partner.name")); // Analyse Debitor
workflowController.getWorkitem().setItemValue("dbtr.mail", if (workflowController.getWorkitem().getModelVersion().startsWith("analyse-debitor")) {
businessPartner.getItemValue("dbtr.mail")); workflowController.getWorkitem().setItemValue("dbtr.number",
} businessPartner.getItemValueString("dbtr.number"));
workflowController.getWorkitem().setItemValue("dbtr.name",
// Analyse Debitor businessPartner.getItemValueString("partner.name"));
if (workflowController.getWorkitem().getModelVersion().startsWith("analyse-debitor")) { workflowController.getWorkitem().setItemValue("dbtr.mail", businessPartner.getItemValue("dbtr.mail"));
workflowController.getWorkitem().setItemValue("dbtr.number", }
businessPartner.getItemValueString("dbtr.number"));
workflowController.getWorkitem().setItemValue("dbtr.name", }
businessPartner.getItemValueString("partner.name")); searchResult = null;
workflowController.getWorkitem().setItemValue("dbtr.mail",
businessPartner.getItemValue("dbtr.mail")); }
}
/**
} * Hilfsmethode die eine JSON Struktur mit allen relevanten Daten für einen
searchResult = null; * BusinessPartner erzeugt.
*
} * @return
*/
/** private String buildJsonData(ItemCollection businessPartner) {
* Hilfsmethode die eine JSON Struktur mit allen relevanten Daten für einen JsonObjectBuilder objectBuilder = Json.createObjectBuilder(). //
* BusinessPartner erzeugt. add("name", jsonVal(businessPartner.getItemValueString("name"))). //
* add("cdtr.number", jsonVal(businessPartner.getItemValueString("cdtr.number"))). //
* @return add("dbtr.number", jsonVal(businessPartner.getItemValueString("dbtr.number"))). //
*/ add("partner.name", jsonVal(businessPartner.getItemValueString("partner.name")));
private String buildJsonData(ItemCollection businessPartner) {
JsonObjectBuilder objectBuilder = Json.createObjectBuilder(). // // get iban list
add("name", jsonVal(businessPartner.getItemValueString("name"))). // // {iban=[Dxxxxxx1], name=[Bank 1], id=[bank1], bic=[CITIDEFF]}
add("cdtr.number", jsonVal(businessPartner.getItemValueString("cdtr.number"))). // ibanList = BusinessPartnerService.getBanks(businessPartner);
add("dbtr.number", jsonVal(businessPartner.getItemValueString("dbtr.number"))). // int i = 1;
add("partner.name", jsonVal(businessPartner.getItemValueString("partner.name"))); for (ItemCollection iban : ibanList) {
objectBuilder.add("iban" + 1, jsonVal(iban.getItemValueString("iban"))). //
// get iban list add("bic" + 1, jsonVal(iban.getItemValueString("bic")));//
// {iban=[Dxxxxxx1], name=[Bank 1], id=[bank1], bic=[CITIDEFF]} }
ibanList = BusinessPartnerService.getBanks(businessPartner);
int i = 1; JsonObject jsonObject = objectBuilder.build();
for (ItemCollection iban : ibanList) {
objectBuilder.add("iban" + 1, jsonVal(iban.getItemValueString("iban"))). // String jsonString = "{}";
add("bic" + 1, jsonVal(iban.getItemValueString("bic")));// try (Writer writer = new StringWriter()) {
} Json.createWriter(writer).write(jsonObject);
jsonString = writer.toString();
JsonObject jsonObject = objectBuilder.build(); } catch (IOException e) {
logger.warning("Unable to build json structure");
String jsonString = "{}"; }
try (Writer writer = new StringWriter()) { return jsonString;
Json.createWriter(writer).write(jsonObject); }
jsonString = writer.toString();
} catch (IOException e) { /**
logger.warning("Unable to build json structure"); * Helper method to remove " and ' characters - causing problems
} *
return jsonString; * @param val
} * @return
*/
/** public static String jsonVal(String val) {
* Helper method to remove " and ' characters - causing problems val = val.replace("\"", "");
* val = val.replace("'", "");
* @param val return val;
* @return }
*/
public static String jsonVal(String val) { }
val = val.replace("\"", "");
val = val.replace("'", "");
return val;
}
}

View file

@ -5,44 +5,44 @@ package com.alexanderlogistics;
* Sucheregenisses nach kreditoren an das Frontend. Die Klasse besteht aus einem * Sucheregenisses nach kreditoren an das Frontend. Die Klasse besteht aus einem
* Key, einer DisplayZeile für die Darstellung und einen JSON String der * Key, einer DisplayZeile für die Darstellung und einen JSON String der
* beliebige Daten enthalten kann. * beliebige Daten enthalten kann.
* *
* @author rsoika * @author rsoika
* *
*/ */
public class BusinessPartnerSearchEntry { public class BusinessPartnerSearchEntry {
private String key; private String key;
private String display; private String display;
private String data; private String data;
public BusinessPartnerSearchEntry(String key, String display, String data) { public BusinessPartnerSearchEntry(String key, String display, String data) {
super(); super();
this.key = key; this.key = key;
this.display = display; this.display = display;
this.data = data; this.data = data;
} }
public String getKey() { public String getKey() {
return key; return key;
} }
public void setKey(String key) { public void setKey(String key) {
this.key = key; this.key = key;
} }
public String getDisplay() { public String getDisplay() {
return display; return display;
} }
public void setDisplay(String display) { public void setDisplay(String display) {
this.display = display; this.display = display;
} }
public String getData() { public String getData() {
return data; return data;
} }
public void setData(String data) { public void setData(String data) {
this.data = data; this.data = data;
} }
} }

View file

@ -1,26 +1,26 @@
/******************************************************************************* /*******************************************************************************
* Imixs Workflow * Imixs Workflow
* Copyright (C) 2001, 2011 Imixs Software Solutions GmbH, * Copyright (C) 2001, 2011 Imixs Software Solutions GmbH,
* http://www.imixs.com * http://www.imixs.com
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2 * as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version. * of the License, or (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details. * General Public License for more details.
* *
* You can receive a copy of the GNU General Public * You can receive a copy of the GNU General Public
* License at http://www.gnu.org/licenses/gpl.html * License at http://www.gnu.org/licenses/gpl.html
* *
* Project: * Project:
* http://www.imixs.org * http://www.imixs.org
* http://java.net/projects/imixs-workflow * http://java.net/projects/imixs-workflow
* *
* Contributors: * Contributors:
* Imixs Software Solutions GmbH - initial API and implementation * Imixs Software Solutions GmbH - initial API and implementation
* Ralph Soika - Software Developer * Ralph Soika - Software Developer
*******************************************************************************/ *******************************************************************************/
@ -51,15 +51,15 @@ import jakarta.annotation.PostConstruct;
import jakarta.annotation.security.DeclareRoles; import jakarta.annotation.security.DeclareRoles;
import jakarta.annotation.security.RolesAllowed; import jakarta.annotation.security.RolesAllowed;
import jakarta.annotation.security.RunAs; import jakarta.annotation.security.RunAs;
import jakarta.ejb.Singleton; import jakarta.ejb.Stateless;
import jakarta.inject.Inject; import jakarta.inject.Inject;
/** /**
* Der BusinessPartnerService stellt Methoden für den Zugriff auf Business * Der BusinessPartnerService stellt Methoden für den Zugriff auf Business
* Partner bereit. * Partner bereit.
* *
* @author rsoika * @author rsoika
* *
*/ */
@DeclareRoles({ "org.imixs.ACCESSLEVEL.NOACCESS", "org.imixs.ACCESSLEVEL.READERACCESS", @DeclareRoles({ "org.imixs.ACCESSLEVEL.NOACCESS", "org.imixs.ACCESSLEVEL.READERACCESS",
@ -68,7 +68,7 @@ import jakarta.inject.Inject;
@RolesAllowed({ "org.imixs.ACCESSLEVEL.NOACCESS", "org.imixs.ACCESSLEVEL.READERACCESS", @RolesAllowed({ "org.imixs.ACCESSLEVEL.NOACCESS", "org.imixs.ACCESSLEVEL.READERACCESS",
"org.imixs.ACCESSLEVEL.AUTHORACCESS", "org.imixs.ACCESSLEVEL.EDITORACCESS", "org.imixs.ACCESSLEVEL.AUTHORACCESS", "org.imixs.ACCESSLEVEL.EDITORACCESS",
"org.imixs.ACCESSLEVEL.MANAGERACCESS" }) "org.imixs.ACCESSLEVEL.MANAGERACCESS" })
@Singleton @Stateless
@RunAs("org.imixs.ACCESSLEVEL.MANAGERACCESS") @RunAs("org.imixs.ACCESSLEVEL.MANAGERACCESS")
public class BusinessPartnerService { public class BusinessPartnerService {
@ -121,13 +121,12 @@ public class BusinessPartnerService {
/** /**
* Diese Methode aktualisiert das business Partner object. Sie wird von den * Diese Methode aktualisiert das business Partner object. Sie wird von den
* Invoice Plugins genutzt. * Invoice Plugins genutzt. Die Methode wirft eine PluginException falls das
* Die Methode wirft eine PluginException falls das BusinessPartner Objekt * BusinessPartner Objekt gesperrt oder in verification ist!
* gesperrt oder in verification ist! *
*
* Gleichzeitig wird bei Rechnungen in Abhängigkeit der Rechnungsart die * Gleichzeitig wird bei Rechnungen in Abhängigkeit der Rechnungsart die
* dbtr./cdtr. items sowie mail und country aktualisiert. * dbtr./cdtr. items sowie mail und country aktualisiert.
* *
* @param partnerID * @param partnerID
* @param workitem * @param workitem
* @throws PluginException * @throws PluginException
@ -145,8 +144,18 @@ public class BusinessPartnerService {
workitem.setItemValue("partner.id", partnerID); workitem.setItemValue("partner.id", partnerID);
} }
if (partnerID == null || partnerID.isEmpty()) {
return;
}
// load Business Partner // load Business Partner
ItemCollection businessPartner = getBusinessPartnerByID(partnerID); ItemCollection businessPartner = getBusinessPartnerByID(partnerID);
if (businessPartner == null) {
return;
}
// because the method can be called multiple times within one worklfow
// processing cycle
// we need to load the object within the current transaction context
businessPartner = documentService.load(businessPartner.getUniqueID());
if (businessPartner != null) { if (businessPartner != null) {
workitem.setItemValue("partner.name", businessPartner.getItemValueString("partner.name")); workitem.setItemValue("partner.name", businessPartner.getItemValueString("partner.name"));
@ -166,13 +175,11 @@ public class BusinessPartnerService {
// Validate Business Partner Status! // Validate Business Partner Status!
if (businessPartner.getTaskID() == TASK_VERIFICATION) { if (businessPartner.getTaskID() == TASK_VERIFICATION) {
throw new PluginException(InvoicePlugin.class.getName(), throw new PluginException(InvoicePlugin.class.getName(), ERROR_BUSINESSPARTNER_VERIFICATION,
ERROR_BUSINESSPARTNER_VERIFICATION,
"Businesspartner is in verification. Invoice can't be processed!"); "Businesspartner is in verification. Invoice can't be processed!");
} }
if (businessPartner.getTaskID() == TASK_LOCKED) { if (businessPartner.getTaskID() == TASK_LOCKED) {
throw new PluginException(InvoicePlugin.class.getName(), throw new PluginException(InvoicePlugin.class.getName(), ERROR_BUSINESSPARTNER_LOCKED,
ERROR_BUSINESSPARTNER_LOCKED,
"Businesspartner is locked. Invoice can't be processed!"); "Businesspartner is locked. Invoice can't be processed!");
} }
// update business partner status... // update business partner status...
@ -195,7 +202,7 @@ public class BusinessPartnerService {
/** /**
* Returns the stubs of all outgoing invoices * Returns the stubs of all outgoing invoices
* *
* @param _dbtrNumber * @param _dbtrNumber
* @return * @return
*/ */
@ -224,9 +231,8 @@ public class BusinessPartnerService {
/** /**
* Diese Methode sucht einen Business Partner anhand seiner BPID * Diese Methode sucht einen Business Partner anhand seiner BPID
* *
* @param bpid * @param bpid - business partner id phrase
* - business partner id phrase
* @return - matching business partner * @return - matching business partner
*/ */
public ItemCollection getBusinessPartnerByID(String bpid) { public ItemCollection getBusinessPartnerByID(String bpid) {
@ -250,9 +256,8 @@ public class BusinessPartnerService {
/** /**
* Diese Methode sucht Business Partner anhand einer Suchphrase * Diese Methode sucht Business Partner anhand einer Suchphrase
* *
* @param phrase * @param phrase - search phrase
* - search phrase
* @return - list of matching business partners * @return - list of matching business partners
*/ */
public List<ItemCollection> search(String phrase) { public List<ItemCollection> search(String phrase) {
@ -283,7 +288,7 @@ public class BusinessPartnerService {
/** /**
* Packt die Liste der Bank Details (ItemCollections) in ein Workitem * Packt die Liste der Bank Details (ItemCollections) in ein Workitem
* *
* @param workitem * @param workitem
*/ */
public static void implodeBanks(ItemCollection workitem, List<ItemCollection> ibanList) { public static void implodeBanks(ItemCollection workitem, List<ItemCollection> ibanList) {
@ -304,7 +309,7 @@ public class BusinessPartnerService {
/** /**
* Enpackt die iban Map Liste von eiem Worktiem in eine Liste von ItemCollection * Enpackt die iban Map Liste von eiem Worktiem in eine Liste von ItemCollection
* *
* @param workitem * @param workitem
*/ */
public static List<ItemCollection> explodeBanks(ItemCollection workitem) { public static List<ItemCollection> explodeBanks(ItemCollection workitem) {
@ -325,7 +330,7 @@ public class BusinessPartnerService {
/** /**
* Liefert die ChildItems mit den IBAN daten * Liefert die ChildItems mit den IBAN daten
* *
* @param workitem * @param workitem
* @return * @return
*/ */
@ -345,7 +350,7 @@ public class BusinessPartnerService {
/** /**
* Diese Methode übertragt neue IBAN/BIC Pare in den Kreditor Banken Bereich * Diese Methode übertragt neue IBAN/BIC Pare in den Kreditor Banken Bereich
* *
* @param cdtrNumber * @param cdtrNumber
* @param iban * @param iban
* @param bic * @param bic
@ -374,7 +379,7 @@ public class BusinessPartnerService {
/** /**
* Diese Methode gibt true zurück wenn die angegebene IBAN/BIC Kombination als * Diese Methode gibt true zurück wenn die angegebene IBAN/BIC Kombination als
* bank bereits bekannt ist * bank bereits bekannt ist
* *
* @param cdtrNumber * @param cdtrNumber
* @param iban * @param iban
* @param bic * @param bic
@ -386,8 +391,7 @@ public class BusinessPartnerService {
List<ItemCollection> bankenListe = explodeBanks(businessPartner); List<ItemCollection> bankenListe = explodeBanks(businessPartner);
for (ItemCollection bank : bankenListe) { for (ItemCollection bank : bankenListe) {
if (iban.equals(bank.getItemValueString("iban")) if (iban.equals(bank.getItemValueString("iban")) && bic.equals(bank.getItemValueString("bic"))) {
&& bic.equals(bank.getItemValueString("bic"))) {
// known! // known!
return true; return true;
} }

View file

@ -25,18 +25,18 @@ import jakarta.inject.Inject;
* <p> * <p>
* report = report definition to transfers the workitem into the cargosoft xml * report = report definition to transfers the workitem into the cargosoft xml
* structure * structure
* *
* <pre> * <pre>
* {@code * {@code
<cargosoft name="report">cargosoft</cargosoft> <cargosoft name="report">cargosoft</cargosoft>
} }
* </pre> * </pre>
* <p> * <p>
* Because we also export the attachment data to cargosoft, the adaper lookups * Because we also export the attachment data to cargosoft, the adaper lookups
* the conente of the attachment in the snapshot of the origin workitem * the conente of the attachment in the snapshot of the origin workitem
* *
* *
* @version 1.0 * @version 1.0
* @author rsoika * @author rsoika
*/ */
@ -113,8 +113,8 @@ public class CargosoftExportAdapter implements SignalAdapter {
// finally append the success event id // finally append the success event id
document.event(EVENT_SUCCESS); document.event(EVENT_SUCCESS);
} catch (PluginException | IOException } catch (PluginException | IOException | jakarta.xml.bind.JAXBException
| jakarta.xml.bind.JAXBException | javax.xml.transform.TransformerException e) { | javax.xml.transform.TransformerException e) {
logger.severe("cargosoft export failed: " + e.getMessage()); logger.severe("cargosoft export failed: " + e.getMessage());
document.setItemValue("cargosoft.error", e.getMessage()); document.setItemValue("cargosoft.error", e.getMessage());
document.event(EVENT_FAILURE); document.event(EVENT_FAILURE);
@ -130,8 +130,8 @@ public class CargosoftExportAdapter implements SignalAdapter {
* Change 14.10.2021 - Wir senden maximal 5MB and Daten, da die Cargosoft * Change 14.10.2021 - Wir senden maximal 5MB and Daten, da die Cargosoft
* Schnittstelle größere Datensätze mit großen Dateianhängen einfach verschluckt * Schnittstelle größere Datensätze mit großen Dateianhängen einfach verschluckt
* ohne eine Fehlermeldugn zu liefern. * ohne eine Fehlermeldugn zu liefern.
* *
* *
* @param document * @param document
* @return * @return
*/ */

View file

@ -32,7 +32,7 @@ import jakarta.inject.Inject;
* ein neue Sequencenummer die im Hautpworkitem gespeichert wird. * ein neue Sequencenummer die im Hautpworkitem gespeichert wird.
* <p> * <p>
* Der Adapter wird im Modell wie folgt konfiguriert * Der Adapter wird im Modell wie folgt konfiguriert
* *
* <pre> * <pre>
* {@code * {@code
<cargosoft> <cargosoft>
@ -41,14 +41,14 @@ import jakarta.inject.Inject;
<event>100</event> <event>100</event>
<items>(?!txtworkflowhistory)(^[a-zA-Z]|^_)</items> <items>(?!txtworkflowhistory)(^[a-zA-Z]|^_)</items>
</cargosoft> </cargosoft>
} }
* </pre> * </pre>
* <p> * <p>
* Because we also export the attachment data to cargosoft, the adaper lookups * Because we also export the attachment data to cargosoft, the adaper lookups
* the conente of the attachment in the snapshot of the origin workitem * the conente of the attachment in the snapshot of the origin workitem
* *
* *
* @version 1.0 * @version 1.0
* @author rsoika * @author rsoika
*/ */
@ -65,7 +65,7 @@ public class CargosoftSplitAdapter implements SignalAdapter {
/** /**
* This method computes the cargosoft export data file * This method computes the cargosoft export data file
* *
* @throws PluginException * @throws PluginException
*/ */
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
@ -189,21 +189,21 @@ public class CargosoftSplitAdapter implements SignalAdapter {
* <p> * <p>
* Example: {@code * Example: {@code
* txttitle,txtfirstname * txttitle,txtfirstname
* *
* txttitle|newitem1,txtfirstname|newitem2 * txttitle|newitem1,txtfirstname|newitem2
* *
* } * }
* *
* <p> * <p>
* Optional also reg expressions are supported. In this case mapping of the item * Optional also reg expressions are supported. In this case mapping of the item
* name is not supported. * name is not supported.
* <p> * <p>
* Example: {@code * Example: {@code
* (^artikel$|^invoice$),txtTitel|txtNewTitel * (^artikel$|^invoice$),txtTitel|txtNewTitel
* *
* *
* } A reg expression must be includes in brackets. * } A reg expression must be includes in brackets.
* *
*/ */
protected void copyItemList(String items, ItemCollection source, ItemCollection target) { protected void copyItemList(String items, ItemCollection source, ItemCollection target) {
// clone the field list... // clone the field list...

View file

@ -1,285 +1,283 @@
/******************************************************************************* /*******************************************************************************
* Imixs Workflow Technology * Imixs Workflow Technology
* Copyright (C) 2003, 2008 Imixs Software Solutions GmbH, * Copyright (C) 2003, 2008 Imixs Software Solutions GmbH,
* http://www.imixs.com * http://www.imixs.com
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2 * as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version. * of the License, or (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details. * General Public License for more details.
* *
* You can receive a copy of the GNU General Public * You can receive a copy of the GNU General Public
* License at http://www.gnu.org/licenses/gpl.html * License at http://www.gnu.org/licenses/gpl.html
* *
* Contributors: * Contributors:
* Imixs Software Solutions GmbH - initial API and implementation * Imixs Software Solutions GmbH - initial API and implementation
* Ralph Soika * Ralph Soika
* *
*******************************************************************************/ *******************************************************************************/
package com.alexanderlogistics; package com.alexanderlogistics;
import java.io.Serializable; import java.io.Serializable;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.imixs.marty.team.TeamController; import org.imixs.marty.team.TeamController;
import org.imixs.workflow.ItemCollection; import org.imixs.workflow.ItemCollection;
import org.imixs.workflow.office.views.SearchController; import org.imixs.workflow.office.views.SearchController;
import org.imixs.workflow.office.views.SearchEvent; import org.imixs.workflow.office.views.SearchEvent;
import jakarta.enterprise.context.SessionScoped; import jakarta.enterprise.context.SessionScoped;
import jakarta.enterprise.event.Observes; import jakarta.enterprise.event.Observes;
import jakarta.inject.Inject; import jakarta.inject.Inject;
import jakarta.inject.Named; import jakarta.inject.Named;
/** /**
* Der CustomSearchController reagiert auf SeachEvents und erweitert das Search * Der CustomSearchController reagiert auf SeachEvents und erweitert das Search
* Query um Rechnungbetrag von-bis. * Query um Rechnungbetrag von-bis.
* *
* @author rsoika * @author rsoika
* @version 1.0 * @version 1.0
*/ */
@Named @Named
@SessionScoped @SessionScoped
// @RequestScoped // @RequestScoped
public class CustomSearchController implements Serializable { public class CustomSearchController implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private static Logger logger = Logger.getLogger(CustomSearchController.class.getName()); private static Logger logger = Logger.getLogger(CustomSearchController.class.getName());
@Inject @Inject
SearchController searchController; SearchController searchController;
@Inject @Inject
TeamController teamController; TeamController teamController;
boolean isArchiveSearch = false; boolean isArchiveSearch = false;
public void init() { public void init() {
isArchiveSearch = true; isArchiveSearch = true;
} }
/** /**
* Diese Suche schränkt immer auf Archive und Rechnugnscontrolling ein!! * Diese Suche schränkt immer auf Archive und Rechnugnscontrolling ein!!
* *
* @param searchEvent * @param searchEvent
*/ */
public void onSearchEvent(@Observes SearchEvent searchEvent) { public void onSearchEvent(@Observes SearchEvent searchEvent) {
if (!isArchiveSearch) { if (!isArchiveSearch) {
// Keine Anpassung für andere Prozesse // Keine Anpassung für andere Prozesse
return; return;
} }
String query = searchEvent.getQuery(); String query = searchEvent.getQuery();
float invoiceTotal = searchEvent.getSearchFilter().getItemValueFloat("invoice.total"); float invoiceTotal = searchEvent.getSearchFilter().getItemValueFloat("invoice.total");
Date invoiceDate = searchEvent.getSearchFilter().getItemValueDate("invoice.date"); Date invoiceDate = searchEvent.getSearchFilter().getItemValueDate("invoice.date");
Date dueDate = searchEvent.getSearchFilter().getItemValueDate("invoice.duedate"); Date dueDate = searchEvent.getSearchFilter().getItemValueDate("invoice.duedate");
Date closingDate = searchEvent.getSearchFilter().getItemValueDate("invoice.closingdate"); Date closingDate = searchEvent.getSearchFilter().getItemValueDate("invoice.closingdate");
String cdtrNumber = searchEvent.getSearchFilter().getItemValueString("cdtr.number").trim(); String cdtrNumber = searchEvent.getSearchFilter().getItemValueString("cdtr.number").trim();
String invoiceType = searchEvent.getSearchFilter().getItemValueString("invoice.type"); String invoiceType = searchEvent.getSearchFilter().getItemValueString("invoice.type");
String invoiceCurrency = searchEvent.getSearchFilter().getItemValueString("invoice.currency").trim(); String invoiceCurrency = searchEvent.getSearchFilter().getItemValueString("invoice.currency").trim();
String invoiceNumber = searchEvent.getSearchFilter().getItemValueString("invoice.number"); String invoiceNumber = searchEvent.getSearchFilter().getItemValueString("invoice.number");
String paymentType = searchEvent.getSearchFilter().getItemValueString("payment.type").trim(); String paymentType = searchEvent.getSearchFilter().getItemValueString("payment.type").trim();
String sequenceNumber = searchEvent.getSearchFilter().getItemValueString("sequencenumber").trim(); String sequenceNumber = searchEvent.getSearchFilter().getItemValueString("sequencenumber").trim();
query += " (type:\"workitemarchive\") "; query += " (type:\"workitemarchive\") ";
// process ref= Rechnugnscontorling Prozess // process ref= Rechnugnscontorling Prozess
ItemCollection process = teamController.getProcessByName("Rechnungscontrolling"); ItemCollection process = teamController.getProcessByName("Rechnungscontrolling");
if (process == null) { if (process == null) {
process = teamController.getProcessByName("Invoice Controlling"); process = teamController.getProcessByName("Invoice Controlling");
} }
if (process != null) { if (process != null) {
query += " AND ($uniqueidref:\"" + process.getUniqueID() + "\") "; query += " AND ($uniqueidref:\"" + process.getUniqueID() + "\") ";
} }
// invoice.total // invoice.total
if (invoiceTotal != 0) { if (invoiceTotal != 0) {
// format form and to with at least one digit // format form and to with at least one digit
DecimalFormat decimalFormat = new DecimalFormat("#.00"); DecimalFormat decimalFormat = new DecimalFormat("#.00");
query += " AND (invoice.total:" + decimalFormat.format(invoiceTotal) + ")"; query += " AND (invoice.total:" + decimalFormat.format(invoiceTotal) + ")";
} }
// invoice.date // invoice.date
if (invoiceDate != null) { if (invoiceDate != null) {
query += " AND (invoice.date:" + CustomSearchController.getRangeDay(invoiceDate) + ")"; query += " AND (invoice.date:" + CustomSearchController.getRangeDay(invoiceDate) + ")";
} }
// invoice.duedate // invoice.duedate
if (dueDate != null) { if (dueDate != null) {
query += " AND (invoice.duedate:" + CustomSearchController.getRangeDay(dueDate) + ")"; query += " AND (invoice.duedate:" + CustomSearchController.getRangeDay(dueDate) + ")";
} }
// invoice.closingdate // invoice.closingdate
if (closingDate != null) { if (closingDate != null) {
query += " AND ($lasteventdate:" + CustomSearchController.getRangeMonth(closingDate) + ")"; query += " AND ($lasteventdate:" + CustomSearchController.getRangeMonth(closingDate) + ")";
} }
if (invoiceNumber != null && !invoiceNumber.isEmpty()) { if (invoiceNumber != null && !invoiceNumber.isEmpty()) {
query += " AND (invoice.number:" + invoiceNumber + ")"; query += " AND (invoice.number:" + invoiceNumber + ")";
} }
if (paymentType != null && !paymentType.isEmpty()) { if (paymentType != null && !paymentType.isEmpty()) {
query += " AND (payment.type:" + paymentType + ")"; query += " AND (payment.type:" + paymentType + ")";
} }
if (sequenceNumber != null && !sequenceNumber.isEmpty()) { if (sequenceNumber != null && !sequenceNumber.isEmpty()) {
query += " AND (sequencenumber:" + sequenceNumber + ")"; query += " AND (sequencenumber:" + sequenceNumber + ")";
} }
if (invoiceCurrency != null && !invoiceCurrency.isEmpty()) { if (invoiceCurrency != null && !invoiceCurrency.isEmpty()) {
query += " AND (invoice.currency:" + invoiceCurrency + ")"; query += " AND (invoice.currency:" + invoiceCurrency + ")";
} }
// Rechnungsart // Rechnungsart
if (invoiceType != null && !"0".equals(invoiceType)) { if (invoiceType != null && !"0".equals(invoiceType)) {
if ("1".equals(invoiceType)) { if ("1".equals(invoiceType)) {
query += " AND ($workflowgroup:\"Rechnungseingang\" AND NOT payment.type:credit)"; query += " AND ($workflowgroup:\"Rechnungseingang\" AND NOT payment.type:credit)";
} }
if ("2".equals(invoiceType)) { if ("2".equals(invoiceType)) {
query += " AND ($workflowgroup:Sachrechnung)"; query += " AND ($workflowgroup:Sachrechnung)";
} }
if ("3".equals(invoiceType)) { if ("3".equals(invoiceType)) {
query += " AND (($workflowgroup:Rechnungseingang) AND (payment.type:credit))"; query += " AND (($workflowgroup:Rechnungseingang) AND (payment.type:credit))";
} }
if ("4".equals(invoiceType)) { if ("4".equals(invoiceType)) {
query += " AND ($workflowgroup:\"Gutschrift Abgleich\")"; query += " AND ($workflowgroup:\"Gutschrift Abgleich\")";
} }
} }
// dbtr.number // dbtr.number
if (cdtrNumber != null && !cdtrNumber.isEmpty()) { if (cdtrNumber != null && !cdtrNumber.isEmpty()) {
cdtrNumber = cdtrNumber.trim().toUpperCase(); cdtrNumber = cdtrNumber.trim().toUpperCase();
if (!cdtrNumber.startsWith("K") && !cdtrNumber.startsWith("D")) { if (!cdtrNumber.startsWith("K") && !cdtrNumber.startsWith("D")) {
query += " AND (cdtr.number:K" + cdtrNumber + " OR dbtr.number:" + cdtrNumber + ")"; query += " AND (cdtr.number:K" + cdtrNumber + " OR dbtr.number:" + cdtrNumber + ")";
} else { } else {
if (cdtrNumber.startsWith("K")) { if (cdtrNumber.startsWith("K")) {
// suche nach Kreditor (Eingangsrechnung) // suche nach Kreditor (Eingangsrechnung)
query += " AND (cdtr.number:" + cdtrNumber + ")"; query += " AND (cdtr.number:" + cdtrNumber + ")";
} else { } else {
// suche nach Debitor (Ausgangsrechnung) // suche nach Debitor (Ausgangsrechnung)
if (cdtrNumber.startsWith("D")) { if (cdtrNumber.startsWith("D")) {
cdtrNumber = cdtrNumber.substring(1); cdtrNumber = cdtrNumber.substring(1);
} }
query += " AND (dbtr.number:" + cdtrNumber + ")"; query += " AND (dbtr.number:" + cdtrNumber + ")";
} }
} }
} }
logger.fine("Custom Query=" + query); logger.fine("Custom Query=" + query);
// Udpate query // Udpate query
searchEvent.setQuery(query); searchEvent.setQuery(query);
} }
/** /**
* Sonder AGL Lösung um im Archiv zu bleiben. * Sonder AGL Lösung um im Archiv zu bleiben.
* *
*/ */
public String refreshAGLArchive() { public String refreshAGLArchive() {
String phrase = searchController.getSearchFilter().getItemValueString("phrase"); String phrase = searchController.getSearchFilter().getItemValueString("phrase");
searchController.refreshSearch(); searchController.refreshSearch();
searchController.getSearchFilter().setItemValue("type", "workitemarchive"); searchController.getSearchFilter().setItemValue("type", "workitemarchive");
String action = "/pages/workitems/agl_archive.xhtml?faces-redirect=true&phrase=" + phrase; String action = "/pages/workitems/agl_archive.xhtml?faces-redirect=true&phrase=" + phrase;
return action; return action;
// String phrase = // String phrase =
// searchController.getSearchFilter().getItemValueString("phrase"); // searchController.getSearchFilter().getItemValueString("phrase");
// searchController.getSearchFilter().setItemValue("type", "workitemarchive"); // searchController.getSearchFilter().setItemValue("type", "workitemarchive");
// try { // try {
// phrase = URLEncoder.encode(phrase, "UTF-8"); // phrase = URLEncoder.encode(phrase, "UTF-8");
// } catch (UnsupportedEncodingException e) { // } catch (UnsupportedEncodingException e) {
// logger.severe("unable to encode search phrase!"); // logger.severe("unable to encode search phrase!");
// e.printStackTrace(); // e.printStackTrace();
// } // }
// String action = // String action =
// "/pages/workitems/agl_archive.xhtml?faces-redirect=true&phrase=" + phrase; // "/pages/workitems/agl_archive.xhtml?faces-redirect=true&phrase=" + phrase;
// searchController.setPageIndex(0); // searchController.setPageIndex(0);
// return action; // return action;
} }
/** /**
* This method reset the search filter and the search pageIndex and creates a * This method reset the search filter and the search pageIndex and creates a
* new bookmarkable search link to the worklist including the current search * new bookmarkable search link to the worklist including the current search
* phrase. * phrase.
* *
*/ */
public String resetSearch() { public String resetSearch() {
searchController.reset(); searchController.reset();
return refreshAGLArchive(); return refreshAGLArchive();
} }
private static String getRangeDay(Date dueDate) { private static String getRangeDay(Date dueDate) {
// Setze die Uhrzeit auf Mitternacht (00:00) // Setze die Uhrzeit auf Mitternacht (00:00)
Calendar startCalendar = Calendar.getInstance(); Calendar startCalendar = Calendar.getInstance();
startCalendar.setTime(dueDate); startCalendar.setTime(dueDate);
startCalendar.set(Calendar.HOUR_OF_DAY, 0); startCalendar.set(Calendar.HOUR_OF_DAY, 0);
startCalendar.set(Calendar.MINUTE, 0); startCalendar.set(Calendar.MINUTE, 0);
startCalendar.set(Calendar.SECOND, 0); startCalendar.set(Calendar.SECOND, 0);
startCalendar.set(Calendar.MILLISECOND, 0); startCalendar.set(Calendar.MILLISECOND, 0);
Date startDate = startCalendar.getTime(); Date startDate = startCalendar.getTime();
// Setze die Uhrzeit auf 23:59:59.999 // Setze die Uhrzeit auf 23:59:59.999
Calendar endCalendar = Calendar.getInstance(); Calendar endCalendar = Calendar.getInstance();
endCalendar.setTime(dueDate); endCalendar.setTime(dueDate);
endCalendar.set(Calendar.HOUR_OF_DAY, 23); endCalendar.set(Calendar.HOUR_OF_DAY, 23);
endCalendar.set(Calendar.MINUTE, 59); endCalendar.set(Calendar.MINUTE, 59);
endCalendar.set(Calendar.SECOND, 59); endCalendar.set(Calendar.SECOND, 59);
endCalendar.set(Calendar.MILLISECOND, 999); endCalendar.set(Calendar.MILLISECOND, 999);
Date endDate = endCalendar.getTime(); Date endDate = endCalendar.getTime();
SimpleDateFormat dateformat = new SimpleDateFormat("yyyyMMddHHmm"); SimpleDateFormat dateformat = new SimpleDateFormat("yyyyMMddHHmm");
String dateRange = "[" + dateformat.format(startDate) + " TO " String dateRange = "[" + dateformat.format(startDate) + " TO " + dateformat.format(endDate) + "]";
+ dateformat.format(endDate) + "]";
return dateRange;
return dateRange; }
}
private static String getRangeMonth(Date dueDate) {
private static String getRangeMonth(Date dueDate) { // Setze die Uhrzeit auf Mitternacht (00:00)
// Setze die Uhrzeit auf Mitternacht (00:00) Calendar startCalendar = Calendar.getInstance();
Calendar startCalendar = Calendar.getInstance(); startCalendar.setTime(dueDate);
startCalendar.setTime(dueDate); startCalendar.set(Calendar.HOUR_OF_DAY, 0);
startCalendar.set(Calendar.HOUR_OF_DAY, 0); startCalendar.set(Calendar.MINUTE, 0);
startCalendar.set(Calendar.MINUTE, 0); startCalendar.set(Calendar.SECOND, 0);
startCalendar.set(Calendar.SECOND, 0); startCalendar.set(Calendar.MILLISECOND, 0);
startCalendar.set(Calendar.MILLISECOND, 0); startCalendar.set(Calendar.DAY_OF_MONTH, 1);
startCalendar.set(Calendar.DAY_OF_MONTH, 1);
Date startDate = startCalendar.getTime();
Date startDate = startCalendar.getTime();
// Setze die Uhrzeit auf 23:59:59.999
// Setze die Uhrzeit auf 23:59:59.999 Calendar endCalendar = Calendar.getInstance();
Calendar endCalendar = Calendar.getInstance(); endCalendar.setTime(dueDate);
endCalendar.setTime(dueDate); endCalendar.set(Calendar.HOUR_OF_DAY, 23);
endCalendar.set(Calendar.HOUR_OF_DAY, 23); endCalendar.set(Calendar.MINUTE, 59);
endCalendar.set(Calendar.MINUTE, 59); endCalendar.set(Calendar.SECOND, 59);
endCalendar.set(Calendar.SECOND, 59); endCalendar.set(Calendar.MILLISECOND, 999);
endCalendar.set(Calendar.MILLISECOND, 999); endCalendar.set(Calendar.DAY_OF_MONTH, 1);
endCalendar.set(Calendar.DAY_OF_MONTH, 1); endCalendar.add(Calendar.MONTH, 1);
endCalendar.add(Calendar.MONTH, 1); endCalendar.add(Calendar.DAY_OF_MONTH, -1);
endCalendar.add(Calendar.DAY_OF_MONTH, -1);
Date endDate = endCalendar.getTime();
Date endDate = endCalendar.getTime(); SimpleDateFormat dateformat = new SimpleDateFormat("yyyyMMddHHmm");
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;
}
return dateRange; }
}
}

View file

@ -40,7 +40,7 @@ public class DebitorStatistikData {
/** /**
* Aktualisiert die statistischen werte * Aktualisiert die statistischen werte
* *
* @param invoiceDate * @param invoiceDate
* @param paymentDate * @param paymentDate
*/ */
@ -90,7 +90,7 @@ public class DebitorStatistikData {
/** /**
* returns * returns
* *
* 202304 * 202304
*/ */
public String toString() { public String toString() {

View file

@ -1,22 +1,22 @@
/******************************************************************************* /*******************************************************************************
* Imixs Workflow Technology * Imixs Workflow Technology
* Copyright (C) 2001, 2008 Imixs Software Solutions GmbH, * Copyright (C) 2001, 2008 Imixs Software Solutions GmbH,
* http://www.imixs.com * http://www.imixs.com
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2 * as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version. * of the License, or (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details. * General Public License for more details.
* *
* You can receive a copy of the GNU General Public * You can receive a copy of the GNU General Public
* License at http://www.gnu.org/licenses/gpl.html * License at http://www.gnu.org/licenses/gpl.html
* *
* Contributors: * Contributors:
* Imixs Software Solutions GmbH - initial API and implementation * Imixs Software Solutions GmbH - initial API and implementation
* Ralph Soika * Ralph Soika
*******************************************************************************/ *******************************************************************************/
@ -42,7 +42,7 @@ import jakarta.inject.Inject;
/** /**
* The FTPConnector service provides methods to push invoices to cargosoft * The FTPConnector service provides methods to push invoices to cargosoft
* *
* @version 1.0 * @version 1.0
* @author rsoika * @author rsoika
*/ */
@ -83,7 +83,7 @@ public class FTPConnector {
/** /**
* This method transfers a snapshot to a ftp server. * This method transfers a snapshot to a ftp server.
* *
* @param fileData object * @param fileData object
* @throws PluginException * @throws PluginException
*/ */
@ -158,7 +158,7 @@ public class FTPConnector {
/** /**
* This method reads data form the current working directory * This method reads data form the current working directory
* *
* @param snapshot * @param snapshot
* @throws ArchiveException * @throws ArchiveException
* @return data * @return data
@ -198,7 +198,7 @@ public class FTPConnector {
/** /**
* This method changes the current working sub-directy. If no corresponding * This method changes the current working sub-directy. If no corresponding
* directory exits the method creats one. * directory exits the method creats one.
* *
* @throws ArchiveException * @throws ArchiveException
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")

View file

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

View file

@ -16,7 +16,7 @@ import jakarta.inject.Inject;
/** /**
* Der InvoiceDispatchAdapter verteilt die vom Abteilungsleiter ausgewählten * Der InvoiceDispatchAdapter verteilt die vom Abteilungsleiter ausgewählten
* Rechnungen * Rechnungen
* *
* @version 1.0 * @version 1.0
* @author rsoika * @author rsoika
*/ */
@ -29,7 +29,7 @@ public class InvoiceDispatchAdapter implements SignalAdapter {
/** /**
* This method computes the cargosoft export data file * This method computes the cargosoft export data file
* *
* @throws PluginException * @throws PluginException
*/ */
@Override @Override

View file

@ -16,7 +16,7 @@ import jakarta.inject.Named;
/** /**
* The InvoiceDispatchController loads open workitems for the current user * The InvoiceDispatchController loads open workitems for the current user
* *
* @author rsoika * @author rsoika
* *
*/ */
@ -50,7 +50,7 @@ public class InvoiceDispatchController implements Serializable {
* ($modelversion:rechnungseingang-de*) AND ($taskid:5100) AND * ($modelversion:rechnungseingang-de*) AND ($taskid:5100) AND
* ($owner:#{user})"></ui:param> <ui:param name="invoiceList" value= * ($owner:#{user})"></ui:param> <ui:param name="invoiceList" value=
* "#{workflowController.documentService.find(query,30,0)}"></ui:param> * "#{workflowController.documentService.find(query,30,0)}"></ui:param>
* *
*/ */
public void searchInvoices() { public void searchInvoices() {
long l = System.currentTimeMillis(); long l = System.currentTimeMillis();

View file

@ -8,40 +8,40 @@ import jakarta.inject.Inject;
/** /**
* Das InvoiceOutgoingPlugin aktualisiert die Debitor E-Mail Adresse * Das InvoiceOutgoingPlugin aktualisiert die Debitor E-Mail Adresse
* *
* Auch das feld _img wird in Abhängigkeit vom item 'invoice.protest' gesetzt * Auch das feld _img wird in Abhängigkeit vom item 'invoice.protest' gesetzt
* *
* @author rsoika * @author rsoika
* @version 1.0 * @version 1.0
* *
*/ */
public class InvoiceOutgoingPlugin extends AbstractPlugin { public class InvoiceOutgoingPlugin extends AbstractPlugin {
@Inject @Inject
BusinessPartnerService businessPartnerService; BusinessPartnerService businessPartnerService;
/** /**
* *
* @throws PluginException - if data is missing * @throws PluginException - if data is missing
* *
**/ **/
@Override @Override
public ItemCollection run(ItemCollection workitem, ItemCollection event) throws PluginException { public ItemCollection run(ItemCollection workitem, ItemCollection event) throws PluginException {
// Update BUsiness Partner Data // Update BUsiness Partner Data
businessPartnerService.updateBusinessPartnerData(workitem); businessPartnerService.updateBusinessPartnerData(workitem);
// Update Invoice.positions // Update Invoice.positions
InvoiceUtil.updateInvoicePositions(workitem); InvoiceUtil.updateInvoicePositions(workitem);
String img = ""; String img = "";
if (workitem.getItemValueBoolean("invoice.protest")) { if (workitem.getItemValueBoolean("invoice.protest")) {
img = img + "<img title='Reklamiert' src=\"/layout/icons/icon106.png\">"; img = img + "<img title='Reklamiert' src=\"/layout/icons/icon106.png\">";
} }
workitem.setItemValue("_img", img); workitem.setItemValue("_img", img);
return workitem; return workitem;
} }
} }

View file

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

View file

@ -1,26 +1,26 @@
/******************************************************************************* /*******************************************************************************
* Imixs Workflow * Imixs Workflow
* Copyright (C) 2001, 2011 Imixs Software Solutions GmbH, * Copyright (C) 2001, 2011 Imixs Software Solutions GmbH,
* http://www.imixs.com * http://www.imixs.com
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2 * as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version. * of the License, or (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details. * General Public License for more details.
* *
* You can receive a copy of the GNU General Public * You can receive a copy of the GNU General Public
* License at http://www.gnu.org/licenses/gpl.html * License at http://www.gnu.org/licenses/gpl.html
* *
* Project: * Project:
* http://www.imixs.org * http://www.imixs.org
* http://java.net/projects/imixs-workflow * http://java.net/projects/imixs-workflow
* *
* Contributors: * Contributors:
* Imixs Software Solutions GmbH - initial API and implementation * Imixs Software Solutions GmbH - initial API and implementation
* Ralph Soika - Software Developer * Ralph Soika - Software Developer
*******************************************************************************/ *******************************************************************************/
@ -47,9 +47,9 @@ import jakarta.inject.Inject;
/** /**
* Der InvoiceService stellt einige Hilfsmethoden bereit. * Der InvoiceService stellt einige Hilfsmethoden bereit.
* *
* @author rsoika * @author rsoika
* *
*/ */
@Singleton @Singleton
@RunAs("org.imixs.ACCESSLEVEL.MANAGERACCESS") @RunAs("org.imixs.ACCESSLEVEL.MANAGERACCESS")
@ -79,7 +79,7 @@ public class InvoiceService {
/** /**
* Liefert die Liste der Ausgehenden Währungen * Liefert die Liste der Ausgehenden Währungen
* *
* @return * @return
* @throws PluginException * @throws PluginException
*/ */
@ -89,8 +89,7 @@ public class InvoiceService {
ItemCollection configItemCollection = configService.loadConfiguration("AGL_CONFIGURATION"); ItemCollection configItemCollection = configService.loadConfiguration("AGL_CONFIGURATION");
List<String> currencyList = configItemCollection.getItemValue("currency.out"); List<String> currencyList = configItemCollection.getItemValue("currency.out");
if (currencyList == null || currencyList.size() < 1) { if (currencyList == null || currencyList.size() < 1) {
throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), InvoiceService.ERROR_CONFIG,
InvoiceService.ERROR_CONFIG,
"missing Currency configuration - please check parameters"); "missing Currency configuration - please check parameters");
} }
return currencyList; return currencyList;
@ -98,7 +97,7 @@ public class InvoiceService {
/** /**
* Gibt die Hauptwährung zurück (1. in der liste) * Gibt die Hauptwährung zurück (1. in der liste)
* *
* @return * @return
* @throws PluginException * @throws PluginException
*/ */
@ -108,8 +107,7 @@ public class InvoiceService {
ItemCollection configItemCollection = configService.loadConfiguration("AGL_CONFIGURATION"); ItemCollection configItemCollection = configService.loadConfiguration("AGL_CONFIGURATION");
List<String> currencyList = configItemCollection.getItemValue("currency.out"); List<String> currencyList = configItemCollection.getItemValue("currency.out");
if (currencyList == null || currencyList.size() < 1) { if (currencyList == null || currencyList.size() < 1) {
throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), InvoiceService.ERROR_CONFIG,
InvoiceService.ERROR_CONFIG,
"missing Currency configuration - please check parameters"); "missing Currency configuration - please check parameters");
} }
return currencyList.get(0); return currencyList.get(0);
@ -117,7 +115,7 @@ public class InvoiceService {
/** /**
* Liefert die Liste der Eingehenden Währungen * Liefert die Liste der Eingehenden Währungen
* *
* @return * @return
* @throws PluginException * @throws PluginException
*/ */
@ -127,16 +125,14 @@ public class InvoiceService {
ItemCollection configItemCollection = configService.loadConfiguration("AGL_CONFIGURATION"); ItemCollection configItemCollection = configService.loadConfiguration("AGL_CONFIGURATION");
List<String> currencyList = configItemCollection.getItemValue("currency.in"); List<String> currencyList = configItemCollection.getItemValue("currency.in");
if (currencyList == null || currencyList.size() < 1) { if (currencyList == null || currencyList.size() < 1) {
throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), InvoiceService.ERROR_CONFIG,
InvoiceService.ERROR_CONFIG,
"missing Currency configuration - please check parameters"); "missing Currency configuration - please check parameters");
} }
return currencyList; return currencyList;
} }
/** /**
* Prüft ob der Vorgang überflällig ist. * Prüft ob der Vorgang überflällig ist. Default ist 21 tage.
* Default ist 21 tage.
*/ */
public boolean isOverdue(Date date, int taskid) { public boolean isOverdue(Date date, int taskid) {
Date now = new Date(); Date now = new Date();
@ -149,9 +145,9 @@ public class InvoiceService {
/** /**
* This method returns a text-block ItemCollection for a specified name. * This method returns a text-block ItemCollection for a specified name.
* *
* @param name in attribute txtname * @param name in attribute txtname
* *
* *
*/ */
public FileData loadTextBlockFileData(String name, String fileName) { public FileData loadTextBlockFileData(String name, String fileName) {

View file

@ -12,14 +12,12 @@ import org.imixs.workflow.ItemCollection;
/** /**
* Hilfsmethoden für die Berechnung und Validierung von Ein und * Hilfsmethoden für die Berechnung und Validierung von Ein und
* Ausgangsrechnugnen * Ausgangsrechnugnen
* *
* - Rundungs Methode * - Rundungs Methode - Berechnung der Positionsnummern - Explode Childitems
* - Berechnung der Positionsnummern *
* - Explode Childitems
*
* See: * See:
* https://stackoverflow.com/questions/2808535/round-a-double-to-2-decimal-places * https://stackoverflow.com/questions/2808535/round-a-double-to-2-decimal-places
* *
* @author rsoika * @author rsoika
* *
*/ */
@ -42,7 +40,7 @@ public class InvoiceUtil {
/** /**
* Rounds a Flat Value * Rounds a Flat Value
* *
* @param value * @param value
* @return * @return
*/ */
@ -54,7 +52,7 @@ public class InvoiceUtil {
/** /**
* Hilfsmethode zum validieren der Mailadresse * Hilfsmethode zum validieren der Mailadresse
* *
* @param emailAddress * @param emailAddress
* @return * @return
*/ */
@ -63,9 +61,7 @@ public class InvoiceUtil {
String regexPattern = "^(?=.{1,64}@)[A-Za-z0-9_-]+(\\.[A-Za-z0-9_-]+)*@" String regexPattern = "^(?=.{1,64}@)[A-Za-z0-9_-]+(\\.[A-Za-z0-9_-]+)*@"
+ "[^-][A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(\\.[A-Za-z]{2,})$"; + "[^-][A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(\\.[A-Za-z]{2,})$";
return Pattern.compile(regexPattern) return Pattern.compile(regexPattern).matcher(emailAddress).matches();
.matcher(emailAddress)
.matches();
} }
// @Deprecated // @Deprecated
@ -102,7 +98,7 @@ public class InvoiceUtil {
/** /**
* Convert the List of ItemCollections back into a List of Map elements * Convert the List of ItemCollections back into a List of Map elements
* *
* @param workitem * @param workitem
*/ */
public static void implodeChildList(ItemCollection workitem, List<ItemCollection> childItems) { public static void implodeChildList(ItemCollection workitem, List<ItemCollection> childItems) {
@ -111,7 +107,7 @@ public class InvoiceUtil {
/** /**
* Convert the List of ItemCollections back into a List of Map elements * Convert the List of ItemCollections back into a List of Map elements
* *
* @param workitem * @param workitem
*/ */
@SuppressWarnings({ "rawtypes" }) @SuppressWarnings({ "rawtypes" })
@ -130,11 +126,10 @@ public class InvoiceUtil {
/** /**
* Diese Methode berechnet das feld 'invoice.positions' welches eine Valueliste * Diese Methode berechnet das feld 'invoice.positions' welches eine Valueliste
* mit allen Positionsnummern aus dem Childliste enthält. * mit allen Positionsnummern aus dem Childliste enthält. Es wird sowohl für
* Es wird sowohl für Ausgangs wie Eingangsrechnungen genutzt. * Ausgangs wie Eingangsrechnungen genutzt. Das feld wird indiziert, so dass
* Das feld wird indiziert, so dass eine gezielte Suche nach positiosnummern * eine gezielte Suche nach positiosnummern möglich ist.
* möglich ist. *
*
* @param workitem * @param workitem
*/ */
public static void updateInvoicePositions(ItemCollection workitem) { public static void updateInvoicePositions(ItemCollection workitem) {
@ -162,9 +157,9 @@ public class InvoiceUtil {
/** /**
* Diese Hilfsmethode harmonisiert die Debitoren/Kreditoren ID von Cargosoft und * Diese Hilfsmethode harmonisiert die Debitoren/Kreditoren ID von Cargosoft und
* liefert eine Geschäftspartner ID zurück. * liefert eine Geschäftspartner ID zurück.
* *
* Aus Kreditor K70153 und Debitor D10153 wird die einheitliche ID: BP0153 * Aus Kreditor K70153 und Debitor D10153 wird die einheitliche ID: BP0153
* *
* @param key * @param key
* @return * @return
*/ */
@ -188,7 +183,7 @@ public class InvoiceUtil {
/** /**
* Diese Hilfmethode berechnet die BusinessPartner id aus einer Invoice * Diese Hilfmethode berechnet die BusinessPartner id aus einer Invoice
* *
* @param Invoice * @param Invoice
* @return * @return
*/ */
@ -204,7 +199,7 @@ public class InvoiceUtil {
/** /**
* Diese Hilfmethode berechnet die BusinessPartner Namen aus einer Invoice * Diese Hilfmethode berechnet die BusinessPartner Namen aus einer Invoice
* *
* @param Invoice * @param Invoice
* @return * @return
*/ */
@ -223,7 +218,7 @@ public class InvoiceUtil {
/** /**
* Gibt true zurück wenn es sich um eine Kreditoren Rechnung/Gutschrift handelt. * Gibt true zurück wenn es sich um eine Kreditoren Rechnung/Gutschrift handelt.
* *
* @param invoice * @param invoice
* @return * @return
*/ */
@ -238,7 +233,7 @@ public class InvoiceUtil {
/** /**
* Gibt true zurück wenn es sich um eine Kreditoren Sachrechnung handelt. * Gibt true zurück wenn es sich um eine Kreditoren Sachrechnung handelt.
* *
* @param invoice * @param invoice
* @return * @return
*/ */
@ -252,7 +247,7 @@ public class InvoiceUtil {
/** /**
* Gibt true zurück wenn es sich um eine Debitoren Rechnung/Gutschrift handelt. * Gibt true zurück wenn es sich um eine Debitoren Rechnung/Gutschrift handelt.
* *
* @param invoice * @param invoice
* @return * @return
*/ */

View file

@ -31,7 +31,7 @@ import jakarta.inject.Named;
* errechnet der Controller die selectieren Invoices und verknpüft diese final * errechnet der Controller die selectieren Invoices und verknpüft diese final
* mit dem Zahlungseingang. * mit dem Zahlungseingang.
* *
* *
* @author rsoika * @author rsoika
* *
*/ */
@ -74,8 +74,7 @@ public class OPListController implements Serializable {
* Diese Methode wird für Zahlungseingänge und Mahnläufe verwendet. * Diese Methode wird für Zahlungseingänge und Mahnläufe verwendet.
* <p> * <p>
* Die Methode liefert die Rechnungen zur gegebenen dbtrNumber. Die Methode * Die Methode liefert die Rechnungen zur gegebenen dbtrNumber. Die Methode
* nutzt * nutzt einen lokalen cache um die Zugriffe zu beschleunigen.
* einen lokalen cache um die Zugriffe zu beschleunigen.
* <p> * <p>
* Befindet sich ein Zahlungseingang in Erstellung (Task=1000) werden alle * Befindet sich ein Zahlungseingang in Erstellung (Task=1000) werden alle
* offenen Rechnungen geholt. * offenen Rechnungen geholt.
@ -90,8 +89,8 @@ public class OPListController implements Serializable {
* will. * will.
* <p> * <p>
* Beim Mahnlauf werden Inkasso Rechnungen ausgenommen. * Beim Mahnlauf werden Inkasso Rechnungen ausgenommen.
* *
* *
* @param _dbtrNumber * @param _dbtrNumber
* @return * @return
*/ */
@ -150,7 +149,7 @@ public class OPListController implements Serializable {
/* /*
* Zahlugnseingang? * Zahlugnseingang?
* *
* Wir berechnen den saldo. * Wir berechnen den saldo.
*/ */
if (workflowController.getWorkitem().getModelVersion().startsWith("zahlungseingang")) { if (workflowController.getWorkitem().getModelVersion().startsWith("zahlungseingang")) {
@ -172,10 +171,10 @@ public class OPListController implements Serializable {
* Bei einem Mahnlauf selektieren wir alle Rechnungne immer vor. Das ist eine * Bei einem Mahnlauf selektieren wir alle Rechnungne immer vor. Das ist eine
* Anforderung von AGL Der Benutzer kann dann einzelne Rechnungen wieder * Anforderung von AGL Der Benutzer kann dann einzelne Rechnungen wieder
* abwählen. * abwählen.
* *
* Eine Ausnahme ist hier nur wenn jemand auf Speichern oder mahnen drückt * Eine Ausnahme ist hier nur wenn jemand auf Speichern oder mahnen drückt
* (Event = 10|20). Dann ändern wir die Liste nicht! * (Event = 10|20). Dann ändern wir die Liste nicht!
* *
* Hinweis: in der Liste befinden sich keine Inkasso Rechnungen, da man diese * Hinweis: in der Liste befinden sich keine Inkasso Rechnungen, da man diese
* nicht anwählen kann. * nicht anwählen kann.
*/ */
@ -185,8 +184,7 @@ public class OPListController implements Serializable {
// einer Rechnung mit dem Event 100 (Mahnlauf erstellen) kommen, oder // einer Rechnung mit dem Event 100 (Mahnlauf erstellen) kommen, oder
// wenn der Mahnlauf von außen gerate eben neu erzeugt wurde // wenn der Mahnlauf von außen gerate eben neu erzeugt wurde
if (workflowController.getWorkitem().getItemValueInteger("$lastevent") == 100 if (workflowController.getWorkitem().getItemValueInteger("$lastevent") == 100
|| || workflowController.getWorkitem().getItemValueInteger("$lastevent") == 0) {
workflowController.getWorkitem().getItemValueInteger("$lastevent") == 0) {
List<String> intialSelection = new ArrayList<String>(); List<String> intialSelection = new ArrayList<String>();
for (ItemCollection invoice : invoiceList) { for (ItemCollection invoice : invoiceList) {
// ist die Rechnung fällig? // ist die Rechnung fällig?
@ -247,7 +245,7 @@ public class OPListController implements Serializable {
/** /**
* On Before Process we store the selected invoices in $worktiemRef * On Before Process we store the selected invoices in $worktiemRef
* *
* @param workflowEvent * @param workflowEvent
*/ */
public void onWorkflowEvent(@Observes WorkflowEvent workflowEvent) { public void onWorkflowEvent(@Observes WorkflowEvent workflowEvent) {
@ -299,7 +297,7 @@ public class OPListController implements Serializable {
/** /**
* Hilfsmethode delegates to ZahlungseingangServcie * Hilfsmethode delegates to ZahlungseingangServcie
* *
* @param payment * @param payment
* @return * @return
*/ */
@ -309,7 +307,7 @@ public class OPListController implements Serializable {
/** /**
* Berechnet den gesammten OP Saldo * Berechnet den gesammten OP Saldo
* *
* @return * @return
*/ */
public double calculateInvoiceTotal() { public double calculateInvoiceTotal() {
@ -323,10 +321,10 @@ public class OPListController implements Serializable {
} }
/** /**
* Berechnet die Summer aller Rechnungen zu einer Währung. * Berechnet die Summer aller Rechnungen zu einer Währung. Es werden nur
* Es werden nur Rechnungen berücksichtigt, deren Haupt Währung der übergebenen * Rechnungen berücksichtigt, deren Haupt Währung der übergebenen Währung
* Währung entspricht. * entspricht.
* *
* @param currency * @param currency
* @return * @return
*/ */
@ -344,7 +342,7 @@ public class OPListController implements Serializable {
/** /**
* Berechnet den gesamten OP Saldo * Berechnet den gesamten OP Saldo
* *
* @return * @return
*/ */
public double calculateInvoiceSaldo() { public double calculateInvoiceSaldo() {
@ -361,7 +359,7 @@ public class OPListController implements Serializable {
/** /**
* Berechnet den noch offenen Differenz vom Zahlbetrag * Berechnet den noch offenen Differenz vom Zahlbetrag
* *
* @return * @return
*/ */
public double calculatePaymentDifference() { public double calculatePaymentDifference() {
@ -379,7 +377,7 @@ public class OPListController implements Serializable {
/** /**
* Berechnet den Zahlbetrag in EUR bei Fremdwärungen * Berechnet den Zahlbetrag in EUR bei Fremdwärungen
* *
* @return * @return
*/ */
public double calculatePaymentSaldoEUR() { public double calculatePaymentSaldoEUR() {
@ -401,7 +399,7 @@ public class OPListController implements Serializable {
/** /**
* Returns true if the reminder date is today or in the past * Returns true if the reminder date is today or in the past
* *
* @param uniqueid * @param uniqueid
* @return * @return
*/ */

View file

@ -35,7 +35,7 @@ import jakarta.inject.Inject;
* Der OPListExportAdapter exportiert eine Excel Datei mit allen offenen * Der OPListExportAdapter exportiert eine Excel Datei mit allen offenen
* Rechnungen gruppiert nach Debitor. Es werden alle Ausgehenden Währungen * Rechnungen gruppiert nach Debitor. Es werden alle Ausgehenden Währungen
* berücksichtigt und ab Spalte H eingefügt. * berücksichtigt und ab Spalte H eingefügt.
* *
* <pre> * <pre>
* {@code * {@code
<opliste name="textblock">textblock-ref</opliste> <opliste name="textblock">textblock-ref</opliste>
@ -46,16 +46,16 @@ import jakarta.inject.Inject;
* <p> * <p>
* Der Adapter erweitert den POIAdapter somit können Felder aktualisiert werden * Der Adapter erweitert den POIAdapter somit können Felder aktualisiert werden
* (siehe POIFineReplaceAdapter). * (siehe POIFineReplaceAdapter).
* *
* <p> * <p>
* Der Adapter kopiert die Rechnungsdaten in neue Zeilen, welche ab Zeilennummer * Der Adapter kopiert die Rechnungsdaten in neue Zeilen, welche ab Zeilennummer
* 12 eingefügt werden. * 12 eingefügt werden.
* <p> * <p>
* *
* Abschliessend werden Summen Formeln in Zeile 14 für jede Währung eingefügt. * Abschliessend werden Summen Formeln in Zeile 14 für jede Währung eingefügt.
* *
* Der Fälligkeitszeitrum von 21 Tagen kann über den Parameter * Der Fälligkeitszeitrum von 21 Tagen kann über den Parameter
* *
* @version 1.0 * @version 1.0
* @author rsoika * @author rsoika
*/ */
@ -78,7 +78,7 @@ public class OPListExportAdapter extends POIFindReplaceAdapter {
/** /**
* This method finds or create the Zahlungsavis and adds a reference * This method finds or create the Zahlungsavis and adds a reference
* ($workitemref) to the current invoice. * ($workitemref) to the current invoice.
* *
* @throws PluginException * @throws PluginException
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -95,9 +95,7 @@ public class OPListExportAdapter extends POIFindReplaceAdapter {
} }
// read the template options // read the template options
ItemCollection evalItemCollection = workflowService.evalWorkflowResult(event, "opliste", ItemCollection evalItemCollection = workflowService.evalWorkflowResult(event, "opliste", document, false);
document,
false);
if (evalItemCollection == null || !evalItemCollection.hasItem("excel-export")) { if (evalItemCollection == null || !evalItemCollection.hasItem("excel-export")) {
throw new PluginException(OPListExportAdapter.class.getSimpleName(), CONFIG_ERROR, throw new PluginException(OPListExportAdapter.class.getSimpleName(), CONFIG_ERROR,
"missing opliste configuration in model event - please check model configuration"); "missing opliste configuration in model event - please check model configuration");
@ -132,13 +130,11 @@ public class OPListExportAdapter extends POIFindReplaceAdapter {
doc.write(byteArrayOutputStream); doc.write(byteArrayOutputStream);
byte[] newContent = byteArrayOutputStream.toByteArray(); byte[] newContent = byteArrayOutputStream.toByteArray();
FileData fileDataNew = new FileData(fileData.getName(), newContent, fileData.getContentType(), FileData fileDataNew = new FileData(fileData.getName(), newContent, fileData.getContentType(), null);
null);
document.addFileData(fileDataNew); document.addFileData(fileDataNew);
logger.finest("......new document added"); logger.finest("......new document added");
} catch (IOException | QueryException e) { } catch (IOException | QueryException e) {
throw new PluginException(OPListExportAdapter.class.getSimpleName(), throw new PluginException(OPListExportAdapter.class.getSimpleName(), InvoiceService.ERROR_CONFIG,
InvoiceService.ERROR_CONFIG,
"failed to update opliste: " + e.getMessage()); "failed to update opliste: " + e.getMessage());
} finally { } finally {
if (doc != null) { if (doc != null) {
@ -157,8 +153,7 @@ public class OPListExportAdapter extends POIFindReplaceAdapter {
} catch ( } catch (
PluginException e) { PluginException e) {
throw new PluginException(OPListExportAdapter.class.getSimpleName(), throw new PluginException(OPListExportAdapter.class.getSimpleName(), InvoiceService.ERROR_CONFIG,
InvoiceService.ERROR_CONFIG,
"failed to update op-liste: " + e.getMessage()); "failed to update op-liste: " + e.getMessage());
} }
logger.fine("... completed!"); logger.fine("... completed!");
@ -169,8 +164,8 @@ public class OPListExportAdapter extends POIFindReplaceAdapter {
/** /**
* EIne OP Liste kann für eine oder mehrere Währungen erstellt werden. Diese * EIne OP Liste kann für eine oder mehrere Währungen erstellt werden. Diese
* Hilfsmethode fügt die Spalten anhand der Währungen ein. * Hilfsmethode fügt die Spalten anhand der Währungen ein.
* *
* *
* @param sheet * @param sheet
* @param currencyList * @param currencyList
*/ */
@ -206,13 +201,12 @@ public class OPListExportAdapter extends POIFindReplaceAdapter {
* <p> * <p>
* The named cell 'TOTAL' should contain the summary formula. It will be * The named cell 'TOTAL' should contain the summary formula. It will be
* evaluated at the end. * evaluated at the end.
* *
* @throws PluginException * @throws PluginException
* @throws QueryException * @throws QueryException
*/ */
private void insertInvoiceRows(XSSFSheet sheet, ItemCollection document, String fileName, boolean filter, private void insertInvoiceRows(XSSFSheet sheet, ItemCollection document, String fileName, boolean filter,
List<String> currencyList) List<String> currencyList) throws PluginException, QueryException {
throws PluginException, QueryException {
String spaceID = document.getItemValueString("space.ref"); String spaceID = document.getItemValueString("space.ref");
logger.fine("... space.ref=" + spaceID + " ...grouping invoices by spaceid...."); logger.fine("... space.ref=" + spaceID + " ...grouping invoices by spaceid....");
@ -344,17 +338,15 @@ public class OPListExportAdapter extends POIFindReplaceAdapter {
/** /**
* This method loads a text-block for a specified ref and appends the named * This method loads a text-block for a specified ref and appends the named
* fileData object of this document. * fileData object of this document.
* *
* @param document * @param document
* @throws PluginException * @throws PluginException
*/ */
private FileData appendExcelTemplate(ItemCollection document, String textblockRef, String template, private FileData appendExcelTemplate(ItemCollection document, String textblockRef, String template,
String targetName) String targetName) throws PluginException {
throws PluginException {
if ((template == null || template.isEmpty()) || (textblockRef == null || textblockRef.isEmpty())) { if ((template == null || template.isEmpty()) || (textblockRef == null || textblockRef.isEmpty())) {
throw new PluginException(OPListExportAdapter.class.getSimpleName(), throw new PluginException(OPListExportAdapter.class.getSimpleName(), InvoiceService.ERROR_CONFIG,
InvoiceService.ERROR_CONFIG,
"invalid POI configuration in model event - textblock/template reference not defined!"); "invalid POI configuration in model event - textblock/template reference not defined!");
} }
@ -363,10 +355,8 @@ public class OPListExportAdapter extends POIFindReplaceAdapter {
// do we found the document? // do we found the document?
if (fileData == null) { if (fileData == null) {
throw new PluginException(OPListExportAdapter.class.getSimpleName(), throw new PluginException(OPListExportAdapter.class.getSimpleName(), InvoiceService.ERROR_CONFIG,
InvoiceService.ERROR_CONFIG, "invalid POI configuration in model event - template=" + template + " not found!");
"invalid POI configuration in model event - template=" + template
+ " not found!");
} }
fileData.setName(targetName); fileData.setName(targetName);
// append document // append document
@ -377,10 +367,10 @@ public class OPListExportAdapter extends POIFindReplaceAdapter {
/** /**
* Diese Methode gruppiert eine Rechnungsliste nach Debitorennummern * Diese Methode gruppiert eine Rechnungsliste nach Debitorennummern
* *
* Falls 'filter==true' werden nur Rechnungen ab der 2. Mahnung oder mit einer * Falls 'filter==true' werden nur Rechnungen ab der 2. Mahnung oder mit einer
* Fälligkeit >10 Tage ausgegeben. Dieses Flag wird im Workflowmodell gesetzt * Fälligkeit >10 Tage ausgegeben. Dieses Flag wird im Workflowmodell gesetzt
* *
* @param spaceID * @param spaceID
* @return * @return
*/ */

View file

@ -46,9 +46,9 @@ import jakarta.inject.Inject;
* Der OPListExportAdapterByWeek ist ählnlich dem Der OPListExportAdapter. Er * Der OPListExportAdapterByWeek ist ählnlich dem Der OPListExportAdapter. Er
* erzeugt eine Excel tabelle mit allen offenen Rechnungen und gruppiert diese * erzeugt eine Excel tabelle mit allen offenen Rechnungen und gruppiert diese
* nach Abteilungen und KW * nach Abteilungen und KW
* *
* Der OPListExportAdapterByWeek importiert eine Excel Datei aus einem Textblock * Der OPListExportAdapterByWeek importiert eine Excel Datei aus einem Textblock
* *
* <pre> * <pre>
* {@code * {@code
<opliste name="textblock">textblock-ref</opliste> <opliste name="textblock">textblock-ref</opliste>
@ -59,14 +59,14 @@ import jakarta.inject.Inject;
* <p> * <p>
* Der Adapter erweitert den POIAdapter somit können felder aktualisiert werden * Der Adapter erweitert den POIAdapter somit können felder aktualisiert werden
* (siehe POIFineReplaceAdapter). * (siehe POIFineReplaceAdapter).
* *
* <p> * <p>
* Der Adapter kopiert die Rechnungsdaten in neue Zeilen, welche ab Zeilnenummer * Der Adapter kopiert die Rechnungsdaten in neue Zeilen, welche ab Zeilnenummer
* 16 eingefügt werden. * 16 eingefügt werden.
* <p> * <p>
* *
* Abschliessend wird die Zelle 'TOTAL' noch aktualisiert. * Abschliessend wird die Zelle 'TOTAL' noch aktualisiert.
* *
* @version 1.0 * @version 1.0
* @author rsoika * @author rsoika
*/ */
@ -97,7 +97,7 @@ public class OPListExportAdapterByWeek extends POIFindReplaceAdapter {
/** /**
* This method finds or create the Zahlungsavis and adds a reference * This method finds or create the Zahlungsavis and adds a reference
* ($workitemref) to the current invoice. * ($workitemref) to the current invoice.
* *
* @throws PluginException * @throws PluginException
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -119,9 +119,7 @@ public class OPListExportAdapterByWeek extends POIFindReplaceAdapter {
Collections.sort(spaces, new ItemCollectionComparator("space.name", true)); Collections.sort(spaces, new ItemCollectionComparator("space.name", true));
// read the template options // read the template options
ItemCollection evalItemCollection = workflowService.evalWorkflowResult(event, "opliste", ItemCollection evalItemCollection = workflowService.evalWorkflowResult(event, "opliste", document, false);
document,
false);
if (evalItemCollection == null || !evalItemCollection.hasItem("excel-export")) { if (evalItemCollection == null || !evalItemCollection.hasItem("excel-export")) {
throw new PluginException(OPListExportAdapter.class.getSimpleName(), CONFIG_ERROR, throw new PluginException(OPListExportAdapter.class.getSimpleName(), CONFIG_ERROR,
"missing opliste configuration in model event - please check model configuration"); "missing opliste configuration in model event - please check model configuration");
@ -155,14 +153,12 @@ public class OPListExportAdapterByWeek extends POIFindReplaceAdapter {
doc.write(byteArrayOutputStream); doc.write(byteArrayOutputStream);
byte[] newContent = byteArrayOutputStream.toByteArray(); byte[] newContent = byteArrayOutputStream.toByteArray();
FileData fileDataNew = new FileData(fileData.getName(), newContent, fileData.getContentType(), FileData fileDataNew = new FileData(fileData.getName(), newContent, fileData.getContentType(), null);
null);
document.addFileData(fileDataNew); document.addFileData(fileDataNew);
logger.finest("......new document added"); logger.finest("......new document added");
} catch (IOException | QueryException e) { } catch (IOException | QueryException e) {
throw new PluginException(OPListExportAdapter.class.getSimpleName(), throw new PluginException(OPListExportAdapter.class.getSimpleName(), InvoiceService.ERROR_CONFIG,
InvoiceService.ERROR_CONFIG,
"failed to update opliste: " + e.getMessage()); "failed to update opliste: " + e.getMessage());
} finally { } finally {
if (doc != null) { if (doc != null) {
@ -180,8 +176,7 @@ public class OPListExportAdapterByWeek extends POIFindReplaceAdapter {
} catch ( } catch (
PluginException e) { PluginException e) {
throw new PluginException(OPListExportAdapter.class.getSimpleName(), throw new PluginException(OPListExportAdapter.class.getSimpleName(), InvoiceService.ERROR_CONFIG,
InvoiceService.ERROR_CONFIG,
"failed to update op-liste: " + e.getMessage()); "failed to update op-liste: " + e.getMessage());
} }
logger.fine("... completed!"); logger.fine("... completed!");
@ -193,7 +188,7 @@ public class OPListExportAdapterByWeek extends POIFindReplaceAdapter {
/** /**
* EIne OP Liste kann für eine oder mehrere Währungen erstellt werden. Diese * EIne OP Liste kann für eine oder mehrere Währungen erstellt werden. Diese
* Hilfsmethode fügt die Spalten anhand der Währungen für jede Abteilung ein. * Hilfsmethode fügt die Spalten anhand der Währungen für jede Abteilung ein.
* *
* @param sheet * @param sheet
* @param currencyList * @param currencyList
*/ */
@ -249,13 +244,12 @@ public class OPListExportAdapterByWeek extends POIFindReplaceAdapter {
* <p> * <p>
* The named cell 'TOTAL' should contain the summary formula. It will be * The named cell 'TOTAL' should contain the summary formula. It will be
* evaluated at the end. * evaluated at the end.
* *
* @throws PluginException * @throws PluginException
* @throws QueryException * @throws QueryException
*/ */
private void insertInvoiceRows(XSSFWorkbook doc, XSSFSheet sheet, List<String> currencyList, private void insertInvoiceRows(XSSFWorkbook doc, XSSFSheet sheet, List<String> currencyList,
List<ItemCollection> spaces) List<ItemCollection> spaces) throws PluginException, QueryException {
throws PluginException, QueryException {
List<String> spaceNames = new ArrayList<String>(); List<String> spaceNames = new ArrayList<String>();
@ -285,8 +279,8 @@ public class OPListExportAdapterByWeek extends POIFindReplaceAdapter {
} }
/* /*
* --- Phase 1 ------------------------------------------------------- * --- Phase 1 ------------------------------------------------------- Im
* Im folgenden gruppieren wir die Rechnungen nach Woche * folgenden gruppieren wir die Rechnungen nach Woche
*/ */
// XSSFCell cellKWTotalReference = referenceRowTotal.getCell(14); // XSSFCell cellKWTotalReference = referenceRowTotal.getCell(14);
@ -306,10 +300,10 @@ public class OPListExportAdapterByWeek extends POIFindReplaceAdapter {
totalDataMapUeberFaellig.put("TOTAL", new TotalInvoiceData("TOTAL")); totalDataMapUeberFaellig.put("TOTAL", new TotalInvoiceData("TOTAL"));
/* /*
* --- Phase 3 ------------------------------------------------------- * --- Phase 3 ------------------------------------------------------- now build
* now build a new row for each week and print out the collected invoice data * a new row for each week and print out the collected invoice data first
* first iterate over all objects and collect all known weeks and sort them into * iterate over all objects and collect all known weeks and sort them into a
* a ordered list.... * ordered list....
*/ */
List<String> sortedWeekList = new ArrayList<String>(); List<String> sortedWeekList = new ArrayList<String>();
Iterator<Map.Entry<String, WeekInvoiceData>> iterator = weekDataMap.entrySet().iterator(); Iterator<Map.Entry<String, WeekInvoiceData>> iterator = weekDataMap.entrySet().iterator();
@ -323,9 +317,9 @@ public class OPListExportAdapterByWeek extends POIFindReplaceAdapter {
Collections.sort(sortedWeekList); Collections.sort(sortedWeekList);
/* /*
* --- Phase 4 ------------------------------------------------------- * --- Phase 4 ------------------------------------------------------- Nun
* Nun müssen wir so viele Zeilen nach unten schieben wie wir gleich einfügen. * müssen wir so viele Zeilen nach unten schieben wie wir gleich einfügen. Dazu
* Dazu müssen wir kurz einen Probelauf machen um die geplante Anzahl zu * müssen wir kurz einen Probelauf machen um die geplante Anzahl zu
* errechnen.... * errechnen....
*/ */
int rowPos = 11; int rowPos = 11;
@ -333,8 +327,8 @@ public class OPListExportAdapterByWeek extends POIFindReplaceAdapter {
sheet.shiftRows(rowPos + 1, 2999, insertCount, true, true); sheet.shiftRows(rowPos + 1, 2999, insertCount, true, true);
/* /*
* --- Phase 5 ------------------------------------------------------- * --- Phase 5 ------------------------------------------------------- Jetzt
* Jetzt Zahlen Zeile für Zeile einfügen.... * Zahlen Zeile für Zeile einfügen....
*/ */
boolean ueberFaellig = true; boolean ueberFaellig = true;
for (String week : sortedWeekList) { for (String week : sortedWeekList) {
@ -399,9 +393,8 @@ public class OPListExportAdapterByWeek extends POIFindReplaceAdapter {
} }
/* /*
* --- Phase 6 ------------------------------------------------------- * --- Phase 6 ------------------------------------------------------- now lets
* now lets update the totals... * update the totals... Überfällig
* Überfällig
*/ */
int _rowNo = sortedWeekList.size() + 14; int _rowNo = sortedWeekList.size() + 14;
XSSFRow rowUeberFaellig = sheet.getRow(_rowNo); XSSFRow rowUeberFaellig = sheet.getRow(_rowNo);
@ -460,16 +453,14 @@ public class OPListExportAdapterByWeek extends POIFindReplaceAdapter {
/** /**
* Diese Methode gruppiert eine Rechnungsliste nach Kalenderwoche * Diese Methode gruppiert eine Rechnungsliste nach Kalenderwoche
* *
* @param spaceID - Space Ref to select a list of invoices associated with * @param spaceID - Space Ref to select a list of invoices associated with
* a * a space
* space
* @param weekDataCache - a local cache storing all invoices by week * @param weekDataCache - a local cache storing all invoices by week
* *
* *
*/ */
private void groupInvoicesByWeek(String spaceID, String spaceName, private void groupInvoicesByWeek(String spaceID, String spaceName, Map<String, WeekInvoiceData> weekDataCache) {
Map<String, WeekInvoiceData> weekDataCache) {
logger.info("...group invoices for " + spaceName + "/" + spaceID); logger.info("...group invoices for " + spaceName + "/" + spaceID);
try { try {
@ -482,9 +473,7 @@ public class OPListExportAdapterByWeek extends POIFindReplaceAdapter {
// compute Week // compute Week
Date dueDate = invoice.getItemValueDate("invoice.duedate"); Date dueDate = invoice.getItemValueDate("invoice.duedate");
LocalDate localDate = dueDate.toInstant() LocalDate localDate = dueDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
.atZone(ZoneId.systemDefault())
.toLocalDate();
int weekNumber = localDate.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR); int weekNumber = localDate.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR);
int weekBasedYear = localDate.get(IsoFields.WEEK_BASED_YEAR); int weekBasedYear = localDate.get(IsoFields.WEEK_BASED_YEAR);
String weekCategory = weekBasedYear + "/" + String.format("%02d", weekNumber); String weekCategory = weekBasedYear + "/" + String.format("%02d", weekNumber);
@ -521,17 +510,15 @@ public class OPListExportAdapterByWeek extends POIFindReplaceAdapter {
/** /**
* This method loads a text-block for a specified ref and appends the named * This method loads a text-block for a specified ref and appends the named
* fileData object of this document. * fileData object of this document.
* *
* @param document * @param document
* @throws PluginException * @throws PluginException
*/ */
private FileData appendExcelTemplate(ItemCollection document, String textblockRef, String template, private FileData appendExcelTemplate(ItemCollection document, String textblockRef, String template,
String targetName) String targetName) throws PluginException {
throws PluginException {
if ((template == null || template.isEmpty()) || (textblockRef == null || textblockRef.isEmpty())) { if ((template == null || template.isEmpty()) || (textblockRef == null || textblockRef.isEmpty())) {
throw new PluginException(OPListExportAdapter.class.getSimpleName(), throw new PluginException(OPListExportAdapter.class.getSimpleName(), InvoiceService.ERROR_CONFIG,
InvoiceService.ERROR_CONFIG,
"invalid POI configuration in model event - textblock/template reference not defined!"); "invalid POI configuration in model event - textblock/template reference not defined!");
} }
@ -540,10 +527,8 @@ public class OPListExportAdapterByWeek extends POIFindReplaceAdapter {
// do we found the document? // do we found the document?
if (fileData == null) { if (fileData == null) {
throw new PluginException(OPListExportAdapter.class.getSimpleName(), throw new PluginException(OPListExportAdapter.class.getSimpleName(), InvoiceService.ERROR_CONFIG,
InvoiceService.ERROR_CONFIG, "invalid POI configuration in model event - template=" + template + " not found!");
"invalid POI configuration in model event - template=" + template
+ " not found!");
} }
fileData.setName(targetName); fileData.setName(targetName);
// append document // append document
@ -555,8 +540,8 @@ public class OPListExportAdapterByWeek extends POIFindReplaceAdapter {
} }
/** /**
* Data Element for invoice totals per week and space. * Data Element for invoice totals per week and space. The object holds multiple
* The object holds multiple currencies. * currencies.
*/ */
class WeekInvoiceData { class WeekInvoiceData {
@ -601,8 +586,8 @@ class WeekInvoiceData {
} }
/** /**
* Data Element for invoice totals per space. * Data Element for invoice totals per space. The object holds multiple
* The object holds multiple currencies. * currencies.
*/ */
class TotalInvoiceData { class TotalInvoiceData {

View file

@ -2,26 +2,26 @@ package com.alexanderlogistics;
/******************************************************************************* /*******************************************************************************
* Imixs Workflow Technology * Imixs Workflow Technology
* Copyright (C) 2003, 2008 Imixs Software Solutions GmbH, * Copyright (C) 2003, 2008 Imixs Software Solutions GmbH,
* http://www.imixs.com * http://www.imixs.com
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2 * as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version. * of the License, or (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details. * General Public License for more details.
* *
* You can receive a copy of the GNU General Public * You can receive a copy of the GNU General Public
* License at http://www.gnu.org/licenses/gpl.html * License at http://www.gnu.org/licenses/gpl.html
* *
* Contributors: * Contributors:
* Imixs Software Solutions GmbH - initial API and implementation * Imixs Software Solutions GmbH - initial API and implementation
* Ralph Soika * Ralph Soika
* *
*******************************************************************************/ *******************************************************************************/
import java.util.logging.Logger; import java.util.logging.Logger;
@ -34,44 +34,43 @@ import jakarta.inject.Named;
/** /**
* The OPListExportController is used to configure the OpListeExporterService. * The OPListExportController is used to configure the OpListeExporterService.
* This service is * This service is used to export workitems.
* used to export workitems.
* <p> * <p>
* The Controller creates a configuration entity "type=configuration; * The Controller creates a configuration entity "type=configuration;
* txtname=OPLIST_EXPORT_CONFIGURATION". * txtname=OPLIST_EXPORT_CONFIGURATION".
* *
* *
* @author rsoika * @author rsoika
* *
*/ */
@Named(value = "oplistExportController") @Named(value = "oplistExportController")
@RequestScoped @RequestScoped
public class OPListExportController extends SchedulerController { public class OPListExportController extends SchedulerController {
public static final String OPLIST_EXPORT_CONFIGURATION = "OPLIST_EXPORT_CONFIGURATION"; public static final String OPLIST_EXPORT_CONFIGURATION = "OPLIST_EXPORT_CONFIGURATION";
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private static Logger logger = Logger.getLogger(OPListExportController.class.getName()); private static Logger logger = Logger.getLogger(OPListExportController.class.getName());
@Inject @Inject
OPListExportScheduler opListExportScheduler; OPListExportScheduler opListExportScheduler;
@Override @Override
public String getName() { public String getName() {
return OPLIST_EXPORT_CONFIGURATION; return OPLIST_EXPORT_CONFIGURATION;
} }
/** /**
* Returns the sepa scheduler class name. This name depends on the _export_type. * Returns the sepa scheduler class name. This name depends on the _export_type.
* *
* There are two export interfaces available - csv and XML * There are two export interfaces available - csv and XML
* *
*/ */
@Override @Override
public String getSchedulerClass() { public String getSchedulerClass() {
String schedulerClass = OPListExportScheduler.class.getName(); String schedulerClass = OPListExportScheduler.class.getName();
logger.finest("...... scheduler: " + schedulerClass); logger.finest("...... scheduler: " + schedulerClass);
return schedulerClass; return schedulerClass;
} }
} }

View file

@ -1,22 +1,22 @@
/******************************************************************************* /*******************************************************************************
* Imixs Workflow Technology * Imixs Workflow Technology
* Copyright (C) 2001, 2008 Imixs Software Solutions GmbH, * Copyright (C) 2001, 2008 Imixs Software Solutions GmbH,
* http://www.imixs.com * http://www.imixs.com
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2 * as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version. * of the License, or (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details. * General Public License for more details.
* *
* You can receive a copy of the GNU General Public * You can receive a copy of the GNU General Public
* License at http://www.gnu.org/licenses/gpl.html * License at http://www.gnu.org/licenses/gpl.html
* *
* Contributors: * Contributors:
* Imixs Software Solutions GmbH - initial API and implementation * Imixs Software Solutions GmbH - initial API and implementation
* Ralph Soika * Ralph Soika
*******************************************************************************/ *******************************************************************************/
@ -61,10 +61,10 @@ import jakarta.inject.Inject;
* The class implements the interface * The class implements the interface
* _org.imixs.workflow.engine.scheduler.Scheduler_ and can be used in * _org.imixs.workflow.engine.scheduler.Scheduler_ and can be used in
* combination with the Imxis-Workflow Scheduler Service. * combination with the Imxis-Workflow Scheduler Service.
* *
* @see SchedulerService * @see SchedulerService
* @author rsoika * @author rsoika
* *
*/ */
public class OPListExportScheduler implements Scheduler { public class OPListExportScheduler implements Scheduler {
@ -89,7 +89,7 @@ public class OPListExportScheduler implements Scheduler {
/** /**
* This is the method which processes the timeout event depending on the running * This is the method which processes the timeout event depending on the running
* timer settings. * timer settings.
* *
* @param timer * @param timer
* @throws QueryException * @throws QueryException
*/ */
@ -131,7 +131,7 @@ public class OPListExportScheduler implements Scheduler {
/** /**
* Prüfen ob es Rechnungen in diesem Space gibt die entweder die 2. Mahnstufe * Prüfen ob es Rechnungen in diesem Space gibt die entweder die 2. Mahnstufe
* erreicht haben oder deren Fälligkeit um mehr als 21 Tage überschritten ist. * erreicht haben oder deren Fälligkeit um mehr als 21 Tage überschritten ist.
* *
* @param space * @param space
* @return * @return
*/ */
@ -139,8 +139,8 @@ public class OPListExportScheduler implements Scheduler {
List<ItemCollection> invoices = new ArrayList<ItemCollection>(); List<ItemCollection> invoices = new ArrayList<ItemCollection>();
try { try {
invoices = documentService.find( invoices = documentService.find(
"$modelversion:rechnungsausgang-* AND type:workitem AND $uniqueidref:" + space.getUniqueID(), "$modelversion:rechnungsausgang-* AND type:workitem AND $uniqueidref:" + space.getUniqueID(), 999,
999, 0); 0);
} catch (QueryException e) { } catch (QueryException e) {
logger.warning("Failed to compute invoice list: " + e.getMessage()); logger.warning("Failed to compute invoice list: " + e.getMessage());
e.printStackTrace(); e.printStackTrace();
@ -168,7 +168,7 @@ public class OPListExportScheduler implements Scheduler {
/** /**
* Creates a new log entry stored in the item _scheduler_log. The log can be * Creates a new log entry stored in the item _scheduler_log. The log can be
* writen optional to the configuraiton and the workitem * writen optional to the configuraiton and the workitem
* *
* @param message * @param message
* @param configuration * @param configuration
*/ */

View file

@ -17,123 +17,122 @@ import jakarta.inject.Inject;
/** /**
* Der RenameFilenamesAdapter kann optional eingesetzt werden. Dieser Adapter * Der RenameFilenamesAdapter kann optional eingesetzt werden. Dieser Adapter
* verändert den Dateinamen von angehangenen Rechnungen * verändert den Dateinamen von angehangenen Rechnungen
* *
* Der Adapter wir beispielweise in DWC eingesetzt bevor die Dateien in einen * Der Adapter wir beispielweise in DWC eingesetzt bevor die Dateien in einen
* FTP Server hochgeladen werden. * FTP Server hochgeladen werden.
* *
* Die Dateinamen werden vom System berechnet. * Die Dateinamen werden vom System berechnet.
* *
* sequencenumber + "_" +invoice.positions+".pdf" * sequencenumber + "_" +invoice.positions+".pdf"
* *
* *
* Bei Sachrechnungen * Bei Sachrechnungen
* *
* sequencenumber + "_" + invoice.number.stripped + ".pdf" * sequencenumber + "_" + invoice.number.stripped + ".pdf" wird das
* wird das Rechnungsdatum verwendet um die Buchungsperiode zu berechnen. * Rechnungsdatum verwendet um die Buchungsperiode zu berechnen.
* *
* *
* *
* @author rsoika * @author rsoika
* @version 1.0 * @version 1.0
* *
*/ */
public class RenameFilenamesAdapter implements SignalAdapter { public class RenameFilenamesAdapter implements SignalAdapter {
private static Logger logger = Logger.getLogger(RenameFilenamesAdapter.class.getName()); private static Logger logger = Logger.getLogger(RenameFilenamesAdapter.class.getName());
@Inject @Inject
SnapshotService snapshotService; SnapshotService snapshotService;
/** /**
* Rename Filenames * Rename Filenames
* *
* @throws PluginException * @throws PluginException
* *
**/ **/
@Override @Override
public ItemCollection execute(ItemCollection workitem, ItemCollection event) public ItemCollection execute(ItemCollection workitem, ItemCollection event)
throws AdapterException, PluginException { throws AdapterException, PluginException {
int fileNameCounter = 0; // for multiple files int fileNameCounter = 0; // for multiple files
List<FileData> fileDataSet = workitem.getFileData(); List<FileData> fileDataSet = workitem.getFileData();
for (FileData fileData : fileDataSet) { for (FileData fileData : fileDataSet) {
String fileName = fileData.getName(); String fileName = fileData.getName();
// only PDF files and X-Rechnung // only PDF files and X-Rechnung
if ((fileName.toLowerCase().endsWith(".pdf") if ((fileName.toLowerCase().endsWith(".pdf") || fileName.toLowerCase().endsWith(".xml"))) {
|| fileName.toLowerCase().endsWith(".xml"))) {
// Build new Filename // Build new Filename
String newFileName = buildFileName(fileName, workitem, fileNameCounter); String newFileName = buildFileName(fileName, workitem, fileNameCounter);
if (!newFileName.equals(fileName)) { if (!newFileName.equals(fileName)) {
logger.info("Rename file name " + fileName + " -> " + newFileName); logger.info("Rename file name " + fileName + " -> " + newFileName);
// do we have content // do we have content
if (fileData.getContent().length < 10) { if (fileData.getContent().length < 10) {
// fetch snaphot data // fetch snaphot data
FileData snapShotFileData = snapshotService.getWorkItemFile(workitem.getUniqueID(), fileName); FileData snapShotFileData = snapshotService.getWorkItemFile(workitem.getUniqueID(), fileName);
// remove old data // remove old data
workitem.removeFile(fileName); workitem.removeFile(fileName);
snapShotFileData.setName(newFileName); snapShotFileData.setName(newFileName);
workitem.addFileData(snapShotFileData); workitem.addFileData(snapShotFileData);
} else { } else {
fileData.setName(newFileName); fileData.setName(newFileName);
} }
fileNameCounter++; fileNameCounter++;
} }
// Update FTP Target path information // Update FTP Target path information
String targetPath = ""; String targetPath = "";
if (InvoiceUtil.isCreditorInvoice(workitem)) { if (InvoiceUtil.isCreditorInvoice(workitem)) {
targetPath = "invoice-in/"; targetPath = "invoice-in/";
String bookingPath = workitem.getItemValueString("invoice.period"); String bookingPath = workitem.getItemValueString("invoice.period");
// Sachrechnung // Sachrechnung
if (bookingPath.isEmpty()) { if (bookingPath.isEmpty()) {
// build period from invoice.date // build period from invoice.date
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMM"); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMM");
LocalDate invoiceDate = workitem.getItemValueLocalDate("invoice.date"); LocalDate invoiceDate = workitem.getItemValueLocalDate("invoice.date");
bookingPath = invoiceDate.format(formatter); bookingPath = invoiceDate.format(formatter);
} }
if (bookingPath.length() > 4) { if (bookingPath.length() > 4) {
targetPath = targetPath + bookingPath.substring(0, 4) + "/" + bookingPath.substring(4); targetPath = targetPath + bookingPath.substring(0, 4) + "/" + bookingPath.substring(4);
} }
} else { } else {
targetPath = "invoice-out/"; targetPath = "invoice-out/";
String bookingPath = workitem.getItemValueString("invoice.bookingperiod"); String bookingPath = workitem.getItemValueString("invoice.bookingperiod");
targetPath = targetPath + bookingPath.substring(0, 4) + "/" + bookingPath.substring(4); targetPath = targetPath + bookingPath.substring(0, 4) + "/" + bookingPath.substring(4);
} }
workitem.setItemValue("ftp.target.path", targetPath + "/"); workitem.setItemValue("ftp.target.path", targetPath + "/");
} }
} }
return workitem; return workitem;
} }
/** /**
* Baut einen Filenamen aus verschiedenen Attributen zusammen. * Baut einen Filenamen aus verschiedenen Attributen zusammen.
* *
* *
* @param workitem * @param workitem
* @return * @return
*/ */
private String buildFileName(String fileName, ItemCollection workitem, int counter) { private String buildFileName(String fileName, ItemCollection workitem, int counter) {
String result = workitem.getItemValueString("sequencenumber") + "_"; String result = workitem.getItemValueString("sequencenumber") + "_";
if (InvoiceUtil.isSachrechnung(workitem)) { if (InvoiceUtil.isSachrechnung(workitem)) {
result = result + workitem.getItemValueString("invoice.number.stripped"); result = result + workitem.getItemValueString("invoice.number.stripped");
} else { } else {
// für ausgangsrechnungen skippen wir die Cargosoft XML Datei // für ausgangsrechnungen skippen wir die Cargosoft XML Datei
if (fileName.toLowerCase().endsWith(".xml")) { if (fileName.toLowerCase().endsWith(".xml")) {
return fileName; return fileName;
} }
// für die PDF Datei bauen wir einen neuen Namen // für die PDF Datei bauen wir einen neuen Namen
result = result + workitem.getItemValueString("invoice.positions"); result = result + workitem.getItemValueString("invoice.positions");
} }
if (counter > 0) { if (counter > 0) {
result = result + "_" + counter; result = result + "_" + counter;
} }
result = result + fileName.substring(fileName.lastIndexOf(".")); result = result + fileName.substring(fileName.lastIndexOf("."));
return result; return result;
} }
} }

View file

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

View file

@ -12,9 +12,9 @@ import jakarta.inject.Inject;
import jakarta.inject.Named; import jakarta.inject.Named;
/** /**
* Der SteuerListController dient dazu in einem Stuerbescheid * Der SteuerListController dient dazu in einem Stuerbescheid die einzelnen
* die einzelnen Zeilen auszugeben. * Zeilen auszugeben.
* *
* @author rsoika * @author rsoika
* *
*/ */
@ -32,7 +32,7 @@ public class SteuerListController implements Serializable {
/** /**
* Gibt die Zeilen aus einem importierten Steuerbeleg (eAkte) zurück * Gibt die Zeilen aus einem importierten Steuerbeleg (eAkte) zurück
* *
* @return * @return
*/ */
public List<ItemCollection> getRows() { public List<ItemCollection> getRows() {

View file

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

View file

@ -21,9 +21,9 @@ import jakarta.inject.Inject;
* Kredito erzeugt der Adapter automatisch eine neue Prozessinstanz. * Kredito erzeugt der Adapter automatisch eine neue Prozessinstanz.
* <p> * <p>
* Wurde die Rechnugn bereits einem Zahlungsavis zugeordnet passiert nichts. * Wurde die Rechnugn bereits einem Zahlungsavis zugeordnet passiert nichts.
* *
* *
* *
* @version 1.0 * @version 1.0
* @author rsoika * @author rsoika
*/ */
@ -40,7 +40,7 @@ public class ZahlungsavisAppendAdapter implements SignalAdapter {
/** /**
* This method finds or create the Zahlungsavis and adds a reference * This method finds or create the Zahlungsavis and adds a reference
* ($workitemref) to the current invoice. * ($workitemref) to the current invoice.
* *
* @throws PluginException * @throws PluginException
*/ */
@Override @Override
@ -54,7 +54,7 @@ public class ZahlungsavisAppendAdapter implements SignalAdapter {
/** /**
* Diese method hängt eine referenz der aktuellen Rechnung an den Zahlungsavis * Diese method hängt eine referenz der aktuellen Rechnung an den Zahlungsavis
* *
* @param document * @param document
* @throws PluginException * @throws PluginException
*/ */
@ -100,7 +100,7 @@ public class ZahlungsavisAppendAdapter implements SignalAdapter {
/** /**
* Prüft alle offenen Zahlugnsaviss und gibt den neuesten zur angegebenen * Prüft alle offenen Zahlugnsaviss und gibt den neuesten zur angegebenen
* cdrNumber zurück, oder null falls es keinen Offenen Zahlungsavis gibt. * cdrNumber zurück, oder null falls es keinen Offenen Zahlungsavis gibt.
* *
* @param cdrNumber * @param cdrNumber
* @return * @return
* @throws QueryException * @throws QueryException

View file

@ -18,7 +18,7 @@ import jakarta.inject.Named;
* <p> * <p>
* Zusätzlich bietet er die Summenberechnung an, bei der die Gutschriften * Zusätzlich bietet er die Summenberechnung an, bei der die Gutschriften
* abgezogen werden. * abgezogen werden.
* *
* @author rsoika * @author rsoika
* *
*/ */
@ -41,7 +41,7 @@ public class ZahlungsavisController implements Serializable {
* result is rounded to 2 digits. * result is rounded to 2 digits.
* <p> * <p>
* Gutschriften werden abgezogen * Gutschriften werden abgezogen
* *
* @param refids - list of workitem uniqueIds * @param refids - list of workitem uniqueIds
* @param item - name of the item to summarize * @param item - name of the item to summarize
* @return sum rounded to 2 digits * @return sum rounded to 2 digits

View file

@ -27,7 +27,7 @@ import jakarta.inject.Inject;
/** /**
* Der ZahlungsavisAdapter importiert eine Excel Datei aus einem Textblock * Der ZahlungsavisAdapter importiert eine Excel Datei aus einem Textblock
* *
* <pre> * <pre>
* {@code * {@code
<zahlungsavis name="textblock">textblock-ref</zahlungsavis> <zahlungsavis name="textblock">textblock-ref</zahlungsavis>
@ -38,7 +38,7 @@ import jakarta.inject.Inject;
* <p> * <p>
* Der Adapter erweitert den POIAdapter somit können felder aktualisiert werden * Der Adapter erweitert den POIAdapter somit können felder aktualisiert werden
* (siehe POIFineReplaceAdapter). * (siehe POIFineReplaceAdapter).
* *
* <p> * <p>
* Der Adapter kopiert die Rechnungsdaten in neue Zeilen, welche ab Zeilnenummer * Der Adapter kopiert die Rechnungsdaten in neue Zeilen, welche ab Zeilnenummer
* 16 eingefügt werden. * 16 eingefügt werden.
@ -46,7 +46,7 @@ import jakarta.inject.Inject;
* Dabei werden Gutschriften in spalte E und Rechnungen in Spalte F eingetrgen. * Dabei werden Gutschriften in spalte E und Rechnungen in Spalte F eingetrgen.
* <p> * <p>
* Abschliessend wird die Zelle 'TOTAL' noch aktualisiert. * Abschliessend wird die Zelle 'TOTAL' noch aktualisiert.
* *
* @version 1.0 * @version 1.0
* @author rsoika * @author rsoika
*/ */
@ -69,7 +69,7 @@ public class ZahlungsavisExportAdapter extends POIFindReplaceAdapter {
/** /**
* This method finds or create the Zahlungsavis and adds a reference * This method finds or create the Zahlungsavis and adds a reference
* ($workitemref) to the current invoice. * ($workitemref) to the current invoice.
* *
* @throws PluginException * @throws PluginException
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -121,7 +121,7 @@ public class ZahlungsavisExportAdapter extends POIFindReplaceAdapter {
* <p> * <p>
* The named cell 'TOTAL' should contain the summary formula. It will be * The named cell 'TOTAL' should contain the summary formula. It will be
* evaluated at the end. * evaluated at the end.
* *
* @throws PluginException * @throws PluginException
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -193,7 +193,7 @@ public class ZahlungsavisExportAdapter extends POIFindReplaceAdapter {
/** /**
* This method loads a text-block for a specified ref and appends the named * This method loads a text-block for a specified ref and appends the named
* fileData object of this document. * fileData object of this document.
* *
* @param document * @param document
* @throws PluginException * @throws PluginException
*/ */
@ -223,9 +223,9 @@ public class ZahlungsavisExportAdapter extends POIFindReplaceAdapter {
/** /**
* This method returns a text-block ItemCollection for a specified name. * This method returns a text-block ItemCollection for a specified name.
* *
* @param name in attribute txtname * @param name in attribute txtname
* *
* *
*/ */
public FileData loadTextBlockFileData(String name, String fileName) { public FileData loadTextBlockFileData(String name, String fileName) {

View file

@ -28,7 +28,7 @@ import jakarta.inject.Inject;
* <li>payment.amount * <li>payment.amount
* <p> * <p>
* <code>com.alexanderlogistics.ZahlungseingangSaldoAdapter</code> * <code>com.alexanderlogistics.ZahlungseingangSaldoAdapter</code>
* *
* @version 1.0 * @version 1.0
* @author rsoika * @author rsoika
*/ */
@ -51,7 +51,7 @@ public class ZahlungseingangSaldoAdapter implements SignalAdapter {
/** /**
* This method finds the outgoing invoices and updates the saldo * This method finds the outgoing invoices and updates the saldo
* (payment.total). * (payment.total).
* *
* @throws PluginException * @throws PluginException
*/ */
@Override @Override
@ -72,7 +72,7 @@ public class ZahlungseingangSaldoAdapter implements SignalAdapter {
/** /**
* Diese methode aktuallisiert alle referenzen der aktuellen Rechnungen * Diese methode aktuallisiert alle referenzen der aktuellen Rechnungen
* *
* @param workitem * @param workitem
* @throws PluginException * @throws PluginException
*/ */
@ -127,13 +127,11 @@ public class ZahlungseingangSaldoAdapter implements SignalAdapter {
String mainCurrency = invoiceService.getCurrenciesOutMain(); String mainCurrency = invoiceService.getCurrenciesOutMain();
if ((!invoiceCurrency.equals(paymentCurrency)) if ((!invoiceCurrency.equals(paymentCurrency))
&& (!invoiceCurrency.equals(mainCurrency) && && (!invoiceCurrency.equals(mainCurrency) && !paymentCurrency.equals(mainCurrency))) {
!paymentCurrency.equals(mainCurrency))) {
String message = resourceBundleHandler.findMessage("ERROR_PAYMENT4"); String message = resourceBundleHandler.findMessage("ERROR_PAYMENT4");
message = message.replace("{1}", mainCurrency); message = message.replace("{1}", mainCurrency);
throw new PluginException(PluginException.class.getName(), throw new PluginException(PluginException.class.getName(),
ZahlungseingangService.ERROR_PAYMENT_BOOKING_FAILED, ZahlungseingangService.ERROR_PAYMENT_BOOKING_FAILED, message);
message);
} }
if (!invoiceCurrency.equals(paymentCurrency)) { if (!invoiceCurrency.equals(paymentCurrency)) {
@ -142,8 +140,7 @@ public class ZahlungseingangSaldoAdapter implements SignalAdapter {
message = message.replace("{1}", paymentCurrency); message = message.replace("{1}", paymentCurrency);
message = message.replace("{2}", invoiceNumber); message = message.replace("{2}", invoiceNumber);
throw new PluginException(PluginException.class.getName(), throw new PluginException(PluginException.class.getName(),
ZahlungseingangService.ERROR_PAYMENT_BOOKING_FAILED, ZahlungseingangService.ERROR_PAYMENT_BOOKING_FAILED, message);
message);
} }
} }
@ -151,8 +148,7 @@ public class ZahlungseingangSaldoAdapter implements SignalAdapter {
* Falls die Währung der Rechnung von der Währung der Zahlung abweicht, dann * Falls die Währung der Rechnung von der Währung der Zahlung abweicht, dann
* rechnen wir den PaymentAmmout mit der Rate aus. * rechnen wir den PaymentAmmout mit der Rate aus.
*/ */
if ("EUR".equals(paymentCurrency) && if ("EUR".equals(paymentCurrency) && !invoiceCurrency.equals(paymentCurrency) && invoiceRate != 0) {
!invoiceCurrency.equals(paymentCurrency) && invoiceRate != 0) {
invoicePaymentAmount = invoicePaymentAmount * invoiceRate; invoicePaymentAmount = invoicePaymentAmount * invoiceRate;
// runde auf 2 Stellen // runde auf 2 Stellen
invoicePaymentAmount = (float) InvoiceUtil.round(invoicePaymentAmount); invoicePaymentAmount = (float) InvoiceUtil.round(invoicePaymentAmount);
@ -213,7 +209,7 @@ public class ZahlungseingangSaldoAdapter implements SignalAdapter {
/** /**
* Returns true wenn der Zahlungseingang bereits gebucht ist * Returns true wenn der Zahlungseingang bereits gebucht ist
* *
* @param workitem * @param workitem
* @return * @return
*/ */

View file

@ -1,26 +1,26 @@
/******************************************************************************* /*******************************************************************************
* Imixs Workflow * Imixs Workflow
* Copyright (C) 2001, 2011 Imixs Software Solutions GmbH, * Copyright (C) 2001, 2011 Imixs Software Solutions GmbH,
* http://www.imixs.com * http://www.imixs.com
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2 * as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version. * of the License, or (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details. * General Public License for more details.
* *
* You can receive a copy of the GNU General Public * You can receive a copy of the GNU General Public
* License at http://www.gnu.org/licenses/gpl.html * License at http://www.gnu.org/licenses/gpl.html
* *
* Project: * Project:
* http://www.imixs.org * http://www.imixs.org
* http://java.net/projects/imixs-workflow * http://java.net/projects/imixs-workflow
* *
* Contributors: * Contributors:
* Imixs Software Solutions GmbH - initial API and implementation * Imixs Software Solutions GmbH - initial API and implementation
* Ralph Soika - Software Developer * Ralph Soika - Software Developer
*******************************************************************************/ *******************************************************************************/
@ -49,9 +49,9 @@ import jakarta.inject.Inject;
/** /**
* Der ZahlungseingangService wird vom ZahlungseingangSaldoAdapter verwendet um * Der ZahlungseingangService wird vom ZahlungseingangSaldoAdapter verwendet um
* Ausgangsrechnungen zu akutalisieren. * Ausgangsrechnungen zu akutalisieren.
* *
* @author rsoika * @author rsoika
* *
*/ */
@DeclareRoles({ "org.imixs.ACCESSLEVEL.NOACCESS", "org.imixs.ACCESSLEVEL.READERACCESS", @DeclareRoles({ "org.imixs.ACCESSLEVEL.NOACCESS", "org.imixs.ACCESSLEVEL.READERACCESS",
@ -79,7 +79,7 @@ public class ZahlungseingangService {
/** /**
* Diese Method sucht eine Invoice mit Manager Rechnten * Diese Method sucht eine Invoice mit Manager Rechnten
* *
* @throws PluginException * @throws PluginException
*/ */
public ItemCollection findInvoice(String id) throws PluginException { public ItemCollection findInvoice(String id) throws PluginException {
@ -89,7 +89,7 @@ public class ZahlungseingangService {
/** /**
* * Diese Method processed eine Invoice mit Manager Rechnten * * Diese Method processed eine Invoice mit Manager Rechnten
* *
* @throws PluginException * @throws PluginException
* @throws ModelException * @throws ModelException
* @throws ProcessingErrorException * @throws ProcessingErrorException
@ -106,10 +106,10 @@ public class ZahlungseingangService {
/** /**
* The method returns a List of ItemCollection holding the payment details * The method returns a List of ItemCollection holding the payment details
* *
* *
* Convert the List of ItemCollections back into a List of Map elements * Convert the List of ItemCollections back into a List of Map elements
* *
* @param invoice * @param invoice
*/ */
@SuppressWarnings({ "rawtypes", "unchecked" }) @SuppressWarnings({ "rawtypes", "unchecked" })

View file

@ -15,23 +15,23 @@ import jakarta.enterprise.event.Observes;
import jakarta.inject.Inject; import jakarta.inject.Inject;
/** /**
* The PromptExampleAdapterXML is a CDI bean that creates XML examples based * The PromptExampleAdapterXML is a CDI bean that creates XML examples based on
* on the last 3 invoices. * the last 3 invoices.
* *
* The adapter only runs if the prompt template contains * The adapter only runs if the prompt template contains
* *
* <<EXAMPLES_XML>> * <<EXAMPLES_XML>>
* *
* Prompt example generated by this Adapter: * Prompt example generated by this Adapter:
* *
* Example: * Example:
* *
* <pre> * <pre>
* {@code * {@code
* 1. Company Data: * 1. Company Data:
* - Name and address of the supplier * - Name and address of the supplier
* - Supplier contact information (phone number, email, etc.) * - Supplier contact information (phone number, email, etc.)
* *
* 2. Invoice Data: * 2. Invoice Data:
* - Invoice number * - Invoice number
* - Invoice date * - Invoice date
@ -41,7 +41,7 @@ import jakarta.inject.Inject;
* *
* <invoice> * <invoice>
* <cdtr.name>...</cdtr.name> * <cdtr.name>...</cdtr.name>
* <invoice.number>...</invoice.number> * <invoice.number>...</invoice.number>
* <invoice.date>2024-12-31</invoice.date> * <invoice.date>2024-12-31</invoice.date>
* <invoice.duedate>2024-12-31</invoice.duedate> * <invoice.duedate>2024-12-31</invoice.duedate>
* <invoice.total type="double">1234.00</invoice.total> * <invoice.total type="double">1234.00</invoice.total>
@ -50,7 +50,7 @@ import jakarta.inject.Inject;
* </invoice> * </invoice>
* } * }
* </pre> * </pre>
* *
*/ */
public class PromptExampleAdapterXML { public class PromptExampleAdapterXML {
private static Logger logger = Logger.getLogger(PromptExampleAdapterXML.class.getName()); private static Logger logger = Logger.getLogger(PromptExampleAdapterXML.class.getName());
@ -79,8 +79,7 @@ public class PromptExampleAdapterXML {
if (invoices != null && invoices.size() > 0) { if (invoices != null && invoices.size() > 0) {
// Build an example for each invoice // Build an example for each invoice
for (ItemCollection invoice : invoices) { for (ItemCollection invoice : invoices) {
promptExamples = promptExamples + buildExampleDE(invoice, exampleHeaderData, promptExamples = promptExamples + buildExampleDE(invoice, exampleHeaderData, exampleHeaderXML);
exampleHeaderXML);
} }
} }
prompt = prompt.replace("<<EXAMPLES_XML>>", "" + promptExamples); prompt = prompt.replace("<<EXAMPLES_XML>>", "" + promptExamples);
@ -91,13 +90,13 @@ public class PromptExampleAdapterXML {
/** /**
* Helper Method that builds a prompt example out of an existing invoice: * Helper Method that builds a prompt example out of an existing invoice:
* *
* <pre> * <pre>
* {@code * {@code
* *
* <invoice> * <invoice>
* <cdtr.name>...</cdtr.name> * <cdtr.name>...</cdtr.name>
* <invoice.number>...</invoice.number> * <invoice.number>...</invoice.number>
* <invoice.date>2024-12-31</invoice.date> * <invoice.date>2024-12-31</invoice.date>
* <invoice.duedate>2024-12-31</invoice.duedate> * <invoice.duedate>2024-12-31</invoice.duedate>
* <invoice.total type="double">1234.00</invoice.total> * <invoice.total type="double">1234.00</invoice.total>
@ -106,7 +105,7 @@ public class PromptExampleAdapterXML {
* </invoice> * </invoice>
* } * }
* </pre> * </pre>
* *
* @param invoice * @param invoice
* @return * @return
*/ */
@ -126,14 +125,12 @@ public class PromptExampleAdapterXML {
example.append( example.append(
" <invoice.number>" + invoice.getItemValueString("invoice.number") + "</invoice.number>\n"); " <invoice.number>" + invoice.getItemValueString("invoice.number") + "</invoice.number>\n");
if (invoice.getItemValueDate("invoice.date") != null) { if (invoice.getItemValueDate("invoice.date") != null) {
example.append( example.append(" <invoice.date>" + dateFormat.format(invoice.getItemValueDate("invoice.date"))
" <invoice.date>" + dateFormat.format(invoice.getItemValueDate("invoice.date")) + "</invoice.date>\n");
+ "</invoice.date>\n");
} }
if (invoice.getItemValueDate("invoice.duedate") != null) { if (invoice.getItemValueDate("invoice.duedate") != null) {
example.append( example.append(" <invoice.duedate>" + dateFormat.format(invoice.getItemValueDate("invoice.duedate"))
" <invoice.duedate>" + dateFormat.format(invoice.getItemValueDate("invoice.duedate")) + "</invoice.duedate>\n");
+ "</invoice.duedate>\n");
} }
example.append(" <invoice.total type=\"double\">" + invoice.getItemValueDouble("invoice.total") example.append(" <invoice.total type=\"double\">" + invoice.getItemValueDouble("invoice.total")
+ "</invoice.total>\n"); + "</invoice.total>\n");
@ -146,7 +143,7 @@ public class PromptExampleAdapterXML {
/** /**
* Helper method to find the last 3 invoices by cdtr.name * Helper method to find the last 3 invoices by cdtr.name
* *
* @param cdtrName * @param cdtrName
* @return * @return
*/ */
@ -154,8 +151,7 @@ public class PromptExampleAdapterXML {
List<ItemCollection> result = new ArrayList<>(); List<ItemCollection> result = new ArrayList<>();
String searchTerm = "(type:workitemarchive) AND ($modelversion:rechnungseingang*) AND ($taskid:5900) AND NOT ($uniqueid:" String searchTerm = "(type:workitemarchive) AND ($modelversion:rechnungseingang*) AND ($taskid:5900) AND NOT ($uniqueid:"
+ currentID + ") AND (document.company:\"" + currentID + ") AND (document.company:\"" + companyName + "\")";
+ companyName + "\")";
try { try {
result = documentService.find(searchTerm, 3, 0, "$modified", true); result = documentService.find(searchTerm, 3, 0, "$modified", true);
} catch (QueryException e) { } catch (QueryException e) {

View file

@ -1,26 +1,26 @@
/******************************************************************************* /*******************************************************************************
* Imixs Workflow * Imixs Workflow
* Copyright (C) 2001, 2011 Imixs Software Solutions GmbH, * Copyright (C) 2001, 2011 Imixs Software Solutions GmbH,
* http://www.imixs.com * http://www.imixs.com
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2 * as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version. * of the License, or (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details. * General Public License for more details.
* *
* You can receive a copy of the GNU General Public * You can receive a copy of the GNU General Public
* License at http://www.gnu.org/licenses/gpl.html * License at http://www.gnu.org/licenses/gpl.html
* *
* Project: * Project:
* http://www.imixs.org * http://www.imixs.org
* http://java.net/projects/imixs-workflow * http://java.net/projects/imixs-workflow
* *
* Contributors: * Contributors:
* Imixs Software Solutions GmbH - initial API and implementation * Imixs Software Solutions GmbH - initial API and implementation
* Ralph Soika - Software Developer * Ralph Soika - Software Developer
*******************************************************************************/ *******************************************************************************/
@ -60,9 +60,9 @@ import jakarta.ws.rs.core.MediaType;
/** /**
** Dieser Service korrigiert falsch importierte Cargosoft daten. ** Dieser Service korrigiert falsch importierte Cargosoft daten.
* Ausgangsrechnungen die ein falsches Jahr haben. * Ausgangsrechnungen die ein falsches Jahr haben.
* *
* Oder DAten die über die XML Schnittstelle falsch reinkamen... * Oder DAten die über die XML Schnittstelle falsch reinkamen...
* *
* @author rsoika * @author rsoika
* @version 1.1 * @version 1.1
*/ */
@ -109,12 +109,12 @@ public class CargosoftMigrationRestService implements Serializable {
* Dieser agent speichert einfach alle Cargosoft Krediotren/Debitoren Daten , * Dieser agent speichert einfach alle Cargosoft Krediotren/Debitoren Daten ,
* was dann zur automatischen Neuanlage bzw. aktualisierung des businesspartner * was dann zur automatischen Neuanlage bzw. aktualisierung des businesspartner
* workflows führt. * workflows führt.
* *
* *
* curl -H "Cookie: * curl -H "Cookie:
* JSESSIONID=ShAegfNiXmqLFLSFwcUZ7hrLLQDKZGKiu4oj_xeX.imixs-office-workflow-8586d7d58d-h6k4q" * JSESSIONID=ShAegfNiXmqLFLSFwcUZ7hrLLQDKZGKiu4oj_xeX.imixs-office-workflow-8586d7d58d-h6k4q"
* https://alexander-logistics-dwc.office-workflow.de/api/cargosoft/bp-sync?maxcount=1000 * https://alexander-logistics-dwc.office-workflow.de/api/cargosoft/bp-sync?maxcount=1000
* *
* @return * @return
* @throws QueryException * @throws QueryException
* @throws AccessDeniedException * @throws AccessDeniedException
@ -145,8 +145,7 @@ public class CargosoftMigrationRestService implements Serializable {
isRunning = true; isRunning = true;
int totalCount = documentService.count(query); int totalCount = documentService.count(query);
log("│   ├── found " + totalCount + " cargosoft entries", log("│   ├── found " + totalCount + " cargosoft entries", messageBuffer);
messageBuffer);
// Berechne Anzahl der benötigten Pages // Berechne Anzahl der benötigten Pages
int totalPages = (int) Math.ceil((double) totalCount / batchSize); int totalPages = (int) Math.ceil((double) totalCount / batchSize);
@ -169,8 +168,8 @@ public class CargosoftMigrationRestService implements Serializable {
} }
// Fortschritt loggen // Fortschritt loggen
log("│ ├── Processed page " + (pageIndex + 1) + " of " + totalPages + log("│ ├── Processed page " + (pageIndex + 1) + " of " + totalPages + " (" + totalObjects
" (" + totalObjects + " objects read, " + syncs + " total syncs)", messageBuffer); + " objects read, " + syncs + " total syncs)", messageBuffer);
// Optional: Kurze Pause nach jedem 5. Batch // Optional: Kurze Pause nach jedem 5. Batch
try { try {
Thread.sleep(100); Thread.sleep(100);
@ -185,8 +184,8 @@ public class CargosoftMigrationRestService implements Serializable {
} }
long duration = System.currentTimeMillis() - l; long duration = System.currentTimeMillis() - l;
double objectsPerSecond = totalObjects / (duration / 1000.0); double objectsPerSecond = totalObjects / (duration / 1000.0);
log("├── Successfully " + syncs + " business partner objects synced in " + log("├── Successfully " + syncs + " business partner objects synced in " + duration + "ms ("
duration + "ms (" + String.format("%.1f", objectsPerSecond) + " objects/sec)", messageBuffer); + String.format("%.1f", objectsPerSecond) + " objects/sec)", messageBuffer);
isRunning = false; isRunning = false;
return messageBuffer.toString(); return messageBuffer.toString();
@ -194,7 +193,7 @@ public class CargosoftMigrationRestService implements Serializable {
/** /**
* Hilfsmethode speichert eine cargoosft kreditor object... * Hilfsmethode speichert eine cargoosft kreditor object...
* *
* @param bpID * @param bpID
* @param messageBuffer * @param messageBuffer
* @return * @return
@ -213,11 +212,11 @@ public class CargosoftMigrationRestService implements Serializable {
/** /**
* Dieser agent löscht einfach alle Businesspartner objekte. Wird eigentlich * Dieser agent löscht einfach alle Businesspartner objekte. Wird eigentlich
* nicht mehr benötigt. * nicht mehr benötigt.
* *
* curl -H "Cookie: * curl -H "Cookie:
* JSESSIONID=ShAegfNiXmqLFLSFwcUZ7hrLLQDKZGKiu4oj_xeX.imixs-office-workflow-8586d7d58d-h6k4q" * JSESSIONID=ShAegfNiXmqLFLSFwcUZ7hrLLQDKZGKiu4oj_xeX.imixs-office-workflow-8586d7d58d-h6k4q"
* https://alexander-logistics-dwc.office-workflow.de/api/cargosoft/bp-delete?maxcount=10000 * https://alexander-logistics-dwc.office-workflow.de/api/cargosoft/bp-delete?maxcount=10000
* *
* @return * @return
* @throws QueryException * @throws QueryException
* @throws AccessDeniedException * @throws AccessDeniedException
@ -273,8 +272,8 @@ public class CargosoftMigrationRestService implements Serializable {
// Fortschritt loggen // Fortschritt loggen
iterations++; iterations++;
log("│ ├── Processed page " + (iterations) + " of " + totalPages + log("│ ├── Processed page " + (iterations) + " of " + totalPages + " (" + deletions + " total deletions)",
" (" + deletions + " total deletions)", messageBuffer); messageBuffer);
// Optional: Kurze Pause nach jedem 5. Batch // Optional: Kurze Pause nach jedem 5. Batch
try { try {
@ -290,8 +289,8 @@ public class CargosoftMigrationRestService implements Serializable {
} }
long duration = System.currentTimeMillis() - l; long duration = System.currentTimeMillis() - l;
double objectsPerSecond = totalObjects / (duration / 1000.0); double objectsPerSecond = totalObjects / (duration / 1000.0);
log("├── Successfully " + deletions + " business partner objects deleted in " + log("├── Successfully " + deletions + " business partner objects deleted in " + duration + "ms ("
duration + "ms (" + String.format("%.1f", objectsPerSecond) + " objects/sec)", messageBuffer); + String.format("%.1f", objectsPerSecond) + " objects/sec)", messageBuffer);
isRunning = false; isRunning = false;
return messageBuffer.toString(); return messageBuffer.toString();
@ -299,7 +298,7 @@ public class CargosoftMigrationRestService implements Serializable {
/** /**
* Entfernt doppelte Eintrage - sind irgnedwie entstandnen :((( * Entfernt doppelte Eintrage - sind irgnedwie entstandnen :(((
* *
* @return * @return
* @throws QueryException * @throws QueryException
* @throws AccessDeniedException * @throws AccessDeniedException
@ -373,8 +372,8 @@ public class CargosoftMigrationRestService implements Serializable {
// Fortschritt loggen // Fortschritt loggen
// Fortschritt loggen // Fortschritt loggen
log("│ ├── Processed page " + (pageIndex + 1) + " of " + totalPages + log("│ ├── Processed page " + (pageIndex + 1) + " of " + totalPages + " (" + deletions
" (" + deletions + " total deletions)", messageBuffer); + " total deletions)", messageBuffer);
// Optional: Kurze Pause nach jedem 5. Batch // Optional: Kurze Pause nach jedem 5. Batch
try { try {
Thread.sleep(100); Thread.sleep(100);
@ -389,8 +388,8 @@ public class CargosoftMigrationRestService implements Serializable {
} }
long duration = System.currentTimeMillis() - l; long duration = System.currentTimeMillis() - l;
double objectsPerSecond = totalObjects / (duration / 1000.0); double objectsPerSecond = totalObjects / (duration / 1000.0);
log("├── Successfully " + deletions + " duplicated cargosoft objects deleted in " + log("├── Successfully " + deletions + " duplicated cargosoft objects deleted in " + duration + "ms ("
duration + "ms (" + String.format("%.1f", objectsPerSecond) + " objects/sec)", messageBuffer); + String.format("%.1f", objectsPerSecond) + " objects/sec)", messageBuffer);
isRunning = false; isRunning = false;
return messageBuffer.toString(); return messageBuffer.toString();
@ -398,7 +397,7 @@ public class CargosoftMigrationRestService implements Serializable {
/** /**
* Hilfsmethode - löscht ein cargoosoft object... * Hilfsmethode - löscht ein cargoosoft object...
* *
* @param bpID * @param bpID
* @param messageBuffer * @param messageBuffer
* @return * @return

View file

@ -1,27 +1,27 @@
/* /*
* Imixs-Workflow * Imixs-Workflow
* *
* Copyright (C) 2001-2020 Imixs Software Solutions GmbH, * Copyright (C) 2001-2020 Imixs Software Solutions GmbH,
* http://www.imixs.com * http://www.imixs.com
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2 * as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version. * of the License, or (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details. * General Public License for more details.
* *
* You can receive a copy of the GNU General Public * You can receive a copy of the GNU General Public
* License at http://www.gnu.org/licenses/gpl.html * License at http://www.gnu.org/licenses/gpl.html
* *
* Project: * Project:
* https://www.imixs.org * https://www.imixs.org
* https://github.com/imixs/imixs-workflow * https://github.com/imixs/imixs-workflow
* *
* Contributors: * Contributors:
* Imixs Software Solutions GmbH - Project Management * Imixs Software Solutions GmbH - Project Management
* Ralph Soika - Software Developer * Ralph Soika - Software Developer
*/ */
@ -58,7 +58,7 @@ import jakarta.ws.rs.core.Response;
/** /**
* The api endpoint '/data' provides methods to read and post data via a access * The api endpoint '/data' provides methods to read and post data via a access
* token * token
* *
* @version 1.0 * @version 1.0
* @author rsoika * @author rsoika
* *
@ -87,7 +87,7 @@ public class DataRestService {
/** /**
* Test method * Test method
* *
* @param workflowgroup * @param workflowgroup
* @param task * @param task
* @return * @return
@ -132,16 +132,14 @@ public class DataRestService {
* this token to the service. * this token to the service.
* <p> * <p>
* The method tests the access. If it is not possible the method returns null. * The method tests the access. If it is not possible the method returns null.
* *
* @see DefaultAuthenicator * @see DefaultAuthenicator
* @return * @return
*/ */
protected Client createClient(String serviceAPI, String sessionID) { protected Client createClient(String serviceAPI, String sessionID) {
// Create a Cookie object with a name and a value // Create a Cookie object with a name and a value
Cookie cookie = new Cookie.Builder("JSESSIONID") Cookie cookie = new Cookie.Builder("JSESSIONID").value(sessionID).path("/")
.value(sessionID)
.path("/")
// .domain("domain.com") // .domain("domain.com")
.build(); .build();
@ -173,7 +171,7 @@ public class DataRestService {
/** /**
* Searches a report by name * Searches a report by name
* *
* @param name * @param name
* @return * @return
* @throws UnsupportedEncodingException * @throws UnsupportedEncodingException

View file

@ -2,26 +2,26 @@ package com.alexanderlogistics.datev;
/******************************************************************************* /*******************************************************************************
* Imixs Workflow Technology * Imixs Workflow Technology
* Copyright (C) 2003, 2008 Imixs Software Solutions GmbH, * Copyright (C) 2003, 2008 Imixs Software Solutions GmbH,
* http://www.imixs.com * http://www.imixs.com
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2 * as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version. * of the License, or (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details. * General Public License for more details.
* *
* You can receive a copy of the GNU General Public * You can receive a copy of the GNU General Public
* License at http://www.gnu.org/licenses/gpl.html * License at http://www.gnu.org/licenses/gpl.html
* *
* Contributors: * Contributors:
* Imixs Software Solutions GmbH - initial API and implementation * Imixs Software Solutions GmbH - initial API and implementation
* Ralph Soika * Ralph Soika
* *
*******************************************************************************/ *******************************************************************************/
import java.util.logging.Logger; import java.util.logging.Logger;
@ -39,43 +39,43 @@ import jakarta.inject.Named;
* txtname=datev". * txtname=datev".
* <p> * <p>
* The following config items are defined: * The following config items are defined:
* *
* The following config items are defined: * The following config items are defined:
* *
* <pre> * <pre>
* _model_version = model version for the SEPA export * _model_version = model version for the SEPA export
* _initial_task = inital task ID * _initial_task = inital task ID
* </pre> * </pre>
* *
* *
* @author rsoika * @author rsoika
* *
*/ */
@Named @Named
@RequestScoped @RequestScoped
public class CargosoftExportController extends SchedulerController { public class CargosoftExportController extends SchedulerController {
public static final String CARGOSOFT_EXPORT_CONFIGURATION = "CARGOSOFT_EXPORT_CONFIGURATION"; public static final String CARGOSOFT_EXPORT_CONFIGURATION = "CARGOSOFT_EXPORT_CONFIGURATION";
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private static Logger logger = Logger.getLogger(CargosoftExportController.class.getName()); private static Logger logger = Logger.getLogger(CargosoftExportController.class.getName());
@Override @Override
public String getName() { public String getName() {
return CARGOSOFT_EXPORT_CONFIGURATION; return CARGOSOFT_EXPORT_CONFIGURATION;
} }
/** /**
* Returns the sepa scheduler class name. This name depends on the _export_type. * Returns the sepa scheduler class name. This name depends on the _export_type.
* *
* There are two export interfaces available - csv and XML * There are two export interfaces available - csv and XML
* *
*/ */
@Override @Override
public String getSchedulerClass() { public String getSchedulerClass() {
String schedulerClass = CargosoftExportScheduler.class.getName(); String schedulerClass = CargosoftExportScheduler.class.getName();
logger.finest("...... scheduler: " + schedulerClass); logger.finest("...... scheduler: " + schedulerClass);
return schedulerClass; return schedulerClass;
} }
} }

View file

@ -1,22 +1,22 @@
/******************************************************************************* /*******************************************************************************
* Imixs Workflow Technology * Imixs Workflow Technology
* Copyright (C) 2001, 2008 Imixs Software Solutions GmbH, * Copyright (C) 2001, 2008 Imixs Software Solutions GmbH,
* http://www.imixs.com * http://www.imixs.com
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2 * as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version. * of the License, or (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details. * General Public License for more details.
* *
* You can receive a copy of the GNU General Public * You can receive a copy of the GNU General Public
* License at http://www.gnu.org/licenses/gpl.html * License at http://www.gnu.org/licenses/gpl.html
* *
* Contributors: * Contributors:
* Imixs Software Solutions GmbH - initial API and implementation * Imixs Software Solutions GmbH - initial API and implementation
* Ralph Soika * Ralph Soika
*******************************************************************************/ *******************************************************************************/
@ -61,10 +61,10 @@ import jakarta.ejb.EJB;
* The class implements the interface * The class implements the interface
* _org.imixs.workflow.engine.scheduler.Scheduler_ and can be used in * _org.imixs.workflow.engine.scheduler.Scheduler_ and can be used in
* combination with the Imxis-Workflow Scheduler Service. * combination with the Imxis-Workflow Scheduler Service.
* *
* @see SchedulerService * @see SchedulerService
* @author rsoika * @author rsoika
* *
*/ */
public class CargosoftExportScheduler implements Scheduler { public class CargosoftExportScheduler implements Scheduler {
@ -94,9 +94,9 @@ public class CargosoftExportScheduler implements Scheduler {
/** /**
* This is the method which processes the timeout event depending on the running * This is the method which processes the timeout event depending on the running
* timer settings. * timer settings.
* *
* *
* *
* @param timer * @param timer
* @throws QueryException * @throws QueryException
*/ */
@ -198,7 +198,7 @@ public class CargosoftExportScheduler implements Scheduler {
/** /**
* Diese Hilfsmehtode überträgt eine Datei an einen FTP Server. Die * Diese Hilfsmehtode überträgt eine Datei an einen FTP Server. Die
* Verbindugnsdaten stehen im Configuraiton Workitem. * Verbindugnsdaten stehen im Configuraiton Workitem.
* *
* @param fileData object * @param fileData object
* @throws PluginException * @throws PluginException
*/ */

View file

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

View file

@ -17,13 +17,13 @@ import jakarta.inject.Inject;
/** /**
* Der InkassoAppendAdapter verknüpft eine Rechnung mit einem Inkasso Workflow * Der InkassoAppendAdapter verknüpft eine Rechnung mit einem Inkasso Workflow
* zum aktuellen Debitor. Existiert aktuell kein Inkasso für diesen * zum aktuellen Debitor. Existiert aktuell kein Inkasso für diesen Debitor
* Debitor erzeugt der Adapter automatisch eine neue Prozessinstanz. * erzeugt der Adapter automatisch eine neue Prozessinstanz.
* <p> * <p>
* Wurde die Rechnung bereits einem Inkasso zugeordnet passiert nichts. * Wurde die Rechnung bereits einem Inkasso zugeordnet passiert nichts.
* *
* *
* *
* @version 1.0 * @version 1.0
* @author rsoika * @author rsoika
*/ */
@ -41,9 +41,9 @@ public class InkassoAppendAdapter implements SignalAdapter {
WorkflowService workflowService; WorkflowService workflowService;
/** /**
* This method finds or create the Inkasso and adds a reference * This method finds or create the Inkasso and adds a reference ($workitemref)
* ($workitemref) to the current invoice. * to the current invoice.
* *
* @throws PluginException * @throws PluginException
*/ */
@Override @Override
@ -59,9 +59,9 @@ public class InkassoAppendAdapter implements SignalAdapter {
/** /**
* Diese method hängt eine referenz der aktuellen Rechnung an den Inkasso * Diese method hängt eine referenz der aktuellen Rechnung an den Inkasso
* Workflow an. Gibt es noch keinen, wird einer erzeugt. * Workflow an. Gibt es noch keinen, wird einer erzeugt. Die method liefert das
* Die method liefert das Inkasso Workitem zurück. * Inkasso Workitem zurück.
* *
* @param document * @param document
* @throws PluginException * @throws PluginException
*/ */
@ -103,7 +103,7 @@ public class InkassoAppendAdapter implements SignalAdapter {
/** /**
* Prüft alle offenen Inkasso Workflows und gibt den neuesten zur angegebenen * Prüft alle offenen Inkasso Workflows und gibt den neuesten zur angegebenen
* dbtrNumber zurück, oder null falls es keinen Offenen gibt. * dbtrNumber zurück, oder null falls es keinen Offenen gibt.
* *
* @param dbtrNumber * @param dbtrNumber
* @return * @return
* @throws QueryException * @throws QueryException

View file

@ -15,12 +15,12 @@ import jakarta.inject.Inject;
import jakarta.inject.Named; import jakarta.inject.Named;
/** /**
* Der InkassoController dient dazu eine bereits zugewiesene Rechnung * Der InkassoController dient dazu eine bereits zugewiesene Rechnung wieder aus
* wieder aus dem Inkasso zu entfernen. * dem Inkasso zu entfernen.
* <p> * <p>
* Zusätzlich bietet er die Summenberechnung an, bei der die Gutschriften * Zusätzlich bietet er die Summenberechnung an, bei der die Gutschriften
* abgezogen werden. * abgezogen werden.
* *
* @author rsoika * @author rsoika
* *
*/ */
@ -43,7 +43,7 @@ public class InkassoController implements Serializable {
* result is rounded to 2 digits. * result is rounded to 2 digits.
* <p> * <p>
* Gutschriften werden abgezogen * Gutschriften werden abgezogen
* *
* @param refids - list of workitem uniqueIds * @param refids - list of workitem uniqueIds
* @param item - name of the item to summarize * @param item - name of the item to summarize
* @return sum rounded to 2 digits * @return sum rounded to 2 digits

View file

@ -27,7 +27,7 @@ import jakarta.inject.Inject;
/** /**
* Der ZahlungsavisAdapter importiert eine Excel Datei aus einem Textblock * Der ZahlungsavisAdapter importiert eine Excel Datei aus einem Textblock
* *
* <pre> * <pre>
* {@code * {@code
<inkasso name="textblock">textblock-ref</inkasso> <inkasso name="textblock">textblock-ref</inkasso>
@ -38,7 +38,7 @@ import jakarta.inject.Inject;
* <p> * <p>
* Der Adapter erweitert den POIAdapter somit können felder aktualisiert werden * Der Adapter erweitert den POIAdapter somit können felder aktualisiert werden
* (siehe POIFineReplaceAdapter). * (siehe POIFineReplaceAdapter).
* *
* <p> * <p>
* Der Adapter kopiert die Rechnungsdaten in neue Zeilen, welche ab Zeilnenummer * Der Adapter kopiert die Rechnungsdaten in neue Zeilen, welche ab Zeilnenummer
* 16 eingefügt werden. * 16 eingefügt werden.
@ -46,7 +46,7 @@ import jakarta.inject.Inject;
* Dabei werden Gutschriften in spalte E und Rechnungen in Spalte F eingetrgen. * Dabei werden Gutschriften in spalte E und Rechnungen in Spalte F eingetrgen.
* <p> * <p>
* Abschliessend wird die Zelle 'TOTAL' noch aktualisiert. * Abschliessend wird die Zelle 'TOTAL' noch aktualisiert.
* *
* @version 1.0 * @version 1.0
* @author rsoika * @author rsoika
*/ */
@ -69,7 +69,7 @@ public class InkassoExportAdapter extends POIFindReplaceAdapter {
/** /**
* This method finds or create the Zahlungsavis and adds a reference * This method finds or create the Zahlungsavis and adds a reference
* ($workitemref) to the current invoice. * ($workitemref) to the current invoice.
* *
* @throws PluginException * @throws PluginException
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -121,7 +121,7 @@ public class InkassoExportAdapter extends POIFindReplaceAdapter {
* <p> * <p>
* The named cell 'TOTAL' should contain the summary formula. It will be * The named cell 'TOTAL' should contain the summary formula. It will be
* evaluated at the end. * evaluated at the end.
* *
* @throws PluginException * @throws PluginException
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -186,7 +186,7 @@ public class InkassoExportAdapter extends POIFindReplaceAdapter {
/** /**
* This method loads a text-block for a specified ref and appends the named * This method loads a text-block for a specified ref and appends the named
* fileData object of this document. * fileData object of this document.
* *
* @param document * @param document
* @throws PluginException * @throws PluginException
*/ */
@ -216,9 +216,9 @@ public class InkassoExportAdapter extends POIFindReplaceAdapter {
/** /**
* This method returns a text-block ItemCollection for a specified name. * This method returns a text-block ItemCollection for a specified name.
* *
* @param name in attribute txtname * @param name in attribute txtname
* *
* *
*/ */
public FileData loadTextBlockFileData(String name, String fileName) { public FileData loadTextBlockFileData(String name, String fileName) {

View file

@ -29,10 +29,10 @@ import jakarta.inject.Inject;
* processed invoices. This item is used in the Workflow Model to construct a * processed invoices. This item is used in the Workflow Model to construct a
* HTML Table wen sending the mail. * HTML Table wen sending the mail.
* <p> * <p>
* The Adapter also splits the item 'dbtr.mail' into a multi value field if * The Adapter also splits the item 'dbtr.mail' into a multi value field if the
* the value contains email addresses separated with new lines. * value contains email addresses separated with new lines.
* *
* *
* @version 1.0 * @version 1.0
* @author rsoika * @author rsoika
*/ */
@ -52,7 +52,7 @@ public class MahnlaufExecuteAdapter implements SignalAdapter {
/** /**
* This method finds all invices ($workitemref) and tirgges event 300 * This method finds all invices ($workitemref) and tirgges event 300
* *
* @throws PluginException * @throws PluginException
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -125,24 +125,24 @@ public class MahnlaufExecuteAdapter implements SignalAdapter {
} }
switch (invoice.getTaskID()) { switch (invoice.getTaskID()) {
case 5200: case 5200:
case 5201: case 5201:
if (dunningLevel < 1) if (dunningLevel < 1)
dunningLevel = 1; dunningLevel = 1;
break; break;
case 5210: case 5210:
case 5211: case 5211:
if (dunningLevel < 2) if (dunningLevel < 2)
dunningLevel = 2; dunningLevel = 2;
break; break;
case 5220: case 5220:
if (dunningLevel < 3) if (dunningLevel < 3)
dunningLevel = 3; dunningLevel = 3;
break; break;
default: default:
if (dunningLevel < 1) if (dunningLevel < 1)
dunningLevel = 1; dunningLevel = 1;
break; break;
} }
// create a workitem stub for the mail message // create a workitem stub for the mail message
@ -179,18 +179,18 @@ public class MahnlaufExecuteAdapter implements SignalAdapter {
// compute subject // compute subject
switch (dunningLevel) { switch (dunningLevel) {
case 2: case 2:
mahnLaufWorkitem.setItemValue("mail.subject_de", "2. MAHNUNG"); mahnLaufWorkitem.setItemValue("mail.subject_de", "2. MAHNUNG");
mahnLaufWorkitem.setItemValue("mail.subject_en", "2. REMINDER"); mahnLaufWorkitem.setItemValue("mail.subject_en", "2. REMINDER");
break; break;
case 3: case 3:
mahnLaufWorkitem.setItemValue("mail.subject_de", "LETZTE AUSSERGERICHTLICHE MAHNUNG"); mahnLaufWorkitem.setItemValue("mail.subject_de", "LETZTE AUSSERGERICHTLICHE MAHNUNG");
mahnLaufWorkitem.setItemValue("mail.subject_en", "LAST REMINDER"); mahnLaufWorkitem.setItemValue("mail.subject_en", "LAST REMINDER");
break; break;
default: default:
mahnLaufWorkitem.setItemValue("mail.subject_de", "1. MAHNUNG"); mahnLaufWorkitem.setItemValue("mail.subject_de", "1. MAHNUNG");
mahnLaufWorkitem.setItemValue("mail.subject_en", "1. REMINDER"); mahnLaufWorkitem.setItemValue("mail.subject_en", "1. REMINDER");
break; break;
} }
// Verify the dbtr.mail item (split into multi value) // Verify the dbtr.mail item (split into multi value)
@ -198,8 +198,7 @@ public class MahnlaufExecuteAdapter implements SignalAdapter {
for (String mail : mailListe) { for (String mail : mailListe) {
logger.fine(" test mail address:'" + mail + "'"); logger.fine(" test mail address:'" + mail + "'");
if (!InvoiceUtil.validateEmail(mail)) { if (!InvoiceUtil.validateEmail(mail)) {
throw new PluginException(MahnlaufExecuteAdapter.class.getName(), throw new PluginException(MahnlaufExecuteAdapter.class.getName(), "INVALID_EMAIL",
"INVALID_EMAIL",
"Die Daten konnten nicht aktualisiert werden! Bitte geben Sie eine gültige E-Mail Adresse ein!"); "Die Daten konnten nicht aktualisiert werden! Bitte geben Sie eine gültige E-Mail Adresse ein!");
} }
} }
@ -208,7 +207,7 @@ public class MahnlaufExecuteAdapter implements SignalAdapter {
/** /**
* Returns true if the reminder date is today or in the past * Returns true if the reminder date is today or in the past
* *
* @param uniqueid * @param uniqueid
* @return * @return
*/ */

View file

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

View file

@ -1,26 +1,26 @@
/******************************************************************************* /*******************************************************************************
* Imixs Workflow * Imixs Workflow
* Copyright (C) 2001, 2011 Imixs Software Solutions GmbH, * Copyright (C) 2001, 2011 Imixs Software Solutions GmbH,
* http://www.imixs.com * http://www.imixs.com
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2 * as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version. * of the License, or (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details. * General Public License for more details.
* *
* You can receive a copy of the GNU General Public * You can receive a copy of the GNU General Public
* License at http://www.gnu.org/licenses/gpl.html * License at http://www.gnu.org/licenses/gpl.html
* *
* Project: * Project:
* http://www.imixs.org * http://www.imixs.org
* http://java.net/projects/imixs-workflow * http://java.net/projects/imixs-workflow
* *
* Contributors: * Contributors:
* Imixs Software Solutions GmbH - initial API and implementation * Imixs Software Solutions GmbH - initial API and implementation
* Ralph Soika - Software Developer * Ralph Soika - Software Developer
*******************************************************************************/ *******************************************************************************/
@ -49,11 +49,11 @@ import jakarta.ejb.Singleton;
import jakarta.inject.Inject; import jakarta.inject.Inject;
/** /**
* Der MahnlaufService wird vom MahnlaufExecuteAdapter um * Der MahnlaufService wird vom MahnlaufExecuteAdapter um Ausgangsrechnungen zu
* Ausgangsrechnungen zu akutalisieren. * akutalisieren.
* *
* @author rsoika * @author rsoika
* *
*/ */
@DeclareRoles({ "org.imixs.ACCESSLEVEL.NOACCESS", "org.imixs.ACCESSLEVEL.READERACCESS", @DeclareRoles({ "org.imixs.ACCESSLEVEL.NOACCESS", "org.imixs.ACCESSLEVEL.READERACCESS",
@ -87,7 +87,7 @@ public class MahnlaufService {
/** /**
* * Diese Method processed eine Invoice mit Manager Rechnten * * Diese Method processed eine Invoice mit Manager Rechnten
* *
* @throws PluginException * @throws PluginException
* @throws ModelException * @throws ModelException
* @throws ProcessingErrorException * @throws ProcessingErrorException
@ -104,7 +104,7 @@ public class MahnlaufService {
/** /**
* Helper method that loads all expressions from the spaces * Helper method that loads all expressions from the spaces
* *
* @return List of list of expressions * @return List of list of expressions
*/ */
public Map<String, List<String>> loadSpacePosMappings() { public Map<String, List<String>> loadSpacePosMappings() {
@ -125,7 +125,7 @@ public class MahnlaufService {
/** /**
* Hilfsmethode zum errechnen des richtigen Spaces zu einer invoice.text Positon * Hilfsmethode zum errechnen des richtigen Spaces zu einer invoice.text Positon
* *
* @param workitem * @param workitem
* @param spacePosMappings * @param spacePosMappings
*/ */

View file

@ -1,27 +1,27 @@
/* /*
* Imixs-Workflow * Imixs-Workflow
* *
* Copyright (C) 2001-2020 Imixs Software Solutions GmbH, * Copyright (C) 2001-2020 Imixs Software Solutions GmbH,
* http://www.imixs.com * http://www.imixs.com
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2 * as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version. * of the License, or (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details. * General Public License for more details.
* *
* You can receive a copy of the GNU General Public * You can receive a copy of the GNU General Public
* License at http://www.gnu.org/licenses/gpl.html * License at http://www.gnu.org/licenses/gpl.html
* *
* Project: * Project:
* https://www.imixs.org * https://www.imixs.org
* https://github.com/imixs/imixs-workflow * https://github.com/imixs/imixs-workflow
* *
* Contributors: * Contributors:
* Imixs Software Solutions GmbH - Project Management * Imixs Software Solutions GmbH - Project Management
* Ralph Soika - Software Developer * Ralph Soika - Software Developer
*/ */
@ -59,11 +59,10 @@ import jakarta.enterprise.event.Observes;
/** /**
* Der BusinessPartnerImportService hängt sich an den standard CSVImport Service * Der BusinessPartnerImportService hängt sich an den standard CSVImport Service
* der die Cargosoft Stammdaten aktualisiert. * der die Cargosoft Stammdaten aktualisiert. Es ist ein simples Observer
* Es ist ein simples Observer Pattern das auf das Document Event * Pattern das auf das Document Event "ON_DOCUMENT_SAVE" reagiert. Hier hängt
* "ON_DOCUMENT_SAVE" reagiert. Hier hängt sich der BusinessPartnerImportService * sich der BusinessPartnerImportService ein und prüft ob der Workflow schon
* ein und prüft ob der Workflow schon existiert oder ggf. aktualisiert werden * existiert oder ggf. aktualisiert werden muss.
* muss.
* <p> * <p>
* Der Service migriert auch die alten zusätzlichen IBAN/BIC felder wenn der * Der Service migriert auch die alten zusätzlichen IBAN/BIC felder wenn der
* Business partner erstmals neu angelegt wird. * Business partner erstmals neu angelegt wird.
@ -73,7 +72,7 @@ import jakarta.enterprise.event.Observes;
* <p> * <p>
* Im Cargosoft Entity wird zusätzlich das flag NOSNAPSHOT=true gesetzt. Dies * Im Cargosoft Entity wird zusätzlich das flag NOSNAPSHOT=true gesetzt. Dies
* ist nur eine Performance Optimierung. * ist nur eine Performance Optimierung.
* *
* @author rsoika * @author rsoika
* *
*/ */
@ -107,8 +106,8 @@ public class BusinessPartnerImportService {
/** /**
* Die Methode reagiert auf das CDI DocumentEvent beim Import von cargosoft * Die Methode reagiert auf das CDI DocumentEvent beim Import von cargosoft
* cargosoftkreditor documenten. * cargosoftkreditor documenten.
* *
* *
*/ */
public void onEvent(@Observes DocumentEvent event) { public void onEvent(@Observes DocumentEvent event) {
@ -138,9 +137,9 @@ public class BusinessPartnerImportService {
/** /**
* Syncnrhonisert ein drecks Cargosoft Objekt mit dem neuen Busienss partner * Syncnrhonisert ein drecks Cargosoft Objekt mit dem neuen Busienss partner
* objekt * objekt
* *
* Gibt true urück wenn sich was verädnert hat * Gibt true urück wenn sich was verädnert hat
* *
* @param creditor * @param creditor
*/ */
public boolean syncCargosoftCreditorBusinessPartner(ItemCollection importDoc) { public boolean syncCargosoftCreditorBusinessPartner(ItemCollection importDoc) {
@ -218,7 +217,7 @@ public class BusinessPartnerImportService {
/** /**
* Erstellt ein neues leeres Business Partner Object * Erstellt ein neues leeres Business Partner Object
* *
* @return * @return
*/ */
private ItemCollection createBusinessPartner(String partnerID) { private ItemCollection createBusinessPartner(String partnerID) {
@ -340,7 +339,7 @@ public class BusinessPartnerImportService {
/** /**
* Sucht nach einem Business partner * Sucht nach einem Business partner
* *
* @param id * @param id
* @return * @return
*/ */

View file

@ -1,27 +1,27 @@
/* /*
* Imixs-Workflow * Imixs-Workflow
* *
* Copyright (C) 2001-2020 Imixs Software Solutions GmbH, * Copyright (C) 2001-2020 Imixs Software Solutions GmbH,
* http://www.imixs.com * http://www.imixs.com
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2 * as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version. * of the License, or (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details. * General Public License for more details.
* *
* You can receive a copy of the GNU General Public * You can receive a copy of the GNU General Public
* License at http://www.gnu.org/licenses/gpl.html * License at http://www.gnu.org/licenses/gpl.html
* *
* Project: * Project:
* https://www.imixs.org * https://www.imixs.org
* https://github.com/imixs/imixs-workflow * https://github.com/imixs/imixs-workflow
* *
* Contributors: * Contributors:
* Imixs Software Solutions GmbH - Project Management * Imixs Software Solutions GmbH - Project Management
* Ralph Soika - Software Developer * Ralph Soika - Software Developer
*/ */
@ -89,20 +89,19 @@ import jakarta.enterprise.event.Observes;
import jakarta.inject.Inject; import jakarta.inject.Inject;
/** /**
* Der CargosoftXMLEAkteImportService erweitert den FTPImportService und * Der CargosoftXMLEAkteImportService erweitert den FTPImportService und kann
* kann XML EAkten (Steuerbescheide) von Cargosoft importieren. * XML EAkten (Steuerbescheide) von Cargosoft importieren. Der Service reagiert
* Der Service reagiert auf DocumentImportEvents und kann über das Options Feld * auf DocumentImportEvents und kann über das Options Feld konfiguriert werden.
* konfiguriert werden.
* <p> * <p>
* Im Gegensatz zum FTPImportService werden die XML Dateien nach einem * Im Gegensatz zum FTPImportService werden die XML Dateien nach einem
* speziellen Verfahren geparsed und dann als Steuerbescheid importiert. * speziellen Verfahren geparsed und dann als Steuerbescheid importiert.
* <p> * <p>
* In den Optionen muss die option "mandant.id" hinterlegt sein. Stimmt * In den Optionen muss die option "mandant.id" hinterlegt sein. Stimmt diese
* diese beim Import nicht mit der Mandanten Nummer in der XML Datei überein, * beim Import nicht mit der Mandanten Nummer in der XML Datei überein, wird das
* wird das XML Dokument gelöscht und nicht importiert. * XML Dokument gelöscht und nicht importiert.
* *
* *
* *
* @author rsoika * @author rsoika
* *
*/ */
@ -135,8 +134,8 @@ public class CargosoftXMLEAkteImportService {
/** /**
* This method reacts on a CDI ImportEvent and reads documents form a ftp * This method reacts on a CDI ImportEvent and reads documents form a ftp
* server. * server.
* *
* *
*/ */
public void onEvent(@Observes DocumentImportEvent event) { public void onEvent(@Observes DocumentImportEvent event) {
@ -162,8 +161,7 @@ public class CargosoftXMLEAkteImportService {
return; return;
} }
documentImportService.logMessage("...CARGOSOFT_EAKTE_XML Import started: mandant.id=" + mandantID, documentImportService.logMessage("...CARGOSOFT_EAKTE_XML Import started: mandant.id=" + mandantID, event);
event);
String ftpServer = event.getSource().getItemValueString(DocumentImportService.SOURCE_ITEM_SERVER); String ftpServer = event.getSource().getItemValueString(DocumentImportService.SOURCE_ITEM_SERVER);
String ftpPort = event.getSource().getItemValueString(DocumentImportService.SOURCE_ITEM_PORT); String ftpPort = event.getSource().getItemValueString(DocumentImportService.SOURCE_ITEM_PORT);
String ftpUser = event.getSource().getItemValueString(DocumentImportService.SOURCE_ITEM_USER); String ftpUser = event.getSource().getItemValueString(DocumentImportService.SOURCE_ITEM_USER);
@ -216,8 +214,8 @@ public class CargosoftXMLEAkteImportService {
ftpClient.connect(ftpServer, ftpPort); ftpClient.connect(ftpServer, ftpPort);
if (ftpClient.login(ftpUser, ftpPassword) == false) { if (ftpClient.login(ftpUser, ftpPassword) == false) {
throw new PluginException(CargosoftXMLEAkteImportService.class.getName(), throw new PluginException(CargosoftXMLEAkteImportService.class.getName(), "FTP_ERROR",
"FTP_ERROR", "FTP file transfer failed: login failed!"); "FTP file transfer failed: login failed!");
} }
ftpClient.enterLocalPassiveMode(); ftpClient.enterLocalPassiveMode();
logger.finest("...... FileType=" + FTP.BINARY_FILE_TYPE); logger.finest("...... FileType=" + FTP.BINARY_FILE_TYPE);
@ -225,8 +223,7 @@ public class CargosoftXMLEAkteImportService {
ftpClient.setControlEncoding("UTF-8"); ftpClient.setControlEncoding("UTF-8");
} catch (IOException e) { } catch (IOException e) {
throw new PluginException(CargosoftXMLEAkteImportService.class.getName(), throw new PluginException(CargosoftXMLEAkteImportService.class.getName(), "FTP_ERROR", e.getMessage());
"FTP_ERROR", e.getMessage());
} }
return ftpClient; return ftpClient;
@ -234,7 +231,7 @@ public class CargosoftXMLEAkteImportService {
/** /**
* Helper method reads the working FTP directory and starts importing the files. * Helper method reads the working FTP directory and starts importing the files.
* *
* @param ftpClient * @param ftpClient
* @param ftpPath * @param ftpPath
* @throws PluginException * @throws PluginException
@ -250,8 +247,8 @@ public class CargosoftXMLEAkteImportService {
// try to enter the working directory and read all files... // try to enter the working directory and read all files...
boolean bWorkingDir = ftpClient.changeWorkingDirectory(ftpPath); boolean bWorkingDir = ftpClient.changeWorkingDirectory(ftpPath);
if (bWorkingDir == false) { if (bWorkingDir == false) {
throw new PluginException(CargosoftXMLEAkteImportService.class.getName(), throw new PluginException(CargosoftXMLEAkteImportService.class.getName(), "FTP_ERROR",
"FTP_ERROR", "...failed to change into working directory: "); "...failed to change into working directory: ");
} }
FTPFile[] allFiles = ftpClient.listFiles(ftpPath); FTPFile[] allFiles = ftpClient.listFiles(ftpPath);
@ -299,10 +296,10 @@ public class CargosoftXMLEAkteImportService {
// "processed/"); // "processed/");
// } // }
} catch (AccessDeniedException | ProcessingErrorException | PluginException } catch (AccessDeniedException | ProcessingErrorException | PluginException | ModelException
| ModelException | XPathExpressionException | TransformerException e) { | XPathExpressionException | TransformerException e) {
throw new PluginException(CargosoftXMLEAkteImportService.class.getName(), throw new PluginException(CargosoftXMLEAkteImportService.class.getName(), "FTP_ERROR",
"FTP_ERROR", e.getMessage()); e.getMessage());
} }
} }
documentImportService.logMessage("..." + count + " new files imported.", event); documentImportService.logMessage("..." + count + " new files imported.", event);
@ -313,8 +310,7 @@ public class CargosoftXMLEAkteImportService {
int r = ftpClient.getReplyCode(); int r = ftpClient.getReplyCode();
logger.severe("FTP ReplyCode=" + r); logger.severe("FTP ReplyCode=" + r);
throw new PluginException(CargosoftXMLEAkteImportService.class.getName(), throw new PluginException(CargosoftXMLEAkteImportService.class.getName(), "FTP_ERROR", e.getMessage());
"FTP_ERROR", e.getMessage());
} finally { } finally {
// do logout.... // do logout....
@ -324,8 +320,7 @@ public class CargosoftXMLEAkteImportService {
ftpClient.disconnect(); ftpClient.disconnect();
} catch (IOException e) { } catch (IOException e) {
documentImportService.logMessage("...FTP file transfer failed: " + e.getMessage(), event); documentImportService.logMessage("...FTP file transfer failed: " + e.getMessage(), event);
throw new PluginException(CargosoftXMLEAkteImportService.class.getName(), throw new PluginException(CargosoftXMLEAkteImportService.class.getName(), "FTP_ERROR", e.getMessage());
"FTP_ERROR", e.getMessage());
} }
} }
} }
@ -333,8 +328,8 @@ public class CargosoftXMLEAkteImportService {
/** /**
* Creates and processes a new workitem with a given xml information from the * Creates and processes a new workitem with a given xml information from the
* filedata * filedata
* *
* *
* @return * @return
* @throws ModelException * @throws ModelException
* @throws PluginException * @throws PluginException
@ -344,9 +339,8 @@ public class CargosoftXMLEAkteImportService {
* @throws XPathExpressionException * @throws XPathExpressionException
*/ */
public ItemCollection createWorkitem(ItemCollection source, String fileName, byte[] rawData, public ItemCollection createWorkitem(ItemCollection source, String fileName, byte[] rawData,
Map<String, List<String>> spacePosMappings) Map<String, List<String>> spacePosMappings) throws AccessDeniedException, ProcessingErrorException,
throws AccessDeniedException, ProcessingErrorException, PluginException, ModelException, PluginException, ModelException, XPathExpressionException, TransformerException {
XPathExpressionException, TransformerException {
ItemCollection workitem = new ItemCollection(); ItemCollection workitem = new ItemCollection();
workitem.model(source.getItemValueString(DocumentImportService.SOURCE_ITEM_MODELVERSION)); workitem.model(source.getItemValueString(DocumentImportService.SOURCE_ITEM_MODELVERSION));
workitem.task(source.getItemValueInteger(DocumentImportService.SOURCE_ITEM_TASK)); workitem.task(source.getItemValueInteger(DocumentImportService.SOURCE_ITEM_TASK));
@ -375,37 +369,37 @@ public class CargosoftXMLEAkteImportService {
// invoice.rate // invoice.rate
// invoice.base.amount // invoice.base.amount
readXMLValue(doc, "/CargoSoftEFile/EFile/References/Reference[@type='client']", workitem, readXMLValue(doc, "/CargoSoftEFile/EFile/References/Reference[@type='client']", workitem, "mandant.id",
"mandant.id", String.class); String.class);
readXMLValue(doc, "/CargoSoftEFile/EFile/References/Reference[@type='cs_voucher_number']", readXMLValue(doc, "/CargoSoftEFile/EFile/References/Reference[@type='cs_voucher_number']", workitem,
workitem, "invoice.number", String.class); "invoice.number", String.class);
readXMLValue(doc, "/CargoSoftEFile/EFile/References/Reference[@type='cs_address_number']", readXMLValue(doc, "/CargoSoftEFile/EFile/References/Reference[@type='cs_address_number']", workitem,
workitem, "cdtr.number", String.class); "cdtr.number", String.class);
readXMLValue(doc, "/CargoSoftEFile/EFile/References/Reference[@type='voucherdate']", readXMLValue(doc, "/CargoSoftEFile/EFile/References/Reference[@type='voucherdate']", workitem,
workitem, "invoice.date", Date.class); "invoice.date", Date.class);
readXMLValue(doc, "/CargoSoftEFile/EFile/References/Reference[@type='currency']", readXMLValue(doc, "/CargoSoftEFile/EFile/References/Reference[@type='currency']", workitem,
workitem, "invoice.currency", String.class); "invoice.currency", String.class);
readXMLValue(doc, "/CargoSoftEFile/EFile/References/Reference[@type='currency_rate']", readXMLValue(doc, "/CargoSoftEFile/EFile/References/Reference[@type='currency_rate']", workitem,
workitem, "invoice.rate", Double.class); "invoice.rate", Double.class);
readXMLValue(doc, "/CargoSoftEFile/EFile/References/Reference[@type='total_net_amount']", readXMLValue(doc, "/CargoSoftEFile/EFile/References/Reference[@type='total_net_amount']", workitem,
workitem, "invoice.total.net", Double.class); "invoice.total.net", Double.class);
readXMLValue(doc, "/CargoSoftEFile/EFile/References/Reference[@type='total_tax_amount']", readXMLValue(doc, "/CargoSoftEFile/EFile/References/Reference[@type='total_tax_amount']", workitem,
workitem, "invoice.total.tax", Double.class); "invoice.total.tax", Double.class);
readXMLValue(doc, "/CargoSoftEFile/EFile/References/Reference[@type='reference']", readXMLValue(doc, "/CargoSoftEFile/EFile/References/Reference[@type='reference']", workitem,
workitem, "invoice.atc.number", String.class); "invoice.atc.number", String.class);
readXMLValue(doc, "/CargoSoftEFile/EFile/References/Reference[@type='booking_period']", readXMLValue(doc, "/CargoSoftEFile/EFile/References/Reference[@type='booking_period']", workitem,
workitem, "invoice.booking_period", String.class); "invoice.booking_period", String.class);
readXMLValue(doc, "/CargoSoftEFile/EFile/References/Reference[@type='booking_date']", readXMLValue(doc, "/CargoSoftEFile/EFile/References/Reference[@type='booking_date']", workitem,
workitem, "invoice.booking_date", Date.class); "invoice.booking_date", Date.class);
readXMLValue(doc, "/CargoSoftEFile/EFile/References/Reference[@type='booking_text']", readXMLValue(doc, "/CargoSoftEFile/EFile/References/Reference[@type='booking_text']", workitem,
workitem, "invoice.booking_text", String.class); "invoice.booking_text", String.class);
// verify if invoice is already imported. // verify if invoice is already imported.
if (alreadyImported(workitem.getItemValueString("invoice.number"))) { if (alreadyImported(workitem.getItemValueString("invoice.number"))) {
@ -440,8 +434,7 @@ public class CargosoftXMLEAkteImportService {
attacheFiles(doc, workitem); attacheFiles(doc, workitem);
} }
} catch (ParserConfigurationException | SAXException | IOException e) { } catch (ParserConfigurationException | SAXException | IOException e) {
throw new PluginException(CargosoftXMLEAkteImportService.class.getName(), throw new PluginException(CargosoftXMLEAkteImportService.class.getName(), "XML_ERROR", e.getMessage());
"XML_ERROR", e.getMessage());
} }
return workitem; return workitem;
} }
@ -449,12 +442,11 @@ public class CargosoftXMLEAkteImportService {
/** /**
* Reads a tag value from the xml tree and set the value into the given * Reads a tag value from the xml tree and set the value into the given
* workitem. * workitem.
* *
* /Invoices/Invoice/InvoiceHeader/Client/Code * /Invoices/Invoice/InvoiceHeader/Client/Code
* *
* Beispiel Datum: * Beispiel Datum: <InvoiceDate>2024-05-13T00:00:00+02:00</InvoiceDate>
* <InvoiceDate>2024-05-13T00:00:00+02:00</InvoiceDate> *
*
* @param doc - xml doc * @param doc - xml doc
* @param expression - xpath expression * @param expression - xpath expression
* @param workitem * @param workitem
@ -513,10 +505,10 @@ public class CargosoftXMLEAkteImportService {
} }
/** /**
* This method attache the pdf file and the XML file to the workitem. * This method attache the pdf file and the XML file to the workitem. The method
* The method removes the file content first form the xml tree and attache the * removes the file content first form the xml tree and attache the XML without
* XML without the pdf data. * the pdf data.
* *
* @throws XPathExpressionException * @throws XPathExpressionException
* @throws TransformerException * @throws TransformerException
*/ */
@ -526,24 +518,23 @@ public class CargosoftXMLEAkteImportService {
XPathFactory xpathFactory = XPathFactory.newInstance(); XPathFactory xpathFactory = XPathFactory.newInstance();
XPath xpath = xpathFactory.newXPath(); XPath xpath = xpathFactory.newXPath();
// read filename and content.... // read filename and content....
readXMLValue(doc, "/CargoSoftEFile/EFile/Attachments/Attachment/Filename", readXMLValue(doc, "/CargoSoftEFile/EFile/Attachments/Attachment/Filename", workitem, "import.filename",
workitem, "import.filename", String.class); String.class);
readXMLValue(doc, "/CargoSoftEFile/EFile/Attachments/Attachment/Content", readXMLValue(doc, "/CargoSoftEFile/EFile/Attachments/Attachment/Content", workitem, "import.filedata",
workitem, "import.filedata", String.class); String.class);
// attache the PDF file content // attache the PDF file content
String fileDataString = workitem.getItemValueString("import.filedata"); String fileDataString = workitem.getItemValueString("import.filedata");
byte[] decodedPDFData = java.util.Base64.getDecoder().decode(fileDataString); byte[] decodedPDFData = java.util.Base64.getDecoder().decode(fileDataString);
FileData fileData = new FileData(workitem.getItemValueString("import.filename"), FileData fileData = new FileData(workitem.getItemValueString("import.filename"), decodedPDFData,
decodedPDFData, "application/pdf", null); "application/pdf", null);
workitem.addFileData(fileData); workitem.addFileData(fileData);
// remove the temp items.. // remove the temp items..
workitem.removeItem("import.filename"); workitem.removeItem("import.filename");
workitem.removeItem("import.filedata"); workitem.removeItem("import.filedata");
// next remove the Attachments/AttachmentContent from the dom tree // next remove the Attachments/AttachmentContent from the dom tree
XPathExpression attachmentsExpr = xpath XPathExpression attachmentsExpr = xpath.compile("/CargoSoftEFile/EFile/Attachments/Attachment/Content");
.compile("/CargoSoftEFile/EFile/Attachments/Attachment/Content");
NodeList contentNodes = (NodeList) attachmentsExpr.evaluate(doc, XPathConstants.NODESET); NodeList contentNodes = (NodeList) attachmentsExpr.evaluate(doc, XPathConstants.NODESET);
// Remove the matching nodes // Remove the matching nodes
for (int i = 0; i < contentNodes.getLength(); i++) { for (int i = 0; i < contentNodes.getLength(); i++) {
@ -569,7 +560,7 @@ public class CargosoftXMLEAkteImportService {
/** /**
* Prüft ob die Belegnummer schon existiert (importiert wurde) * Prüft ob die Belegnummer schon existiert (importiert wurde)
* *
* @param belegNummer * @param belegNummer
* @return * @return
*/ */
@ -592,24 +583,23 @@ public class CargosoftXMLEAkteImportService {
/** /**
* This helper method reads the references starting with 'Row_' and creates a * This helper method reads the references starting with 'Row_' and creates a
* child Workitem for each row. * child Workitem for each row.
* *
* The method assumes that the rows are starting with the type 'row_n_' where * The method assumes that the rows are starting with the type 'row_n_' where
* 'n' is the row number. * 'n' is the row number. We start with row 1 and read until we found more rows.
* We start with row 1 and read until we found more rows. *
*
* Example: * Example:
* *
* <pre>{@code * <pre>{@code
* ... * ...
<Reference type="row_1_activity_type" /> <Reference type="row_1_activity_type" />
<Reference type="row_1_amount">3591.340</Reference> <Reference type="row_1_amount">3591.340</Reference>
<Reference type="row_1_tax_code">0</Reference> <Reference type="row_1_tax_code">0</Reference>
<Reference type="row_1_cs_filenumber">IM-GCA-2408-116</Reference> <Reference type="row_1_cs_filenumber">IM-GCA-2408-116</Reference>
* }</pre> * }</pre>
* *
* <p> * <p>
* The last filenumber text will be transferred into the item 'invoice.text' * The last filenumber text will be transferred into the item 'invoice.text'
* *
* @param xml * @param xml
* @return * @return
* @throws Exception * @throws Exception

View file

@ -1,27 +1,27 @@
/* /*
* Imixs-Workflow * Imixs-Workflow
* *
* Copyright (C) 2001-2020 Imixs Software Solutions GmbH, * Copyright (C) 2001-2020 Imixs Software Solutions GmbH,
* http://www.imixs.com * http://www.imixs.com
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2 * as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version. * of the License, or (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details. * General Public License for more details.
* *
* You can receive a copy of the GNU General Public * You can receive a copy of the GNU General Public
* License at http://www.gnu.org/licenses/gpl.html * License at http://www.gnu.org/licenses/gpl.html
* *
* Project: * Project:
* https://www.imixs.org * https://www.imixs.org
* https://github.com/imixs/imixs-workflow * https://github.com/imixs/imixs-workflow
* *
* Contributors: * Contributors:
* Imixs Software Solutions GmbH - Project Management * Imixs Software Solutions GmbH - Project Management
* Ralph Soika - Software Developer * Ralph Soika - Software Developer
*/ */
@ -89,22 +89,21 @@ import jakarta.inject.Inject;
/** /**
* Der CargosoftXMLInvoiceImportImportService erweitert den FTPImportService und * Der CargosoftXMLInvoiceImportImportService erweitert den FTPImportService und
* kann XML Invoice Dokumente von Cargosoft importieren. * kann XML Invoice Dokumente von Cargosoft importieren. Der Service reagiert
* Der Service reagiert auf DocumentImportEvents und kann über das Options Feld * auf DocumentImportEvents und kann über das Options Feld konfiguriert werden.
* konfiguriert werden.
* <p> * <p>
* Im Gegensatz zum FTPImportService werden die XML Dateien nach einem * Im Gegensatz zum FTPImportService werden die XML Dateien nach einem
* speziellen Verfahren geparsed und dann als Ausgangsrechnungen importiert. * speziellen Verfahren geparsed und dann als Ausgangsrechnungen importiert. Der
* Der Service ersetzt den alten DATEV Import. Falls eine Rechnung schon * Service ersetzt den alten DATEV Import. Falls eine Rechnung schon existiert
* existiert (Rechnungsnummer) wird der Datensatz vom FTP Laufwerk gelöscht und * (Rechnungsnummer) wird der Datensatz vom FTP Laufwerk gelöscht und nicht
* nicht importiert. * importiert.
* <p> * <p>
* In den Optionen muss die option "mandant.id" hinterlegt sein. Stimmt * In den Optionen muss die option "mandant.id" hinterlegt sein. Stimmt diese
* diese beim Import nicht mit der Mandanten Nummer in der XML Datei überein, * beim Import nicht mit der Mandanten Nummer in der XML Datei überein, wird das
* wird das XML Dokument gelöscht und nicht importiert. * XML Dokument gelöscht und nicht importiert.
* *
* *
* *
* @author rsoika * @author rsoika
* *
*/ */
@ -137,8 +136,8 @@ public class CargosoftXMLInvoiceImportService {
/** /**
* This method reacts on a CDI ImportEvent and reads documents form a ftp * This method reacts on a CDI ImportEvent and reads documents form a ftp
* server. * server.
* *
* *
*/ */
public void onEvent(@Observes DocumentImportEvent event) { public void onEvent(@Observes DocumentImportEvent event) {
@ -164,8 +163,7 @@ public class CargosoftXMLInvoiceImportService {
return; return;
} }
documentImportService.logMessage("...CARGOSOFT_INVOICE_XML Import started: mandant.id=" + mandantID, documentImportService.logMessage("...CARGOSOFT_INVOICE_XML Import started: mandant.id=" + mandantID, event);
event);
String ftpServer = event.getSource().getItemValueString(DocumentImportService.SOURCE_ITEM_SERVER); String ftpServer = event.getSource().getItemValueString(DocumentImportService.SOURCE_ITEM_SERVER);
String ftpPort = event.getSource().getItemValueString(DocumentImportService.SOURCE_ITEM_PORT); String ftpPort = event.getSource().getItemValueString(DocumentImportService.SOURCE_ITEM_PORT);
String ftpUser = event.getSource().getItemValueString(DocumentImportService.SOURCE_ITEM_USER); String ftpUser = event.getSource().getItemValueString(DocumentImportService.SOURCE_ITEM_USER);
@ -218,8 +216,8 @@ public class CargosoftXMLInvoiceImportService {
ftpClient.connect(ftpServer, ftpPort); ftpClient.connect(ftpServer, ftpPort);
if (ftpClient.login(ftpUser, ftpPassword) == false) { if (ftpClient.login(ftpUser, ftpPassword) == false) {
throw new PluginException(CargosoftXMLInvoiceImportService.class.getName(), throw new PluginException(CargosoftXMLInvoiceImportService.class.getName(), "FTP_ERROR",
"FTP_ERROR", "FTP file transfer failed: login failed!"); "FTP file transfer failed: login failed!");
} }
ftpClient.enterLocalPassiveMode(); ftpClient.enterLocalPassiveMode();
logger.finest("...... FileType=" + FTP.BINARY_FILE_TYPE); logger.finest("...... FileType=" + FTP.BINARY_FILE_TYPE);
@ -227,8 +225,7 @@ public class CargosoftXMLInvoiceImportService {
ftpClient.setControlEncoding("UTF-8"); ftpClient.setControlEncoding("UTF-8");
} catch (IOException e) { } catch (IOException e) {
throw new PluginException(CargosoftXMLInvoiceImportService.class.getName(), throw new PluginException(CargosoftXMLInvoiceImportService.class.getName(), "FTP_ERROR", e.getMessage());
"FTP_ERROR", e.getMessage());
} }
return ftpClient; return ftpClient;
@ -236,7 +233,7 @@ public class CargosoftXMLInvoiceImportService {
/** /**
* Helper method reads the working FTP directory and starts importing the files. * Helper method reads the working FTP directory and starts importing the files.
* *
* @param ftpClient * @param ftpClient
* @param ftpPath * @param ftpPath
* @throws PluginException * @throws PluginException
@ -252,8 +249,8 @@ public class CargosoftXMLInvoiceImportService {
// try to enter the working directory and read all files... // try to enter the working directory and read all files...
boolean bWorkingDir = ftpClient.changeWorkingDirectory(ftpPath); boolean bWorkingDir = ftpClient.changeWorkingDirectory(ftpPath);
if (bWorkingDir == false) { if (bWorkingDir == false) {
throw new PluginException(CargosoftXMLInvoiceImportService.class.getName(), throw new PluginException(CargosoftXMLInvoiceImportService.class.getName(), "FTP_ERROR",
"FTP_ERROR", "...failed to change into working directory: "); "...failed to change into working directory: ");
} }
FTPFile[] allFiles = ftpClient.listFiles(ftpPath); FTPFile[] allFiles = ftpClient.listFiles(ftpPath);
@ -301,10 +298,10 @@ public class CargosoftXMLInvoiceImportService {
// "processed/"); // "processed/");
// } // }
} catch (AccessDeniedException | ProcessingErrorException | PluginException } catch (AccessDeniedException | ProcessingErrorException | PluginException | ModelException
| ModelException | XPathExpressionException | TransformerException e) { | XPathExpressionException | TransformerException e) {
throw new PluginException(CargosoftXMLInvoiceImportService.class.getName(), throw new PluginException(CargosoftXMLInvoiceImportService.class.getName(), "FTP_ERROR",
"FTP_ERROR", e.getMessage()); e.getMessage());
} }
} }
documentImportService.logMessage("..." + count + " new files imported.", event); documentImportService.logMessage("..." + count + " new files imported.", event);
@ -315,8 +312,7 @@ public class CargosoftXMLInvoiceImportService {
int r = ftpClient.getReplyCode(); int r = ftpClient.getReplyCode();
logger.severe("FTP ReplyCode=" + r); logger.severe("FTP ReplyCode=" + r);
throw new PluginException(CargosoftXMLInvoiceImportService.class.getName(), throw new PluginException(CargosoftXMLInvoiceImportService.class.getName(), "FTP_ERROR", e.getMessage());
"FTP_ERROR", e.getMessage());
} finally { } finally {
// do logout.... // do logout....
@ -326,8 +322,8 @@ public class CargosoftXMLInvoiceImportService {
ftpClient.disconnect(); ftpClient.disconnect();
} catch (IOException e) { } catch (IOException e) {
documentImportService.logMessage("...FTP file transfer failed: " + e.getMessage(), event); documentImportService.logMessage("...FTP file transfer failed: " + e.getMessage(), event);
throw new PluginException(CargosoftXMLInvoiceImportService.class.getName(), throw new PluginException(CargosoftXMLInvoiceImportService.class.getName(), "FTP_ERROR",
"FTP_ERROR", e.getMessage()); e.getMessage());
} }
} }
} }
@ -335,8 +331,8 @@ public class CargosoftXMLInvoiceImportService {
/** /**
* Creates and processes a new workitem with a given xml information from the * Creates and processes a new workitem with a given xml information from the
* fileData * fileData
* *
* *
* @return * @return
* @throws ModelException * @throws ModelException
* @throws PluginException * @throws PluginException
@ -346,9 +342,8 @@ public class CargosoftXMLInvoiceImportService {
* @throws XPathExpressionException * @throws XPathExpressionException
*/ */
public ItemCollection createWorkitem(ItemCollection source, String fileName, byte[] rawData, public ItemCollection createWorkitem(ItemCollection source, String fileName, byte[] rawData,
Map<String, List<String>> spacePosMappings) Map<String, List<String>> spacePosMappings) throws AccessDeniedException, ProcessingErrorException,
throws AccessDeniedException, ProcessingErrorException, PluginException, ModelException, PluginException, ModelException, XPathExpressionException, TransformerException {
XPathExpressionException, TransformerException {
ItemCollection workitem = new ItemCollection(); ItemCollection workitem = new ItemCollection();
workitem.model(source.getItemValueString(DocumentImportService.SOURCE_ITEM_MODELVERSION)); workitem.model(source.getItemValueString(DocumentImportService.SOURCE_ITEM_MODELVERSION));
workitem.task(source.getItemValueInteger(DocumentImportService.SOURCE_ITEM_TASK)); workitem.task(source.getItemValueInteger(DocumentImportService.SOURCE_ITEM_TASK));
@ -377,11 +372,11 @@ public class CargosoftXMLInvoiceImportService {
// invoice.rate // invoice.rate
// invoice.base.amount // invoice.base.amount
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/Client/Codes/Code", workitem, readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/Client/Codes/Code", workitem, "mandant.id",
"mandant.id", String.class); String.class);
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceNumber", readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceNumber", workitem, "invoice.number",
workitem, "invoice.number", String.class); String.class);
// verify if invoice is already imported. // verify if invoice is already imported.
if (alreadyImported(workitem.getItemValueString("invoice.number"))) { if (alreadyImported(workitem.getItemValueString("invoice.number"))) {
@ -389,14 +384,14 @@ public class CargosoftXMLInvoiceImportService {
return null; return null;
} }
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceType/Codes/Code", readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceType/Codes/Code", workitem, "invoice.type",
workitem, "invoice.type", String.class); String.class);
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceType/Description", readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceType/Description", workitem,
workitem, "invoice.type.description", String.class); "invoice.type.description", String.class);
// Rechnungssumme errechnen..... // Rechnungssumme errechnen.....
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceAmount/NetAmount/Amount/Value", readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceAmount/NetAmount/Amount/Value", workitem,
workitem, "invoice.total.net", Double.class); "invoice.total.net", Double.class);
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceAmount/VATInformation/VATAmount/Amount/Value", readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceAmount/VATInformation/VATAmount/Amount/Value",
workitem, "invoice.total.tax", Double.class); workitem, "invoice.total.tax", Double.class);
if (!workitem.hasItem("invoice.total.tax")) { if (!workitem.hasItem("invoice.total.tax")) {
@ -416,34 +411,33 @@ public class CargosoftXMLInvoiceImportService {
} }
// Steuer // Steuer
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceAmount/VATInformation/VAT/VATRate", readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceAmount/VATInformation/VAT/VATRate", workitem,
workitem, "invoice.vatrate", Double.class); "invoice.vatrate", Double.class);
// Booking Period // Booking Period
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/BookingPeriod", readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/BookingPeriod", workitem, "invoice.bookingperiod",
workitem, "invoice.bookingperiod", String.class); String.class);
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceCurrency/Codes/Code", readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceCurrency/Codes/Code", workitem,
workitem, "invoice.currency", String.class); "invoice.currency", String.class);
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceDate", readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceDate", workitem, "invoice.date", Date.class);
workitem, "invoice.date", Date.class);
readXMLValue(doc, readXMLValue(doc,
"/Invoices/Invoice/InvoiceHeader/PaymentConditions/PaymentCondition/Codes/Code[@Type='cs']", "/Invoices/Invoice/InvoiceHeader/PaymentConditions/PaymentCondition/Codes/Code[@Type='cs']",
workitem, "payment.term", String.class); workitem, "payment.term", String.class);
// kostenstelle // kostenstelle
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/CostUnit/Description", readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/CostUnit/Description", workitem, "invoice.CostUnit",
workitem, "invoice.CostUnit", String.class); String.class);
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/PaymentConditions/DueDate", readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/PaymentConditions/DueDate", workitem, "invoice.duedate",
workitem, "invoice.duedate", Date.class); Date.class);
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/PaymentConditions/DueDate", readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/PaymentConditions/DueDate", workitem, "invoice.reminder",
workitem, "invoice.reminder", Date.class); Date.class);
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceAddress/Codes/Code", readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/InvoiceAddress/Codes/Code", workitem, "dbtr.number",
workitem, "dbtr.number", String.class); String.class);
String dbtrNumber = workitem.getItemValueString("dbtr.number"); String dbtrNumber = workitem.getItemValueString("dbtr.number");
if (dbtrNumber.startsWith("K") || dbtrNumber.startsWith("D")) { if (dbtrNumber.startsWith("K") || dbtrNumber.startsWith("D")) {
workitem.setItemValue("dbtr.number", dbtrNumber.substring(1)); workitem.setItemValue("dbtr.number", dbtrNumber.substring(1));
@ -487,17 +481,16 @@ public class CargosoftXMLInvoiceImportService {
attacheFiles(doc, workitem); attacheFiles(doc, workitem);
} catch (ParserConfigurationException | SAXException | IOException e) { } catch (ParserConfigurationException | SAXException | IOException e) {
throw new PluginException(CargosoftXMLInvoiceImportService.class.getName(), throw new PluginException(CargosoftXMLInvoiceImportService.class.getName(), "XML_ERROR", e.getMessage());
"XML_ERROR", e.getMessage());
} }
return workitem; return workitem;
} }
/** /**
* Prüft ob die Rechnung eine Rückstellung ist also mit R1 bis R8 beginnt * Prüft ob die Rechnung eine Rückstellung ist also mit R1 bis R8 beginnt
* *
* ^R[1-8] . * ^R[1-8] .
* *
* @param workitem * @param workitem
* @return * @return
*/ */
@ -509,12 +502,11 @@ public class CargosoftXMLInvoiceImportService {
/** /**
* Reads a tag value from the xml tree and set the value into the given * Reads a tag value from the xml tree and set the value into the given
* workitem. * workitem.
* *
* /Invoices/Invoice/InvoiceHeader/Client/Code * /Invoices/Invoice/InvoiceHeader/Client/Code
* *
* Beispiel Datum: * Beispiel Datum: <InvoiceDate>2024-05-13T00:00:00+02:00</InvoiceDate>
* <InvoiceDate>2024-05-13T00:00:00+02:00</InvoiceDate> *
*
* @param doc - xml doc * @param doc - xml doc
* @param expression - xpath expression * @param expression - xpath expression
* @param workitem * @param workitem
@ -566,12 +558,10 @@ public class CargosoftXMLInvoiceImportService {
/** /**
* Hilfsmethode die die ATC Nummer sucht und in das workitem feld * Hilfsmethode die die ATC Nummer sucht und in das workitem feld
* invoice.atc.number einträgt. * invoice.atc.number einträgt. Wir gehen davon aus, das wir über die ChildItems
* Wir gehen davon aus, das wir über die ChildItems diese aus dem billingtexts * diese aus dem billingtexts schon ermittel haben. Es können mehrere ATC
* schon ermittel haben. * nummern existieren. Diese werden in einer Liste gespeichert
* Es können mehrere ATC nummern existieren. Diese werden in einer Liste *
* gespeichert
*
* @param doc * @param doc
* @param workitem * @param workitem
*/ */
@ -592,19 +582,13 @@ public class CargosoftXMLInvoiceImportService {
/** /**
* Reads the positions rows * Reads the positions rows
* *
* InvoiceRows/InvoiceRow * InvoiceRows/InvoiceRow
* *
* Each row has the following items: * Each row has the following items: _childitems { datev.shzeichen=[H],
* _childitems { * datev.kurs=[0.0], datev.basisumsatz=[0.0], datev.text=[R LA-PAP-2405-051],
* datev.shzeichen=[H], * datev.konto=[3851], datev.umsatz=[3307.81], datev.wkz=[EUR]}}
* datev.kurs=[0.0], *
* datev.basisumsatz=[0.0],
* datev.text=[R LA-PAP-2405-051],
* datev.konto=[3851],
* datev.umsatz=[3307.81],
* datev.wkz=[EUR]}}
*
* @param doc - xml doc * @param doc - xml doc
* @param workitem * @param workitem
*/ */
@ -622,8 +606,7 @@ public class CargosoftXMLInvoiceImportService {
XPathExpression netAmountExchangeRateExpr = xPath XPathExpression netAmountExchangeRateExpr = xPath
.compile("InvoiceAmount/NetAmount/Amount/ExchangeRate/text()"); .compile("InvoiceAmount/NetAmount/Amount/ExchangeRate/text()");
XPathExpression netActivityTypeExpr = xPath XPathExpression netActivityTypeExpr = xPath.compile("ActivityType/Codes/Code[@Type='cs']/text()");
.compile("ActivityType/Codes/Code[@Type='cs']/text()");
NodeList rowList = doc.getElementsByTagName("InvoiceRow"); NodeList rowList = doc.getElementsByTagName("InvoiceRow");
double _kurs = 0.0; double _kurs = 0.0;
@ -679,7 +662,7 @@ public class CargosoftXMLInvoiceImportService {
/** /**
* S/H Kennzeichen auflösen * S/H Kennzeichen auflösen
* *
* Bei G = Gutschrift oder SR = Stornorechnung müssen wir das vorzeichen ändern * Bei G = Gutschrift oder SR = Stornorechnung müssen wir das vorzeichen ändern
* Invoice Type S/H * Invoice Type S/H
*/ */
@ -693,7 +676,7 @@ public class CargosoftXMLInvoiceImportService {
/** /**
* Liste der BillingText-Elemente lesen.... * Liste der BillingText-Elemente lesen....
* *
* und ggf. die ATC Nummer finden.... * und ggf. die ATC Nummer finden....
**/ **/
childItemCol.setItemValue("BillingCode", billingCode); childItemCol.setItemValue("BillingCode", billingCode);
@ -732,14 +715,11 @@ public class CargosoftXMLInvoiceImportService {
} }
/** /**
* G = Gutschrift * G = Gutschrift SR = Stornorechnung GK = Sammelgutschrift SS = Sammelrechnung
* SR = Stornorechnung * Storno
* GK = Sammelgutschrift *
* SS = Sammelrechnung Storno * müssen wir das vorzeichen ändern dies wird über den Invoice.Type geprüft
* *
* müssen wir das vorzeichen ändern
* dies wird über den Invoice.Type geprüft
*
* @return * @return
*/ */
private boolean isGutschrift(ItemCollection workitem) { private boolean isGutschrift(ItemCollection workitem) {
@ -748,10 +728,10 @@ public class CargosoftXMLInvoiceImportService {
} }
/** /**
* This method attache the pdf file and the XML file to the workitem. * This method attache the pdf file and the XML file to the workitem. The method
* The method removes the file content first form the xml tree and attache the * removes the file content first form the xml tree and attache the XML without
* XML without the pdf data. * the pdf data.
* *
* @throws XPathExpressionException * @throws XPathExpressionException
* @throws TransformerException * @throws TransformerException
*/ */
@ -761,16 +741,16 @@ public class CargosoftXMLInvoiceImportService {
XPathFactory xpathFactory = XPathFactory.newInstance(); XPathFactory xpathFactory = XPathFactory.newInstance();
XPath xpath = xpathFactory.newXPath(); XPath xpath = xpathFactory.newXPath();
// read filename and content.... // read filename and content....
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/Attachments/Attachment/Filename", readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/Attachments/Attachment/Filename", workitem,
workitem, "import.filename", String.class); "import.filename", String.class);
readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/Attachments/Attachment/Content", readXMLValue(doc, "/Invoices/Invoice/InvoiceHeader/Attachments/Attachment/Content", workitem, "import.filedata",
workitem, "import.filedata", String.class); String.class);
// attache the PDF file content // attache the PDF file content
String fileDataString = workitem.getItemValueString("import.filedata"); String fileDataString = workitem.getItemValueString("import.filedata");
byte[] decodedPDFData = java.util.Base64.getDecoder().decode(fileDataString); byte[] decodedPDFData = java.util.Base64.getDecoder().decode(fileDataString);
FileData fileData = new FileData(workitem.getItemValueString("import.filename"), FileData fileData = new FileData(workitem.getItemValueString("import.filename"), decodedPDFData,
decodedPDFData, "application/pdf", null); "application/pdf", null);
workitem.addFileData(fileData); workitem.addFileData(fileData);
// remove the temp items.. // remove the temp items..
workitem.removeItem("import.filename"); workitem.removeItem("import.filename");
@ -804,7 +784,7 @@ public class CargosoftXMLInvoiceImportService {
/** /**
* Prüft ob die Belegnummer schon existiert (importiert wurde) * Prüft ob die Belegnummer schon existiert (importiert wurde)
* *
* @param belegNummer * @param belegNummer
* @return * @return
*/ */
@ -828,7 +808,7 @@ public class CargosoftXMLInvoiceImportService {
* Diese Method sucht einen Debitor. * Diese Method sucht einen Debitor.
* <p> * <p>
* Diese Methode erwartet das führende D * Diese Methode erwartet das führende D
* *
* @throws PluginException * @throws PluginException
*/ */
public ItemCollection findDebitor(String dbtrNumber) throws PluginException { public ItemCollection findDebitor(String dbtrNumber) throws PluginException {

View file

@ -2,9 +2,9 @@ package com.alexanderlogistics.zoho;
/** /**
* Helper class for JSON deserialization * Helper class for JSON deserialization
* *
* If the request is successful, you would receive the following: * If the request is successful, you would receive the following:
* *
* <pre> * <pre>
{ {
"access_token": "{access_token}", "access_token": "{access_token}",

View file

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

View file

@ -2,26 +2,26 @@ package com.alexanderlogistics.zoho;
/******************************************************************************* /*******************************************************************************
* Imixs Workflow Technology * Imixs Workflow Technology
* Copyright (C) 2003, 2008 Imixs Software Solutions GmbH, * Copyright (C) 2003, 2008 Imixs Software Solutions GmbH,
* http://www.imixs.com * http://www.imixs.com
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2 * as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version. * of the License, or (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details. * General Public License for more details.
* *
* You can receive a copy of the GNU General Public * You can receive a copy of the GNU General Public
* License at http://www.gnu.org/licenses/gpl.html * License at http://www.gnu.org/licenses/gpl.html
* *
* Contributors: * Contributors:
* Imixs Software Solutions GmbH - initial API and implementation * Imixs Software Solutions GmbH - initial API and implementation
* Ralph Soika * Ralph Soika
* *
*******************************************************************************/ *******************************************************************************/
import java.util.logging.Logger; import java.util.logging.Logger;
@ -35,54 +35,54 @@ import jakarta.inject.Named;
/** /**
* Der ZohoController ist für das Admin Interface um Access Tokens zu erzeugen. * Der ZohoController ist für das Admin Interface um Access Tokens zu erzeugen.
* *
* @see admin/zoho.xhml * @see admin/zoho.xhml
* *
* @author rsoika * @author rsoika
* *
*/ */
@Named("zohoController") @Named("zohoController")
@ApplicationScoped @ApplicationScoped
public class ZohoController extends ConfigController { public class ZohoController extends ConfigController {
public static final String ZOHO_CONFIGURATION = "ZOHO_CONFIGURATION"; public static final String ZOHO_CONFIGURATION = "ZOHO_CONFIGURATION";
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private static Logger logger = Logger.getLogger(ZohoController.class.getName()); private static Logger logger = Logger.getLogger(ZohoController.class.getName());
@Inject @Inject
ZohoOAuthManager zohoOAuthManager; ZohoOAuthManager zohoOAuthManager;
public ZohoController() { public ZohoController() {
super(); super();
setName(ZOHO_CONFIGURATION); setName(ZOHO_CONFIGURATION);
} }
/** /**
* Ruft zohoOAuhtManager.updateToken auf * Ruft zohoOAuhtManager.updateToken auf
*/ */
public void updateAccessToken() { public void updateAccessToken() {
// //
try { try {
zohoOAuthManager.updateTokens(this.getWorkitem().getItemValueString("zoho.code")); zohoOAuthManager.updateTokens(this.getWorkitem().getItemValueString("zoho.code"));
} catch (PluginException e) { } catch (PluginException e) {
String message = "Failed to generate token: " + e.getMessage(); String message = "Failed to generate token: " + e.getMessage();
logger.warning(message); logger.warning(message);
this.getWorkitem().setItemValue("message", message); this.getWorkitem().setItemValue("message", message);
} }
} }
/** /**
* Ruft zohoOAuhtManager.refreshToken auf * Ruft zohoOAuhtManager.refreshToken auf
*/ */
public void refreshAccessToken() { public void refreshAccessToken() {
// //
try { try {
zohoOAuthManager.refreshAccessToken(); zohoOAuthManager.refreshAccessToken();
} catch (PluginException e) { } catch (PluginException e) {
String message = "Failed to refresh token: " + e.getMessage(); String message = "Failed to refresh token: " + e.getMessage();
logger.warning(message); logger.warning(message);
this.getWorkitem().setItemValue("message", message); this.getWorkitem().setItemValue("message", message);
} }
} }
} }

View file

@ -18,7 +18,7 @@ import jakarta.inject.Inject;
/** /**
* This adapter exports the invoice data to zoho * This adapter exports the invoice data to zoho
* *
* @version 1.0 * @version 1.0
* @author rsoika * @author rsoika
*/ */
@ -44,7 +44,7 @@ public class ZohoExportAdapter implements SignalAdapter {
/** /**
* This method calls the zoho api * This method calls the zoho api
* *
* @throws PluginException * @throws PluginException
*/ */
@Override @Override

View file

@ -1,27 +1,27 @@
/* /*
* Imixs-Workflow * Imixs-Workflow
* *
* Copyright (C) 2001-2020 Imixs Software Solutions GmbH, * Copyright (C) 2001-2020 Imixs Software Solutions GmbH,
* http://www.imixs.com * http://www.imixs.com
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2 * as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version. * of the License, or (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details. * General Public License for more details.
* *
* You can receive a copy of the GNU General Public * You can receive a copy of the GNU General Public
* License at http://www.gnu.org/licenses/gpl.html * License at http://www.gnu.org/licenses/gpl.html
* *
* Project: * Project:
* https://www.imixs.org * https://www.imixs.org
* https://github.com/imixs/imixs-workflow * https://github.com/imixs/imixs-workflow
* *
* Contributors: * Contributors:
* Imixs Software Solutions GmbH - Project Management * Imixs Software Solutions GmbH - Project Management
* Ralph Soika - Software Developer * Ralph Soika - Software Developer
*/ */
@ -45,20 +45,20 @@ import jakarta.ws.rs.core.MediaType;
/** /**
* Dieser API Endpoint dient dazu über zoho einen sogenannten Grant Token zu * Dieser API Endpoint dient dazu über zoho einen sogenannten Grant Token zu
* erhalten. * erhalten.
* *
* https://www.zoho.com/books/api/v3/oauth/ * https://www.zoho.com/books/api/v3/oauth/
* *
* Der Service wird aktuell nicht benötigt, das wir sogenannte Zoho Self-Clients * Der Service wird aktuell nicht benötigt, das wir sogenannte Zoho Self-Clients
* verwenden! * verwenden!
* *
* Der Service stellt im Grunde nur eine Callback URL für einen normalen Zoho * Der Service stellt im Grunde nur eine Callback URL für einen normalen Zoho
* Client ein. Also wenn jemand einen solchen Token über den Browser * Client ein. Also wenn jemand einen solchen Token über den Browser anfordert.
* anfordert. Der URL muss dann odrt als CallBack URL angegeben sein! * Der URL muss dann odrt als CallBack URL angegeben sein!
* *
* Beispiel: * Beispiel:
* *
* https://accounts.zoho.com/oauth/v2/auth?scope=ZohoBooks.invoices.CREATE,ZohoBooks.invoices.READ,ZohoBooks.invoices.UPDATE,ZohoBooks.invoices.DELETE&client_id=1000.NLTM2KUEHI696MSLGEIANATVGGX7IN&state=testing&response_type=code&redirect_uri=https://alexander-logistics-dwc.office-workflow.de/api/zoho/grant * https://accounts.zoho.com/oauth/v2/auth?scope=ZohoBooks.invoices.CREATE,ZohoBooks.invoices.READ,ZohoBooks.invoices.UPDATE,ZohoBooks.invoices.DELETE&client_id=1000.NLTM2KUEHI696MSLGEIANATVGGX7IN&state=testing&response_type=code&redirect_uri=https://alexander-logistics-dwc.office-workflow.de/api/zoho/grant
* *
* @version 1.0 * @version 1.0
* @author rsoika * @author rsoika
* *
@ -85,15 +85,14 @@ public class ZohoOAuthCallbackService {
/** /**
* Receive Grant Code. * Receive Grant Code.
* *
* The expected query params look like this: * The expected query params look like this:
* *
* state=testing * state=testing
* code=1000.c27802b99c007ad8db93a5a7291cb15a.8cb60b6a51df13ea627ef3ac7c2e4791 * code=1000.c27802b99c007ad8db93a5a7291cb15a.8cb60b6a51df13ea627ef3ac7c2e4791
* location=eu * location=eu accounts-server=https%3A%2F%2Faccounts.zoho.eu
* accounts-server=https%3A%2F%2Faccounts.zoho.eu *
* *
*
* @param workflowgroup * @param workflowgroup
* @param task * @param task
* @return * @return
@ -102,11 +101,8 @@ public class ZohoOAuthCallbackService {
@GET @GET
@Path("/grant") @Path("/grant")
@Produces({ MediaType.TEXT_HTML }) @Produces({ MediaType.TEXT_HTML })
public String getGrantToken( public String getGrantToken(@QueryParam("state") String state, @QueryParam("code") String code,
@QueryParam("state") String state, @QueryParam("location") String location, @QueryParam("accounts-server") String accountsServer)
@QueryParam("code") String code,
@QueryParam("location") String location,
@QueryParam("accounts-server") String accountsServer)
throws QueryException { throws QueryException {
logger.info("├── Receive Grant Token from Zoho"); logger.info("├── Receive Grant Token from Zoho");

View file

@ -27,8 +27,8 @@ import jakarta.json.bind.JsonbBuilder;
/** /**
* Der ZohoOAuthManager stellt Methoden bereit um einen Access Token zu * Der ZohoOAuthManager stellt Methoden bereit um einen Access Token zu
* erstellen. * erstellen.
* *
* *
*/ */
@Singleton @Singleton
@RunAs("org.imixs.ACCESSLEVEL.MANAGERACCESS") @RunAs("org.imixs.ACCESSLEVEL.MANAGERACCESS")
@ -47,9 +47,7 @@ public class ZohoOAuthManager {
@PostConstruct @PostConstruct
void init() { void init() {
this.httpClient = HttpClient.newBuilder() this.httpClient = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10)).build();
.connectTimeout(Duration.ofSeconds(10))
.build();
this.jsonb = JsonbBuilder.create(); this.jsonb = JsonbBuilder.create();
} }
@ -57,7 +55,7 @@ public class ZohoOAuthManager {
* Gibt den aktuellen Access Token zurück. Falls dieser expired ist wird * Gibt den aktuellen Access Token zurück. Falls dieser expired ist wird
* automatisch ein refresh durchgeführt. Somit liefert die Methode immer einen * automatisch ein refresh durchgeführt. Somit liefert die Methode immer einen
* gültigen token. * gültigen token.
* *
* @return * @return
* @throws PluginException * @throws PluginException
*/ */
@ -87,14 +85,26 @@ public class ZohoOAuthManager {
return null; return null;
} }
public String getBaseURI() throws PluginException {
String uri = "";
ItemCollection zohoConfig = configService.loadConfiguration(ZohoController.ZOHO_CONFIGURATION, false);
if (zohoConfig != null) {
uri = zohoConfig.getItemValueString("zoho.uri");
if (!uri.endsWith("/")) {
uri = uri + "/";
}
}
return uri;
}
/** /**
* Step 3: Generate Access and Refresh Token * Step 3: Generate Access and Refresh Token
* *
* After getting code from the GrantService we make a POST request * After getting code from the GrantService we make a POST request
* *
* See description: https://www.zoho.com/books/api/v3/oauth/ * See description: https://www.zoho.com/books/api/v3/oauth/
* *
* *
* @param code * @param code
* @throws PluginException * @throws PluginException
*/ */
@ -107,24 +117,21 @@ public class ZohoOAuthManager {
String clientID = zohoConfig.getItemValueString("zoho.clientid"); String clientID = zohoConfig.getItemValueString("zoho.clientid");
String clientSecret = zohoConfig.getItemValueString("zoho.clientsecret"); String clientSecret = zohoConfig.getItemValueString("zoho.clientsecret");
String requestUrl = "https://accounts.zoho.eu/oauth/v2/token" + String requestUrl = "https://accounts.zoho.eu/oauth/v2/token" + "?code="
"?code=" + URLEncoder.encode(code, StandardCharsets.UTF_8) + + URLEncoder.encode(code, StandardCharsets.UTF_8) + "&client_id="
"&client_id=" + URLEncoder.encode(clientID, StandardCharsets.UTF_8) + + URLEncoder.encode(clientID, StandardCharsets.UTF_8) + "&client_secret="
"&client_secret=" + URLEncoder.encode(clientSecret, StandardCharsets.UTF_8) + + URLEncoder.encode(clientSecret, StandardCharsets.UTF_8) + "&redirect_uri="
"&redirect_uri=" + URLEncoder.encode(redirectURI, StandardCharsets.UTF_8) + + URLEncoder.encode(redirectURI, StandardCharsets.UTF_8) + "&grant_type=authorization_code";
"&grant_type=authorization_code";
logger.fine("│ ├── Request URI=" + requestUrl); logger.fine("│ ├── Request URI=" + requestUrl);
HttpRequest request = HttpRequest.newBuilder() HttpRequest request = HttpRequest.newBuilder().uri(URI.create(requestUrl))
.uri(URI.create(requestUrl))
.header("Content-Type", "application/x-www-form-urlencoded") .header("Content-Type", "application/x-www-form-urlencoded")
.POST(HttpRequest.BodyPublishers.noBody()) // Wichtig: Kein Body! .POST(HttpRequest.BodyPublishers.noBody()) // Wichtig: Kein Body!
.build(); .build();
HttpResponse<String> response; HttpResponse<String> response;
response = httpClient.send(request, response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
HttpResponse.BodyHandlers.ofString());
logger.fine("│ ├── Response statusCode=" + response.statusCode()); logger.fine("│ ├── Response statusCode=" + response.statusCode());
if (response.statusCode() == 200) { if (response.statusCode() == 200) {
@ -132,29 +139,26 @@ public class ZohoOAuthManager {
logger.fine("│ ├── Response Body=" + response.body()); logger.fine("│ ├── Response Body=" + response.body());
if (response.body().contains("error")) { if (response.body().contains("error")) {
throw new PluginException(ZohoOAuthManager.class.getSimpleName(), throw new PluginException(ZohoOAuthManager.class.getSimpleName(), ZohoOAuthManager.ERROR_CONFIG,
ZohoOAuthManager.ERROR_CONFIG,
"Ungültiger Access Code!"); "Ungültiger Access Code!");
} }
TokenResponse tokenResponse = jsonb.fromJson(response.body(), TokenResponse tokenResponse = jsonb.fromJson(response.body(), TokenResponse.class);
TokenResponse.class);
saveTokenData(zohoConfig, tokenResponse); saveTokenData(zohoConfig, tokenResponse);
} else { } else {
throw new RuntimeException("Failed to get tokens: " + throw new RuntimeException("Failed to get tokens: " + response.statusCode() + " - " + response.body());
response.statusCode() + " - " + response.body());
} }
} catch (IOException | InterruptedException e) { } catch (IOException | InterruptedException e) {
throw new PluginException(ZohoOAuthManager.class.getSimpleName(), throw new PluginException(ZohoOAuthManager.class.getSimpleName(), ZohoOAuthManager.ERROR_CONFIG,
ZohoOAuthManager.ERROR_CONFIG, "Unable to resolve access tokens: " + e.getMessage()); "Unable to resolve access tokens: " + e.getMessage());
} }
} }
/** /**
* This method updates the Access Token based on the current Refresh Token * This method updates the Access Token based on the current Refresh Token
* *
* @throws IOException * @throws IOException
* @throws PluginException * @throws PluginException
*/ */
@ -168,26 +172,21 @@ public class ZohoOAuthManager {
String refreshToken = zohoConfig.getItemValueString("zoho.refreshToken"); String refreshToken = zohoConfig.getItemValueString("zoho.refreshToken");
try { try {
String requestUrl = "https://accounts.zoho.eu/oauth/v2/token" + String requestUrl = "https://accounts.zoho.eu/oauth/v2/token" + "?refresh_token="
"?refresh_token=" + URLEncoder.encode(refreshToken, StandardCharsets.UTF_8) + + URLEncoder.encode(refreshToken, StandardCharsets.UTF_8) + "&client_id="
"&client_id=" + URLEncoder.encode(clientID, StandardCharsets.UTF_8) + + URLEncoder.encode(clientID, StandardCharsets.UTF_8) + "&client_secret="
"&client_secret=" + URLEncoder.encode(clientSecret, + URLEncoder.encode(clientSecret, StandardCharsets.UTF_8) + "&grant_type=refresh_token";
StandardCharsets.UTF_8)
+
"&grant_type=refresh_token";
logger.fine("│ ├── Request URI=" + requestUrl); logger.fine("│ ├── Request URI=" + requestUrl);
HttpRequest request = HttpRequest.newBuilder() HttpRequest request = HttpRequest.newBuilder().uri(URI.create(requestUrl))
.uri(URI.create(requestUrl))
.header("Content-Type", "application/x-www-form-urlencoded") .header("Content-Type", "application/x-www-form-urlencoded")
.POST(HttpRequest.BodyPublishers.noBody()) // Wichtig: Kein Body! .POST(HttpRequest.BodyPublishers.noBody()) // Wichtig: Kein Body!
.build(); .build();
HttpResponse<String> response; HttpResponse<String> response;
response = httpClient.send(request, response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
HttpResponse.BodyHandlers.ofString());
logger.fine("│ ├── Response statusCode=" + response.statusCode()); logger.fine("│ ├── Response statusCode=" + response.statusCode());
if (response.statusCode() == 200) { if (response.statusCode() == 200) {
@ -195,25 +194,23 @@ public class ZohoOAuthManager {
// 1. Response-Logging für Debugging // 1. Response-Logging für Debugging
logger.fine("│ ├── Response Body=" + response.body()); logger.fine("│ ├── Response Body=" + response.body());
TokenResponse tokenResponse = jsonb.fromJson(response.body(), TokenResponse tokenResponse = jsonb.fromJson(response.body(), TokenResponse.class);
TokenResponse.class);
saveTokenData(zohoConfig, tokenResponse); saveTokenData(zohoConfig, tokenResponse);
return zohoConfig.getItemValueString("zoho.accessToken"); return zohoConfig.getItemValueString("zoho.accessToken");
} else { } else {
throw new RuntimeException("Failed to get tokens: " + throw new RuntimeException("Failed to get tokens: " + response.statusCode() + " - " + response.body());
response.statusCode() + " - " + response.body());
} }
} catch (IOException | InterruptedException e) { } catch (IOException | InterruptedException e) {
throw new PluginException(ZohoOAuthManager.class.getSimpleName(), throw new PluginException(ZohoOAuthManager.class.getSimpleName(), ZohoOAuthManager.ERROR_CONFIG,
ZohoOAuthManager.ERROR_CONFIG, "Unable to refresh access tokens: " + e.getMessage()); "Unable to refresh access tokens: " + e.getMessage());
} }
} }
/** /**
* Helper method to update the config itemcolleciton based on a tokenResponse * Helper method to update the config itemcolleciton based on a tokenResponse
* *
* @param zohoConfig * @param zohoConfig
* @param tokenResponse * @param tokenResponse
*/ */

View file

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

View file

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

View file

@ -10,12 +10,12 @@
<ui:define name="scripts"> <ui:define name="scripts">
<script type="text/javascript"> <script type="text/javascript">
/*<![CDATA[*/ /*<![CDATA[*/
function handleSubmit(event) { function handleSubmit(event) {
// remove ajax kringel // remove ajax kringel
setTimeout(function() { setTimeout(function () {
document.body.classList.remove('loading'); document.body.classList.remove('loading');
}, 5000); }, 5000);
} }
/*]]>*/ /*]]>*/
</script> </script>
</ui:define> </ui:define>
@ -32,7 +32,8 @@
<h:panelGroup layout="block" styleClass="imixs-body" id="analyse_panel"> <h:panelGroup layout="block" styleClass="imixs-body" id="analyse_panel">
<div class="imixs-form-panel"> <div class="imixs-form-panel">
<div class="ui-state-highlight ui-corner-all" style="margin-bottom: 10px; padding: .5em;"> <div class="ui-state-highlight ui-corner-all" style="margin-bottom: 10px; padding: .5em;">
<p>Der folgende Report exportiert <i>Ausgangsrechnungen</i>, in eine Excel Datei. Der Export ist auf maximal 3000 Einträge pro Tabelle beschränkt <p>Der folgende Report exportiert <i>Ausgangsrechnungen</i>, in eine Excel Datei. Der
Export ist auf maximal 3000 Einträge pro Tabelle beschränkt
</p> </p>
</div> </div>
<div class="imixs-form-section-2"> <div class="imixs-form-section-2">
@ -64,13 +65,15 @@
<dt> <dt>
Abteilung: Abteilung:
</dt> </dt>
<dd> <dd>
<h:selectOneMenu noSelectionOption="true" <h:selectOneMenu noSelectionOption="true"
value="#{excelExportController.filter.item['space.ref']}"> value="#{excelExportController.filter.item['space.ref']}">
<f:selectItem itemLabel=" - " itemValue="-" ></f:selectItem> <f:selectItem itemLabel=" - " itemValue="-"></f:selectItem>
<c:forEach items="#{spaceRefFormController.getSpaces(workflowController.workitem, options)}" <c:forEach
items="#{spaceRefFormController.getSpaces(workflowController.workitem, options)}"
var="space"> var="space">
<f:selectItem itemLabel="#{space.item['name']}" itemValue="#{space.item['$UniqueID']}"> <f:selectItem itemLabel="#{space.item['name']}"
itemValue="#{space.item['$UniqueID']}">
</f:selectItem> </f:selectItem>
</c:forEach> </c:forEach>
</h:selectOneMenu> </h:selectOneMenu>
@ -82,27 +85,26 @@
Debitor: Debitor:
</dt> </dt>
<dd> <dd>
<ui:include src="/pages/workitems/parts/alexander/debitor_search.xhtml" > <ui:include src="/pages/workitems/parts/alexander/businesspartner_search.xhtml">
<ui:param name="workitem" value="#{workflowController.workitem}" /> <ui:param name="workitem" value="#{workflowController.workitem}" />
<ui:param name="item" value="#{excelExportController.item}" /> <ui:param name="item" value="#{excelExportController.item}" />
</ui:include> </ui:include>
</dd> </dd>
</dl> </dl>
<dl> <dl>
<dt> <dt>
Positionsnummer: Positionsnummer:
</dt> </dt>
<dd> <dd>
<h:inputText <h:inputText value="#{excelExportController.filter.item['invoice.text']}">
value="#{excelExportController.filter.item['invoice.text']}">
</h:inputText> </h:inputText>
</dd> </dd>
</dl> </dl>
</div> </div>
<h:commandButton actionListener="#{excelExportController.export()}" <h:commandButton actionListener="#{excelExportController.export()}"
value="Excel Export starten"> value="Excel Export starten">
</h:commandButton> </h:commandButton>
<h:commandButton value="#{message.close}" action="notes" /> <h:commandButton value="#{message.close}" action="notes" />
</div> </div>

View file

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

View file

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

View file

@ -19,9 +19,9 @@ import org.junit.jupiter.api.Test;
/** /**
* This test generates random test data * This test generates random test data
* *
* @author rsoika * @author rsoika
* *
*/ */
public class TestFormat { public class TestFormat {
@ -54,7 +54,7 @@ public class TestFormat {
* ;"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";""; * ;"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";
* "";"";"";"SO";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";0;"01012023" * "";"";"";"SO";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";"";0;"01012023"
* ;"" * ;""
* *
*/ */
@Test @Test
public void testDatum() { public void testDatum() {

View file

@ -23,9 +23,9 @@ import jakarta.mail.Store;
/** /**
* This test class test the Imixs IMAPAuthenticator classes * This test class test the Imixs IMAPAuthenticator classes
* *
* @author rsoika * @author rsoika
* *
*/ */
public class TestIMAPAuhtenticators { public class TestIMAPAuhtenticators {
@ -35,7 +35,7 @@ public class TestIMAPAuhtenticators {
/** /**
* Test IMAP connect via OAuth for email address imixs@alexander-logistics.com * Test IMAP connect via OAuth for email address imixs@alexander-logistics.com
* *
* https://techcommunity.microsoft.com/t5/exchange/javamail-connecting-to-office-365-xoauth2-for-imap/m-p/1505026 * https://techcommunity.microsoft.com/t5/exchange/javamail-connecting-to-office-365-xoauth2-for-imap/m-p/1505026
*/ */
@Test @Test
@ -76,7 +76,7 @@ public class TestIMAPAuhtenticators {
/** /**
* Test IMAP connect via OAuth for email address imixs@alexander-logistics.com * Test IMAP connect via OAuth for email address imixs@alexander-logistics.com
* *
* https://techcommunity.microsoft.com/t5/exchange/javamail-connecting-to-office-365-xoauth2-for-imap/m-p/1505026 * https://techcommunity.microsoft.com/t5/exchange/javamail-connecting-to-office-365-xoauth2-for-imap/m-p/1505026
*/ */
@Test @Test
@ -118,7 +118,7 @@ public class TestIMAPAuhtenticators {
/** /**
* Test IMAP connect via OAuth for email address imixs@alexander-logistics.com * Test IMAP connect via OAuth for email address imixs@alexander-logistics.com
* *
* https://techcommunity.microsoft.com/t5/exchange/javamail-connecting-to-office-365-xoauth2-for-imap/m-p/1505026 * https://techcommunity.microsoft.com/t5/exchange/javamail-connecting-to-office-365-xoauth2-for-imap/m-p/1505026
*/ */
@Test @Test
@ -163,7 +163,7 @@ public class TestIMAPAuhtenticators {
* <p> * <p>
* NOTE: This test should fail in December 2022 as Basic Authentication is no * NOTE: This test should fail in December 2022 as Basic Authentication is no
* longer supported by Microsoft Outlook. * longer supported by Microsoft Outlook.
* *
*/ */
@Test @Test
@Disabled @Disabled

View file

@ -41,11 +41,11 @@ import jakarta.mail.internet.MimeUtility;
/** /**
* This test shows how to access outlook via oauth2 * This test shows how to access outlook via oauth2
* *
* See: https://www.java-forum.org/thema/javamail-mit-oauth2-an-o365.199133/ * See: https://www.java-forum.org/thema/javamail-mit-oauth2-an-o365.199133/
* *
* @author rsoika * @author rsoika
* *
*/ */
public class TestIMAPOutlookOAuth { public class TestIMAPOutlookOAuth {
@ -58,9 +58,9 @@ public class TestIMAPOutlookOAuth {
/* /*
* Neue Daten * Neue Daten
* *
* Wert: zDL8Q~iEy8AX~dEmIOHrVv3aBTCZLrHIFmShibZ_ * Wert: zDL8Q~iEy8AX~dEmIOHrVv3aBTCZLrHIFmShibZ_ Geheime ID:
* Geheime ID: bcaa558a-3300-4498-94e1-2267f418afd5 * bcaa558a-3300-4498-94e1-2267f418afd5
*/ */
@Test @Test
@ -403,21 +403,21 @@ public class TestIMAPOutlookOAuth {
/** /**
* Helper method to receive a Microsoft access Token * Helper method to receive a Microsoft access Token
* *
* https://jwt.ms/ * https://jwt.ms/
* *
* "OAuth2": { * "OAuth2": {
* *
* "Authority": * "Authority":
* https://login.microsoftonline.com/51e2f038-0a96-41be-801d-bb4118aa018e/v2.0, * https://login.microsoftonline.com/51e2f038-0a96-41be-801d-bb4118aa018e/v2.0,
* *
* "ClientId": "39e9eec5-6939-47dc-9729-3438a9898e63", * "ClientId": "39e9eec5-6939-47dc-9729-3438a9898e63",
* *
* "ClientSecret": "............." * "ClientSecret": "............."
* *
* } * }
* *
* *
* @return * @return
* @throws IOException * @throws IOException
* @throws ClientProtocolException * @throws ClientProtocolException
@ -455,7 +455,7 @@ public class TestIMAPOutlookOAuth {
/** /**
* Helper method to read bytes from strem * Helper method to read bytes from strem
* *
* @param inputStream * @param inputStream
* @return * @return
* @throws IOException * @throws IOException

View file

@ -40,7 +40,7 @@ import com.sun.mail.imap.IMAPFolder;
/** /**
* This test shows how to access outlook via oauth2 * This test shows how to access outlook via oauth2
* *
* @author rsoika * @author rsoika
*/ */
public class TestIMAPOutlookOAuthDebug { public class TestIMAPOutlookOAuthDebug {
@ -223,7 +223,7 @@ public class TestIMAPOutlookOAuthDebug {
/** /**
* Helper method to read bytes from stream * Helper method to read bytes from stream
* *
* @param inputStream * @param inputStream
* @return * @return
* @throws IOException * @throws IOException

View file

@ -8,9 +8,9 @@ import org.junit.jupiter.api.Test;
/** /**
* This test tests the InvoicePlugin isCargosoft method * This test tests the InvoicePlugin isCargosoft method
* *
* @author rsoika * @author rsoika
* *
*/ */
public class TestModelMatcher { public class TestModelMatcher {

View file

@ -17,9 +17,9 @@ import org.junit.jupiter.api.Test;
/** /**
* Testen der KW Berechnung * Testen der KW Berechnung
* *
* @author rsoika * @author rsoika
* *
*/ */
public class TestOPListen { public class TestOPListen {

View file

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

View file

@ -12,9 +12,9 @@ import org.junit.jupiter.api.Test;
/** /**
* This test generates random test data * This test generates random test data
* *
* @author rsoika * @author rsoika
* *
*/ */
public class TestRunden { public class TestRunden {

View file

@ -52,7 +52,7 @@ public class DueDateBugfix {
* Diese Test Method wandelt die Datei OPD_migration_230126.csv in eine EXTV * Diese Test Method wandelt die Datei OPD_migration_230126.csv in eine EXTV
* Datev Datei um, welche wir dann für einen einmaligen Import der * Datev Datei um, welche wir dann für einen einmaligen Import der
* Rechnugnsdaten verwenden können. * Rechnugnsdaten verwenden können.
* *
*/ */
@Test @Test
@Disabled @Disabled
@ -64,38 +64,24 @@ public class DueDateBugfix {
/* /*
* Erledigte * Erledigte
* *
* *
* EXTF_202301_202302030602_1295 * EXTF_202301_202302030602_1295 EXTF_202301_202302060601_1296
* EXTF_202301_202302060601_1296 * EXTF_202301_202302070603_1297 EXTF_202301_202302080603_1298
* EXTF_202301_202302070603_1297 * EXTF_202301_202302090604_1299 EXTF_202301_202302100603_1300
* EXTF_202301_202302080603_1298 * EXTF_202301_202302130601_1301 EXTF_202301_202302140604_1302
* EXTF_202301_202302090604_1299 * EXTF_202301_202302141840_1303 EXTF_202301_202302160944_1304
* EXTF_202301_202302100603_1300 * EXTF_202302_202302020601_1294 EXTF_202302_202302030602_1295
* EXTF_202301_202302130601_1301 * EXTF_202302_202302060601_1296 EXTF_202302_202302070603_1297
* EXTF_202301_202302140604_1302 * EXTF_202302_202302080603_1298 EXTF_202302_202302090604_1299
* EXTF_202301_202302141840_1303 * EXTF_202302_202302100604_1300 EXTF_202302_202302130601_1301
* EXTF_202301_202302160944_1304 * EXTF_202302_202302140604_1302 EXTF_202302_202302141840_1303
* EXTF_202302_202302020601_1294 * EXTF_202302_202302160944_1304 EXTF_202303_202302070603_1297
* EXTF_202302_202302030602_1295 * EXTF_202303_202302080603_1298 EXTF_202303_202302090604_1299
* EXTF_202302_202302060601_1296 * EXTF_202303_202302100603_1300 EXTF_202303_202302130601_1301
* EXTF_202302_202302070603_1297 * EXTF_202303_202302140604_1302 EXTF_202303_202302141840_1303
* EXTF_202302_202302080603_1298
* EXTF_202302_202302090604_1299
* EXTF_202302_202302100604_1300
* EXTF_202302_202302130601_1301
* EXTF_202302_202302140604_1302
* EXTF_202302_202302141840_1303
* EXTF_202302_202302160944_1304
* EXTF_202303_202302070603_1297
* EXTF_202303_202302080603_1298
* EXTF_202303_202302090604_1299
* EXTF_202303_202302100603_1300
* EXTF_202303_202302130601_1301
* EXTF_202303_202302140604_1302
* EXTF_202303_202302141840_1303
* EXTF_202303_202302160944_1304 * EXTF_202303_202302160944_1304
* *
*/ */
int count = 0; int count = 0;

View file

@ -59,10 +59,10 @@ public class MigrationMahnerAusziffern {
* Diese Test Method wandelt die Datei OPD_migration_230126.csv in eine EXTV * Diese Test Method wandelt die Datei OPD_migration_230126.csv in eine EXTV
* Datev Datei um, welche wir dann für einen einmaligen Import der * Datev Datei um, welche wir dann für einen einmaligen Import der
* Rechnugnsdaten verwenden können. * Rechnugnsdaten verwenden können.
* *
* @throws PluginException * @throws PluginException
* @throws RestAPIException * @throws RestAPIException
* *
*/ */
@Test @Test
@Disabled @Disabled
@ -131,7 +131,7 @@ public class MigrationMahnerAusziffern {
/** /**
* Prüft eine einzelen Datenzeile * Prüft eine einzelen Datenzeile
* *
* @param writer * @param writer
* @param data * @param data
* @throws IOException * @throws IOException
@ -231,9 +231,9 @@ public class MigrationMahnerAusziffern {
/** /**
* Diese Methode schließt die Rechnungen mit den übergebenen Rechnungsnummer ab * Diese Methode schließt die Rechnungen mit den übergebenen Rechnungsnummer ab
* *
* Beispiel: Bitte Ausziffern : 17755 = "190818" -> "190313" * Beispiel: Bitte Ausziffern : 17755 = "190818" -> "190313"
* *
* @throws UnsupportedEncodingException * @throws UnsupportedEncodingException
* @throws RestAPIException * @throws RestAPIException
* @throws PluginException * @throws PluginException
@ -265,7 +265,7 @@ public class MigrationMahnerAusziffern {
/** /**
* Teste ob es diese Rechnung im Office gibt. * Teste ob es diese Rechnung im Office gibt.
* *
* @param rech1 * @param rech1
* @throws RestAPIException * @throws RestAPIException
* @throws UnsupportedEncodingException * @throws UnsupportedEncodingException
@ -287,7 +287,7 @@ public class MigrationMahnerAusziffern {
/** /**
* Hilfsmethdoe die prüft ob es zu diesr Rechnung shcon eine passende * Hilfsmethdoe die prüft ob es zu diesr Rechnung shcon eine passende
* Gegenrechnung gibt. * Gegenrechnung gibt.
* *
* @param rech * @param rech
* @return * @return
*/ */

View file

@ -37,7 +37,7 @@ public class MigrationMahnerDatev {
* Diese Test Method wandelt die Datei OPD_migration_230126.csv in eine EXTV * Diese Test Method wandelt die Datei OPD_migration_230126.csv in eine EXTV
* Datev Datei um, welche wir dann für einen einmaligen Import der * Datev Datei um, welche wir dann für einen einmaligen Import der
* Rechnugnsdaten verwenden können. * Rechnugnsdaten verwenden können.
* *
*/ */
@Test @Test
@Disabled @Disabled
@ -139,7 +139,7 @@ public class MigrationMahnerDatev {
/** /**
* Hilfsmethode die einen DATEV Header schreibt * Hilfsmethode die einen DATEV Header schreibt
* *
* @param writer * @param writer
* @throws IOException * @throws IOException
*/ */
@ -157,7 +157,7 @@ public class MigrationMahnerDatev {
/** /**
* Schreibt eine einzelen Datenzeile * Schreibt eine einzelen Datenzeile
* *
* @param writer * @param writer
* @param data * @param data
* @throws IOException * @throws IOException

View file

@ -19,7 +19,7 @@ import org.junit.jupiter.api.Test;
/** /**
* Dieser Agent ordnet nachträglich Ausgangsrechnungen aus der Migraiton einem * Dieser Agent ordnet nachträglich Ausgangsrechnungen aus der Migraiton einem
* Space zu. * Space zu.
* *
* @author rsoika * @author rsoika
* *
*/ */
@ -53,11 +53,11 @@ public class MigrationSpaceZuordnung {
/** /**
* Diese methode liest offene Ausgangsrechnungen aus udn prüft of space.ref * Diese methode liest offene Ausgangsrechnungen aus udn prüft of space.ref
* bereits getzt ist. * bereits getzt ist.
* *
* @throws PluginException * @throws PluginException
* @throws RestAPIException * @throws RestAPIException
* @throws UnsupportedEncodingException * @throws UnsupportedEncodingException
* *
*/ */
@Test @Test
@Disabled @Disabled
@ -149,7 +149,7 @@ public class MigrationSpaceZuordnung {
/** /**
* Helper method that loads all expressions from the spaces * Helper method that loads all expressions from the spaces
* *
* @return List of list of expressions * @return List of list of expressions
* @throws UnsupportedEncodingException * @throws UnsupportedEncodingException
* @throws RestAPIException * @throws RestAPIException

View file

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

View file

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

View file

@ -13,12 +13,12 @@ import org.imixs.workflow.xml.XSLHandler;
/** /**
* The XSLTester is used in junit tests to transform XSL/XML files * The XSLTester is used in junit tests to transform XSL/XML files
* *
* Do not forget to change your pom.xml and add the following to your build * Do not forget to change your pom.xml and add the following to your build
* section * section
* *
* <pre> * <pre>
* {@code * {@code
* <testResources> * <testResources>
* <testResource> * <testResource>
* <directory>${basedir}/../reports</directory> * <directory>${basedir}/../reports</directory>
@ -29,64 +29,64 @@ import org.imixs.workflow.xml.XSLHandler;
* </testResources> * </testResources>
* } * }
* </pre> * </pre>
* *
* @author rsoika * @author rsoika
*/ */
public class XSLTester { public class XSLTester {
final static String ENCODING = "UTF-8"; final static String ENCODING = "UTF-8";
/** /**
* XSL Transformation * XSL Transformation
* *
* @throws IOException * @throws IOException
* @throws TransformerException * @throws TransformerException
*/ */
public static void transform(String xmlSourcePath, String xslSourcePath, String outputPath) public static void transform(String xmlSourcePath, String xslSourcePath, String outputPath)
throws IOException, TransformerException { throws IOException, TransformerException {
OutputStream outputStream = new FileOutputStream(outputPath); OutputStream outputStream = new FileOutputStream(outputPath);
// Use the class loader to load the XML file as a resource // Use the class loader to load the XML file as a resource
ClassLoader classLoader = XSLTester.class.getClassLoader(); ClassLoader classLoader = XSLTester.class.getClassLoader();
InputStream xmlInputStream = classLoader.getResourceAsStream(xmlSourcePath); InputStream xmlInputStream = classLoader.getResourceAsStream(xmlSourcePath);
InputStream xslInputStream = classLoader.getResourceAsStream(xslSourcePath); InputStream xslInputStream = classLoader.getResourceAsStream(xslSourcePath);
try { try {
if (xmlInputStream == null) { if (xmlInputStream == null) {
throw new RuntimeException("XML file not found: " + xmlSourcePath); throw new RuntimeException("XML file not found: " + xmlSourcePath);
} }
if (xslInputStream == null) { if (xslInputStream == null) {
throw new RuntimeException("XSL file not found: " + xslSourcePath); throw new RuntimeException("XSL file not found: " + xslSourcePath);
} }
String xmlSource = readXml(xmlInputStream); String xmlSource = readXml(xmlInputStream);
String xslSource = readXml(xslInputStream); String xslSource = readXml(xslInputStream);
XSLHandler.transform(xmlSource, xslSource, ENCODING, outputStream); XSLHandler.transform(xmlSource, xslSource, ENCODING, outputStream);
} finally { } finally {
if (xmlInputStream != null) if (xmlInputStream != null)
xmlInputStream.close(); xmlInputStream.close();
if (xslInputStream != null) if (xslInputStream != null)
xslInputStream.close(); xslInputStream.close();
if (outputStream != null) if (outputStream != null)
outputStream.close(); outputStream.close();
} }
} }
/** /**
* read and return the XML content as a String * read and return the XML content as a String
* *
*/ */
private static String readXml(InputStream inputStream) { private static String readXml(InputStream inputStream) {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) { try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
StringBuilder xmlContent = new StringBuilder(); StringBuilder xmlContent = new StringBuilder();
String line; String line;
while ((line = reader.readLine()) != null) { while ((line = reader.readLine()) != null) {
xmlContent.append(line).append("\n"); xmlContent.append(line).append("\n");
} }
return xmlContent.toString(); return xmlContent.toString();
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException("Error reading XML file", e); throw new RuntimeException("Error reading XML file", e);
} }
} }
} }

View file

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

View file

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

View file

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

View file

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

39
pom.xml
View file

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

View file

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