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
Vetriselvan ManoharanVetriselvan Manoharan 

Bulk update validation via data loader

Hi,

While updating data via data laoder and it will trigger the operation. If the 3rd record enters in custom validation condition, the update fails and the all the records displaying same validation message. Please clear it.

Thanks,
Vetri
Best Answer chosen by Vetriselvan Manoharan
Swayam  AroraSwayam Arora
Sample code:-
Map<String, List<Object>> errorRecords = new Map<String, List<Object>>();
if(condition1 met) {
        acc.addError(' MyError1');   //Don't throw error here (i.e. don't use this line here)      
        if(errorRecords.contains('MyError1')) {
            errorRecords.get('MyError1').add(acc);
        } else {
            errorRecords.put('MyError1', new List<Object>(acc));
        }
        
} else if(condition2 met) {
        acc.addError(' MyError2');   //Don't throw error here (i.e. don't use this line here)       
        if(errorRecords.contains('MyError2')) {
            errorRecords.get('MyError2').add(acc);
        } else {
            errorRecords.put('MyError2', new List<Object>(acc));
        }
}
.
.
.else if(condition10 met) {
        acc.addError(' MyError10');   //Don't throw error here (i.e. don't use this line here)       
        if(errorRecords.contains('MyError10')) {
            errorRecords.get('MyError10').add(acc);
        } else {
            errorRecords.put('MyError10', new List<Object>(acc));
        }
} else {

}
In the end after everything is done
for(String str : errorRecords.keySet) {
    for(Object o : errorRecords.get(str)) {
        o.addError(str);
    }
}

All Answers

PratikPratik (Salesforce Developers) 
Hi vetriselvan,

You can inactivate the validation rule and the workflow too till the time you upload the data so data will get inserted without any error. (If you have done the data quality checks). Once you are done with bulk upload, you can activate the validation & workflow or even the triggers.

Thanks,
Pratik
Vishal NegandhiVishal Negandhi
Ideally I would prefer not to insert such data. If I have a validation rule in my system, it is because I want my data that way. Ideally, you should be correcting your data and then inserting it back.
Swayam  AroraSwayam Arora
Hi Vetri,

When for the 3rd record you are getting error, transaction stops there itself and that is why for all the records it is showing same error. One thing can be done for this.
All the records for which you are throwing custom validation error ( one of the example in your case is the 3rd record), don't throw the error message then and there, collect them in a list and when all the other records are executed then iterate throw the created list and throw error for those records.

Sample code:-
List<Object> errorRecords = new List<Object>();
if(condition met) {
        acc.addError(' MyError');   //Don't throw error here (i.e. don't use this line here)
        errorRecords.add(acc);
}
In the end after everything is done

for(Object o : errorRecords) {
        o.addError('MyError');
}

Let me know if you need further help.

Please close the thread marking this answer as Best Answer if it really helped. Closing the thread help others finding the correct answer.

Regards,
Swayam Arora


 
Swayam  AroraSwayam Arora
Sample code:-
Map<String, List<Object>> errorRecords = new Map<String, List<Object>>();
if(condition1 met) {
        acc.addError(' MyError1');   //Don't throw error here (i.e. don't use this line here)      
        if(errorRecords.contains('MyError1')) {
            errorRecords.get('MyError1').add(acc);
        } else {
            errorRecords.put('MyError1', new List<Object>(acc));
        }
        
} else if(condition2 met) {
        acc.addError(' MyError2');   //Don't throw error here (i.e. don't use this line here)       
        if(errorRecords.contains('MyError2')) {
            errorRecords.get('MyError2').add(acc);
        } else {
            errorRecords.put('MyError2', new List<Object>(acc));
        }
}
.
.
.else if(condition10 met) {
        acc.addError(' MyError10');   //Don't throw error here (i.e. don't use this line here)       
        if(errorRecords.contains('MyError10')) {
            errorRecords.get('MyError10').add(acc);
        } else {
            errorRecords.put('MyError10', new List<Object>(acc));
        }
} else {

}
In the end after everything is done
for(String str : errorRecords.keySet) {
    for(Object o : errorRecords.get(str)) {
        o.addError(str);
    }
}
This was selected as the best answer
Vetriselvan ManoharanVetriselvan Manoharan

Hi Swayam,

I have more than 10 validations. How could I display the messages for corresponding record? Also it say Sobject doesn't allow errors.. Thanks for the help

Thanks,
Vetri
Vetriselvan ManoharanVetriselvan Manoharan
Hi,

