neue Code Formatierung

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

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

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

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

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

View file

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

View file

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

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

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

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

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

View file

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

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

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -25,18 +25,18 @@ import jakarta.inject.Inject;
* <p>
* report = report definition to transfers the workitem into the cargosoft xml
* structure
*
*
* <pre>
* {@code
<cargosoft name="report">cargosoft</cargosoft>
}
* </pre>
* <p>
* Because we also export the attachment data to cargosoft, the adaper lookups
* the conente of the attachment in the snapshot of the origin workitem
*
*
*
*
* @version 1.0
* @author rsoika
*/
@ -113,8 +113,8 @@ public class CargosoftExportAdapter implements SignalAdapter {
// finally append the success event id
document.event(EVENT_SUCCESS);
} catch (PluginException | IOException
| jakarta.xml.bind.JAXBException | javax.xml.transform.TransformerException e) {
} catch (PluginException | IOException | jakarta.xml.bind.JAXBException
| javax.xml.transform.TransformerException e) {
logger.severe("cargosoft export failed: " + e.getMessage());
document.setItemValue("cargosoft.error", e.getMessage());
document.event(EVENT_FAILURE);
@ -130,8 +130,8 @@ public class CargosoftExportAdapter implements SignalAdapter {
* 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
* ohne eine Fehlermeldugn zu liefern.
*
*
*
*
* @param document
* @return
*/

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,26 +1,26 @@
/*******************************************************************************
* Imixs Workflow
* Copyright (C) 2001, 2011 Imixs Software Solutions GmbH,
* Imixs Workflow
* Copyright (C) 2001, 2011 Imixs Software Solutions GmbH,
* http://www.imixs.com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
*
* You can receive a copy of the GNU General Public
* License at http://www.gnu.org/licenses/gpl.html
*
* Project:
*
* Project:
* http://www.imixs.org
* http://java.net/projects/imixs-workflow
*
* Contributors:
*
* Contributors:
* Imixs Software Solutions GmbH - initial API and implementation
* Ralph Soika - Software Developer
*******************************************************************************/
@ -47,9 +47,9 @@ import jakarta.inject.Inject;
/**
* Der InvoiceService stellt einige Hilfsmethoden bereit.
*
*
* @author rsoika
*
*
*/
@Singleton
@RunAs("org.imixs.ACCESSLEVEL.MANAGERACCESS")
@ -79,7 +79,7 @@ public class InvoiceService {
/**
* Liefert die Liste der Ausgehenden Währungen
*
*
* @return
* @throws PluginException
*/
@ -89,8 +89,7 @@ public class InvoiceService {
ItemCollection configItemCollection = configService.loadConfiguration("AGL_CONFIGURATION");
List<String> currencyList = configItemCollection.getItemValue("currency.out");
if (currencyList == null || currencyList.size() < 1) {
throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(),
InvoiceService.ERROR_CONFIG,
throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), InvoiceService.ERROR_CONFIG,
"missing Currency configuration - please check parameters");
}
return currencyList;
@ -98,7 +97,7 @@ public class InvoiceService {
/**
* Gibt die Hauptwährung zurück (1. in der liste)
*
*
* @return
* @throws PluginException
*/
@ -108,8 +107,7 @@ public class InvoiceService {
ItemCollection configItemCollection = configService.loadConfiguration("AGL_CONFIGURATION");
List<String> currencyList = configItemCollection.getItemValue("currency.out");
if (currencyList == null || currencyList.size() < 1) {
throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(),
InvoiceService.ERROR_CONFIG,
throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), InvoiceService.ERROR_CONFIG,
"missing Currency configuration - please check parameters");
}
return currencyList.get(0);
@ -117,7 +115,7 @@ public class InvoiceService {
/**
* Liefert die Liste der Eingehenden Währungen
*
*
* @return
* @throws PluginException
*/
@ -127,16 +125,14 @@ public class InvoiceService {
ItemCollection configItemCollection = configService.loadConfiguration("AGL_CONFIGURATION");
List<String> currencyList = configItemCollection.getItemValue("currency.in");
if (currencyList == null || currencyList.size() < 1) {
throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(),
InvoiceService.ERROR_CONFIG,
throw new PluginException(AGLAnalyticExcelAdapter.class.getSimpleName(), InvoiceService.ERROR_CONFIG,
"missing Currency configuration - please check parameters");
}
return currencyList;
}
/**
* Prüft ob der Vorgang überflällig ist.
* Default ist 21 tage.
* Prüft ob der Vorgang überflällig ist. Default ist 21 tage.
*/
public boolean isOverdue(Date date, int taskid) {
Date now = new Date();
@ -149,9 +145,9 @@ public class InvoiceService {
/**
* This method returns a text-block ItemCollection for a specified name.
*
*
* @param name in attribute txtname
*
*
*
*/
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
* Ausgangsrechnugnen
*
* - Rundungs Methode
* - Berechnung der Positionsnummern
* - Explode Childitems
*
*
* - Rundungs Methode - Berechnung der Positionsnummern - Explode Childitems
*
* See:
* https://stackoverflow.com/questions/2808535/round-a-double-to-2-decimal-places
*
*
* @author rsoika
*
*/
@ -42,7 +40,7 @@ public class InvoiceUtil {
/**
* Rounds a Flat Value
*
*
* @param value
* @return
*/
@ -54,7 +52,7 @@ public class InvoiceUtil {
/**
* Hilfsmethode zum validieren der Mailadresse
*
*
* @param emailAddress
* @return
*/
@ -63,9 +61,7 @@ public class InvoiceUtil {
String regexPattern = "^(?=.{1,64}@)[A-Za-z0-9_-]+(\\.[A-Za-z0-9_-]+)*@"
+ "[^-][A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(\\.[A-Za-z]{2,})$";
return Pattern.compile(regexPattern)
.matcher(emailAddress)
.matches();
return Pattern.compile(regexPattern).matcher(emailAddress).matches();
}
// @Deprecated
@ -102,7 +98,7 @@ public class InvoiceUtil {
/**
* Convert the List of ItemCollections back into a List of Map elements
*
*
* @param workitem
*/
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
*
*
* @param workitem
*/
@SuppressWarnings({ "rawtypes" })
@ -130,11 +126,10 @@ public class InvoiceUtil {
/**
* Diese Methode berechnet das feld 'invoice.positions' welches eine Valueliste
* mit allen Positionsnummern aus dem Childliste enthält.
* Es wird sowohl für Ausgangs wie Eingangsrechnungen genutzt.
* Das feld wird indiziert, so dass eine gezielte Suche nach positiosnummern
* möglich ist.
*
* mit allen Positionsnummern aus dem Childliste enthält. Es wird sowohl für
* Ausgangs wie Eingangsrechnungen genutzt. Das feld wird indiziert, so dass
* eine gezielte Suche nach positiosnummern möglich ist.
*
* @param workitem
*/
public static void updateInvoicePositions(ItemCollection workitem) {
@ -162,9 +157,9 @@ public class InvoiceUtil {
/**
* Diese Hilfsmethode harmonisiert die Debitoren/Kreditoren ID von Cargosoft und
* liefert eine Geschäftspartner ID zurück.
*
*
* Aus Kreditor K70153 und Debitor D10153 wird die einheitliche ID: BP0153
*
*
* @param key
* @return
*/
@ -188,7 +183,7 @@ public class InvoiceUtil {
/**
* Diese Hilfmethode berechnet die BusinessPartner id aus einer Invoice
*
*
* @param Invoice
* @return
*/
@ -204,7 +199,7 @@ public class InvoiceUtil {
/**
* Diese Hilfmethode berechnet die BusinessPartner Namen aus einer Invoice
*
*
* @param Invoice
* @return
*/
@ -223,7 +218,7 @@ public class InvoiceUtil {
/**
* Gibt true zurück wenn es sich um eine Kreditoren Rechnung/Gutschrift handelt.
*
*
* @param invoice
* @return
*/
@ -238,7 +233,7 @@ public class InvoiceUtil {
/**
* Gibt true zurück wenn es sich um eine Kreditoren Sachrechnung handelt.
*
*
* @param invoice
* @return
*/
@ -252,7 +247,7 @@ public class InvoiceUtil {
/**
* Gibt true zurück wenn es sich um eine Debitoren Rechnung/Gutschrift handelt.
*
*
* @param invoice
* @return
*/

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -18,7 +18,7 @@ import jakarta.inject.Named;
* <p>
* Zusätzlich bietet er die Summenberechnung an, bei der die Gutschriften
* abgezogen werden.
*
*
* @author rsoika
*
*/
@ -41,7 +41,7 @@ public class ZahlungsavisController implements Serializable {
* result is rounded to 2 digits.
* <p>
* Gutschriften werden abgezogen
*
*
* @param refids - list of workitem uniqueIds
* @param item - name of the item to summarize
* @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
*
*
* <pre>
* {@code
<zahlungsavis name="textblock">textblock-ref</zahlungsavis>
@ -38,7 +38,7 @@ import jakarta.inject.Inject;
* <p>
* Der Adapter erweitert den POIAdapter somit können felder aktualisiert werden
* (siehe POIFineReplaceAdapter).
*
*
* <p>
* Der Adapter kopiert die Rechnungsdaten in neue Zeilen, welche ab Zeilnenummer
* 16 eingefügt werden.
@ -46,7 +46,7 @@ import jakarta.inject.Inject;
* Dabei werden Gutschriften in spalte E und Rechnungen in Spalte F eingetrgen.
* <p>
* Abschliessend wird die Zelle 'TOTAL' noch aktualisiert.
*
*
* @version 1.0
* @author rsoika
*/
@ -69,7 +69,7 @@ public class ZahlungsavisExportAdapter extends POIFindReplaceAdapter {
/**
* This method finds or create the Zahlungsavis and adds a reference
* ($workitemref) to the current invoice.
*
*
* @throws PluginException
*/
@SuppressWarnings("unchecked")
@ -121,7 +121,7 @@ public class ZahlungsavisExportAdapter extends POIFindReplaceAdapter {
* <p>
* The named cell 'TOTAL' should contain the summary formula. It will be
* evaluated at the end.
*
*
* @throws PluginException
*/
@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
* fileData object of this document.
*
*
* @param document
* @throws PluginException
*/
@ -223,9 +223,9 @@ public class ZahlungsavisExportAdapter extends POIFindReplaceAdapter {
/**
* This method returns a text-block ItemCollection for a specified name.
*
*
* @param name in attribute txtname
*
*
*
*/
public FileData loadTextBlockFileData(String name, String fileName) {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -15,12 +15,12 @@ import jakarta.inject.Inject;
import jakarta.inject.Named;
/**
* Der InkassoController dient dazu eine bereits zugewiesene Rechnung
* wieder aus dem Inkasso zu entfernen.
* Der InkassoController dient dazu eine bereits zugewiesene Rechnung wieder aus
* dem Inkasso zu entfernen.
* <p>
* Zusätzlich bietet er die Summenberechnung an, bei der die Gutschriften
* abgezogen werden.
*
*
* @author rsoika
*
*/
@ -43,7 +43,7 @@ public class InkassoController implements Serializable {
* result is rounded to 2 digits.
* <p>
* Gutschriften werden abgezogen
*
*
* @param refids - list of workitem uniqueIds
* @param item - name of the item to summarize
* @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
*
*
* <pre>
* {@code
<inkasso name="textblock">textblock-ref</inkasso>
@ -38,7 +38,7 @@ import jakarta.inject.Inject;
* <p>
* Der Adapter erweitert den POIAdapter somit können felder aktualisiert werden
* (siehe POIFineReplaceAdapter).
*
*
* <p>
* Der Adapter kopiert die Rechnungsdaten in neue Zeilen, welche ab Zeilnenummer
* 16 eingefügt werden.
@ -46,7 +46,7 @@ import jakarta.inject.Inject;
* Dabei werden Gutschriften in spalte E und Rechnungen in Spalte F eingetrgen.
* <p>
* Abschliessend wird die Zelle 'TOTAL' noch aktualisiert.
*
*
* @version 1.0
* @author rsoika
*/
@ -69,7 +69,7 @@ public class InkassoExportAdapter extends POIFindReplaceAdapter {
/**
* This method finds or create the Zahlungsavis and adds a reference
* ($workitemref) to the current invoice.
*
*
* @throws PluginException
*/
@SuppressWarnings("unchecked")
@ -121,7 +121,7 @@ public class InkassoExportAdapter extends POIFindReplaceAdapter {
* <p>
* The named cell 'TOTAL' should contain the summary formula. It will be
* evaluated at the end.
*
*
* @throws PluginException
*/
@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
* fileData object of this document.
*
*
* @param document
* @throws PluginException
*/
@ -216,9 +216,9 @@ public class InkassoExportAdapter extends POIFindReplaceAdapter {
/**
* This method returns a text-block ItemCollection for a specified name.
*
*
* @param name in attribute txtname
*
*
*
*/
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
* HTML Table wen sending the mail.
* <p>
* The Adapter also splits the item 'dbtr.mail' into a multi value field if
* the value contains email addresses separated with new lines.
*
*
* The Adapter also splits the item 'dbtr.mail' into a multi value field if the
* value contains email addresses separated with new lines.
*
*
* @version 1.0
* @author rsoika
*/
@ -52,7 +52,7 @@ public class MahnlaufExecuteAdapter implements SignalAdapter {
/**
* This method finds all invices ($workitemref) and tirgges event 300
*
*
* @throws PluginException
*/
@SuppressWarnings("unchecked")
@ -125,24 +125,24 @@ public class MahnlaufExecuteAdapter implements SignalAdapter {
}
switch (invoice.getTaskID()) {
case 5200:
case 5201:
if (dunningLevel < 1)
dunningLevel = 1;
break;
case 5210:
case 5211:
if (dunningLevel < 2)
dunningLevel = 2;
break;
case 5220:
if (dunningLevel < 3)
dunningLevel = 3;
break;
default:
if (dunningLevel < 1)
dunningLevel = 1;
break;
case 5200:
case 5201:
if (dunningLevel < 1)
dunningLevel = 1;
break;
case 5210:
case 5211:
if (dunningLevel < 2)
dunningLevel = 2;
break;
case 5220:
if (dunningLevel < 3)
dunningLevel = 3;
break;
default:
if (dunningLevel < 1)
dunningLevel = 1;
break;
}
// create a workitem stub for the mail message
@ -179,18 +179,18 @@ public class MahnlaufExecuteAdapter implements SignalAdapter {
// compute subject
switch (dunningLevel) {
case 2:
mahnLaufWorkitem.setItemValue("mail.subject_de", "2. MAHNUNG");
mahnLaufWorkitem.setItemValue("mail.subject_en", "2. REMINDER");
break;
case 3:
mahnLaufWorkitem.setItemValue("mail.subject_de", "LETZTE AUSSERGERICHTLICHE MAHNUNG");
mahnLaufWorkitem.setItemValue("mail.subject_en", "LAST REMINDER");
break;
default:
mahnLaufWorkitem.setItemValue("mail.subject_de", "1. MAHNUNG");
mahnLaufWorkitem.setItemValue("mail.subject_en", "1. REMINDER");
break;
case 2:
mahnLaufWorkitem.setItemValue("mail.subject_de", "2. MAHNUNG");
mahnLaufWorkitem.setItemValue("mail.subject_en", "2. REMINDER");
break;
case 3:
mahnLaufWorkitem.setItemValue("mail.subject_de", "LETZTE AUSSERGERICHTLICHE MAHNUNG");
mahnLaufWorkitem.setItemValue("mail.subject_en", "LAST REMINDER");
break;
default:
mahnLaufWorkitem.setItemValue("mail.subject_de", "1. MAHNUNG");
mahnLaufWorkitem.setItemValue("mail.subject_en", "1. REMINDER");
break;
}
// Verify the dbtr.mail item (split into multi value)
@ -198,8 +198,7 @@ public class MahnlaufExecuteAdapter implements SignalAdapter {
for (String mail : mailListe) {
logger.fine(" test mail address:'" + mail + "'");
if (!InvoiceUtil.validateEmail(mail)) {
throw new PluginException(MahnlaufExecuteAdapter.class.getName(),
"INVALID_EMAIL",
throw new PluginException(MahnlaufExecuteAdapter.class.getName(), "INVALID_EMAIL",
"Die Daten konnten nicht aktualisiert werden! Bitte geben Sie eine gültige E-Mail Adresse ein!");
}
}
@ -208,7 +207,7 @@ public class MahnlaufExecuteAdapter implements SignalAdapter {
/**
* Returns true if the reminder date is today or in the past
*
*
* @param uniqueid
* @return
*/

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -23,9 +23,9 @@ import jakarta.mail.Store;
/**
* This test class test the Imixs IMAPAuthenticator classes
*
*
* @author rsoika
*
*
*/
public class TestIMAPAuhtenticators {
@ -35,7 +35,7 @@ public class TestIMAPAuhtenticators {
/**
* 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
*/
@Test
@ -76,7 +76,7 @@ public class TestIMAPAuhtenticators {
/**
* 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
*/
@Test
@ -118,7 +118,7 @@ public class TestIMAPAuhtenticators {
/**
* 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
*/
@Test
@ -163,7 +163,7 @@ public class TestIMAPAuhtenticators {
* <p>
* NOTE: This test should fail in December 2022 as Basic Authentication is no
* longer supported by Microsoft Outlook.
*
*
*/
@Test
@Disabled

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -12,9 +12,9 @@ import org.junit.jupiter.api.Test;
/**
* This test generates random test data
*
*
* @author rsoika
*
*
*/
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
* Datev Datei um, welche wir dann für einen einmaligen Import der
* Rechnugnsdaten verwenden können.
*
*
*/
@Test
@Disabled
@ -64,38 +64,24 @@ public class DueDateBugfix {
/*
* Erledigte
*
*
* EXTF_202301_202302030602_1295
* EXTF_202301_202302060601_1296
* EXTF_202301_202302070603_1297
* EXTF_202301_202302080603_1298
* EXTF_202301_202302090604_1299
* EXTF_202301_202302100603_1300
* EXTF_202301_202302130601_1301
* EXTF_202301_202302140604_1302
* EXTF_202301_202302141840_1303
* EXTF_202301_202302160944_1304
* EXTF_202302_202302020601_1294
* EXTF_202302_202302030602_1295
* EXTF_202302_202302060601_1296
* EXTF_202302_202302070603_1297
* EXTF_202302_202302080603_1298
* EXTF_202302_202302090604_1299
* EXTF_202302_202302100604_1300
* EXTF_202302_202302130601_1301
* EXTF_202302_202302140604_1302
* EXTF_202302_202302141840_1303
* EXTF_202302_202302160944_1304
* EXTF_202303_202302070603_1297
* EXTF_202303_202302080603_1298
* EXTF_202303_202302090604_1299
* EXTF_202303_202302100603_1300
* EXTF_202303_202302130601_1301
* EXTF_202303_202302140604_1302
* EXTF_202303_202302141840_1303
*
*
* EXTF_202301_202302030602_1295 EXTF_202301_202302060601_1296
* EXTF_202301_202302070603_1297 EXTF_202301_202302080603_1298
* EXTF_202301_202302090604_1299 EXTF_202301_202302100603_1300
* EXTF_202301_202302130601_1301 EXTF_202301_202302140604_1302
* EXTF_202301_202302141840_1303 EXTF_202301_202302160944_1304
* EXTF_202302_202302020601_1294 EXTF_202302_202302030602_1295
* EXTF_202302_202302060601_1296 EXTF_202302_202302070603_1297
* EXTF_202302_202302080603_1298 EXTF_202302_202302090604_1299
* EXTF_202302_202302100604_1300 EXTF_202302_202302130601_1301
* EXTF_202302_202302140604_1302 EXTF_202302_202302141840_1303
* EXTF_202302_202302160944_1304 EXTF_202303_202302070603_1297
* EXTF_202303_202302080603_1298 EXTF_202303_202302090604_1299
* EXTF_202303_202302100603_1300 EXTF_202303_202302130601_1301
* EXTF_202303_202302140604_1302 EXTF_202303_202302141840_1303
* EXTF_202303_202302160944_1304
*
*
*/
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
* Datev Datei um, welche wir dann für einen einmaligen Import der
* Rechnugnsdaten verwenden können.
*
*
* @throws PluginException
* @throws RestAPIException
*
*
*/
@Test
@Disabled
@ -131,7 +131,7 @@ public class MigrationMahnerAusziffern {
/**
* Prüft eine einzelen Datenzeile
*
*
* @param writer
* @param data
* @throws IOException
@ -231,9 +231,9 @@ public class MigrationMahnerAusziffern {
/**
* Diese Methode schließt die Rechnungen mit den übergebenen Rechnungsnummer ab
*
*
* Beispiel: Bitte Ausziffern : 17755 = "190818" -> "190313"
*
*
* @throws UnsupportedEncodingException
* @throws RestAPIException
* @throws PluginException
@ -265,7 +265,7 @@ public class MigrationMahnerAusziffern {
/**
* Teste ob es diese Rechnung im Office gibt.
*
*
* @param rech1
* @throws RestAPIException
* @throws UnsupportedEncodingException
@ -287,7 +287,7 @@ public class MigrationMahnerAusziffern {
/**
* Hilfsmethdoe die prüft ob es zu diesr Rechnung shcon eine passende
* Gegenrechnung gibt.
*
*
* @param rech
* @return
*/

View file

@ -37,7 +37,7 @@ public class MigrationMahnerDatev {
* 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
* Rechnugnsdaten verwenden können.
*
*
*/
@Test
@Disabled
@ -139,7 +139,7 @@ public class MigrationMahnerDatev {
/**
* Hilfsmethode die einen DATEV Header schreibt
*
*
* @param writer
* @throws IOException
*/
@ -157,7 +157,7 @@ public class MigrationMahnerDatev {
/**
* Schreibt eine einzelen Datenzeile
*
*
* @param writer
* @param data
* @throws IOException

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

39
pom.xml
View file

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

View file

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