About Me

My photo
PLANO, Texas, United States

Tuesday, May 28, 2013

MIXED_DML_OPERATION

“MIXED_DML_OPERATION, DML operation on setup object is not permitted after you have updated a non-setup object (or vice versa): User, original object: Contact

You can easily run into this error if you are trying to perform DML on setup and non-setup objects in the same transaction. Non-Setup objects can be any one of standard objects like Account or any custom object, whereas a “setup" object is one that must be edited from the setup or builder area of the platform. These objects include the User object, Organization object, Email templates and so on. Here are few examples of the Setup Objects
1.     Group1
2.     GroupMember
3.     QueueSObject
4.     User2
5.     UserRole
6.     UserTerritory
7.     Territory

Error Conditions :
You can get this error in two blow conditions
1.     Non-Test code (Non-Test code means any Apex code that is not written for test cases)
2.     Test Code
      For Non-Test Class  
      Use Future method to resolve this issue.
      I got this error few month back when I was writing the Trigger on Contact whenever someone make Contact Inactive then its associated Owner become inactive.  
 trigger UpdateContactTrigger on Contact (after update)
 {
       List<User> lstUser = new List<User>();
       for(Contact oContact : Trigger.New){
          if(oContact.isActive__c == false && oContact.isActive__ c! =     Trigger.oldMap.get(oContact.Id).isActive__c){
             User oUser = new User(Id=oContact.OwnerId);
             oUser.IsActive = false;
             lstUser.add(oUser);}
            }
                   if(lstUser != null){
                    update lstUser;
               }
         }
       Below are the workaround for this Trigger:
 Trigger :
trigger MakeUserInactiveTrigger on Contact (after update) 
{
    List<Id> sUser = new List<Id>();
   for(Contact oContact : Trigger.New){
       if(oContact.isActive__c == false && oContact.isActive__c !=Trigger.oldMap.get(oContact.Id).isActive__c) {
           sUser.(oContact.OwnerId);
       }
    }
    UpdateUserUtil.updateUser(sUser);
}

Future Class to handle this Exception:

