• zigzag
  • NEWBIE
  • 45 Points
  • Member since 2008

  • Chatter
    Feed
  • 1
    Best Answers
  • 0
    Likes Received
  • 0
    Likes Given
  • 4
    Questions
  • 3
    Replies

We use the PRM partner portal, and in order to keep the approvals workflow simple we have set it up so the Partner User's Manager gets approval requests.

 

This Manager field should always be the same as the Partner Account's Owner, and we would like it to be automatically updated for all of the relevant Partner Users when the Owner field changes on the Account.


It should be a simple thing to do, but turned out to be more complex than I thought because Salesforce doesn't allow a User record to be updated in the same transaction as an Account object - which means that you can't update the User object in an Account trigger.

 

The solution is to split the code into two parts: the trigger, and a separate class that does all the work and is marked @future.  In doing this, salesforce executes the code asynchroneously and it all works.

 

In case it's useful to someone, here is the complete functioning code:

 

 

// Trigger on Account changes
// Looks for changes to the Owner field
// When a Partner user is associated with the Account, make sure
// the Manager field on that User is updated to have the same value
//
// This is necessary because the approvals workflow for Partner-entered Leads
// uses on the Manager field.
//
// NOTE: Relies on the userUpdateAsyncClass class for most of the work because
// Salesforce can't update a User in an Account trigger.
//
// VERSION HISTORY
// 8 Aug 2009/Allan M: Initial version
//
trigger PartnerAccountOwnerChange on Account (before update) {
System.Debug('DBG: In PartnerAccountOwnerChange trigger');

List<Account> theAccounts = new List<Account>();
for (Account newAccount:System.Trigger.new) {
Account oldAccount = System.Trigger.oldMap.get(newAccount.Id);
if (oldAccount.OwnerId == newAccount.OwnerId) {
System.Debug('DBG: Owner was "' + oldAccount.OwnerId + '" and is now "' + newAccount.OwnerId + '"');
continue; // Ignore changes that do not affect the Account Owner
}

// We could also filter on Type to consider only Partner accounts
theAccounts.add(newAccount);
}

if (theAccounts.isEmpty()) {
System.debug('DBG: Nothing to do; no relevant changes');
return; // Nothing to do
}

System.debug('UPDATE: There may be something to do.');

List<Id> AccountIds = new List<Id>();
for (Account a: theAccounts)
AccountIds.add(a.Id);

// Perform the change. However, because Salesforce doesn't allow the User object
// to be updated in the same transaction that updates an Account object (which
// is what triggered this trigger, it has to be done in an asynchroneous static
// method annotated as @future.
userUpdateAsyncClass.updatePartnerUsersForAccounts(AccountIds);
} // trigger

 The bulk of the work is done in a separate class called userUpdateAsyncClass:

 

// Class used by the PartnerAccountOwnerChange trigger
//
// Works around the fact that a User cannot be updated by a trigger caused by an Account change
//
// VERSION HISTORY
// 8 Aug 2009/Allan M: Initial version
//
public class userUpdateAsyncClass {

@future
public static void updatePartnerUsersForAccounts(Id[] accountIds) {

// Retrieve the account objects for the Ids
List<Account> theAccounts = new List<Account> (
[select Id, Name, OwnerId from Account where Id in :AccountIds]
);

// Bulk retrieve all Partner User objects
// Might be better to retrieve based on PRM Profile rather than UserType?
List<User> theUsers = new List<User> (
[select Id, Name, ContactId, ManagerId from User where UserType = 'PowerPartner']
);

// Retrieve all of the associated Contact records that relate to the trigger Accounts
Set<Id> ContactIds = new Set<Id>();
for (User u: theUsers)
ContactIds.add(u.ContactId);
Map<Id, Contact> theContacts = new Map<Id, Contact>(
[select Id, AccountId from Contact where Id in :ContactIds and AccountId in :AccountIds]
);

if (theContacts.isEmpty()) {
System.Debug('DBG No matching Partner user objects need updating.');
return; // Nothing to do
}

// I need the actual User objects for the update to work, for some reason :(
Set<Id> theNewOwnerIds = new Set<Id>();
for (Account a: theAccounts)
theNewOwnerIds.add(a.OwnerId);
Map<Id, User> userMap = new Map<Id, User>(
[select Id, Name from User where Id in :theNewOwnerIds]
);

// Now do the actual update
for (User u:theUsers) {
Contact c = theContacts.get(u.ContactId);
if (c == null)
continue; // This user is not affected

System.Debug('DBG: Look for manager for User "' + u.Name + '"');
Id newOwnerId = null;
for (Account a:theAccounts){
System.Debug('DBG: Checking ' + c.AccountId + ' against ' + a.Id );
if (c.AccountId == a.Id)
newOwnerId = a.OwnerId;
} // theAccounts

if (newOwnerId == null) {
System.Debug('ERROR: ??? Cannot find new Owner!?!');
continue;
}

System.Debug('DBG: Setting new Manager to ' + newOwner);
u.ManagerId = newOwnerId;
User newOwner = userMap.get(newOwnerId);
u.Manager = newOwner;

try {
update u;
} catch (DmlException e) {
System.debug('ERROR: ' + e.getMessage());
}
} // theUsers
}
}

 

