package com.alexanderlogistics; import java.time.LocalDate; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.logging.Logger; import javax.inject.Inject; import org.imixs.workflow.ItemCollection; import org.imixs.workflow.engine.plugins.AbstractPlugin; import org.imixs.workflow.exceptions.PluginException; import org.imixs.workflow.exceptions.QueryException; /** * Das InvoicePlugin prüft die Eingaben auf gültigkeit, so dass diese problemlos * nach Cargosoft expoertier werden können. *
* Konkret geht es darum, das im Status 5200 in den ChildItems keine leeren * Zeilen vorkommen dürfen. *
* Im Status 5000 muss immer der payment.type ausgewählt werden. Dies wird vom * Plugin explizit geprüft *
* Das Plugin prüft auch auf doppelte Rechnungsnummern. Dabei wird Systemweit * geprüft ob die Rechnungsnummer schon einmal vorkam. Falls ja, wird eine * Warnung ausgegeben. Der Benutzer kann diese dann skippen. *
* 2.2.22 - Das Plugin erzeugt auch das Feld _img welches die Images von
* Sofortüberweisung, Mahnung und Ablehnung anzeigt.
*
*
* @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 String ITEM_INVOICE_PERIOD = "invoice.period";
public static final String CHILD_ITEM_PROPERTY = "_ChildItems";
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";
// XX-YYY-####-###
public static final String REGEX_POSNUMER = "([A-Z]{2}-[A-Z]{3}-[0-9]{4}-[0-9]{3})";
private static Logger logger = Logger.getLogger(InvoicePlugin.class.getName());
@Inject
KreditorService kreditorService;
/**
* Test childworkitems for empty lines
*
* @throws PluginException - if data is missing
*
**/
@Override
public ItemCollection run(ItemCollection workitem, ItemCollection event) throws PluginException {
updateImg(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 isCargoRechnung = ("Rechnungseingang".equals(workitem.getWorkflowGroup()));
// 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!
throw new PluginException(InvoicePlugin.class.getName(), ERROR_MISSING_DATA,
"Eingabefehler - Bitte wählen Sie eine Zahlungsart aus!");
}
// 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".equals(workitem.getType())) {
validateBuchungsperiode(workitem.getItemValueString(ITEM_INVOICE_PERIOD));
}
// Cargosoft Kreditorennnummer prüfen
if (isCargoRechnung) {
validateCargosoftCdtrNumber(workitem);
}
}
}
// die prüfung der positionsnummern und Category erfolgt nur im Status
// Sachprüfung (5200) und nur beim Freigeben (20)!
if (isCargoRechnung
&& ((workitem.getTaskID() == TASK_SACHPRUEFUNG || workitem.getTaskID() == TASK_SACHPRUEFUNG_DELEGATION)
&& workitem.getEventID() == EVENT_FREIGEBEN)) {
List
* Query Example:
*
*
* 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");
// 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 {
// set _invoicenumber_duplicate - dadurch wird die warnmeldung ausgegeben und
// der Vorgang nicht weitergeleitet
workitem.replaceItemValue("invoice.number.duplicate", invoiceNumber);
throw new PluginException(InvoicePlugin.class.getName(), ERROR_DUPPLICATE_INVOICE_NUMBER,
"ACHTUNG: Die Rechnungsnummer wurde bereits gebucht. Bitte prüfen Sie den Beleg. Sollte die Belegnummer korrekt sein, wiederholen Sie die Aktion.");
}
} 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 {
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;
}
// wenn bereits eine Überneahme angedroht wurde dann übernehemen!
if (!overtakeIBAN.isEmpty()) {
kreditorService.addNewIBANBIC(cdtrNumber, iban, bic);
workitem.setItemValue("ibanbic.overtake","");
return;
}
// search the kreditor
ItemCollection cdtr = kreditorService.findCreditor(cdtrNumber);
if (cdtr != null) {
// Prüfen ob wir die IBAN schon kennen
if (iban.equals(cdtr.getItemValueString("cdtr.iban")) && bic.equals(cdtr.getItemValueString("cdtr.bic"))) {
return;
}
if (iban.equals(cdtr.getItemValueString("cdtr.iban2"))
&& bic.equals(cdtr.getItemValueString("cdtr.bic2"))) {
return;
}
if (iban.equals(cdtr.getItemValueString("cdtr.iban3"))
&& bic.equals(cdtr.getItemValueString("cdtr.bic3"))) {
return;
}
if (iban.equals(cdtr.getItemValueString("cdtr.iban4"))
&& bic.equals(cdtr.getItemValueString("cdtr.bic4"))) {
return;
}
// OK - scheinbar ist diese IBAN/BIC nicht bekannt. Also fragen wir mal nach....
workitem.setItemValue("ibanbic.overtake", iban + bic);
throw new PluginException(InvoicePlugin.class.getName(), ERROR_NEW_IBANBIC,
"Die von Ihnen erfasste IBAN/BIC ist noch nicht bekannt. Sollte die Eingabe korrekt sein, wiederholen Sie die Aktion.");
}
}
/**
* 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;
}
// 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
";
}
// 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);
/*
* for (String entry: eventLog) { // sofortüberweisung if (entry.contains(".96")
* && !img.contains("100.png")) {
* img=img+"
"; } // mahnen if
* (entry.contains(".95") && !img.contains("074.png")) {
* img=img+"
"; } // ablehnen if
* (entry.contains("5100.90") && !img.contains("031.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 {
// 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!
throw new PluginException(InvoicePlugin.class.getName(), ERROR_MISSING_DATA,
"Eingabefehler - Bitte geben Sie eine gültige Buchungsperiode ein! Prüfen Sie auch die Spalte BP in der Positionstabelle.");
}
}
}
/**
* Diese Method prüft ob die "invoice.number" bereits einmal im
* Rechnugnsworkflow vergeben wurde. Falls ja wird eine PluginExcpetion
* ausgelöst.
* NOT $uniqueid:"9f72fa50-4845-41ea-b6b9-ccd518c353be" AND
txtcooperatespace:"9dba107e-f8ef-4150-a832-040d75a6eda7" AND invoice.number:"45"
*
*