office-alexander-logistics/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/DebitorStatistikController.java
2023-08-03 12:05:05 +02:00

578 lines
16 KiB
Java

package com.alexanderlogistics;
import java.io.Serializable;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import javax.annotation.PostConstruct;
import javax.enterprise.context.ConversationScoped;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
import javax.inject.Named;
import org.imixs.workflow.ItemCollection;
import org.imixs.workflow.engine.DocumentService;
import org.imixs.workflow.exceptions.PluginException;
import org.imixs.workflow.exceptions.QueryException;
import org.imixs.workflow.faces.data.WorkflowController;
import org.imixs.workflow.office.views.SearchController;
/**
* Der InvoiceAnalyseController dient zur Auswertung der Sachprüfungs WOrklfow
* Schritte. Die Analyse Seite ist büer das Admin Menü erreichbar.
*
* @author rsoika
*
*/
@Named
@ConversationScoped
// @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<String, DebitorStatistikData> stats = null;
@Inject
protected WorkflowController workflowController;
@Inject
protected SearchController searchController;
@Inject
protected KreditorDebitorService kreditorDebitorService;
/**
* 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<String, String> paramMap = fc.getExternalContext().getRequestParameterMap();
String id = paramMap.get("dbtr.number");
if (id != null && !id.isEmpty()) {
workflowController.getWorkitem().setItemValue("dbtr.number", id);
try {
ItemCollection dbtr = kreditorDebitorService.findDebitor(id);
if (dbtr != null) {
workflowController.getWorkitem().setItemValue("dbtr.name", dbtr.getItemValueString("_vendor_name"));
}
} catch (PluginException e) {
// e.printStackTrace();
}
}
}
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<String, DebitorStatistikData> 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<String, DebitorStatistikData> 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<String, DebitorStatistikData> getStats() {
if (stats != null) {
return stats;
}
// recompute
stats = new TreeMap<String, DebitorStatistikData>();
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<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;
}
/**
* 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);
}
/**
* 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<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;
}
/**
* 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<String> 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
*
*
* <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 = "{";
// 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 + "] }";
return result;
}
}