+ Start a Discussion
forrest138forrest138 

account sharing using apex

i am a salesforce admin brand new to development, and i want to programatically share accounts using apex.  What i am trying to accomplish is automatically share parent accounts with the child account owner, as my org has a private sharing model and right now i have to share each one manually as needed.  if anyone can help me out with some sample code to get started, i will be very greatful.
Best Answer chosen by Admin (Salesforce Developers) 
JonSimmonsJonSimmons

 

Here is a trigger that will create new AccountShare objects when a contact is inserted or updated.  It's quick and dirty but will get the job done in a limited set of circumstances.

 

There is one problem,  if you attempt to insert a duplicate AccountShare the insert will fail with an obscure error.  This sample code should get you started, hopefully you can figure out a way around this one issue.

 

 

trigger AccountShareOnNewContact on Contact (after insert, after update)
{


//loop through each contact record, capturing the ownerid and accountid associated with it
// for each one, create a new AccountShare record specifying the accountid and the contactid to provide access to, as well as the access level
// we will compile all of the new AccountShare objects into a single List and insert them all into Salesforce at one time

//problem, if an AccountShare already exists this trigger will fail with an INSUFICIENT_ACCESS_ON_CROSS_REFERENCE_ENTITY error
// you could query to verify that an account share doesn't already exist but that won't fly on bulk inserts/updates and it
// also doesn't account for groups that inherently have access

//create a container to hold all of the new AccountShares
List<AccountShare> newAccountShares = new List<AccountShare>();

for(Contact theContact : trigger.new) //loop through all contacts in the trigger's dataset
{
AccountShare thisAccountShare = new AccountShare(); //a new empty AccountShare object

thisAccountShare.userorgroupid = theContact.ownerid;
thisAccountShare.accountid = theContact.accountid;
thisAccountShare.accountaccesslevel = 'Edit';
thisAccountShare.OpportunityAccessLevel = 'None';
thisAccountShare.CaseAccessLevel = 'None';
thisAccountShare.ContactAccessLevel = 'Edit';

newAccountShares.add(thisAccountShare);


}

//now that we have created all of our new AccountShare objects, let's insert them into Salesforce
insert newAccountShares;


}

 

 You do know about unit tests right?  All APEX code must have test code to go with it, used to verify that the code works as expected.

 

This unit test code will test the above trigger code and serves as a primer to creating more complex test code of your own.

 

 

@isTest
private class AccountShare_Trigger_Support
{

static testMethod void myUnitTest()
{

//grab a real user to make the owner of the test contact,
// someone other then the user running this test
User newContactOwner = [select id from user where alias = 'missey'];

//create a new blank account
System.Debug('Creating a New Test Account');
Account testAccount = new Account(name='the test account');
insert testAccount;

//create a new contact, setting it's owner to the above user
Contact testContact = new Contact();
testContact.FirstName = 'Joe';
testContact.LastName = 'TestUser';
testContact.ownerid = newContactOwner.id;
testContact.accountid = testAccount.id;

//this should fire the AccountShare trigger, giving access to
// the above account to the owner of this contact
insert testContact;

//get a count of all of the AccountShares that give the user access to the
// new account, as a basic test to verify that the trigger worked as expected
Integer theNewAccountShareCount = [select count()
from accountshare
where userorgroupid = :testContact.OwnerId
and accountid = :testContact.accountid];

//if the assertion evaluates to true then we passed the test
System.Assert(theNewAccountShareCount > 0);



}
}

 

 

I hope this helps and good luck with your project.

 

Jon

 

 

 

 

Message Edited by JonSimmons on 09-17-2009 03:36 PM

All Answers

JonSimmonsJonSimmons

 

Take a look at Triggers and the AccountShare object.

 

 

I assume that, since you want this to happen automatically, you will want to create a Trigger OnInsert of the child.  In this trigger you will need to, at a minimum, create a new AccountShare object, specifying the Parent Account Id and the User/Group ID that you want to provide access.

 

Basically, Salesforce will trigger on insert of a new child (contact?), grab the ownerid and parent accountid of the contact and create a new AccountShare object specifying each.  Don't forget to check into writing your trigger  to handle bulk inserts.

 

 

This should get you started, if you need more I can probably dig up a code example.

 

Jon

 

forrest138forrest138

thanks jon.  since is there are already multiple accounts in our instance that need this rule, i will create the trigger on update.

 

if you could provide a code example that would be awesome

 

 

JonSimmonsJonSimmons

I would do both insert and update, else you will have to go back and update all of the children after you create them, just to get the trigger to fire.

 

 

 I will see what I can put together, meanwhile there are a ton of examples here and in the documentation available here.

http://wiki.developerforce.com/index.php/Apex

 

 

Jon

 

 

NOTSOFASTNOTSOFAST

I'm a little puzzled why the owner of an account does not already have access to the parent of that account, assuming the owner was already the record creator. Sounds like there may be a way to implement an off-the-shelf method of providing the needed access via user organization (role hierarchy, public groups, territories). In general manual shares should only be necessary in order to provide arbitrary access to a record (user is not related to account owner organizationally).

 

