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
vinni v cvinni v c 

batch class to update custom field based on opportunity status

on an opportunity, we have a lookup field to the custom object(customobject__c) and we need to update the custom field(Text__c) of customobject__c based on the opportunity status field.
Note: On opportunity, we have a lookup field to the customobject__c and On customobject__c, we have opportunity related list where we can have many opportunities associated with the 1 customobject__c record
Best Answer chosen by vinni v c
Pavithra PeriyasamyPavithra Periyasamy
Vinni,

It is due to the logic that I have missed. Sorry for that,
 
global class BatchUpdateStatus implements Database.Batchable<sobject>{
    
    // Start Method
    global Database.Querylocator start (Database.BatchableContext BC) {
        return Database.getQueryLocator('SELECT Id, StatusAmount__c, LastModifiedDate ,Feedback_Service_Case__c,Feedback_Service_Case__r.Name,Feedback_Service_Case__r.Status__c From Opportunity WHERE Feedback_Service_Case__c != null AND StatusAmount__c != NULL ORDER BY Feedback_Service_Case__c DESC');
        // Query which will be determine the scope of Records fetching the same
    }
    
    // Execute method
    global void execute (Database.BatchableContext BC, List<Opportunity> scope) {
        List<Feedback_Service_Case_Data__c> updatedRelatedRecords = new List<Feedback_Service_Case_Data__c>();
        Set<Id> parentIdSet = new Set<Id>();
        Map<String,String> statusCodeValue = new Map<String,String>();
        for(Opportunity_Status__mdt statusCode : [SELECT Id,MasterLabel,Status_Code__c FROM Opportunity_Status__mdt]){
            statusCodeValue.put(statusCode.MasterLabel,statusCode.Status_Code__c);
        }
        Integer maxStatusCode = 0;
        for (Opportunity objScope: scope) {
            if(!parentIdSet.contains(objScope.Feedback_Service_Case__c)){
               maxStatusCode = 0;            
            }
             maxStatusCode = Integer.valueOf(statusCodeValue.get(objScope.StatusAmount__c)) > maxStatusCode ? Integer.valueOf(statusCodeValue.get(objScope.StatusAmount__c)) : maxStatusCode;
            if(maxStatusCode == 2){
                objScope.Feedback_Service_Case__r.Status__c = 'CT Required';                
            }else if(maxStatusCode == 1){
                objScope.Feedback_Service_Case__r.Status__c = 'CT Recommended';
            }else if(maxStatusCode == 0){
                objScope.Feedback_Service_Case__r.Status__c = 'Not Required';
            }
            System.debug('Updated Value >>'+ objScope.Feedback_Service_Case__r.Status__c );
            if(updatedRelatedRecords.contains(objScope.Feedback_Service_Case__r)){
                System.debug('Details '+objScope.Feedback_Service_Case__r.Name+'\nIndex >> '+updatedRelatedRecords.indexOf(objScope.Feedback_Service_Case__r));
                Integer index = updatedRelatedRecords.indexOf(objScope.Feedback_Service_Case__r);
                updatedRelatedRecords.remove(index);
                
            }
            updatedRelatedRecords.add(objScope.Feedback_Service_Case__r); 
            parentIdSet.add(objScope.Feedback_Service_Case__c);
        }
        
        if (updatedRelatedRecords != null && updatedRelatedRecords.size() > 0) {
            // Check if List is empty or not
            Database.update(updatedRelatedRecords); 
            System.debug('List Size ' + updatedRelatedRecords.size());
            // Update the Records
        }
    } 
    // Finish Method
    global void finish(Database.BatchableContext BC) {}
}

Here I have updated the setting maxStatusCode value
 
if(!parentIdSet.contains(objScope.Feedback_Service_Case__c)){
               maxStatusCode = 0;            
            }
             maxStatusCode = Integer.valueOf(statusCodeValue.get(objScope.StatusAmount__c)) > maxStatusCode ? Integer.valueOf(statusCodeValue.get(objScope.StatusAmount__c)) : maxStatusCode;

Thanks,
Pavithra P

All Answers

Pavithra PeriyasamyPavithra Periyasamy
Hi Vinni,

What is the purpose to have this logic in batch class? Instead, we can have in Trigger itself right.
Which Opportunity's stage should be updated to the custom object Text field?


Thanks,
Pavithra P
vinni v cvinni v c
Hi Pavithra,

