+ Start a Discussion
Nicholas Sewitz 9Nicholas Sewitz 9 

Batch Matching Apex Job only merging one of the matches

Hi I have a batch matching job running in salesforce. It was working and then I did something and now it has stopped functioning as I intended. I've tried debugging but for the life of me can't figure out what is going on. The goal of the script is to take one list of opportunities that match a certain criteria and another list of opportunities on the same account that match another criteria, map them, and then merge them.

It seems like it properly brings in the two lists of opportunities but when it comes time to match and merge it works on 1 mapped records in the entire list. So If there are 12 opps in each list, one of them will be merge the rest will remain untouched.
 
/**
       * called by AccountOpptyMatchingScheduler scheduler to perform opportunity matching and consolidation
       *
       * 1. get all the opportunities inserted by redshift today (with subscription id) and is not a matched opportunity (Matched_Opportunity__c = false)
       * 2. look for opportunities under the same account without subscription id (salesforce manually inserted)
       * 3. sf opportunity with the highest attempt__c and most recent createdDate and same Type and same recordtype will be the match
       * 4. if sf oppty with highest attempt__c is not the most recent or most recent does not have highest attempt, email nicholas
       * 5. otherwise, perform merge
       */
      global class OpportunityMatchingBatch implements Database.Batchable<sObject>, Database.Stateful {
          String query;
          List<Opportunity> matchedopptys;    // holds all the oppty matched today
          List<String> erroropptys;    // holds all the opptys that isn't highest attempt or most recent

          Date cdate;

          global OpportunityMatchingBatch() {
              this(null);
          }

          global OpportunityMatchingBatch(Date cdate) {
              this.cdate = cdate;
              String datestr = 'CreatedDate = LAST_N_DAYS:14';
              if (cdate != null) datestr = 'CreatedDate >= :cdate';

       /* This was what it was like before tried to slim down....
       * 
       * Date_of_Subscription_Start__c,Date_of_Subscription_End__c,'
                  + 'Total_Value__c,Virtual_MRR__c,Is_Last_Subscription__c,Active_Subscription_Number__c,'
                  + 'Number_of_Failed_Charges__c,Relative_Size__c,Is_Active_Subscription__c,Type,'
                  + 'Duration__c,Payment_Frequency__c,Ended_Early__c,Months_Paid_Up_Front__c,Total_Charges__c,'
                  + 'Last_Subscription_MRR__c,Partner_Slug__c,Pending_Charges__c,Pending_Charges_Dollar_Value__c,'
                  + 'Completed_Charges__c,Completed_Charges_Dollar_Value__c,Partner_Subscription_ID__c,RecordTypeId '
      */
              query = 'SELECT AccountId,Name,Type,RecordTypeId,Partner_Subscription_ID__c '
                  + 'FROM Opportunity WHERE ' + datestr + ' AND Partner_Subscription_ID__c <> null AND Matched_Opportunity__c = false';
              matchedopptys = new List<Opportunity>();
              erroropptys = new List<String>();
              System.debug(Logginglevel.INFO, 'Step 1');
          }

          global Database.QueryLocator start(Database.BatchableContext BC) {
              System.debug(Logginglevel.INFO, 'Step 2');
              return Database.getQueryLocator(query);
              
          }

          /**
           * record comes in one at a time
           */
          global void execute(Database.BatchableContext BC, List<Opportunity> opplist) {
              Opportunity redshifta = opplist[0];
              System.debug(Logginglevel.INFO, 'Step 3');

              // should only match with the highest attempt and most recently created sf opportunity
              for (Opportunity opp : [SELECT Name, CreatedDate, AccountId FROM Opportunity
                  WHERE AccountId = :redshifta.AccountId AND Partner_Subscription_ID__c = null ORDER BY CreatedDate DESC LIMIT 1]) {

                  // got here, it's okay to write soql in here because we are only matching with one opportunity, there won't be a loop
                  // get the highst attempt and most recent createddate from this account
                  //String highestattempt = [SELECT Attempt__c FROM Opportunity WHERE AccountId = :opp.AccountId AND Type = :redshifta.Type AND RecordTypeId // = :redshifta.RecordTypeId AND Partner_Subscription_ID__c = null ORDER BY Attempt__c DESC LIMIT 1].Attempt__c;
                  Datetime mostrecentdate = [SELECT CreatedDate FROM Opportunity WHERE AccountId = :opp.AccountId AND Type = :redshifta.Type AND Partner_Subscription_ID__c = null ORDER BY CreatedDate DESC LIMIT 1].CreatedDate;

                  // only merge if it is highest attempt and most recent
                  if (mostrecentdate == opp.CreatedDate) {
                      // create match result reason
                      String matchedon = null;

                      opp.Matching_Result__c = matchedon;

                      // merge fields
                      opp.Name = redshifta.Name;
                      /*opp.Date_of_Subscription_Start__c = redshifta.Date_of_Subscription_Start__c;
                      opp.Date_of_Subscription_End__c = redshifta.Date_of_Subscription_End__c;
                      opp.Total_Value__c = redshifta.Total_Value__c;
                      opp.Virtual_MRR__c = redshifta.Virtual_MRR__c;
                      opp.Is_Last_Subscription__c = redshifta.Is_Last_Subscription__c;
                      opp.Active_Subscription_Number__c = redshifta.Active_Subscription_Number__c;
                      opp.Number_of_Failed_Charges__c = redshifta.Number_of_Failed_Charges__c;
                      opp.Relative_Size__c = redshifta.Relative_Size__c;
                      opp.Is_Active_Subscription__c = redshifta.Is_Active_Subscription__c;
                      opp.Type = redshifta.Type;
                      opp.Duration__c = redshifta.Duration__c;
                      opp.Payment_Frequency__c = redshifta.Payment_Frequency__c;
                      opp.Ended_Early__c = redshifta.Ended_Early__c;
                      opp.Months_Paid_Up_Front__c = redshifta.Months_Paid_Up_Front__c;
                      opp.Total_Charges__c = redshifta.Total_Charges__c;
                      opp.Last_Subscription_MRR__c = redshifta.Last_Subscription_MRR__c;
                      opp.Partner_Slug__c = redshifta.Partner_Slug__c;
                      opp.Pending_Charges__c = redshifta.Pending_Charges__c;
                      opp.Pending_Charges_Dollar_Value__c = redshifta.Pending_Charges_Dollar_Value__c;
                      opp.Completed_Charges__c = redshifta.Completed_Charges__c;
                      opp.Completed_Charges_Dollar_Value__c = redshifta.Completed_Charges_Dollar_Value__c;*/
                      opp.Partner_Subscription_ID__c = redshifta.Partner_Subscription_ID__c;
                      opp.Matched_Opportunity__c = true;

                      update opp;
                      delete redshifta;
                      matchedopptys.add(opp);
                  } else {
                      // error
                      erroropptys.add('SF Name: ' + opp.Name + ', SF Id: ' + opp.Id + ', Partner Sub Name: ' + redshifta.Name + ', Redshift Id: ' + redshifta.Id
                          + ' SF CreatedDate: ' + opp.createdDate.format('MM/dd/YYYY HH:mm:ss')
                          + ' Most Recent CreatedDate: ' + mostrecentdate.format('MM/dd/YYYY HH:mm:ss'));
                  }

              }

          }

          /**
           * after batch is done, email nicholas two reports, one is the opportunity ids merged, one is the error report
           * @param  BC [description]
           * @return    [description]
           */
          global void finish(Database.BatchableContext BC) {
              List<Messaging.SingleEmailMessage> emailList = new List<Messaging.SingleEmailMessage>();

              // send matched opportunity email
              if (!matchedopptys.isEmpty()) {
                  Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
                  mail.setToAddresses(new String[] {'nicholas@artsy.net','matt@gsdcompany.com'});
                  mail.setSenderDisplayName('Artsy Matching Agent');
                  mail.setUseSignature(false);
                  mail.setSubject('Opportunity Matching Result');
                  String body = Datetime.now().format('MM/dd/YYYY') + ' opportunity match result:\n\n';
                  for (Opportunity opp : matchedopptys) {
                      body += 'Name: ' + opp.Name + ' | SF ID: ' + opp.Id + ' | Partner Subscription ID: ' + opp.Partner_Subscription_ID__c + '\n';
                  }
                  mail.setPlainTextBody(body);
                  emailList.add(mail);
              }

              // send error email
              if (!erroropptys.isEmpty()) {
                  Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
                  //'nicholas@artsy.net',
                  mail.setToAddresses(new String[] {'nicholas@artsy.net','matt@gsdcompany.com'});
                  mail.setSenderDisplayName('Artsy Matching Agent');
                  mail.setUseSignature(false);
                  mail.setSubject('Opportunity Match Errors (Not Highest Attempt or Not Most Recent');
                  String body = Datetime.now().format('MM/dd/YYYY') + ' Opportunity Match Errors (Not Highest Attempt or Not Most Recent):\n\n';
                  for (String e : erroropptys) {
                      body += e + '\n\n';
                  }
                  mail.setPlainTextBody(body);
                  emailList.add(mail);
              }

              Messaging.sendEmail(emailList);
          }
      }

 
Nicholas Sewitz 9Nicholas Sewitz 9
FYI the fix here was in the APEX Call I you need to ask for 1 record at a time. So it would look something like this: 
Database.executeBatch(new OpportunityMatchingBatch(), 1);