List<Account> accountsToUpdate = new List<Account>(); 
       List<Account> account = [SELECT Id, Name, Program_Status__c, Discount_Type__c,(SELECT Id, Account.Program_Status__c, Name, Auth_Payment_Status__c, StageName, Account.ParentId, DiscountTotal__c,Coupon_Codes__c  from Opportunities ORDER BY CreatedDate DESC LIMIT 1) FROM Account WHERE Id in :ids];
       List<Opportunity> oppMap = new List<Opportunity>{};
       List<Account> errorRecords = new List<Account>();
       for(Account record: account)
       {
               Account accsError = accountNewMap.get(record.Id); 
               programStatusOld = accountOldMap.get(record.Id).Program_Status__c;
            programStatusNew = record.Program_Status__c;
               for(Opportunity opps : record.Opportunities)
               { 
                   if(opps.AccountId == record.Id)
                   {
                    if(programStatusNew != 'New Customer' && programStatusNew != 'Returning Customer')
                    {
                        runStatusUpdate = true;
                        if(opps.Account.Program_Status__c == OnHold || opps.Account.Program_Status__c == HoldwithResume)
                        { 
                            opps.StageName = 'On Hold';
                        }
                        else if(opps.Account.Program_Status__c == Finished || opps.Account.Program_Status__c == 'Never Started')
                        {
                            opps.StageName = 'Closed Lost';
                            opps.CloseDate = Date.today();
                        } 
                        else if((programStatusOld != programStatusNew) && programStatusNew == 'On Program' && (programStatusOld == 'New Customer' || programStatusOld == 'Returning Customer') && StatusController.runClosedWon != true)
                        {
                            runStatusUpdate = false;
                            if((record.Discount_Type__c == 'Date Specified' || record.Discount_Type__c == 'Life Time') &&  opps.Coupon_Codes__c == null)
                            {
                                errorRecords.add(record);
                            }
                            else
                            {
                                opps.StageName = 'Closed Won';
                                opps.CloseDate = Date.today();
                            }
                        }
                        else if(opps.Account.Program_Status__c == OnProgram) 
                        {
                            opps.StageName = 'On Program';
                        }
                        oppMap.Add(opps); 
                    }
                }                    
               } 
       } 
       if(oppMap.size() > 0)
       {
               update oppMap;
       }
       for(Account o : errorRecords) 
       {
            Account oAccount = accountNewMap.get(o.Id);
            oAccount.addError('MyError'); 
       }

Still it is not updating the opportunities, but displaying error messages correctly via data loader. Please!!!

Thanks,
Vetri
Vetriselvan ManoharanVetriselvan Manoharan
Are you sure because acc is looped through for loop and it can't be added as list. 'Save error: Incompatible value type Account for Map<String,List<Account>>'
Vetriselvan ManoharanVetriselvan Manoharan
I done it. Error message works perfectly, but update operation is not working,.
Swayam  AroraSwayam Arora
This is because when any System or Manual exception occurs then all the DML exceptions rolls back. So I think because we are throwing manual errors update is rolling back.

What is the need of throwing exception? I think we can handle using Transactiong in Salesforce, but I am not sure about that.
Swayam  AroraSwayam Arora
just try one more thing, try replacing  o.addError(str); with o.addError(str, true); and  o.addError(str, false); and see if any one of these works.
Swayam  AroraSwayam Arora
Sorry, this will not help. Give me some time to think over it and you also think if you can put any solution other then throwing exception.
Vetriselvan ManoharanVetriselvan Manoharan
i tried something like this

Database.DMLOptions dmo = new Database.DMLOptions();
dmo.optAllOrNone = false;
Database.Update(oppMap, dmo);

optAllorNone means allows partial transaction, commits partially.. but doesn't work..

https://www.salesforce.com/us/developer/docs/apexcode/Content/apex_methods_system_database_dmloptions.htm
Vetriselvan ManoharanVetriselvan Manoharan
Can't able to get solution..
Swayam  AroraSwayam Arora
Ok, there is one solution but I am not sure if it is a good solution or not.

Have you heard about asynchronous methods? You will have to use them to update the Opportunities.
Swayam  AroraSwayam Arora
Is it done?
 