Actually we have a status field of type formula in opportunity. Based on that status field, the custom field(Text__c) is getting updated in the customobject__c through the trigger. But now, we have changed the values of the status field in the opportunity. Since it is a formula field in the opportunity the existing records got updated automatically. But, the existing records in the customobject__c have to be updated now with the new values. So for the data fix purpose, I need to write the batch class. 

Thanks in advance.
Pavithra PeriyasamyPavithra Periyasamy
Vinni,

If we have more than one Opportunity for a custom _Object__c record, which opportunity's status field should be populated to custom object record?

Thanks,
Pavithra P
vinni v cvinni v c
Hi Pavithra,

In that case, We should check the opportunity that was lastly updated and we have to populate the same status value.

Thanks in advance
Pavithra PeriyasamyPavithra Periyasamy
Vinni,

Please find the batch below,

Here I have used Feedback_Service_Case_Data__c as the parent object. 

global class BatchUpdateStatus implements Database.Batchable<sobject>{
    
    // Start Method
    global Database.Querylocator start (Database.BatchableContext BC) {
        return Database.getQueryLocator('SELECT id, FeedBackStatus__c, Feedback_Service_Case__c,Feedback_Service_Case__r.Status__c From Opportunity WHERE Feedback_Service_Case__c != null AND FeedBackStatus__c != NULL ORDER BY LastModifiedDate Desc');
        // Query which will be determine the scope of Records fetching the same
    }
    
    // Execute method
    global void execute (Database.BatchableContext BC, List<Opportunity> scope) {
        List<Feedback_Service_Case_Data__c> updatedRelatedRecords = new List<Feedback_Service_Case_Data__c>();
        Set<Id> parentIdSet = new Set<Id>();
        
        for (Opportunity objScope: scope) {
            
            if(!parentIdSet.contains(objScope.Feedback_Service_Case__c) && objScope.FeedBackStatus__c != objScope.Feedback_Service_Case__r.Status__c){
                objScope.Feedback_Service_Case__r.Status__c = objScope.FeedBackStatus__c;
                updatedRelatedRecords.add(objScope.Feedback_Service_Case__r);
            }
            //Set to hold the parent Id of Opportunity. Used to prevent iterating the child record whose parent got updated
            parentIdSet.add(objScope.Feedback_Service_Case__c);
        }
        
        if (updatedRelatedRecords != null && updatedRelatedRecords.size() > 0) {
            // Check if List is empty or not
            Database.update(updatedRelatedRecords); 
            System.debug('List Size ' + updatedRelatedRecords.size());
            // Update the Records
        }
    } 
    // Finish Method
    global void finish(Database.BatchableContext BC) {}
}


Thanks,
Pavithra P
vinni v cvinni v c
Hi Pavithra,

Thank you so much for your response.

It works as perfect as I want. I appreciate all of your help and your time in taking to resolve my query.

Thanks,
vinni v cvinni v c
Hi Pavithra,

I need your help. 

There is a change in the requirement now. 
Opportunity status(formula field) has 2 values. i.e., TestValue1(Lower Value) and TestValue2(Higher Value). 
The custom_Object__c record which is associated with 2 or more opportunities and having the above lower and higher value respectively, the 
custom_object__c status field has to be populated with TestValue2 i.e., a Higher value.

Note: If a custom object record is associated with 10 opportunities, it should get the record having the highest value(TestValue2) and populate the same to the custom object value. Otherwise, the lowest value i.e., TestValue1

Thanks in advance
Pavithra PeriyasamyPavithra Periyasamy
Hi Vinni,

Can you please explain the followings,
1. Provide more details about the formula field Opportunity.Status. What's the formula used in that?
2. Whether the Status field is populated with TestValue1(Lower Value) or TestValue2(Higher Value).


Thanks,
Pavithra P
 
vinni v cvinni v c
Hi Pavithra,

Thanks for your response.

Please find the below response to your queries.

Objects involved: Opportunity, End_User_Project.
Fields involved: Opportunity Status(Opportunity formula field) and End_User_Project_Status(End User Project text field)
Opportunity Status:-
IF(OR(Technical_Amount_USD__c < 5000000,Target__c = FALSE), "TestValue0", IF(AND(Technical_Amount_USD__c >= 5000000,Target__c = TRUE, End_User_Project__r.Capture_Team_Count__c > 0), "TestValue2", IF(AND(Technical_Amount_USD__c >= 5000000,Target__c = TRUE,
End_User_Project__r.Capture_Team_Count__c <= 0), "TestValue1", '') ) )

