+ Start a Discussion
L.DelanoL.Delano 

Account Sharing Trigger

We have some strict requirements on account sharing amongst sales teams members in our org. I have created a custom object which holds the rules of who can see which account based on the Owner on the account. I wrote a trigger which makes use of AccountTeamMember object. Everything is working fine. However I would like to bulkify my trigger but have not been able too after a few attempts.
Below is part of my trigger I would like to bulkify: (the rest of it has code unrelated to account sharing).
Could someone suggest a solution?

Thank you
List<AccountTeamMember> atmToAdd = new List<AccountTeamMember>();

for (Account a : trigger.new) 
{
	//Custom object holding sharing rules
	SharingRuleMap__c[] sMap = [SELECT SharingWith_ID__c, 
										TeamMemberRole__c, 
										AccountAccessLevel__c, 
										ContactAccessLevel__c, 
										CaseAccessLevel__c, 
										OpportunityAccessLevel__c 
								   FROM SharingRuleMap__c
								  WHERE Dealing_Rep__c = :a.OwnerId];
	 
	if (sMap.size() > 0) 
	{ 
		For (Integer i=0; i<sMap.size();i++) 
		{
			AccountTeamMember atm = new AccountTeamMember();
			atm.AccountId = a.Id;
			atm.UserId = sMap.get(i).SharingWith_ID__c;
			atm.TeamMemberRole = sMap.get(i).TeamMemberRole__c;
			atm.AccountAccessLevel = sMap.get(i).AccountAccessLevel__c;
			atm.ContactAccessLevel = sMap.get(i).ContactAccessLevel__c;
			atm.CaseAccessLevel = sMap.get(i).CaseAccessLevel__c;
			atm.OpportunityAccessLevel = sMap.get(i).OpportunityAccessLevel__c;
			atmToAdd.add(atm);
		}
	}
}

insert atmToAdd;

 
Best Answer chosen by L.Delano
RovRov
Oh ok Got it! 

In that case all you do is, check if the Key exists in the map and if it does, get the list of values related to the key, and add the new value to the list. 
You just need to do a minor modification to the code. I have done it for you. 

Feel free to ask questions if you have. 
 
for( SharingRuleMap__c srm : lstSharingRuleMap ){
				if(mapSharingRuleDealingRep.contaisKey(srm.Dealing_Rep__c)){
					List<SharingRuleMap__c> lstSharingRuleMapRecords = mapSharingRuleDealingRep.get(srm.Dealing_Rep__c).add(srm);
					//lstSharingRuleMapRecords.add(srm);
				}
				else{
					List<SharingRuleMap__c> lstSharingRuleMapRecords = new List<SharingRuleMap__c>();
					lstSharingRuleMapRecords.add(srm);
					mapSharingRuleDealingRep.put(srm.Dealing_Rep__c,lstSharingRuleMapRecords);
				}

 

All Answers

BENESTRA DeveloperBENESTRA Developer
Hi L.Delano.
First of all, you should NEVER put a query inside a for loop!
Then you should, inside a for loop for trigger, first gather all owner ids into a list. Like
Set<Id> listAccOwnerId = new Set<Id>();
for (Account acc : trigger. new) {
setAccOwnerId.add (acc.ownerId);
}

Then after first for loop select your rules, and parse them into an actual Map by owner
SharingRuleMap__c[] sMap = [SELECT SharingWith_ID__c,
TeamMemberRole__c,
AccountAccessLevel__c,
ContactAccessLevel__c,
CaseAccessLevel__c,
OpportunityAccessLevel__c
FROM SharingRuleMap__c
WHERE Dealing_Rep__c in :listAccOwnerId];

Map<Id, SharingRuleMap__c> mapAccOwnerId2SharingRule = new Map<Id, SharingRuleMap__c>();
for (SharingRuleMap__c srm : sMap) {
mapAccOwnerId2SharingRule.put(srm.Dealing_Rep__c, srm);
}
And finally run a second loop for triggered accounts where you'll do the rest
List<AccountTeamMember> atmToAdd = new List<AccountTeamMember>();

for (Account acc : trigger. new) {
AccountTeamMember atm = new AccountTeamMember();
SharingRuleMap__c temp_srm = mapAccOwnerId2SharingRule.get(acc.ownerId);
atm.AccountId = acc.Id;
atm.UserId = temp_srm.SharingWith_ID__c;
atm.TeamMemberRole = temp_srm.TeamMemberRole__c;
atm.AccountAccessLevel = temp_srm).AccountAccessLevel__c;
atm.ContactAccessLevel = temp_srm.ContactAccessLevel__c;
atm.CaseAccessLevel = temp_srm.CaseAccessLevel__c;
atm.OpportunityAccessLevel = temp_srm.OpportunityAccessLevel__c;
atmToAdd.add(atm);
}

