SHARE
TWEET

Untitled

a guest Jun 25th, 2019 52 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /**
  2.  *  @author         vladimir.romanov
  3.  *  @description    Apex class to illustrate the Clean Code Principles:
  4.  *                          Boy Scout Rule,
  5.  *                          Meaningful naming,
  6.  *                          Using constants instead of hardcoded values,
  7.  *                          Small functions.
  8.  *
  9.  *                  The class contains update trigger logic that filters quotes and inserts new QuoteFollowUp__c records
  10.  *
  11.  *                  createFollowUps():  public static method that creates an instance of an inner class and launches the logic.
  12.  *                 
  13.  *                  FollowUpCreator:    inner class to encapsulate processing details              
  14.  *                  FollowUpCreator.createFollowUps(): private instance method that contains the processing logic
  15.  *
  16.  *                  Working with an instance of a class instead of a static function gives us
  17.  *                                  possibility to reuse instance variables
  18.  *                                  instead of passing them every time from one function to another
  19.  *                                  thus reducing number of arguments of the functions.
  20.  */
  21. public class QuoteFollowUpCreator
  22. {
  23.     // CONSTANTS
  24.  
  25.     // if tomorrow there will be one more condition for Request__c query the only thing to update is this constant!
  26.     // Test classes reference this constant!
  27.     @TestVisible
  28.     private static final Set<String> REQUEST_CONDITIONS = new Set<String>
  29.     {
  30.         'Condition1',
  31.         'Condition2'
  32.     };
  33.  
  34.     // set up a 120 ruler in your IDE and split longer lines
  35.     // to make sure one function is entirely in your field of vision
  36.     private static final Id RECORDTYPEID_MY_REQUEST = [SELECT Id FROM RecordType WHERE SobjectType = 'Request__c'
  37.                                                                                 AND DeveloperName = 'MyRequest'].Id;
  38.  
  39.     // PUBLIC METHOD
  40.  
  41.     public static void createFollowUps(Map<Id, Quote> newQuoteById, Map<Id, Quote> oldQuoteById)
  42.     {
  43.         FollowUpCreator followUpCreator = new FollowUpCreator(newQuoteById, oldQuoteById);
  44.         followUpCreator.createFollowUps();
  45.     }
  46.  
  47.     // PRIVATE INNER CLASS (encapsulating processing details)
  48.  
  49.     private class FollowUpCreator
  50.     {
  51.         private Map<Id, Quote> quoteToProcessById = new Map<Id, Quote>();
  52.  
  53.         public FollowUpCreator(Map<Id, Quote> newQuoteById, Map<Id, Quote> oldQuoteById)
  54.         {
  55.             this.quoteToProcessById = getQuotesToProcessByIdsMap(newQuoteById, oldQuoteById);
  56.         }
  57.  
  58.         // In the high level functions we write a human-readable story about what the code is doing!
  59.         // We delegate details to the lower-levels functions!
  60.         public void createFollowUps()
  61.         {
  62.             if (quoteToProcessById.isEmpty())
  63.                 return;
  64.             List<Request__c> requests = retrieveRequests();
  65.             Map<Id, Request__c> requestByQuoteId = getRequestByQuoteIdMap(requests);
  66.             List<QuoteFollowUp__c> quoteFollowUps = getFollowUps(requestByQuoteId);
  67.             if (!quoteFollowUps.isEmpty())
  68.                 insert quoteFollowUps;
  69.  
  70.             // Include name of the class, name of the function and name of the variable in the debug message.
  71.             // this helps to know the origin of a message in debug log
  72.             // You can develop a custom Logger class to avoid hardcoding the class name every time.
  73.             System.debug('QuoteFollowUpCreator.FollowUpCreator::createFollowUps()::quoteFollowUps: '
  74.                 + JSON.serialize(quoteFollowUps));
  75.         }
  76.  
  77.         private Map<Id, Quote> getQuotesToProcessByIdsMap(Map<Id, Quote> newQuoteById, Map<Id, Quote> oldQuoteById)
  78.         {
  79.             Map<Id, Quote> quoteToProcessById = new Map<Id, Quote>();
  80.             for (Quote newQuote: newQuoteById.values())
  81.             {
  82.                 Quote oldQuote = oldQuoteById.get(newQuote.Id);
  83.                 if (isFollowUpNeeded(newQuote, oldQuote))
  84.                     quoteToProcessById.put(newQuote.Id, newQuote);
  85.             }
  86.             return quoteToProcessById;
  87.         }
  88.  
  89.         // Encapsulate logical condition into a small function that provides additional information
  90.         // about the intention of the condition in the function name
  91.         private Boolean isFollowUpNeeded(Quote newQuote, Quote oldQuote)
  92.         {
  93.             if (newQuote.Is_Acquired__c != true)
  94.                 return false;
  95.             if (oldQuote.Acquired_Date__c == null && newQuote.Acquired_Date__c != null)
  96.                 return false; // the accuired date is set for the first time - old date null new not null
  97.             return true;
  98.         }
  99.  
  100.         private Map<Id, Request__c> getRequestByQuoteIdMap(List<Request__c> requests)
  101.         {
  102.             Map<Id, Request__c> requestByQuoteId = new Map<Id, Request__c>();
  103.  
  104.             Map<Id, List<Request__c>> requestsByAccountId = getRequestsByAccountIdMap(requests);
  105.             requestByQuoteId = getRequestByQuoteIdMapFromRequestsByAccountId(requestsByAccountId);
  106.  
  107.             return requestByQuoteId;
  108.         }
  109.  
  110.         // SOQL query is encapsulated to a small function for better readability and maintainability
  111.         private List<Request__c> retrieveRequests()
  112.         {
  113.             Date earliestOrderDate;
  114.             Set<Id> accountIds = new Set<Id>();
  115.  
  116.             for (Quote quoteRecord: quoteToProcessById.values())
  117.             {
  118.                 if (earliestOrderDate == null
  119.                     || quoteRecord.Acquired_Date__c < earliestOrderDate)
  120.                     earliestOrderDate = quoteRecord.Acquired_Date__c;
  121.                 accountIds.add(quoteRecord.Account__c);
  122.             }
  123.  
  124.             System.debug('QuoteFollowUpCreator.FollowUpCreator::retrieveRequests()::earliestOrderDate: ' + earliestOrderDate);
  125.             System.debug('QuoteFollowUpCreator.FollowUpCreator::retrieveRequests()::accountIds: ' + accountIds);
  126.  
  127.             return
  128.             [
  129.                 SELECT
  130.                     Id,
  131.                     Account__c,
  132.                     Account__r.Name,
  133.                     ExecutionDate__c,
  134.                     ExpirationDate__c
  135.                 FROM Request__c
  136.                 WHERE Condition__c IN :REQUEST_CONDITIONS
  137.                 AND ExecutionDate__c <= :earliestOrderDate
  138.                 AND RecordTypeId != :RECORDTYPEID_MY_REQUEST
  139.                 AND Account__c IN :accountIds
  140.                 ORDER BY ExecutionDate__c ASC
  141.             ];
  142.         }
  143.  
  144.         private Map<Id, List<Request__c>> getRequestsByAccountIdMap(List<Request__c> requests)
  145.         {
  146.             Map<Id, List<Request__c>> requestsByAccountId = new Map<Id, List<Request__c>>();
  147.             for (Request__c request: requests)
  148.             {
  149.                 List<Request__c> accountScoringRequests = requestsByAccountId.get(request.Account__c);
  150.                 if (accountScoringRequests != null)
  151.                     accountScoringRequests.add(request);
  152.                 else
  153.                     requestsByAccountId.put(request.Account__c, new List<Request__c> {request});
  154.             }
  155.  
  156.             return requestsByAccountId;
  157.         }
  158.  
  159.         private Map<Id, Request__c> getRequestByQuoteIdMapFromRequestsByAccountId(Map<Id, List<Request__c>> requestsByAccountId)
  160.         {
  161.             Map<Id, Request__c> requestByQuoteId = new Map<Id, Request__c>();
  162.             for (Quote quoteRecord: quoteToProcessById.values())
  163.             {
  164.                 List<Request__c> accountScoringRequests = requestsByAccountId.get(quoteRecord.Account__c);
  165.                 if (accountScoringRequests == null)
  166.                     continue; // using the 'continue;' statement to avoid adding unnecessary indentation layer
  167.                 for (Request__c request: accountScoringRequests)
  168.                 {
  169.                     //there should be just one request for each quote, here we'll just take the last one in the list
  170.                     if (request.ExpirationDate__c >= quoteRecord.Acquired_Date__c)
  171.                         requestByQuoteId.put(quoteRecord.Id, request);
  172.                 }
  173.             }
  174.             return requestByQuoteId;
  175.         }
  176.  
  177.         private void getFollowUps()
  178.         {
  179.             List<QuoteFollowUp__c> quoteFollowUps = new List<QuoteFollowUp__c>();
  180.             for (Quote quoteRecord: quoteToProcessById.values())
  181.             {
  182.                 Request__c request = requestByQuoteId.get(quoteRecord.Id);
  183.                 if (request != null)
  184.                     quoteFollowUps.add(getNewFollowUp(quoteRecord, request));
  185.             }
  186.             return quoteFollowUps;
  187.         }
  188.  
  189.         // this function constructs new records to insert, it is doing only one thing, and can be easily reused!
  190.         private QuoteFollowUp__c getNewFollowUp(Quote quoteRecord, Request__c request)
  191.         {
  192.             QuoteFollowUp__c followUp = new QuoteFollowUp__c();
  193.             followUp.Quote__c = quoteRecord.Id;
  194.             followUp.Request__c = request.Id;
  195.             followUp.Total_Investment__c = quoteRecord.Total_Investment__c;
  196.             return followUp;
  197.         }
  198.     }
  199.  
  200. }
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top