You need to sign in to do that
Don't have an account?
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
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
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
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
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
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);
}
}
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
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
What is the need of throwing exception? I think we can handle using Transactiong in Salesforce, but I am not sure about that.
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
Have you heard about asynchronous methods? You will have to use them to update the Opportunities.
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.
If you really want some easier solution then think over it and I will also try to think on this.
Now update your code:-
if(oppMap.size() > 0)
{
Util.updateOpportunities(oppMap);
}
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
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>.
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
for (Opportunity o : oppsToUpdate)
{
for(Id oppId : oppMap.keySet())
{
o.StageName = 'Closed Won';
o.CloseDate = Date.today();
}
}
update oppsToUpdate;
Above is the future method