From 21df2d0bd21f5fbcb9c614faee6c2427e2e89319 Mon Sep 17 00:00:00 2001 From: Christopher Gary Date: Mon, 12 Apr 2021 05:44:06 -0400 Subject: [PATCH] Modifications to lightning component --- .forceignore | 8 +- .../OppCustomCloneAura.auradoc | 6 + .../OppCustomCloneAura/OppCustomCloneAura.cmp | 3 + .../OppCustomCloneAura.cmp-meta.xml | 5 + .../OppCustomCloneAura/OppCustomCloneAura.css | 2 + .../OppCustomCloneAura.design | 3 + .../OppCustomCloneAura/OppCustomCloneAura.svg | 10 ++ .../OppCustomCloneAuraController.js | 5 + .../OppCustomCloneAuraHelper.js | 5 + .../OppCustomCloneAuraRenderer.js | 5 + .../classes/OpportunityCloneService.cls | 142 +++++++++++++++++- .../opportunityCustomClone.html | 23 ++- .../opportunityCustomClone.js | 123 ++++++++++++++- .../opportunityCustomClone.js-meta.xml | 8 + .../Account/listViews/All.listView-meta.xml | 12 ++ ...pportunity.SmartClone.quickAction-meta.xml | 10 ++ 16 files changed, 363 insertions(+), 7 deletions(-) create mode 100644 force-app/main/default/aura/OppCustomCloneAura/OppCustomCloneAura.auradoc create mode 100644 force-app/main/default/aura/OppCustomCloneAura/OppCustomCloneAura.cmp create mode 100644 force-app/main/default/aura/OppCustomCloneAura/OppCustomCloneAura.cmp-meta.xml create mode 100644 force-app/main/default/aura/OppCustomCloneAura/OppCustomCloneAura.css create mode 100644 force-app/main/default/aura/OppCustomCloneAura/OppCustomCloneAura.design create mode 100644 force-app/main/default/aura/OppCustomCloneAura/OppCustomCloneAura.svg create mode 100644 force-app/main/default/aura/OppCustomCloneAura/OppCustomCloneAuraController.js create mode 100644 force-app/main/default/aura/OppCustomCloneAura/OppCustomCloneAuraHelper.js create mode 100644 force-app/main/default/aura/OppCustomCloneAura/OppCustomCloneAuraRenderer.js create mode 100644 force-app/main/default/objects/Account/listViews/All.listView-meta.xml create mode 100644 force-app/main/default/quickActions/Opportunity.SmartClone.quickAction-meta.xml diff --git a/.forceignore b/.forceignore index 7a7923f..c27d65b 100644 --- a/.forceignore +++ b/.forceignore @@ -14,5 +14,11 @@ package.xml # Exclude applications *.appMenu +# Exclude Layouts +*.layout + # Exclude FlexiPage -*.flexipage \ No newline at end of file +*.flexipage + +# Exclude FieldSets +*.fieldSet \ No newline at end of file diff --git a/force-app/main/default/aura/OppCustomCloneAura/OppCustomCloneAura.auradoc b/force-app/main/default/aura/OppCustomCloneAura/OppCustomCloneAura.auradoc new file mode 100644 index 0000000..d0d6eb5 --- /dev/null +++ b/force-app/main/default/aura/OppCustomCloneAura/OppCustomCloneAura.auradoc @@ -0,0 +1,6 @@ + + Documentation + + Example Description + + \ No newline at end of file diff --git a/force-app/main/default/aura/OppCustomCloneAura/OppCustomCloneAura.cmp b/force-app/main/default/aura/OppCustomCloneAura/OppCustomCloneAura.cmp new file mode 100644 index 0000000..ea2d5c4 --- /dev/null +++ b/force-app/main/default/aura/OppCustomCloneAura/OppCustomCloneAura.cmp @@ -0,0 +1,3 @@ + + + diff --git a/force-app/main/default/aura/OppCustomCloneAura/OppCustomCloneAura.cmp-meta.xml b/force-app/main/default/aura/OppCustomCloneAura/OppCustomCloneAura.cmp-meta.xml new file mode 100644 index 0000000..dc8ed55 --- /dev/null +++ b/force-app/main/default/aura/OppCustomCloneAura/OppCustomCloneAura.cmp-meta.xml @@ -0,0 +1,5 @@ + + + 51.0 + A Lightning Component Bundle + \ No newline at end of file diff --git a/force-app/main/default/aura/OppCustomCloneAura/OppCustomCloneAura.css b/force-app/main/default/aura/OppCustomCloneAura/OppCustomCloneAura.css new file mode 100644 index 0000000..f407185 --- /dev/null +++ b/force-app/main/default/aura/OppCustomCloneAura/OppCustomCloneAura.css @@ -0,0 +1,2 @@ +.THIS { +} diff --git a/force-app/main/default/aura/OppCustomCloneAura/OppCustomCloneAura.design b/force-app/main/default/aura/OppCustomCloneAura/OppCustomCloneAura.design new file mode 100644 index 0000000..d247163 --- /dev/null +++ b/force-app/main/default/aura/OppCustomCloneAura/OppCustomCloneAura.design @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/force-app/main/default/aura/OppCustomCloneAura/OppCustomCloneAura.svg b/force-app/main/default/aura/OppCustomCloneAura/OppCustomCloneAura.svg new file mode 100644 index 0000000..bc0ef2a --- /dev/null +++ b/force-app/main/default/aura/OppCustomCloneAura/OppCustomCloneAura.svg @@ -0,0 +1,10 @@ + + + + + + CP-Social-Logo + + + + \ No newline at end of file diff --git a/force-app/main/default/aura/OppCustomCloneAura/OppCustomCloneAuraController.js b/force-app/main/default/aura/OppCustomCloneAura/OppCustomCloneAuraController.js new file mode 100644 index 0000000..b235899 --- /dev/null +++ b/force-app/main/default/aura/OppCustomCloneAura/OppCustomCloneAuraController.js @@ -0,0 +1,5 @@ +({ + myAction : function(component, event, helper) { + + } +}) diff --git a/force-app/main/default/aura/OppCustomCloneAura/OppCustomCloneAuraHelper.js b/force-app/main/default/aura/OppCustomCloneAura/OppCustomCloneAuraHelper.js new file mode 100644 index 0000000..f8a77e3 --- /dev/null +++ b/force-app/main/default/aura/OppCustomCloneAura/OppCustomCloneAuraHelper.js @@ -0,0 +1,5 @@ +({ + helperMethod : function() { + + } +}) diff --git a/force-app/main/default/aura/OppCustomCloneAura/OppCustomCloneAuraRenderer.js b/force-app/main/default/aura/OppCustomCloneAura/OppCustomCloneAuraRenderer.js new file mode 100644 index 0000000..3a11ff5 --- /dev/null +++ b/force-app/main/default/aura/OppCustomCloneAura/OppCustomCloneAuraRenderer.js @@ -0,0 +1,5 @@ +({ + +// Your renderer method overrides go here + +}) diff --git a/force-app/main/default/classes/OpportunityCloneService.cls b/force-app/main/default/classes/OpportunityCloneService.cls index da7f4ca..ae32bf4 100644 --- a/force-app/main/default/classes/OpportunityCloneService.cls +++ b/force-app/main/default/classes/OpportunityCloneService.cls @@ -12,7 +12,13 @@ public with sharing class OpportunityCloneService { * clonde process. */ public class CloneException extends System.Exception {} + /** + * @description Map of Child relationship objects to the opportunity. + */ public static Map opportunityChildrenMap; + /** + * @description static constructor that will populate the opportunityChildrenMap + */ static { List childRelationshipList = Schema.Opportunity.getSObjectType().getDescribe().getChildRelationships(); @@ -22,12 +28,93 @@ public with sharing class OpportunityCloneService { opportunityChildrenMap.put(childObjRes.getName(),childObjRes); } } - + /** + * @description Lightning Exposed method that will provide a map of Child relationships + * to an Opportunity that can be cloned. + * @return Map Map where the key represents the relationship name and the value is the plural label + * of the child object. + */ @AuraEnabled(cacheable=true) - public static List ObtainOpporunityChildRelationships(){ - return new List(opportunityChildrenMap.keySet()); + public static Map ObtainOpporunityChildRelationships(){ + //return new List(opportunityChildrenMap.keySet()); + Map relationshipMap = new Map(); + for(String key:opportunityChildrenMap.keySet()){ + Schema.DescribeSObjectResult res = opportunityChildrenMap.get(key); + if(res.isAccessible() && res.isCreateable()){ + String pluralLabel = res.getLabelPlural(); + relationshipMap.put(key,pluralLabel); + } + } + return relationshipMap; + } + /** + * @description Lightning Exposed method that will provide a Map of fields in a fieldset on the + * opportunity. + * @param fieldsetName The name of the fieldset to use + * @return Map Map where the key is the apiFieldName and the value is whether the field is marked as + * 'required' in the fieldset. + */ + @AuraEnabled(cacheable=true) + public static Map ObtainCloneOpportunityFields(String fieldsetName){ + Map fieldsToReturn = new Map(); + Map fieldSetMap = Schema.Opportunity.getSObjectType().getDescribe().fieldsets.getMap(); + Schema.FieldSet fieldSetItem = fieldSetMap.get(fieldsetName); + if(fieldSetItem == null){ + throw new OpportunityCloneService.CloneException('No field set named ' + fieldsetName + 'found on the Opportunity.'); + } + for(Schema.FieldSetMember fieldMember:fieldSetMap.get(fieldsetName).getFields()) { + fieldsToReturn.put(fieldMember.getFieldPath(),fieldMember.getRequired()); + } + return fieldsToReturn; } + @AuraEnabled + public static Id CloneOpportunityWithChildren(Id oppId, Map newValues, + List childRelationshipsToClone) { + //dynamic query in order to find the original opportunity. + String originalOppQueryString = ObtainQueryStringForOppAndChildren(oppId, childRelationshipsToClone); + List oppList = Database.query(originalOppQueryString); + //if the original opp cannot be found, throw and exception + if(oppList.isEmpty()){ + throw new AuraHandledException('There was a problem obtaining original opportunity.'); + } + Opportunity originalOpp = oppList[0]; + //clone the original opportunity + Opportunity clonedOpp = originalOpp.clone(false,true,false,false); + //replace the values on the Opportunity with the new ones + for(String fieldName:newValues.keySet()){ + clonedOpp.put(fieldName, CreateObjectValueFromString(fieldName,newValues.get(fieldName))); + } + //insert the new Opportunity + try{ + insert clonedOpp; + } catch(System.Exception ex){ + System.debug(LoggingLevel.INFO,'\n\n*****\nProblem creating new Opportunity: ' + ex.getMessage() + '\n*****\n\n'); + throw new AuraHandledException('Unable to create new opportunity.'); + } + //now we try to loop through the children and clone the records + List childRelationships = + Schema.Opportunity.getSObjectType().getDescribe().getChildRelationships(); + List childRecsToInsert = new List(); + for(Schema.ChildRelationship childRel:childRelationships){ + //if the child relationship name was passed in, get the records + if(childRelationshipsToClone.contains(childRel.getRelationshipName())){ + List childRecs = originalOpp.getSObjects(childRel.getRelationshipName()); + childRecsToInsert.addAll(CloneAndReassociateRecords(childRecs, + childRel.getField().getDescribe().getName(),clonedOpp.Id)); + } + } + if(!childRecsToInsert.isEmpty()){ + try{ + insert childRecsToInsert; + } catch(System.Exception ex) { + System.debug(LoggingLevel.INFO,'\n\n*****\nError cloning children: ' + ex.getMessage() + '\n*****\n\n'); + throw new AuraHandledException('Unable to clone children.'); + } + } + return clonedOpp.Id; + } + public static String ObtainQueryStringForOppAndChildren(Id oppRecordId, List relationships) { String queryString = 'SELECT '; queryString += String.join(ObtainFieldsForObject('Opportunity'),','); @@ -62,4 +149,53 @@ public with sharing class OpportunityCloneService { } return fieldNameList; } + + private static List CloneAndReassociateRecords(List recList, String parentFieldName, Id newParentId) { + List newList = new List(); + for(SObject rec:recList){ + SObject newRec = rec.clone(false,true,false,false); + newRec.put(parentFieldName, newParentId); + newList.add(newRec); + } + return newList; + } + + private static Object CreateObjectValueFromString(String fieldName, String fieldValue) { + Map fieldSObjectMapping = Schema.Opportunity.getSObjectType().getDescribe().fields.getMap(); + Schema.DisplayType fieldType = fieldSObjectMapping.get(fieldName).getDescribe().getType(); + Object returnObj; + switch on fieldType { + when ADDRESS, COMBOBOX, EMAIL, ENCRYPTEDSTRING, MULTIPICKLIST, PHONE, PICKLIST, STRING, TEXTAREA,URL { + returnObj = fieldValue; + } + when BOOLEAN { + returnObj = Boolean.valueOf(fieldValue); + } + when CURRENCY, DOUBLE, PERCENT { + returnObj = Double.valueOf(fieldValue); + } + when DATE { + returnObj = Date.parse(fieldValue); + } + when DATETIME { + returnObj = Datetime.parse(fieldValue); + } + when ID { + returnObj = Id.valueOf(fieldValue); + } + when INTEGER { + returnObj = Integer.valueOf(fieldValue); + } + when LONG { + returnObj = Long.valueOf(fieldValue); + } + when TIME { + returnObj = fieldValue; + } + when else { + returnObj = fieldValue; + } + } + return returnObj; + } } diff --git a/force-app/main/default/lwc/opportunityCustomClone/opportunityCustomClone.html b/force-app/main/default/lwc/opportunityCustomClone/opportunityCustomClone.html index 27e0f69..b5c24e2 100644 --- a/force-app/main/default/lwc/opportunityCustomClone/opportunityCustomClone.html +++ b/force-app/main/default/lwc/opportunityCustomClone/opportunityCustomClone.html @@ -1,3 +1,24 @@ \ No newline at end of file diff --git a/force-app/main/default/lwc/opportunityCustomClone/opportunityCustomClone.js b/force-app/main/default/lwc/opportunityCustomClone/opportunityCustomClone.js index b08bbe7..272a3db 100644 --- a/force-app/main/default/lwc/opportunityCustomClone/opportunityCustomClone.js +++ b/force-app/main/default/lwc/opportunityCustomClone/opportunityCustomClone.js @@ -1,3 +1,122 @@ -import { LightningElement } from 'lwc'; +import { LightningElement, api, wire, track } from 'lwc'; +import { getObjectInfo } from 'lightning/uiObjectInfoApi'; +import { getRecord } from 'lightning/uiRecordApi'; +import OPPORTUNITY_OBJECT from '@salesforce/schema/Opportunity'; +import ObtainCloneOpportunityFields from '@salesforce/apex/OpportunityCloneService.ObtainCloneOpportunityFields'; +import ObtainChildRelationships from '@salesforce/apex/OpportunityCloneService.ObtainOpporunityChildRelationships'; +import CloneOpportunityWithChildren from '@salesforce/apex/OpportunityCloneService.CloneOpportunityWithChildren'; -export default class OpportunityCustomClone extends LightningElement {} \ No newline at end of file +export default class OpportunityCustomClone extends LightningElement { + @api recordId; + + formObjectApiName; + displayedFields; + requiredFieldMap; + opportunityFields; + childRelationshipList; + recordInfo; + childRelationshipDisplayList = []; + @track childRelationshipSelectionList = []; + + @wire(getRecord,{recordId:'$recordId',layoutTypes:['Full'],modes:['Edit']}) + processRecordInfo({err, data}) { + if(err) { + window.console.error('There was a problem obtaining Opportunity Record Data'); + window.console.error(err); + } + + if(data) { + //window.console.log('Original Data: %s',JSON.stringify(data,null,"\t")); + this.recordInfo = data.fields; + } + } + + @wire(ObtainChildRelationships) + processChildRelationships({error, data}) { + if(error) { + window.console.log('There is an error obtaining Cild Relationships'); + window.console.error(error); + } + + if(data) { + var childRelationshipPicklistArray = []; + Object.keys(data).forEach(childItem => { + childRelationshipPicklistArray.push({'label':data[childItem],'value':childItem}); + }) + this.childRelationshipDisplayList = childRelationshipPicklistArray; + } + } + + @wire(ObtainCloneOpportunityFields,{fieldsetName:'ClonedFields'}) + processFieldsetInfo({error, data}) { + if(error){ + window.console.error('There is an error obtaining ClonedFields fieldset'); + window.console.error(error); + } + if(data){ + this.displayedFields = Object.keys(data); + this.requiredFieldMap = data; + } + } + + @wire(getObjectInfo,{ objectApiName: OPPORTUNITY_OBJECT }) + processOpportunityObjectInfo({error, data}) { + if(error){ + window.console.error('There is an error obtaining fields'); + } + if(data){ + this.formObjectApiName = OPPORTUNITY_OBJECT.objectApiName; + this.opportunityFields = data.fields; + } + } + /** + * @description Handler that executes on selection of child Relationship + */ + handleChildRelationshipSelection(evt){ + //find the item that was selected from the distplay list + var selectedItem = evt.target.value; + let fullSelectedList = this.childRelationshipSelectionList; + let displayList = this.childRelationshipDisplayList; + let chosenObj = displayList.find( element => element.value === selectedItem); + let alreadySelected = fullSelectedList.findIndex(element => element.value === selectedItem); + window.console.log('Already Selected Value: %s',alreadySelected); + if(chosenObj && alreadySelected < 0){ + fullSelectedList.push(chosenObj); + } + this.childRelationshipSelectionList = fullSelectedList; + window.console.log('Full Selected List: %s',JSON.stringify(this.childRelationshipSelectionList,null,"\t")); + } + + handleChildSelectionRemoval(evt){ + window.console.log('Item Clicked: %s',evt.detail.index); + let fullSelectedList = this.childRelationshipSelectionList; + let removedItem = fullSelectedList.splice(evt.detail.index,1); + this.childRelationshipSelectionList = fullSelectedList; + } + /** + * @description This will override the submission + * @param {*} evt + */ + handleSubmit(evt){ + evt.preventDefault(); + var fields = evt.detail.fields; + window.console.log('Fields Submitted: %s',JSON.stringify(fields,null,"\t")); + } + + get formFields(){ + var formFieldsToReturn = []; + var fieldRequirementMap = this.requiredFieldMap; + if(this.opportunityFields && this.displayedFields && this.recordInfo) { + window.console.log('fieldInfo %s: ', JSON.stringify(this.recordInfo,null,"\t")); + this.displayedFields.forEach(fieldName => { + let fieldItem = fieldRequirementMap[fieldName]; + let fieldDisplay = JSON.parse(JSON.stringify(this.opportunityFields[fieldName])); + if(!fieldItem){ + fieldDisplay.value = this.recordInfo[fieldName].value; + } + formFieldsToReturn.push(fieldDisplay); + }); + } + return formFieldsToReturn; + } +} \ No newline at end of file diff --git a/force-app/main/default/lwc/opportunityCustomClone/opportunityCustomClone.js-meta.xml b/force-app/main/default/lwc/opportunityCustomClone/opportunityCustomClone.js-meta.xml index 0416469..41c640c 100644 --- a/force-app/main/default/lwc/opportunityCustomClone/opportunityCustomClone.js-meta.xml +++ b/force-app/main/default/lwc/opportunityCustomClone/opportunityCustomClone.js-meta.xml @@ -2,4 +2,12 @@ 51.0 false + + lightning__RecordPage + + + + + + \ No newline at end of file diff --git a/force-app/main/default/objects/Account/listViews/All.listView-meta.xml b/force-app/main/default/objects/Account/listViews/All.listView-meta.xml new file mode 100644 index 0000000..76fe768 --- /dev/null +++ b/force-app/main/default/objects/Account/listViews/All.listView-meta.xml @@ -0,0 +1,12 @@ + + + All + ACCOUNT.NAME + ACCOUNT.SITE + ACCOUNT.ADDRESS1_STATE + ACCOUNT.PHONE1 + ACCOUNT.TYPE + CORE.USERS.ALIAS + Everything + + diff --git a/force-app/main/default/quickActions/Opportunity.SmartClone.quickAction-meta.xml b/force-app/main/default/quickActions/Opportunity.SmartClone.quickAction-meta.xml new file mode 100644 index 0000000..f8f059e --- /dev/null +++ b/force-app/main/default/quickActions/Opportunity.SmartClone.quickAction-meta.xml @@ -0,0 +1,10 @@ + + + Customized Cloning of Opportunity Record + 400 + + OppCustomCloneAura + false + LightningComponent + -100 +