2

One of our classes in Production has begun to produce errors. Because of this we are unable to deploy new code into Production. I am assuming something was done directly inside Production to produce this error such as a new Validation Rule added or a field deleted etc. In the meantime, we would like to disable the class which is producing errors in order to deploy new code. The only thing I can think of at this point is to comment-out the entire class in a sandbox and deploy it that way. Does anybody have any advice as to what could be done with this ? .

public with sharing class Client_Status_History_Review {    
/* Our goals:

Once a Month we want to process all the Accounts with Client Status of Managed, and the ones that are 
Lightly Managed only every quarter.  They use standard Calendar Quarters, 1st = Jan - Mar, etc.

The Managed clients should be run on the 27th of the month.
The Lightly Managed clients on the 15th of the month, in the end-of-quarter Months only.

The way we do this is by scheduling this class to run every day and then check to see if it's the 15th or 27th+quarter
and only doing work for those days.

The two Account record types we care about are "Account - National (NAR)" and "Account - Multinational Master (MNMAR)"
The Client Status History record types we care about are "Client Status Managed" and "Client Status Lightly Managed"

When we create a new Client Status History record, we're going to create it as owned by the Owner of the Account.
However, if the Parent Account of that Account has a different Owner, we also want that Owner to have access
to this record, so we may have to create a new Share record as well.

*/

  // Helper class to hold a CSH and a Share pair.
  public class CSH_and_Share_Pair {
    Client_Status_History__c csh;
    Client_Status_History__Share share;
    //Account acc;
    Id accountParent;
    Id accountOwnerId;
    Id accountParentOwnerId;
    String accountParentName;
    String accountParentOwnerUsername;


    CSH_and_Share_Pair(Account a, Client_Status_History__c csh) {
      this.accountParentName = a.Parent.Name;
      this.accountParentOwnerUsername = a.Parent.Owner.Username;

      this.csh = csh;
                // the ownership of the Account and it's Parent Account (if present).  If they are different
      // we want to create a Share record to allow that owner access to this record.

      // So, if we have a non-null Parent Account which has a different Owner that the Account, 
      // create a share, otherwise not.
      if ((a.Parent != null) && (a.OwnerId != a.Parent.OwnerId)) {
        share = new Client_Status_History__Share(AccessLevel='Edit', UserOrGroupId= a.Parent.OwnerId);
      } else {
        share = null;
      }
    }

    private void reconcileParentId() {
      if (csh.id != null && share != null) share.ParentId = csh.id;
    }
    private String skipShare() {
      share = null;
      return 'Parent Account: ' + accountParentName + ' has an inactive owner.  Username: ' + accountParentOwnerUsername + '.';
    }
  }


  static Id getRecordTypeId(String sObjectType, String Name) {
    return [select id from recordtype where sobjecttype=:sObjectType and name=:Name limit 1].id;
  }

  // First we want to get the recordid's for the types of records we want to search for.
  static final Id A_N_Rectype = getRecordTypeId('Account','Account - National (NAR)');
  static final Id A_MM_Rectype = getRecordTypeId('Account','Account - Multinational Master (MNMAR)');

  // And the types we want to create
  static final Id CSM_Rectype = getRecordTypeId('Client_Status_History__c','Client Status Managed');
  static final Id CSLM_Rectype = getRecordTypeId('Client_Status_History__c','Client Status Lightly Managed');

  List<String> monthNames = new List<String>{
    'January',
    'February',
    'March',
    'April',
    'May',
    'June',
    'July',
    'August',
    'September',
    'October',
    'November',
    'December'};

  String monthName;
  String year;

  // Which quarter we are in.
  String quarterSelected = '';

  Date AM_Date_15;
  Date AM_Date_10;

  String emailMsg = '';
  Boolean error = false; // Is there an error of some sort?  If so, we'll highlight it on the Email Subject line.
  Boolean DEBUG = true;
  public void addMsg(String s) {system.debug(s); emailMsg = emailMsg + '\n' + DateTime.now() + ' - ' + s;}
  // If we are doing debugging... not Testing, but debugging, then change the If to true.  See below in the testing code to understand the distinction.
  public void addMsg(Boolean dbg, String s) {if (false) {system.debug(s); emailMsg = emailMsg + '\n' + DateTime.now() + ' - ' + s;}}

  Date tod;
  Boolean isTesting = false;

  List<CSH_and_Share_Pair> CSH_Data = new List<CSH_and_Share_Pair>();  
  Integer totalManagedCSHs = 0;
  Integer totalLightlyManagedCSHs = 0;
  Integer totalInactiveOwners = 0;
  Integer totalInactiveParentOwners = 0; 

  public Client_Status_History_Review() {}

  // Method to just specify Testing, and not provide a date.
  public String doReview(Boolean testing) {
    return doReview(testing, null);
  }

  public String doReview(Date pretendDate) {
    return doReview(false, pretendDate);
  }

  public String doReview(Boolean testing, Date pretendDate) {
    // We kick off all execution from the Constructor.

    // Are we Testing? 
    isTesting = testing;

    // What is todays date
    if (pretendDate == null) {
      tod = Date.Today();
    } else {
      tod = pretendDate;
    }
    // Now use the date to build strings for the new Name fields.
    monthName = monthNames[tod.month()-1];  // -1 as array starts at zero.
    year = '' + tod.Year();

    addMsg('We are running in the Date context of today being: ' + tod);  

    // First, are we doing any work today?
    // On the 15th of the month we are processing the Lightly Managed records only if it's the end of a quarter.
    // On the 27th of the month we are processing the Managed records for every month.
    Boolean runToday = False;

    if (isTesting) addMsg(DEBUG,'WE ARE IN TESTING MODE.');
    // Calculate the dates we might use for AM_Completion depending on record type.
    addMsg('Starting Client Status History Review procedure');
    AM_Date_10 = Date.newInstance(tod.addMonths(1).year(),tod.addMonths(1).month(),10);
    addMsg('The 10th of next month is: ' + AM_Date_10);
    AM_Date_15 = Date.newInstance(tod.addMonths(1).year(),tod.addMonths(1).month(),15);
    addMsg('The 15th of next month is: ' + AM_Date_15);

    // In case we need to roll back, we'll roll back everything.
    Savepoint sp = Database.setSavepoint();

    // Always do the Managed ones for this month.
    if (tod.day() == 27 || isTesting) {
      runToday = true;
      doManagedClients();
    }

    // Now figure out if we're doing a Quarterly review where we're doing both the 
    // regular Managed clients as well as the Lightly managed clients.
    if (tod.month() == 3) quarterSelected = '1st Quarter '; 
    if (tod.month() == 6) quarterSelected = '2nd Quarter '; 
    if (tod.month() == 9) quarterSelected = '3rd Quarter '; 
    if (tod.month() == 12) quarterSelected = '4th Quarter '; 
    if (quarterSelected != '') addMsg('We think we are running the ' + quarterSelected);    
    // is this quarterly?
    if ( 
        ( 
          tod.day() == 15 &&
          (tod.month() == 3 || tod.month() == 6 || tod.month() == 9 || tod.month() == 12)
        ) ||
        isTesting
      ) 
    {
      runToday = true;
      doLightlyManagedClients();
    }

    // If we didn't run today, just return.
    if (! runToday) return '';

    // If we have any errors, we want to roll back everything.
    if (error) {
      addMsg('Error, rolling back all database changes.');
      Database.rollback(sp);
      totalManagedCSHs = 0;
      totalLightlyManagedCSHs = 0;
      totalInactiveOwners = 0;
      totalInactiveParentOwners = 0;       
    }

    addMSg('');
    addMsg('Total Managed Accounts processed: ' + totalManagedCSHs);
    addMsg('Total Lightly Managed Accounts processed: ' + totalLightlyManagedCSHs);
    addMsg('Number of Accounts with inActive owners: ' + totalInactiveOwners);
    addMsg('Number of Accounts with Parent account with inActive owners: ' + totalInactiveParentOwners);

    sendEmailResults();

    return emailMsg;
  }

  public Boolean needFlush(Boolean forced) {
    // If we already have an error, return false as we did nothing.
    if (error) return false;
    // Return true if we did any inserts.  Used for testing.
    if (CSH_Data.isEmpty()) return false; // Nothing to do.

    if (CSH_Data.size() == 200 || forced) {      
      addMsg('Inserting ' + CSH_Data.size() + ' CSH/Share Pairs.');      
      doInsert();      
      CSH_Data.clear();
      return true;
    }
    return false;
  }
  public void doInsert() {    
    // Insert all the CSH's we have to insert.
    List<Client_Status_History__c> cshs = new List<Client_Status_History__c>();
    for (CSH_and_Share_Pair x: CSH_Data) cshs.add(x.csh);
    addMsg(DEBUG,'Trying to insert ' + cshs.size() + ' new Client Status History records.');
    try {
      insert cshs;
      addMsg(DEBUG,'Insert Successful.');
    } catch (Exception e) {
      addMsg('Exception in inserting Client Status History records:');
      addMsg(e.getTypeName());
      addMsg(e.getMessage());
      error = true;
    }
    // Reconcile the ParentId's for the new shares.
    List<Client_Status_History__Share> shares = new List<Client_Status_History__Share>();
    List<Id> checkUserIds = new List<Id>();
    for (CSH_and_Share_Pair x: CSH_Data) if (x.share != null) checkUserIds.add(x.share.UserOrGroupId);
    // Now we want to see if all the Id's are active users.
    List<User> inActiveUsers = new List<User>();
    for (User u: [select id from User where isActive=false and id in :checkUserIds]) inActiveUsers.add(u);
    if (!inActiveUsers.isEmpty()) {
      // Review them and knock out the bad ones in this set.
      for (User u: inactiveUsers) {
        // This is an inactive user, remove it's share.
        for (CSH_and_Share_Pair x: CSH_Data) if ((x.share != null) && (x.share.UserOrGroupId == u.id)) {
          addMsg(x.skipShare());
          totalInactiveParentOwners++; 
        }
      }
      inActiveUsers.clear();
    }
    // Now that the shares are all good, continue....
    for (CSH_and_Share_Pair x: CSH_Data) {
      if (x.share != null) {
        x.reconcileParentId();
        shares.add(x.share);
      }        
    }
    if (shares.isEmpty()) {
      addMsg('No Shares were created for this set of Client Status History records.');
    } else {
      addMsg(DEBUG,'Trying to insert ' + shares.size() + ' new Share permission records for some of the Client Status History records.');
      try {
        insert shares;
        addMsg(DEBUG,'Insert Successful.');
      } catch (System.DmlException e) {
        addMsg('DML Exception in inserting Sharing records for the Client Status History records:');
        addMsg(e.getTypeName());
        addMsg(e.getMessage());
        error = true;
        for (Integer i = 0; i<e.getNumDml(); i++) {
          addMsg(e.getDmlMessage(i));
          for (String s: e.getDmlFieldNames(i)) addMsg(s);
        }        
      } catch (Exception e) {
        addMsg('Exception in inserting Sharing records for the Client Status History records:');
        addMsg(e.getTypeName());
        addMsg(e.getMessage());
        error = true;
      }
    }    
  }

  public void sendEmailResults() {
    Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
    String[] toAddresses;  
    if (isTesting) {  
      toAddresses = new String[] {'sbower@stevebower.com'};
    } else { 
      toAddresses = new String[] {'Leslie.Vidal@bcdtravel.com', 'omnisystemadministrator@bcdtravel.com','bcdtravel@stevebower.com'};
    } 
    mail.setToAddresses(toAddresses);
      mail.setReplyTo('NoReply@bcdtravel.com');    
    mail.setSenderDisplayName('Create Client Status History scheduled Apex job');
    if (error) {
      mail.setSubject('ERROR in processing the Create Client Status History scheduled Apex job.');
    } else {
      mail.setSubject('Create Client Status History processing report.');  
    }    
    mail.setPlainTextBody(emailMsg);    
    Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });    
  }

  public String buildListOfFieldsForClone() {
    // Get the sObject describe result for the Opportunity object
    Schema.DescribeSObjectResult r = Client_Status_History__c.sObjectType.getDescribe();

    //Generate a Map of the fields
    Map<String, Schema.SObjectField> M = r.fields.getMap();

    //Now loop through the list of Field Names and concatenate the SOQL query string
    String SOQL = ''; 
    for (String fieldName : M.keySet()) if (M.get(fieldName).getDescribe().isCreateable()) SOQL += fieldName + ',';  

    //Remove the last , unnecessary comma
    SOQL = SOQL.substring(0,SOQL.length()-1);

    return SOQL;  
  }

  public void doLightlyManagedClients() {
    Integer startSize = CSH_Data.size();
    addMsg('Starting processing of Lightly Managed Clients.');

    // For Lightly Managed we want to get all the fields of the existing Client Status History objects so we can clone them.
    // This means building the query w/ Dynamic Apex... pain in the butt... ah well.

//    String SOQL = 'Select Id, OwnerId, Owner.UserName, Owner.isActive, Site, RecordTypeId, Name, Client_Mgmt__c,  ParentId, Parent.Name, Parent.OwnerId, Parent.Owner.userName, ' +
    String SOQL = 'Select Id, OwnerId, Owner.UserName, Owner.isActive, Site, RecordTypeId, Name, Client_Status_History__c,  ParentId, Parent.Name, Parent.OwnerId, Parent.Owner.userName, ' +
        '(select ' + buildListOfFieldsForClone() + ' ' +
        'from Company_Sensitive_History__r ' +
        'where RecordTypeId = \'' + CSLM_Rectype + '\' ' + 
        'order by CreatedDate DESC NULLS LAST LIMIT 1) ' +
      'From Account Where ' +
      '  ( recordtypeid = \''+ A_N_Rectype + '\' or recordtypeid = \'' + A_MM_Rectype + '\' ) and ' + 
      '  ( ' +
      '    (status_corporate_travel__c = \'Client\') or ' + 
      '    (status_corporate_travel__c = \'Partner Client\') ' + 
      '  ) and ' +
//      '  client_mgmt__c = \'Lightly Managed\' ' + 
      '  client_Status_History__c = \'Quarterly\' ' + 
      'order by createddate desc nulls first' ;

    addMsg(DEBUG,'Lightly Managed Query is: ' + SOQL + '\n');

    for (Account a: Database.query(SOQL)) {
      if (!a.Owner.isActive) {
        totalInactiveOwners++;
// added message for Lightly Managed accounts on 3/10/2011 
        addMsg('Lightly Managed Account: ' + a.Name + ', Site: ' + a.Site + ' has an inactive owner.  Username: ' + a.Owner.UserName + '.');
      } else {
        totalLightlyManagedCSHs++;
        addMsg(DEBUG,'Creating a Lightly Managed CSH for Account: ' + a);
        CSH_Data.add(new CSH_and_Share_Pair(a, createClientStatusHistory(a)));
        needflush(false);
        if (error) break;
      }
    }
    needflush(true);
    addMsg('End of Lightly Managed Clients.');
  }

  public void doManagedClients() {
    addMsg('Starting processing of Managed Clients.');
    Integer startSize = CSH_Data.size();
//    String SOQL = 'Select Id, OwnerId, Owner.UserName, Owner.isActive, Site, RecordTypeId, Name, Client_mgmt__c,  ParentId, Parent.Name, Parent.OwnerId, Parent.Owner.userName ' +
    String SOQL = 'Select Id, OwnerId, Owner.UserName, Owner.isActive, Site, RecordTypeId, Name, Client_Status_History__c,  ParentId, Parent.Name, Parent.OwnerId, Parent.Owner.userName ' +
      'From Account Where ' +
      '  ( recordtypeid = \''+ ((String)A_N_Rectype).substring(0,15) + '\' or recordtypeid = \'' + ((String)A_MM_Rectype).substring(0,15) + '\' ) and ' + 
      '  ( ' +
      '    (status_corporate_travel__c = \'Client\') or ' + 
      '    (status_corporate_travel__c = \'Partner Client\') ' + 
      '  ) and ' +
//      '  client_mgmt__c = \'Managed\' ' +
      '  client_status_history__c = \'Monthly\' ' +
      'order by createddate desc nulls first' ;

    addMsg(DEBUG,'Managed Query is: ' + SOQL + '\n');
    for (Account a: Database.Query(SOQL)) {
      if (!a.Owner.isActive) {
        totalInactiveOwners++;
// Bug - Don't report the Parent information, report the information on the actual account.
// Bug        addMsg('Account: ' + a.Parent.Name + ' has an inactive owner.  Username: ' + a.Parent.Owner.UserName + '.');
        addMsg('Account: ' + a.Name + ', Site: ' + a.Site + ', has an inactive owner.  Username: ' + a.Owner.UserName + '.');
      } else {      
        totalManagedCSHs++;
        addMsg(DEBUG,'Creating a Managed CSH for Account: ' + a);
        CSH_Data.add(new CSH_and_Share_Pair(a, createClientStatusHistory(a)));
        needflush(false);
        if (error) break;
      }
    }
    needflush(true);
    addMsg('End of Managed Clients.');    
  }

  public Client_Status_History__c createClientStatusHistory(Account acc) {
    // Create a new Client Status History record...
    Client_Status_History__c csh = new Client_Status_History__c();

//    if (acc.Client_Mgmt__c == 'Lightly Managed' && acc.Company_Sensitive_History__r != null && !acc.Company_Sensitive_History__r.isEmpty()) {
    if (acc.Client_Status_History__c == 'Quarterly' && acc.Company_Sensitive_History__r != null && !acc.Company_Sensitive_History__r.isEmpty()) {
      // There are some.  
      // We need to find the "right" Client Status History record to clone.
      // We want the most recently created record.  E.G. the one with the largest CreatedDate
      // Fortunately, we queried all the fields, and sorted Descending, Limiting to one.  So, 
      // if we got here, we already have it as the first and only retrieved record.
      csh = acc.Company_Sensitive_History__r[0].clone(false, true);  // Don't keep Id. Create a new record, not a reference.

      // Make sure CSH_Approved_Date__c is null and not carried over from the previous one.
      csh.CSH_Approved_Date__c = null;      
    }

    // Common code to both Managed and Lightly Managed Accounts to load the new (or cloned) CSH record 
    // with a proper Name, and the correct Contract Expiration Date.

    csh.Name = monthName + ', ' + year + ' Status: ' + acc.Name;
    csh.Account__c = acc.Id;
    csh.OwnerId = acc.OwnerId;  
    csh.API_CREATE__c = true;

//    if (acc.Client_Mgmt__c == 'Managed') {
    if (acc.Client_Status_History__c == 'Monthly') {
      csh.RecordTypeId = CSM_Rectype;
      // set the Completion date using the values computer at the beginning.
      if (acc.RecordTypeId == A_N_Rectype) {
        // Date should be the 10th of the following month.
        csh.AM_Completion_Due_Date__c = AM_Date_10;
      }
      if (acc.RecordTypeId == A_MM_Rectype) {
        // Date should be the 15th of the following month.
        csh.AM_Completion_Due_Date__c = AM_Date_15;
      }

    } else {
      csh.RecordTypeId = CSLM_Rectype;
      csh.Name = quarterSelected + csh.Name;  // Lightly Managed get the Quarter pre-pended  
      csh.AM_Completion_Due_Date__c = AM_Date_10;
    }
    // If the name is too long it causes an error instead of just truncating it.  
    // So, we truncate it to the max of 80
    if (csh.Name.length() > 80) csh.Name = csh.Name.substring(0,80);
    return csh;
  }

  public class testData {
    // Ok, we're going to need two National Account, and two Multinational Masters.
    // The first National and MNMAR are Lightly Managed
    // The second two are regular Managed.
    // The the second National will also have a "parent" account create by a different OwnerId.
    // The first Lightly Managed National will have some historical CSH records which could be cloned.  The second won't.
    User User1, User2;
    Account Acc1, Acc2, Acc3, Acc4;
    Account Parent1;
    Client_Status_History__c csh1, csh2, cshErr;
    String u;  // Unique number for this test run.

    testData() {

      // Create a unique string for this test so we're never confused with existing data.
      u = Datetime.now().millisecond().format();

      // Create the two users:
      Profile p = [select id from profile where name='Standard User']; 

      User1 = new User(LastName=u+'Test1', email='test1@stevebower.com', CompanyName='BCD Testing', Country='BCDISTAN', Department='Sales', Title='Tester 1', alias='test1',
      username='test1@stevebower.com', emailencodingkey='UTF-8', languagelocalekey='en_US', localesidkey='en_US', profileid = p.Id, timezonesidkey='America/Los_Angeles',
      Division='North America');
      insert User1;
      User2 = new User(LastName=u+'Test2', email='test2@stevebower.com', CompanyName='BCD Testing', Country='BCDISTAN', Department='Sales', Title='Tester 2', alias='test2',
      username='test2@stevebower.com', emailencodingkey='UTF-8', languagelocalekey='en_US', localesidkey='en_US', profileid = p.Id, timezonesidkey='America/Los_Angeles',
      Division='North America');
      insert User2;

//      Acc1 = new Account(Name=u+'Test1', recordtypeid=A_N_Rectype, status_corporate_travel__c = 'Client', client_mgmt__c = 'Lightly Managed', ownerid=User1.id, billingcountry='BCDISTAN');
      Acc1 = new Account(Name=u+'Test1', recordtypeid=A_N_Rectype, status_corporate_travel__c = 'Client', client_status_history__c = 'Quarterly', ownerid=User1.id, billingcountry='BCDISTAN');
      insert Acc1;      
//      Acc2 = new Account(Name=u+'Test2', recordtypeid=A_MM_Rectype, status_corporate_travel__c = 'Partner Client', client_mgmt__c = 'Lightly Managed', ownerid=User1.id, billingcountry='BCDISTAN');
      Acc2 = new Account(Name=u+'Test2', recordtypeid=A_MM_Rectype, status_corporate_travel__c = 'Partner Client', client_status_history__c = 'Quarterly', ownerid=User1.id, billingcountry='BCDISTAN');
      insert Acc2;      
      // Parent Account for the second National
      Parent1 = new Account(Name=u+'Parent1', recordtypeid=A_MM_Rectype, ownerid=User2.id, billingcountry='BCDISTAN', client_mgmt__c = 'Unmanaged');
      insert Parent1;
//      Acc3 = new Account(Name=u+'Test3', recordtypeid=A_N_Rectype, status_corporate_travel__c = 'Partner Client', client_mgmt__c = 'Managed', ownerid=User1.id, ParentId=Parent1.id, billingcountry='BCDISTAN');
      Acc3 = new Account(Name=u+'Test3', recordtypeid=A_N_Rectype, status_corporate_travel__c = 'Partner Client', client_status_history__c = 'Monthly', ownerid=User1.id, ParentId=Parent1.id, billingcountry='BCDISTAN');
      insert Acc3;      
//      Acc4 = new Account(Name=u+'Test4', recordtypeid=A_MM_Rectype, status_corporate_travel__c = 'Client', client_mgmt__c = 'Managed', ownerid=User1.id, billingcountry='BCDISTAN');
      Acc4 = new Account(Name=u+'Test4', recordtypeid=A_MM_Rectype, status_corporate_travel__c = 'Client', client_status_history__c = 'Monthly', ownerid=User1.id, billingcountry='BCDISTAN');
      insert Acc4;

      // Create some historical CSH records for the first National Lightly Managed account.
      // Create two with different Picklist values so that when we clone it, we can check and make sure we've cloned the right one.
      csh1 = new Client_Status_History__c(Name=u+'CSH1', Account__c = Acc1.id, recordtypeid=CSLM_Rectype, Satisfaction_with_CDS_Reporting__c='Unhappy');
      insert csh1;
      killtime(); // See below.
      csh2 = new Client_Status_History__c(Name=u+'CSH2', Account__c = Acc1.id, recordtypeid=CSLM_Rectype, Satisfaction_with_CDS_Reporting__c='Satisfied');
      insert csh2;
      killtime();
      // Note: Satisfied comes *after* unhappy, so that's the one we should be cloning.
    }
  }

  static testMethod void testReview() {
    // Reduce the number of message we get during debugging.
    system.debug(Logginglevel.DEBUG);

    // All we're really doing is setting up the data, calling the routine, and checking the results.
    testData x = new testData();

    Date tod = Date.Today();
    // Calculate the dates we might use for AM_Completion depending on record type.
    Date AM_Date_10 = Date.newInstance(tod.addMonths(1).year(),tod.addMonths(1).month(),10);
    Date AM_Date_15 = Date.newInstance(tod.addMonths(1).year(),tod.addMonths(1).month(),15);    

    // Each of the two regular Managed accounts, should have no csh records.
    system.assertEquals(0, [select count() from Client_Status_History__c where Account__c = :x.Acc3.id]);
    system.assertEquals(0, [select count() from Client_Status_History__c where Account__c = :x.Acc4.id]);    

    String unique = '%' + x.u + '%';
    Integer nCSHatStart = [select count() from Client_Status_History__c where Name like :unique and CreatedDate = Today];
    system.debug('Number of CSH\'s before the run is: ' + nCSHatStart);

    // Instantiate the class which runs the code.
    test.startTest();
    Client_Status_History_Review CSHR = new Client_Status_History_Review();
    //
    // Because the production system has so many validations and workflow rules set up on Client Status History records, we can't 
    // see the entire debug log if something goes wrong in order to see our error messages.  So, the only way we can get them is
    // to return them to the calling test function.  And, even then they won't show up in the debug log unless we can force it somehow.
    // One way to force it is to have an assertion fail with the text of the Debug log in the assertion.
    String xxx = CSHR.doReview(true);
    // The following fails and gives us our Debug log.
    // In real testing we don't want this line as we would never go on to the testing assertions below.
    //system.assertEquals('',xxx);
    test.stopTest();

    // check results.

    // Regardless of what, we should have created one for each of the four accounts.
    system.assertEquals(2,[select count() from Client_Status_History__c where Name like :unique and CreatedDate = Today]);

    // For each of the two regular Managed accounts, we should now have one new CSH record.
    system.assertEquals(1, 1);
    system.assertEquals(1, 1);
    // and Acc2 should just have a new one.
    Client_Status_History__c Acc2Csh = [select id, AM_Completion_Due_Date__c from Client_Status_History__c where Account__c = '0012000000XPijYAAT' Limit 1];
    // system.assertEquals(AM_Date_10, Acc2Csh.AM_Completion_Due_Date__c);
}

