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
Maggie Farley 20Maggie Farley 20 

Fields updated then immediately deleted

Hi there,

I have been going in circles with an issue on the Case object. The main issue is that Case fields are being updated but the values are immediately deleted.  I am new to Apex and need some help breaking this issue down because I cannot find the root cause. I'd like to simplify the solution, but I'd like to understand what is going on before I make any changes.

Here is an example of the issue:
User-added image


What is supposed to happen:
Whenever a task is completed, a trigger fires to count the number of completed tasks and add that value into the Completed Activities field. When the Completed Activities field is populated, a workflow rule fires to update the FPR field. There is another trigger that updates the Time to FPR(Business Hours) using the FPR field value. I am new to Apex and need some help breaking this issue down because I cannot find the root cause. 

Components:

1. CaseHelper Class
public static void FPRCalc(List<Case> cases, List<Case> oldList, Map<ID, sObject> newMap, Map<ID, sObject> oldMap){
		for(Case c : cases){
			if(c.FPR__c != NULL && c.BusinessHoursId !=NULL){
				if(c.Time_to_FPR_Business_Hours__c != ((Double)(BusinessHours.diff(c.BusinessHoursId, c.CreatedDate, c.FPR__c))/3600000)){
                	c.Time_to_FPR_Business_Hours__c = ((Double)(BusinessHours.diff(c.BusinessHoursId, c.CreatedDate, c.FPR__c))/3600000);
				}
            }

            if(c.Date_Time_Assigned__c != NULL && c.BusinessHoursId !=NULL){
				if(c.Time_to_Assignment_Hours__c != ((Double)(BusinessHours.diff(c.BusinessHoursId, c.CreatedDate, c.Date_Time_Assigned__c))/3600000)){
                	c.Time_to_Assignment_Hours__c = (((Double)(BusinessHours.diff(c.BusinessHoursId, c.CreatedDate, c.Date_Time_Assigned__c))/3600000));
	            }
			}
		}
	}
