1

I have written a Schedule Class to insert records when a certain record's date is four days ago. It should insert a new record but of a different record type.

So for Example, if a package record type of 'Processed' has a date removal of 08-02-2018 with # of Orders to Remove > 1, then four days from that date we want a new package record type of 'Removal' to be created.

global class insertNewPackageRecords implements Database.Batchable<sObject>, Database.AllowsCallouts{


global Database.QueryLocator start(Database.BatchableContext BC) {
    Date today = Date.today();
    //if the date to remove was 4 days ago and package has orders to remove, insert new r'emoval' package 
    Date fourDays = today - 4;
    String query = 'SELECT ID,  Date_to_remove__c, Team__c, Team__r.id, recordTypeId, of_orders_to_remove__c FROM package__c WHERE date_to_remove__c =: fourDays AND of_orders_to_remove__c > 1';
    if(Test.isRunningTest()){
        query = 'SELECT ID, Date_to_remove__c, Team__c, Team__r.id, recordTypeId, of_orders_to_remove__c FROM package__c WHERE of_orders_to_remove__c > 1';
    }
    return Database.getQueryLocator(query);
    return null;
}

global void execute(Database.BatchableContext BC, List<Package__c> packages) {
    Date today = Date.today();
    Date fourDays = today - 4;
    List<package__c> removePackRecords = new List<Package__c>();
    for(Package__c package : packages){
        if(package.Date_to_remove__c != null && package.of_orders_to_remove__c > 1 && package.date_to_remove__c == fourDays){
            removePackRecords.add(new Package__c(Team__c = package.Team__r.id, RecordTypeId = OrgDataClass.returnPackage));
        }
        insert removePackRecords;
    }
}
global void finish(Database.BatchableContext BC){

   }
}

my schedule class:

global class ScheduleInsertNewPackageRecords implements Schedulable {
   global void execute(SchedulableContext sc) {
       insertNewPackageRecords auto = new insertNewPackageRecords();
       Database.executeBatch(auto);
   }
}

I've also set a scheduled job to run at like every day, at 7pm. However it does not look like the records are being created even though it hits the criteria of Date_to_remove__c being 4 days ago and having of_orders_to_remove__c >1

I also do have rules in place that does not allow more than one removal package to be linked to a Team. So if there is already another removal package attached to a team, this cannot create the removal package. I have checked the teams and none have removal packages so not sure why this is not being fired.

Is there anything that I'm not setting up? Or anything I should fix?

I've written a test class and its passing and inserting the records correctly in the test class as well.

Edit:

I also decided to just remove the 4 days and see if it works that way and it looked like it went off but then I received this error: INVALID_FIELD_FOR_INSERT_UPDATE, cannot specify Id in an insert call: [Id]

  • This error you get in test class or in main class? – Tushar Sharma Aug 3 '18 at 4:43
  • main class, saw it in debug log. changed it to upsert and it created the records but still can't figure out how to do it if date_removed is 4 days from today – J.koh Aug 3 '18 at 5:03
2

Well, you have multiple errors in the main class that may be the cause of failures. I assume that most probably an exception arose during the execution and therefore nothing got inserted. I suggest that you go to Setup > Apex Jobs and check if they failed or not.

In any case, here are the main problems which probably cause your code to fail:

  1. Since the insert statement is inside the for loop, you are inserting the same records over and over again. That is more than sure a DMLException on runtime.

  2. Furthermore, if you didn't get a DMLException for inserting records with Ids, which you probably did, you would get it one more time for issuing an insert statement more than 150 times in a single Apex Transaction (that is, each batch).

Now, some other code smells you have:

  1. There makes no point in having two return statements in the start. If the first return statement is success, the second will never be reached, and if by some change an Exception arises, it will not follow the normal flow since it will bubble up to the first catch you have.

  2. Even though Date.today() - 4 works, I suggest you use the standard method addDays. It's the API exposed by the Date class and people know what it does, but adding an integer to a class is awkward and even though today it works, nobody assures you it will still work in a few months.

  3. You have duplicated code: transform the fourDays local variable to a class variable so you can reuse it. Less code, less bugs.

And just as a sidenote, if you were to want to track failures of batches and do something with the errors, like some other retry logic, then instead of doing a plain insert you can always use the method Database.insert which returns some Database.SaveResult[]. You can then save those results in an instance variable, make the batch implement the Stateful interface (see the Using State in Batch Apex section) and apply your logic in the finish method.

Here is how I would code the class:

global class InsertNewPackageRecordsBatch implements Database.Batchable<sObject>, Database.AllowsCallouts {

    private static Date fourDaysAgo = Date.today().addDays(-4);
    private static final String realQuery = 'SELECT ID,  Date_to_remove__c, Team__c, Team__r.id, recordTypeId, of_orders_to_remove__c FROM package__c WHERE date_to_remove__c =: fourDaysAgo AND of_orders_to_remove__c > 1';
    private static final String testQuery = 'SELECT ID, Date_to_remove__c, Team__c, Team__r.id, recordTypeId, of_orders_to_remove__c FROM package__c WHERE of_orders_to_remove__c > 1';

    global Database.QueryLocator start(Database.BatchableContext BC) {
        String query = Test.isRunningTest() ? testQuery : realQuery;
        return Database.getQueryLocator(query);
    }

    global void execute(Database.BatchableContext BC, List<Package__c> packages) {
        List<Package__c> removePackRecords = new List<Package__c>();
        for(Package__c package : packages){
            if(package.Date_to_remove__c != null && package.of_orders_to_remove__c > 1 && package.date_to_remove__c == fourDaysAgo){
                removePackRecords.add(new Package__c(Team__c = package.Team__r.id, RecordTypeId = OrgDataClass.returnPackage)); // I assume that OrgDataClass is a class you haven't exposed
            }
        }

        insert removePackRecords;
    }

    global void finish(Database.BatchableContext BC){

    }
}
  • Thank you for your feedback. I've set this up and I got 100% on my test class but when it comes to actually inserting the records it doesn't look like its working if the date was 4 days ago. Does it matter that date_to_remove__c is a date/time field? If that field is 7/30/2018 9:38AM it should have triggered it since it was four days ago from Today? should it be addDays(4) and not addDays(-4) since its four days ago from today and not four days from today? – J.koh Aug 3 '18 at 16:15
  • @J.koh yeah, true, it should be addDays(-4) – Javier García Manzano Aug 4 '18 at 7:28
  • Doesn't seem to be creating the records even if its four days away. When i remove it, it works. – J.koh Aug 4 '18 at 12:18
  • @J.koh remove it then. That's up to you - your business logic :) – Javier García Manzano Aug 6 '18 at 6:58

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.