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
Tim AndrewsTim Andrews 

Need Help with Another Apex Trigger

Goal: Populate the fields Parent_Project__c and Child Project__c on the custom object Work_Products__c after a new Work_Products__c record is created. 

Background:
A Process Builder process creates a new Project__c record, with a record type of “Parent Project” when a TaskRay__Project__c parent project record is inserted.

Then,

A Process Builder process creates a new Project__c record, with a record type of “Child Project” when a TaskRay__Project__c child project record is inserted.

Then,

A Process Builder process creates a new Work_Products__c record, with a record type of “Deliverable” when a TaskRay__Project_Task__c project task record is inserted. This is where I run into trouble.
  • The Process Builder process is able to create the new Work_Products__c project deliverable record and populate all fields except for the Child_Project__c and Parent_Project__c fields, because the Child_Project__c and Parent_Project__c fields do not look-up to TaskRay__Project_Task__c.
  • There are lookup fields on Work_Products__c called Get_Child_Project__c and Get_Parent_Project__c, which look-up to TaskRay__Project_Task__c and link these fields with the associated TaskRay__Project__c (child project level) and TaskRay__Project_Parent__c records via traversing relationships through TaskRay__Project_Task__c up to TaskRay__Project__c and then up to TaskRay_-project_Parent__c.
  • As mentioned above, there are lookup fields on Work_Products__c called Child_Project__c and Parent_Project__c; these lookups look back up to the Project__c object, filtered for the “Child Project” and “Parent Project” record types.
    • These lookups capture the associated Child Project and Parent Project records on Project__c.
    • They look back up to Project__c because of roll-up relationships that are required for reporting.
I need a trigger that populates the Child_Project__c and Parent_Project__c lookup fields on Work_Products__c with the Child Project and Parent Project record-type records on Project__c that share their names with the associated Get_Child_Project__c and Get_Parent_Project__c record names.

Schema showing the Work_Products__c, Project__c,  TaskRay__Project_Task__c and TaskRay__Project__c objects and their relationships are attached; my attempt at the required code is below.
Schema
trigger UpdateProjects on Work_Products__c (before insert) {
    Map<String, RecordType> rtMap = new Map<String, RecordType>();

    // Build a map of record type name to record types
    for (RecordType rt : [
        select Name
        from RecordType
        where Name = 'Deliverable' or
            Name = 'Expense'
  ]) {
        rtMap.put(rt.Name, rt);
    } 

    Set<Id> parentProjectIds = new Set<Id>();
 
    // Iterate over all the work products
    for (Work_Products__c theProduct : Trigger.New) {
        // If we are dealing with a "deliverable" then put it's taskray parent Id in the set
        if (theProduct.RecordTypeId == rtMap.get('Deliverable').Id) {
            parentProjectIds.add(theProduct.Get_Parent_Project__c);  
        }
    }

    // Remove any null ids
    parentProjectIds.remove(null);
    
    Set<Id> childProjectIds = new Set<Id>();
 
    // Iterate over all the work products again
    for (Work_Products__c theProduct1 : Trigger.New) {
        // If we are dealing with a "deliverable" then put it's taskray child Id in the set
        if (theProduct1.RecordTypeId == rtMap.get('Deliverable').Id) {
            childProjectIds.add(theProduct1.Get_Child_Project__c);  
        }
    }

    // Remove any null ids
    childProjectIds.remove(null);
    
    // If we have taskray parent projects, deal with them
    if (!parentProjectIds.isEmpty()) {
        Map<Id, String> taskrayParentMap = new Map<Id, String>();
        
        // Get all the taskray parent projects we need to deal with and map their Ids to their names
        for (Taskray__Project__c trpp : [
            select Name
            from Taskray__Project__c
            where Id in :parentProjectIds
        ]) {
            taskrayParentMap.put(trpp.Id, trpp.Name);
        }
        
          // If we have taskray child projects, deal with them
    if (!childProjectIds.isEmpty()) {
        Map<Id, String> taskrayChildMap = new Map<Id, String>();
        
        // Get all the taskray child projects we need to deal with and map their Ids to their names
        for (Taskray__Project__c trpc : [
            select Name
            from Taskray__Project__c
            where Id in :childProjectIds
        ]) {
            taskrayChildMap.put(trpc.Id, trpc.Name);
        }  

        Map <String, Work_Products__c> workProductMap = new Map <String, Work_Products__c>();
        // Build a map of work product parent project names to projects for any work products that match our taskray project name and are "deliverables".
        for (Work_Products__c WorkProducts : [
            select Get_Parent_Project__r.Name
            from Work_Products__c
            where Name in :taskrayParentMap.values() and
                RecordTypeId = :rtMap.get('Deliverable').Id
        ]) {
            workProductMap.put(workProducts.Name, workProducts);
        } 
        
        Map <String, Work_Products__c> workProductMap1 = new Map <String, Work_Products__c>();
        // Build a map of work product child project names to projects for any work products that match our taskray project name and are "deliverables".
        for (Work_Products__c WorkProducts1 : [
            select Get_Child_Project__r.Name
            from Work_Products__c
            where Name in :taskraychildMap.values() and
                RecordTypeId = :rtMap.get('Deliverable').Id
        ]) {
            workProductMap1.put(workProducts1.Name, workProducts1);
        } 

        // Iterate through all the work products and do the mapping
        for (Work_Products__c theProducts : trigger.New) {
            // Bail out if we're not dealing with a "deliverable"
            if (theProducts.RecordTypeId != rtMap.get('Deliverable').Id) {
                continue;
            }

            // Bail out if we can't find the task ray parent project for that id
            if (!taskrayParentMap.containsKey(theProducts.Get_Parent_Project__c)) {
                continue;
            }
            
            // Bail out if we can't find the task ray child projects for that id
            if (!taskrayChildMap.containsKey(theProducts.Get_Child_Project__c)) {
                continue;
            }

            // Bail out if we cant find a parent project for the task ray project name
            if (!workProductMap.containsKey(taskrayParentMap.get(theProducts.Get_Parent_Project__c))) {
                continue;
            }
            
            // Bail out if we cant find a child project for the task ray project name
            if (!workProductMap1.containsKey(taskrayChildMap.get(theProducts.Get_Child_Project__c))) {
                continue;
            }

            // Set the parent project lookup
            theProducts.Parent_Projects__c = workProductMap.get(taskrayParentMap.get(theProducts.Get_Parent_Project__c)).Id;

	        // Set the child project lookup
	        theProducts.Child_Project__c = workProductMap1.get(taskrayChildMap.get(theProducts.Get_Child_Project__c)).Id;
         }
      }
   }
}