2. taskAndActivity Trigger
trigger taskAndActivityTrigger on Task (after insert, after update, before delete) 
{

/*	taskandactivity trigger to update Completed Activities
*/
	List<Task> taskList = new List<Task>();
	Set<Id> casesToDecrement = new Set<Id>();
	List<Id> leadIds = new List<Id>();
	Map<Id, Lead> leadIdandLead = new Map<Id, Lead>();
	Map<Id, Decimal> leadIdandCount = new Map<Id, Decimal>();
	Map<Id, Date> leadIdandMaxDate = new Map<Id, Date>();
	Map<Id, Id> leadIdandTaskId = new Map<Id, Id>();
	List<Lead> leadsToUpdate = new List<Lead>();
	Set<Id> leadsToUpdateIds = new Set<Id>();
	Map<Id, Case> caseIdandCase = new Map<Id, Case>();
	List<Id> caseIds = new List<Id>();
    Map<Id, Decimal> caseIdandCount = new Map<Id, Decimal>();
    Map<Id, Datetime> caseIdandDateTime = new Map<Id, DateTime>();
    List<Case> casesToUpdate = new List<Case>();
    Set<Id> casesToUpdateIds = new Set<Id>();

	/*Can't traverse What and Who relationships in Trigger.new, so must query for these fields. Can't use trigger.newMap in delete trigger*/
	if (!Trigger.isDelete)
	{
		taskList = [SELECT Id, Status, isClosed, What.Type, Who.Type, What.Id, Who.Id, ActivityDate, CreatedDate FROM Task Where Id IN : trigger.newMap.keyset() AND Lead_Source_Activity__c != TRUE];
		system.debug('taskList =  ' + taskList);
	}
	else
	{
		taskList = [SELECT Id, Status, isClosed, What.Type, Who.Type, What.Id, Who.Id, ActivityDate, CreatedDate FROM Task Where Id IN : trigger.oldMap.keyset() AND Lead_Source_Activity__c != TRUE];
		system.debug('taskList =  ' + taskList);
	}

	if (!taskList.isEmpty())
	{
		for (Task t : taskList)
		{

			/*Only want closed tasks/activities*/
			if (Trigger.isInsert && t.isClosed)
			{
				system.debug('Trigger.isInsert');

				if (t.What.Type == 'Case')
				{
					system.debug('What.Type = Case');

					/*Can't add to caseIdandCase map here - can't get full Case object off Task. Will query for this and add to map outside of this loop.*/ 
					caseIds.add(t.WhatId);
					system.debug('caseIds = ' + caseIds);
				}


			/*Only want to get the tasks on update that have changed Status*/
			if (Trigger.isUpdate) 
			{

				if ((Trigger.oldMap.get(t.Id).Status != Trigger.newMap.get(t.Id).Status) || (t.Status ==  'Completed' && Trigger.oldMap.get(t.Id).ActivityDate != Trigger.newMap.get(t.Id).ActivityDate))
				{
					system.debug('Trigger.isUpdate and isClosed changed');
					system.debug('new task status = ' + Trigger.newMap.get(t.Id).Status);
					system.debug('old task status = ' + Trigger.oldMap.get(t.Id).Status);

					if (t.What.Type == 'Case')
					{
						system.debug('What.Type = Case');
						/*Can't add to caseIdandCase map here - can't get full Case object off Task. Will query for this and add to map outside of this loop.*/ 
						caseIds.add(t.WhatId);
						system.debug('caseIds = ' + caseIds);
					}

					if (t.Who.Type == 'Lead')
					{
						system.debug('What.Type = Lead');
						/*Can't add to leadIdandLead map here - can't get full Lead object off Task. Will query for this and add to map outside of this loop.*/  
						leadIds.add(t.WhoId);
						system.debug('leadIds = ' + leadIds);
					}
				}
			}

			if (Trigger.isDelete)
			{
				if (t.What.Type == 'Case' && t.isClosed == TRUE)
				{
					leadIds.add(t.WhoId);
					system.debug('leadIds = ' + leadIds);
					system.debug('What.Type = Case');
					casesToDecrement.add(t.WhatId);
					system.debug('casesToDecrement = ' + casesToDecrement);
				}
				if (t.Who.Type == 'Lead' && t.isClosed == TRUE)
				{
					leadIds.add(t.WhoId);
					system.debug('leadIds = ' + leadIds);
					system.debug('Who.Type = Lead');
					leadIdandTaskId.put(t.Id, t.WhoId);
					system.debug('leadIdandTaskId = ' + leadIdandTaskId);
				}
			}
		}
	}

	/*Build key-value pairs from list of ids obtained in trigger context, since task --> case relationship can't be traversed*/ 
	if (!caseIds.isEmpty())
	{
		for (Case c : [SELECT Id, Last_Activity__c, Completed_Activities__c FROM Case WHERE Id IN : caseIds])
		{
			caseIdandCase.put(c.Id, c);
		}
	}

    if (!caseIdandCase.isEmpty())
    {
	    AggregateResult[] caseAggregate = [SELECT WhatId, Count(Id)closedTaskCount FROM Task WHERE isCLosed = TRUE AND What.Type = 'Case' AND WhatId IN : caseIdandCase.keyset() GROUP BY WhatId];
	    system.debug('caseAggregate =  ' + caseAggregate);

	    if (!caseAggregate.isEmpty())
	    {
	    	for (AggregateResult ca : caseAggregate)
	    	{
	    		caseIdandCount.put((ID)ca.get('WhatId'), (Decimal)ca.get('closedTaskCount'));
	    		system.debug('caseIdandCount = ' + caseIdandCount);
	    	}

	    	if (!caseIdandCount.isEmpty())
	    	{
	    		for (Case c : [SELECT Id, Completed_Activities__c FROM CAse WHERE Id IN : caseIdandCount.keyset()])
	    		{
	    			caseIdandCase.get(c.Id).Completed_Activities__c = caseIdandCount.get(c.Id);
	    		}
	    	}
	    }

		/*If task is updated from isClosed to !isClosed, and it is the only task related to that case, we need to decrement it. This list also contains deleted deleted case tasks.*/
		else if (caseAggregate.isEmpty())
		{
			for (Id caseId : caseIdandCase.keySet())
			{
				casesToDecrement.add(caseId);
				system.debug('casesToDecrement = ' + casesToDecrement);
			}
		}

		if (!caseIdandDateTime.isEmpty())
		{
			for (Case c : [SELECT Id, Last_Activity__c, Next_Business_Day_After_Last_Activity__c, BusinessHoursId FROM Case Where Id IN : caseIdandDateTime.keySet()])
			{
				caseIdandCase.get(c.Id).Last_Activity__c = caseIdandDateTime.get(c.Id);
				system.debug('caseIdandCase.get(c.Id).Last_Activity__c ' + caseIdandCase.get(c.Id).Last_Activity__c);
				dateTime tempDateTime = caseIdandDateTime.get(c.Id);
				system.debug('tempDateTime = ' + tempDateTime);

				if (c.BusinessHoursId != null && tempDateTime != null)
				{
					BusinessDays bizDays = new BusinessDays(c.BusinessHoursId);
					system.debug('bizDays = ' + bizDays);
					caseIdandCase.get(c.Id).Next_Business_Day_After_Last_Activity__c = BizDays.nextBusinessDay(date.newInstance(tempDateTime.year(), tempDateTime.month(), tempDateTime.day()));
					system.debug('caseIdandCase.get(c.Id).Next_Business_Day_After_Last_Activity__c = ' + caseIdandCase.get(c.Id).Next_Business_Day_After_Last_Activity__c);
				}
			}
		}

		if (!casesToDecrement.isEmpty())
		{
			for (Case c : [SELECT Id, Completed_Activities__c FROM Case WHERE Id IN : casesToDecrement])
			{
				caseIdandCase.get(c.Id).Completed_Activities__c = caseIdandCase.get(c.Id).Completed_Activities__c - 1;
			}
		}
	}

	/*Loop through the key-value pairs and add them to the list of Cases to pass into the saveResult - only List of sObjects accepted, and don't want dups, hence the set.*/
    for (Id caseId : caseIdAndCase.keySet())
    {
        if (!casesToUpdateIds.contains(caseId))
        {
            casesToUpdate.add(caseIdAndCase.get(caseId));
            system.debug('casesToUpdate = ' + casesToUpdate);
        }
        else
        {
            casesToUpdateIds.add(caseId);
            system.debug('casesToUpdateIds = ' + casesToUpdateIds);
        }
    }

	if (!casesToUpdate.isEmpty())
	{
		/*Update cases, partial processing*/
	    Database.saveResult[] caseResults = Database.update(casesToUpdate, false);
		
	    String subject = 'Error Updating cases with Completed_Activities__c';
	    List<String> toAddresses = new List<String>{'bspencer@appextremes.com, rdente@appextremes.com'};
	    String body = '';
	    
	    for (Database.saveResult res : caseResults)
	    {
	        if (!res.isSuccess())
	        {
	            body += 'Id = ' + res.getId() + '\n\n' + res.getErrors() + '\n\n';
	        }
	    }
	}
}
3. FPR Timestamp WFR

Criteria: ISBLANK(FPR__c) && Completed_Activities__c > 0
Evaluation: Evaluate the rule when a record is created, and any time it's edited to subsequently meet criteria
Shivdeep KumarShivdeep Kumar
Hi Maggie,

Let me tell you as per my understanding .
  1. Whenever your task status is 'Completed' , a trigger is fire and on task which will count the total no. of completed task on case object.
  2. Based on this trigger, your workflow is fire, which is used to update the field,
  3. Based on this workflow your next trigger is fire.
If I am right, so please use and check it for counting the task records on case. You can also merge your workflow logic on this trigger.
Trigger updateCaseClosedTask on Task(after insert, after update, after delete, after undelete) {
   List<Case> cases = new List<Case>();   
   Set<Id> caseIds = new Set<Id>();
   
   if(Trigger.isDelete) {
     for(Task t :Trigger.Old) {  
         If (t.whatid <> null || t.whatId <> ''){
             If (string.valueof(t.whatId).startswith('500')){
                caseIds.add(t.whatId);
                system.debug('### caseId Del'+caseIds);    
             }      
        }
     }
   }else if(Trigger.isUpdate) {
     for(Task t :Trigger.New) {      
        If (t.whatId <> null || t.whatId <> ''){
             If (string.valueof(t.whatId).startswith('500') ){
                caseIds.add(t.whatId); 
                system.debug('### caseId new updat'+caseIds);   
             }      
        }     
     }
     for(Task t : Trigger.Old) {      
         If (t.whatId <> null || t.whatId <> ''){
            If (string.valueof(t.whatId).startswith('500')){
                caseIds.add(t.whatId); 
                system.debug('### caseId old upd '+caseIds);   
             }      
         }  
     }      
   }else {
     for(Task t : Trigger.New) {      
        If (t.whatId <> null || t.whatId <> ''){
             If (string.valueof(t.whatId).startswith('500')){
                caseIds.add(t.whatId);
                system.debug('### caseId else '+caseIds);    
             }      
        }    
     }
   }
   system.debug('### caseId '+caseIds);
   Integer count ;
   AggregateResult[] groupedResults = [SELECT COUNT(Id), whatId FROM task where whatId IN :caseIds AND Status =: 'Completed' GROUP BY whatId];
   system.debug('### ar '+groupedResults );
   if(groupedResults.size() > 0){
       for(AggregateResult ar:groupedResults) {     
         Id custid = (ID)ar.get('whatId');     
         count = (INTEGER)ar.get('expr0');     
         Case cs = new Case(Id=custid);     
         cs.Completed_Activities__c = count;     
         cases.add(cs); 
     
     }     
   }else{
       for(AggregateResult ar: [SELECT COUNT(Id), whatId FROM task where whatId IN :caseIds GROUP BY whatId]) {     
         Id custid = (ID)ar.get('whatId');     
         count = (INTEGER)ar.get('expr0');     
         case cs = new Case(Id=custid);     
         cs.Completed_Activities__c = 0;     
         cases.add(cs); 
     
     }  
   }
   
If(cases.size()>0)
   update cases;
}

Please let me know if this help !

Thanks
Shivdeep
Maggie Farley 20Maggie Farley 20
Hi Shivdeep,

You are correct. This is very helpful. Can you tell which part was causing the values to be deleted immediately after they were updated? See example below.  

User-added image

Thank you so much!
Shivdeep KumarShivdeep Kumar
Hi Maggie,
As per my understanding please check the trigger on case object because when your workflow fires then the trigger on case object executed.

Sorry if I am wrong.

Thanks
Shivdeep