function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Kris WebsterKris Webster 

Schedule Apex class to query Assignments and add Assignment to Contact record.

I am trying to create an Apex class that will run every night and update a field on Contact based on specidic criteria.

Basically what I need to happen is for the system to query all assignemnts (pse__assignment__c) in the system with an end date (pse__End_Date__c) greater than TODAY (today__c) 

I then need to add that assignemnt to a custom field I have created on Contact if the assignment passes the criteria. If there are no assignments for the specific contact then I need the assignment field to be NULL. 

There is a lookup relationship between Assignemtn and Contact. In my code I think I am querying the assignments correctly but I am strugling with figuring out how to then associate that specific assignment with the correct contact. I am also struggling with how to make this Class a schedulable class that I am able to schedule to run every night. 

Here is my code so far. 

Any help would be GREATLY APPRECIATED   
 
public class AssignmentToContact implements Database.Batchable<sObject> {
        
   global Database.queryLocator start(Database.BatchableContext ctx )
   {
       
        list<pse__Assignment__c> aid=New list<pse__Assignment__c>(); 
    	list<Contact> updatecontact = new list<Contact>();
       
    	for(pse__Assignment__c ass: Trigger.new){ 
        if(ass.pse__End_Date__c >= today__c && ass.pse__Is_Billable__c = TRUE){
            aid.add(ass.id);
        } 

        list<Contact> contactlist=[SELECT Id,Assignment__c FROM Contact WHERE id = :aid]; 
        for(Contact con: contactlist){ 
            con.Assignment__c=ass.Id; 
            updatecontact.add(con);
        }
    }
    update updatecontact; 
	}
}

//IF THE list is empty then the field on CONTACT should be NULL

 
Karthikeyan Rajendran 14Karthikeyan Rajendran 14
Hi Kris Webster

    For a class to be schedulable it should implement the Schedulable Interface.

Since only part of the code is available I assume that you are aware of the Database.Batchable Interface.  The interface has three methods which needs to be implemented.
  • Start 
  • execute
  • finish
The main purpose of the start method is to fetch the records which are needed for the batch to process. All sorts of logic should be carried out in execute method.
gloabl class AssignmentToContact implements Schedulable, Database.Batchable<sObject> {

    //method to implement for Schedulable interface 
    global void execute(SchedulableContext sc){
        Database.executeBatch(this);
    }
        
    //Start method implementation for batchable interface
    global Database.queryLocator start(Database.BatchableContext bc){
       
        String query = 'SELECT Id, Contact__c FROM pse__Assignment__c WHERE pse__End_Date__c >= TODAY AND pse__Is_Billable__c = TRUE';
        return Database.getQueryLocator(query);
    }

    //execute method implementation for batchable interface
    global void execute (Database.BatchableContext bc, List<pse__Assignment__c> assignmentList){

        //all your business logic and update operation should be done here.
    }

    //finish method implementation for batchable interface
    global void finish (Database.BatchableContext bc){

    }
}

>> line no 14 : list<Contact> contactlist=[SELECT Id,Assignment__c FROM Contact WHERE id =:aid];
Always you wont get any values in contactlist as you are checking two different SF ID

To assign the assignments to a contact you need to have some fields or parameters to find the matching contact. Please be bit more clear about what you need. 

Regards
Karthik
Kris WebsterKris Webster
Thanks for the help so far! @Karthikeyan Rajendran 14. For line 14 I know I am off, so let me describe how I want the class to act and maybe you can help me. 

So once we have the list of Assignemnts that meet the criteria in line 11 I then want to insert each specific assignemnt to the associated contact record. So on the Assignment record the Contact_c field is a lookup field to the contact record, so I want the assignment associated with each specifric contact that passes the criteria to then be inserted in the Assignment__c field on the Contact record. 

So I guess for line 14 it needs to be something like 

list<Contact> contactlist=[SELECT Id,Assignment__c FROM Contact WHERE id =:aid];
list<Contact> contactlist=[SELECT Id,Assignment__c FROM Contact WHERE Id= :CONTACT__C ON THE ASSIGNMENT RECORD];

Is there a way to query the ID of the assignment as well as the ID of the contact (Contact__c) associacted with the Assignemnt? I figure once we have both those IDs we can then use the Contact__c ID to associate the assignments with the correct contacts. 

Thanks !
Karthikeyan Rajendran 14Karthikeyan Rajendran 14
Hi Kris Webster

      From your above comment I think this is what you wanted to achieve.
 
gloabl class AssignmentToContact implements Schedulable, Database.Batchable<sObject> {

    //method to implement for Schedulable interface 
    global void execute(SchedulableContext sc){
        Database.executeBatch(this);
    }
        
    //Start method implementation for batchable interface
    global Database.queryLocator start(Database.BatchableContext bc){
       
        String query = 'SELECT Id, Contact__c FROM pse__Assignment__c WHERE pse__End_Date__c >= TODAY AND pse__Is_Billable__c = TRUE';
        return Database.getQueryLocator(query);
    }

    //execute method implementation for batchable interface
    global void execute (Database.BatchableContext bc, List<pse__Assignment__c> assignmentList){

        Map<Id,Id> contactAssignmentMap = new Map<Id,Id>();
        List<Contact> updateContactList = new List<Contact>();

        //assignmentList will only have records those passed the criteria in line no 11
        for(pse__Assignment__c assgin : assignmentList){
            //preparing a map with contact ID and associated assginment
            contactAssignmentMap.put(assign.Contact__c,assign.Id);
        }


        //fetching the contact records based on the ID to update the assigment field.
        for(Contact con : [Select Id, Assignment__c from Contact where Id IN: contactAssignmentMap.keySet()]){
            if(contactAssignmentMap.containsKey(con.Id)){
                con.Assignment__c = contactAssignmentMap.get(con.Id);
            }
            updateContactList.add(con);
        }

        if(updateContactList != null && updateContactList.size() > 0)
            UPDATE updateContactList;
    }

    //finish method implementation for batchable interface
    global void finish (Database.BatchableContext bc){

    }
}

