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
Alex K.ax676Alex K.ax676 

How to keep sharing rules on Opportunity after a transfer?

Hi everyone,

 

Opportunity object is set to private for my organization and the owner of an opportunity can share it with other users.

When this owner transfer his opportunity to another user, all the old sharing rules are deleted, i need to keep those rules.

 

I first tought to keep the shareTable lines before the opportunity update but it doesn't seem to go anywhere.

Then i tought to disable the deleted rule in the share Table when the owner of the opportunity change but i can't figure out how to to that...

 

I'm stuck with this problem :(

 

Any help would be very appreciated...

 

Alex

mtbclimbermtbclimber

A couple of thoughts.

 

You could query for the shares in a before update trigger on oppty, assign the shares you want to maintain to a static variable which you then utilize in an after update trigger to re-insert. If that doesn't work you could always drop the share information to an async (@future) method.

 

Unfortunately the best solution,  Apex Managed Sharing, isn't available for standard objects yet. Not surprisingly, there is an idea for that. Vote on it if you think you'd benefit from it were it delivered.

Alex K.ax676Alex K.ax676

Hi Andrew,

 

I voted for the idea...

I guess the async method will not fit for my problem cause the new opp owner needs to see in real time who he's sharing the object with.

 

But the static variable may worked, I'm going to try that later and i'll give you a feed back.

 

I'm actually working on another solution which is not really a 'best practice' but it's the only one i found...

So i'm querying the shares in the before update trigger, then insert the shares' list in a long-text (not visible) field of the opportunity (I know... I know... :smileysad:), and eventually in the after update trigger i do the reverse manipulation.

...

 

I try the solutions and write back the results.

 

Thanks for your help.

Alex

mtbclimbermtbclimber

Sure thing.

 

Granted I don't know your use case exactly but this seems like one of those opportunities (no pun intended) where you can reasonably push work to the async context.  If the new owner is "taking" the opportiunity (which means he/she's the manager, right?) then what's the urgency of being able to show them the same shares that previously existed on the record?

 

This may well be the only thing you are doing in a trigger on oppty but in my experience managing this area for the last few years the opportunity save is one of the most contentious in terms of wanting to add logic and I would try to preserve the synchronous processing for those things that *really* are critical.  We're usually pretty quick to process async jobs and it will make your user experience faster which is always better :)

 

Now, here's another thought for you. If the reason you need real-time feedback is to allow the owner taking the oppty to select which shares to remove following the change then perhaps a Visualforce page that shows the shares and changes owner and creates/maintains the desired shares in a single action would work as well.

 

Either way, I hope you find the right solution.

 

Regards,

ahab1372ahab1372

did you try the sales team feature (Opportunity Team Selling)? I am not sure if that sticks when the opp is ransfered, but worth a try

 

Edit: Yes it should work. When you transfer an opportunity, you can select the option to keep the Sales team. You just need to add the Sales Team related list to the page layout.. Nice side effect is that users can have their default teams so they can add several users in one step

Alex K.ax676Alex K.ax676

Hi everyone,

 

  2 ahab1372 : I tink you're right, sales team should work for that case but unfortunately, my client here wants to use sharing as a condidentiality feature, so it won't be a sharing with the sales team but for exemple with some managers.

 

 2   The way my client uses the oppties pushed me to implement a sync solution. Moreover, the Apex managed sharing is not working for my case either (i had to face the same issue for a custom object), cause when the new owner take the object, he cannot changed the sharing anymore.

 

So here is what i eventually did, i used a technical field to store the sharing rules in the before update trigger, and to retrieve them in the after update trigger. Then i update the sharing table.

I know it's kind of an ugly solution but i needed to find something quickly!!!

 

Thank you all for your help and ideas.

 

 

Here is a part of the code for those who are interested in :

 

trigger Opp_Access on Opportunity (before update, after update) {
        // Before the update we collect the sharing rules for the Opp
        if(Trigger.isBefore){
            for (Opportunity Opp : trigger.new){
                // Only if the record owner changed
                if (Trigger.oldMap.get(Opp.id).OwnerId != Opp.OwnerId) {
                    List<OpportunityShare> oppShrList = new List<OpportunityShare>();                    
                    oppShrList=[Select o.UserOrGroupId, o.RowCause, o.OpportunityId, o.OpportunityAccessLevel
                                    From OpportunityShare o Where o.OpportunityId =: Opp.Id];
                                    
                    // The information is stored in a technical field of Opp
                    // Due to its size, the max sharing rules storage is 200 for an Opp
                    Opp.Transfer_Tech__c = ''+oppShrList;
                }
            }
        // After the update, we recreate the rules to keep the same share        
        }else if (Trigger.isAfter){
            for (Opportunity Opp : trigger.new){
                // Only if the record owner changed
                if (Trigger.oldMap.get(Opp.id).OwnerId != Opp.OwnerId) {
                    // Get all the sharing rules in a string
                    String sharingList = Opp.Transfer_Tech__c;
                    // Work on the string to keep only the pertinent info
                    String regex = 'OpportunityShare\\:\\{';
                    String[] sharingListTab = sharingList.split(regex);
                    
                    for(Integer j = 1; j<sharingListTab.size(); j++){
                        // Create the new sharing rules with the stored data
                        OpportunityShare oppShr  = new OpportunityShare();
                        String[] aShareElementsTab = sharingListTab[j].trim().split('\\,');

                        for(Integer k = 0; k<aShareElementsTab.size(); k++){
                            if(aShareElementsTab[k].contains('OpportunityId')){
                                oppShr.OpportunityId = (Id)aShareElementsTab[k].split('\\=')[1].trim();
                            }
                            if(aShareElementsTab[k].contains('UserOrGroupId')){
                                oppShr.UserOrGroupId = (Id)aShareElementsTab[k].split('\\=')[1].trim();
                            }
                            if(aShareElementsTab[k].contains('OpportunityAccessLevel')){
                                String access = aShareElementsTab[k].split('\\=')[1].trim();
                                // If it's the old owner access we only grant him a new read access
                                if(access.equals('All')){
                                    oppShr.OpportunityAccessLevel = 'Read';
                                }else{
                                    oppShr.OpportunityAccessLevel = access;
                                }
                            }
                         }
                        sharesToCreate.add(oppShr);
                    }
                    // Eventually the Oppty has the exact same sharing rules as before transfer
                    Database.SaveResult[] sr = Database.insert(sharesToCreate,false);
                    for(Integer i = 0; i<sr.size(); i++){
                          // Process the save results  
                        if(!sr[i].isSuccess()){
                            // Get save result error  
                            Database.Error err = sr[i].getErrors()[0];
                            if(!(err.getStatusCode() == StatusCode.FIELD_INTEGRITY_EXCEPTION  &&  
                                                err.getMessage().contains('AccessLevel'))){
                                // Throw an error when the error is not related to trivial access level.      
                                trigger.newMap.get(sharesToCreate[i].OpportunityId).
                                  addError('Impossible de garder les règles de partage lors du transfert : '
                                      + err.getMessage());
                            }                        
                        }                        
                     }                    
                }
            }
        }
}