insert atmToAdd;

 
$hwet@$hwet@
Hi L.Delano,

You have to create an extra list to store the ownerId and a map to store the owner id and SharingRuleMap__c

so you can store all the ownerid in the list and use the same list to query the SharingRuleMap__c records. Then you loop over to the data returned by the query and save the ownerid and SharingRuleMap__c record in the map.

then just call the get method of map inside the for(Account a : trigger.new) loop to get SharingRuleMap__c  records based on the a.ownerId.

I hope this is the correct approach. Guys Please correct me if I am wrong.

 
$hwet@$hwet@
List<AccountTeamMember> atmToAdd = new List<AccountTeamMember>();
List<id> listownerID = new list<id>();
map<id,SharingRuleMap__c> mapofownerIdandsharingrule = new map<id,SharingRuleMap__c>();
for (Account a : trigger.new) 
{
    listownerID.add(a.OwnerId);
}
SharingRuleMap__c[] sMap1 = [SELECT SharingWith_ID__c, 
                                        TeamMemberRole__c, 
                                        AccountAccessLevel__c, 
                                        ContactAccessLevel__c, 
                                        CaseAccessLevel__c, 
                                        OpportunityAccessLevel__c 
                                   FROM SharingRuleMap__c
                                  WHERE Dealing_Rep__c IN : listownerID];
for(SharingRuleMap__c ss :sMap1 )
{
    mapofownerIdandsharingrule.put(ss.Dealing_Rep__c,ss);
}


for (Account a : trigger.new) 
{
    //Custom object holding sharing rules
    /*SharingRuleMap__c[] sMap = [SELECT SharingWith_ID__c, 
                                        TeamMemberRole__c, 
                                        AccountAccessLevel__c, 
                                        ContactAccessLevel__c, 
                                        CaseAccessLevel__c, 
                                        OpportunityAccessLevel__c 
                                   FROM SharingRuleMap__c
                                  WHERE Dealing_Rep__c = :a.OwnerId]; */
    list<SharingRuleMap__c> sMap =  mapofownerIdandsharingrule.get(a.OwnerId);
    if (sMap.size() > 0) 
    { 
        For (Integer i=0; i<sMap.size();i++) 
        {
            AccountTeamMember atm = new AccountTeamMember();
            atm.AccountId = a.Id;
            atm.UserId = sMap.get(i).SharingWith_ID__c;
            atm.TeamMemberRole = sMap.get(i).TeamMemberRole__c;
            atm.AccountAccessLevel = sMap.get(i).AccountAccessLevel__c;
            atm.ContactAccessLevel = sMap.get(i).ContactAccessLevel__c;
            atm.CaseAccessLevel = sMap.get(i).CaseAccessLevel__c;
            atm.OpportunityAccessLevel = sMap.get(i).OpportunityAccessLevel__c;
            atmToAdd.add(atm);
        }
    }
}

insert atmToAdd;
L.DelanoL.Delano
Thank you for your suggestions, however the problem is with the map of (Key:OwnerId, Value:SharingRuleMap__c).
The SharingRuleMap__c custom object contains multiple records for the same OwnerId, one record per user with whom the Owners are sharing their accounts with.
The map cannot use the OwnerId as the key since keys cannot be duplicated. In my attempts I got close to you suggested, but I don’t know how to get around the map problem.
 
RovRov
Try this ...

Set<ID> setAccOwnerID = new Set<ID>();

for (Account a : trigger.new) {

	if (a.OwnerId !=null){
			
		setAccOwnerID.add(a.OwnerId);
	}
	
}

