fixes debitor analyse

This commit is contained in:
Ralph Soika 2024-11-02 15:19:20 +01:00
parent ae7b7abf8e
commit 3973e1ae91
5 changed files with 99 additions and 49 deletions

View file

@ -96,10 +96,11 @@ public class AGLAnalyticControllerDebitor implements Serializable {
// load all invoices? // load all invoices?
if (invoices == null || (!dbtrNumber.isEmpty() && !dbtrNumber.equals(dbtrNumberLast))) { if (invoices == null || (!dbtrNumber.isEmpty() && !dbtrNumber.equals(dbtrNumberLast))) {
resetStats();
event.getWorkitem().setItemValue("dbtr.number.last", dbtrNumber);
logger.info("Analyse new debitor data for : " + dbtrNumber); logger.info("Analyse new debitor data for : " + dbtrNumber);
loadRechnungen(); loadRechnungen();
event.getWorkitem().setItemValue("dbtr.number.last", dbtrNumber); chartData = buildChartData();
} }
String link = "/pages/workitems/worklist.xhtml" + "?phrase=" + getDbtNr(); String link = "/pages/workitems/worklist.xhtml" + "?phrase=" + getDbtNr();
@ -187,13 +188,15 @@ public class AGLAnalyticControllerDebitor implements Serializable {
private void loadRechnungen() { private void loadRechnungen() {
if (getDbtNr().isEmpty()) { if (getDbtNr().isEmpty()) {
invoices = new ArrayList(); invoices = new ArrayList();
stats = null;
} else { } else {
logger.info(" ├──load invoices for " + getDbtNr() + "....");
String query = "(type:workitem) AND dbtr.number:" + getDbtNr() String query = "(type:workitem) AND dbtr.number:" + getDbtNr()
+ " AND $modelversion:rechnungsausgang-*"; + " AND $modelversion:rechnungsausgang-*";
try { try {
logger.info("...debitor analysis for " + getDbtNr() + "...."); logger.info(" ├──refresh invoice stats for " + getDbtNr() + "....");
invoices = documentService.find(query, 999, 0, "$created", false); invoices = documentService.find(query, 999, 0, "$created", false);
refreshStats(); calculateStats();
} 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();
@ -202,12 +205,17 @@ public class AGLAnalyticControllerDebitor implements Serializable {
} }
private void refreshStats() { /**
* Läd die statistik daten zu einem debitor aus den aktuellen Rechnungen
*/
private void calculateStats() {
countAll = 0; countAll = 0;
countOpen = 0; countOpen = 0;
countDue = 0; countDue = 0;
countDunning = 0; countDunning = 0;
if (invoices != null) { logger.info(" ├──calculate stats for " + getDbtNr() + "....");
// do we have data?
if (invoices != null && invoices.size() > 0) {
countAll = invoices.size(); countAll = invoices.size();
for (ItemCollection invoice : invoices) { for (ItemCollection invoice : invoices) {
@ -264,15 +272,37 @@ public class AGLAnalyticControllerDebitor implements Serializable {
totalDunningCurrency1 = InvoiceUtil.round(totalDunningCurrency1); totalDunningCurrency1 = InvoiceUtil.round(totalDunningCurrency1);
totalDunningCurrency2 = InvoiceUtil.round(totalDunningCurrency2); totalDunningCurrency2 = InvoiceUtil.round(totalDunningCurrency2);
chartData = buildChartData(); } else {
// System.out.println(chartData); // reset data because no invoices are available ...
resetStats();
getDurchschnittZahlungsziel();
getDurchschnittZahlungsdauer();
} }
} }
/**
* Setzt alle Statistikwerte zurück
*/
private void resetStats() {
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;
}
private void getDurchschnittZahlungsziel() { private void getDurchschnittZahlungsziel() {
if (stats == null) { if (stats == null) {
averagePaymentDue = 0; averagePaymentDue = 0;
@ -335,19 +365,20 @@ public class AGLAnalyticControllerDebitor implements Serializable {
* Zahlungszeitspanne gruppiert nach monaten * Zahlungszeitspanne gruppiert nach monaten
* *
*/ */
public Map<String, DebitorStatistikData> getStats() { public Map<String, DebitorStatistikData> loadStats() {
if (stats != null) { if (stats != null) {
return stats; return stats;
} }
logger.info(" ├──load stats for " + getDbtNr() + "....");
// recompute // recompute
stats = new TreeMap<String, DebitorStatistikData>(); stats = new TreeMap<String, DebitorStatistikData>();
if (getDbtNr().isEmpty()) { if (getDbtNr().isEmpty()) {
return stats; return stats;
} }
// Letzen 6 Monate // Letzen 12 Monate
LocalDate endDate = LocalDate.now(); LocalDate endDate = LocalDate.now();
LocalDate startDate = endDate.minusMonths(12); LocalDate startDate = endDate.minusMonths(12);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd"); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
@ -372,7 +403,7 @@ public class AGLAnalyticControllerDebitor implements Serializable {
Date invoiceDueDate = invoice.getItemValueDate("invoice.duedate"); Date invoiceDueDate = invoice.getItemValueDate("invoice.duedate");
Date paymentDate = findPaymentDateByWorkitem(invoice); Date paymentDate = findPaymentDateByWorkitem(invoice);
if (paymentDate == null) { if (paymentDate == null) {
logger.warning("Es wurde kein Zahlungseingang gefunden"); logger.fine("Es wurde kein Zahlungseingang gefunden");
continue; continue;
} }
@ -402,6 +433,10 @@ public class AGLAnalyticControllerDebitor implements Serializable {
} }
// Step 2: Sort the list based on the alphanumeric order of the keys // Step 2: Sort the list based on the alphanumeric order of the keys
completeMissingMonths(); completeMissingMonths();
// berechne zahluntsziel durchscnitt
getDurchschnittZahlungsziel();
getDurchschnittZahlungsdauer();
return stats; return stats;
} }
@ -475,8 +510,8 @@ public class AGLAnalyticControllerDebitor implements Serializable {
*/ */
public String buildChartData() { public String buildChartData() {
getStats(); loadStats();
logger.info(" ├──build chart for " + getDbtNr() + "....");
// build a list of all lables.... // build a list of all lables....
List<String> statusLabels = new ArrayList<String>(); List<String> statusLabels = new ArrayList<String>();
@ -488,38 +523,43 @@ public class AGLAnalyticControllerDebitor implements Serializable {
String result = "{\n \"type\": \"bar\",\n \"data\": { "; String result = "{\n \"type\": \"bar\",\n \"data\": { ";
// Lables // Lables
result = result + "labels : [ "; result = result + "\"labels\" : [ ";
result = result + statusLabels.stream().collect(Collectors.joining("','", "'", "'")); result = result + statusLabels.stream().collect(Collectors.joining("\",\"", "\"", "\""));
result = result + "],"; result = result + "],";
result = result + "datasets: ["; result = result + "\"datasets\": [";
// Datasets 1 // Datasets 1
result = result + "{label: 'Days for payment',borderWidth: 1,"; result = result + "{\"label\": \"Days for payment\",\"borderWidth\": 1,";
result = result + " borderColor: [\"#3B6B82\"],"; result = result + " \"borderColor\": [\"#3B6B82\"],";
result = result + " \"backgroundColor\" : [\"#CFE9F5\"], fill: true,tension: 0.5,"; result = result + " \"backgroundColor\" : [\"#CFE9F5\"], \"fill\": true,\"tension\": 0.5,";
result = result + "data: ["; result = result + "\"data\": [";
for (Map.Entry<String, DebitorStatistikData> entry : stats.entrySet()) { for (Map.Entry<String, DebitorStatistikData> entry : stats.entrySet()) {
result = result + entry.getValue().getAverageDueDays() + ","; result = result + entry.getValue().getAverageDueDays() + ",";
} }
// cut last comma // cut last comma
result = result.substring(0, result.length() - 1); if (result.endsWith(",")) {
result = result.substring(0, result.length() - 1);
}
result = result + "]"; result = result + "]";
result = result + "}, "; result = result + "}, ";
// Datasets 2 // Datasets 2
result = result + "{label: 'Payment duration',borderWidth: 1,"; result = result + "{\"label\": \"Payment duration\",\"borderWidth\": 1,";
result = result + " borderColor: [\"#E73B65\"],\"backgroundColor\" : [\"#70B088\" ], tension: 0.5,fill: true,"; result = result
+ " \"borderColor\": [\"#E73B65\"],\"backgroundColor\" : [\"#70B088\" ], \"tension\": 0.5,\"fill\": true,";
result = result result = result
+ " trendlineLinear: { colorMin: \"red\", colorMax: \"green\", lineStyle: \"dotted\", width: 2 , projection: true },"; + " \"trendlineLinear\": { \"colorMin\": \"red\", \"colorMax\": \"green\", \"lineStyle\": \"dotted\", \"width\": 2 , \"projection\": true },";
result = result + "data: ["; result = result + "\"data\": [";
for (Map.Entry<String, DebitorStatistikData> entry : stats.entrySet()) { for (Map.Entry<String, DebitorStatistikData> entry : stats.entrySet()) {
result = result + entry.getValue().getAveragePaymentDays() + ","; result = result + entry.getValue().getAveragePaymentDays() + ",";
} }
// cut last comma // cut last comma
result = result.substring(0, result.length() - 1); if (result.endsWith(",")) {
result = result.substring(0, result.length() - 1);
}
result = result + "]"; result = result + "]";
result = result + "} "; result = result + "} ";

View file

@ -53,6 +53,22 @@ public class AnalyticController implements Serializable {
return analyticData.getItemValueString("value"); 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 Double for a given key. * Returns a analytic value as a Double for a given key.
* *

View file

@ -34,27 +34,14 @@
</div> </div>
<script type="text/javascript" <script type="text/javascript"
src="#{facesContext.externalContext.requestContextPath}/js/chartjs/chart.min.js?build=#{app.application_build_timestamp}"></script> src="#{facesContext.externalContext.requestContextPath}/js/chartjs/chart.min.js?build=#{app.application_build_timestamp}"></script>
<script type="text/javascript"> <script type="text/javascript">
/*<![CDATA[*/ /*<![CDATA[*/
$(document).ready( var ctx = document.getElementById("analytics_canvas_#{item.name}").getContext("2d");
function () { var chartData = #{ analyticController.getAsJson(item.name)};
// update the diagram new Chart(ctx, chartData);
var ctx = document.getElementById("analytics_canvas_#{item.name}").getContext("2d");
var chartData = #{ analyticController.getAsString(item.name)
};
console.log(chartData);
//jsObject = JSON.parse(chartData);
window.myBar = new Chart(ctx, chartData);
});
/*]]>*/ /*]]>*/
</script> </script>

View file

@ -44,6 +44,12 @@
* Hilfsmethode die nur fur Ajax Events in JSF render verwendet wird * Hilfsmethode die nur fur Ajax Events in JSF render verwendet wird
*/ */
function ajaxUpdateDbtrSearchPart(data) { function ajaxUpdateDbtrSearchPart(data) {
var $body = $("body");
if (data.status === 'begin') {
// show ajax loader...
//imixsOfficeMain.layoutAjaxEvent(data, '#{customFormComponents.clientId}');
$body.addClass("loading");
}
if (data.status === 'success') { if (data.status === 'success') {
// add autocomplete feature to dbtr_number_part... // add autocomplete feature to dbtr_number_part...
var creditorField = $("input[data-item='dbtr_number_part']"); var creditorField = $("input[data-item='dbtr_number_part']");
@ -51,7 +57,9 @@
var dbtrName = "#{workflowController.workitem.item['dbtr.name']}"; var dbtrName = "#{workflowController.workitem.item['dbtr.name']}";
// append span with dbtr.name.... // append span with dbtr.name....
creditorField.after("<span id='dbtr-name-id' class='small'>" + dbtrName + "</p>"); creditorField.after("<span id='dbtr-name-id' class='small'>" + dbtrName + "</p>");
//$("[id$=autocomplete-resultlist-cargosoft_part]").hide();
// remove ajax loader
$body.removeClass("loading");
} }
} }
@ -81,6 +89,7 @@
<h:inputText value="#{workitem.item[item.name]}" pt:data-item="dbtr_number_part" <h:inputText value="#{workitem.item[item.name]}" pt:data-item="dbtr_number_part"
required="#{required?validationController.required:false}" rendered="#{!readonly}"> required="#{required?validationController.required:false}" rendered="#{!readonly}">
</h:inputText> </h:inputText>
<!-- hidden input to store name --> <!-- hidden input to store name -->

View file

@ -174,22 +174,20 @@ 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="4" label="Rechnungen"> <imixs-form-section columns="4" label="Rechnungen">
<item name="analytic.invoices.count.all" type="custom" path="alexander/analyze_plain" label="Alle Rechnungen:" /> <item name="analytic.invoices.count.all" type="custom" path="alexander/analyze_plain" label="Alle Rechnungen:" />
<item name="analytic.invoices.count.open" type="custom" path="alexander/analyze_plain" label="Offene Rechnungen:" /> <item name="analytic.invoices.count.open" type="custom" path="alexander/analyze_plain" label="Offene Rechnungen:" />
<item name="analytic.invoices.count.due" type="custom" path="alexander/analyze_plain" label="Fällige Rechnungen:" /> <item name="analytic.invoices.count.due" type="custom" path="alexander/analyze_plain" label="Fällige Rechnungen:" />
<item name="analytic.invoices.count.dunning" type="custom" path="alexander/analyze_plain" label="Rechnungen in Mahnung:" /> <item name="analytic.invoices.count.dunning" type="custom" path="alexander/analyze_plain" label="Rechnungen in Mahnung:" />
<item name="analytic.payment.avg.due" type="custom" path="alexander/analyze_plain" label="Average term of credit:" /> <item name="analytic.payment.avg.due" type="custom" path="alexander/analyze_plain" label="Average term of credit:" />
<item name="analytic.payment.avg.days" type="custom" path="alexander/analyze_plain" label="Average payment duration:" /> <item name="analytic.payment.avg.days" type="custom" path="alexander/analyze_plain" label="Average payment duration:" />
</imixs-form-section>
</imixs-form-section>
<imixs-form-section columns="1" label="Trend Zahlungsmoral"> <imixs-form-section columns="1" label="Trend Zahlungsmoral">
<item name="analytic.invoices.trend" type="custom" path="alexander/analyze_chart" label="Zahlungen in den letzten 6 Monaten:" /> <item name="analytic.invoices.trend" type="custom" path="alexander/analyze_chart" label="Zahlungen in den letzten 6 Monaten:" />
</imixs-form-section> </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>