To step off the soapbox and address your direct question, something like the following called from an after insert/after update trigger should get your started:

 

    private boolean addShareToParentAcct(Account childAcct) {
        boolean success = false;
        String userId = childAcct.OwnerId;
        String parentRecId = childAcct.ParentId;
        String permissionLevel = 'Edit';
        AccountShare thisShare;

        if (userId != null && parentRecId != null)
        try {
            thisShare =
                new AccountShare(
                    UserOrGroupId=userId,
                    AccountId=parentRecId,
                    AccountAccessLevel=permissionLevel);
            insert thisShare;
            success = true;
        } catch(Exception exc) {
            System.debug('addShareToParentAcct(): ' + exc.getMessage());
        }
        return success;
    }

 

Hope something above helped.

forrest138forrest138

thanks for the help.  We have an integration that automatically creates accounts from an erp system, and assigns the owner of the account to be the Sales Rep in whose territory the account lies.  some of these accounts have child account in other rep's territories, and since we have a private sharing model, these repscurrently cannot view the parent accounts unless i go in and manually share them. 

 

i followed the example you provided, slightly modified, and it fired, but didn't create any new sharing rules.  could it be because there are already other sharing rules applied to the accounts? below is the trigger i used

 

trigger addShareToParentAcct on Account (after insert,after update) {private boolean addShareToParentAcct(Account childAcct) {
boolean success = false;
String userId = childAcct.OwnerId;
String parentRecId = childAcct.ParentId;
String permissionLevel = 'Edit';
AccountShare thisShare;

if (userId != null && parentRecId != null)
try {
thisShare =
new AccountShare(
UserOrGroupId=userId,
AccountId=parentRecId,
AccountAccessLevel=permissionLevel);
insert thisShare;
success = true;
} catch(Exception exc) {
System.debug('addShareToParentAcct(): ' + exc.getMessage());
}
return success;
}



}

 

JonSimmonsJonSimmons

 

I think the problem with your code below is that it only contains the one function addShareToParentAcct, but nothing to call that function.

 

 

If you want to create a function you will want to place it into a seperate class then call it from within the trigger.  You could also place your code, slightly modified some more, into your trigger and run it from there.

 

 

Give me 15 minutes or so and I will see if I can come up with something close to help you.

 

 

Jon

 

JonSimmonsJonSimmons

 

Here is a trigger that will create new AccountShare objects when a contact is inserted or updated.  It's quick and dirty but will get the job done in a limited set of circumstances.

 

There is one problem,  if you attempt to insert a duplicate AccountShare the insert will fail with an obscure error.  This sample code should get you started, hopefully you can figure out a way around this one issue.

 

 

trigger AccountShareOnNewContact on Contact (after insert, after update)
{


//loop through each contact record, capturing the ownerid and accountid associated with it
// for each one, create a new AccountShare record specifying the accountid and the contactid to provide access to, as well as the access level
// we will compile all of the new AccountShare objects into a single List and insert them all into Salesforce at one time

//problem, if an AccountShare already exists this trigger will fail with an INSUFICIENT_ACCESS_ON_CROSS_REFERENCE_ENTITY error
// you could query to verify that an account share doesn't already exist but that won't fly on bulk inserts/updates and it
// also doesn't account for groups that inherently have access

//create a container to hold all of the new AccountShares
List<AccountShare> newAccountShares = new List<AccountShare>();

for(Contact theContact : trigger.new) //loop through all contacts in the trigger's dataset
{
AccountShare thisAccountShare = new AccountShare(); //a new empty AccountShare object

thisAccountShare.userorgroupid = theContact.ownerid;
thisAccountShare.accountid = theContact.accountid;
thisAccountShare.accountaccesslevel = 'Edit';
thisAccountShare.OpportunityAccessLevel = 'None';
thisAccountShare.CaseAccessLevel = 'None';
thisAccountShare.ContactAccessLevel = 'Edit';

newAccountShares.add(thisAccountShare);


}

//now that we have created all of our new AccountShare objects, let's insert them into Salesforce
insert newAccountShares;


}

 

 You do know about unit tests right?  All APEX code must have test code to go with it, used to verify that the code works as expected.

 

This unit test code will test the above trigger code and serves as a primer to creating more complex test code of your own.

 

 

@isTest
private class AccountShare_Trigger_Support
{

static testMethod void myUnitTest()
{

//grab a real user to make the owner of the test contact,
// someone other then the user running this test
User newContactOwner = [select id from user where alias = 'missey'];

//create a new blank account
System.Debug('Creating a New Test Account');
Account testAccount = new Account(name='the test account');
insert testAccount;

//create a new contact, setting it's owner to the above user
Contact testContact = new Contact();
testContact.FirstName = 'Joe';
testContact.LastName = 'TestUser';
testContact.ownerid = newContactOwner.id;
testContact.accountid = testAccount.id;

//this should fire the AccountShare trigger, giving access to
// the above account to the owner of this contact
insert testContact;

//get a count of all of the AccountShares that give the user access to the
// new account, as a basic test to verify that the trigger worked as expected
Integer theNewAccountShareCount = [select count()
from accountshare
where userorgroupid = :testContact.OwnerId
and accountid = :testContact.accountid];

//if the assertion evaluates to true then we passed the test
System.Assert(theNewAccountShareCount > 0);



}
}

 

 

I hope this helps and good luck with your project.

 

Jon

 

 

 

 

Message Edited by JonSimmons on 09-17-2009 03:36 PM
This was selected as the best answer