1

Getting an error with trigger/class.

We have an automated process the creates a user record (from our AD/SSO process). When a User is created, a trigger sends an email to our support group to finalize some manual setup steps, but one of those manual steps is to create a Contact record and relate the User to it. I'm trying to add to our trigger to also create and relate the contact record. However, when it fires, I get the following error.

Error: Invalid Data. Review all error messages below to correct your data. Apex trigger NewUserCreatedTrigger caused an unexpected exception, contact your administrator: NewUserCreatedTrigger: execution of AfterInsert caused by: System.DmlException: Insert failed. First exception on row 0; first error: MIXED_DML_OPERATION, DML operation on setup object is not permitted after you have updated a non-setup object (or vice versa): Contact, original object: User: []: Class.NewUserCreatedNotification.createUserContact: line 32, column 1

Here's the TRIGGER:

trigger NewUserCreatedTrigger on User (after insert) {
    user[] users = trigger.new;

    NewUserCreatedNotification.sendEmail(users);
    NewUserCreatedNotification.createUserContact(users);
}

And here's the CLASS:

public class NewUserCreatedNotification  {
    public static void sendEmail(user[] newUser){
        for (user u : newUser){
            Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
            String[] toAddresses = new String[] {'SalesforceSupport@companyname.com'};
            mail.setToAddresses(toAddresses); 
            mail.setSenderDisplayName ('Salesforce Support');
            mail.setSubject('A New Salesforce User has been created in ORG-2'); 
            mail.setPlainTextBody(GetMessageBodyText(u.ID)); 
            Messaging.sendEmail(new Messaging.SingleEMailMessage[]{mail});
        }
    }

    public static void createUserContact(user[] newUser){    
        for (user u: newUser){        
            account myFirm = [Select ID from account where name = 'CorpName'];
            Contact userContact = new Contact(
                user__c = u.ID,
                AccountID = myFirm.id,
                FirstName = u.firstname,
                Lastname = u.lastname,
                Title = u.title,
                Department = u.department,
                Phone = u.Phone,
                email = u.email,
                MailingStreet = u.street,
                MailingCity = u.city,
                MailingState = u.state,
                MailingPostalCode = u.postalcode,
                MailingCountry = u.country);
                insert userContact;

        }
    }

    public static string GetMessageBodyText(Id userID){
        user q = [select id, name, firstname, lastname, ProfileID, email,UserRoleID, userrole.name, profile.name, CreatedDate,LastLoginDate,FederationIdentifier from user where id =: userID];
        return ' A new user record for '+ q.name +', has been created in ORG-2.\n Please confirm that this user has the correct security and Role settings.\n\n Name:  '+
                q.name+'\n Email Address:  '+
                q.Email+'\n Profile:  '+
                q.Profile.Name+'\n Role:  '+
                q.UserRole.Name+'\n Federation ID:  '+
                q.FederationIdentifier+'\n Created Date:  '+
                q.CreatedDate;      
    }
}

The email sends fine if I remove the Contact part, so I know that's not it. Thoughts?

2

As the MIXED_DML_OPERATION implies, you can't simply create a user (or other "Setup" type objects), and contacts (or other non-"Setup" type objects) in the same transaction. The usual way to avoid this is to make the function asynchronous.

@future public static void createUserContact(Set<Id> newUserIds){

Note that you can only send "primitives" through a future method, so you'll have to change your trigger slightly:

NewUserCreatedNotification.createUserContact(Trigger.newMap.keySet());

Also, your function doesn't support mass imports of users, so you might want to fix that.

  • 1
    You could also pass the user as a serialized JSON string if needed – Eric Jul 20 '15 at 19:50
  • 1
    @Eric True enough, but that's usually overkill. Yes, you need to do an extra query, but since it's a different context, it usually doesn't matter. – sfdcfox Jul 20 '15 at 20:06
  • Hmmm. I'm not very familiar (yet) with future methods, so that would explain my confusion. What's going on with your trigger? That's all new code to me. Thanks @sfdcfox. – HomerJ Jul 20 '15 at 20:14
  • @sfdcfox - Yup just adding another option for those interested – Eric Jul 20 '15 at 20:15
  • @HomerJ The @future annotation simply delays the function call to a different transaction (i.e. sometime after the user records have been saved). There's limitations on what you can do with this, so i encourage you to take a good read, but really you just need to know that they run "after" the primary transaction that called them. – sfdcfox Jul 20 '15 at 20:33
0

Just following up on this question. Here's the final version of my effort (minus test classes . . .)

Trigger

trigger NewUserCreatedTrigger on User (after insert) {
    user[] users = trigger.new;
    NewUserCreatedNotification.sendEmail(users);
    NewUserCreatedNotification.createUserContact(Trigger.newMap.keySet());
}

Class

public class NewUserCreatedNotification  {

    public static void sendEmail(user[] newUser){
        for (user u : newUser){
            Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
            String[] toAddresses = new String[] {'sa@support.com'};
            mail.setToAddresses(toAddresses); 
            mail.setSenderDisplayName ('Salesforce Support');
            mail.setSubject('A New Salesforce User has been created in ORG-2'); 
            mail.setPlainTextBody(GetMessageBodyText(u.ID)); 
            Messaging.sendEmail(new Messaging.SingleEMailMessage[]{mail});
        }
    }

    public static string GetMessageBodyText(Id userID){
        user q = [select id, name, firstname, lastname, ProfileID, email,UserRoleID, userrole.name, profile.name, CreatedDate,LastLoginDate,FederationIdentifier from user where id =: userID];
        return ' A new user record for '+ q.name +', has been created in ORG-2.\n Please confirm that this user has the correct security and Role settings.\n\n Name:  '+
                q.name+'\n Email Address:  '+
                q.Email+'\n Profile:  '+
                q.Profile.Name+'\n Role:  '+
                q.UserRole.Name+'\n Federation ID:  '+
                q.FederationIdentifier+'\n Created Date:  '+
                q.CreatedDate;      
    }


@future
public static void createUserContact(set<ID> recordIDs){    
        list<user> users = [Select ID, Firstname, Lastname, title,department,phone, email,street,city,state,postalcode,country from user where id IN :recordIDs];
        for (user u: users){        
            account myFirm = [Select ID from account where name = 'CorpName'];
            Contact userContact = new Contact(
                user__c = u.ID,
                AccountID = myFirm.id,
                FirstName = u.firstname,
                Lastname = u.lastname,
                RecordtypeID = '012o0000000Qw66',
                Title = u.title,
                Department = u.department,
                Phone = u.Phone,
                email = u.email,
                MailingStreet = u.street,
                MailingCity = u.city,
                MailingState = u.state,
                MailingPostalCode = u.postalcode,
                MailingCountry = u.country
                );
                insert userContact;

        }
    }
}

Any additional comments are appreciated.

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.