1

I am writing batch class to deactivate users if no dependency and freeze user if any dependency.When i hardcode singal value of userid and run batch it works fine it deactivates user.It is not going to catch block which is fine.But when i run it dynamically same record is going to catch block which it should not.Donot know why it is behaving as such.Is it due to collection declaration not proper.

Batch class:

global class Lighting_DeactivateUserBatch implements Database.Batchable<sObject>{

    global final MO_Inactive_Users__c dateParam = MO_Inactive_Users__c.getValues('Date Param');
    global final Date newUserNotificationDate = System.Today() - (Integer)dateParam.Inactivation_days_to_Notify_New_User__c;
    global final Date newUserDeactivationDate = System.Today() - (Integer)dateParam.Inactivation_Days_to_Deactivate_New_User__c;
    global final Date oldUserNotificationDate = System.Today() - (Integer)dateParam.Inactivation_Days_to_Notify__c;
    global final Date oldUserDeactivationDate = System.Today() - (Integer)dateParam.Inactivation_Days_to_Deactivate__c;
    global final String Query;
    // global final List<User> listUserDeactivate = new List<User>();
     global final List<String> setUserMailID = new List<String>();
    global final Set<ID> setRecordOwner = new set<ID>();
    //global final Set<ID> setPutToLog = new set<ID>();
    //global final Map<ID,String> mapLog = new Map<ID,String>();
    //global final List<User_Deactivation_log__c> listLog= new List<User_Deactivation_log__c>();
     global final List<UserLogin> frozenUserList;// = new List<UserLogin>();
     global final List<ID> frozenIDList = new List<ID>();
     Set<Id> ids = new Set<Id>();
    global List<UserLogin> freezeUserList;
    global List<UserLogin> freezeForDeactivateUserList;
    global Set<ID> userIDSet;
    global Set<ID> freezeUserID;
    global List<UserLogin> freezeExceptionUserList;
    Integer j = 0;


    global Lighting_DeactivateUserBatch () {
        frozenUserList = [Select UserID from UserLogin where isFrozen=true];
        for(userlogin frozen : frozenUserList) {
            frozenIDList.add(frozen.UserID);
        }
        AggregateResult[] ownerListOpp = [SELECT OwnerID Owner FROM Opportunity group by OwnerID];
        AggregateResult[] ownerListAcc = [SELECT OwnerID Owner FROM Account group by OwnerID];
        AggregateResult[] ownerListCon = [SELECT OwnerID Owner FROM Contact group by OwnerID];
        AggregateResult[] ownerListLead = [SELECT OwnerID Owner FROM Lead group by OwnerID];
        ownerListOpp.addall(ownerListACC);
        ownerListOpp.addall(ownerListCon);
        ownerListOpp.addall(ownerListLead);
        for(AggregateResult agg : ownerListOpp) {
            setRecordOwner.add((ID) agg.get('Owner'));
        }
    }


    global List<User> start(Database.BatchableContext BC){

        //user u = new user(ID='0058E000000dPr9QAE');
        //List<user> queryForEmailAlert = [Select ID, Email, LastLoginDate ,createddate,Reactivation_Date__c,isportalenabled,TEST_CREATED_DATE__C,TEST_LOGIN_DATE__C from User where Functional_System_User__c=false and isActive =true and ID=:U.Id AND ID not in :frozenIDList ORDER BY TEST_LOGIN_DATE__C ASC NULLS LAST];
        List<user> queryForEmailAlert = [Select ID, Email, LastLoginDate ,createddate,Reactivation_Date__c,isportalenabled,TEST_CREATED_DATE__C,TEST_LOGIN_DATE__C from User where Functional_System_User__c=false and isActive =true AND ID not in :frozenIDList ORDER BY TEST_LOGIN_DATE__C ASC NULLS LAST];
        System.debug('users applicable for batch deactivation ::::: '+queryForEmailAlert);
        return (queryForEmailAlert);
        //return (u);
    }

    global void execute(Database.BatchableContext BC, List<sObject> scope){
        Set<ID> userOwnerSet = new Set<ID>();
        List<User> listAllUser = (List<User>) scope;
        List<User> listUserDeactivate = new List<User>();
        List<ID> sendEmailList = new List<ID>();
        List<ID> sendEmailPartnerList = new List<ID>();
        List<ID> sendEmailDeactivationList = new List<ID>();
        system.debug('this is the size'+listAllUser.size());
        system.debug('this is test newUserNotificationDate :::'+newUserNotificationDate);
        system.debug('this is test newUserDeactivationDate:::'+newUserDeactivationDate);
        system.debug('this is test oldUserNotificationDate:::'+oldUserNotificationDate);
        system.debug('this is test oldUserDeactivationDate:::'+oldUserDeactivationDate);
        date testcreatedDate;
        date testlastLoginDate;
        for(User loopVar : listAllUser){
        if(loopVar.TEST_CREATED_DATE__C!=null){
            testcreatedDate = date.newInstance(loopVar.TEST_CREATED_DATE__C.year(),loopVar.TEST_CREATED_DATE__C.month(),loopVar.TEST_CREATED_DATE__C.day());
            system.debug('this is test create date'+testcreatedDate);
        }
            if(loopVar.TEST_LOGIN_DATE__C != null ){
            testlastLoginDate = date.newInstance(loopVar.TEST_LOGIN_DATE__C.year(),loopVar.TEST_LOGIN_DATE__C.month(),loopVar.TEST_LOGIN_DATE__C.day());
            system.debug('this is test login date'+testlastLoginDate);
            if(testlastLoginDate  == oldUserNotificationDate && (loopVar.Reactivation_Date__c<System.Today()- ((Integer)dateParam.Grace_Period__c) || loopVar.Reactivation_Date__c==null )) {

            system.debug('this is 2nd loop portalenabled is false'+oldUserNotificationDate);
                system.debug('this is the size1'+listAllUser.size()+loopVar.Email);
                if(loopVar.isportalenabled==false){
                sendEmailList.add(loopVar.ID);
                }else{
                sendEmailPartnerList.add(loopVar.ID);
                }
            system.debug('this is 2nd loop portalenabled is false mailed');

            }else if(loopVar.TEST_LOGIN_DATE__C <= oldUserDeactivationDate && (loopVar.Reactivation_Date__c<System.Today()- ((Integer)dateParam.Grace_Period__c) || loopVar.Reactivation_Date__c==null)) {
                boolean bijtest=(loopVar.TEST_LOGIN_DATE__C <= oldUserDeactivationDate && (loopVar.Reactivation_Date__c<System.Today()- ((Integer)dateParam.Grace_Period__c) || loopVar.Reactivation_Date__c==null));
                System.debug('value of bijtest ::::'+bijtest+ 'and for userid :::: '+loopVar.ID);
                system.debug('this is 4rth loop freeze user after adding to freeze set');
                if(setRecordOwner.contains(loopVar.ID)) {
                    freezeUserID.add(loopVar.ID);
                    system.debug('this is 4rth loop freeze user after adding to freeze set inside if loop');
                }
                listUserDeactivate.add(new user(id=loopVar.ID, isActive=false,Deactivate_Reason__c='Deactivated due to inactivity'));
                sendEmailDeactivationList.add(loopVar.ID);
                system.debug('this is 4rth loop freeze user after adding to freeze set deactivation mail sent');

            }   
            }else if(loopVar.TEST_LOGIN_DATE__C == null && loopVar.TEST_CREATED_DATE__C <= newUserDeactivationDate && (loopVar.Reactivation_Date__c<System.Today()- ((Integer)dateParam.Grace_Period__c) || loopVar.Reactivation_Date__c==null)) {
                if(setRecordOwner.contains(loopVar.ID)) {
                system.debug('this is 3rd loop freeze user');
                    freezeUserID.add(loopVar.ID);
                    system.debug('this is 3rd loop freeze user after adding to freeze set');
                }
                listUserDeactivate.add(new user(id=loopVar.ID, isActive=false,Deactivate_Reason__c='Deactivated due to inactivity'));
                sendEmailDeactivationList.add(loopVar.ID);

            }else if(testcreatedDate == newUserNotificationDate && loopVar.TEST_LOGIN_DATE__C == null && (loopVar.Reactivation_Date__c<System.Today()- ((Integer)dateParam.Grace_Period__c) || loopVar.Reactivation_Date__c==null) ) {

                system.debug('this is 1st loop portalenabled is false'+newUserNotificationDate);
                system.debug('this is the size1'+listAllUser.size()+loopVar.Email);
                if(loopVar.isportalenabled==false){
                sendEmailList.add(loopVar.ID);
                system.debug('this is 1st loop portalenabled is false mailed');
                }else{
                sendEmailPartnerList.add(loopVar.ID);
                }

            } 

        }
        System.debug('value of freezeUserID test  ::::: '+freezeUserID);
        if(freezeUserID!= null){
            System.debug('entered error block :::::');
            freezeUserList = [SELECT Id,IsFrozen,UserId FROM UserLogin where UserId = :freezeUserID];
            for (UserLogin freezeuser : freezeUserList) {
                freezeuser.IsFrozen = true;
            }
            update freezeUserList;
            System.debug('entered error block :::::');
        }
        System.debug('value of users for deactivation before database update :::::'+listUserDeactivate);

        try{
            System.debug(':::::::inside try block ::::::::'+listUserDeactivate);

            List<Database.SaveResult> results= Database.update(listUserDeactivate);
            System.debug(':::::::inside try block 2 ::::::::'+results);
        }catch(exception e) {
            System.debug('value of ids of deactivation users ::::: '+listUserDeactivate);
            Set<Id> resultIds = (new Map<Id,User>(listUserDeactivate)).keySet();
            System.debug('value of ids of exception users ::::: '+resultIds);
            freezeForDeactivateUserList = [SELECT Id,IsFrozen,UserId FROM UserLogin where UserId in :resultIds];
            for (UserLogin freezeuser : freezeForDeactivateUserList) {
                freezeuser.IsFrozen = true;
                System.debug('inside catch with freeze ::::: '+freezeuser);  
            }
            update freezeForDeactivateUserList;
            System.debug('finish catch block ::::: '+freezeForDeactivateUserList);

            //System.debug('error details in catch block::::: '+e.getTypeName() + ' - ' + e.getCause() + ': ' + e.getMessage());
        }
        if(freezeForDeactivateUserList!=null && !freezeForDeactivateUserList.isEmpty()){
        for(userlogin ul : freezeForDeactivateUserList){
                ids.add(ul.userId);
            }
        }

        if(sendEmailDeactivationList!=null && ids!=null){        
            while(j<sendEmailDeactivationList.size()){
                if(ids.contains(sendEmailDeactivationList[j])){
                    sendEmailDeactivationList.remove(j);
                    system.debug('value of deactivation after removing freezed user 1 ::::: '+sendEmailDeactivationList);
                }else{
                    j++;
                }

            }
        }
        system.debug('value of deactivation after removing freezed user 2 ::::: '+sendEmailDeactivationList);
        System.debug('value of sendEmailList ::::: '+sendEmailList);
        System.debug('value of sendEmailPartnerList ::::: '+sendEmailPartnerList);
        System.debug('value of sendEmailDeactivationList ::::: '+sendEmailDeactivationList);
        /* Email Block */

        if(sendEmailList!=null && !sendEmailList.isEmpty()){
            Messaging.MassEmailMessage email1 = new Messaging.MassEmailMessage(); 
            email1.setSaveAsActivity(false);
            email1.setTargetObjectIds(sendEmailList);
            email1.setTemplateId('00X8E000000QP68');
            Messaging.SendEmail(New Messaging.MassEmailMessage[] {email1});
        } 

            if(sendEmailPartnerList!=null && !sendEmailPartnerList.isEmpty()){
            Messaging.MassEmailMessage email2 = new Messaging.MassEmailMessage(); 
            email2.setSaveAsActivity(false);
            email2.setTargetObjectIds(sendEmailPartnerList);
            email2.setTemplateId('00X8E000000QQki');
            Messaging.SendEmail(New Messaging.MassEmailMessage[] {email2});
            }

            if(sendEmailDeactivationList!=null && !sendEmailDeactivationList.isEmpty()){
            Messaging.MassEmailMessage email3 = new Messaging.MassEmailMessage(); 
            email3.setSaveAsActivity(false);
            email3.setTargetObjectIds(sendEmailDeactivationList);
            email3.setTemplateId('00X8E000000QP8Y');
            Messaging.SendEmail(New Messaging.MassEmailMessage[] {email3});
            }

        System.debug('value of sendEmailList after mail ::::: '+sendEmailList);
        System.debug('value of sendEmailPartnerList after mail ::::: '+sendEmailPartnerList);
        System.debug('value of sendEmailDeactivationList after mail ::::: '+sendEmailDeactivationList);
    }

    global void finish(Database.BatchableContext BC){
        //if(listlog != null)
        // insert listlog;
    }
}
  • This is working fine if similar user records are processed.But if mixed records are there then records with no exception goes to catch block with records having exception.How to avoid such situation. – Jack vardy Jan 24 '16 at 14:09
