Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /**
- * @author vladimir.romanov
- * @description Apex class to illustrate the Clean Code Principles:
- * Boy Scout Rule,
- * Meaningful naming,
- * Using constants instead of hardcoded values,
- * Small functions.
- *
- * The class contains update trigger logic that filters quotes and inserts new QuoteFollowUp__c records
- *
- * createFollowUps(): public static method that creates an instance of an inner class and launches the logic.
- *
- * FollowUpCreator: inner class to encapsulate processing details
- * FollowUpCreator.createFollowUps(): private instance method that contains the processing logic
- *
- * Working with an instance of a class instead of a static function gives us
- * possibility to reuse instance variables
- * instead of passing them every time from one function to another
- * thus reducing number of arguments of the functions.
- */
- public class QuoteFollowUpCreator
- {
- // CONSTANTS
- // if tomorrow there will be one more condition for Request__c query the only thing to update is this constant!
- // Test classes reference this constant!
- @TestVisible
- private static final Set<String> REQUEST_CONDITIONS = new Set<String>
- {
- 'Condition1',
- 'Condition2'
- };
- // set up a 120 ruler in your IDE and split longer lines
- // to make sure one function is entirely in your field of vision
- private static final Id RECORDTYPEID_MY_REQUEST = [SELECT Id FROM RecordType WHERE SobjectType = 'Request__c'
- AND DeveloperName = 'MyRequest'].Id;
- // PUBLIC METHOD
- public static void createFollowUps(Map<Id, Quote> newQuoteById, Map<Id, Quote> oldQuoteById)
- {
- FollowUpCreator followUpCreator = new FollowUpCreator(newQuoteById, oldQuoteById);
- followUpCreator.createFollowUps();
- }
- // PRIVATE INNER CLASS (encapsulating processing details)
- private class FollowUpCreator
- {
- private Map<Id, Quote> quoteToProcessById = new Map<Id, Quote>();
- public FollowUpCreator(Map<Id, Quote> newQuoteById, Map<Id, Quote> oldQuoteById)
- {
- this.quoteToProcessById = getQuotesToProcessByIdsMap(newQuoteById, oldQuoteById);
- }
- // In the high level functions we write a human-readable story about what the code is doing!
- // We delegate details to the lower-levels functions!
- public void createFollowUps()
- {
- if (quoteToProcessById.isEmpty())
- return;
- List<Request__c> requests = retrieveRequests();
- Map<Id, Request__c> requestByQuoteId = getRequestByQuoteIdMap(requests);
- List<QuoteFollowUp__c> quoteFollowUps = getFollowUps(requestByQuoteId);
- if (!quoteFollowUps.isEmpty())
- insert quoteFollowUps;
- // Include name of the class, name of the function and name of the variable in the debug message.
- // this helps to know the origin of a message in debug log
- // You can develop a custom Logger class to avoid hardcoding the class name every time.
- System.debug('QuoteFollowUpCreator.FollowUpCreator::createFollowUps()::quoteFollowUps: '
- + JSON.serialize(quoteFollowUps));
- }
- private Map<Id, Quote> getQuotesToProcessByIdsMap(Map<Id, Quote> newQuoteById, Map<Id, Quote> oldQuoteById)
- {
- Map<Id, Quote> quoteToProcessById = new Map<Id, Quote>();
- for (Quote newQuote: newQuoteById.values())
- {
- Quote oldQuote = oldQuoteById.get(newQuote.Id);
- if (isFollowUpNeeded(newQuote, oldQuote))
- quoteToProcessById.put(newQuote.Id, newQuote);
- }
- return quoteToProcessById;
- }
- // Encapsulate logical condition into a small function that provides additional information
- // about the intention of the condition in the function name
- private Boolean isFollowUpNeeded(Quote newQuote, Quote oldQuote)
- {
- if (newQuote.Is_Acquired__c != true)
- return false;
- if (oldQuote.Acquired_Date__c == null && newQuote.Acquired_Date__c != null)
- return false; // the accuired date is set for the first time - old date null new not null
- return true;
- }
- private Map<Id, Request__c> getRequestByQuoteIdMap(List<Request__c> requests)
- {
- Map<Id, Request__c> requestByQuoteId = new Map<Id, Request__c>();
- Map<Id, List<Request__c>> requestsByAccountId = getRequestsByAccountIdMap(requests);
- requestByQuoteId = getRequestByQuoteIdMapFromRequestsByAccountId(requestsByAccountId);
- return requestByQuoteId;
- }
- // SOQL query is encapsulated to a small function for better readability and maintainability
- private List<Request__c> retrieveRequests()
- {
- Date earliestOrderDate;
- Set<Id> accountIds = new Set<Id>();
- for (Quote quoteRecord: quoteToProcessById.values())
- {
- if (earliestOrderDate == null
- || quoteRecord.Acquired_Date__c < earliestOrderDate)
- earliestOrderDate = quoteRecord.Acquired_Date__c;
- accountIds.add(quoteRecord.Account__c);
- }
- System.debug('QuoteFollowUpCreator.FollowUpCreator::retrieveRequests()::earliestOrderDate: ' + earliestOrderDate);
- System.debug('QuoteFollowUpCreator.FollowUpCreator::retrieveRequests()::accountIds: ' + accountIds);
- return
- [
- SELECT
- Id,
- Account__c,
- Account__r.Name,
- ExecutionDate__c,
- ExpirationDate__c
- FROM Request__c
- WHERE Condition__c IN :REQUEST_CONDITIONS
- AND ExecutionDate__c <= :earliestOrderDate
- AND RecordTypeId != :RECORDTYPEID_MY_REQUEST
- AND Account__c IN :accountIds
- ORDER BY ExecutionDate__c ASC
- ];
- }
- private Map<Id, List<Request__c>> getRequestsByAccountIdMap(List<Request__c> requests)
- {
- Map<Id, List<Request__c>> requestsByAccountId = new Map<Id, List<Request__c>>();
- for (Request__c request: requests)
- {
- List<Request__c> accountScoringRequests = requestsByAccountId.get(request.Account__c);
- if (accountScoringRequests != null)
- accountScoringRequests.add(request);
- else
- requestsByAccountId.put(request.Account__c, new List<Request__c> {request});
- }
- return requestsByAccountId;
- }
- private Map<Id, Request__c> getRequestByQuoteIdMapFromRequestsByAccountId(Map<Id, List<Request__c>> requestsByAccountId)
- {
- Map<Id, Request__c> requestByQuoteId = new Map<Id, Request__c>();
- for (Quote quoteRecord: quoteToProcessById.values())
- {
- List<Request__c> accountScoringRequests = requestsByAccountId.get(quoteRecord.Account__c);
- if (accountScoringRequests == null)
- continue; // using the 'continue;' statement to avoid adding unnecessary indentation layer
- for (Request__c request: accountScoringRequests)
- {
- //there should be just one request for each quote, here we'll just take the last one in the list
- if (request.ExpirationDate__c >= quoteRecord.Acquired_Date__c)
- requestByQuoteId.put(quoteRecord.Id, request);
- }
- }
- return requestByQuoteId;
- }
- private void getFollowUps()
- {
- List<QuoteFollowUp__c> quoteFollowUps = new List<QuoteFollowUp__c>();
- for (Quote quoteRecord: quoteToProcessById.values())
- {
- Request__c request = requestByQuoteId.get(quoteRecord.Id);
- if (request != null)
- quoteFollowUps.add(getNewFollowUp(quoteRecord, request));
- }
- return quoteFollowUps;
- }
- // this function constructs new records to insert, it is doing only one thing, and can be easily reused!
- private QuoteFollowUp__c getNewFollowUp(Quote quoteRecord, Request__c request)
- {
- QuoteFollowUp__c followUp = new QuoteFollowUp__c();
- followUp.Quote__c = quoteRecord.Id;
- followUp.Request__c = request.Id;
- followUp.Total_Investment__c = quoteRecord.Total_Investment__c;
- return followUp;
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement