0

I have a Custom Lookup Relationship on Case with Ship. I want to populate Ship's Rollup fields with no of Case on the basis of Types- If Case Type is 'A'- I want to populate a field 'No of A Cases' with the count of no of Cases with type 'A' whereas if Case has Type 'B', I want to populate a field 'No of B Cases' with the count of no of Cases with type 'B', below is the code I have written-

It is not behaving as expected, it is calculating all the number of cases in the rollup summary fields

Class-

public class RollupCaseonShip{

    public static void RollupCaseonShip(Set<Id> Ids){
        List<Ship__c> shiplist = [Select Id, Count_A_Case__c, Count_B_Case__c, Count_C_Case__c,(Select Id,Type from Cases__r) from Ship__c where Id in :Ids];
        List<Ship__c> sList = new List<Ship__c>();

        for(Ship__c s : shiplist){
            for(Case c: s.Cases__r){
                if(c.Type = 'A' && c.Type != null){
                    s.Count_A_Case__c = s.Cases__r.size();
                }
                if(c.Type = 'B' && c.Type != null){
                    s.Count_B_Case__c = s.Cases__r.size();
                }
                if(c.Type = 'C' && c.Type != null){
                    s.Count_C_Case__c = s.Cases__r.size();
                }
            }
            sList.add(s);
        }

        if(!sList.isEmpty()){
            upsert sList;
        }

    }

    public static void OnInsertDeleteUndelete(List<Case> newCaseList, List<Case> oldCaseList){
        Set<Id> shipId = new Set<Id>();
        if(newCaseList != null){
            for(Case c : newCaseList){
                shipId.add(c.Ship__c.Id);
            }
        }

        if(oldCaseList != null){
            for(Case c : oldCaseList){
                shipId.add(c.Ship__c.Id);
            }

        }

        RollupCaseonShip(shipId);
    }

}

Trigger-

trigger RollupCaseonShip on Case (after insert, afterdelete, afterundelete) {
    if( Trigger.IsAfter){
         if(Trigger.isInsert || Trigger.IsUndelete || Trigger.isDelete) {
               RollupCaseonShip.RollupCaseonShip(trigger.new, trigger.Old);
          }

    }
}
  • What is your specific question about this code? If you're encountering an error, please include it verbatim. If you're encountering behavior that is not expected, please edit your question to give a detailed description of the problem and what you've tried so far to correct it. – David Reed Jan 18 at 19:07
  • 2
    If you're not specifically trying to learn how to do rollups via triggers, or not wanting to avoid third party solutions, then the Declarative Lookup Rollup Summary tool is worth looking into. – Derek F Jan 18 at 19:10
  • Hi David, edit the question, it is not behaving as per the expectation, the rollup summary fields are getting updated with the number of cases, it is not differentiating the cases on the basis of Type – user68525 Jan 18 at 19:11
  • 1
    Please do not remove all of the content of your question after the community has engaged to provide assistance. I have rolled back this change. – David Reed Jan 18 at 19:59
3

The problem here is that once you find one case that satisfies your rollup criteria, your code assumes that all of the other cases also satisfy that criteria as well (that's whats.Cases__r.size() give you, the number of related Cases).

To do this via a trigger on Case (this would probably be more reliable in a trigger on Ship__c), you'd probably want to make use of a query with Aggregate Operators and grouping. You could do this by iterating over all of the related Case records on your Ship__c object, but why do work in code when something else can do the work for you? (less chance of introducing bugs)

An example of a query that you'd use could be

[SELECT COUNT(Id) count, Case__c case, Type__c, type FROM Ship__c WHERE Ship__c IN :shipsInTrigger GROUP BY Case__c, Type__c]

This would give you results along the line of the following

Count(Id) count, case,            Type__c
5              , 005000000000001, A
2              , 005000000000001, C
3              , 005000000000002, B
1              , 005000000000002, C

The results for all Cases (005000000000001, 005000000000002, etc...) are contiguous because it's the first field we sorted by. Within each "bucket" of case Id, you get buckets of the Type

You could then iterate over the AggregateResult records like so

Map<Id, Case> casesToUpdate = new Map<Id, Case>();

for(AggregateResult ar :queryResults){
    // The alias you specify after the field name in the query is what you use to 
    //   access the result.
    // You can do without aliases, but aliases make things easier to deal with (since
    //   you know _exactly_ how to reference the grouped/aggregated fields)
    // aggregateResult.get() returns an Object, so we need to explicitly cast the result
    //   to make it useful.
    Id caseId = (Id)ar.get('case');

    // This is a common pattern for populating a map
    // If the map doesn't contain a particular Id yet, add something for that Id
    if(!casesToUpdate.containsKey(caseId)){
        casesToUpdate.put(caseId, new Case(Id = caseId));
    }

    // Since we did the previous step, we are guaranteed to have a case in our map
    //   that we can work with.
    Case c = casesToUpdate.get(caseId);

    // Each of the AggregateResult records will have _some_ value for "count"
    // It doesn't matter if the case Ids in the results are continguous (but they will be)
    //  because, in each iteration over the AggregateResult list we get from the query,
    //  we specifically say "ok, we're working on _this_ particular case now. Any 
    //  modifications to case fields are going to be made on the case with this
    //  particular Id".
    switch on (String)ar.get('type'){
        case 'A':
            c.Count_A_Case__c = (Integer)ar.get('count');
        case 'B':
            c.Count_B_Case__c = (Integer)ar.get('count');
        case 'C':
            c.Count_C_Case__c = (Integer)ar.get('count');
    }
}

// we can now DML update to save the individual counts to each case
update casesToUpdate.values();

The code is fairly straightforward, but if you don't want to maintain it, or don't 100% understand how it works, the DLRS tool would be a better bet.

2

Your logic as written sets the Count_X_Case__c fields on each Ship__c record to the total count of related Cases when any one of the Cases has the Type X:

    for(Ship__c s : shiplist){
        for(Case c: s.Cases__r){
            if(c.Type = 'A' && c.Type != null){
                // This is incorrect - s.Cases__r.size() is the count of all Cases
                s.Count_A_Case__c = s.Cases__r.size();
            }
            if(c.Type = 'B' && c.Type != null){
                // Same problem
                s.Count_B_Case__c = s.Cases__r.size();
            }
            if(c.Type = 'C' && c.Type != null){
                // Same problem
                s.Count_C_Case__c = s.Cases__r.size();
            }
        }
        sList.add(s);
    }

Additionally, note that = is an assignment. You need to use == for a comparison, and there's no need to do c.Type == 'A' && c.Type != null. If Type is 'A', then logically it cannot be `null.

As you loop over the list s.Cases__r, you'll need to increment a counter variable - an integer - for each case type you're interested in. For example,

if (c.Type == 'A') {
     typeACases++;
}

Then, when you're finished looping over Cases, set each of the parent Ship__c record's Count_X_Case__c fields to the corresponding counter:

s.Count_A_Case__c = typeACases;
// and so on.

But like Derek said in a comment, Declarative Lookup Rollup Summaries will do this for you with zero code.

Your Answer

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