Vetriselvan ManoharanVetriselvan Manoharan
nope.. But showing error message for corresponding records works perfect, but addError method not allowing to update opportunites. I am facing porblems because of it. :(
Swayam  AroraSwayam Arora
Don't worry that will also work. Have you heard about Asynchronous Methods?
If not, go through below link
http://www.greytrix.com/blogs/salesforce/2014/10/30/invoke-future-methods-through-apex-trigger-for-web-service-callout/

Inspite of updating Opportunities in the trigger you need to call a class method and pass the opportunity list that will call the future(asynchronous) method (pass opportunity list to this method also) where you will have to update the Opportuities.

Let me know if you face any problem.
Vetriselvan ManoharanVetriselvan Manoharan
I dont have idea on this and I can't be to create a class with future annotation. It is getting difficult. Have to look for some easier solution. :( :(
Swayam  AroraSwayam Arora
Not a problem, I can help you with this. It will be a learning for you also.
If you really want some easier solution then think over it and I will also try to think on this.
Vetriselvan ManoharanVetriselvan Manoharan
Thank you Swayam!!!
Vetriselvan ManoharanVetriselvan Manoharan
Still no luck
Swayam  AroraSwayam Arora
Create a class Util:-
public class Util {
	@future
	public void updateOpportunities(List<Opportunities> opps) {
		update opps;
	}
}


Now update your code:-

if(oppMap.size() > 0)
       {              
                Util.updateOpportunities(oppMap);
       }
Vetriselvan ManoharanVetriselvan Manoharan
Save error: Unsupported parameter type List<Opportunity> salesforce. Getting this error. Hope sboject cannot be passed as parameter
Vetriselvan ManoharanVetriselvan Manoharan
I tried like this

Calling the future method from class

for (Opportunity records:oppMap)
            {  
               updateOpps.add(records.Id);
               updateOppsStage.add(records.StageName);
            }
            UtilityClass.updateOpportunities(updateOpps, updateOppsStage);

@future
    public static void updateOpportunities(Set<Id> oppIds, Set<String> oppStage) { 
            
        List<Opportunity> oppsToUpdate = new List<Opportunity>();
        Opportunity oppsSave = new Opportunity();
        // iterate through the list of accounts to process
        for (Opportunity o : [Select Id, Name, StageName From Opportunity where Id IN :oppIds]) 
        {            
            if(o.StageName == 'Closed Won')
            {
                oppsSave.StageName = 'Closed Won';
                oppsSave.CloseDate = Date.today();
            }
            else
            {
                oppsSave.StageName = o.StageName;
            }
            oppsToUpdate.add(oppsSave);
        }
        update oppsToUpdate;
    }

I didn't get no erros, but the method is not called and DML operations doesn't take place
Vetriselvan ManoharanVetriselvan Manoharan
If I see the APEX jobs, it says the status as completed. But the DML not performed. User-added image

User-added image
Swayam  AroraSwayam Arora
Map<Id, String> updateOppsMap = new <Id, String>(); 
for (Opportunity records:oppMap)
            {  

               updateOppsMap.put(records.Id, records.StageName);
            }
            UtilityClass.updateOpportunities(updateOppsMap);

@future
    public static void updateOpportunities(Map<Id, String> oppMap) { 
            
        List<Opportunity> oppsToUpdate = [Select Id, Name, StageName From Opportunity where Id IN :oppMap.keySet()];
        // iterate through the list of accounts to process
        for (Opportunity o : oppsToUpdate) 
        {            
            for(Id oppId : oppMap.keySet()) {
                    if(o.StageName == 'Closed Won') {
                            o.CloseDate = Date.today();                  
                    }
            }                        
        }
        update oppsToUpdate;
    }


If you are not using the Stage values in the future method there is no use of passing it. Instead you can just send List<Id>.
Vetriselvan ManoharanVetriselvan Manoharan
Future call is successful bud, but the DML doesn't work, the update doesn't take place :(
Swayam  AroraSwayam Arora
Using my code also DML is not happening??
Vetriselvan ManoharanVetriselvan Manoharan
Yes swayam.. I saw the APEX jobs but it is completed by but update didn't takes place. :(
Vetriselvan ManoharanVetriselvan Manoharan
Thanks swayam.. It worked.. the problem is with stagename condition.. :)
Swayam  AroraSwayam Arora
Welcome and well done Vetri.
Vetriselvan ManoharanVetriselvan Manoharan
Is there any problem in my code. Can you please check and let me know.

Opportunity oppsUpdate = new Opportunity();  
       List<Account> account = [SELECT Id, Name, Program_Status__c, Discount_Type__c, ParentId, (SELECT Id, Account.Program_Status__c, Name, Auth_Payment_Status__c, StageName, Account.ParentId, DiscountTotal__c,Coupon_Codes__c,ShippingTotal__c  from Opportunities ORDER BY CreatedDate DESC LIMIT 1) FROM Account WHERE Id in :ids]; 
       for(Account records: account)
       {
           for(Opportunity opp : records.Opportunities) {
                   oppsID.add(opp.Id);
            }
       }
       AggregateResult[] shippingCounts = [SELECT Count(Id) c, OpportunityId FROM OpportunityLineItem WHERE OpportunityId in :oppsID and (pricebookEntry.product2.ProductCode = 'FedEx') GROUP BY OpportunityId]; 
       AggregateResult[] discountCounts = [SELECT Count(Id) c, OpportunityId FROM OpportunityLineItem WHERE OpportunityId in :oppsID and (pricebookEntry.product2.ProductCode = 'DiscountAmount') GROUP BY OpportunityId];
       AggregateResult[] oldProductCounts = [SELECT Count(Id) c, OpportunityId FROM OpportunityLineItem WHERE OpportunityId in :oppsID and ((pricebookEntry.product2.ProductCode = 'EZShipping') OR (pricebookEntry.product2.ProductCode = 'EZDiscount')) GROUP BY OpportunityId];
       for(Account record: account)
       {
               programStatusOld = accountOldMap.get(record.Id).Program_Status__c;
            programStatusNew = record.Program_Status__c;
               for(Opportunity opps : record.Opportunities)
               { 
                   if(programStatusNew != 'New Customer' && programStatusNew != 'Returning Customer')
                {
                       if(opps.AccountId == record.Id)
                       {                    
                        oppsUpdate.Id = opps.Id;
                        if(opps.Account.Program_Status__c == 'On Hold' || opps.Account.Program_Status__c == 'Hold with Resume Date')
                        {                             
                            oppsUpdate.StageName = 'On Hold'; 
                        }
                        else if(opps.Account.Program_Status__c == 'Finished' || opps.Account.Program_Status__c == 'Never Started')
                        {
                            oppsUpdate.StageName = 'Closed Lost';
                            oppsUpdate.CloseDate = Date.today();
                        } 
                        else if((programStatusOld != programStatusNew) && programStatusNew == 'On Program' && (programStatusOld == 'New Customer' || programStatusOld == 'Returning Customer') && StatusController.runClosedWon != true)
                        {
                                                      
                            if((record.Discount_Type__c == 'Date Specified' || record.Discount_Type__c == 'Life Time') &&  opps.Coupon_Codes__c == null)
                            {
                                accError.add(record);
                                errorRecords.put(' Please provide coupon code for the discount type specified!', accError);
                            }
                            else if(record.Discount_Type__c == 'Date Specified' && String.valueOf(record.Discount_End_Date__c) == null)
                            {
                                accError.add(record);
                                errorRecords.put(' Please choose the discount end date in account before clone the new opportunity', accError);
                            }
                            else if(record.Discount_Type__c == 'Date Specified' && Date.today() > record.Discount_End_Date__c)    // Validations for DateSpecified Discount Type 
                            {
                                accError.add(record);
                                errorRecords.put(' Coupon code expired. Please verify the Discount end date', accError);
                            } 
                            else
                            {
                                oppsUpdate.StageName = 'Closed Won';
                                oppsUpdate.CloseDate = Date.today();
                            }
                        }
                        else if(opps.Account.Program_Status__c == 'On Program')  
                        {
                            oppsUpdate.StageName = 'On Program';
                        }
                    }
                    if(oppsUpdate != null)
                    {
                        oppMap.Add(oppsUpdate);
                    } 
                }  
                                 
               } 
       } 
       if(oppMap.size() > 0)
       {
               Map<Id, String> updateOppsMap = new Map<Id, String>(); 
               for (Opportunity records:oppMap)
            {
               if(records.StageName == 'Closed Won') 
               {
                       updateOppsMap.put(records.Id, records.StageName);
               }
               else
               {
                       oppFinalUpdate.add(records);
               }
            }
               if(!updateOppsMap.isEmpty())
               {
                  runStatusUpdate = false;
                  UtilityClass.updateOpportunities(updateOppsMap);  // Calls the future method
                  if(accError.size() > 0)
                  {
                       for(String str : errorRecords.keySet()) 
                    {
                        for(Account o : errorRecords.get(str)) 
                        {
                            Account oAccErrors = accountNewMap.get(o.Id);
                            oAccErrors.addError(str); 
                        }
                    }
                  }
               }
               if(oppFinalUpdate.size() > 0)
               {
                   runStatusUpdate = true;
                   update oppFinalUpdate; 
               }               
       }

Thanks in advance
Vetriselvan ManoharanVetriselvan Manoharan
only first records gets update via future method.. its my problem now.. It worked properly before, but I think I have made some mistakes in my code..
Vetriselvan ManoharanVetriselvan Manoharan
List<Opportunity> oppsToUpdate = [Select Id, Name, StageName From Opportunity where Id IN :oppMap.keySet()];
        for (Opportunity o : oppsToUpdate) 
        {            
            for(Id oppId : oppMap.keySet()) 
            {
                o.StageName = 'Closed Won';
                o.CloseDate = Date.today(); 
            }                        
        }
        update oppsToUpdate;

Above is the future method