This is the actual formula field used in the opportunity status. Based on this opportunity status field value, the same is auto-populated in the End_User_Project_Status through the trigger. For example, if it satisfies the first condition, it will display TestVAlue0, else TestVAlue1, else TestVAlue2 in both the status fields. Since opportunity status is a formula field in the opportunity the existing records got updated automatically. But, the existing records in the End_User_Project have to be updated now with the new values.

Note: As I mentioned earlier, one End_User_Project is associated with more than 10 opportunities and if the opportunity Status has TestValue2 for multiple opportunities, we can consider one random opportunity and can be populated with this End_User_Project_Status field

For example, u can have a look into these below scenarios: 
1. If one End_User_Project is associated with more than 10 opportunities, among 10 opportunities, 4 opportunity status has TestValue0, 2 Opportunity has TestVAlue1 and 4 opportunities has TestVAlue2 -->  End_User_Project_Status field has to be populated with TestValue2 (In this case, we can pick one random opportunity from those 4 opportunities and can display the TestVAlue2 in the End_User_Project_Status)

2. If one End_User_Project is associated with more than 10 opportunities, among 10 opportunities, 5 opportunity status has TestValue0, 4 opportunity has TestValue1 and 1 opportunity has TestValue2 --> End_User_Project_Status field has to be populated with TestValue2

3. If one End_User_Project is associated with more than 10 opportunities, among 10 opportunities, 5 opportunity status has TestValue0, 5 opportunity status has TestValue1 and 0 opportunity has TestValue2 --> End_User_Project_Status field has to be populated with TestValue1

 4. If one End_User_Project is associated with more than 10 opportunities, among 10 opportunities, 10 opportunity status has TestValue0, 0 opportunity has TestValue1 and TestValue2 --> End_User_Project_Status field has to be populated with TestValue0

Kindly let me know in case of any queries. Thanks in advance.
 
Pavithra PeriyasamyPavithra Periyasamy
Hi Vinni,

Please find the updated code below,
 
global class BatchUpdateStatus implements Database.Batchable<sobject>{
    
    // Start Method
    global Database.Querylocator start (Database.BatchableContext BC) {
        return Database.getQueryLocator('SELECT Id, StatusAmount__c, LastModifiedDate ,Feedback_Service_Case__c,Feedback_Service_Case__r.Name,Feedback_Service_Case__r.Status__c From Opportunity WHERE Feedback_Service_Case__c != null AND StatusAmount__c != NULL ORDER BY Feedback_Service_Case__c DESC, StatusAmount__c DESC');
        // Query which will be determine the scope of Records fetching the same
    }
    
    // Execute method
    global void execute (Database.BatchableContext BC, List<Opportunity> scope) {
        List<Feedback_Service_Case_Data__c> updatedRelatedRecords = new List<Feedback_Service_Case_Data__c>();
        Set<Id> parentIdSet = new Set<Id>();
        
        for (Opportunity objScope: scope) {
            
            if(!parentIdSet.contains(objScope.Feedback_Service_Case__c) && objScope.StatusAmount__c != objScope.Feedback_Service_Case__r.Status__c){
                objScope.Feedback_Service_Case__r.Status__c = objScope.StatusAmount__c;
                updatedRelatedRecords.add(objScope.Feedback_Service_Case__r);
            }
            //Set to hold the parent Id of Opportunity. Used to prevent iterating the child record whose parent got updated
            parentIdSet.add(objScope.Feedback_Service_Case__c);
        }
        
        if (updatedRelatedRecords != null && updatedRelatedRecords.size() > 0) {
            // Check if List is empty or not
            Database.update(updatedRelatedRecords); 
            System.debug('List Size ' + updatedRelatedRecords.size());
            // Update the Records
        }
    } 
    // Finish Method
    global void finish(Database.BatchableContext BC) {}
}

Here I have modified the query and execute method.

Thanks,
Pavithra P
vinni v cvinni v c
Hi Pavithra,

I'm getting "Comparison arguments must be compatible types: Decimal, String" error in the below line.
objScope.Feedback_Service_Case__r.Status__c = objScope.StatusAmount__c;

Also, Feedback_Service_Case__r.Status__c is a text field while StatusAmount__c is a number field.

Could you please let me know the logic u r trying to implement here??

Thanks in advance
Pavithra PeriyasamyPavithra Periyasamy
Hi Vinni,

