top of page

Order Management: How to Clone Orders and Assign Them to New Accounts Using Apex in Salesforce CPQ



In today's fast-paced business environment, managing and replicating orders efficiently is a key driver of success. This is particularly true when there's a need to recreate similar orders for different accounts. Manually tackling this task is not only time-consuming but also prone to errors. In this blog, we delve into an automated solution for cloning orders and their related products in Salesforce, attaching them to a new account using a custom Apex class and a screen flow.


Business Need


Efficiency in Order Management

Businesses often need to duplicate existing orders for different accounts. Doing this manually is not only labor-intensive but also increases the likelihood of errors.


Customization and Flexibility

Each order might require minor adjustments, like changing the associated account or tweaking product details. Automation should provide this level of customization.


Data Integrity

It's crucial that the cloning process maintains the integrity of the original data while allowing necessary modifications.


Solution Overview


Apex Class Implementation

The `OrderTreeCloneInvocable` Apex class is engineered to simplify the cloning process. It duplicates an existing order along with its related products, linking the clone to the selected account.

public with sharing class OrderTreeCloneInvocable {


private static Map<String, Schema.SObjectType> globalDescribeMap{

get{

if(globalDescribeMap == null){

globalDescribeMap = Schema.getGlobalDescribe();

}

return globalDescribeMap;

}

set;

}


@InvocableMethod(label='Clone Order Tree' description='Clones the order and all related order products and child usage summaries' category='Billing')

public static List<Results> cloneOrderTree(List<Requests> requestList) {

Results res = new Results();

Savepoint savepoint = Database.setSavepoint();


try{

//Only one order at a time

Requests req = requestList[0];


//Get new Account with Billing Account info

Account newAcc = [SELECT Id, BillingCity, BillingCountry, BillingGeocodeAccuracy, BillingLatitude,

BillingLongitude, BillingPostalCode, BillingState, ShippingCity, ShippingCountry, ShippingGeocodeAccuracy,

ShippingLatitude, ShippingLongitude, ShippingPostalCode, ShippingState, ShippingStreet

FROM Account

WHERE Id = :req.accountId];


//clone the order

String orderId = req.orderId;

String queryString = 'SELECT '+getAllFields('Order')+' FROM Order WHERE Id = :orderId';

Order originalOrder = Database.query(queryString);


if(!String.isBlank(originalOrder.Name) || !originalOrder.Name.containsIgnoreCase('_Mother Order')){

originalOrder.OrderName__c += '_Mother Order';

update originalOrder;

}

Order clonedOrder = originalOrder.clone(false, true, req.preserveReadonlyTimestamps, false);

clonedOrder.AccountId = newAcc.Id;

clonedOrder.ContractId = originalOrder.ContractId;

clonedOrder.OrderName__c = originalOrder.OrderName__c.remove('_Mother Order');

clonedOrder.SBQQ__Contracted__c = false;

clonedOrder.Status = 'Draft';

clonedOrder.BillingCity = newAcc.BillingCity;

clonedOrder.BillingCountry = newAcc.BillingCountry;

clonedOrder.BillingGeocodeAccuracy = newAcc.BillingGeocodeAccuracy;

clonedOrder.BillingLatitude = newAcc.BillingLatitude;

clonedOrder.BillingLongitude = newAcc.BillingLongitude;

clonedOrder.BillingPostalCode = newAcc.BillingPostalCode;

clonedOrder.BillingState = newAcc.BillingState;

clonedOrder.ShippingCity = newAcc.ShippingCity;

clonedOrder.ShippingCountry = newAcc.ShippingCountry;

clonedOrder.ShippingGeocodeAccuracy = newAcc.ShippingGeocodeAccuracy;

clonedOrder.ShippingLatitude = newAcc.ShippingLatitude;

clonedOrder.ShippingLongitude = newAcc.ShippingLongitude;

clonedOrder.ShippingPostalCode = newAcc.ShippingPostalCode;

clonedOrder.ShippingState = newAcc.ShippingState;

clonedOrder.ShippingStreet = newAcc.ShippingStreet;


SBQQ.TriggerControl.disable();


insert clonedOrder;

res.clonedOrderId = clonedOrder.Id;

String originalOrderId = originalOrder.Id;

//clone order products

Map<Id,Id> oldToNewOrderProductIdMap = new Map<Id,Id>();

List<OrderItem> clonedOrderProducts = new List<OrderItem>();


queryString = 'SELECT '+getAllFields('OrderItem')+' FROM OrderItem WHERE OrderId = :originalOrderId';

List<OrderItem> originalOrderProducts = Database.query(queryString);


if(!originalOrderProducts.isEmpty()){

for(OrderItem op : originalOrderProducts){

OrderItem clonedOP = op.clone(false, true, req.preserveReadonlyTimestamps, false);

clonedOP.OrderId = clonedOrder.Id;

clonedOrderProducts.add(clonedOP);

}

insert clonedOrderProducts;

res.nClonedOrderProducts = clonedOrderProducts.size();


}

clonedOrder.Status = 'Activated';

update clonedOrder;


res.isSuccess = true;

}catch(Exception e){

res.errorMessage = 'Order cloning process failed: '+'\n\n'+e.getMessage()+'\n\n'+e.getStackTraceString();

Database.rollback(savepoint);

}finally{

SBQQ.TriggerControl.enable();

}


return new List<Results>{res};

}


private static String getAllFields(String objName){

String fieldsNames = '';

for (Schema.SObjectField sObjectField : globalDescribeMap.get(objName).getDescribe().fields.getMap().values()) {

fieldsNames += sObjectField.getDescribe().getName() + ',';

}

return fieldsNames.removeEnd(',');

}


public class Requests {

@InvocableVariable(label='Order Id' description='Id of the order to clone' required=true)

public Id orderId;

@InvocableVariable(label='New Account Id' description='Id of the new account for the cloned order' required=true)

public Id accountId;

@InvocableVariable(label='preserve time stamps?' description='flag to control if clone should preserve timestamps fields or not' required=true)

public Boolean preserveReadonlyTimestamps;

}

public class Results {

@InvocableVariable(label='clonedOrderID' description='Id of the cloned order' required=false)

public Id clonedOrderId;

@InvocableVariable(label='clonedOrderProductsCount' description='Number of cloned child order products' required=false)

public Integer nClonedOrderProducts;

@InvocableVariable(label='clonedUsageSummaries' description='Number of cloned child usage summaries' required=false)

public Integer nUsageSummaries;

@InvocableVariable(label='errorMessage' description='Error message if process fails' required=false)

public String errorMessage;

@InvocableVariable(label='isSuccess' description='Successful operation true/false' required=true)

public Boolean isSuccess;


public Results(){

this.nUsageSummaries = 0;

this.nClonedOrderProducts = 0;

this.isSuccess = false;

}

}

}

Screen Flow Integration

A user-friendly screen flow in Salesforce will enable users to select a new account before initiating the cloning process.







Conclusion

Efficiently cloning orders and reassigning them to different accounts not only saves valuable time but also enhances data accuracy and operational efficiency. By leveraging the capabilities of Apex and Salesforce flows, this solution streamlines order management processes, making them more agile and adaptable to the ever-changing business needs.

1 view0 comments

Recent Posts

See All

Comments

Rated 0 out of 5 stars.
No ratings yet

Add a rating
bottom of page