top of page

Increase Price with renewal

Introduction

I want to increase the Price of a product renewed, how to do it?


Apex

CreatePriceRaiseQuote


// This class contains methods to process and update Salesforce CPQ data, particularly Contracts, Quotes, Opportunities.
public class CreatePriceRaiseQuote {

    // Resets fields on Contract objects and updates them in Salesforce.
    public static void resetContracts(List<Contract> contracts, Map<Id, Id> contractsIdAndOpportunityId) {
        // Iterating over each contract to reset certain fields.
        for (Contract c : contracts) {
            c.SBQQ__RenewalForecast__c = false;  // Resets the Renewal Forecast field.
            c.SBQQ__RenewalOpportunity__c = null;  // Clears the Renewal Opportunity field.
            c.SBQQ__RenewalQuoted__c = false;  // Resets the Renewal Quoted field.
        }
        update contracts;  // Updates the contract records in Salesforce.

        // Iterating again to set new values for the previously reset fields.
        for (Contract c : contracts) {
            c.SBQQ__RenewalForecast__c = true;  // Sets the Renewal Forecast field to true.
            c.SBQQ__RenewalOpportunity__c = contractsIdAndOpportunityId.get(c.Id);  // Sets the Renewal Opportunity field with a corresponding Opportunity ID.
            c.SBQQ__RenewalQuoted__c = true;  // Sets the Renewal Quoted field to true.
        }
        update contracts;  // Updates the contract records again with new values.
    }

    // Updates a list of Quotes based on associated Accounts and Opportunities.
    public static List<SBQQ__Quote__c> updateQuotes(List<Account> accounts, List<Opportunity> opportunities) {
        Set<String> accountIds = new Set<String>();
        // Extracting Account IDs from the provided Accounts list.
        for (Account acc : accounts) {
            accountIds.add(acc.Id);
        }

        // Query to fetch Quotes linked to the collected Account IDs.
        List<SBQQ__Quote__c> quotes = [
            SELECT Id, SBQQ__BillingCity__c, SBQQ__BillingCountry__c, SBQQ__BillingName__c,
                    SBQQ__BillingPostalCode__c, SBQQ__BillingState__c, SBQQ__BillingStreet__c, SBQQ__Opportunity2__c, SBQQ__Primary__c, SBQQ__SalesRep__c,
                    SBQQ__ShippingCity__c, SBQQ__ShippingCountry__c, SBQQ__ShippingName__c, SBQQ__ShippingPostalCode__c, SBQQ__ShippingState__c, SBQQ__ShippingStreet__c,
                    SBQQ__SubscriptionTerm__c, SBQQ__Account__c, SBQQ__Account__r.ShippingStreet, SBQQ__Account__r.ShippingCity, SBQQ__Account__r.Name, SBQQ__Account__r.ShippingPostalCode,
                    SBQQ__Account__r.Website, SBQQ__Account__r.BillingCity, SBQQ__Account__r.BillingCountry,
                    SBQQ__Account__r.BillingPostalCode, SBQQ__Account__r.BillingState, SBQQ__Account__r.BillingStreet, SBQQ__Account__r.ShippingCountry, SBQQ__Account__r.ShippingState
            FROM SBQQ__Quote__c
            WHERE SBQQ__Primary__c = TRUE
            AND SBQQ__Type__c = 'Renewal'
            AND SBQQ__Opportunity2__r.StageName = 'Draft'
            AND SBQQ__Account__c IN :accountIds
        ];

        // Associating each Quote with the corresponding Opportunity.
        for (SBQQ__Quote__c q : quotes) {
            Opportunity opportunityToQuote = new Opportunity();
            // Matching each Quote to a an Opportunity based on Account ID.
            for (Opportunity o : opportunities) {
                if (q.SBQQ__Account__c == o.AccountId) {
                    opportunityToQuote = o;
                    break;
                }
            }
            // Update individual Quote record with specific field values.
            updateQuote(q, opportunityToQuote);
        }
        return quotes;  // Return the updated list of Quotes.
    }

