240 lines
12 KiB
OpenEdge ABL
240 lines
12 KiB
OpenEdge ABL
/**
|
|
* @description This service exposes methods that contain logic to make cloning an opportunity
|
|
* much easier and more functional.
|
|
* @author Cloud Performer Inc. <cgary@cloudperformer.com>
|
|
* @version 1.0
|
|
* @group Opportunity Cloning
|
|
* @history 2021-04-02 - CJG - Created
|
|
* 2021-04-12 - CJG - Added setting to restrict child records shown to a particular List
|
|
*/
|
|
public with sharing class OpportunityCloneService {
|
|
/**
|
|
* @description exception class that is thrown when something happens in the opportunity
|
|
* clonde process.
|
|
*/
|
|
public class CloneException extends System.Exception {}
|
|
/**
|
|
* @description Map of Child relationship objects to the opportunity.
|
|
*/
|
|
public static Map<String,Schema.DescribeSObjectResult> opportunityChildrenMap;
|
|
/**
|
|
* @description static constructor that will populate the opportunityChildrenMap
|
|
*/
|
|
static {
|
|
List<Schema.ChildRelationship> childRelationshipList =
|
|
Schema.Opportunity.getSObjectType().getDescribe().getChildRelationships();
|
|
opportunityChildrenMap = new Map<String, Schema.DescribeSObjectResult>();
|
|
List<String> restrictedRelationshipList = new List<String>();
|
|
String restrictedChildList = Label.SmartCloneChildList;
|
|
if(String.isNotBlank(restrictedChildList)){
|
|
restrictedRelationshipList = restrictedChildList.split(';');
|
|
}
|
|
for(Schema.ChildRelationship childRel:childRelationshipList) {
|
|
if(String.isNotBlank(childRel.getRelationshipName())){
|
|
Schema.DescribeSObjectResult childObjRes = childRel.getChildSObject().getDescribe();
|
|
if(!restrictedRelationshipList.isEmpty() && restrictedRelationshipList[0] != 'N/A'){
|
|
if(restrictedRelationshipList.contains(childObjRes.getName())){
|
|
opportunityChildrenMap.put(childRel.getRelationshipName(),childObjRes);
|
|
}
|
|
} else {
|
|
opportunityChildrenMap.put(childRel.getRelationshipName(),childObjRes);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* @description Lightning Exposed method that will provide a map of Child relationships
|
|
* to an Opportunity that can be cloned.
|
|
* @return Map<String,String> Map where the key represents the relationship name and the value is the plural label
|
|
* of the child object.
|
|
*/
|
|
@AuraEnabled(cacheable=true)
|
|
public static Map<String,String> ObtainOpporunityChildRelationships(){
|
|
//return new List<String>(opportunityChildrenMap.keySet());
|
|
Map<String,String> relationshipMap = new Map<String,String>();
|
|
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<String,Boolean> 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<String, Boolean> ObtainCloneOpportunityFields(String fieldsetName){
|
|
Map<String,Boolean> fieldsToReturn = new Map<String, Boolean>();
|
|
Map<String,Schema.FieldSet> 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<String,String> newValues,
|
|
List<String> childRelationshipsToClone) {
|
|
String oppFieldListToClearString = Label.SmartCloneForceClearFieldList;
|
|
List<String> oppFieldListToClear = new List<String>();
|
|
if(!oppFieldListToClearString.contains('N/A')){
|
|
oppFieldListToClear = oppFieldListToClearString.split(';');
|
|
}
|
|
//dynamic query in order to find the original opportunity.
|
|
String originalOppQueryString = ObtainQueryStringForOppAndChildren(oppId, childRelationshipsToClone);
|
|
System.debug(LoggingLevel.INFO,'\n\n*****\nQuery: ' + originalOppQueryString + '\n*****\n\n');
|
|
List<Opportunity> 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)));
|
|
}
|
|
//clear out field values listed
|
|
for(String fieldName:oppFieldListToClear) {
|
|
clonedOpp.put(fieldName,null);
|
|
}
|
|
//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<Schema.ChildRelationship> childRelationships =
|
|
Schema.Opportunity.getSObjectType().getDescribe().getChildRelationships();
|
|
List<SObject> childRecsToInsert = new List<SObject>();
|
|
for(Schema.ChildRelationship childRel:childRelationships){
|
|
//if the child relationship name was passed in, get the records
|
|
if(childRelationshipsToClone.contains(childRel.getRelationshipName())){
|
|
List<SObject> 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<String> relationships) {
|
|
String queryString = 'SELECT ';
|
|
queryString += String.join(ObtainFieldsForObject('Opportunity'),',');
|
|
List<String> subqueryList = new List<String>();
|
|
for(String relationship:relationships){
|
|
Schema.DescribeSObjectResult childObject = opportunityChildrenMap.get(relationship);
|
|
String childQueryString = '(SELECT ' + String.join(ObtainFieldsForObject(childObject.getName()),',') + ' FROM ';
|
|
childQueryString += relationship + ')';
|
|
subqueryList.add(childQueryString);
|
|
}
|
|
if(!subqueryList.isEmpty()){
|
|
queryString += ', ' + String.join(subqueryList,',') + 'FROM Opportunity WHERE Id =\'' + oppRecordId + '\'';
|
|
} else {
|
|
queryString += ' FROM Opportunity WHERE Id =\'' + oppRecordId + '\'';
|
|
}
|
|
|
|
return queryString;
|
|
}
|
|
/**
|
|
* @description this method is responsible for getting the list of fields for a particular object
|
|
*/
|
|
private static List<String> ObtainFieldsForObject(String objectName) {
|
|
List<String> fieldNameList;
|
|
List<String> objectListToDescribe = new List<String>{objectName};
|
|
List<Schema.DescribeSObjectResult> describeItem = System.Schema.describeSObjects(objectListToDescribe);
|
|
if(describeItem.isEmpty()) {
|
|
throw new OpportunityCloneService.CloneException('Could not find info for ' + objectName + '.');
|
|
}
|
|
Map<String, Schema.SObjectField> fieldSObjectMapping = describeItem[0].fields.getMap();
|
|
fieldNameList = new List<String>();
|
|
for(Schema.SObjectField fieldToken:fieldSObjectMapping.values()) {
|
|
Schema.DescribeFieldResult fieldTokenDescribe = fieldToken.getDescribe();
|
|
if(fieldTokenDescribe.isCreateable() && fieldTokenDescribe.isUpdateable()) {
|
|
fieldNameList.add(fieldTokenDescribe.getName());
|
|
}
|
|
}
|
|
//need to account for weird objects where not all of the fields come back
|
|
if(objectName.toUpperCase() == 'OPPORTUNITYLINEITEM'){
|
|
fieldNameList.add('PricebookEntryId');
|
|
}
|
|
return fieldNameList;
|
|
}
|
|
|
|
private static List<SObject> CloneAndReassociateRecords(List<SObject> recList, String parentFieldName, Id newParentId) {
|
|
List<SObject> newList = new List<sObject>();
|
|
for(SObject rec:recList){
|
|
SObject newRec = rec.clone(false,true,false,false);
|
|
newRec.put(parentFieldName, newParentId);
|
|
if(newRec.getSObjectType().getDescribe().getName().toUpperCase() == 'OPPORTUNITYLINEITEM') {
|
|
newRec.put('UnitPrice',null);
|
|
}
|
|
newList.add(newRec);
|
|
}
|
|
return newList;
|
|
}
|
|
|
|
private static Object CreateObjectValueFromString(String fieldName, String fieldValue) {
|
|
Map<String,Schema.SObjectField> fieldSObjectMapping = Schema.Opportunity.getSObjectType().getDescribe().fields.getMap();
|
|
Schema.DisplayType fieldType = fieldSObjectMapping.get(fieldName).getDescribe().getType();
|
|
if(String.isBlank(fieldValue)){
|
|
return null;
|
|
}
|
|
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.valueOf(fieldValue);
|
|
}
|
|
when DATETIME {
|
|
returnObj = Datetime.valueOf(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;
|
|
}
|
|
}
|