www.pudn.com > code_source_compiere_erp_crm_logiciel_java.zip > MTab.java


/****************************************************************************** 
 * The contents of this file are subject to the   Compiere License  Version 1.1 
 * ("License"); You may not use this file except in compliance with the License 
 * You may obtain a copy of the License at http://www.compiere.org/license.html 
 * Software distributed under the License is distributed on an  "AS IS"  basis, 
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for 
 * the specific language governing rights and limitations under the License. 
 * The Original Code is                  Compiere  ERP & CRM  Business Solution 
 * The Initial Developer of the Original Code is Jorg Janke  and ComPiere, Inc. 
 * Portions created by Jorg Janke are Copyright (C) 1999-2001 Jorg Janke, parts 
 * created by ComPiere are Copyright (C) ComPiere, Inc.;   All Rights Reserved. 
 * Contributor(s): ______________________________________. 
 *****************************************************************************/ 
package org.compiere.model; 
 
import java.sql.*; 
import java.util.*; 
import java.math.*; 
import java.text.*; 
import java.beans.*; 
import java.io.*; 
 
import org.compiere.util.ValueNamePair; 
import org.compiere.util.*; 
 
/** 
 *	Tab Model. 
 *  - a combination of AD_Tab (the display attributes) and AD_Table information. 
 *  

* The Tab owns also it's Table model * and listens to data changes to update the Field values. * *

* The Tab maintains the bound property: CurrentRow * *

 
 *  Event Hierarchies: 
 *      - dataChanged (from MTable) 
 *          - setCurrentRow 
 *              - Update all Field Values 
 * 
 *      - setValue 
 *          - Update Field Value 
 *          - Callout 
 *  
* @author Jorg Janke * @version $Id: MTab.java,v 1.41 2003/04/30 06:24:22 jjanke Exp $ */ public final class MTab implements DataStatusListener, Serializable { /** * Create Tab (Model) from Value Object. *

* MTab provides a property listener for changed rows and a * DataStatusListener for communicating changes of the underlying data * @param vo Value Object */ public MTab(MTabVO vo) { m_vo = vo; // Create MTable m_mTable = new MTable (m_vo.ctx, m_vo.TableName, m_vo.WindowNo, m_vo.TabNo, true); m_mTable.setReadOnly(m_vo.IsReadOnly || m_vo.IsView); m_mTable.setDeleteable(m_vo.IsDeleteable); // Load Tab // if (vo.TabNo == 0) initTab(false); // else // { // m_loader = new Loader(); // m_loader.setPriority(Thread.MIN_PRIORITY); // m_loader.start(); // } // waitLoadCompete(); } // M_Tab /** Value Object */ private MTabVO m_vo; /** The Table Model for Query */ private MTable m_mTable = null; private String m_keyColumnName = ""; private String m_linkColumnName = ""; private String m_extendedWhere; /** Attachments */ private HashMap m_Attachment = null; /** Current Row */ private int m_currentRow = -1; /** Property Change */ private PropertyChangeSupport m_propertyChangeSupport = new PropertyChangeSupport(this); /** Property Change Type */ public static final String PROPERTY = "CurrentRow"; private Vector m_dataStatusListeners = null; private DataStatusEvent m_DataStatusEvent = null; // private MQuery m_query = new MQuery(); private String m_oldQuery = "0=9"; private String m_linkValue = "999999"; /** Order By Array if SortNo 1..2 */ private String[] m_OrderBys = new String[2]; /** List of Key Parents */ private ArrayList m_parents = new ArrayList(2); /** Map of ColumnName of source field (key) and the dependent field (value) */ private MultiMap m_depOnField = new MultiMap(); /** Async Loader */ private Loader m_loader = null; /** Async Loading complete */ private volatile boolean m_loadComplete = false; /*************************************************************************/ /** * Tab loader for Tabs > 0 */ class Loader extends Thread { /** * Async Loading of Tab > 0 */ public void run() { initTab (true); } // run } // Loader /** * Wait until load is complete */ private void waitLoadCompete() { if (m_loadComplete) return; // m_loader.setPriority(Thread.NORM_PRIORITY); Log.trace(Log.l1_User, "MTab.waitLoadComplete"); while (m_loader.isAlive()) { try { Thread.sleep(100); // 1/10 sec } catch (Exception e) { Log.error("MTab.waitLoadComplete", e); } } Log.trace(Log.l1_User, "MTab.waitLoadComplete - fini"); } // waitLoadComplete /** * Initialize Tab with record from AD_Tab_v * @param async async * @return true, if correctly initialized (ignored) */ private boolean initTab (boolean async) { Log.trace(Log.l3_Util, "MTab.initTab #" + m_vo.TabNo + " - Async=" + async); m_extendedWhere = m_vo.WhereClause; // Get Field Data if (!loadFields()) { m_loadComplete = true; return false; } // Order By m_mTable.setOrderClause(getOrderByClause(m_vo.onlyCurrentRows)); if (async) Log.trace(Log.l3_Util, "MTab.initTab #" + m_vo.TabNo + " - Async=" + async, "fini"); m_loadComplete = true; return true; } // initTab /** * Dispose - clean up resources */ protected void dispose() { Log.trace(Log.l3_Util, "MTab.dispose #" + m_vo.TabNo); m_OrderBys = null; // m_parents.clear(); m_parents = null; // m_mTable.close (true); // also disposes Fields m_mTable = null; // m_depOnField.clear(); m_depOnField = null; if (m_Attachment != null) m_Attachment.clear(); m_Attachment = null; // m_vo.Fields.clear(); m_vo.Fields = null; m_vo = null; } // dispose /** * Get Field data and add to MTable, if it's required or displayed. * Reqiored fields are keys, parents, or standard Columns * @return true if fields loaded */ private boolean loadFields() { Log.trace(Log.l3_Util, "MTab.loadFields #" + m_vo.TabNo); if (m_vo.Fields == null) return false; // Add RowID if this is not a view if (!m_vo.IsView) { MField rowID = new MField (MFieldVO.createRowID (m_vo.ctx, m_vo.WindowNo, m_vo.TabNo, m_vo.AD_Window_ID)); m_mTable.addField(rowID); } // Add Fields for (int f = 0; f < m_vo.Fields.size(); f++) { MFieldVO voF = (MFieldVO)m_vo.Fields.get(f); // Add Fields to Table if (voF != null) { MField field = new MField (voF); String columnName = field.getColumnName(); // Record Info if (field.isKey()) { if (m_vo.IsView) // make it a RowID for selection & update voF.initRowID(); m_keyColumnName = columnName; } // Parent Column(s) if (field.isParent()) m_parents.add(columnName); // Order By if (field.getSortNo() == 1) m_OrderBys[0] = columnName; else if (field.getSortNo() == 2) m_OrderBys[1] = columnName; // Add field m_mTable.addField(field); // List of ColumnNames, this field is dependent on ArrayList list = field.getDependentOn(); for (int i = 0; i < list.size(); i++) m_depOnField.put(list.get(i), field); // ColumnName, Field // Add fields all fields are dependent on if (columnName.equals("IsActive") || columnName.equals("Processed")) m_depOnField.put(columnName, null); } } // for all fields // Add Standard Fields if (m_mTable.getField("Created") == null) { MField created = new MField (MFieldVO.createStdField(m_vo.ctx, m_vo.WindowNo, m_vo.TabNo, m_vo.AD_Window_ID, false, true, true)); m_mTable.addField(created); } if (m_mTable.getField("CreatedBy") == null) { MField createdBy = new MField (MFieldVO.createStdField(m_vo.ctx, m_vo.WindowNo, m_vo.TabNo, m_vo.AD_Window_ID, false, true, false)); m_mTable.addField(createdBy); } if (m_mTable.getField("Updated") == null) { MField updated = new MField (MFieldVO.createStdField(m_vo.ctx, m_vo.WindowNo, m_vo.TabNo, m_vo.AD_Window_ID, false, false, true)); m_mTable.addField(updated); } if (m_mTable.getField("UpdatedBy") == null) { MField updatedBy = new MField (MFieldVO.createStdField(m_vo.ctx, m_vo.WindowNo, m_vo.TabNo, m_vo.AD_Window_ID, false, false, false)); m_mTable.addField(updatedBy); } return true; } // loadFields /** * Get TableModel. * Do not directly communicate with the table model, * but through the methods of this class * @return Table Model */ public MTable getTableModel() { return m_mTable; } // getTableModel /** * Get Tab Icon * @return Icon */ public javax.swing.Icon getIcon() { if (m_vo.AD_Image_ID == 0) return null; // /** @todo Load Image */ return null; } // getIcon /*************************************************************************/ /** * Has this field dependents ? * @param columnName column name * @return true if column has dependent */ public boolean isDependentOn (String columnName) { // m_depOnField.printToLog(); return m_depOnField.containsKey(columnName); } // isDependentOn /** * Get dependent fields of columnName * @param columnName column name * @return ArrayList with MFields dependent on columnName */ public ArrayList getDependentFieldList (String columnName) { return m_depOnField.getValues(columnName); } // getDependentFieldList /*************************************************************************/ /** * Set Query * @param query query */ public void setQuery(MQuery query) { if (query == null) m_query = new MQuery(); else m_query = query; } // setQuery /** * Get Query * @return query */ public MQuery getQuery() { return m_query; } // getQuery /** * Is Query Active * @return true if query active */ public boolean isQueryActive() { return m_query.isActive(); } // isQueryActive /** * Enable Events - enable data events of tabs (add listeners) */ public void enableEvents() { // Setup Events m_mTable.addDataStatusListener(this); // m_mTable.addTableModelListener(this); } // enableEvents /** * Assemble whereClause and query MTable and position to row 0. *

 
	 *		Scenarios: 
	 *		- Never opened 					(full query) 
	 *		- query changed 				(full query) 
	 *		- Detail link value changed		(full query) 
	 *		- otherwise 					(refreshAll) 
	 *  
* @param onlyCurrentRows only current rows (1 day) */ public void query (boolean onlyCurrentRows) { query (onlyCurrentRows, 1); } // query /** * Assemble whereClause and query MTable and position to row 0. *
 
	 *		Scenarios: 
	 *		- Never opened 					(full query) 
	 *		- query changed 				(full query) 
	 *		- Detail link value changed		(full query) 
	 *		- otherwise 					(refreshAll) 
	 *  
* @param onlyCurrentRows only current rows * @param onlyCurrentDays if only current row, how many days back */ public void query (boolean onlyCurrentRows, int onlyCurrentDays) { Log.trace(Log.l3_Util, "MTab.query #" + m_vo.TabNo); // is it same query? boolean refresh = m_oldQuery.equals(m_query.getWhereClause()) && m_vo.onlyCurrentRows == onlyCurrentRows && m_vo.onlyCurrentDays == onlyCurrentDays; m_oldQuery = m_query.getWhereClause(); m_vo.onlyCurrentRows = onlyCurrentRows; m_vo.onlyCurrentDays = onlyCurrentDays; /** * Set Where Clause */ // Tab Where Clause StringBuffer where = new StringBuffer(m_vo.WhereClause); // Detail Query if (isDetail()) { String lc = getLinkColumnName(); if (lc.equals("")) Log.error("MTab.query - no link column"); else { String value = Env.getContext(m_vo.ctx, m_vo.WindowNo, lc); // Same link value? if (refresh) refresh = m_linkValue.equals(value); m_linkValue = value; // Check validity if (value.length() == 0) Log.error("MTab.query - no value for link column " + lc); else { // we have column and value if (where.length() != 0) where.append(" AND "); where.append(lc).append("="); if (lc.endsWith("_ID")) where.append(value); else where.append("'").append(value).append("'"); } } } // isDetail m_extendedWhere = where.toString(); // Final Query if (m_query.isActive()) { String q = validateQuery(m_query); if (q != null) { if (where.length() > 0 ) where.append(" AND "); where.append(q); } } /** * Query */ if (m_mTable.isOpen()) { if (refresh) m_mTable.dataRefreshAll(); else m_mTable.dataRequery(where.toString(), m_vo.onlyCurrentRows && !isDetail(), onlyCurrentDays); } else { m_mTable.setWhereClause(where.toString(), m_vo.onlyCurrentRows && !isDetail(), onlyCurrentDays); m_mTable.open(); } // Go to Record 0 setCurrentRow(0, true); } // query /** * Validate Query. * If query column is not a tab column create EXISTS query * @param query query * @return where clause */ private String validateQuery (MQuery query) { if (query == null || query.getRestrictionCount() == 0) return null; // Check: only one restriction if (query.getRestrictionCount() != 1) { Log.trace(Log.l6_Database, "MTab.validateQuery - ignored: " + query); return query.getWhereClause(); } String colName = query.getColumnName(0); // a '(' in the name = function - don't try to resolve if (colName.indexOf('(') != -1) { Log.trace(Log.l6_Database, "MTab.validateQuery - ignored: " + colName); return query.getWhereClause(); } // Query is valid ? if (getField(colName) != null) return query.getWhereClause(); // Find Refernce Column e.g. BillTo_ID -> C_BPartner_Location_ID String sql = "SELECT cc.ColumnName " + "FROM AD_Column c" + " INNER JOIN AD_Ref_Table r ON (c.AD_Reference_Value_ID=r.AD_Reference_ID)" + " INNER JOIN AD_Column cc ON (r.AD_Key=cc.AD_Column_ID) " + "WHERE c.AD_Reference_ID=18" // Table + " AND c.ColumnName=?"; String refColName = null; try { PreparedStatement pstmt = DB.prepareStatement(sql); pstmt.setString(1, colName); ResultSet rs = pstmt.executeQuery(); if (rs.next()) refColName = rs.getString(1); rs.close(); pstmt.close(); } catch (SQLException e) { Log.error("MTab.validateQuery (ref) - Column=" + colName, e); return query.getWhereClause(); } // Reference Column found if (refColName != null) { query.setColumnName(0, refColName); if (getField(refColName) != null) { Log.trace(Log.l6_Database, "MTab.validateQuery - Column " + colName + " replaced with " + refColName); return query.getWhereClause(); } colName = refColName; } // Column NOT in Tab - create EXISTS subquery String tableName = null; String tabKeyColumn = getKeyColumnName(); // Column=SalesRep_ID, Key=AD_User_ID, Query=SalesRep_ID=101 sql = "SELECT t.TableName " + "FROM AD_Column c" + " INNER JOIN AD_Table t ON (c.AD_Table_ID=t.AD_Table_ID) " + "WHERE c.ColumnName=? AND IsKey='Y'" // #1 Link Column + " AND EXISTS (SELECT * FROM AD_Column cc" + " WHERE cc.AD_Table_ID=t.AD_Table_ID AND cc.ColumnName=?)"; // #2 Tab Key Column try { PreparedStatement pstmt = DB.prepareStatement(sql); pstmt.setString(1, colName); pstmt.setString(2, tabKeyColumn); ResultSet rs = pstmt.executeQuery(); if (rs.next()) tableName = rs.getString(1); rs.close(); pstmt.close(); } catch (SQLException e) { Log.error("MTab.validateQuery - Column=" + colName + ", Key=" + tabKeyColumn, e); return null; } // Causes could be functions in query // e.g. Column=UPPER(Name), Key=AD_Element_ID, Query=UPPER(AD_Element.Name) LIKE '%CUSTOMER%' if (tableName == null) { Log.trace(Log.l3_Util, "MTab.validateQuery not successfull - Column=" + colName + ", Key=" + tabKeyColumn + ", Query=" + query); return query.getWhereClause(); } query.setTableName("xx"); StringBuffer result = new StringBuffer ("EXISTS (SELECT * FROM ") .append(tableName).append(" xx WHERE ") .append(query.getWhereClause(true)) .append(" AND xx.").append(tabKeyColumn).append("=") .append(getTableName()).append(".").append(tabKeyColumn).append(")"); Log.trace(Log.l6_Database, "MTab.validateQuery", result); return result.toString(); } // validateQuery /** * Refresh all data */ public void dataRefreshAll () { Log.trace(Log.l3_Util, "MTab.dataRefreshAll #" + m_vo.TabNo); /** @todo does not work with alpha key */ int keyNo = m_mTable.getKeyID(m_currentRow); m_mTable.dataRefreshAll(); // Should use RowID - not working for tables with multiple keys if (keyNo != -1) { if (keyNo != m_mTable.getKeyID(m_currentRow)) // something changed { int size = getRowCount(); for (int i = 0; i < size; i++) { if (keyNo == m_mTable.getKeyID(i)) { m_currentRow = i; break; } } } } setCurrentRow(m_currentRow, true); } // dataRefreshAll /** * Refresh current row data */ public void dataRefresh () { dataRefresh (m_currentRow); } // dataRefresh /** * Refresh row data * @param row index */ public void dataRefresh (int row) { Log.trace(Log.l3_Util, "MTab.dataRefresh #" + m_vo.TabNo, "row=" + row); m_mTable.dataRefresh(row); setCurrentRow(row, true); } // dataRefresh /** * Uncoditionally Save data * @param manualCmd if true, no vetoable PropertyChange will be fired for save confirmation from MTable * @return true if save complete (or nor required) */ public boolean dataSave(boolean manualCmd) { Log.trace(Log.l3_Util, "MTab.dataSave #" + m_vo.TabNo, "row=" + m_currentRow); boolean retValue = (m_mTable.dataSave(manualCmd) == MTable.SAVE_OK); if (manualCmd) setCurrentRow(m_currentRow, false); return retValue; } // dataSave /** * Do we need to Save? * @param rowChange row change * @param onlyRealChange if true the value of a field was actually changed * (e.g. for new records, which have not been changed) - default false * @return true it needs to be saved */ public boolean needSave (boolean rowChange, boolean onlyRealChange) { if (rowChange) { return m_mTable.needSave(-2, onlyRealChange); } else { if (onlyRealChange) return m_mTable.needSave(); else return m_mTable.needSave(onlyRealChange); } } // isDataChanged /** * Ignore data changes */ public void dataIgnore() { Log.trace(Log.l3_Util, "MTab.dataIgnore #" + m_vo.TabNo); m_mTable.dataIgnore(); setCurrentRow(m_currentRow, false); // re-load data Log.trace(Log.l3_Util, "MTab.dataIgnore #" + m_vo.TabNo + "- fini"); } // dataIgnore /** * Create (copy) new Row * and process Callouts * @param copy copy * @return true if copied/new */ public boolean dataNew (boolean copy) { Log.trace(Log.l3_Util, "MTab.dataNew #" + m_vo.TabNo); boolean retValue = m_mTable.dataNew (m_currentRow, copy); 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)); // check validity of defaults for (int i = 0; i < getFieldCount(); i++) getField(i).validateValue(); m_mTable.setChanged(false); return retValue; } // dataNew /** * Delete current Row * @return true if deleted */ public boolean dataDelete() { Log.trace(Log.l3_Util, "MTab.dataDelete #" + m_vo.TabNo, "row=" + m_currentRow); boolean retValue = m_mTable.dataDelete(m_currentRow); setCurrentRow(m_currentRow, true); return retValue; } // dataDelete /** * Get Name of Tab * @return name */ public String getName() { return m_vo.Name; } // getName /** * Get Description of Tab * @return description */ public String getDescription() { return m_vo.Description; } // getDescription /** * Get Help of Tab * @return help */ public String getHelp() { return m_vo.Help; } // getHelp /** * Get Tab Level * @return tab level */ public int getTabLevel() { return m_vo.TabLevel; } // getTabLevel /** * Get Commit Warning * @return commit warning */ public String getCommitWarning() { return m_vo.CommitWarning; } // getCommitWarning /** * Return Table Model * @return MTable */ protected MTable getMTable() { return m_mTable; } // getMTable /** * Return the name of the key column - may be "" * @return key column name */ public String getKeyColumnName() { return m_keyColumnName; } // getKeyColumnName /** * Return Name of link column * @return link column name */ public String getLinkColumnName() { return m_linkColumnName; } // getLinkColumnName /** * Set Name of link column. * Set from MWindow.loadTabData * Used in MTab.isCurreny, (.setCurrentRow) .query - APanel.cmd_report * and MField.isEditable and .isDefault via context * @param linkColumnName name of column - or sets name to AD_Column_ID, if exists */ public void setLinkColumnName (String linkColumnName) { if (linkColumnName != null) m_linkColumnName = linkColumnName; else { if (m_vo.AD_Column_ID == 0) return; // we have a link column identified (primary parent column) else { String SQL = "SELECT ColumnName FROM AD_Column WHERE AD_Column_ID=?"; try { PreparedStatement pstmt = DB.prepareStatement(SQL); pstmt.setInt(1, m_vo.AD_Column_ID); // Link Column ResultSet rs = pstmt.executeQuery(); if (rs.next()) m_linkColumnName = rs.getString(1); rs.close(); pstmt.close(); } catch (SQLException e) { Log.error("MTab.setLinkColumn", e); } Log.trace(Log.l6_Database, "MTab.setLinkColumnName - " + m_vo.AD_Column_ID, m_linkColumnName); } } Env.setContext(m_vo.ctx, m_vo.WindowNo, m_vo.TabNo, "LinkColumnName", linkColumnName); } // setLinkColumnName /** * Is the tab current?. *
 
	 *	Yes 	- Table must be open 
	 *			- Query String is the same 
	 *			- Not Detail 
	 *			- Old link column value is same as current one 
	 *  
* @return true if current */ public boolean isCurrent() { // Open? if (!m_mTable.isOpen()) return false; // Same Query if (!m_oldQuery.equals(m_query.getWhereClause())) return false; // Detail? if (!isDetail()) return true; // Same link column value String value = Env.getContext(m_vo.ctx, m_vo.WindowNo, getLinkColumnName()); return m_linkValue.equals(value); } // isCurrent /** * Are Only Current Rows displayed * @return true if no history */ public boolean isOnlyCurrentRows() { return m_vo.onlyCurrentRows; } // isOnlyCurrentRows /** * Return Parent ArrayList * @return parent column names */ public ArrayList getParentColumnNames() { return m_parents; } // getParentColumnNames /** * Get Tree ID of this tab * @return ID */ private int getTreeID() { Log.trace(Log.l5_DData, "MTab.getTreeID " + m_vo.TableName); String SQL = "SELECT * FROM AD_ClientInfo WHERE AD_Client=" + Env.getContext(m_vo.ctx, m_vo.WindowNo, "#AD_Client_ID") + " ORDER BY AD_Org DESC"; // if (m_vo.TableName.equals("AD_Menu")) return 10; // MM else if (m_vo.TableName.equals("C_ElementValue")) return 20; // EV else if (m_vo.TableName.equals("M_Product")) return 30; // PR else if (m_vo.TableName.equals("C_BPartner")) return 40; // BP else if (m_vo.TableName.equals("AD_Org")) return 50; // OO else if (m_vo.TableName.equals("C_Project")) return 60; // PJ return 0; } // getTreeID /** * Returns true if this is a detail record * @return true if not parent tab */ public boolean isDetail() { // We have IsParent columns and/or a link column if (m_parents.size() > 0 || m_vo.AD_Column_ID != 0) return true; return false; } // isDetail /** * Is Printed (Document can be printed) * @return true if printing */ public boolean isPrinted() { return m_vo.AD_Process_ID != 0; } // isPrinted /** * Get WindowNo * @return window no */ public int getWindowNo() { return m_vo.WindowNo; } // getWindowNo /** * Get TabNo * @return tab no */ public int getTabNo() { return m_vo.TabNo; } // getTabNo /** * Get Process ID * @return Process ID */ public int getAD_Process_ID() { return m_vo.AD_Process_ID; } // getAD_Process_ID /** * Is High Volume? * @return true if high volumen table */ public boolean isHighVolume() { return m_vo.IsHighVolume; } // isHighVolume /** * Is Read Only? * @return true if read only */ public boolean isReadOnly() { return m_vo.IsReadOnly; } // isReadOnly /** * Is Single Row * @return true if single row */ public boolean isSingleRow() { return m_vo.IsSingleRow; } // isSingleRow; /** * Set Single Row. * Temporary store of current value * @param isSingleRow toggle */ public void setSingleRow (boolean isSingleRow) { m_vo.IsSingleRow = isSingleRow; } // setSingleRow /** * Has Tree * @return true if tree exists */ public boolean isTreeTab() { return m_vo.HasTree; } // isTreeTab /** * Get Table ID * @return Table ID */ public int getAD_Table_ID() { return m_vo.AD_Table_ID; } // getAD_Table_ID /** * Get TableName * @return Table Name */ public String getTableName() { return m_vo.TableName; } // getTableName /** * Get Tab Where Clause * @return where clause */ public String getWhereClause() { return m_vo.WhereClause; } // getWhereClause /** * Is Sort Tab * @return true if sort tab */ public boolean isSortTab() { return m_vo.IsSortTab; } // isSortTab /** * Get Order column for sort tab * @return AD_Column_ID */ public int getAD_ColumnSortOrder_ID() { return m_vo.AD_ColumnSortOrder_ID; } // getAD_ColumnSortOrder_ID /** * Get Yes/No column for sort tab * @return AD_Column_ID */ public int getAD_ColumnSortYesNo_ID() { return m_vo.AD_ColumnSortYesNo_ID; } // getAD_ColumnSortYesNo_ID /*************************************************************************/ /** * Get extended Where Clause (parent link) * @return parent link */ public String getWhereExtended() { return m_extendedWhere; } // getWhereExtended /** * Get Order By Clause * @param onlyCurrentRows only current rows * @return Order By Clause */ private String getOrderByClause(boolean onlyCurrentRows) { // If there is no Tab Order By - use info from Fields if (m_vo.OrderByClause.length() == 0 && m_OrderBys[0] != null) { m_vo.OrderByClause = m_OrderBys[0]; if (onlyCurrentRows && !isDetail()) // transaction order descending m_vo.OrderByClause += " DESC"; if (m_OrderBys[1] != null) m_vo.OrderByClause += "," + m_OrderBys[1]; } return m_vo.OrderByClause; } // getOrderByClause /*************************************************************************/ /** * Transaction support. * Depending on Table returns transaction info * @return info */ public String getTrxInfo() { // Order || Invoice if (m_vo.TableName.startsWith("C_Order") || m_vo.TableName.startsWith("C_Invoice")) { int Record_ID; boolean isOrder = m_vo.TableName.startsWith("C_Order"); // StringBuffer sql = new StringBuffer("SELECT COUNT(*) AS Lines,c.ISO_Code,o.TotalLines,o.GrandTotal," + "C_Base_Convert(o.GrandTotal,o.C_Currency_ID,o.AD_Client_ID,SysDate,o.AD_Org_ID) AS ConvAmt "); if (isOrder) { Record_ID = Env.getContextAsInt(m_vo.ctx, m_vo.WindowNo, "C_Order_ID"); sql.append("FROM C_Order o, C_Currency c, C_OrderLine l " + "WHERE o.C_Currency_ID=c.C_Currency_ID" + " AND o.C_Order_ID=?" + " AND o.C_Order_ID=l.C_Order_ID "); } else { Record_ID = Env.getContextAsInt(m_vo.ctx, m_vo.WindowNo, "C_Invoice_ID"); sql.append("FROM C_Invoice o, C_Currency c, C_InvoiceLine l " + "WHERE o.C_Currency_ID=c.C_Currency_ID" + " AND o.C_Invoice_ID=?" + " AND o.C_Invoice_ID=l.C_Invoice_ID "); } sql.append("GROUP BY o.C_Currency_ID, c.ISO_Code, o.TotalLines, o.GrandTotal, o.AD_Client_ID, o.AD_Org_ID"); Log.trace(Log.l3_Util, "MTab.getTrxInfo " + m_vo.TableName + " - " + Record_ID); MessageFormat mf = null; try { mf = new MessageFormat(Msg.getMsg(Env.getAD_Language(m_vo.ctx), "OrderSummary")); } catch (Exception e) { Log.error("MTab.getTrxInfo - OrderSummary=" + Msg.getMsg(Env.getAD_Language(m_vo.ctx), "OrderSummary"), e); } if (mf == null) return " "; /********************************************************************** * ** Message: OrderSummary ** * {0} Line(s) - {1,number,#,##0.00} - Toral: {2,number,#,##0.00} {3} = {4,number,#,##0.00} * * {0} - Number of lines * {1} - Line toral * {2} - Grand total (including tax, etc.) * {3} - Currency * (4) - Grand total converted to local currency */ Object[] arguments = new Object[5]; boolean filled = false; // try { PreparedStatement pstmt = DB.prepareStatement(sql.toString()); pstmt.setInt(1, Record_ID); ResultSet rs = pstmt.executeQuery(); if (rs.next()) { // {0} - Number of lines Integer lines = new Integer(rs.getInt(1)); arguments[0] = lines; // {1} - Line toral Double lineTotal = new Double(rs.getDouble(3)); arguments[1] = lineTotal; // {2} - Grand total (including tax, etc.) Double grandTotal = new Double(rs.getDouble(4)); arguments[2] = grandTotal; // {3} - Currency String currency = rs.getString(2); arguments[3] = currency; // (4) - Grand total converted to Euro Double grandEuro = new Double(rs.getDouble(5)); arguments[4] = grandEuro; filled = true; } rs.close(); pstmt.close(); } catch (SQLException e) { Log.error("MTab.getTrxInfo " + m_vo.TableName + "\nSQL=" + sql, e); } if (filled) return mf.format (arguments); return " "; } // Order || Invoice // Expense Report else if (m_vo.TableName.startsWith("S_TimeExpense") && m_vo.TabNo == 0) { int Record_ID = Env.getContextAsInt(m_vo.ctx, m_vo.WindowNo, "S_TimeExpense_ID"); Log.trace(Log.l3_Util, "MTab.getTrxInfo " + m_vo.TableName + " - " + Record_ID); MessageFormat mf = null; try { mf = new MessageFormat(Msg.getMsg(Env.getAD_Language(m_vo.ctx), "ExpenseSummary")); } catch (Exception e) { Log.error("MTab.getTrxInfo - ExpenseSummary=" + Msg.getMsg(Env.getAD_Language(m_vo.ctx), "ExpenseSummary"), e); } if (mf == null) return " "; /********************************************************************** * ** Message: ExpenseSummary ** * {0} Line(s) - Total: {1,number,#,##0.00} {2} * * {0} - Number of lines * {1} - Toral * {2} - Currency */ Object[] arguments = new Object[3]; boolean filled = false; // String SQL = "SELECT COUNT(*) AS Lines, SUM(ConvertedAmt*Qty) " + "FROM S_TimeExpenseLine " + "WHERE S_TimeExpense_ID=?"; // try { PreparedStatement pstmt = DB.prepareStatement(SQL); pstmt.setInt(1, Record_ID); ResultSet rs = pstmt.executeQuery(); if (rs.next()) { // {0} - Number of lines Integer lines = new Integer(rs.getInt(1)); arguments[0] = lines; // {1} - Line toral Double total = new Double(rs.getDouble(2)); arguments[1] = total; // {3} - Currency arguments[2] = " "; filled = true; } rs.close(); pstmt.close(); } catch (SQLException e) { Log.error("MTab.getTrxInfo " + m_vo.TableName + "\nSQL=" + SQL, e); } if (filled) return mf.format (arguments); return " "; } // S_TimeExpense // Default - No Trx Info return null; } // getTrxInfo /** * Load Dependant Information */ private void loadDependentInfo() { /** * Load Order Type from C_DocTypeTarget_ID */ if (m_vo.TableName.equals("C_Order")) { int C_DocTyp_ID = 0; Integer target = (Integer)getValue("C_DocTypeTarget_ID"); if (target != null) C_DocTyp_ID = target.intValue(); if (C_DocTyp_ID == 0) return; String sql = "SELECT DocSubTypeSO FROM C_DocType WHERE C_DocType_ID=?"; try { PreparedStatement pstmt = DB.prepareStatement(sql); pstmt.setInt(1, C_DocTyp_ID); ResultSet rs = pstmt.executeQuery(); if (rs.next()) Env.setContext(m_vo.ctx, m_vo.WindowNo, "OrderType", rs.getString(1)); rs.close(); pstmt.close(); } catch (SQLException e) { Log.error("MTab.loadOrderType", e); } } // loadOrderInfo } // loadDependentInfo /*************************************************************************/ /** * Load Attachments for this table */ public void loadAttachments() { Log.trace(Log.l3_Util, "MTab.loadAttachments #" + m_vo.TabNo); if (!canHaveAttachment()) return; String SQL = "SELECT AD_Attachment_ID, Record_ID FROM AD_Attachment " + "WHERE AD_Table_ID=?"; try { if (m_Attachment == null) m_Attachment = new HashMap(); else m_Attachment.clear(); PreparedStatement pstmt = DB.prepareStatement(SQL); pstmt.setInt(1, m_vo.AD_Table_ID); ResultSet rs = pstmt.executeQuery(); while (rs.next()) { Integer key = new Integer(rs.getInt(2)); Integer value = new Integer(rs.getInt(1)); m_Attachment.put(key, value); } rs.close(); pstmt.close(); } catch (SQLException e) { Log.error("MTab.loadAttachments", e); } Log.trace(Log.l4_Data, "Attachments=" + m_Attachment.size()); } // loadAttachment /** * Can this tab have Attachments?. *

* It can have an attachment if it has a key column ending with _ID. * The key column is empty, if there is no single identifying key. * @return true if record can have attachment */ public boolean canHaveAttachment() { if (getKeyColumnName().endsWith("_ID")) return true; return false; } // canHaveAttachment /** * Returns true, if current row has an Attachment * @return true if record has attchment */ public boolean hasAttachment() { if (m_Attachment == null) loadAttachments(); if (m_Attachment == null || m_Attachment.isEmpty()) return false; // Integer key = new Integer(m_mTable.getKeyID (m_currentRow)); return m_Attachment.containsKey(key); } // hasAttachment /** * Get Attachment_ID. * @return ID or 0, if not found */ public int getAD_AttachmentID() { if (m_Attachment == null) loadAttachments(); if (m_Attachment.isEmpty()) return 0; // Integer key = new Integer(m_mTable.getKeyID (m_currentRow)); Integer value = (Integer)m_Attachment.get(key); if (value == null) return 0; else return value.intValue(); } // getAttachmentID /*************************************************************************/ /** * Data Status Listener from MTable. * - get raw info and add current row information * - update the current row * - redistribute (fire) Data Status event * @param e event */ public void dataStatusChanged (DataStatusEvent e) { Log.trace(Log.l3_Util, "MTab.dataStatusChanged #" + m_vo.TabNo, e.toString()); int oldCurrentRow = e.getCurrentRow(); m_DataStatusEvent = e; // save it // when sorted set current row to 0 String msg = m_DataStatusEvent.getAD_Message(); if (msg != null && msg.equals("Sorted")) setCurrentRow(0, true); // set current row m_DataStatusEvent.setCurrentRow(m_currentRow); // Same row - update value if (oldCurrentRow == m_currentRow) { MField field = m_mTable.getField(e.getChangedColumn()); Object value = m_mTable.getValueAt(m_currentRow, e.getChangedColumn()); field.setValue(value, m_mTable.isInserting()); } else // Redistribute Info with current row info fireDataStatusChanged(m_DataStatusEvent); // Log.trace(Log.l3_Util, "MTab.dataStatusChanged #" + m_vo.TabNo + "- fini", e.toString()); } // dataStatusChanged /** * Inform Listeners and build WHO info * @param e event */ private void fireDataStatusChanged (DataStatusEvent e) { Log.trace(Log.l4_Data, "MTab.fireDataStatusChanged", e.toString()); if (m_dataStatusListeners != null) { // WHO Info if (e.getCurrentRow() >= 0) { e.Created = (Timestamp)getValue("Created"); e.CreatedBy = getValue("CreatedBy"); e.Updated = (Timestamp)getValue("Updated"); e.UpdatedBy = getValue("UpdatedBy"); // Info StringBuffer info = new StringBuffer(getTableName()); // We have a key column if (m_keyColumnName != null && m_keyColumnName.length() > 0) { info.append(" - ") .append(m_keyColumnName).append("=").append(getValue(m_keyColumnName)); } else // we have multiple parents { for (int i = 0; i < m_parents.size(); i++) { String keyCol = (String)m_parents.get(i); info.append(" - ") .append(keyCol).append("=").append(getValue(keyCol)); } } e.Info = info.toString(); } e.setInserting(m_mTable.isInserting()); // Distribute/fire it Vector listeners = m_dataStatusListeners; int count = listeners.size(); for (int i = 0; i < count; i++) ((DataStatusListener) listeners.elementAt(i)).dataStatusChanged(e); } // Log.trace(Log.l4_Data, "MTab.fireDataStatusChanged - fini", e.toString()); } // fireDataStatusChanged /** * Create and fire Data Status Error Event * @param AD_Message message * @param info info */ protected void fireDataStatusEEvent(String AD_Message, String info) { m_mTable.fireDataStatusEEvent(AD_Message, info); } // fireDataStatusEvent /** * Create and fire Data Status Error Event (from Error Log) * @param errorLog log */ protected void fireDataStatusEEvent (ValueNamePair errorLog) { if (errorLog != null) m_mTable.fireDataStatusEEvent(errorLog); } // fireDataStatusEvent /** * Get Current Row * @return current row */ public int getCurrentRow() { if (m_currentRow != verifyRow(m_currentRow)) setCurrentRow(m_mTable.getRowCount()-1, true); return m_currentRow; } // getCurrentRow /** * Get Current TableKey_ID * @return ID */ public int getCurrentKeyID() { return m_mTable.getKeyID(m_currentRow); } // getCurrentKeyID /** * Get Key ID of row * @param row row number * @return The Key ID of the row or -1 if not found */ public int getKeyID (int row) { return m_mTable.getKeyID (row); } // getCurrentKeyID /** * Navigate absulute - goto Row - (zero based). * - does nothing, if in current row * - saves old row if required * @param targetRow target row * @return current row */ public int navigate (int targetRow) { // nothing to do if (targetRow == m_currentRow) return m_currentRow; Log.trace(Log.l1_User, "MTab.navigate - Row=" + targetRow); // Row range check int newRow = verifyRow(targetRow); // Check, if we have old uncommitted data m_mTable.dataSave(newRow, false); // new position return setCurrentRow(newRow, true); } // navigate /** * Navigate relatively - i.e. plus/minus from current position * @param rowChange row change * @return current row */ public int navigateRelative (int rowChange) { return navigate (m_currentRow + rowChange); } // navigateRelative /** * Navigate to current now (reload) * @return current row */ public int navigateCurrent() { Log.trace(Log.l1_User, "MTab.navigateCurrent"); return setCurrentRow(m_currentRow, true); } // navigateCurrent /** * Row Range check * @param targetRow target row * @return checked row */ private int verifyRow (int targetRow) { int newRow = targetRow; // Table Open? if (!m_mTable.isOpen()) { Log.error("MTab.verifyRow - Table not open"); return -1; } // Row Count int rows = getRowCount(); if (rows == 0) { Log.trace(Log.l4_Data, "MTab.verifyRow", "No Rows"); return -1; } if (newRow >= rows) { newRow = rows-1; Log.trace(Log.l4_Data, "MTab.verifyRow", "Set to max Row: " + newRow); } else if (newRow < 0) { newRow = 0; Log.trace(Log.l4_Data, "MTab.verifyRow", "Set to first Row"); } return newRow; } // verifyRow /** * Set current row and load data into fields. * If there is no row - load nulls * @param newCurrentRow new current row * @param fireEvents fire events * @return current row */ private int setCurrentRow (int newCurrentRow, boolean fireEvents) { int oldCurrentRow = m_currentRow; m_currentRow = verifyRow (newCurrentRow); Log.trace(Log.l4_Data, "MTab.setCurrentRow = " + m_currentRow, "fire=" + fireEvents); // Update Field Values int size = m_mTable.getColumnCount(); for (int i = 0; i < size; i++) { MField mField = m_mTable.getField(i); // get Value from Table if (m_currentRow >= 0) { Object value = m_mTable.getValueAt(m_currentRow, i); mField.setValue(value, m_mTable.isInserting()); } else { // no rows - set to a reasonable value - not updateable // Object value = null; // if (mField.isKey() || mField.isParent() || mField.getColumnName().equals(m_linkColumnName)) // value = mField.getDefault(); mField.setValue(); } } loadDependentInfo(); if (!fireEvents) // prevents informing twice return m_currentRow; // inform VTable/.. -> rowChanged m_propertyChangeSupport.firePropertyChange(PROPERTY, oldCurrentRow, m_currentRow); // inform APanel/.. -> dataStatus with row updated if (m_DataStatusEvent == null) { Log.trace(Log.l5_DData, "MTab.setCurrentRow - no existing data status event"); } else { m_DataStatusEvent.setCurrentRow(m_currentRow); String status = m_DataStatusEvent.getAD_Message(); if (status == null || status.length() == 0) m_DataStatusEvent.setInfo("NavigateOrUpdate", null, false); fireDataStatusChanged(m_DataStatusEvent); } return m_currentRow; } // setCurrentRow /*************************************************************************/ /** * Get RowCount * @return row count */ public int getRowCount() { int count = m_mTable.getRowCount(); // Wait a bit if currently loading if (count == 0 && m_mTable.isLoading()) { try { Thread.sleep(100); // .1 sec } catch (Exception e) {} count = m_mTable.getRowCount(); } return count; } // getRowCount /** * Get Column/Field Count * @return field count */ public int getFieldCount() { return m_mTable.getColumnCount(); } // getFieldCount /** * Get Field by index * @param index index * @return MField */ public MField getField (int index) { return m_mTable.getField(index); } // getField /** * Get Field by DB column name * @param columnName column name * @return MField */ public MField getField (String columnName) { return m_mTable.getField(columnName); } // getField /** * Set New Value & call Callout * @param columnName database column name * @param value value * @return error message or "" */ public String setValue (String columnName, Object value) { if (columnName == null) return "NoColumn"; return setValue(m_mTable.getField(columnName), value); } // setValue /** * Set New Value & call Callout * @param field field * @param value value * @return error message or "" */ public String setValue (MField field, Object value) { if (field == null) return "NoField"; Log.trace(Log.l3_Util, "MTab.setValue - " + field.getColumnName() + "=" + value + " for row=" + m_currentRow); int col = m_mTable.findColumn(field.getColumnName()); m_mTable.setValueAt(value, m_currentRow, col, false); // return processFieldChange (field); } // setValue /** * Process Field Change - evaluate Dependencies and process Callouts. * * called from MTab.setValue or GridController.dataStatusChanged * @param changedField changed field * @return error message or "" */ public String processFieldChange (MField changedField) { processDependencies (changedField); return processCallout (changedField); } // processFieldChange /** * Evaluate Dependencies * @param changedField changed field */ private void processDependencies (MField changedField) { String columnName = changedField.getColumnName(); // Log.trace(Log.l4_Data, "Changed Column", columnName); // when column name is not in list of DependentOn fields - fini if (!isDependentOn(columnName)) return; // Get dependent MFields (may be because of display or dynamic lookup) ArrayList list = getDependentFieldList(columnName); for (int i = 0; i < list.size(); i++) { MField dependentField = (MField)list.get(i); // Log.trace(Log.l5_DData, "Dependent Field", dependentField==null ? "null" : dependentField.getColumnName()); // if the field has a lookup if (dependentField != null && dependentField.getLookup() instanceof MLookup) { MLookup mLookup = (MLookup)dependentField.getLookup(); // Log.trace(Log.l6_Database, "Lookup Validation", mLookup.getValidation()); // if the lookup is dynamic (i.e. contains this columnName as variable) if (mLookup.getValidation().indexOf("@"+columnName+"@") != -1) { Log.trace(Log.l6_Database, "MTab.processDependencies", columnName + " changed - " + dependentField.getColumnName() + " set to null"); // invalidate current selection setValue(dependentField, null); } } } // for all dependent fields } // processDependencies /** * Process Callout(s). *

* The Callout is in the string of * "class.method;class.method;" * If there is no class name, i.e. only a method name, the class is regarded * as CalloutSystem. * The class needs to comply with the Interface Callout. * * For a limited time, the old nptation of Sx_matheod / Ux_menthod is maintained. * * @param field field * @return error message or "" * @see org.compiere.model.Callout */ private String processCallout (MField field) { String callout = field.getCallout(); if (callout.length() == 0) return ""; Object value = field.getValue(); Object oldValue = field.getOldValue(); Log.trace(Log.l3_Util, "MTab.processCallout - " + field.getColumnName() + "=" + value + " (" + callout + ") - old=" + oldValue); StringTokenizer st = new StringTokenizer(callout, ";", false); while (st.hasMoreTokens()) // for each callout { String cmd = st.nextToken().trim(); Callout call = null; String method = null; int methodStart = cmd.lastIndexOf("."); try { if (methodStart == -1) // no class { // old version compatibility if (cmd.charAt(0) == 'S' && cmd.charAt(2) == '_') { call = new CalloutSystem(); if (cmd.length() > 3) method = cmd.substring(3); } else if (cmd.charAt(0) == 'U' && cmd.charAt(2) == '_') { Class cClass = Class.forName("com.compiere.custom.CalloutUser"); call = (Callout)cClass.newInstance(); if (cmd.length() > 3) method = cmd.substring(3); } else { call = new CalloutSystem(); method = cmd; } } else { Class cClass = Class.forName(cmd.substring(0,methodStart)); call = (Callout)cClass.newInstance(); method = cmd.substring(methodStart+1); } } catch (Exception e) { Log.error("MTab.processCallout", e); return "CalloutNameInvalid = " + cmd + " (" + e.toString() + ")"; } String retValue = ""; if (call != null && method != null && !method.equals("")) retValue = call.start(m_vo.ctx, method, m_vo.WindowNo, this, field, value, oldValue); else retValue = "CalloutNameInvalid = " + method; if (!retValue.equals("")) // interrupt on first error { Log.error("MTab.processCallout - " + retValue); return retValue; } } // for each callout return ""; } // processCallout /** * Get Value of Field with columnName * @param columnName column name * @return value */ public Object getValue (String columnName) { if (columnName == null) return null; MField field = m_mTable.getField(columnName); return getValue(field); } // getValue /** * Get Value of Field * @param field field * @return value */ public Object getValue (MField field) { if (field == null) return null; return field.getValue(); } // getValue /** * Get Value of Field in row * @param row row * @param columnName column name * @return value */ public Object getValue (int row, String columnName) { int col = m_mTable.findColumn(columnName); if (col == -1) return null; return m_mTable.getValueAt(row, col); } // getValue /** * toString * @return String representation */ public String toString() { String retValue = "MTab #" + m_vo.TabNo; if (m_vo != null) retValue += " " + m_vo.Name + " (" + m_vo.AD_Tab_ID + ")"; return retValue; } // toString /*************************************************************************/ /** * @param l listener */ public synchronized void removePropertyChangeListener(PropertyChangeListener l) { m_propertyChangeSupport.removePropertyChangeListener(l); } /** * @param l listener */ public synchronized void addPropertyChangeListener(PropertyChangeListener l) { m_propertyChangeSupport.addPropertyChangeListener(l); } /** * @param l listener */ public synchronized void removeDataStatusListener(DataStatusListener l) { if (m_dataStatusListeners != null && m_dataStatusListeners.contains(l)) { Vector v = (Vector) m_dataStatusListeners.clone(); v.removeElement(l); m_dataStatusListeners = v; } } /** * @param l listener */ public synchronized void addDataStatusListener(DataStatusListener l) { Vector v = m_dataStatusListeners == null ? new Vector(2) : (Vector) m_dataStatusListeners.clone(); if (!v.contains(l)) { v.addElement(l); m_dataStatusListeners = v; } } } // M_Tab