I have used StatusAmount__c Formula field with Text data type. If you have used as Number data type, then you typecast as follow as,
objScope.Feedback_Service_Case__r.Status__c = String.valueOf(objScope.StatusAmount__c)

Thanks,
Pavithra P
vinni v cvinni v c
Also, Please find the below existing trigger which is working fine for the newly created records.

Set<Id> setEUPId = new Set<Id>();
Map<Id,List<Opportunity>> mapEUPIdAndOpportunity = new Map<Id,List<Opportunity>>();
List<End_User_Project__c> lstEndUsr = new List<End_User_Project__c>();

for(Opportunity opp: sObjList ){
if(opp.End_User_Project__c != NULL){
setEUPId.add(opp.End_User_Project__c);
}
}
if (!setEUPId.isEmpty()) {
for(Opportunity op : [SELECT Id,Opportunity_Status__c,End_User_Project__c
FROM Opportunity where End_User_Project__c in: setEUPId LIMIT 10000] )
{
List<Opportunity> lstop = new List<Opportunity>();
lstop.add(op);

if (mapEUPIdAndOpportunity.containsKey(op.End_User_Project__c))
{
mapEUPIdAndOpportunity.get(op.End_User_Project__c).add(op);
}
else
{
mapEUPIdAndOpportunity.put(op.End_User_Project__c, lstop);
}

}
}

for(String endusrid : setEUPId)
{
String endUsrStatus = '';
Boolean isNotRequired = False;
Boolean isCTRecommended = False;
Boolean isCTCreated = False;
List<Opportunity> lstopty = new List<Opportunity>();
if(mapEUPIdAndOpportunity.containsKey(endusrid)){
lstopty = mapEUPIdAndOpportunity.get(endusrid);
}
if(lstopty.size() >0){
for(Opportunity opp : lstopty)
{
if(null!=opp.Opportunity_Status__c&&opp.Opportunity_Status__c.equals(System.label.TestValue0))
{
isNotRequired = True;
}
if(null!=opp.Opportunity_Status__c&&opp.Opportunity_Status__c.equals(System.label.TestValue1))
{
isCTRecommended = True;
}
if(null!=opp.Opportunity_Status__c&&opp.Opportunity_Status__c.equals(System.label.TestValue2))
{
isCTCreated = True;
}
}
}
if(isCTCreated)
{
endUsrStatus = System.label.TestValue2;
}
else if(isCTRecommended)
{
endUsrStatus = System.label.TestValue1;
}
else if(isNotRequired)
{
endUsrStatus = System.label.TestValue0;
}
else
{
endUsrStatus = System.label.TestValue0;
}

End_User_Project__c edUsrRec = new End_User_Project__c();
edUsrRec.Id = endusrid;
edUsrRec.End_User_Project_Status__c= endUsrStatus;
lstEndUsr.add(edUsrRec);
}

if(lstEndUsr.size() > 0)
{
Database.update(lstEndUsr);
}
}
vinni v cvinni v c
Hi Pavithra,

Sorry for not mentioning this before.
Since StatusAmount__c is a formula field of type text, will ORDER BY StatusAmount__c DESC work??

Thanks in advance.
vinni v cvinni v c
Also... those TestVAlue1, TestValue2 and TestValue3 holds "Not Required", "CT Required", "CT Recommended" values respectively.
Pavithra PeriyasamyPavithra Periyasamy
Ohh I thought it will be holding 0,1,2. If it is holding "Not Required", "CT Required", "CT Recommended" values. Which one do we want to consider first?
vinni v cvinni v c
yeah.. it was my fault. I haven't mentioned it properly. Sorry about that.

"Not Required" - 0
"CT Recommended" - 1
"CT Required" - 2
Since 2 is the highest value, we should display 2(CT Required) has to display.
If the "CT Required" value holding opportunity is not associated, then We have to check if any "CT Recommened" value is there. If "CT Recommended" value holding opportunity is also not associated, then "NOT Required"  will be the only value remained. So it has to display

 
Pavithra PeriyasamyPavithra Periyasamy
Vinni,

It's ok No Problem. I have created a custom metadata type and created 3 records in that 
User-added image

Please find the updated code below,
 
