From e9e7486473a835f2fbe6bc6fc4e13c8788faf30a Mon Sep 17 00:00:00 2001 From: hengsin Date: Fri, 29 May 2020 19:25:24 +0800 Subject: [PATCH] IDEMPIERE-1540 Autocomplete for field type "Search" (#79) * IDEMPIERE-1540 Autocomplete for field type "Search" Implement auto complete for search field * IDEMPIERE-1540 Autocomplete for field type "Search" Add support for isAutoComplete flag (default is off). Add isAutoComplete to AD_UserDef_Field, AD_InfoColumn and AD_Process_Para. Performance improvement - reduce number of queries for autocomplete and added some cache. * IDEMPIERE-1540 Autocomplete for field type "Search" add constant for auto complete query timeout use union all to further reduce the number of queries --- .../oracle/202005272300_IDEMPIERE-1540.sql | 67 ++++ .../202005272300_IDEMPIERE-1540.sql | 64 ++++ .../src/org/compiere/model/GridFieldVO.java | 4 + .../org/compiere/model/I_AD_InfoColumn.java | 13 + .../org/compiere/model/I_AD_Process_Para.java | 13 + .../compiere/model/I_AD_UserDef_Field.java | 13 + .../src/org/compiere/model/Lookup.java | 16 + .../src/org/compiere/model/MInfoColumn.java | 21 ++ .../src/org/compiere/model/MInfoWindow.java | 65 ++-- .../src/org/compiere/model/MLookup.java | 237 +++++++++++++- .../org/compiere/model/X_AD_InfoColumn.java | 28 +- .../org/compiere/model/X_AD_Process_Para.java | 28 +- .../compiere/model/X_AD_UserDef_Field.java | 26 +- .../webui/apps/ProcessParameterPanel.java | 4 +- .../webui/component/ComboEditorBox.java | 192 ++++++++++++ .../webui/editor/InfoListSubModel.java | 122 ++++++++ .../webui/editor/WChosenboxSearchEditor.java | 288 +++--------------- .../adempiere/webui/editor/WSearchEditor.java | 54 +++- .../org/adempiere/webui/info/InfoWindow.java | 54 ++-- .../webui/panel/InfoGeneralPanel.java | 76 +++-- .../org/adempiere/webui/panel/InfoPanel.java | 61 +++- 21 files changed, 1085 insertions(+), 361 deletions(-) create mode 100644 migration/i7.1z/oracle/202005272300_IDEMPIERE-1540.sql create mode 100644 migration/i7.1z/postgresql/202005272300_IDEMPIERE-1540.sql create mode 100644 org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/component/ComboEditorBox.java create mode 100644 org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/editor/InfoListSubModel.java diff --git a/migration/i7.1z/oracle/202005272300_IDEMPIERE-1540.sql b/migration/i7.1z/oracle/202005272300_IDEMPIERE-1540.sql new file mode 100644 index 0000000000..508482f7f1 --- /dev/null +++ b/migration/i7.1z/oracle/202005272300_IDEMPIERE-1540.sql @@ -0,0 +1,67 @@ +SET SQLBLANKLINES ON +SET DEFINE OFF + +-- IDEMPIERE-1540 Autocomplete for field type "Search" +-- May 21, 2020, 10:15:20 PM MYT +UPDATE AD_Field SET DisplayLogic='@AD_Reference_ID@=10 | @AD_Reference_ID@=17 | @AD_Reference_ID@=18 | @AD_Reference_ID@=19 | @AD_Reference_ID@=30', AD_Reference_Value_ID=NULL, AD_Val_Rule_ID=NULL, IsToolbarButton=NULL,Updated=TO_DATE('2020-05-21 22:15:20','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=56279 +; + +-- May 22, 2020, 11:34:12 PM MYT +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,SeqNoSelection,IsToolbarButton,IsSecure,IsHtml) VALUES (214201,0,'Autocomplete','Automatic completion for textfields','The autocompletion uses all existing values (from the same client and organization) of the field.',464,'IsAutocomplete',NULL,1,'N','N','N','N','N',0,'N',20,0,0,'Y',TO_DATE('2020-05-22 23:34:11','YYYY-MM-DD HH24:MI:SS'),100,TO_DATE('2020-05-22 23:34:11','YYYY-MM-DD HH24:MI:SS'),100,53655,'Y','N','D','N','N','N','Y','1254c9c5-43a3-45a2-9dff-a80df5aa89d7','N',0,'N','N','N') +; + +-- May 22, 2020, 11:34:17 PM MYT +ALTER TABLE AD_UserDef_Field ADD IsAutocomplete CHAR(1) DEFAULT NULL CHECK (IsAutocomplete IN ('Y','N')) +; + +-- May 22, 2020, 11:37:48 PM MYT +INSERT INTO AD_Field (AD_Field_ID,Name,Description,Help,AD_Tab_ID,AD_Column_ID,IsDisplayed,DisplayLogic,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,IsQuickForm) VALUES (206408,'Autocomplete','Automatic completion for textfields','The autocompletion uses all existing values (from the same client and organization) of the field.',395,214201,'Y','@AD_Reference_ID@=10 | @AD_Reference_ID@=17 | @AD_Reference_ID@=18 | @AD_Reference_ID@=19 | @AD_Reference_ID@=30',0,330,0,'N','N','N','N',0,0,'Y',TO_DATE('2020-05-22 23:37:47','YYYY-MM-DD HH24:MI:SS'),100,TO_DATE('2020-05-22 23:37:47','YYYY-MM-DD HH24:MI:SS'),100,'N','Y','D','37df68ac-aafb-458e-878c-efb421b9a129','Y',330,2,2,1,'N','N','N','N') +; + +-- May 22, 2020, 11:39:28 PM MYT +UPDATE AD_Field SET SeqNo=305, AD_Reference_Value_ID=NULL, AD_Val_Rule_ID=NULL, SeqNoGrid=305, IsToolbarButton=NULL,Updated=TO_DATE('2020-05-22 23:39:28','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=206408 +; + +-- May 22, 2020, 11:39:43 PM MYT +UPDATE AD_Field SET SeqNo=330, AD_Reference_Value_ID=NULL, AD_Val_Rule_ID=NULL, IsToolbarButton=NULL,Updated=TO_DATE('2020-05-22 23:39:43','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=5057 +; + +-- May 22, 2020, 11:39:50 PM MYT +UPDATE AD_Field SET SeqNo=320, AD_Reference_Value_ID=NULL, AD_Val_Rule_ID=NULL, IsToolbarButton=NULL,Updated=TO_DATE('2020-05-22 23:39:50','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=5051 +; + +-- May 22, 2020, 11:40:05 PM MYT +UPDATE AD_Field SET SeqNo=310, AD_Reference_Value_ID=NULL, AD_Val_Rule_ID=NULL, SeqNoGrid=310, IsToolbarButton=NULL,Updated=TO_DATE('2020-05-22 23:40:05','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=206408 +; + +-- May 22, 2020, 11:42:22 PM MYT +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,SeqNoSelection,IsToolbarButton,IsSecure,IsHtml) VALUES (214202,0,'Autocomplete','Automatic completion for textfields','The autocompletion uses all existing values (from the same client and organization) of the field.',897,'IsAutocomplete','N',1,'N','N','Y','N','N',0,'N',20,0,0,'Y',TO_DATE('2020-05-22 23:42:21','YYYY-MM-DD HH24:MI:SS'),100,TO_DATE('2020-05-22 23:42:21','YYYY-MM-DD HH24:MI:SS'),100,53655,'Y','N','D','N','N','N','Y','1fc6ddb8-4449-46ab-a54e-418260e069cf','N',0,'N','N','N') +; + +-- May 22, 2020, 11:42:29 PM MYT +ALTER TABLE AD_InfoColumn ADD IsAutocomplete CHAR(1) DEFAULT 'N' CHECK (IsAutocomplete IN ('Y','N')) NOT NULL +; + +-- May 22, 2020, 11:45:54 PM MYT +INSERT INTO AD_Field (AD_Field_ID,Name,Description,Help,AD_Tab_ID,AD_Column_ID,IsDisplayed,DisplayLogic,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,IsQuickForm) VALUES (206409,'Autocomplete','Automatic completion for textfields','The autocompletion uses all existing values (from the same client and organization) of the field.',844,214202,'Y','@AD_Reference_ID@=10 | @AD_Reference_ID@=17 | @AD_Reference_ID@=18 | @AD_Reference_ID@=19 | @AD_Reference_ID@=30',0,320,0,'N','N','N','N',0,0,'Y',TO_DATE('2020-05-22 23:45:54','YYYY-MM-DD HH24:MI:SS'),100,TO_DATE('2020-05-22 23:45:54','YYYY-MM-DD HH24:MI:SS'),100,'N','Y','D','49343858-2e9b-437b-9131-d10e069674bb','Y',320,2,2,1,'N','N','N','N') +; + +-- May 22, 2020, 11:47:35 PM MYT +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,SeqNoSelection,IsToolbarButton,IsSecure,FKConstraintType,IsHtml) VALUES (214203,0,'Autocomplete','Automatic completion for textfields','The autocompletion uses all existing values (from the same client and organization) of the field.',285,'IsAutocomplete','N',1,'N','N','Y','N','N',0,'N',20,0,0,'Y',TO_DATE('2020-05-22 23:47:35','YYYY-MM-DD HH24:MI:SS'),100,TO_DATE('2020-05-22 23:47:35','YYYY-MM-DD HH24:MI:SS'),100,53655,'Y','N','D','N','N','N','Y','2594d121-f632-44b5-b79e-d630ed950a70','Y',0,'N','N','N','N') +; + +-- May 22, 2020, 11:47:38 PM MYT +ALTER TABLE AD_Process_Para ADD IsAutocomplete CHAR(1) DEFAULT 'N' CHECK (IsAutocomplete IN ('Y','N')) NOT NULL +; + +-- May 22, 2020, 11:49:01 PM MYT +INSERT INTO AD_Field (AD_Field_ID,Name,Description,Help,AD_Tab_ID,AD_Column_ID,IsDisplayed,DisplayLogic,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,IsQuickForm) VALUES (206410,'Autocomplete','Automatic completion for textfields','The autocompletion uses all existing values (from the same client and organization) of the field.',246,214203,'Y','@AD_Reference_ID@=10 | @AD_Reference_ID@=17 | @AD_Reference_ID@=18 | @AD_Reference_ID@=19 | @AD_Reference_ID@=30',0,300,0,'N','N','N','N',0,0,'Y',TO_DATE('2020-05-22 23:49:00','YYYY-MM-DD HH24:MI:SS'),100,TO_DATE('2020-05-22 23:49:00','YYYY-MM-DD HH24:MI:SS'),100,'N','Y','D','8e6fd1fe-8d03-4105-a76f-b563e8ba9637','Y',280,2,2,1,'N','N','N','N') +; + +-- May 27, 2020, 10:03:39 AM MYT +UPDATE AD_Column SET AD_Reference_ID=17, AD_Reference_Value_ID=319,Updated=TO_DATE('2020-05-27 10:03:39','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Column_ID=214201 +; + +SELECT register_migration_script('202005272300_IDEMPIERE-1540.sql') FROM dual +; + diff --git a/migration/i7.1z/postgresql/202005272300_IDEMPIERE-1540.sql b/migration/i7.1z/postgresql/202005272300_IDEMPIERE-1540.sql new file mode 100644 index 0000000000..77c95f09ba --- /dev/null +++ b/migration/i7.1z/postgresql/202005272300_IDEMPIERE-1540.sql @@ -0,0 +1,64 @@ +-- IDEMPIERE-1540 Autocomplete for field type "Search" +-- May 21, 2020, 10:15:20 PM MYT +UPDATE AD_Field SET DisplayLogic='@AD_Reference_ID@=10 | @AD_Reference_ID@=17 | @AD_Reference_ID@=18 | @AD_Reference_ID@=19 | @AD_Reference_ID@=30', AD_Reference_Value_ID=NULL, AD_Val_Rule_ID=NULL, IsToolbarButton=NULL,Updated=TO_TIMESTAMP('2020-05-21 22:15:20','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=56279 +; + +-- May 22, 2020, 11:34:12 PM MYT +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,SeqNoSelection,IsToolbarButton,IsSecure,IsHtml) VALUES (214201,0,'Autocomplete','Automatic completion for textfields','The autocompletion uses all existing values (from the same client and organization) of the field.',464,'IsAutocomplete',NULL,1,'N','N','N','N','N',0,'N',20,0,0,'Y',TO_TIMESTAMP('2020-05-22 23:34:11','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2020-05-22 23:34:11','YYYY-MM-DD HH24:MI:SS'),100,53655,'Y','N','D','N','N','N','Y','1254c9c5-43a3-45a2-9dff-a80df5aa89d7','N',0,'N','N','N') +; + +-- May 22, 2020, 11:34:17 PM MYT +ALTER TABLE AD_UserDef_Field ADD COLUMN IsAutocomplete CHAR(1) DEFAULT NULL CHECK (IsAutocomplete IN ('Y','N')) +; + +-- May 22, 2020, 11:37:48 PM MYT +INSERT INTO AD_Field (AD_Field_ID,Name,Description,Help,AD_Tab_ID,AD_Column_ID,IsDisplayed,DisplayLogic,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,IsQuickForm) VALUES (206408,'Autocomplete','Automatic completion for textfields','The autocompletion uses all existing values (from the same client and organization) of the field.',395,214201,'Y','@AD_Reference_ID@=10 | @AD_Reference_ID@=17 | @AD_Reference_ID@=18 | @AD_Reference_ID@=19 | @AD_Reference_ID@=30',0,330,0,'N','N','N','N',0,0,'Y',TO_TIMESTAMP('2020-05-22 23:37:47','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2020-05-22 23:37:47','YYYY-MM-DD HH24:MI:SS'),100,'N','Y','D','37df68ac-aafb-458e-878c-efb421b9a129','Y',330,2,2,1,'N','N','N','N') +; + +-- May 22, 2020, 11:39:28 PM MYT +UPDATE AD_Field SET SeqNo=305, AD_Reference_Value_ID=NULL, AD_Val_Rule_ID=NULL, SeqNoGrid=305, IsToolbarButton=NULL,Updated=TO_TIMESTAMP('2020-05-22 23:39:28','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=206408 +; + +-- May 22, 2020, 11:39:43 PM MYT +UPDATE AD_Field SET SeqNo=330, AD_Reference_Value_ID=NULL, AD_Val_Rule_ID=NULL, IsToolbarButton=NULL,Updated=TO_TIMESTAMP('2020-05-22 23:39:43','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=5057 +; + +-- May 22, 2020, 11:39:50 PM MYT +UPDATE AD_Field SET SeqNo=320, AD_Reference_Value_ID=NULL, AD_Val_Rule_ID=NULL, IsToolbarButton=NULL,Updated=TO_TIMESTAMP('2020-05-22 23:39:50','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=5051 +; + +-- May 22, 2020, 11:40:05 PM MYT +UPDATE AD_Field SET SeqNo=310, AD_Reference_Value_ID=NULL, AD_Val_Rule_ID=NULL, SeqNoGrid=310, IsToolbarButton=NULL,Updated=TO_TIMESTAMP('2020-05-22 23:40:05','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=206408 +; + +-- May 22, 2020, 11:42:22 PM MYT +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,SeqNoSelection,IsToolbarButton,IsSecure,IsHtml) VALUES (214202,0,'Autocomplete','Automatic completion for textfields','The autocompletion uses all existing values (from the same client and organization) of the field.',897,'IsAutocomplete','N',1,'N','N','Y','N','N',0,'N',20,0,0,'Y',TO_TIMESTAMP('2020-05-22 23:42:21','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2020-05-22 23:42:21','YYYY-MM-DD HH24:MI:SS'),100,53655,'Y','N','D','N','N','N','Y','1fc6ddb8-4449-46ab-a54e-418260e069cf','N',0,'N','N','N') +; + +-- May 22, 2020, 11:42:29 PM MYT +ALTER TABLE AD_InfoColumn ADD COLUMN IsAutocomplete CHAR(1) DEFAULT 'N' CHECK (IsAutocomplete IN ('Y','N')) NOT NULL +; + +-- May 22, 2020, 11:45:54 PM MYT +INSERT INTO AD_Field (AD_Field_ID,Name,Description,Help,AD_Tab_ID,AD_Column_ID,IsDisplayed,DisplayLogic,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,IsQuickForm) VALUES (206409,'Autocomplete','Automatic completion for textfields','The autocompletion uses all existing values (from the same client and organization) of the field.',844,214202,'Y','@AD_Reference_ID@=10 | @AD_Reference_ID@=17 | @AD_Reference_ID@=18 | @AD_Reference_ID@=19 | @AD_Reference_ID@=30',0,320,0,'N','N','N','N',0,0,'Y',TO_TIMESTAMP('2020-05-22 23:45:54','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2020-05-22 23:45:54','YYYY-MM-DD HH24:MI:SS'),100,'N','Y','D','49343858-2e9b-437b-9131-d10e069674bb','Y',320,2,2,1,'N','N','N','N') +; + +-- May 22, 2020, 11:47:35 PM MYT +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,SeqNoSelection,IsToolbarButton,IsSecure,FKConstraintType,IsHtml) VALUES (214203,0,'Autocomplete','Automatic completion for textfields','The autocompletion uses all existing values (from the same client and organization) of the field.',285,'IsAutocomplete','N',1,'N','N','Y','N','N',0,'N',20,0,0,'Y',TO_TIMESTAMP('2020-05-22 23:47:35','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2020-05-22 23:47:35','YYYY-MM-DD HH24:MI:SS'),100,53655,'Y','N','D','N','N','N','Y','2594d121-f632-44b5-b79e-d630ed950a70','Y',0,'N','N','N','N') +; + +-- May 22, 2020, 11:47:38 PM MYT +ALTER TABLE AD_Process_Para ADD COLUMN IsAutocomplete CHAR(1) DEFAULT 'N' CHECK (IsAutocomplete IN ('Y','N')) NOT NULL +; + +-- May 22, 2020, 11:49:01 PM MYT +INSERT INTO AD_Field (AD_Field_ID,Name,Description,Help,AD_Tab_ID,AD_Column_ID,IsDisplayed,DisplayLogic,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,IsQuickForm) VALUES (206410,'Autocomplete','Automatic completion for textfields','The autocompletion uses all existing values (from the same client and organization) of the field.',246,214203,'Y','@AD_Reference_ID@=10 | @AD_Reference_ID@=17 | @AD_Reference_ID@=18 | @AD_Reference_ID@=19 | @AD_Reference_ID@=30',0,300,0,'N','N','N','N',0,0,'Y',TO_TIMESTAMP('2020-05-22 23:49:00','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2020-05-22 23:49:00','YYYY-MM-DD HH24:MI:SS'),100,'N','Y','D','8e6fd1fe-8d03-4105-a76f-b563e8ba9637','Y',280,2,2,1,'N','N','N','N') +; + +-- May 27, 2020, 10:03:39 AM MYT +UPDATE AD_Column SET AD_Reference_ID=17, AD_Reference_Value_ID=319,Updated=TO_TIMESTAMP('2020-05-27 10:03:39','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Column_ID=214201 +; + +SELECT register_migration_script('202005272300_IDEMPIERE-1540.sql') FROM dual +; + diff --git a/org.adempiere.base/src/org/compiere/model/GridFieldVO.java b/org.adempiere.base/src/org/compiere/model/GridFieldVO.java index 444f751ce7..346fc90dfe 100644 --- a/org.adempiere.base/src/org/compiere/model/GridFieldVO.java +++ b/org.adempiere.base/src/org/compiere/model/GridFieldVO.java @@ -330,6 +330,9 @@ else if (columnName.equalsIgnoreCase("IsHtml")) if (userDef.getPlaceholder() != null) vo.Placeholder = userDef.getPlaceholder(); + + if (userDef.getIsAutocomplete() != null) + vo.IsAutocomplete = "Y".equals(userDef.getIsAutocomplete()); } } // @@ -384,6 +387,7 @@ public static GridFieldVO createParameter (Properties ctx, int WindowNo, int Pro vo.MandatoryLogic = rs.getString("MandatoryLogic"); vo.Placeholder = rs.getString("Placeholder"); vo.Placeholder2 = rs.getString("Placeholder2"); + vo.IsAutocomplete = "Y".equals(rs.getString("IsAutoComplete")); } catch (SQLException e) { 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 e1d39c2cbd..f1983fc6a6 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 @@ -298,6 +298,19 @@ public interface I_AD_InfoColumn */ public boolean isActive(); + /** Column name IsAutocomplete */ + public static final String COLUMNNAME_IsAutocomplete = "IsAutocomplete"; + + /** Set Autocomplete. + * Automatic completion for textfields + */ + public void setIsAutocomplete (boolean IsAutocomplete); + + /** Get Autocomplete. + * Automatic completion for textfields + */ + public boolean isAutocomplete(); + /** Column name IsCentrallyMaintained */ public static final String COLUMNNAME_IsCentrallyMaintained = "IsCentrallyMaintained"; diff --git a/org.adempiere.base/src/org/compiere/model/I_AD_Process_Para.java b/org.adempiere.base/src/org/compiere/model/I_AD_Process_Para.java index 359510f03a..506c133502 100644 --- a/org.adempiere.base/src/org/compiere/model/I_AD_Process_Para.java +++ b/org.adempiere.base/src/org/compiere/model/I_AD_Process_Para.java @@ -294,6 +294,19 @@ public interface I_AD_Process_Para */ public boolean isActive(); + /** Column name IsAutocomplete */ + public static final String COLUMNNAME_IsAutocomplete = "IsAutocomplete"; + + /** Set Autocomplete. + * Automatic completion for textfields + */ + public void setIsAutocomplete (boolean IsAutocomplete); + + /** Get Autocomplete. + * Automatic completion for textfields + */ + public boolean isAutocomplete(); + /** Column name IsCentrallyMaintained */ public static final String COLUMNNAME_IsCentrallyMaintained = "IsCentrallyMaintained"; diff --git a/org.adempiere.base/src/org/compiere/model/I_AD_UserDef_Field.java b/org.adempiere.base/src/org/compiere/model/I_AD_UserDef_Field.java index fd08503294..0cbbcca7a0 100644 --- a/org.adempiere.base/src/org/compiere/model/I_AD_UserDef_Field.java +++ b/org.adempiere.base/src/org/compiere/model/I_AD_UserDef_Field.java @@ -303,6 +303,19 @@ public interface I_AD_UserDef_Field */ public String getIsAlwaysUpdateable(); + /** Column name IsAutocomplete */ + public static final String COLUMNNAME_IsAutocomplete = "IsAutocomplete"; + + /** Set Autocomplete. + * Automatic completion for textfields + */ + public void setIsAutocomplete (String IsAutocomplete); + + /** Get Autocomplete. + * Automatic completion for textfields + */ + public String getIsAutocomplete(); + /** Column name IsDisplayed */ public static final String COLUMNNAME_IsDisplayed = "IsDisplayed"; diff --git a/org.adempiere.base/src/org/compiere/model/Lookup.java b/org.adempiere.base/src/org/compiere/model/Lookup.java index f2171c64e7..d35b2f86ca 100644 --- a/org.adempiere.base/src/org/compiere/model/Lookup.java +++ b/org.adempiere.base/src/org/compiere/model/Lookup.java @@ -18,6 +18,7 @@ import java.io.Serializable; import java.util.ArrayList; +import java.util.List; import java.util.logging.Level; import javax.swing.AbstractListModel; @@ -481,6 +482,21 @@ public NamePair getDirect (Object key, boolean saveInCache, boolean cacheLocal) return get (key); } // getDirect + /** + * + * @param keys + * @return name pair arrays + */ + public NamePair[] getDirect(Object[] keys) + { + List list = new ArrayList(); + for (Object key : keys) + { + list.add(getDirect(key, false, isValidated())); + } + return list.toArray(new NamePair[0]); + } + /** * Dispose - clear items w/o firing events */ diff --git a/org.adempiere.base/src/org/compiere/model/MInfoColumn.java b/org.adempiere.base/src/org/compiere/model/MInfoColumn.java index b309d119b4..68ea9b77cc 100644 --- a/org.adempiere.base/src/org/compiere/model/MInfoColumn.java +++ b/org.adempiere.base/src/org/compiere/model/MInfoColumn.java @@ -211,4 +211,25 @@ public int getInfoColumnID() { public MInfoColumn getAD_InfoColumn() { return this; } + + @Override + public I_AD_Val_Rule getAD_Val_Rule() throws RuntimeException { + if (get_TrxName() != null) + return new MValRule(getCtx(), getAD_Val_Rule_ID(), get_TrxName()); + else + return MValRule.get(getCtx(), getAD_Val_Rule_ID()); + } + + @Override + protected MInfoColumn clone() { + try { + MInfoColumn ic = (MInfoColumn) super.clone(); + ic.m_parent = null; + return ic; + } catch (CloneNotSupportedException e) { + throw new RuntimeException(e); + } + } + + } // MInfoColumn diff --git a/org.adempiere.base/src/org/compiere/model/MInfoWindow.java b/org.adempiere.base/src/org/compiere/model/MInfoWindow.java index 41179d2bc0..7eca0cd6cf 100644 --- a/org.adempiere.base/src/org/compiere/model/MInfoWindow.java +++ b/org.adempiere.base/src/org/compiere/model/MInfoWindow.java @@ -19,6 +19,7 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Properties; @@ -188,27 +189,29 @@ public static MInfoWindow get(int infoWindowID, String trxName) { return null; } - public MInfoColumn[] getInfoColumns(TableInfo[] tableInfos) { - Query query = new Query(getCtx(), MTable.get(getCtx(), I_AD_InfoColumn.Table_ID), I_AD_InfoColumn.COLUMNNAME_AD_InfoWindow_ID+"=?", get_TrxName()); - List list = query.setParameters(getAD_InfoWindow_ID()) - .setOnlyActiveRecords(true) - .setOrderBy("SeqNo, AD_InfoColumn_ID") - .list(); - for(int i = list.size() - 1; i >= 0; i--) { - MInfoColumn infoColumn = list.get(i); - if (!infoColumn.isColumnAccess(tableInfos)) - list.remove(i); + public synchronized MInfoColumn[] getInfoColumns(TableInfo[] tableInfos) { + getInfoColumns(); + List list = new ArrayList(); + for(MInfoColumn ic : m_infocolumns) { + if (ic.isColumnAccess(tableInfos)) + list.add(ic); } return list.toArray(new MInfoColumn[0]); } - public MInfoColumn[] getInfoColumns() { - Query query = new Query(getCtx(), MTable.get(getCtx(), I_AD_InfoColumn.Table_ID), I_AD_InfoColumn.COLUMNNAME_AD_InfoWindow_ID+"=?", get_TrxName()); - List list = query.setParameters(getAD_InfoWindow_ID()) - .setOnlyActiveRecords(true) - .setOrderBy("SeqNo, AD_InfoColumn_ID") - .list(); - return list.toArray(new MInfoColumn[0]); + public synchronized MInfoColumn[] getInfoColumns() { + if (m_infocolumns == null) { + Query query = new Query(getCtx(), MTable.get(getCtx(), I_AD_InfoColumn.Table_ID), I_AD_InfoColumn.COLUMNNAME_AD_InfoWindow_ID+"=?", get_TrxName()); + List list = query.setParameters(getAD_InfoWindow_ID()) + .setOnlyActiveRecords(true) + .setOrderBy("SeqNo, AD_InfoColumn_ID") + .list(); + m_infocolumns = list.toArray(new MInfoColumn[0]); + } + + if (get_TrxName() != null) + set_TrxName(m_infocolumns, get_TrxName()); + return m_infocolumns; } /** @@ -216,26 +219,22 @@ public MInfoColumn[] getInfoColumns() { */ private MInfoColumn[] m_infocolumns = null; - public MInfoColumn[] getInfoColumns(boolean requery, boolean checkDisplay) { - if ((this.m_infocolumns != null) && (!requery)) { - set_TrxName(this.m_infocolumns, get_TrxName()); - return this.m_infocolumns; + public synchronized MInfoColumn[] getInfoColumns(boolean requery, boolean checkDisplay) { + if (m_infocolumns == null || requery) { + m_infocolumns = null; + getInfoColumns(); } + if (checkDisplay) { - List list = new Query(getCtx(), MInfoColumn.Table_Name, "AD_InfoWindow_ID=? AND IsDisplayed='Y'", get_TrxName()) - .setParameters(get_ID()) - .setOrderBy("SeqNo") - .list(); - this.m_infocolumns = list.toArray(new MInfoColumn[list.size()]); + List list = new ArrayList(); + for(MInfoColumn ic : m_infocolumns) { + if (ic.isDisplayed()) + list.add(ic); + } + return list.toArray(new MInfoColumn[list.size()]); } else { - List list = new Query(getCtx(), MInfoColumn.Table_Name, "AD_InfoWindow_ID=?", get_TrxName()) - .setParameters(get_ID()) - .setOrderBy("SeqNo") - .list(); - this.m_infocolumns = list.toArray(new MInfoColumn[list.size()]); + return m_infocolumns; } - - return this.m_infocolumns; } /** diff --git a/org.adempiere.base/src/org/compiere/model/MLookup.java b/org.adempiere.base/src/org/compiere/model/MLookup.java index b2f65f4a78..39608ab35a 100644 --- a/org.adempiere.base/src/org/compiere/model/MLookup.java +++ b/org.adempiere.base/src/org/compiere/model/MLookup.java @@ -23,11 +23,15 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.concurrent.Future; import java.util.logging.Level; import org.adempiere.util.ContextRunnable; import org.compiere.Adempiere; +import org.compiere.util.CCache; import org.compiere.util.CLogMgt; import org.compiere.util.DB; import org.compiere.util.DisplayType; @@ -129,6 +133,7 @@ public MLookup (MLookupInfo info, int TabNo) private boolean m_hasShortListItems = false; // IDEMPIERE 90 + private final static int MAX_NAMEPAIR_CACHE_SIZE = 1000; /** * Dispose */ @@ -540,7 +545,31 @@ public NamePair getDirect (Object key, boolean saveInCache, boolean cacheLocal, } if (log.isLoggable(Level.FINER)) log.finer(m_info.KeyColumn + ": " + key + ", SaveInCache=" + saveInCache + ",Local=" + cacheLocal); + + String cacheKey = m_info.TableName+"|"+m_info.KeyColumn; boolean isNumber = m_info.KeyColumn.endsWith("_ID"); + CCache knpCache = null; + CCache vnpCache = null; + if (isNumber) + { + knpCache = s_directKeyNamePairCache.get(cacheKey); + if (knpCache != null) + { + KeyNamePair knp = knpCache.get(Integer.parseInt(key.toString())); + if (knp != null) + return knp; + } + } + else + { + vnpCache = s_directValueNamePairCache.get(cacheKey); + if (vnpCache != null) + { + ValueNamePair vnp = vnpCache.get(key.toString()); + if (vnp != null) + return vnp; + } + } PreparedStatement pstmt = null; ResultSet rs = null; try @@ -567,6 +596,16 @@ public NamePair getDirect (Object key, boolean saveInCache, boolean cacheLocal, if (saveInCache) // save if m_lookup.put(Integer.valueOf(keyValue), p); directValue = p; + if (knpCache != null) + { + knpCache.put(p.getKey(), p); + } + else + { + knpCache = new CCache(null, "MLookup.DirectKeyNamePairCache", 100, 60, false, MAX_NAMEPAIR_CACHE_SIZE); + knpCache.put(p.getKey(), p); + s_directKeyNamePairCache.put(cacheKey, knpCache); + } } else { @@ -575,6 +614,16 @@ public NamePair getDirect (Object key, boolean saveInCache, boolean cacheLocal, if (saveInCache) // save if m_lookup.put(value, p); directValue = p; + if (vnpCache != null) + { + vnpCache.put(p.getValue(), p); + } + else + { + vnpCache = new CCache(null, "MLookup.DirectValueNamePairCache", 100, 60, false, MAX_NAMEPAIR_CACHE_SIZE); + vnpCache.put(p.getValue(), p); + s_directValueNamePairCache.put(cacheKey, vnpCache); + } } if (rs.next()) log.log(Level.SEVERE, m_info.KeyColumn + ": Not unique (first returned) for " @@ -616,6 +665,132 @@ else if (!m_lookupDirect.containsKey(key)) return directValue; } // getDirect + + @Override + public NamePair[] getDirect(Object[] keys) + { + List list = new ArrayList(); + String cacheKey = m_info.TableName+"|"+m_info.KeyColumn; + boolean isNumber = m_info.KeyColumn.endsWith("_ID"); + CCache knpCache = null; + CCache vnpCache = null; + Map notInCaches = new HashMap(); + for (int i = 0; i < keys.length; i++) + { + Object key = keys[i]; + if (isNumber) + { + KeyNamePair knp = null; + int id = Integer.parseInt(key.toString()); + knpCache = s_directKeyNamePairCache.get(cacheKey); + if (knpCache != null) + { + knp = knpCache.get(id); + } + if (knp == null) + knp = new KeyNamePair(id, null); + list.add(knp); + notInCaches.put(id, i); + } + else + { + ValueNamePair vnp = null; + vnpCache = s_directValueNamePairCache.get(cacheKey); + if (vnpCache != null) + { + vnp = vnpCache.get(key.toString()); + } + if (vnp == null) + vnp = new ValueNamePair(key.toString(), null); + list.add(vnp); + notInCaches.put(key.toString(), i); + } + } + + StringBuilder builder = new StringBuilder(); + for(int i = 0; i < notInCaches.size(); i++) + { + if (builder.length() > 0) + builder.append(" UNION ALL "); + builder.append(m_info.QueryDirect); + } + + try (PreparedStatement pstmt = DB.prepareStatement(builder.toString(), null)) + { + Set keySet = notInCaches.keySet(); + int i = 0; + for(Object id : keySet) + { + i++; + if (id instanceof Integer) + { + pstmt.setInt(i, (int) id); + } + else + { + pstmt.setString(i, id.toString()); + } + } + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) + { + StringBuilder name = new StringBuilder().append(rs.getString(3)); + boolean isActive = rs.getString(4).equals("Y"); + if (!isActive) + { + name.insert(0, INACTIVE_S).append(INACTIVE_E); + } + if (isNumber) + { + int keyValue = rs.getInt(1); + KeyNamePair p = new KeyNamePair(keyValue, name.toString()); + if (knpCache != null) + { + knpCache.put(p.getKey(), p); + } + else + { + knpCache = new CCache(null, "MLookup.DirectKeyNamePairCache", 100, 60, false, MAX_NAMEPAIR_CACHE_SIZE); + knpCache.put(p.getKey(), p); + s_directKeyNamePairCache.put(cacheKey, knpCache); + } + Integer idx = notInCaches.get(p.getKey()); + if (idx != null) + list.set(idx.intValue(), p); + } + else + { + String value = rs.getString(2); + ValueNamePair p = new ValueNamePair(value, name.toString()); + if (vnpCache != null) + { + vnpCache.put(p.getValue(), p); + } + else + { + vnpCache = new CCache(null, "MLookup.DirectValueNamePairCache", 100, 60, false, MAX_NAMEPAIR_CACHE_SIZE); + vnpCache.put(p.getValue(), p); + s_directValueNamePairCache.put(cacheKey, vnpCache); + } + Integer idx = notInCaches.get(p.getValue()); + if (idx != null) + list.set(idx.intValue(), p); + } + } + } catch (SQLException e) { + log.log(Level.SEVERE, e.getMessage(), e); + } + + for(int i = list.size()-1; i >= 0; i--) + { + NamePair np = list.get(i); + if (np.getName() == null) + list.remove(i); + } + + return list.toArray(new NamePair[0]); + } + /** * Get Zoom * @return Zoom AD_Window_ID @@ -759,6 +934,12 @@ public MLookupInfo getLookupInfo() return m_info; } + private final static CCache> s_keyNamePairCache = new CCache>(null, "MLookup.KeyNamePairCache", 100, 60, false, 500); + private final static CCache> s_valueNamePairCache = new CCache>(null, "MLookup.ValueNamePairCache", 100, 60, false, 500); + + private final static CCache> s_directKeyNamePairCache = new CCache>(null, "", 100, 60, false, 500); + private final static CCache> s_directValueNamePairCache = new CCache>(null, "", 100, 60, false, 500); + /************************************************************************** * MLookup Loader */ @@ -785,8 +966,6 @@ public MLoader() protected void doRun() { long startTime = System.currentTimeMillis(); - if (Ini.isClient()) - MLookupCache.loadStart (m_info); StringBuilder sql = new StringBuilder().append(m_info.Query); // IDEMPIERE 90 @@ -849,6 +1028,44 @@ protected void doRun() // Reset m_lookup.clear(); boolean isNumber = m_info.KeyColumn.endsWith("_ID"); + + String cacheKey = sql.toString(); + List knpCache = null; + List vnpCache = null; + if (isNumber) + { + knpCache = s_keyNamePairCache.get(cacheKey); + if (knpCache != null) + { + for(KeyNamePair knp : knpCache) + { + m_lookup.put(knp.getKey(), knp); + } + return; + } + else + { + knpCache = new ArrayList(); + } + } + else + { + vnpCache = s_valueNamePairCache.get(cacheKey); + if (vnpCache != null) + { + for(ValueNamePair vnp : vnpCache) + { + m_lookup.put(vnp.getValue(), vnp); + } + return; + + } + else + { + vnpCache = new ArrayList(); + } + } + m_hasInactive = false; int rows = 0; PreparedStatement pstmt = null; @@ -905,15 +1122,28 @@ protected void doRun() int key = rs.getInt(1); KeyNamePair p = new KeyNamePair(key, name.toString()); m_lookup.put(Integer.valueOf(key), p); + knpCache.add(p); } else { String value = rs.getString(2); ValueNamePair p = new ValueNamePair(value, name.toString()); m_lookup.put(value, p); + vnpCache.add(p); } // if (log.isLoggable(Level.FINE)) log.fine( m_info.KeyColumn + ": " + name); } + + if (isNumber) + { + if (knpCache.size() <= MAX_NAMEPAIR_CACHE_SIZE) + s_keyNamePairCache.put(cacheKey, knpCache); + } + else + { + if (vnpCache.size() <= MAX_NAMEPAIR_CACHE_SIZE) + s_valueNamePairCache.put(cacheKey, vnpCache); + } } catch (SQLException e) { @@ -930,9 +1160,6 @@ protected void doRun() + " - Loader complete #" + size + " - all=" + m_allLoaded + " - ms=" + String.valueOf(System.currentTimeMillis()-m_startTime) + " (" + String.valueOf(System.currentTimeMillis()-startTime) + ")"); - // if (m_allLoaded) - if (Ini.isClient()) - MLookupCache.loadEnd (m_info, m_lookup); } // run } // Loader 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 13ba381fd0..b403a068f5 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 = 20200413L; + private static final long serialVersionUID = 20200523L; /** Standard Constructor */ public X_AD_InfoColumn (Properties ctx, int AD_InfoColumn_ID, String trxName) @@ -44,6 +44,8 @@ public X_AD_InfoColumn (Properties ctx, int AD_InfoColumn_ID, String trxName) setColumnName (null); setEntityType (null); // @SQL=select get_sysconfig('DEFAULT_ENTITYTYPE','U',0,0) from dual + setIsAutocomplete (false); +// N setIsCentrallyMaintained (true); // Y setIsDisplayed (true); @@ -417,6 +419,30 @@ public String getInputFieldValidation () return (String)get_Value(COLUMNNAME_InputFieldValidation); } + /** Set Autocomplete. + @param IsAutocomplete + Automatic completion for textfields + */ + public void setIsAutocomplete (boolean IsAutocomplete) + { + set_Value (COLUMNNAME_IsAutocomplete, Boolean.valueOf(IsAutocomplete)); + } + + /** Get Autocomplete. + @return Automatic completion for textfields + */ + public boolean isAutocomplete () + { + Object oo = get_Value(COLUMNNAME_IsAutocomplete); + if (oo != null) + { + if (oo instanceof Boolean) + return ((Boolean)oo).booleanValue(); + return "Y".equals(oo); + } + return false; + } + /** Set Centrally maintained. @param IsCentrallyMaintained Information maintained in System Element table diff --git a/org.adempiere.base/src/org/compiere/model/X_AD_Process_Para.java b/org.adempiere.base/src/org/compiere/model/X_AD_Process_Para.java index 7212ecca81..e9e88eb7f7 100644 --- a/org.adempiere.base/src/org/compiere/model/X_AD_Process_Para.java +++ b/org.adempiere.base/src/org/compiere/model/X_AD_Process_Para.java @@ -30,7 +30,7 @@ public class X_AD_Process_Para extends PO implements I_AD_Process_Para, I_Persis /** * */ - private static final long serialVersionUID = 20200413L; + private static final long serialVersionUID = 20200523L; /** Standard Constructor */ public X_AD_Process_Para (Properties ctx, int AD_Process_Para_ID, String trxName) @@ -45,6 +45,8 @@ public X_AD_Process_Para (Properties ctx, int AD_Process_Para_ID, String trxName setEntityType (null); // @SQL=select get_sysconfig('DEFAULT_ENTITYTYPE','U',0,0) from dual setFieldLength (0); + setIsAutocomplete (false); +// N setIsCentrallyMaintained (true); // Y setIsEncrypted (false); @@ -401,6 +403,30 @@ public String getHelp () return (String)get_Value(COLUMNNAME_Help); } + /** Set Autocomplete. + @param IsAutocomplete + Automatic completion for textfields + */ + public void setIsAutocomplete (boolean IsAutocomplete) + { + set_Value (COLUMNNAME_IsAutocomplete, Boolean.valueOf(IsAutocomplete)); + } + + /** Get Autocomplete. + @return Automatic completion for textfields + */ + public boolean isAutocomplete () + { + Object oo = get_Value(COLUMNNAME_IsAutocomplete); + if (oo != null) + { + if (oo instanceof Boolean) + return ((Boolean)oo).booleanValue(); + return "Y".equals(oo); + } + return false; + } + /** Set Centrally maintained. @param IsCentrallyMaintained Information maintained in System Element table diff --git a/org.adempiere.base/src/org/compiere/model/X_AD_UserDef_Field.java b/org.adempiere.base/src/org/compiere/model/X_AD_UserDef_Field.java index 243df164b0..7819b45927 100644 --- a/org.adempiere.base/src/org/compiere/model/X_AD_UserDef_Field.java +++ b/org.adempiere.base/src/org/compiere/model/X_AD_UserDef_Field.java @@ -30,7 +30,7 @@ public class X_AD_UserDef_Field extends PO implements I_AD_UserDef_Field, I_Pers /** * */ - private static final long serialVersionUID = 20200413L; + private static final long serialVersionUID = 20200527L; /** Standard Constructor */ public X_AD_UserDef_Field (Properties ctx, int AD_UserDef_Field_ID, String trxName) @@ -441,6 +441,30 @@ public String getIsAlwaysUpdateable () return (String)get_Value(COLUMNNAME_IsAlwaysUpdateable); } + /** IsAutocomplete AD_Reference_ID=319 */ + public static final int ISAUTOCOMPLETE_AD_Reference_ID=319; + /** Yes = Y */ + public static final String ISAUTOCOMPLETE_Yes = "Y"; + /** No = N */ + public static final String ISAUTOCOMPLETE_No = "N"; + /** Set Autocomplete. + @param IsAutocomplete + Automatic completion for textfields + */ + public void setIsAutocomplete (String IsAutocomplete) + { + + set_Value (COLUMNNAME_IsAutocomplete, IsAutocomplete); + } + + /** Get Autocomplete. + @return Automatic completion for textfields + */ + public String getIsAutocomplete () + { + return (String)get_Value(COLUMNNAME_IsAutocomplete); + } + /** IsDisplayed AD_Reference_ID=319 */ public static final int ISDISPLAYED_AD_Reference_ID=319; /** Yes = Y */ diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/ProcessParameterPanel.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/ProcessParameterPanel.java index 27f7ec2123..e292a12663 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/ProcessParameterPanel.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/ProcessParameterPanel.java @@ -209,7 +209,7 @@ public boolean init() { + "p.FieldLength, p.IsMandatory, p.IsRange, p.ColumnName, " + "p.DefaultValue, p.DefaultValue2, p.VFormat, p.ValueMin, p.ValueMax, " + "p.SeqNo, p.AD_Reference_Value_ID, vr.Code AS ValidationCode, " - + "p.ReadOnlyLogic, p.DisplayLogic, p.IsEncrypted, NULL AS FormatPattern, p.MandatoryLogic, p.Placeholder, p.Placeholder2 " + + "p.ReadOnlyLogic, p.DisplayLogic, p.IsEncrypted, NULL AS FormatPattern, p.MandatoryLogic, p.Placeholder, p.Placeholder2, p.isAutoComplete " + "FROM AD_Process_Para p" + " LEFT OUTER JOIN AD_Val_Rule vr ON (p.AD_Val_Rule_ID=vr.AD_Val_Rule_ID) " + "WHERE p.AD_Process_ID=?" // 1 @@ -220,7 +220,7 @@ public boolean init() { + "p.FieldLength, p.IsMandatory, p.IsRange, p.ColumnName, " + "p.DefaultValue, p.DefaultValue2, p.VFormat, p.ValueMin, p.ValueMax, " + "p.SeqNo, p.AD_Reference_Value_ID, vr.Code AS ValidationCode, " - + "p.ReadOnlyLogic, p.DisplayLogic, p.IsEncrypted, NULL AS FormatPattern,p.MandatoryLogic, t.Placeholder, t.Placeholder2 " + + "p.ReadOnlyLogic, p.DisplayLogic, p.IsEncrypted, NULL AS FormatPattern,p.MandatoryLogic, t.Placeholder, t.Placeholder2, p.isAutoComplete " + "FROM AD_Process_Para p" + " INNER JOIN AD_Process_Para_Trl t ON (p.AD_Process_Para_ID=t.AD_Process_Para_ID)" + " LEFT OUTER JOIN AD_Val_Rule vr ON (p.AD_Val_Rule_ID=vr.AD_Val_Rule_ID) " diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/component/ComboEditorBox.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/component/ComboEditorBox.java new file mode 100644 index 0000000000..4e1474899d --- /dev/null +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/component/ComboEditorBox.java @@ -0,0 +1,192 @@ +/*********************************************************************** + * 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. * + * * + * Contributors: * + * - hengsin * + **********************************************************************/ +package org.adempiere.webui.component; + +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; + +import org.adempiere.webui.LayoutUtils; +import org.adempiere.webui.util.ZKUpdateUtil; +import org.compiere.util.ValueNamePair; +import org.zkoss.zk.ui.event.EventListener; +import org.zkoss.zk.ui.event.Events; +import org.zkoss.zul.Comboitem; +import org.zkoss.zul.ComboitemRenderer; +import org.zkoss.zul.Div; + +/** + * @author Low Heng Sin + */ +public class ComboEditorBox extends Div { + /** + * + */ + private static final long serialVersionUID = 4187563277424346012L; + protected PropertyChangeSupport m_propertyChangeListeners = new PropertyChangeSupport( + this); + protected Combobox txt; + protected Button btn; + + public ComboEditorBox() { + initComponents(); + } + + /** + * @param text + */ + public ComboEditorBox(String text) { + initComponents(); + setText(text); + } + + /** + * @param imageSrc + */ + public void setButtonImage(String imageSrc) { + btn.setImage(imageSrc); + } + + private void initComponents() { + txt = new Combobox(); + txt.setButtonVisible(false); + txt.setSclass("editor-input"); + txt.setAutocomplete(true); + txt.setAutodrop(true); + txt.setSubmitByEnter(true); + txt.setInstantSelect(false); + ZKUpdateUtil.setHflex(txt, "0"); + appendChild(txt); + btn = new Button(); + btn.setTabindex(-1); + ZKUpdateUtil.setHflex(btn, "0"); + btn.setSclass("editor-button"); + appendChild(btn); + + LayoutUtils.addSclass("editor-box", this); + setTableEditorMode(false); + + txt.setItemRenderer(new ComboitemRenderer() { + public void render(Comboitem item, ValueNamePair data, int index){ + item.setValue(data); + item.setLabel(data.getName()); + } + }); + } + + /** + * @return combobox component + */ + public Combobox getCombobox() { + return txt; + } + + /** + * @param value + */ + public void setText(String value) { + txt.setText(value); + } + + /** + * @return text + */ + public String getText() { + return txt.getText(); + } + + /** + * @param enabled + */ + public void setEnabled(boolean enabled) { + txt.setReadonly(!enabled); + btn.setEnabled(enabled); + btn.setVisible(enabled); + if (enabled) { + if (btn.getParent() != txt.getParent()) + btn.setParent(txt.getParent()); + } else { + if (btn.getParent() != null) + btn.detach(); + } + if (enabled) { + LayoutUtils.removeSclass("editor-input-disd", txt); + } else { + LayoutUtils.addSclass("editor-input-disd", txt); + } + } + + /** + * @return boolean + */ + public boolean isEnabled() { + return btn.isEnabled(); + } + + /** + * @param evtnm + * @param listener + */ + public boolean addEventListener(String evtnm, EventListener listener) { + if (Events.ON_CLICK.equals(evtnm)) { + return btn.addEventListener(evtnm, listener); + } else { + return txt.addEventListener(evtnm, listener); + } + } + + /** + * @param l + */ + public synchronized void addPropertyChangeListener(PropertyChangeListener l) { + m_propertyChangeListeners.addPropertyChangeListener(l); + } + + /** + * @param tooltiptext + */ + public void setToolTipText(String tooltiptext) { + txt.setTooltiptext(tooltiptext); + } + + /** + * @return Button + */ + public Button getButton() { + return btn; + } + + public void setTableEditorMode(boolean flag) { + if (flag) { + ZKUpdateUtil.setHflex(this, "0"); + LayoutUtils.addSclass("grid-editor-input", txt); + LayoutUtils.addSclass("grid-editor-button", btn); + } else { + ZKUpdateUtil.setHflex(this, "1"); + LayoutUtils.removeSclass("grid-editor-input", txt); + LayoutUtils.removeSclass("grid-editor-button", btn); + } + + } +} diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/editor/InfoListSubModel.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/editor/InfoListSubModel.java new file mode 100644 index 0000000000..fab03cc3be --- /dev/null +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/editor/InfoListSubModel.java @@ -0,0 +1,122 @@ +/*********************************************************************** + * 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. * + * * + * Contributors: * + * - hengsin * + **********************************************************************/ +package org.adempiere.webui.editor; + +import java.util.ArrayList; +import java.util.List; + +import org.adempiere.webui.factory.InfoManager; +import org.adempiere.webui.panel.InfoPanel; +import org.compiere.model.GridField; +import org.compiere.model.Lookup; +import org.compiere.util.NamePair; +import org.compiere.util.Util; +import org.compiere.util.ValueNamePair; +import org.zkoss.zul.ListModel; +import org.zkoss.zul.ListModelList; +import org.zkoss.zul.ListSubModel; + +/** + * + * @author hengsin + * + */ +public class InfoListSubModel implements ListSubModel { + + private Lookup lookup; + private GridField gridField; + private String tableName; + private String keyColumnName; + private String whereClause; + + /** + * + * @param lookup + * @param gridField + * @param tableName + * @param keyColumnName + */ + public InfoListSubModel(Lookup lookup, GridField gridField, String tableName, String keyColumnName) { + this.lookup = lookup; + this.gridField = gridField; + this.tableName = tableName; + this.keyColumnName = keyColumnName; + } + + /** + * + * @param whereClause + */ + public void setWhereClause(String whereClause) { + this.whereClause = whereClause; + } + + /** + * + * @return where clause + */ + public String getWhereClause() { + return this.whereClause; + } + + @Override + public ListModel getSubModel(Object value, int nRows) { + ListModelList model = new ListModelList<>(); + if (value != null && !Util.isEmpty(value.toString(), true)) { + String queryText = value.toString().trim(); + + final InfoPanel ip = InfoManager.create(lookup, gridField, tableName, keyColumnName, queryText, false, getWhereClause()); + if (ip != null && ip.loadedOK()) { + int rowCount = ip.getRowCount(); + if (rowCount > 0) { + List added = new ArrayList(); + List keys = new ArrayList(); + for(int i = 0; i < rowCount; i++) { + Integer key = ip.getRowKeyAt(i); + if (key != null && key.intValue() > 0) { + keys.add(key); + } + if (nRows > 0 && keys.size() >= nRows) + break; + } + NamePair[] namePairs = lookup.getDirect(keys.toArray()); + for(NamePair np : namePairs) { + String name = np.getName(); + if (added.contains(name)) + continue; + else + added.add(name); + ValueNamePair pair = new ValueNamePair(np.getID(), name); + model.add(pair); + if (nRows > 0 && added.size() >= nRows) + break; + } + } + } + } + return model; + } + +} diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/editor/WChosenboxSearchEditor.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/editor/WChosenboxSearchEditor.java index be6c566c29..f380ec15b4 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/editor/WChosenboxSearchEditor.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/editor/WChosenboxSearchEditor.java @@ -14,12 +14,7 @@ package org.adempiere.webui.editor; import java.beans.PropertyChangeEvent; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.ArrayList; import java.util.LinkedHashSet; -import java.util.List; import java.util.Properties; import java.util.Set; import java.util.logging.Level; @@ -40,11 +35,11 @@ import org.adempiere.webui.window.WFieldRecordInfo; import org.compiere.model.GridField; import org.compiere.model.Lookup; +import org.compiere.model.MColumn; import org.compiere.model.MLookup; -import org.compiere.model.MRole; +import org.compiere.model.MTable; import org.compiere.model.X_AD_CtxHelp; import org.compiere.util.CLogger; -import org.compiere.util.DB; import org.compiere.util.Env; import org.compiere.util.Util; import org.compiere.util.ValueNamePair; @@ -71,9 +66,11 @@ public class WChosenboxSearchEditor extends WEditor implements ContextMenuListen private String value; private InfoPanel infoPanel = null; private String imageUrl; - private MyListModel model = new MyListModel(); + private MyListModel model = new MyListModel(); + private InfoListSubModel subModel = null; private static final CLogger log = CLogger.getCLogger(WChosenboxSearchEditor.class); + private static final int MAX_AUTO_COMPLETE_ROWS = 50; private boolean onselecting; public WChosenboxSearchEditor (GridField gridField) @@ -170,6 +167,9 @@ else if ("M_Product_ID".equals(mlookup.getLookupInfo().KeyColumn)) } popupMenu = new WEditorPopupMenu(false, true, isShowPreference(), false, false, false, lookup); getComponent().getButton().setImage(imageUrl); + + setTableAndKeyColumn(); + subModel = new InfoListSubModel(lookup, gridField, m_tableName, m_keyColumnName); getComponent().getChosenbox().setModel(model); addChangeLogMenu(popupMenu); @@ -422,7 +422,7 @@ private void actionButton() String whereClause = getWhereClause(); if (m_tableName == null) // sets table name & key column - getDirectAccessSQL("*"); + setTableAndKeyColumn(); final InfoPanel ip = InfoManager.create(lookup, gridField, m_tableName, m_keyColumnName, null, false, whereClause); if (ip != null) @@ -466,228 +466,37 @@ public void onEvent(Event event) throws Exception { } /** - * Generate Access SQL for Search. - * The SQL returns the ID of the value entered - * Also sets m_tableName and m_keyColumnName - * @param text uppercase text for LIKE comparison - * @return sql or "" - * Example - * SELECT C_Payment_ID FROM C_Payment WHERE UPPER(DocumentNo) LIKE x OR ... + * Sets m_tableName and m_keyColumnName */ - private String getDirectAccessSQL (String text) - { - String m_columnName = getColumnName(); - - StringBuffer sql = new StringBuffer(); - m_tableName = m_columnName.substring(0, m_columnName.length()-3); - m_keyColumnName = m_columnName; - - if (m_columnName.equals("M_Product_ID")) - { + private void setTableAndKeyColumn() { + if (lookup != null && lookup instanceof MLookup) { + // foreign table defined in lookup + m_keyColumnName = ((MLookup)lookup).getColumnName(); + if (m_keyColumnName.contains(".")) { + m_tableName = m_keyColumnName.substring(0, m_keyColumnName.indexOf(".")); + m_keyColumnName = m_keyColumnName.substring(m_keyColumnName.indexOf(".")+1); + } else { + m_tableName = m_keyColumnName.substring(0, m_keyColumnName.length()-3); + } + } else if (getGridField() != null && getGridField().getGridTab() != null && getGridField().getAD_Column_ID() > 0) { + // field - this search editor comes from a window, when it comes from process parameter it doesn't have a gridtab + MColumn column = MColumn.get(Env.getCtx(), getGridField().getAD_Column_ID()); + m_tableName = column.getReferenceTableName(); + MTable table = MTable.get(Env.getCtx(), m_tableName); + m_keyColumnName = table.getKeyColumns()[0]; + } else { + // no field - the search editor is defined programatically + m_keyColumnName = getColumnName(); + m_tableName = m_keyColumnName.substring(0, m_keyColumnName.length()-3); + } + if (m_keyColumnName.equals("M_Product_ID")) { // Reset Env.setContext(Env.getCtx(), lookup.getWindowNo(), Env.TAB_INFO, "M_Product_ID", "0"); Env.setContext(Env.getCtx(), lookup.getWindowNo(), Env.TAB_INFO, "M_AttributeSetInstance_ID", "0"); Env.setContext(Env.getCtx(), lookup.getWindowNo(), Env.TAB_INFO, "M_Locator_ID", "0"); - - sql.append("SELECT M_Product_ID FROM M_Product WHERE (UPPER(Value) LIKE ") - .append(DB.TO_STRING(text)) - .append(" OR UPPER(Name) LIKE ").append(DB.TO_STRING(text)) - .append(" OR UPC LIKE ").append(DB.TO_STRING(text)).append(")"); - } - else if (m_columnName.equals("C_BPartner_ID")) - { - sql.append("SELECT C_BPartner_ID FROM C_BPartner WHERE (UPPER(Value) LIKE ") - .append(DB.TO_STRING(text)) - .append(" OR UPPER(Name) LIKE ").append(DB.TO_STRING(text)).append(")"); - } - else if (m_columnName.equals("C_Order_ID")) - { - sql.append("SELECT C_Order_ID FROM C_Order WHERE UPPER(DocumentNo) LIKE ") - .append(DB.TO_STRING(text)); - } - else if (m_columnName.equals("C_Invoice_ID")) - { - sql.append("SELECT C_Invoice_ID FROM C_Invoice WHERE UPPER(DocumentNo) LIKE ") - .append(DB.TO_STRING(text)); - } - else if (m_columnName.equals("M_InOut_ID")) - { - sql.append("SELECT M_InOut_ID FROM M_InOut WHERE UPPER(DocumentNo) LIKE ") - .append(DB.TO_STRING(text)); - } - else if (m_columnName.equals("C_Payment_ID")) - { - sql.append("SELECT C_Payment_ID FROM C_Payment WHERE UPPER(DocumentNo) LIKE ") - .append(DB.TO_STRING(text)); - } - else if (m_columnName.equals("GL_JournalBatch_ID")) - { - sql.append("SELECT GL_JournalBatch_ID FROM GL_JournalBatch WHERE UPPER(DocumentNo) LIKE ") - .append(DB.TO_STRING(text)); - } - else if (m_columnName.equals("SalesRep_ID")) - { - sql.append("SELECT AD_User_ID FROM AD_User WHERE UPPER(Name) LIKE ") - .append(DB.TO_STRING(text)); - - m_tableName = "AD_User"; - m_keyColumnName = "AD_User_ID"; - } - - // Predefined - - if (sql.length() > 0) - { - String wc = getWhereClause(); - - if (wc != null && wc.length() > 0) - sql.append(" AND ").append(wc); - - sql.append(" AND IsActive='Y'"); - // *** - - if (log.isLoggable(Level.FINEST)) log.finest(m_columnName + " (predefined) " + sql.toString()); - - return MRole.getDefault().addAccessSQL(sql.toString(), - m_tableName, MRole.SQL_NOTQUALIFIED, MRole.SQL_RO); - } - - // Check if it is a Table Reference - - if (lookup != null && lookup instanceof MLookup) - { - int AD_Reference_ID = ((MLookup)lookup).getAD_Reference_Value_ID(); - - if (AD_Reference_ID != 0) - { - boolean isValueDisplayed = false; - String query = "SELECT kc.ColumnName, dc.ColumnName, t.TableName, rt.IsValueDisplayed " - + "FROM AD_Ref_Table rt" - + " INNER JOIN AD_Column kc ON (rt.AD_Key=kc.AD_Column_ID)" - + " INNER JOIN AD_Column dc ON (rt.AD_Display=dc.AD_Column_ID)" - + " INNER JOIN AD_Table t ON (rt.AD_Table_ID=t.AD_Table_ID) " - + "WHERE rt.AD_Reference_ID=?"; - - String displayColumnName = null; - PreparedStatement pstmt = null; - ResultSet rs = null; - - try - { - pstmt = DB.prepareStatement(query, null); - pstmt.setInt(1, AD_Reference_ID); - rs = pstmt.executeQuery(); - - if (rs.next()) - { - m_keyColumnName = rs.getString(1); - displayColumnName = rs.getString(2); - m_tableName = rs.getString(3); - String t = rs.getString(4); - isValueDisplayed = "Y".equalsIgnoreCase(t); - } - } - catch (Exception e) - { - log.log(Level.SEVERE, query, e); - } - finally - { - DB.close(rs, pstmt); - } - - - if (displayColumnName != null) - { - sql = new StringBuffer(); - sql.append("SELECT ").append(m_keyColumnName) - .append(" FROM ").append(m_tableName) - .append(" WHERE (UPPER(").append(displayColumnName) - .append(") LIKE ").append(DB.TO_STRING(text)); - if (isValueDisplayed) - { - sql.append(" OR UPPER(").append("Value") - .append(") LIKE ").append(DB.TO_STRING(text)); - } - sql.append(")"); - sql.append(" AND IsActive='Y'"); - - String wc = getWhereClause(); - - if (wc != null && wc.length() > 0) - sql.append(" AND ").append(wc); - - // *** - - if (log.isLoggable(Level.FINEST)) log.finest(m_columnName + " (Table) " + sql.toString()); - - return MRole.getDefault().addAccessSQL(sql.toString(), - m_tableName, MRole.SQL_NOTQUALIFIED, MRole.SQL_RO); - } - } // Table Reference - } // MLookup - - /** Check Well Known Columns of Table - assumes TableDir **/ - - String query = "SELECT t.TableName, c.ColumnName " - + "FROM AD_Column c " - + " INNER JOIN AD_Table t ON (c.AD_Table_ID=t.AD_Table_ID AND t.IsView='N') " - + "WHERE (c.ColumnName IN ('DocumentNo', 'Value', 'Name') OR c.IsIdentifier='Y')" - + " AND c.AD_Reference_ID IN (10,14)" - + " AND EXISTS (SELECT * FROM AD_Column cc WHERE cc.AD_Table_ID=t.AD_Table_ID" - + " AND cc.IsKey='Y' AND cc.ColumnName=?)"; - - m_keyColumnName = m_columnName; - sql = new StringBuffer(); - PreparedStatement pstmt = null; - ResultSet rs = null; - try - { - pstmt = DB.prepareStatement(query, null); - pstmt.setString(1, m_keyColumnName); - rs = pstmt.executeQuery(); - - while (rs.next()) - { - if (sql.length() != 0) - sql.append(" OR "); - - m_tableName = rs.getString(1); - sql.append("UPPER(").append(rs.getString(2)).append(") LIKE ").append(DB.TO_STRING(text)); - } - } - catch (SQLException ex) - { - log.log(Level.SEVERE, query, ex); - } - finally - { - DB.close(rs, pstmt); - rs = null; - pstmt = null; - } - // - if (sql.length() == 0) - { - log.log(Level.SEVERE, m_columnName + " (TableDir) - no standard/identifier columns"); - return ""; } - // - StringBuffer retValue = new StringBuffer ("SELECT ") - .append(m_columnName).append(" FROM ").append(m_tableName) - .append(" WHERE ").append(sql) - .append(" AND IsActive='Y'"); - - String wc = getWhereClause(); - - if (wc != null && wc.length() > 0) - retValue.append(" AND ").append(wc); - // *** - if (log.isLoggable(Level.FINEST)) log.finest(m_columnName + " (TableDir) " + sql.toString()); - return MRole.getDefault().addAccessSQL(retValue.toString(), - m_tableName, MRole.SQL_NOTQUALIFIED, MRole.SQL_RO); } - + private String getWhereClause() { String whereClause = ""; @@ -756,35 +565,8 @@ private class MyListModel extends ListModelList implements ListSu @Override public ListModel getSubModel(Object value, int nRows) { - ListModelList model = new ListModelList<>(); - if (value != null && !Util.isEmpty(value.toString(), true)) { - String queryText = value.toString().trim(); - - if (m_tableName == null) // sets table name & key column - getDirectAccessSQL("*"); - - final InfoPanel ip = InfoManager.create(lookup, gridField, m_tableName, m_keyColumnName, queryText, false, getWhereClause()); - if (ip != null && ip.loadedOK()) { - int rowCount = ip.getRowCount(); - if (rowCount > 0) { - List added = new ArrayList(); - for(int i = 0; i < rowCount; i++) { - Integer key = ip.getRowKeyAt(i); - if (key != null && key.intValue() > 0) { - String name = getLookup().getDisplay(key); - if (added.contains(name)) - continue; - else - added.add(name); - ValueNamePair pair = new ValueNamePair(key.toString(), name); - model.add(pair); - if (added.size() == 50) - break; - } - } - } - } - } + subModel.setWhereClause(getWhereClause()); + ListModel model = subModel.getSubModel(value, MAX_AUTO_COMPLETE_ROWS); getComponent().getChosenbox().setSubListModel(model); return model; } diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/editor/WSearchEditor.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/editor/WSearchEditor.java index c000a36dae..7e9e29e775 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/editor/WSearchEditor.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/editor/WSearchEditor.java @@ -31,6 +31,7 @@ import org.adempiere.webui.adwindow.ADWindowContent; import org.adempiere.webui.adwindow.QuickGridTabRowRenderer; import org.adempiere.webui.apps.AEnv; +import org.adempiere.webui.component.ComboEditorBox; import org.adempiere.webui.component.Searchbox; import org.adempiere.webui.event.ContextMenuEvent; import org.adempiere.webui.event.ContextMenuListener; @@ -55,6 +56,7 @@ import org.compiere.util.CLogger; import org.compiere.util.DisplayType; import org.compiere.util.Env; +import org.compiere.util.Util; import org.zkoss.zk.au.out.AuScript; import org.zkoss.zk.ui.Component; import org.zkoss.zk.ui.Executions; @@ -62,6 +64,7 @@ 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.InputEvent; import org.zkoss.zk.ui.util.Clients; /** @@ -73,6 +76,8 @@ */ public class WSearchEditor extends WEditor implements ContextMenuListener, ValueChangeListener, IZoomableEditor { + private static final int MAX_AUTO_COMPLETE_ROWS = 50; + private static final int AUTO_COMPLETE_QUERY_TIMEOUT = 1; //1 second private static final String[] LISTENER_EVENTS = {Events.ON_CLICK, Events.ON_CHANGE, Events.ON_OK}; public static final String ATTRIBUTE_IS_INFO_PANEL_OPEN = "ATTRIBUTE_IS_INFO_PANEL_OPEN"; private Lookup lookup; @@ -82,6 +87,7 @@ public class WSearchEditor extends WEditor implements ContextMenuListener, Value private Object value; private InfoPanel infoPanel = null; private String imageUrl; + private InfoListSubModel listModel = null; private static final CLogger log = CLogger.getCLogger(WSearchEditor.class); @@ -104,8 +110,8 @@ public WSearchEditor (GridField gridField) @Override - public Searchbox getComponent() { - return (Searchbox) super.getComponent(); + public ComboEditorBox getComponent() { + return (ComboEditorBox) super.getComponent(); } @Override @@ -201,7 +207,35 @@ else if (columnName.equals("M_Product_ID")) addChangeLogMenu(popupMenu); if (gridField != null) - getComponent().getTextbox().setPlaceholder(gridField.getPlaceholder()); + getComponent().getCombobox().setPlaceholder(gridField.getPlaceholder()); + + if (gridField != null && gridField.isAutocomplete()) { + setTableAndKeyColumn(); + listModel = new InfoListSubModel(lookup, gridField, m_tableName, m_keyColumnName); + getComponent().getCombobox().setModel(listModel.getSubModel(null, MAX_AUTO_COMPLETE_ROWS)); + + getComponent().getCombobox().addEventListener(Events.ON_CHANGING, (EventListener)(e) -> { + if (!e.isChangingBySelectBack()) { + listModel.setWhereClause(getWhereClause()); + String s = e.getValue(); + if (!Util.isEmpty(s, true)) { + StringBuilder query = new StringBuilder(s); + query.append("?autocomplete={"); + query.append("timeout:") + .append(AUTO_COMPLETE_QUERY_TIMEOUT) + .append(",") + .append("pagesize:") + .append(MAX_AUTO_COMPLETE_ROWS); + query.append("}"); + s = query.toString(); + } + getComponent().getCombobox().setModel(listModel.getSubModel(s, MAX_AUTO_COMPLETE_ROWS)); + } + }); + } else { + getComponent().getCombobox().setAutodrop(false); + } + return; } @@ -401,7 +435,7 @@ protected void actionText(String text) focusNext(); //safety check: if focus is going no where, focus back to self - String uid = getComponent().getTextbox().getUuid(); + String uid = getComponent().getCombobox().getUuid(); String script = "setTimeout(function(){try{var e = zk.Widget.$('#" + uid + "').$n(); if (jq(':focus').size() == 0) e.focus();} catch(error){}}, 100);"; Clients.response(new AuScript(script)); @@ -614,12 +648,12 @@ else if (cancelled) getComponent().setText(""); actionCombo(null); } - getComponent().getTextbox().focus(); + getComponent().getCombobox().focus(); } else { if (log.isLoggable(Level.CONFIG)) log.config(getColumnName() + " - Result = null (not cancelled)"); - getComponent().getTextbox().focus(); + getComponent().getCombobox().focus(); getComponent().setAttribute(ATTRIBUTE_IS_INFO_PANEL_OPEN, false); } } @@ -780,7 +814,7 @@ public void dynamicDisplay(Properties ctx) { } - static class CustomSearchBox extends Searchbox { + static class CustomSearchBox extends ComboEditorBox { /** * generated serial id @@ -794,15 +828,15 @@ public void onPageAttached(Page newpage, Page oldpage) { String w = "try{var btn=jq('#'+this.parent.uuid+' @button').zk.$();}catch(err){}"; if (ThemeManager.isUseFontIconForImage()) { String sclass = "z-icon-spinner z-icon-spin"; - getTextbox().setWidgetListener("onChange", "try{"+w+"btn.setIconSclass('" + sclass + "');" + getCombobox().setWidgetListener("onChange", "try{"+w+"btn.setIconSclass('" + sclass + "');" + "btn.setDisabled(true, {adbs: false, skip: false});}catch(err){}"); } else { - getTextbox().setWidgetListener("onChange", "try{"+w+"btn.setImage(\"" + getCombobox().setWidgetListener("onChange", "try{"+w+"btn.setImage(\"" + Executions.getCurrent().encodeURL(IN_PROGRESS_IMAGE)+"\");" + "btn.setDisabled(true, {adbs: false, skip: false});}catch(err){}"); } } } - } + } } 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 0a4ef1afbe..927c5ba915 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 @@ -147,7 +147,7 @@ public class InfoWindow extends InfoPanel implements ValueChangeListener, EventL protected ColumnInfo[] columnInfos; protected TableInfo[] tableInfos; protected MInfoColumn[] infoColumns; - protected String queryValue; + protected WQuickEntry vqe; private List gridFields; @@ -206,9 +206,8 @@ public InfoWindow(int WindowNo, String tableName, String keyColumn, String query public InfoWindow(int WindowNo, String tableName, String keyColumn, String queryValue, boolean multipleSelection, String whereClause, int AD_InfoWindow_ID, boolean lookup, GridField field) { super(WindowNo, tableName, keyColumn, multipleSelection, whereClause, - lookup, AD_InfoWindow_ID); + lookup, AD_InfoWindow_ID, queryValue); this.m_gridfield = field; - this.queryValue = queryValue; //Xolali IDEMPIERE-1045 contentPanel.addActionListener(new EventListener() { @@ -242,8 +241,10 @@ public void onEvent(Event event) throws Exception { if (haveProcess) p_multipleSelection = true; } - - loadInfoRelatedTabs(); + + if (!isAutoComplete) + loadInfoRelatedTabs(); + if (loadedOK()) { if (isLookup()) { Env.clearTabContext(Env.getCtx(), p_WindowNo, Env.TAB_INFO); @@ -263,8 +264,8 @@ public void onEvent(Event event) throws Exception { } // F3P: add export button - - initExport(); + if (!isAutoComplete) + initExport(); } /** @@ -505,6 +506,9 @@ protected void processQueryValue() { isQueryByUser = true; for (int i = 0; i < identifiers.size(); i++) { WEditor editor = identifiers.get(i); + if (isAutoComplete && i > 0) { + break; + } try{ editor.setValue(queryValue); }catch(Exception ex){ @@ -520,17 +524,19 @@ protected void processQueryValue() { } boolean splitValue = false; - if (m_count <= 0) { - String separator = MSysConfig.getValue(MSysConfig.IDENTIFIER_SEPARATOR, "_", Env.getAD_Client_ID(Env.getCtx())); - String[] values = queryValue.split("[" + separator.trim()+"]"); - if (values.length == 2) { - splitValue = true; - for(int i = 0; i < values.length && i < identifiers.size(); i++) { - WEditor editor = identifiers.get(i); - editor.setValue(values[i].trim()); - } - testCount(false); - } + if (!isAutoComplete) { + if (m_count <= 0) { + String separator = MSysConfig.getValue(MSysConfig.IDENTIFIER_SEPARATOR, "_", Env.getAD_Client_ID(Env.getCtx())); + String[] values = queryValue.split("[" + separator.trim()+"]"); + if (values.length == 2) { + splitValue = true; + for(int i = 0; i < values.length && i < identifiers.size(); i++) { + WEditor editor = identifiers.get(i); + editor.setValue(values[i].trim()); + } + testCount(false); + } + } } if (m_count > 0) { @@ -605,6 +611,7 @@ protected boolean loadInfoDefinition() { String help = infoColumn.get_Translation("Help"); vo.Help = help != null ? help : ""; vo.AD_FieldStyle_ID = infoColumn.getAD_FieldStyle_ID(); + vo.IsAutocomplete = infoColumn.isAutocomplete(); GridField gridField = new GridField(vo); gridFields.add(gridField); } @@ -1445,10 +1452,12 @@ protected void layoutParameterGrid(boolean update) { row.appendChild(checkAND); } } - evalDisplayLogic(); + if (!isAutoComplete) + evalDisplayLogic(); if (!update) initParameters(); - dynamicDisplay(null); + if (!isAutoComplete) + dynamicDisplay(null); } protected void evalDisplayLogic() { @@ -1927,6 +1936,8 @@ protected boolean testCount(boolean promptError) try { pstmt = DB.prepareStatement(countSql, null); + if (queryTimeout > 0) + pstmt.setQueryTimeout(queryTimeout); setParameters (pstmt, true); rs = pstmt.executeQuery(); @@ -2122,6 +2133,9 @@ protected boolean validateField (WEditor wEditor){ @Override protected boolean hasNew() { + if (isAutoComplete) + return false; + boolean hasNew = getADWindowID () > 0; if (hasNew && vqe == null && hasRightQuickEntry){ GridWindow gridwindow = GridWindow.get(Env.getCtx(), 0, getADWindowID()); diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/panel/InfoGeneralPanel.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/panel/InfoGeneralPanel.java index f9bc142498..cfcec5066b 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/panel/InfoGeneralPanel.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/panel/InfoGeneralPanel.java @@ -101,7 +101,7 @@ public InfoGeneralPanel(String queryValue, int windowNo,String tableName,String public InfoGeneralPanel(String queryValue, int windowNo,String tableName,String keyColumn, boolean isSOTrx, String whereClause, boolean lookup) { - super(windowNo, tableName, keyColumn, false,whereClause, lookup); + super(windowNo, tableName, keyColumn, false, whereClause, lookup, 0, queryValue); setTitle(Msg.getMsg(Env.getCtx(), "Info")); @@ -112,26 +112,7 @@ public InfoGeneralPanel(String queryValue, int windowNo,String tableName,String p_loadedOK = initInfo (); - if (queryValue != null && queryValue.length() > 0) - { - Textbox[] txts = new Textbox[] {txt1, txt2, txt3, txt4}; - for(Textbox t : txts) - { - if (t != null && t.isVisible()) - { - t.setValue(queryValue); - testCount(); - if (m_count <= 0) - t.setValue(null); - else - break; - } - } - if (m_count <= 0) - { - txt1.setValue(queryValue); - } - } + processQueryValue(); } catch (Exception e) { @@ -146,22 +127,25 @@ public InfoGeneralPanel(String queryValue, int windowNo,String tableName,String if (queryValue != null && queryValue.length() > 0) { - MTable table = MTable.get(Env.getCtx(), p_tableName); - if ( table.getIdentifierColumns().length > 1 - && !p_tableName.startsWith("AD_")) // 32 AD tables with identifiers containing _ + if (!isAutoComplete) { - String separator = I_C_ElementValue.Table_Name.equalsIgnoreCase(p_tableName) ? "-" : "_"; - if (txt2.isVisible()) + MTable table = MTable.get(Env.getCtx(), p_tableName); + if ( table.getIdentifierColumns().length > 1 + && !p_tableName.startsWith("AD_")) // 32 AD tables with identifiers containing _ { - String[] values = queryValue.split("["+separator+"]"); - if (values != null && values.length == 2) + String separator = I_C_ElementValue.Table_Name.equalsIgnoreCase(p_tableName) ? "-" : "_"; + if (txt2.isVisible()) { - txt1.setValue(values[0]); - txt2.setValue(values[1]); + String[] values = queryValue.split("["+separator+"]"); + if (values != null && values.length == 2) + { + txt1.setValue(values[0]); + txt2.setValue(values[1]); + } } - } - - } + + } + } executeQuery(); renderItems(); @@ -172,6 +156,32 @@ public InfoGeneralPanel(String queryValue, int windowNo,String tableName,String } } + private void processQueryValue() { + if (queryValue != null && queryValue.length() > 0) + { + Textbox[] txts = new Textbox[] {txt1, txt2, txt3, txt4}; + for(Textbox t : txts) + { + if (t != null && t.isVisible()) + { + t.setValue(queryValue); + testCount(); + if (m_count <= 0) + t.setValue(null); + else + break; + } + + if (isAutoComplete) + break; + } + if (m_count <= 0) + { + txt1.setValue(queryValue); + } + } + } + private void initComponents() { Grid grid = GridFactory.newGridLayout(); 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 9989d45ae1..4b9a9510e6 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 @@ -87,6 +87,7 @@ 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.zk.au.out.AuEcho; import org.zkoss.zk.ui.Page; @@ -221,17 +222,26 @@ protected InfoPanel (int WindowNo, lookup, 0); } + protected InfoPanel (int WindowNo, + String tableName, String keyColumn,boolean multipleSelection, + String whereClause, boolean lookup, int ADInfoWindowID) + { + this(WindowNo, tableName, keyColumn, multipleSelection, + whereClause, lookup, ADInfoWindowID, null); + } + /************************************************** * Detail Constructor * @param WindowNo WindowNo * @param tableName tableName * @param keyColumn keyColumn * @param whereClause whereClause + * @param queryValue */ protected InfoPanel (int WindowNo, String tableName, String keyColumn,boolean multipleSelection, - String whereClause, boolean lookup, int ADInfoWindowID) - { + String whereClause, boolean lookup, int ADInfoWindowID, String queryValue) + { if (WindowNo <= 0) { p_WindowNo = SessionManager.getAppDesktop().registerWindow(this); } else { @@ -243,6 +253,12 @@ protected InfoPanel (int WindowNo, this.m_infoWindowID = ADInfoWindowID; p_keyColumn = keyColumn; + this.queryValue = queryValue; + if (queryValue != null && queryValue.trim().length() > 0) + { + parseQueryValue(); + } + p_multipleSelection = multipleSelection; m_lookup = lookup; loadInfoWindowData(); @@ -275,6 +291,38 @@ public void onSelect() { } // InfoPanel + protected void parseQueryValue() { + if (Util.isEmpty(queryValue, true)) + return; + + int start = queryValue.indexOf("?autocomplete={"); + if (start > 0 && queryValue.endsWith("}")) { + this.isAutoComplete = true; + this.numPagePreLoad = 1; + String optionInput = queryValue.substring(start+"?autocomplete={".length(), queryValue.length()-1); + queryValue = queryValue.substring(0, start); + String[] options = optionInput.split("[,]"); + for(String option : options) { + String[] pair = option.trim().split("[:]"); + if (pair.length != 2) + continue; + if (pair[0].equalsIgnoreCase("timeout")) { + try { + int t = Integer.parseInt(pair[1]); + if (t > 0) + this.queryTimeout = t; + } catch (Exception e) {} + } else if (pair[0].equalsIgnoreCase("pagesize")) { + try { + int t = Integer.parseInt(pair[1]); + if (t > 0) + this.pageSize = t; + } catch (Exception e) {} + } + } + } + } + private void init() { if (isLookup()) @@ -409,6 +457,13 @@ private void init() * IDEMPIERE-1979 */ protected boolean isQueryByUser = false; + + protected boolean isAutoComplete = false; + + protected int queryTimeout = 0; + + protected String queryValue; + /** * save where clause of prev requery */ @@ -892,6 +947,8 @@ private List readLine(int start, int end) { trx = Trx.get(trxName, true); trx.setDisplayName(getClass().getName()+"_readLine"); m_pstmt = DB.prepareStatement(dataSql, trxName); + if (queryTimeout > 0) + m_pstmt.setQueryTimeout(queryTimeout); setParameters (m_pstmt, false); // no count if (log.isLoggable(Level.FINE)) log.fine("Start query - " + (System.currentTimeMillis()-startTime) + "ms");