0

Let's consider your use of Database.Update You have:

try{
    List<Database.SaveResult> results= Database.update(listUserDeactivate);
}
catch(exception e) {...}

With this form of Database.update, where you let optional argument allOrNothing default (to true), partial successes are not allowed and any error will throw an exception causing no updates to occur.

So, if your batch scope has some records in error and some not, the catch block will get invoked.

To illustrate this, consider the following anonymous Apex where one of the two Accounts has no name.

Account[] aList = new List<Account> {new Account(), new Account (name='01Foo')};

try {
    Database.SaveResult[] srList = Database.insert(aList);
    system.debug(LOGGINGLEVEL.INFO,'no exception thrown when allOrNothing defaulted');
    }
catch (Exception e) {system.debug(LOGGINGLEVEL.INFO,'exception thrown when allOrNothing defaulted');}

RESULT: USER_DEBUG [7]|INFO|exception thrown when allOrNothing defaulted

try {
    Database.SaveResult[] srList = Database.insert(aList,true);
    system.debug(LOGGINGLEVEL.INFO,'no exception thrown when allOrNothing=true');
    }
catch (Exception e) {system.debug(LOGGINGLEVEL.INFO,'exception thrown when allOrNothing=true');}

RESULT: USER_DEBUG [14]|INFO|exception thrown when allOrNothing=true