global class BatchUpdateStatus implements Database.Batchable<sobject>{
    
    // Start Method
    global Database.Querylocator start (Database.BatchableContext BC) {
        return Database.getQueryLocator('SELECT Id, StatusAmount__c, LastModifiedDate ,Feedback_Service_Case__c,Feedback_Service_Case__r.Name,Feedback_Service_Case__r.Status__c From Opportunity WHERE Feedback_Service_Case__c != null AND StatusAmount__c != NULL ORDER BY Feedback_Service_Case__c DESC');
        // Query which will be determine the scope of Records fetching the same
    }
    
    // Execute method
    global void execute (Database.BatchableContext BC, List<Opportunity> scope) {
        List<Feedback_Service_Case_Data__c> updatedRelatedRecords = new List<Feedback_Service_Case_Data__c>();
        Set<Id> parentIdSet = new Set<Id>();
        Map<String,String> statusCodeValue = new Map<String,String>();
        for(Opportunity_Status__mdt statusCode : [SELECT Id,MasterLabel,Status_Code__c FROM Opportunity_Status__mdt]){
            statusCodeValue.put(statusCode.MasterLabel,statusCode.Status_Code__c);
        }
        Integer maxStatusCode = 0;
        for (Opportunity objScope: scope) {
            if(parentIdSet.contains(objScope.Feedback_Service_Case__c)){
                maxStatusCode = Integer.valueOf(statusCodeValue.get(objScope.StatusAmount__c)) > maxStatusCode ? Integer.valueOf(statusCodeValue.get(objScope.StatusAmount__c)) : maxStatusCode;
            
            }else{
                maxStatusCode = 0;
            }
            if(maxStatusCode == 2){
                objScope.Feedback_Service_Case__r.Status__c = 'CT Required';                
            }else if(maxStatusCode == 1){
                objScope.Feedback_Service_Case__r.Status__c = 'CT Recommended';
            }else if(maxStatusCode == 0){
                objScope.Feedback_Service_Case__r.Status__c = 'Not Required';
            }
            System.debug('Updated Value >>'+ objScope.Feedback_Service_Case__r.Status__c );
            if(updatedRelatedRecords.contains(objScope.Feedback_Service_Case__r)){
                System.debug('Details '+objScope.Feedback_Service_Case__r.Name+'\nIndex >> '+updatedRelatedRecords.indexOf(objScope.Feedback_Service_Case__r));
                Integer index = updatedRelatedRecords.indexOf(objScope.Feedback_Service_Case__r);
                updatedRelatedRecords.remove(index);
                
            }
            updatedRelatedRecords.add(objScope.Feedback_Service_Case__r); 
            parentIdSet.add(objScope.Feedback_Service_Case__c);
        }
        
        if (updatedRelatedRecords != null && updatedRelatedRecords.size() > 0) {
            // Check if List is empty or not
            Database.update(updatedRelatedRecords); 
            System.debug('List Size ' + updatedRelatedRecords.size());
            // Update the Records
        }
    } 
    // Finish Method
    global void finish(Database.BatchableContext BC) {}
}

Thanks,
Pavithra P​​​​​​​
vinni v cvinni v c
Hi Pavithra,

I have tested this code after implementing my changes. It is not working fine as expected. Every time, it is returning status code 0 and the same value is getting updated even though we have opportunities with higher values. 

Thanks.
Pavithra PeriyasamyPavithra Periyasamy
Vinni,

Have you created the metadata with 3 values?

Thanks,
vinni v cvinni v c
Yes, Pavithra.

User-added image
Pavithra PeriyasamyPavithra Periyasamy
Can you please share your code

Thanks,
Pavithra P
vinni v cvinni v c
Please find my code below.

