+ Start a Discussion
CodeHeartsSFDCCodeHeartsSFDC 

Error: Update parent status based on child statuses

Hi Guys,

The functionality i'm trying to perform here is to update the parent object status to complete when all related child object records have a status complete. But i'm receiving the error "Error: Compile Error: Variable does not exist: id at line 11"

I'm pasting the code below. Can you please help me out here. Thanks!!

trigger updateParentStatus on Child_Object__c (after insert, after update) {

    Set<Id> parentIds = new Set<Id>();
    Integer count = 0;

        for(Child_Object__c wd: Trigger.new) {
            parentIds.add(wd.Parent_Object__c);
        }
    
    List<Parent_Object__c> parentList = [Select id, Status__c from Parent_Object__c where id =: parentIds];
    List<Child_Object__c> childList = [Select id, Status__c from Parent_Object__c Where parentIds =: parentList.id];
    
    for(Child_Object__c wd: Trigger.new) {
        if(wd.Status__c != 'Completed') {
            count++;
        }
    }
    
    for(Parent_Object__c wo: parentList) {
        wo.Status__c = 'Completed';    
    }
    
    if(count==0) {
        update parentList;
    }
}
 
Best Answer chosen by CodeHeartsSFDC
@anilbathula@@anilbathula@
Hi CodeHeartsSFDC,

Then Add the child query and modify the trigger.new to childList.
List<Child_Object__c> childList = [Select id, Status__c from Child_Object__c Where Parent_Object__c IN: parentIds];
    
    for(Child_Object__c wd: childList ) {
        if(wd.Status__c != 'Completed') {
            count++;
        }
    }

Thanks
Anil.B

All Answers

@anilbathula@@anilbathula@
Hi CodeHeartsSFDC,

Its the problem with your queries
Remove the child object query as your are not using it and change the parent Query to this
List<Parent_Object__c> parentList = [Select id, Status__c from Parent_Object__c where id IN:parentIds];
Thanks
Anil.B


 
CodeHeartsSFDCCodeHeartsSFDC
@Anil

I had tried the query you had mentioned above previously, but the issue is, that does not check if all the related child records has a status of completed. It updates the parent if any of the child record status is set to complete.

I want the parent status to be set to complete only if all the related child records statuses are set to complete

 
@anilbathula@@anilbathula@
Hi CodeHeartsSFDC,

Then Add the child query and modify the trigger.new to childList.
List<Child_Object__c> childList = [Select id, Status__c from Child_Object__c Where Parent_Object__c IN: parentIds];
    
    for(Child_Object__c wd: childList ) {
        if(wd.Status__c != 'Completed') {
            count++;
        }
    }

Thanks
Anil.B
This was selected as the best answer
Andrew GAndrew G
If we look at the original code
List<Parent_Object__c> parentList = [Select id, Status__c from Parent_Object__c where id =: parentIds];
this returns a List of Objects.  Your next query which has Where parentIds =: parentList.id won't work as the List doesn't really have an id.  To access the id of a record in a list you would need something like WHERE parentId = :parentList[0].Id  
But that would only be the first element in the list. 

Note, we are also querying a list, so we would use IN and the the equals .. a single Id would equal a list of Ids

Next, we note that we are bulkifying the trigger.  Nice.  But why do we bulkify?  So that we can do a dataload and have the trigger work correctly.  Under the current structure, what would happen if I loaded a file with records for two parent records?  All the children would be determined against a single parent or all the parents would have the same status regardless of what their children were actually set to.

So, what to do:
We could do a Map .... Map<Id, List<childrecord>>  where the ID is the Id of the parent record, and it has a list of children records.  We could then loop through the Map and test the list and update the record if required.

So some code
    Set<Id> parentIds = new Set<Id>();
	Integer count = 0;
//a list of parent Ids for the records being inserted/updated
	for(Child child : Trigger.new) {
		parentIds.add(child.parentId);
	}
//a list of the parent records for the inserted/updated records
	List<Parent> parentList 	= new List<Parent>([SELECT Id, Status__c FROM Parent WHERE Id IN :parentIds]);

