office-alexander-logistics/office-alexander-logistics-app/src/main/java/com/alexanderlogistics/BusinessPartnerService.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;
}
}