474 lines
18 KiB
Java
474 lines
18 KiB
Java
/*******************************************************************************
|
|
* Imixs Workflow
|
|
* Copyright (C) 2001, 2011 Imixs Software Solutions GmbH,
|
|
* http://www.imixs.com
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You can receive a copy of the GNU General Public
|
|
* License at http://www.gnu.org/licenses/gpl.html
|
|
*
|
|
* Project:
|
|
* http://www.imixs.org
|
|
* http://java.net/projects/imixs-workflow
|
|
*
|
|
* Contributors:
|
|
* Imixs Software Solutions GmbH - initial API and implementation
|
|
* Ralph Soika - Software Developer
|
|
*******************************************************************************/
|
|
|
|
package com.alexanderlogistics;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.Collections;
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Optional;
|
|
import java.util.logging.Logger;
|
|
|
|
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
|
import org.imixs.workflow.ItemCollection;
|
|
import org.imixs.workflow.ItemCollectionComparator;
|
|
import org.imixs.workflow.engine.DocumentService;
|
|
import org.imixs.workflow.engine.WorkflowService;
|
|
import org.imixs.workflow.engine.index.SchemaService;
|
|
import org.imixs.workflow.exceptions.AccessDeniedException;
|
|
import org.imixs.workflow.exceptions.ModelException;
|
|
import org.imixs.workflow.exceptions.PluginException;
|
|
import org.imixs.workflow.exceptions.ProcessingErrorException;
|
|
import org.imixs.workflow.exceptions.QueryException;
|
|
|
|
import jakarta.annotation.PostConstruct;
|
|
import jakarta.annotation.security.DeclareRoles;
|
|
import jakarta.annotation.security.RolesAllowed;
|
|
import jakarta.annotation.security.RunAs;
|
|
import jakarta.ejb.Stateless;
|
|
import jakarta.inject.Inject;
|
|
|
|
/**
|
|
* Der BusinessPartnerService stellt Methoden für den Zugriff auf Business
|
|
* Partner bereit.
|
|
*
|
|
* @author rsoika
|
|
*
|
|
*/
|
|
|
|
@DeclareRoles({ "org.imixs.ACCESSLEVEL.NOACCESS", "org.imixs.ACCESSLEVEL.READERACCESS",
|
|
"org.imixs.ACCESSLEVEL.AUTHORACCESS", "org.imixs.ACCESSLEVEL.EDITORACCESS",
|
|
"org.imixs.ACCESSLEVEL.MANAGERACCESS" })
|
|
@RolesAllowed({ "org.imixs.ACCESSLEVEL.NOACCESS", "org.imixs.ACCESSLEVEL.READERACCESS",
|
|
"org.imixs.ACCESSLEVEL.AUTHORACCESS", "org.imixs.ACCESSLEVEL.EDITORACCESS",
|
|
"org.imixs.ACCESSLEVEL.MANAGERACCESS" })
|
|
@Stateless
|
|
@RunAs("org.imixs.ACCESSLEVEL.MANAGERACCESS")
|
|
public class BusinessPartnerService {
|
|
|
|
public static final int MAX_SEARCH_RESULT = 100;
|
|
public static final String ITEM_IBAN_LIST = "partner.bank.list";
|
|
public static final String WORKFLOW_SERVICE_ENDPOINT = "workflow.service.endpoint";
|
|
public static final String WORKFLOW_SERVICE_USER = "workflow.service.user";
|
|
public static final String WORKFLOW_SERVICE_PASSWORD = "workflow.service.password";
|
|
|
|
public static final String ERROR_BUSINESSPARTNER_MISSING = "ERROR_BUSINESSPARTNER_MISSING";
|
|
public static final String ERROR_BUSINESSPARTNER_LOCKED = "ERROR_BUSINESSPARTNER_LOCKED";
|
|
public static final String ERROR_BUSINESSPARTNER_VERIFICATION = "ERROR_BUSINESSPARTNER_VERIFICATION";
|
|
|
|
public static final int TASK_ACTIVE = 1100;
|
|
public static final int TASK_VERIFICATION = 1300;
|
|
public static final int TASK_INACTIVE = 1700;
|
|
public static final int TASK_LOCKED = 1800;
|
|
public static final int EVENT_ACTIVATE = 210;
|
|
|
|
private static Logger logger = Logger.getLogger(BusinessPartnerService.class.getName());
|
|
|
|
@Inject
|
|
@ConfigProperty(name = WORKFLOW_SERVICE_ENDPOINT)
|
|
Optional<String> workflowServiceEndpoint;
|
|
|
|
@Inject
|
|
@ConfigProperty(name = WORKFLOW_SERVICE_USER)
|
|
Optional<String> workflowServiceUser;
|
|
|
|
@Inject
|
|
@ConfigProperty(name = WORKFLOW_SERVICE_PASSWORD)
|
|
Optional<String> workflowServicePassword;
|
|
|
|
@Inject
|
|
DocumentService documentService;
|
|
|
|
@Inject
|
|
WorkflowService workflowService;
|
|
|
|
@Inject
|
|
SchemaService schemaService;
|
|
|
|
/**
|
|
* Initialize the rest clients
|
|
*/
|
|
@PostConstruct
|
|
public void init() {
|
|
|
|
}
|
|
|
|
/**
|
|
* Diese Methode aktualisiert das business Partner object. Sie wird von den
|
|
* Invoice Plugins genutzt. Die Methode wirft eine PluginException falls das
|
|
* BusinessPartner Objekt gesperrt oder in verification ist!
|
|
*
|
|
* Gleichzeitig wird bei Rechnungen in Abhängigkeit der Rechnungsart die
|
|
* dbtr./cdtr. items sowie mail und country aktualisiert.
|
|
*
|
|
* @param partnerID
|
|
* @param workitem
|
|
* @return BusinessPartner Object
|
|
* @throws PluginException
|
|
*/
|
|
public ItemCollection updateBusinessPartnerData(ItemCollection workitem) throws PluginException {
|
|
String partnerID = workitem.getItemValueString("partner.id").trim();
|
|
// Migration wenn keine partner.id existier!
|
|
if (partnerID.isEmpty()) {
|
|
if (InvoiceUtil.isCreditorInvoice(workitem)) {
|
|
partnerID = InvoiceUtil.buildBPID(workitem.getItemValueString("cdtr.number"));
|
|
}
|
|
if (InvoiceUtil.isDebitorInvoice(workitem)) {
|
|
partnerID = InvoiceUtil.buildBPID(workitem.getItemValueString("dbtr.number"));
|
|
}
|
|
workitem.setItemValue("partner.id", partnerID);
|
|
}
|
|
|
|
if (partnerID == null || partnerID.isEmpty()) {
|
|
return null;
|
|
}
|
|
|
|
// load Business Partner
|
|
//
|
|
// DO NOT CHANGE THIS LOGIC!
|
|
//
|
|
ItemCollection businessPartner = getBusinessPartnerByID(partnerID);
|
|
if (businessPartner == null) {
|
|
// Falls jemand eine ungültige Partner ID eingetippt hat und einfach auf weiter
|
|
// klickt , müssen wir genau prüfen
|
|
// Wir prüfen ob es eine Zahl war - dann versuchen wir nochmal mit dem Prefix BP
|
|
// zu suchen....
|
|
// Beispiel: 13342
|
|
try {
|
|
Integer.parseInt(partnerID);
|
|
// it is an int - get last 4 digits
|
|
partnerID = "BP" + partnerID.substring(Math.max(0, partnerID.length() - 4));
|
|
workitem.setItemValue("partner.id", partnerID);
|
|
businessPartner = getBusinessPartnerByID(partnerID);
|
|
} catch (NumberFormatException e) {
|
|
/* not an int */
|
|
}
|
|
if (businessPartner == null) {
|
|
workitem.removeItem("partner.id");
|
|
logger.warning("Business Partner for Partner ID " + partnerID + " not found!");
|
|
return null;
|
|
}
|
|
|
|
}
|
|
// because the method can be called multiple times within one worklfow
|
|
// processing cycle
|
|
// we need to load the object within the current transaction context
|
|
businessPartner = documentService.load(businessPartner.getUniqueID());
|
|
if (businessPartner != null) {
|
|
// update partner id data
|
|
workitem.setItemValue("partner.id", partnerID);
|
|
workitem.setItemValue("partner.name", businessPartner.getItemValueString("partner.name"));
|
|
logger.info("check invoice type....");
|
|
// Update Invoice Partner Meta Data
|
|
if (InvoiceUtil.isDebitorInvoice(workitem)) {
|
|
logger.info("...is debitor invoice");
|
|
workitem.setItemValue("dbtr.number", businessPartner.getItemValueString("dbtr.number"));
|
|
workitem.setItemValue("dbtr.name", businessPartner.getItemValueString("partner.name"));
|
|
workitem.setItemValue("dbtr.mail", businessPartner.getItemValueString("dbtr.mail"));
|
|
workitem.setItemValue("invoice.country", businessPartner.getItemValueString("_vendor_country"));
|
|
}
|
|
logger.info("...is creditor invoice");
|
|
if (InvoiceUtil.isCreditorInvoice(workitem)) {
|
|
workitem.setItemValue("cdtr.name", businessPartner.getItemValueString("partner.name"));
|
|
workitem.setItemValue("cdtr.mail", businessPartner.getItemValueString("cdtr.mail"));
|
|
workitem.setItemValue("invoice.country", businessPartner.getItemValueString("_vendor_country"));
|
|
|
|
/*
|
|
* Sonderfall - Frau Mahner 9.10.2025
|
|
* Wenn es eine Gutschrift ist und man über das Custom part 'zahlungsart.xthml'
|
|
* ein alternatives Konto gewählt hat muss dieses gewinnen!
|
|
* Ansonsten nehmen wir die cdtr.number
|
|
*/
|
|
if ("credit".equals(workitem.getItemValueString("payment.type"))) {
|
|
String bookingAccount = workitem.getItemValueString("payment.booking.account");
|
|
if (!bookingAccount.isBlank()) {
|
|
workitem.setItemValue("cdtr.number", bookingAccount);
|
|
} else {
|
|
workitem.setItemValue("cdtr.number", businessPartner.getItemValueString("cdtr.number"));
|
|
}
|
|
} else {
|
|
// default - cdtr.number
|
|
workitem.setItemValue("cdtr.number", businessPartner.getItemValueString("cdtr.number"));
|
|
}
|
|
}
|
|
|
|
// Validate Business Partner Status!
|
|
if (businessPartner.getTaskID() == TASK_VERIFICATION) {
|
|
throw new PluginException(InvoicePlugin.class.getName(), ERROR_BUSINESSPARTNER_VERIFICATION,
|
|
"Businesspartner is in verification. Invoice can't be processed!");
|
|
}
|
|
if (businessPartner.getTaskID() == TASK_LOCKED) {
|
|
throw new PluginException(InvoicePlugin.class.getName(), ERROR_BUSINESSPARTNER_LOCKED,
|
|
"Businesspartner is locked. Invoice can't be processed!");
|
|
}
|
|
// update business partner status...
|
|
try {
|
|
if (businessPartner.getTaskID() == TASK_INACTIVE) {
|
|
// Workitem erneut aktivieren
|
|
businessPartner.event(EVENT_ACTIVATE);
|
|
workflowService.processWorkItem(businessPartner);
|
|
} else {
|
|
// workItem einfach nur speichern!
|
|
documentService.save(businessPartner);
|
|
}
|
|
} catch (PluginException | ProcessingErrorException | AccessDeniedException | ModelException e) {
|
|
logger.warning("Failed to update BusinessPartner object '" + partnerID + "'!");
|
|
}
|
|
return businessPartner;
|
|
} else {
|
|
logger.warning("BusinessPartner '" + partnerID + "' not found!");
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the stubs of all outgoing invoices
|
|
*
|
|
* @param _dbtrNumber
|
|
* @return
|
|
*/
|
|
public List<ItemCollection> findOutgoingInvoices(String _dbtrNumber) {
|
|
// Aus der dbtrNummer muss das führendde D entfernt werden
|
|
String query = "(type:workitem) AND ($modelversion:rechnungsausgang-*)";
|
|
|
|
// Aus der dbtrNummer muss das führendde D entfernt werden
|
|
// wir speichern dieses zwar im Zahlungseingangs Workflow, aber eine
|
|
// Ausganksrechnung kennt das nicht
|
|
// Das ganze ist ein übles Relikt aus Cargosoft und kann nicht vermieden werden
|
|
String _dbtrNumberOld = _dbtrNumber;
|
|
if (_dbtrNumberOld.startsWith("D")) {
|
|
_dbtrNumberOld = _dbtrNumber.substring(1);
|
|
}
|
|
query = query + " AND (dbtr.number:" + _dbtrNumber + " OR dbtr.number:" + _dbtrNumberOld + ")";
|
|
|
|
try {
|
|
return documentService.find(query, 999, 0, "invoice.number", false);
|
|
} catch (QueryException e) {
|
|
logger.severe("Failed to compute outgoing invoices: " + e.getMessage());
|
|
}
|
|
|
|
return new ArrayList<>();
|
|
}
|
|
|
|
/**
|
|
* Diese Methode sucht einen Business Partner anhand seiner BPID
|
|
*
|
|
* @param bpid - business partner id phrase
|
|
* @return - matching business partner
|
|
*/
|
|
public ItemCollection getBusinessPartnerByID(String bpid) {
|
|
if (bpid == null || bpid.isEmpty()) {
|
|
return null;
|
|
}
|
|
List<ItemCollection> result;
|
|
try {
|
|
String sQuery = "(type:\"workitem\" OR type:\"workitemarchive\" ) AND ($modelversion:businesspartner-*)";
|
|
sQuery += " AND (name:" + bpid.trim() + ")";
|
|
logger.finest("SearchQuery= " + sQuery);
|
|
result = documentService.find(sQuery, 1, 0);
|
|
if (result.size() > 0) {
|
|
return result.get(0);
|
|
}
|
|
} catch (Exception e) {
|
|
logger.warning(" lucene error - " + e.getMessage());
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/*
|
|
* Prüft ob es eine D oder K Nummer ist
|
|
*/
|
|
public static boolean isCargosoftNumber(String str) {
|
|
if (str == null || str.isEmpty() || str.length() > 5) {
|
|
return false;
|
|
}
|
|
try {
|
|
Long.parseLong(str); // Versuche, den String in eine Zahl umzuwandeln
|
|
return true;
|
|
} catch (NumberFormatException e) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Diese Methode sucht Business Partner anhand einer Suchphrase
|
|
*
|
|
* @param phrase - search phrase
|
|
* @return - list of matching business partners
|
|
*/
|
|
public List<ItemCollection> search(String phrase) {
|
|
|
|
List<ItemCollection> searchResult = new ArrayList<ItemCollection>();
|
|
if (phrase == null || phrase.isEmpty()) {
|
|
return searchResult;
|
|
}
|
|
|
|
try {
|
|
phrase = phrase.trim();
|
|
// phrase = LuceneSearchService.escapeSearchTerm(phrase);
|
|
phrase = schemaService.normalizeSearchTerm(phrase);
|
|
String sQuery = "(type:\"workitem\" OR type:\"workitemarchive\") AND ($modelversion:businesspartner-*)";
|
|
sQuery += " AND NOT ($taskid:1800) ";
|
|
|
|
// ist nur eine Zahl eingegeben worden?
|
|
if (isCargosoftNumber(phrase)) {
|
|
sQuery += " AND ((k" + phrase + "*) OR (d" + phrase + "*) OR (bp" + phrase + "*))";
|
|
|
|
} else {
|
|
// normale Phrase
|
|
sQuery += " AND (" + phrase + "*)";
|
|
}
|
|
logger.finest("SearchQuery= " + sQuery);
|
|
|
|
searchResult = documentService.find(sQuery, MAX_SEARCH_RESULT, 0);
|
|
} catch (Exception e) {
|
|
logger.warning(" lucene error - " + e.getMessage());
|
|
}
|
|
|
|
// sort by txtname..
|
|
Collections.sort(searchResult, new ItemCollectionComparator("$workflowsummary", true));
|
|
return searchResult;
|
|
}
|
|
|
|
/**
|
|
* Packt die Liste der Bank Details (ItemCollections) in ein Workitem
|
|
*
|
|
* @param workitem
|
|
*/
|
|
public static void implodeBanks(ItemCollection workitem, List<ItemCollection> ibanList) {
|
|
List<Map> mapOrderItems = new ArrayList();
|
|
if (ibanList != null) {
|
|
logger.fine("Convert child items into Map...");
|
|
Iterator<?> var3 = ibanList.iterator();
|
|
|
|
while (var3.hasNext()) {
|
|
ItemCollection orderItem = (ItemCollection) var3.next();
|
|
mapOrderItems.add(orderItem.getAllItems());
|
|
}
|
|
|
|
workitem.replaceItemValue(BusinessPartnerService.ITEM_IBAN_LIST, mapOrderItems);
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Enpackt die iban Map Liste von eiem Worktiem in eine Liste von ItemCollection
|
|
*
|
|
* @param workitem
|
|
*/
|
|
public static List<ItemCollection> explodeBanks(ItemCollection workitem) {
|
|
List<ItemCollection> ibanList = new ArrayList();
|
|
List<Object> mapOrderItems = workitem.getItemValue(BusinessPartnerService.ITEM_IBAN_LIST);
|
|
Iterator<?> var4 = mapOrderItems.iterator();
|
|
while (var4.hasNext()) {
|
|
Object mapOderItem = var4.next();
|
|
if (mapOderItem instanceof Map) {
|
|
ItemCollection itemCol = new ItemCollection((Map) mapOderItem);
|
|
|
|
ibanList.add(itemCol);
|
|
|
|
}
|
|
}
|
|
return ibanList;
|
|
}
|
|
|
|
/**
|
|
* Liefert die ChildItems mit den IBAN daten
|
|
*
|
|
* @param workitem
|
|
* @return
|
|
*/
|
|
public static List<ItemCollection> getBanks(ItemCollection workitem) {
|
|
List<ItemCollection> result = new ArrayList<>();
|
|
List<Object> mapOrderItems = workitem.getItemValue(BusinessPartnerService.ITEM_IBAN_LIST);
|
|
Iterator<?> var4 = mapOrderItems.iterator();
|
|
while (var4.hasNext()) {
|
|
Object mapOderItem = var4.next();
|
|
if (mapOderItem instanceof Map) {
|
|
ItemCollection itemCol = new ItemCollection((Map) mapOderItem);
|
|
result.add(itemCol);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Diese Methode übertragt neue IBAN/BIC Pare in den Kreditor Banken Bereich
|
|
*
|
|
* @param cdtrNumber
|
|
* @param iban
|
|
* @param bic
|
|
* @throws PluginException
|
|
*/
|
|
public void addNewIBANBIC(ItemCollection businessPartner, String iban, String bic) throws PluginException {
|
|
if (businessPartner != null) {
|
|
|
|
List<ItemCollection> bankenListe = explodeBanks(businessPartner);
|
|
ItemCollection newbank = new ItemCollection();
|
|
// {iban=[IBAN], name=[Test Bank], id=[test ], bic=[BIC]}
|
|
newbank.setItemValue("iban", iban);
|
|
newbank.setItemValue("bic", bic);
|
|
newbank.setItemValue("id", bankenListe.size() + 1);
|
|
newbank.setItemValue("name", "Bank-" + bankenListe.size() + 1);
|
|
|
|
bankenListe.add(newbank);
|
|
implodeBanks(businessPartner, bankenListe);
|
|
|
|
documentService.save(businessPartner);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Diese Methode gibt true zurück wenn die angegebene IBAN/BIC Kombination als
|
|
* bank bereits bekannt ist
|
|
*
|
|
* @param cdtrNumber
|
|
* @param iban
|
|
* @param bic
|
|
* @throws PluginException
|
|
*/
|
|
public boolean isIBANBICKnown(ItemCollection businessPartner, String iban, String bic) throws PluginException {
|
|
|
|
if (businessPartner != null) {
|
|
|
|
List<ItemCollection> bankenListe = explodeBanks(businessPartner);
|
|
for (ItemCollection bank : bankenListe) {
|
|
if (iban.equals(bank.getItemValueString("iban")) && bic.equals(bank.getItemValueString("bic"))) {
|
|
// known!
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|