/****************************************************************************** * Product: Adempiere ERP & CRM Smart Business Solution * * Copyright (C) 1999-2006 Adempiere, Inc. 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 * * 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; import java.math.BigDecimal; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Timestamp; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.logging.Level; import org.adempiere.webui.Extensions; import org.adempiere.webui.LayoutUtils; import org.adempiere.webui.component.Button; import org.adempiere.webui.component.Column; import org.adempiere.webui.component.Columns; import org.adempiere.webui.component.EditorBox; import org.adempiere.webui.component.Grid; import org.adempiere.webui.component.GridFactory; import org.adempiere.webui.component.Group; import org.adempiere.webui.component.NumberBox; import org.adempiere.webui.component.Panel; import org.adempiere.webui.component.Row; import org.adempiere.webui.component.Rows; import org.adempiere.webui.component.Urlbox; import org.adempiere.webui.editor.DateRangeEditor; import org.adempiere.webui.editor.IZoomableEditor; import org.adempiere.webui.editor.WEditor; import org.adempiere.webui.editor.WEditorPopupMenu; import org.adempiere.webui.editor.WSearchEditor; import org.adempiere.webui.editor.WebEditorFactory; import org.adempiere.webui.event.ContextMenuListener; import org.adempiere.webui.event.ValueChangeEvent; import org.adempiere.webui.event.ValueChangeListener; import org.adempiere.webui.factory.ButtonFactory; import org.adempiere.webui.session.SessionManager; import org.adempiere.webui.util.ZKUpdateUtil; import org.adempiere.webui.window.DateRangeButton; import org.adempiere.webui.window.Dialog; import org.compiere.apps.IProcessParameter; import org.compiere.model.GridField; import org.compiere.model.GridFieldVO; import org.compiere.model.MClient; import org.compiere.model.MLookup; import org.compiere.model.MPInstance; import org.compiere.model.MPInstancePara; import org.compiere.model.MProcess; import org.compiere.model.MProcessPara; import org.compiere.model.X_AD_FieldGroup; import org.compiere.process.ProcessInfo; import org.compiere.process.ProcessInfoParameter; import org.compiere.util.CLogger; import org.compiere.util.DB; import org.compiere.util.DisplayType; import org.compiere.util.Env; import org.compiere.util.Evaluatee; import org.compiere.util.Msg; import org.compiere.util.Util; import org.zkoss.zk.ui.Component; import org.zkoss.zk.ui.HtmlBasedComponent; import org.zkoss.zk.ui.WrongValueException; import org.zkoss.zk.ui.WrongValuesException; import org.zkoss.zk.ui.event.Event; import org.zkoss.zk.ui.event.EventListener; import org.zkoss.zk.ui.event.Events; import org.zkoss.zul.Cell; import org.zkoss.zul.Div; import org.zkoss.zul.Label; import org.zkoss.zul.Separator; import org.zkoss.zul.Space; import org.zkoss.zul.impl.InputElement; import org.zkoss.zul.impl.XulElement; /** * Process Parameter Panel. * Embedded in {@link ProcessDialog} and {@link ProcessModalDialog}. * Capture parameters input, validate and save to DB. * * @author Low Heng Sin * @version 2006-12-01 */ public class ProcessParameterPanel extends Panel implements ValueChangeListener, IProcessParameter, EventListener, Evaluatee { /** * generated serial id */ private static final long serialVersionUID = -8476698839617674953L; /** Event post from {@link #valueChange(ValueChangeEvent)} **/ private static final String ON_POST_EDITOR_VALUE_CHANGE_EVENT = "onPostEditorValueChange"; /** * Dynamic generated Parameter panel. * * @param WindowNo register window number * @param pi process info */ public ProcessParameterPanel(int WindowNo, ProcessInfo pi) { this(WindowNo, 0, pi); } /** * Dynamic generated Parameter panel. * * @param WindowNo register window number * @param tabNo tabNo * @param pi process info */ public ProcessParameterPanel(int WindowNo,int tabNo, ProcessInfo pi) { // m_WindowNo = WindowNo; m_TabNo = tabNo; m_processInfo = pi; m_AD_Window_ID = AEnv.getADWindowID (WindowNo); this.m_InfoWindowID = pi.getAD_InfoWindow_ID(); // initComponent(); addEventListener("onDynamicDisplay", this); addEventListener(ON_POST_EDITOR_VALUE_CHANGE_EVENT, this); } // ProcessParameterPanel /** * Layout UI */ private void initComponent() { centerPanel = GridFactory.newGridLayout(); this.appendChild(centerPanel); // setup columns Columns columns = new Columns(); centerPanel.appendChild(columns); Column col = new Column(); ZKUpdateUtil.setWidth(col, "30%"); columns.appendChild(col); col = new Column(); ZKUpdateUtil.setWidth(col, "70%"); columns.appendChild(col); } private int m_WindowNo; private int m_TabNo; private ProcessInfo m_processInfo; /** AD_Window_ID if process dialog is launch by AD_Window **/ private int m_AD_Window_ID = 0; /** Info_Window_ID if process dialog is launch by Info Window **/ private int m_InfoWindowID = 0; /** Logger */ private static final CLogger log = CLogger .getCLogger(ProcessParameterPanel.class); /** parameter editor list **/ private ArrayList m_wEditors = new ArrayList(); /** to parameter editor list for range parameter **/ private ArrayList m_wEditors2 = new ArrayList(); /** parameter field list **/ private ArrayList m_mFields = new ArrayList(); /** to parameter field list for range parameter **/ private ArrayList m_mFields2 = new ArrayList(); /** list of separators **/ private ArrayList m_separators = new ArrayList(); /** all rows of {@link #centerPanel} **/ private ArrayList m_Rows = new ArrayList(); /** list of all date range editors **/ private ArrayList m_dateRangeEditors = new ArrayList(); // /** layout grid for parameter fields **/ private Grid centerPanel = null; /** Group Name:Rows for parameter field **/ private Map> fieldGroupContents = new HashMap>(); /** Group Name:Rows for group header **/ private Map> fieldGroupHeaders = new HashMap>(); /** rows of current rendering group **/ private ArrayList rowList; /** all groups of field type collapsible or tab **/ private List allCollapsibleGroups = new ArrayList(); /** current rendering group **/ private Group currentGroup; /** * Dispose */ public void dispose() { m_wEditors.clear(); m_wEditors2.clear(); m_mFields.clear(); m_mFields2.clear(); } // dispose /** * Render all visible fields * * @return true if loaded OK */ public boolean init() { if (log.isLoggable(Level.CONFIG)) log.config(""); // ASP MClient client = MClient.get(Env.getCtx()); String ASPFilter = ""; if (client.isUseASP()) ASPFilter = " AND ( p.AD_Process_Para_ID not IN ( " // Just ASP subscribed process parameters for client " + " SELECT pp.AD_Process_Para_ID " + " FROM ASP_Process_Para pp, ASP_Process p, ASP_Level l, ASP_ClientLevel cl " + " WHERE p.ASP_Level_ID = l.ASP_Level_ID " + " AND cl.AD_Client_ID = " + client.getAD_Client_ID() + " AND cl.ASP_Level_ID = l.ASP_Level_ID " + " AND pp.ASP_Process_ID = p.ASP_Process_ID " + " AND pp.IsActive = 'Y' " + " AND p.IsActive = 'Y' " + " AND l.IsActive = 'Y' " + " AND cl.IsActive = 'Y' " + " AND p.ad_process_ID="+m_processInfo.getAD_Process_ID() + " AND pp.ASP_Status = 'H' " // Show +" AND pp.AD_Process_Para_ID not in (" +" SELECT AD_Process_Para_ID" +" FROM ASP_ClientException ce" +" WHERE ce.AD_Client_ID ="+ client.getAD_Client_ID() +" AND ce.IsActive = 'Y'" +" AND ce.AD_Process_Para_ID IS NOT NULL" +" AND ce.AD_Tab_ID IS NULL" +" AND ce.AD_Field_ID IS NULL" +" AND ce.ASP_Status in('S','U') ) " + " UNION ALL " // minus hide ASP exceptions for client + " SELECT AD_Process_Para_ID " + " FROM ASP_ClientException ce " + " WHERE ce.AD_Client_ID = " + client.getAD_Client_ID() + " AND ce.IsActive = 'Y' " + " AND ce.AD_Process_Para_ID IS NOT NULL " + " AND ce.AD_Tab_ID IS NULL " + " AND ce.AD_Process_Para_ID="+m_processInfo.getAD_Process_ID() + " AND ce.AD_Field_ID IS NULL " + " AND ce.ASP_Status = 'H'))"; // Hide // String sql = null; if (Env.isBaseLanguage(Env.getCtx(), "AD_Process_Para")) sql = "SELECT p.Name, p.Description, p.Help, " + "p.AD_Reference_ID, p.AD_Process_Para_ID, " + "p.FieldLength, p.IsMandatory, p.IsRange, p.dateRangeOption, p.ColumnName, " + "p.DefaultValue, p.DefaultValue2, p.VFormat, p.ValueMin, p.ValueMax, " + "p.SeqNo, p.AD_Reference_Value_ID, vr.Code AS ValidationCode, " + "p.ReadOnlyLogic, p.DisplayLogic, p.IsEncrypted, NULL AS FormatPattern, p.MandatoryLogic, p.Placeholder, p.Placeholder2, p.isAutoComplete, " + "'' AS ValidationCodeLookup, " + "fg.Name AS FieldGroup, fg.FieldGroupType, fg.IsCollapsedByDefault, p.IsShowNegateButton " + "FROM AD_Process_Para p" + " LEFT OUTER JOIN AD_Val_Rule vr ON (p.AD_Val_Rule_ID=vr.AD_Val_Rule_ID) " + " LEFT OUTER JOIN AD_FieldGroup fg ON (p.AD_FieldGroup_ID=fg.AD_FieldGroup_ID) " + "WHERE p.AD_Process_ID=?" // 1 + " AND p.IsActive='Y' " + ASPFilter + " ORDER BY SeqNo"; else sql = "SELECT t.Name, t.Description, t.Help, " + "p.AD_Reference_ID, p.AD_Process_Para_ID, " + "p.FieldLength, p.IsMandatory, p.IsRange, p.dateRangeOption, p.ColumnName, " + "p.DefaultValue, p.DefaultValue2, p.VFormat, p.ValueMin, p.ValueMax, " + "p.SeqNo, p.AD_Reference_Value_ID, vr.Code AS ValidationCode, " + "p.ReadOnlyLogic, p.DisplayLogic, p.IsEncrypted, NULL AS FormatPattern,p.MandatoryLogic, t.Placeholder, t.Placeholder2, p.isAutoComplete, " + "'' AS ValidationCodeLookup, " + "fgt.Name AS FieldGroup, fg.FieldGroupType, fg.IsCollapsedByDefault, p.IsShowNegateButton " + "FROM AD_Process_Para p" + " INNER JOIN AD_Process_Para_Trl t ON (p.AD_Process_Para_ID=t.AD_Process_Para_ID)" + " LEFT OUTER JOIN AD_Val_Rule vr ON (p.AD_Val_Rule_ID=vr.AD_Val_Rule_ID) " + " LEFT OUTER JOIN AD_FieldGroup fg ON (p.AD_FieldGroup_ID=fg.AD_FieldGroup_ID) " + " LEFT OUTER JOIN AD_FieldGroup_Trl fgt ON (p.AD_FieldGroup_ID=fgt.AD_FieldGroup_ID AND fgt.AD_Language='" + Env.getAD_Language(Env.getCtx()) + "') " + "WHERE p.AD_Process_ID=?" // 1 + " AND t.AD_Language='" + Env.getAD_Language(Env.getCtx()) + "'" + " AND p.IsActive='Y' " + ASPFilter + " ORDER BY SeqNo"; // Create Fields boolean hasFields = false; String currentFieldGroup = null; Rows rows = new Rows(); Row row = new Row(); PreparedStatement pstmt = null; ResultSet rs = null; try { pstmt = DB.prepareStatement(sql, null); pstmt.setInt(1, m_processInfo.getAD_Process_ID()); rs = pstmt.executeQuery(); ArrayList listVO = new ArrayList(); ListtoCollapsed = new ArrayList(); while (rs.next()) { hasFields = true; // Create Field GridFieldVO voF = GridFieldVO.createParameter(Env.getCtx(), m_WindowNo, m_TabNo, m_processInfo.getAD_Process_ID(), m_AD_Window_ID, m_InfoWindowID,rs); listVO.add(voF); } Collections.sort(listVO, new GridFieldVO.SeqNoComparator()); for (int i = 0; i < listVO.size(); i++) { GridFieldVO voF = listVO.get(i); GridField field = new GridField(voF); m_mFields.add(field); // add to Fields field.setParentEvaluatee(this); String fieldGroup = field.getFieldGroup(); if (!Util.isEmpty(fieldGroup) && !fieldGroup.equals(currentFieldGroup) && !X_AD_FieldGroup.FIELDGROUPTYPE_DoNothing.equals(field.getFieldGroupType())) // group changed { currentFieldGroup = fieldGroup; row.setGroup(currentGroup); rows.appendChild(row); if (rowList != null) rowList.add(row); List headerRows = new ArrayList(); fieldGroupHeaders.put(fieldGroup, headerRows); rowList = new ArrayList(); fieldGroupContents.put(fieldGroup, rowList); if (X_AD_FieldGroup.FIELDGROUPTYPE_Label.equals(field.getFieldGroupType())) { row = new Row(); Label groupLabel = new Label(fieldGroup); row.appendCellChild(groupLabel, 3); rows.appendChild(row); headerRows.add(row); row = new Row(); Separator separator = new Separator(); separator.setBar(true); row.appendCellChild(separator, 3); rows.appendChild(row); headerRows.add(row); currentGroup = null; } else { Group rowg = new Group(fieldGroup); Cell cell = (Cell) rowg.getFirstChild(); cell.setSclass("z-group-inner"); cell.setColspan(3); allCollapsibleGroups.add(rowg); if (X_AD_FieldGroup.FIELDGROUPTYPE_Tab.equals(field.getFieldGroupType()) || field.getIsCollapsedByDefault()) { toCollapsed.add(rowg); } currentGroup = rowg; rows.appendChild(rowg); headerRows.add(rowg); } row = new Row(); } row = new Row(); createField(voF, field, row); row.setGroup(currentGroup); rows.appendChild(row); m_Rows.add(row); if (rowList != null) rowList.add(row); if (log.isLoggable(Level.INFO)) log.info(listVO.get(i).ColumnName + listVO.get(i).SeqNo); } if (toCollapsed.size() > 0) toCollapsed.stream().forEach(g -> g.setOpen(false)); } catch (SQLException e) { log.log(Level.SEVERE, sql, e); } finally{ DB.close(rs,pstmt); rs = null; pstmt = null; } // both vectors the same? if (m_mFields.size() != m_mFields2.size() || m_mFields.size() != m_wEditors.size() || m_mFields2.size() != m_wEditors2.size()) log.log(Level.SEVERE, "View & Model vector size is different"); // clean up if (hasFields) { centerPanel.appendChild(rows); dynamicDisplay(); } else dispose(); return hasFields; } // initDialog /** * Create editor and adds it to {@link #m_wEditors}. *
* For range type field, create the to field and add it to {@link #m_mFields2} and * create the to editor and adds it to {@link #m_wEditors2}. *

* mField is used for default value and mandatory checking and editor is * used to capture input value from user (no data binding). * * @param voF GridFieldVO * @param mField * @param row */ private void createField(GridFieldVO voF, GridField mField, Row row) { // The Editor WEditor editor = WebEditorFactory.getEditor(mField, false); editor.setProcessParameter(true); editor.getComponent().addEventListener(Events.ON_FOCUS, this); editor.addValueChangeListener(this); editor.dynamicDisplay(); // MField => editor - New Field value to be updated to editor mField.addPropertyChangeListener(editor); // Set Default Object defaultObject = mField.getDefaultForPanel(); mField.setValue(defaultObject, true); // streach component to fill grid cell editor.fillHorizontal(); // setup editor context menu WEditorPopupMenu popupMenu = editor.getPopupMenu(); if (popupMenu != null) { popupMenu.addMenuListener((ContextMenuListener)editor); popupMenu.setId(mField.getColumnName()+"-popup"); this.appendChild(popupMenu); if (!mField.isFieldOnly()) { Label label = editor.getLabel(); if (popupMenu.isZoomEnabled() && editor instanceof IZoomableEditor) { label.addEventListener(Events.ON_CLICK, new ZoomListener((IZoomableEditor) editor)); } popupMenu.addContextElement(label); if (editor.getComponent() instanceof XulElement) { popupMenu.addContextElement((XulElement) editor.getComponent()); } } } // m_wEditors.add(editor); // add to Editors Div div = new Div(); div.setStyle("text-align: right;"); org.adempiere.webui.component.Label label = editor.getLabel(); div.appendChild(label); if (label.getDecorator() != null) div.appendChild(label.getDecorator()); row.appendChild(div); // Div box = new Div(); box.setStyle("display: flex; align-items: center;"); ZKUpdateUtil.setWidth(box, "100%"); //create to field and editor if (voF.isRange) { box.appendChild(editor.getComponent()); ZKUpdateUtil.setWidth((HtmlBasedComponent) editor.getComponent(), "49%"); // GridFieldVO voF2 = GridFieldVO.createParameter(voF); GridField mField2 = new GridField(voF2); m_mFields2.add(mField2); // The Editor WEditor editor2 = WebEditorFactory.getEditor(mField2, false); editor2.setProcessParameter(true); //override attribute editor2.getComponent().setClientAttribute("columnName", mField2.getColumnName()+"_To"); editor2.getComponent().addEventListener(Events.ON_FOCUS, this); // New Field value to be updated to editor mField2.addPropertyChangeListener(editor2); editor2.addValueChangeListener(this); editor2.dynamicDisplay(); ZKUpdateUtil.setWidth((HtmlBasedComponent) editor2.getComponent(), "49%"); setEditorPlaceHolder(editor2, mField2.getPlaceholder2()); // setup editor context menu popupMenu = editor2.getPopupMenu(); if (popupMenu != null) { popupMenu.addMenuListener((ContextMenuListener) editor2); this.appendChild(popupMenu); } // Set Default Object defaultObject2 = mField2.getDefaultForPanel(); mField2.setValue(defaultObject2, true); // m_wEditors2.add(editor2); Space separator = new Space(); separator.setStyle("margin:0; width: 2%;"); m_separators.add(separator); box.appendChild(separator); box.appendChild(editor2.getComponent()); row.appendChild(box); if (((mField.getDisplayType() == DisplayType.Date) || (mField.getDisplayType() == DisplayType.DateTime)) && ((mField2.getDisplayType() == DisplayType.Date) || (mField2.getDisplayType() == DisplayType.DateTime))) { if(MProcessPara.DATERANGEOPTION_TextAndRangePicker.equalsIgnoreCase(mField.getDateRangeOption())) { editor.setVisible(false, true); editor2.setVisible(false, true); DateRangeEditor dateRangeEditor = new DateRangeEditor(editor, editor2); box.appendChild(dateRangeEditor); dateRangeEditor.setVisible(mField.isDisplayed(true)); label.setVisible(dateRangeEditor.isVisible()); dateRangeEditor.setReadOnly(!(editor.isReadWrite() && editor2.isReadWrite())); m_dateRangeEditors.add(dateRangeEditor); } else { DateRangeButton dateRangeButton = new DateRangeButton(editor, editor2); box.appendChild(dateRangeButton); m_dateRangeEditors.add(null); } } else { m_dateRangeEditors.add(null); } } else { box.appendChild(editor.getComponent()); m_mFields2.add(null); m_wEditors2.add(null); m_separators.add(null); m_dateRangeEditors.add(null); //add not in support for multi selection field if(DisplayType.isChosenMultipleSelection(mField.getDisplayType()) && voF.IsShowNegateButton) { Button bNegate = ButtonFactory.createButton("", null, null); bNegate.setTooltiptext(Msg.translate(Env.getCtx(), "IncludeSelectedValues")); bNegate.setIconSclass("z-icon-IncludeSelected"); bNegate.setSclass("btn-negate btn-negate-include"); bNegate.setAttribute("isSelected", false); bNegate.setVisible(false); bNegate.addActionListener(this); box.appendChild(bNegate); editor.getComponent().setAttribute("isNotClause", bNegate); } } row.appendChild(box); } // createField /** * set place holder message * @param editor * @param msg */ private void setEditorPlaceHolder(WEditor editor, String msg) { Component c = editor.getComponent(); if (c instanceof InputElement) { ((InputElement) c).setPlaceholder(msg); } else { for (Component e : c.getChildren()) { if (e instanceof InputElement) { ((InputElement) e).setPlaceholder(msg); break; } } } } /** * Validate Parameter values * * @return true if parameters are valid */ public boolean validateParameters() { if (log.isLoggable(Level.CONFIG)) log.config(""); //mandatory fields validation Map wrongValidateComponents = new HashMap<>(); int size = m_mFields.size(); for (int i = 0; i < size; i++) { GridField field = (GridField) m_mFields.get(i); GridField field2 = (GridField) m_mFields2.get(i); WEditor wEditor = (WEditor) m_wEditors.get(i); if (wEditor.getComponent() instanceof InputElement) ((InputElement)wEditor.getComponent()).clearErrorMessage(); WEditor wEditor2 = (WEditor) m_wEditors2.get(i); if (wEditor2 != null && wEditor2.getComponent() instanceof InputElement) ((InputElement)wEditor2.getComponent()).clearErrorMessage(); Object data = wEditor.getValue(); String msg = validate(data, field.getValueMin(), field.getValueMax(), field.isMandatory(true), field.getDisplayType()); if (msg != null) { field.setInserting(true); // set editable (i.e. updateable) otherwise deadlock field.setError(true); wrongValidateComponents.put(wEditor.getComponent(), msg); } if (m_wEditors2.get(i) != null) { // is a range data = wEditor2.getValue(); msg = validate(data, field.getValueMin(), field.getValueMax(), field.isMandatory(true), field.getDisplayType()); if (msg != null) { field2.setInserting(true); // set editable (i.e. updateable) otherwise deadlock field2.setError(true); wrongValidateComponents.put(wEditor2.getComponent(), msg); } } } // field loop List wrongValues = new ArrayList(); wrongValidateComponents.forEach((component, msg) -> { WrongValueException wrongValueException = new WrongValueException(component, msg); wrongValues.add(wrongValueException); }); if (wrongValues.size() > 0) throw new WrongValuesException(wrongValues.toArray(new WrongValueException[0])); /** call {@link IProcessParameterListener} validate(ProcessParameterPanel) **/ if (m_processInfo.getAD_Process_ID() > 0) { String className = MProcess.get(Env.getCtx(), m_processInfo.getAD_Process_ID()).getClassname(); List listeners = Extensions.getProcessParameterListeners(className, null); for(IProcessParameterListener listener : listeners) { String error = listener.validate(this); if (!Util.isEmpty(error)) { Dialog.error(m_WindowNo, error); return false; } } } return true; } // validateParameters /** * Validate mandatory and min/max value * @param value * @param valueMin * @param valueMax * @param isMandatory * @param fieldType * @return null if OK, any message if not OK */ public static String validate(Object value, String valueMin, String valueMax, boolean isMandatory, int fieldType) { if (isMandatory) { if (value == null || value.toString().length() == 0) { return Msg.getMsg(Env.getCtx(), "FillMandatory"); } } BigDecimal value_BD = null; BigDecimal valueMin_BD = null; BigDecimal valueMax_BD = null; Timestamp value_TS = null; Timestamp valueMin_TS = null; Timestamp valueMax_TS = null; if (fieldType == DisplayType.Date) { SimpleDateFormat dateFormat = new SimpleDateFormat(DisplayType.DEFAULT_DATE_FORMAT); if (value != null) { try { value_TS = new Timestamp(dateFormat.parse(value.toString()).getTime()); } catch (Exception ex){} } if (valueMin != null) { try { valueMin_TS = new Timestamp(dateFormat.parse(valueMin).getTime()); } catch (Exception ex){} } if (valueMax != null) { try { valueMax_TS = new Timestamp(dateFormat.parse(valueMax).getTime()); } catch (Exception ex){} } if (value_TS != null && valueMin_TS != null && value_TS.before(valueMin_TS)) return Msg.getMsg(Env.getCtx(), "LessThanMinValue", new Object[] {valueMin}); if (value_TS != null && valueMax_TS != null && value_TS.after(valueMax_TS)) return Msg.getMsg(Env.getCtx(), "MoreThanMaxValue", new Object[] {valueMax}); } else if (DisplayType.isNumeric(fieldType)) { if (value != null) { try { value_BD = new BigDecimal(value.toString()); } catch (Exception ex){} } if (valueMin != null) { try { valueMin_BD = new BigDecimal(valueMin); } catch (Exception ex){} } if (valueMax != null) { try { valueMax_BD = new BigDecimal(valueMax); } catch (Exception ex){} } if (value_BD != null && valueMin_BD != null && valueMin_BD.compareTo(value_BD) > 0) return Msg.getMsg(Env.getCtx(), "LessThanMinValue", new Object[] {valueMin}); if (value_BD != null && valueMax_BD != null && valueMax_BD.compareTo(value_BD) < 0) return Msg.getMsg(Env.getCtx(), "MoreThanMaxValue", new Object[] {valueMax}); } else { if (value != null && valueMin != null && valueMin.compareTo(value.toString()) > 0) return Msg.getMsg(Env.getCtx(), "LessThanMinValue", new Object[] {valueMin}); if (value != null && valueMax != null && valueMax.compareTo(value.toString()) < 0) return Msg.getMsg(Env.getCtx(), "MoreThanMaxValue", new Object[] {valueMax}); } return null; } /** * load parameters from saved instance * @param instance */ public boolean loadParameters(MPInstance instance) { if (log.isLoggable(Level.CONFIG)) log.config(""); MPInstancePara[] params = instance.getParameters(); for (int j = 0; j < m_mFields.size(); j++) { GridField mField = (GridField)m_mFields.get(j); if (!mField.isEditablePara(true)) continue; // Saved parameters must not change read-only parameters // Get Values WEditor editor = (WEditor)m_wEditors.get(j); WEditor editor2 = (WEditor)m_wEditors2.get(j); editor.setValue(null); if (editor2 != null) editor2.setValue(null); for ( int i = 0; i paras = new ArrayList(); /** create MPInstancePara from editors and add to paras (without saving MPInstancePara to DB) **/ for (int i = 0; i < m_mFields.size(); i++) { // Get Values WEditor editor = (WEditor) m_wEditors.get(i); WEditor editor2 = (WEditor) m_wEditors2.get(i); Object result = editor.getValue(); Object result2 = null; if (editor2 != null) result2 = editor2.getValue(); // Create Parameter MPInstancePara para = new MPInstancePara(Env.getCtx(), 0, i); GridField mField = (GridField) m_mFields.get(i); para.setParameterName(mField.getColumnName()); para.setP_Date(null); para.setP_Date_To(null); para.setP_Number((BigDecimal)null); para.setP_Number_To((BigDecimal)null); para.setP_String(null); para.setP_String_To(null); // Date if (result instanceof Timestamp || result2 instanceof Timestamp) { if (result instanceof Timestamp) para.setP_Date((Timestamp) result); if (editor2 != null && result2 != null && result2 instanceof Timestamp) para.setP_Date_To((Timestamp) result2); } // Integer else if (result instanceof Integer || result2 instanceof Integer) { if (result != null && result instanceof Integer) { Integer ii = (Integer) result; para.setP_Number(ii.intValue()); } if (editor2 != null && result2 != null && result2 instanceof Integer) { Integer ii = (Integer) result2; para.setP_Number_To(ii.intValue()); } } // BigDecimal else if (result instanceof BigDecimal || result2 instanceof BigDecimal) { if (result instanceof BigDecimal) para.setP_Number((BigDecimal) result); if (editor2 != null && result2 != null && result2 instanceof BigDecimal) para.setP_Number_To((BigDecimal) result2); } // Boolean else if (result instanceof Boolean) { Boolean bb = (Boolean) result; String value = bb.booleanValue() ? "Y" : "N"; para.setP_String(value); // to does not make sense } // String else { if (result != null) para.setP_String(result.toString()); if (editor2 != null && result2 != null) para.setP_String_To(result2.toString()); } // Info para.setInfo(editor.getDisplay()); if (editor2 != null) para.setInfo_To(editor2.getDisplay()); // paras.add(para); } // for every parameter return paras.toArray(new MPInstancePara[0]); } // saveParameters /** * Editor value change listener. * * @param evt ValueChangeEvent */ public void valueChange(ValueChangeEvent evt) { String propName = evt.getPropertyName(); if (evt.getSource() instanceof WEditor) { WEditor editor = (WEditor) evt.getSource(); if (m_wEditors2.contains(editor)) { // is a _To editor for ranges propName += "_2"; // same as web services } GridField changedField = editor.getGridField(); if (changedField != null) { processDependencies (changedField); // future processCallout (changedField); } Events.postEvent(ON_POST_EDITOR_VALUE_CHANGE_EVENT, this, evt.getSource()); } processNewValue(evt.getNewValue(), propName); } @Override public void onEvent(Event event) throws Exception { if (event.getName().equals(Events.ON_FOCUS)) { //update tooltip text inside desktop help panel. for (WEditor editor : m_wEditors) { if (editor.isComponentOfEditor(event.getTarget())) { SessionManager.getAppDesktop().updateHelpTooltip(editor.getGridField()); return; } } for (WEditor editor : m_wEditors2) { if (editor != null && editor.getComponent() != null && editor.isComponentOfEditor(event.getTarget())) { SessionManager.getAppDesktop().updateHelpTooltip(editor.getGridField()); return; } } } else if (event.getName().equals("onDynamicDisplay")) { dynamicDisplay(); } else if (event.getName().equals(ON_POST_EDITOR_VALUE_CHANGE_EVENT)) { WEditor editor = (WEditor)event.getData(); onPostEditorValueChange(editor); if(editor.getComponent() != null) { Button bNegate = (Button) editor.getComponent().getAttribute("isNotClause"); if (bNegate != null) { if (editor.getValue() != null) { bNegate.setVisible(true); } else { bNegate.setVisible(false); bNegate.setAttribute("isSelected", false); bNegate.setTooltiptext(Msg.translate(Env.getCtx(), "IncludeSelectedValues")); bNegate.setIconSclass("z-icon-IncludeSelected"); bNegate.setSclass("btn-negate btn-negate-include"); } } } } else if (event.getName().equals(Events.ON_CLICK)) { if(event.getTarget() instanceof Button) { //from not in button of multi selection field Button bNegate = (Button)event.getTarget(); boolean isSelected = !(boolean)bNegate.getAttribute("isSelected"); if(isSelected) { bNegate.setTooltiptext(Msg.translate(Env.getCtx(), "ExcludeSelectedValues")); bNegate.setIconSclass("z-icon-ExcludeSelected"); bNegate.setSclass("btn-negate btn-negate-exclude"); } else { bNegate.setTooltiptext(Msg.translate(Env.getCtx(), "IncludeSelectedValues")); bNegate.setIconSclass("z-icon-IncludeSelected"); bNegate.setSclass("btn-negate btn-negate-include"); } bNegate.setAttribute("isSelected", isSelected); } } } /** * Handle ON_POST_EDITOR_VALUE_CHANGE_EVENT event. *
* Call {@link IProcessParameterListener#validate(ProcessParameterPanel)}. * @param editor */ private void onPostEditorValueChange(WEditor editor) { if (m_processInfo.getAD_Process_ID() > 0) { String className = MProcess.get(Env.getCtx(), m_processInfo.getAD_Process_ID()).getClassname(); String colName = editor.getColumnName(); if (m_wEditors2.contains(editor)) { // is a _To editor for ranges colName += "_2"; // same as web services } List listeners = Extensions.getProcessParameterListeners(className, colName); for(IProcessParameterListener listener : listeners) { listener.onChange(this, colName, editor); } } } /** * Notify dependent fields. * @param changedField changed field */ private void processDependencies (GridField changedField) { String columnName = changedField.getColumnName(); for (GridField field : m_mFields) { if (field == null || field == changedField) continue; verifyChangedField(field, columnName); } for (GridField field : m_mFields2) { if (field == null || field == changedField) continue; verifyChangedField(field, columnName); } } // processDependencies /** * Reset field value to null if field depends on columnName. * @param field * @param columnName column name of changed field */ private void verifyChangedField(GridField field, String columnName) { ArrayList list = field.getDependentOn(); if (list.contains(columnName)) { if (field.getLookup() instanceof MLookup) { MLookup mLookup = (MLookup)field.getLookup(); // if the lookup is dynamic (i.e. contains this columnName as variable) if (mLookup.getValidation().indexOf("@"+columnName+"@") != -1) { if (log.isLoggable(Level.FINE)) log.fine(columnName + " changed - " + field.getColumnName() + " set to null"); // invalidate current selection field.setValue(null, true); } } } } /** * Process new value from {@link #valueChange(ValueChangeEvent)}. * @param value * @param name */ private void processNewValue(Object value, String name) { if (value == null) value = new String(""); if (value instanceof String) Env.setContext(Env.getCtx(), m_WindowNo, name, (String) value); else if (value instanceof Integer) Env.setContext(Env.getCtx(), m_WindowNo, name, ((Integer) value).intValue()); else if (value instanceof Boolean) Env.setContext(Env.getCtx(), m_WindowNo, name, ((Boolean) value).booleanValue()); else if (value instanceof Timestamp) Env.setContext(Env.getCtx(), m_WindowNo, name, (Timestamp) value); else Env.setContext(Env.getCtx(), m_WindowNo, name, value.toString()); Events.postEvent("onDynamicDisplay", this, (Object)null); } /** * Dynamic update the UI state and properties of all fields. */ private void dynamicDisplay() { for (int i = 0; i < m_wEditors.size(); i++) { WEditor editor = m_wEditors.get(i); GridField mField = editor.getGridField(); GridField mField2 = null; if (mField.getVO().isRange) { mField2 = m_wEditors2.get(i).getGridField(); } if (mField.isDisplayed(true)) { if (!editor.isVisible()) { editor.setVisible(true); m_Rows.get(i).setVisible(true); m_Rows.get(i).setAttribute(Group.GROUP_ROW_VISIBLE_KEY, "true"); if (mField.getVO().isRange) { m_separators.get(i).setVisible(true); m_wEditors2.get(i).setVisible(true); } Button bNegate = null; if(editor.getComponent() != null) bNegate = (Button) editor.getComponent().getAttribute("isNotClause"); if(bNegate != null) { bNegate.setVisible(true); } } boolean rw = mField.isEditablePara(true); // r/w - check if field is Editable editor.setReadWrite(rw); editor.dynamicDisplay(); if (mField.getVO().isRange) { m_wEditors2.get(i).setReadWrite(rw); m_wEditors2.get(i).dynamicDisplay(); } } else if (editor.isVisible()) { editor.setVisible(false); m_Rows.get(i).setVisible(false); m_Rows.get(i).setAttribute(Group.GROUP_ROW_VISIBLE_KEY, "false"); if (mField.getVO().isRange) { m_separators.get(i).setVisible(false); m_wEditors2.get(i).setVisible(false); } Button bNegate = null; if(editor.getComponent() != null) bNegate = (Button) editor.getComponent().getAttribute("isNotClause"); if(bNegate != null) { bNegate.setVisible(false); } } editor.setMandatory(mField.isMandatory(true)); editor.updateStyle(); if (mField.getVO().isRange) { m_wEditors2.get(i).setMandatory(mField.isMandatory(true)); m_wEditors2.get(i).updateStyle(); // Add mandatory style on label when Parameter To is still blank if (editor.isMandatory() && editor.getLabel() != null && m_wEditors2.get(i).isNullOrEmpty()) { LayoutUtils.addSclass("idempiere-mandatory-label", editor.getLabel()); } } // Handle Dynamic Display for Date Range Picker if (((mField.getDisplayType() == DisplayType.Date) || (mField.getDisplayType() == DisplayType.DateTime)) && mField2 != null && ((mField2.getDisplayType() == DisplayType.Date) || (mField2.getDisplayType() == DisplayType.DateTime)) && MProcessPara.DATERANGEOPTION_TextAndRangePicker.equalsIgnoreCase(mField.getDateRangeOption())) { DateRangeEditor dateRangeEditor = m_dateRangeEditors.get(i); if(dateRangeEditor != null) { dateRangeEditor.setVisible(editor.isVisible()); m_Rows.get(i).setVisible(editor.isVisible()); m_Rows.get(i).setAttribute(Group.GROUP_ROW_VISIBLE_KEY, editor.isVisible()); editor.setVisible(false, true); if (mField.getVO().isRange) { m_separators.get(i).setVisible(false); m_wEditors2.get(i).setVisible(false ,true); } dateRangeEditor.setFieldMandatoryStyle(); dateRangeEditor.setReadOnly(!(editor.isReadWrite() && m_wEditors2.get(i).isReadWrite())); } } } if (getParent() != null) { getParent().invalidate(); } } /** * Restore window context. * * author teo_sarca [ 1699826 ] * @see org.compiere.model.GridField#restoreValue() */ protected void restoreContext() { for (GridField f : m_mFields) { if (f != null) f.restoreValue(); } for (GridField f : m_mFields2) { if (f != null) f.restoreValue(); } } /** * @param processInfo */ public void setProcessInfo(ProcessInfo processInfo) { m_processInfo = processInfo; } public ProcessInfo getProcessInfo() { return m_processInfo; } /** * focus to first visible field editor. * @return true if there is at least one visible field editor. */ public boolean focusToFirstEditor() { if (m_wEditors.isEmpty()) return false; for(WEditor editor : m_wEditors) { if (editor.isVisible()) { focusToEditor(editor); return true; } } return false; } /** * @param toFocus */ private void focusToEditor(WEditor toFocus) { Component c = toFocus.getComponent(); if (c instanceof EditorBox) { c = ((EditorBox)c).getTextbox(); } else if (c instanceof NumberBox) { c = ((NumberBox)c).getDecimalbox(); } else if (c instanceof Urlbox) { c = ((Urlbox)c).getTextbox(); } ((HtmlBasedComponent)c).focus(); } /** * Get parameter field editor by column name * @param columnName * @return editor */ public WEditor getEditor(String columnName) { for(int i = 0; i < m_mFields.size(); i++) { if (m_mFields.get(i).getColumnName().equals(columnName)) { return m_wEditors.get(i); } } return null; } /** * Get parameter field to editor by column name * @param columnName * @return editor */ public WEditor getEditorTo(String columnName) { for(int i = 0; i < m_mFields.size(); i++) { if (m_mFields.get(i).getColumnName().equals(columnName)) { return m_wEditors2.get(i); } } return null; } /** * @return true if editor is showing dialog awaiting user action (usually info window). */ public boolean isWaitingForDialog() { for (int i = 0; i < m_mFields.size(); i++) { // Get Values WEditor editor = (WEditor) m_wEditors.get(i); WEditor editor2 = (WEditor) m_wEditors2.get(i); if (editor != null && editor instanceof WSearchEditor) { if (((WSearchEditor)editor).isShowingDialog()) return true; } else if (editor2 != null && editor2 instanceof WSearchEditor) { if (((WSearchEditor)editor2).isShowingDialog()) return true; } } return false; } /** * Field label ON_CLICK listener for {@link IZoomableEditor}. */ static class ZoomListener implements EventListener { private IZoomableEditor searchEditor; ZoomListener(IZoomableEditor editor) { searchEditor = editor; } public void onEvent(Event event) throws Exception { if (Events.ON_CLICK.equals(event.getName())) { searchEditor.actionZoom(); } } } /** * @return register window number. */ public int getWindowNo() { return m_WindowNo; } @Override public String get_ValueAsString(String variableName) { for(WEditor editor : m_wEditors) { if (editor.getGridField().getColumnName().equals(variableName)) { //base on code in GridField.updateContext() method int displayType = editor.getGridField().getVO().displayType; if (displayType == DisplayType.Text || displayType == DisplayType.Memo || displayType == DisplayType.TextLong || displayType == DisplayType.JSON || displayType == DisplayType.Binary || displayType == DisplayType.RowID || editor.getGridField().isEncrypted()) return ""; // ignore Object value = editor.getValue(); if (value == null) return ""; else if (value instanceof Boolean) { return (((Boolean)value) ? "Y" : "N"); } else if (value instanceof Timestamp) { String stringValue = null; if (value != null && !value.toString().equals("")) { Calendar c1 = Calendar.getInstance(); c1.setTime((Date) value); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); stringValue = sdf.format(c1.getTime()); } return stringValue; } else { return value.toString(); } } } return null; } } // ProcessParameterPanel