top of page

Contract Transfert : Streamlining Contract Transfer Between Accounts in Salesforce: A Deep Dive into

Updated: Dec 1, 2023



Github Link : https://github.com/AourLegacy/AourFactory/tree/TransfertContract

Introduction


In the realm of Salesforce CRM, the transfer of contracts between accounts is a common yet complex task. This process often involves more than just transferring the contract itself; it necessitates the cloning and linking of associated opportunities, quotes, and orders. Let's explore how Salesforce Apex can be harnessed to streamline this process, ensuring a smooth and error-free transfer of contracts from one account to another.


The Challenge


The principal challenge in transferring a contract between accounts lies in maintaining the relational integrity of the associated records. This involves replicating the opportunity, quote, and order related to the original contract and linking these clones to the new account. It's a task that demands careful coding, especially in handling scenarios where some records might not be present.


Step-by-Step Solution


1. Cloning the Opportunity


The transfer process starts with the opportunity linked to the contract. We clone this opportunity to maintain its relationship with the original contract while associating it with the new account.


2. Cloning the Quote


Next, we focus on the quote linked to the original contract. This is cloned and associated with both the cloned opportunity and the new account, ensuring consistency in the sales process.


3. Cloning or Creating the Order


One of the trickier aspects is handling the order. If an existing order is linked to the contract, it's cloned; if not, a new order is created. This ensures continuity in the order management process.


4. Cloning the Contract


Finally, the contract itself is cloned, linking it to the new account as well as the freshly cloned opportunity, quote, and order. This step is crucial as it ensures that the new contract maintains its association with the relevant sales records.


5. Error Handling and Validation


Throughout the process, robust error handling is vital. This includes managing null references and ensuring that cloned records are valid and complete. Utilizing `try-catch` blocks helps in managing unexpected issues during the cloning process.


Best Practices


- Bulkification: Adapt the code to handle multiple records simultaneously, keeping in mind Salesforce's governor limits.

- Data Integrity: Ensure that the cloned records adhere to your org’s business logic and data validation rules.

- Comprehensive Testing: Implement extensive test coverage to validate various scenarios and maintain compliance with Salesforce's testing requirements.

- Governor Limits: Be mindful of Salesforce's limits in terms of SOQL queries, DML operations, and script execution time.

The Solution :


Flow :









Apex Code :


