
org.spin.eca46.process.WorkflowProcessor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of adempiere-business-processors Show documentation
Show all versions of adempiere-business-processors Show documentation
A project to convert all processors to process and call it from extern services
The newest version!
/******************************************************************************
* Product: ADempiere ERP & CRM Smart Business Solution *
* Copyright (C) 2006-2017 ADempiere Foundation, All Rights Reserved. *
* This program is free software, you can redistribute it and/or modify it *
* under the terms version 2 of the GNU General Public License as published *
* or (at your option) any later version. *
* by the Free Software Foundation. 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 should have received a copy of the GNU General Public License along *
* with this program, if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
* For the text or an alternative of this public license, you may reach us *
* or via [email protected] or http://www.adempiere.net/license.html *
*****************************************************************************/
package org.spin.eca46.process;
import java.io.File;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Level;
import org.adempiere.exceptions.AdempiereException;
import org.compiere.model.MClient;
import org.compiere.model.MOrgInfo;
import org.compiere.model.MUserRoles;
import org.compiere.model.PO;
import org.compiere.process.DocAction;
import org.compiere.process.StateEngine;
import org.compiere.util.DB;
import org.compiere.util.Msg;
import org.compiere.util.TimeUtil;
import org.compiere.util.Trx;
import org.compiere.wf.MWFActivity;
import org.compiere.wf.MWFNode;
import org.compiere.wf.MWFProcess;
import org.compiere.wf.MWFResponsible;
import org.compiere.wf.MWorkflowProcessor;
import org.compiere.wf.MWorkflowProcessorLog;
import org.spin.queue.notification.DefaultNotifier;
import org.spin.queue.util.QueueLoader;
/**
* Generated Process for (Product Internal Use)
* @author Jorg Janke
* @version $Id: RequestProcessor.java,v 1.3 2006/07/30 00:53:33 jjanke Exp $
* @version Release 3.9.3
*/
public class WorkflowProcessor extends WorkflowProcessorAbstract {
/** The Concrete Model */
private MWorkflowProcessor workflowProcessor = null;
/** Last Summary */
private StringBuffer summary = new StringBuffer();
/** Client onfo */
private MClient client = null;
/** Initial work time */
private long startWork;
@Override
protected void prepare() {
super.prepare();
if(getWorkflowProcessorId() <= 0) {
throw new AdempiereException("@AD_WorkflowProcessor_ID@ @NotFound@");
}
workflowProcessor = new MWorkflowProcessor(getCtx(), getWorkflowProcessorId(), get_TrxName());
client = MClient.get(getCtx(), workflowProcessor.getAD_Client_ID());
}
@Override
protected String doIt() throws Exception {
startWork = System.currentTimeMillis();
summary = new StringBuffer();
//
wakeup();
dynamicPriority();
sendAlerts();
//
int no = workflowProcessor.deleteLog();
summary.append("Logs deleted=").append(no);
if (workflowProcessor.get_TrxName() == null) {
Trx.run(this::addWorkflowProcessorLog);
} else {
addWorkflowProcessorLog(workflowProcessor.get_TrxName());
}
return TimeUtil.formatElapsed(new Timestamp(startWork));
}
/**
* Add Workflow Processor Log
* @param trxName
*/
private void addWorkflowProcessorLog(String trxName) {
MWorkflowProcessorLog workflowProcessorLog = new MWorkflowProcessorLog(workflowProcessor, summary.toString(), trxName);
workflowProcessorLog.setReference(TimeUtil.formatElapsed(new Timestamp(startWork)));
workflowProcessorLog.saveEx();
}
/**
* Continue Workflow After Sleep
*/
private void wakeup()
{
String sql = "SELECT * "
+ "FROM AD_WF_Activity a "
+ "WHERE Processed='N' AND WFState='OS'" // suspended
+ " AND EndWaitTime > SysDate"
+ " AND AD_Client_ID=?"
+ " AND EXISTS (SELECT * FROM AD_Workflow wf "
+ " INNER JOIN AD_WF_Node wfn ON (wf.AD_Workflow_ID=wfn.AD_Workflow_ID) "
+ "WHERE a.AD_WF_Node_ID=wfn.AD_WF_Node_ID"
+ " AND wfn.Action='Z'" // sleeping
+ " AND (wf.AD_WorkflowProcessor_ID IS NULL OR wf.AD_WorkflowProcessor_ID=?))";
PreparedStatement pstmt = null;
int count = 0;
try
{
pstmt = DB.prepareStatement (sql, null);
pstmt.setInt (1, workflowProcessor.getAD_Client_ID());
pstmt.setInt (2, workflowProcessor.getAD_WorkflowProcessor_ID());
ResultSet rs = pstmt.executeQuery ();
while (rs.next ())
{
MWFActivity activity = new MWFActivity (getCtx(), rs, null);
activity.setWFState (StateEngine.STATE_Completed);
// saves and calls MWFProcess.checkActivities();
count++;
}
rs.close ();
}
catch (Exception e)
{
log.log(Level.SEVERE, "wakeup", e);
}
finally
{
DB.close(pstmt);
}
summary.append("Wakeup #").append(count).append (" - ");
} // wakeup
/**
* Set/Increase Priority dynamically
*/
private void dynamicPriority()
{
// suspend activities with dynamic priority node
String sql = "SELECT * "
+ "FROM AD_WF_Activity a "
+ "WHERE Processed='N' AND WFState='OS'" // suspended
+ " AND EXISTS (SELECT * FROM AD_Workflow wf"
+ " INNER JOIN AD_WF_Node wfn ON (wf.AD_Workflow_ID=wfn.AD_Workflow_ID) "
+ "WHERE a.AD_WF_Node_ID=wfn.AD_WF_Node_ID AND wf.AD_WorkflowProcessor_ID=?"
+ " AND wfn.DynPriorityUnit IS NOT NULL AND wfn.DynPriorityChange IS NOT NULL)";
PreparedStatement pstmt = null;
int count = 0;
try
{
pstmt = DB.prepareStatement (sql, null);
pstmt.setInt(1, workflowProcessor.getAD_WorkflowProcessor_ID());
ResultSet rs = pstmt.executeQuery ();
while (rs.next ())
{
MWFActivity activity = new MWFActivity (getCtx(), rs, null);
if (activity.getDynPriorityStart() == 0)
activity.setDynPriorityStart(activity.getPriority());
long ms = System.currentTimeMillis() - activity.getCreated().getTime();
MWFNode node = activity.getNode();
int prioDiff = node.calculateDynamicPriority ((int)(ms / 1000));
activity.setPriority(activity.getDynPriorityStart() + prioDiff);
activity.saveEx();
count++;
}
rs.close ();
}
catch (Exception e)
{
log.log(Level.SEVERE, sql, e);
}
finally
{
DB.close(pstmt);
}
summary.append("DynPriority #").append(count).append (" - ");
} // setPriority
/**
* Send Alerts
*/
private void sendAlerts()
{
// Alert over Priority
if (workflowProcessor.getAlertOverPriority() > 0)
{
String sql = "SELECT * "
+ "FROM AD_WF_Activity a "
+ "WHERE Processed='N' AND WFState='OS'" // suspended
+ " AND Priority >= ?" // ##1
+ " AND (DateLastAlert IS NULL";
if (workflowProcessor.getRemindDays() > 0)
sql += " OR (DateLastAlert+" + workflowProcessor.getRemindDays()
+ ") < SysDate";
sql += ") AND EXISTS (SELECT * FROM AD_Workflow wf "
+ " INNER JOIN AD_WF_Node wfn ON (wf.AD_Workflow_ID=wfn.AD_Workflow_ID) "
+ "WHERE a.AD_WF_Node_ID=wfn.AD_WF_Node_ID"
+ " AND (wf.AD_WorkflowProcessor_ID IS NULL OR wf.AD_WorkflowProcessor_ID=?))";
int count = 0;
int countEMails = 0;
PreparedStatement pstmt = null;
try
{
pstmt = DB.prepareStatement(sql, null);
pstmt.setInt (1, workflowProcessor.getAlertOverPriority());
pstmt.setInt (2, workflowProcessor.getAD_WorkflowProcessor_ID());
ResultSet rs = pstmt.executeQuery();
while (rs.next())
{
MWFActivity activity = new MWFActivity (getCtx(), rs, null);
boolean escalate = activity.getDateLastAlert() != null;
countEMails += sendEmail (activity, "ActivityOverPriority",
escalate, true);
activity.setDateLastAlert(new Timestamp(System.currentTimeMillis()));
activity.saveEx();
count++;
}
rs.close();
pstmt.close();
}
catch (SQLException e)
{
log.log(Level.SEVERE, "(Priority) - " + sql, e);
}
finally
{
DB.close(pstmt);
}
summary.append("OverPriority #").append(count);
if (countEMails > 0)
summary.append(" (").append(countEMails).append(" EMail)");
summary.append (" - ");
} // Alert over Priority
/**
* Over End Wait
*/
String sql = "SELECT * "
+ "FROM AD_WF_Activity a "
+ "WHERE Processed='N' AND WFState='OS'" // suspended
+ " AND EndWaitTime > SysDate"
+ " AND (DateLastAlert IS NULL";
if (workflowProcessor.getRemindDays() > 0)
sql += " OR (DateLastAlert+" + workflowProcessor.getRemindDays()
+ ") < SysDate";
sql += ") AND EXISTS (SELECT * FROM AD_Workflow wf "
+ " INNER JOIN AD_WF_Node wfn ON (wf.AD_Workflow_ID=wfn.AD_Workflow_ID) "
+ "WHERE a.AD_WF_Node_ID=wfn.AD_WF_Node_ID"
+ " AND wfn.Action<>'Z'" // not sleeping
+ " AND (wf.AD_WorkflowProcessor_ID IS NULL OR wf.AD_WorkflowProcessor_ID=?))";
PreparedStatement pstmt = null;
int count = 0;
int countEMails = 0;
try
{
pstmt = DB.prepareStatement (sql, null);
pstmt.setInt(1, workflowProcessor.getAD_WorkflowProcessor_ID());
ResultSet rs = pstmt.executeQuery ();
while (rs.next ())
{
MWFActivity activity = new MWFActivity (getCtx(), rs, null);
boolean escalate = activity.getDateLastAlert() != null;
countEMails += sendEmail (activity, "ActivityEndWaitTime",
escalate, false);
activity.setDateLastAlert(new Timestamp(System.currentTimeMillis()));
activity.saveEx();
count++;
}
rs.close ();
}
catch (Exception e)
{
log.log(Level.SEVERE, "(EndWaitTime) - " + sql, e);
}
finally
{
DB.close(pstmt);
}
summary.append("EndWaitTime #").append(count);
if (countEMails > 0)
summary.append(" (").append(countEMails).append(" EMail)");
summary.append (" - ");
/**
* Send inactivity alerts
*/
if (workflowProcessor.getInactivityAlertDays() > 0)
{
sql = "SELECT * "
+ "FROM AD_WF_Activity a "
+ "WHERE Processed='N' AND WFState='OS'" // suspended
+ " AND (Updated+" + workflowProcessor.getInactivityAlertDays() + ") < SysDate"
+ " AND (DateLastAlert IS NULL";
if (workflowProcessor.getRemindDays() > 0)
sql += " OR (DateLastAlert+" + workflowProcessor.getRemindDays()
+ ") < SysDate";
sql += ") AND EXISTS (SELECT * FROM AD_Workflow wf "
+ " INNER JOIN AD_WF_Node wfn ON (wf.AD_Workflow_ID=wfn.AD_Workflow_ID) "
+ "WHERE a.AD_WF_Node_ID=wfn.AD_WF_Node_ID"
+ " AND (wf.AD_WorkflowProcessor_ID IS NULL OR wf.AD_WorkflowProcessor_ID=?))";
count = 0;
countEMails = 0;
try
{
pstmt = DB.prepareStatement(sql, null);
pstmt.setInt (1, workflowProcessor.getAD_WorkflowProcessor_ID());
ResultSet rs = pstmt.executeQuery();
while (rs.next())
{
MWFActivity activity = new MWFActivity (getCtx(), rs, null);
boolean escalate = activity.getDateLastAlert() != null;
countEMails += sendEmail (activity, "ActivityInactivity",
escalate, false);
activity.setDateLastAlert(new Timestamp(System.currentTimeMillis()));
activity.saveEx();
count++;
}
rs.close();
pstmt.close();
}
catch (SQLException e)
{
log.log(Level.SEVERE, "(Inactivity): " + sql, e);
}
finally
{
DB.close(pstmt);
}
summary.append("Inactivity #").append(count);
if (countEMails > 0)
summary.append(" (").append(countEMails).append(" EMail)");
summary.append (" - ");
} // Inactivity
} // sendAlerts
/**
* Send Alert EMail
* @param activity activity
* @param AD_Message message
* @param toProcess true if to process owner
* @param toSupervisor true if to Supervisor
* @return number of mails sent
*/
private int sendEmail (MWFActivity activity, String AD_Message,
boolean toProcess, boolean toSupervisor)
{
if (client == null || client.getAD_Client_ID() != activity.getAD_Client_ID())
client = MClient.get(getCtx(), activity.getAD_Client_ID());
MWFProcess process = new MWFProcess (getCtx(), activity.getAD_WF_Process_ID(), null);
String subjectVar = activity.getNode().getName();
AtomicReference message = new AtomicReference(activity.getTextMsg());
if (message.get() == null || message.get().length() == 0)
message.set(process.getTextMsg());
AtomicReference attachmentAsPDF = new AtomicReference<>();
PO po = activity.getPO();
if (po instanceof DocAction) {
message.set(((DocAction)po).getDocumentInfo() + "\n" + message.get());
attachmentAsPDF.set(((DocAction)po).createPDF());
}
// Inactivity Alert: Workflow Activity {0}
String subject = Msg.getMsg(client.getAD_Language(), AD_Message,
new Object[] {subjectVar});
// Prevent duplicates
ArrayList list = new ArrayList();
int counter = 0;
// To Activity Owner
counter++;
list.add(Integer.valueOf(activity.getAD_User_ID()));
// To Process Owner
if (toProcess
&& process.getAD_User_ID() != activity.getAD_User_ID()) {
counter++;
list.add(Integer.valueOf(process.getAD_User_ID()));
}
// To Activity Responsible
MWFResponsible responsible = MWFResponsible.get(getCtx(), activity.getAD_WF_Responsible_ID());
counter += sendAlertToResponsible (responsible, list, process,
subject, message.get(), attachmentAsPDF.get(), po);
// To Process Responsible
if (toProcess
&& process.getAD_WF_Responsible_ID() != activity.getAD_WF_Responsible_ID()) {
responsible = MWFResponsible.get(getCtx(), process.getAD_WF_Responsible_ID());
counter += sendAlertToResponsible (responsible, list, process,
subject, message.get(), attachmentAsPDF.get(), po);
}
// Processor SuperVisor
if (toSupervisor
&& workflowProcessor.getSupervisor_ID() != 0
&& !list.contains(Integer.valueOf(workflowProcessor.getSupervisor_ID()))) {
counter++;
list.add(Integer.valueOf(workflowProcessor.getSupervisor_ID()));
}
Trx.run(transactionName -> {
DefaultNotifier notifier = getDefaultNotifierInstance(transactionName);
notifier.clearMessage()
.withApplicationType(DefaultNotifier.DefaultNotificationType_UserDefined)
.withText(subject)
.addAttachment(attachmentAsPDF.get())
.withDescription(message.get())
.withEntity(po);
list.forEach(userId -> notifier.addRecipient(userId));
notifier.addToQueue();
});
return counter;
} // sendAlert
/**
* Get Default notifier
* @param transactionName
* @return
*/
private DefaultNotifier getDefaultNotifierInstance(String transactionName) {
return (DefaultNotifier) QueueLoader.getInstance().getQueueManager(DefaultNotifier.QUEUETYPE_DefaultNotifier)
.withContext(getCtx())
.withTransactionName(transactionName);
}
/**
* Send Alert To Responsible
* @param responsible responsible
* @param list list of already sent users
* @param process process
* @param subject subject
* @param message message
* @param pdf optional pdf
* @return number of mail sent
*/
private int sendAlertToResponsible (MWFResponsible responsible,
ArrayList list, MWFProcess process,
String subject, String message, File pdf, PO po)
{
int counter = 0;
if (responsible.isInvoker())
;
// Human
else if (MWFResponsible.RESPONSIBLETYPE_Human.equals(responsible.getResponsibleType())
&& responsible.getAD_User_ID() != 0
&& !list.contains(Integer.valueOf(responsible.getAD_User_ID()))) {
counter++;
list.add(Integer.valueOf(responsible.getAD_User_ID()));
}
// Org of the Document
else if (MWFResponsible.RESPONSIBLETYPE_Organization.equals(responsible.getResponsibleType())) {
PO document = process.getPO();
if (document != null) {
MOrgInfo org = MOrgInfo.get (getCtx(), document.getAD_Org_ID(), null);
if (org.getSupervisor_ID() != 0
&& !list.contains(Integer.valueOf(org.getSupervisor_ID()))) {
counter++;
list.add(Integer.valueOf(org.getSupervisor_ID()));
}
}
}
// Role
else if (MWFResponsible.RESPONSIBLETYPE_Role.equals(responsible.getResponsibleType())
&& responsible.getAD_Role_ID() != 0) {
MUserRoles[] userRoles = MUserRoles.getOfRole(getCtx(), responsible.getAD_Role_ID());
for (int i = 0; i < userRoles.length; i++)
{
MUserRoles roles = userRoles[i];
if (!roles.isActive())
continue;
int AD_User_ID = roles.getAD_User_ID();
if (!list.contains(Integer.valueOf(AD_User_ID))) {
counter++;
list.add(Integer.valueOf(AD_User_ID));
}
}
}
// Send
Trx.run(transactionName -> {
DefaultNotifier notifier = getDefaultNotifierInstance(transactionName);
notifier
.clearMessage()
.withApplicationType(DefaultNotifier.DefaultNotificationType_UserDefined)
.withText(subject)
.addAttachment(pdf)
.withDescription(message)
.withEntity(po);
// Add all recipients
list.forEach(userId -> notifier.addRecipient(userId));
// Add to queue
notifier.addToQueue();
});
return counter;
} // sendAlertToResponsible
/**
* Get Server Info
* @return info
*/
public String getServerInfo()
{
return "Last=" + summary.toString();
} // getServerInfo
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy