4

The basic premise of this code, for those of you who have not seen my 600+ other posts about it, is that it creates a Contact whenever someone creates a User. Certain fields of the contact are then automatically populated using the same fields from the User.

I thought I finally finished this code, when I got the Mixed DML Operation error after some more extensive testing. When I create a new user with a "Chatter Free" license, no Role, and the "Chatter Free User" profile, it goes through perfectly, even brought a tear to my eye. However, when I create a User with any other license, a role, and any other profile, I get the above error (this brought many more tears to my eye).

I'm not sure if I actually need a workaround, or if there is some small, stupid problem with my code. Hopefully someone here can offer some insight. I tried using a @future class, as well as system.runas (Sorry about all the comments, I'm very new to this and that's how I keep my thought process straight while learning).

here is my CLASS

public class UserContact {   
//Create the class and grab the IDs needed
 public static void createUserContact(set<ID> recordIDs){
//Grab all the required fields from user, essential to test class

    list<user> users = [SELECT ID, Firstname, Lastname, email, name, username, UserRoleId, CommunityNickname, TimeZoneSidKey, LocaleSidKey, EmailEncodingKey, ProfileId, LanguageLocaleKey 
                        FROM user
                        WHERE ID in :recordIDs];

        String accountName = 'Company Internal';
        //In this query you want to "LIMIT 1" to avoid more than 
        //1 record returning
        Account Accountx = [SELECT ID
                            FROM account
                             Where Name = :accountName
                            LIMIT 1];

    //Create a list to hold the contacts being created
    List<Contact> contactsToCreate = new List<Contact>();

     //Iterate through each field we want to automatically populate
    for (user x: users){

        //Assign the email related to the user grabbed to a string so 
        //we can reference it later.
        //If this was not done, we would get an error for trying to 
        //mix an SObject with a String.
        String userEmail = x.Email;

        //If statement containing the contact creation.  If 
        //the parameters are not met, the user is still
        //created, but a contact is not.
        if(userEmail.endsWith('@company.com')){
        Contact userCon = new contact(
        AccountID = AccountX.id,    
        FirstName = x.FirstName,
        LastName = x.LastName,
        Username__C = x.Username,
        email = userEmail);
        contactsToCreate.add(userCon);
        insert contactsToCreate;                
        }
        else{
            system.debug('Email not internal, Contact not created');
        }}}}

Here is what I tried with the @Future method. The error I got was... "Variable does not exist: recordIDs" I played around with this for quite a while, so this example may not be the closest I got. If I remember correctly, my problem was referencing the lists I got in the first class.

public class UserContact {   
//Create the class and grab the IDs needed
 @future public static void createUserContact(set<ID> recordIDs){
//Grab all the required fields from user, essential to test class

    list<user> users = [SELECT ID, Firstname, Lastname, email, name, username, UserRoleId, CommunityNickname, TimeZoneSidKey, LocaleSidKey, EmailEncodingKey, ProfileId, LanguageLocaleKey 
                        FROM user
                        WHERE ID in :recordIDs];

        String accountName = 'Company Internal'; //This is where you will provide your name (could be any other field)
        //In this query you want to "LIMIT 1" to avoid more than 1 record returning
        Account Accountx = [SELECT ID
                            FROM account
                             Where Name = :accountName
                            LIMIT 1];
public class insertContactClass{
    public void insertContactMethod(){
    //Create a list to hold the contacts being created
    List<Contact> contactsToCreate = new List<Contact>();

    UserContact.createUserContact(recordIDs);

     //Iterate through each field we want to automatically populate
    for (user x: users){
        String userEmail = x.Email;
        if(userEmail.endsWith('@company.com')){
        Contact userCon = new contact(
        AccountID = AccountX.id,    
        FirstName = x.FirstName,
        LastName = x.LastName,
        Username__C = x.Username,
        email = userEmail);
        contactsToCreate.add(userCon);
        insert contactsToCreate;                
        }
        else{
            system.debug('Email not internal, Contact not created');
        }}}}}

Here is the TRIGGER:

trigger NewUserCreatedTrigger on User (after insert) {
    //Creates an array to hold the users caused by trigger firing
    user[] users = trigger.new;
    //Creates a map of records being inserted, then keyset returns the IDs for all records in the map
    UserContact.createUserContact(Trigger.newMap.keySet());
    }
7
  • Daniel.. Can you please update your question with how you have tried with @future method ? .... Generally future method resolve mix dml issue. – Ratan Paul Mar 31 '16 at 12:49
  • Absolutely! Just a moment. – Daniel Sassone Mar 31 '16 at 12:52
  • Can you update stack strace? – Kiran Machhewar Mar 31 '16 at 12:58
  • Daniel I can see you are just inserting contact records in above class? is this class fired from user trigger ? – Ratan Paul Mar 31 '16 at 13:06
  • It is, sorry, didn't thing the trigger would be relevant, updating question to include trigger. – Daniel Sassone Mar 31 '16 at 13:09
5

So this is the code may work

Future method should work for DML error.

Trigger

trigger NewUserCreatedTrigger on User (after insert) {
    //Creates an array to hold the users caused by trigger firing
    user[] users = trigger.new;
    //Creates a map of records being inserted, then keyset 
    //returns the IDs for all records in the map
    UserContact.createUserContact(Trigger.newMap.keySet());
}

Handler

public class UserContact {   
    //Create the class and grab the IDs needed
     @future 
     public static void createUserContact(set<ID> recordIDs){
    //Grab all the required fields from user, essential to test class

        list<user> users = [SELECT ID, Firstname, Lastname, email, name,
                                   username, UserRoleId, CommunityNickname, 
                                   TimeZoneSidKey, LocaleSidKey, EmailEncodingKey, 
                                   ProfileId, LanguageLocaleKey 
                              FROM user
                             WHERE ID in :recordIDs];
        //This is where you will provide your name (could be any other field)
        String accountName = 'Company Internal'; 
        //In this query you want to "LIMIT 1" to avoid more than 1 record returning
        Account Accountx = [SELECT ID FROM account
                             Where Name = :accountName LIMIT 1];
        List<Contact> contactsToCreate = new List<Contact>();

         //Iterate through each field we want to automatically populate
        for (user x: users){
            String userEmail = x.Email;
            if(userEmail.endsWith('@company.com')){
            Contact userCon = new contact(
            AccountID = AccountX.id,    
            FirstName = x.FirstName,
            LastName = x.LastName,
            Username__C = x.Username,
            email = userEmail);
            contactsToCreate.add(userCon);

            }
            else{
                system.debug('Email not internal, Contact not created');
            }
        }
        insert contactsToCreate;//make DML outside of for loop 
    }
}
3
  • 1
    Incredible, one word.... Well I feel like an idiot after spending 3-4 hours on this, but thank you, it seems to be working perfectly! – Daniel Sassone Mar 31 '16 at 13:35
  • @DanielSassone hahaha. I did nothing, just formatted few line of your code and little bit changes.. And always we are ready to help in community.. :) – Ratan Paul Mar 31 '16 at 13:36
  • For any who stumble across this I will add, that while the coverage is 100%, the test fails with the error "System.DmlException: Insert failed. First exception on row 0; first error: CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY, pse_resource.populateLatitudeAndLongitudeByMailingAddress: execution of AfterInsert caused by: System.AsyncException: Future method cannot be called from a future or batch method: pse_resource.GeoLocationManager.handleResourceChanges(Set<Id>) (pse_resource) : []" ................ I'm working on this right now, if I find a solution, I will post it here as an answer. – Daniel Sassone Apr 1 '16 at 16:58

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.