diff --git a/BPMN-KI-Test/prompt1.txt b/BPMN-KI-Test/prompt1.txt new file mode 100644 index 0000000..2474e4b --- /dev/null +++ b/BPMN-KI-Test/prompt1.txt @@ -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: + + + + + + + + + + + true + + + + + + + + + task_7Y3UUA + event_bH7Wag + event_oNXxgg + event_GEqHhQ + event_VJXKGg + task_SP75zg + gateway_ASbu1A + gateway_Vyo0Zw + task_xAzhzw + event_YH3bnw + event_1qR49w + task_fHgZBg + event_YnFi7A + + + + event_A1J0OA + task_5lS3mw + gateway_Hn5ZhQ + event_dJ89xQ + event_30uUNA + + + + + sequenceFlow_8yitUQ + + + + sequenceFlow_BGVbSA + sequenceFlow_FyAf3g + sequenceFlow_hvQWmg + sequenceFlow_j4GDgg + + + + sequenceFlow_iVpj0g + + + + sequenceFlow_8yitUQ + sequenceFlow_BGVbSA + + + + + + + + + + sequenceFlow_rZXh6g + sequenceFlow_sNlWkQ + + + + sequenceFlow_sNlWkQ + sequenceFlow_nxZ9bA + + + + sequenceFlow_DXGQrw + sequenceFlow_UebylQ + + + + sequenceFlow_FyAf3g + sequenceFlow_rZXh6g + sequenceFlow_i7cH2A + + + + + + + + + + + + + sequenceFlow_i7cH2A + sequenceFlow_DXGQrw + + + + + + + + + + sequenceFlow_nxZ9bA + sequenceFlow_9tp42Q + sequenceFlow_h4SfiQ + + + + sequenceFlow_PLk0RA + sequenceFlow_A0FQoA + + + + sequenceFlow_UebylQ + sequenceFlow_PLk0RA + sequenceFlow_xVEjcA + + + + + + + sequenceFlow_9OrwEg + sequenceFlow_A0FQoA + sequenceFlow_OfKNWA + + + + + + + + + + sequenceFlow_9tp42Q + sequenceFlow_9OrwEg + + + + + + + + + + sequenceFlow_h4SfiQ + sequenceFlow_hvQWmg + + + + sequenceFlow_xVEjcA + sequenceFlow_j4GDgg + + + + + + + + + + + + + + + + + + + sequenceFlow_A0FQoA + sequenceFlow_sUEtcA + sequenceFlow_iVpj0g + + + + sequenceFlow_OfKNWA + sequenceFlow_sUEtcA + + + + + + + + + + + + + \ No newline at end of file diff --git a/BPMN-KI-Test/prompt2.txt b/BPMN-KI-Test/prompt2.txt new file mode 100644 index 0000000..edc985c --- /dev/null +++ b/BPMN-KI-Test/prompt2.txt @@ -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. diff --git a/BPMN-KI-Test/rechnung-ai-test.bpmn b/BPMN-KI-Test/rechnung-ai-test.bpmn new file mode 100644 index 0000000..654d785 --- /dev/null +++ b/BPMN-KI-Test/rechnung-ai-test.bpmn @@ -0,0 +1,389 @@ + + + + + + + + + + true + + + + + + + + + task_7Y3UUA + event_bH7Wag + event_oNXxgg + event_GEqHhQ + event_VJXKGg + task_SP75zg + gateway_ASbu1A + gateway_Vyo0Zw + task_xAzhzw + event_YH3bnw + event_1qR49w + task_fHgZBg + event_YnFi7A + + + + event_A1J0OA + task_5lS3mw + gateway_Hn5ZhQ + event_dJ89xQ + event_30uUNA + + + + + sequenceFlow_8yitUQ + + + + sequenceFlow_BGVbSA + sequenceFlow_FyAf3g + sequenceFlow_hvQWmg + sequenceFlow_j4GDgg + + + + sequenceFlow_iVpj0g + + + + sequenceFlow_8yitUQ + sequenceFlow_BGVbSA + + + + + + + + + + sequenceFlow_rZXh6g + sequenceFlow_sNlWkQ + + + + sequenceFlow_sNlWkQ + sequenceFlow_nxZ9bA + + + + sequenceFlow_DXGQrw + sequenceFlow_UebylQ + + + + sequenceFlow_FyAf3g + sequenceFlow_rZXh6g + sequenceFlow_i7cH2A + + + + + + + + + + + + + sequenceFlow_i7cH2A + sequenceFlow_DXGQrw + + + + + + + + + + sequenceFlow_nxZ9bA + sequenceFlow_9tp42Q + sequenceFlow_h4SfiQ + + + + sequenceFlow_PLk0RA + sequenceFlow_A0FQoA + + + + sequenceFlow_UebylQ + sequenceFlow_PLk0RA + sequenceFlow_xVEjcA + + + + + + + sequenceFlow_9OrwEg + sequenceFlow_A0FQoA + sequenceFlow_OfKNWA + + + + + + + + + + sequenceFlow_9tp42Q + sequenceFlow_9OrwEg + + + + + + + + + + sequenceFlow_h4SfiQ + sequenceFlow_hvQWmg + + + + sequenceFlow_xVEjcA + sequenceFlow_j4GDgg + + + + + + + + + + + + + + + + + + + sequenceFlow_A0FQoA + sequenceFlow_sUEtcA + sequenceFlow_iVpj0g + + + + sequenceFlow_OfKNWA + sequenceFlow_sUEtcA + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/BPMN-KI-Test/rechnung-ai-test_plain.bpmn b/BPMN-KI-Test/rechnung-ai-test_plain.bpmn new file mode 100644 index 0000000..c143e34 --- /dev/null +++ b/BPMN-KI-Test/rechnung-ai-test_plain.bpmn @@ -0,0 +1,207 @@ + + + + + + + + + + true + + + + + + + + + task_7Y3UUA + event_bH7Wag + event_oNXxgg + event_GEqHhQ + event_VJXKGg + task_SP75zg + gateway_ASbu1A + gateway_Vyo0Zw + task_xAzhzw + event_YH3bnw + event_1qR49w + task_fHgZBg + event_YnFi7A + + + + event_A1J0OA + task_5lS3mw + gateway_Hn5ZhQ + event_dJ89xQ + event_30uUNA + + + + + sequenceFlow_8yitUQ + + + + sequenceFlow_BGVbSA + sequenceFlow_FyAf3g + sequenceFlow_hvQWmg + sequenceFlow_j4GDgg + + + + sequenceFlow_iVpj0g + + + + sequenceFlow_8yitUQ + sequenceFlow_BGVbSA + + + + + + + + + + sequenceFlow_rZXh6g + sequenceFlow_sNlWkQ + + + + sequenceFlow_sNlWkQ + sequenceFlow_nxZ9bA + + + + sequenceFlow_DXGQrw + sequenceFlow_UebylQ + + + + sequenceFlow_FyAf3g + sequenceFlow_rZXh6g + sequenceFlow_i7cH2A + + + + + + + + + + + + + sequenceFlow_i7cH2A + sequenceFlow_DXGQrw + + + + + + + + + + sequenceFlow_nxZ9bA + sequenceFlow_9tp42Q + sequenceFlow_h4SfiQ + + + + sequenceFlow_PLk0RA + sequenceFlow_A0FQoA + + + + sequenceFlow_UebylQ + sequenceFlow_PLk0RA + sequenceFlow_xVEjcA + + + + + + + sequenceFlow_9OrwEg + sequenceFlow_A0FQoA + sequenceFlow_OfKNWA + + + + + + + + + + sequenceFlow_9tp42Q + sequenceFlow_9OrwEg + + + + + + + + + + sequenceFlow_h4SfiQ + sequenceFlow_hvQWmg + + + + sequenceFlow_xVEjcA + sequenceFlow_j4GDgg + + + + + + + + + + + + + + + + + + + sequenceFlow_A0FQoA + sequenceFlow_sUEtcA + sequenceFlow_iVpj0g + + + + sequenceFlow_OfKNWA + sequenceFlow_sUEtcA + + + + + + + + + + + + + \ No newline at end of file diff --git a/BPMN-KI-Test/schema-1.txt b/BPMN-KI-Test/schema-1.txt new file mode 100644 index 0000000..68eab89 --- /dev/null +++ b/BPMN-KI-Test/schema-1.txt @@ -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) \ No newline at end of file diff --git a/BPMN-KI-Test/schema-2.txt b/BPMN-KI-Test/schema-2.txt new file mode 100644 index 0000000..259d973 --- /dev/null +++ b/BPMN-KI-Test/schema-2.txt @@ -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 diff --git a/doc/zoho/README.md b/doc/zoho/README.md index bc728ce..2660495 100644 --- a/doc/zoho/README.md +++ b/doc/zoho/README.md @@ -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: + + + +3. Und dort die Option "Enter invoice numbers manually" auswählen + + diff --git a/doc/zoho/SECRETS.md b/doc/zoho/SECRETS.md new file mode 100644 index 0000000..63761e4 --- /dev/null +++ b/doc/zoho/SECRETS.md @@ -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/ diff --git a/doc/zoho/disable-auto-generated-number-01.png b/doc/zoho/disable-auto-generated-number-01.png new file mode 100644 index 0000000..676fe50 Binary files /dev/null and b/doc/zoho/disable-auto-generated-number-01.png differ diff --git a/doc/zoho/disable-auto-generated-number-02.png b/doc/zoho/disable-auto-generated-number-02.png new file mode 100644 index 0000000..a2e043d Binary files /dev/null and b/doc/zoho/disable-auto-generated-number-02.png differ diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/AGLAnalyticControllerDebitor.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/AGLAnalyticControllerDebitor.java index 7cdf274..fd6b866 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/AGLAnalyticControllerDebitor.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/AGLAnalyticControllerDebitor.java @@ -34,7 +34,7 @@ import jakarta.inject.Named; /** * Der AGLAnalyticControllerDebitor berechnet verschiedene Analyse Daten von * Ausgangsrechnungen zu einem Business partner - * + * * @author rsoika * */ @@ -42,614 +42,607 @@ import jakarta.inject.Named; @ConversationScoped public class AGLAnalyticControllerDebitor implements Serializable { - private static final long serialVersionUID = 1L; - private static Logger logger = Logger.getLogger(AGLAnalyticControllerDebitor.class.getName()); - - @Inject - protected DocumentService documentService; - - @Inject - protected AGLConfigController aglConfigController; - - @Inject - protected WorkflowController workflowController; - - @Inject - TeamService teamService; - - TreeMap stats = null; - - List invoices = null; - int countAll = 0; - int countOpen = 0; - int countDue = 0; - int countDunning = 0; - - double totalAllCurrency1 = 0; - double totalAllCurrency2 = 0; - - double totalOpenCurrency1 = 0; - double totalOpenCurrency2 = 0; - - double totalDueCurrency1 = 0; - double totalDueCurrency2 = 0; - - double totalDunningCurrency1 = 0; - double totalDunningCurrency2 = 0; - - double averagePaymentDue = 0; - double averagePaymentDays = 0; - - String chartData = ""; - - public void onEvent(@Observes AnalyticEvent event) { - - if (!event.getWorkitem().getModelVersion().startsWith("businesspartner") - && !event.getWorkitem().getModelVersion().startsWith("analyse-debitor")) { - // no op - return; - } - - String dbtrNumber = event.getWorkitem().getItemValueString("dbtr.number"); - logger.fine("Analytic Event : " + dbtrNumber); - String dbtrNumberLast = event.getWorkitem().getItemValueString("dbtr.number.last"); - - // Recompute only if last dbtr.number has changed or no values yet computed - - // load all invoices? - if (invoices == null || (!dbtrNumber.isEmpty() && !dbtrNumber.equals(dbtrNumberLast))) { - resetStats(event); - event.getWorkitem().setItemValue("dbtr.number.last", dbtrNumber); - logger.info("Analyse new debitor data for : " + dbtrNumber); - loadRechnungen(event); - chartData = buildChartData(); - } - - // use cache? - if (event.getWorkitem().hasItem(event.getKey())) { - // logger.info(" use cache for " + event.getKey()); - // no op - return; - } - - String link = "/pages/workitems/worklist.xhtml" + "?phrase=" + getDbtNr(); - 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() - + "&phrase=" + getDbtNr(); - } - if ("analytic.invoices.count.all".equals(event.getKey())) { - event.setValue("" + countAll); - event.setLabel(formatTotals(totalAllCurrency1, totalAllCurrency2)); - event.setDescription("Total invoices"); - event.getWorkitem().setItemValue("invoices.total", event.getValue()); - event.setLink(link); - } - - if ("analytic.invoices.count.open".equals(event.getKey())) { - event.setValue("" + countOpen); - event.setLabel(formatTotals(totalOpenCurrency1, totalOpenCurrency2)); - event.setDescription("Total invoices not yet in due"); - event.getWorkitem().setItemValue("invoices.total.open", event.getValue()); - event.setLink(link); - } - - if ("analytic.invoices.count.due".equals(event.getKey())) { - event.setValue("" + countDue); - event.setLabel(formatTotals(totalDueCurrency1, totalDueCurrency2)); - event.setDescription("Total invoices in 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(formatTotals(totalDunningCurrency1, totalDunningCurrency2)); - event.setDescription("Total invoices in dunning"); - event.getWorkitem().setItemValue("invoices.total.dunning", event.getValue()); - event.setLink(link); - } - - if ("analytic.payment.avg.due".equals(event.getKey())) { - event.setValue("" + averagePaymentDue); - event.setLabel("days"); - event.setDescription("Average terms of credit in the last 12 months."); - event.getWorkitem().setItemValue("payment.avg.due", event.getValue()); - event.setLink(link); - } - if ("analytic.payment.avg.days".equals(event.getKey())) { - event.setValue("" + averagePaymentDays); - event.setLabel("days"); - event.setDescription("Average duration for payment during the last 12 months."); - event.getWorkitem().setItemValue("payment.avg.days", event.getValue()); - event.setLink(link); - } - - if ("analytic.invoices.trend".equals(event.getKey())) { - event.setValue(chartData); - event.setLabel("Payment duration in days"); - event.setDescription("Payment practice by week"); - } - } - - /** - * Formatiert zwei währungen untereinander - * - * @param totalCurrency1 - * @param totalCurrency2 - * @return - */ - private String formatTotals(double totalCurrency1, double totalCurrency2) { - - List currencies = aglConfigController.getWorkitem().getItemValueList("currency.out", String.class); - if (currencies.size() < 2) { - currencies.add(currencies.get(0)); - } - String label = "" + formatCurrency(totalCurrency1) + " " + currencies.get(0) - + ""; - label = label + "
" + - formatCurrency(totalCurrency2) + " " + currencies.get(1) + ""; - - return label; - } - - private void loadRechnungen(AnalyticEvent event) { - if (getDbtNr().isEmpty()) { - invoices = new ArrayList(); - stats = null; - } else { - logger.info(" ├──load invoices for " + getDbtNr() + "...."); - String query = "(type:workitem) AND dbtr.number:" + getDbtNr() - + " AND $modelversion:rechnungsausgang-*"; - try { - logger.info(" ├──refresh invoice stats for " + getDbtNr() + "...."); - invoices = documentService.find(query, 999, 0, "$created", false); - calculateStats(event); - } catch (QueryException e) { - logger.warning("Failed to query invoices: " + query + " - Error: " + e.getMessage()); - invoices = new ArrayList(); - } - } - - } - - /** - * Läd die statistik daten zu einem debitor aus den aktuellen Rechnungen - */ - private void calculateStats(AnalyticEvent event) { - countAll = 0; - countOpen = 0; - countDue = 0; - countDunning = 0; - logger.info(" ├──calculate stats for " + getDbtNr() + "...."); - // do we have data? - if (invoices != null && invoices.size() > 0) { - countAll = invoices.size(); - - for (ItemCollection invoice : invoices) { - - // währung 1 oder 2? - if (invoice.getItemValueDouble("invoice.rate") == 0) { - totalAllCurrency1 = totalAllCurrency1 + invoice.getItemValueDouble("invoice.saldo"); - } else { - totalAllCurrency2 = totalAllCurrency2 + invoice.getItemValueDouble("invoice.saldo"); - } - - int task = invoice.getTaskID(); - - // Open - if (task >= 5000 && task <= 5099) { - countOpen++; - // währung 1 oder 2? - if (invoice.getItemValueDouble("invoice.rate") == 0) { - totalOpenCurrency1 = totalOpenCurrency1 + invoice.getItemValueDouble("invoice.saldo"); - } else { - totalOpenCurrency2 = totalOpenCurrency2 + invoice.getItemValueDouble("invoice.saldo"); - } - } - - // Due - if (task >= 5100 && task <= 5199) { - countDue++; - // währung 1 oder 2? - if (invoice.getItemValueDouble("invoice.rate") == 0) { - totalDueCurrency1 = totalDueCurrency1 + invoice.getItemValueDouble("invoice.saldo"); - } else { - totalDueCurrency2 = totalDueCurrency2 + invoice.getItemValueDouble("invoice.saldo"); - } - } - - // Dunning - if (task >= 5200) { - countDunning++; - // währung 1 oder 2? - if (invoice.getItemValueDouble("invoice.rate") == 0) { - totalDunningCurrency1 = totalDunningCurrency1 + invoice.getItemValueDouble("invoice.saldo"); - } else { - totalDunningCurrency2 = totalDunningCurrency2 + invoice.getItemValueDouble("invoice.saldo"); - } - } - } - - totalAllCurrency1 = InvoiceUtil.round(totalAllCurrency1); - totalAllCurrency2 = InvoiceUtil.round(totalAllCurrency2); - totalOpenCurrency1 = InvoiceUtil.round(totalOpenCurrency1); - totalOpenCurrency2 = InvoiceUtil.round(totalOpenCurrency2); - totalDueCurrency1 = InvoiceUtil.round(totalDueCurrency1); - totalDueCurrency2 = InvoiceUtil.round(totalDueCurrency2); - totalDunningCurrency1 = InvoiceUtil.round(totalDunningCurrency1); - totalDunningCurrency2 = InvoiceUtil.round(totalDunningCurrency2); - - } else { - // reset data because no invoices are available ... - resetStats(event); - - } - - } - - /** - * Setzt alle Statistikwerte zurück - */ - private void resetStats(AnalyticEvent event) { - logger.info(" ├──reset stats for " + getDbtNr() + "...."); - stats = null; - - totalAllCurrency1 = 0; - totalAllCurrency2 = 0; - totalOpenCurrency1 = 0; - totalOpenCurrency2 = 0; - totalDueCurrency1 = 0; - totalDueCurrency2 = 0; - totalDunningCurrency1 = 0; - totalDunningCurrency2 = 0; - - // chartData = null; - chartData = "{}"; - - averagePaymentDue = 0; - averagePaymentDays = 0; - - 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"); - - event.getWorkitem().removeItem("analytic.payment.avg.due"); - event.getWorkitem().removeItem("analytic.payment.avg.days"); - event.getWorkitem().removeItem("analytic.invoices.trend"); - - } - - private void getDurchschnittZahlungsziel() { - if (stats == null) { - averagePaymentDue = 0; - return; - } - int count = 0; - long gesamt = 0; - for (Map.Entry entry : stats.entrySet()) { - DebitorStatistikData statData = entry.getValue(); - if (statData.count > 0) { - gesamt = gesamt + statData.getAverageDueDays(); - count++; - } - } - averagePaymentDue = Math.round((double) gesamt / count); - } - - private void getDurchschnittZahlungsdauer() { - if (stats == null) { - averagePaymentDays = 0; - return; - } - int count = 0; - long gesamt = 0; - for (Map.Entry entry : stats.entrySet()) { - DebitorStatistikData statData = entry.getValue(); - if (statData.count > 0) { - gesamt = gesamt + statData.getAveragePaymentDays(); - count++; - } - } - averagePaymentDays = Math.round((double) gesamt / count); - } - - private String formatCurrency(Double value) { - - DecimalFormatSymbols symbols = new DecimalFormatSymbols(Locale.getDefault()); - symbols.setGroupingSeparator('.'); - symbols.setDecimalSeparator(','); - DecimalFormat formatter = new DecimalFormat("#,##0.00", symbols); - - return formatter.format(value); - } - - /** - * Hilfsmethode die das fuehrende K/D aus der Debitorennummer entfernt - * - * @return - */ - private String getDbtNr() { - String dbtNr = workflowController.getWorkitem().getItemValueString("dbtr.number"); - if (dbtNr.startsWith("D") || dbtNr.startsWith("K")) { - dbtNr = dbtNr.substring(1); - } - return dbtNr; - } - - /** - * Sucht alle Rechnugnen aus einem Zeitraum und sammelt Zahlungsziel und - * Zahlungszeitspanne gruppiert nach monaten - * - */ - public Map loadStats() { - - if (stats != null) { - return stats; - } - - logger.info(" ├──load stats for " + getDbtNr() + "...."); - // recompute - stats = new TreeMap(); - if (getDbtNr().isEmpty()) { - return stats; - } - - // Letzen 12 Monate - LocalDate endDate = LocalDate.now(); - LocalDate startDate = endDate.minusMonths(12); - DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd"); - String startDateStr = startDate.format(formatter); - String endDateStr = endDate.format(formatter); - - String query = "(type:workitemarchive) AND " - + " invoice.date:[" + startDateStr + " TO " + endDateStr + "] AND " - + "dbtr.number:" + getDbtNr() - + " AND $modelversion:rechnungsausgang-* AND ($taskid:[5900 TO 5999])"; - - logger.fine("query = " + query); - try { - List invoices = documentService - .findStubs(query, 9999, 0, "$created", false); - - for (ItemCollection invoice : invoices) { - try { - - logger.fine("Rechnung: " + invoice.getUniqueID()); - Date invoiceDate = invoice.getItemValueDate("invoice.date"); - Date invoiceDueDate = invoice.getItemValueDate("invoice.duedate"); - Date paymentDate = findPaymentDateByWorkitem(invoice); - if (paymentDate == null) { - logger.fine("Es wurde kein Zahlungseingang gefunden"); - continue; - } - - if (invoiceDate != null && paymentDate != null && invoiceDueDate != null) { - // do we have a stats object? - DebitorStatistikData statData = stats.get(getYearMonth(invoiceDate)); - if (statData == null) { - // create a new one - statData = new DebitorStatistikData(invoiceDate); - } - // jetzt irgendwas ausrechnen - statData.compute(invoiceDate, invoiceDueDate, paymentDate); - // .. und wieder speichern - stats.put(statData.toString(), statData); - - } else { - logger.warning("No payment Date found for invoice " + invoice.getUniqueID()); - } - - } catch (ParseException e) { - logger.warning("Unable to parse payment Date for invoice " + invoice.getUniqueID()); - } - } - - } catch (QueryException e) { - logger.severe("Failed to get statistic: " + e.getMessage()); - } - // Step 2: Sort the list based on the alphanumeric order of the keys - completeMissingMonths(); - - // berechne zahluntsziel durchscnitt - getDurchschnittZahlungsziel(); - getDurchschnittZahlungsdauer(); - return stats; - - } - - /** - * Finds the payment.date for a invoice. - * - * Wir suchen alle zugeordneten Zahlungseingägne und nehmen den letzten. - * - * WICHTIG: Es muss ggf. der index neu aufgebaut werden, da payment.date nun ein - * index feld ist - * - * @param invoice - * @return - * @throws ParseException - */ - public Date findPaymentDateByWorkitem(ItemCollection invoice) throws ParseException { - - // Wir selektieren alle Zahlungseingänge interessieren uns aber nur für den - // letzten - String sQuery = " (type:\"workitem\" OR type:\"workitemarchive\") " + // - " AND ($modelversion:zahlungseingang-*) AND ($workitemref:\"" - + invoice.getUniqueID() + "\" )"; - - List workitems = null; - - try { - workitems = documentService.findStubs(sQuery, 99, 0, - "payment.date", true); - if (workitems.size() > 0) { - return workitems.get(0).getItemValueDate("payment.date"); - } - } catch (QueryException e) { - - e.printStackTrace(); - } - - // no date found! - return null; - - } - - /** - * Diese Methode baut die Datenstruktur für das Chart Diagram zusammen - * - * - *
-		{
-		 labels: ["January", "February", "March", "April", "May", "June", "July"],
-		 datasets: [{
-			label: 'Dataset 1',
-			//backgroundColor: color(window.chartColors.red).alpha(0.5).rgbString(),
-			//borderColor: window.chartColors.red,
-			borderWidth: 1,
-			data: [
-				70, 70, 70, 70, 79, 50, 50
-			]
-		}, {
-			label: 'Dataset 2',
-			//backgroundColor: color(window.chartColors.blue).alpha(0.5).rgbString(),
-			//borderColor: window.chartColors.blue,
-			borderWidth: 1,
-			data: [
-				70, 70, 170, 7, 79, 50, 50
-			]
-		}]
-	   }
-	 * 
- * - * @return - */ - public String buildChartData() { - - loadStats(); - logger.info(" ├──build chart for " + getDbtNr() + "...."); - // build a list of all lables.... - List statusLabels = new ArrayList(); - - Set keys = stats.keySet(); - for (String _key : keys) { - statusLabels.add(_key); - } - - String result = "{\n \"type\": \"bar\",\n \"data\": { "; - - // Lables - result = result + "\"labels\" : [ "; - result = result + statusLabels.stream().collect(Collectors.joining("\",\"", "\"", "\"")); - result = result + "],"; - result = result + "\"datasets\": ["; - - // Datasets 1 - result = result + "{\"label\": \"Days for payment\",\"borderWidth\": 1,"; - result = result + " \"borderColor\": [\"#3B6B82\"],"; - result = result + " \"backgroundColor\" : [\"#CFE9F5\"], \"fill\": true,\"tension\": 0.5,"; - - result = result + "\"data\": ["; - for (Map.Entry entry : stats.entrySet()) { - result = result + entry.getValue().getAverageDueDays() + ","; - } - // cut last comma - if (result.endsWith(",")) { - result = result.substring(0, result.length() - 1); - } - result = result + "]"; - result = result + "}, "; - - // Datasets 2 - result = result + "{\"label\": \"Payment duration\",\"borderWidth\": 1,"; - result = result - + " \"borderColor\": [\"#E73B65\"],\"backgroundColor\" : [\"#70B088\" ], \"tension\": 0.5,\"fill\": true,"; - - result = result - + " \"trendlineLinear\": { \"colorMin\": \"red\", \"colorMax\": \"green\", \"lineStyle\": \"dotted\", \"width\": 2 , \"projection\": true },"; - - result = result + "\"data\": ["; - for (Map.Entry entry : stats.entrySet()) { - result = result + entry.getValue().getAveragePaymentDays() + ","; - } - // cut last comma - if (result.endsWith(",")) { - result = result.substring(0, result.length() - 1); - } - result = result + "]"; - result = result + "} "; - - // ende - result = result + "] }"; - - result = result + "}"; - - return result; - } - - /** - * Hilfsmethode ergänzt die fehlenden Monate - * - * @param yearMonths - * @return - */ - private void completeMissingMonths() { - if (stats == null || stats.size() < 3) { - return; - } - - // Convert the list of strings to a list of LocalDate objects - List dates = new ArrayList<>(); - Set yearMonths = this.stats.keySet(); - DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMM"); - for (String yearMonth : yearMonths) { - dates.add(YearMonth.parse(yearMonth, formatter).atDay(1)); - } - - // Find the lowest and highest dates - LocalDate lowestDate = Collections.min(dates); - LocalDate highestDate = Collections.max(dates); - - LocalDate current = lowestDate; - current = current.plusMonths(1); - while (!current.isAfter(highestDate)) { - - // existiert der monat? - String formattedDate = current.format(formatter); - DebitorStatistikData entry = stats.get(formattedDate); - if (entry == null) { - // add missing entry - stats.put(formattedDate, new DebitorStatistikData(current.getYear(), current.getMonthValue())); - } - current = current.plusMonths(1); - - } - - } - - /** - * returns - * - * 202304 from a given date - */ - public String getYearMonth(Date date) { - - // Create a Calendar instance and set the date - Calendar calendar = Calendar.getInstance(); - calendar.setTime(date); - - // Get the year from the Calendar object - int year = calendar.get(Calendar.YEAR); - // Get the month from the Calendar object - int month = calendar.get(Calendar.MONTH); - // Increment the month by 1 since Calendar months are zero-based - month++; - // Convert the month to a String with leading "0" if necessary - return "" + year + (month < 10 ? "0" + month : "" + month); - - } + private static final long serialVersionUID = 1L; + private static Logger logger = Logger.getLogger(AGLAnalyticControllerDebitor.class.getName()); + + @Inject + protected DocumentService documentService; + + @Inject + protected AGLConfigController aglConfigController; + + @Inject + protected WorkflowController workflowController; + + @Inject + TeamService teamService; + + TreeMap stats = null; + + List invoices = null; + int countAll = 0; + int countOpen = 0; + int countDue = 0; + int countDunning = 0; + + double totalAllCurrency1 = 0; + double totalAllCurrency2 = 0; + + double totalOpenCurrency1 = 0; + double totalOpenCurrency2 = 0; + + double totalDueCurrency1 = 0; + double totalDueCurrency2 = 0; + + double totalDunningCurrency1 = 0; + double totalDunningCurrency2 = 0; + + double averagePaymentDue = 0; + double averagePaymentDays = 0; + + String chartData = ""; + + public void onEvent(@Observes AnalyticEvent event) { + + if (!event.getWorkitem().getModelVersion().startsWith("businesspartner") + && !event.getWorkitem().getModelVersion().startsWith("analyse-debitor")) { + // no op + return; + } + + String dbtrNumber = event.getWorkitem().getItemValueString("dbtr.number"); + logger.fine("Analytic Event : " + dbtrNumber); + String dbtrNumberLast = event.getWorkitem().getItemValueString("dbtr.number.last"); + + // Recompute only if last dbtr.number has changed or no values yet computed + + // load all invoices? + if (invoices == null || (!dbtrNumber.isEmpty() && !dbtrNumber.equals(dbtrNumberLast))) { + resetStats(event); + event.getWorkitem().setItemValue("dbtr.number.last", dbtrNumber); + logger.info("Analyse new debitor data for : " + dbtrNumber); + loadRechnungen(event); + chartData = buildChartData(); + } + + // use cache? + if (event.getWorkitem().hasItem(event.getKey())) { + // logger.info(" use cache for " + event.getKey()); + // no op + return; + } + + String link = "/pages/workitems/worklist.xhtml" + "?phrase=" + getDbtNr(); + 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() + "&phrase=" + getDbtNr(); + } + if ("analytic.invoices.count.all".equals(event.getKey())) { + event.setValue("" + countAll); + event.setLabel(formatTotals(totalAllCurrency1, totalAllCurrency2)); + event.setDescription("Total invoices"); + event.getWorkitem().setItemValue("invoices.total", event.getValue()); + event.setLink(link); + } + + if ("analytic.invoices.count.open".equals(event.getKey())) { + event.setValue("" + countOpen); + event.setLabel(formatTotals(totalOpenCurrency1, totalOpenCurrency2)); + event.setDescription("Total invoices not yet in due"); + event.getWorkitem().setItemValue("invoices.total.open", event.getValue()); + event.setLink(link); + } + + if ("analytic.invoices.count.due".equals(event.getKey())) { + event.setValue("" + countDue); + event.setLabel(formatTotals(totalDueCurrency1, totalDueCurrency2)); + event.setDescription("Total invoices in 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(formatTotals(totalDunningCurrency1, totalDunningCurrency2)); + event.setDescription("Total invoices in dunning"); + event.getWorkitem().setItemValue("invoices.total.dunning", event.getValue()); + event.setLink(link); + } + + if ("analytic.payment.avg.due".equals(event.getKey())) { + event.setValue("" + averagePaymentDue); + event.setLabel("days"); + event.setDescription("Average terms of credit in the last 12 months."); + event.getWorkitem().setItemValue("payment.avg.due", event.getValue()); + event.setLink(link); + } + if ("analytic.payment.avg.days".equals(event.getKey())) { + event.setValue("" + averagePaymentDays); + event.setLabel("days"); + event.setDescription("Average duration for payment during the last 12 months."); + event.getWorkitem().setItemValue("payment.avg.days", event.getValue()); + event.setLink(link); + } + + if ("analytic.invoices.trend".equals(event.getKey())) { + event.setValue(chartData); + event.setLabel("Payment duration in days"); + event.setDescription("Payment practice by week"); + } + } + + /** + * Formatiert zwei währungen untereinander + * + * @param totalCurrency1 + * @param totalCurrency2 + * @return + */ + private String formatTotals(double totalCurrency1, double totalCurrency2) { + + List currencies = aglConfigController.getWorkitem().getItemValueList("currency.out", String.class); + if (currencies.size() < 2) { + currencies.add(currencies.get(0)); + } + String label = "" + formatCurrency(totalCurrency1) + " " + currencies.get(0) + + ""; + label = label + "
" + formatCurrency(totalCurrency2) + " " + currencies.get(1) + + ""; + + return label; + } + + private void loadRechnungen(AnalyticEvent event) { + if (getDbtNr().isEmpty()) { + invoices = new ArrayList(); + stats = null; + } else { + logger.info(" ├──load invoices for " + getDbtNr() + "...."); + String query = "(type:workitem) AND dbtr.number:" + getDbtNr() + " AND $modelversion:rechnungsausgang-*"; + try { + logger.info(" ├──refresh invoice stats for " + getDbtNr() + "...."); + invoices = documentService.find(query, 999, 0, "$created", false); + calculateStats(event); + } catch (QueryException e) { + logger.warning("Failed to query invoices: " + query + " - Error: " + e.getMessage()); + invoices = new ArrayList(); + } + } + + } + + /** + * Läd die statistik daten zu einem debitor aus den aktuellen Rechnungen + */ + private void calculateStats(AnalyticEvent event) { + countAll = 0; + countOpen = 0; + countDue = 0; + countDunning = 0; + logger.info(" ├──calculate stats for " + getDbtNr() + "...."); + // do we have data? + if (invoices != null && invoices.size() > 0) { + countAll = invoices.size(); + + for (ItemCollection invoice : invoices) { + + // währung 1 oder 2? + if (invoice.getItemValueDouble("invoice.rate") == 0) { + totalAllCurrency1 = totalAllCurrency1 + invoice.getItemValueDouble("invoice.saldo"); + } else { + totalAllCurrency2 = totalAllCurrency2 + invoice.getItemValueDouble("invoice.saldo"); + } + + int task = invoice.getTaskID(); + + // Open + if (task >= 5000 && task <= 5099) { + countOpen++; + // währung 1 oder 2? + if (invoice.getItemValueDouble("invoice.rate") == 0) { + totalOpenCurrency1 = totalOpenCurrency1 + invoice.getItemValueDouble("invoice.saldo"); + } else { + totalOpenCurrency2 = totalOpenCurrency2 + invoice.getItemValueDouble("invoice.saldo"); + } + } + + // Due + if (task >= 5100 && task <= 5199) { + countDue++; + // währung 1 oder 2? + if (invoice.getItemValueDouble("invoice.rate") == 0) { + totalDueCurrency1 = totalDueCurrency1 + invoice.getItemValueDouble("invoice.saldo"); + } else { + totalDueCurrency2 = totalDueCurrency2 + invoice.getItemValueDouble("invoice.saldo"); + } + } + + // Dunning + if (task >= 5200) { + countDunning++; + // währung 1 oder 2? + if (invoice.getItemValueDouble("invoice.rate") == 0) { + totalDunningCurrency1 = totalDunningCurrency1 + invoice.getItemValueDouble("invoice.saldo"); + } else { + totalDunningCurrency2 = totalDunningCurrency2 + invoice.getItemValueDouble("invoice.saldo"); + } + } + } + + totalAllCurrency1 = InvoiceUtil.round(totalAllCurrency1); + totalAllCurrency2 = InvoiceUtil.round(totalAllCurrency2); + totalOpenCurrency1 = InvoiceUtil.round(totalOpenCurrency1); + totalOpenCurrency2 = InvoiceUtil.round(totalOpenCurrency2); + totalDueCurrency1 = InvoiceUtil.round(totalDueCurrency1); + totalDueCurrency2 = InvoiceUtil.round(totalDueCurrency2); + totalDunningCurrency1 = InvoiceUtil.round(totalDunningCurrency1); + totalDunningCurrency2 = InvoiceUtil.round(totalDunningCurrency2); + + } else { + // reset data because no invoices are available ... + resetStats(event); + + } + + } + + /** + * Setzt alle Statistikwerte zurück + */ + private void resetStats(AnalyticEvent event) { + logger.info(" ├──reset stats for " + getDbtNr() + "...."); + stats = null; + + totalAllCurrency1 = 0; + totalAllCurrency2 = 0; + totalOpenCurrency1 = 0; + totalOpenCurrency2 = 0; + totalDueCurrency1 = 0; + totalDueCurrency2 = 0; + totalDunningCurrency1 = 0; + totalDunningCurrency2 = 0; + + // chartData = null; + chartData = "{}"; + + averagePaymentDue = 0; + averagePaymentDays = 0; + + 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"); + + event.getWorkitem().removeItem("analytic.payment.avg.due"); + event.getWorkitem().removeItem("analytic.payment.avg.days"); + event.getWorkitem().removeItem("analytic.invoices.trend"); + + } + + private void getDurchschnittZahlungsziel() { + if (stats == null) { + averagePaymentDue = 0; + return; + } + int count = 0; + long gesamt = 0; + for (Map.Entry entry : stats.entrySet()) { + DebitorStatistikData statData = entry.getValue(); + if (statData.count > 0) { + gesamt = gesamt + statData.getAverageDueDays(); + count++; + } + } + averagePaymentDue = Math.round((double) gesamt / count); + } + + private void getDurchschnittZahlungsdauer() { + if (stats == null) { + averagePaymentDays = 0; + return; + } + int count = 0; + long gesamt = 0; + for (Map.Entry entry : stats.entrySet()) { + DebitorStatistikData statData = entry.getValue(); + if (statData.count > 0) { + gesamt = gesamt + statData.getAveragePaymentDays(); + count++; + } + } + averagePaymentDays = Math.round((double) gesamt / count); + } + + private String formatCurrency(Double value) { + + DecimalFormatSymbols symbols = new DecimalFormatSymbols(Locale.getDefault()); + symbols.setGroupingSeparator('.'); + symbols.setDecimalSeparator(','); + DecimalFormat formatter = new DecimalFormat("#,##0.00", symbols); + + return formatter.format(value); + } + + /** + * Hilfsmethode die das fuehrende K/D aus der Debitorennummer entfernt + * + * @return + */ + private String getDbtNr() { + String dbtNr = workflowController.getWorkitem().getItemValueString("dbtr.number"); + if (dbtNr.startsWith("D") || dbtNr.startsWith("K")) { + dbtNr = dbtNr.substring(1); + } + return dbtNr; + } + + /** + * Sucht alle Rechnugnen aus einem Zeitraum und sammelt Zahlungsziel und + * Zahlungszeitspanne gruppiert nach monaten + * + */ + public Map loadStats() { + + if (stats != null) { + return stats; + } + + logger.info(" ├──load stats for " + getDbtNr() + "...."); + // recompute + stats = new TreeMap(); + if (getDbtNr().isEmpty()) { + return stats; + } + + // Letzen 12 Monate + LocalDate endDate = LocalDate.now(); + LocalDate startDate = endDate.minusMonths(12); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd"); + String startDateStr = startDate.format(formatter); + String endDateStr = endDate.format(formatter); + + String query = "(type:workitemarchive) AND " + " invoice.date:[" + startDateStr + " TO " + endDateStr + "] AND " + + "dbtr.number:" + getDbtNr() + " AND $modelversion:rechnungsausgang-* AND ($taskid:[5900 TO 5999])"; + + logger.fine("query = " + query); + try { + List invoices = documentService.findStubs(query, 9999, 0, "$created", false); + + for (ItemCollection invoice : invoices) { + try { + + logger.fine("Rechnung: " + invoice.getUniqueID()); + Date invoiceDate = invoice.getItemValueDate("invoice.date"); + Date invoiceDueDate = invoice.getItemValueDate("invoice.duedate"); + Date paymentDate = findPaymentDateByWorkitem(invoice); + if (paymentDate == null) { + logger.fine("Es wurde kein Zahlungseingang gefunden"); + continue; + } + + if (invoiceDate != null && paymentDate != null && invoiceDueDate != null) { + // do we have a stats object? + DebitorStatistikData statData = stats.get(getYearMonth(invoiceDate)); + if (statData == null) { + // create a new one + statData = new DebitorStatistikData(invoiceDate); + } + // jetzt irgendwas ausrechnen + statData.compute(invoiceDate, invoiceDueDate, paymentDate); + // .. und wieder speichern + stats.put(statData.toString(), statData); + + } else { + logger.warning("No payment Date found for invoice " + invoice.getUniqueID()); + } + + } catch (ParseException e) { + logger.warning("Unable to parse payment Date for invoice " + invoice.getUniqueID()); + } + } + + } catch (QueryException e) { + logger.severe("Failed to get statistic: " + e.getMessage()); + } + // Step 2: Sort the list based on the alphanumeric order of the keys + completeMissingMonths(); + + // berechne zahluntsziel durchscnitt + getDurchschnittZahlungsziel(); + getDurchschnittZahlungsdauer(); + return stats; + + } + + /** + * Finds the payment.date for a invoice. + * + * Wir suchen alle zugeordneten Zahlungseingägne und nehmen den letzten. + * + * WICHTIG: Es muss ggf. der index neu aufgebaut werden, da payment.date nun ein + * index feld ist + * + * @param invoice + * @return + * @throws ParseException + */ + public Date findPaymentDateByWorkitem(ItemCollection invoice) throws ParseException { + + // Wir selektieren alle Zahlungseingänge interessieren uns aber nur für den + // letzten + String sQuery = " (type:\"workitem\" OR type:\"workitemarchive\") " + // + " AND ($modelversion:zahlungseingang-*) AND ($workitemref:\"" + invoice.getUniqueID() + "\" )"; + + List workitems = null; + + try { + workitems = documentService.findStubs(sQuery, 99, 0, "payment.date", true); + if (workitems.size() > 0) { + return workitems.get(0).getItemValueDate("payment.date"); + } + } catch (QueryException e) { + + e.printStackTrace(); + } + + // no date found! + return null; + + } + + /** + * Diese Methode baut die Datenstruktur für das Chart Diagram zusammen + * + * + *
+    	{
+    	 labels: ["January", "February", "March", "April", "May", "June", "July"],
+    	 datasets: [{
+    		label: 'Dataset 1',
+    		//backgroundColor: color(window.chartColors.red).alpha(0.5).rgbString(),
+    		//borderColor: window.chartColors.red,
+    		borderWidth: 1,
+    		data: [
+    			70, 70, 70, 70, 79, 50, 50
+    		]
+    	}, {
+    		label: 'Dataset 2',
+    		//backgroundColor: color(window.chartColors.blue).alpha(0.5).rgbString(),
+    		//borderColor: window.chartColors.blue,
+    		borderWidth: 1,
+    		data: [
+    			70, 70, 170, 7, 79, 50, 50
+    		]
+    	}]
+       }
+     * 
+ * + * @return + */ + public String buildChartData() { + + loadStats(); + logger.info(" ├──build chart for " + getDbtNr() + "...."); + // build a list of all lables.... + List statusLabels = new ArrayList(); + + Set keys = stats.keySet(); + for (String _key : keys) { + statusLabels.add(_key); + } + + String result = "{\n \"type\": \"bar\",\n \"data\": { "; + + // Lables + result = result + "\"labels\" : [ "; + result = result + statusLabels.stream().collect(Collectors.joining("\",\"", "\"", "\"")); + result = result + "],"; + result = result + "\"datasets\": ["; + + // Datasets 1 + result = result + "{\"label\": \"Days for payment\",\"borderWidth\": 1,"; + result = result + " \"borderColor\": [\"#3B6B82\"],"; + result = result + " \"backgroundColor\" : [\"#CFE9F5\"], \"fill\": true,\"tension\": 0.5,"; + + result = result + "\"data\": ["; + for (Map.Entry entry : stats.entrySet()) { + result = result + entry.getValue().getAverageDueDays() + ","; + } + // cut last comma + if (result.endsWith(",")) { + result = result.substring(0, result.length() - 1); + } + result = result + "]"; + result = result + "}, "; + + // Datasets 2 + result = result + "{\"label\": \"Payment duration\",\"borderWidth\": 1,"; + result = result + + " \"borderColor\": [\"#E73B65\"],\"backgroundColor\" : [\"#70B088\" ], \"tension\": 0.5,\"fill\": true,"; + + result = result + + " \"trendlineLinear\": { \"colorMin\": \"red\", \"colorMax\": \"green\", \"lineStyle\": \"dotted\", \"width\": 2 , \"projection\": true },"; + + result = result + "\"data\": ["; + for (Map.Entry entry : stats.entrySet()) { + result = result + entry.getValue().getAveragePaymentDays() + ","; + } + // cut last comma + if (result.endsWith(",")) { + result = result.substring(0, result.length() - 1); + } + result = result + "]"; + result = result + "} "; + + // ende + result = result + "] }"; + + result = result + "}"; + + return result; + } + + /** + * Hilfsmethode ergänzt die fehlenden Monate + * + * @param yearMonths + * @return + */ + private void completeMissingMonths() { + if (stats == null || stats.size() < 3) { + return; + } + + // Convert the list of strings to a list of LocalDate objects + List dates = new ArrayList<>(); + Set yearMonths = this.stats.keySet(); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMM"); + for (String yearMonth : yearMonths) { + dates.add(YearMonth.parse(yearMonth, formatter).atDay(1)); + } + + // Find the lowest and highest dates + LocalDate lowestDate = Collections.min(dates); + LocalDate highestDate = Collections.max(dates); + + LocalDate current = lowestDate; + current = current.plusMonths(1); + while (!current.isAfter(highestDate)) { + + // existiert der monat? + String formattedDate = current.format(formatter); + DebitorStatistikData entry = stats.get(formattedDate); + if (entry == null) { + // add missing entry + stats.put(formattedDate, new DebitorStatistikData(current.getYear(), current.getMonthValue())); + } + current = current.plusMonths(1); + + } + + } + + /** + * returns + * + * 202304 from a given date + */ + public String getYearMonth(Date date) { + + // Create a Calendar instance and set the date + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + + // Get the year from the Calendar object + int year = calendar.get(Calendar.YEAR); + // Get the month from the Calendar object + int month = calendar.get(Calendar.MONTH); + // Increment the month by 1 since Calendar months are zero-based + month++; + // Convert the month to a String with leading "0" if necessary + return "" + year + (month < 10 ? "0" + month : "" + month); + + } } diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/AGLAnalyticControllerOPListe.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/AGLAnalyticControllerOPListe.java index d34c954..f918881 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/AGLAnalyticControllerOPListe.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/AGLAnalyticControllerOPListe.java @@ -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 stats = null; + TreeMap 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); + } } diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/AGLAnalyticControllerSteuer.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/AGLAnalyticControllerSteuer.java index 59d09ac..2f3682a 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/AGLAnalyticControllerSteuer.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/AGLAnalyticControllerSteuer.java @@ -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 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 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 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 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 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 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 positionsTabelle = InvoiceUtil.explodeChildList(steuerItemCol); - for (ItemCollection pos : positionsTabelle) { - if (type.equalsIgnoreCase(pos.getItemValueString("activity.type"))) { - result = result + pos.getItemValueDouble("amount"); - } - } - return result; - } - - private List 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 - * - *
-	 * 
-	   chartCfg = {
-		  type: 'bar',
-		  data: {
-			datasets: [{
-			  data: [20, 10],
-			}],
-			labels: ['1', '2']
-		  }
-	    }
-	 * 
-	 * 
- * - * @return - */ - private String accumulateSteuerbescheideByWeek() { - logger.fine("accumulate steuer by week..."); - - List list = getSteuerbescheide(); - Map 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 - * - *
-	 * 
-	   chartCfg = {
-		  type: 'bar',
-		  data: {
-			datasets: [{
-			  data: [20, 10],
-			}],
-			labels: ['1', '2']
-		  }
-	    }
-	 * 
-	 * 
- * - * @return - */ - private String accumulateSteuerbescheideAbteilungByWeek() { - logger.fine("accumulate steuer by Abteilung week..."); - - String[] colors = { - "#36a2eb", - "#ff6384", - "#4bc0c0", - "#ff9f40", - "#96f", - "#ffcd56", - "#c9cbcf" - }; - - List list = getSteuerbescheide(); - Map totalByWeek = new HashMap<>(); - Calendar calendar = Calendar.getInstance(); - int earliestWeek = Integer.MAX_VALUE; - int latestWeek = Integer.MIN_VALUE; - - List 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 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 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 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 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 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 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 positionsTabelle = InvoiceUtil.explodeChildList(steuerItemCol); + for (ItemCollection pos : positionsTabelle) { + if (type.equalsIgnoreCase(pos.getItemValueString("activity.type"))) { + result = result + pos.getItemValueDouble("amount"); + } + } + return result; + } + + private List 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 + * + *
+     *
+       chartCfg = {
+    	  type: 'bar',
+    	  data: {
+    		datasets: [{
+    		  data: [20, 10],
+    		}],
+    		labels: ['1', '2']
+    	  }
+        }
+     *
+     * 
+ * + * @return + */ + private String accumulateSteuerbescheideByWeek() { + logger.fine("accumulate steuer by week..."); + + List list = getSteuerbescheide(); + Map 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 + * + *
+     *
+       chartCfg = {
+    	  type: 'bar',
+    	  data: {
+    		datasets: [{
+    		  data: [20, 10],
+    		}],
+    		labels: ['1', '2']
+    	  }
+        }
+     *
+     * 
+ * + * @return + */ + private String accumulateSteuerbescheideAbteilungByWeek() { + logger.fine("accumulate steuer by Abteilung week..."); + + String[] colors = { "#36a2eb", "#ff6384", "#4bc0c0", "#ff9f40", "#96f", "#ffcd56", "#c9cbcf" }; + + List list = getSteuerbescheide(); + Map totalByWeek = new HashMap<>(); + Calendar calendar = Calendar.getInstance(); + int earliestWeek = Integer.MAX_VALUE; + int latestWeek = Integer.MIN_VALUE; + + List 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; + } + + } } diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/AGLAnalyticExcelAdapter.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/AGLAnalyticExcelAdapter.java index 8af8716..0c6cb79 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/AGLAnalyticExcelAdapter.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/AGLAnalyticExcelAdapter.java @@ -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 - * + * *
  * {@code
         
             textblock-ref
             
             filename
-         
+        
    }
  * 
- * + * *

* Der Adapter erweitert den POIAdapter somit können Felder aktualisiert werden * (siehe POIFineReplaceAdapter). - * + * *

* Der Adapter kopiert die Steuerbescheiddaten in neue Zeilen, welche ab - * Zeilennummer - * 16 eingefügt werden. + * Zeilennummer 16 eingefügt werden. *

- * + * * 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 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 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 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 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 - *

- * The method copies the Row A16 as a reference row - *

- * 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 excelDefList = steuerbescheideConfig.getItemValue("excel-export"); + ItemCollection excelDefCollection = XMLParser.parseItemStructure(excelDefList.get(0)); - // load dummy rechnungen - List 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 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 + *

+ * The method copies the Row A16 as a reference row + *

+ * 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 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 loadSteuerbescheide() { - List 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 loadSteuerbescheide() { + List result = new ArrayList<>(); + try { + result = documentService.find("$modelversion:steuerbescheid-* AND type:workitem", 9999, 0, "invoice.number", + false); + } catch (QueryException e) { + e.printStackTrace(); } + return result; + } } \ No newline at end of file diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/AGLAnalyticExcelAdapterDebitor.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/AGLAnalyticExcelAdapterDebitor.java index 2cd4b18..a761ca0 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/AGLAnalyticExcelAdapterDebitor.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/AGLAnalyticExcelAdapterDebitor.java @@ -35,30 +35,29 @@ import jakarta.inject.Inject; * Textblock und fügt die offenen Recnungen ein * * Der Adapter wird im Modell wie folgt konfiguriert - * + * *

  * {@code
         
             textblock-ref
             
             filename
-         
+        
    }
  * 
- * + * *

* Der Adapter erweitert den POIAdapter somit können Felder aktualisiert werden * (siehe POIFineReplaceAdapter). - * + * *

* Der Adapter kopiert die ausgangsrechnungen in neue Zeilen, welche ab - * Zeilennummer - * 16 eingefügt werden. + * Zeilennummer 16 eingefügt werden. *

- * + * * 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 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 *

* The method copies the Row A16 as a reference row *

* 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 loadInvoices(String dbtrNumber) { List 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(); } diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/AGLConfigController.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/AGLConfigController.java index 09cbac6..a2212bd 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/AGLConfigController.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/AGLConfigController.java @@ -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"); + } + +} diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/BusinessPartnerController.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/BusinessPartnerController.java index e22ec66..749cc60 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/BusinessPartnerController.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/BusinessPartnerController.java @@ -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 ibanList = null; - protected List invoicesOut = null; - protected List invoicesIn = null; - protected List dunnings = null; - - @Inject - protected WorkflowController workflowController; - - @Inject - DocumentService documentService; - - @Inject - BusinessPartnerService businessPartnerService; - - private List searchResult = null; - - private static Logger logger = Logger.getLogger(BusinessPartnerController.class.getName()); - - public List getInvoicesOut() { - if (invoicesOut == null) { - invoicesOut = new ArrayList<>(); - } - return invoicesOut; - } - - public List getInvoicesIn() { - if (invoicesIn == null) { - invoicesIn = new ArrayList<>(); - } - return invoicesIn; - } - - public List getDunnings() { - if (dunnings == null) { - dunnings = new ArrayList<>(); - } - return dunnings; - } - - /** - * This method searches a text phrase within the list of DATEV kreditoren - *

- * JSF Integration: - * - * {@code - - * } - */ - public void search(String options) { - List resultList = null; - searchResult = new ArrayList(); - // 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 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 getIbanList() { - return ibanList; - } - - public void addIBAN() { - if (ibanList == null) { - ibanList = new ArrayList(); - } - 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. - *

- * 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 ibanList = null; + protected List invoicesOut = null; + protected List invoicesIn = null; + protected List dunnings = null; + + @Inject + protected WorkflowController workflowController; + + @Inject + DocumentService documentService; + + @Inject + BusinessPartnerService businessPartnerService; + + private List searchResult = null; + + private static Logger logger = Logger.getLogger(BusinessPartnerController.class.getName()); + + public List getInvoicesOut() { + if (invoicesOut == null) { + invoicesOut = new ArrayList<>(); + } + return invoicesOut; + } + + public List getInvoicesIn() { + if (invoicesIn == null) { + invoicesIn = new ArrayList<>(); + } + return invoicesIn; + } + + public List getDunnings() { + if (dunnings == null) { + dunnings = new ArrayList<>(); + } + return dunnings; + } + + /** + * This method searches a text phrase within the list of DATEV kreditoren + *

+ * JSF Integration: + * + * {@code + } + */ + public void search(String options) { + List resultList = null; + searchResult = new ArrayList(); + // 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 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 getIbanList() { + return ibanList; + } + + public void addIBAN() { + if (ibanList == null) { + ibanList = new ArrayList(); + } + 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. + *

+ * 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; + } + +} diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/BusinessPartnerSearchEntry.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/BusinessPartnerSearchEntry.java index dcd7234..227c42b 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/BusinessPartnerSearchEntry.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/BusinessPartnerSearchEntry.java @@ -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; + } } diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/BusinessPartnerService.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/BusinessPartnerService.java index dc77c7d..b5bd30e 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/BusinessPartnerService.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/BusinessPartnerService.java @@ -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 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 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 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 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; } diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/CargosoftExportAdapter.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/CargosoftExportAdapter.java index eca0fdc..2db1e2e 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/CargosoftExportAdapter.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/CargosoftExportAdapter.java @@ -25,18 +25,18 @@ import jakarta.inject.Inject; *

