[SalesForce] Comparing Parent fields with all the child records and updating all records

I have a child object having Master-Detail relationship with contact object. There is a checkbox Primary__c on both of the object. I need to see if that checkbox is checked for more than one record and if it is I need make another checkbox = true on every record.

For ex. Contact A have the primary__c checkbox = true (checked) and the child object address__c also have the checkbox = true, when this is the scenario I need to update another checkbox popup__c on all the records that are related to the contact and also the contact itself. I tried to write after insert, update and delete trigger and was not able to achieve it. Relationship name is addresses__r. Please help me as in how this can be done.

trigger AlternateAddressPopupAlert on Alternate_Address__c (after insert, after update,after delete){
map<Id,Alternate_address__c> mapOfAllFamily = new map<Id,Alternate_address__c>();
set<Id> setOfparentconact = new set<Id>();

if(trigger.isInsert || trigger.isUpdate){
    for(Alternate_Address__c altObj : trigger.new){
        if((trigger.isInsert || trigger.isUpdate) && altObj.checkbox__c!=trigger.oldmap.get(altObj.Id).checkbox__c){
            if(altObj.contact__c != null){
                setOfparentconact.add(altObj.contact__c); 
            }else{

            }           
        }
        system.debug('Set of primary contact----'+setOfparentconact);
    }
}else if(trigger.isDelete){
    for(Alternate_Address__c altObj : trigger.old){
        if(altObj.contact__c != null){
            setOfparentconact.add(altObj.contact__c); 
        }   
    }
}
if(!setOfparentconact.isEmpty()){
    for(Contact cntObj : [SELECT id,checkbox__c,(SELECT id,checkbox__c from Contacts__r) from Contact WHERE id in : setOfParentContacts]){
        set<Id> setOfOwners = new set<Id>();
        if(cntObj.Contacts__r != null && cntObj.Contacts__r.Size() > 0 ){
            for(Contact ChildCntObj : cntObj.Contacts__r){
                setOfOwners.add(ChildCntObj.OwnerId);
            }
        }
        setOfOwners.add(cntObj.OwnerId);
        mapOfAllFamily.put(cntObj.Id,setOfOwners);     
    }}

I haven't got very far as I am unable to decide how to proceed with this.

Best Answer

I'm not entirely certain I understand the relationships, but let me see if I can point you in the right direction. I'm going to make the assumption that you want to do this afterinsert and afterupdate and before Delete since an after Delete makes no sense. I'm also understanding that the checkbox field is named primary__c on both the objects Contact and it's child Alternate_Address__c. There's a third Object named Address__c that's also a child of Contact with a checkbox field named popup__c.

You mention "needing to see if that checkbox is checked for more than one record, and if it is, needing to make another checkbox = true on every record." Its unclear if this refers to it being checked on more than one related record causing you to need to check it all on other records or something else. If so, you'll need to clarify that requirement as that can be a lot of work to make happen. You might say, that's a big ask and beyond the scope of what I intend to answer.

trigger AlternateAddressPopupAlert on Alternate_Address__c (after insert, after update, before delete){

map<Id,Alternate_address__c> mapOfAllFamily = new map<Id,Alternate_address__c>();
set<Id> setOfparentconact = new set<Id>();
map<Id,Id>ctc2altadd = new map<Id,Id>();

if(trigger isBefore)
{
    if(trigger.isDelete)
    {
        // Note, if deleting, not certain what you really want to do here other than clear any addresses that might match in Contact Object, so logic should be different
        for(Alternate_Address__c altObj : trigger.old){
        if(altObj.contact__c != null){
            ctc2altadd.put(altObj.contact__c,altObj.Id);
            system.debug('altobjcontact__c = `+ altObj.contact__c);
            system.debug('setOfparentconact size = `+ctc2altadd.keyset().size());
        }   
    }
}

if(trigger isAfter) 
{
    if(trigger triggerIsInsert || trigger.IsUpdate)
    {
        for(Alternate_Address__c altObj : trigger.new)
        {
            // As I understand it, the field to test is primary__c, it needs to be = true & you only want to do something when it's changed
            if(altObj.primary__c!=trigger.oldmap.get(altObj.Id).primary__c && alt.primary__c == true && altObj.contact__c != null). 
            // last test condition seems superfluous since since in a M-D relationship, you'd need to have the reference to the parent
            {
                ctc2altadd.put(altObj.contact__c,altObj.Id);
                system.debug('altobjcontact__c = `+ altObj.contact__c);
                system.debug('setOfparentconact size = `+ctc2altadd.keyset().size());
            }
        }
    }
    system.debug('Set of primary contacts size = '+ctc2altadd.keyset().size());
}


set<id>ctcIds = new set<id>();
// test to see if Contact also  
if(!setOfparentconact.isEmpty())
{
    // add any other fields to this query (like address fields) that you may need to update
    for(Contact cntObj : [SELECT id,Primary__c from Contact WHERE id in : setOfParentContacts])
    {
        // test to see if Contact checkbox = true
        if (cntObj.Primary__c = true;)
        {
           ctcIds.add(cntObj.Id);
           system.debug('contactId = '+cntObj.Id);
         }        
     }        
}

// time to query for address__c records
if(!ctcIds.isEmpty())
{
    // add any other fields to this query (like address fields) that you may need to update
    for( address__c addr : [SELECT Id, popup__c, contact__c (M-D child ID) FROM Address__c where contact__c in :ctcIds]}
    {
        if(addr.popup__c = true)
        {
           // add your logic here as it's not clear what you want to do
        }else{


        }           
    }       
}

// Update all the objects that you need to at the end of the trigger here. Be certain to add recursion protection using Booleans applied at the entry points of your trigger

}// end of trigger

Hopefully this shows you the kind of approach using MAPS that you'll want to take. Get your fields sorted out along with your queries and use the Maps to determine which records are a match between your object for updating or manipulation. Since I don't know your schema, the exact fields to use, or precisely what you're trying to do, I can't really help you any further but this should point you in the right direction to follow.

Edit

Regardless of the relationship names, you'll need to follow the kind of pattern I've outlined above. What you'll need to do in addition is once you have a list of all children, for each parent record, you'll need to iterate through the list of child records and count the number of children to see how many have the checkbox = true. If the number is more than one (you can break if you want to), you add that list to a map for updating.

To help you with that, instead of keeping a map<ParentId,ChildId> (or vice-versa), you'll want to keep either a map> or map> plus a map<ChildId,ChildsObject>. so you can pull everything out that needs to be updated when it's needed.

Based on your comments, it sounds as though your entry criteria would need to be altered to something like below for the afterUpdate part of your trigger. Note that an AfterInsert trigger doesn't have a trigger.old, so best to do these in separate blocks :

    if(trigger.IsUpdate)
    {
        for(Alternate_Address__c altObj : trigger.new)
        {
            if(altObj.primary__c!=trigger.oldmap.get(altObj.Id).primary__c && alt.primary__c == true && altObj.contact__c != null). 
            // last test condition seems superfluous since since in a M-D relationship, you'd need to have the reference to the parent
            {
                ctc2altadd.put(altObj.contact__c,altObj.Id);
                system.debug('altobjcontact__c = `+ altObj.contact__c);
                system.debug('setOfparentconact size = `+ctc2altadd.keyset().size());
            }
        }
    }
    system.debug('Set of primary contacts size = '+ctc2altadd.keyset().size());
}
// you have your list of changed records and now need to compare with ALL related     
// Alternate_Address__c records that are children of the same Contact record

list<Alternate_Address__c>altObj2 = [SELECT Id, primary__c, contact__c FROM Alternate_Address__c WHERE contact__c IN: ctc2altadd.keyset() ];

// Sort results into map of contact__c and list<Id> or list<Alternate_Address__c> (your choice)
 map<contact__c, list<Alternate_Address__c>>ctcId2AAC = new map<contact__c, list<Alternate_Address__c>>();
for(Alternate_Address__c aa:altObj2)
{
    for(contact__c c:ctc2altadd.keyset())
    {
         list<Alternate_Address__c>aac=new list<Alternate_Address__c>();
         if(aa.contact__c = c)
         {
             aac.add(aa);
             ctcId2AAC.put(c,aac);
         }
         //clear list for next contact__c
         aac.clear();
    }
}

// now check to see which lists of aac had more than one checkbox = true
map<contact__c, list<Alternate_Address__c>>ctcId2AACSort = new mapmap<contact__c, list<Alternate_Address__c>>();
for{contact__c c:ctcId2AAC.keyset())
{
     for(Alternate_Address__c a:ctcId2AAC.get(c))
     {
         integer i=0;
         if(a.primary__c = true) i++;
         if(i>=2)
         {
             ctcId2AACSort.put(c, ctcId2AAC.get(c));
         }
     }
}

That should give you a sorted map of all the child records that have more than 1 checkbox checked. If I've used the wrong field name, you'll be able to correct that. At least you can see the pattern now and apply it to all the other parts of your trigger.

Related Topic