analyse debitor
This commit is contained in:
parent
8a0e3b1e26
commit
ec2ee8a8e9
3 changed files with 435 additions and 70 deletions
|
|
@ -1,9 +1,23 @@
|
||||||
package com.alexanderlogistics;
|
package com.alexanderlogistics;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.text.DecimalFormat;
|
||||||
|
import java.text.DecimalFormatSymbols;
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.YearMonth;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.TreeMap;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.imixs.marty.team.TeamService;
|
import org.imixs.marty.team.TeamService;
|
||||||
import org.imixs.workflow.ItemCollection;
|
import org.imixs.workflow.ItemCollection;
|
||||||
|
|
@ -26,7 +40,6 @@ import jakarta.inject.Named;
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@Named
|
@Named
|
||||||
// @RequestScoped
|
|
||||||
@ConversationScoped
|
@ConversationScoped
|
||||||
public class AGLAnalyticControllerDebitor implements Serializable {
|
public class AGLAnalyticControllerDebitor implements Serializable {
|
||||||
|
|
||||||
|
|
@ -36,13 +49,36 @@ public class AGLAnalyticControllerDebitor implements Serializable {
|
||||||
@Inject
|
@Inject
|
||||||
protected DocumentService documentService;
|
protected DocumentService documentService;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
protected AGLConfigController aglConfigController;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
protected WorkflowController workflowController;
|
protected WorkflowController workflowController;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
TeamService teamService;
|
TeamService teamService;
|
||||||
|
|
||||||
|
TreeMap<String, DebitorStatistikData> stats = null;
|
||||||
|
|
||||||
List<ItemCollection> invoices = null;
|
List<ItemCollection> 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;
|
||||||
|
|
||||||
|
String chartData = "";
|
||||||
|
|
||||||
public void onEvent(@Observes AnalyticEvent event) {
|
public void onEvent(@Observes AnalyticEvent event) {
|
||||||
|
|
||||||
|
|
@ -68,28 +104,62 @@ public class AGLAnalyticControllerDebitor implements Serializable {
|
||||||
+ "&workflowgroup=Steuerbescheid";
|
+ "&workflowgroup=Steuerbescheid";
|
||||||
|
|
||||||
if ("analytic.invoices.count.all".equals(event.getKey())) {
|
if ("analytic.invoices.count.all".equals(event.getKey())) {
|
||||||
event.setValue("" + getRechnungen());
|
event.setValue("" + countAll);
|
||||||
event.setLabel("");
|
event.setLabel(formatTotals(totalAllCurrency1, totalAllCurrency2));
|
||||||
event.setDescription("Total invoices");
|
event.setDescription("Total invoices");
|
||||||
event.getWorkitem().setItemValue("invoices.total", event.getValue());
|
event.getWorkitem().setItemValue("invoices.total", event.getValue());
|
||||||
event.setLink(link);
|
event.setLink(link);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("analytic.invoices.count.open".equals(event.getKey())) {
|
if ("analytic.invoices.count.open".equals(event.getKey())) {
|
||||||
event.setValue("" + getRechnungenOffen());
|
event.setValue("" + countOpen);
|
||||||
event.setLabel("");
|
event.setLabel(formatTotals(totalOpenCurrency1, totalOpenCurrency2));
|
||||||
event.setDescription("Total invoices not yet in due");
|
event.setDescription("Total invoices not yet in due");
|
||||||
event.getWorkitem().setItemValue("invoices.total.open", event.getValue());
|
event.getWorkitem().setItemValue("invoices.total.open", event.getValue());
|
||||||
event.setLink(link);
|
event.setLink(link);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("analytic.invoices.count.due".equals(event.getKey())) {
|
if ("analytic.invoices.count.due".equals(event.getKey())) {
|
||||||
event.setValue("" + getRechnungenFaellig());
|
event.setValue("" + countDue);
|
||||||
event.setLabel("");
|
event.setLabel(formatTotals(totalDueCurrency1, totalDueCurrency2));
|
||||||
event.setDescription("Total invoices in due");
|
event.setDescription("Total invoices in due");
|
||||||
event.getWorkitem().setItemValue("invoices.total.due", event.getValue());
|
event.getWorkitem().setItemValue("invoices.total.due", event.getValue());
|
||||||
event.setLink(link);
|
event.setLink(link);
|
||||||
}
|
}
|
||||||
|
if ("analytic.invoices.count.dunning".equals(event.getKey())) {
|
||||||
|
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.invoices.trend".equals(event.getKey())) {
|
||||||
|
event.setValue(chartData);
|
||||||
|
event.setLabel("Zahlung Tage");
|
||||||
|
event.setDescription("Zahlungsmoral nach KW");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formatiert zwei währungen untereinander
|
||||||
|
*
|
||||||
|
* @param totalCurrency1
|
||||||
|
* @param totalCurrency2
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private String formatTotals(double totalCurrency1, double totalCurrency2) {
|
||||||
|
|
||||||
|
List<String> currencies = aglConfigController.getWorkitem().getItemValueList("currency.out", String.class);
|
||||||
|
if (currencies.size() < 2) {
|
||||||
|
currencies.add(currencies.get(0));
|
||||||
|
}
|
||||||
|
String label = "<span class=\"pull-right\">" + formatCurrency(totalCurrency1) + " " + currencies.get(0)
|
||||||
|
+ "</span>";
|
||||||
|
label = label + "</br><span class=\"pull-right\">" +
|
||||||
|
formatCurrency(totalCurrency2) + " " + currencies.get(1) + "</span>";
|
||||||
|
|
||||||
|
return label;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadRechnungen() {
|
private void loadRechnungen() {
|
||||||
|
|
@ -100,8 +170,8 @@ public class AGLAnalyticControllerDebitor implements Serializable {
|
||||||
+ " AND $modelversion:rechnungsausgang-*";
|
+ " AND $modelversion:rechnungsausgang-*";
|
||||||
try {
|
try {
|
||||||
logger.info("...debitor analysis for " + getDbtNr() + "....");
|
logger.info("...debitor analysis for " + getDbtNr() + "....");
|
||||||
|
|
||||||
invoices = documentService.find(query, 999, 0, "$created", false);
|
invoices = documentService.find(query, 999, 0, "$created", false);
|
||||||
|
refreshStats();
|
||||||
} catch (QueryException e) {
|
} catch (QueryException e) {
|
||||||
logger.warning("Failed to query invoices: " + query + " - Error: " + e.getMessage());
|
logger.warning("Failed to query invoices: " + query + " - Error: " + e.getMessage());
|
||||||
invoices = new ArrayList();
|
invoices = new ArrayList();
|
||||||
|
|
@ -110,67 +180,87 @@ public class AGLAnalyticControllerDebitor implements Serializable {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private void refreshStats() {
|
||||||
* Berechnet die Anzahl aller Rechnungen
|
countAll = 0;
|
||||||
*
|
countOpen = 0;
|
||||||
* @return
|
countDue = 0;
|
||||||
*/
|
countDunning = 0;
|
||||||
public int getRechnungen() {
|
|
||||||
int result = 0;
|
|
||||||
if (invoices != null) {
|
if (invoices != null) {
|
||||||
result = invoices.size();
|
countAll = invoices.size();
|
||||||
}
|
|
||||||
return result;
|
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();
|
||||||
* Berechnet die Anzahl aller noch nicht fälligen rechnungen
|
|
||||||
*
|
// Open
|
||||||
* @return
|
if (task >= 5000 && task <= 5099) {
|
||||||
*/
|
countOpen++;
|
||||||
public int getRechnungenOffen() {
|
// währung 1 oder 2?
|
||||||
// int result = 0;
|
if (invoice.getItemValueDouble("invoice.rate") == 0) {
|
||||||
// if (getDbtNr().isEmpty()) {
|
totalOpenCurrency1 = totalOpenCurrency1 + invoice.getItemValueDouble("invoice.saldo");
|
||||||
// return 0;
|
} else {
|
||||||
// }
|
totalOpenCurrency2 = totalOpenCurrency2 + invoice.getItemValueDouble("invoice.saldo");
|
||||||
// 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;
|
|
||||||
int result = 0;
|
|
||||||
if (invoices != null) {
|
|
||||||
result = invoices.size();
|
|
||||||
}
|
}
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Due
|
||||||
* Berechnet die Anzahl aller fälligen rechnungen
|
if (task >= 5100 && task <= 5199) {
|
||||||
*
|
countDue++;
|
||||||
* @return
|
// währung 1 oder 2?
|
||||||
*/
|
if (invoice.getItemValueDouble("invoice.rate") == 0) {
|
||||||
public int getRechnungenFaellig() {
|
totalDueCurrency1 = totalDueCurrency1 + invoice.getItemValueDouble("invoice.saldo");
|
||||||
// int result = 0;
|
} else {
|
||||||
// if (getDbtNr().isEmpty()) {
|
totalDueCurrency2 = totalDueCurrency2 + invoice.getItemValueDouble("invoice.saldo");
|
||||||
// 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;
|
|
||||||
int result = 0;
|
|
||||||
if (invoices != null) {
|
|
||||||
result = invoices.size();
|
|
||||||
}
|
}
|
||||||
return result;
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
chartData = buildChartData();
|
||||||
|
System.out.println(chartData);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
// %, => local-specific thousands separator
|
||||||
|
// .2f => positions after decimal point
|
||||||
|
// return String.format("%,.2f", value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -186,4 +276,267 @@ public class AGLAnalyticControllerDebitor implements Serializable {
|
||||||
return dbtNr;
|
return dbtNr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sucht alle Rechnugnen aus einem Zeitraum und sammelt Zahlungsziel und
|
||||||
|
* Zahlungszeitspanne gruppiert nach monaten
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public Map<String, DebitorStatistikData> getStats() {
|
||||||
|
|
||||||
|
if (stats != null) {
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
// recompute
|
||||||
|
stats = new TreeMap<String, DebitorStatistikData>();
|
||||||
|
if (getDbtNr().isEmpty()) {
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Letzen 6 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<ItemCollection> 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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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<ItemCollection> 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
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
{
|
||||||
|
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
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String buildChartData() {
|
||||||
|
|
||||||
|
getStats();
|
||||||
|
|
||||||
|
// build a list of all lables....
|
||||||
|
List<String> statusLabels = new ArrayList<String>();
|
||||||
|
|
||||||
|
Set<String> 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: 'Zahlungsziel',borderWidth: 1,";
|
||||||
|
result = result + " borderColor: [\"#3B6B82\"],";
|
||||||
|
result = result + " \"backgroundColor\" : [\"#CFE9F5\"], fill: true,tension: 0.5,";
|
||||||
|
|
||||||
|
result = result + "data: [";
|
||||||
|
for (Map.Entry<String, DebitorStatistikData> 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<String, DebitorStatistikData> 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 + "] }";
|
||||||
|
|
||||||
|
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<LocalDate> dates = new ArrayList<>();
|
||||||
|
Set<String> 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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,9 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="analytic-card-content">#{analyticController.getLabel(item.name)}</div>
|
<div class="analytic-card-content">
|
||||||
|
<h:outputText escape="false" value="#{analyticController.getLabel(item.name)}" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="analytic-card-footer">
|
<div class="analytic-card-footer">
|
||||||
|
|
|
||||||
|
|
@ -174,13 +174,18 @@ th { font-weight: bold;}
|
||||||
<imixs-form-section columns="2">
|
<imixs-form-section columns="2">
|
||||||
<item name="dbtr.number" type="custom" path="alexander/debitor_search" required="true" label="Debitor:" />
|
<item name="dbtr.number" type="custom" path="alexander/debitor_search" required="true" label="Debitor:" />
|
||||||
</imixs-form-section>
|
</imixs-form-section>
|
||||||
<imixs-form-section columns="3" label="Invoices">
|
<imixs-form-section columns="4" label="Invoices">
|
||||||
<item name="analytic.invoices.count.all" type="custom" path="alexander/analyze_plain" label="All Invoices:" />
|
<item name="analytic.invoices.count.all" type="custom" path="alexander/analyze_plain" label="All Invoices:" />
|
||||||
<item name="analytic.invoices.count.open" type="custom" path="alexander/analyze_plain" label="Open Invoices open:" />
|
<item name="analytic.invoices.count.open" type="custom" path="alexander/analyze_plain" label="Open Invoices open:" />
|
||||||
<item name="analytic.invoices.count.due" type="custom" path="alexander/analyze_plain" label="Invoices in due:" />
|
<item name="analytic.invoices.count.due" type="custom" path="alexander/analyze_plain" label="Invoices in due:" />
|
||||||
|
<item name="analytic.invoices.count.dunning" type="custom" path="alexander/analyze_plain" label="Invoices in dunning:" />
|
||||||
</imixs-form-section>
|
</imixs-form-section>
|
||||||
|
|
||||||
|
<imixs-form-section columns="1" label="Trend Zahlungsmoral">
|
||||||
|
<item name="analytic.invoices.trend" type="custom" path="alexander/analyze_chart" label="Zahlungen innerhalb der letzten 6 Monate:" />
|
||||||
|
</imixs-form-section>
|
||||||
|
|
||||||
|
|
||||||
</imixs-form>]]></bpmn2:documentation>
|
</imixs-form>]]></bpmn2:documentation>
|
||||||
<bpmn2:dataState id="DataState_1"/>
|
<bpmn2:dataState id="DataState_1"/>
|
||||||
</bpmn2:dataObject>
|
</bpmn2:dataObject>
|
||||||
|
|
@ -340,15 +345,20 @@ th { font-weight: bold;}
|
||||||
<bpmn2:dataObject id="dataObject_00mX6w" name="Form">
|
<bpmn2:dataObject id="dataObject_00mX6w" name="Form">
|
||||||
<bpmn2:documentation id="documentation_lUG0qw"><![CDATA[<imixs-form>
|
<bpmn2:documentation id="documentation_lUG0qw"><![CDATA[<imixs-form>
|
||||||
<imixs-form-section columns="2">
|
<imixs-form-section columns="2">
|
||||||
<item name="dbtr.name" type="text" required="false" label="Debitor:" />
|
<item name="dbtr.number" type="custom" path="alexander/debitor_search" required="true" label="Debitor:" />
|
||||||
</imixs-form-section>
|
</imixs-form-section>
|
||||||
<imixs-form-section columns="3" label="Invoices">
|
<imixs-form-section columns="4" label="Invoices">
|
||||||
<item name="analytic.invoices.count.all" type="custom" path="alexander/analyze_plain" label="All Invoices:" />
|
<item name="analytic.invoices.count.all" type="custom" path="alexander/analyze_plain" label="All Invoices:" />
|
||||||
<item name="analytic.invoices.count.open" type="custom" path="alexander/analyze_plain" label="Open Invoices open:" />
|
<item name="analytic.invoices.count.open" type="custom" path="alexander/analyze_plain" label="Open Invoices open:" />
|
||||||
<item name="analytic.invoices.count.due" type="custom" path="alexander/analyze_plain" label="Invoices in due:" />
|
<item name="analytic.invoices.count.due" type="custom" path="alexander/analyze_plain" label="Invoices in due:" />
|
||||||
|
<item name="analytic.invoices.count.dunning" type="custom" path="alexander/analyze_plain" label="Invoices in dunning:" />
|
||||||
</imixs-form-section>
|
</imixs-form-section>
|
||||||
|
|
||||||
|
<imixs-form-section columns="1" label="Trend Zahlungsmoral">
|
||||||
|
<item name="analytic.invoices.trend" type="custom" path="alexander/analyze_chart" label="Zahlungen / Monat:" />
|
||||||
|
</imixs-form-section>
|
||||||
|
|
||||||
|
|
||||||
</imixs-form>]]></bpmn2:documentation>
|
</imixs-form>]]></bpmn2:documentation>
|
||||||
<bpmn2:dataState id="dataState_n87Eag"/>
|
<bpmn2:dataState id="dataState_n87Eag"/>
|
||||||
</bpmn2:dataObject>
|
</bpmn2:dataObject>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue