1

Parent case has multiple child cases when i am trying to closed parent case it closed now but according to requirment if any child case has status open or anything except Closed then parent case cannot be closed. Its not working correclty.

trigger CloseCase on Case (Before Update) {

      if(Trigger.isBefore && Trigger.isUpdate){
          CaseCloseController.caseError(Trigger.New);
      }
 }

----------------Handler class -----------------------

   public class CaseCloseController{

  public static void caseError(List<Case> caseList){

  System.debug('CaseList---->'+caseList);
  Set<Id> caseIds = new Set<Id>();
  if(caseList != null && caseList.size()>0){

      for(Case c : caseList){
          if(c.Id != null){
              caseIds.add(c.Id);
          }
      }

      System.debug('id-->'+caseIds);
      List<Case> csList  = new List<case>();
      csList = [SELECT id,status,(Select Id,Status,ParentId From Cases where status != 'Closed' and ParentId != null) From Case Where Id IN : caseIds];
      System.debug('case details----->'+csList);

      if(csList.size()>0){ 
            for(Case c : caseList){
              if(c.cases.size()>0){
                  c.addError('Parent case cannot be closed because Child case(s) has not closed yet');
              }
            }
      }
    }      
  }
}

4 Answers 4

0

I would say pass oldMap also to your class, to check the status if it is changing from not closed to closed.

In your code I think if(c.cases.size()>0) the line is problematic and you don't need to do an inner query, you can directly query all the child cases.

As mentioned by David you can add errors only in Trigger Context Variables, which in your case is caseList

Try below code, hope it will work

public class CaseCloseController{

  public static void caseError(List<Case> caseList,Map<Id,Case> oldMap){
    Set<Id> caseIds = new Set<Id>();
    for(Case c : caseList){
        if(c.status == 'Closed' && c.Status != oldMap.get(c.Id).Status){
            caseIds.add(c.Id);
        }
    }

    List<Case> csList = [SELECT Select Id,Status,ParentId From Case where status != 'Closed' and ParentId IN : caseIds];
    Map<Id,List<Case>> caseListMapWithId = new Map<Id,List<Case>>();
    for(Case cs : csList){
        if(caseListMapWithId.containsKey(cs.ParentId)){
            caseListMapWithId.get(cs.ParentId).add(cs);
        }else{
            caseListMapWithId.put(cs.ParentId,cs);
        }
    }

    for(Case c : caseList){
        if(caseListMapWithId.get(c.Id).size() > 0){
            c.addError('Parent case cannot be closed because Child case(s) has not closed yet');
        }
    }
  }
} 
2

You've got this all backwards; it's much easier to just perform an aggregate result query. Here's what the modified version would look like:

public class CaseCloseController {
  public static void caseError(Case[] cases) {
    Set<Id> closedCases = new Set<Id>();
    Map<Id, Case> caseMap = new Map<Id, Case>(cases);
    for(Case record: cases) {
      if(record.Status == 'Closed') {
        closedCases.add(record.Id);
      }
    }
    if(!closedCases.isEmpty()) {
      Set<Id> openCases = new Map<Id, AggregateResult>([
        SELECT ParentId Id
        FROM Case
        WHERE ParentId = :closedCases AND 
              IsClosed = FALSE
        GROUP BY ParentId]).keySet();
      for(Id caseId: openCases) {
        caseMap.get(caseId).addError('You must close all child cases first.');
      }
    }
  }
}

First, we find out which cases have closed, and then query child open cases, and finally display an error on any closed cases that have any open cases.

2
  • Its not working and second problem in this closedCases.keySet() only closedCases will be there when compare with parentId. May 1, 2018 at 9:25
  • @cloudZigZag The .keySet bit was apparently an accident, so I changed it. And yes, this logic is indeed correct; we look at cases that have been closed, then see if they have any open children. Try it out.
    – sfdcfox
    May 1, 2018 at 14:12
0

You cannot add errors to sObjects that are not part of the trigger context variables (Trigger.new, Trigger.newMap, Trigger.old, Trigger.oldMap), such as the Cases that you query in your handler. It simply has no effect on the DML operation.

You'd need to use the Case Id to cross-reference back to Trigger.newMap (for example) and add the error to that object to stop the update.

Another way to approach this, which I'd actually say is preferable, is to route your query into a Map<Id, Case>, and then loop over caseList again. You can use the Id of the Case in caseList to get the queried version from the map and check its count of child cases, then add the error as appropriate to the right object.

0

Here is a version that fits best practices. It compares old value and new value, uses less loop and heap is decreased by querying with enhanced for loop. Additionally for every kind of error it is better to use custom labels, so that it can be translated and do not need another deployment if you need to change it again.

Set<Id> caseIdWithStatusChange = new Set<Id>();
for(Id c : caseNewValueMap.keySet())
{
    Case newCase = caseNewValueMap.get(c);
    Case oldCase = oldCaseMap.get(c);       
    if(newCase.Status == 'Closed' && newCase.Status != oldCase.Status)
        caseIdWithStatusChange.add(c);
}
if(!caseIdWithStatusChange.isEmpty())
{
    for(Case cse : [Select Status, ParentId From Case where ParentId in :caseIdWithStatusChange AND Status != 'Closed']){
        caseNewValueMap.get(cse.ParentId).addError(System.Label.ChildCaseIsOpen);
    }       
}

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .