0

I worked on one trigger which will update the related account and contacts related to that account on insert or update.

but on inserting bulk opportunities for single account then I am getting below error. "DonorStatusUpdate: execution of AfterInsert

caused by: System.ListException: Duplicate id in list"

I am unable to understand the error that I did in my code,can anyone helps me out in this issue please.

trigger DonorStatusUpdate on Opportunity (after insert, after update) {
//trigger to update donor status on account and contact from opportunity based on opportunity close date
set<Id> Accountid = new set<Id>();
for(Opportunity opp: trigger.new){
    Accountid.add(opp.AccountId);
}
Map<Id,Account> accountsmap = new map<Id,Account>([select ID,Name,npo02__NumberOfClosedOpps__c from Account where Id IN: Accountid]);
List<Account> AccountUpdate = new List<Account>();
for(Opportunity opp : trigger.new){
    Account acc = accountsmap.get(opp.AccountId);
    Date startDate = opp.CloseDate;
    Date endDate = system.today();
    Integer noOfDays = startDate.daysBetween(endDate);
    system.debug('No. of Days are:'+ noOfDays);
    if(noOfDays >= 0 && noOfDays <= 365 && acc.npo02__NumberOfClosedOpps__c != 0){
          acc.Donor_Status__c = 'Active';
    }
    else if (noOfDays > 365 && noOfDays <= 1095 && acc.npo02__NumberOfClosedOpps__c != 0){
          acc.Donor_Status__c = 'Lapsed';
    }
    else if(noOfDays > 1095 && acc.npo02__NumberOfClosedOpps__c != 0){
          acc.Donor_Status__c = 'In Active';
    }
    else if(acc.npo02__NumberOfClosedOpps__c == 0){
        acc.Donor_Status__c = 'Prospect';
    }
    AccountUpdate.add(acc);
}
update AccountUpdate;
Map<Id,Account> updatedAccount = new map<Id,Account>();
for(Account acc: AccountUpdate){
    updatedAccount.put(acc.Id,acc);
}
List<Contact> contacts = [select Id, AccountId from Contact where AccountId IN: Accountid];
for(Contact con: contacts){
   Account acc = updatedAccount.get(con.AccountId);
    con.Donor_Status__c = acc.Donor_Status__c;
}
update contacts;
 }

1 Answer 1

2

This is because you have the same sobject in your list multiple times:

Account a = [SELECT Id FROM Account LIMIT 1];
List<Account> accounts = new List<Account>();
accounts.add(a);
accounts.add(a);
update accounts; // Throws error

What I usually do is use a map with the record Id as the key to provide some de-dup protection:

Map<Id, Account> accountsToUpdate = new Map<Id, Account>();

for (Opportunity opp : Trigger.new) {
    if (!accountsToUpdate.containsKey(opp.AccountId)) {
        accountsToUpdate.put(opp.AccountId, new Account(id = opp.AccountId));
    }

    if(noOfDays >= 0 && noOfDays <= 365 && acc.npo02__NumberOfClosedOpps__c != 0){
        accountsToUpdate.get(opp.AccountId).Donor_Status__c = 'Active';
    } else if (noOfDays > 365 && noOfDays <= 1095 && acc.npo02__NumberOfClosedOpps__c != 0) {
        accountsToUpdate.get(opp.AccountId).Donor_Status__c = 'Lapsed';
    } else if(noOfDays > 1095 && acc.npo02__NumberOfClosedOpps__c != 0){
        accountsToUpdate.get(opp.AccountId).Donor_Status__c = 'In Active';
    } else if(acc.npo02__NumberOfClosedOpps__c == 0){
        accountsToUpdate.get(opp.AccountId).Donor_Status__c = 'Prospect';
    }
}

I'd also recommend checking the opp stage to make sure the opp is open or closed. Ultimately what is happening is you are updating several opportunities for a single account and your code is not set up to handle that use case.

Alternatively,

Another pattern I have used is to move your code into the account trigger. The account trigger would then query for related opportunities and perform what is effectively a roll-up. Then your opp trigger would simply cause a phantom update to the necessary accounts (like if the opp stage changes, then the account updates should fire). This update will call the account trigger which will re-calc the necessary fields. This approach is better for encapsulation.

You must log in to answer this question.

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