+ Start a Discussion
F SmoakF Smoak 

share records with the end user when created by batch class

share records with the end user when created by batch class
I have a requirement:

For an account in user's territory, we have calls created. Now based on this I need to create alerts where user can view alerts showing his account got calls on which date by which user in last 7 days. For this I need to create a batch but since all alerts are created by admin end user do not have access to alerts. How to fix this?
Please find my code below:

global class BatchCreateAlertforCalls implements Database.Batchable<sObject> {
global Database.QueryLocator start(Database.BatchableContext BC) {  
    
    String query = 'SELECT Id,Call_Date_vod__c,Account_vod__c,createdby.name FROM Call2_vod__c where Status_vod__c = \'Submitted_vod\' AND Call_Date_vod__c = N_DAYS_AGO:7 AND OwnerId!=\'005U0000001vQ0a\'';
        return Database.getQueryLocator(query);
    }
    
    global void execute(Database.BatchableContext BC, List<sObject>scope) {
        List<Call2_vod__c> callList = (List<Call2_vod__c>) scope;
        system.debug('call list>>>>'+calllist);
        List<Alert_vod__c> alertsubmittedcalllist = new List<Alert_vod__c>();
        for(Call2_vod__c call: callList){
         Alert_vod__c alert = new Alert_vod__c();
            alert.Activation_Date_vod__c = system.now();
            alert.Alert_Text_vod__c = call.Account_vod__c+' seen by '+call.createdby.name;
            alert.Link_Reference_vod__c =call.Id;
            alert.Expiration_Date_vod__c = system.now()+1;
            alertsubmittedcalllist.add(alert);
        }
        if(alertsubmittedcalllist.size() >0)
        {
            insert alertsubmittedcalllist;
            system.debug('alert list>>>>'+alertsubmittedcalllist);
        }
}
     global void finish(Database.BatchableContext BC) {
        // execute any post-processing operations
  }
}
Alain CabonAlain Cabon

alert.Expiration_Date_vod__c = system.now()+1;   
alert.OwnerId =call.OwnerId;
alertsubmittedcalllist.add(alert);

 
Alain CabonAlain Cabon
Change a Record’s Owner
You can give ownership of a record to another user as long as that user has at least Read permission for the type of record being transferred.
https://help.salesforce.com/articleView?id=account_owner.htm&type=5 (https://help.salesforce.com/articleView?id=account_owner.htm&type=5)

The profile of the targeted users must have the Read permission for the object Alert_vod__c.
F SmoakF Smoak
but calls OWD is private and user A do not have access to call C created by User B for account X shared between users A and B. So alerts if made ownership based on call owner then will be visible only to B and not A But req says alerts visible to A and B for all calls of account X
Alain CabonAlain Cabon

The level of access (e.g., Read Only, Read/Write) being granted to the user or group

1) Sharing Rows and Hierarchy-based Inheritance


Salesforce also uses group membership tables together with the role hierarchy to give users assigned to roles access to records owned by or shared to members of subordinate roles, and records shared to the subordinate roles themselves.

2) Ownership-based Sharing Rules

Criteria-based sharing rules operate just like ownership-based sharing rules, but they determine which records should be shared differently.

https://developer.salesforce.com/blogs/engineering/2013/10/behind-the-scenes-of-record-ownership-in-salesforce.html

3) Creating User Managed Sharing Using Apex

It is possible to manually share a record to a user or a group using Apex or the SOAP API.

The users must be organized by groups ou you can create records in your MyCustomObject__Share with Apex.

Alert_vod__Share (here)
F SmoakF Smoak
I have Alert_vod__Share object . So I will create this records for all account owners in the batch code itself?
If I use managed sharing using apex I can share record to a user. But hiw do I understand if the intended user will get read acces to alerts because user should see alerts only for the accounts shared with him regardless of calls shared.
Alain CabonAlain Cabon

Pre requisite: The object’s organization-wide default access level must not be set to the most permissive access level. For custom objects, this is Public Read/Write. For more information, see Access Levels. This object is used for creating Apex based sharing.
The user to which the record going to be shared must have the object level permission. If you are trying to share the record with edit permission but user does not have the edit permission on that object, then it will not work.

Only users with the “Modify All Data” permission can add, edit, or delete sharing that uses an Apex sharing reason.

https://www.jitendrazaa.com/blog/salesforce/apex-based-sharing-in-salesforce/

The target user must have the Read right at least on the object Alert_vod__c (object level) whatever happens and that is defined in the profiles and permission sets..

https://help.salesforce.com/articleView?id=perm_sets_object_perms_edit.htm&type=5 (https://help.salesforce.com/articleView?id=perm_sets_object_perms_edit.htm&type=5)

There is also the record level that depends on the owner (user; group) and the role hierarchy ( organization-wide default and   Alert_vod__Share object in order to break the hierarchy at record level )