Regards
Karthik
 
Kris WebsterKris Webster
Thank you for that help!! So I have made a few changes to the code. I went ahead and only implemented the Batchable interface. 

I also changed the code in Line 31 to get(assign.Id) because that is what I hope to insert into the Assignment__c field on the contact. 

Here is my class thus far. 
 
global class AssignmentToContact implements Database.Batchable<sObject> {
    
    //Start method implementation for batchable interface
    global Database.queryLocator start(Database.BatchableContext bc){
       
        String query = 'SELECT Id, Contact__c FROM pse__Assignment__c WHERE pse__End_Date__c >= TODAY AND pse__Is_Billable__c = TRUE';
        return Database.getQueryLocator(query);
    }

    //execute method implementation for batchable interface
    global void execute (Database.BatchableContext bc, List<pse__Assignment__c> assignmentList){

        Map<Id,Id> contactAssignmentMap = new Map<Id,Id>();
        List<Contact> updateContactList = new List<Contact>();

        //assignmentList will only have records that passed the criteria in line no 11
        for(pse__Assignment__c assign : assignmentList){
            //preparing a map with contact ID and associated assginment
            contactAssignmentMap.put(assign.pse__Resource__c,assign.Id);
        


        //fetching the contact records based on the ID to update the assigment field.
        for(Contact con : [Select Id, Assignment__c from Contact where Id IN: contactAssignmentMap.keySet()])
            if(contactAssignmentMap.containsKey(con.Id)){
                con.Assignment__c = contactAssignmentMap.get(assign.Id);
            
            updateContactList.add(con);
        }

        if(updateContactList != null && updateContactList.size() > 0)
            UPDATE updateContactList;
    }

    //finish method implementation for batchable interface
    global void finish (Database.BatchableContext bc);

    }
}

Please let me know if that looks okay ?? 

I am now working on the TEST class and am trying to figure out how to get it working and how to get it to pass coverage.. 

I have a good start but still need a little direction. Here is what I have so far. 
 
@isTest
private class AssignmentToContactTest {
    static testmethod void testAssignmentToContactScheduledJob(){
        
        //create a contact record 
        //create an assignment where the contact is = to the contact we just created 
        //make sure the assignment__c field is NULL 
        //make sure the assignment end date is greater that today and is billable        
           
        disableTriggerHandlers();
                
        //create a contact record 
        TestDataFactory.initContacts();
        
        //create an assignment where the contact is = to the contact we just created 
        //make sure the assignment__c field is NULL 
        //make sure the assignment end date is greater that today and is billable 
        
        TestDataFactory.initAssignments();

        // Make the needed updates to the assignment (lines 19,20,21)
        	TestDataFactory.assignments[0].pse__Resource__c = TestDataFactory.contacts[0].Id;
        	TestDataFactory.assignments[0].pse__Is_Billable__c = TRUE;
      
    	}
    
     	// Schedule the test 
        
        String jobId = System.schedule('testBasicScheduledApex',
        AssignmentToContact.CRON_EXP, 
          new AssignmentToContact());
        
        // Get the information from the CronTrigger API object
        CronTrigger ct = [SELECT Id, CronExpression, TimesTriggered, NextFireTime
         				  FROM CronTrigger WHERE id = :jobId];
        
        // Verify the expressions are the same
        System.assertEquals(AssignmentToContact.CRON_EXP, ct.CronExpression);
        
        // Verify the job has not run
        System.assertEquals(0, ct.TimesTriggered);
        
        // Verify the next time the job will run
        System.assertEquals('2019-07-12 00:00:00', String.valueOf(ct.NextFireTime));
        System.assertNotEquals(false,
                               [SELECT Id FROM pse__Assignment__c WHERE id = :TestDataFactory.assignments[0].id]);
        
        
        List<Contact> ContactList = [SELECT id FROM Contact WHERE assignment__c = NULL];
        update ContactList;
        Test.stopTest();
    




	/************************************************************************************************************
    // Name         disableTriggerHandlers
    // Description  Disables non-critical custom triggers to reduce load on governor limits and increase 
    //              test performance
    *************************************************************************************************************/
    private static void disableTriggerHandlers()
    {
        TriggerHandlerBase.disableAllTriggerHandlers(Account.sObjectType);
        TriggerHandlerBase.disableAllTriggerHandlers(Contact.sObjectType);
        TriggerHandlerBase.disableAllTriggerHandlers(pse__Region__c.sObjectType);
        TriggerHandlerBase.disableAllTriggerHandlers(pse__Practice__c.sObjectType);
        TriggerHandlerBase.disableAllTriggerHandlers(pse__Grp__c.sObjectType);
        TriggerHandlerBase.disableAllTriggerHandlers(pse__Proj__c.sObjectType);
    }

}

I am first creating a contact record via my TestDataFactory, and then creating an Assignment where the pse__resource__c (contact lookup field on assignment) is equal to the contact that I created in the previous lines, and I made sure the Assignemnt is billable. 

I then start the test as you can see and that is where things get hard for me.. 

Any help would be greatly appreciated!! 

Thanks.