global class CaptureTeamStatusDataFix implements Database.Batchable<sobject>{
    
    // Start Method
    global Database.Querylocator start (Database.BatchableContext BC) {
        return Database.getQueryLocator('SELECT Id, Capture_Team_Indicator_Status__c, LastModifiedDate ,End_User_Project__c,End_User_Project__r.Name,End_User_Project__r.Capture_Team_Status__c From Opportunity WHERE End_User_Project__c = \'a0V0O00000JG4NBUA1\' AND Capture_Team_Indicator_Status__c != NULL ORDER BY End_User_Project__c DESC');
        // Query which will be determine the scope of Records fetching the same
    }
    
    // Execute method
    global void execute (Database.BatchableContext BC, List<Opportunity> scope) {
        List<End_User_Project__c> updatedRelatedRecords = new List<End_User_Project__c>();
        Set<Id> parentIdSet = new Set<Id>();
        Map<String,String> statusCodeValue = new Map<String,String>();
        for(Opportunity_Status__mdt statusCode : [SELECT Id,MasterLabel,Status_Code__c FROM Opportunity_Status__mdt]){
            statusCodeValue.put(statusCode.MasterLabel,statusCode.Status_Code__c);
        }
        Integer maxStatusCode = 0;
        for (Opportunity objScope: scope) {
            if(parentIdSet.contains(objScope.End_User_Project__c)){
                maxStatusCode = Integer.valueOf(statusCodeValue.get(objScope.Capture_Team_Indicator_Status__c)) > maxStatusCode ? Integer.valueOf(statusCodeValue.get(objScope.Capture_Team_Indicator_Status__c)) : maxStatusCode;
            }else{
                maxStatusCode = 0;
            }
            if(maxStatusCode == 2){
                objScope.End_User_Project__r.Capture_Team_Status__c = 'CT Created';                
            }else if(maxStatusCode == 1){
                objScope.End_User_Project__r.Capture_Team_Status__c = 'CT Recommended';
            }else if(maxStatusCode == 0){
                objScope.End_User_Project__r.Capture_Team_Status__c = 'No CT Required';
            }
            System.debug('Updated Value >>'+ objScope.End_User_Project__r.Capture_Team_Status__c );
            if(updatedRelatedRecords.contains(objScope.End_User_Project__r)){
                System.debug('Details '+objScope.End_User_Project__r.Name+'\nIndex >> '+updatedRelatedRecords.indexOf(objScope.End_User_Project__r));
                Integer index = updatedRelatedRecords.indexOf(objScope.End_User_Project__r);
                updatedRelatedRecords.remove(index);
                
            }
            updatedRelatedRecords.add(objScope.End_User_Project__r); 
            parentIdSet.add(objScope.End_User_Project__c);
        }
        
        if (updatedRelatedRecords != null && updatedRelatedRecords.size() > 0) {
            // Check if List is empty or not
            Database.update(updatedRelatedRecords); 
            System.debug('List Size ' + updatedRelatedRecords.size());
            // Update the Records
        }
    } 
    // Finish Method
    global void finish(Database.BatchableContext BC) {}
}

Replaced with my original fields
vinni v cvinni v c
For testing purpose, I hardcoded a record ID 
Pavithra PeriyasamyPavithra Periyasamy
Vinni,

For me, it is updated correctly, 

User-added image
vinni v cvinni v c
Hi Pavithra,

I validated one more record now. Still, I'm not getting the expected result. Is something wrong in my code?? 
Pavithra PeriyasamyPavithra Periyasamy
No Vinni. Can you please share the result for the below Query.
vinni v cvinni v c
User-added image
Pavithra PeriyasamyPavithra Periyasamy
Sorry Vinni,

Can you please share the result of the below query,

SELECT Id,MasterLabel,Status_Code__c FROM Opportunity_Status__mdt

 
vinni v cvinni v c
User-added image
Pavithra PeriyasamyPavithra Periyasamy
Vinni,

It is due to the logic that I have missed. Sorry for that,
 
global class BatchUpdateStatus implements Database.Batchable<sobject>{
    
    // Start Method
    global Database.Querylocator start (Database.BatchableContext BC) {
        return Database.getQueryLocator('SELECT Id, StatusAmount__c, LastModifiedDate ,Feedback_Service_Case__c,Feedback_Service_Case__r.Name,Feedback_Service_Case__r.Status__c From Opportunity WHERE Feedback_Service_Case__c != null AND StatusAmount__c != NULL ORDER BY Feedback_Service_Case__c DESC');
        // Query which will be determine the scope of Records fetching the same
    }
    
    // Execute method
    global void execute (Database.BatchableContext BC, List<Opportunity> scope) {
        List<Feedback_Service_Case_Data__c> updatedRelatedRecords = new List<Feedback_Service_Case_Data__c>();
        Set<Id> parentIdSet = new Set<Id>();
        Map<String,String> statusCodeValue = new Map<String,String>();
        for(Opportunity_Status__mdt statusCode : [SELECT Id,MasterLabel,Status_Code__c FROM Opportunity_Status__mdt]){
            statusCodeValue.put(statusCode.MasterLabel,statusCode.Status_Code__c);
        }
        Integer maxStatusCode = 0;
        for (Opportunity objScope: scope) {
            if(!parentIdSet.contains(objScope.Feedback_Service_Case__c)){
               maxStatusCode = 0;            
            }
             maxStatusCode = Integer.valueOf(statusCodeValue.get(objScope.StatusAmount__c)) > maxStatusCode ? Integer.valueOf(statusCodeValue.get(objScope.StatusAmount__c)) : maxStatusCode;
            if(maxStatusCode == 2){
                objScope.Feedback_Service_Case__r.Status__c = 'CT Required';                
            }else if(maxStatusCode == 1){
                objScope.Feedback_Service_Case__r.Status__c = 'CT Recommended';
            }else if(maxStatusCode == 0){
                objScope.Feedback_Service_Case__r.Status__c = 'Not Required';
            }
            System.debug('Updated Value >>'+ objScope.Feedback_Service_Case__r.Status__c );
            if(updatedRelatedRecords.contains(objScope.Feedback_Service_Case__r)){
                System.debug('Details '+objScope.Feedback_Service_Case__r.Name+'\nIndex >> '+updatedRelatedRecords.indexOf(objScope.Feedback_Service_Case__r));
                Integer index = updatedRelatedRecords.indexOf(objScope.Feedback_Service_Case__r);
                updatedRelatedRecords.remove(index);
                
            }
            updatedRelatedRecords.add(objScope.Feedback_Service_Case__r); 
            parentIdSet.add(objScope.Feedback_Service_Case__c);
        }
        
        if (updatedRelatedRecords != null && updatedRelatedRecords.size() > 0) {
            // Check if List is empty or not
            Database.update(updatedRelatedRecords); 
            System.debug('List Size ' + updatedRelatedRecords.size());
            // Update the Records
        }
    } 
    // Finish Method
    global void finish(Database.BatchableContext BC) {}
}

