2

From udemy's "Salesforce Apex Trigger and Trigger Design Pattern", I'm trying The tutorial #3, called "6. Trigger Example 4".

public class OpportunityTriggerHandler {
    public static void afterInsertHandler(List<Opportunity> oppList){
        Map<Id, Id> oppOwnerMap = new Map<Id, Id>();
        
        for(Opportunity opp : oppList){
            oppOwnerMap.put(opp.id, opp.OwnerId);
        }
        
        List<OpportunityShare> oppShareList = new List<OpportunityShare>();
        
        for(Id oppId : oppOwnerMap.keySet()){
            OpportunityShare oppShare = new OpportunityShare();
            oppShare.OpportunityId = oppId;
            // oppShare.UserOrGroupId = oppOwnerMap.get(oppId);
            // oppShare.OpportunityAccessLevel = 'Edit';
            oppShareList.add(oppShare);
        }
        if(oppShareList.size() > 0){
            // insert oppShareList;
        }
    }
}

This is supposed to process Opportunity records from a trigger, but probably the issue isn't there

trigger OpportunityTrigger on Opportunity (after insert) {
    OpportunityTriggerHandler.afterInsertHandler(Trigger.New);
}

i get the error: Field is not writeable: OpportunityShare.OpportunityId

in the console, i tried a simpler version

Opportunity op = [Select id, OwnerId from Opportunity where id = '0065J000006PwnpQAC' limit 1];
OpportunityShare oppShare = new OpportunityShare();
oppShare.OpportunityId = op.id;
oppShare.UserOrGroupId = oppOwnerMap.get(op.id);
oppShare.OpportunityAccessLevel = 'Edit';
insert oppShare;

same error

As a last resort:

OpportunityShare oppShare = new OpportunityShare();
insert oppShare;

It's now

DML operation Insert not allowed on OpportunityShare

If i comment the all OpportunityShare fields, there's no error, and the trigger runs well

// oppShare.OpportunityId = op.id;

Probably not field access, but access to object. So i go to all Settings i can remember for permissions on OpportunityShare (although the tutorials, the instructor never mentions the need for aditional settings) In Organization wide defaults and i can see Opportunity Sharing Rules, with none configured. So i gave Read/Write permissions, to all internal users, with a new rule.

In profiles, i just see permissions for see Opportunity, Opportunity Contact Role and Opportunity Product. Object Manager also wont show this object.

So, can someone tell me where else i can set this permissions?

Thanks

2

Looks like the instructor failed to explain the data model behind Opportunity share and straight went to show the code.

OpportunityShare is a special object in Salesforce which is a junction object between Opportunity and User.

You cannot insert entries into this object if the opportunity records are already shared with the users.

This is possible by writing a sharing rule like the one you have or automatic via implicit sharing (if the user has parent record access, few related records are implicitly shared), via roles and subordinates, and many other ways. Sharing architecture in salesforce is a complex topic and look into Trailhead to learn more about it.

For now, this object will be relevant only if you do not have a sharing rule on the opportunity record and keep the Opportunity OWD (From Setup, enter Sharing Settings in the Quick Find box, then select Sharing Settings) to Private and Share your opportunity record with a user who does not have access to it (Not an owner and not above the record owners Role).

Also, make sure to add inherited sharing to your class signature so the class is executed in the system context.

2

This is a common problem when trying to write to a field where the relationship is not reparentable. Apex marks the field as read-only to prevent DML errors. To overcome this problem, use the sObject constructor with field initialization list instead of assigning individual fields.

OpportunityShare oppShare = new OpportunityShare(OpportunityId = oppId, UserOrGroupId = oppOwnerMap.get(oppId));
oppShareList.add(oppShare);

In fact, you should learn to do this anyways; it is 100% more CPU efficient to assign as many values in the constructor as possible. I also tend to eliminate the temporary variable for even better performance:

oppShareList.add(
    new OpportunityShare(OpportunityId = oppId, UserOrGroupId = oppOwnerMap.get(oppId)
);

P.S. if(oppShareList.size() > 0){ is now obsolete and should never be used to guard potentially empty DML operations. It is a waste of code and CPU time, and does not reduce governor limit usage in any way.

2
  • 1
    Are you sure that it is because field is marked read only? Because the author of question just tried to also insert opportunity share using developer console by simply inserting the record and the error popped up. – Mohith Shrivastava Jan 2 at 14:45
  • Thanks for the input. I just tried that instantiating method, and yes seems cleaner. But didn't make any difference in those issues. Gonna do that trail, and try to sort this out – Luis Aguiar Jan 2 at 23:24

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.