265 lines
12 KiB
Java
265 lines
12 KiB
Java
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 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.
|
|
* <p>
|
|
* Konkret geht es darum, das im Status 5200 in den ChildItems keine leeren
|
|
* Zeilen vorkommen dürfen.
|
|
* <p>
|
|
* Im Status 5000 muss immer der payment.type ausgewählt werden. Dies wird vom
|
|
* Plugin explizit geprüft
|
|
* <p>
|
|
* 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.
|
|
*
|
|
*
|
|
* @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 EVENT_FREIGEBEN = 20;
|
|
|
|
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";
|
|
|
|
// XX-YYY-####-###
|
|
public static final String REGEX_POSNUMER = "([A-Z]{2}-[A-Z]{3}-[0-9]{4}-[0-9]{3})";
|
|
|
|
@SuppressWarnings("unused")
|
|
private static Logger logger = Logger.getLogger(InvoicePlugin.class.getName());
|
|
|
|
/**
|
|
* Test childworkitems for empty lines
|
|
*
|
|
* @throws PluginException - if data is missing
|
|
*
|
|
**/
|
|
@Override
|
|
public ItemCollection run(ItemCollection workitem, ItemCollection event) throws PluginException {
|
|
|
|
// 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")));
|
|
|
|
// 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);
|
|
}
|
|
|
|
if (workitem.getTaskID() == TASK_ERFASSUNG || workitem.getTaskID() == TASK_SACHPRUEFUNG) {
|
|
// Buchungsperiode auf plausi prüfen
|
|
validateBuchungsperiode(workitem);
|
|
// Cargosoft Kreditorennnummer prüfen
|
|
validateCargosoftCdtrNumber(workitem);
|
|
}
|
|
}
|
|
|
|
// die prüfung der positionsnummern und Category erfolgt nur im Status
|
|
// Sachprüfung (5200) und nur beim Freigeben (20)!
|
|
if (workitem.getTaskID() == TASK_SACHPRUEFUNG && workitem.getEventID() == EVENT_FREIGEBEN) {
|
|
List<ItemCollection> childs = explodeChildList(workitem);
|
|
for (ItemCollection posItem : childs) {
|
|
if (posItem.getItemValueString("name").trim().isEmpty()) {
|
|
// throw a plugin exception - because name is missing!
|
|
throw new PluginException(InvoicePlugin.class.getName(), ERROR_MISSING_DATA,
|
|
"Eingabefehler - Die Positionsnummer in Zeile " + posItem.getItemValueString("numpos")
|
|
+ " muss aufgefüllt sein!");
|
|
}
|
|
|
|
// validate pos for regex pattern 'XX-YYY-####-###'
|
|
if (!posItem.getItemValueString("name").matches(REGEX_POSNUMER)) {
|
|
// throw a plugin exception - because name is missing!
|
|
throw new PluginException(InvoicePlugin.class.getName(), ERROR_MISSING_DATA,
|
|
"Eingabefehler - Die Positionsnummer in Zeile " + posItem.getItemValueString("numpos")
|
|
+ " muss im Format 'XX-YYY-####-###' eingeben werden!");
|
|
}
|
|
|
|
if (posItem.getItemValueString("category").trim().isEmpty()) {
|
|
// throw a plugin exception - because name is missing!
|
|
throw new PluginException(InvoicePlugin.class.getName(), ERROR_MISSING_DATA,
|
|
"Eingabefehler - Die Leistungsart in Zeile " + posItem.getItemValueString("numpos")
|
|
+ " muss aufgefüllt sein!");
|
|
}
|
|
|
|
if (posItem.getItemValueString("tax").trim().isEmpty()) {
|
|
// throw a plugin exception - because name is missing!
|
|
throw new PluginException(InvoicePlugin.class.getName(), ERROR_MISSING_DATA,
|
|
"Eingabefehler - Die Steuer in Zeile " + posItem.getItemValueString("numpos")
|
|
+ " muss aufgefüllt sein!");
|
|
}
|
|
|
|
if (posItem.getItemValueFloat("amount") == 0) {
|
|
// throw a plugin exception - because name is missing!
|
|
throw new PluginException(InvoicePlugin.class.getName(), ERROR_MISSING_DATA,
|
|
"Eingabefehler - Der Nettobetrag in in Zeile " + posItem.getItemValueString("numpos")
|
|
+ " darf nicht 0 sein!");
|
|
}
|
|
}
|
|
}
|
|
|
|
return workitem;
|
|
}
|
|
|
|
/**
|
|
* 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(ItemCollection workitem) throws PluginException {
|
|
String period = workitem.getItemValueString("invoice.period");
|
|
// buchungsperionde nur prüfen wenn noch nicht archiviert
|
|
if (!period.isEmpty() && "workitem".equals(workitem.getType())) {
|
|
LocalDate localDate = LocalDate.now();
|
|
int year = localDate.getYear();
|
|
// build regex....
|
|
String regex = "(" + year + "|" + (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 ein gültige Buchungsperiode ein!");
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Diese Method prüft ob die "invoice.number" bereits einmal im
|
|
* Rechnugnsworkflow vergeben wurde. Falls ja wird eine PluginExcpetion
|
|
* ausgelöst.
|
|
* <p>
|
|
* Query Example:
|
|
* <p>
|
|
* <code>NOT $uniqueid:"9f72fa50-4845-41ea-b6b9-ccd518c353be" AND
|
|
txtcooperatespace:"9dba107e-f8ef-4150-a832-040d75a6eda7" AND invoice.number:"45"</code>
|
|
*
|
|
* <p>
|
|
* 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");
|
|
String invoiceNumberDuplicate = workitem.getItemValueString("invoice.number.duplicate");
|
|
|
|
// wenn keine Rechnungsnummer eingegeben wurde gehts weiter!
|
|
if (invoiceNumber.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
// rechnungseingang only workitems...
|
|
String query = "(type:workitem OR type:workitemarchive) AND ($modelversion:rechnungseingang*) AND NOT ($uniqueid:\""
|
|
+ workitem.getUniqueID() + "\") AND (invoice.number:\"" + invoiceNumber + "\")";
|
|
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 Atkion.");
|
|
}
|
|
} 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);
|
|
}
|
|
}
|
|
|
|
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 + ")";
|
|
List<ItemCollection> result = this.getWorkflowService().getDocumentService().find(query, 1, 0);
|
|
if (result == null || result.size() == 0) {
|
|
throw new PluginException(InvoicePlugin.class.getName(), ERROR_MISSING_DATA,
|
|
"Die Kreditorennummer ist nicht gültig.");
|
|
|
|
} else {
|
|
// update cargosoft crediotr name
|
|
ItemCollection cargo = result.get(0);
|
|
workitem.setItemValue("cdtr.name.cargosoft", cargo.getItemValueString("_VENDOR_Name"));
|
|
}
|
|
} catch (QueryException e) {
|
|
e.printStackTrace();
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* converts the Map List of a workitem into a List of ItemCollectons
|
|
*/
|
|
@SuppressWarnings({ "rawtypes", "unchecked" })
|
|
protected List<ItemCollection> explodeChildList(ItemCollection workitem) {
|
|
// convert current list of childItems into ItemCollection elements
|
|
ArrayList<ItemCollection> childItems = new ArrayList<ItemCollection>();
|
|
|
|
List<Object> mapOrderItems = workitem.getItemValue(CHILD_ITEM_PROPERTY);
|
|
for (Object mapOderItem : mapOrderItems) {
|
|
if (mapOderItem instanceof Map) {
|
|
ItemCollection itemCol = new ItemCollection((Map) mapOderItem);
|
|
childItems.add(itemCol);
|
|
}
|
|
}
|
|
return childItems;
|
|
}
|
|
}
|