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
Jos VervoornJos Vervoorn 

EntitlementContact Inserts

Hi,

I'm stuck (real stuck) .. as to working on the following challenge. We have many accounts and each account can have several entitlements. Some even have 20 !. Now, these accounts also have multiple contacts. We have performed import of accounts, contacts and entitlements but now I want to insert all (account) contactId's with all (account) entitlementid's into the entitlementcontact object. Like for example acount Y has 2 contacts and 3 entitlements. This should give 6 rows inside Entitlement contacts. However ... some records are already there as we are onboarding another team 

So I managed to create some code ... however I keep running into the following error: Before Insert or Upsert list must not have two identically equal elements

Below my complete code ... maybe i'm on the wrong trail here but ive tried several other ways. Joining data within Excel is not really within my conform zone. All support is much appreciated.
 
List<Id> contactIds = new List<Id>();
List<Id> EntIds = new List<Id>();
List<EntitlementContact> NewEnTlCont = new List<EntitlementContact>();
List<Account> listAccount = [select id,Name ,(select id, Product__c from Entitlements), (select id, email from contacts) from account where IsService__c = true and id in (Select Accountid from entitlement where Asset.Config_ID__c != null and TierSupportLookup__c = 'a0T25000003e0cX' and status='Active' and Status__c = 'No' and CreatedById='0052X000007vbYw') limit 500];

system.debug(LoggingLevel.Info,'*** Numb of Accounts ' + listAccount.size());

For(Account acc : listAccount)
{
        EntitlementContact newEntCnt = new EntitlementContact();
	List<Contact> lstContact = acc.contacts;
	for(Contact cont :lstContact)
	{
		//System.debug(LoggingLevel.Info,cont.id+'('+cont.email+')');
            //**    contactIds.add(cont.id);

               List<Entitlement> lstEntitlement = acc.Entitlements;
               for(entitlement entl:lstentitlement){
            //      System.debug(LoggingLevel.Info,'*** ContactId:'+Cont.id+', EntitlementId:'+  entl.id);
                  contactIds.add(cont.id);
                  EntIds.add(entl.id);
                 
                    newEntCnt.ContactId = cont.id;
                    newEntCnt.EntitlementId = Entl.id;
                    NewEnTlCont.add(NewEntCnt);
                 
               }
	}
}
system.debug(LoggingLevel.Info,'*** contactIds:' + contactIds.size());
system.debug(LoggingLevel.Info,'*** EntIds:' + EntIds.size());
System.debug(logginglevel.info,'*** NewEnTlCont:'+NewEnTlCont.size());
Insert NewEnTlCont;


 
Best Answer chosen by Jos Vervoorn
Alain CabonAlain Cabon
Hi,

If you think that  String key = cont.id + ':' +  Entl.id ;  already exists into EntitlementContact.

Set<String> setEntitlementIds  = new Set<String>();  ( or Set<Id> setEntitlementIds  = new Set<Id>();   )
newEntCnt.ContactId = cont.id;
newEntCnt.EntitlementId = Entl.id;
String key = cont.id + ':' +  Entl.id ;
if ( ! MapNewEnTlCont.containsKey( key )) {   // no duplicates here according your own test
    MapNewEnTlCont.put( key ,  newEntCnt );
}
if ( ! setEntitlementIds.contains( Entl.id )) {  // test not mandatory because the values are unique into a set
    setEntitlementIds.add( );
}
Outside the loops :
// search existing keys reading EntitlementContact based on the read Entitlement Ids in the loops

List<EntitlementContact> ExistingEtlCnt = [select ContactId , EntitlementId from EntitlementContact where EntitlementId in :setEntitlementIds];

// remove the existing keys from the map

for (EntitlementContact ect : ExistingEtlCnt ) {
    String key = ect.ContactId  + ':' + ect.EntitlementId;
    MapNewEnTlCont.remove( key );
}

List<EntitlementContact> NewEnTlCont = (List<EntitlementContact>) MapNewEnTlCont.values();

// DML statement

Database.SaveResult[] srList = Database.insert(NewEnTlCont, false);

// Iterate through each returned result
for (Database.SaveResult sr : srList) {
    if (sr.isSuccess()) {
        // Operation was successful, so get the ID of the record that was processed
        System.debug('Successfully inserted EntitlementContact. ID: ' + sr.getId());
    }
    else {
        // Operation failed, so get all errors                
        for(Database.Error err : sr.getErrors()) {
            System.debug('The following error has occurred.');                    
            System.debug(err.getStatusCode() + ': ' + err.getMessage());
            System.debug('EntitlementContact fields that affected this error: ' + err.getFields());
        }
    }
}

 

