From 5a3712abad939a20cc4d29bf9854de3f16c4e4b9 Mon Sep 17 00:00:00 2001 From: ninoish Date: Thu, 27 May 2021 21:19:16 +0900 Subject: [PATCH 01/10] avoid using SOQL subqueries for query locator error --- .../main/default/classes/CloneQueryResult.cls | 9 + .../classes/CloneQueryResult.cls-meta.xml | 5 + .../default/classes/RecordCloneController.cls | 11 +- .../default/classes/RecordCloneHandler.cls | 252 +++++++++++------- .../classes/RecordCloneHandlerTest.cls | 96 +++++-- package.json | 2 +- 6 files changed, 262 insertions(+), 113 deletions(-) create mode 100644 force-app/main/default/classes/CloneQueryResult.cls create mode 100644 force-app/main/default/classes/CloneQueryResult.cls-meta.xml diff --git a/force-app/main/default/classes/CloneQueryResult.cls b/force-app/main/default/classes/CloneQueryResult.cls new file mode 100644 index 0000000..9528b24 --- /dev/null +++ b/force-app/main/default/classes/CloneQueryResult.cls @@ -0,0 +1,9 @@ +public class CloneQueryResult { + public CloneQueryResult(sObject parent, Map> childrens) { + this.parent = parent; + this.childrens = childrens; + } + + public sObject parent; + public Map> childrens; +} diff --git a/force-app/main/default/classes/CloneQueryResult.cls-meta.xml b/force-app/main/default/classes/CloneQueryResult.cls-meta.xml new file mode 100644 index 0000000..d75b058 --- /dev/null +++ b/force-app/main/default/classes/CloneQueryResult.cls-meta.xml @@ -0,0 +1,5 @@ + + + 51.0 + Active + diff --git a/force-app/main/default/classes/RecordCloneController.cls b/force-app/main/default/classes/RecordCloneController.cls index 95ce86c..12ab22c 100755 --- a/force-app/main/default/classes/RecordCloneController.cls +++ b/force-app/main/default/classes/RecordCloneController.cls @@ -72,18 +72,19 @@ global with sharing class RecordCloneController { } // get the original record - sObject originalRecord = RecordCloneHandler.getOriginalRecord( + CloneQueryResult queryResult = RecordCloneHandler.getOriginalRecord( recordId, sObjSummary ); - if (originalRecord == null) { + if (queryResult == null) { return null; } // exec clone sObject clonedRecord = RecordCloneHandler.cloneRecord( - originalRecord, + queryResult.parent, + queryResult.childrens, sObjSummary, newParentRecordName, childRecordNameType @@ -114,14 +115,14 @@ global with sharing class RecordCloneController { sObjectSummary sObjSummary = sObjectSummary.getByRecordId(rids[0]); // get original records - List originalRecords = RecordCloneHandler.getOriginalRecords( + List cloneQueryResults = RecordCloneHandler.getOriginalRecords( rids, sObjSummary ); // exec clone Map clonedRecords = new Map( - RecordCloneHandler.cloneRecords(originalRecords, sObjSummary) + RecordCloneHandler.cloneRecords(cloneQueryResults, sObjSummary) ); clonedRecordList.add(new List(clonedRecords.keySet())); diff --git a/force-app/main/default/classes/RecordCloneHandler.cls b/force-app/main/default/classes/RecordCloneHandler.cls index 743b5c9..f9ceaa7 100755 --- a/force-app/main/default/classes/RecordCloneHandler.cls +++ b/force-app/main/default/classes/RecordCloneHandler.cls @@ -6,7 +6,7 @@ */ public with sharing class RecordCloneHandler { // single records - public static sObject getOriginalRecord( + public static CloneQueryResult getOriginalRecord( Id recordId, sObjectSummary sObjSummary ) { @@ -19,20 +19,25 @@ public with sharing class RecordCloneHandler { String q = 'SELECT Id, ' + String.join(sObjSummary.filterClonableFields(), ',') + - buildChildrenSelectQuery(sObjSummary) + ' FROM ' + sObjSummary.apiName + ' WHERE Id = \'' + recordId + '\''; - return Database.query(q); + sObject parent = Database.query(q); + Map> childrens = execChildQueries( + recordId, + sObjSummary + ); + CloneQueryResult cqr = new CloneQueryResult(parent, childrens); + return cqr; } catch (System.QueryException e) { return null; } } // multiple records - public static List getOriginalRecords( + public static List getOriginalRecords( List recordIds, sObjectSummary sObjSummary ) { @@ -45,20 +50,105 @@ public with sharing class RecordCloneHandler { String q = 'SELECT Id, ' + String.join(sObjSummary.filterClonableFields(), ',') + - buildChildrenSelectQuery(sObjSummary) + ' FROM ' + sObjSummary.apiName + ' WHERE Id IN (\'' + String.join(recordIds, '\',\'') + '\' )'; - return Database.query(q); + List parents = Database.query(q); + + Map>> childrens = execChildrenQueries( + recordIds, + sObjSummary + ); + List cqrs = new List(); + for (sObject parent : parents) { + Map> children = childrens.get(parent.Id); + cqrs.add(new CloneQueryResult(parent, children)); + } + + return cqrs; } catch (System.QueryException e) { return null; } } + public static Map> execChildQueries( + Id parentId, + sObjectSummary parentSummary + ) { + Map> childrens = new Map>(); + if (parentSummary.children != null && parentSummary.children.size() > 0) { + for (String childRelName : parentSummary.children.keySet()) { + sObjectSummary child = parentSummary.children.get(childRelName); + List childFields = child.filterClonableFields(); + if (child.isClonable && childFields.size() > 0) { + String q = + 'SELECT Id, ' + + String.join(childFields, ',') + + ' FROM ' + + child.apiName + + ' WHERE ' + + child.parentRelationshipField + + ' = \'' + + parentId + + '\''; + List res = Database.query(q); + childrens.put(childRelName, res); + } + } + } + return childrens; + } + + public static Map>> execChildrenQueries( + List parentIds, + sObjectSummary parentSummary + ) { + Map>> childrens = new Map>>(); + Map> children = new Map>(); + if (parentSummary.children != null && parentSummary.children.size() > 0) { + for (String childRelName : parentSummary.children.keySet()) { + sObjectSummary child = parentSummary.children.get(childRelName); + List childFields = child.filterClonableFields(); + childFields.add(child.parentRelationshipField); + childFields = new List(new Set(childFields)); + if (child.isClonable && childFields.size() > 0) { + String q = + 'SELECT Id, ' + + String.join(childFields, ',') + + ' FROM ' + + child.apiName + + ' WHERE ' + + child.parentRelationshipField + + ' IN (\'' + + String.join(parentIds, '\',\'') + + '\')'; + List records = Database.query(q); + for (sObject record : records) { + Id pId = (Id) record.get(child.parentRelationshipField); + if (childrens.get(pId) == null) { + childrens.put( + pId, + new Map>{ + childRelName => new List{ record } + } + ); + } else if (childrens.get(pId).get(childRelName) == null) { + childrens.get(pId).put(childRelName, new List{ record }); + } else { + childrens.get(pId).get(childRelName).add(record); + } + } + } + } + } + return childrens; + } + public static sObject cloneRecord( sObject cloned, + Map> childrens, sObjectSummary sObjectSummary, String newRecordName, String childRecordNameType @@ -87,33 +177,14 @@ public with sharing class RecordCloneHandler { String todayStr = Datetime.now().format('yyyy/MM/dd HH:mm'); // remove child record IDs, assign then cloned parent record Id to ref field - for (String childRelName : sObjectSummary.children.keySet()) { + for (String childRelName : childrens.keySet()) { sObjectSummary childSummary = sObjectSummary.children.get(childRelName); if (!childSummary.isClonable) { continue; } - List children; - try { - try { - // System.QueryException: Aggregate query has too many rows for direct assignment, use FOR loop - // https://www.kineticgrowth.com/system-queryexception-aggregate-query-many-rows-direct-assignment-use-loop/ - // https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/langCon_apex_loops_for_SOQL.htm - // There are weird cases. - // https://developer.salesforce.com/forums/?id=906F0000000AtTyIAK - children = cloned.getSObjects(childRelName); - } catch (System.QueryException qe) { - System.debug('QueryException. Now try using for loop.'); - children = new List(); - for (sObject child : cloned.getSObjects(childRelName)) { - children.add(child); - } - } - } catch (System.SObjectException e) { - System.debug('SObjectException'); - continue; - } + List children = childrens.get(childRelName); if (children == null || children.size() == 0) { continue; } @@ -164,39 +235,29 @@ public with sharing class RecordCloneHandler { children[i].put(childSummary.parentRelationshipField, cloned.Id); } insert children; + + System.debug(childSummary.apiName + ' : ' + children.size()); } } return cloned; } - private static String buildChildrenSelectQuery(sObjectSummary sObjSummary) { - // concatenate clonable field names of child sObjects to build SOQL string - String childrenSelect = ''; - if (sObjSummary.children != null && sObjSummary.children.size() > 0) { - for (String childRelName : sObjSummary.children.keySet()) { - sObjectSummary child = sObjSummary.children.get(childRelName); - List childFields = child.filterClonableFields(); - if (child.isClonable && childFields.size() > 0) { - childrenSelect += - ', ( SELECT Id, ' + - String.join(childFields, ',') + - ' FROM ' + - childRelName + - ' )'; - } - } - } - return childrenSelect; - } - public static List cloneRecords( - List clonings, + List results, sObjectSummary sObjectSummary ) { // append to child records' name fields String todayStr = Datetime.now().format('yyyy/MM/dd HH:mm'); - for (sObject cloning : clonings) { + + List parents = new List(); + for (CloneQueryResult result : results) { + parents.add(result.parent); + } + + List oldIds = new List(); + for (sObject cloning : parents) { + oldIds.add(cloning.Id); cloning.Id = null; if (sObjectSummary.isNamable) { // Parent record name @@ -224,7 +285,14 @@ public with sharing class RecordCloneHandler { } } - insert clonings; + // insert parent records + insert parents; + + // create a map matching old and new ids; + Map idMap = new Map(); + for (Integer i = 0; i < parents.size(); i++) { + idMap.put(oldIds[i], parents[i].Id); + } if ( sObjectSummary.children != null && @@ -241,51 +309,51 @@ public with sharing class RecordCloneHandler { continue; } - for (sObject cloning : clonings) { - sObject[] children; - try { - children = cloning.getSObjects(childRelName); - } catch (System.SObjectException e) { - continue; - } - - if (children == null) { - continue; - } - - // On creating an opportunity record, system automatically creates an OpportunityContactRole record. - // Delete the created record as of now. - if ( - sObjectSummary.apiName == 'Opportunity' && - childRelName == 'OpportunityContactRoles' - ) { - ocrIsDeletable = sObjectSummary.isDeletable; - ocrIds.add(cloning.Id); - } + for (CloneQueryResult result : results) { + for (String oldParentId : result.childrens.keySet()) { + List children = result.childrens.get(childRelName); + if (children == null) { + continue; + } + Id newId = idMap.get(oldParentId); + // On creating an opportunity record, system automatically creates an OpportunityContactRole record. + // Delete the created record as of now. + if ( + sObjectSummary.apiName == 'Opportunity' && + childRelName == 'OpportunityContactRoles' + ) { + ocrIsDeletable = sObjectSummary.isDeletable; + ocrIds.add(newId); + } - for (Integer i = 0; i < children.size(); i++) { - children[i].put('Id', null); - if (childSummary.isNamable) { - String originalRecordName = (String) children[i] - .get(childSummary.nameField); - String nameValue = originalRecordName; - if ( - sObjectSummary.childRecordNameFormat == - RecordCloneChildRecordNamePicklist.valueWithCloned - ) { - nameValue = 'Cloned - ' + originalRecordName; - } else if ( - sObjectSummary.childRecordNameFormat == - RecordCloneChildRecordNamePicklist.valueWithClonedAndDate - ) { - nameValue = 'Cloned - ' + originalRecordName + ' - ' + todayStr; + for (Integer i = 0; i < children.size(); i++) { + children[i].put('Id', null); + if (childSummary.isNamable) { + String originalRecordName = (String) children[i] + .get(childSummary.nameField); + String nameValue = originalRecordName; + if ( + sObjectSummary.childRecordNameFormat == + RecordCloneChildRecordNamePicklist.valueWithCloned + ) { + nameValue = 'Cloned - ' + originalRecordName; + } else if ( + sObjectSummary.childRecordNameFormat == + RecordCloneChildRecordNamePicklist.valueWithClonedAndDate + ) { + nameValue = + 'Cloned - ' + + originalRecordName + + ' - ' + + todayStr; + } + children[i].put(childSummary.nameField, nameValue); } - children[i].put(childSummary.nameField, nameValue); - } - children[i].put(childSummary.parentRelationshipField, cloning.Id); + children[i].put(childSummary.parentRelationshipField, newId); + } + childrens.addAll(children); } - childrens.addAll(children); } } @@ -306,6 +374,6 @@ public with sharing class RecordCloneHandler { insert childrens; } - return clonings; + return parents; } } diff --git a/force-app/main/default/classes/RecordCloneHandlerTest.cls b/force-app/main/default/classes/RecordCloneHandlerTest.cls index 45aa47e..63d956a 100755 --- a/force-app/main/default/classes/RecordCloneHandlerTest.cls +++ b/force-app/main/default/classes/RecordCloneHandlerTest.cls @@ -13,30 +13,77 @@ private class RecordCloneHandlerTest { insert acc; insert con; + Account acc2 = new Account(Name = 'TestAccount2'); + insert acc2; + List cons = new List(); + for (Integer i = 0; i < 200; i++) { + cons.add(new Contact(LastName = 'TestContact' + i, AccountId = acc2.Id)); + } + insert cons; + List opps = new List(); + for (Integer i = 0; i < 100; i++) { + opps.add( + new Opportunity( + Name = 'TestOpp' + i, + StageName = 'New', + CloseDate = Date.today(), + AccountId = acc2.Id + ) + ); + } + insert opps; + + List cases = new List(); + for (Integer i = 0; i < 50; i++) { + cases.add(new Case(Subject = 'TestOpp' + i, AccountId = acc2.Id)); + } + insert cases; + sObjectSummary accSummary = new sObjectSummary( Account.sObjectType.getDescribe() ); - sObject withNullRecordId = RecordCloneHandler.getOriginalRecord( + CloneQueryResult withNullRecordId = RecordCloneHandler.getOriginalRecord( null, accSummary ); System.assertEquals(withNullRecordId, null); - sObject withInvalidRecordId = RecordCloneHandler.getOriginalRecord( + CloneQueryResult withInvalidRecordId = RecordCloneHandler.getOriginalRecord( con.Id, accSummary ); System.assertEquals(withInvalidRecordId, null); - sObject withNullSummary = RecordCloneHandler.getOriginalRecord( + CloneQueryResult withNullSummary = RecordCloneHandler.getOriginalRecord( acc.Id, null ); System.assertEquals(withNullSummary, null); - sObject withCorrectRecordId = RecordCloneHandler.getOriginalRecord( + CloneQueryResult withCorrectRecordId = RecordCloneHandler.getOriginalRecord( acc.Id, accSummary ); System.assertNotEquals(withCorrectRecordId, null); + + sObjectSummary accSummary2 = sObjectSummary.getByRecordId( + acc2.Id, + 'Contacts,Opportunities,Cases', + '' + ); + CloneQueryResult acc2WithManyChildren = RecordCloneHandler.getOriginalRecord( + acc2.Id, + accSummary2 + ); + + // exec clone + sObject clonedAcc2WithManyChildren = RecordCloneHandler.cloneRecord( + acc2WithManyChildren.parent, + acc2WithManyChildren.childrens, + accSummary2, + 'hello', + RecordCloneChildRecordNamePicklist.valueWithClonedAndDate + ); + + System.debug(clonedAcc2WithManyChildren); } @IsTest @@ -57,28 +104,28 @@ private class RecordCloneHandlerTest { Account.sObjectType.getDescribe() ); - List withNullRecordIds = RecordCloneHandler.getOriginalRecords( + List withNullRecordIds = RecordCloneHandler.getOriginalRecords( null, accSummary ); System.assertEquals(withNullRecordIds, null); - List withEmptyRecordIds = RecordCloneHandler.getOriginalRecords( + List withEmptyRecordIds = RecordCloneHandler.getOriginalRecords( new List(), accSummary ); System.assertEquals(withEmptyRecordIds, null); - List withInvalidRecordIds = RecordCloneHandler.getOriginalRecords( + List withInvalidRecordIds = RecordCloneHandler.getOriginalRecords( conIds, accSummary ); System.assertNotEquals(withInvalidRecordIds, null); System.assertEquals(withInvalidRecordIds.size(), 0); - List withNullSummary = RecordCloneHandler.getOriginalRecords( + List withNullSummary = RecordCloneHandler.getOriginalRecords( accIds, null ); System.assertEquals(withNullSummary, null); - List withCorrectRecordId = RecordCloneHandler.getOriginalRecords( + List withCorrectRecordId = RecordCloneHandler.getOriginalRecords( accIds, accSummary ); @@ -121,9 +168,17 @@ private class RecordCloneHandlerTest { 'StageName' ); + Map> accChildren = new Map>{ + 'Contacts' => new List{ con } + }; + Map> oppChildren = new Map>{ + 'OpportunityContactRoles' => new List{ ocr } + }; + // no child, no excluded sObject cloned1 = RecordCloneHandler.cloneRecord( acc, + accChildren, accSummary1, newRecordName, RecordCloneChildRecordNamePicklist.valueWithClonedAndDate @@ -138,6 +193,7 @@ private class RecordCloneHandlerTest { ); sObject cloned2 = RecordCloneHandler.cloneRecord( acc, + accChildren, accSummary2, newRecordName, RecordCloneChildRecordNamePicklist.valueWithCloned @@ -148,6 +204,7 @@ private class RecordCloneHandlerTest { accSummary1.isNamable = false; sObject notNamable = RecordCloneHandler.cloneRecord( acc, + accChildren, accSummary1, null, RecordCloneChildRecordNamePicklist.valueWithClonedAndDate @@ -157,6 +214,7 @@ private class RecordCloneHandlerTest { // with opp and auto created opp contact role sObject oppCloned = RecordCloneHandler.cloneRecord( opp, + oppChildren, oppSummary1, newRecordName, RecordCloneChildRecordNamePicklist.valueOriginalName @@ -193,15 +251,23 @@ private class RecordCloneHandlerTest { Account.sObjectType.getDescribe() ); - List originals = new List(); - originals.add(acc); - originals.add(acc2); + List originals = new List(); + originals.add(acc.Id); + originals.add(acc2.Id); - List clones = RecordCloneHandler.cloneRecords( + // get original records + List cloneQueryResults = RecordCloneHandler.getOriginalRecords( originals, accSummary1 ); - System.assertNotEquals(clones, null); - System.assertEquals(clones.size(), 2); + + // exec clone + List cloneds = RecordCloneHandler.cloneRecords( + cloneQueryResults, + accSummary1 + ); + + System.assertNotEquals(cloneds, null); + System.assertEquals(cloneds.size(), 2); } } diff --git a/package.json b/package.json index b7f575b..05da92e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "salesforce-app", "private": true, - "version": "1.0.0", + "version": "2.0.0", "description": "Salesforce App", "scripts": { "lint": "npm run lint:lwc", From 4808ff1d618bc2e0dafbfd02cc3123651afbff38 Mon Sep 17 00:00:00 2001 From: ninoish Date: Fri, 28 May 2021 14:30:27 +0900 Subject: [PATCH 02/10] fix multiple cloning in certain env bug --- .../default/classes/RecordCloneHandler.cls | 112 ++++++++------- .../main/default/classes/RecordCloneTest.cls | 133 +++++++++++++----- .../default/lwc/recordClone/recordClone.html | 8 +- .../recordCloneFieldItem.css | 44 ++++++ .../recordCloneFieldItem.html | 42 ++++++ .../recordCloneFieldItem.js | 46 ++++++ .../recordCloneFieldItem.js-meta.xml | 11 ++ .../recordCloneRelatedListItem.html | 4 +- package.json | 6 +- 9 files changed, 310 insertions(+), 96 deletions(-) create mode 100644 force-app/main/default/lwc/recordCloneFieldItem/recordCloneFieldItem.css create mode 100644 force-app/main/default/lwc/recordCloneFieldItem/recordCloneFieldItem.html create mode 100644 force-app/main/default/lwc/recordCloneFieldItem/recordCloneFieldItem.js create mode 100644 force-app/main/default/lwc/recordCloneFieldItem/recordCloneFieldItem.js-meta.xml diff --git a/force-app/main/default/classes/RecordCloneHandler.cls b/force-app/main/default/classes/RecordCloneHandler.cls index f9ceaa7..6776bdf 100755 --- a/force-app/main/default/classes/RecordCloneHandler.cls +++ b/force-app/main/default/classes/RecordCloneHandler.cls @@ -111,7 +111,30 @@ public with sharing class RecordCloneHandler { for (String childRelName : parentSummary.children.keySet()) { sObjectSummary child = parentSummary.children.get(childRelName); List childFields = child.filterClonableFields(); - childFields.add(child.parentRelationshipField); + + List parentRelFieldNameArr = child.parentRelationshipField.split( + '__' + ); + System.debug(child.parentRelationshipField + ' ' + parentRelFieldNameArr.size()); + if (parentRelFieldNameArr.size() > 2) { + // with name space. + Boolean hasMatched = false; + for (String childField : childFields) { + System.debug(childField + ' vs ' + parentRelFieldNameArr[1] + parentRelFieldNameArr[2]); + if ( + childField == child.parentRelationshipField || + childField == parentRelFieldNameArr[1] + '__' + parentRelFieldNameArr[2] + ) { + hasMatched = true; + } + } + + if(!hasMatched) { + childFields.add(child.parentRelationshipField); + } + } else { + childFields.add(child.parentRelationshipField); + } childFields = new List(new Set(childFields)); if (child.isClonable && childFields.size() > 0) { String q = @@ -288,12 +311,6 @@ public with sharing class RecordCloneHandler { // insert parent records insert parents; - // create a map matching old and new ids; - Map idMap = new Map(); - for (Integer i = 0; i < parents.size(); i++) { - idMap.put(oldIds[i], parents[i].Id); - } - if ( sObjectSummary.children != null && sObjectSummary.children.keySet().size() > 0 @@ -310,50 +327,47 @@ public with sharing class RecordCloneHandler { } for (CloneQueryResult result : results) { - for (String oldParentId : result.childrens.keySet()) { - List children = result.childrens.get(childRelName); - if (children == null) { - continue; - } - Id newId = idMap.get(oldParentId); - // On creating an opportunity record, system automatically creates an OpportunityContactRole record. - // Delete the created record as of now. - if ( - sObjectSummary.apiName == 'Opportunity' && - childRelName == 'OpportunityContactRoles' - ) { - ocrIsDeletable = sObjectSummary.isDeletable; - ocrIds.add(newId); - } + if (result.childrens == null) { + continue; + } + List children = result.childrens.get(childRelName); + if (children == null) { + continue; + } + // On creating an opportunity record, system automatically creates an OpportunityContactRole record. + // Delete the created record as of now. + if ( + sObjectSummary.apiName == 'Opportunity' && + childRelName == 'OpportunityContactRoles' + ) { + ocrIsDeletable = sObjectSummary.isDeletable; + ocrIds.add(result.parent.Id); + } - for (Integer i = 0; i < children.size(); i++) { - children[i].put('Id', null); - if (childSummary.isNamable) { - String originalRecordName = (String) children[i] - .get(childSummary.nameField); - String nameValue = originalRecordName; - if ( - sObjectSummary.childRecordNameFormat == - RecordCloneChildRecordNamePicklist.valueWithCloned - ) { - nameValue = 'Cloned - ' + originalRecordName; - } else if ( - sObjectSummary.childRecordNameFormat == - RecordCloneChildRecordNamePicklist.valueWithClonedAndDate - ) { - nameValue = - 'Cloned - ' + - originalRecordName + - ' - ' + - todayStr; - } - children[i].put(childSummary.nameField, nameValue); + for (Integer i = 0; i < children.size(); i++) { + children[i].put('Id', null); + if (childSummary.isNamable) { + String originalRecordName = (String) children[i] + .get(childSummary.nameField); + String nameValue = originalRecordName; + if ( + sObjectSummary.childRecordNameFormat == + RecordCloneChildRecordNamePicklist.valueWithCloned + ) { + nameValue = 'Cloned - ' + originalRecordName; + } else if ( + sObjectSummary.childRecordNameFormat == + RecordCloneChildRecordNamePicklist.valueWithClonedAndDate + ) { + nameValue = 'Cloned - ' + originalRecordName + ' - ' + todayStr; } - - children[i].put(childSummary.parentRelationshipField, newId); + children[i].put(childSummary.nameField, nameValue); } - childrens.addAll(children); + + children[i] + .put(childSummary.parentRelationshipField, result.parent.Id); } + childrens.addAll(children); } } @@ -371,7 +385,9 @@ public with sharing class RecordCloneHandler { } } - insert childrens; + if (childrens.size() > 0) { + insert childrens; + } } return parents; diff --git a/force-app/main/default/classes/RecordCloneTest.cls b/force-app/main/default/classes/RecordCloneTest.cls index 0ccb3ab..1b14012 100755 --- a/force-app/main/default/classes/RecordCloneTest.cls +++ b/force-app/main/default/classes/RecordCloneTest.cls @@ -349,16 +349,25 @@ private class RecordCloneTest { ); System.assertNotEquals(noYesNoCloned1, null); System.assertNotEquals(noYesNoCloned1.Id, null); + + sObject noYesNoCloned1Queried = [ + SELECT Id, (SELECT Id FROM CaseContactRoles), (SELECT Id FROM Cases) + FROM Case + WHERE Id = :noYesNoCloned1.Id + ]; System.assertNotEquals( - noYesNoCloned1.getSObjects('CaseContactRoles'), + noYesNoCloned1Queried.getSObjects('CaseContactRoles'), null ); System.assertNotEquals( - noYesNoCloned1.getSObjects('CaseContactRoles').size(), + noYesNoCloned1Queried.getSObjects('CaseContactRoles').size(), + 0 + ); + System.assertNotEquals(noYesNoCloned1Queried.getSObjects('Cases'), null); + System.assertNotEquals( + noYesNoCloned1Queried.getSObjects('Cases').size(), 0 ); - System.assertNotEquals(noYesNoCloned1.getSObjects('Cases'), null); - System.assertNotEquals(noYesNoCloned1.getSObjects('Cases').size(), 0); sObject yesYesNoCloned1 = RecordCloneController.execClone( a.Id, @@ -367,12 +376,18 @@ private class RecordCloneTest { null, RecordCloneChildRecordNamePicklist.valueWithClonedAndDate ); + + sObject yesYesNoCloned1Queried = [ + SELECT Id, (SELECT Id FROM Contacts), (SELECT Id FROM Cases) + FROM Account + WHERE Id = :yesYesNoCloned1.Id + ]; System.assertNotEquals(yesYesNoCloned1, null); System.assertNotEquals(yesYesNoCloned1.Id, null); - System.assertNotEquals(yesYesNoCloned1.getSObjects('Cases'), null); - System.assertNotEquals(yesYesNoCloned1.getSObjects('Cases').size(), 0); - System.assertNotEquals(yesYesNoCloned1.getSObjects('Contacts'), null); - System.assertNotEquals(yesYesNoCloned1.getSObjects('Contacts').size(), 0); + System.assertNotEquals(yesYesNoCloned1Queried.getSObjects('Cases'), null); + System.assertNotEquals(yesYesNoCloned1Queried.getSObjects('Cases').size(), 0); + System.assertNotEquals(yesYesNoCloned1Queried.getSObjects('Contacts'), null); + System.assertNotEquals(yesYesNoCloned1Queried.getSObjects('Contacts').size(), 0); sObject yesYesNoCloned2 = RecordCloneController.execClone( a.Id, @@ -381,15 +396,20 @@ private class RecordCloneTest { '', RecordCloneChildRecordNamePicklist.valueWithCloned ); + sObject yesYesNoCloned2Queried = [ + SELECT Id, (SELECT Id FROM Opportunities), (SELECT Id FROM Contacts) + FROM Account + WHERE Id = :yesYesNoCloned2.Id + ]; System.assertNotEquals(yesYesNoCloned2, null); System.assertNotEquals(yesYesNoCloned2.Id, null); - System.assertNotEquals(yesYesNoCloned2.getSObjects('Opportunities'), null); + System.assertNotEquals(yesYesNoCloned2Queried.getSObjects('Opportunities'), null); System.assertNotEquals( - yesYesNoCloned2.getSObjects('Opportunities').size(), + yesYesNoCloned2Queried.getSObjects('Opportunities').size(), 0 ); - System.assertNotEquals(yesYesNoCloned2.getSObjects('Contacts'), null); - System.assertNotEquals(yesYesNoCloned2.getSObjects('Contacts').size(), 0); + System.assertNotEquals(yesYesNoCloned2Queried.getSObjects('Contacts'), null); + System.assertNotEquals(yesYesNoCloned2Queried.getSObjects('Contacts').size(), 0); // without relationship, with excludedFields sObject noNoYesCloned1 = RecordCloneController.execClone( @@ -436,22 +456,22 @@ private class RecordCloneTest { System.assertNotEquals(noYesYesCloned1, null); System.assertNotEquals(noYesYesCloned1.Id, null); Case checkNoYesYesCloned1 = [ - SELECT AccountId, ClosedDate + SELECT AccountId, ClosedDate, (SELECT Id FROM CaseContactRoles), (SELECT Id FROM Cases) FROM Case WHERE Id = :noYesYesCloned1.Id ]; System.assertEquals(checkNoYesYesCloned1.AccountId, null); System.assertEquals(checkNoYesYesCloned1.ClosedDate, null); System.assertNotEquals( - noYesYesCloned1.getSObjects('CaseContactRoles'), + checkNoYesYesCloned1.getSObjects('CaseContactRoles'), null ); System.assertNotEquals( - noYesYesCloned1.getSObjects('CaseContactRoles').size(), + checkNoYesYesCloned1.getSObjects('CaseContactRoles').size(), 0 ); - System.assertNotEquals(noYesYesCloned1.getSObjects('Cases'), null); - System.assertNotEquals(noYesYesCloned1.getSObjects('Cases').size(), 0); + System.assertNotEquals(checkNoYesYesCloned1.getSObjects('Cases'), null); + System.assertNotEquals(checkNoYesYesCloned1.getSObjects('Cases').size(), 0); sObject withNullChildRecordName = RecordCloneController.execClone( a.Id, @@ -460,11 +480,16 @@ private class RecordCloneTest { 'AccountId, SourceId, SuppliedCompany, CreatedDate, CreatedById, LastModifiedDate, LastModifiedById', null ); + sObject withNullChildRecordNameQueried = [ + SELECT Id, (SELECT Id FROM Cases) + FROM Account + WHERE Id = :withNullChildRecordName.Id + ]; System.assertNotEquals(withNullChildRecordName, null); System.assertNotEquals(withNullChildRecordName.Id, null); - System.assertNotEquals(withNullChildRecordName.getSObjects('Cases'), null); + System.assertNotEquals(withNullChildRecordNameQueried.getSObjects('Cases'), null); System.assertNotEquals( - withNullChildRecordName.getSObjects('Cases').size(), + withNullChildRecordNameQueried.getSObjects('Cases').size(), 0 ); @@ -475,22 +500,27 @@ private class RecordCloneTest { '', RecordCloneChildRecordNamePicklist.valueWithClonedAndDate ); + sObject oppExceptionalQueried = [ + SELECT Id, (SELECT Id FROM OpportunityContactRoles), (SELECT Id FROM OpportunityLineItems) + FROM Opportunity + WHERE Id = :oppExceptional.Id + ]; System.assertNotEquals(oppExceptional, null); System.assertNotEquals(oppExceptional.Id, null); System.assertNotEquals( - oppExceptional.getSObjects('OpportunityContactRoles'), + oppExceptionalQueried.getSObjects('OpportunityContactRoles'), null ); System.assertNotEquals( - oppExceptional.getSObjects('OpportunityContactRoles').size(), + oppExceptionalQueried.getSObjects('OpportunityContactRoles').size(), 0 ); System.assertNotEquals( - oppExceptional.getSObjects('OpportunityLineItems'), + oppExceptionalQueried.getSObjects('OpportunityLineItems'), null ); System.assertNotEquals( - oppExceptional.getSObjects('OpportunityLineItems').size(), + oppExceptionalQueried.getSObjects('OpportunityLineItems').size(), 0 ); } @@ -566,17 +596,27 @@ private class RecordCloneTest { caseIds.add(c3.Id); List oppIds = new List(); oppIds.add(o.Id); - List> forInvocable = new List>(); - forInvocable.add(caseIds); - forInvocable.add(oppIds); - - List> clonedMultiple = RecordCloneController.clone(forInvocable); - - System.assertNotEquals(clonedMultiple, null); - System.assertNotEquals(clonedMultiple.size(), 0); - System.assertNotEquals(clonedMultiple[0],null); - System.assertNotEquals(clonedMultiple[0].size(),0); - System.assertNotEquals(clonedMultiple[0][0],null); + List> forInvocable1 = new List>(); + forInvocable1.add(caseIds); + List> forInvocable2 = new List>(); + forInvocable2.add(caseIds); + forInvocable2.add(oppIds); + + List> clonedMultiple1 = RecordCloneController.clone(forInvocable1); + System.assertNotEquals(clonedMultiple1, null); + System.assertNotEquals(clonedMultiple1.size(), 0); + System.assertNotEquals(clonedMultiple1[0], null); + System.assertNotEquals(clonedMultiple1[0].size(), 0); + System.assertNotEquals(clonedMultiple1[0][0], null); + + + List> clonedMultiple2 = RecordCloneController.clone(forInvocable2); + + System.assertNotEquals(clonedMultiple2, null); + System.assertNotEquals(clonedMultiple2.size(), 0); + System.assertNotEquals(clonedMultiple2[0], null); + System.assertNotEquals(clonedMultiple2[0].size(), 0); + System.assertNotEquals(clonedMultiple2[0][0], null); } @IsTest @@ -586,25 +626,37 @@ private class RecordCloneTest { insert c; // Debug - RecordCloneDebugController.RecordCloneDebugSummary summary1 = RecordCloneDebugController.showAllChildRelationships(null, 'Account'); + RecordCloneDebugController.RecordCloneDebugSummary summary1 = RecordCloneDebugController.showAllChildRelationships( + null, + 'Account' + ); System.assertNotEquals(summary1, null); System.assertEquals(summary1.objectName, 'Account'); System.assertNotEquals(summary1.relations, null); System.assertNotEquals(summary1.relations.size(), 0); - RecordCloneDebugController.RecordCloneDebugSummary summary2 = RecordCloneDebugController.showAllChildRelationships(c.Id, null); + RecordCloneDebugController.RecordCloneDebugSummary summary2 = RecordCloneDebugController.showAllChildRelationships( + c.Id, + null + ); System.assertNotEquals(summary2, null); System.assertEquals(summary2.objectName, 'Case'); System.assertNotEquals(summary2.relations, null); System.assertNotEquals(summary2.relations.size(), 0); - RecordCloneDebugController.RecordCloneDebugSummary summary3 = RecordCloneDebugController.showAllChildRelationships(c.Id, 'Account'); + RecordCloneDebugController.RecordCloneDebugSummary summary3 = RecordCloneDebugController.showAllChildRelationships( + c.Id, + 'Account' + ); System.assertNotEquals(summary3, null); System.assertEquals(summary3.objectName, 'Account'); System.assertNotEquals(summary3.relations, null); System.assertNotEquals(summary3.relations.size(), 0); - RecordCloneDebugController.RecordCloneDebugSummary summary4 = RecordCloneDebugController.showAllChildRelationships(null, null); + RecordCloneDebugController.RecordCloneDebugSummary summary4 = RecordCloneDebugController.showAllChildRelationships( + null, + null + ); System.assertEquals(summary4, null); RecordCloneDebugController.RecordCloneDebugSummary summary5 = RecordCloneDebugController.showAllChildRelationships( @@ -616,7 +668,10 @@ private class RecordCloneTest { System.assertNotEquals(summary5.relations, null); System.assertNotEquals(summary5.relations.size(), 0); - RecordCloneDebugController.RecordCloneDebugSummary summary6 = RecordCloneDebugController.showAllChildRelationships(null, 'abcdefg'); + RecordCloneDebugController.RecordCloneDebugSummary summary6 = RecordCloneDebugController.showAllChildRelationships( + null, + 'abcdefg' + ); System.assertEquals(summary6, null); } } diff --git a/force-app/main/default/lwc/recordClone/recordClone.html b/force-app/main/default/lwc/recordClone/recordClone.html index ab4966f..8417c06 100755 --- a/force-app/main/default/lwc/recordClone/recordClone.html +++ b/force-app/main/default/lwc/recordClone/recordClone.html @@ -90,16 +90,16 @@
Initializing...
> - + > - + > diff --git a/force-app/main/default/lwc/recordCloneFieldItem/recordCloneFieldItem.css b/force-app/main/default/lwc/recordCloneFieldItem/recordCloneFieldItem.css new file mode 100644 index 0000000..98c70d5 --- /dev/null +++ b/force-app/main/default/lwc/recordCloneFieldItem/recordCloneFieldItem.css @@ -0,0 +1,44 @@ +/* + Copyright (c) 2020, salesforce.com, inc. + All rights reserved. + SPDX-License-Identifier: BSD-3-Clause + For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause +*/ +.details-item { + text-align: left; + padding: .5rem .75rem; + border-top: 1px solid #f3f3f3; +} +.details-item:first-child { + border-top: none; +} +.details-item-title { + line-height: 16px; + font-weight: bold; + cursor: pointer; +} +.toggle-icon { + vertical-align: text-top; + display: inline-block; + margin-right: .5rem; +} +.details-item-elements { + border-top: 1px dashed #dadada; + margin-top: .5rem; + padding-top: .5rem; + list-style: disc; + padding-left: 1rem; +} +.details-item-elements > li { + padding: .25rem 0; + line-height: 1rem; +} +.details-item-elements .apiname { + font-style: oblique; +} +.child-rel-error { + color: #ff0000; +} +.ncf-reason { + color: #f14848; +} \ No newline at end of file diff --git a/force-app/main/default/lwc/recordCloneFieldItem/recordCloneFieldItem.html b/force-app/main/default/lwc/recordCloneFieldItem/recordCloneFieldItem.html new file mode 100644 index 0000000..a099933 --- /dev/null +++ b/force-app/main/default/lwc/recordCloneFieldItem/recordCloneFieldItem.html @@ -0,0 +1,42 @@ + + \ No newline at end of file diff --git a/force-app/main/default/lwc/recordCloneFieldItem/recordCloneFieldItem.js b/force-app/main/default/lwc/recordCloneFieldItem/recordCloneFieldItem.js new file mode 100644 index 0000000..4222474 --- /dev/null +++ b/force-app/main/default/lwc/recordCloneFieldItem/recordCloneFieldItem.js @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2020, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause + * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause + */ +import { LightningElement, api } from "lwc"; + +export default class RecordCloneFieldItem extends LightningElement { + @api + title; + @api + okItems; + @api + ngItems; + @api + errorMessage; + @api + isError = false; + + shouldShow = false; + + toggle() { + this.shouldShow = !this.shouldShow; + } + + get hasElements() { + return this.totalLength > 0; + } + + get iconName() { + return this.shouldShow ? "utility:contract_alt" : "utility:expand_alt"; + } + + get okLength() { + return this.okItems ? this.okItems.length : 0; + } + + get ngLength() { + return this.ngItems ? this.ngItems.length : 0; + } + + get totalLength() { + return this.okLength + this.ngLength; + } +} diff --git a/force-app/main/default/lwc/recordCloneFieldItem/recordCloneFieldItem.js-meta.xml b/force-app/main/default/lwc/recordCloneFieldItem/recordCloneFieldItem.js-meta.xml new file mode 100644 index 0000000..913ee26 --- /dev/null +++ b/force-app/main/default/lwc/recordCloneFieldItem/recordCloneFieldItem.js-meta.xml @@ -0,0 +1,11 @@ + + + + 52.0 + false + \ No newline at end of file diff --git a/force-app/main/default/lwc/recordCloneRelatedListItem/recordCloneRelatedListItem.html b/force-app/main/default/lwc/recordCloneRelatedListItem/recordCloneRelatedListItem.html index 8803d7f..dfb9bc2 100755 --- a/force-app/main/default/lwc/recordCloneRelatedListItem/recordCloneRelatedListItem.html +++ b/force-app/main/default/lwc/recordCloneRelatedListItem/recordCloneRelatedListItem.html @@ -25,12 +25,12 @@

    diff --git a/package.json b/package.json index 05da92e..c2881be 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { - "name": "salesforce-app", + "name": "record-clone", "private": true, - "version": "2.0.0", - "description": "Salesforce App", + "version": "1.7.0", + "description": "Clone record with its child records together", "scripts": { "lint": "npm run lint:lwc", "lint:lwc": "eslint force-app/main/default/lwc", From dbae1c9e4736a29a5f17cffdb24e62be08b5d6fb Mon Sep 17 00:00:00 2001 From: ninoish Date: Fri, 28 May 2021 22:51:03 +0900 Subject: [PATCH 03/10] remove logs --- .../classes/RecordCloneDebugController.cls | 1 - .../default/classes/RecordCloneHandler.cls | 9 ++-- .../classes/RecordCloneHandlerTest.cls | 2 - .../main/default/classes/RecordCloneTest.cls | 48 ++++++++++++++----- 4 files changed, 40 insertions(+), 20 deletions(-) diff --git a/force-app/main/default/classes/RecordCloneDebugController.cls b/force-app/main/default/classes/RecordCloneDebugController.cls index e6f7134..168442e 100755 --- a/force-app/main/default/classes/RecordCloneDebugController.cls +++ b/force-app/main/default/classes/RecordCloneDebugController.cls @@ -32,7 +32,6 @@ public with sharing class RecordCloneDebugController { DescribeSObjectResult describe = recordId.getSObjectType().getDescribe(); objectName = describe.getLocalName(); } - System.debug(objectName); Map result = new Map(); sObjectType sObjType = (sObjectType) Schema.getGlobalDescribe() .get(objectName); diff --git a/force-app/main/default/classes/RecordCloneHandler.cls b/force-app/main/default/classes/RecordCloneHandler.cls index 6776bdf..c957ace 100755 --- a/force-app/main/default/classes/RecordCloneHandler.cls +++ b/force-app/main/default/classes/RecordCloneHandler.cls @@ -115,21 +115,20 @@ public with sharing class RecordCloneHandler { List parentRelFieldNameArr = child.parentRelationshipField.split( '__' ); - System.debug(child.parentRelationshipField + ' ' + parentRelFieldNameArr.size()); if (parentRelFieldNameArr.size() > 2) { // with name space. Boolean hasMatched = false; for (String childField : childFields) { - System.debug(childField + ' vs ' + parentRelFieldNameArr[1] + parentRelFieldNameArr[2]); if ( childField == child.parentRelationshipField || - childField == parentRelFieldNameArr[1] + '__' + parentRelFieldNameArr[2] + childField == + parentRelFieldNameArr[1] + '__' + parentRelFieldNameArr[2] ) { hasMatched = true; } } - if(!hasMatched) { + if (!hasMatched) { childFields.add(child.parentRelationshipField); } } else { @@ -258,8 +257,6 @@ public with sharing class RecordCloneHandler { children[i].put(childSummary.parentRelationshipField, cloned.Id); } insert children; - - System.debug(childSummary.apiName + ' : ' + children.size()); } } diff --git a/force-app/main/default/classes/RecordCloneHandlerTest.cls b/force-app/main/default/classes/RecordCloneHandlerTest.cls index 63d956a..8799b2c 100755 --- a/force-app/main/default/classes/RecordCloneHandlerTest.cls +++ b/force-app/main/default/classes/RecordCloneHandlerTest.cls @@ -82,8 +82,6 @@ private class RecordCloneHandlerTest { 'hello', RecordCloneChildRecordNamePicklist.valueWithClonedAndDate ); - - System.debug(clonedAcc2WithManyChildren); } @IsTest diff --git a/force-app/main/default/classes/RecordCloneTest.cls b/force-app/main/default/classes/RecordCloneTest.cls index 1b14012..4756907 100755 --- a/force-app/main/default/classes/RecordCloneTest.cls +++ b/force-app/main/default/classes/RecordCloneTest.cls @@ -337,7 +337,6 @@ private class RecordCloneTest { ); System.assertNotEquals(yesNoNoCloned2, null); System.assertNotEquals(yesNoNoCloned2.Id, null); - System.debug(yesNoNoCloned2); // with relationship, without excludedFields sObject noYesNoCloned1 = RecordCloneController.execClone( @@ -385,9 +384,18 @@ private class RecordCloneTest { System.assertNotEquals(yesYesNoCloned1, null); System.assertNotEquals(yesYesNoCloned1.Id, null); System.assertNotEquals(yesYesNoCloned1Queried.getSObjects('Cases'), null); - System.assertNotEquals(yesYesNoCloned1Queried.getSObjects('Cases').size(), 0); - System.assertNotEquals(yesYesNoCloned1Queried.getSObjects('Contacts'), null); - System.assertNotEquals(yesYesNoCloned1Queried.getSObjects('Contacts').size(), 0); + System.assertNotEquals( + yesYesNoCloned1Queried.getSObjects('Cases').size(), + 0 + ); + System.assertNotEquals( + yesYesNoCloned1Queried.getSObjects('Contacts'), + null + ); + System.assertNotEquals( + yesYesNoCloned1Queried.getSObjects('Contacts').size(), + 0 + ); sObject yesYesNoCloned2 = RecordCloneController.execClone( a.Id, @@ -403,13 +411,22 @@ private class RecordCloneTest { ]; System.assertNotEquals(yesYesNoCloned2, null); System.assertNotEquals(yesYesNoCloned2.Id, null); - System.assertNotEquals(yesYesNoCloned2Queried.getSObjects('Opportunities'), null); + System.assertNotEquals( + yesYesNoCloned2Queried.getSObjects('Opportunities'), + null + ); System.assertNotEquals( yesYesNoCloned2Queried.getSObjects('Opportunities').size(), 0 ); - System.assertNotEquals(yesYesNoCloned2Queried.getSObjects('Contacts'), null); - System.assertNotEquals(yesYesNoCloned2Queried.getSObjects('Contacts').size(), 0); + System.assertNotEquals( + yesYesNoCloned2Queried.getSObjects('Contacts'), + null + ); + System.assertNotEquals( + yesYesNoCloned2Queried.getSObjects('Contacts').size(), + 0 + ); // without relationship, with excludedFields sObject noNoYesCloned1 = RecordCloneController.execClone( @@ -456,7 +473,11 @@ private class RecordCloneTest { System.assertNotEquals(noYesYesCloned1, null); System.assertNotEquals(noYesYesCloned1.Id, null); Case checkNoYesYesCloned1 = [ - SELECT AccountId, ClosedDate, (SELECT Id FROM CaseContactRoles), (SELECT Id FROM Cases) + SELECT + AccountId, + ClosedDate, + (SELECT Id FROM CaseContactRoles), + (SELECT Id FROM Cases) FROM Case WHERE Id = :noYesYesCloned1.Id ]; @@ -487,7 +508,10 @@ private class RecordCloneTest { ]; System.assertNotEquals(withNullChildRecordName, null); System.assertNotEquals(withNullChildRecordName.Id, null); - System.assertNotEquals(withNullChildRecordNameQueried.getSObjects('Cases'), null); + System.assertNotEquals( + withNullChildRecordNameQueried.getSObjects('Cases'), + null + ); System.assertNotEquals( withNullChildRecordNameQueried.getSObjects('Cases').size(), 0 @@ -501,7 +525,10 @@ private class RecordCloneTest { RecordCloneChildRecordNamePicklist.valueWithClonedAndDate ); sObject oppExceptionalQueried = [ - SELECT Id, (SELECT Id FROM OpportunityContactRoles), (SELECT Id FROM OpportunityLineItems) + SELECT + Id, + (SELECT Id FROM OpportunityContactRoles), + (SELECT Id FROM OpportunityLineItems) FROM Opportunity WHERE Id = :oppExceptional.Id ]; @@ -609,7 +636,6 @@ private class RecordCloneTest { System.assertNotEquals(clonedMultiple1[0].size(), 0); System.assertNotEquals(clonedMultiple1[0][0], null); - List> clonedMultiple2 = RecordCloneController.clone(forInvocable2); System.assertNotEquals(clonedMultiple2, null); From 6a157b0617dcb4c43244297ec2f4e6e30246a4ad Mon Sep 17 00:00:00 2001 From: ninoish Date: Mon, 31 May 2021 14:58:15 +0900 Subject: [PATCH 04/10] sfdx api version 51.0 --- .../lwc/recordClone/recordClone.js-meta.xml | 229 +++++++++++++++--- .../recordCloneConfigurator.js-meta.xml | 2 +- .../recordCloneFieldItem.js-meta.xml | 2 +- .../recordCloneRelatedListItem.js-meta.xml | 2 +- package-lock.json | 7 +- sfdx-project.json | 2 +- 6 files changed, 202 insertions(+), 42 deletions(-) diff --git a/force-app/main/default/lwc/recordClone/recordClone.js-meta.xml b/force-app/main/default/lwc/recordClone/recordClone.js-meta.xml index 65f2f2d..9f9028a 100755 --- a/force-app/main/default/lwc/recordClone/recordClone.js-meta.xml +++ b/force-app/main/default/lwc/recordClone/recordClone.js-meta.xml @@ -6,7 +6,7 @@ For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause --> - 52.0 + 51.0 true Record Clone Cloning a record with related child records. @@ -21,52 +21,213 @@ - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - - + + + + + + + + + diff --git a/force-app/main/default/lwc/recordCloneConfigurator/recordCloneConfigurator.js-meta.xml b/force-app/main/default/lwc/recordCloneConfigurator/recordCloneConfigurator.js-meta.xml index eccc55c..201134c 100755 --- a/force-app/main/default/lwc/recordCloneConfigurator/recordCloneConfigurator.js-meta.xml +++ b/force-app/main/default/lwc/recordCloneConfigurator/recordCloneConfigurator.js-meta.xml @@ -6,7 +6,7 @@ For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause --> - 52.0 + 51.0 true Record Clone Configuration Helper Helper component for Record Clone. diff --git a/force-app/main/default/lwc/recordCloneFieldItem/recordCloneFieldItem.js-meta.xml b/force-app/main/default/lwc/recordCloneFieldItem/recordCloneFieldItem.js-meta.xml index 7564190..1dc40f7 100644 --- a/force-app/main/default/lwc/recordCloneFieldItem/recordCloneFieldItem.js-meta.xml +++ b/force-app/main/default/lwc/recordCloneFieldItem/recordCloneFieldItem.js-meta.xml @@ -6,6 +6,6 @@ For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause --> - 52.0 + 51.0 false diff --git a/force-app/main/default/lwc/recordCloneRelatedListItem/recordCloneRelatedListItem.js-meta.xml b/force-app/main/default/lwc/recordCloneRelatedListItem/recordCloneRelatedListItem.js-meta.xml index 7564190..1dc40f7 100755 --- a/force-app/main/default/lwc/recordCloneRelatedListItem/recordCloneRelatedListItem.js-meta.xml +++ b/force-app/main/default/lwc/recordCloneRelatedListItem/recordCloneRelatedListItem.js-meta.xml @@ -6,6 +6,6 @@ For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause --> - 52.0 + 51.0 false diff --git a/package-lock.json b/package-lock.json index 46fbf49..297907f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,11 @@ { - "name": "salesforce-app", - "version": "1.0.0", + "name": "record-clone", + "version": "1.7.0", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "salesforce-app", - "version": "1.0.0", + "version": "1.7.0", "devDependencies": { "@prettier/plugin-xml": "^0.12.0", "@salesforce/eslint-config-lwc": "^0.11.0", diff --git a/sfdx-project.json b/sfdx-project.json index 3ed7100..c391ff2 100755 --- a/sfdx-project.json +++ b/sfdx-project.json @@ -8,5 +8,5 @@ "name": "record-clone", "namespace": "", "sfdcLoginUrl": "https://login.salesforce.com", - "sourceApiVersion": "52.0" + "sourceApiVersion": "51.0" } From ca1c47bf5521159f335e5f2582e84af2d9694d13 Mon Sep 17 00:00:00 2001 From: ninoish Date: Fri, 4 Jun 2021 21:35:27 +0900 Subject: [PATCH 05/10] bugfix : double cloning for a single record with multiple child obj fields --- .../default/classes/RecordCloneHandler.cls | 37 ++++++++++++++++++- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/force-app/main/default/classes/RecordCloneHandler.cls b/force-app/main/default/classes/RecordCloneHandler.cls index c957ace..4d803ee 100755 --- a/force-app/main/default/classes/RecordCloneHandler.cls +++ b/force-app/main/default/classes/RecordCloneHandler.cls @@ -198,6 +198,10 @@ public with sharing class RecordCloneHandler { // append to child records' name fields String todayStr = Datetime.now().format('yyyy/MM/dd HH:mm'); + // > + Map> uniqSobjMap = new Map>(); + Map> parentRelFieldMap = new Map>(); + Map summaryMap = new Map(); // remove child record IDs, assign then cloned parent record Id to ref field for (String childRelName : childrens.keySet()) { sObjectSummary childSummary = sObjectSummary.children.get(childRelName); @@ -233,6 +237,31 @@ public with sharing class RecordCloneHandler { } } + List existingRecords = uniqSobjMap.get(childSummary.apiName); + List existingParentNames = parentRelFieldMap.get( + childSummary.apiName + ); + if (existingRecords == null) { + summaryMap.put(childSummary.apiName, childSummary); + uniqSobjMap.put(childSummary.apiName, children); + parentRelFieldMap.put( + childSummary.apiName, + new List{ childSummary.parentRelationshipField } + ); + } else { + existingRecords.addAll(children); + uniqSobjMap.put( + childSummary.apiName, + new List(new Set(existingRecords)) + ); + existingParentNames.add(childSummary.parentRelationshipField); + parentRelFieldMap.put(childSummary.apiName, existingParentNames); + } + } + + for (String childApiName : uniqSobjMap.keySet()) { + List children = uniqSobjMap.get(childApiName); + sObjectSummary childSummary = summaryMap.get(childApiName); for (Integer i = 0; i < children.size(); i++) { children[i].put('Id', null); if (childSummary.isNamable) { @@ -253,9 +282,13 @@ public with sharing class RecordCloneHandler { } children[i].put(childSummary.nameField, nameValue); } - - children[i].put(childSummary.parentRelationshipField, cloned.Id); + for ( + String parentRelationshipField : parentRelFieldMap.get(childApiName) + ) { + children[i].put(parentRelationshipField, cloned.Id); + } } + insert children; } } From a90c8d695c3a4ae9bb65512e983c080337345a91 Mon Sep 17 00:00:00 2001 From: ninoish Date: Fri, 4 Jun 2021 21:37:55 +0900 Subject: [PATCH 06/10] prevent key duplication on repeat: add unique name for each sObj summary --- .../main/default/classes/sObjectSummary.cls | 10 ++++--- .../recordCloneFieldItem.html | 26 ++++++++++++------- .../recordCloneRelatedListItem.html | 12 +++------ 3 files changed, 27 insertions(+), 21 deletions(-) diff --git a/force-app/main/default/classes/sObjectSummary.cls b/force-app/main/default/classes/sObjectSummary.cls index 1606ddc..caec658 100755 --- a/force-app/main/default/classes/sObjectSummary.cls +++ b/force-app/main/default/classes/sObjectSummary.cls @@ -10,6 +10,8 @@ public with sharing class sObjectSummary { @AuraEnabled public String apiName; @AuraEnabled + public String uniqueName; + @AuraEnabled public Boolean isClonable; @AuraEnabled public String nameField; @@ -115,6 +117,7 @@ public with sharing class sObjectSummary { ) { this.label = describe.getLabel(); this.apiName = describe.getLocalName(); + this.uniqueName = this.apiName; this.isNamable = false; this.fields = new List(); @@ -334,13 +337,14 @@ public with sharing class sObjectSummary { for (Schema.ChildRelationship child : describe.getChildRelationships()) { // Match inputted child relationship names with actual object names. if (childRelationshipSet.contains(child.getRelationshipName())) { - sObjectSummary sObjectSummary = new sObjectSummary( + sObjectSummary childSummary = new sObjectSummary( child.getChildSObject().getDescribe() ); - sObjectSummary.parentRelationshipField = child.getField() + childSummary.parentRelationshipField = child.getField() .getDescribe() .getName(); - children.put(child.getRelationshipName(), sObjectSummary); + childSummary.uniqueName += ' - ' + childSummary.parentRelationshipField; + children.put(child.getRelationshipName(), childSummary); } } return children; diff --git a/force-app/main/default/lwc/recordCloneFieldItem/recordCloneFieldItem.html b/force-app/main/default/lwc/recordCloneFieldItem/recordCloneFieldItem.html index b73ff2d..0f54f87 100644 --- a/force-app/main/default/lwc/recordCloneFieldItem/recordCloneFieldItem.html +++ b/force-app/main/default/lwc/recordCloneFieldItem/recordCloneFieldItem.html @@ -21,17 +21,23 @@