top of page

Ensuring Subscription Compliance in Salesforce CPQ: A Necessity for Today's Businesses

Updated: Dec 1, 2023


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


In the realm of modern enterprise resource planning and customer relationship management, Salesforce CPQ (Configure, Price, Quote) stands out as a pivotal tool. It streamlines the process of pricing and quoting, making it indispensable for businesses looking to enhance their sales operations. However, with the sophistication of such systems comes the need for meticulous management, particularly regarding subscription compliance. This is where a custom Apex code solution comes into play.


The Imperative of Subscription Compliance


Keeping Contracts in Check

At the heart of Salesforce CPQ's utility is its ability to manage complex contractual agreements and subscriptions. But what happens when these subscriptions deviate from their intended terms? That's a risk no business can afford. Subscription compliance checks ensure that every contract reflects what was agreed upon, safeguarding against discrepancies that can lead to financial loss or legal complications.


Regulatory Alignment

For many businesses, especially in sectors like finance or healthcare, adhering to industry-specific regulations is not just best practice—it's a legal requirement. Automated compliance checks help ensure that all subscriptions are in line with these regulations, mitigating the risk of costly penalties.


Customer Trust and Transparency

Customers expect clarity and accuracy in their contracts. By ensuring subscription compliance, businesses maintain a high level of trust and transparency with their clients, which is crucial for long-term relationships and customer satisfaction.


Operational Efficiency

Automating the process of compliance checking through a tailored Apex code solution significantly reduces manual workload. It allows for handling large volumes of data efficiently, cutting down on the time and resources typically required for manual compliance checks.


What Does the Custom Apex Code Do?


The Apex code for Salesforce CPQ compliance checks serves as a specialized tool designed to automatically verify the conformity of subscriptions within the system. It consists of a series of classes, each with a specific role:


1. **OneComplianceCheck**: This class acts as the entry point. It's designed to be invoked from Salesforce's process builder or other flows, kickstarting the compliance check process.


2. **OneComplianceCheckBatch**: This batch class processes data in batches, a necessary approach due to Salesforce's governor limits. It ensures that the system can handle large sets of subscription data without performance hiccups.


3. **SubscriptionComplianceCheck**: This class contains the core logic defining the compliance criteria for various subscription types. It updates the subscription records based on these predefined rules, ensuring that each subscription adheres to the required standards.

Classe Apex :

