office-alexander-logistics/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/AGLAnalyticControllerSteuer.java
2024-10-09 00:12:09 +02:00

517 lines
16 KiB
Java

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