    // Helper method to set specific fields on an SBQQ__Quote__c object.
    public static void updateQuote(SBQQ__Quote__c q, Opportunity o) {
        // Assigning various billing and shipping details from the Account to the Quote.
        q.SBQQ__BillingCity__c = q.SBQQ__Account__r.BillingCity;
        q.SBQQ__BillingCountry__c = q.SBQQ__Account__r.BillingCountry;
        q.SBQQ__BillingName__c = q.SBQQ__Account__r.Name;
        q.SBQQ__BillingPostalCode__c = q.SBQQ__Account__r.BillingPostalCode;
        q.SBQQ__BillingState__c = q.SBQQ__Account__r.BillingState;
        q.SBQQ__BillingStreet__c = q.SBQQ__Account__r.BillingStreet;
        q.SBQQ__ShippingCity__c = q.SBQQ__Account__r.ShippingCity;
        q.SBQQ__ShippingCountry__c = q.SBQQ__Account__r.ShippingCountry;
        q.SBQQ__ShippingName__c = q.SBQQ__Account__r.Name;
        q.SBQQ__ShippingPostalCode__c = q.SBQQ__Account__r.ShippingPostalCode;
        q.SBQQ__ShippingState__c = q.SBQQ__Account__r.ShippingState;
        q.SBQQ__ShippingStreet__c = q.SBQQ__Account__r.ShippingStreet;
        q.SBQQ__SubscriptionTerm__c = 12;  // Setting the Subscription Term to 12 months.
        q.SBQQ__Opportunity2__c = o.Id;  // Linking the Quote to the Opportunity.
    }

    // Updates a list of Opportunities with specific values and links them to Contracts.
    public static void updateOpportunities(List<Opportunity> opportunities, List<Contract> contracts){
        for(Opportunity o: opportunities){
            o.SBQQ__Renewal__c = true;  // Marking the Opportunity as a renewal.
            // Linking the Opportunity to the corresponding Contract.
            for(Contract c: contracts){
                if(c.SBQQ__RenewalOpportunity__c == o.Id){
                    o.SBQQ__RenewedContract__c = c.Id;  // Setting the Renewed Contract field on the Opportunity.
                    break;
                }
            }
        }
        update opportunities;  // Update the Opportunities in Salesforce.
    }
}

CreatePriceRaiseQuoteLinesWrapper