Here I have updated the setting maxStatusCode value
 
if(!parentIdSet.contains(objScope.Feedback_Service_Case__c)){
               maxStatusCode = 0;            
            }
             maxStatusCode = Integer.valueOf(statusCodeValue.get(objScope.StatusAmount__c)) > maxStatusCode ? Integer.valueOf(statusCodeValue.get(objScope.StatusAmount__c)) : maxStatusCode;

Thanks,
Pavithra P
This was selected as the best answer
vinni v cvinni v c
Thank you so much Pavithra, it is working fine now.

Thanks a lot for your time fixing on it.
vinni v cvinni v c
Hi Pavithra,

I have written a test class for the above batch class. It has 48% code coverage now. But, the test class is failing saying that Argument cannot be a null exception. Please find the below test class and kindly let me know what am I doing wrong.

@isTest
public class CaptureTeamStatusDataFixTest {      
    private static final String OPP_NAME = 'Test Opp';
    
    static testmethod void updateCaptureTeamStatus() { 
        
        Account acc = (Account) DataLoaderUtil.createObject('Account');         
        Product_Group__c pg = MasterdataManager.createProductGroup();
        ABB_Location__c location = MasterdataManager.createABBLocation();                        
        
        List<End_User_Project__c> EUPList = new List<End_User_Project__c>();
        End_User_Project__c EUP = new End_User_Project__c();
        EUP.Account_End_User__c = acc.id;
        EUP.Name = 'TEST End User Project';
        EUP.End_User_Project_Status__c = 'Announced';
        EUP.Industry_Usage_Level_1__c ='UT';
        EUP.Industry_Usage_Level_2__c ='UT.4';
        EUP.Industry_Usage_Level_3__c = 'UT.4.8';
        EUP.Capture_Team_Status__c ='No CT Required';
        EUP.End_User_Project_Site_Country__c = 'AD';
        EUPList.add(EUP);
        insert EUPList;
                
        List<Opportunity> opportunity = new List<Opportunity>();
        Opportunity opp = new Opportunity();
        opp.name = OPP_NAME;
        opp.AccoutId = acc.id;
        opp.StageName = 'Prospecting';
        opp.Product_Group__c = pg.id;
        opp.ABB_Location__c = location.id;
        opp.Target__c = false;
        opp.End_User_Project__c = EUPList[0].id;
        opportunity.add(opp);
        insert opportunity;
        
        Test.startTest();
       /* CaptureTeamStatusDataFix batchClass = new CaptureTeamStatusDataFix();
        ID batchProcessId = Database.executeBatch(batchClass);
        Database.BatchableContext bc;   
        batchClass.execute(null, opportunity);*/        
        
        CaptureTeamStatusDataFix batchClass = new CaptureTeamStatusDataFix();
        Database.BatchableContext bc;
        Database.QueryLocator ql = batchClass.start(null);        
        batchClass.execute(null, opportunity);
        batchClass.finish(null);
                
        Test.stopTest();
    }
}