* report = report definition to transfers the workitem into the cargosoft xml * structure - * + * *

  * {@code
         cargosoft
-       
+
    }
  * 
*

* 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 */ diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/CargosoftSplitAdapter.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/CargosoftSplitAdapter.java index 6ddc396..51eb38f 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/CargosoftSplitAdapter.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/CargosoftSplitAdapter.java @@ -32,7 +32,7 @@ import jakarta.inject.Inject; * ein neue Sequencenummer die im Hautpworkitem gespeichert wird. *

* Der Adapter wird im Modell wie folgt konfiguriert - * + * *

  * {@code
         
@@ -41,14 +41,14 @@ import jakarta.inject.Inject;
             100
             (?!txtworkflowhistory)(^[a-zA-Z]|^_)
         
-       
+
    }
  * 
*

* 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 { *

* Example: {@code * txttitle,txtfirstname - * + * * txttitle|newitem1,txtfirstname|newitem2 - * + * * } - * + * *

* Optional also reg expressions are supported. In this case mapping of the item * name is not supported. *

* 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... diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/CustomSearchController.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/CustomSearchController.java index c8eafa0..757b127 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/CustomSearchController.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/CustomSearchController.java @@ -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; + } +} diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/DebitorStatistikController.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/DebitorStatistikController.java index 1275762..a30698a 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/DebitorStatistikController.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/DebitorStatistikController.java @@ -33,7 +33,7 @@ import jakarta.inject.Named; /** * Der InvoiceAnalyseController dient zur Auswertung der Sachprüfungs WOrklfow * Schritte. Die Analyse Seite ist büer das Admin Menü erreichbar. - * + * * @author rsoika * */ @@ -42,542 +42,533 @@ import jakarta.inject.Named; // @SessionScoped public class DebitorStatistikController implements Serializable { - private static final long serialVersionUID = 1L; - private static Logger logger = Logger.getLogger(DebitorStatistikController.class.getName()); - - @Inject - protected DocumentService documentService; - - TreeMap stats = null; - - @Inject - protected WorkflowController workflowController; - - @Inject - protected SearchController searchController; - - @Inject - protected BusinessPartnerService businessPartnerService; - - /** - * Initialize default behavior initialize an opitonal id from the query string - * - */ - @PostConstruct - public void init() { - // extract the processref and page from the query string - FacesContext fc = FacesContext.getCurrentInstance(); - Map paramMap = fc.getExternalContext().getRequestParameterMap(); - - String id = paramMap.get("dbtr.number"); - if (id != null && !id.isEmpty()) { - workflowController.getWorkitem().setItemValue("dbtr.number", id); - - ItemCollection dbtr = businessPartnerService.getBusinessPartnerByID(InvoiceUtil.buildBPID(id)); - // ItemCollection dbtr = kreditorDebitorService.findDebitor(id); - if (dbtr != null) { - workflowController.getWorkitem().setItemValue("dbtr.name", dbtr.getItemValueString("partner.name")); - } - - } - } - - public ItemCollection getWorkitem() { - return workflowController.getWorkitem(); - } - - public int getZahlungsmoral() { - return 0; - } - - /** - * Gibt eine bookmarkable url zurück. - * Wir vom Aktualisieren button in der Page genutzt - * - * @return - */ - public String getBookmark() { - return "/pages/dbtr-stats.xhtml?dbtr.number=" + getDbtNr() + "&faces-redirect=true"; - } - - /* - * Gesamt - */ - public String getQueryRechnungen() { - String query = "(type:workitem OR type:workitemarchive) AND dbtr.number:" + getDbtNr() - + " AND $workflowgroup:\"Rechnungsausgang\""; - return "/pages/workitems/worklist?title=Rechnungen " + getDbtNr() + " gesamt&query=" + query; - } - - public int getRechnungen() { - int result = 0; - if (getDbtNr().isEmpty()) { - return 0; - } - try { - result = documentService - .count("(type:workitem OR type:workitemarchive) AND dbtr.number:" + getDbtNr() - + " AND $workflowgroup:\"Rechnungsausgang\""); - } catch (QueryException e) { - logger.severe("Failed to get statistic: " + e.getMessage()); - } - return result; - } - - /* - * Bezahlt - */ - public String getQueryRechnungenBezahlt() { - String query = "(type:workitem OR type:workitemarchive) AND dbtr.number:" + getDbtNr() - + " AND $workflowgroup:\"Rechnungsausgang\" AND ($taskid:[5900 TO 5999])"; - return "/pages/workitems/worklist?title=Rechnungen " + getDbtNr() + " bezahlt&query=" + query; - } - - public int getRechnungenBezahlt() { - int result = 0; - if (getDbtNr().isEmpty()) { - return 0; - } - try { - result = documentService - .count("(type:workitem OR type:workitemarchive) AND dbtr.number:" + getDbtNr() - + " AND $workflowgroup:\"Rechnungsausgang\" AND ($taskid:[5900 TO 5999])"); - } catch (QueryException e) { - logger.severe("Failed to get statistic: " + e.getMessage()); - } - return result; - } - - /* - * Offen - */ - public String getQueryRechnungenOffen() { - String query = "(type:workitem) AND dbtr.number:" + getDbtNr() - + " AND $workflowgroup:\"Rechnungsausgang\" AND ($taskid:[5000 TO 5099])"; - return "/pages/workitems/worklist?title=Rechnungen " + getDbtNr() + " offen&query=" + query; - } - - public int getRechnungenOffen() { - int result = 0; - if (getDbtNr().isEmpty()) { - return 0; - } - try { - result = documentService - .count("(type:workitem) AND dbtr.number:" + getDbtNr() - + " AND $workflowgroup:\"Rechnungsausgang\" AND ($taskid:[5000 TO 5099])"); - } catch (QueryException e) { - logger.severe("Failed to get statistic: " + e.getMessage()); - } - return result; - } - - /* - * Fällig - */ - public String getQueryRechnungenFaellig() { - String query = "(type:workitem) AND dbtr.number:" + getDbtNr() - + " AND $workflowgroup:\"Rechnungsausgang\" AND ($taskid:[5100 TO 5199])"; - return "/pages/workitems/worklist?title=Rechnungen " + getDbtNr() + " fällig&query=" + query; - } - - public int getRechnungenFaellig() { - int result = 0; - if (getDbtNr().isEmpty()) { - return 0; - } - try { - result = documentService - .count("(type:workitem) AND dbtr.number:" + getDbtNr() - + " AND $workflowgroup:\"Rechnungsausgang\" AND ($taskid:[5100 TO 5199])"); - } catch (QueryException e) { - logger.severe("Failed to get statistic: " + e.getMessage()); - } - return result; - } - - /* - * In Mahnung - */ - public String getQueryRechnungenGemahnt() { - String query = "(type:workitem) AND dbtr.number:" + getDbtNr() - + " AND $workflowgroup:\"Rechnungsausgang\" AND ($taskid:[5200 TO 5299])"; - return "/pages/workitems/worklist?title=Rechnungen " + getDbtNr() + " in Mahnung&query=" + query; - } - - public int getRechnungenGemahnt() { - int result = 0; - if (getDbtNr().isEmpty()) { - return 0; - } - try { - result = documentService - .count("(type:workitem ) AND dbtr.number:" + getDbtNr() - + " AND $workflowgroup:\"Rechnungsausgang\" AND ($taskid:[5200 TO 5299])"); - } catch (QueryException e) { - logger.severe("Failed to get statistic: " + e.getMessage()); - } - return result; - } - - /* - * Inkasso - */ - public String getQueryRechnungenInkasso() { - String query = "(type:workitem) AND dbtr.number:" + getDbtNr() - + " AND $workflowgroup:\"Rechnungsausgang\" AND ($taskid:[5300 TO 5399])"; - return "/pages/workitems/worklist?title=Rechnungen " + getDbtNr() + " in Inkasso&query=" + query; - } - - public int getRechnungenInkasso() { - int result = 0; - if (getDbtNr().isEmpty()) { - return 0; - } - try { - result = documentService - .count("(type:workitem OR type:workitemarchive) AND dbtr.number:" + getDbtNr() - + " AND $workflowgroup:\"Rechnungsausgang\" AND ($taskid:[5300 TO 5399])"); - } catch (QueryException e) { - logger.severe("Failed to get statistic: " + e.getMessage()); - } - return result; - } - - /** - * Hilfsmethode die das fuehrende K/D aus der Debitorennummer entfernt - * - * @return - */ - public String getDbtNr() { - String dbtNr = getWorkitem().getItemValueString("dbtr.number"); - if (dbtNr.startsWith("D") || dbtNr.startsWith("K")) { - dbtNr = dbtNr.substring(1); - } - return dbtNr; - } - - public long getDurchschnittZahlungsziel() { - getStats(); - int count = 0; - long gesamt = 0; - for (Map.Entry entry : stats.entrySet()) { - DebitorStatistikData statData = entry.getValue(); - if (statData.count > 0) { - gesamt = gesamt + statData.getAverageDueDays(); - count++; - } - } - return Math.round((double) gesamt / count); - } - - public long getDurchschnittZahlungsdauer() { - getStats(); - int count = 0; - long gesamt = 0; - for (Map.Entry entry : stats.entrySet()) { - DebitorStatistikData statData = entry.getValue(); - if (statData.count > 0) { - gesamt = gesamt + statData.getAveragePaymentDays(); - count++; - } - } - return Math.round((double) gesamt / count); - } - - /** - * Sucht alle Rechnugnen aus einem Zeitraum und sammelt Zahlungsziel und - * Zahlungszeitspanne gruppiert nach monaten - * - */ - public Map getStats() { - - if (stats != null) { - return stats; - } - - // recompute - stats = new TreeMap(); - if (getDbtNr().isEmpty()) { - return stats; - } - // invoice.date:[20210101 TO 20210201] - DateFormat df = new SimpleDateFormat("yyyymmdd"); - - String query = "(type:workitemarchive) AND invoice.date:[20221201 TO 20500101] AND dbtr.number:" + getDbtNr() - + " AND $workflowgroup:\"Rechnungsausgang\" AND ($taskid:[5900 TO 5999])"; - - // query = query + " AND (invoice.date:[" + df.format(from) + " TO " + - // df.format(to) + "])"; - logger.fine("query = " + query); - try { - List invoices = documentService - .findStubs(query, 9999, 0, "$created", false); - - for (ItemCollection invoice : invoices) { - try { - - logger.fine("Rechnung: " + invoice.getUniqueID()); - Date invoiceDate = invoice.getItemValueDate("invoice.date"); - Date invoiceDueDate = invoice.getItemValueDate("invoice.duedate"); - Date paymentDate = findPaymentDateByWorkitem(invoice); - if (paymentDate == null) { - logger.warning("Es wurde kein Zahlungseingang gefunden"); - continue; - } - - if (invoiceDate != null && paymentDate != null && invoiceDueDate != null) { - // do we have a stats object? - DebitorStatistikData statData = stats.get(getYearMonth(invoiceDate)); - if (statData == null) { - // create a new one - statData = new DebitorStatistikData(invoiceDate); - } - // jetzt irgendwas ausrechnen - statData.compute(invoiceDate, invoiceDueDate, paymentDate); - // .. und wieder speichern - stats.put(statData.toString(), statData); - - } else { - logger.warning("No payment Date found for invoice " + invoice.getUniqueID()); - } - - } catch (ParseException e) { - logger.warning("Unable to parse payment Date for invoice " + invoice.getUniqueID()); - } - } - - } catch (QueryException e) { - logger.severe("Failed to get statistic: " + e.getMessage()); - } - // Step 2: Sort the list based on the alphanumeric order of the keys - completeMissingMonths(); - return stats; - - } - - /** - * Hilfsmethode ergänzt die fehlenden Monate - * - * @param yearMonths - * @return - */ - private void completeMissingMonths() { - if (stats == null || stats.size() < 3) { - return; - } - - // Convert the list of strings to a list of LocalDate objects - List dates = new ArrayList<>(); - Set yearMonths = this.stats.keySet(); - DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMM"); - for (String yearMonth : yearMonths) { - dates.add(YearMonth.parse(yearMonth, formatter).atDay(1)); - } - - // Find the lowest and highest dates - LocalDate lowestDate = Collections.min(dates); - LocalDate highestDate = Collections.max(dates); - - LocalDate current = lowestDate; - current = current.plusMonths(1); - while (!current.isAfter(highestDate)) { - - // existiert der monat? - String formattedDate = current.format(formatter); - DebitorStatistikData entry = stats.get(formattedDate); - if (entry == null) { - // add missing entry - stats.put(formattedDate, new DebitorStatistikData(current.getYear(), current.getMonthValue())); - } - current = current.plusMonths(1); - - } - - } - - /** - * returns - * - * 202304 from a given date - */ - public String getYearMonth(Date date) { - - // Create a Calendar instance and set the date - Calendar calendar = Calendar.getInstance(); - calendar.setTime(date); - - // Get the year from the Calendar object - int year = calendar.get(Calendar.YEAR); - // Get the month from the Calendar object - int month = calendar.get(Calendar.MONTH); - // Increment the month by 1 since Calendar months are zero-based - month++; - // Convert the month to a String with leading "0" if necessary - return "" + year + (month < 10 ? "0" + month : "" + month); - - } - - /** - * Finds the payment.date for a invoice. - * - * Wir suchen alle zugeordneten Zahlungseingägne und nehmen den letzten. - * - * WICHTIG: Es muss ggf. der index neu aufgebaut werden, da payment.date nun ein - * index feld ist - * - * @param invoice - * @return - * @throws ParseException - */ - public Date findPaymentDateByWorkitem(ItemCollection invoice) throws ParseException { - - // Wir selektieren alle Zahlungseingänge interessieren usn aber nur für den - // letzten - String sQuery = " (type:\"workitem\" OR type:\"workitemarchive\") " + // - " AND ($workflowgroup:Zahlungseingang) AND ($workitemref:\"" - + invoice.getUniqueID() + "\" )"; - - List workitems = null; - - try { - workitems = documentService.findStubs(sQuery, 99, 0, - "payment.date", true); - if (workitems.size() > 0) { - return workitems.get(0).getItemValueDate("payment.date"); - } - } catch (QueryException e) { - - e.printStackTrace(); - } - - // no date found! - return null; - - } - - /** - * Finds the date when a workitem last reached the current task 5900 by reading - * the eventLog - * - * This method is not so save as the method findPaymentDateByWorkitem that is - * searching the payment workitem. - * - * 2023-06-28T12:40:17.437|rechnungsausgang-de-1.0|5001.50|5900| - * - * @param invoice - * @return - * @throws ParseException - */ - public Date findPaymentDateByLog(ItemCollection invoice) throws ParseException { - - // Create a SimpleDateFormat instance with the desired format - SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS"); - List logEntries = invoice.getItemValue("$eventlog"); - - // Iterate through the List from the last element to the first - for (int i = logEntries.size() - 1; i >= 0; i--) { - String entry = logEntries.get(i); - - if (entry.endsWith(invoice.getTaskID() + "|")) { - // found! - String dateString = entry.substring(0, entry.indexOf("|") - 1); - // Parse the date string into a Date object - Date date = sdf.parse(dateString); - // Print the Date object - System.out.println("Date: " + date); - return date; - } - } - - // no date found! - return null; - - } - - /** - * Diese Methode baut die Datenstruktur für das Chart Diagram zusammen - * - * - *