static String killtime() {
    // Try to kill a whole second or so that the CreatedDate value for records that are created one after another can really be used...
    // Otherwise, the search doesn't sort them properly and they are returned in random order.
    // This is only needed for testing because the normal operation will have things days apart instead of microseconds.
    String s = '';
    for (Integer ijk = 0; ijk < 20000; ijk++) s = s + ijk;
        return s;
    }
}

The class is too long for a past, so I only included it up to the line which is producing an error. Thank you for any kind of input you can give.

1
  • 1
    Can you post the error that you are getting in Production? – Doug B Aug 21 '13 at 14:42
4

OK, so if I read correctly the line which is failing is this?

system.assertEquals(2,[select count() from Client_Status_History__c where Name like :unique and CreatedDate = Today]);

We know that this assertion must have been true at the time that the class was deployed, or the deployment would not have succeeded.

Can you look at what API version this class is saved with? Because if it is API 23 or lower then it is seeing all your organisation data (equivalent to seealldata=true).

If this is the case, then you are relying on the Where clause to identify which is test data and which is not test data. How many client_status_history__c records were created in production today? Any of them could potentially be picked up as well as the test data and this will make your assertion false. The value :unique is a millisecond (so a value from 0 - 999) which if you are unlucky will not exclude the production data.

2
  • Thank you very much for the input Doug. The code is actually on a scheduled job and wasn't run today. During this debugging we have gotten rid of the scheduled job in order to prevent it from working. The class was saved in API version 19. Is there a way to just satisfy that line and continue ? or do you have a suggestion as to what could be done to just deactivate or successfully comment-out the class ? I managed to include the rest of the class here. If you feel like giving any further input I would be very grateful. If you don't feel like going through all of that I understand wholly. – Zoom_v Aug 21 '13 at 17:11
  • 1
    I would suggest trying saving it in a later version (between 24 and 27, not 28 as it doesn't allow tests outside test classes) in your sandbox. Then by default it will not see the production data. If this works then that's the quickest fix. If it doesn't work, typically because your tests rely on other production data, then best thing would be to fix the tests, move them to a test class and make them generate data. For a quicker possibility, change the value of u to something less likely to match production data (eg a longer random, or concatenate 'test' to it) – Doug B Aug 21 '13 at 21:57
3

No reason you can't. If you have any version control, you could move your changes into an feature branch and just delete the class before deploying.

3
  • 1
    And to ensure that if you comment out Class A, and it is referenced by a method in Class B, you need to comment out that reference and deploy that at the same time. Etc. – Doug B Aug 21 '13 at 14:31
  • @Bob Roberts - we don't have version control for the entire Prod env. We just ran a refresh on one of our Sandboxes two weeks ago - and that sandbox is currently testing correctly. I am beginning to consider refreshing the entire Production from that sandbox - but that has so many different remifications, won't it ? Would anybody recommend going that route ? – Zoom_v Aug 21 '13 at 15:21
  • 1
    I would not recommend that. – Mike Chale Aug 21 '13 at 19:54
3

As Bob suggested, you should be fine commenting out this logic.

However, there are a few things to consider:

  • The only thing you will want to verify is that with this code now gone, you still hit your code coverage amount of 75%.
  • You will also want to comment out your unit test for your trigger.
  • You also want to verify by commenting out a trigger that all of your other unit tests still pass (which they most likely shouldn't unless your trigger wasn't modifying any data).

Your absolute best case scenario would be to just fix the errors that are being thrown. I know it sucks, but rushing to get changes into production can cause major issues down the road. Also, this is a huge reminder that ALL changes NEED to be done in a sandbox. Changing a required field in production, adding fields, etc. have implications. If your unit test is failing for your trigger in production now, that means that the logic behind your trigger has been broken (or at least different than expected) this entire time. That is a major problem for any true enterprise situation.

2
  • Jesse - oh, you have no idea how many times I have said "DO NOT MAKE CHANGES DIRECTLY IN PRODUCTION !". That has been the most frustrating part of this whole thing ! Since then, I have even been scouring through the audit trail trying to see what could be producing the errors. – Zoom_v Aug 21 '13 at 14:55
  • and btw - the test code is written within the class itself. So, I was assuming we could account for it by just commenting out the entire class. – Zoom_v Aug 21 '13 at 15:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.