diff --git a/base/schema/org.adempiere.base.Callout.exsd b/base/schema/org.adempiere.base.Callout.exsd index a4657b6ea9..f80d1e9141 100644 --- a/base/schema/org.adempiere.base.Callout.exsd +++ b/base/schema/org.adempiere.base.Callout.exsd @@ -58,7 +58,7 @@ Instead, you specify Table and Column in the extension declaration. - + diff --git a/base/src/org/adempiere/base/Core.java b/base/src/org/adempiere/base/Core.java index df6ce32268..643292bc85 100644 --- a/base/src/org/adempiere/base/Core.java +++ b/base/src/org/adempiere/base/Core.java @@ -9,14 +9,14 @@ import org.compiere.model.Callout; /** * This is a facade class for the Service Locator. * It provides simple access to all core services. - * + * * @author viola */ public class Core { public static IResourceFinder getResourceFinder() { return new IResourceFinder() { - + public URL getResource(String name) { List f = Service.list(IResourceFinder.class); for (IResourceFinder finder : f) { @@ -29,13 +29,13 @@ public class Core { }; } - public static Callout getCallout(String tableName, String columnName) { + public static List findCallout(String tableName, String columnName) { ServiceQuery query = new ServiceQuery(); query.put("tableName", tableName); query.put("columnName", columnName); - - return Service.locate(Callout.class, query); + + return Service.list(IColumnCallout.class, query); } - - + + } diff --git a/base/src/org/adempiere/base/IColumnCallout.java b/base/src/org/adempiere/base/IColumnCallout.java new file mode 100755 index 0000000000..baaa3bed4a --- /dev/null +++ b/base/src/org/adempiere/base/IColumnCallout.java @@ -0,0 +1,48 @@ +/****************************************************************************** + * Product: Adempiere ERP & CRM Smart Business Solution * + * Copyright (C) 1999-2006 ComPiere, 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. * + * For the text or an alternative of this public license, you may reach us * + * ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA * + * or via info@compiere.org or http://www.compiere.org/license.html * + *****************************************************************************/ +package org.adempiere.base; + +import java.util.Properties; + +import org.compiere.model.GridField; +import org.compiere.model.GridTab; + +/** + * Column Callout Interface + */ +public interface IColumnCallout extends IService +{ + /** + * Start Callout. + *

+ * Callout's are used for cross field validation and setting values in other fields + * when returning a non empty (error message) string, an exception is raised + *

+ * When invoked, the Tab model has the new value! + * + * @param ctx Context + * @param WindowNo current Window No + * @param mTab Model Tab + * @param mField Model Field + * @param value The new value + * @param oldValue The old value + * @return Error message or "" + */ + public String start (Properties ctx, int WindowNo, + GridTab mTab, GridField mField, Object value, Object oldValue); + +} diff --git a/base/src/org/compiere/model/Callout.java b/base/src/org/compiere/model/Callout.java index 1709ed74df..3ddfcf7b20 100644 --- a/base/src/org/compiere/model/Callout.java +++ b/base/src/org/compiere/model/Callout.java @@ -27,7 +27,7 @@ import org.adempiere.base.IService; * @author Jorg Janke * @version $Id: Callout.java,v 1.2 2006/07/30 00:51:05 jjanke Exp $ */ -public interface Callout extends IService +public interface Callout { /** * Start Callout. diff --git a/base/src/org/compiere/model/GridTab.java b/base/src/org/compiere/model/GridTab.java index 041ede129a..40ff01a1b3 100644 --- a/base/src/org/compiere/model/GridTab.java +++ b/base/src/org/compiere/model/GridTab.java @@ -38,6 +38,7 @@ import javax.script.ScriptEngine; import javax.swing.event.EventListenerList; import org.adempiere.base.Core; +import org.adempiere.base.IColumnCallout; import org.adempiere.base.Service; import org.compiere.util.CLogMgt; import org.compiere.util.CLogger; @@ -72,7 +73,7 @@ import org.compiere.util.ValueNamePair; * * @author Jorg Janke * @version $Id: GridTab.java,v 1.10 2006/10/02 05:18:39 jjanke Exp $ - * + * * @author Teo Sarca, SC ARHIPAC SERVICE SRL *

  • BF [ 1742159 ] Editable number field for inactive record *
  • BF [ 1968598 ] Callout is not called if tab is processed @@ -86,7 +87,7 @@ import org.compiere.util.ValueNamePair; * https://sourceforge.net/tracker/?func=detail&aid=2874109&group_id=176962&atid=879332 *
  • BF [ 2905287 ] GridTab query is not build correctly * https://sourceforge.net/tracker/?func=detail&aid=2905287&group_id=176962&atid=879332 - * @author Victor Perez , e-Evolution.SC + * @author Victor Perez , e-Evolution.SC *
  • FR [1877902] Implement JSR 223 Scripting APIs to Callout *
  • BF [ 2910358 ] Error in context when a field is found in different tabs. * https://sourceforge.net/tracker/?func=detail&aid=2910358&group_id=176962&atid=879332 @@ -102,12 +103,12 @@ import org.compiere.util.ValueNamePair; public class GridTab implements DataStatusListener, Evaluatee, Serializable { /** - * + * */ private static final long serialVersionUID = -8762357519103152929L; public static final String DEFAULT_STATUS_MESSAGE = "NavigateOrUpdate"; - + /** * Create Tab (Model) from Value Object. *

    @@ -120,7 +121,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable { this(vo, w, false); } - + /** * Create Tab (Model) from Value Object. *

    @@ -196,13 +197,13 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable /** Logger */ protected CLogger log = CLogger.getCLogger(getClass()); - + private boolean m_parentNeedSave = false; private long m_lastDataStatusEventTime; private DataStatusEvent m_lastDataStatusEvent; - + // Context property names: public static final String CTX_KeyColumnName = "_TabInfo_KeyColumnName"; public static final String CTX_LinkColumnName = "_TabInfo_LinkColumnName"; @@ -213,7 +214,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable public static final String CTX_AD_Table_ID = "_TabInfo_AD_Table_ID"; public static final String CTX_FindSQL = "_TabInfo_FindSQL"; public static final String CTX_SQL = "_TabInfo_SQL"; - + /************************************************************************** @@ -258,7 +259,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable { return m_loadComplete; } - + /** * Initialize Tab with record from AD_Tab_v * @param async async @@ -268,7 +269,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable { log.fine("#" + m_vo.TabNo + " - Async=" + async + " - Where=" + m_vo.WhereClause); if (isLoadComplete()) return true; - + if (m_loader != null && m_loader.isAlive()) { waitLoadCompete(); @@ -289,9 +290,9 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable } // initTab protected boolean loadTab() - { + { m_extendedWhere = m_vo.WhereClause; - + // Get Field Data if (!loadFields()) { @@ -305,7 +306,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable m_loadComplete = true; return true; } - + /** * Dispose - clean up resources */ @@ -412,7 +413,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable } } // for all fields - if (! m_mTable.getTableName().equals(X_AD_PInstance_Log.Table_Name)) { // globalqss, bug 1662433 + if (! m_mTable.getTableName().equals(X_AD_PInstance_Log.Table_Name)) { // globalqss, bug 1662433 // Add Standard Fields if (m_mTable.getField("Created") == null) { @@ -609,7 +610,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable public void query (boolean onlyCurrentRows, int onlyCurrentDays, int maxRows) { if (!m_loadComplete) initTab(false); - + log.fine("#" + m_vo.TabNo + " - Only Current Rows=" + onlyCurrentRows + ", Days=" + onlyCurrentDays + ", Detail=" + isDetail()); @@ -645,7 +646,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable { String value = null; if ( m_parentColumnName.length() > 0 ) - { + { // explicit parent link defined value = Env.getContext(m_vo.ctx, m_vo.WindowNo, getParentTabNo(), m_parentColumnName, true); if (value == null || value.length() == 0) @@ -654,8 +655,8 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable value = Env.getContext(m_vo.ctx, m_vo.WindowNo, getParentTabNo(), lc, true); if (value == null || value.length() == 0) value = Env.getContext(m_vo.ctx, m_vo.WindowNo, lc, true); // back compatibility - } - + } + // Same link value? if (refresh) refresh = m_linkValue.equals(value); @@ -933,25 +934,25 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable { if (hasChangedCurrentTabAndParents()) return false; - + boolean retValue = (m_mTable.dataSave(manualCmd) == GridTable.SAVE_OK); if (manualCmd) { setCurrentRow(m_currentRow, false); - if (m_lastDataStatusEvent != null && m_lastDataStatusEvent.getCurrentRow() == m_currentRow - && ((m_lastDataStatusEvent.Record_ID != null && m_lastDataStatusEvent.Record_ID instanceof Integer + if (m_lastDataStatusEvent != null && m_lastDataStatusEvent.getCurrentRow() == m_currentRow + && ((m_lastDataStatusEvent.Record_ID != null && m_lastDataStatusEvent.Record_ID instanceof Integer && (Integer) m_lastDataStatusEvent.Record_ID == 0) || m_lastDataStatusEvent.Record_ID == null)) { updateDataStatusEventProperties(m_lastDataStatusEvent); } } fireStateChangeEvent(new StateChangeEvent(this, StateChangeEvent.DATA_SAVE)); - + if (retValue) { // refresh parent tabs refreshParents(); } - + return retValue; } catch (Exception e) @@ -1052,7 +1053,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable log.fine("#" + m_vo.TabNo); m_mTable.dataIgnore(); setCurrentRow(m_currentRow, false); // re-load data - + fireStateChangeEvent(new StateChangeEvent(this, StateChangeEvent.DATA_IGNORE)); log.fine("#" + m_vo.TabNo + "- fini"); } // dataIgnore @@ -1085,11 +1086,11 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable } log.finest("Processed=" + processed); } - + //hengsin, dont create new when parent is empty if (isDetail() && m_parentNeedSave) return false; - + /** * temporary set currentrow to point to the new row to ensure even cause by m_mTable.dataNew * is handle properly. @@ -1101,7 +1102,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable if (!retValue) return retValue; setCurrentRow(m_currentRow + 1, true); - + // process all Callouts (no dependency check - assumed that settings are valid) for (int i = 0; i < getFieldCount(); i++) processCallout(getField(i)); @@ -1112,7 +1113,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable getField(i).validateValue(); } m_mTable.setChanged(false); - + fireStateChangeEvent(new StateChangeEvent(this, StateChangeEvent.DATA_NEW)); return retValue; } // dataNew @@ -1193,7 +1194,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable { return m_keyColumnName; } // getKeyColumnName - + /** * Set Name of the Key Column * @param keyColumnName @@ -1226,9 +1227,9 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable m_parentColumnName = DB.getSQLValueString(null, sql, m_vo.Parent_Column_ID ); if ( m_parentColumnName == null ) m_parentColumnName = ""; - - - + + + if (linkColumnName != null) m_linkColumnName = linkColumnName; else @@ -1344,7 +1345,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable */ public boolean isDetail() { - // First Tab Level is not a detail + // First Tab Level is not a detail if (m_vo.TabLevel == 0) return false; // We have IsParent columns and/or a link column @@ -1406,7 +1407,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable { if (m_vo.IsReadOnly) return true; - + //hengsin, make detail readonly when parent is empty if (m_parentNeedSave) return true; @@ -1790,7 +1791,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable DB.close(rs, pstmt); rs = null; pstmt = null; } - + if (filled) return mf.format (arguments); return " "; @@ -1857,7 +1858,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable DB.close(rs, pstmt); rs = null; pstmt = null; } - + if (filled) return mf.format (arguments); return " "; @@ -1935,7 +1936,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable if (colFax != null && (colFax.getVFormat() == null || colFax.getVFormat().length() == 0)) fFax.setVFormat(phone_frm); } - + } // loadDependentInfo @@ -2202,11 +2203,11 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable } else // Redistribute Info with current row info fireDataStatusChanged(m_DataStatusEvent); - + //reset m_lastDataStatusEventTime = System.currentTimeMillis(); m_lastDataStatusEvent = m_DataStatusEvent; - m_DataStatusEvent = null; + m_DataStatusEvent = null; // log.fine("dataStatusChanged #" + m_vo.TabNo + "- fini", e.toString()); } // dataStatusChanged @@ -2325,9 +2326,9 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable // Row range check int newRow = verifyRow(targetRow); - + // Check, if we have old uncommitted data - if (m_mTable.dataSave(newRow, false) == false) + if (m_mTable.dataSave(newRow, false) == false) return m_currentRow; //remove/ignore new and unchange row @@ -2337,7 +2338,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable newRow--; dataIgnore(); } - + // new position return setCurrentRow(newRow, true); } // navigate @@ -2427,7 +2428,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable // Object value = null; // if (mField.isKey() || mField.isParent() || mField.getColumnName().equals(m_linkColumnName)) // value = mField.getDefault(); - + // CarlosRuiz - globalqss [ 1881480 ] Navigation problem between tabs // the implementation of linking with window context variables is very weak // you must be careful when defining a column in a detail tab with a field @@ -2453,7 +2454,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable { m_DataStatusEvent = m_lastDataStatusEvent; } - + // inform APanel/.. -> dataStatus with row updated if (m_DataStatusEvent == null) m_DataStatusEvent = new DataStatusEvent(this, getRowCount(), @@ -2465,10 +2466,10 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable if (status == null || status.length() == 0) m_DataStatusEvent.setInfo(DEFAULT_STATUS_MESSAGE, null, false,false); fireDataStatusChanged(m_DataStatusEvent); - + //reset m_DataStatusEvent = null; - + return m_currentRow; } // setCurrentRow @@ -2565,10 +2566,10 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable return "NoField"; log.fine(field.getColumnName() + "=" + value + " - Row=" + m_currentRow); - + if (DisplayType.isID(field.getDisplayType()) && value instanceof Integer && ((Integer)value).intValue() < 0) value = null; - + int col = m_mTable.findColumn(field.getColumnName()); m_mTable.setValueAt(value, m_currentRow, col, false); // @@ -2643,12 +2644,12 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable } // for all dependent fields } // processDependencies - + private List activeCallouts = new ArrayList(); private List activeCalloutInstance = new ArrayList(); - + /** - * + * * @return list of active call out for this tab */ public String[] getActiveCallouts() @@ -2656,9 +2657,9 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable String[] list = new String[activeCallouts.size()]; return activeCallouts.toArray(list); } - + /** - * + * * @return list of active call out instance for this tab */ public Callout[] getActiveCalloutInstance() @@ -2684,20 +2685,48 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable */ public String processCallout (GridField field) { - Object value = field.getValue(); - Object oldValue = field.getOldValue(); - Callout co = Core.getCallout(getTableName(), field.getColumnName()); - if (co!=null) { - return co.start(m_vo.ctx, "", m_vo.WindowNo, this, field, value, oldValue); - } - - String callout = field.getCallout(); - if (callout.length() == 0) - return ""; // if (isProcessed() && !field.isAlwaysUpdateable()) // only active records return ""; // "DocProcessed"; + Object value = field.getValue(); + Object oldValue = field.getOldValue(); + List callouts = Core.findCallout(getTableName(), field.getColumnName()); + if (callouts != null && !callouts.isEmpty()) { + for(IColumnCallout co : callouts) + { + String retValue = ""; + + String cmd = co.getClass().getName(); + //detect infinite loop + if (activeCallouts.contains(cmd)) continue; + try + { + activeCallouts.add(cmd); + retValue = co.start(m_vo.ctx, m_vo.WindowNo, this, field, value, oldValue); + } + catch (Exception e) + { + log.log(Level.SEVERE, "start", e); + retValue = "Callout Invalid: " + e.toString(); + return retValue; + } + finally + { + activeCallouts.remove(cmd); + } + if (!Util.isEmpty(retValue)) // interrupt on first error + { + log.severe (retValue); + return retValue; + } + } + } + + String callout = field.getCallout(); + if (callout.length() == 0) + return ""; + log.fine(field.getColumnName() + "=" + value + " (" + callout + ") - old=" + oldValue); @@ -2705,26 +2734,26 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable while (st.hasMoreTokens()) // for each callout { String cmd = st.nextToken().trim(); - + //detect infinite loop if (activeCallouts.contains(cmd)) continue; - + String retValue = ""; // FR [1877902] // CarlosRuiz - globalqss - implement beanshell callout // Victor Perez - vpj-cd implement JSR 223 Scripting if (cmd.toLowerCase().startsWith(MRule.SCRIPT_PREFIX)) { - + MRule rule = MRule.get(m_vo.ctx, cmd.substring(MRule.SCRIPT_PREFIX.length())); if (rule == null) { - retValue = "Callout " + cmd + " not found"; + retValue = "Callout " + cmd + " not found"; log.log(Level.SEVERE, retValue); return retValue; } - if ( ! (rule.getEventType().equals(MRule.EVENTTYPE_Callout) + if ( ! (rule.getEventType().equals(MRule.EVENTTYPE_Callout) && rule.getRuleType().equals(MRule.RULETYPE_JSR223ScriptingAPIs))) { retValue = "Callout " + cmd - + " must be of type JSR 223 and event Callout"; + + " must be of type JSR 223 and event Callout"; log.log(Level.SEVERE, retValue); return retValue; } @@ -2734,7 +2763,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable // Window context are W_ // Login context are G_ MRule.setContext(engine, m_vo.ctx, m_vo.WindowNo); - // now add the callout parameters windowNo, tab, field, value, oldValue to the engine + // now add the callout parameters windowNo, tab, field, value, oldValue to the engine // Method arguments context are A_ engine.put(MRule.ARGUMENTS_PREFIX + "WindowNo", m_vo.WindowNo); engine.put(MRule.ARGUMENTS_PREFIX + "Tab", this); @@ -2743,7 +2772,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable engine.put(MRule.ARGUMENTS_PREFIX + "OldValue", oldValue); engine.put(MRule.ARGUMENTS_PREFIX + "Ctx", m_vo.ctx); - try + try { activeCallouts.add(cmd); retValue = engine.eval(rule.getScript()).toString(); @@ -2758,7 +2787,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable { activeCallouts.remove(cmd); } - + } else { Callout call = null; @@ -2799,9 +2828,9 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable activeCallouts.remove(cmd); activeCalloutInstance.remove(call); } - - } - + + } + if (!Util.isEmpty(retValue)) // interrupt on first error { log.severe (retValue); @@ -2870,7 +2899,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable return null; return m_mTable.getValueAt(row, col); } // getValue - + /* public boolean isNeedToSaveParent() { @@ -2924,11 +2953,11 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable { m_listenerList.add(DataStatusListener.class, l); } - + /** * @param l */ - public synchronized void addStateChangeListener(StateChangeListener l) + public synchronized void addStateChangeListener(StateChangeListener l) { m_listenerList.add(StateChangeListener.class, l); } @@ -2936,11 +2965,11 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable /** * @param l */ - public synchronized void removeStateChangeListener(StateChangeListener l) + public synchronized void removeStateChangeListener(StateChangeListener l) { m_listenerList.remove(StateChangeListener.class, l); } - + /** * Feature Request [1707462] * Enable runtime change of VFormat @@ -2951,7 +2980,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable public void setFieldVFormat (String identifier, String strNewFormat) { m_mTable.setFieldVFormat(identifier, strNewFormat); - } // setFieldVFormat + } // setFieldVFormat /** * Switches the line/seqNo of the two rows @@ -3010,10 +3039,10 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable if (lineNoCurrentRow >= 9900 || lineNoNextRow >= 9900) { log.fine("don't sort - might be special lines"); - return; + return; } // switch the line numbers and save new values - + m_mTable.setValueAt(lineNoCurrentRow, to, lineCol); setCurrentRow(to, false); m_mTable.dataSave(true); @@ -3028,7 +3057,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable } navigate(to); } - + private void fireStateChangeEvent(StateChangeEvent e) { StateChangeListener[] listeners = m_listenerList.getListeners(StateChangeListener.class); @@ -3037,11 +3066,11 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable for(int i = 0; i < listeners.length; i++) { listeners[i].stateChange(e); } - + } - + /** - * + * * @return list of all tabs included in this tab */ public List getIncludedTabs() @@ -3064,8 +3093,8 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable } return list; } - - //BF [ 2910358 ] + + //BF [ 2910358 ] /** * get Parent Tab No * @return Tab No @@ -3079,9 +3108,9 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable return tabNo; while (parentLevel != currentLevel) { - tabNo--; + tabNo--; currentLevel = Env.getContextAsInt(m_vo.ctx, m_vo.WindowNo, tabNo, GridTab.CTX_TabLevel); } return tabNo; - } + } } // GridTab