Monday, March 4, 2024

Salesforce Apex: Template Method Pattern

The Template Method design pattern is used to define the skeleton of an algorithm in an operation, deferring some steps to subclasses. It lets one redefine certain steps of an algorithm without changing the algorithm's structure. In Salesforce Apex, particularly in the context of CPQ, this pattern can be useful for setting up a standard process for quote calculation with certain steps that vary depending on the context or product.

A common functionality in CPQ where the Template Method pattern can be applied is in the customization of quote or quote line calculations. CPQ has a standard calculation process, but businesses often need to apply additional, context-specific logic, such as industry-specific pricing adjustments, geographical tax calculations, or promotional discounts.

Let's design a Template Method pattern to implement a customized pricing algorithm that allows for different types of adjustments to be made to the quote lines.

Step 1: Define the abstract base class with a template method

public abstract class QuoteCalculatorTemplate {
    // The template method defines the skeleton of the algorithm.
    public final void calculate(List<SBQQ__QuoteLine__c> quoteLines) {
        applyStandardPricing(quoteLines);
        applyCustomAdjustments(quoteLines);
        applyTaxes(quoteLines);
        finalizeCalculations(quoteLines);
    }
    
    // These methods are common and have a standard implementation.
    private void applyStandardPricing(List<SBQQ__QuoteLine__c> quoteLines) {
        // Apply the standard CPQ pricing logic.
    }
    
    private void applyTaxes(List<SBQQ__QuoteLine__c> quoteLines) {
        // Apply standard tax logic.
    }
    
    private void finalizeCalculations(List<SBQQ__QuoteLine__c> quoteLines) {
        // Finalize the calculations, e.g., rounding off the prices.
    }
    
    // These methods are placeholders for custom logic and should be overridden.
    protected abstract void applyCustomAdjustments(List<SBQQ__QuoteLine__c> quoteLines);
}

Step 2: Create concrete subclasses to implement the varying steps

public class IndustrySpecificQuoteCalculator extends QuoteCalculatorTemplate {
    // Override the necessary methods to provide custom behavior.
    protected override void applyCustomAdjustments(List<SBQQ__QuoteLine__c> quoteLines) {
        for (SBQQ__QuoteLine__c line : quoteLines) {
            // Apply industry-specific pricing adjustments.
            // For example, apply a discount based on the product's industry segment.
        }
    }
}

public class PromotionalQuoteCalculator extends QuoteCalculatorTemplate {
    protected override void applyCustomAdjustments(List<SBQQ__QuoteLine__c> quoteLines) {
        for (SBQQ__QuoteLine__c line : quoteLines) {
            // Apply promotional discounts based on certain criteria.
            // For example, check if the product is part of a current promotion.
        }
    }
}

Step 3: Use the concrete classes in the CPQ process

public with sharing class QuoteLinePriceManager {
    public static void calculatePrices(List<SBQQ__QuoteLine__c> quoteLines) {
        // Determine the context and select the appropriate calculator.
        QuoteCalculatorTemplate calculator;
        
        if (isIndustrySpecificContext()) {
            calculator = new IndustrySpecificQuoteCalculator();
        } else if (isPromotionalPeriod()) {
            calculator = new PromotionalQuoteCalculator();
        } else {
            // Fallback to a default calculator if needed
            calculator = new DefaultQuoteCalculator();
        }
        
        // Execute the template method to perform the calculations.
        calculator.calculate(quoteLines);
        
        // Bulk update the quote lines with the calculated prices.
        update quoteLines;
    }

    private static Boolean isIndustrySpecificContext() {
        // Implement your logic to determine if the context is industry-specific.
        return false; // Simplified for example purposes.
    }

    private static Boolean isPromotionalPeriod() {
        // Implement your logic to determine if it's a promotional period.
        return true; // Simplified for example purposes.
    }
}

In this example, QuoteCalculatorTemplate provides the structure for the quote calculation process. It defines the template method calculate, which outlines the steps for calculating quotes. The steps that are common across all calculations, such as applying standard pricing and taxes, are implemented directly within this class. applyCustomAdjustments is an abstract method that subclasses must implement to provide custom pricing logic.

IndustrySpecificQuoteCalculator and PromotionalQuoteCalculator are concrete implementations that provide specific algorithms for their respective scenarios. When calculatePrices is called, it determines the context and chooses the appropriate subclass of QuoteCalculatorTemplate to use. The chosen calculator's calculate method is then called to perform the pricing calculation, and the quote lines are updated in bulk.

Share This:    Facebook Twitter

1 comment:

Total Pageviews

My Social Profiles

View Sonal's profile on LinkedIn

Tags

__proto__ $Browser Access Grants Accessor properties Admin Ajax AllowsCallouts Apex Apex Map Apex Sharing AssignmentRuleHeader AsyncApexJob Asynchronous Auth Provider AWS Callbacks Connected app constructor Cookie CPU Time CSP Trusted Sites CSS Custom settings CustomLabels Data properties Database.Batchable Database.BatchableContext Database.query Describe Result Destructuring Dynamic Apex Dynamic SOQL Einstein Analytics enqueueJob Enterprise Territory Management Enumeration escapeSingleQuotes featured Flows geolocation getGlobalDescribe getOrgDefaults() getPicklistValues getRecordTypeId() getRecordTypeInfosByName() getURLParameters Google Maps Governor Limits hasOwnProperty() Heap Heap Size IIFE Immediately Invoked Function Expression Interview questions isCustom() Javascript Javascript Array jsForce Lightning Lightning Components Lightning Events lightning-record-edit-form lightning:combobox lightning:icon lightning:input lightning:select LockerService Lookup LWC Manual Sharing Map Modal Module Pattern Named Credentials NodeJS OAuth Object.freeze() Object.keys() Object.preventExtensions() Object.seal() Organization Wide Defaults Override PDF Reader Performance performance.now() Permission Sets Picklist Platform events Popup Postman Primitive Types Profiles Promise propertyIsEnumerable() prototype Query Selectivity Queueable Record types Reference Types Regex Regular Expressions Relationships Rest API Rest Operator Revealing Module Pattern Role Hierarchy Salesforce Salesforce Security Schema.DescribeFieldResult Schema.DescribeSObjectResult Schema.PicklistEntry Schema.SObjectField Schema.SObjectType Security Service Components Shadow DOM Sharing Sharing Rules Singleton Slots SOAP API SOAP Web Services SOQL SOQL injection Spread Operator Star Rating stripInaccessible svg svgIcon Synchronous this Token Triggers uiObjectInfoApi Upload Files VSCode Web Services XHR
Scroll To Top