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
Rupesh A 8Rupesh A 8 

How to share multiple object's records in Batch Apex

Hi,

I have two custom objects like Private1 and Private2. For these two objects given lookup relationship to Account object. Here whatever the records are entered into these two objects then that particular Account team member can able to see the two custom object's records.

Actually, I have tried for one custom object and below is the code but I need the same scenario for multiple objects. Can anybody please help me on this.
And also I am new to salesforce inaddition to this, Can anybody help on test class for the above scenario?

Single Object record sharing (Private1):
global class PrivateSharingAccMember implements Database.Batchable<sObject> {
    static String emailAddress = 'rupesh@gmail.com';
    
    global Database.QueryLocator start(Database.BatchableContext BC) {
        return Database.getQueryLocator([select Id, Account__c from Private1__c]);
    }
    
    global void execute(Database.BatchableContext BC, List<sObject> scope) {
        Map<ID, Private1__c> pvtMap = new Map<ID, Private1__c>((List<Private1__c>)scope);
        List<Private1__Share> newJobShrs = new List<Private1__Share>();
        
        List<Private1__Share> oldJobShrs = [select Id from Private1__Share WHERE ParentId IN :pvtMap.keySet() AND (RowCause = :Schema.Private1__Share.rowCause.Private_One__c)];
        
        for(Private1__c pvt : pvtMap.values()){
            Private1__Share pvtAccountShr = new Private1__Share();
            
            pvtAccountShr.ParentId = pvt.Id;
            pvtAccountShr.UserOrGroupId = pvt.Account__c;
            pvtAccountShr.AccessLevel = 'Read';
            pvtAccountShr.RowCause = Schema.Private1__Share.RowCause.Private_One__c;
            
            newJobShrs.add(pvtAccountShr);
        }
        
        }
    global void finish(Database.BatchableContext BC){
        Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
        String[] toAddress = new String[] {emailAddress};
        mail.setToAddresses(toAddress);
        mail.setSubject('Apex Sharing Records Completed');
        mail.setPlainTextBody('The Apex Sharing Records finished processing');
        Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
    }

}

Please help me on this at your earliest convenience anyone.
Best Answer chosen by Rupesh A 8
Rohit Sharma 66Rohit Sharma 66
Following is the code:
global class OnePrivateSharingAccMember implements Database.Batchable<sObject> {
    static String emailAddress = 'rupesh.059@gmail.com';
    
    global Database.QueryLocator start(Database.BatchableContext BC) {
        return Database.getQueryLocator([select Id, Account__c, Account__r.Id from Private1__c]);
    }
    
    global void execute(Database.BatchableContext BC, List<sObject> scope) {
        List<Private1__Share> newJobShrs = new List<Private1__Share>();
        List<Private1__c> List_private = new List<Private1__c>();
        Map<Id, set<Id>> map_AccountIdAndAccountTeam = new Map<Id, set<Id>>();
        Map<Private1__c, Id> map_PrivateIdAndAccountId = new Map<Private1__c, Id>();
        List<AccountTeamMember> accTeam = new List<AccountTeamMember>();
        
        system.debug('*****scope'+scope);

        for(sObject obj : scope){
            Private1__c p = (Private1__c)obj;
                List_private.add(p);
                map_AccountIdAndAccountTeam.put(p.Account__r.Id, new set<Id>());
                map_PrivateIdAndAccountId.put(p, p.Account__r.Id);
                system.debug('*****map_AccountIdAndAccountTeam'+map_AccountIdAndAccountTeam);
                system.debug('*****map_PrivateIdAndAccountId'+map_PrivateIdAndAccountId);
        }   
        if(!map_AccountIdAndAccountTeam.isEmpty()){
            accTeam = [select Id, AccountId, UserId from AccountTeamMember where AccountId =: map_AccountIdAndAccountTeam.keySet()];
            system.debug('*****accTeam'+accTeam);
        }
        
        if(!accTeam.isEmpty()){
            for(AccountTeamMember atm : accTeam){
                 map_AccountIdAndAccountTeam.get(atm.AccountId).add(atm.UserId);
                 system.debug('*****map_AccountIdAndAccountTeam'+map_AccountIdAndAccountTeam);
            }    
        }
        
        for(Private1__c pvt : map_PrivateIdAndAccountId.keySet()){
            set<Id> accountMemberId = map_AccountIdAndAccountTeam.get(pvt.Account__r.Id);
            system.debug('*****accountMemberId'+accountMemberId);
            for(Id userId : accountMemberId){
                Private1__Share pvtAccountShr = new Private1__Share();
                pvtAccountShr.ParentId = pvt.Id;
                pvtAccountShr.UserOrGroupId = userId;
                pvtAccountShr.AccessLevel = 'Read';
           //   pvtAccountShr.RowCause = Schema.Private1__Share.RowCause.Private_One__c;
                
                newJobShrs.add(pvtAccountShr);
                system.debug('*****newJobShrs'+newJobShrs);
            }   
        }
        system.debug('*****newJobShrs'+newJobShrs);   
        if(!newJobShrs.isEmpty()){
            Database.SaveResult[] IsShareResult = Database.insert(newJobShrs, false);
        }
    }
    global void finish(Database.BatchableContext BC){
        Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
        String[] toAddress = new String[] {emailAddress};
        mail.setToAddresses(toAddress);
        mail.setSubject('Apex Sharing Records Completed');
        mail.setPlainTextBody('The Apex Sharing Records finished processing');
        Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
    }

}
Please mark this as best answer if worked for you.
 

