IDEMPIERE-5567 Support of UUID as Key (FHCA-4195) - ChangeLog - FKRules (#1852)

* IDEMPIERE-5567 Support of UUID as Key (FHCA-4195) - ChangeLog - FKRules

* - change warning by IllegalArgumentException
- change package visibility to protected

* - remove unnecessary code - the Model Cascade is calling the deleteModelCascade for children too

* - Implement SysConfig AD_CHANGELOG_SAVE_UUID
- reorganize MSysConfig (there were entries out of order)

* - Add index for performance on AD_ChangeLog.Record_UU

* - Enable change log for tables with multi-key (like _Trl or _Access)

* - Enable saving change log for AD_ClientInfo

* - when updating a record that doesn't have UUID - assign one
This commit is contained in:
Carlos Ruiz 2023-05-28 17:47:30 +02:00 committed by GitHub
parent 1ff182e605
commit 281333e8b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 744 additions and 151 deletions

View File

@ -0,0 +1,190 @@
-- IDEMPIERE-5567 Support of UUID - ChangeLog (FHCA-4195)
SELECT register_migration_script('202304151832_IDEMPIERE-5567.sql') FROM dual;
SET SQLBLANKLINES ON
SET DEFINE OFF
-- Apr 15, 2023, 6:32:28 PM CEST
INSERT INTO AD_Column (AD_Column_ID,Version,Name,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,FKConstraintType,IsHtml) VALUES (215835,0,'Record UUID',580,'Record_UU',36,'N','N','N','N','N',0,'N',200240,0,0,'Y',TO_TIMESTAMP('2023-04-15 18:32:27','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2023-04-15 18:32:27','YYYY-MM-DD HH24:MI:SS'),100,203804,'N','N','D','N','N','N','Y','42c26432-ba61-4375-901b-5573a94b3ca3','Y',0,'N','N','N','N')
;
-- Apr 15, 2023, 6:32:30 PM CEST
ALTER TABLE AD_ChangeLog ADD Record_UU VARCHAR2(36 CHAR) DEFAULT NULL
;
-- Apr 15, 2023, 6:34:30 PM CEST
UPDATE AD_Column SET IsMandatory='N',Updated=TO_TIMESTAMP('2023-04-15 18:34:30','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Column_ID=8817
;
-- Apr 15, 2023, 6:34:32 PM CEST
ALTER TABLE AD_ChangeLog MODIFY Record_ID NUMBER(10) DEFAULT NULL
;
-- Apr 15, 2023, 6:34:32 PM CEST
ALTER TABLE AD_ChangeLog MODIFY Record_ID NULL
;
-- Apr 15, 2023, 6:34:44 PM CEST
INSERT INTO AD_Field (AD_Field_ID,Name,AD_Tab_ID,AD_Column_ID,IsDisplayed,DisplayLength,SeqNo,IsSameLine,IsHeading,IsFieldOnly,IsEncrypted,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,IsReadOnly,IsCentrallyMaintained,EntityType,AD_Field_UU,IsDisplayedGrid,SeqNoGrid,ColumnSpan) VALUES (207619,'Record UUID',488,215835,'Y',36,190,'N','N','N','N',0,0,'Y',TO_TIMESTAMP('2023-04-15 18:34:43','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2023-04-15 18:34:43','YYYY-MM-DD HH24:MI:SS'),100,'N','Y','D','863e5828-5d78-43d9-bfd6-ef0bd26c6980','Y',190,2)
;
-- Apr 15, 2023, 6:34:50 PM CEST
INSERT INTO AD_Field (AD_Field_ID,Name,AD_Tab_ID,AD_Column_ID,IsDisplayed,DisplayLength,SeqNo,IsSameLine,IsHeading,IsFieldOnly,IsEncrypted,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,IsReadOnly,IsCentrallyMaintained,EntityType,AD_Field_UU,IsDisplayedGrid,SeqNoGrid,ColumnSpan) VALUES (207620,'Record UUID',487,215835,'Y',36,190,'N','N','N','N',0,0,'Y',TO_TIMESTAMP('2023-04-15 18:34:50','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2023-04-15 18:34:50','YYYY-MM-DD HH24:MI:SS'),100,'N','Y','D','dab3415a-c850-401f-8b9a-891e60e2999e','Y',190,2)
;
-- Apr 15, 2023, 6:35:45 PM CEST
UPDATE AD_Field SET IsDisplayed='Y', SeqNo=100, XPosition=4,Updated=TO_TIMESTAMP('2023-04-15 18:35:45','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=207619
;
-- Apr 15, 2023, 6:35:45 PM CEST
UPDATE AD_Field SET IsDisplayed='Y', SeqNo=110, XPosition=1,Updated=TO_TIMESTAMP('2023-04-15 18:35:45','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=6779
;
-- Apr 15, 2023, 6:35:45 PM CEST
UPDATE AD_Field SET SeqNo=120,Updated=TO_TIMESTAMP('2023-04-15 18:35:45','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=6774
;
-- Apr 15, 2023, 6:35:45 PM CEST
UPDATE AD_Field SET SeqNo=130,Updated=TO_TIMESTAMP('2023-04-15 18:35:45','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=10946
;
-- Apr 15, 2023, 6:35:45 PM CEST
UPDATE AD_Field SET SeqNo=140,Updated=TO_TIMESTAMP('2023-04-15 18:35:45','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=54397
;
-- Apr 15, 2023, 6:35:45 PM CEST
UPDATE AD_Field SET SeqNo=150,Updated=TO_TIMESTAMP('2023-04-15 18:35:45','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=6780
;
-- Apr 15, 2023, 6:35:45 PM CEST
UPDATE AD_Field SET SeqNo=160,Updated=TO_TIMESTAMP('2023-04-15 18:35:45','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=6773
;
-- Apr 15, 2023, 6:35:45 PM CEST
UPDATE AD_Field SET SeqNo=170,Updated=TO_TIMESTAMP('2023-04-15 18:35:45','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=12356
;
-- Apr 15, 2023, 6:35:45 PM CEST
UPDATE AD_Field SET SeqNo=180,Updated=TO_TIMESTAMP('2023-04-15 18:35:45','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=10948
;
-- Apr 15, 2023, 6:35:45 PM CEST
UPDATE AD_Field SET SeqNo=190,Updated=TO_TIMESTAMP('2023-04-15 18:35:45','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=10947
;
-- Apr 15, 2023, 6:35:45 PM CEST
UPDATE AD_Field SET SeqNo=0,Updated=TO_TIMESTAMP('2023-04-15 18:35:45','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=204473
;
-- Apr 15, 2023, 6:35:59 PM CEST
UPDATE AD_Field SET IsDisplayed='Y', SeqNo=100, XPosition=4,Updated=TO_TIMESTAMP('2023-04-15 18:35:59','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=207620
;
-- Apr 15, 2023, 6:35:59 PM CEST
UPDATE AD_Field SET IsDisplayed='Y', SeqNo=110, XPosition=1,Updated=TO_TIMESTAMP('2023-04-15 18:35:59','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=6769
;
-- Apr 15, 2023, 6:35:59 PM CEST
UPDATE AD_Field SET SeqNo=120,Updated=TO_TIMESTAMP('2023-04-15 18:35:59','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=6764
;
-- Apr 15, 2023, 6:35:59 PM CEST
UPDATE AD_Field SET SeqNo=130,Updated=TO_TIMESTAMP('2023-04-15 18:35:59','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=10949
;
-- Apr 15, 2023, 6:35:59 PM CEST
UPDATE AD_Field SET SeqNo=140,Updated=TO_TIMESTAMP('2023-04-15 18:35:59','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=54396
;
-- Apr 15, 2023, 6:35:59 PM CEST
UPDATE AD_Field SET SeqNo=150,Updated=TO_TIMESTAMP('2023-04-15 18:35:59','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=6770
;
-- Apr 15, 2023, 6:35:59 PM CEST
UPDATE AD_Field SET SeqNo=160,Updated=TO_TIMESTAMP('2023-04-15 18:35:59','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=6763
;
-- Apr 15, 2023, 6:35:59 PM CEST
UPDATE AD_Field SET SeqNo=170,Updated=TO_TIMESTAMP('2023-04-15 18:35:59','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=12357
;
-- Apr 15, 2023, 6:35:59 PM CEST
UPDATE AD_Field SET SeqNo=180,Updated=TO_TIMESTAMP('2023-04-15 18:35:59','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=10951
;
-- Apr 15, 2023, 6:35:59 PM CEST
UPDATE AD_Field SET SeqNo=190,Updated=TO_TIMESTAMP('2023-04-15 18:35:59','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=10950
;
-- Apr 15, 2023, 6:35:59 PM CEST
UPDATE AD_Field SET SeqNo=0,Updated=TO_TIMESTAMP('2023-04-15 18:35:59','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=204474
;
-- May 21, 2023, 4:07:46 PM CEST
UPDATE AD_Field SET DisplayLogic='@AD_Reference_ID@=19 | @AD_Reference_ID@=30 | @AD_Reference_ID@=18 | @AD_Reference_ID@=21 | @AD_Reference_ID@=25 | @AD_Reference_ID@=31 | @AD_Reference_ID@=35 | @AD_Reference_ID@=33 | @AD_Reference_ID@=32 | @AD_Reference_ID@=53370 | @AD_Reference_ID@=200233 | @AD_Reference_ID@=200234 | @AD_Reference_ID@=200235 | @AD_Reference_ID@=200202 | @AD_Reference_ID@=200240',Updated=TO_TIMESTAMP('2023-05-21 16:07:46','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=202519
;
-- May 21, 2023, 4:13:15 PM CEST
UPDATE AD_Val_Rule SET Code='(
AD_Ref_List.Value = ''D''
/* Cascade/SetNull/Forbid supported for all DB constraints */
OR (AD_Ref_List.Value IN (''C'',''S'',''N'') AND @AD_Reference_ID@ IN (18,19,21,25,30,31,32,33,35,53370,200233,200234,200235))
/* ModelCascade supported for Table/TableDir/Search/RecordID */
OR (AD_Ref_List.Value = ''M'' AND @AD_Reference_ID@ IN (18,19,30,200202,200240))
/* ModelSetNull/ModelForbid supported for RecordID */
OR (AD_Ref_List.Value IN (''T'',''O'') AND @AD_Reference_ID@ IN (200202,200240))
)',Updated=TO_TIMESTAMP('2023-05-21 16:13:15','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Val_Rule_ID=200064
;
-- May 21, 2023, 4:14:25 PM CEST
UPDATE AD_Column SET FieldLength=36, FKConstraintType='D',Updated=TO_TIMESTAMP('2023-05-21 16:14:25','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Column_ID=215833
;
-- May 22, 2023, 5:37:16 PM CEST
INSERT INTO AD_SysConfig (AD_SysConfig_ID,AD_Client_ID,AD_Org_ID,Created,Updated,CreatedBy,UpdatedBy,IsActive,Name,Value,Description,EntityType,ConfigurationLevel,AD_SysConfig_UU) VALUES (200228,0,0,TO_TIMESTAMP('2023-05-22 17:37:15','YYYY-MM-DD HH24:MI:SS'),TO_TIMESTAMP('2023-05-22 17:37:15','YYYY-MM-DD HH24:MI:SS'),100,100,'Y','AD_CHANGELOG_SAVE_UUID','B','Save the AD_ChangeLog.Record_UU -> B | Just for UUID based tables , A | Always , U | just UUID not ID','D','S','32161230-22c9-43eb-a327-f3b4841eeace')
;
-- May 23, 2023, 5:17:25 PM CEST
INSERT INTO AD_TableIndex (AD_Client_ID,AD_Org_ID,AD_TableIndex_ID,AD_TableIndex_UU,Created,CreatedBy,EntityType,IsActive,Name,Updated,UpdatedBy,AD_Table_ID,IsCreateConstraint,IsUnique,Processing,IsKey) VALUES (0,0,201247,'d0ab3ee4-418c-42e9-acfe-618750685bcc',TO_TIMESTAMP('2023-05-23 17:17:24','YYYY-MM-DD HH24:MI:SS'),100,'D','Y','ad_changelog_record_uu',TO_TIMESTAMP('2023-05-23 17:17:24','YYYY-MM-DD HH24:MI:SS'),100,580,'N','N','N','N')
;
-- May 23, 2023, 5:17:48 PM CEST
INSERT INTO AD_IndexColumn (AD_Client_ID,AD_Org_ID,AD_IndexColumn_ID,AD_IndexColumn_UU,Created,CreatedBy,EntityType,IsActive,Updated,UpdatedBy,AD_Column_ID,AD_TableIndex_ID,SeqNo) VALUES (0,0,201686,'6ce73812-0b30-4145-a9c5-ab1c73d4c386',TO_TIMESTAMP('2023-05-23 17:17:47','YYYY-MM-DD HH24:MI:SS'),100,'D','Y',TO_TIMESTAMP('2023-05-23 17:17:47','YYYY-MM-DD HH24:MI:SS'),100,215835,201247,10)
;
-- May 23, 2023, 5:17:53 PM CEST
CREATE INDEX ad_changelog_record_uu ON AD_ChangeLog (Record_UU)
;
/*
SET SERVEROUTPUT on;
-- Set Record_UU for existing records
DECLARE
cmd varchar2(2000);
v_cnt numeric;
BEGIN
FOR r IN (
SELECT DISTINCT t.TableName, cl.AD_Table_ID
FROM AD_ChangeLog cl
JOIN AD_Table t ON (cl.AD_Table_ID=t.AD_Table_ID)
WHERE cl.Record_UU IS NULL
AND cl.Record_ID IS NOT NULL
AND cl.EventChangeLog!='D'
) LOOP
cmd := 'UPDATE AD_ChangeLog SET Record_UU=(SELECT '
|| r.TableName
|| '_UU FROM '
|| r.TableName
|| ' WHERE '
|| r.TableName
|| '_ID=AD_ChangeLog.Record_ID) WHERE AD_Table_ID='
|| r.AD_Table_ID
|| ' AND Record_ID IS NOT NULL AND Record_UU IS NULL AND EventChangeLog!=''D''';
EXECUTE IMMEDIATE cmd;
DBMS_OUTPUT.PUT_LINE(SQL%ROWCOUNT || ' AD_ChangeLog.Record_UU set in ' || r.TableName);
END LOOP;
END;
/
*/

View File

@ -0,0 +1,189 @@
-- IDEMPIERE-5567 Support of UUID - ChangeLog (FHCA-4195)
SELECT register_migration_script('202304151832_IDEMPIERE-5567.sql') FROM dual;
-- Apr 15, 2023, 6:32:28 PM CEST
INSERT INTO AD_Column (AD_Column_ID,Version,Name,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,FKConstraintType,IsHtml) VALUES (215835,0,'Record UUID',580,'Record_UU',36,'N','N','N','N','N',0,'N',200240,0,0,'Y',TO_TIMESTAMP('2023-04-15 18:32:27','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2023-04-15 18:32:27','YYYY-MM-DD HH24:MI:SS'),100,203804,'N','N','D','N','N','N','Y','42c26432-ba61-4375-901b-5573a94b3ca3','Y',0,'N','N','N','N')
;
-- Apr 15, 2023, 6:32:30 PM CEST
ALTER TABLE AD_ChangeLog ADD COLUMN Record_UU VARCHAR(36) DEFAULT NULL
;
-- Apr 15, 2023, 6:34:30 PM CEST
UPDATE AD_Column SET IsMandatory='N',Updated=TO_TIMESTAMP('2023-04-15 18:34:30','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Column_ID=8817
;
-- Apr 15, 2023, 6:34:32 PM CEST
INSERT INTO t_alter_column values('ad_changelog','Record_ID','NUMERIC(10)',null,'NULL')
;
-- Apr 15, 2023, 6:34:32 PM CEST
INSERT INTO t_alter_column values('ad_changelog','Record_ID',null,'NULL',null)
;
-- Apr 15, 2023, 6:34:44 PM CEST
INSERT INTO AD_Field (AD_Field_ID,Name,AD_Tab_ID,AD_Column_ID,IsDisplayed,DisplayLength,SeqNo,IsSameLine,IsHeading,IsFieldOnly,IsEncrypted,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,IsReadOnly,IsCentrallyMaintained,EntityType,AD_Field_UU,IsDisplayedGrid,SeqNoGrid,ColumnSpan) VALUES (207619,'Record UUID',488,215835,'Y',36,190,'N','N','N','N',0,0,'Y',TO_TIMESTAMP('2023-04-15 18:34:43','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2023-04-15 18:34:43','YYYY-MM-DD HH24:MI:SS'),100,'N','Y','D','863e5828-5d78-43d9-bfd6-ef0bd26c6980','Y',190,2)
;
-- Apr 15, 2023, 6:34:50 PM CEST
INSERT INTO AD_Field (AD_Field_ID,Name,AD_Tab_ID,AD_Column_ID,IsDisplayed,DisplayLength,SeqNo,IsSameLine,IsHeading,IsFieldOnly,IsEncrypted,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,IsReadOnly,IsCentrallyMaintained,EntityType,AD_Field_UU,IsDisplayedGrid,SeqNoGrid,ColumnSpan) VALUES (207620,'Record UUID',487,215835,'Y',36,190,'N','N','N','N',0,0,'Y',TO_TIMESTAMP('2023-04-15 18:34:50','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2023-04-15 18:34:50','YYYY-MM-DD HH24:MI:SS'),100,'N','Y','D','dab3415a-c850-401f-8b9a-891e60e2999e','Y',190,2)
;
-- Apr 15, 2023, 6:35:45 PM CEST
UPDATE AD_Field SET IsDisplayed='Y', SeqNo=100, XPosition=4,Updated=TO_TIMESTAMP('2023-04-15 18:35:45','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=207619
;
-- Apr 15, 2023, 6:35:45 PM CEST
UPDATE AD_Field SET IsDisplayed='Y', SeqNo=110, XPosition=1,Updated=TO_TIMESTAMP('2023-04-15 18:35:45','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=6779
;
-- Apr 15, 2023, 6:35:45 PM CEST
UPDATE AD_Field SET SeqNo=120,Updated=TO_TIMESTAMP('2023-04-15 18:35:45','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=6774
;
-- Apr 15, 2023, 6:35:45 PM CEST
UPDATE AD_Field SET SeqNo=130,Updated=TO_TIMESTAMP('2023-04-15 18:35:45','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=10946
;
-- Apr 15, 2023, 6:35:45 PM CEST
UPDATE AD_Field SET SeqNo=140,Updated=TO_TIMESTAMP('2023-04-15 18:35:45','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=54397
;
-- Apr 15, 2023, 6:35:45 PM CEST
UPDATE AD_Field SET SeqNo=150,Updated=TO_TIMESTAMP('2023-04-15 18:35:45','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=6780
;
-- Apr 15, 2023, 6:35:45 PM CEST
UPDATE AD_Field SET SeqNo=160,Updated=TO_TIMESTAMP('2023-04-15 18:35:45','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=6773
;
-- Apr 15, 2023, 6:35:45 PM CEST
UPDATE AD_Field SET SeqNo=170,Updated=TO_TIMESTAMP('2023-04-15 18:35:45','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=12356
;
-- Apr 15, 2023, 6:35:45 PM CEST
UPDATE AD_Field SET SeqNo=180,Updated=TO_TIMESTAMP('2023-04-15 18:35:45','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=10948
;
-- Apr 15, 2023, 6:35:45 PM CEST
UPDATE AD_Field SET SeqNo=190,Updated=TO_TIMESTAMP('2023-04-15 18:35:45','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=10947
;
-- Apr 15, 2023, 6:35:45 PM CEST
UPDATE AD_Field SET SeqNo=0,Updated=TO_TIMESTAMP('2023-04-15 18:35:45','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=204473
;
-- Apr 15, 2023, 6:35:59 PM CEST
UPDATE AD_Field SET IsDisplayed='Y', SeqNo=100, XPosition=4,Updated=TO_TIMESTAMP('2023-04-15 18:35:59','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=207620
;
-- Apr 15, 2023, 6:35:59 PM CEST
UPDATE AD_Field SET IsDisplayed='Y', SeqNo=110, XPosition=1,Updated=TO_TIMESTAMP('2023-04-15 18:35:59','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=6769
;
-- Apr 15, 2023, 6:35:59 PM CEST
UPDATE AD_Field SET SeqNo=120,Updated=TO_TIMESTAMP('2023-04-15 18:35:59','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=6764
;
-- Apr 15, 2023, 6:35:59 PM CEST
UPDATE AD_Field SET SeqNo=130,Updated=TO_TIMESTAMP('2023-04-15 18:35:59','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=10949
;
-- Apr 15, 2023, 6:35:59 PM CEST
UPDATE AD_Field SET SeqNo=140,Updated=TO_TIMESTAMP('2023-04-15 18:35:59','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=54396
;
-- Apr 15, 2023, 6:35:59 PM CEST
UPDATE AD_Field SET SeqNo=150,Updated=TO_TIMESTAMP('2023-04-15 18:35:59','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=6770
;
-- Apr 15, 2023, 6:35:59 PM CEST
UPDATE AD_Field SET SeqNo=160,Updated=TO_TIMESTAMP('2023-04-15 18:35:59','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=6763
;
-- Apr 15, 2023, 6:35:59 PM CEST
UPDATE AD_Field SET SeqNo=170,Updated=TO_TIMESTAMP('2023-04-15 18:35:59','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=12357
;
-- Apr 15, 2023, 6:35:59 PM CEST
UPDATE AD_Field SET SeqNo=180,Updated=TO_TIMESTAMP('2023-04-15 18:35:59','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=10951
;
-- Apr 15, 2023, 6:35:59 PM CEST
UPDATE AD_Field SET SeqNo=190,Updated=TO_TIMESTAMP('2023-04-15 18:35:59','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=10950
;
-- Apr 15, 2023, 6:35:59 PM CEST
UPDATE AD_Field SET SeqNo=0,Updated=TO_TIMESTAMP('2023-04-15 18:35:59','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=204474
;
-- May 21, 2023, 4:07:46 PM CEST
UPDATE AD_Field SET DisplayLogic='@AD_Reference_ID@=19 | @AD_Reference_ID@=30 | @AD_Reference_ID@=18 | @AD_Reference_ID@=21 | @AD_Reference_ID@=25 | @AD_Reference_ID@=31 | @AD_Reference_ID@=35 | @AD_Reference_ID@=33 | @AD_Reference_ID@=32 | @AD_Reference_ID@=53370 | @AD_Reference_ID@=200233 | @AD_Reference_ID@=200234 | @AD_Reference_ID@=200235 | @AD_Reference_ID@=200202 | @AD_Reference_ID@=200240',Updated=TO_TIMESTAMP('2023-05-21 16:07:46','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=202519
;
-- May 21, 2023, 4:13:15 PM CEST
UPDATE AD_Val_Rule SET Code='(
AD_Ref_List.Value = ''D''
/* Cascade/SetNull/Forbid supported for all DB constraints */
OR (AD_Ref_List.Value IN (''C'',''S'',''N'') AND @AD_Reference_ID@ IN (18,19,21,25,30,31,32,33,35,53370,200233,200234,200235))
/* ModelCascade supported for Table/TableDir/Search/RecordID */
OR (AD_Ref_List.Value = ''M'' AND @AD_Reference_ID@ IN (18,19,30,200202,200240))
/* ModelSetNull/ModelForbid supported for RecordID */
OR (AD_Ref_List.Value IN (''T'',''O'') AND @AD_Reference_ID@ IN (200202,200240))
)',Updated=TO_TIMESTAMP('2023-05-21 16:13:15','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Val_Rule_ID=200064
;
-- May 21, 2023, 4:14:25 PM CEST
UPDATE AD_Column SET FieldLength=36, FKConstraintType='D',Updated=TO_TIMESTAMP('2023-05-21 16:14:25','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Column_ID=215833
;
-- May 22, 2023, 5:37:16 PM CEST
INSERT INTO AD_SysConfig (AD_SysConfig_ID,AD_Client_ID,AD_Org_ID,Created,Updated,CreatedBy,UpdatedBy,IsActive,Name,Value,Description,EntityType,ConfigurationLevel,AD_SysConfig_UU) VALUES (200228,0,0,TO_TIMESTAMP('2023-05-22 17:37:15','YYYY-MM-DD HH24:MI:SS'),TO_TIMESTAMP('2023-05-22 17:37:15','YYYY-MM-DD HH24:MI:SS'),100,100,'Y','AD_CHANGELOG_SAVE_UUID','B','Save the AD_ChangeLog.Record_UU -> B | Just for UUID based tables , A | Always , U | just UUID not ID','D','S','32161230-22c9-43eb-a327-f3b4841eeace')
;
-- May 23, 2023, 5:17:25 PM CEST
INSERT INTO AD_TableIndex (AD_Client_ID,AD_Org_ID,AD_TableIndex_ID,AD_TableIndex_UU,Created,CreatedBy,EntityType,IsActive,Name,Updated,UpdatedBy,AD_Table_ID,IsCreateConstraint,IsUnique,Processing,IsKey) VALUES (0,0,201247,'d0ab3ee4-418c-42e9-acfe-618750685bcc',TO_TIMESTAMP('2023-05-23 17:17:24','YYYY-MM-DD HH24:MI:SS'),100,'D','Y','ad_changelog_record_uu',TO_TIMESTAMP('2023-05-23 17:17:24','YYYY-MM-DD HH24:MI:SS'),100,580,'N','N','N','N')
;
-- May 23, 2023, 5:17:48 PM CEST
INSERT INTO AD_IndexColumn (AD_Client_ID,AD_Org_ID,AD_IndexColumn_ID,AD_IndexColumn_UU,Created,CreatedBy,EntityType,IsActive,Updated,UpdatedBy,AD_Column_ID,AD_TableIndex_ID,SeqNo) VALUES (0,0,201686,'6ce73812-0b30-4145-a9c5-ab1c73d4c386',TO_TIMESTAMP('2023-05-23 17:17:47','YYYY-MM-DD HH24:MI:SS'),100,'D','Y',TO_TIMESTAMP('2023-05-23 17:17:47','YYYY-MM-DD HH24:MI:SS'),100,215835,201247,10)
;
-- May 23, 2023, 5:17:53 PM CEST
CREATE INDEX ad_changelog_record_uu ON AD_ChangeLog (Record_UU)
;
/*
-- Set Record_UU for existing records
DO $$
DECLARE
cmd varchar(2000);
r record;
v_cnt numeric;
BEGIN
FOR r IN
SELECT DISTINCT t.TableName, cl.AD_Table_ID
FROM AD_ChangeLog cl
JOIN AD_Table t ON (cl.AD_Table_ID=t.AD_Table_ID)
WHERE cl.Record_UU IS NULL
AND cl.Record_ID IS NOT NULL
AND cl.EventChangeLog!='D'
LOOP
cmd := 'UPDATE AD_ChangeLog SET Record_UU=(SELECT '
|| r.TableName
|| '_UU FROM '
|| r.TableName
|| ' WHERE '
|| r.TableName
|| '_ID=AD_ChangeLog.Record_ID) WHERE AD_Table_ID='
|| r.AD_Table_ID
|| ' AND Record_ID IS NOT NULL AND Record_UU IS NULL AND EventChangeLog!=''D''';
EXECUTE cmd;
GET DIAGNOSTICS v_cnt = ROW_COUNT;
RAISE NOTICE '% AD_ChangeLog.Record_UU set in %', v_cnt, r.TableName;
END LOOP;
END;
$$
;
*/

View File

@ -113,7 +113,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
/** /**
* *
*/ */
private static final long serialVersionUID = 2604313946261586651L; private static final long serialVersionUID = 4674027561845549215L;
public static final String DEFAULT_STATUS_MESSAGE = "NavigateOrUpdate"; public static final String DEFAULT_STATUS_MESSAGE = "NavigateOrUpdate";
@ -2437,6 +2437,16 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable
return m_mTable.getKeyID(m_currentRow); return m_mTable.getKeyID(m_currentRow);
} // getRecord_ID } // getRecord_ID
/**
* Get Current Table UUID
* @return Record_UU
*/
public String getRecord_UU()
{
UUID uuid = m_mTable.getUUID(m_currentRow);
return (uuid == null ? null : uuid.toString());
} // getRecord_UU
/** /**
* Get Key ID of row * Get Key ID of row
* @param row row number * @param row row number

View File

@ -236,6 +236,15 @@ public interface I_AD_ChangeLog
*/ */
public int getRecord_ID(); public int getRecord_ID();
/** Column name Record_UU */
public static final String COLUMNNAME_Record_UU = "Record_UU";
/** Set Record UUID */
public void setRecord_UU (String Record_UU);
/** Get Record UUID */
public String getRecord_UU();
/** Column name Redo */ /** Column name Redo */
public static final String COLUMNNAME_Redo = "Redo"; public static final String COLUMNNAME_Redo = "Redo";

View File

@ -25,6 +25,7 @@ import java.util.logging.Level;
import org.compiere.util.CLogger; import org.compiere.util.CLogger;
import org.compiere.util.DB; import org.compiere.util.DB;
import org.compiere.util.Util;
/** /**
* Change Log Model * Change Log Model
@ -37,7 +38,7 @@ public class MChangeLog extends X_AD_ChangeLog
/** /**
* *
*/ */
private static final long serialVersionUID = 7262833610411402160L; private static final long serialVersionUID = 3082084206319959526L;
/** /**
* Do we track changes for this table * Do we track changes for this table
@ -170,6 +171,33 @@ public class MChangeLog extends X_AD_ChangeLog
int AD_Table_ID, int AD_Column_ID, int Record_ID, int AD_Table_ID, int AD_Column_ID, int Record_ID,
int AD_Client_ID, int AD_Org_ID, int AD_Client_ID, int AD_Org_ID,
Object OldValue, Object NewValue, String event) Object OldValue, Object NewValue, String event)
{
this(ctx, AD_ChangeLog_ID, TrxName, AD_Session_ID,
AD_Table_ID, AD_Column_ID, Record_ID, null,
AD_Client_ID, AD_Org_ID,
OldValue, NewValue, event);
}
/**
* Full Constructor
* @param ctx context
* @param AD_ChangeLog_ID 0 for new change log
* @param TrxName transaction
* @param AD_Session_ID session
* @param AD_Table_ID table
* @param AD_Column_ID column
* @param Record_ID record
* @param Record_UU record UUID
* @param AD_Client_ID client
* @param AD_Org_ID org
* @param OldValue old
* @param NewValue new
*/
public MChangeLog (Properties ctx,
int AD_ChangeLog_ID, String TrxName, int AD_Session_ID,
int AD_Table_ID, int AD_Column_ID, int Record_ID, String Record_UU,
int AD_Client_ID, int AD_Org_ID,
Object OldValue, Object NewValue, String event)
{ {
this (ctx, 0, TrxName); this (ctx, 0, TrxName);
if (AD_ChangeLog_ID == 0) if (AD_ChangeLog_ID == 0)
@ -184,7 +212,16 @@ public class MChangeLog extends X_AD_ChangeLog
// //
setAD_Table_ID (AD_Table_ID); setAD_Table_ID (AD_Table_ID);
setAD_Column_ID (AD_Column_ID); setAD_Column_ID (AD_Column_ID);
String saveUUID = MSysConfig.getValue(MSysConfig.AD_CHANGELOG_SAVE_UUID, "B");
// B - just based UUID tables (default)
// A - always
// U - just UUID, not ID
if (Record_ID > 0 && (!"U".equals(saveUUID) || Util.isEmpty(Record_UU))) {
setRecord_ID (Record_ID); setRecord_ID (Record_ID);
}
if ("U".equals(saveUUID) || "A".equals(saveUUID) || ("B".equals(saveUUID) && (Record_ID <= 0 || MTable.get(AD_Table_ID).isUUIDKeyTable()))) {
setRecord_UU (Record_UU);
}
// //
setClientOrg (AD_Client_ID, AD_Org_ID); setClientOrg (AD_Client_ID, AD_Org_ID);
// //

View File

@ -82,7 +82,7 @@ public final class MLookup extends Lookup implements Serializable
} }
// Don't load Search or CreatedBy/UpdatedBy // Don't load Search or CreatedBy/UpdatedBy
if (m_info.DisplayType == DisplayType.Search if (m_info.DisplayType == DisplayType.Search || m_info.DisplayType == DisplayType.SearchUU
|| m_info.IsCreadedUpdatedBy) || m_info.IsCreadedUpdatedBy)
return; return;
// Don't load Parents/Keys // Don't load Parents/Keys

View File

@ -46,8 +46,7 @@ public class MSession extends X_AD_Session implements ImmutablePOSupport
/** /**
* *
*/ */
private static final long serialVersionUID = 480745219310430126L; private static final long serialVersionUID = -5836154187760734691L;
/** /**
* Get existing or create local session * Get existing or create local session
@ -323,7 +322,7 @@ public class MSession extends X_AD_Session implements ImmutablePOSupport
Object OldValue, Object NewValue) Object OldValue, Object NewValue)
{ {
return changeLog(TrxName, AD_ChangeLog_ID, AD_Table_ID, AD_Column_ID, return changeLog(TrxName, AD_ChangeLog_ID, AD_Table_ID, AD_Column_ID,
Record_ID, AD_Client_ID, AD_Org_ID, OldValue, NewValue, Record_ID, null, AD_Client_ID, AD_Org_ID, OldValue, NewValue,
(String) null); (String) null);
} // changeLog } // changeLog
@ -345,6 +344,31 @@ public class MSession extends X_AD_Session implements ImmutablePOSupport
int AD_Table_ID, int AD_Column_ID, int Record_ID, int AD_Table_ID, int AD_Column_ID, int Record_ID,
int AD_Client_ID, int AD_Org_ID, int AD_Client_ID, int AD_Org_ID,
Object OldValue, Object NewValue, String event) Object OldValue, Object NewValue, String event)
{
return changeLog(TrxName, AD_ChangeLog_ID, AD_Table_ID, AD_Column_ID,
Record_ID, null, AD_Client_ID, AD_Org_ID, OldValue, NewValue,
(String) null);
}
/**
* Create Change Log only if table is logged
* @param TrxName transaction name
* @param AD_ChangeLog_ID 0 for new change log
* @param AD_Table_ID table
* @param AD_Column_ID column
* @param Record_ID record
* @param Record_UU record UUID
* @param AD_Client_ID client
* @param AD_Org_ID org
* @param OldValue old
* @param NewValue new
* @return saved change log or null
*/
public MChangeLog changeLog (
String TrxName, int AD_ChangeLog_ID,
int AD_Table_ID, int AD_Column_ID, int Record_ID, String Record_UU,
int AD_Client_ID, int AD_Org_ID,
Object OldValue, Object NewValue, String event)
{ {
// Null handling // Null handling
if (OldValue == null && NewValue == null) if (OldValue == null && NewValue == null)
@ -372,7 +396,7 @@ public class MSession extends X_AD_Session implements ImmutablePOSupport
PO.setCrossTenantSafe(); PO.setCrossTenantSafe();
MChangeLog cl = new MChangeLog(getCtx(), MChangeLog cl = new MChangeLog(getCtx(),
AD_ChangeLog_ID, TrxName, getAD_Session_ID(), AD_ChangeLog_ID, TrxName, getAD_Session_ID(),
AD_Table_ID, AD_Column_ID, Record_ID, AD_Client_ID, AD_Org_ID, AD_Table_ID, AD_Column_ID, Record_ID, Record_UU, AD_Client_ID, AD_Org_ID,
OldValue, NewValue, event); OldValue, NewValue, event);
if (cl.save()) if (cl.save())
return cl; return cl;

View File

@ -44,8 +44,9 @@ public class MSysConfig extends X_AD_SysConfig
/** /**
* *
*/ */
private static final long serialVersionUID = -1169550637760344445L; private static final long serialVersionUID = 1700160594551368619L;
public static final String AD_CHANGELOG_SAVE_UUID = "AD_CHANGELOG_SAVE_UUID";
public static final String ADDRESS_VALIDATION = "ADDRESS_VALIDATION"; public static final String ADDRESS_VALIDATION = "ADDRESS_VALIDATION";
public static final String ALERT_SEND_ATTACHMENT_AS_XLS = "ALERT_SEND_ATTACHMENT_AS_XLS"; public static final String ALERT_SEND_ATTACHMENT_AS_XLS = "ALERT_SEND_ATTACHMENT_AS_XLS";
public static final String ALLOCATION_DESCRIPTION = "ALLOCATION_DESCRIPTION"; public static final String ALLOCATION_DESCRIPTION = "ALLOCATION_DESCRIPTION";
@ -85,11 +86,11 @@ public class MSysConfig extends X_AD_SysConfig
public static final String CHECK_CREDIT_ON_PREPAY_ORDER = "CHECK_CREDIT_ON_PREPAY_ORDER"; public static final String CHECK_CREDIT_ON_PREPAY_ORDER = "CHECK_CREDIT_ON_PREPAY_ORDER";
public static final String CLIENT_ACCOUNTING = "CLIENT_ACCOUNTING"; public static final String CLIENT_ACCOUNTING = "CLIENT_ACCOUNTING";
public static final String DASHBOARD_LAYOUT_ORIENTATION = "DASHBOARD_LAYOUT_ORIENTATION"; public static final String DASHBOARD_LAYOUT_ORIENTATION = "DASHBOARD_LAYOUT_ORIENTATION";
public static final String DEFAULT_COA_PATH = "DEFAULT_COA_PATH";
public static final String DEFAULT_ENTITYTYPE = "DEFAULT_ENTITYTYPE"; // used as default in entity type columns with get_sysconfig
public static final String DB_READ_REPLICA_NORMAL_MAX_ITERATIONS = "DB_READ_REPLICA_NORMAL_MAX_ITERATIONS"; public static final String DB_READ_REPLICA_NORMAL_MAX_ITERATIONS = "DB_READ_REPLICA_NORMAL_MAX_ITERATIONS";
public static final String DB_READ_REPLICA_NORMAL_TIMEOUT_IN_MILLISECONDS = "DB_READ_REPLICA_NORMAL_TIMEOUT_IN_MILLISECONDS"; public static final String DB_READ_REPLICA_NORMAL_TIMEOUT_IN_MILLISECONDS = "DB_READ_REPLICA_NORMAL_TIMEOUT_IN_MILLISECONDS";
public static final String DB_READ_REPLICA_URLS = "DB_READ_REPLICA_URLS"; public static final String DB_READ_REPLICA_URLS = "DB_READ_REPLICA_URLS";
public static final String DEFAULT_COA_PATH = "DEFAULT_COA_PATH";
public static final String DEFAULT_ENTITYTYPE = "DEFAULT_ENTITYTYPE"; // used as default in entity type columns with get_sysconfig
public static final String DICTIONARY_ID_PASSWORD = "DICTIONARY_ID_PASSWORD"; public static final String DICTIONARY_ID_PASSWORD = "DICTIONARY_ID_PASSWORD";
public static final String DICTIONARY_ID_USE_CENTRALIZED_ID = "DICTIONARY_ID_USE_CENTRALIZED_ID"; public static final String DICTIONARY_ID_USE_CENTRALIZED_ID = "DICTIONARY_ID_USE_CENTRALIZED_ID";
public static final String DICTIONARY_ID_USER = "DICTIONARY_ID_USER"; public static final String DICTIONARY_ID_USER = "DICTIONARY_ID_USER";
@ -144,10 +145,10 @@ public class MSysConfig extends X_AD_SysConfig
public static final String MESSAGES_AT_TENANT_LEVEL = "MESSAGES_AT_TENANT_LEVEL"; public static final String MESSAGES_AT_TENANT_LEVEL = "MESSAGES_AT_TENANT_LEVEL";
public static final String MFA_NTP_TIMEOUT_IN_MILLISECONDS = "MFA_NTP_TIMEOUT_IN_MILLISECONDS"; public static final String MFA_NTP_TIMEOUT_IN_MILLISECONDS = "MFA_NTP_TIMEOUT_IN_MILLISECONDS";
public static final String MFA_REGISTERED_DEVICE_EXPIRATION_DAYS = "MFA_REGISTERED_DEVICE_EXPIRATION_DAYS"; public static final String MFA_REGISTERED_DEVICE_EXPIRATION_DAYS = "MFA_REGISTERED_DEVICE_EXPIRATION_DAYS";
public static final String MONITOR_INITIAL_WAIT_FOR_CLUSTER_IN_SECONDS = "MONITOR_INITIAL_WAIT_FOR_CLUSTER_IN_SECONDS";
public static final String MONITOR_MAX_WAIT_FOR_CLUSTER_IN_SECONDS = "MONITOR_MAX_WAIT_FOR_CLUSTER_IN_SECONDS";
public static final String MFG_ValidateCostsDifferenceOnCreate = "MFG_ValidateCostsDifferenceOnCreate"; public static final String MFG_ValidateCostsDifferenceOnCreate = "MFG_ValidateCostsDifferenceOnCreate";
public static final String MFG_ValidateCostsOnCreate = "MFG_ValidateCostsOnCreate"; public static final String MFG_ValidateCostsOnCreate = "MFG_ValidateCostsOnCreate";
public static final String MONITOR_INITIAL_WAIT_FOR_CLUSTER_IN_SECONDS = "MONITOR_INITIAL_WAIT_FOR_CLUSTER_IN_SECONDS";
public static final String MONITOR_MAX_WAIT_FOR_CLUSTER_IN_SECONDS = "MONITOR_MAX_WAIT_FOR_CLUSTER_IN_SECONDS";
public static final String MSEQUENCE_GETNEXT_TIMEOUT = "MSEQUENCE_GETNEXT_TIMEOUT"; public static final String MSEQUENCE_GETNEXT_TIMEOUT = "MSEQUENCE_GETNEXT_TIMEOUT";
public static final String PAYMENT_OVERWRITE_DOCUMENTNO_WITH_CHECK_ON_PAYMENT = "PAYMENT_OVERWRITE_DOCUMENTNO_WITH_CHECK_ON_PAYMENT"; public static final String PAYMENT_OVERWRITE_DOCUMENTNO_WITH_CHECK_ON_PAYMENT = "PAYMENT_OVERWRITE_DOCUMENTNO_WITH_CHECK_ON_PAYMENT";
public static final String PAYMENT_OVERWRITE_DOCUMENTNO_WITH_CHECK_ON_RECEIPT = "PAYMENT_OVERWRITE_DOCUMENTNO_WITH_CHECK_ON_RECEIPT"; public static final String PAYMENT_OVERWRITE_DOCUMENTNO_WITH_CHECK_ON_RECEIPT = "PAYMENT_OVERWRITE_DOCUMENTNO_WITH_CHECK_ON_RECEIPT";
@ -242,8 +243,8 @@ public class MSysConfig extends X_AD_SysConfig
public static final String ZK_SEARCH_AUTO_COMPLETE_MAX_ROWS = "ZK_SEARCH_AUTO_COMPLETE_MAX_ROWS"; public static final String ZK_SEARCH_AUTO_COMPLETE_MAX_ROWS = "ZK_SEARCH_AUTO_COMPLETE_MAX_ROWS";
public static final String ZK_SEQ_DEFAULT_VALUE_PANEL = "ZK_SEQ_DEFAULT_VALUE_PANEL"; public static final String ZK_SEQ_DEFAULT_VALUE_PANEL = "ZK_SEQ_DEFAULT_VALUE_PANEL";
public static final String ZK_SESSION_TIMEOUT_IN_SECONDS = "ZK_SESSION_TIMEOUT_IN_SECONDS"; public static final String ZK_SESSION_TIMEOUT_IN_SECONDS = "ZK_SESSION_TIMEOUT_IN_SECONDS";
public static final String ZK_THEME_USE_FONT_ICON_FOR_IMAGE = "ZK_THEME_USE_FONT_ICON_FOR_IMAGE";
public static final String ZK_THEME = "ZK_THEME"; public static final String ZK_THEME = "ZK_THEME";
public static final String ZK_THEME_USE_FONT_ICON_FOR_IMAGE = "ZK_THEME_USE_FONT_ICON_FOR_IMAGE";
public static final String ZK_TOOLBAR_SHOW_MORE_VERTICAL = "ZK_TOOLBAR_SHOW_MORE_VERTICAL"; public static final String ZK_TOOLBAR_SHOW_MORE_VERTICAL = "ZK_TOOLBAR_SHOW_MORE_VERTICAL";
public static final String ZK_USE_PDF_JS_VIEWER = "ZK_USE_PDF_JS_VIEWER"; public static final String ZK_USE_PDF_JS_VIEWER = "ZK_USE_PDF_JS_VIEWER";
public static final String ZOOM_ACROSS_QUERY_TIMEOUT = "ZOOM_ACROSS_QUERY_TIMEOUT"; public static final String ZOOM_ACROSS_QUERY_TIMEOUT = "ZOOM_ACROSS_QUERY_TIMEOUT";

View File

@ -878,6 +878,7 @@ public class MTable extends X_AD_Table implements ImmutablePOSupport
return (tablename.equals("AD_Org") || return (tablename.equals("AD_Org") ||
tablename.equals("AD_OrgInfo") || tablename.equals("AD_OrgInfo") ||
tablename.equals("AD_Client") || // IDEMPIERE-668 tablename.equals("AD_Client") || // IDEMPIERE-668
tablename.equals("AD_ClientInfo") ||
tablename.equals("AD_AllClients_V") || tablename.equals("AD_AllClients_V") ||
tablename.equals("AD_ReportView") || tablename.equals("AD_ReportView") ||
tablename.equals("AD_Role") || tablename.equals("AD_Role") ||

View File

@ -2336,11 +2336,6 @@ public abstract class PO
*/ */
public boolean save() public boolean save()
{ {
checkImmutable();
checkValidContext();
checkCrossTenant(true);
checkRecordIDCrossTenant();
CLogger.resetLast(); CLogger.resetLast();
boolean newRecord = is_new(); // save locally as load resets boolean newRecord = is_new(); // save locally as load resets
if (!newRecord && !is_Changed()) if (!newRecord && !is_Changed())
@ -2349,6 +2344,12 @@ public abstract class PO
return true; return true;
} }
checkImmutable();
checkValidContext();
checkCrossTenant(true);
checkRecordIDCrossTenant();
checkRecordUUCrossTenant();
if (m_setErrorsFilled) { if (m_setErrorsFilled) {
for (int i = 0; i < m_setErrors.length; i++) { for (int i = 0; i < m_setErrors.length; i++) {
ValueNamePair setError = m_setErrors[i]; ValueNamePair setError = m_setErrors[i];
@ -2830,6 +2831,18 @@ public abstract class PO
log.fine("No Session found"); log.fine("No Session found");
int AD_ChangeLog_ID = 0; int AD_ChangeLog_ID = 0;
//uuid secondary key - when updating, if the record doesn't have UUID, assign one
int uuidIndex = p_info.getColumnIndex(getUUIDColumnName());
if (uuidIndex >= 0)
{
String value = (String)get_Value(uuidIndex);
if (p_info.getColumn(uuidIndex).FieldLength == 36 && (value == null || value.length() == 0))
{
UUID uuid = UUID.randomUUID();
set_ValueNoCheck(p_info.getColumnName(uuidIndex), uuid.toString());
}
}
int size = get_ColumnCount(); int size = get_ColumnCount();
for (int i = 0; i < size; i++) for (int i = 0; i < size; i++)
{ {
@ -3009,7 +3022,6 @@ public abstract class PO
// Change Log - Only // Change Log - Only
if (session != null if (session != null
&& m_IDs.length == 1
&& p_info.isAllowLogging(i) // logging allowed && p_info.isAllowLogging(i) // logging allowed
&& !p_info.isEncrypted(i) // not encrypted && !p_info.isEncrypted(i) // not encrypted
&& !p_info.isVirtualColumn(i) // no virtual column && !p_info.isVirtualColumn(i) // no virtual column
@ -3026,7 +3038,7 @@ public abstract class PO
MChangeLog cLog = session.changeLog ( MChangeLog cLog = session.changeLog (
m_trxName, AD_ChangeLog_ID, m_trxName, AD_ChangeLog_ID,
p_info.getAD_Table_ID(), p_info.getColumn(i).AD_Column_ID, p_info.getAD_Table_ID(), p_info.getColumn(i).AD_Column_ID,
get_ID(), getAD_Client_ID(), getAD_Org_ID(), oldV, newV, MChangeLog.EVENTCHANGELOG_Update); (m_IDs.length == 1 ? get_ID() : 0), get_UUID(), getAD_Client_ID(), getAD_Org_ID(), oldV, newV, MChangeLog.EVENTCHANGELOG_Update);
if (cLog != null) if (cLog != null)
AD_ChangeLog_ID = cLog.getAD_ChangeLog_ID(); AD_ChangeLog_ID = cLog.getAD_ChangeLog_ID();
} }
@ -3355,29 +3367,33 @@ public abstract class PO
if (withValues && m_IDs.length == 1 && p_info.hasKeyColumn() if (withValues && m_IDs.length == 1 && p_info.hasKeyColumn()
&& m_KeyColumns[0].endsWith("_ID") && !Env.isUseCentralizedId(p_info.getTableName())) && m_KeyColumns[0].endsWith("_ID") && !Env.isUseCentralizedId(p_info.getTableName()))
{ {
int id = DB.getSQLValueEx(get_TrxName(), "SELECT " + m_KeyColumns[0] + " FROM " StringBuilder sql = new StringBuilder("SELECT ").append(m_KeyColumns[0]).append(" FROM ").append(p_info.getTableName()).append(" WHERE ").append(getUUIDColumnName()).append("=?");
+ p_info.getTableName() + " WHERE " + getUUIDColumnName() + "=?", get_ValueAsString(getUUIDColumnName())); int id = DB.getSQLValueEx(get_TrxName(), sql.toString(), get_ValueAsString(getUUIDColumnName()));
m_IDs[0] = Integer.valueOf(id); m_IDs[0] = Integer.valueOf(id);
set_ValueNoCheck(m_KeyColumns[0], m_IDs[0]); set_ValueNoCheck(m_KeyColumns[0], m_IDs[0]);
}
if (!Env.isUseCentralizedId(p_info.getTableName()))
{
int ki = p_info.getColumnIndex(m_KeyColumns[0]); int ki = p_info.getColumnIndex(m_KeyColumns[0]);
// Change Log - Only // Change Log - Only
String insertLog = MSysConfig.getValue(MSysConfig.SYSTEM_INSERT_CHANGELOG, "Y", getAD_Client_ID()); String insertLog = MSysConfig.getValue(MSysConfig.SYSTEM_INSERT_CHANGELOG, "Y", getAD_Client_ID());
if ( session != null if ( session != null
&& m_IDs.length == 1
&& p_info.isAllowLogging(ki) // logging allowed && p_info.isAllowLogging(ki) // logging allowed
&& !p_info.isEncrypted(ki) // not encrypted && !p_info.isEncrypted(ki) // not encrypted
&& !p_info.isVirtualColumn(ki) // no virtual column && !p_info.isVirtualColumn(ki) // no virtual column
&& !"Password".equals(p_info.getColumnName(ki)) && !"Password".equals(p_info.getColumnName(ki))
&& ( insertLog.equalsIgnoreCase("Y") && ( insertLog.equalsIgnoreCase("Y")
|| (insertLog.equalsIgnoreCase("K") && p_info.getColumn(ki).IsKey)) || ( insertLog.equalsIgnoreCase("K")
) && ( p_info.getColumn(ki).IsKey
|| p_info.getColumn(ki).ColumnName.equals(PO.getUUIDColumnName(p_info.getTableName()))))))
{ {
int id = (m_IDs.length == 1 ? get_ID() : 0);
// change log on new // change log on new
MChangeLog cLog = session.changeLog ( MChangeLog cLog = session.changeLog (
m_trxName, AD_ChangeLog_ID, m_trxName, AD_ChangeLog_ID,
p_info.getAD_Table_ID(), p_info.getColumn(ki).AD_Column_ID, p_info.getAD_Table_ID(), p_info.getColumn(ki).AD_Column_ID,
get_ID(), getAD_Client_ID(), getAD_Org_ID(), null, id, MChangeLog.EVENTCHANGELOG_Insert); (m_IDs.length == 1 ? get_ID() : 0), get_UUID(), getAD_Client_ID(), getAD_Org_ID(), null, (id == 0 ? get_UUID() : id), MChangeLog.EVENTCHANGELOG_Insert);
if (cLog != null) if (cLog != null)
AD_ChangeLog_ID = cLog.getAD_ChangeLog_ID(); AD_ChangeLog_ID = cLog.getAD_ChangeLog_ID();
} }
@ -3637,7 +3653,6 @@ public abstract class PO
// Change Log - Only // Change Log - Only
String insertLog = MSysConfig.getValue(MSysConfig.SYSTEM_INSERT_CHANGELOG, "Y", getAD_Client_ID()); String insertLog = MSysConfig.getValue(MSysConfig.SYSTEM_INSERT_CHANGELOG, "Y", getAD_Client_ID());
if (!generateScriptOnly && session != null if (!generateScriptOnly && session != null
&& m_IDs.length == 1
&& p_info.isAllowLogging(i) // logging allowed && p_info.isAllowLogging(i) // logging allowed
&& !p_info.isEncrypted(i) // not encrypted && !p_info.isEncrypted(i) // not encrypted
&& !p_info.isVirtualColumn(i) // no virtual column && !p_info.isVirtualColumn(i) // no virtual column
@ -3650,7 +3665,7 @@ public abstract class PO
MChangeLog cLog = session.changeLog ( MChangeLog cLog = session.changeLog (
m_trxName, AD_ChangeLog_ID, m_trxName, AD_ChangeLog_ID,
p_info.getAD_Table_ID(), p_info.getColumn(i).AD_Column_ID, p_info.getAD_Table_ID(), p_info.getColumn(i).AD_Column_ID,
get_ID(), getAD_Client_ID(), getAD_Org_ID(), null, value, MChangeLog.EVENTCHANGELOG_Insert); (m_IDs.length == 1 ? get_ID() : 0), get_UUID(), getAD_Client_ID(), getAD_Org_ID(), null, value, MChangeLog.EVENTCHANGELOG_Insert);
if (cLog != null) if (cLog != null)
AD_ChangeLog_ID = cLog.getAD_ChangeLog_ID(); AD_ChangeLog_ID = cLog.getAD_ChangeLog_ID();
} }
@ -3843,16 +3858,17 @@ public abstract class PO
*/ */
public boolean delete (boolean force) public boolean delete (boolean force)
{ {
checkImmutable();
checkValidContext();
checkCrossTenant(true);
CLogger.resetLast(); CLogger.resetLast();
if (is_new()) if (is_new())
return true; return true;
checkImmutable();
checkValidContext();
checkCrossTenant(true);
int AD_Table_ID = p_info.getAD_Table_ID(); int AD_Table_ID = p_info.getAD_Table_ID();
int Record_ID = get_ID(); int Record_ID = get_ID();
String Record_UU = get_UUID();
if (!force) if (!force)
{ {
@ -3951,6 +3967,8 @@ public abstract class PO
} }
// Delete Restrict AD_Table_ID/Record_ID (Requests, ..) // Delete Restrict AD_Table_ID/Record_ID (Requests, ..)
String errorMsg = PO_Record.exists(AD_Table_ID, Record_ID, m_trxName); String errorMsg = PO_Record.exists(AD_Table_ID, Record_ID, m_trxName);
if (errorMsg == null && Record_UU != null)
errorMsg = PO_Record.exists(AD_Table_ID, Record_UU, m_trxName);
if (errorMsg != null) if (errorMsg != null)
{ {
log.saveError("CannotDelete", errorMsg); log.saveError("CannotDelete", errorMsg);
@ -4000,9 +4018,14 @@ public abstract class PO
//delete cascade only for single key column record //delete cascade only for single key column record
PO_Record.deleteModelCascade(p_info.getTableName(), Record_ID, localTrxName); PO_Record.deleteModelCascade(p_info.getTableName(), Record_ID, localTrxName);
// Delete Cascade AD_Table_ID/Record_ID (Attachments, ..) // Delete Cascade AD_Table_ID/Record_ID (Attachments, ..)
PO_Record.deleteRecordIdCascade(AD_Table_ID, Record_ID, localTrxName); PO_Record.deleteRecordCascade(AD_Table_ID, Record_ID, localTrxName);
// Set referencing Record_ID Null AD_Table_ID/Record_ID // Set referencing Record_ID Null AD_Table_ID/Record_ID
PO_Record.setRecordIdNull(AD_Table_ID, Record_ID, localTrxName); PO_Record.setRecordNull(AD_Table_ID, Record_ID, localTrxName);
}
if (Record_UU != null) {
PO_Record.deleteModelCascade(p_info.getTableName(), Record_UU, localTrxName);
PO_Record.deleteRecordCascade(AD_Table_ID, Record_UU, localTrxName);
PO_Record.setRecordNull(AD_Table_ID, Record_UU, localTrxName);
} }
// The Delete Statement // The Delete Statement
@ -4082,7 +4105,7 @@ public abstract class PO
MChangeLog cLog = session.changeLog ( MChangeLog cLog = session.changeLog (
m_trxName != null ? m_trxName : localTrxName, AD_ChangeLog_ID, m_trxName != null ? m_trxName : localTrxName, AD_ChangeLog_ID,
AD_Table_ID, p_info.getColumn(i).AD_Column_ID, AD_Table_ID, p_info.getColumn(i).AD_Column_ID,
Record_ID, getAD_Client_ID(), getAD_Org_ID(), value, null, MChangeLog.EVENTCHANGELOG_Delete); (m_IDs.length == 1 ? Record_ID : 0), Record_UU, getAD_Client_ID(), getAD_Org_ID(), value, null, MChangeLog.EVENTCHANGELOG_Delete);
if (cLog != null) if (cLog != null)
AD_ChangeLog_ID = cLog.getAD_ChangeLog_ID(); AD_ChangeLog_ID = cLog.getAD_ChangeLog_ID();
} }
@ -5765,6 +5788,52 @@ public abstract class PO
throw new AdempiereException("Cross tenant ID " + recordId + " not allowed in " + ft.getTableName()); throw new AdempiereException("Cross tenant ID " + recordId + " not allowed in " + ft.getTableName());
} }
/**
* Verify Foreign key based on AD_Table_ID+Record_UU for cross tenant
* @return true if all the foreign keys are valid
*/
private void checkRecordUUCrossTenant() {
if (isSafeCrossTenant.get())
return;
int idxRecordUU = p_info.getColumnIndex("Record_UU");
if (idxRecordUU < 0)
return;
int idxTableId = p_info.getColumnIndex("AD_Table_ID");
if (idxTableId < 0)
return;
if ( ! (is_new() || is_ValueChanged(idxTableId) || is_ValueChanged(idxRecordUU)))
return;
int tableId = get_ValueAsInt(idxTableId);
if (tableId <= 0)
return;
String recordUU = get_ValueAsString(idxRecordUU);
if (Util.isEmpty(recordUU))
return;
MTable ft = MTable.get(getCtx(), tableId);
if (!ft.hasUUIDKey())
return; // no UUID key in table
boolean systemAccess = false;
String accessLevel = ft.getAccessLevel();
if ( MTable.ACCESSLEVEL_All.equals(accessLevel)
|| MTable.ACCESSLEVEL_SystemOnly.equals(accessLevel)
|| MTable.ACCESSLEVEL_SystemPlusClient.equals(accessLevel)) {
systemAccess = true;
}
StringBuilder sql = new StringBuilder("SELECT AD_Client_ID FROM ")
.append(ft.getTableName())
.append(" WHERE ")
.append(PO.getUUIDColumnName(ft.getTableName()))
.append("=?");
int pocid = DB.getSQLValue(get_TrxName(), sql.toString(), recordUU);
if (pocid < 0)
throw new AdempiereException("Foreign UUID " + recordUU + " not found in " + ft.getTableName());
if (pocid == 0 && !systemAccess)
throw new AdempiereException("System UUID " + recordUU + " cannot be used in " + ft.getTableName());
int curcid = getAD_Client_ID();
if (pocid > 0 && pocid != curcid)
throw new AdempiereException("Cross tenant UUID " + recordUU + " not allowed in " + ft.getTableName());
}
/** /**
* Returns a list of indexes for the foreign columns, null if none * Returns a list of indexes for the foreign columns, null if none
* @return array of int indexes * @return array of int indexes

View File

@ -16,6 +16,7 @@
*****************************************************************************/ *****************************************************************************/
package org.compiere.model; package org.compiere.model;
import java.io.Serializable;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.List; import java.util.List;
import java.util.logging.Level; import java.util.logging.Level;
@ -39,92 +40,73 @@ public class PO_Record
/* Cache for arrays of KeyNamePair<AD_Table_ID, TableName> for types of deletion: Cascade, Set Null, No Action */ /* Cache for arrays of KeyNamePair<AD_Table_ID, TableName> for types of deletion: Cascade, Set Null, No Action */
private static final CCache<String, KeyNamePair[]> s_po_record_tables_cache = new CCache<>(null, "PORecordTables", 100, 120, false); private static final CCache<String, KeyNamePair[]> s_po_record_tables_cache = new CCache<>(null, "PORecordTables", 100, 120, false);
/** Parent Tables */
private static int[] s_parents = new int[]{
X_C_Order.Table_ID
};
private static String[] s_parentNames = new String[]{
X_C_Order.Table_Name
};
private static int[] s_parentChilds = new int[]{
X_C_OrderLine.Table_ID
};
private static String[] s_parentChildNames = new String[]{
X_C_OrderLine.Table_Name
};
/** Logger */ /** Logger */
private static CLogger log = CLogger.getCLogger (PO_Record.class); private static CLogger log = CLogger.getCLogger (PO_Record.class);
/** /**
* Delete Cascade including (selected)parent relationships * Delete Cascade including (selected)parent relationships
* @param AD_Table_ID table * @param AD_Table_ID table
* @param Record_ID record * @param Record_IDorUU record ID (int) or UUID (String)
* @param trxName transaction * @param trxName transaction
* @return false if could not be deleted * @return false if could not be deleted
*/ */
static boolean deleteRecordIdCascade (int AD_Table_ID, int Record_ID, String trxName) protected static boolean deleteRecordCascade (int AD_Table_ID, Serializable Record_IDorUU, String trxName)
{ {
KeyNamePair[] cascades = getTablesWithRecordIDColumn(MColumn.FKCONSTRAINTTYPE_ModelCascade, trxName); int refId;
String columnName;
if (Record_IDorUU instanceof Integer) {
refId = DisplayType.RecordID;
columnName = "Record_ID";
} else if (Record_IDorUU instanceof String) {
refId = DisplayType.RecordUU;
columnName = "Record_UU";
} else {
throw new IllegalArgumentException(Record_IDorUU.getClass().getName() + " not supported for ID/UUID");
}
KeyNamePair[] cascades = getTablesWithConstraintType(refId, MColumn.FKCONSTRAINTTYPE_ModelCascade, trxName);
// Table Loop // Table Loop
StringBuilder whereClause = new StringBuilder("AD_Table_ID=? AND ").append(columnName).append("=?");
for (KeyNamePair table : cascades) for (KeyNamePair table : cascades)
{ {
// DELETE FROM table WHERE AD_Table_ID=#1 AND Record_ID=#2 // DELETE FROM table WHERE AD_Table_ID=#1 AND Record_ID=#2
List<PO> poList = new Query(Env.getCtx(), table.getName(), "AD_Table_ID=? AND Record_ID=?", trxName) List<PO> poList = new Query(Env.getCtx(), table.getName(), whereClause.toString(), trxName)
.setParameters(AD_Table_ID, Record_ID) .setParameters(AD_Table_ID, Record_IDorUU)
.list(); .list();
int count = 0; int count = 0;
for(PO po : poList) for(PO po : poList)
{ {
if (po.get_Table_ID() == AD_Table_ID && po.get_ID() == Record_ID) if ( po.get_Table_ID() == AD_Table_ID
&& ( (Record_IDorUU instanceof Integer && po.get_ID() == (Integer)Record_IDorUU)
|| (Record_IDorUU instanceof String && Record_IDorUU.equals(po.get_UUID()))
)
)
continue; continue;
po.deleteEx(true); po.deleteEx(true);
count++; count++;
} }
if (count > 0) if (count > 0)
if (log.isLoggable(Level.CONFIG)) log.config(table.getName() + " (" + AD_Table_ID + "/" + Record_ID + ") #" + count); if (log.isLoggable(Level.CONFIG)) log.config(table.getName() + " (" + AD_Table_ID + "/" + Record_IDorUU + ") #" + count);
}
// Parent Loop
for (int i = 0; i < s_parents.length; i++)
{
if (s_parents[i] == AD_Table_ID)
{
int AD_Table_IDchild = s_parentChilds[i];
for (KeyNamePair table : cascades)
{
String whereClause = " AD_Table_ID=? AND Record_ID IN (SELECT "
+ s_parentChildNames[i] + "_ID FROM "
+ s_parentChildNames[i] + " WHERE "
+ s_parentNames[i] + "_ID=?) ";
List<PO> poList = new Query(Env.getCtx(), table.getName(), whereClause, trxName)
.setParameters(AD_Table_IDchild, Record_ID)
.list();
int count = 0;
for(PO po : poList)
{
po.deleteEx(true);
count++;
}
if(count > 0) {
if (log.isLoggable(Level.CONFIG)) log.config(table.getName() + " " + s_parentNames[i]
+ " (" + AD_Table_ID + "/" + Record_ID + ") #" + count);
}
}
}
} }
return true; return true;
} // deleteCascade } // deleteRecordIdCascade
//IDEMPIERE-2060 //IDEMPIERE-2060
/** /**
* @param tableName * @param tableName
* @param Record_ID * @param Record_IDorUU record ID (int) or UUID (String)
* @param trxName * @param trxName
*/ */
public static void deleteModelCascade(String tableName, int Record_ID, String trxName) { public static void deleteModelCascade(String tableName, Serializable Record_IDorUU, String trxName) {
KeyNamePair[] tables = getTablesWithModelCascade(tableName, trxName); int refId;
if (Record_IDorUU instanceof Integer) {
refId = DisplayType.RecordID;
} else if (Record_IDorUU instanceof String) {
refId = DisplayType.RecordUU;
} else {
throw new IllegalArgumentException(Record_IDorUU.getClass().getName() + " not supported for ID/UUID");
}
KeyNamePair[] tables = getTablesWithModelCascade(refId, tableName, trxName);
for (KeyNamePair table : tables) { for (KeyNamePair table : tables) {
int dependentTableId = table.getKey(); int dependentTableId = table.getKey();
String dependentColumnName = table.getName(); String dependentColumnName = table.getName();
@ -132,7 +114,7 @@ public class PO_Record
List<PO> poList = new Query(Env.getCtx(), List<PO> poList = new Query(Env.getCtx(),
MTable.get(dependentTableId).getTableName(), MTable.get(dependentTableId).getTableName(),
dependentWhere, dependentWhere,
trxName).setParameters(Record_ID).list(); trxName).setParameters(Record_IDorUU).list();
for (PO po : poList) { for (PO po : poList) {
po.deleteEx(true, trxName); po.deleteEx(true, trxName);
} }
@ -140,12 +122,27 @@ public class PO_Record
} }
/** /**
* @param refId AD_Reference_ID - Record_ID or Record_UU
* @param tableName * @param tableName
* @param trxName * @param trxName
* @return * @return
*/ */
private static KeyNamePair[] getTablesWithModelCascade(String tableName, String trxName) { private static KeyNamePair[] getTablesWithModelCascade(int refId, String tableName, String trxName) {
StringBuilder key = new StringBuilder(MColumn.FKCONSTRAINTTYPE_ModelCascade).append("|").append(tableName); int refTableDirId;
int refTableId;
int refTableSearchId;
if (refId == DisplayType.RecordID) {
refTableDirId = DisplayType.TableDir;
refTableId = DisplayType.Table;
refTableSearchId = DisplayType.Search;
} else if (refId == DisplayType.RecordUU) {
refTableDirId = DisplayType.TableDirUU;
refTableId = DisplayType.TableUU;
refTableSearchId = DisplayType.SearchUU;
} else {
throw new IllegalArgumentException(refId + " not supported for ID/UUID");
}
StringBuilder key = new StringBuilder(MColumn.FKCONSTRAINTTYPE_ModelCascade).append("|").append(refId).append("|").append(tableName);
KeyNamePair[] tables = s_po_record_tables_cache.get(key.toString()); KeyNamePair[] tables = s_po_record_tables_cache.get(key.toString());
if (tables != null) if (tables != null)
return tables; return tables;
@ -159,12 +156,13 @@ public class PO_Record
+ "WHERE t.IsView = 'N' " + "WHERE t.IsView = 'N' "
+ " AND t.IsActive = 'Y' " + " AND t.IsActive = 'Y' "
+ " AND c.IsActive = 'Y' " + " AND c.IsActive = 'Y' "
+ " AND ( ( c.AD_Reference_ID = " + DisplayType.TableDir + " AND ( ( c.AD_Reference_ID = ? "
+ " AND c.ColumnName = ? || '_ID' ) " + " AND c.ColumnName = ? || '_ID' ) "
+ " OR ( c.AD_Reference_ID IN ( " + DisplayType.Table + ", " + DisplayType.Search + " ) " + " OR ( c.AD_Reference_ID IN (? , ?) "
+ " AND ( tr.TableName = ? OR ( tr.TableName IS NULL AND c.ColumnName = ? || '_ID' ) ) ) ) " + " AND ( tr.TableName = ? OR ( tr.TableName IS NULL AND c.ColumnName = ? || '_ID' ) ) ) ) "
+ " AND c.FKConstraintType = '" + MColumn.FKCONSTRAINTTYPE_ModelCascade + "' "; + " AND c.FKConstraintType = ?";
List<List<Object>> dependents = DB.getSQLArrayObjectsEx(trxName, sql, tableName, tableName, tableName); List<List<Object>> dependents = DB.getSQLArrayObjectsEx(trxName, sql,
refTableDirId, tableName, refTableId, refTableSearchId, tableName, tableName, MColumn.FKCONSTRAINTTYPE_ModelCascade);
if (dependents != null) { if (dependents != null) {
tables = new KeyNamePair[dependents.size()]; tables = new KeyNamePair[dependents.size()];
for (int i=0; i<dependents.size(); i++) { for (int i=0; i<dependents.size(); i++) {
@ -184,30 +182,50 @@ public class PO_Record
/** /**
* If a referencing Record ID or Record UU exists to the deleted record, set it to NULL * If a referencing Record ID or Record UU exists to the deleted record, set it to NULL
* @param AD_Table_ID * @param AD_Table_ID
* @param Record_ID * @param Record_IDorUU record ID (int) or UUID (String)
* @param trxName * @param trxName
*/ */
public static void setRecordIdNull(int AD_Table_ID, int Record_ID, String trxName){ public static void setRecordNull(int AD_Table_ID, Serializable Record_IDorUU, String trxName){
KeyNamePair[] tables = getTablesWithRecordIDColumn(MColumn.FKCONSTRAINTTYPE_ModelSetNull, trxName); int refId;
String columnName;
if (Record_IDorUU instanceof Integer) {
refId = DisplayType.RecordID;
columnName = "Record_ID";
} else if (Record_IDorUU instanceof String) {
refId = DisplayType.RecordUU;
columnName = "Record_UU";
} else {
throw new IllegalArgumentException(Record_IDorUU.getClass().getName() + " not supported for ID/UUID");
}
KeyNamePair[] tables = getTablesWithConstraintType(refId, MColumn.FKCONSTRAINTTYPE_ModelSetNull, trxName);
// Table loop // Table loop
StringBuilder whereClause = new StringBuilder("AD_Table_ID=? AND ").append(columnName).append("=?");
for (KeyNamePair table : tables) { for (KeyNamePair table : tables) {
List<PO> poList = new Query(Env.getCtx(), table.getName(), " AD_Table_ID = ? AND Record_ID = ? ", trxName) List<PO> poList = new Query(Env.getCtx(), table.getName(), whereClause.toString(), trxName)
.setParameters(AD_Table_ID, Record_ID) .setParameters(AD_Table_ID, Record_IDorUU)
.list(); .list();
int count = 0; int count = 0;
for(PO po : poList) { for(PO po : poList) {
if (po.get_Table_ID() == AD_Table_ID && po.get_ID() == Record_ID) if ( po.get_Table_ID() == AD_Table_ID
&& ( (Record_IDorUU instanceof Integer && po.get_ID() == (Integer) Record_IDorUU)
|| (Record_IDorUU instanceof String && Record_IDorUU.equals(po.get_UUID()))
)
)
continue; continue;
if (po.isColumnMandatory(po.get_ColumnIndex("Record_ID"))) if (po.isColumnMandatory(po.get_ColumnIndex(columnName))) {
po.set_Value("Record_ID", 0); if (Record_IDorUU instanceof Integer)
po.set_Value(columnName, 0);
else else
po.set_Value("Record_ID", null); po.set_Value(columnName, "");
} else {
po.set_Value(columnName, null);
}
po.saveEx(trxName); po.saveEx(trxName);
count++; count++;
} }
if (count > 0) { if (count > 0) {
if (log.isLoggable(Level.CONFIG)) log.config(table.getName() + " (" + AD_Table_ID + "/" + Record_ID + ") #" + count); if (log.isLoggable(Level.CONFIG)) log.config(table.getName() + " (" + AD_Table_ID + "/" + Record_IDorUU + ") #" + count);
} }
} }
} }
@ -215,22 +233,34 @@ public class PO_Record
/** /**
* An entry Exists for restrict table/record combination * An entry Exists for restrict table/record combination
* @param AD_Table_ID table * @param AD_Table_ID table
* @param Record_ID record * @param Record_IDorUU record ID (int) or UUID (String)
* @param trxName transaction * @param trxName transaction
* @return error message (Table Name) or null * @return error message (Table Name) or null
*/ */
static String exists (int AD_Table_ID, int Record_ID, String trxName) protected static String exists (int AD_Table_ID, Serializable Record_IDorUU, String trxName)
{ {
KeyNamePair[] restricts = getTablesWithRecordIDColumn(MColumn.FKCONSTRAINTTYPE_ModelNoAction_ForbidDeletion, trxName); int refId;
String columnName;
if (Record_IDorUU instanceof Integer) {
refId = DisplayType.RecordID;
columnName = "Record_ID";
} else if (Record_IDorUU instanceof String) {
refId = DisplayType.RecordUU;
columnName = "Record_UU";
} else {
log.warning(Record_IDorUU.getClass().getName() + " not supported for ID/UUID");
return null;
}
KeyNamePair[] restricts = getTablesWithConstraintType(refId, MColumn.FKCONSTRAINTTYPE_ModelNoAction_ForbidDeletion, trxName);
// Table Loop only // Table Loop only
for (int i = 0; i < restricts.length; i++) for (int i = 0; i < restricts.length; i++)
{ {
// SELECT 1 FROM table WHERE AD_Table_ID=#1 AND Record_ID=#2 FETCH FIRST 1 ROWS ONLY // SELECT 1 FROM table WHERE AD_Table_ID=#1 AND Record_ID=#2 FETCH FIRST 1 ROWS ONLY
StringBuilder sqlb = new StringBuilder ("SELECT 1 FROM ") StringBuilder sqlb = new StringBuilder ("SELECT 1 FROM ")
.append(restricts[i].getName()) .append(restricts[i].getName())
.append(" WHERE AD_Table_ID=? AND Record_ID=?"); .append(" WHERE AD_Table_ID=? AND ").append(columnName).append("=?");
String sql = DB.getDatabase().addPagingSQL(sqlb.toString(), 1, 1); String sql = DB.getDatabase().addPagingSQL(sqlb.toString(), 1, 1);
int no = DB.getSQLValueEx(trxName, sql.toString(), AD_Table_ID, Record_ID); int no = DB.getSQLValueEx(trxName, sql.toString(), AD_Table_ID, Record_IDorUU);
if (no == 1) if (no == 1)
return Msg.getMsg(Env.getCtx(), "DeleteErrorDependent") + " -> " + restricts[i].getName(); return Msg.getMsg(Env.getCtx(), "DeleteErrorDependent") + " -> " + restricts[i].getName();
} }
@ -238,26 +268,37 @@ public class PO_Record
} // exists } // exists
/** /**
* Get array of tables which has a Record_ID column with the defined Constraint Type * Get array of tables which has a refId column with the defined Constraint Type
* @param refId AD_Reference_ID - Record_ID or Record_UU
* @param constraintType - FKConstraintType of AD_Column * @param constraintType - FKConstraintType of AD_Column
* @param trxName * @param trxName
* @return array of KeyNamePair<AD_Table_ID, TableName> * @return array of KeyNamePair<AD_Table_ID, TableName>
*/ */
private static KeyNamePair[] getTablesWithRecordIDColumn(String constraintType, String trxName) { private static KeyNamePair[] getTablesWithConstraintType(int refId, String constraintType, String trxName) {
KeyNamePair[] tables = s_po_record_tables_cache.get(constraintType); String columnName;
if (refId == DisplayType.RecordID) {
columnName = "Record_ID";
} else if (refId == DisplayType.RecordUU) {
columnName = "Record_UU";
} else {
log.warning(refId + " not supported for ID/UUID");
return null;
}
StringBuilder key = new StringBuilder(constraintType).append("|").append(refId);
KeyNamePair[] tables = s_po_record_tables_cache.get(key.toString());
if (tables != null) if (tables != null)
return tables; return tables;
List<MTable> listTables = new Query(Env.getCtx(), MTable.Table_Name, "c.AD_Reference_ID=? AND c.FKConstraintType=? AND AD_Table.IsView='N'", trxName) List<MTable> listTables = new Query(Env.getCtx(), MTable.Table_Name, "c.AD_Reference_ID=? AND c.FKConstraintType=? AND AD_Table.IsView='N' AND c.ColumnName=?", trxName)
.addJoinClause("JOIN AD_Column c ON (c.AD_Table_ID=AD_Table.AD_Table_ID)") .addJoinClause("JOIN AD_Column c ON (c.AD_Table_ID=AD_Table.AD_Table_ID)")
.setOnlyActiveRecords(true) .setOnlyActiveRecords(true)
.setParameters(DisplayType.RecordID, constraintType) .setParameters(refId, constraintType, columnName)
.list(); .list();
tables = new KeyNamePair[listTables.size()]; tables = new KeyNamePair[listTables.size()];
for (int i=0; i<listTables.size(); i++) { for (int i=0; i<listTables.size(); i++) {
MTable table = listTables.get(i); MTable table = listTables.get(i);
tables[i] = new KeyNamePair(table.getAD_Table_ID(), table.getTableName()); tables[i] = new KeyNamePair(table.getAD_Table_ID(), table.getTableName());
} }
s_po_record_tables_cache.put(constraintType, tables); s_po_record_tables_cache.put(key.toString(), tables);
return tables; return tables;
} }

View File

@ -31,7 +31,7 @@ public class X_AD_ChangeLog extends PO implements I_AD_ChangeLog, I_Persistent
/** /**
* *
*/ */
private static final long serialVersionUID = 20230409L; private static final long serialVersionUID = 20230415L;
/** Standard Constructor */ /** Standard Constructor */
public X_AD_ChangeLog (Properties ctx, int AD_ChangeLog_ID, String trxName) public X_AD_ChangeLog (Properties ctx, int AD_ChangeLog_ID, String trxName)
@ -44,7 +44,6 @@ public class X_AD_ChangeLog extends PO implements I_AD_ChangeLog, I_Persistent
setAD_Session_ID (0); setAD_Session_ID (0);
setAD_Table_ID (0); setAD_Table_ID (0);
setIsCustomization (false); setIsCustomization (false);
setRecord_ID (0);
} */ } */
} }
@ -59,7 +58,6 @@ public class X_AD_ChangeLog extends PO implements I_AD_ChangeLog, I_Persistent
setAD_Session_ID (0); setAD_Session_ID (0);
setAD_Table_ID (0); setAD_Table_ID (0);
setIsCustomization (false); setIsCustomization (false);
setRecord_ID (0);
} */ } */
} }
@ -74,7 +72,6 @@ public class X_AD_ChangeLog extends PO implements I_AD_ChangeLog, I_Persistent
setAD_Session_ID (0); setAD_Session_ID (0);
setAD_Table_ID (0); setAD_Table_ID (0);
setIsCustomization (false); setIsCustomization (false);
setRecord_ID (0);
} */ } */
} }
@ -89,7 +86,6 @@ public class X_AD_ChangeLog extends PO implements I_AD_ChangeLog, I_Persistent
setAD_Session_ID (0); setAD_Session_ID (0);
setAD_Table_ID (0); setAD_Table_ID (0);
setIsCustomization (false); setIsCustomization (false);
setRecord_ID (0);
} */ } */
} }
@ -368,6 +364,21 @@ public class X_AD_ChangeLog extends PO implements I_AD_ChangeLog, I_Persistent
return ii.intValue(); return ii.intValue();
} }
/** Set Record UUID.
@param Record_UU Record UUID
*/
public void setRecord_UU (String Record_UU)
{
set_ValueNoCheck (COLUMNNAME_Record_UU, Record_UU);
}
/** Get Record UUID.
@return Record UUID */
public String getRecord_UU()
{
return (String)get_Value(COLUMNNAME_Record_UU);
}
/** Set Redo. /** Set Redo.
@param Redo Redo @param Redo Redo
*/ */

View File

@ -46,6 +46,7 @@ import org.compiere.util.DisplayType;
import org.compiere.util.Env; import org.compiere.util.Env;
import org.compiere.util.Msg; import org.compiere.util.Msg;
import org.compiere.util.NamePair; import org.compiere.util.NamePair;
import org.compiere.util.Util;
import org.zkoss.zk.ui.event.Event; import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener; import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.Events; import org.zkoss.zk.ui.event.Events;
@ -62,10 +63,15 @@ import org.zkoss.zul.South;
*/ */
public class WFieldRecordInfo extends Window implements EventListener<Event> public class WFieldRecordInfo extends Window implements EventListener<Event>
{ {
private static final long serialVersionUID = 3859352394520596098L; /**
*
*/
private static final long serialVersionUID = 439310027130417727L;
private int AD_Table_ID; private int AD_Table_ID;
private int AD_Column_ID; private int AD_Column_ID;
private int Record_ID; private int Record_ID;
private String Record_UU;
/** /**
* Record Info * Record Info
@ -73,8 +79,9 @@ public class WFieldRecordInfo extends Window implements EventListener<Event>
* @param AD_Table_ID * @param AD_Table_ID
* @param AD_Column_ID * @param AD_Column_ID
* @param Record_ID * @param Record_ID
* @param Record_UU
*/ */
public WFieldRecordInfo (String title, int AD_Table_ID, int AD_Column_ID, int Record_ID) public WFieldRecordInfo (String title, int AD_Table_ID, int AD_Column_ID, int Record_ID, String Record_UU)
{ {
super (); super ();
this.setTitle(title); this.setTitle(title);
@ -99,6 +106,7 @@ public class WFieldRecordInfo extends Window implements EventListener<Event>
this.AD_Table_ID = AD_Table_ID; this.AD_Table_ID = AD_Table_ID;
this.AD_Column_ID = AD_Column_ID; this.AD_Column_ID = AD_Column_ID;
this.Record_ID = Record_ID; this.Record_ID = Record_ID;
this.Record_UU = Record_UU;
try try
{ {
@ -186,13 +194,13 @@ public class WFieldRecordInfo extends Window implements EventListener<Event>
if (!MRole.PREFERENCETYPE_Client.equals(MRole.getDefault().getPreferenceType())) if (!MRole.PREFERENCETYPE_Client.equals(MRole.getDefault().getPreferenceType()))
return false; return false;
if (Record_ID == 0) if (Record_ID == 0 && Util.isEmpty(Record_UU))
return false; return false;
// Data // Data
String sql = "SELECT AD_Column_ID, Updated, UpdatedBy, OldValue, NewValue " String sql = "SELECT AD_Column_ID, Updated, UpdatedBy, OldValue, NewValue "
+ "FROM AD_ChangeLog " + "FROM AD_ChangeLog "
+ "WHERE AD_Table_ID=? AND Record_ID=? AND AD_Column_ID=? " + "WHERE AD_Table_ID=? AND (Record_ID=? OR Record_UU=?) AND AD_Column_ID=? "
+ "ORDER BY Updated DESC"; + "ORDER BY Updated DESC";
PreparedStatement pstmt = null; PreparedStatement pstmt = null;
ResultSet rs = null; ResultSet rs = null;
@ -201,7 +209,8 @@ public class WFieldRecordInfo extends Window implements EventListener<Event>
pstmt = DB.prepareStatement (sql, null); pstmt = DB.prepareStatement (sql, null);
pstmt.setInt (1, AD_Table_ID); pstmt.setInt (1, AD_Table_ID);
pstmt.setInt (2, Record_ID); pstmt.setInt (2, Record_ID);
pstmt.setInt (3, AD_Column_ID); pstmt.setString (3, Record_UU);
pstmt.setInt (4, AD_Column_ID);
rs = pstmt.executeQuery (); rs = pstmt.executeQuery ();
while (rs.next ()) while (rs.next ())
{ {
@ -379,7 +388,8 @@ public class WFieldRecordInfo extends Window implements EventListener<Event>
public static void start(GridField gridField) { public static void start(GridField gridField) {
new WFieldRecordInfo(gridField.getColumnName(), new WFieldRecordInfo(gridField.getColumnName(),
gridField.getGridTab().getAD_Table_ID(), gridField.getAD_Column_ID(), gridField.getGridTab().getAD_Table_ID(), gridField.getAD_Column_ID(),
gridField.getGridTab().getRecord_ID()); gridField.getGridTab().getRecord_ID(),
gridField.getGridTab().getRecord_UU());
} }
/** /**

View File

@ -317,6 +317,7 @@ public class WRecordInfo extends Window implements EventListener<Event>
int Record_ID = -1; int Record_ID = -1;
if (dse.Record_ID instanceof Integer) if (dse.Record_ID instanceof Integer)
Record_ID = ((Integer)dse.Record_ID).intValue(); Record_ID = ((Integer)dse.Record_ID).intValue();
String Record_UU = null;
MTable dbtable = null; MTable dbtable = null;
if (dse.AD_Table_ID != 0) if (dse.AD_Table_ID != 0)
@ -327,7 +328,6 @@ public class WRecordInfo extends Window implements EventListener<Event>
PO po = gridTable.getPO(dse.getCurrentRow()); PO po = gridTable.getPO(dse.getCurrentRow());
if (po != null) { if (po != null) {
String uuidcol = po.getUUIDColumnName(); String uuidcol = po.getUUIDColumnName();
String uuid = null;
if (po.is_new()) { if (po.is_new()) {
if (Record_ID == 0 && MTable.isZeroIDTable(dbtable.getTableName())) { if (Record_ID == 0 && MTable.isZeroIDTable(dbtable.getTableName())) {
// Need to read the UUID directly from database because the PO cannot be used with zero ID records // Need to read the UUID directly from database because the PO cannot be used with zero ID records
@ -338,13 +338,13 @@ public class WRecordInfo extends Window implements EventListener<Event>
.append(" WHERE ") .append(" WHERE ")
.append(dbtable.getTableName()) .append(dbtable.getTableName())
.append("_ID=0"); .append("_ID=0");
uuid = DB.getSQLValueString(null, sql.toString()); Record_UU = DB.getSQLValueString(null, sql.toString());
} }
} else { } else {
uuid = po.get_UUID(); Record_UU = po.get_UUID();
} }
if (!Util.isEmpty(uuid)) { if (!Util.isEmpty(Record_UU)) {
StringBuilder uuinfo = new StringBuilder(uuidcol).append("=").append(uuid); StringBuilder uuinfo = new StringBuilder(uuidcol).append("=").append(Record_UU);
if (! m_info.toString().contains(uuinfo)) if (! m_info.toString().contains(uuinfo))
m_info.append("\n ").append(uuinfo); m_info.append("\n ").append(uuinfo);
} }
@ -365,7 +365,7 @@ public class WRecordInfo extends Window implements EventListener<Event>
}); });
} }
m_permalink.setVisible(po.get_KeyColumns().length == 1); m_permalink.setVisible(po.get_KeyColumns().length == 1);
final String whereClause = po.get_WhereClause(true, uuid); final String whereClause = po.get_WhereClause(true, Record_UU);
m_copySelect.addEventListener(Events.ON_CLICK, new EventListener<Event>() { m_copySelect.addEventListener(Events.ON_CLICK, new EventListener<Event>() {
public void onEvent(Event event) throws Exception { public void onEvent(Event event) throws Exception {
StringBuffer query = new StringBuffer("navigator.clipboard.writeText(\"SELECT * FROM ") StringBuffer query = new StringBuffer("navigator.clipboard.writeText(\"SELECT * FROM ")
@ -396,13 +396,13 @@ public class WRecordInfo extends Window implements EventListener<Event>
if (!MRole.PREFERENCETYPE_Client.equals(MRole.getDefault().getPreferenceType())) if (!MRole.PREFERENCETYPE_Client.equals(MRole.getDefault().getPreferenceType()))
return false; return false;
if (Record_ID <= 0) if (Record_ID <= 0 && Util.isEmpty(Record_UU))
return false; return false;
// Data // Data
String sql = "SELECT AD_Column_ID, Updated, UpdatedBy, OldValue, NewValue " String sql = "SELECT AD_Column_ID, Updated, UpdatedBy, OldValue, NewValue "
+ "FROM AD_ChangeLog " + "FROM AD_ChangeLog "
+ "WHERE AD_Table_ID=? AND Record_ID=? " + "WHERE AD_Table_ID=? AND (Record_ID=? OR Record_UU=?) "
+ "ORDER BY Updated DESC"; + "ORDER BY Updated DESC";
PreparedStatement pstmt = null; PreparedStatement pstmt = null;
ResultSet rs = null; ResultSet rs = null;
@ -411,6 +411,7 @@ public class WRecordInfo extends Window implements EventListener<Event>
pstmt = DB.prepareStatement (sql, null); pstmt = DB.prepareStatement (sql, null);
pstmt.setInt (1, dse.AD_Table_ID); pstmt.setInt (1, dse.AD_Table_ID);
pstmt.setInt (2, Record_ID); pstmt.setInt (2, Record_ID);
pstmt.setString (3, Record_UU);
rs = pstmt.executeQuery (); rs = pstmt.executeQuery ();
while (rs.next ()) while (rs.next ())
{ {