When setting up Resource Hero on a custom or standard object, you can add a Resource Hero trigger to that object. The trigger does the following:
- When a record is deleted, its related Resource Assignment and Resource Forecast records are deleted with it.
- When a record’s name changes, the Assigned To field on its Resource Assignments is updated to match. Assigned To is mainly used in reports and does not affect the RH Matrix.
- If your object has any of these checkbox fields, changes to them are copied to the record’s Resource Assignments:
RH_Make_Read_Only__c(sets Make Read Only),RH_Exclude_on_Resources_Tab__c(sets Archive), andRH_Exclude_from_Utilization_Coloring__c(sets Exclude From Utilization Coloring).
Create the trigger and its test class in a sandbox, then deploy them to production with a metadata deployment tool such as Salesforce CLI, Salesforce Extensions for Visual Studio Code, or change sets.
Before you begin
The examples on this page use a custom object named ProjectObject__c with a lookup field on Resource Assignment named ProjectField__c. Replace these names everywhere they appear in the trigger and the test class:
| In the examples | Replace with |
|---|---|
ProjectObject__c | The API name of your object |
ProjectField__c | The API name of your lookup field on the Resource Assignment object |
ProjectObjectTrigger | A name for your trigger |
ProjectObjectTrigger_Test | A name for your test class |
Step 1: Create the trigger
trigger ProjectObjectTrigger on ProjectObject__c (before insert, before update, before delete, after insert, after update, after delete, after undelete) {
ResourceHeroApp.RHA_TriggerDispatcher.entry(new ResourceHeroApp.RHA_TriggerDispatcher.TriggerParameters(Trigger.isBefore, Trigger.isAfter, Trigger.isInsert, Trigger.isUpdate, Trigger.isDelete, Trigger.isUndelete, Trigger.isExecuting, Trigger.old, Trigger.new, Trigger.oldMap, Trigger.newMap));
}
Step 2: Create the test class
Salesforce requires test coverage for a trigger before it can be deployed to production (Testing and Code Coverage). The test class below covers the trigger and checks two things: that renaming a record updates Assigned To on its Resource Assignments, and that deleting a record deletes its Resource Assignments.
If your object has required fields or validation rules, set valid values in createObjects (see the commented line). Do the same in createResources and createAssignments if your org requires additional fields on Resource or Resource Assignment.
Resource Hero names each RHA Object Translation record after the lookup field on Resource Assignment. The test checks for and creates a record named ProjectField__c, so use your lookup field’s API name in both places.
@isTest
private class ProjectObjectTrigger_Test {
// Tells Resource Hero which object your Resource Assignment lookup field points to.
// Object Translation records are named after the lookup field on Resource Assignment.
static void createObjectTranslation() {
if (ResourceHeroApp__RHA_Object_Translation__c.getInstance('ProjectField__c') == null) {
ResourceHeroApp__RHA_Object_Translation__c translation = new ResourceHeroApp__RHA_Object_Translation__c();
translation.Name = 'ProjectField__c';
translation.ResourceHeroApp__Object_Name__c = 'ProjectObject__c';
translation.ResourceHeroApp__Name_Field__c = 'Name';
insert translation;
}
}
static List<ProjectObject__c> createObjects(String baseName, Integer numRecords) {
List<ProjectObject__c> results = new List<ProjectObject__c>();
for (Integer i = 0; i < numRecords; i++) {
ProjectObject__c o = (ProjectObject__c) ProjectObject__c.sObjectType.newSObject(null, true);
o.Name = baseName + String.valueOf(i);
// o.Other_Required_Field__c = 'A valid value';
results.add(o);
}
return results;
}
static List<ResourceHeroApp__Resource__c> createResources(String baseName, Integer numRecords) {
List<ResourceHeroApp__Resource__c> results = new List<ResourceHeroApp__Resource__c>();
for (Integer i = 0; i < numRecords; i++) {
ResourceHeroApp__Resource__c res = (ResourceHeroApp__Resource__c) ResourceHeroApp__Resource__c.sObjectType.newSObject(null, true);
res.Name = baseName + String.valueOf(i);
res.ResourceHeroApp__Weekly_Target_Min_Hours__c = 35;
res.ResourceHeroApp__Weekly_Target_Max_Hours__c = 45;
results.add(res);
}
return results;
}
static List<ResourceHeroApp__Resource_Assignment__c> createAssignments(List<ProjectObject__c> objects, List<ResourceHeroApp__Resource__c> resources) {
List<ResourceHeroApp__Resource_Assignment__c> results = new List<ResourceHeroApp__Resource_Assignment__c>();
for (ProjectObject__c o : objects) {
for (ResourceHeroApp__Resource__c r : resources) {
ResourceHeroApp__Resource_Assignment__c ra = (ResourceHeroApp__Resource_Assignment__c) ResourceHeroApp__Resource_Assignment__c.sObjectType.newSObject(null, true);
ra.ProjectField__c = o.Id;
ra.ResourceHeroApp__Resource__c = r.Id;
ra.ResourceHeroApp__Rate__c = 10;
results.add(ra);
}
}
return results;
}
@isTest
static void updateSyncsAssignedTo() {
createObjectTranslation();
List<ProjectObject__c> objects = createObjects('Object Update Test', 1);
insert objects;
insert createAssignments(objects, insertResources());
List<ResourceHeroApp__Resource_Assignment__c> assignments = [SELECT Id, ResourceHeroApp__Assigned_To__c FROM ResourceHeroApp__Resource_Assignment__c WHERE ProjectField__c IN :objects];
Assert.areEqual(3, assignments.size());
Assert.isTrue(assignments[0].ResourceHeroApp__Assigned_To__c.startsWith('Object Update Test'));
objects[0].Name = 'Updated Name';
update objects;
assignments = [SELECT Id, ResourceHeroApp__Assigned_To__c FROM ResourceHeroApp__Resource_Assignment__c WHERE ProjectField__c IN :objects];
Assert.areEqual('Updated Name', assignments[0].ResourceHeroApp__Assigned_To__c);
}
@isTest
static void deleteRemovesAssignments() {
createObjectTranslation();
List<ProjectObject__c> objects = createObjects('Object Delete Test', 1);
insert objects;
insert createAssignments(objects, insertResources());
delete objects;
Assert.areEqual(0, [SELECT COUNT() FROM ResourceHeroApp__Resource_Assignment__c]);
}
static List<ResourceHeroApp__Resource__c> insertResources() {
List<ResourceHeroApp__Resource__c> resources = createResources('Test Resource', 3);
insert resources;
return resources;
}
}
Step 3: Deploy to production
Run the test class in your sandbox and confirm it passes, then deploy the trigger and the test class to production together.