All Answers

Rohit Sharma 66Rohit Sharma 66
HI,

Is your code working properly for the single object ? Why u not writing the trigger for this scenerio, like whenever u create a record it will get share with the Account Team Member. Your batch class will be schedule on once a day, or u have u run it manually every time. Let me know if your above code working.
Rupesh A 8Rupesh A 8
Hi Rohit,

Thanks for the reply. Yes, the above code is working fine for one object and for one Account Team Member for that Account. Actually I have given that particular user id in the place of "pvt.Account__c" in the above code. Now, I need if I assign multiple Account team members for one Account then for all the team members should able to see the records. And the second case is, it should work for multiple objects.
As you mentioned that, we can able to write trigger also but my requirement is to write Batch class for this. Please help me on this class and also test class.

Thanks.
Rohit Sharma 66Rohit Sharma 66
Hi Rupesh,
Please try the following code,
global class PrivateSharingAccMember implements Database.Batchable<sObject> {
    static String emailAddress = 'rupesh@gmail.com';
    
    global Database.QueryLocator start(Database.BatchableContext BC) {
        return Database.getQueryLocator([select Id, Account__c from Private1__c]);
    }
    
    global void execute(Database.BatchableContext BC, List<sObject> scope) {
        Map<ID, Private1__c> pvtMap = new Map<ID, Private1__c>((List<Private1__c>)scope);
        List<Private1__Share> newJobShrs = new List<Private1__Share>();
        
        List<Private1__Share> oldJobShrs = [select Id from Private1__Share WHERE ParentId IN :pvtMap.keySet() AND (RowCause = :Schema.Private1__Share.rowCause.Private_One__c)];
        
		// Query Account team member for the Account selected on Private_One__c object
		List<AccountTeamMember> accTeam = [Select Id from AccountTeamMember where AccountId = '0019000000YUBWI']; // please put Account Field here in place of Id
		
        for(Private1__c pvt : pvtMap.values()){
            Private1__Share pvtAccountShr = new Private1__Share();
			// check if AccountTeamMember list is not empty
            if(!accTeam.isEmpty()){	
				// looping through each Account tema member and add that member Id to the UserOrGroupId
				for(AccountTeamMember acc : accTeam){
					pvtAccountShr.ParentId = pvt.Id;
					pvtAccountShr.UserOrGroupId = acc.Id;
					pvtAccountShr.AccessLevel = 'Read';
					pvtAccountShr.RowCause = Schema.Private1__Share.RowCause.Private_One__c;					
					newJobShrs.add(pvtAccountShr);
				}	
			}	
        }
		
		// insert share records into the database
		if(!newJobShrs.isEmpty()){
                Database.SaveResult[] lsShareResult = Database.insert(newJobShrs ,false);
        }
		
		// SIMILARLY create another block of code for Private_two__c object, where u have to query all the records and insert them.
        
    }
    global void finish(Database.BatchableContext BC){
        Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
        String[] toAddress = new String[] {emailAddress};
        mail.setToAddresses(toAddress);
        mail.setSubject('Apex Sharing Records Completed');
        mail.setPlainTextBody('The Apex Sharing Records finished processing');
        Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
    }

}

I have added few comments as well, please read them as well.
 
Rupesh A 8Rupesh A 8
Hi Rohit,