Thoughts, comments or improvements to get this done easier are much welcome.  Feel free to make use of the code to make your life easier in some way too :)

 

Allan Mertner

 

 

 

 

  • August 21, 2009
  • Like
  • 0
I am trying to create a formula field to hold the day of week; the cookbook helpfully suggests the following nifty formula:

MOD(MyField__c - DATE(1900, 1, 7), 7)

Unfortunately, this only works if MyField__c is a Date field, and not if it is a DateTime field.  It seems silly, but I cannot for the life of me figure out how to convert one to the other - why does it not just do it automatically?

As far as I can tell, none of the date functions work on a DateTime, so I can't just brute force it by extracting the year, month and day to create a new Date like this:

DATE(YEAR(MyField__c), MONTH(MyField__c), DAY(MyField__c)

Any advice?  Hopefully there is a workaround - surely a DateTime is just a Date that also has a timestamp attached to it...

Thanks
  • November 28, 2008
  • Like
  • 0
I have developed a simple application for managing, measuring and reporting on SLAs, and would like to understand the Entitlements application better to decide whether I should drop my own app and migrate to the Entitlements one.  I would also like to see the Entitlement apps source code as it is actual working code and might give me some hints as to what I'm doing wrong in a particular area of my own app.

The source code is however not available (shows as Hidden in Eclipse) - would the developer be willing to share it with the community as an educational tool?

Thanks!
Allan Mertner
  • November 14, 2008
  • Like
  • 0
I have an email template that goes out when a case changes; I would like to include the most recent case comment in the email.

Does anyone know what the merge field for this is?

Thanks!


  • October 14, 2008
  • Like
  • 0
I am trying to create a formula field to hold the day of week; the cookbook helpfully suggests the following nifty formula:

MOD(MyField__c - DATE(1900, 1, 7), 7)

Unfortunately, this only works if MyField__c is a Date field, and not if it is a DateTime field.  It seems silly, but I cannot for the life of me figure out how to convert one to the other - why does it not just do it automatically?

As far as I can tell, none of the date functions work on a DateTime, so I can't just brute force it by extracting the year, month and day to create a new Date like this:

DATE(YEAR(MyField__c), MONTH(MyField__c), DAY(MyField__c)

Any advice?  Hopefully there is a workaround - surely a DateTime is just a Date that also has a timestamp attached to it...

Thanks
  • November 28, 2008
  • Like
  • 0
Hi,
 
Im tring to create a case in an apex class. I want to force the case assignment rules to work.
 
So tried using AssignmentRuleHeader which can be used to for this prupose,but getting an error saying invalid type.
 
Is it possible to force assignment rules to trigger in apex code.
 
Can any one please suggest me any work around or alternative for this.
 
Any pointers will be of great help.
 
Thanks in advance.
  • July 24, 2008
  • Like
  • 0
Hi All,
 
We have an integration system that has been running for more than 2 years without any problems. The system creates contacts / leads...etc via the API.
 
Two days ago we started getting the XML response below everytime we try to create leads....the message says: :insufficient access rights on cross-reference id
 
Did something chnage in the API recently ? our code hasnt been updated for two years...would it be some new permissions ?
 
Thanks,
 
 
The XML response looks like this:
 
  <?xml version="1.0" encoding="UTF-8" ?>
- <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
- <soapenv:Body>
- <createResponse xmlns="urn:partner.soap.sforce.com">
- <result>
- <errors>
  <fields xsi:nil="true" />
  <message>insufficient access rights on cross-reference id</message>
  <statusCode>INSUFFICIENT_ACCESS_ON_CROSS_REFERENCE_ENTITY</statusCode>
  </errors>
  <id xsi:nil="true" />
  <success>false</success>
  </result>
  </createResponse>
  </soapenv:Body>
  </soapenv:Envelope>
 
 
  • July 17, 2007
  • Like
  • 0