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
Vivian Ho 9Vivian Ho 9 

Trigger to update parent object (to CONCATENATE string) when child record is added/updated

Hello All,

I am trying to write a trigger to update a parent object field when its child record is added/updated. I am quite new to Salesforce Apex and tried to read a few posts in the forum and still could not get it working. Any thoughts on this is appreciated. Thank you!

Example:
Parent Object: Sales_Event__c
Field to update: Cost_Item_Update__c
Child Object: Sales_Event_Cost_Item__c (Master-Detail(Sales Event))
Sales_Event_ID__c = Sales_Event__r.Id (I’m not too sure if there is proper way to get Sales_Event ID in Sales_Event_Cost_Item__c so I create a new formula field here which I think may not be necessary though..)
Fields to be CONCATENATED: Cost_Items__c and  Quantity__c
So I would like to get below in my Parent Object:
e.g. Sales Event A has 2 Cost items,
Cost_Item_Update__c = “Cost_Item one (Quantity__c), Cost_Item two (Quantity__c)”
But my Cost_Item_Update__c  even can’t get the Cost_Items__c (Cost_Item one, Cost_Item two iteration) right.. I am wondering if I map the objects incorrectly...
trigger TestSalesEventCostItemUpdate on Sales_Event_Cost_Item__c (after insert, after delete, after update) {

    list<id> parentIDstoUpdate = new list<id>();
    if(Trigger.isInsert||Trigger.isAfter||Trigger.isDelete ){
            for (Sales_Event_Cost_Item__c co : Trigger.new) {
                parentIDstoUpdate.add(co.Sales_Event__c);       
            } 
    }

    map<id,string>  mapParentIdtoString = new Map<ID,String>();
    string str;

    for (Sales_Event_Cost_Item__c co: [SELECT Sales_Event_ID__c, Cost_Items__c from Sales_Event_Cost_Item__c WHERE Sales_Event_ID__c =:parentIDstoUpdate ]){
        if(mapParentIdtoString.containsKey(co.Sales_Event_ID__c)){
            str = mapParentIdtoString.get(co.Sales_Event_ID__c);
            str = str + ';' + co.Cost_Items__c;  
            mapParentIdtoString.put(co.Sales_Event_ID__c,str);
        }else{
            mapParentIdtoString.put(co.Sales_Event_ID__c,co.Cost_Items__c);
        }
    }
    
    list<Sales_Event__c> recordstoUpdate = new list<Sales_Event__c>();
    for (Sales_Event__c p: [SELECT ID, Cost_Item_Update__c FROM Sales_Event__c WHERE id =:recordstoUpdate ]){ 
        p.Cost_Item_Update__c = mapParentIdtoString.get(p.id);
        recordstoUpdate.add(p);
    }

    if (recordstoUpdate != null && recordstoUpdate.size()>0){
        update recordstoUpdate;
    }
}
Best Answer chosen by Vivian Ho 9
SUCHARITA MONDALSUCHARITA MONDAL
Hi Vivian Ho 9,

There are some observation on your code.
1. For Id, use SET instead of List  (as SET contains non-duplicate elements)
2. Trigger.isDelete doesn't work with Trigger.new  (check with context variable and it's usage)
3. Condition on Line 14,  will never meet as mapParentIdtoString  is blank.
4. Similarly Line 24, will return give error as recordstoUpdate  is blank.

You can go with something as below:

