IDEMPIERE-4187 implement the usage of workflows in processes (#236)
Implement chaining of workflow through the use of Process Action Implement overwrite of Record_ID for Process through Node Parameter
This commit is contained in:
parent
9d614b985e
commit
7324dde78e
|
@ -25,9 +25,11 @@ import org.adempiere.util.ProcessUtil;
|
|||
import org.compiere.process.ProcessInfo;
|
||||
import org.compiere.util.CCache;
|
||||
import org.compiere.util.DB;
|
||||
import org.compiere.util.Env;
|
||||
import org.compiere.util.Trx;
|
||||
import org.compiere.util.Util;
|
||||
import org.compiere.wf.MWFNode;
|
||||
import org.compiere.wf.MWFProcess;
|
||||
|
||||
/**
|
||||
* Process Model
|
||||
|
@ -264,38 +266,51 @@ public class MProcess extends X_AD_Process
|
|||
// Lock
|
||||
pInstance.setIsProcessing(true);
|
||||
pInstance.saveEx();
|
||||
pi.setAD_PInstance_ID(pInstance.getAD_PInstance_ID());
|
||||
}
|
||||
|
||||
boolean ok = false;
|
||||
|
||||
// Java Class
|
||||
String Classname = getClassname();
|
||||
if (Classname != null && Classname.length() > 0)
|
||||
if (isWorkflow())
|
||||
{
|
||||
pi.setClassName(Classname);
|
||||
ok = startClass(pi, trx, managedTrx);
|
||||
}
|
||||
else
|
||||
{
|
||||
// PL/SQL Procedure
|
||||
String ProcedureName = getProcedureName();
|
||||
if (ProcedureName != null && ProcedureName.length() > 0)
|
||||
pi.setTransactionName(trx.getTrxName());
|
||||
MWFProcess wfprocess = ProcessUtil.startWorkFlow(getCtx(), pi, getAD_Workflow_ID());
|
||||
if (wfprocess == null)
|
||||
{
|
||||
ok = startProcess (ProcedureName, pi, trx, managedTrx);
|
||||
ok = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// BF IDEMPIERE-165
|
||||
if (this.isReport()) {
|
||||
ok = true;
|
||||
}
|
||||
else {
|
||||
String msg = "No Classname or ProcedureName for " + getName();
|
||||
pi.setSummary(msg, ok);
|
||||
log.warning(msg);
|
||||
}
|
||||
MPInstance pinstance = new MPInstance(Env.getCtx(), pi.getAD_PInstance_ID(), null);
|
||||
String errmsg = pi.getSummary();
|
||||
pinstance.setResult(!pi.isError());
|
||||
pinstance.setErrorMsg(errmsg);
|
||||
pinstance.saveEx();
|
||||
ok = !pi.isError();
|
||||
}
|
||||
}
|
||||
else if (isJavaProcess())
|
||||
{
|
||||
pi.setClassName(getClassname());
|
||||
ok = startClass(pi, trx, managedTrx);
|
||||
}
|
||||
else if (isDatabaseProcedure())
|
||||
{
|
||||
// PL/SQL Procedure
|
||||
ok = startProcess (getProcedureName(), pi, trx, managedTrx);
|
||||
}
|
||||
else if (this.isReport())
|
||||
{
|
||||
// BF IDEMPIERE-165
|
||||
ok = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
String msg = "No Workflow, Classname or ProcedureName for " + getName();
|
||||
pi.setSummary(msg, ok);
|
||||
log.warning(msg);
|
||||
}
|
||||
|
||||
return ok;
|
||||
} // process
|
||||
|
||||
|
@ -305,10 +320,18 @@ public class MProcess extends X_AD_Process
|
|||
*/
|
||||
public boolean isJavaProcess()
|
||||
{
|
||||
String Classname = getClassname();
|
||||
return (Classname != null && Classname.length() > 0);
|
||||
return !Util.isEmpty(getClassname(), true);
|
||||
} // is JavaProcess
|
||||
|
||||
/**
|
||||
* Is this a db procedure
|
||||
* @return true if db procedure
|
||||
*/
|
||||
public boolean isDatabaseProcedure()
|
||||
{
|
||||
return !Util.isEmpty(getProcedureName(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is Force Background
|
||||
* @return true if force background
|
||||
|
|
|
@ -1142,6 +1142,29 @@ public class MWFActivity extends X_AD_WF_Activity implements Runnable
|
|||
//
|
||||
ProcessInfo pi = new ProcessInfo (m_node.getName(true), m_node.getAD_Process_ID(),
|
||||
getAD_Table_ID(), getRecord_ID());
|
||||
|
||||
//check record id overwrite
|
||||
MWFNodePara[] nParams = m_node.getParameters();
|
||||
for(MWFNodePara p : nParams)
|
||||
{
|
||||
if (p.getAD_Process_Para_ID() == 0 && p.getAttributeName().equalsIgnoreCase("Record_ID") && !Util.isEmpty(p.getAttributeValue(), true))
|
||||
{
|
||||
try
|
||||
{
|
||||
Object value = parseNodeParaAttribute(p);
|
||||
if (value == p || value == null)
|
||||
break;
|
||||
int recordId = Integer.valueOf(value.toString());
|
||||
pi.setRecord_ID(recordId);
|
||||
}
|
||||
catch (NumberFormatException e)
|
||||
{
|
||||
log.log(Level.WARNING, e.getMessage(), e);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pi.setAD_User_ID(getAD_User_ID());
|
||||
pi.setAD_Client_ID(getAD_Client_ID());
|
||||
pi.setAD_PInstance_ID(pInstance.getAD_PInstance_ID());
|
||||
|
@ -1610,50 +1633,13 @@ public class MWFActivity extends X_AD_WF_Activity implements Runnable
|
|||
MPInstancePara iPara = iParams[pi];
|
||||
for (int np = 0; np < nParams.length; np++)
|
||||
{
|
||||
MWFNodePara nPara = nParams[np];
|
||||
MWFNodePara nPara = nParams[np];
|
||||
if (iPara.getParameterName().equals(nPara.getAttributeName()))
|
||||
{
|
||||
String variableName = nPara.getAttributeValue();
|
||||
if (log.isLoggable(Level.FINE)) log.fine(nPara.getAttributeName()
|
||||
+ " = " + variableName);
|
||||
// Value - Constant/Variable
|
||||
Object value = variableName;
|
||||
if (variableName == null
|
||||
|| (variableName != null && variableName.length() == 0))
|
||||
value = null;
|
||||
else if (variableName.indexOf('@') != -1 && m_po != null) // we have a variable
|
||||
{
|
||||
// Strip
|
||||
int index = variableName.indexOf('@');
|
||||
String columnName = variableName.substring(index+1);
|
||||
index = columnName.indexOf('@');
|
||||
if (index == -1)
|
||||
{
|
||||
log.warning(nPara.getAttributeName()
|
||||
+ " - cannot evaluate=" + variableName);
|
||||
break;
|
||||
}
|
||||
columnName = columnName.substring(0, index);
|
||||
index = m_po.get_ColumnIndex(columnName);
|
||||
if (index != -1)
|
||||
{
|
||||
value = m_po.get_Value(index);
|
||||
}
|
||||
else // not a column
|
||||
{
|
||||
// try Env
|
||||
String env = Env.getContext(getCtx(), columnName);
|
||||
if (env.length() == 0)
|
||||
{
|
||||
log.warning(nPara.getAttributeName()
|
||||
+ " - not column nor environment =" + columnName
|
||||
+ "(" + variableName + ")");
|
||||
break;
|
||||
}
|
||||
else
|
||||
value = env;
|
||||
}
|
||||
} // @variable@
|
||||
Object value = parseNodeParaAttribute(nPara);
|
||||
if (value == nPara)
|
||||
break;
|
||||
|
||||
// No Value
|
||||
if (value == null)
|
||||
|
@ -1718,6 +1704,52 @@ public class MWFActivity extends X_AD_WF_Activity implements Runnable
|
|||
} // instance parameter loop
|
||||
} // fillParameter
|
||||
|
||||
private Object parseNodeParaAttribute(MWFNodePara nPara)
|
||||
{
|
||||
String variableName = nPara.getAttributeValue();
|
||||
if (log.isLoggable(Level.FINE)) log.fine(nPara.getAttributeName()
|
||||
+ " = " + variableName);
|
||||
// Value - Constant/Variable
|
||||
Object value = variableName;
|
||||
if (variableName == null
|
||||
|| (variableName != null && variableName.length() == 0))
|
||||
value = null;
|
||||
else if (variableName.indexOf('@') != -1 && m_po != null) // we have a variable
|
||||
{
|
||||
// Strip
|
||||
int index = variableName.indexOf('@');
|
||||
String columnName = variableName.substring(index+1);
|
||||
index = columnName.indexOf('@');
|
||||
if (index == -1)
|
||||
{
|
||||
log.warning(nPara.getAttributeName()
|
||||
+ " - cannot evaluate=" + variableName);
|
||||
return nPara;
|
||||
}
|
||||
columnName = columnName.substring(0, index);
|
||||
index = m_po.get_ColumnIndex(columnName);
|
||||
if (index != -1)
|
||||
{
|
||||
value = m_po.get_Value(index);
|
||||
}
|
||||
else // not a column
|
||||
{
|
||||
// try Env
|
||||
String env = Env.getContext(getCtx(), columnName);
|
||||
if (env.length() == 0)
|
||||
{
|
||||
log.warning(nPara.getAttributeName()
|
||||
+ " - not column nor environment =" + columnName
|
||||
+ "(" + variableName + ")");
|
||||
return nPara;
|
||||
}
|
||||
else
|
||||
value = env;
|
||||
}
|
||||
} // @variable@
|
||||
return value;
|
||||
}
|
||||
|
||||
/*********************************
|
||||
* Send EMail
|
||||
*/
|
||||
|
|
|
@ -210,7 +210,7 @@ public abstract class AbstractProcessCtl implements Runnable
|
|||
startWorkflow (AD_Workflow_ID);
|
||||
MPInstance pinstance = new MPInstance(Env.getCtx(), m_pi.getAD_PInstance_ID(), null);
|
||||
String errmsg = m_pi.getSummary();
|
||||
pinstance.setResult(m_pi.isError());
|
||||
pinstance.setResult(!m_pi.isError());
|
||||
pinstance.setErrorMsg(errmsg);
|
||||
pinstance.saveEx();
|
||||
unlock();
|
||||
|
|
|
@ -0,0 +1,238 @@
|
|||
/***********************************************************************
|
||||
* This file is part of iDempiere ERP Open Source *
|
||||
* http://www.idempiere.org *
|
||||
* *
|
||||
* Copyright (C) Contributors *
|
||||
* *
|
||||
* 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 should have received a copy of the GNU General Public License *
|
||||
* along with this program; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
|
||||
* MA 02110-1301, USA. *
|
||||
* *
|
||||
* Contributors: *
|
||||
* - hengsin *
|
||||
**********************************************************************/
|
||||
package org.idempiere.test.model;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.Timestamp;
|
||||
|
||||
import org.compiere.model.MBPartner;
|
||||
import org.compiere.model.MInOut;
|
||||
import org.compiere.model.MInOutConfirm;
|
||||
import org.compiere.model.MInOutLine;
|
||||
import org.compiere.model.MOrder;
|
||||
import org.compiere.model.MOrderLine;
|
||||
import org.compiere.model.MProcess;
|
||||
import org.compiere.model.MProduct;
|
||||
import org.compiere.process.DocAction;
|
||||
import org.compiere.process.ProcessInfo;
|
||||
import org.compiere.process.ServerProcessCtl;
|
||||
import org.compiere.util.Env;
|
||||
import org.compiere.util.TimeUtil;
|
||||
import org.compiere.util.Util;
|
||||
import org.compiere.wf.MWFNode;
|
||||
import org.compiere.wf.MWFNodeNext;
|
||||
import org.compiere.wf.MWFNodePara;
|
||||
import org.compiere.wf.MWorkflow;
|
||||
import org.idempiere.test.AbstractTestCase;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* @author hengsin
|
||||
*
|
||||
*/
|
||||
public class ProcessTest extends AbstractTestCase {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public ProcessTest() {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWorkflowProcess() {
|
||||
//first test, using MProcess.processIt
|
||||
MOrder order = new MOrder(Env.getCtx(), 0, getTrxName());
|
||||
//Joe Block
|
||||
order.setBPartner(MBPartner.get(Env.getCtx(), 118));
|
||||
order.setC_DocTypeTarget_ID(MOrder.DocSubTypeSO_Standard);
|
||||
order.setDeliveryRule(MOrder.DELIVERYRULE_CompleteOrder);
|
||||
order.setDocStatus(DocAction.STATUS_Drafted);
|
||||
order.setDocAction(DocAction.ACTION_Complete);
|
||||
Timestamp today = TimeUtil.getDay(System.currentTimeMillis());
|
||||
order.setDateOrdered(today);
|
||||
order.setDatePromised(today);
|
||||
order.saveEx();
|
||||
|
||||
MOrderLine line1 = new MOrderLine(order);
|
||||
line1.setLine(10);
|
||||
//Azalea Bush
|
||||
line1.setProduct(MProduct.get(Env.getCtx(), 128));
|
||||
line1.setQty(new BigDecimal("1"));
|
||||
line1.setDatePromised(today);
|
||||
line1.saveEx();
|
||||
|
||||
int Process_Order=104;
|
||||
MProcess process = MProcess.get(Env.getCtx(), Process_Order);
|
||||
ProcessInfo pi = new ProcessInfo(process.getName(), process.get_ID());
|
||||
pi.setAD_Client_ID(getAD_Client_ID());
|
||||
pi.setAD_User_ID(getAD_User_ID());
|
||||
pi.setRecord_ID(order.get_ID());
|
||||
pi.setTransactionName(getTrxName());
|
||||
|
||||
boolean ok = process.processIt(pi, getTrx(), false);
|
||||
if (!ok || pi.isError()) {
|
||||
fail("Error running Process_Order workflow process" + (Util.isEmpty(pi.getSummary()) ? "" : " : "+pi.getSummary()));
|
||||
return;
|
||||
}
|
||||
|
||||
order.load(getTrxName());
|
||||
assertTrue(order.getDocStatus().equals(DocAction.STATUS_Completed), "Order not completed");
|
||||
|
||||
//second test, using MWorkflow.runDocumentActionWorkflow
|
||||
pi = MWorkflow.runDocumentActionWorkflow(order, DocAction.ACTION_ReActivate);
|
||||
if (pi.isError()) {
|
||||
fail("Failed to reactivate order" + (Util.isEmpty(pi.getSummary()) ? "" : " : "+pi.getSummary()));
|
||||
return;
|
||||
}
|
||||
|
||||
order.load(getTrxName());
|
||||
assertTrue(order.getDocStatus().equals(DocAction.STATUS_InProgress), "Order not reactivated");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJavaProcess() {
|
||||
int Verify_BOM=136;
|
||||
int Patio_Chair=133;
|
||||
|
||||
//first, test MProcess.processIt
|
||||
MProcess process = MProcess.get(Env.getCtx(), Verify_BOM);
|
||||
ProcessInfo pi = new ProcessInfo(process.getName(), process.get_ID());
|
||||
pi.setAD_Client_ID(getAD_Client_ID());
|
||||
pi.setAD_User_ID(getAD_User_ID());
|
||||
pi.setRecord_ID(Patio_Chair);
|
||||
pi.setTransactionName(getTrxName());
|
||||
|
||||
boolean ok = process.processIt(pi, getTrx(), false);
|
||||
if (!ok || pi.isError()) {
|
||||
fail("Error running Verify BOM process" + (Util.isEmpty(pi.getSummary()) ? "" : " : "+pi.getSummary()));
|
||||
return;
|
||||
}
|
||||
|
||||
//second, test ServerProcessCtl.process
|
||||
pi = new ProcessInfo(process.getName(), process.get_ID());
|
||||
pi.setAD_Client_ID(getAD_Client_ID());
|
||||
pi.setAD_User_ID(getAD_User_ID());
|
||||
pi.setRecord_ID(Patio_Chair);
|
||||
pi.setTransactionName(getTrxName());
|
||||
ServerProcessCtl.process(pi, getTrx());
|
||||
if (pi.isError()) {
|
||||
fail("Error running Verify BOM process" + (Util.isEmpty(pi.getSummary()) ? "" : " : "+pi.getSummary()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChainWorkflow() {
|
||||
MOrder order = new MOrder(Env.getCtx(), 0, getTrxName());
|
||||
//Joe Block
|
||||
order.setBPartner(MBPartner.get(Env.getCtx(), 118));
|
||||
order.setC_DocTypeTarget_ID(MOrder.DocSubTypeSO_Standard);
|
||||
order.setDeliveryRule(MOrder.DELIVERYRULE_CompleteOrder);
|
||||
order.setDocStatus(DocAction.STATUS_Drafted);
|
||||
order.setDocAction(DocAction.ACTION_Complete);
|
||||
Timestamp today = TimeUtil.getDay(System.currentTimeMillis());
|
||||
order.setDateOrdered(today);
|
||||
order.setDatePromised(today);
|
||||
order.saveEx();
|
||||
|
||||
MOrderLine line1 = new MOrderLine(order);
|
||||
line1.setLine(10);
|
||||
//Azalea Bush
|
||||
line1.setProduct(MProduct.get(Env.getCtx(), 128));
|
||||
line1.setQty(new BigDecimal("1"));
|
||||
line1.setDatePromised(today);
|
||||
line1.saveEx();
|
||||
|
||||
ProcessInfo pi = MWorkflow.runDocumentActionWorkflow(order, DocAction.ACTION_Complete);
|
||||
if (pi.isError()) {
|
||||
fail("Failed to complete order" + (Util.isEmpty(pi.getSummary()) ? "" : " : "+pi.getSummary()));
|
||||
return;
|
||||
}
|
||||
order.load(getTrxName());
|
||||
assertEquals(DocAction.STATUS_Completed, order.getDocStatus(), "Expected Completed Status for Order");
|
||||
|
||||
int Process_InOutConfirmation = 124;
|
||||
MWorkflow wf = new MWorkflow(Env.getCtx(), Process_InOutConfirmation, null);
|
||||
MWFNode processNode = new MWFNode(wf, "Process_InOut", "Process_InOut");
|
||||
processNode.set_ValueNoCheck("AD_Client_ID", getAD_Client_ID());
|
||||
processNode.setAction(MWFNode.ACTION_AppsProcess);
|
||||
processNode.setAD_Process_ID(109); //M_InOut Process
|
||||
processNode.setEntityType(MWFNode.ENTITYTYPE_UserMaintained);
|
||||
processNode.saveEx();
|
||||
MWFNodePara processNodePara = new MWFNodePara(Env.getCtx(), 0, null);
|
||||
processNodePara.set_ValueNoCheck("AD_Org_ID", 0);
|
||||
processNodePara.setAD_WF_Node_ID(processNode.getAD_WF_Node_ID());
|
||||
processNodePara.setAttributeName("Record_ID");
|
||||
processNodePara.setAttributeValue("@M_InOut_ID@");
|
||||
processNodePara.setEntityType(MWFNodePara.ENTITYTYPE_UserMaintained);
|
||||
processNodePara.saveEx();
|
||||
MWFNode docCompleteNode = new MWFNode(Env.getCtx(), 219, null);
|
||||
MWFNodeNext docCompleteNodeNext = new MWFNodeNext(docCompleteNode, processNode.getAD_WF_Node_ID());
|
||||
docCompleteNodeNext.set_ValueNoCheck("AD_Client_ID", getAD_Client_ID());
|
||||
docCompleteNodeNext.setEntityType(MWFNodeNext.ENTITYTYPE_UserMaintained);
|
||||
docCompleteNodeNext.saveEx();
|
||||
|
||||
try {
|
||||
int MM_Shipment_With_Pick=148;
|
||||
MInOut inout = new MInOut(order, MM_Shipment_With_Pick, order.getDateOrdered());
|
||||
inout.setDocStatus(DocAction.STATUS_Drafted);
|
||||
inout.setDocAction(DocAction.STATUS_Completed);
|
||||
inout.saveEx();
|
||||
|
||||
MInOutLine il = new MInOutLine(inout);
|
||||
il.setOrderLine(line1, 0, new BigDecimal("1"));
|
||||
il.saveEx();
|
||||
pi = MWorkflow.runDocumentActionWorkflow(inout, DocAction.ACTION_Complete);
|
||||
if (pi.isError()) {
|
||||
fail("Failed to complete shipment" + (Util.isEmpty(pi.getSummary()) ? "" : " : "+pi.getSummary()));
|
||||
return;
|
||||
}
|
||||
inout.load(getTrxName());
|
||||
assertEquals(DocAction.STATUS_InProgress, inout.getDocStatus(), "Expected In Progress Status for Shipment");
|
||||
|
||||
MInOutConfirm[] confirmations = inout.getConfirmations(true);
|
||||
assertEquals(1, confirmations.length, "Expected 1 Shipment Confirmation Document");
|
||||
|
||||
pi = MWorkflow.runDocumentActionWorkflow(confirmations[0], DocAction.ACTION_Complete);
|
||||
if (pi.isError()) {
|
||||
fail("Failed to complete shipment confirmation" + (Util.isEmpty(pi.getSummary()) ? "" : " : "+pi.getSummary()));
|
||||
return;
|
||||
}
|
||||
confirmations[0].load(getTrxName());
|
||||
assertEquals(DocAction.STATUS_Completed, confirmations[0].getDocStatus(), "Expected Completed Status for Shipment Confirmation");
|
||||
|
||||
inout.load(getTrxName());
|
||||
assertEquals(DocAction.STATUS_Completed, inout.getDocStatus(), "Expected Completed Status for Shipment");
|
||||
} finally {
|
||||
docCompleteNodeNext.deleteEx(true);
|
||||
processNodePara.deleteEx(true);
|
||||
processNode.deleteEx(true);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue