All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.spin.eca46.process.WorkflowProcessor Maven / Gradle / Ivy

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