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){
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';
update AccountUpdate;
Map<Id,Account> updatedAccount = new map<Id,Account>();
for(Account acc: AccountUpdate){
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;

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>();
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.


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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.