trigger TestSalesEventCostItemUpdate on Sales_Event_Cost_Item__c (after insert, after delete, after update, after Undelete) {
   Set<Id> parentIds = new Set<Id>();
    Map<Id,Sales_Event_Cost_Item__c > canMap = new Map<Id,Sales_Event_Cost_Item__c >();
    if(Trigger.isInsert || Trigger.isUndelete || Trigger.isUpdate){
        for(Sales_Event_Cost_Item__c  can : Trigger.new){
            parentIds.add(can.Sales_Event__c);    
           
        }
    }    
    
    if(Trigger.isDelete){
        for(Sales_Event_Cost_Item__c can : Trigger.old){
                 parentIds.add(can.Sales_Event__c);
                canMap.put(can.Sales_Event__c,can); //map for delete
        }
    }
    List<Sales_Event__c>salesUpdateList = new List<Sales_Event__c>();
    List<Sales_Event__c> salesList = new List<Sales_Event__c>([SELECT Id, Cost_Item_Update__c ,(SELECT Id, Cost_Items__c  FROM       Sales_Event_Cost_Items__r) FROM Sales_Event__c WHERE Id IN:parentIds]);
    
    for(Sales_Event__c sales : salesList){
        for(Sales_Event_Cost_Item__c can : sales.Sales_Event_Cost_Items__r){
            if(canMap.containsKey(sales.Id)){ //check for deletion
                if(sales.Cost_Item_Update__c!=null){  
                    sales.Cost_Item_Update__c = sales.Cost_Item_Update__c - can.Cost_Items__c; //removing the deleted amount
                    salesUpdateList.add(sales);
                } 
            }else{
                if(sales.Cost_Item_Update__c!=null){
                    sales.Cost_Item_Update__c = sales.Cost_Item_Update__c + can.Cost_Items__c;
                    salesUpdateList.add(sales);
                }
                
            }
        }
    }
    if(salesUpdateList.size()>0){
        update salesUpdateList;
    }


Share your thoughts.

Thanks,
Sucharita

All Answers

SUCHARITA MONDALSUCHARITA MONDAL
Hi Vivian Ho 9,

There are some observation on your code.
1. For Id, use SET instead of List  (as SET contains non-duplicate elements)
2. Trigger.isDelete doesn't work with Trigger.new  (check with context variable and it's usage)
3. Condition on Line 14,  will never meet as mapParentIdtoString  is blank.
4. Similarly Line 24, will return give error as recordstoUpdate  is blank.

You can go with something as below:

trigger TestSalesEventCostItemUpdate on Sales_Event_Cost_Item__c (after insert, after delete, after update, after Undelete) {
   Set<Id> parentIds = new Set<Id>();
    Map<Id,Sales_Event_Cost_Item__c > canMap = new Map<Id,Sales_Event_Cost_Item__c >();
    if(Trigger.isInsert || Trigger.isUndelete || Trigger.isUpdate){
        for(Sales_Event_Cost_Item__c  can : Trigger.new){
            parentIds.add(can.Sales_Event__c);    
           
        }
    }    
    
    if(Trigger.isDelete){
        for(Sales_Event_Cost_Item__c can : Trigger.old){
                 parentIds.add(can.Sales_Event__c);
                canMap.put(can.Sales_Event__c,can); //map for delete
        }
    }
    List<Sales_Event__c>salesUpdateList = new List<Sales_Event__c>();
    List<Sales_Event__c> salesList = new List<Sales_Event__c>([SELECT Id, Cost_Item_Update__c ,(SELECT Id, Cost_Items__c  FROM       Sales_Event_Cost_Items__r) FROM Sales_Event__c WHERE Id IN:parentIds]);
    
    for(Sales_Event__c sales : salesList){
        for(Sales_Event_Cost_Item__c can : sales.Sales_Event_Cost_Items__r){
            if(canMap.containsKey(sales.Id)){ //check for deletion
                if(sales.Cost_Item_Update__c!=null){  
                    sales.Cost_Item_Update__c = sales.Cost_Item_Update__c - can.Cost_Items__c; //removing the deleted amount
                    salesUpdateList.add(sales);
                } 
            }else{
                if(sales.Cost_Item_Update__c!=null){
                    sales.Cost_Item_Update__c = sales.Cost_Item_Update__c + can.Cost_Items__c;
                    salesUpdateList.add(sales);
                }
                
            }
        }
    }
    if(salesUpdateList.size()>0){
        update salesUpdateList;
    }


Share your thoughts.

Thanks,
Sucharita
This was selected as the best answer
Vivian Ho 9Vivian Ho 9
Hi Sucharita,

Many thanks for your help! 
For item 1,2; your suggestion is very true. I need to further check on Trigger.isDelete/Trigger.new context variable.
For item 3,4 , I tried to take link (https://salesforce.stackexchange.com/questions/208000/trigger-to-update-child-object-values-on-parent-object-field-concatenated-with)as reference (but I failed to amend my own code given I'm not too sure how to specify 'ParentId' in the link and I guess the link only shares part of the trigger code)

For the code you suggested, it works well on the variable_assignment (thanks again!).

However, I still couldn't get the strings in Child Object Sales_Event_Cost_Item__c concatenated (the parent field to update is blank now)
Parent Field to update: Cost_Item_Update__c and Cost_Items__c are string; Quantity__c is number
I would like to get "e.g. MealCost; quantity: 1 & TransportationCost; quantity: 10" in Cost_Item_Update__c as the 'result' text of the trigger for new cost items. (if 1 out of 2 cost items is deleted, the result text should have 1 remaining item only)

It looks the sales.Cost_Item_Update__c is blank now (I guess my syntax to 'get' the child string is not correct below....Wonder if i need to use the map.put() / map.get() to assign the result text properly in the parent field.

Appricate if you could help again!! Many thanks!!
trigger TestSalesEventCostItemUpdate on Sales_Event_Cost_Item__c (after insert, after delete, after update, after Undelete) {
   Set<Id> parentIds = new Set<Id>();
    Map<Id,Sales_Event_Cost_Item__c > canMap = new Map<Id,Sales_Event_Cost_Item__c >();
    if(Trigger.isInsert || Trigger.isUndelete || Trigger.isUpdate){
        for(Sales_Event_Cost_Item__c  can : Trigger.new){
            parentIds.add(can.Sales_Event__c);    
           
        }
    }    
    
    if(Trigger.isDelete){
        for(Sales_Event_Cost_Item__c can : Trigger.old){
                 parentIds.add(can.Sales_Event__c);
                canMap.put(can.Sales_Event__c,can); //map for delete
        }
    }
    List<Sales_Event__c>salesUpdateList = new List<Sales_Event__c>();
    List<Sales_Event__c> salesList = new List<Sales_Event__c>([SELECT Id, Cost_Item_Update__c,(SELECT Id, Cost_Items__c, Quantity__c  FROM  Sales_Event_Cost_Items1__r) FROM Sales_Event__c WHERE Id IN:parentIds]);
    
    for(Sales_Event__c sales : salesList){
        for(Sales_Event_Cost_Item__c can : sales.Sales_Event_Cost_Items1__r){
            if(canMap.containsKey(sales.Id)){ //check for deletion
                if(sales.Cost_Item_Update__c!=null){  
                    sales.Cost_Item_Update__c = sales.Cost_Item_Update__c;  // - can.Cost_Items__c; //removing the deleted amount
                    salesUpdateList.add(sales);
                } 
            }else{
                if(sales.Cost_Item_Update__c!=null){
                    sales.Cost_Item_Update__c = sales.Cost_Item_Update__c + can.Cost_Items__c + ';quantity:' + can.Quantity__c + ')'; //sales.Cost_Item_Update__c (text area) = can.Cost_Items__c (string) + '; quantity:' + can.Quantity__c + ' & ' (e.g. MealCost; quantity: 1 & TransportationCost; quantity: 10)
                    salesUpdateList.add(sales);
                }
                
            }
        }
    }
    if(salesUpdateList.size()>0){
        update salesUpdateList;
    }
}
SUCHARITA MONDALSUCHARITA MONDAL

Hi Vivian,

Try to put debug logs to check why it's not going inside the condition part. (System.debug ('--cost Item update--'+sales.Cost_Item_Update__c)

If you want to bypass the condition then (let's say initially, you don't have any value on Cost_Item_Update__c  field), try as 
sales.Cost_Item_Update__c = can.Cost_Items__c + ';quantity:' + can.Quantity__c + ')';
and remove the null check. But later your above code will work.

Please share your thougths!

Thanks,
Sucharita
 

Vivian Ho 9Vivian Ho 9
Hi Sucharita,

The code works now after taking out the condition part. Thanks for your help!!!