public class TransfertContract {


@InvocableMethod(label='Transfert Contract Apex')

public static List<String> transfertContract(List<FlowInputs> param) {

Id contractId = param[0].recordId;

Id AccountId = param[0].IdAccount;

List<String> ids = new List<String> ();

Contract contract = [SELECT id,AccountId, ActivatedById, ActivatedDate, BillingAddress, CompanySignedDate, CompanySignedId, ContractTerm, CreatedById, CreatedDate, CustomerSignedDate, CustomerSignedId, CustomerSignedTitle, Description, DummyObject__c, EndDate, IsDeleted,SBQQ__AmendmentOwner__c, SBQQ__AmendmentPricebookId__c, SBQQ__AmendmentRenewalBehavior__c, SBQQ__AmendmentStartDate__c, SBQQ__DefaultRenewalContactRoles__c, SBQQ__DefaultRenewalPartners__c, SBQQ__DisableAmendmentCoTerm__c, SBQQ__Evergreen__c, SBQQ__ExpirationDate__c, SBQQ__MasterContract__c, SBQQ__MDQRenewalBehavior__c, SBQQ__Opportunity__c, SBQQ__OpportunityPricebookId__c, SBQQ__Order__c, SBQQ__PreserveBundleStructureUponRenewals__c, SBQQ__Quote__c, SBQQ__RenewalForecast__c, SBQQ__RenewalOpportunity__c, SBQQ__RenewalOpportunityRecordTypeId__c, SBQQ__RenewalOpportunityStage__c, SBQQ__RenewalOwner__c, SBQQ__RenewalPricebookId__c, SBQQ__RenewalQuoted__c, SBQQ__RenewalTerm__c, SBQQ__RenewalUpliftRate__c, SBQQ__SubscriptionQuantitiesCombined__c, ShippingAddress, StartDate, Status, StatusCode, Terminated_Date__c FROM Contract

WHERE Id =:contractId LIMIT 1];

SBQQ.TriggerControl.disable();

try {


List<Opportunity> oppList = [SELECT id,AccountId,CloseDate, Achievement__c, Amount, AwareCPQGoal__c, AwareCPQProd__c, AwareCPQQTCPP__c, AwareDreamOutcome__c, AwareLinkCPQTasks__c, ContactRole__c, ContractId, CreatedById, CreatedDate, Description, Fiscal, FiscalQuarter, FiscalYear, IsClosed, HasOpenActivity, ForecastCategoryName, ForecastCategory, LastCloseDateChangedHistoryId, LastModifiedById, LastModifiedDate, LastReferencedDate, LastStageChangeDate, LastViewedDate, LeadSource, Length__c, Loss_Reason__c, Market__c, LostReasonComment__c, Name, NextStep, SBQQ__AmendedContract__c, RecordTypeId, RecordType__c, Pricebook2Id, Probability, Period__c, OwnerId, Objective__c, SBQQ__QuotePricebookId__c, SBQQ__Renewal__c, SBQQ__RenewedContract__c, SBQQ__Contracted__c, SBQQ__CreateContractedPrices__c, SBQQ__Ordered__c, SBQQ__OrderGroupID__c, SolutionInterest__c, SolutionShared__c, StageName, Type__c, Type FROM Opportunity WHERE Id =:contract.SBQQ__Opportunity__c LIMIT 1 ];

Opportunity orgOpp = oppList[0];


SBQQ__Quote__c orgQuote = [SELECT Id, BillingAdresse__c, BundleDiscountAmount__c, BundleDiscountPercentage__c, DiscountAmount__c, Discount1__c, DifficultytoMaintainCustomer__c, DeltaDiscount__c, CreatedDate, CreatedById, IsDeleted, NBbundle__c, NetAfterTva__c, NombreDevisiteur__c, NombreUtilisateur__c, Objectif__c, Optional__c, OwnerId, PartnerLevel__c, Pricehold__c, PricingMethods__c, PrixNegocie__c, Process__c, SBQQ__AdditionalDiscountAmount__c, SBQQ__Account__c, Promotion__c, SBQQ__AverageCustomerDiscount__c, SBQQ__AveragePartnerDiscount__c, SBQQ__Distributor__c, SBQQ__DeliveryMethod__c, SBQQ__DefaultTemplate__c, SBQQ__DaysQuoteOpen__c, SBQQ__CustomerDiscount__c, SBQQ__CustomerAmount__c, SBQQ__ContractingMethod__c, SBQQ__ConsumptionRateOverride__c, SBQQ__DistributorDiscount__c, SBQQ__EndDate__c, SBQQ__ExpirationDate__c, SBQQ__FirstSegmentTermEndDate__c, SBQQ__MarkupRate__c, SBQQ__ListAmount__c, SBQQ__LineItemsPrinted__c, SBQQ__LineItemsGrouped__c, SBQQ__LineItemCount__c, SBQQ__MasterEvergreenContract__c, SBQQ__NetAmount__c, SBQQ__Notes__c, SBQQ__Opportunity2__c, SBQQ__OrderBy__c, SBQQ__OrderByQuoteLineGroup__c, SBQQ__Ordered__c, SBQQ__Primary__c, SBQQ__PricebookId__c, SBQQ__PriceBook__c, SBQQ__PaymentTerms__c, SBQQ__PartnerDiscount__c, SBQQ__Partner__c, SBQQ__OriginalQuote__c, SBQQ__PrimaryContact__c, SBQQ__RegularAmount__c, SBQQ__RenewalTerm__c, SBQQ__StartDate__c, SBQQ__Source__c , SBQQ__SalesRep__c, SBQQ__Status__c, SBQQ__SubscriptionTerm__c, SBQQ__Type__c, SBQQ__Uncalculated__c FROM SBQQ__Quote__c WHERE id=:contract.SBQQ__Quote__c];

Order orgOrder = [SELECT Id,AccountId, ActivatedById, ActivatedDate, BillingAddress, CompanyAuthorizedById, ContractId, CreatedById, CreatedDate, CustomerAuthorizedById, Description, EffectiveDate, EndDate, IsDeleted, IsReductionOrder, LastModifiedById, LastModifiedDate, OriginalOrderId, OwnerId, Pricebook2Id, SBQQ__Contracted__c, SBQQ__ContractingMethod__c, SBQQ__OrderBookings__c, SBQQ__PaymentTerm__c, SBQQ__PriceCalcStatus__c, SBQQ__PriceCalcStatusMessage__c, SBQQ__Quote__c, Status, ShippingAddress, SBQQ__TaxAmount__c, SBQQ__RenewalUpliftRate__c, SBQQ__RenewalTerm__c, StatusCode FROM Order

WHERE Id =:contract.SBQQ__Order__c ];

// Clone and link Opportunity

Opportunity clonedOpportunity = orgOpp.clone(false, true);

clonedOpportunity.AccountId = accountId;

// Set any other fields required for the cloned opportunity

insert clonedOpportunity;

// Clone and link Quote

SBQQ__Quote__c clonedQuote = orgQuote.clone(false, true);

clonedQuote.SBQQ__Account__c = accountId;

clonedQuote.SBQQ__Opportunity2__c = clonedOpportunity.Id;

// Set any other fields required for the cloned quote

insert clonedQuote;

System.debug('@@@ Quote good');

Id newOrderid;

if(orgOrder != null){

Order clonedOrder = orgOrder.clone(false, true);

clonedOrder.Status = 'Draft';

clonedOrder.SBQQ__Quote__c = clonedQuote.Id;

// Set any other fields required for the cloned order

insert clonedOrder;

newOrderid =clonedOrder.Id;

}else {

// Create a new Order

Order newOrder = new Order();

newOrder.Status = 'Draft';

newOrder.SBQQ__Quote__c = clonedQuote.Id;

// Initialize other fields as necessary for the new order

insert newOrder;

newOrderid = newOrder.Id;

}

// Clone and link Contract

Contract clonedContract = contract.clone(false, true);

clonedContract.AccountId = accountId;

clonedContract.SBQQ__Opportunity__c = clonedOpportunity.Id;

clonedContract.SBQQ__Quote__c = clonedQuote.Id;

clonedContract.SBQQ__Order__c = newOrderid;

// Set any other fields required for the cloned contract

insert clonedContract;

ids.add(clonedContract.Id);

return ids;

}catch (System.QueryException e) {

System.debug('Error : '+e);

}finally {

SBQQ.TriggerControl.enable();

}

return ids;

}

public class FlowInputs{

@InvocableVariable

public Id recordId;

@InvocableVariable

public Id IdAccount;

}

}


Conclusion


Transferring contracts between accounts in Salesforce, while maintaining the integrity and association of related sales records, can be streamlined using Apex. The key lies in effectively cloning the associated opportunity, quote, order, and finally, the contract itself. By following the outlined steps and adhering to Salesforce's best practices, developers can ensure a seamless and error-free contract transfer process, enhancing the efficiency and reliability of their Salesforce CRM system.

2 views0 comments

Recent Posts

See All

Comments

Rated 0 out of 5 stars.
No ratings yet

Add a rating
bottom of page