aList = new List<Account> {new Account(), new Account (name='01Foo')};

try {
    Database.SaveResult[] srList = Database.insert(aList,false);
    system.debug(LOGGINGLEVEL.INFO,'no exception thrown when allOrNothing=false');
    }
catch (Exception e) {system.debug(LOGGINGLEVEL.INFO,'exception thrown when allOrNothing=false');}

RESULT: USER_DEBUG [20]|INFO|no exception thrown when allOrNothing=false

Finally, and I echo @crmprogdev, this batch class execute() method is far too confusing to follow and should be rethought using helper methods

This is pseudo code but you want to get to something like this:

 private class EmailAction {
    ID[] sendEmailList = new List<ID> ();
    ID[] sendEmailPartnerList = new List<ID> ();
    ID[] sendEmailDeactivationList = new List<ID> ();
 }
 global void execute(Database.BatchableContext BC, List<sObject> scope){
      EmailAction ea = new EmailAction();
      for (User u: (List<User>) scope) {
          ea = handleUseCase1(u);
          ea = handleUseCase2(u);

          ...

          sendMassEmail(template1,ea.sendEmailList);
          sendMassEmail(template2,ea.sendPartnerEmailList);
          sendMassEmail(template3,ea.sendDeactivationEmailList);
     }

}   