The words "object", "record"  and "fields" are always very important for the accesses in Salesforce but they are often mixed in the same sentence like above (  object’s organization-wide default access level is in fact a record level here defined for each object )
F SmoakF Smoak
Thank you for your help! I have followed this and updated my code as following where I am creating alerts and alertshare based on accounts shared but when I run batch I am getting this error in Apex Jobs:
First error: Insert failed. First exception on row 0; first error: FIELD_INTEGRITY_EXCEPTION, Parent ID: id value of incorrect type: 0011800000hms5qAAA: [ParentId]

Please find my code below:
global class BatchCreateAlertforCalls implements Database.Batchable<sObject> {
global Database.QueryLocator start(Database.BatchableContext BC) {  
    
    String query = 'SELECT Id,Call_Date_vod__c,Account_vod__r.name,Account_vod__c,createdby.name FROM Call2_vod__c where Status_vod__c = \'Submitted_vod\' AND Call_Date_vod__c >= N_DAYS_AGO:7 AND OwnerId!=\'005U0000001vQ0a\'';
        return Database.getQueryLocator(query);
    }
    
    global void execute(Database.BatchableContext BC, List<sObject>scope) {
         boolean isRunningBatch;
        List<Call2_vod__c> callList = (List<Call2_vod__c>) scope;
        system.debug('call list>>>>'+calllist);
        List<Alert_vod__c> alertsubmittedcalllist = new List<Alert_vod__c>();
       Set<Id> accIds = new Set<Id>();
        List<Alert_vod__Share> alsharelist = new List<Alert_vod__Share>();
        /* List<AccountShare> accshare = new List<AccountShare>();
                
        
        accshare.clear();*/
        accIds.clear();
        for(Call2_vod__c call: callList){
           accIds.add(call.Account_vod__c);
            //accshare = [Select UserorGroupId from AccountShare where AccountId in : accId];
         Alert_vod__c alert = new Alert_vod__c();
            alert.Activation_Date_vod__c = system.today();
            alert.Alert_Text_vod__c = call.Account_vod__r.name+' seen by '+call.createdby.name + ' on '+call.Call_Date_vod__c.format();
            alert.Link_Reference_vod__c =call.Id+',Call2_vod__c';
            alert.Expiration_Date_vod__c = call.Call_Date_vod__c+7;
            alert.Created_by_batch__c = true;
           alert.Account__c = call.Account_vod__c;
            alertsubmittedcalllist.add(alert);
      
       Map<String,List<AccountShare>> accsharemap = new Map<String,List<AccountShare>>(); 
List<AccountShare> accsharelist = [Select id,UserorGroupId,accountid from AccountShare where accountid in: accIds];
    for(AccountShare accshare : accsharelist){
            accsharemap.put(accshare.AccountId,new List<AccountShare>{accshare});
            system.debug('accsharemap>>>'+accsharemap);
        }
           for(List<AccountShare> a:accsharemap.values()){
               for(AccountShare ash:a){
Alert_vod__Share alertshare = new Alert_vod__Share();
            alertshare.UserOrGroupId = ash.UserOrGroupId;
                alertshare.ParentId = ash.AccountId;
                alertshare.AccessLevel = 'Read';
                alertshare.RowCause = Schema.Alert_vod__Share.RowCause.ShareAlertsforSubmittedcalls__c;
                alsharelist.add(alertshare);
               }   }
            if(alsharelist.size()>0){
        insert alsharelist;
            }
        }
                      
        if(alertsubmittedcalllist.size() >0)
        {
            insert alertsubmittedcalllist;
            system.debug('alert list>>>>'+alertsubmittedcalllist);
        }
        List<Alert_vod__c> deleteAlerts = new List<Alert_vod__c>();
        deleteAlerts = [Select Id from Alert_vod__c where Expiration_Date_vod__c <= today and Created_by_batch__c =true ];
        if(deleteAlerts.size()>0){
            delete deleteAlerts;
        }
}
     global void finish(Database.BatchableContext BC) {
        // execute any post-processing operations
  }
}


Kindly help me find the error, its been hours but I have no progress.
Alain CabonAlain Cabon
The relationship between account and alert must not be a master/detail relationship (prevents the creations of manual sharings).

If Alert_vod__c is linked by a master/detail relationship, the parent imposes its own rights to all its children.

The Owner field on the detail and subdetail records is not available and is automatically set to the owner of the master record. Custom objects on the “detail” side of a master-detail relationship can't have sharing rules, manual sharing, or queues, as these require the Owner field.

https://help.salesforce.com/articleView?id=overview_of_custom_object_relationships.htm&type=5 (https://help.salesforce.com/articleView?id=overview_of_custom_object_relationships.htm&type=5)
F SmoakF Smoak
its not MD relationship, its lookup created for this requirement.
Alain CabonAlain Cabon
Alert_vod__c alert = [select Id,  .... from Alert_vod__c ];
Alert_vod__Share alertshare = new Alert_vod__Share();

alertshare.ParentId = alert.Id;
alertshare.UserOrGroupId = <UserOrGroupId> ;
alertshare.AccessLevel = 'read';

https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_bulk_sharing_creating_with_apex.htm


  Job__Share hmShr = new Job__Share();
  // Set the ID of record being shared
 hmShr.ParentId = job.Id;
 hmShr.AccessLevel = 'read';