0

I have two custom objects: ObjectA__c as the master object, while ObjectB__c as the detail object.

Objects with Fields:

ObjectA__c : Id, Name, checkbox__c, Total__c
ObjectB__c : Id, Name, ObjectA__c, Qty__c

I created an apex class that is supposed to update Total__c field from ObjectA__c, every time I delete an ObjectB__c record. This class is called in an apex trigger.

I know that my problem with would be clearer if I provide an example.

example:

Total__c = 1000 (assume that this is the current value)

Then suppose we have an ObjectB__c record with the Qty__c of 500

Now when I delete this ObjectB__c record, Total__c now must be updated to 500, following this formula:

Total__c = current total field value - qty field value of recently deleted ObjectB__c record

Thus: 1000 - 500 = 500

Problem is, every time I delete a detail record, Total__c field is not updated at all.

Apex Class

public class MyController {

 public static void UpdateTotalViaObjectBDelete(List<ObjectB__c> objBList) {

        List<ObjectA__c> newObjA = new List<ObjectA__c>();
        Decimal t = 0;
        List<ObjectA__c> aToUpdate = new List<ObjectA__c>();

        objBList = [SELECT Id, Name, Qty__c, ObjectA__c, ObjectA__r.Total__c FROM ObjectB__c WHERE ID IN: objBList];  

        if(!objBList.isEmpty()) {

            newObjA = [SELECT Id, Name, checkbox__c, Total__c FROM ObjectA__c WHERE ID =: objBList[0].Id];

            if(!newObjA.isEmpty()) {

                t = newObjA[0].Total__c;

            }

        }        

        for(ObjectA__c a : newObjA) { 

            if(a.checkbox__c) {

                a.Total__c = t - objBList[0].Qty__c;        
                aToUpdate.add(a);        

            }

        }     

        if(!aToUpdate.isEmpty()) {  

            update aToUpdate;

        }    

    } 

}

Apex Trigger

trigger updateTotal on ObjectB__c (after delete) {
    if(Trigger.isAfter && Trigger.isDelete) {
            for(ObjectB__c ob: Trigger.Old) {
                MyController.UpdateTotalViaObjectBDelete(Trigger.Old);
            }    
    }
}
  • You are passing objBList values from trigger. Then why again query using this list. Also ID IN: objBList condition is not valid. objBList contains ObjectB__c records not ids. Make your trigger bulkified too. – Reshma Jan 9 '17 at 9:52
  • 2
    can't you use a Rollup summary field for this? – Novarg Jan 9 '17 at 10:08
  • Thanks to those also who made their effort to help, appreciate it. Will do all your suggestions next time I encounter this kind of scenario. Thank you very much. – curdual Jan 9 '17 at 11:03
0

Your code can be optimised in this way. There are few issues with your code.

Trigger:

trigger updateTotal on ObjectB__c (after delete) {
    MyController.UpdateTotalViaObjectBDelete(Trigger.Old);
}

class:

public class MyController {

    public static void UpdateTotalViaObjectBDelete(List<ObjectB__c> objBList) {
        if(!objBList.isEmpty()){
             List<Id> objAIds = new List<Id>();

             for(ObjectB__c obj : objBList){
                 objAIds.add(obj.ObjectA__c);
             } 

             Map<Id, ObjectA__c> objAById = new Map<Id, ObjectA__c>([SELECT Id, Total__c FROM ObjectA__c WHERE Id in :objAIds]);

             for(ObjectB__c objB : objBList){
                  ObjectA__c objA = objAById.get(objB.ObjectA__c);
                  objA.Total__c -= objB.Qty__c;
                  objAById.put(objB.ObjectA__c, objA);
             }
             update objAById.values();
        } 
    }
}
0

There are several problems in your code. First of all, you loop through a list in your trigger and send this list to your controller for each element in this list. Here's the right code:

trigger updateTotal on ObjectB__c (after delete) {
    MyController.UpdateTotalViaObjectBDelete(Trigger.Old);
}

Then, in your controller, you select your object by id by using a list of object. And after that, there are more problems. Here's the right code:

public class MyController {

 public static void UpdateTotalViaObjectBDelete(List<ObjectB__c> objBList) {
        List<Id> objAIds = new List<Id>();
        for(ObjectB__c obj : objBList){
             objAIds.add(obj.ObjectA__c);
        } 

        Map<Id, ObjectA__c> objAById = new Map<Id, ObjectA__c>([SELECT Id, Total__c FROM ObjectA__c WHERE Id in :objAIds]);

        for(ObjectB__c objB : objBList){
             ObjectA__c objA = objAById.get(objB.ObjectA__c);
             objA.Total__c -= objB.Qty__c;
        }

        update objAById.values();
    } 

}
0

