/******************************************************************************
* Copyright (C) 2008 Low Heng Sin *
* Copyright (C) 2008 Idalica Corporation *
* 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 *
* 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. *
*****************************************************************************/
package org.adempiere.webui.apps.wf;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import org.adempiere.webui.apps.AEnv;
import org.adempiere.webui.component.Button;
import org.adempiere.webui.component.Grid;
import org.adempiere.webui.component.Label;
import org.adempiere.webui.component.ListHeader;
import org.adempiere.webui.component.ListItem;
import org.adempiere.webui.component.ListModelTable;
import org.adempiere.webui.component.Listbox;
import org.adempiere.webui.component.Row;
import org.adempiere.webui.component.Rows;
import org.adempiere.webui.component.Textbox;
import org.adempiere.webui.component.WListItemRenderer;
import org.adempiere.webui.component.WListbox;
import org.adempiere.webui.component.Window;
import org.adempiere.webui.editor.WSearchEditor;
import org.adempiere.webui.panel.ADForm;
import org.adempiere.webui.panel.StatusBarPanel;
import org.adempiere.webui.window.FDialog;
import org.compiere.model.MColumn;
import org.compiere.model.MLookup;
import org.compiere.model.MLookupFactory;
import org.compiere.model.MQuery;
import org.compiere.model.MRefList;
import org.compiere.model.MRole;
import org.compiere.model.MSysConfig;
import org.compiere.util.CLogger;
import org.compiere.util.DB;
import org.compiere.util.DisplayType;
import org.compiere.util.Env;
import org.compiere.util.Msg;
import org.compiere.util.Trx;
import org.compiere.util.ValueNamePair;
import org.compiere.wf.MWFActivity;
import org.compiere.wf.MWFNode;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zk.ui.util.Clients;
import org.zkoss.zkex.zul.Borderlayout;
import org.zkoss.zkex.zul.Center;
import org.zkoss.zkex.zul.North;
import org.zkoss.zkex.zul.South;
import org.zkoss.zul.Div;
import org.zkoss.zul.Hbox;
import org.zkoss.zul.Html;
/**
* Direct port from WFActivity
* @author hengsin
*
*/
public class WWFActivity extends ADForm implements EventListener
{
/**
*
*/
private static final long serialVersionUID = -8405802852868437716L;
/** Window No */
private int m_WindowNo = 0;
/** Open Activities */
private MWFActivity[] m_activities = null;
/** Current Activity */
private MWFActivity m_activity = null;
/** Current Activity */
private int m_index = 0;
/** Set Column */
private MColumn m_column = null;
/** Logger */
private static CLogger log = CLogger.getCLogger(WWFActivity.class);
//
private Label lNode = new Label(Msg.translate(Env.getCtx(), "AD_WF_Node_ID"));
private Textbox fNode = new Textbox();
private Label lDesctiption = new Label(Msg.translate(Env.getCtx(), "Description"));
private Textbox fDescription = new Textbox();
private Label lHelp = new Label(Msg.translate(Env.getCtx(), "Help"));
private Textbox fHelp = new Textbox();
private Label lHistory = new Label(Msg.translate(Env.getCtx(), "History"));
private Html fHistory = new Html();
private Label lAnswer = new Label(Msg.getMsg(Env.getCtx(), "Answer"));
private Textbox fAnswerText = new Textbox();
private Listbox fAnswerList = new Listbox();
private Button fAnswerButton = new Button();
private Button bZoom = new Button();
private Label lTextMsg = new Label(Msg.getMsg(Env.getCtx(), "Messages"));
private Textbox fTextMsg = new Textbox();
private Button bOK = new Button();
private WSearchEditor fForward = null; // dynInit
private Label lForward = new Label(Msg.getMsg(Env.getCtx(), "Forward"));
private Label lOptional = new Label("(" + Msg.translate(Env.getCtx(), "Optional") + ")");
private StatusBarPanel statusBar = new StatusBarPanel();
private ListModelTable model = null;
private WListbox listbox = new WListbox();
private final static String HISTORY_DIV_START_TAG = "<div style='width: 100%; height: 100px; border: 1px solid #7F9DB9;'>";
public WWFActivity()
{
super();
}
protected void initForm()
{
loadActivities();
fAnswerList.setMold("select");
bZoom.setImage("/images/Zoom16.png");
bOK.setImage("/images/Ok24.png");
MLookup lookup = MLookupFactory.get(Env.getCtx(), m_WindowNo,
0, 10443, DisplayType.Search);
fForward = new WSearchEditor(lookup, Msg.translate(
Env.getCtx(), "AD_User_ID"), "", true, false, true);
init();
display(-1);
}
private void init()
{
Grid grid = new Grid();
grid.setWidth("100%");
grid.setHeight("100%");
grid.setStyle("margin:0; padding:0; position: absolute; align: center; valign: center;");
grid.makeNoStrip();
grid.setOddRowSclass("even");
Rows rows = new Rows();
grid.appendChild(rows);
Row row = new Row();
rows.appendChild(row);
Div div = new Div();
div.setAlign("right");
div.appendChild(lNode);
row.appendChild(div);
row.appendChild(fNode);
fNode.setWidth("100%");
fNode.setReadonly(true);
row = new Row();
rows.appendChild(row);
row.setValign("top");
div = new Div();
div.setAlign("right");
div.appendChild(lDesctiption);
row.appendChild(div);
row.appendChild(fDescription);
fDescription.setMultiline(true);
fDescription.setWidth("100%");
fDescription.setReadonly(true);
row = new Row();
rows.appendChild(row);
div = new Div();
div.setAlign("right");
div.appendChild(lHelp);
row.appendChild(div);
row.appendChild(fHelp);
fHelp.setMultiline(true);
fHelp.setWidth("100%");
fHelp.setReadonly(true);
fHelp.setRows(3);
row.appendChild(new Label());
row = new Row();
rows.appendChild(row);
div = new Div();
div.setAlign("right");
div.appendChild(lHistory);
row.appendChild(div);
row.appendChild(fHistory);
row.appendChild(new Label());
row = new Row();
rows.appendChild(row);
div = new Div();
div.setAlign("right");
div.appendChild(lAnswer);
row.appendChild(div);
Hbox hbox = new Hbox();
hbox.appendChild(fAnswerText);
hbox.appendChild(fAnswerList);
hbox.appendChild(fAnswerButton);
fAnswerButton.addEventListener(Events.ON_CLICK, this);
row.appendChild(hbox);
row.appendChild(bZoom);
bZoom.addEventListener(Events.ON_CLICK, this);
row = new Row();
rows.appendChild(row);
div = new Div();
div.setAlign("right");
div.appendChild(lTextMsg);
row.appendChild(div);
row.appendChild(fTextMsg);
fTextMsg.setMultiline(true);
fTextMsg.setWidth("100%");
row.appendChild(new Label());
row = new Row();
rows.appendChild(row);
div = new Div();
div.setAlign("right");
div.appendChild(lForward);
row.appendChild(div);
hbox = new Hbox();
hbox.appendChild(fForward.getComponent());
hbox.appendChild(lOptional);
row.appendChild(hbox);
row.appendChild(bOK);
bOK.addEventListener(Events.ON_CLICK, this);
Borderlayout layout = new Borderlayout();
layout.setWidth("100%");
layout.setHeight("100%");
layout.setStyle("background-color: transparent; position: absolute;");
North north = new North();
north.appendChild(listbox);
north.setSplittable(true);
north.setFlex(true);
north.setHeight("50%");
layout.appendChild(north);
north.setStyle("background-color: transparent");
listbox.addEventListener(Events.ON_SELECT, this);
Center center = new Center();
center.appendChild(grid);
layout.appendChild(center);
center.setStyle("background-color: transparent");
center.setFlex(true);
South south = new South();
south.appendChild(statusBar);
layout.appendChild(south);
south.setStyle("background-color: transparent");
this.appendChild(layout);
this.setStyle("height: 100%; width: 100%; position: absolute;");
}
public void onEvent(Event event) throws Exception
{
Component comp = event.getTarget();
String eventName = event.getName();
if(eventName.equals(Events.ON_CLICK))
{
if (comp == bZoom)
cmd_zoom();
else if (comp == bOK)
{
Clients.showBusy(Msg.getMsg(Env.getCtx(), "Processing"), true);
Events.echoEvent("onOK", this, null);
}
else if (comp == fAnswerButton)
cmd_button();
} else if (Events.ON_SELECT.equals(eventName) && comp == listbox)
{
m_index = listbox.getSelectedIndex();
if (m_index >= 0)
display(m_index);
}
}
/**
* Get active activities count
* @return int
*/
public int getActivitiesCount()
{
int count = 0;
String sql = "SELECT count(*) FROM AD_WF_Activity a "
+ "WHERE a.Processed='N' AND a.WFState='OS' AND ("
// Owner of Activity
+ " a.AD_User_ID=?" // #1
// Invoker (if no invoker = all)
+ " OR EXISTS (SELECT * FROM AD_WF_Responsible r WHERE a.AD_WF_Responsible_ID=r.AD_WF_Responsible_ID"
+ " AND COALESCE(r.AD_User_ID,0)=0 AND COALESCE(r.AD_Role_ID,0)=0 AND (a.AD_User_ID=? OR a.AD_User_ID IS NULL))" // #2
// Responsible User
+ " OR EXISTS (SELECT * FROM AD_WF_Responsible r WHERE a.AD_WF_Responsible_ID=r.AD_WF_Responsible_ID"
+ " AND r.AD_User_ID=?)" // #3
// Responsible Role
+ " OR EXISTS (SELECT * FROM AD_WF_Responsible r INNER JOIN AD_User_Roles ur ON (r.AD_Role_ID=ur.AD_Role_ID)"
+ " WHERE a.AD_WF_Responsible_ID=r.AD_WF_Responsible_ID AND ur.AD_User_ID=?))"; // #4
//
//+ ") ORDER BY a.Priority DESC, Created";
int AD_User_ID = Env.getAD_User_ID(Env.getCtx());
MRole role = MRole.get(Env.getCtx(), Env.getAD_Role_ID(Env.getCtx()));
sql = role.addAccessSQL(sql, "a", true, false);
PreparedStatement pstmt = null;
ResultSet rs = null;
try
{
pstmt = DB.prepareStatement (sql, null);
pstmt.setInt (1, AD_User_ID);
pstmt.setInt (2, AD_User_ID);
pstmt.setInt (3, AD_User_ID);
pstmt.setInt (4, AD_User_ID);
rs = pstmt.executeQuery ();
if (rs.next ()) {
count = rs.getInt(1);
}
}
catch (Exception e)
{
log.log(Level.SEVERE, sql, e);
}
finally
{
DB.close(rs, pstmt);
rs = null; pstmt = null;
}
return count;
}
/**
* Load Activities
* @return int
*/
public int loadActivities()
{
long start = System.currentTimeMillis();
int MAX_ACTIVITIES_IN_LIST = MSysConfig.getIntValue("MAX_ACTIVITIES_IN_LIST", 200, Env.getAD_Client_ID(Env.getCtx()));
model = new ListModelTable();
ArrayList<MWFActivity> list = new ArrayList<MWFActivity>();
String sql = "SELECT * FROM AD_WF_Activity a "
+ "WHERE a.Processed='N' AND a.WFState='OS' AND ("
// Owner of Activity
+ " a.AD_User_ID=?" // #1
// Invoker (if no invoker = all)
+ " OR EXISTS (SELECT * FROM AD_WF_Responsible r WHERE a.AD_WF_Responsible_ID=r.AD_WF_Responsible_ID"
+ " AND COALESCE(r.AD_User_ID,0)=0 AND COALESCE(r.AD_Role_ID,0)=0 AND (a.AD_User_ID=? OR a.AD_User_ID IS NULL))" // #2
// Responsible User
+ " OR EXISTS (SELECT * FROM AD_WF_Responsible r WHERE a.AD_WF_Responsible_ID=r.AD_WF_Responsible_ID"
+ " AND r.AD_User_ID=?)" // #3
// Responsible Role
+ " OR EXISTS (SELECT * FROM AD_WF_Responsible r INNER JOIN AD_User_Roles ur ON (r.AD_Role_ID=ur.AD_Role_ID)"
+ " WHERE a.AD_WF_Responsible_ID=r.AD_WF_Responsible_ID AND ur.AD_User_ID=?)" // #4
//
+ ") ORDER BY a.Priority DESC, Created";
int AD_User_ID = Env.getAD_User_ID(Env.getCtx());
MRole role = MRole.get(Env.getCtx(), Env.getAD_Role_ID(Env.getCtx()));
sql = role.addAccessSQL(sql, "a", true, false);
PreparedStatement pstmt = null;
ResultSet rs = null;
try
{
pstmt = DB.prepareStatement (sql, null);
pstmt.setInt (1, AD_User_ID);
pstmt.setInt (2, AD_User_ID);
pstmt.setInt (3, AD_User_ID);
pstmt.setInt (4, AD_User_ID);
rs = pstmt.executeQuery ();
while (rs.next ())
{
MWFActivity activity = new MWFActivity(Env.getCtx(), rs, null);
list.add (activity);
List<Object> rowData = new ArrayList<Object>();
rowData.add(activity.getPriority());
rowData.add(activity.getNodeName());
rowData.add(activity.getSummary());
model.add(rowData);
if (list.size() > MAX_ACTIVITIES_IN_LIST && MAX_ACTIVITIES_IN_LIST > 0)
{
log.warning("More then 200 Activities - ignored");
break;
}
}
}
catch (Exception e)
{
log.log(Level.SEVERE, sql, e);
}
finally
{
DB.close(rs, pstmt);
rs = null; pstmt = null;
}
m_activities = new MWFActivity[list.size ()];
list.toArray (m_activities);
//
log.fine("#" + m_activities.length
+ "(" + (System.currentTimeMillis()-start) + "ms)");
m_index = 0;
String[] columns = new String[]{Msg.translate(Env.getCtx(), "Priority"),
Msg.translate(Env.getCtx(), "AD_WF_Node_ID"),
Msg.translate(Env.getCtx(), "Summary")};
WListItemRenderer renderer = new WListItemRenderer(Arrays.asList(columns));
ListHeader header = new ListHeader();
header.setWidth("30px");
renderer.setListHeader(0, header);
renderer.addTableValueChangeListener(listbox);
model.setNoColumns(columns.length);
listbox.setModel(model);
listbox.setItemRenderer(renderer);
listbox.repaint();
listbox.setFixedLayout(true);
return m_activities.length;
} // loadActivities
/**
* Reset Display
* @param selIndex select index
* @return selected activity
*/
private MWFActivity resetDisplay(int selIndex)
{
fAnswerText.setVisible(false);
fAnswerList.setVisible(false);
fAnswerButton.setVisible(false);
fAnswerButton.setImage("/images/mWindow.png");
fTextMsg.setReadonly(!(selIndex >= 0));
bZoom.setEnabled(selIndex >= 0);
bOK.setEnabled(selIndex >= 0);
fForward.setValue(null);
fForward.setReadWrite(selIndex >= 0);
//
statusBar.setStatusDB(String.valueOf(selIndex+1) + "/" + m_activities.length);
m_activity = null;
m_column = null;
if (m_activities.length > 0)
{
if (selIndex >= 0 && selIndex < m_activities.length)
m_activity = m_activities[selIndex];
}
// Nothing to show
if (m_activity == null)
{
fNode.setText ("");
fDescription.setText ("");
fHelp.setText ("");
fHistory.setContent(HISTORY_DIV_START_TAG + " </div>");
statusBar.setStatusDB("0/0");
statusBar.setStatusLine(Msg.getMsg(Env.getCtx(), "WFNoActivities"));
}
return m_activity;
} // resetDisplay
/**
* Display.
* Fill Editors
*/
public void display (int index)
{
log.fine("Index=" + index);
//
m_activity = resetDisplay(index);
// Nothing to show
if (m_activity == null)
{
return;
}
// Display Activity
fNode.setText (m_activity.getNodeName());
fDescription.setValue (m_activity.getNodeDescription());
fHelp.setValue (m_activity.getNodeHelp());
//
fHistory.setContent (HISTORY_DIV_START_TAG+m_activity.getHistoryHTML()+"</div>");
// User Actions
MWFNode node = m_activity.getNode();
if (MWFNode.ACTION_UserChoice.equals(node.getAction()))
{
if (m_column == null)
m_column = node.getColumn();
if (m_column != null && m_column.get_ID() != 0)
{
fAnswerList.removeAllItems();
int dt = m_column.getAD_Reference_ID();
if (dt == DisplayType.YesNo)
{
ValueNamePair[] values = MRefList.getList(Env.getCtx(), 319, false); // _YesNo
for(int i = 0; i < values.length; i++)
{
fAnswerList.appendItem(values[i].getName(), values[i].getValue());
}
fAnswerList.setVisible(true);
}
else if (dt == DisplayType.List)
{
ValueNamePair[] values = MRefList.getList(Env.getCtx(), m_column.getAD_Reference_Value_ID(), false);
for(int i = 0; i < values.length; i++)
{
fAnswerList.appendItem(values[i].getName(), values[i].getValue());
}
fAnswerList.setVisible(true);
}
else // other display types come here
{
fAnswerText.setText ("");
fAnswerText.setVisible(true);
}
}
}
// --
else if (MWFNode.ACTION_UserWindow.equals(node.getAction())
|| MWFNode.ACTION_UserForm.equals(node.getAction()))
{
fAnswerButton.setLabel(node.getName());
fAnswerButton.setTooltiptext(node.getDescription());
fAnswerButton.setVisible(true);
}
else
log.log(Level.SEVERE, "Unknown Node Action: " + node.getAction());
statusBar.setStatusDB((m_index+1) + "/" + m_activities.length);
statusBar.setStatusLine(Msg.getMsg(Env.getCtx(), "WFActivities"));
} // display
/**
* Zoom
*/
private void cmd_zoom()
{
log.config("Activity=" + m_activity);
if (m_activity == null)
return;
AEnv.zoom(m_activity.getAD_Table_ID(), m_activity.getRecord_ID());
} // cmd_zoom
/**
* Answer Button
*/
private void cmd_button()
{
log.config("Activity=" + m_activity);
if (m_activity == null)
return;
//
MWFNode node = m_activity.getNode();
if (MWFNode.ACTION_UserWindow.equals(node.getAction()))
{
int AD_Window_ID = node.getAD_Window_ID(); // Explicit Window
String ColumnName = m_activity.getPO().get_TableName() + "_ID";
int Record_ID = m_activity.getRecord_ID();
MQuery query = MQuery.getEqualQuery(ColumnName, Record_ID);
boolean IsSOTrx = m_activity.isSOTrx();
//
log.info("Zoom to AD_Window_ID=" + AD_Window_ID
+ " - " + query + " (IsSOTrx=" + IsSOTrx + ")");
AEnv.zoom(AD_Window_ID, query);
}
else if (MWFNode.ACTION_UserForm.equals(node.getAction()))
{
int AD_Form_ID = node.getAD_Form_ID();
Window form = ADForm.openForm(AD_Form_ID);
AEnv.showWindow(form);
}
else
log.log(Level.SEVERE, "No User Action:" + node.getAction());
} // cmd_button
/**
* Save
*/
public void onOK()
{
log.config("Activity=" + m_activity);
if (m_activity == null)
{
Clients.showBusy(null, false);
return;
}
int AD_User_ID = Env.getAD_User_ID(Env.getCtx());
String textMsg = fTextMsg.getValue();
//
MWFNode node = m_activity.getNode();
Object forward = null;//fForward.getValue();
// ensure activity is ran within a transaction - [ 1953628 ]
Trx trx = null;
try {
trx = Trx.get(Trx.createTrxName("FWFA"), true);
m_activity.set_TrxName(trx.getTrxName());
if (forward != null)
{
log.config("Forward to " + forward);
int fw = ((Integer)forward).intValue();
if (fw == AD_User_ID || fw == 0)
{
log.log(Level.SEVERE, "Forward User=" + fw);
trx.rollback();
trx.close();
return;
}
if (!m_activity.forwardTo(fw, textMsg))
{
FDialog.error(m_WindowNo, this, "CannotForward");
trx.rollback();
trx.close();
return;
}
}
// User Choice - Answer
else if (MWFNode.ACTION_UserChoice.equals(node.getAction()))
{
if (m_column == null)
m_column = node.getColumn();
// Do we have an answer?
int dt = m_column.getAD_Reference_ID();
String value = fAnswerText.getText();
if (dt == DisplayType.YesNo || dt == DisplayType.List)
{
ListItem li = fAnswerList.getSelectedItem();
if(li != null) value = li.getValue().toString();
}
if (value == null || value.length() == 0)
{
FDialog.error(m_WindowNo, this, "FillMandatory", Msg.getMsg(Env.getCtx(), "Answer"));
trx.rollback();
trx.close();
return;
}
//
log.config("Answer=" + value + " - " + textMsg);
try
{
m_activity.setUserChoice(AD_User_ID, value, dt, textMsg);
}
catch (Exception e)
{
log.log(Level.SEVERE, node.getName(), e);
FDialog.error(m_WindowNo, this, "Error", e.toString());
trx.rollback();
trx.close();
return;
}
}
// User Action
else
{
log.config("Action=" + node.getAction() + " - " + textMsg);
try
{
// ensure activity is ran within a transaction
m_activity.setUserConfirmation(AD_User_ID, textMsg);
}
catch (Exception e)
{
log.log(Level.SEVERE, node.getName(), e);
FDialog.error(m_WindowNo, this, "Error", e.toString());
trx.rollback();
trx.close();
return;
}
}
trx.commit();
}
finally
{
Clients.showBusy(null, false);
if (trx != null)
trx.close();
}
// Next
loadActivities();
display(-1);
} // onOK
}