Any help would be greatly appreciated!
Best Answer chosen by Tim Andrews
Abhishek BansalAbhishek Bansal
Hi Tim,

I read you problem many times and finally understoos what you want to achieve here and have modified your trigger code accordingly.
Please find the updated trigger below :
trigger UpdateProjects on Work_Products__c (before insert) {
    Id deliverableRecordTypeId = Schema.SObjectType.Work_Products__c.getRecordTypeInfosByName().get('Deliverable').getRecordTypeId();
    
    Set<Id> taskRayIds = new Set<Id>();
    
    // Iterate over all the work products
    for (Work_Products__c theProduct : Trigger.New) {
        // If we are dealing with a "deliverable" then put it's taskray parent Id in the set
        if (theProduct.RecordTypeId == deliverableRecordTypeId) {
        	if(theProduct.Get_Parent_Project__c != null){
            	taskRayIds.add(theProduct.Get_Parent_Project__c);
        	}
        	if(theProduct.Get_Child_Project__c != null){
            	taskRayIds.add(theProduct1.Get_Child_Project__c);
        	}
        }
    }
    
    Map<Id,Taskray__Project__c> mapOfTaskRay = new Map<Id,Taskray__Project__c>([Select Name from Taskray__Project__c where id IN :taskRayIds]);
    Set<String> setOfNames = new Set<String>();
    for(Taskray__Project__c taskRay : mapOfTaskRay.values()){
    	setOfNames.add(taskRay.Name);
    }
   	
   	Map<String,Project__c> mapOfProject = new Map<String,Project__c>();
   	for(Project__c proj : [Select Id from Project__c where Name IN :setOfNames]){
   		mapOfProject.put(proj.Name, proj);
   	}
    
    for(Work_Products__c workProduct : trigger.new){
    	if(workProduct.RecordTypeId == deliverableRecordTypeId){
	    	if(mapOfTaskRay.containsKey(workProduct.Get_Parent_Project__c)){
	    		if(mapOfProject.containsKey(mapOfTaskRay.get(workProduct.Get_Parent_Project__c).Name)){
	    			workProduct.Parent_Projects__c = mapOfProject.get(mapOfTaskRay.get(workProduct.Get_Parent_Project__c).Name).id;
	    		}
	    	}
	    	
	    	if(mapOfTaskRay.containsKey(workProduct.Get_Child_Project__c)){
	    		if(mapOfProject.containsKey(mapOfTaskRay.get(workProduct.Get_Child_Project__c).Name)){
	    			workProduct.Child_Project__c = mapOfProject.get(mapOfTaskRay.get(workProduct.Get_Child_Project__c).Name).id;
	    		}
	    	}
    	}
    }
}
Please try with the above trigger code and let me know if you have any issue with it.