// A global class implementing Salesforce's batch and stateful interfaces for processing Quote Line records.
global class CreatePriceRaiseQuoteLinesWrapper implements Database.Batchable<SObject>, Database.Stateful {

    // Holds the IDs of the quotes to be processed.
    public Set<String> quotesIds = new Set<String>();

    // Flag to determine if automatic price raise options should be applied.
    public Boolean autoRaiseOptions;

    // Constructor: Initializes the class with a set of Quote IDs and a boolean for auto raise options.
    global CreatePriceRaiseQuoteLinesWrapper(Set<String> quotesIds, Boolean autoRaiseOptions) {
        this.quotesIds = quotesIds;
        this.autoRaiseOptions = autoRaiseOptions;
    }

    // Called at the start of the batch process; returns a query locator for Quotes to be processed.
    global Database.QueryLocator start(Database.BatchableContext bc) {
        return Database.getQueryLocator([SELECT Id FROM SBQQ__Quote__c WHERE Id IN :quotesIds]);
    }

    // Called for each batch of records; handles the business logic for updating Quote Lines.
    global void execute(Database.BatchableContext bc, List<SBQQ__Quote__c> scope) {

        // Query to fetch all related Quote Lines for the current batch of Quotes.
        List<SBQQ__QuoteLine__c> allQuoteLinesToProcess = [
            SELECT Id, Name, SBQQ__StartDate__c, SBQQ__RenewedSubscription__c,
            SBQQ__RenewedSubscription__r.NewDiscountAmount__c, SBQQ__AdditionalDiscountAmount__c, SBQQ__ListPrice__c, SBQQ__PricebookEntryId__c,
            SBQQ__RequiredBy__c, SBQQ__RequiredBy__r.SBQQ__RequiredBy__c, SBQQ__Quote__c,
            SBQQ__RenewedSubscription__r.SBQQ__SpecialPrice__c, SBQQ__RenewedSubscription__r.SBQQ__AdditionalDiscountAmount__c,
            SBQQ__Product__c
            FROM SBQQ__QuoteLine__c
            WHERE SBQQ__Quote__c IN :scope
        ];

        // Initialize collections for processing.
        Set<String> pricebookEntryId = new Set<String>();
        Set<SBQQ__QuoteLine__c> relatedQuoteLines = new Set<SBQQ__QuoteLine__c>();
        Set<Id> risePricesIds = new Set<Id>();
        Set<String> relatedParentIds = new Set<String>();
        List<SBQQ__QuoteLine__c> quoteLinesToUpdate = new List<SBQQ__QuoteLine__c>();

        // Collect Pricebook Entry IDs from all quote lines.
        for(SBQQ__QuoteLine__c ql : allQuoteLinesToProcess){
            pricebookEntryId.add(ql.SBQQ__PricebookEntryId__c);
        }

        // Fetch Pricebook Entry details for price calculations.
        Map<Id, PricebookEntry> priceBookEntries = getPriceBookEntries(pricebookEntryId);

        // Main logic to process each Quote Line and apply necessary updates.
        for (SBQQ__QuoteLine__c ql : allQuoteLinesToProcess) {
            if (String.isNotBlank(ql.SBQQ__RenewedSubscription__c) && ql.SBQQ__RenewedSubscription__r.NewDiscountAmount__c != null) {
                ql.SBQQ__AdditionalDiscountAmount__c = ql.SBQQ__RenewedSubscription__r.NewDiscountAmount__c;
                if (ql.SBQQ__PricebookEntryId__c != null) {
                    ql.SBQQ__ListPrice__c = priceBookEntries.get(ql.SBQQ__PricebookEntryId__c).UnitPrice;
                } else {
                    PricebookEntry pricebook = [SELECT Id, UnitPrice FROM PricebookEntry WHERE Product2Id =: ql.SBQQ__Product__c];
                    ql.SBQQ__ListPrice__c = pricebook.UnitPrice;
                }
                ql.SBQQ__StartDate__c = Date.today();

                relatedQuoteLines.add(ql.SBQQ__RequiredBy__r);
                risePricesIds.add(ql.Id);
                quoteLinesToUpdate.add(ql);
            }
        }

        // Process related parent Quote Lines if any exist.
        if (relatedQuoteLines.size() > 0) {
            for (SBQQ__QuoteLine__c ql : relatedQuoteLines) {
                relatedParentIds.add(ql.Id);
            }
        }

        // Fetch and process related parent Quote Lines.
        List<SBQQ__QuoteLine__c> relatedParentQuoteLinesToUpdate = [
            SELECT ID, SBQQ__ListPrice__c
            FROM SBQQ__QuoteLine__c
            WHERE Id =: relatedParentIds
            AND ID NOT IN: risePricesIds
        ];

        // Update start dates for related parent Quote Lines and add them to the update list.
        if (relatedParentQuoteLinesToUpdate.size() > 0) {
            for (SBQQ__QuoteLine__c quoteLine : relatedParentQuoteLinesToUpdate) {
                quoteLine.SBQQ__StartDate__c = Date.today();
                quoteLinesToUpdate.add(quoteLine);
            }
        }

        // Fetch and process related child Quote Lines.
        List<SBQQ__QuoteLine__c> relatedChildQuoteLinesToUpdate = [
            SELECT ID, SBQQ__ListPrice__c, SBQQ__RenewedSubscription__r.NewDiscountAmount__c,
            SBQQ__PricebookEntryId__c, SBQQ__RenewedSubscription__r.SBQQ__SpecialPrice__c,
            SBQQ__AdditionalDiscountAmount__c, SBQQ__RenewedSubscription__r.SBQQ__AdditionalDiscountAmount__c
            FROM SBQQ__QuoteLine__c
            WHERE SBQQ__RequiredBy__c =: relatedParentIds
            AND ID NOT IN: risePricesIds
        ];

        // Additional processing for related child Quote Lines.
        for(SBQQ__QuoteLine__c ql : relatedChildQuoteLinesToUpdate){
            pricebookEntryId.add(ql.SBQQ__PricebookEntryId__c);
        }

        // Fetch price book details for related child Quote Lines.
        Map<Id, PricebookEntry> priceBookForRelatedQuoteLines = getPriceBookEntries(pricebookEntryId);

        // Apply price adjustments and update start dates for related child Quote Lines.
        for (SBQQ__QuoteLine__c quoteLine : relatedChildQuoteLinesToUpdate) {
            if (autoRaiseOptions == true) {
                if (quoteLine.SBQQ__RenewedSubscription__r.NewDiscountAmount__c == null && quoteLine.SBQQ__RenewedSubscription__c != null) {
                    Double newPrice = priceBookForRelatedQuoteLines.get(quoteLine.SBQQ__PricebookEntryId__c).UnitPrice;
                    Double oldPrice = quoteLine.SBQQ__RenewedSubscription__r.SBQQ__SpecialPrice__c;
                    quoteLine.SBQQ__AdditionalDiscountAmount__c = quoteLine.SBQQ__RenewedSubscription__r.SBQQ__AdditionalDiscountAmount__c + (newPrice - oldPrice);
                    quoteLine.SBQQ__ListPrice__c = priceBookForRelatedQuoteLines.get(quoteLine.SBQQ__PricebookEntryId__c).UnitPrice;
                }
            }
            quoteLine.SBQQ__StartDate__c = Date.today();

            quoteLinesToUpdate.add(quoteLine);
        }

        // Update all modified Quote Lines in the database.
        update quoteLinesToUpdate;
    }

    // Called after the batch job is completed; schedules another batch job for further processing.
    global void finish(Database.BatchableContext bc) {
        System.scheduleBatch(new CreatePriceRaiseUpdatePartDiscWrapper(quotesIds), 'Price Raise Update Part Disc', 5);
    }

    // Helper method to fetch Pricebook Entry records for given IDs.
    private Map<Id, PricebookEntry> getPriceBookEntries(Set<String> pricebookEntryId){
        return new Map<Id, PricebookEntry>([SELECT Id, UnitPrice FROM PricebookEntry WHERE Id IN: pricebookEntryId]);
    }
}