if (accountOwnerID.size()>0){

		List<AccountTeamMember> atmToAdd = new List<AccountTeamMember>();
		List<SharingRuleMap__c> lstSharingRuleMap = [SELECT SharingWith_ID__c, 
										TeamMemberRole__c, 
										AccountAccessLevel__c, 
										ContactAccessLevel__c, 
										CaseAccessLevel__c, 
										OpportunityAccessLevel__c 
								   FROM SharingRuleMap__c
								  WHERE Dealing_Rep__c IN :setAccOwnerID];
}								  
		
		// Map of SharingRuleMap and DealingReps
		Map<Id,List<SharingRuleMap__c>> mapSharingRuleDealingRep = new Map<Id,List<SharingRuleMap__c>>;



		// iterate throug the list of SharingRuleMap records to add the ID of Dealing_Rep and list of SharingRileMap__c 
		
		for( SharingRuleMap__c srm : lstSharingRuleMap ){
				if(mapSharingRuleDealingRep.contaisKey(srm.Dealing_Rep__c)){
					List<SharingRuleMap__c> lstSharingRuleMapRecords = mapSharingRuleDealingRep.get(srm.Dealing_Rep__c);
					lstSharingRuleMapRecords.add(srm);
				}
				else{
					List<SharingRuleMap__c> lstSharingRuleMapRecords = new List<SharingRuleMap__c>();
					lstSharingRuleMapRecords.add(srm);
					mapSharingRuleDealingRep.put(srm.Dealing_Rep__c,lstSharingRuleMapRecords);
				}
			
		}  // Break the for loop 
		
		// Again iterate through each record in trigger.new
		// just get a list of those  SharingRuleMap__c records from the map we created above
		// then iterate through this list of SharingRuleMap__c to create a new AccountTeamMember
		
		
		for(Account a : trigger.new){
			
			List<SharingRuleMap__c> lstSharingMap = mapSharingRuleDealingRep.get(a.OwnerId);
				
				if (lstSharingMap.size() > 0) { 
					For (Integer i=0; i<lstSharingMap.size();i++) {
						AccountTeamMember atm = new AccountTeamMember();
						atm.AccountId = a.Id;
						atm.UserId = lstSharingMap.get(i).SharingWith_ID__c;
						atm.TeamMemberRole = lstSharingMap.get(i).TeamMemberRole__c;
						atm.AccountAccessLevel = lstSharingMap.get(i).AccountAccessLevel__c;
						atm.ContactAccessLevel = lstSharingMap.get(i).ContactAccessLevel__c;
						atm.CaseAccessLevel = lstSharingMap.get(i).CaseAccessLevel__c;
						atm.OpportunityAccessLevel = lstSharingMap.get(i).OpportunityAccessLevel__c;
						atmToAdd.add(atm);
					}
				}
		}


insert atmToAdd;

 
L.DelanoL.Delano
@Rov
Unfortunately this has the same problem. When iterating through "lstSharingRuleMap" and using the OwnerId (Dealing_Rep__c) as the key, the map will end up with unique records only.
What lstSharingRuleMap contains is something like this:
OwnerId 1 sharing with User 1
OwnerId 1 sharing with User 2
OwnerId 1 sharing with User 3
... and so on for other account owners. If you put this into a map where the Key is the OwnerId the only key/value pair would be the last one.
$hwet@$hwet@
Hi LDelano,

Have you tried using "Map<Id,List<SharingRuleMap__c>>". Below is the link for how to use it:
 https://salesforce.stackexchange.com/questions/87951/better-way-to-use-map-of-list
RovRov
Oh ok Got it! 

In that case all you do is, check if the Key exists in the map and if it does, get the list of values related to the key, and add the new value to the list. 
You just need to do a minor modification to the code. I have done it for you. 

Feel free to ask questions if you have. 
 
for( SharingRuleMap__c srm : lstSharingRuleMap ){
				if(mapSharingRuleDealingRep.contaisKey(srm.Dealing_Rep__c)){
					List<SharingRuleMap__c> lstSharingRuleMapRecords = mapSharingRuleDealingRep.get(srm.Dealing_Rep__c).add(srm);
					//lstSharingRuleMapRecords.add(srm);
				}
				else{
					List<SharingRuleMap__c> lstSharingRuleMapRecords = new List<SharingRuleMap__c>();
					lstSharingRuleMapRecords.add(srm);
					mapSharingRuleDealingRep.put(srm.Dealing_Rep__c,lstSharingRuleMapRecords);
				}

 
This was selected as the best answer
L.DelanoL.Delano
@Rov I will try that as well.
Thank you all fro your suggestions.
L.DelanoL.Delano
Rov, your code worked great with one small adjustment:
List<SharingRuleMap__c> lstSharingRuleMapRecords = mapSharingRuleDealingRep.get(srm.Dealing_Rep__c).add(srm);
mapSharingRuleDealingRep.get(srm.Dealing_Rep__c).add(srm);
Thank you very much, I learned something new as well :-)