All Answers

Alain CabonAlain Cabon
"my confort zone" (exists also perhaps).

You should use a Map moreover :

Map<String,EntitlementContact> MapNewEnTlCont = new Map<String,EntitlementContact>();

In the loop :

newEntCnt.ContactId = cont.id;
newEntCnt.EntitlementId = Entl.id;
String key = cont.id + ':' +  Entl.id ;
if ( ! MapNewEnTlCont.containsKey( key )) {
       MapNewEnTlCont.put( key ,  newEntCnt   );
}

Outside the loops :
 
List<EntitlementContact> NewEnTlCont = (List<EntitlementContact>) MapNewEnTlCont.values();

// DML statement

Database.SaveResult[] srList = Database.insert(NewEnTlCont, false);

// Iterate through each returned result
for (Database.SaveResult sr : srList) {
    if (sr.isSuccess()) {
        // Operation was successful, so get the ID of the record that was processed
        System.debug('Successfully inserted EntitlementContact. ID: ' + sr.getId());
    }
    else {
        // Operation failed, so get all errors                
        for(Database.Error err : sr.getErrors()) {
            System.debug('The following error has occurred.');                    
            System.debug(err.getStatusCode() + ': ' + err.getMessage());
            System.debug('EntitlementContact fields that affected this error: ' + err.getFields());
        }
    }
}

If "Before Insert or Upsert list must not have two identically equal elements" is related to Insert NewEnTlCont; (and not a trigger elsewhere).
 
Jos VervoornJos Vervoorn
ThanksAlain, I will give it a try and let you know in case I succeed or it still fails I i'm nut sure it's capable to validate a unique constraint on both the ContactID and EntitlementId at the same time.

Jos/
Jos VervoornJos Vervoorn

Hi Alain,

I changed the code to however i'm facing the same error and I guess .. it's due to the fact that the combination ContactId and EntitlementId already exists.

EXCEPTION: System.ListException: Before Insert or Upsert list must not have two identically equal elements
STACKTRACE:
AnonymousBlock: line 35, column 1
LINE: 35 COLUMN: 1
....
 
Map<String,EntitlementContact> MapNewEnTlCont = new Map<String,EntitlementContact>();
List<Id> contactIds = new List<Id>();
List<Id> EntIds = new List<Id>();
List<Account> listAccount = [select id,Name ,(select id, Product__c from Entitlements), (select id, email from contacts) from account where IsService__c = true and id in (Select Accountid from entitlement where Asset.Config_ID__c != null and TierSupportLookup__c = 'a0T25000003e0cX' and status='Active' and Status__c = 'No' and CreatedById='0052X000007vbYw') limit 100];

List<entitlementContact> SavedEntlContact = [select ContactId, Entitlementid from EntitlementContact where Entitlement.Accountid in :ListAccount];

system.debug(LoggingLevel.Info,'*** Numb of Accounts ' + listAccount.size());
system.debug(LoggingLevel.Info,'*** Numb of Entitlement contacts ' + SavedEntlContact.size());
For(Account acc : listAccount)
{
        EntitlementContact newEntCnt = new EntitlementContact();
	List<Contact> lstContact = acc.contacts;
	for(Contact cont :lstContact)
	{
		//System.debug(LoggingLevel.Info,cont.id+'('+cont.email+')');
            //**    contactIds.add(cont.id);

               List<Entitlement> lstEntitlement = acc.Entitlements;
               for(entitlement entl:lstentitlement){
                  System.debug(LoggingLevel.Info,'*** ContactId:'+Cont.id+', EntitlementId:'+  entl.id);

                    newEntCnt.ContactId = cont.id;
                    newEntCnt.EntitlementId = Entl.id;
                    String key = cont.id + ':' +  Entl.id ;
                    if ( ! MapNewEnTlCont.containsKey( key )) {
                           MapNewEnTlCont.put( key ,  newEntCnt   );
                    }
               }
	}
}

List<EntitlementContact> NewEnTlCont = (List<EntitlementContact>) MapNewEnTlCont.values();
Database.SaveResult[] srList = Database.insert(NewEnTlCont, false);
// Iterate through each returned result
for (Database.SaveResult sr : srList) {
    if (sr.isSuccess()) {
        System.debug('Successfully inserted EntitlementContact. ID: ' + sr.getId());
    }
    else {              
        for(Database.Error err : sr.getErrors()) {
            System.debug('The following error has occurred.');                    
            System.debug(err.getStatusCode() + ': ' + err.getMessage());
            System.debug('EntitlementContact fields that affected this error: ' + err.getFields());
        }
    }
}



 
Alain CabonAlain Cabon