public class OneComplianceCheck {

private static String source = OneComplianceCheck.class.getname();

/**

* @description Mise à jour de la conformité de la souscription

* @param FlowInputParameters Paramète du flux

*/

@InvocableMethod(label='OneComplianceCheck')

public static void complianceCheck(List<FlowInputs> param) {

system.debug('Subscriptions Compliance check of ContractId : '+param[0].recordId);

OneComplianceCheckBatch batch = new OneComplianceCheckBatch(param[0].recordId);

Database.executeBatch(batch);

}

public class FlowInputs{

@InvocableVariable

public Id recordId;


}

} global class OneComplianceCheckBatch implements Database.Batchable<sObject>, Database.Stateful {

private static String source = OneComplianceCheckBatch.class.getname();

private Id recordId;

private String contractNumber;

private String raisonSociale;

public OneComplianceCheckBatch(Id contractId) {

Contract contract = [SELECT Id, ContractNumber, Account.Name FROM Contract WHERE Id = :contractId];

this.raisonSociale = contract.Account.Name;

this.contractNumber = contract.ContractNumber;

this.recordId = contract.Id;

}

/**

* start

* @description create the querey to be performed in the batch

* @param Database.BatchableContext cnx

* @return Database.QueryLocator

*/

global Database.QueryLocator start(Database.BatchableContext cnx) {

return Database.getQueryLocator('SELECT Id, SBQQ__RequiredById__c, '+

'EffectiveEndDate__c,'+

' SBQQ__Contract__r.CustomerSigned.Email, SBQQ__Contract__r.CustomerSigned.Phone, SBQQ__Contract__r.CustomerSigned.Name,' +

'SBQQ__Account__r.BillingStreet, SBQQ__Account__r.BillingPostalCode, SBQQ__Account__r.BillingCity, SBQQ__Account__r.BillingCountry ' +

'FROM SBQQ__Subscription__c '+

'WHERE SBQQ__Contract__c = \''+ recordId +'\'');

}


/**

* execute

* @description prepapre en retour un e requete avec les donnees necessaire pour l'execution du batch

* @param cDatabase.BatchableContextx cnx

* @param List<Contract> scope

*/

global void execute(Database.BatchableContext cnx, List<SBQQ__Subscription__c> scope) {

Try {

if (scope.size() > 0) {

List<SBQQ__Subscription__c> subsConforme = SubscriptionComplianceCheck.setConformite(scope);

if(subsConforme.size()>0){


//Mise à jour des subs conformes

Update subsConforme;

}

}

if(Test.isRunningTest()) {

CalloutException e = new CalloutException();

e.setMessage('Test des exceptions');

throw e;

}

} catch(Exception e) {

System.debug(source +'complianceCheck'+ e);

}

}


/**

* finish

* @description execute le batch et termine les procedures de modification/insertion

* @param Database.BatchableContext cnx

*/

global void finish(Database.BatchableContext cnx) {

}

} public class SubscriptionComplianceCheck {

public List<SBQQ__Subscription__c> listSubsConforme = new List<SBQQ__Subscription__c>();

public static String className = SubscriptionComplianceCheck.class.getname();

/**

* @description Vérifie si la souscription est conforme

* @param sub Enregistrement de souscription

* @return SBQQ__Subscription__c Retourne l'enregistrement de souscription

*/

public static List<SBQQ__Subscription__c> setConformite(List<SBQQ__Subscription__c> subList) {

List<SBQQ__Subscription__c> subs = new List<SBQQ__Subscription__c>();

for(SBQQ__Subscription__c sub : subList) {

//Définition sub avec champs MAJ par la conformité

SBQQ__Subscription__c subToCheck = new SBQQ__Subscription__c (Id = sub.Id,

Souscription_Conforme__c = sub.Souscription_Conforme__c,

MissingInformations__c = sub.MissingInformations__c,

SBQQ__RequiredById__c = sub.SBQQ__RequiredById__c); //Sub avec les infos MAJ par la conformité

SBQQ__Subscription__c subToUpdate = new SBQQ__Subscription__c ();

if (sub.ComplianceProcess__c == 'Boutiques') {

subToUpdate = setConformiteBoutique(sub);

} else if (sub.ComplianceProcess__c == 'Credit') {

subToUpdate = setConformiteCredits(sub);

}

//Ajout de la sub pour update que si changement effectué via conformité

if (subToUpdate.Id != null && subToCheck != subToUpdate){

subs.add(subToUpdate);

}

}

System.debug(' sub ' + subs);

return subs;

}

public static SBQQ__Subscription__c setConformiteBoutique(SBQQ__Subscription__c sub) {

SBQQ__Subscription__c subToUpdate = new SBQQ__Subscription__c(Id = sub.Id);

try {

if(sub.EffectiveEndDate__c >= Date.today() ) {

subToUpdate.Souscription_Conforme__c = true;

subToUpdate.MissingInformations__c ='';

subToUpdate.SBQQ__RequiredById__c= sub.SBQQ__RequiredById__c;

} else {

subToUpdate.MissingInformations__c = echecConformiteBoutique(sub).MissingInformations__c;

}

if(Test.isRunningTest()) {

CalloutException e = new CalloutException();

e.setMessage('Test des exceptions Conformité Boutique');

throw e;

}

} catch(Exception e) {

System.debug(className+'setConformiteBoutique'+ e);

}

return subToUpdate;

}



public static SBQQ__Subscription__c setConformiteCredits(SBQQ__Subscription__c sub) {

SBQQ__Subscription__c subToUpdate = new SBQQ__Subscription__c (Id = sub.Id);

try {

if(sub.EffectiveEndDate__c >= Date.today() ) {

subToUpdate.Souscription_Conforme__c = true;

subToUpdate.MissingInformations__c ='';

subToUpdate.SBQQ__RequiredById__c= sub.SBQQ__RequiredById__c;

} else {

subToUpdate.MissingInformations__c = echecConformiteCredits(sub).MissingInformations__c;

}

if(Test.isRunningTest()) {

CalloutException e = new CalloutException();

e.setMessage('Test des exceptions Conformité Crédit');

throw e;

}

} catch(Exception e) {

System.debug(className +'setConformiteCredits'+ e);

}

return subToUpdate;

}


public static SBQQ__Subscription__c echecConformiteBoutique(SBQQ__Subscription__c sub) {

String message = 'Conformite Boutique \n'+

'Date de fin : ' +sub.EffectiveEndDate__c ;

sub.MissingInformations__c = message;

return sub;

}

/**

* @description Mise en forme de la souscription lorsqu'elle n'est pas conforme pour des Crédits

* @param sub Enregistrement de souscription

* @return SBQQ__Subscription__c Retourne l'enregistrement de souscription

*/

public static SBQQ__Subscription__c echecConformiteCredits(SBQQ__Subscription__c sub) {

String message = 'Conformite Credits \n'+

'Date de fin : ' +sub.EffectiveEndDate__c +'\n';

sub.MissingInformations__c = message;

return sub;

}

}

Conclusion


In conclusion, the integration of a custom Apex code for subscription compliance checks in Salesforce CPQ is not just a luxury—it's a necessity in today's business environment. It enhances the accuracy of contract management, ensures regulatory compliance, fosters customer trust, and boosts operational efficiency. As businesses continue to navigate the complexities of digital transformations, solutions like this represent a significant step towards smarter, more reliable, and efficient

0 views0 comments

Recent Posts

See All

Comments

Rated 0 out of 5 stars.
No ratings yet

Add a rating
bottom of page