CreatePriceRaiseQuoteWrapper


// A global class implementing Salesforce's Schedulable, Batchable, and Stateful interfaces.
// Designed for batch processing related to price raises in Quotes, linked to Accounts and related entities.
global class CreatePriceRaiseQuoteWrapper implements Schedulable, Database.Batchable<SObject>, Database.Stateful {

    // A set to hold the IDs of quotes that are processed by this batch.
    private Set<String> quoteIds = new Set<String>();

    // A Boolean flag indicating whether automatic price raising options should be applied.
    public Boolean autoRaiseOptions;

    // Constructor: Initializes the class with the autoRaiseOptions parameter.
    global CreatePriceRaiseQuoteWrapper(Boolean autoRaiseOptions) {
        this.autoRaiseOptions = autoRaiseOptions;
    }

    // The start method for the batch process. Defines the scope of records (Accounts and related entities) to be processed.
    global Database.QueryLocator start(Database.BatchableContext bc) {
        Date today = Date.today();

        return Database.getQueryLocator([
            SELECT Id, Name, BillingCity, BillingCountry, BillingPostalCode, BillingState, BillingStreet, ShippingCity, ShippingCountry, ShippingPostalCode,
            ShippingState, ShippingStreet, Website,
            (SELECT Id, SBQQ__RenewalForecast__c, SBQQ__RenewalOpportunity__c, SBQQ__RenewalQuoted__c FROM Contracts),
            (SELECT Id, AccountId, SBQQ__Renewal__c, SBQQ__RenewedContract__c FROM Opportunities WHERE StageName = 'Draft' LIMIT 1)
            FROM Account
        ]);
    }

    // The execute method is called for each batch of records. It processes the Accounts and their related entities.
    global void execute(Database.BatchableContext bc, List<Account> scope) {
        // List to hold accounts that meet certain criteria.
        List<Account> goodAccounts = new List<Account>();

        // Filtering accounts with associated contracts and opportunities.
        for (Account a : scope) {
            if (!a.Contracts.isEmpty() && !a.Opportunities.isEmpty()) {
                goodAccounts.add(a);
            }
        }

        // Collections for holding and processing related records.
        List<Contract> contracts = new List<Contract>();
        List<Opportunity> opportunities = new List<Opportunity>();
        Map<Id, Id> contractsIdAndOpportunityId = new Map<Id, Id>();

        // Loop through filtered accounts and collect related records.
        for (Account a : goodAccounts) {
            for (Contract c : a.Contracts) {
                contracts.add(c);
                contractsIdAndOpportunityId.put(c.Id, a.Opportunities[0].Id);
            }
            opportunities.add(a.Opportunities[0]);
        }

        // Call methods to reset contracts and update quotes based on the collected data.
        CreatePriceRaiseQuote.resetContracts(contracts, contractsIdAndOpportunityId);
        List<SBQQ__Quote__c> quotes = CreatePriceRaiseQuote.updateQuotes(goodAccounts, opportunities);

        // Prepare a list of accounts to update.
        List<Account> accountsToUpdate = new List<Account>();

        // Assign corresponding accounts to quotes and add to the update list.
        for (SBQQ__Quote__c quote : quotes) {
            Account accountToUpdate = quote.SBQQ__Account__r;
            accountsToUpdate.add(accountToUpdate);
        }

        // Update accounts and quotes in Salesforce.
        update accountsToUpdate;
        update quotes;

        // Add processed quote IDs to the set for further processing.
        for (SBQQ__Quote__c q : quotes) {
            quoteIds.add(q.Id);
        }

        // Call methods to update opportunities.
        CreatePriceRaiseQuote.updateOpportunities(opportunities, contracts);
    }

    // The finish method is called after the last batch execution. It schedules another batch job for further processing.
    global void finish(Database.BatchableContext bc) {
        System.scheduleBatch(new CreatePriceRaiseQuoteLinesWrapper(quoteIds, autoRaiseOptions), 'Price Raise Quote Lines', 5, 20);
    }

    // Execute method for the Schedulable interface; used to trigger the batch job.
    global void execute(SchedulableContext ctx) {
        CreatePriceRaiseQuoteWrapper scheduledCreatePriceRaiseQuote = new CreatePriceRaiseQuoteWrapper(null);
        Database.executeBatch(scheduledCreatePriceRaiseQuote);
    }
}