Thanks in Advance.
Pavithra PeriyasamyPavithra Periyasamy
Vinni,

Can you please post the error in detail like in which line it is failing?

Thanks,
Pavithra P
vinni v cvinni v c
Hi Pavithra,

Please find the error message below.
System.NullPointerException: Argument cannot be null.
Class.CaptureTeamStatusDataFix.execute: line 22, column 1
Class.CaptureTeamStatusDataFixTest.updateCaptureTeamStatus: line 48, column 1


In the batch class, line 22 has the below line.
maxStatusCode = Integer.valueOf(statusCodeValue.get(objScope.Capture_Team_Indicator_Status__c)) > maxStatusCode ? Integer.valueOf(statusCodeValue.get(objScope.Capture_Team_Indicator_Status__c)) : maxStatusCode;

As per my knowledge, I think test data is no need for the custom metadata right?? Please help me.

Thanks in advance
Pavithra PeriyasamyPavithra Periyasamy

Vinni,

Yes you are correct we don't need to insert test data for custom metadata. Can you please check whether objScope.Capture_Team_Indicator_Status__c has value.

Thanks!!

vinni v cvinni v c
Hi Pavithra,

Since objScope.Capture_Team_Indicator_Status__c is a formula field I am not passing value directly. But I have given Target__c =false which auto populate the objScope.Capture_Team_Indicator_Status__c field with No CT Required value as it satisfied one of the conditions mentioned in the formula field.

Thanks in advance
Pavithra PeriyasamyPavithra Periyasamy
Vinni, 

Seems like the scope is not getting value in Execute method. Can you please try as follow as,

In test class, instead of calling the execute method and finish method explicitly we can call 
BatchUpdateStatus objClass = new BatchUpdateStatus();
Database.executeBatch (objClass);

Thanks!!
 
vinni v cvinni v c
Hi Pavithra,

I tried that already... But the execute method is not at all covering since the scope has no value. PFB the screenshot for reference.

User-added image
So, I called the execute method explicitly.

Thanks in advance.
Pavithra PeriyasamyPavithra Periyasamy
Vinni,

It means no records were retrieved in the query. We need to create records by matching the WHERE condition in the query

Thanks!!
vinni v cvinni v c
Hi Pavithra,

​​​​​​Since I have scheduled the batch class, all records got updated. There are no records present in the dev org matching the where condition. If I push this code to higher environments...will it get pass??

Thanks
Pavithra PeriyasamyPavithra Periyasamy
Vinni,

Below is the sample test class,
 
@isTest
public class TestBatchUpdateStatus {
    @testSetup static void methodName() {
         Feedback_Service_Case_Data__c feedbackRecord = new Feedback_Service_Case_Data__c(Capture_Team_Count__c = 1, Feedback_Submitted_Date__c = System.today());
         insert feedbackRecord;
        
        Opportunity oppRecord = new Opportunity (Name = 'Test Opp', Amount=500,Feedback_Service_Case__c = feedbackRecord.Id, Needs_Waiver__c = true,StageName = 'Demo',CloseDate = System.today() + 5);
        insert oppRecord;
    }
    
    @isTest
    public static void testBatchClass(){
        Test.startTest();
            BatchUpdateStatus objClass = new BatchUpdateStatus();
			Database.executeBatch (objClass);
        
            
        Test.stopTest();
    }

}

In the above class, you find that we have @testSetup - which will act as a database when the test class runs. If we have any query it will use the data from @testSetup. Can you try this?

Thanks,
vinni v cvinni v c
Hi Pavithra,

I tried the above method. But, no luck. It has the same coverage as I shared in the above screenshot.

Thanks in advance
Pavithra PeriyasamyPavithra Periyasamy

Vinni,

Is that test class fixed?

 

vinni v cvinni v c
Hi Pavithra,

Thanks for your reply.
The test class is not yet fixed.
Earlier I had the same issue. At that time, I called execute method explicitly and it got fixed.
But now, I am not sure what the issue is with my test class.

Regards,
Vinodhini
Pavithra PeriyasamyPavithra Periyasamy
Vinni,

Instead of having  opp.End_User_Project__c = EUPList[0].id; you can query the inserted End_User_Project__c and map to the opportunity.

Thanks!!
vinni v cvinni v c
Hi Pavithra,

I have modified the batch class by adding a few changes in my query. Now, it is working fine and I have 85% of coverage.

Thanks & Regards,
Vinni