Thanks,
Abhishek Bansal

All Answers

Abhishek BansalAbhishek Bansal
Hi Tim,

I read you problem many times and finally understoos what you want to achieve here and have modified your trigger code accordingly.
Please find the updated trigger below :
trigger UpdateProjects on Work_Products__c (before insert) {
    Id deliverableRecordTypeId = Schema.SObjectType.Work_Products__c.getRecordTypeInfosByName().get('Deliverable').getRecordTypeId();
    
    Set<Id> taskRayIds = new Set<Id>();
    
    // Iterate over all the work products
    for (Work_Products__c theProduct : Trigger.New) {
        // If we are dealing with a "deliverable" then put it's taskray parent Id in the set
        if (theProduct.RecordTypeId == deliverableRecordTypeId) {
        	if(theProduct.Get_Parent_Project__c != null){
            	taskRayIds.add(theProduct.Get_Parent_Project__c);
        	}
        	if(theProduct.Get_Child_Project__c != null){
            	taskRayIds.add(theProduct1.Get_Child_Project__c);
        	}
        }
    }
    
    Map<Id,Taskray__Project__c> mapOfTaskRay = new Map<Id,Taskray__Project__c>([Select Name from Taskray__Project__c where id IN :taskRayIds]);
    Set<String> setOfNames = new Set<String>();
    for(Taskray__Project__c taskRay : mapOfTaskRay.values()){
    	setOfNames.add(taskRay.Name);
    }
   	
   	Map<String,Project__c> mapOfProject = new Map<String,Project__c>();
   	for(Project__c proj : [Select Id from Project__c where Name IN :setOfNames]){
   		mapOfProject.put(proj.Name, proj);
   	}
    
    for(Work_Products__c workProduct : trigger.new){
    	if(workProduct.RecordTypeId == deliverableRecordTypeId){
	    	if(mapOfTaskRay.containsKey(workProduct.Get_Parent_Project__c)){
	    		if(mapOfProject.containsKey(mapOfTaskRay.get(workProduct.Get_Parent_Project__c).Name)){
	    			workProduct.Parent_Projects__c = mapOfProject.get(mapOfTaskRay.get(workProduct.Get_Parent_Project__c).Name).id;
	    		}
	    	}
	    	
	    	if(mapOfTaskRay.containsKey(workProduct.Get_Child_Project__c)){
	    		if(mapOfProject.containsKey(mapOfTaskRay.get(workProduct.Get_Child_Project__c).Name)){
	    			workProduct.Child_Project__c = mapOfProject.get(mapOfTaskRay.get(workProduct.Get_Child_Project__c).Name).id;
	    		}
	    	}
    	}
    }
}
Please try with the above trigger code and let me know if you have any issue with it.

Thanks,
Abhishek Bansal
This was selected as the best answer
Tim AndrewsTim Andrews
Abhishek, when I attempt to enter a new TaskRay Task record, (which kicks off the Process Builder flow that creates the Work_Product__c record that this trigger acts on), the insert fails. In looking at the Execute Anonymous log, this is the fatal error that is causing the exception and the process builer flow to fail to insert the TaskRay Task record: 

08:41:35:380 FATAL_ERROR System.SObjectException: SObject row was retrieved via SOQL without querying the requested field: Project__c.Name
Tim AndrewsTim Andrews
I was able to resolve this by adding the Name field to the SOQL query on Line 26 and now the record inserts and the trigger fires with the expected result. Thank you very much for your help!