CreatePriceRaiseUpdatePartDiscWrapper

// Declares a global class that implements the Salesforce Batchable interface for processing SObjects.
global class CreatePriceRaiseUpdatePartDiscWrapper implements Database.Batchable<SObject>{
    // A public set to store Quote IDs.
    public Set<String> quotesIds = new Set<String>();

    // Constructor for the class, initializing it with a set of Quote IDs.
    global CreatePriceRaiseUpdatePartDiscWrapper(Set<String> quotesIds) {
        this.quotesIds = quotesIds;
    }
    
    // The start method of the Batchable interface. This is where the batch job begins.
    // It returns a QueryLocator for the batch process, selecting quotes with IDs in the quotesIds set.
    global Database.QueryLocator start(Database.BatchableContext bc) {
        return Database.getQueryLocator([
        SELECT Id, SBQQ__PartnerDiscount__c
        FROM SBQQ__Quote__c
        WHERE Id IN :quotesIds
        ]);
    }
    
    // The execute method is called for each batch of records processed.
    // It processes each Quote in the provided scope.
    global void execute(Database.BatchableContext bc, List<SBQQ__Quote__c> scope) {
        
        // Debug statements to log the size and content of the current batch scope.
        System.debug(scope.size());
        System.debug(scope);

        // Iterates through each Quote in the scope and resets its Partner Discount to 0.
        for(SBQQ__Quote__c q: scope){
            q.SBQQ__PartnerDiscount__c = 0;
        }
        // Updates the Quote records in the database with the new Partner Discount values.
        update scope;
        
        // Queries for QuoteLine records related to the first Quote in the scope.
        // Note: This query seems to be unused or incomplete in the current context.
        List<SBQQ__QuoteLine__c> qls = [SELECT Id, SBQQ__ListPrice__c, SBQQ__PricebookEntryId__c, SBQQ__AdditionalDiscountAmount__c FROM SBQQ__QuoteLine__c WHERE SBQQ__Quote__c =: scope[0].id];
        
    }
    
    // The finish method is called after the batch process is completed.
    global void finish(Database.BatchableContext bc) {
    }
}

Result

You need sometimes to wait, so i show you how it will work


the result should be like this:



0 views0 comments

Recent Posts

See All

Comentários

Avaliado com 0 de 5 estrelas.
Ainda sem avaliações

Adicione uma avaliação
bottom of page