EXCEPTION: System.ListException: Before Insert or Upsert list must not have two identically equal elements  = there is trigger elsewhere probably and you need to watch at your log.

It is not the list above because all the items are different ( different keys ).
 
Jos VervoornJos Vervoorn
Yes but the records already in the table. So I think it's throwing the error based on existing records having the same combination ContactID & EntitlementId
 
Jos VervoornJos Vervoorn

The below code works .. however ... only with small batches
String nVar = '%ABCDE%';
List<EntitlementContact> NewEnTlCont = new List<EntitlementContact>();
List<Account> AccList = [Select id from Account where IsService__c = true and name like :nvar];
List<Contact> ContactList = [Select Id,Email, Accountid, account.name from Contact where id not in (Select ContactID from EntitlementContact) and email like '%Dummy%' and AccountId in :Acclist];
System.debug(LoggingLevel.Info,'** Contacts without entitlement..:' + ContactList.size());
List<Entitlement> EntitlementList = [Select id, Accountid from Entitlement where Accountid in :AccList];
System.debug(LoggingLevel.Info,'** Account entitlements ..:' + EntitlementList .size());

For (Contact Cnt:ContactList){
    for (Entitlement Ent: EntitlementList){
            EntitlementContact newEntCnt = new EntitlementContact();
            if (Cnt.accountid == Ent.accountid){
                newEntCnt.ContactId = Cnt.id;
                NewEntCnt.EntitlementId = Ent.id;
            }
    }
}
System.debug(LoggingLevel.Info,'*** ** *** New records ...: '+NewEnTlCont.size());
Insert NewEnTlCont;

So I want this to be capable to handle large amounts of contacts and not limited in batches like for example accounts like 'ABCDE%' etc.
The alternative I have is to include this inside the trigger on entitlement insert but I rather not as that would cascade DML and cause DML .. if not CPU timeout issues.....


 
Alain CabonAlain Cabon
Hi,

If you think that  String key = cont.id + ':' +  Entl.id ;  already exists into EntitlementContact.

Set<String> setEntitlementIds  = new Set<String>();  ( or Set<Id> setEntitlementIds  = new Set<Id>();   )
newEntCnt.ContactId = cont.id;
newEntCnt.EntitlementId = Entl.id;
String key = cont.id + ':' +  Entl.id ;
if ( ! MapNewEnTlCont.containsKey( key )) {   // no duplicates here according your own test
    MapNewEnTlCont.put( key ,  newEntCnt );
}
if ( ! setEntitlementIds.contains( Entl.id )) {  // test not mandatory because the values are unique into a set
    setEntitlementIds.add( );
}
Outside the loops :
// search existing keys reading EntitlementContact based on the read Entitlement Ids in the loops

List<EntitlementContact> ExistingEtlCnt = [select ContactId , EntitlementId from EntitlementContact where EntitlementId in :setEntitlementIds];

// remove the existing keys from the map

for (EntitlementContact ect : ExistingEtlCnt ) {
    String key = ect.ContactId  + ':' + ect.EntitlementId;
    MapNewEnTlCont.remove( key );
}

List<EntitlementContact> NewEnTlCont = (List<EntitlementContact>) MapNewEnTlCont.values();

// DML statement

Database.SaveResult[] srList = Database.insert(NewEnTlCont, false);

// Iterate through each returned result
for (Database.SaveResult sr : srList) {
    if (sr.isSuccess()) {
        // Operation was successful, so get the ID of the record that was processed
        System.debug('Successfully inserted EntitlementContact. ID: ' + sr.getId());
    }
    else {
        // Operation failed, so get all errors                
        for(Database.Error err : sr.getErrors()) {
            System.debug('The following error has occurred.');                    
            System.debug(err.getStatusCode() + ': ' + err.getMessage());
            System.debug('EntitlementContact fields that affected this error: ' + err.getFields());
        }
    }
}

 
This was selected as the best answer
Jos VervoornJos Vervoorn
Hi Alian, the final script for inserting 'bulk' entitlement contacts, as I think more people could benefit from this, is pasted below.

Just for anyone using this script, on the top, there's a nVar string you can use to manipulate batches like all accounts starting with 'abc%' or accounts with '%abc%'in their name. It might also be useful to limit the account selection when applicable..
 
String nVar = 'A%';