//a list of all child records where a "sibling" record is being inserted / updated
	List<Child> childList = new List<Child>([SELECT Id, Status__c FROM Child_Object__c WHERE ParentId IN :parentIds]);
//now lets build a Map
	Map<Id,List<Child_Object__c >> mapChildrenbyParent = new Map<Id,List<Child_Object__c >>();
	if (childList.size() > 0) {
		for (Child_Object__c child : childList ) {
			if ( mapChildrenbyParent.containsKey (child.ParentId)) {
				List<Child_Object__c > childs = mapChildrenbyParent.get (child.ParentId);
				childs.add(child);
				mapChildrenbyParent.put (child.ParentId, Childs);
			} else {
				mapChildrenbyParent.put (child.ParentId, new List<Child_Object__c > {child});
			}
		}
	}

//now we can loop the Map or we can loop the parent Ids to access the Map or loop the parent record list (which may be easieest)...and test
	List<Parent> toUpdate = new List<Parent>();
	for(Parent parent: parentList)	 {
		count = 0;
		List<Child> childChilds = mapChildsbyParent.get(parent.Id);
		for(Child cs : childChilds ) {
			if(cs.Status__c != 'Completed') {
				count++;
			}
		}
		if(count == childChilds.size() ){
			parent.Status = 'Completed';
			toUpdate.add(parent);
		}
	}

	if(toUpdate.size() > ==0) {
		update toUpdate;
	}

Note, we could also use the Aggregate function in SOQL as we are simply after a number:
AggregateResult[] closedChildren = [SELECT ParentId, Count(Id) cId FROM Child_Object__c WHERE ParentId IN :parent AND Status = 'completed' GROUP BY ParentId];
And do a size compare to the Aggregate count of all children records
AggregateResult[] allChildren = [SELECT ParentId, Count(Id) cId FROM Child_Object__c WHERE ParentId IN :parent GROUP BY ParentId];
Note, that becomes tricky because you can end up with parentIds in one Aggregate result and not the other. (i.e. they are all Completed or none are completed) So your logic needs to be very precise.

Or you could get funky and do a combo on AggregateResult with Map<Id,List<child>> and loop the Aggregate and use the .get method to pull and compare to the Map.


Just some ideas to consider.'

Regards
Andrew

 
Andrew GAndrew G
Edits to the above
Note, we are also querying a list, so we would use IN and the the equals .. a single Id would equal a list of Ids
should read
Note, we are also querying a list, so we would use IN and not the equals .. a single Id would equal a list of Ids

and in the code
if(toUpdate.size() > ==0) {
should read
if(toUpdate.size() > 0) {

regards
MUHAMMED SEMIN P NMUHAMMED SEMIN P N
Hi CodeHeartsSFDC,
please go through given below code,hope this will help you 
trigger updateParentStatus on Child_Object__c(after insert,after update) {
    set<id> parentids = new set<id>();
    for(Child_Object__c childObj : trigger.new)
    {
        //parent__c is relationship field,you can use your relationship field
         parentids.add(childObj.parent__c); 
    }
    list<Parent_Object__c> parentList = [select id,status__c from Parent_Object__c where id in : parentids];
    list<Child_Object__c> childList = [select id,status__c,parent__c from Child_Object__c where parent__c in : parentids];
    for(Parent_Object__c parentObj:parentList)
    {
        Integer count=0;
        for(Child_Object__c childObj : childList)
        {
            if(parentObj.id == childObj.parent__c && childObj.status__c !='completed')
            {
                
                count++;
                break;
            }
        }
        if(count==0)
        {
            parentObj.status__c='completed';
        }
    } 
    update parentList;
}
If it solve your problem please mark it as best answer.
Thanks,
 
MUHAMMED SEMIN P NMUHAMMED SEMIN P N
Edit to the above
if(count==0)
   parentObj.status__c='completed';
else 
   parentObj.status__c=' ';
thanks,
 
CodeHeartsSFDCCodeHeartsSFDC
Thanks Guys, all your solutions work well.