To me it seems that you only want to see the correct total of all quantities on the child side of a Master-Detail relationship.

For this I would recommend you using Rollup Summary Field instead of triggers.

Just go to ObjectA__c fields, create a new field of type Roll-up Summary.

Then choose ObjectB__c as Summarized Object and choose SUM of the Qty__c field. Example screenshot:

enter image description here

Do not forget: code you don't write - is the code you don't have to maintain!

0


There are two things here
1. If you are having Master Detail relation ship between Object A and Object B then as best practice says that go for out-of-Box feature that Salesforce has already provided i.e create roll-up summary field so that you don't have to code explicitly.
2. If you are having Lookup relationship between Object A and Object B

Then try following code for your Trigger, I have optimized and Bulkified the code, here you don't have to explicitly make adjustment to minus quantity from total, this will perform recalculation each time record is edited or created or delated etc.
This will cover scenarios when child record is
1. Created
2. Updated
3. Deleted
4. Restored from recycle Bin(i.e Trigger.undelete)
And why you only need to calculate Total here only when child record is deleted, ideally it should reflect for all scenarios right?

//Remove For Loop from the code
trigger updateTotal on ObjectB__c (after delete) {
   new MyController().UpdateTotalViaObjectBDelete(Trigger.Old);
}

public class MyController {
//Variable Declaration.
Map<Id, List<ObjectB>> objectAIdVsObjectBRecordsListMap = new Map<Id, List<ObjectB>>();
List<ObjectA__c> objectAList = new List<ObjectA__c>();
Set<Id> objectAIdSet = new Set<Id>();

public void UpdateTotalViaObjectBDelete(List<ObjectB__c> objBList) {
    //Prepare Object A Ids Set
    objectAIdSet = getObjectAIdsSet(objBList);

    if(objectAIdSet.isEmpty())  return; //return if Set is Empty

    // Prepare Map of Parent Id vs Associated Child Records ObjectA Id Vs List of Associated
    // Object B Records
    objectAIdVsObjectBRecordsListMap = getObjectAidvsObjectBListMap(objectAIdSet);

    if(objectAIdVsObjectBRecordsListMap.isEmpty()) return;//return If Map is Empty

    //Prepare Parent Object List to Update i.e Object A List
    objectAList = getObjectAList(objectAIdVsObjectBRecordsListMap);

    if(objectAList.isEmpty()) return;//return If List is  Empty 

    //Update Parent Object
    Database.update(objectAList,false);       

}//UpdateTotalViaObjectBDelete

//Method to return Set of Parent object A Ids
public set<Id> getObjectAIdsSet(List<ObjectB__c> objectBList){
    for(ObjectB__c objObjectB : objectBList){
        objectAIdSet.add(objObjectB.ObjectA__c);
    }//for
    return objectAIdSet;
}//getObjectAIdsSet

//Method to return ObjectA Parent Id vs associated child records
public Map<Id, List<ObjectB__c>> getObjectAidvsObjectBListMap (Set<Id> objectAIdSet){

    for(ObjectB__c  objObjectB : [SELECT Id, Name, Qty__c,ObjectA__c
                                      FROM ObjectB__c
                                      WHERE ObjectA__c IN : objectAIdSet]) {
        if(objObjectB.ObjectA__c != NULL) {
            if(objectAIdVsObjectBRecordsListMap.containsKey(objObjectB.ObjectA__c)) {
                objectAIdVsObjectBRecordsListMap.get(objObjectB.ObjectA__c).add(objObjectB);
            }//if
            else {                    
                objectAIdVsObjectBRecordsListMap.put(objObjectB.ObjectA__c, new List<ObjectB__c> {objObjectB});
            }//else
        }//if
    }//for
    return objectAIdVsObjectBRecordsListMap;
}//objectAIdVsObjectBRecordsListMap

//Get Object A List
public List<ObjectA> getObjectAList(Map<Id, List<ObjectB__c>> objectAIdVsObjectBRecordsListMap){
    Map<Id,ObjectA__c> objectAIdMap = new Map<Id,ObjectA__c>();

    for(ObjectB__c  objObjectB : [[SELECT Id, Name, Qty__c, ObjectA__c From ObjectB__c
                                      WHERE ObjectA__c IN : objectAIdSet]) {
        ObjectA__c objObjectA = objectAIdMap.get(objObjectB.ObjectA__c);
        if (objObjectA == null) {
            objectAIdMap.put(objObjectA.ObjectA__c, new ObjectA__c(Id=objObjectB.ObjectA__c, Total__c  = objObjectB.Qty__c));
        } else {
            objObjectA.Total__c += objObjectB.Qty__c;                
        }
    }
    return objectAIdMap.values();
}//getObjectAList
}//MyController

Thanks,
Nachiket

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.