From c0163196337f05138f4dd354d34bf198e0bdadf7 Mon Sep 17 00:00:00 2001 From: hieplq Date: Tue, 4 Aug 2015 20:56:46 +0800 Subject: [PATCH] IDEMPIERE-2230:Info Window - remember selection during pagination --- .../oracle/201411061604_IDEMPIERE-2230.sql | 65 ++++ .../201411061604_IDEMPIERE-2230.sql | 62 ++++ .../org/compiere/model/I_AD_InfoColumn.java | 13 + .../src/org/compiere/model/MInfoColumn.java | 13 +- .../org/compiere/model/X_AD_InfoColumn.java | 26 +- .../org/adempiere/webui/info/InfoWindow.java | 31 +- .../org/adempiere/webui/panel/InfoPanel.java | 327 ++++++++++++++---- 7 files changed, 468 insertions(+), 69 deletions(-) create mode 100644 migration/i2.1z/oracle/201411061604_IDEMPIERE-2230.sql create mode 100644 migration/i2.1z/postgresql/201411061604_IDEMPIERE-2230.sql diff --git a/migration/i2.1z/oracle/201411061604_IDEMPIERE-2230.sql b/migration/i2.1z/oracle/201411061604_IDEMPIERE-2230.sql new file mode 100644 index 0000000000..5edc2d26d8 --- /dev/null +++ b/migration/i2.1z/oracle/201411061604_IDEMPIERE-2230.sql @@ -0,0 +1,65 @@ +SET SQLBLANKLINES ON +SET DEFINE OFF + +-- Nov 6, 2014 3:13:05 PM ICT +-- I forgot to set the DICTIONARY_ID_COMMENTS System Configurator +UPDATE AD_SysConfig SET Value='hieplq',Updated=TO_DATE('2014-11-06 15:13:05','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_SysConfig_ID=50001 +; + +-- Nov 6, 2014 3:13:13 PM ICT +INSERT INTO AD_Column (SeqNoSelection,IsSyncDatabase,Version,AD_Column_ID,IsMandatory,IsTranslated,IsIdentifier,SeqNo,IsParent,FieldLength,IsSelectionColumn,IsKey,IsAutocomplete,IsAllowLogging,AD_Column_UU,IsUpdateable,ColumnName,Description,Help,Name,IsAllowCopy,Updated,CreatedBy,IsActive,Created,UpdatedBy,IsToolbarButton,IsAlwaysUpdateable,IsEncrypted,IsSecure,AD_Element_ID,AD_Reference_ID,AD_Table_ID,AD_Client_ID,AD_Org_ID,EntityType) VALUES (0,'N',0,211799,'N','N','N',0,'N',1,'N','N','N','Y','4ef802de-84a4-4ebc-a364-8ba9a27a39b5','Y','IsKey','This column is the key in this table','The key column must also be display sequence 0 in the field definition and may be hidden.','Key column','N',TO_DATE('2014-11-06 15:13:12','YYYY-MM-DD HH24:MI:SS'),100,'Y',TO_DATE('2014-11-06 15:13:12','YYYY-MM-DD HH24:MI:SS'),100,'N','N','N','N',389,20,897,0,0,'D') +; + +-- Nov 6, 2014 3:13:25 PM ICT +ALTER TABLE AD_InfoColumn ADD IsKey CHAR(1) DEFAULT NULL CHECK (IsKey IN ('Y','N')) +; + +-- Nov 6, 2014 3:14:19 PM ICT +INSERT INTO AD_Field (SortNo,IsEncrypted,AD_Tab_ID,DisplayLength,IsSameLine,IsHeading,SeqNo,IsCentrallyMaintained,AD_Field_ID,IsReadOnly,Help,EntityType,Description,Name,AD_Field_UU,IsDisplayed,IsFieldOnly,UpdatedBy,CreatedBy,Updated,IsActive,IsDisplayedGrid,SeqNoGrid,XPosition,IsQuickEntry,AD_Client_ID,ColumnSpan,NumLines,IsAdvancedField,IsDefaultFocus,AD_Column_ID,AD_Org_ID,Created) VALUES (0,'N',844,0,'N','N',260,'Y',203471,'N','The key column must also be display sequence 0 in the field definition and may be hidden.','D','This column is the key in this table','Key column','dd0b953a-d575-4eac-b435-87db841346c6','Y','N',100,100,TO_DATE('2014-11-06 15:14:18','YYYY-MM-DD HH24:MI:SS'),'Y','Y',170,1,'N',0,1,1,'N','N',211799,0,TO_DATE('2014-11-06 15:14:18','YYYY-MM-DD HH24:MI:SS')) +; + +-- Nov 6, 2014 3:17:53 PM ICT +UPDATE AD_Field SET Help='In case data of info window come from a view, define a column contain unique value as key to keep selected record', Description='Define this column has unique value, play as key of table', Name='Is View Key',Updated=TO_DATE('2014-11-06 15:17:53','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=203471 +; + +-- Nov 6, 2014 3:18:15 PM ICT +UPDATE AD_Field SET SeqNo=170, IsDisplayed='Y', XPosition=6,Updated=TO_DATE('2014-11-06 15:18:15','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=203471 +; + +-- Nov 6, 2014 3:18:15 PM ICT +UPDATE AD_Field SET SeqNo=180,Updated=TO_DATE('2014-11-06 15:18:15','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=201720 +; + +-- Nov 6, 2014 3:18:15 PM ICT +UPDATE AD_Field SET SeqNo=190,Updated=TO_DATE('2014-11-06 15:18:15','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=201625 +; + +-- Nov 6, 2014 3:18:15 PM ICT +UPDATE AD_Field SET SeqNo=200,Updated=TO_DATE('2014-11-06 15:18:15','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=203048 +; + +-- Nov 6, 2014 3:18:15 PM ICT +UPDATE AD_Field SET SeqNo=210,Updated=TO_DATE('2014-11-06 15:18:15','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=201635 +; + +-- Nov 6, 2014 3:18:15 PM ICT +UPDATE AD_Field SET SeqNo=220,Updated=TO_DATE('2014-11-06 15:18:15','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=201636 +; + +-- Nov 6, 2014 3:18:15 PM ICT +UPDATE AD_Field SET SeqNo=230,Updated=TO_DATE('2014-11-06 15:18:15','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=13603 +; + +-- Nov 6, 2014 3:18:15 PM ICT +UPDATE AD_Field SET SeqNo=240,Updated=TO_DATE('2014-11-06 15:18:15','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=201622 +; + +-- Nov 6, 2014 3:18:15 PM ICT +UPDATE AD_Field SET SeqNo=250,Updated=TO_DATE('2014-11-06 15:18:15','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=201623 +; + +-- Nov 6, 2014 3:18:15 PM ICT +UPDATE AD_Field SET SeqNo=260,Updated=TO_DATE('2014-11-06 15:18:15','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=13597 +; +SELECT register_migration_script('201411061604_IDEMPIERE-2230.sql') FROM dual +; diff --git a/migration/i2.1z/postgresql/201411061604_IDEMPIERE-2230.sql b/migration/i2.1z/postgresql/201411061604_IDEMPIERE-2230.sql new file mode 100644 index 0000000000..0b6d0bb1a3 --- /dev/null +++ b/migration/i2.1z/postgresql/201411061604_IDEMPIERE-2230.sql @@ -0,0 +1,62 @@ +-- Nov 6, 2014 3:13:05 PM ICT +-- I forgot to set the DICTIONARY_ID_COMMENTS System Configurator +UPDATE AD_SysConfig SET Value='hieplq',Updated=TO_TIMESTAMP('2014-11-06 15:13:05','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_SysConfig_ID=50001 +; + +-- Nov 6, 2014 3:13:13 PM ICT +INSERT INTO AD_Column (SeqNoSelection,IsSyncDatabase,Version,AD_Column_ID,IsMandatory,IsTranslated,IsIdentifier,SeqNo,IsParent,FieldLength,IsSelectionColumn,IsKey,IsAutocomplete,IsAllowLogging,AD_Column_UU,IsUpdateable,ColumnName,Description,Help,Name,IsAllowCopy,Updated,CreatedBy,IsActive,Created,UpdatedBy,IsToolbarButton,IsAlwaysUpdateable,IsEncrypted,IsSecure,AD_Element_ID,AD_Reference_ID,AD_Table_ID,AD_Client_ID,AD_Org_ID,EntityType) VALUES (0,'N',0,211799,'N','N','N',0,'N',1,'N','N','N','Y','4ef802de-84a4-4ebc-a364-8ba9a27a39b5','Y','IsKey','This column is the key in this table','The key column must also be display sequence 0 in the field definition and may be hidden.','Key column','N',TO_TIMESTAMP('2014-11-06 15:13:12','YYYY-MM-DD HH24:MI:SS'),100,'Y',TO_TIMESTAMP('2014-11-06 15:13:12','YYYY-MM-DD HH24:MI:SS'),100,'N','N','N','N',389,20,897,0,0,'D') +; + +-- Nov 6, 2014 3:13:25 PM ICT +ALTER TABLE AD_InfoColumn ADD COLUMN IsKey CHAR(1) DEFAULT NULL CHECK (IsKey IN ('Y','N')) +; + +-- Nov 6, 2014 3:14:19 PM ICT +INSERT INTO AD_Field (SortNo,IsEncrypted,AD_Tab_ID,DisplayLength,IsSameLine,IsHeading,SeqNo,IsCentrallyMaintained,AD_Field_ID,IsReadOnly,Help,EntityType,Description,Name,AD_Field_UU,IsDisplayed,IsFieldOnly,UpdatedBy,CreatedBy,Updated,IsActive,IsDisplayedGrid,SeqNoGrid,XPosition,IsQuickEntry,AD_Client_ID,ColumnSpan,NumLines,IsAdvancedField,IsDefaultFocus,AD_Column_ID,AD_Org_ID,Created) VALUES (0,'N',844,0,'N','N',260,'Y',203471,'N','The key column must also be display sequence 0 in the field definition and may be hidden.','D','This column is the key in this table','Key column','dd0b953a-d575-4eac-b435-87db841346c6','Y','N',100,100,TO_TIMESTAMP('2014-11-06 15:14:18','YYYY-MM-DD HH24:MI:SS'),'Y','Y',170,1,'N',0,1,1,'N','N',211799,0,TO_TIMESTAMP('2014-11-06 15:14:18','YYYY-MM-DD HH24:MI:SS')) +; + +-- Nov 6, 2014 3:17:53 PM ICT +UPDATE AD_Field SET Help='In case data of info window come from a view, define a column contain unique value as key to keep selected record', Description='Define this column has unique value, play as key of table', Name='Is View Key',Updated=TO_TIMESTAMP('2014-11-06 15:17:53','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=203471 +; + +-- Nov 6, 2014 3:18:15 PM ICT +UPDATE AD_Field SET SeqNo=170, IsDisplayed='Y', XPosition=6,Updated=TO_TIMESTAMP('2014-11-06 15:18:15','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=203471 +; + +-- Nov 6, 2014 3:18:15 PM ICT +UPDATE AD_Field SET SeqNo=180,Updated=TO_TIMESTAMP('2014-11-06 15:18:15','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=201720 +; + +-- Nov 6, 2014 3:18:15 PM ICT +UPDATE AD_Field SET SeqNo=190,Updated=TO_TIMESTAMP('2014-11-06 15:18:15','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=201625 +; + +-- Nov 6, 2014 3:18:15 PM ICT +UPDATE AD_Field SET SeqNo=200,Updated=TO_TIMESTAMP('2014-11-06 15:18:15','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=203048 +; + +-- Nov 6, 2014 3:18:15 PM ICT +UPDATE AD_Field SET SeqNo=210,Updated=TO_TIMESTAMP('2014-11-06 15:18:15','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=201635 +; + +-- Nov 6, 2014 3:18:15 PM ICT +UPDATE AD_Field SET SeqNo=220,Updated=TO_TIMESTAMP('2014-11-06 15:18:15','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=201636 +; + +-- Nov 6, 2014 3:18:15 PM ICT +UPDATE AD_Field SET SeqNo=230,Updated=TO_TIMESTAMP('2014-11-06 15:18:15','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=13603 +; + +-- Nov 6, 2014 3:18:15 PM ICT +UPDATE AD_Field SET SeqNo=240,Updated=TO_TIMESTAMP('2014-11-06 15:18:15','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=201622 +; + +-- Nov 6, 2014 3:18:15 PM ICT +UPDATE AD_Field SET SeqNo=250,Updated=TO_TIMESTAMP('2014-11-06 15:18:15','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=201623 +; + +-- Nov 6, 2014 3:18:15 PM ICT +UPDATE AD_Field SET SeqNo=260,Updated=TO_TIMESTAMP('2014-11-06 15:18:15','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=13597 +; +SELECT register_migration_script('201411061604_IDEMPIERE-2230.sql') FROM dual +; diff --git a/org.adempiere.base/src/org/compiere/model/I_AD_InfoColumn.java b/org.adempiere.base/src/org/compiere/model/I_AD_InfoColumn.java index f61a56cac7..ce72c9ed80 100644 --- a/org.adempiere.base/src/org/compiere/model/I_AD_InfoColumn.java +++ b/org.adempiere.base/src/org/compiere/model/I_AD_InfoColumn.java @@ -309,6 +309,19 @@ public interface I_AD_InfoColumn */ public boolean isIdentifier(); + /** Column name IsKey */ + public static final String COLUMNNAME_IsKey = "IsKey"; + + /** Set Key column. + * This column is the key in this table + */ + public void setIsKey (boolean IsKey); + + /** Get Key column. + * This column is the key in this table + */ + public boolean isKey(); + /** Column name IsMandatory */ public static final String COLUMNNAME_IsMandatory = "IsMandatory"; diff --git a/org.adempiere.base/src/org/compiere/model/MInfoColumn.java b/org.adempiere.base/src/org/compiere/model/MInfoColumn.java index 66e7549ffc..9cbf39906b 100644 --- a/org.adempiere.base/src/org/compiere/model/MInfoColumn.java +++ b/org.adempiere.base/src/org/compiere/model/MInfoColumn.java @@ -20,6 +20,7 @@ import java.sql.ResultSet; import java.util.Properties; import java.util.logging.Level; +import org.adempiere.model.IInfoColumn; import org.compiere.model.AccessSqlParser.TableInfo; import org.compiere.util.Env; import org.compiere.util.Evaluatee; @@ -31,7 +32,7 @@ import org.compiere.util.Evaluator; * @author Jorg Janke * @version $Id: MInfoColumn.java,v 1.2 2006/07/30 00:51:03 jjanke Exp $ */ -public class MInfoColumn extends X_AD_InfoColumn +public class MInfoColumn extends X_AD_InfoColumn implements IInfoColumn { /** * @@ -176,4 +177,14 @@ public class MInfoColumn extends X_AD_InfoColumn getParent().saveEx(get_TrxName()); return super.afterDelete(success); } + + @Override + public int getInfoColumnID() { + return get_ID(); + } + + @Override + public MInfoColumn getAD_InfoColumn() { + return this; + } } // MInfoColumn diff --git a/org.adempiere.base/src/org/compiere/model/X_AD_InfoColumn.java b/org.adempiere.base/src/org/compiere/model/X_AD_InfoColumn.java index 977a1ca9b4..5eb12ca92e 100644 --- a/org.adempiere.base/src/org/compiere/model/X_AD_InfoColumn.java +++ b/org.adempiere.base/src/org/compiere/model/X_AD_InfoColumn.java @@ -30,7 +30,7 @@ public class X_AD_InfoColumn extends PO implements I_AD_InfoColumn, I_Persistent /** * */ - private static final long serialVersionUID = 20141030L; + private static final long serialVersionUID = 20141106L; /** Standard Constructor */ public X_AD_InfoColumn (Properties ctx, int AD_InfoColumn_ID, String trxName) @@ -442,6 +442,30 @@ public class X_AD_InfoColumn extends PO implements I_AD_InfoColumn, I_Persistent return false; } + /** Set Key column. + @param IsKey + This column is the key in this table + */ + public void setIsKey (boolean IsKey) + { + set_Value (COLUMNNAME_IsKey, Boolean.valueOf(IsKey)); + } + + /** Get Key column. + @return This column is the key in this table + */ + public boolean isKey () + { + Object oo = get_Value(COLUMNNAME_IsKey); + if (oo != null) + { + if (oo instanceof Boolean) + return ((Boolean)oo).booleanValue(); + return "Y".equals(oo); + } + return false; + } + /** Set Mandatory. @param IsMandatory Data entry is required in this column diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/info/InfoWindow.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/info/InfoWindow.java index 0d6a45614d..ea65a2d772 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/info/InfoWindow.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/info/InfoWindow.java @@ -206,6 +206,7 @@ public class InfoWindow extends InfoPanel implements ValueChangeListener, EventL processQueryValue(); } } + } /** @@ -507,7 +508,10 @@ public class InfoWindow extends InfoPanel implements ValueChangeListener, EventL infoColumns = infoWindow.getInfoColumns(tableInfos); gridFields = new ArrayList(); + for(MInfoColumn infoColumn : infoColumns) { + if (infoColumn.isKey()) + keyColumnOfView = infoColumn; String columnName = infoColumn.getColumnName(); /*!m_lookup && infoColumn.isMandatory():apply Mandatory only case open as window and only for criteria field*/ boolean isMandatory = !m_lookup && infoColumn.isMandatory() && infoColumn.isQueryCriteria(); @@ -686,11 +690,21 @@ public class InfoWindow extends InfoPanel implements ValueChangeListener, EventL } columnInfo.setColDescription(infoColumn.get_Translation("Description")); columnInfo.setGridField(gridFields.get(i)); - list.add(columnInfo); + list.add(columnInfo); + + if (keyColumnOfView == infoColumn){ + if (columnInfo.getColClass().equals(IDColumn.class)) + isIDColumnKeyOfView = true; + indexKeyOfView = list.size() - 1; + } } i++; } + if (keyColumnOfView == null){ + isIDColumnKeyOfView = true;// because use main key + } + columnInfos = list.toArray(new ColumnInfo[0]); prepareTable(columnInfos, infoWindow.getFromClause(), p_whereClause, infoWindow.getOrderByClause()); @@ -974,7 +988,7 @@ public class InfoWindow extends InfoPanel implements ValueChangeListener, EventL } addViewIDToQuery(); - + addKeyViewToQuery(); if (m_sqlMain.length() > 0 && infoWindow.isDistinct()) { m_sqlMain = m_sqlMain.substring("SELECT ".length()); @@ -999,6 +1013,19 @@ public class InfoWindow extends InfoPanel implements ValueChangeListener, EventL m_sqlMain = addMoreColumnToQuery (m_sqlMain, infoProcessList); } + /** + * if {@link #keyColumnOfView} not null and not display, add query to query it's value + */ + protected void addKeyViewToQuery () { + if (isNeedAppendKeyViewData()){ + m_sqlMain = addMoreColumnToQuery (m_sqlMain, new IInfoColumn [] {keyColumnOfView}); + } + } + + @Override + public boolean isNeedAppendKeyViewData() { + return (keyColumnOfView != null && !keyColumnOfView.isDisplayed(infoContext, p_WindowNo)); + } /** * because data of infoColumn have isDisplay = false not load, diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/panel/InfoPanel.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/panel/InfoPanel.java index 7fe1a9646b..4e794b4a22 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/panel/InfoPanel.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/panel/InfoPanel.java @@ -30,9 +30,11 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Vector; import java.util.logging.Level; +import org.adempiere.exceptions.AdempiereException; import org.adempiere.model.IInfoColumn; import org.adempiere.model.MInfoProcess; import org.adempiere.model.MInfoRelated; @@ -131,6 +133,41 @@ public abstract class InfoPanel extends Window implements EventListener, // Num of page preload, default is 2 page before current and 2 page after current protected int numPagePreLoad = MSysConfig.getIntValue(MSysConfig.ZK_INFO_NUM_PAGE_RELOAD, DEFAULT_PAGE_RELOAD); + /** + * MInfoColumn has isKey = true, play as key column in case non column has + * isKey = true, this column is null and we use {@link #p_keyColumn} + */ + protected MInfoColumn keyColumnOfView = null; + + /** + * index of {@link #keyColumnOfView} in data model, set when prepare listbox + */ + protected int indexKeyOfView = -1; + + protected boolean isIDColumnKeyOfView = false; + + /** + * store selected record info + * key of map is value of column play as keyView + * in case has no key coloumn of view, use value of {@link #p_keyColumn} + * zk6.x listview don't provide event when click to checkbox select all, + * so we can't manage selectedRecord time by time. + * each time change page we will update this list with current + * selected record of this page by call function + * {@link #updateListSelected()} when move to zk7, just enough handle + * onclick. because don't direct use recordSelectedData, call + * {@link #getSelectedRowInfo()} + */ + protected Map> recordSelectedData = new HashMap>(); + + /** + * when requery but don't clear selected record (example after run process) + * set flag to true to run sync selected record, also + * {@link #syncSelectedAfterRequery()} + */ + protected boolean isRequeryByRunSuccessProcess = false; + + public static InfoPanel create (int WindowNo, String tableName, String keyColumn, String value, boolean multiSelection, String whereClause) @@ -552,6 +589,8 @@ public abstract class InfoPanel extends Window implements EventListener, appendDataForViewID(rs, data, lsReadedColumn); appendDataForParentLink(rs, data, lsReadedColumn); + + appendDataForKeyView (rs, data, lsReadedColumn); } /** @@ -580,6 +619,20 @@ public abstract class InfoPanel extends Window implements EventListener, appendInfoColumnData(rs, data, relatedInfoList, listReadedColumn); } + /** + * save data of all viewID column in infoProcessList to end of data line + * when override {@link #readData(ResultSet)} consider call this method + * IDEMPIERE-1970 + * @param rs record set to read data + * @param data data line to append + * @param listReadedColumn list column is appended + * @throws SQLException + */ + protected void appendDataForKeyView(ResultSet rs, List data, List listReadedColumn) throws SQLException { + if (isNeedAppendKeyViewData()) + appendInfoColumnData(rs, data, new IInfoColumn [] {keyColumnOfView}, listReadedColumn); + } + /** * save data of all infoColumn in listModelHaveInfoColumn to end of data line * @param rs record set to read data @@ -678,6 +731,7 @@ public abstract class InfoPanel extends Window implements EventListener, model.setMultiple(p_multipleSelection); contentPanel.setData(model, null); } + restoreSelectedInPage(); int no = m_count; setStatusLine(Integer.toString(no) + " " + Msg.getMsg(Env.getCtx(), "SearchRows_EnterQuery"), false); setStatusDB(Integer.toString(no)); @@ -957,47 +1011,24 @@ public abstract class InfoPanel extends Window implements EventListener, /** * Get the keys of selected row/s based on layout defined in prepareTable + * @deprecated this function should deprecated and replace with {@link #getListKeyValueOfSelectedRow()} to support view at infoWindow * @return IDs if selection present * @author ashley */ protected ArrayList getSelectedRowKeys() { ArrayList selectedDataList = new ArrayList(); - - if (contentPanel.getKeyColumnIndex() == -1) + Collection lsKeyValueOfSelectedRow = getSelectedRowInfo().keySet(); + if (lsKeyValueOfSelectedRow.size() == 0) { return selectedDataList; } if (p_multipleSelection) - { - int[] rows = contentPanel.getSelectedIndices(); - for (int row = 0; row < rows.length; row++) - { - Object data = contentPanel.getModel().getValueAt(rows[row], contentPanel.getKeyColumnIndex()); - if (data instanceof IDColumn) - { - IDColumn dataColumn = (IDColumn)data; - selectedDataList.add(dataColumn.getRecord_ID()); - } - else - { - log.severe("For multiple selection, IDColumn should be key column for selection"); - } - } - } - - if (selectedDataList.size() == 0) - { - int row = contentPanel.getSelectedRow(); - if (row != -1 && contentPanel.getKeyColumnIndex() != -1) - { - Object data = contentPanel.getModel().getValueAt(row, contentPanel.getKeyColumnIndex()); - if (data instanceof IDColumn) - selectedDataList.add(((IDColumn)data).getRecord_ID()); - if (data instanceof Integer) - selectedDataList.add((Integer)data); - } + { + for (Integer key : lsKeyValueOfSelectedRow){ + selectedDataList.add(key); + } } return selectedDataList; @@ -1026,39 +1057,24 @@ public abstract class InfoPanel extends Window implements EventListener, if (p_multipleSelection) { - int[] rows = contentPanel.getSelectedIndices(); - - // this flag to just check key column in first record - boolean isCheckedKeyType = false; - - for (int row = 0; row < rows.length; row++) + Map > selectedRow = getSelectedRowInfo(); + + for (Entry> selectedInfo : selectedRow.entrySet()) { // get key data column - Object keyData = contentPanel.getModel().getValueAt(rows[row], contentPanel.getKeyColumnIndex()); + Integer keyData = selectedInfo.getKey(); - // check key data must is IDColumn - if (!isCheckedKeyType){ - if (keyData instanceof IDColumn){ - isCheckedKeyType = true; - }else{ - log.severe("For multiple selection, IDColumn should be key column for selection"); - break; - } - } - - IDColumn dataColumn = (IDColumn)keyData; - if (infoCulumnId > 0){ // have viewID, get it int dataIndex = columnDataIndex.get(infoCulumnId) + p_layout.length; // get row data from model - Object viewIDValue = contentPanel.getModel().getDataAt(rows[row], dataIndex); + Object viewIDValue = selectedInfo.getValue().get(dataIndex); - m_viewIDMap.add (new KeyNamePair(dataColumn.getRecord_ID(), viewIDValue == null?null:viewIDValue.toString())); + m_viewIDMap.add (new KeyNamePair(keyData, viewIDValue == null?null:viewIDValue.toString())); }else{ // hasn't viewID, set viewID value is null - m_viewIDMap.add (new KeyNamePair(dataColumn.getRecord_ID(), null)); + m_viewIDMap.add (new KeyNamePair(keyData, null)); } } @@ -1070,7 +1086,180 @@ public abstract class InfoPanel extends Window implements EventListener, } } - + + /** + * need overrider at infoWindow to check isDisplay + * @return + */ + protected boolean isNeedAppendKeyViewData (){ + return false; + } + + /** + * Check type of object is IDColumn + * @param keyData + * @param isCheckNull when true, raise exception when data is null + * @return + */ + protected boolean isIDColumn(Object keyData, boolean isCheckNull){ + if (isCheckNull && keyData == null){ + AdempiereException ex = getKeyNullException(); + log.severe(ex.getMessage()); + throw ex; + } + + if (keyData != null && keyData instanceof IDColumn){ + return true; + } + + return false; + } + + /** + * call {@link #isIDColumn(Object, boolean)} without check null value + * @param keyData + * @return + */ + protected boolean isIDColumn(Object keyData){ + return isIDColumn(keyData, false); + } + + /** + * get all selected record of current page and update to {@link #recordSelectedData} + * remove unselected record and add new selected record + * we maintain value of key, and extra value append by {@link #appendInfoColumnData(ResultSet, List, IInfoColumn[], List)} + */ + protected void updateListSelected (){ + if (!p_multipleSelection){ + return; + } + + for (int rowIndex = 0; rowIndex < contentPanel.getModel().getRowCount(); rowIndex++){ + Integer keyCandidate = getColumnValue(rowIndex); + + @SuppressWarnings("unchecked") + List candidateRecord = (List)contentPanel.getModel().get(rowIndex); + + if (contentPanel.getModel().isSelected(candidateRecord)){ + recordSelectedData.put(keyCandidate, candidateRecord);// add or update selected record info + }else{ + if (recordSelectedData.containsKey(keyCandidate)){// unselected record + recordSelectedData.remove(keyCandidate); + } + } + + } + } + + /** + * get data index of keyView + * @return + */ + protected int getIndexKeyColumnOfView (){ + if (keyColumnOfView == null){ + return contentPanel.getKeyColumnIndex(); + }else if (isNeedAppendKeyViewData()){ + return columnDataIndex.get(keyColumnOfView.getInfoColumnID()) + p_layout.length; + }else{ + return indexKeyOfView; + } + } + + /** + * go through all data record, in case key value is in {@link #recordSelectedData}, mark it as selected record + */ + protected void restoreSelectedInPage (){ + if (!p_multipleSelection) + return; + + Collection lsSelectionRecord = new ArrayList(); + for (int rowIndex = 0; rowIndex < contentPanel.getModel().getRowCount(); rowIndex++){ + Integer keyViewValue = getColumnValue(rowIndex); + if (recordSelectedData.containsKey(keyViewValue)){ + // TODO: maybe add logic to check value of current record (focus only to viewKeys value) is same as value save in lsSelectedKeyValue + // because record can change by other user + lsSelectionRecord.add(contentPanel.getModel().get(rowIndex)); + } + } + + contentPanel.getModel().setSelection(lsSelectionRecord); + } + + + protected AdempiereException getKeyNullException (){ + String errorMessage = String.format("has null value at column %1$s use as key of view in info window %2$s", + keyColumnOfView == null ? p_keyColumn : keyColumnOfView, infoWindow.getName()); + return new AdempiereException(errorMessage); + } + /** + * get keyView value at rowIndex and clumnIndex + * also check in case value is null will rise a exception + * @param rowIndex + * @param columnIndex + * @return + */ + protected Integer getColumnValue (int rowIndex){ + + int keyIndex = getIndexKeyColumnOfView(); + Integer keyValue = null; + // get row data from model + Object keyColumValue = contentPanel.getModel().getDataAt(rowIndex, keyIndex); + // throw exception when value is null + if (keyColumValue == null){ + AdempiereException ex = getKeyNullException(); + log.severe(ex.getMessage()); + throw ex; + } + + // IDColumn is recreate after change page, because use value of IDColumn + if (keyColumValue != null && keyColumValue instanceof IDColumn){ + keyColumValue = ((IDColumn)keyColumValue).getRecord_ID(); + } + + if (keyColumValue instanceof Integer){ + keyValue = (Integer)keyColumValue; + }else { + String msg = "column play keyView should is integer"; + AdempiereException ex = new AdempiereException (msg); + log.severe(msg); + throw ex; + } + + return (Integer)keyValue; + } + + /** + * in case requery data, but want store selected record (example when run success a process) + * we must sync selected row, because some selected row maybe not at data list (process make it change not map with query) + * current 1000 line cache + * because in case query get more 1000 record we can't sync or maintain selected record (ever maintain for current page will make user confuse). + * just clear selection + * in case < 1000 record is ok + * TODO:rewrite + */ + protected void syncSelectedAfterRequery (){ + if (isRequeryByRunSuccessProcess){ + isRequeryByRunSuccessProcess = false; + //TODO:it's hard to ensure in case use keyViewId we can re-sync. some issue: + // + after RunSuccessProcess maybe key of record is change. + // + after RunSuccessProcess maybe value of viewID change. + // + after RunSuccessProcess maybe some record is out of query result + // + when load many page, sync at one time effect to performance + // maybe make two list, just sync for first page, old list use for reference, + // when user change page will use it for restore selected record, synced record will copy to new list + } + } + + /** + * update list column key value of selected record and return this list + * @return {@link #recordSelectedData} after update + */ + public Map> getSelectedRowInfo (){ + updateListSelected(); + return recordSelectedData; + } + + /** * Get selected Keys * @return selected keys (Integers) @@ -1173,7 +1362,7 @@ public abstract class InfoPanel extends Window implements EventListener, * enable all control button or disable all rely to selected record */ protected void enableButtons (){ - boolean enable = (contentPanel.getSelectedCount() > 0); + boolean enable = (contentPanel.getSelectedCount() > 0 || getSelectedRowInfo().size() > 0); enableButtons(enable); } @@ -1276,6 +1465,9 @@ public abstract class InfoPanel extends Window implements EventListener, /** * Save Selection Details * To be overwritten by concrete classes + * this function call when close info window. + * default infoWindow will set value of all column of current selected record to environment variable with {@link Env.TAB_INF} + * class extends can do more by override it. */ protected void saveSelectionDetail() {} @@ -1425,6 +1617,7 @@ public abstract class InfoPanel extends Window implements EventListener, // IDEMPIERE-1334 handle event click into process button end else if (event.getTarget() == paging) { + updateListSelected(); int pgNo = paging.getActivePage(); if (pageNo != pgNo) { @@ -1442,7 +1635,7 @@ public abstract class InfoPanel extends Window implements EventListener, model.addTableModelListener(this); model.setMultiple(p_multipleSelection); contentPanel.setData(model, null); - + restoreSelectedInPage(); //contentPanel.setSelectedIndex(0); } } @@ -1570,6 +1763,7 @@ public abstract class InfoPanel extends Window implements EventListener, public void onEvent(Event event) throws Exception { ProcessModalDialog processModalDialog = (ProcessModalDialog)event.getTarget(); if (DialogEvents.ON_BEFORE_RUN_PROCESS.equals(event.getName())){ + updateListSelected(); // store in T_Selection table selected rows for Execute Process that retrieves from T_Selection in code. DB.createT_SelectionNew(pInstanceID, getSaveKeys(getInfoColumnIDFromProcess(processModalDialog.getAD_Process_ID())), null); @@ -1585,7 +1779,8 @@ public abstract class InfoPanel extends Window implements EventListener, enableButtons(); }else if (!m_pi.isError()){ ProcessInfoDialog.showProcessInfo(m_pi, p_WindowNo, InfoPanel.this, true); - Clients.response(new AuEcho(InfoPanel.this, "onQueryCallback", m_results)); + isRequeryByRunSuccessProcess = true; + Clients.response(new AuEcho(InfoPanel.this, "onQueryCallback", null)); } } @@ -1638,17 +1833,18 @@ public abstract class InfoPanel extends Window implements EventListener, } m_sqlUserOrder=""; executeQuery(); - renderItems(); - // IDEMPIERE-1334 after refresh, restore prev selected item start - if (event != null && event.getData() != null){ - @SuppressWarnings("unchecked") - List lsSelectedKey = (List)event.getData(); - contentPanel.setSelectedByKeys(lsSelectedKey); - m_results.clear(); - } + renderItems(); + // IDEMPIERE-1334 after refresh, restore prev selected item start // just evaluate display logic of process button when requery by use click requery button if (isQueryByUser){ bindInfoProcess(); + // reset selected list + recordSelectedData.clear(); + isRequeryByRunSuccessProcess = false; + } + if (isRequeryByRunSuccessProcess){ + syncSelectedAfterRequery(); + restoreSelectedInPage(); } // IDEMPIERE-1334 after refresh, restore prev selected item end updateSubcontent (); @@ -1668,7 +1864,7 @@ public abstract class InfoPanel extends Window implements EventListener, protected void onOk() { - if (!contentPanel.getChildren().isEmpty() && contentPanel.getSelectedRowKey()!=null) + if (!contentPanel.getChildren().isEmpty() && getSelectedRowInfo().size() > 0) { dispose(true); } @@ -1763,6 +1959,7 @@ public abstract class InfoPanel extends Window implements EventListener, } // dispose public void sort(Comparator cmpr, boolean ascending) { + updateListSelected(); WListItemRenderer.ColumnComparator lsc = (WListItemRenderer.ColumnComparator) cmpr; if (m_useDatabasePaging) {