Friday, July 1, 2016

How to run apex job in certain intervals

Hello Everyone,

We have two limitation on salesforce 

  1. We can schedule job once daily/weekly/monthly from UI.
  2. We can only have 100 scheduled job in our org.
Recently I was working to run a job on leads in every four hours with different group of inputs. Every company has his own new fields and criteria. So I ended up with hitting the 100 job limit. My each lead group will need 6 job to run every day and then I ended up with only supporting 16 lead group based on the company.

So I thought of passing an interval to the scheduler which some of us already know but it seems it wont delete the job it has only next run as blank









So you will be hitting the 100 limit . See here





















So I thought to remove the jobs where next-run is empty 

global class TestSchedulable implements Schedulable{
  Integer intervalMinutes;
  String jobName
  global TestSchedulable (String jName,Integer intervalMin){
        intervalMinutes = intervalMin;
        jobName = jName;
  }
  global void execute(SchedulableContext SC) {
        DateTime now  = DateTime.now();
        DateTime nextRunTime = now.addMinutes(intervalMinutes);
        String cronString = '' + nextRunTime.second() + ' ' +                                                                                        nextRunTime.minute() + ' ' + 
                                     nextRunTime.hour() + ' ' + nextRunTime.day() + ' ' + 
                                     nextRunTime.month() + ' ? ' + nextRunTime.year(); 
        System.schedule(jobName + '-' + now.format(), cronString, new                                                                    TestSchedulable (jobName ,intervalMinutes));
         TestSchedulable.removePreviousJob();
  }
     @future     global static void removePreviousJob(){  
          // you may pass the job name and get the id for that job only as well. Below query will remove any job
         // where next-run is null          for(CronTrigger job : [SELECT Id, NextFireTime FROM CronTrigger WHERE NextFireTime = NULL ]){               System.abortJob(job.Id);           }      }
}


Monday, June 20, 2016

Synchronous Deletion more than 10000 rows.


Salesforce documentation says
  • "Total number of records processed as a result of DML statementsApproval.process, or database.emptyRecycleBin is 10,000"
  • "Total number of DML statements issued 150" 

It does not mean you can delete/insert/update/upsert a row maximum of 9999 in a group up to 150 times. It is cumulative that mean the number of records gets added as a single list. Which means as soon you start your DML process or enter inside your apex function your session starts and then with each DML it keeps adding rows to check the count is less than 10,000 or not. 

For Example:

public void delTest(){
  List<Account> a; // say of size 9000
  List<Account__c> a1; // Say list of 2000;
  // Now if we fire 
  delete a;
  delete a1;
  // We are going to receive too many DML rows as the total count of DML rows is 
  // 9000 + 2000 = 11,000. 
}  

To overcome this issue we have two possible solution 

  1. To implement a batch/future/remote [asynchronous] process to delete 
  2. Or we need to check the total size of the DML rows and go back to the VF page and initiate same function until all desired record got deleted.
Now we can chain batch-process from the finish method and can pass "this" parameter to the same batch process and can make it stateful so that it can remember the records on which DML was performed. 
But if we want synchronous operation actionFunction is the only operation which can help up to do more DML.
<apex:page>
   <apex:form>
     <apex:actionFunction name="delTest" action="{!delTest}" rerender="scriptPanel"                                                onComplete="CheckAllDone();" />
     <apex:actionFunction name="allDone" action="{!allDone}" rerender="page" />
   </apex:form>
   <apex:outputPanel id="scriptPanel" >
     <script>
        function CheckAllDone(){
           var hasMore = {!hasMore} ;
           if(hasMore){
                delTest();
           }
           else{
              allDone();
           }
        }  
     </script>
   <apex:outputPanel/>
</apex:page>

public Boolean allDone {get;set;} {allDone false; }
public Boolean hasMore {get;set;} {hasMore =false; }
public void delTest(){
  allDone  = false;
  hasMore = false;
  List<Account> a; // SOQL to get records
  List<Account__c> a1; // SOQL to get records
  List<sObject> objList = new List<sObject>();
  for(Account temp : a){
   if(objList.size() >= 9999){
     hasMore = true;
     break;
   }
      objList.add(temp);
  }
  for(Account__c temp : a1){
   if(objList.size() >= 9999){
     hasMore = true;
     break;
   }
      objList.add(temp);
  }
  // do check for sizes here if size > 10,000 limit the DML and prepare batches
  delete objList;
  if(hasMore == false){
   allDone = true;
  }
}  
public void allDone(){
 // do nothing;
}

//====================== batch Class ===========================

global class BatchDelete implements Database.Batchable<sObject>,Database.Stateful{
  // to remember List Of dml rows.
   global Set<Id> sobjList = new Set<Id>();
   global Boolean allDone = false;  
   // constructor 
   global BatchDelete (){
   }
   global Database.QueryLocator start(Database.BatchableContext batchContext) { 
     // do sqol to get data 
     String query = 'Your SOQL here';
     return Database.getQueryLocator(query); 
   }
   global void execute(Database.BatchableContext BC, list<sObject> forDML){
      // add here and manipulate your dmlList.
      // add to the set sobjList 
      // set allDone to true if all dml completed
   }
   global void finish(Database.BatchableContext BC){
      // chain the batch job
      if(!allDone){
          Database.executeBatch(this); 
      }
      else{
         // fire a trigger or email to start another process.
      } 
   }
}

Wednesday, March 16, 2016

Generate strong random password in Apex

Hi Coders,

I thought this might be handy so thought of sharing this .

It will create a random password with specified length  

public String generateRandomString(Integer len){
        String randStr = '';
        final String upperChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
        final String numChars = '0123456789';
        final String LowerChars = 'abcdefghijklmnopqrstuvwxyz';
        final String specialChars = '!#$%-_=+<>';
        while (randStr.length() < len) {
           Integer idx = Math.mod(Math.abs(Crypto.getRandomInteger()), upperChars.length());
           randStr += upperChars.substring(idx, idx+1);
           idx = Math.mod(Math.abs(Crypto.getRandomInteger()), numChars.length());
           randStr += numChars.substring(idx, idx+1);
           idx = Math.mod(Math.abs(Crypto.getRandomInteger()), LowerChars.length());
           randStr += LowerChars.substring(idx, idx+1);
           idx = Math.mod(Math.abs(Crypto.getRandomInteger()), specialChars.length());
           randStr += specialChars.substring(idx, idx+1);
        }
        return randStr; 
    }

Happy Coding !!!!!!!!!!!!!!!

Thursday, March 10, 2016

How to terminate AsyncApexJob [EXCEPTION: System.StringException: You can't abort scheduled apex ]

I thought this might be useful to developers. 

EXCEPTION: System.StringException: You can't abort scheduled apex : This exception may happen for 2 reasons
  1. It is not a CronTrigger ID (parent Id) for the Async Apex job 
  2. You are trying to delete a orphan Async Apex job, that is no parent Id.


Second situation is rare but it can happen. In that case you might face problem while deploying  the batch class again. The deployment will fail as the batch is already executing (queued )  for a long time.

If this happens you need to call saleesforce support to clear this. BUT before you call salesforce support please tray this which might help .

  1.  login to "https://workbench.developerforce.com/login.php
  2. On right corner, it will be showing your name and API version. Click on that link
  3. There you will find change API version, change it to 32
  4. Go to Utilities >> Apex Execute
  5. There run this command System.abortjob() with job Id

Please note the API version is very important we can't do this from developer console because we can't change the API version there, alternatively you may choose the version during login as well while logging -in to workbench. I believe this would help you. Please let me know if you have any other questions, I would love to help you in that as well.

Have a Great Day! 


Monday, February 22, 2016

Get Salesforce record ID from lookup field in Visualforce page

Hello Friends,

Recently I was working on a requirement where I have to validate some lookup field on visualforce page itself not in apex without submitting form to server. 

I did lots of research and after having a lots of dig down of salesforce source code I identified each lookup field is associated with one hidden field with a "_lkid" appended to the id this help me to identify the record uniquely 

<apex:page standardController="Contact">
   <apex:form>
      <apex:inputField value="{!Contact.AccountId}" id="accountId onchange="getAccountSFId();">
          <script>
                  var accountId = "{!$Component.accountId}";
          </script>
   </apex:form>
   <script>
           function getAccountSFId(){
              var accId =  accountId + '_lkid';  
              // This will give you the 18 digit SF id of the account and you can do what ever you want with this 
              console.log(document.getElementById(accId ).value);
           }
   </script> 
</apex:page>

Tips on passing Salesforce AI Associate Certification

  🌟 Motivation to Pursue the Salesforce AI Associate Certification 🌟 The world of technology is in a state of perpetual evolution, and on...