-		{
-		 labels: ["January", "February", "March", "April", "May", "June", "July"],
-		 datasets: [{
-			label: 'Dataset 1',
-			//backgroundColor: color(window.chartColors.red).alpha(0.5).rgbString(),
-			//borderColor: window.chartColors.red,
-			borderWidth: 1,
-			data: [
-				70, 70, 70, 70, 79, 50, 50
-			]
-		}, {
-			label: 'Dataset 2',
-			//backgroundColor: color(window.chartColors.blue).alpha(0.5).rgbString(),
-			//borderColor: window.chartColors.blue,
-			borderWidth: 1,
-			data: [
-				70, 70, 170, 7, 79, 50, 50
-			]
-		}]
-	   }
-	 * 
- * - * @return - */ - public String buildChartData() { - - getStats(); - - // build a list of all lables.... - List statusLabels = new ArrayList(); - - Set keys = stats.keySet(); - for (String _key : keys) { - statusLabels.add(_key); - } - - String result = "{"; - - // Lables - result = result + "labels : [ "; - result = result + statusLabels.stream().collect(Collectors.joining("','", "'", "'")); - result = result + "],"; - result = result + "datasets: ["; - - // Datasets 1 - result = result + "{label: 'Zahlungsziel',borderWidth: 1,"; - result = result + " borderColor: [\"#3B6B82\"],"; - result = result + " \"backgroundColor\" : [\"#CFE9F5\"], fill: true,tension: 0.5,"; - - result = result + "data: ["; - for (Map.Entry entry : stats.entrySet()) { - result = result + entry.getValue().getAverageDueDays() + ","; - } - // cut last comma - result = result.substring(0, result.length() - 1); - result = result + "]"; - result = result + "}, "; - - // Datasets 2 - result = result + "{label: 'Zahldauer',borderWidth: 1,"; - result = result + " borderColor: [\"#E73B65\"],\"backgroundColor\" : [\"#70B088\" ], tension: 0.5,fill: true,"; - - result = result - + " trendlineLinear: { colorMin: \"red\", colorMax: \"green\", lineStyle: \"dotted\", width: 2 , projection: true },"; - - result = result + "data: ["; - for (Map.Entry entry : stats.entrySet()) { - result = result + entry.getValue().getAveragePaymentDays() + ","; - } - // cut last comma - result = result.substring(0, result.length() - 1); - result = result + "]"; - result = result + "} "; - - // ende - result = result + "] }"; - - return result; - } + private static final long serialVersionUID = 1L; + private static Logger logger = Logger.getLogger(DebitorStatistikController.class.getName()); + + @Inject + protected DocumentService documentService; + + TreeMap stats = null; + + @Inject + protected WorkflowController workflowController; + + @Inject + protected SearchController searchController; + + @Inject + protected BusinessPartnerService businessPartnerService; + + /** + * Initialize default behavior initialize an opitonal id from the query string + * + */ + @PostConstruct + public void init() { + // extract the processref and page from the query string + FacesContext fc = FacesContext.getCurrentInstance(); + Map paramMap = fc.getExternalContext().getRequestParameterMap(); + + String id = paramMap.get("dbtr.number"); + if (id != null && !id.isEmpty()) { + workflowController.getWorkitem().setItemValue("dbtr.number", id); + + ItemCollection dbtr = businessPartnerService.getBusinessPartnerByID(InvoiceUtil.buildBPID(id)); + // ItemCollection dbtr = kreditorDebitorService.findDebitor(id); + if (dbtr != null) { + workflowController.getWorkitem().setItemValue("dbtr.name", dbtr.getItemValueString("partner.name")); + } + + } + } + + public ItemCollection getWorkitem() { + return workflowController.getWorkitem(); + } + + public int getZahlungsmoral() { + return 0; + } + + /** + * Gibt eine bookmarkable url zurück. Wir vom Aktualisieren button in der Page + * genutzt + * + * @return + */ + public String getBookmark() { + return "/pages/dbtr-stats.xhtml?dbtr.number=" + getDbtNr() + "&faces-redirect=true"; + } + + /* + * Gesamt + */ + public String getQueryRechnungen() { + String query = "(type:workitem OR type:workitemarchive) AND dbtr.number:" + getDbtNr() + + " AND $workflowgroup:\"Rechnungsausgang\""; + return "/pages/workitems/worklist?title=Rechnungen " + getDbtNr() + " gesamt&query=" + query; + } + + public int getRechnungen() { + int result = 0; + if (getDbtNr().isEmpty()) { + return 0; + } + try { + result = documentService.count("(type:workitem OR type:workitemarchive) AND dbtr.number:" + getDbtNr() + + " AND $workflowgroup:\"Rechnungsausgang\""); + } catch (QueryException e) { + logger.severe("Failed to get statistic: " + e.getMessage()); + } + return result; + } + + /* + * Bezahlt + */ + public String getQueryRechnungenBezahlt() { + String query = "(type:workitem OR type:workitemarchive) AND dbtr.number:" + getDbtNr() + + " AND $workflowgroup:\"Rechnungsausgang\" AND ($taskid:[5900 TO 5999])"; + return "/pages/workitems/worklist?title=Rechnungen " + getDbtNr() + " bezahlt&query=" + query; + } + + public int getRechnungenBezahlt() { + int result = 0; + if (getDbtNr().isEmpty()) { + return 0; + } + try { + result = documentService.count("(type:workitem OR type:workitemarchive) AND dbtr.number:" + getDbtNr() + + " AND $workflowgroup:\"Rechnungsausgang\" AND ($taskid:[5900 TO 5999])"); + } catch (QueryException e) { + logger.severe("Failed to get statistic: " + e.getMessage()); + } + return result; + } + + /* + * Offen + */ + public String getQueryRechnungenOffen() { + String query = "(type:workitem) AND dbtr.number:" + getDbtNr() + + " AND $workflowgroup:\"Rechnungsausgang\" AND ($taskid:[5000 TO 5099])"; + return "/pages/workitems/worklist?title=Rechnungen " + getDbtNr() + " offen&query=" + query; + } + + public int getRechnungenOffen() { + int result = 0; + if (getDbtNr().isEmpty()) { + return 0; + } + try { + result = documentService.count("(type:workitem) AND dbtr.number:" + getDbtNr() + + " AND $workflowgroup:\"Rechnungsausgang\" AND ($taskid:[5000 TO 5099])"); + } catch (QueryException e) { + logger.severe("Failed to get statistic: " + e.getMessage()); + } + return result; + } + + /* + * Fällig + */ + public String getQueryRechnungenFaellig() { + String query = "(type:workitem) AND dbtr.number:" + getDbtNr() + + " AND $workflowgroup:\"Rechnungsausgang\" AND ($taskid:[5100 TO 5199])"; + return "/pages/workitems/worklist?title=Rechnungen " + getDbtNr() + " fällig&query=" + query; + } + + public int getRechnungenFaellig() { + int result = 0; + if (getDbtNr().isEmpty()) { + return 0; + } + try { + result = documentService.count("(type:workitem) AND dbtr.number:" + getDbtNr() + + " AND $workflowgroup:\"Rechnungsausgang\" AND ($taskid:[5100 TO 5199])"); + } catch (QueryException e) { + logger.severe("Failed to get statistic: " + e.getMessage()); + } + return result; + } + + /* + * In Mahnung + */ + public String getQueryRechnungenGemahnt() { + String query = "(type:workitem) AND dbtr.number:" + getDbtNr() + + " AND $workflowgroup:\"Rechnungsausgang\" AND ($taskid:[5200 TO 5299])"; + return "/pages/workitems/worklist?title=Rechnungen " + getDbtNr() + " in Mahnung&query=" + query; + } + + public int getRechnungenGemahnt() { + int result = 0; + if (getDbtNr().isEmpty()) { + return 0; + } + try { + result = documentService.count("(type:workitem ) AND dbtr.number:" + getDbtNr() + + " AND $workflowgroup:\"Rechnungsausgang\" AND ($taskid:[5200 TO 5299])"); + } catch (QueryException e) { + logger.severe("Failed to get statistic: " + e.getMessage()); + } + return result; + } + + /* + * Inkasso + */ + public String getQueryRechnungenInkasso() { + String query = "(type:workitem) AND dbtr.number:" + getDbtNr() + + " AND $workflowgroup:\"Rechnungsausgang\" AND ($taskid:[5300 TO 5399])"; + return "/pages/workitems/worklist?title=Rechnungen " + getDbtNr() + " in Inkasso&query=" + query; + } + + public int getRechnungenInkasso() { + int result = 0; + if (getDbtNr().isEmpty()) { + return 0; + } + try { + result = documentService.count("(type:workitem OR type:workitemarchive) AND dbtr.number:" + getDbtNr() + + " AND $workflowgroup:\"Rechnungsausgang\" AND ($taskid:[5300 TO 5399])"); + } catch (QueryException e) { + logger.severe("Failed to get statistic: " + e.getMessage()); + } + return result; + } + + /** + * Hilfsmethode die das fuehrende K/D aus der Debitorennummer entfernt + * + * @return + */ + public String getDbtNr() { + String dbtNr = getWorkitem().getItemValueString("dbtr.number"); + if (dbtNr.startsWith("D") || dbtNr.startsWith("K")) { + dbtNr = dbtNr.substring(1); + } + return dbtNr; + } + + public long getDurchschnittZahlungsziel() { + getStats(); + int count = 0; + long gesamt = 0; + for (Map.Entry entry : stats.entrySet()) { + DebitorStatistikData statData = entry.getValue(); + if (statData.count > 0) { + gesamt = gesamt + statData.getAverageDueDays(); + count++; + } + } + return Math.round((double) gesamt / count); + } + + public long getDurchschnittZahlungsdauer() { + getStats(); + int count = 0; + long gesamt = 0; + for (Map.Entry entry : stats.entrySet()) { + DebitorStatistikData statData = entry.getValue(); + if (statData.count > 0) { + gesamt = gesamt + statData.getAveragePaymentDays(); + count++; + } + } + return Math.round((double) gesamt / count); + } + + /** + * Sucht alle Rechnugnen aus einem Zeitraum und sammelt Zahlungsziel und + * Zahlungszeitspanne gruppiert nach monaten + * + */ + public Map getStats() { + + if (stats != null) { + return stats; + } + + // recompute + stats = new TreeMap(); + if (getDbtNr().isEmpty()) { + return stats; + } + // invoice.date:[20210101 TO 20210201] + DateFormat df = new SimpleDateFormat("yyyymmdd"); + + String query = "(type:workitemarchive) AND invoice.date:[20221201 TO 20500101] AND dbtr.number:" + getDbtNr() + + " AND $workflowgroup:\"Rechnungsausgang\" AND ($taskid:[5900 TO 5999])"; + + // query = query + " AND (invoice.date:[" + df.format(from) + " TO " + + // df.format(to) + "])"; + logger.fine("query = " + query); + try { + List invoices = documentService.findStubs(query, 9999, 0, "$created", false); + + for (ItemCollection invoice : invoices) { + try { + + logger.fine("Rechnung: " + invoice.getUniqueID()); + Date invoiceDate = invoice.getItemValueDate("invoice.date"); + Date invoiceDueDate = invoice.getItemValueDate("invoice.duedate"); + Date paymentDate = findPaymentDateByWorkitem(invoice); + if (paymentDate == null) { + logger.warning("Es wurde kein Zahlungseingang gefunden"); + continue; + } + + if (invoiceDate != null && paymentDate != null && invoiceDueDate != null) { + // do we have a stats object? + DebitorStatistikData statData = stats.get(getYearMonth(invoiceDate)); + if (statData == null) { + // create a new one + statData = new DebitorStatistikData(invoiceDate); + } + // jetzt irgendwas ausrechnen + statData.compute(invoiceDate, invoiceDueDate, paymentDate); + // .. und wieder speichern + stats.put(statData.toString(), statData); + + } else { + logger.warning("No payment Date found for invoice " + invoice.getUniqueID()); + } + + } catch (ParseException e) { + logger.warning("Unable to parse payment Date for invoice " + invoice.getUniqueID()); + } + } + + } catch (QueryException e) { + logger.severe("Failed to get statistic: " + e.getMessage()); + } + // Step 2: Sort the list based on the alphanumeric order of the keys + completeMissingMonths(); + return stats; + + } + + /** + * Hilfsmethode ergänzt die fehlenden Monate + * + * @param yearMonths + * @return + */ + private void completeMissingMonths() { + if (stats == null || stats.size() < 3) { + return; + } + + // Convert the list of strings to a list of LocalDate objects + List dates = new ArrayList<>(); + Set yearMonths = this.stats.keySet(); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMM"); + for (String yearMonth : yearMonths) { + dates.add(YearMonth.parse(yearMonth, formatter).atDay(1)); + } + + // Find the lowest and highest dates + LocalDate lowestDate = Collections.min(dates); + LocalDate highestDate = Collections.max(dates); + + LocalDate current = lowestDate; + current = current.plusMonths(1); + while (!current.isAfter(highestDate)) { + + // existiert der monat? + String formattedDate = current.format(formatter); + DebitorStatistikData entry = stats.get(formattedDate); + if (entry == null) { + // add missing entry + stats.put(formattedDate, new DebitorStatistikData(current.getYear(), current.getMonthValue())); + } + current = current.plusMonths(1); + + } + + } + + /** + * returns + * + * 202304 from a given date + */ + public String getYearMonth(Date date) { + + // Create a Calendar instance and set the date + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + + // Get the year from the Calendar object + int year = calendar.get(Calendar.YEAR); + // Get the month from the Calendar object + int month = calendar.get(Calendar.MONTH); + // Increment the month by 1 since Calendar months are zero-based + month++; + // Convert the month to a String with leading "0" if necessary + return "" + year + (month < 10 ? "0" + month : "" + month); + + } + + /** + * Finds the payment.date for a invoice. + * + * Wir suchen alle zugeordneten Zahlungseingägne und nehmen den letzten. + * + * WICHTIG: Es muss ggf. der index neu aufgebaut werden, da payment.date nun ein + * index feld ist + * + * @param invoice + * @return + * @throws ParseException + */ + public Date findPaymentDateByWorkitem(ItemCollection invoice) throws ParseException { + + // Wir selektieren alle Zahlungseingänge interessieren usn aber nur für den + // letzten + String sQuery = " (type:\"workitem\" OR type:\"workitemarchive\") " + // + " AND ($workflowgroup:Zahlungseingang) AND ($workitemref:\"" + invoice.getUniqueID() + "\" )"; + + List workitems = null; + + try { + workitems = documentService.findStubs(sQuery, 99, 0, "payment.date", true); + if (workitems.size() > 0) { + return workitems.get(0).getItemValueDate("payment.date"); + } + } catch (QueryException e) { + + e.printStackTrace(); + } + + // no date found! + return null; + + } + + /** + * Finds the date when a workitem last reached the current task 5900 by reading + * the eventLog + * + * This method is not so save as the method findPaymentDateByWorkitem that is + * searching the payment workitem. + * + * 2023-06-28T12:40:17.437|rechnungsausgang-de-1.0|5001.50|5900| + * + * @param invoice + * @return + * @throws ParseException + */ + public Date findPaymentDateByLog(ItemCollection invoice) throws ParseException { + + // Create a SimpleDateFormat instance with the desired format + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS"); + List logEntries = invoice.getItemValue("$eventlog"); + + // Iterate through the List from the last element to the first + for (int i = logEntries.size() - 1; i >= 0; i--) { + String entry = logEntries.get(i); + + if (entry.endsWith(invoice.getTaskID() + "|")) { + // found! + String dateString = entry.substring(0, entry.indexOf("|") - 1); + // Parse the date string into a Date object + Date date = sdf.parse(dateString); + // Print the Date object + System.out.println("Date: " + date); + return date; + } + } + + // no date found! + return null; + + } + + /** + * Diese Methode baut die Datenstruktur für das Chart Diagram zusammen + * + * + *
+    	{
+    	 labels: ["January", "February", "March", "April", "May", "June", "July"],
+    	 datasets: [{
+    		label: 'Dataset 1',
+    		//backgroundColor: color(window.chartColors.red).alpha(0.5).rgbString(),
+    		//borderColor: window.chartColors.red,
+    		borderWidth: 1,
+    		data: [
+    			70, 70, 70, 70, 79, 50, 50
+    		]
+    	}, {
+    		label: 'Dataset 2',
+    		//backgroundColor: color(window.chartColors.blue).alpha(0.5).rgbString(),
+    		//borderColor: window.chartColors.blue,
+    		borderWidth: 1,
+    		data: [
+    			70, 70, 170, 7, 79, 50, 50
+    		]
+    	}]
+       }
+     * 
+ * + * @return + */ + public String buildChartData() { + + getStats(); + + // build a list of all lables.... + List statusLabels = new ArrayList(); + + Set keys = stats.keySet(); + for (String _key : keys) { + statusLabels.add(_key); + } + + String result = "{"; + + // Lables + result = result + "labels : [ "; + result = result + statusLabels.stream().collect(Collectors.joining("','", "'", "'")); + result = result + "],"; + result = result + "datasets: ["; + + // Datasets 1 + result = result + "{label: 'Zahlungsziel',borderWidth: 1,"; + result = result + " borderColor: [\"#3B6B82\"],"; + result = result + " \"backgroundColor\" : [\"#CFE9F5\"], fill: true,tension: 0.5,"; + + result = result + "data: ["; + for (Map.Entry entry : stats.entrySet()) { + result = result + entry.getValue().getAverageDueDays() + ","; + } + // cut last comma + result = result.substring(0, result.length() - 1); + result = result + "]"; + result = result + "}, "; + + // Datasets 2 + result = result + "{label: 'Zahldauer',borderWidth: 1,"; + result = result + " borderColor: [\"#E73B65\"],\"backgroundColor\" : [\"#70B088\" ], tension: 0.5,fill: true,"; + + result = result + + " trendlineLinear: { colorMin: \"red\", colorMax: \"green\", lineStyle: \"dotted\", width: 2 , projection: true },"; + + result = result + "data: ["; + for (Map.Entry entry : stats.entrySet()) { + result = result + entry.getValue().getAveragePaymentDays() + ","; + } + // cut last comma + result = result.substring(0, result.length() - 1); + result = result + "]"; + result = result + "} "; + + // ende + result = result + "] }"; + + return result; + } } diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/DebitorStatistikData.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/DebitorStatistikData.java index ea07475..f145afe 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/DebitorStatistikData.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/DebitorStatistikData.java @@ -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() { diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/FTPConnector.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/FTPConnector.java index d728880..7438dad 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/FTPConnector.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/FTPConnector.java @@ -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") diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/InvoiceAnalyseController.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/InvoiceAnalyseController.java index 2c6e7f9..a9098d9 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/InvoiceAnalyseController.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/InvoiceAnalyseController.java @@ -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 allCounts = cat.getLabels().values(); - result = allCounts.stream().reduce(0, Integer::sum); + Collection 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 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 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"); + } } diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/InvoiceDispatchAdapter.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/InvoiceDispatchAdapter.java index f7216d2..1408601 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/InvoiceDispatchAdapter.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/InvoiceDispatchAdapter.java @@ -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 diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/InvoiceDispatchController.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/InvoiceDispatchController.java index 6c17746..860020f 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/InvoiceDispatchController.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/InvoiceDispatchController.java @@ -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})"> - * + * */ public void searchInvoices() { long l = System.currentTimeMillis(); diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/InvoiceOutgoingPlugin.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/InvoiceOutgoingPlugin.java index 4c2b576..10a452d 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/InvoiceOutgoingPlugin.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/InvoiceOutgoingPlugin.java @@ -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 + ""; + String img = ""; + if (workitem.getItemValueBoolean("invoice.protest")) { + img = img + ""; - } - workitem.setItemValue("_img", img); + } + workitem.setItemValue("_img", img); - return workitem; - } + return workitem; + } } diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/InvoicePlugin.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/InvoicePlugin.java index 2704e0e..30b267a 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/InvoicePlugin.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/InvoicePlugin.java @@ -35,403 +35,402 @@ import jakarta.inject.Inject; *

* 2.2.22 - Das Plugin erzeugt auch das Feld _img welches die Images von * Sofortüberweisung, Mahnung und Ablehnung anzeigt. - * - * 26.4.25 - Das Plugin setzt nun auch partner.name und partner.id - * zusätzlich processed es das BP Workitem - * + * + * 26.4.25 - Das Plugin setzt nun auch partner.name und partner.id zusätzlich + * processed es das BP Workitem + * * @author rsoika * @version 1.0 - * + * */ 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 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 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 + ""; - } - // mahnen - if (workitem.getEventID() == 95 && !img.contains("103.png")) { - img = img + ""; + // 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 + ""; + } + // mahnen + if (workitem.getEventID() == 95 && !img.contains("103.png")) { + img = img + ""; - } - // ablehnen - if (workitem.getTaskID() == 5100 && workitem.getEventID() == 90 && !img.contains("028.png")) { - img = img + ""; - } - if (workitem.getTaskID() == 5200 && workitem.getEventID() == 90 && !img.contains("028.png")) { - img = img + ""; - } - workitem.setItemValue("_img", img); + } + // ablehnen + if (workitem.getTaskID() == 5100 && workitem.getEventID() == 90 && !img.contains("028.png")) { + img = img + ""; + } + if (workitem.getTaskID() == 5200 && workitem.getEventID() == 90 && !img.contains("028.png")) { + img = img + ""; + } + 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. - *

- * Query Example: - *

- * NOT $uniqueid:"9f72fa50-4845-41ea-b6b9-ccd518c353be" AND - txtcooperatespace:"9dba107e-f8ef-4150-a832-040d75a6eda7" AND invoice.number:"45" - * - *

- * 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. + *

+ * Query Example: + *

+ * NOT $uniqueid:"9f72fa50-4845-41ea-b6b9-ccd518c353be" AND + txtcooperatespace:"9dba107e-f8ef-4150-a832-040d75a6eda7" AND invoice.number:"45" + * + *

+ * 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. - *

- * 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. + *

+ * 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 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 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(); + } } diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/InvoiceService.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/InvoiceService.java index 873b42c..19770c2 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/InvoiceService.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/InvoiceService.java @@ -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 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 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 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) { diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/InvoiceUtil.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/InvoiceUtil.java index cc7a582..c358d67 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/InvoiceUtil.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/InvoiceUtil.java @@ -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 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 */ diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/OPListController.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/OPListController.java index 078e00f..36d15e6 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/OPListController.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/OPListController.java @@ -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. *

* 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. *

* Befindet sich ein Zahlungseingang in Erstellung (Task=1000) werden alle * offenen Rechnungen geholt. @@ -90,8 +89,8 @@ public class OPListController implements Serializable { * will. *

* 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 intialSelection = new ArrayList(); 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 */ diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/OPListExportAdapter.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/OPListExportAdapter.java index 1b4dff5..f123efb 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/OPListExportAdapter.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/OPListExportAdapter.java @@ -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. - * + * *

  * {@code
         textblock-ref
@@ -46,16 +46,16 @@ import jakarta.inject.Inject;
  * 

* Der Adapter erweitert den POIAdapter somit können Felder aktualisiert werden * (siehe POIFineReplaceAdapter). - * + * *

* Der Adapter kopiert die Rechnungsdaten in neue Zeilen, welche ab Zeilennummer * 12 eingefügt werden. *

- * + * * 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 { *

* 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 currencyList) - throws PluginException, QueryException { + List 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 */ diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/OPListExportAdapterByWeek.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/OPListExportAdapterByWeek.java index 49de735..b110389 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/OPListExportAdapterByWeek.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/OPListExportAdapterByWeek.java @@ -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 - * + * *

  * {@code
         textblock-ref
@@ -59,14 +59,14 @@ import jakarta.inject.Inject;
  * 

* Der Adapter erweitert den POIAdapter somit können felder aktualisiert werden * (siehe POIFineReplaceAdapter). - * + * *

* Der Adapter kopiert die Rechnungsdaten in neue Zeilen, welche ab Zeilnenummer * 16 eingefügt werden. *

- * + * * 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 { *

* 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 currencyList, - List spaces) - throws PluginException, QueryException { + List spaces) throws PluginException, QueryException { List spaceNames = new ArrayList(); @@ -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 sortedWeekList = new ArrayList(); Iterator> 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 weekDataCache) { + private void groupInvoicesByWeek(String spaceID, String spaceName, Map 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 { diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/OPListExportController.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/OPListExportController.java index 550b888..37fab8b 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/OPListExportController.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/OPListExportController.java @@ -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. *

* 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; + } } diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/OPListExportScheduler.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/OPListExportScheduler.java index 154e8c1..542ba56 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/OPListExportScheduler.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/OPListExportScheduler.java @@ -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 invoices = new ArrayList(); 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 */ diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/RenameFilenamesAdapter.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/RenameFilenamesAdapter.java index a4763dc..662d1c5 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/RenameFilenamesAdapter.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/RenameFilenamesAdapter.java @@ -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 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 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; + } } diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/SEPAExportControllerAGL.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/SEPAExportControllerAGL.java index 3de738c..d7d71e9 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/SEPAExportControllerAGL.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/SEPAExportControllerAGL.java @@ -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 * */ diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/SteuerListController.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/SteuerListController.java index 5b1ce41..dd2334a 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/SteuerListController.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/SteuerListController.java @@ -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 getRows() { diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/SteuerbescheidPlugin.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/SteuerbescheidPlugin.java index dc0aa00..7430eb9 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/SteuerbescheidPlugin.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/SteuerbescheidPlugin.java @@ -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 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 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 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 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 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 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 invoices = findInvoices(atcNummer); + String atcNummer = workitem.getItemValueString("invoice.atc.number"); + logger.fine("Verify ATC-Nr.: " + atcNummer); + double amount = workitem.getItemValueDouble("invoice.total.net"); + List 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 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 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 findInvoices(String atcNumber) { - List result = new ArrayList(); - 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 findInvoices(String atcNumber) { + List result = new ArrayList(); + 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; + } } diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/ZahlungsavisAppendAdapter.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/ZahlungsavisAppendAdapter.java index 9dda062..e2eba98 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/ZahlungsavisAppendAdapter.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/ZahlungsavisAppendAdapter.java @@ -21,9 +21,9 @@ import jakarta.inject.Inject; * Kredito erzeugt der Adapter automatisch eine neue Prozessinstanz. *

* 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 diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/ZahlungsavisController.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/ZahlungsavisController.java index 0e98a34..5ee511d 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/ZahlungsavisController.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/ZahlungsavisController.java @@ -18,7 +18,7 @@ import jakarta.inject.Named; *

* 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. *

* Gutschriften werden abgezogen - * + * * @param refids - list of workitem uniqueIds * @param item - name of the item to summarize * @return sum rounded to 2 digits diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/ZahlungsavisExportAdapter.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/ZahlungsavisExportAdapter.java index 7904a7b..2eaeebb 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/ZahlungsavisExportAdapter.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/ZahlungsavisExportAdapter.java @@ -27,7 +27,7 @@ import jakarta.inject.Inject; /** * Der ZahlungsavisAdapter importiert eine Excel Datei aus einem Textblock - * + * *

  * {@code
         textblock-ref
@@ -38,7 +38,7 @@ import jakarta.inject.Inject;
  * 

* Der Adapter erweitert den POIAdapter somit können felder aktualisiert werden * (siehe POIFineReplaceAdapter). - * + * *

* 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. *

* 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 { *

* 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) { diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/ZahlungseingangSaldoAdapter.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/ZahlungseingangSaldoAdapter.java index cf049d3..27c9809 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/ZahlungseingangSaldoAdapter.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/ZahlungseingangSaldoAdapter.java @@ -28,7 +28,7 @@ import jakarta.inject.Inject; *

  • payment.amount *

    * com.alexanderlogistics.ZahlungseingangSaldoAdapter - * + * * @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 */ diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/ZahlungseingangService.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/ZahlungseingangService.java index 542a180..365c85c 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/ZahlungseingangService.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/ZahlungseingangService.java @@ -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" }) diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/ai/PromptExampleAdapterXML.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/ai/PromptExampleAdapterXML.java index 243bd2e..ff0f15f 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/ai/PromptExampleAdapterXML.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/ai/PromptExampleAdapterXML.java @@ -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 - * + * * <> - * + * * Prompt example generated by this Adapter: - * + * * Example: - * + * *

      * {@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;
      *
      * 
      *   ...
    - *   ... 
    + *   ...
      *   2024-12-31
      *   2024-12-31
      *   1234.00
    @@ -50,7 +50,7 @@ import jakarta.inject.Inject;
      * 
     * }
     * 
    - * + * */ 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("<>", "" + promptExamples); @@ -91,13 +90,13 @@ public class PromptExampleAdapterXML { /** * Helper Method that builds a prompt example out of an existing invoice: - * + * *
          * {@code
          *
          * 
          *   ...
    -     *   ... 
    +     *   ...
          *   2024-12-31
          *   2024-12-31
          *   1234.00
    @@ -106,7 +105,7 @@ public class PromptExampleAdapterXML {
          * 
         * }
         * 
    - * + * * @param invoice * @return */ @@ -126,14 +125,12 @@ public class PromptExampleAdapterXML { example.append( " " + invoice.getItemValueString("invoice.number") + "\n"); if (invoice.getItemValueDate("invoice.date") != null) { - example.append( - " " + dateFormat.format(invoice.getItemValueDate("invoice.date")) - + "\n"); + example.append(" " + dateFormat.format(invoice.getItemValueDate("invoice.date")) + + "\n"); } if (invoice.getItemValueDate("invoice.duedate") != null) { - example.append( - " " + dateFormat.format(invoice.getItemValueDate("invoice.duedate")) - + "\n"); + example.append(" " + dateFormat.format(invoice.getItemValueDate("invoice.duedate")) + + "\n"); } example.append(" " + invoice.getItemValueDouble("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 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) { diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/api/CargosoftMigrationRestService.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/api/CargosoftMigrationRestService.java index 38c757c..333750d 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/api/CargosoftMigrationRestService.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/api/CargosoftMigrationRestService.java @@ -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 diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/api/DataRestService.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/api/DataRestService.java index 5194d52..450b3f3 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/api/DataRestService.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/api/DataRestService.java @@ -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. *

    * 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 diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/datev/CargosoftExportController.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/datev/CargosoftExportController.java index b2efbf2..910abb5 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/datev/CargosoftExportController.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/datev/CargosoftExportController.java @@ -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". *

    * The following config items are defined: - * + * * The following config items are defined: - * + * *

      * _model_version = model version for the SEPA export
      * _initial_task = inital task ID
      * 
    - * - * + * + * * @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; + } } diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/datev/CargosoftExportScheduler.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/datev/CargosoftExportScheduler.java index 7857bb3..b42b8da 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/datev/CargosoftExportScheduler.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/datev/CargosoftExportScheduler.java @@ -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 */ diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/mahnlauf/ExcelExportController.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/mahnlauf/ExcelExportController.java index d046104..7d7c2d5 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/mahnlauf/ExcelExportController.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/mahnlauf/ExcelExportController.java @@ -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 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 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 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 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 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 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 - *

    - * Examples for refs are 'A1', 'AB3', 'MyCell' where 'MyCell' is a named cell. - *

    - * - */ - public static XSSFCell getCellByRef(XSSFWorkbook doc, XSSFSheet sheet, String cellReference) { - XSSFCell cell = null; + /** + * Returns a Cell by name or an optional absolute cell postion + *

    + * Examples for refs are 'A1', 'AB3', 'MyCell' where 'MyCell' is a named cell. + *

    + * + */ + 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; + } } diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/mahnlauf/InkassoAppendAdapter.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/mahnlauf/InkassoAppendAdapter.java index 3031b06..d2e9300 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/mahnlauf/InkassoAppendAdapter.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/mahnlauf/InkassoAppendAdapter.java @@ -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. *

    * 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 diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/mahnlauf/InkassoController.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/mahnlauf/InkassoController.java index 20a7533..a98aa84 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/mahnlauf/InkassoController.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/mahnlauf/InkassoController.java @@ -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. *

    * 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. *

    * Gutschriften werden abgezogen - * + * * @param refids - list of workitem uniqueIds * @param item - name of the item to summarize * @return sum rounded to 2 digits diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/mahnlauf/InkassoExportAdapter.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/mahnlauf/InkassoExportAdapter.java index cdc81a3..4016091 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/mahnlauf/InkassoExportAdapter.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/mahnlauf/InkassoExportAdapter.java @@ -27,7 +27,7 @@ import jakarta.inject.Inject; /** * Der ZahlungsavisAdapter importiert eine Excel Datei aus einem Textblock - * + * *

      * {@code
             textblock-ref
    @@ -38,7 +38,7 @@ import jakarta.inject.Inject;
      * 

    * Der Adapter erweitert den POIAdapter somit können felder aktualisiert werden * (siehe POIFineReplaceAdapter). - * + * *

    * 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. *

    * 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 { *

    * 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) { diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/mahnlauf/MahnlaufExecuteAdapter.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/mahnlauf/MahnlaufExecuteAdapter.java index e5898ca..ed5a2c2 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/mahnlauf/MahnlaufExecuteAdapter.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/mahnlauf/MahnlaufExecuteAdapter.java @@ -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. *

    - * 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 */ diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/mahnlauf/MahnlaufRefAddAdapter.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/mahnlauf/MahnlaufRefAddAdapter.java index e26bba8..c95b3a4 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/mahnlauf/MahnlaufRefAddAdapter.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/mahnlauf/MahnlaufRefAddAdapter.java @@ -30,152 +30,151 @@ import jakarta.inject.Inject; *

    * If the invoice has already been linked to a Mahnlauf nothing happens. *

    - * + * * @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 refList = mahnLaufWorkitem.getItemValue("$workitemref"); - if (!refList.contains(invoice.getUniqueID())) { - mahnLaufWorkitem.appendItemValueUnique("$workitemref", invoice.getUniqueID()); - } + // add Invoice to Mahnlauf + List 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 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 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; + } } \ No newline at end of file diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/mahnlauf/MahnlaufService.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/mahnlauf/MahnlaufService.java index ea9828b..2516dda 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/mahnlauf/MahnlaufService.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/mahnlauf/MahnlaufService.java @@ -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> loadSpacePosMappings() { @@ -125,7 +125,7 @@ public class MahnlaufService { /** * Hilfsmethode zum errechnen des richtigen Spaces zu einer invoice.text Positon - * + * * @param workitem * @param spacePosMappings */ diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/xml/BusinessPartnerImportService.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/xml/BusinessPartnerImportService.java index 8dbcced..2da25e6 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/xml/BusinessPartnerImportService.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/xml/BusinessPartnerImportService.java @@ -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. *

    * 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; *

    * 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 */ diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/xml/CargosoftXMLEAkteImportService.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/xml/CargosoftXMLEAkteImportService.java index 47b8194..198069f 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/xml/CargosoftXMLEAkteImportService.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/xml/CargosoftXMLEAkteImportService.java @@ -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. *

    * Im Gegensatz zum FTPImportService werden die XML Dateien nach einem * speziellen Verfahren geparsed und dann als Steuerbescheid 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. - * - * - * + * 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> spacePosMappings) - throws AccessDeniedException, ProcessingErrorException, PluginException, ModelException, - XPathExpressionException, TransformerException { + Map> 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: - * 2024-05-13T00:00:00+02:00 - * + * + * Beispiel Datum: 2024-05-13T00:00:00+02:00 + * * @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: - * + * *

    {@code
    -     *  ... 
    +     *  ...
             
             3591.340
             0
             IM-GCA-2408-116
          * }
    - * + * *

    * The last filenumber text will be transferred into the item 'invoice.text' - * + * * @param xml * @return * @throws Exception diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/xml/CargosoftXMLInvoiceImportService.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/xml/CargosoftXMLInvoiceImportService.java index 10ac3aa..3b02d3a 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/xml/CargosoftXMLInvoiceImportService.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/xml/CargosoftXMLInvoiceImportService.java @@ -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. *

    * 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. *

    - * 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> spacePosMappings) - throws AccessDeniedException, ProcessingErrorException, PluginException, ModelException, - XPathExpressionException, TransformerException { + Map> 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: - * 2024-05-13T00:00:00+02:00 - * + * + * Beispiel Datum: 2024-05-13T00:00:00+02:00 + * * @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. *

    * Diese Methode erwartet das führende D - * + * * @throws PluginException */ public ItemCollection findDebitor(String dbtrNumber) throws PluginException { diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/zoho/TokenResponse.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/zoho/TokenResponse.java index 66cb23e..433a866 100644 --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/zoho/TokenResponse.java +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/zoho/TokenResponse.java @@ -2,9 +2,9 @@ package com.alexanderlogistics.zoho; /** * Helper class for JSON deserialization - * + * * If the request is successful, you would receive the following: - * + * *

         {
         "access_token": "{access_token}",
    diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/zoho/ZohoAPIService.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/zoho/ZohoAPIService.java
    index 0486d5e..06ac9a1 100644
    --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/zoho/ZohoAPIService.java
    +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/zoho/ZohoAPIService.java
    @@ -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 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 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 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 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 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 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 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 positionen = InvoiceUtil.explodeChildList(invoice);
    -
    -                List 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 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 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 positionen = InvoiceUtil.explodeChildList(invoice);
    +
    +        List 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 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);
    +        }
    +    }
    +
     }
    diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/zoho/ZohoController.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/zoho/ZohoController.java
    index 6f7675d..292012a 100644
    --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/zoho/ZohoController.java
    +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/zoho/ZohoController.java
    @@ -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);
    +        }
    +    }
     }
    diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/zoho/ZohoExportAdapter.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/zoho/ZohoExportAdapter.java
    index 0909a53..9394df1 100644
    --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/zoho/ZohoExportAdapter.java
    +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/zoho/ZohoExportAdapter.java
    @@ -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
    diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/zoho/ZohoOAuthCallbackService.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/zoho/ZohoOAuthCallbackService.java
    index 6c2ffaa..8afb329 100644
    --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/zoho/ZohoOAuthCallbackService.java
    +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/zoho/ZohoOAuthCallbackService.java
    @@ -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");
    diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/zoho/ZohoOAuthManager.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/zoho/ZohoOAuthManager.java
    index f888173..666c5fe 100644
    --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/zoho/ZohoOAuthManager.java
    +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/zoho/ZohoOAuthManager.java
    @@ -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 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 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
          */
    diff --git a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/zoho/dto/ZohoInvoiceItem.java b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/zoho/dto/ZohoInvoiceItem.java
    index 8971bd5..5fe608a 100644
    --- a/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/zoho/dto/ZohoInvoiceItem.java
    +++ b/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/zoho/dto/ZohoInvoiceItem.java
    @@ -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;
    +    }
     }
    diff --git a/office-alexander-logistics-app/src/main/java/org/imixs/workflow/office/forms/AnalyticController.java b/office-alexander-logistics-app/src/main/java/org/imixs/workflow/office/forms/AnalyticController.java
    index 923a98c..3952bd6 100644
    --- a/office-alexander-logistics-app/src/main/java/org/imixs/workflow/office/forms/AnalyticController.java
    +++ b/office-alexander-logistics-app/src/main/java/org/imixs/workflow/office/forms/AnalyticController.java
    @@ -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 analyticEvents;
    +    @Inject
    +    protected Event 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 detailsList = new ArrayList();
    -		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 detailsList = new ArrayList();
    +        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 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 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;
    +    }
     
     }
    diff --git a/office-alexander-logistics-app/src/main/webapp/pages/admin/excel_export_rechnungsausgang.xhtml b/office-alexander-logistics-app/src/main/webapp/pages/admin/excel_export_rechnungsausgang.xhtml
    index 1508cae..c55d6ab 100644
    --- a/office-alexander-logistics-app/src/main/webapp/pages/admin/excel_export_rechnungsausgang.xhtml
    +++ b/office-alexander-logistics-app/src/main/webapp/pages/admin/excel_export_rechnungsausgang.xhtml
    @@ -10,12 +10,12 @@
     	
     		
     	
    @@ -32,7 +32,8 @@
     					
     						
    -

    Der folgende Report exportiert Ausgangsrechnungen, in eine Excel Datei. Der Export ist auf maximal 3000 Einträge pro Tabelle beschränkt +

    Der folgende Report exportiert Ausgangsrechnungen, in eine Excel Datei. Der + Export ist auf maximal 3000 Einträge pro Tabelle beschränkt

    @@ -64,13 +65,15 @@
    Abteilung:
    -
    - + - - + - + @@ -82,27 +85,26 @@ Debitor:
    - + - +
    - +
    Positionsnummer:
    - +
    - +
    diff --git a/office-alexander-logistics-app/src/main/webapp/pages/admin/zoho.xhtml b/office-alexander-logistics-app/src/main/webapp/pages/admin/zoho.xhtml index 63b5c9d..486afd4 100644 --- a/office-alexander-logistics-app/src/main/webapp/pages/admin/zoho.xhtml +++ b/office-alexander-logistics-app/src/main/webapp/pages/admin/zoho.xhtml @@ -19,13 +19,13 @@ /*]]>*/ - +

    ZOHO - API

    - +
    @@ -41,8 +41,27 @@
    +
    -
    Client ID
    +
    API Base URL *
    +
    + + +
    +
    + + +
    +
    Organization *
    +
    + + +
    +
    +
    +
    Client ID *
    @@ -51,27 +70,20 @@
    -
    Client Secret
    +
    Client Secret *
    -
    -
    Organization
    -
    - - -
    -
    +
    -
    Access Code
    +
    Access Code *
    diff --git a/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/TestFTP.java b/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/TestFTP.java index 8369d88..6e7a552 100644 --- a/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/TestFTP.java +++ b/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/TestFTP.java @@ -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(); + } - } + } } diff --git a/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/TestFormat.java b/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/TestFormat.java index 3d2c3da..1634c48 100644 --- a/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/TestFormat.java +++ b/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/TestFormat.java @@ -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() { diff --git a/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/TestIMAPAuhtenticators.java b/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/TestIMAPAuhtenticators.java index aa532e3..070f175 100644 --- a/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/TestIMAPAuhtenticators.java +++ b/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/TestIMAPAuhtenticators.java @@ -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 { *

    * NOTE: This test should fail in December 2022 as Basic Authentication is no * longer supported by Microsoft Outlook. - * + * */ @Test @Disabled diff --git a/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/TestIMAPOutlookOAuth.java b/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/TestIMAPOutlookOAuth.java index 0bc2ff7..87c16b9 100644 --- a/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/TestIMAPOutlookOAuth.java +++ b/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/TestIMAPOutlookOAuth.java @@ -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 diff --git a/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/TestIMAPOutlookOAuthDebug.java b/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/TestIMAPOutlookOAuthDebug.java index 26ba922..8b20918 100644 --- a/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/TestIMAPOutlookOAuthDebug.java +++ b/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/TestIMAPOutlookOAuthDebug.java @@ -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 diff --git a/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/TestModelMatcher.java b/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/TestModelMatcher.java index 4de70ea..2f0d21c 100644 --- a/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/TestModelMatcher.java +++ b/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/TestModelMatcher.java @@ -8,9 +8,9 @@ import org.junit.jupiter.api.Test; /** * This test tests the InvoicePlugin isCargosoft method - * + * * @author rsoika - * + * */ public class TestModelMatcher { diff --git a/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/TestOPListen.java b/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/TestOPListen.java index 7ee5c7c..8bc30f4 100644 --- a/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/TestOPListen.java +++ b/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/TestOPListen.java @@ -17,9 +17,9 @@ import org.junit.jupiter.api.Test; /** * Testen der KW Berechnung - * + * * @author rsoika - * + * */ public class TestOPListen { diff --git a/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/TestPostionsnummernRegex.java b/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/TestPostionsnummernRegex.java index 3d0f3f9..b9a2db2 100644 --- a/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/TestPostionsnummernRegex.java +++ b/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/TestPostionsnummernRegex.java @@ -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)); - } + } } diff --git a/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/TestRunden.java b/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/TestRunden.java index 27f308d..30fda80 100644 --- a/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/TestRunden.java +++ b/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/TestRunden.java @@ -12,9 +12,9 @@ import org.junit.jupiter.api.Test; /** * This test generates random test data - * + * * @author rsoika - * + * */ public class TestRunden { diff --git a/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/migration/DueDateBugfix.java b/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/migration/DueDateBugfix.java index f730339..b70aa31 100644 --- a/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/migration/DueDateBugfix.java +++ b/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/migration/DueDateBugfix.java @@ -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; diff --git a/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/migration/MigrationMahnerAusziffern.java b/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/migration/MigrationMahnerAusziffern.java index 1dafb38..925a900 100644 --- a/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/migration/MigrationMahnerAusziffern.java +++ b/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/migration/MigrationMahnerAusziffern.java @@ -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 */ diff --git a/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/migration/MigrationMahnerDatev.java b/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/migration/MigrationMahnerDatev.java index c4588d7..c0ca012 100644 --- a/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/migration/MigrationMahnerDatev.java +++ b/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/migration/MigrationMahnerDatev.java @@ -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 diff --git a/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/migration/MigrationSpaceZuordnung.java b/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/migration/MigrationSpaceZuordnung.java index 9d793d0..018034d 100644 --- a/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/migration/MigrationSpaceZuordnung.java +++ b/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/migration/MigrationSpaceZuordnung.java @@ -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 diff --git a/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/rest/TestDataClient.java b/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/rest/TestDataClient.java index 4e7546c..dd93722 100644 --- a/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/rest/TestDataClient.java +++ b/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/rest/TestDataClient.java @@ -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 diff --git a/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/sepa/TestSEPATransform.java b/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/sepa/TestSEPATransform.java index 85c4970..62522a9 100644 --- a/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/sepa/TestSEPATransform.java +++ b/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/sepa/TestSEPATransform.java @@ -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(); + } + } } diff --git a/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/sepa/XSLTester.java b/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/sepa/XSLTester.java index 6950b6c..fda237c 100644 --- a/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/sepa/XSLTester.java +++ b/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/sepa/XSLTester.java @@ -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 - * + * *

    - * {@code		
    + * {@code
      * 
      *   
      *     ${basedir}/../reports
    @@ -29,64 +29,64 @@ import org.imixs.workflow.xml.XSLHandler;
      * 
      * }
      * 
    - * + * * @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); + } + } } diff --git a/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/xml/EAkteImportTester.java b/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/xml/EAkteImportTester.java index 31334de..9799829 100644 --- a/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/xml/EAkteImportTester.java +++ b/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/xml/EAkteImportTester.java @@ -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 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 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; + } } diff --git a/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/xml/InvoiceImportTester.java b/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/xml/InvoiceImportTester.java index e31794b..80231ed 100644 --- a/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/xml/InvoiceImportTester.java +++ b/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/xml/InvoiceImportTester.java @@ -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 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 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 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 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 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 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; + } } diff --git a/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/zoho/TestUpdateTokens.java b/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/zoho/TestUpdateTokens.java index a201409..1e7a144 100644 --- a/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/zoho/TestUpdateTokens.java +++ b/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/zoho/TestUpdateTokens.java @@ -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 response; + HttpResponse 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 response; + HttpResponse 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 response = httpClient.send( - request, - HttpResponse.BodyHandlers.ofString()); + HttpResponse 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 response = httpClient.send( - request, - HttpResponse.BodyHandlers.ofString()); + HttpResponse 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()); + } - } + } } diff --git a/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/zoho/test.json b/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/zoho/test.json index 831eaed..630cd66 100644 --- a/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/zoho/test.json +++ b/office-alexander-logistics-app/src/test/java/com/alexanderlogistics/zoho/test.json @@ -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" } -} \ No newline at end of file +} diff --git a/pom.xml b/pom.xml index 1f3abcc..581cf6b 100644 --- a/pom.xml +++ b/pom.xml @@ -128,8 +128,45 @@ ${project.build.sourceEncoding} - + + + net.revelc.code.formatter + formatter-maven-plugin + 2.26.0 + + + https://raw.githubusercontent.com/imixs/imixs-workflow/refs/heads/master/imixs-code-style.xml + LF + + + + validate + + format + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + 3.3.0 + + + https://raw.githubusercontent.com/imixs/imixs-workflow/master/imixs-checkstyle-8.44.xml + true + + + + verify + + check + + + + + office-alexander-logistics diff --git a/workflow/businesspartner-en-1.0.0.bpmn b/workflow/businesspartner-en-1.0.0.bpmn index 40411b3..e4b4991 100644 --- a/workflow/businesspartner-en-1.0.0.bpmn +++ b/workflow/businesspartner-en-1.0.0.bpmn @@ -74,6 +74,7 @@ event_kT136g event_jkHxJA event_zzWQSA + event_k4Ms1Q @@ -155,6 +156,7 @@ sequenceFlow_03b60w sequenceFlow_u8PLGw sequenceFlow_ZTiO7w + sequenceFlow_gIdPow @@ -719,7 +721,40 @@ + + + + + + + + + + + SOA Template + + soa_dbtr.number_$lasteventdate.xlsx + + + F6 + $lasteventdate + date + + + A7 + dbtr.name +]]> + + + + + sequenceFlow_gIdPow + + + + + @@ -1070,6 +1105,18 @@ + + + + + + + + + + + +