diff --git a/migration/i5.1z/oracle/201507032015_IDEMPIERE-2709.sql b/migration/i5.1z/oracle/201507032015_IDEMPIERE-2709.sql new file mode 100644 index 0000000000..c2b4acd210 --- /dev/null +++ b/migration/i5.1z/oracle/201507032015_IDEMPIERE-2709.sql @@ -0,0 +1,35 @@ +SET SQLBLANKLINES ON +SET DEFINE OFF + +-- IDEMPIERE-2709: Adding support for editable field on info window +-- Jul 3, 2015 6:10:42 PM IST +INSERT INTO AD_Column (AD_Column_ID,Version,Name,Description,Help,AD_Table_ID,ColumnName,DefaultValue,FieldLength,IsKey,IsParent,IsMandatory,IsTranslated,IsIdentifier,SeqNo,IsEncrypted,AD_Reference_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Element_ID,IsUpdateable,IsSelectionColumn,EntityType,IsSyncDatabase,IsAlwaysUpdateable,IsAutocomplete,IsAllowLogging,AD_Column_UU,IsAllowCopy,IsToolbarButton,IsSecure) VALUES (212216,0,'Read Only','Field is read only','The Read Only indicates that this field may only be Read. It may not be updated.',897,'IsReadOnly','Y',1,'N','N','Y','N','N',0,'N',20,0,0,'Y',TO_DATE('2015-07-03 18:10:41','YYYY-MM-DD HH24:MI:SS'),100,TO_DATE('2015-07-03 18:10:41','YYYY-MM-DD HH24:MI:SS'),100,405,'Y','N','D','N','N','N','Y','3a94dcce-25f4-4382-9547-a8f18949bbe7','Y','N','N') +; + +-- Jul 3, 2015 6:10:49 PM IST +ALTER TABLE AD_InfoColumn ADD IsReadOnly CHAR(1) DEFAULT 'Y' CHECK (IsReadOnly IN ('Y','N')) NOT NULL +; + +-- Jul 3, 2015 6:15:02 PM IST +INSERT INTO AD_Field (AD_Field_ID,Name,Description,Help,AD_Tab_ID,AD_Column_ID,IsDisplayed,DisplayLength,SeqNo,SortNo,IsSameLine,IsHeading,IsFieldOnly,IsEncrypted,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,IsReadOnly,IsCentrallyMaintained,EntityType,AD_Field_UU,IsDisplayedGrid,SeqNoGrid,XPosition,ColumnSpan,NumLines,IsQuickEntry,IsDefaultFocus,IsAdvancedField) VALUES (203829,'Read Only','Field is read only','The Read Only indicates that this field may only be Read. It may not be updated.',844,212216,'Y',0,165,0,'N','N','N','N',0,0,'Y',TO_DATE('2015-07-03 18:14:59','YYYY-MM-DD HH24:MI:SS'),100,TO_DATE('2015-07-03 18:14:59','YYYY-MM-DD HH24:MI:SS'),100,'N','Y','D','ebc3464e-96a2-447d-a846-b964ede1b66f','Y',170,1,1,1,'N','N','N') +; + +-- Table: t_selection_infowindow + +-- DROP TABLE t_selection_infowindow; + +CREATE TABLE t_selection_infowindow +( + ad_pinstance_id NUMBER(10,0) NOT NULL, + t_selection_id NUMBER(10,0) NOT NULL, + viewid VARCHAR2(30), + columnname VARCHAR2(255) NOT NULL, + value_string VARCHAR2(255), + value_date date, + value_number NUMBER, + info VARCHAR2(60), + CONSTRAINT t_selection_infowindow_key PRIMARY KEY (ad_pinstance_id, t_selection_id, columnname) +); + +SELECT register_migration_script('201507032015_IDEMPIERE-2709.sql') FROM dual +; diff --git a/migration/i5.1z/oracle/201806181810_IDEMPIERE-2709.sql b/migration/i5.1z/oracle/201806181810_IDEMPIERE-2709.sql new file mode 100644 index 0000000000..4f2007a8b6 --- /dev/null +++ b/migration/i5.1z/oracle/201806181810_IDEMPIERE-2709.sql @@ -0,0 +1,54 @@ +SET SQLBLANKLINES ON +SET DEFINE OFF + +-- InfoWindow selection editable fields +-- 18-giu-2018 16.11.33 CEST +INSERT INTO AD_Element (AD_Element_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,ColumnName,Name,Description,Help,PrintName,EntityType,AD_Element_UU) VALUES (203216,0,0,'Y',TO_DATE('2018-06-18 16:11:32','YYYY-MM-DD HH24:MI:SS'),100,TO_DATE('2018-06-18 16:11:32','YYYY-MM-DD HH24:MI:SS'),100,'InputFieldValidation','Input field validation','Input field validaton query','Input field validaton query','Input field validation','D','b457c250-ced2-415f-aaae-9bc7e545cb01') +; + +-- 18-giu-2018 16.12.33 CEST +INSERT INTO AD_Column (AD_Column_ID,Version,Name,Description,Help,AD_Table_ID,ColumnName,FieldLength,IsKey,IsParent,IsMandatory,IsTranslated,IsIdentifier,SeqNo,IsEncrypted,AD_Reference_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Element_ID,IsUpdateable,IsSelectionColumn,EntityType,IsSyncDatabase,IsAlwaysUpdateable,IsAutocomplete,IsAllowLogging,AD_Column_UU,IsAllowCopy,SeqNoSelection,IsToolbarButton,IsSecure) VALUES (213525,0,'Input field validation','Input field validaton query','Input field validaton query',897,'InputFieldValidation',2000,'N','N','N','N','N',0,'N',14,0,0,'Y',TO_DATE('2018-06-18 16:12:33','YYYY-MM-DD HH24:MI:SS'),100,TO_DATE('2018-06-18 16:12:33','YYYY-MM-DD HH24:MI:SS'),100,203216,'Y','N','D','N','N','N','Y','d0a5e559-7621-496c-9269-715b1040395b','Y',0,'N','N') +; + +-- 18-giu-2018 16.12.49 CEST +UPDATE AD_Column SET EntityType='D',Updated=TO_DATE('2018-06-18 16:12:49','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Column_ID=213525 +; + +-- 18-giu-2018 16.21.51 CEST +INSERT INTO AD_Field (AD_Field_ID,Name,Description,Help,AD_Tab_ID,AD_Column_ID,IsDisplayed,DisplayLength,SeqNo,SortNo,IsSameLine,IsHeading,IsFieldOnly,IsEncrypted,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,IsReadOnly,IsCentrallyMaintained,EntityType,AD_Field_UU,IsDisplayedGrid,SeqNoGrid,XPosition,ColumnSpan,NumLines,IsQuickEntry,IsDefaultFocus,IsAdvancedField) VALUES (205588,'Input field validation','Input field validaton query','Input field validaton query',844,213525,'Y',0,280,0,'N','N','N','N',0,0,'Y',TO_DATE('2018-06-18 16:21:50','YYYY-MM-DD HH24:MI:SS'),100,TO_DATE('2018-06-18 16:21:50','YYYY-MM-DD HH24:MI:SS'),100,'N','Y','D','26da27d1-b040-49dc-a3e6-ecd9f273636f','Y',190,1,1,1,'N','N','N') +; + +-- 18-giu-2018 16.22.23 CEST +UPDATE AD_Field SET SeqNo=290, AD_Reference_Value_ID=NULL, AD_Val_Rule_ID=NULL, IsToolbarButton=NULL,Updated=TO_DATE('2018-06-18 16:22:23','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=205588 +; + +-- 18-giu-2018 16.24.44 CEST +UPDATE AD_Field SET AD_Reference_Value_ID=NULL, AD_Val_Rule_ID=NULL, ColumnSpan=5, NumLines=3, IsToolbarButton=NULL,Updated=TO_DATE('2018-06-18 16:24:44','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=205588 +; + +-- 18-giu-2018 16.25.24 CEST +UPDATE AD_Field SET AD_Reference_Value_ID=NULL, AD_Val_Rule_ID=NULL, IsDisplayedGrid='N', IsToolbarButton=NULL,Updated=TO_DATE('2018-06-18 16:25:24','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=205588 +; + +-- 18-giu-2018 16.26.02 CEST +ALTER TABLE AD_InfoColumn ADD InputFieldValidation VARCHAR2(2000) DEFAULT NULL +; + +-- 18-giu-2018 17.39.36 CEST +UPDATE AD_Field SET DisplayLogic='@IsReadOnly@=N', AD_Reference_Value_ID=NULL, AD_Val_Rule_ID=NULL, IsToolbarButton=NULL,Updated=TO_DATE('2018-06-18 17:39:36','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=205588 +; + +-- 18-giu-2018 18.00.13 CEST +UPDATE AD_Column SET EntityType='D',Updated=TO_DATE('2018-06-18 18:00:13','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Column_ID=212216 +; + +-- 18-giu-2018 18.04.58 CEST +UPDATE AD_Field SET EntityType='D', AD_Reference_Value_ID=NULL, AD_Val_Rule_ID=NULL, XPosition=2, ColumnSpan=2, IsToolbarButton=NULL,Updated=TO_DATE('2018-06-18 18:04:58','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=203829 +; + +-- 18-giu-2018 18.05.07 CEST +UPDATE AD_Column SET Help='Input field validaton query. If this query returns at least a row, an error will be displayed and the new value will be refused. The query can use all the fields in row as context fields (using the usual @...@ syntax). The error messages is composed appending the first column of every rows of the result, and its then translated, so the message can contains traslatable parts in the form @@ and/or @@',Updated=TO_DATE('2018-06-18 18:05:07','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Column_ID=213525 +; + +SELECT register_migration_script('201806181810_IDEMPIERE-2709.sql') FROM dual +; diff --git a/migration/i5.1z/postgresql/201507032015_IDEMPIERE-2709.sql b/migration/i5.1z/postgresql/201507032015_IDEMPIERE-2709.sql new file mode 100644 index 0000000000..66dcf4ddf5 --- /dev/null +++ b/migration/i5.1z/postgresql/201507032015_IDEMPIERE-2709.sql @@ -0,0 +1,32 @@ +-- IDEMPIERE-2709: Adding support for editable field on info window +-- Jul 3, 2015 6:10:42 PM IST +INSERT INTO AD_Column (AD_Column_ID,Version,Name,Description,Help,AD_Table_ID,ColumnName,DefaultValue,FieldLength,IsKey,IsParent,IsMandatory,IsTranslated,IsIdentifier,SeqNo,IsEncrypted,AD_Reference_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Element_ID,IsUpdateable,IsSelectionColumn,EntityType,IsSyncDatabase,IsAlwaysUpdateable,IsAutocomplete,IsAllowLogging,AD_Column_UU,IsAllowCopy,IsToolbarButton,IsSecure) VALUES (212216,0,'Read Only','Field is read only','The Read Only indicates that this field may only be Read. It may not be updated.',897,'IsReadOnly','Y',1,'N','N','Y','N','N',0,'N',20,0,0,'Y',TO_TIMESTAMP('2015-07-03 18:10:41','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2015-07-03 18:10:41','YYYY-MM-DD HH24:MI:SS'),100,405,'Y','N','D','N','N','N','Y','3a94dcce-25f4-4382-9547-a8f18949bbe7','Y','N','N') +; + +-- Jul 3, 2015 6:10:49 PM IST +ALTER TABLE AD_InfoColumn ADD COLUMN IsReadOnly CHAR(1) DEFAULT 'Y' CHECK (IsReadOnly IN ('Y','N')) NOT NULL +; + +-- Jul 3, 2015 6:15:02 PM IST +INSERT INTO AD_Field (AD_Field_ID,Name,Description,Help,AD_Tab_ID,AD_Column_ID,IsDisplayed,DisplayLength,SeqNo,SortNo,IsSameLine,IsHeading,IsFieldOnly,IsEncrypted,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,IsReadOnly,IsCentrallyMaintained,EntityType,AD_Field_UU,IsDisplayedGrid,SeqNoGrid,XPosition,ColumnSpan,NumLines,IsQuickEntry,IsDefaultFocus,IsAdvancedField) VALUES (203829,'Read Only','Field is read only','The Read Only indicates that this field may only be Read. It may not be updated.',844,212216,'Y',0,165,0,'N','N','N','N',0,0,'Y',TO_TIMESTAMP('2015-07-03 18:14:59','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2015-07-03 18:14:59','YYYY-MM-DD HH24:MI:SS'),100,'N','Y','D','ebc3464e-96a2-447d-a846-b964ede1b66f','Y',170,1,1,1,'N','N','N') +; + +-- Table: t_selection_infowindow + +-- DROP TABLE t_selection_infowindow; + +CREATE TABLE t_selection_infowindow +( + ad_pinstance_id numeric(10) NOT NULL, + t_selection_id numeric(10) NOT NULL, + viewid varchar(30), + columnname varchar(255) NOT NULL, + value_string varchar(255), + value_date timestamp, + value_number numeric, + info varchar(60), + CONSTRAINT t_selection_infowindow_key PRIMARY KEY (ad_pinstance_id, t_selection_id, columnname) +); + +SELECT register_migration_script('201507032015_IDEMPIERE-2709.sql') FROM dual +; diff --git a/migration/i5.1z/postgresql/201806181810_IDEMPIERE-2709.sql b/migration/i5.1z/postgresql/201806181810_IDEMPIERE-2709.sql new file mode 100644 index 0000000000..6a43444496 --- /dev/null +++ b/migration/i5.1z/postgresql/201806181810_IDEMPIERE-2709.sql @@ -0,0 +1,51 @@ +-- InfoWindow selection editable fields +-- 18-giu-2018 16.11.33 CEST +INSERT INTO AD_Element (AD_Element_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,ColumnName,Name,Description,Help,PrintName,EntityType,AD_Element_UU) VALUES (203216,0,0,'Y',TO_TIMESTAMP('2018-06-18 16:11:32','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2018-06-18 16:11:32','YYYY-MM-DD HH24:MI:SS'),100,'InputFieldValidation','Input field validation','Input field validaton query','Input field validaton query','Input field validation','D','b457c250-ced2-415f-aaae-9bc7e545cb01') +; + +-- 18-giu-2018 16.12.33 CEST +INSERT INTO AD_Column (AD_Column_ID,Version,Name,Description,Help,AD_Table_ID,ColumnName,FieldLength,IsKey,IsParent,IsMandatory,IsTranslated,IsIdentifier,SeqNo,IsEncrypted,AD_Reference_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Element_ID,IsUpdateable,IsSelectionColumn,EntityType,IsSyncDatabase,IsAlwaysUpdateable,IsAutocomplete,IsAllowLogging,AD_Column_UU,IsAllowCopy,SeqNoSelection,IsToolbarButton,IsSecure) VALUES (213525,0,'Input field validation','Input field validaton query','Input field validaton query',897,'InputFieldValidation',2000,'N','N','N','N','N',0,'N',14,0,0,'Y',TO_TIMESTAMP('2018-06-18 16:12:33','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2018-06-18 16:12:33','YYYY-MM-DD HH24:MI:SS'),100,203216,'Y','N','D','N','N','N','Y','d0a5e559-7621-496c-9269-715b1040395b','Y',0,'N','N') +; + +-- 18-giu-2018 16.12.49 CEST +UPDATE AD_Column SET EntityType='D',Updated=TO_TIMESTAMP('2018-06-18 16:12:49','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Column_ID=213525 +; + +-- 18-giu-2018 16.21.51 CEST +INSERT INTO AD_Field (AD_Field_ID,Name,Description,Help,AD_Tab_ID,AD_Column_ID,IsDisplayed,DisplayLength,SeqNo,SortNo,IsSameLine,IsHeading,IsFieldOnly,IsEncrypted,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,IsReadOnly,IsCentrallyMaintained,EntityType,AD_Field_UU,IsDisplayedGrid,SeqNoGrid,XPosition,ColumnSpan,NumLines,IsQuickEntry,IsDefaultFocus,IsAdvancedField) VALUES (205588,'Input field validation','Input field validaton query','Input field validaton query',844,213525,'Y',0,280,0,'N','N','N','N',0,0,'Y',TO_TIMESTAMP('2018-06-18 16:21:50','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2018-06-18 16:21:50','YYYY-MM-DD HH24:MI:SS'),100,'N','Y','D','26da27d1-b040-49dc-a3e6-ecd9f273636f','Y',190,1,1,1,'N','N','N') +; + +-- 18-giu-2018 16.22.23 CEST +UPDATE AD_Field SET SeqNo=290, AD_Reference_Value_ID=NULL, AD_Val_Rule_ID=NULL, IsToolbarButton=NULL,Updated=TO_TIMESTAMP('2018-06-18 16:22:23','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=205588 +; + +-- 18-giu-2018 16.24.44 CEST +UPDATE AD_Field SET AD_Reference_Value_ID=NULL, AD_Val_Rule_ID=NULL, ColumnSpan=5, NumLines=3, IsToolbarButton=NULL,Updated=TO_TIMESTAMP('2018-06-18 16:24:44','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=205588 +; + +-- 18-giu-2018 16.25.24 CEST +UPDATE AD_Field SET AD_Reference_Value_ID=NULL, AD_Val_Rule_ID=NULL, IsDisplayedGrid='N', IsToolbarButton=NULL,Updated=TO_TIMESTAMP('2018-06-18 16:25:24','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=205588 +; + +-- 18-giu-2018 16.26.02 CEST +ALTER TABLE AD_InfoColumn ADD COLUMN InputFieldValidation VARCHAR(2000) DEFAULT NULL +; + +-- 18-giu-2018 17.39.36 CEST +UPDATE AD_Field SET DisplayLogic='@IsReadOnly@=N', AD_Reference_Value_ID=NULL, AD_Val_Rule_ID=NULL, IsToolbarButton=NULL,Updated=TO_TIMESTAMP('2018-06-18 17:39:36','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=205588 +; + +-- 18-giu-2018 18.00.13 CEST +UPDATE AD_Column SET EntityType='D',Updated=TO_TIMESTAMP('2018-06-18 18:00:13','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Column_ID=212216 +; + +-- 18-giu-2018 18.04.58 CEST +UPDATE AD_Field SET EntityType='D', AD_Reference_Value_ID=NULL, AD_Val_Rule_ID=NULL, XPosition=2, ColumnSpan=2, IsToolbarButton=NULL,Updated=TO_TIMESTAMP('2018-06-18 18:04:58','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=203829 +; + +-- 18-giu-2018 18.05.07 CEST +UPDATE AD_Column SET Help='Input field validaton query. If this query returns at least a row, an error will be displayed and the new value will be refused. The query can use all the fields in row as context fields (using the usual @...@ syntax). The error messages is composed appending the first column of every rows of the result, and its then translated, so the message can contains traslatable parts in the form @@ and/or @@',Updated=TO_TIMESTAMP('2018-06-18 18:05:07','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Column_ID=213525 +; + +SELECT register_migration_script('201806181810_IDEMPIERE-2709.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 9db00522d1..f43d114dca 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 @@ -257,6 +257,19 @@ public interface I_AD_InfoColumn */ public String getHelp(); + /** Column name InputFieldValidation */ + public static final String COLUMNNAME_InputFieldValidation = "InputFieldValidation"; + + /** Set Input field validation. + * Input field validaton query + */ + public void setInputFieldValidation (String InputFieldValidation); + + /** Get Input field validation. + * Input field validaton query + */ + public String getInputFieldValidation(); + /** Column name IsActive */ public static final String COLUMNNAME_IsActive = "IsActive"; @@ -348,6 +361,19 @@ public interface I_AD_InfoColumn */ public boolean isQueryCriteria(); + /** Column name IsReadOnly */ + public static final String COLUMNNAME_IsReadOnly = "IsReadOnly"; + + /** Set Read Only. + * Field is read only + */ + public void setIsReadOnly (boolean IsReadOnly); + + /** Get Read Only. + * Field is read only + */ + public boolean isReadOnly(); + /** Column name Name */ public static final String COLUMNNAME_Name = "Name"; 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 f523edeab2..24cf9c8b9a 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 = 20180719L; + private static final long serialVersionUID = 20180828L; /** Standard Constructor */ public X_AD_InfoColumn (Properties ctx, int AD_InfoColumn_ID, String trxName) @@ -369,6 +369,23 @@ public class X_AD_InfoColumn extends PO implements I_AD_InfoColumn, I_Persistent { return (String)get_Value(COLUMNNAME_Help); } + + /** Set Input field validation. + @param InputFieldValidation + Input field validaton query + */ + public void setInputFieldValidation (String InputFieldValidation) + { + set_Value (COLUMNNAME_InputFieldValidation, InputFieldValidation); + } + + /** Get Input field validation. + @return Input field validaton query + */ + public String getInputFieldValidation () + { + return (String)get_Value(COLUMNNAME_InputFieldValidation); + } /** Set Centrally maintained. @param IsCentrallyMaintained @@ -417,6 +434,31 @@ public class X_AD_InfoColumn extends PO implements I_AD_InfoColumn, I_Persistent } return false; } + + /** + * Set Read Only. + * + * @param IsReadOnly + * Determines, if this field is Read Only + */ + public void setIsReadOnly(boolean IsReadOnly) { + set_Value(COLUMNNAME_IsReadOnly, Boolean.valueOf(IsReadOnly)); + } + + /** + * Get Read Only. + * + * @return Determines, if this field is Read Only + */ + public boolean isReadOnly() { + Object oo = get_Value(COLUMNNAME_IsReadOnly); + if (oo != null) { + if (oo instanceof Boolean) + return ((Boolean) oo).booleanValue(); + return "Y".equals(oo); + } + return false; + } /** Set Identifier. @param IsIdentifier diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/component/WInfoWindowListItemRenderer.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/component/WInfoWindowListItemRenderer.java new file mode 100644 index 0000000000..94121d2159 --- /dev/null +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/component/WInfoWindowListItemRenderer.java @@ -0,0 +1,130 @@ +/********************************************************************** +* This file is part of iDempiere ERP Open Source * +* http://www.idempiere.org * +* * +* Copyright (C) Contributors * +* * +* This program is free software; you can redistribute it and/or * +* modify it under the terms of the GNU General Public License * +* as published by the Free Software Foundation; either version 2 * +* of the License, or (at your option) any later version. * +* * +* 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., 51 Franklin Street, Fifth Floor, Boston, * +* MA 02110-1301, USA. * +**********************************************************************/ +package org.adempiere.webui.component; + +import java.util.List; + +import org.adempiere.webui.editor.WEditor; +import org.adempiere.webui.editor.WebEditorFactory; +import org.adempiere.webui.event.ValueChangeEvent; +import org.adempiere.webui.event.ValueChangeListener; +import org.adempiere.webui.info.InfoWindow; +import org.compiere.minigrid.IDColumn; +import org.compiere.model.GridField; +import org.compiere.model.MInfoColumn; +import org.compiere.util.KeyNamePair; +import org.zkoss.zul.Listcell; + +public class WInfoWindowListItemRenderer extends WListItemRenderer +{ + private MInfoColumn[] infoColumns; + private GridField[] gridFields; + private int gridFieldsOffset = -1; // There are added columns in front of the first real gridField, instead of a fixed +1 we use an offset + private InfoWindow infoWindow; + + public WInfoWindowListItemRenderer(InfoWindow infoWindow, MInfoColumn[] infoColumns, List gridFields) + { + this.infoColumns = infoColumns; + this.gridFields = gridFields.toArray(new GridField[infoColumns.length]); + this.infoWindow = infoWindow; + } + + public WInfoWindowListItemRenderer(InfoWindow infoWindow, MInfoColumn[] infoColumns, List gridFields, List columnNames) + { + super(columnNames); + this.infoColumns = infoColumns; + this.gridFields = gridFields.toArray(new GridField[infoColumns.length]); + this.infoWindow = infoWindow; + } + + private void calculateFieldOffest() + { + int colCount = getTableColumns().size(); + + if(colCount > infoColumns.length) // Added columns: selecetion + gridFieldsOffset = colCount - infoColumns.length; + } + + @Override + protected Listcell getCellComponent(WListbox table, Object field, + final int rowIndex, final int columnIndex) + { + if(gridFieldsOffset < 0) // Just do it once, this assumes this rendered is not shared between grids + calculateFieldOffest(); + + Listcell listcell = null; + ListModelTable model = table.getModel(); + Object obj = model.get(rowIndex); + + int effectiveFieldIndex = columnIndex - gridFieldsOffset; + + if(effectiveFieldIndex >= 0 + && model.isSelected(obj) ) + { + MInfoColumn infoColumn = infoColumns[effectiveFieldIndex]; + + if(infoColumn.isReadOnly() == false + && columnIndex > 0) + { + ListCell listCell = new ListCell(); + + final GridField gridField = gridFields[effectiveFieldIndex]; + final WEditor editor = WebEditorFactory.getEditor(gridField, false); + + // Set editor value + + Object value = table.getValueAt(rowIndex, columnIndex); + + if(value instanceof IDColumn) + { + IDColumn idc = (IDColumn)value; + value = idc.getRecord_ID(); + } + else if(value instanceof KeyNamePair) + { + KeyNamePair knp = (KeyNamePair)value; + value = knp.getKey(); + } + + editor.setValue(value); + + editor.addValueChangeListener(new ValueChangeListener() + { + @Override + public void valueChange(ValueChangeEvent evt) + { + infoWindow.onCellEditCallback(evt, rowIndex, columnIndex, editor, gridField); + } + }); + + listCell.appendChild(editor.getComponent()); + listcell = listCell; + } + } + + if(listcell == null) + listcell = super.getCellComponent(table, field, rowIndex, columnIndex); + + return listcell; + } + +} diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/component/WListItemRenderer.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/component/WListItemRenderer.java index 14f988f807..9a9776755c 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/component/WListItemRenderer.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/component/WListItemRenderer.java @@ -25,6 +25,7 @@ import java.sql.Timestamp; import java.text.DecimalFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashSet; @@ -197,7 +198,7 @@ public class WListItemRenderer implements ListitemRenderer, EventListene * @param columnIndex The column in which the cell is to be placed. * @return The list cell component. */ - private Listcell getCellComponent(WListbox table, Object field, + protected Listcell getCellComponent(WListbox table, Object field, int rowIndex, int columnIndex) { ListCell listcell = new ListCell(); @@ -864,6 +865,10 @@ public class WListItemRenderer implements ListitemRenderer, EventListene m_tableColumns.get(index).setColumnClass(classType); } } + + public List getTableColumns() { + return Collections.unmodifiableList(m_tableColumns); + } class CellListener implements EventListener { diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/component/WListbox.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/component/WListbox.java index 7d59ed67f7..f442ceb2a2 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/component/WListbox.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/component/WListbox.java @@ -81,6 +81,9 @@ public class WListbox extends Listbox implements IMiniTable, TableValueChangeLis private int m_colorColumnIndex = -1; /** Color Column compare data. */ private Object m_colorDataCompare = Env.ZERO; + + // F3P: support IDColumn for selection + private boolean allowIDColumnForReadWrite = false; /** * Default constructor. @@ -194,12 +197,27 @@ public class WListbox extends Listbox implements IMiniTable, TableValueChangeLis public boolean isCellEditable(int row, int column) { // if the first column holds a boolean and it is false, it is not editable - if (column != 0 - && (getValueAt(row, 0) instanceof Boolean) - && !((Boolean)getValueAt(row, 0)).booleanValue()) + + // F3P: If allowed, use idcolumn as a switch for read/write + + if (column != 0) + return false; + + Object val = getValueAt(row, 0); + + if ((val instanceof Boolean) + && !((Boolean)val).booleanValue()) { return false; } + + if(val instanceof IDColumn) + { + IDColumn idc = (IDColumn)val; + + if(!idc.isSelected()) + return false; + } // is the column read/write? if (m_readWriteColumn.contains(new Integer(column))) @@ -1228,4 +1246,14 @@ public class WListbox extends Listbox implements IMiniTable, TableValueChangeLis } } + public boolean isAllowIDColumnForReadWrite() + { + return allowIDColumnForReadWrite; + } + + public void setAllowIDColumnForReadWrite(boolean allowIDColumnForReadWrite) + { + this.allowIDColumnForReadWrite = allowIDColumnForReadWrite; + } + } 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 3c043f903e..ff8afc7823 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 @@ -3,6 +3,7 @@ */ package org.adempiere.webui.info; +import java.io.File; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; @@ -11,10 +12,13 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Properties; import java.util.TreeMap; import java.util.logging.Level; +import org.adempiere.exceptions.AdempiereException; +import org.adempiere.impexp.AbstractExcelExporter; import org.adempiere.model.IInfoColumn; import org.adempiere.model.MInfoProcess; import org.adempiere.model.MInfoRelated; @@ -33,6 +37,7 @@ import org.adempiere.webui.component.EditorBox; import org.adempiere.webui.component.Grid; import org.adempiere.webui.component.GridFactory; import org.adempiere.webui.component.Label; +import org.adempiere.webui.component.ListModelTable; import org.adempiere.webui.component.Menupopup; import org.adempiere.webui.component.Row; import org.adempiere.webui.component.Rows; @@ -41,6 +46,7 @@ import org.adempiere.webui.component.Tabbox; import org.adempiere.webui.component.Tabpanel; import org.adempiere.webui.component.Tabpanels; import org.adempiere.webui.component.Tabs; +import org.adempiere.webui.component.WInfoWindowListItemRenderer; import org.adempiere.webui.component.WListbox; import org.adempiere.webui.editor.WEditor; import org.adempiere.webui.editor.WSearchEditor; @@ -49,6 +55,8 @@ import org.adempiere.webui.editor.WebEditorFactory; import org.adempiere.webui.event.DialogEvents; import org.adempiere.webui.event.ValueChangeEvent; import org.adempiere.webui.event.ValueChangeListener; +import org.adempiere.webui.event.WTableModelEvent; +import org.adempiere.webui.factory.ButtonFactory; import org.adempiere.webui.grid.WQuickEntry; import org.adempiere.webui.panel.InfoPanel; import org.adempiere.webui.session.SessionManager; @@ -63,6 +71,7 @@ import org.compiere.model.AccessSqlParser.TableInfo; import org.compiere.model.GridField; import org.compiere.model.GridFieldVO; import org.compiere.model.GridWindow; +import org.compiere.model.Lookup; import org.compiere.model.MInfoColumn; import org.compiere.model.MInfoWindow; import org.compiere.model.MLookupFactory; @@ -77,14 +86,17 @@ import org.compiere.util.DisplayType; import org.compiere.util.Env; import org.compiere.util.KeyNamePair; import org.compiere.util.Msg; +import org.compiere.util.Trx; import org.compiere.util.Util; import org.compiere.util.ValueNamePair; +import org.zkoss.util.media.AMedia; import org.zkoss.zk.au.out.AuEcho; import org.zkoss.zk.ui.Component; import org.zkoss.zk.ui.Page; import org.zkoss.zk.ui.event.Event; import org.zkoss.zk.ui.event.EventListener; import org.zkoss.zk.ui.event.Events; +import org.zkoss.zk.ui.event.SelectEvent; import org.zkoss.zk.ui.event.SwipeEvent; import org.zkoss.zk.ui.util.Clients; import org.zkoss.zul.Center; @@ -92,7 +104,9 @@ import org.zkoss.zul.Checkbox; import org.zkoss.zul.Comboitem; import org.zkoss.zul.ComboitemRenderer; import org.zkoss.zul.Div; +import org.zkoss.zul.Filedownload; import org.zkoss.zul.ListModelList; +import org.zkoss.zul.Listitem; import org.zkoss.zul.Menuitem; import org.zkoss.zul.North; import org.zkoss.zul.Paging; @@ -138,6 +152,16 @@ public class InfoWindow extends InfoPanel implements ValueChangeListener, EventL private List gridFields; private Checkbox checkAND; + + // F3P: Keep original values: when a row is unselected, restore original values + + private boolean hasEditable = false; + private Map> cacheOriginalValues = new HashMap<>(); + private Map> temporarySelectedData = new HashMap<>(); + + // F3P: export + + private Button exportButton = null; /** * Menu contail process menu item @@ -188,7 +212,21 @@ public class InfoWindow extends InfoPanel implements ValueChangeListener, EventL //Xolali IDEMPIERE-1045 contentPanel.addActionListener(new EventListener() { public void onEvent(Event event) throws Exception { - updateSubcontent(); + + int row = -1; + + if(event instanceof SelectEvent) + { + @SuppressWarnings("unchecked") + SelectEvent> selEvent = (SelectEvent>)event; + + if(selEvent.getReference() != null) + { + row = selEvent.getReference().getIndex(); + } + } + + updateSubcontent(row); } }); //xolali --end- @@ -223,14 +261,21 @@ public class InfoWindow extends InfoPanel implements ValueChangeListener, EventL ClientInfo.onClientInfo(this, this::onClientInfo); } + // F3P: add export button + + initExport(); } - /** + /** + * * {@inheritDoc} */ @Override - protected void updateSubcontent (){ - int row = contentPanel.getSelectedRow(); + protected void updateSubcontent (int row){ // F3P: For multi-selection info, using selected row blocks the dislay to the first selected + + if(row < 0) + row = contentPanel.getSelectedRow(); + if (row >= 0) { for (EmbedWinInfo embed : embeddedWinList) { // default link column is key column @@ -562,6 +607,36 @@ public class InfoWindow extends InfoPanel implements ValueChangeListener, EventL gridFields.add(gridField); } + // If we have a process and at least one process and an editable field, change to the info window rendered + + int processCount = 0; + + if(infoWindow != null) + { + MInfoProcess processes[] = infoWindow.getInfoProcess(false); + processCount = processes.length; + } + + if(processCount > 0) + { + for(MInfoColumn infoColumn:infoColumns) + { + if(infoColumn.isReadOnly() == false) + { + hasEditable = true; + break; + } + } + + if(hasEditable) + { + WInfoWindowListItemRenderer renderer = new WInfoWindowListItemRenderer(this, infoColumns, gridFields); + contentPanel.setItemRenderer(renderer); + contentPanel.setAllowIDColumnForReadWrite(true); + renderer.addTableValueChangeListener(contentPanel); // Replicated from WListbox constructor + } + } + StringBuilder builder = new StringBuilder(p_whereClause != null ? p_whereClause.trim() : ""); String infoWhereClause = infoWindow.getWhereClause(); if (infoWhereClause != null && infoWhereClause.indexOf("@") >= 0) { @@ -689,8 +764,10 @@ public class InfoWindow extends InfoPanel implements ValueChangeListener, EventL : tableInfos[0].getTableName(); String keySelectClause = keyTableAlias+"."+p_keyColumn; - list.add(new ColumnInfo(" ", keySelectClause, IDColumn.class)); + list.add(new ColumnInfo(" ", keySelectClause, IDColumn.class, true, false, null, p_keyColumn)); + boolean haveNotProcess = !haveProcess; // A field is editabile only if is not readonly and theres a process + int i = 0; for(MInfoColumn infoColumn : infoColumns) { @@ -705,7 +782,7 @@ public class InfoWindow extends InfoPanel implements ValueChangeListener, EventL if (infoColumn.getSelectClause().equalsIgnoreCase(keySelectClause)) continue; - columnInfo = new ColumnInfo(infoColumn.get_Translation("Name"), colSQL, DisplayType.getClass(infoColumn.getAD_Reference_ID(), true)); + columnInfo = new ColumnInfo(infoColumn.get_Translation("Name"), colSQL, DisplayType.getClass(infoColumn.getAD_Reference_ID(), true), infoColumn.isReadOnly() || haveNotProcess); } else if (DisplayType.isLookup(infoColumn.getAD_Reference_ID())) { @@ -716,7 +793,7 @@ public class InfoWindow extends InfoPanel implements ValueChangeListener, EventL editor.setMandatory(false); editor.setReadWrite(false); editorMap.put(colSQL, editor); - columnInfo = new ColumnInfo(infoColumn.get_Translation("Name"), colSQL, ValueNamePair.class, (String)null); + columnInfo = new ColumnInfo(infoColumn.get_Translation("Name"), colSQL, ValueNamePair.class, (String)null, infoColumn.isReadOnly() || haveNotProcess); } else { @@ -725,11 +802,12 @@ public class InfoWindow extends InfoPanel implements ValueChangeListener, EventL } else { - columnInfo = new ColumnInfo(infoColumn.get_Translation("Name"), colSQL, DisplayType.getClass(infoColumn.getAD_Reference_ID(), true)); + columnInfo = new ColumnInfo(infoColumn.get_Translation("Name"), colSQL, DisplayType.getClass(infoColumn.getAD_Reference_ID(), true), infoColumn.isReadOnly() || haveNotProcess); } columnInfo.setColDescription(infoColumn.get_Translation("Description")); columnInfo.setAD_Reference_ID(infoColumn.getAD_Reference_ID()); columnInfo.setGridField(gridFields.get(i)); + columnInfo.setColumnName(infoColumn.getColumnName()); list.add(columnInfo); if (keyColumnOfView == infoColumn){ @@ -757,6 +835,8 @@ public class InfoWindow extends InfoPanel implements ValueChangeListener, EventL MLookupInfo lookupInfo = MLookupFactory.getLookupInfo(Env.getCtx(), p_WindowNo, 0, infoColumn.getAD_Reference_ID(), Env.getLanguage(Env.getCtx()), columnName, infoColumn.getAD_Reference_Value_ID(), false, validationCode); String displayColumn = lookupInfo.DisplayColumn; + boolean haveNotProcess = !haveProcess; // A field is editabile only if is not readonly and theres a process; + int index = infoColumn.getSelectClause().indexOf("."); if (index == infoColumn.getSelectClause().lastIndexOf(".")) { @@ -768,7 +848,7 @@ public class InfoWindow extends InfoPanel implements ValueChangeListener, EventL if (tableInfo.getTableName().equalsIgnoreCase(lookupInfo.TableName)) { displayColumn = displayColumn.replace(lookupInfo.TableName+".", tableInfo.getSynonym()+"."); - ColumnInfo columnInfo = new ColumnInfo(infoColumn.get_Translation("Name"), displayColumn, KeyNamePair.class, infoColumn.getSelectClause()); + ColumnInfo columnInfo = new ColumnInfo(infoColumn.get_Translation("Name"), displayColumn, KeyNamePair.class, infoColumn.getSelectClause(), infoColumn.isReadOnly() || haveNotProcess); return columnInfo; } break; @@ -785,7 +865,7 @@ public class InfoWindow extends InfoPanel implements ValueChangeListener, EventL if (! colSQL.toUpperCase().contains(" AS ")) colSQL += " AS " + infoColumn.getColumnName(); editorMap.put(colSQL, editor); - ColumnInfo columnInfo = new ColumnInfo(infoColumn.get_Translation("Name"), colSQL, KeyNamePair.class, (String)null); + ColumnInfo columnInfo = new ColumnInfo(infoColumn.get_Translation("Name"), colSQL, KeyNamePair.class, (String)null, infoColumn.isReadOnly() || haveNotProcess); return columnInfo; } @@ -1577,6 +1657,7 @@ public class InfoWindow extends InfoPanel implements ValueChangeListener, EventL if (!isRequeryByRunSuccessProcess) prepareTable(); super.executeQuery(); + cacheOriginalValues.clear(); // F3P: Clear original values if (ClientInfo.maxHeight(ClientInfo.SMALL_HEIGHT-1) || ClientInfo.maxWidth(ClientInfo.SMALL_WIDTH-1)) { layout.getNorth().setOpen(false); @@ -1954,7 +2035,6 @@ public class InfoWindow extends InfoPanel implements ValueChangeListener, EventL return gridField; } - /** * {@inheritDoc} @@ -2069,5 +2149,504 @@ public class InfoWindow extends InfoPanel implements ValueChangeListener, EventL } } } + + // Edit Callback method and original values management + + public Properties getRowaAsCtx(int row, int editingColumn, Object editingValue) + { + ListModelTable model = contentPanel.getModel(); + Properties ctx = new Properties(Env.getCtx()); // Allow session values + + // Parameter dditors + + for(WEditor e:editors) + { + Object val = e.getValue(); + String column = e.getColumnName(); + + if(val != null) + { + if(val instanceof Integer) + Env.setContext(ctx, 0, column, (Integer)val); + else if(val instanceof Timestamp) + Env.setContext(ctx, 0, column, (Timestamp)val); + else if(val instanceof Boolean) + Env.setContext(ctx, 0, column, (Boolean)val); + else + Env.setContext(ctx, 0, column, val.toString()); + } + } + + for(int i=0; i < p_layout.length; i++) + { + String column = p_layout[i].getColumnName(); + + Object val = null; + + if(i != editingColumn) + val = model.getValueAt(row, i); + else + val = editingValue; + + // Get id from 'complex' types + + if(val != null) + { + if(val instanceof IDColumn) + { + IDColumn idc = (IDColumn)val; + val = idc.getRecord_ID(); + } + else if(val instanceof KeyNamePair) + { + KeyNamePair knp = (KeyNamePair)val; + val = knp.getKey(); + } + + if(val instanceof Integer) + Env.setContext(ctx, 0, column, (Integer)val); + else if(val instanceof Timestamp) + Env.setContext(ctx, 0, column, (Timestamp)val); + else if(val instanceof Boolean) + Env.setContext(ctx, 0, column, (Boolean)val); + else + Env.setContext(ctx, 0, column, val.toString()); + } + } + + return ctx; + } + + public void onCellEditCallback(ValueChangeEvent event, int rowIndex, int colIndex, WEditor editor, GridField field) + { + Object val = event.getNewValue(); + + if(val != null && columnInfos[colIndex].getColClass().equals(KeyNamePair.class)) + { + Integer iVal = (Integer)val; + String display = editor.getDisplay(); + + KeyNamePair kdc = new KeyNamePair(iVal, display); + val = kdc; + } + + MInfoColumn infoColumn = infoColumns[colIndex - 1]; + boolean changeIsValid = true; + String validationSQL = null; + + if(!Util.isEmpty(infoColumn.getInputFieldValidation(), true)) // Run validation + { + changeIsValid = false; + + Properties ctx = getRowaAsCtx(rowIndex, colIndex, val); + + String rawSQL = infoColumn.getInputFieldValidation(); + validationSQL = Env.parseContext(ctx, 0, rawSQL, false); + + try + { + List> errors = DB.getSQLArrayObjectsEx(null, validationSQL); + + if(errors != null && errors.size() > 0) + { + StringBuilder sbError = new StringBuilder(); + for(List line:errors) + { + if(line.size() > 0) + { + if(sbError.length() > 0) + sbError.append('\n'); + + sbError.append(line.get(0)); + } + } + + String msg = Msg.translate(ctx, sbError.toString()); + FDialog.error(0, this, "ValidationError", msg); // TODO messaggio + } + else + changeIsValid = true; + } + catch(Exception e) + { + log.log(Level.SEVERE, "Error executing validation SQL: " + validationSQL, e); + + FDialog.error(0, this, "Error", validationSQL); // TODO messaggio + changeIsValid = false; + } + } + + if(changeIsValid) + { + // Editing a row delesects it, make sure it stays selected + + ListModelTable model = contentPanel.getModel(); + Object row = model.get(rowIndex); + + // Since the row object is a collection, we can update it safely, but the hash code will be different from the one stored + // in the selection. So we need to remove and re-add the row after to keep the selection in sync + + model.removeFromSelection(row); + contentPanel.setValueAt(val, rowIndex, colIndex); + model.addToSelection(row); + + Clients.resize(contentPanel); + } + else + { + editor.setValue(event.getOldValue()); + } + } + + protected void restoreOriginalValues(int rowIndex) + { + Integer viewIdKey = getColumnValue(rowIndex); + + if(cacheOriginalValues.containsKey(viewIdKey)) // Only cache if not cached to avoid caching subsequent modifications + { + int colCount = contentPanel.getColumnCount(); + List row = cacheOriginalValues.get(viewIdKey); + + for(int i=1; i < colCount; i++) // Skip first row (selection) + { + Object val = row.get(i-1); + contentPanel.setValueAt(val, rowIndex, i); + } + } + } + + protected void cacheOriginalValues(int rowIndex) + { + Integer viewIdKey = getColumnValue(rowIndex); + + if(cacheOriginalValues.containsKey(viewIdKey) == false) // Only cache if not cached to avoid caching subsequent modifications + { + int colCount = contentPanel.getColumnCount(); + List row = new ArrayList<>(); + + for(int i=1; i < colCount; i++) // Skip first row (selection) + { + Object val = contentPanel.getValueAt(rowIndex, i); + row.add(val); + } + + cacheOriginalValues.put(viewIdKey, row); + } + } + + @Override + public void tableChanged(WTableModelEvent event) + { + // Manage cache of values + + if(hasEditable && event.getColumn() == 0) + { + for(int row=event.getFirstRow(); row <= event.getLastRow(); row++) + { + Object col0 = contentPanel.getValueAt(row, 0); + + if(col0 instanceof IDColumn) + { + IDColumn idc = (IDColumn)col0; + + if(idc.isSelected()) + { + cacheOriginalValues(row); + } + else + { + restoreOriginalValues(row); + } + } + } + + Clients.resize(contentPanel); + } + + super.tableChanged(event); + } + + @Override + public void onQueryCallback(Event event) + { + super.onQueryCallback(event); + + enableExportButton(); + } + + + @Override + protected void updateListSelected() + { + if(hasEditable) + { + temporarySelectedData = new HashMap<>(); + + // The list contents (rows) will be cleared during query, so we need a backup to restore the in-edit data + + ListModelTable model = contentPanel.getModel(); + + for(int rowIndex:contentPanel.getSelectedIndices()) + { + Integer keyViewValue = getColumnValue(rowIndex); + @SuppressWarnings("unchecked") + List row = (List)model.get(rowIndex); + + ArrayList clonedRow = new ArrayList<>(row); + temporarySelectedData.put(keyViewValue, clonedRow); + } + + for(Entry> entry: recordSelectedData.entrySet()) + { + ArrayList clonedRow = new ArrayList<>(entry.getValue()); + temporarySelectedData.put(entry.getKey(), clonedRow); + } + } + + super.updateListSelected(); + } + + @Override + protected void restoreSelectedInPage() + { + super.restoreSelectedInPage(); + + if(temporarySelectedData != null) + { + temporarySelectedData.clear(); + temporarySelectedData = null; + } + } + + + @Override + public boolean onRestoreSelectedItemIndexInPage(Integer keyViewValue, int rowIndex, Object oRow) + { + if(hasEditable && temporarySelectedData != null) + { + + cacheOriginalValues(rowIndex); + + int gridFieldsOffset = 1; // First column is the id, and its not on the infoColumns + + @SuppressWarnings("unchecked") + List row = (List)oRow; + List originalSelectedRow = temporarySelectedData.get(keyViewValue); + ListModelTable model = contentPanel.getModel(); + + // While restoring values we dong want to trigger listeners + model.removeTableModelListener(this); + + for(int i=0; i < infoColumns.length; i++) + { + if(infoColumns[i].isReadOnly() == false) // Only replace editable column, in case some other data changed on db + { + int colIndex = i + gridFieldsOffset; + Object obj = originalSelectedRow.get(colIndex); + model.setValueAt( obj, rowIndex, colIndex); + } + } + + // Restore isSelected status on IDColumn + Object id = (IDColumn)row.get(0); + + if(id instanceof IDColumn) + { + IDColumn idc = (IDColumn)id; + idc.setSelected(true); + } + + // Restore listners + model.addTableModelListener(this); + } + + return super.onRestoreSelectedItemIndexInPage(keyViewValue, rowIndex, oRow); + } + + // F3P: Export function + + protected void initExport() + { + exportButton = ButtonFactory.createNamedButton("Export", false, true); + exportButton.setId("Export"); + exportButton.setEnabled(false); + exportButton.addEventListener(Events.ON_CLICK, new XlsExportAction()); + + confirmPanel.addComponentsLeft(exportButton); + } + + protected void enableExportButton() + { + if(exportButton == null) + return; + + exportButton.setEnabled(contentPanel.getRowCount() > 0); + } + + private class XlsExportAction implements EventListener + { + @Override + public void onEvent(Event evt) throws Exception + { + if(evt.getTarget() == exportButton) + { + XlsExporter exporter = new XlsExporter(); + + exporter.doExport(); + } + } + } + + private class XlsExporter extends AbstractExcelExporter + { + private ResultSet m_rs = null; + private int rowCount = -1; + private int currentRow = -1; + + public void doExport() throws Exception + { + int originalCount = m_count; + + String dataSql = buildDataSQL(0, 0); + + File file = File.createTempFile("Export", ".xls"); + + testCount(); + + rowCount = m_count; + m_count = originalCount; + + if(rowCount > 0) + { + PreparedStatement pstmt = null; + Trx trx = null; + + try + { + String trxName = Trx.createTrxName("InfoPanelLoad:"); + trx = Trx.get(trxName, true); + trx.setDisplayName(getClass().getName()+"_exportXls"); + pstmt = DB.prepareStatement(dataSql, trxName); + setParameters (pstmt, false); // no count + + pstmt.setFetchSize(100); + m_rs = pstmt.executeQuery(); + + export(file, null); + } + catch(SQLException e) + { + log.log(Level.SEVERE, dataSql, e); + } + finally + { + DB.close(m_rs, pstmt); + trx.close(); + + m_rs = null; + currentRow = -1; + } + + AMedia media = null; + media = new AMedia(file.getName(), null, "application/vnd.ms-excel", file, true); + Filedownload.save(media); + } + } + + @Override + public boolean isFunctionRow() + { + return false; + } + + @Override + public int getColumnCount() + { + return columnInfos.length; + } + + @Override + public int getRowCount() + { + return rowCount; + } + + @Override + protected void setCurrentRow(int row) + { + if(row > currentRow) + { + try + { + m_rs.next(); + currentRow = row; + } + catch(SQLException e) + { + throw new AdempiereException(e); + } + } + } + + @Override + protected int getCurrentRow() + { + return currentRow; + } + + @Override + public boolean isColumnPrinted(int col) + { + return (columnInfos[col].getGridField() != null); + } + + @Override + public String getHeaderName(int col) + { + return columnInfos[col].getColHeader(); + } + + @Override + public int getDisplayType(int row, int col) + { + int displayType = -1; + GridField gridField = columnInfos[col].getGridField(); + displayType = gridField.getDisplayType(); + + return displayType; + } + + @Override + public Object getValueAt(int row, int col) + { + Object val = null; + + try + { + val = m_rs.getObject(col + 1); // Col are zero-based, while resultset col are 1 based + } + catch(SQLException e) + { + throw new AdempiereException(e); + } + + GridField gridField = columnInfos[col].getGridField(); + + Lookup lookup = gridField.getLookup(); + + if (lookup != null) + { + val = lookup.getDisplay(val); + } + + return val; + } + + @Override + public boolean isPageBreak(int row, int col) + { + return false; + } + } } 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 b764513f7d..ee414959aa 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 @@ -17,6 +17,7 @@ package org.adempiere.webui.panel; +import java.awt.event.MouseEvent; import java.math.BigDecimal; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -26,8 +27,10 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; +import java.util.Date; import java.util.HashMap; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -89,7 +92,6 @@ import org.zkoss.zk.ui.event.Event; import org.zkoss.zk.ui.event.EventListener; import org.zkoss.zk.ui.event.Events; import org.zkoss.zk.ui.event.KeyEvent; -import org.zkoss.zk.ui.event.MouseEvent; import org.zkoss.zk.ui.event.SelectEvent; import org.zkoss.zk.ui.util.Clients; import org.zkoss.zul.Comboitem; @@ -129,6 +131,7 @@ public abstract class InfoPanel extends Window implements EventListener, // attribute key of info process protected final static String ATT_INFO_PROCESS_KEY = "INFO_PROCESS"; protected int pageSize; + public LinkedHashMap> m_values = null; protected MInfoRelated[] relatedInfoList; // for test disable load all record when num of record < 1000 protected boolean isIgnoreCacheAll = true; @@ -391,7 +394,7 @@ public abstract class InfoPanel extends Window implements EventListener, private boolean m_useDatabasePaging = false; private BusyDialog progressWindow; // in case double click to item. this store clicked item (maybe it's un-select item) - private Listitem m_lastOnSelectItem; + private int m_lastSelectedIndex = -1; protected GridField m_gridfield; /** @@ -1348,14 +1351,28 @@ public abstract class InfoPanel extends Window implements EventListener, 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)); + Object row = contentPanel.getModel().get(rowIndex); + + if(onRestoreSelectedItemIndexInPage(keyViewValue, rowIndex, row)) // F3P: provide an hook for operations on restored index + lsSelectionRecord.add(row); } } contentPanel.getModel().setSelection(lsSelectionRecord); } - + /** Hook to intercept 'restore selection' actions + * + * @param keyViewValue row view key + * @param rowIndex row index + * @param row row + * @return false to skip restore selection + */ + public boolean onRestoreSelectedItemIndexInPage(Integer keyViewValue, int rowIndex, Object row) + { + return true; + } + 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()); @@ -1697,11 +1714,14 @@ public abstract class InfoPanel extends Window implements EventListener, else if (event.getTarget() == contentPanel && event.getName().equals(Events.ON_SELECT)) { setStatusSelected (); - m_lastOnSelectItem = null; + SelectEvent selectEvent = (SelectEvent) event; if (selectEvent.getReference() != null && selectEvent.getReference() instanceof Listitem) - m_lastOnSelectItem = (Listitem) selectEvent.getReference(); - }else if (event.getTarget() == contentPanel && event.getName().equals("onAfterRender")){ + { + Listitem m_lastOnSelectItem = (Listitem) selectEvent.getReference(); + m_lastSelectedIndex = m_lastOnSelectItem.getIndex(); + } + }else if (event.getTarget() == contentPanel && event.getName().equals("onAfterRender")){ //IDEMPIERE-1334 at this event selected item from listBox and model is sync enableButtons(); } @@ -1710,18 +1730,34 @@ public abstract class InfoPanel extends Window implements EventListener, if (event.getClass().equals(MouseEvent.class)){ return; } - if (contentPanel.isMultiple()) { - //un-select all selected column - if (m_lastOnSelectItem != null){ - contentPanel.getModel().clearSelection(); - int clickItemIndex = contentPanel.getIndexOfItem(m_lastOnSelectItem); - Object selectedItemModle = contentPanel.getModel().get(clickItemIndex); - contentPanel.getModel().addToSelection(selectedItemModle); - } - // clean selected record in cache - recordSelectedData.clear(); + if (contentPanel.isMultiple() && m_lastSelectedIndex >= 0) { + + contentPanel.setSelectedIndex(m_lastSelectedIndex); + + model.clearSelection(); + List lsSelectedItem = new ArrayList(); + lsSelectedItem.add(model.getElementAt(m_lastSelectedIndex)); + model.setSelection(lsSelectedItem); + + int m_keyColumnIndex = contentPanel.getKeyColumnIndex(); + for (int i = 0; i < contentPanel.getRowCount(); i++) { + // Find the IDColumn Key + Object data = contentPanel.getModel().getValueAt(i, m_keyColumnIndex); + if (data instanceof IDColumn) { + IDColumn dataColumn = (IDColumn) data; + + if (i == m_lastSelectedIndex) { + dataColumn.setSelected(true); + } + else { + dataColumn.setSelected(false); + } + } + } } onDoubleClick(); + contentPanel.repaint(); + m_lastSelectedIndex = -1; } else if (event.getTarget().equals(confirmPanel.getButton(ConfirmPanel.A_REFRESH))) { @@ -1903,7 +1939,13 @@ public abstract class InfoPanel extends Window implements EventListener, /** * Update relate info when selection in main info change */ - protected void updateSubcontent (){}; + protected void updateSubcontent (){ updateSubcontent(-1);}; + + /** + * Update relate info for a specific row, if targetRow < 0 update using selected row + */ + protected void updateSubcontent (int targetRow){}; + /** * Reset parameter to default value or to empty value? implement at @@ -1950,8 +1992,10 @@ public abstract class InfoPanel extends Window implements EventListener, 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); + DB.createT_SelectionNew(pInstanceID, getSaveKeys(getInfoColumnIDFromProcess(processModalDialog.getAD_Process_ID())), + null); + saveResultSelection(getInfoColumnIDFromProcess(processModalDialog.getAD_Process_ID())); + createT_Selection_InfoWindow(pInstanceID); }else if (ProcessModalDialog.ON_WINDOW_CLOSE.equals(event.getName())){ if (processModalDialog.isCancel()){ //clear back @@ -1975,6 +2019,147 @@ public abstract class InfoPanel extends Window implements EventListener, }); } + + /** + * save result values + */ + protected void saveResultSelection(int infoColumnId) { + int m_keyColumnIndex = contentPanel.getKeyColumnIndex(); + if (m_keyColumnIndex == -1) { + return; + } + + m_values = new LinkedHashMap>(); + + if (p_multipleSelection) { + + Map > selectedRow = getSelectedRowInfo(); + + // for selected rows + for (Entry> selectedInfo : selectedRow.entrySet()) + { + // get key and viewID + Integer keyData = selectedInfo.getKey(); + KeyNamePair kp = null; + + if (infoColumnId > 0){ + int dataIndex = columnDataIndex.get(infoColumnId) + p_layout.length; + Object viewIDValue = selectedInfo.getValue().get(dataIndex); + kp = new KeyNamePair(keyData, viewIDValue == null ? null : viewIDValue.toString()); + }else{ + kp = new KeyNamePair(keyData, null); + } + + // get Data + LinkedHashMap values = new LinkedHashMap(); + for(int col = 0 ; col < p_layout.length; col ++) + { + // layout has same columns as selectedInfo + if (!p_layout[col].isReadOnly()) + values.put(p_layout[col].getColumnName(), selectedInfo.getValue().get(col)); + } + if(values.size() > 0) + m_values.put(kp, values); + } + } + } // saveResultSelection + + /** + * Insert result values + * @param AD_PInstance_ID + */ + public void createT_Selection_InfoWindow(int AD_PInstance_ID) + { + StringBuilder insert = new StringBuilder(); + insert.append("INSERT INTO T_Selection_InfoWindow (AD_PINSTANCE_ID, T_SELECTION_ID, COLUMNNAME , VALUE_STRING, VALUE_NUMBER , VALUE_DATE ) VALUES(?,?,?,?,?,?) "); + for (Entry> records : m_values.entrySet()) { + //set Record ID + + LinkedHashMap fields = records.getValue(); + for(Entry field : fields.entrySet()) + { + List parameters = new ArrayList(); + parameters.add(AD_PInstance_ID); + + Object key = records.getKey(); + + if(key instanceof KeyNamePair) + { + KeyNamePair knp = (KeyNamePair)key; + parameters.add(knp.getKey()); + } + else + { + parameters.add(key); + } + + parameters.add(field.getKey()); + + Object data = field.getValue(); + // set Values + if (data instanceof IDColumn) + { + IDColumn id = (IDColumn) data; + parameters.add(null); + parameters.add(id.getRecord_ID()); + parameters.add(null); + } + else if (data instanceof String) + { + parameters.add(data); + parameters.add(null); + parameters.add(null); + } + else if (data instanceof BigDecimal || data instanceof Integer || data instanceof Double) + { + parameters.add(null); + if(data instanceof Double) + { + BigDecimal value = BigDecimal.valueOf((Double)data); + parameters.add(value); + } + else + parameters.add(data); + parameters.add(null); + } + else if (data instanceof Integer) + { + parameters.add(null); + parameters.add((Integer)data); + parameters.add(null); + } + else if (data instanceof Timestamp || data instanceof Date) + { + parameters.add(null); + parameters.add(null); + if(data instanceof Date) + { + Timestamp value = new Timestamp(((Date)data).getTime()); + parameters.add(value); + } + else + parameters.add(data); + } + else if(data instanceof KeyNamePair) + { + KeyNamePair knpData = (KeyNamePair)data; + + parameters.add(null); + parameters.add(knpData.getKey()); + parameters.add(null); + } + else + { + parameters.add(data); + parameters.add(null); + parameters.add(null); + } + DB.executeUpdateEx(insert.toString(),parameters.toArray() , null); + + } + } + } // createT_Selection_InfoWindow + /** * Get InfoColumnID of infoProcess have processID is processId * @param processId diff --git a/org.adempiere.ui/src/org/compiere/minigrid/ColumnInfo.java b/org.adempiere.ui/src/org/compiere/minigrid/ColumnInfo.java index 9fd98b7d42..fc69e23db9 100644 --- a/org.adempiere.ui/src/org/compiere/minigrid/ColumnInfo.java +++ b/org.adempiere.ui/src/org/compiere/minigrid/ColumnInfo.java @@ -38,6 +38,19 @@ public class ColumnInfo this(colHeader, colSQL, colClass, true, false, null); } // ColumnInfo + /** + * Create Info Column (r/o and not color column) + * + * @param colHeader Column Header + * @param colSQL SQL select code for column + * @param colClass class of column - determines display + * @param readOnly column is read only + */ + public ColumnInfo (String colHeader, String colSQL, Class colClass, boolean readOnly) + { + this(colHeader, colSQL, colClass, readOnly, false, null); + } // ColumnInfo + /** * Create Info Column (r/o and not color column) * @@ -51,6 +64,20 @@ public class ColumnInfo this(colHeader, colSQL, colClass, true, false, keyPairColSQL); } // ColumnInfo + /** + * Create Info Column (r/o and not color column) + * + * @param colHeader Column Header + * @param colSQL SQL select code for column + * @param colClass class of column - determines display + * @param keyPairColSQL SQL select for the ID of the for the displayed column + * @param readOnly column is read only + */ + public ColumnInfo (String colHeader, String colSQL, Class colClass, String keyPairColSQL, boolean readOnly) + { + this(colHeader, colSQL, colClass, readOnly, false, keyPairColSQL); + } // ColumnInfo + /** * Create Info Column * @@ -63,17 +90,36 @@ public class ColumnInfo */ public ColumnInfo (String colHeader, String colSQL, Class colClass, boolean readOnly, boolean colorColumn, String keyPairColSQL) + { + this(colHeader, colSQL, colClass, readOnly, false, keyPairColSQL, null); + } + + /** + * Create Info Column + * + * @param colHeader Column Header + * @param colSQL SQL select code for column + * @param colClass class of column - determines display + * @param readOnly column is read only + * @param colorColumn if true, value of column determines foreground color + * @param keyPairColSQL SQL select for the ID of the for the displayed column + * @param columnName Column Name + */ + public ColumnInfo (String colHeader, String colSQL, Class colClass, + boolean readOnly, boolean colorColumn, String keyPairColSQL, String columnName) { setColHeader(colHeader); setColSQL(colSQL); setColClass(colClass); setReadOnly(readOnly); setColorColumn(colorColumn); + setColumnName(columnName); setKeyPairColSQL(keyPairColSQL); } // ColumnInfo private String m_colHeader; + private String m_columnName; private String m_colSQL; private Class m_colClass; private boolean m_readOnly; @@ -100,6 +146,14 @@ public class ColumnInfo { return m_colHeader; } + /** + * Get Column Name + * @return Column Name + */ + public String getColumnName() + { + return m_columnName; + } /** * Get Col SQL * @return sql @@ -138,6 +192,14 @@ public class ColumnInfo m_colHeader = colHeader.substring(0, index) + colHeader.substring(index+1); } } + /** + * Set Column Name + * @param columnName Column Name + */ + public void setColumnName(String columnName) + { + m_columnName = columnName; + } /** * Set Col SQL * @param colSQL sql