Map<String,EntitlementContact> MapNewEnTlCont = new Map<String,EntitlementContact>();
Set<String> setEntitlementIds  = new Set<String>();

List<EntitlementContact> NewEnTlCont = new List<EntitlementContact>();
List<Account> AccList = [Select id from Account where name like :nvar];
List<Contact> ContactList = [Select Id,Email, Accountid, account.name from Contact where id not in (Select ContactID from EntitlementContact) and AccountId in :Acclist];

System.debug(LoggingLevel.Info,'** Contacts without entitlement..:' + ContactList.size());
List<Entitlement> EntitlementList = [Select id, Accountid from Entitlement where Accountid in :AccList];
System.debug(LoggingLevel.Info,'** Account entitlements ..:' + EntitlementList .size());

For (Contact Cnt:ContactList){
    for (Entitlement Ent: EntitlementList){
            EntitlementContact newEntCnt = new EntitlementContact();
            if (Cnt.accountid == Ent.accountid){
                newEntCnt.ContactId = Cnt.id;
                NewEntCnt.EntitlementId = Ent.id;

               String key = cnt.id + ':' +  Ent.id ;
               if (!MapNewEnTlCont.containsKey( key )) {   // no duplicates here according your own test
                  MapNewEnTlCont.put( key ,  newEntCnt );
               }
            }
    }
}
System.debug(LoggingLevel.Info,'*** ** *** New records ...: '+NewEnTlCont.size());

//** search existing keys reading EntitlementContact based on the read Entitlement Ids in the loops
List<EntitlementContact> ExistingEtlCnt = [select ContactId , EntitlementId from EntitlementContact where EntitlementId in :setEntitlementIds];

//** remove the existing keys from the map
for (EntitlementContact ect : ExistingEtlCnt ) {
    String key = ect.ContactId  + ':' + ect.EntitlementId;
    MapNewEnTlCont.remove( key );
}
List<EntitlementContact> NewEnTlContIns = (List<EntitlementContact>) MapNewEnTlCont.values();
System.debug(LoggingLevel.Info,'** Records to insert into EntitlementContact ..:' + NewEnTlContIns.size());
//** Below loop can be used to check individual Id's
//** For (EntitlementContact Cins:NewEnTlContIns){
//**     System.debug(LoggingLevel.Info,'** ContactId:' + Cins.ContactId);
//**     System.debug(LoggingLevel.Info,'** EntitlementIdId:' + Cins.Entitlementid); 
//**     System.debug(LoggingLevel.Info,'** ---------------------------------');
//** }

//** DML statement to write distinct rows to the table (EntitlenentContact)
Database.SaveResult[] srList = Database.insert(NewEnTlContIns, false);
//** Iterate through each returned result
for (Database.SaveResult sr : srList) {
    if (sr.isSuccess()) {
        //** Operation was successful, so get the ID of the record that was processed
        System.debug(LoggingLevel.Info,'Successfully inserted EntitlementContact. ID: ' + sr.getId());
    }
    else {
        //** Operation failed, so get all errors                
        for(Database.Error err : sr.getErrors()) {
            System.debug(LoggingLevel.Info,'The following error has occurred.');                    
            System.debug(LoggingLevel.Info,err.getStatusCode() + ': ' + err.getMessage());
            System.debug(LoggingLevel.Info,'EntitlementContact fields that affected this error: ' + err.getFields());
        }
    }
}

 
Alain CabonAlain Cabon
Hi Jos,

By the way, I forgot  Entl.id in  setEntitlementIds.add( Entl.id );   // wrong cut/paste instead of copy/paste (sorry)

You have fixed it by yourself and improved the proposed solution for your need.

You just needed a little extra help or an experience feedback for the same kind of problem but I don't use EntitlementContact myself so I leant something.
newEntCnt.ContactId = cont.id;
newEntCnt.EntitlementId = Entl.id;
String key = cont.id + ':' +  Entl.id ;
if ( ! MapNewEnTlCont.containsKey( key )) {   // no duplicates here according your own test
    MapNewEnTlCont.put( key ,  newEntCnt );
}
if ( ! setEntitlementIds.contains( Entl.id )) {  // test not mandatory because the values are unique into a set
    setEntitlementIds.add( Entl.id  );
}

setEntitlementIds.add( Entl.id );
 
Alain CabonAlain Cabon
I learned something for the EntitlementContact  (shoud be more grammatically correct ).
Jos VervoornJos Vervoorn
Thanks, Alain, I'm not a developer, just a simple consultant, however .... learning by having such challenges is having fun and makes me enjoy the daily fruits of working with Salesforce and the community.