where your handlers deal with the messy conditional logic and return a list of ID with arity 0 or 1.

I find that if my execute() method spans more than one page, it becomes really hard to follow, hide the complexity in the helper methods which can be debugged individually and asserted individually via testmethods.

  • Batch class is working fine with batch size =1 but it does not work when batch size is more.Is this a practical scenario for batch class. – Jack vardy Jan 25 '16 at 7:34
  • i don't see why not – cropredy Jan 25 '16 at 21:10
0

Right off the bat, I see issues with your initial declarations below:

global final Date newUserNotificationDate = System.Today() - (Integer)dateParam.Inactivation_days_to_Notify_New_User__c;
global final Date newUserDeactivationDate = System.Today() - (Integer)dateParam.Inactivation_Days_to_Deactivate_New_User__c;
global final Date oldUserNotificationDate = System.Today() - (Integer)dateParam.Inactivation_Days_to_Notify__c;
global final Date oldUserDeactivationDate = System.Today() - (Integer)dateParam.Inactivation_Days_to_Deactivate__c;

These should all look like:

global final Date newUserNotificationDate = System.Today().addDays(-(Integer)dateParam.Inactivation_days_to_Notify_New_User__c);

See the Date Class Methods for more on this.

While not part of your problem, there's no need to go through the following:

    frozenUserList = [Select UserID from UserLogin where isFrozen=true];
    for(userlogin frozen : frozenUserList) {
        frozenIDList.add(frozen.UserID);
    }