As you said, I have place Account field API name which I have create in Pravate1 object in the place of ID. But is showing the below error.
Line 14: invalid ID field: Account__c (// Here Account__c is the field of Private1 Object)

Instead of the field name directly whatelse we can give. Thanks.
Rohit Sharma 66Rohit Sharma 66
Following is the sample code: Create a SOQL query as I mentioned below:
Use Account__r.Id in place of Account__c, also add this field in your soql query.

Private1__c p = [Select Id, Account__c, Account__r.Id from Private1__c where Id = 'a009000002p9SeP' limit 1];
AccountTeamMember acc = [Select Id , AccountId from AccountTeamMember where AccountId =: p.Account__r.Id];
system.debug('*********'+acc);
Rupesh A 8Rupesh A 8
After we use Account__r.Id in place of Account__c also it is showing the same error like "Invalid ID field". Here I tried with "Account record ID", it is working but after batch runs if we go and see from Account Team member account it is not showing the private1 object records.

ok Rohit, If this is the case how you will write the trigger. Can you please provide me that......
Thanks.
Rohit Sharma 66Rohit Sharma 66
I am not sure why u getting the error and what error u exactly getting. If possible can u share your Dev credentials, I'll take a look, and try whatever possible from my end. My email id is rohit30always@gmail.com
Rupesh A 8Rupesh A 8
Hi Rohit,

Please find "OnePrivateSharingAccMember" Apex class in my dev edition. Credentials I have sent to your email id.
Thanks.
Rohit Sharma 66Rohit Sharma 66
Following is the code:
global class OnePrivateSharingAccMember implements Database.Batchable<sObject> {
    static String emailAddress = 'rupesh.059@gmail.com';
    
    global Database.QueryLocator start(Database.BatchableContext BC) {
        return Database.getQueryLocator([select Id, Account__c, Account__r.Id from Private1__c]);
    }
    
    global void execute(Database.BatchableContext BC, List<sObject> scope) {
        List<Private1__Share> newJobShrs = new List<Private1__Share>();
        List<Private1__c> List_private = new List<Private1__c>();
        Map<Id, set<Id>> map_AccountIdAndAccountTeam = new Map<Id, set<Id>>();
        Map<Private1__c, Id> map_PrivateIdAndAccountId = new Map<Private1__c, Id>();
        List<AccountTeamMember> accTeam = new List<AccountTeamMember>();
        
        system.debug('*****scope'+scope);

        for(sObject obj : scope){
            Private1__c p = (Private1__c)obj;
                List_private.add(p);
                map_AccountIdAndAccountTeam.put(p.Account__r.Id, new set<Id>());
                map_PrivateIdAndAccountId.put(p, p.Account__r.Id);
                system.debug('*****map_AccountIdAndAccountTeam'+map_AccountIdAndAccountTeam);
                system.debug('*****map_PrivateIdAndAccountId'+map_PrivateIdAndAccountId);
        }   
        if(!map_AccountIdAndAccountTeam.isEmpty()){
            accTeam = [select Id, AccountId, UserId from AccountTeamMember where AccountId =: map_AccountIdAndAccountTeam.keySet()];
            system.debug('*****accTeam'+accTeam);
        }
        
        if(!accTeam.isEmpty()){
            for(AccountTeamMember atm : accTeam){
                 map_AccountIdAndAccountTeam.get(atm.AccountId).add(atm.UserId);
                 system.debug('*****map_AccountIdAndAccountTeam'+map_AccountIdAndAccountTeam);
            }    
        }
        
        for(Private1__c pvt : map_PrivateIdAndAccountId.keySet()){
            set<Id> accountMemberId = map_AccountIdAndAccountTeam.get(pvt.Account__r.Id);
            system.debug('*****accountMemberId'+accountMemberId);
            for(Id userId : accountMemberId){
                Private1__Share pvtAccountShr = new Private1__Share();
                pvtAccountShr.ParentId = pvt.Id;
                pvtAccountShr.UserOrGroupId = userId;
                pvtAccountShr.AccessLevel = 'Read';
           //   pvtAccountShr.RowCause = Schema.Private1__Share.RowCause.Private_One__c;
                
                newJobShrs.add(pvtAccountShr);
                system.debug('*****newJobShrs'+newJobShrs);
            }   
        }
        system.debug('*****newJobShrs'+newJobShrs);   
        if(!newJobShrs.isEmpty()){
            Database.SaveResult[] IsShareResult = Database.insert(newJobShrs, false);
        }
    }
    global void finish(Database.BatchableContext BC){
        Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
        String[] toAddress = new String[] {emailAddress};
        mail.setToAddresses(toAddress);
        mail.setSubject('Apex Sharing Records Completed');
        mail.setPlainTextBody('The Apex Sharing Records finished processing');
        Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
    }

}
Please mark this as best answer if worked for you.
 
This was selected as the best answer