global class UpdateUserUtil
{
    @future    
    public static void updateUser(List<> userId) 
    {     
        List<User> lstUser = new List<User>();
        for( User oUser : [select IsActive,Id   from User  where Id=:userId]) 
        {
                  User u = new User(Id=oUser.Id);
                  u.isActive = false; 
                  lstUser.add(u);
        }
        if (lstUser != null) 
        {    
            upadate lstUser;
        }
}

In this way, we can resolve the issue. So use future method to resolve this error.

     For Test Class  
     Use system.runAs() Method to resolve this issue.
      In Above method, We are using the currently updated User ID in Creation in the Sony Account  Team(Custom Object)
     Public static testMethod void EmailAddressLookup() {
            Test.startTest();
            User uID=SFE_UtilForSFE.CreateUser('newUser','newuserTY@testorg.com','Testing','newuser@testorg.com');
            uID.IsActive = true;
            update uID;
            Account oAccount = SFE_UtilForSFE.CreateAccount('Test Account','678906',null,'AE11');
            Contact oContact = SFE_UtilForSFE.createContact('testContact',oAccount.ID );
            oContact.Email = 'newcontact@testorg.com';
            update oContact;
            Sony_Account_Team__c oSAT=SFE_UtilForSFE.Create_Sony_Account_Team('Testsat',oAccount.id,uID.ID );
           Test.stopTest();
    }
  
So Once I run the test class I got MIXED_DML_OPERATION, DML error and to resolve this error, I used System.RunAs() like below :

public static testMethod void EmailAddressLookup()
{
       Test.startTest();
       User uID=SFE_UtilForSFE.CreateUser('newUser','newuserTY@testorg.com','Testing','newuser@testorg.com');
       uID.IsActive = true;
       update uID;
       List<User> lstUsers = [select Id, ContactId, AccountId from User where Id =:uID.Id];
       if(lstUsers != null && lstUsers.size() > 0)
       {
            User userInstance = lstUsers[0];
            system.runAs(userInstance)
            { 
                Account oAccount = SFE_UtilForSFE.CreateAccount('Test Account','678906',null,'AE11');
                Contact oContact = SFE_UtilForSFE.createContact('testContact',oAccount.ID );
                oContact.Email = 'newcontact@testorg.com';
                 update oContact;
                Sony_Account_Team__c oSAT=SFE_UtilForSFE.Create_Sony_Account_Team('Testsat',oAccount.id,uID.ID );
            }
       }
      Test.stopTest();
}   

So this is what I came out from this error.

Hope this will help in understanding or learning purpose. 

Thanks










Sunday, May 19, 2013

Insufficient Access On Cross_reference_entity Error

I was stuck in my code and getting insufficient_access_on_cross_reference_entity error while writing the trigger and creating the the Account Team Member from the trigger. After spending lot of time and got some good blogs and good information, Finally, i got it sorted out and i hope this post helps someone understand and solve as well.

When does this error happen?
This error normally happens when a DML operation is performed. Meaning that, whenever an Insert or Update is performed. You won't see this error when you save your APEX class or TRIGGER, because this error happens at RUNTIME.

What does this error mean?
This error means that you are trying to do an operation which is not supposed to be done, or the operation you are about to perform is not permitted according to the Sharing and Security settings defined. This error message does NOT always mean that you lack access to do the operation, even though it might be the case sometimes. So, even if you are an ADMINISTRATOR you may get this message.

Possible Causes:
Let's take some scenario's and analyze.

 Scenario 1:  Creating a new record (Account/Contact/...) and setting the Owner. Applies to updating records as well.

So, in your code you create some records. By Default the creator of the record is the Owner. You want to change this and you modify the OwnerId column of the newly created records. You make "User X" as the Owner. Now when you run the code, you get the Error below:
System.DmlException: Insert failed. First exception on row 2; first error: INSUFFICIENT_ACCESS_ON_CROSS_REFERENCE_ENTITY, insufficient access rights on cross-reference id: []
Things to check:
  *Check that the Running user (in this case you) has access to the object being operated. Check that he has CREATE privileges on the object. This is optional, but is always better to start from here
  *Check that the Owner ie User X has CREATE permission on the object. Check that his profile has the CREATE permission on the particular object. He might not be having it, grant him permission and the issue is resolved.

 Scenario 2:
Creating or Updating an Opportunity (just for an example,  might be any object). Setting the related Account of the Opportunity.

So, let's say that you create 5 Opportunities and you set the Owner to "User X". You set the Account to "Account X". When your code tries to insert these 5 opportunities, it fails and you get the same error message.
System.DmlException: Insert failed. First exception on row 2; first error: INSUFFICIENT_ACCESS_ON_CROSS_REFERENCE_ENTITY, insufficient access rights on cross-reference id: []
Reason:
This is because "User X" does not have access to "Account X". When you try to create an Opportunity for "Account X" that he does not have access to the code fails. Either grant access to "User X" for "Account X" manually or through code and then do the Insert.

Scenario 3:
The Sharing Object.

This is a bit complex to get at. atleast for me. You might be aware that every object has its own Shareobject. So, Account has AccountShare and Customobj__c has Customobj__Share
When you insert or update records in this object you might receive the same error message. There are a number of reasons for this to happen. 

    *If you are trying to share "Record X" with "User Y" and you yourself do not have access to "Record x", this error happens.
    *If you are trying to share "Record X" with "User Y" and "User Y" does not have access to the object (the profile level permission, create read edit delete), this error happens.
    *If you are trying to share "Record X" with "User Y" and "User Y" already has access to "Record X" this error happens.

Saturday, May 18, 2013

WebService Fuctions

Apex Web Service

Apex class methods can be exposed as custom Force.com Web services API calls. This allows an external application to invoke an Apex web service to perform an action in Salesforce.com. Use the Webservice keyword to define these methods. For
Example:
global class MyWebService {
webService static Id makeContact(String lastName, Account a) {
Contact c = new Contact(lastName = 'Weissman', AccountId = a.Id);
insert c;
return c.id;
}
}
A developer of an external application can integrate with an Apex class containing webService methods by generating a WSDL for the class.To generate a WSDL from an Apex class detail page:
1. In the application navigate to Your Name  Setup  Develop  Apex Classes.
2. Click the name of a class that contains web Service methods.
3. Click Generate WSDL.

Considerations for Using the WebService Keyword:

  • You cannot use the webService keyword when defining a class. However, you can use it to define top-level, outer class methods, and methods of an inner class.
  • You cannot use the webService keyword to define an interface, or to define an interface's methods and variables.
  • System-defined enums cannot be used in Web service methods.
  • You cannot use the webService keyword in a trigger because you cannot define a method in a trigger.
  • All classes that contain methods defined with the webService keyword must be declared as global. If a method or inner class is declared as global, the outer, top-level class must also be defined as global.
  • Methods defined with the webService keyword are inherently global. These methods can be used by any Apex script that has access to the class. You can consider the webService keyword as a type of access modifier that enables more access than global.
  • You must define any method that uses the webService keyword as static.
  • You cannot deprecate web Service methods or variables in managed package code.
  • Because there are no SOAP analogs for certain Apex elements, methods defined with the webService keyword cannot take the following elements as  parameters.While these elements can be used within the method, they also cannot be marked as return values.
    • Maps
    • Sets
    • Pattern objects
    • Matcher objects
    • Exception objects
  • You must use the webService keyword with any member variables that you want to expose as part of a Web service. You should not mark these member variables as static.
  • Salesforce.com denies access to Web service and execute anonymous requests from an AppExchange package that has restricted access.
  • Apex classes and triggers saved (compiled) using API version 15.0 and higher produce a runtime error if you assign a String value that is too long for the field.
To know about other integration API at a glance, pls Click here
To know more about the security aspect of integration, pls Click here

Change Set Deployment Steps

What is Change Set?
Change Set is the deployment tool by which Salesforce Developer/Administrator can upload/deploy the changes to Sandbox (Test Environment) to Production. You can go in Setup>Deploy>Select any option of deployment. You can deploy changes in between Sandbox to Sandbox also.
There are three main topics and steps to understand first before deployment:
1. Outbound Change set:
This is first step to the deployment through Change set. Outbound change set is nothing but creation of connection or select the changes you done in Sandbox like Object, Custom Fields, Validation, Workflow, Classes, Trigger etc. For that you have to follow below steps.
· Login in to Sandbox account of Salesforce.com.
· Go to Setup>Deploy>Outbound Change set: It will show you information on Change set and Outbound/Inbound change set information.
· Press Continue button on this page.
· Click on New button and create the outbound change set. Enter the Name of outbound change set and description of this and Click on Save.
· Once you get outbound change set detail page click Add button on Change Set Components.
· This page will show you Component Type (App, Analytical Snapshot, Apex Class, Apex Sharing Reason, Apex Trigger, Button or Link, Custom Fields, Custom Label, Object, Report Type, Custom Setting, Dashboard, Document, Email Template, Field Set, Folder, Home page Component etc.) Select any of above part and Click on Add To Change Set Button.
· After above step you will get the list of components added on change set component section.
· You can view or add dependencies in this section.
· Click on Add Profile in Profile Setting for included components means you can ass profile your can see or share the changes whatever you have done.
· After completing above steps click on Upload button. This will do the actual deployment to the Production/other Sandbox.
· Select any option from given list of Sandbox and Production.
· Click on Upload button to selected environment.
· After above step you will get Completion Email on your given email id. Means you have successfully uploaded the outbound change set.
2. Deployment Connection:
This connection step will automatically created by Sales force. Which is allows the customization to be copied from one organization to another organization. Your should be login in Production to check above list.
3. Inbound Change Set:
Inbound change set is automatically created once outbound change set it created inbound change set gets the all changes sent by outbound change set.
· Select on inbound change set and get detail of outbound.
· You can view change set components list and deployment history.
· Click on validate it will show validation history in deployment History.
· Click on Deployment after successful validation and can see Deployment History.

Friday, May 17, 2013

Apex REST API Stuck Points

Let me share my experience with REST.
1.  Apex REST supports both XML and JSON for resource formats sent in REST request and responses. By default, Apex REST uses JSON to represent resources. Now here I got the problem with the size of JSON file because SDFC can accept less than 3 MB file. SO What I did, I removed all blank spaces and also we suggested third application developer to send the file less than 3 MB if the file is larger than they will split the file into a smaller size. That helps us a lot.
2.  Always tried to map the exact api name of the field in the JSON that you are accepting from the other side. Otherwise, you need to deserialize the parameter in the method that will unnecessarily increase the complexity.
Ø  For example Below are two formats of JSON and used to update account :
   1.
            {
                 "Name" : "ParentUpdate Manoj11",
                 "Industry" : "Consultant",
                 "Phone" : "9891798737“
            }
  2.
          {
                 "Account_Name" : "ParentUpdate Manoj11",
                 "Account_Industry" : "Consultant",
                 "Account_Phone" : "9891798737",
         }
•So both will work fine but In first we no need to deserialize the JSON because we are mapping with the same API but In the second JSON we need to make this JSON deserialize. That will  unnecessarily make the method complex.

Data Rollback in Apex


Hi Everyone,
I want to share my experience of current problem. Basically it is the minor thing however it’s always good to share your experience with other. First Let  me tell you my scenario. I have three SFDC Custom Object Call Report, Meeting Attendee and Action. Now I have to save these three object on button click on VF page.
So my code was like below:

try {
 insert oCallReport;
  insert oMeetingAttendee;
 insert oAction;
} catch (Exception e) {
  ...
}

This is still alright if there is any error occurs in the first DML insert statement as the error will be caught and the second DML insert statement won't be execute. But, what if there is a problem occurs on the second DML insert statement? This will cause some bad data in your Salesforce if the data in the “oCallReport″ should not be inserted if there is any error occurs on “oAction″.
To overcome this issue, you can implement transaction control into your code as following:

Savepoint sp = Database.setSavepoint();
try {
  insert oCallReport;
  insert oMeetingAttendee;
  insert oAction;
} catch (Exception e) {
  Database.rollback(sp);
}

By doing this, Salesforce will helps you to revert data back to the state before the first DML insert statement is executed. But again I got one issue that salesforce.com sets the ID of a record before the insert as you can’t set values after inserts as the object would become read-only. To overcome this issue I have created clone the record without preserving the Id and assign this clone back to the original object .Something like this:

Savepoint sp = Database.setSavepoint();
try{                        
insert oCallReport;
  insert oMeetingAttendee;
 insert oAction;
}catch(exception e){
                Database.rollback(sp);
              oCallReport = oCallReport.clone(false);
                return null;                        
}

The false parameter in the clone method makes an exact copy of the object without preserving the Id. So the next time the user attempts to save, the Id is null, and there should be no issues.