Instead, you can just do this:

    <Id,User>frozenUserListMp = new map<Id,User>([Select UserID from UserLogin where isFrozen=true]);
    frozenIDList=frozenUserListMp.keyset();

I see where some of your conditional statements in the global void execute(Database.BatchableContext BC, List<sObject> scope) method have the same issue as your initial declarations which may be what's causing at least some of your problems. For example:

      if(testlastLoginDate  == oldUserNotificationDate && (loopVar.Reactivation_Date__c<System.Today()- ((Integer)dateParam.Grace_Period__c) || loopVar.Reactivation_Date__c==null )) {

In the above, the && (loopVar.Reactivation_Date__c<System.Today()- ((Integer)dateParam.Grace_Period__c) should be using the Date.addDays() method.

  • I have one question regarding batch class and batch size.I would much appreciate if you can solve my queries.Suppose i have set batch size as 100 and i am running batch class.There is try catch block to handle exception.So if in one of batch list, one record has exception.So whether this record will go to catch block or whole that particular batch list of 100 will go to catch block. – Jack vardy Jan 25 '16 at 12:43
  • As @crop1645 pointed out, if you're using all or none, anything that goes to your catch block will cause your batch to fail. If you allow partial success, it would depend on whether your try-catch was located prior to or after any critical points that might stop all records from being processed. If not a "choke point", and you allow partial success, your batch would continue to process. Just keep a record of those that fail and use an error handler class to report them. – crmprogdev Jan 25 '16 at 13:50
  • -if we use all or none parameter,is it possible to get id of failed records?.I tried with all or none parameter but i could not get id.May b i dont have full knowledge.If i can get id of failed records that would solve my problem easily.Currently my code is working fine with batch size=1. – Jack vardy Jan 26 '16 at 3:21
  • Yes, you can get the Ids of failed records rather easily. Use the database save results class to collect your results, including your errors. With all or none, it's not as simple. But with DML Options enabled it is. – crmprogdev Jan 26 '16 at 3:33

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.