Thursday, June 13, 2013

PUBLISHER ACTIONS in Summer 13 Features

Add and Customize Actions in the Chatter Publisher
Available in: Group, Professional, Enterprise, Unlimited, Contact Manager, Database.com, and Developer Editions





The Chatter publisher is the tool at the top of each feed that lets users post comments and polls and share files and links. With publisher actions, we’ve made the publisher more useful and more flexible. The publisher actions feature lets you create actions and add them to the Chatter publisher on the home page, the Chatter tab, and record detail pages. It also allows you to customize the order in which the standard Chatter actions appear, including Post, File, Link, and Poll.

Actions Overview
Actions let users do more in Chatter, like create records directly in the feed. There are two general types of actions: create actions and custom actions.
Create actions let users create records. They’re different from the New button on object tabs and the Quick Create and Create New features on the home page. Create actions respect your validation rules and field requiredness, and you can choose each action’s fields.
Custom actions are Visualforce pages with functionality you define. For example, you might create a custom action to let users write comments longer than 1000 characters.
Note: For both types of actions, you can create either object-specific actions or global actions.















Enabling Publisher Actions
1. From Setup, click Customize > Chatter > Settings.
2. Click Edit.
3. Select Enable Publisher Actions.
4. Click Save.

New Feed Tracking Option with Publisher Actions
When you enable publisher actions, a new option, All Related Objects, appears on the feed tracking page for account, campaign, case, contact, custom objects, lead, and opportunity. This option lets users view feed activity for objects that have a parent-child or lookup relationship to the host object.
1. From Setup, click Customize > Chatter > Feed Tracking > All Related Objects.

Object-Specific (Create Action)
1. From Setup, click Customize > Lead > Buttons, Links, and Actions
2. From Setup, click Customize > Lead > Page Layouts


































Global Action (Create Actions)
1. From Setup, click Create > Global Actions
2. From Setup, click Customize > Chatter > Publisher Layouts
3. From Setup, click Customize > Account > Buttons, Links, and Actions














































Object-Specific Custom Actions 
Object-specific custom actions let users interact with or create records that have a relationship to an object record. The Visualforce page for an object-specific custom action must include the standard controller for the relevant object. For example, if you want to create a custom action that lets users import a contact’s Twitter profile and add that information to a contact record, you need to use the standard contact controller.
1. From Setup, click Customize > Lead > Buttons, Links, and Actions
2. From Setup, click Customize > Lead > Page Layouts

Global Custom Actions
Create a global custom action for tasks that don’t require users to interact with or create records that have a relationship to a specific object. Visualforce pages you want to use as global custom actions can’t use standard controllers. For example, if you want to create a custom action that lets users enter a street address and see a map, the local time, and the local weather, create a Visualforce page that doesn’t use any of the standard controllers, and add it as a custom global action.
1. From Setup, click Create > Global Actions
2. From Setup, click Customize > Chatter > Publisher Layouts
3. From Setup, click Customize > Lead > Buttons, Links, and Actions















Supported Objects
You can create object-specific actions on these objects:
• Account
• Campaign
• Case
• Contact
• Custom objects
• Lead
• Opportunity
• User
You can create actions that let users create these kinds of records:
• Account
• Asset
• Campaign
• Case
• Contact
• Contract
• Custom objects
• Entitlement
• Event (without invitees)
• Lead
• Opportunity
• Service Contract
• Task

Note: When you create an object-specific action, you can choose as a target object only event, task, or any object that has a parent-child or lookup relationship to the host object.


Wednesday, June 12, 2013

Syncing Salesforce Quotes using Apex

Wondering how to sync quote with an opportunity using Apex? Did you tried updating the “IsSyncing” field and it showed an error saying field is not writable? If yes then here is the solution for you. At the Opportunity level you have a field named as “SyncedQuoteId” and if you update this field with the desired quote id, then salesforce automatically sync this quote with opportunity.

Opportunity.SyncedQuoteId = (desired) Quote.id;
update opportunity;

In the same way do you wanna sync your custom fields for quote and quote line item then here is the application from appexchange.

App Exchange App's:
Custom Quote Sync (Managed)

Custom Quote Sync (Unmanaged)

Tuesday, June 4, 2013

Integrate Custom Apex WebService with .Net Using Force.com SOAP API

This blog post will describe that how to create an Apex web service and integrate it with .Net App using Force.com SOAP API.

Below is an example of how to integrate Apex webservice with Force.com SOAP API and .Net Apps using Visual Studio.

To get started, we'll run through the following steps:

1) Create an Apex webService class

Apex Code:
/********************************************************************************/
global class SuperClass{
    global class RequestClass{
        webService String accountName;
    }
    global class ResponseClass{
        webService String responseResultID;
        webService String responseResultName;
        webService String responseResultRecordType;
    }
    webService static ResponseClass behaviourOfWebService(RequestClass reqClass){
        Account acct = new Account();
        acct.Name = reqClass.accountName;
        insert acct;
        ResponseClass resClass = new ResponseClass();
        resClass.responseResultID = acct.Id;
        resClass.responseResultName = acct.Name;
        resClass.responseResultRecordType = acct.RecordTypeId;
        return resClass;
    }
}
/********************************************************************************/

2) Download the "Enterprise WSDL" or "Partner WSDL" from your salesforce.com organization.

Log in into salesforce.com organization -> User Name ->  Setup -> App Setup section -> Develop -> API -> click on Generate Enterprise or Partner WSDL -> Save file with extension ".wsdl" as "EnterpriseWSDL.wsdl" or "PartnerWSDL.wsdl".

3) Download the Apex class "SuperClass" wsdl.

Log in into salesforce.com organization -> User Name ->  Setup -> App Setup section -> Develop -> Apex Classes-> click on SuperClass -> click Generate WSDL -> Save file with extension ".wsdl" as "WebServiceWSDL.wsdl".

4) Create a .Net App in Visual Studio

Open Visual Studio -> File -> New -> Project -> Visual C# -> Windows -> Console Application -> Name it "CustomWebService" -> OK

5) Copy the path where your both WSDL's are present.

For Example:
C:\Users\Administrator\Desktop\EnterpriseWSDL.wsdl
C:\Users\Administrator\Desktop\WebServiceWSDL.wsdl

6) Add these WSDL's as Web References in Visual Studio.

In Solution Explorer -> right click on project -> Add Web Reference -> add the path in URL for Enterprise WSDL -> Web reference name = EnterpriseWSDL -> click Add Reference.

For Example:
C:\Users\Administrator\Desktop\EnterpriseWSDL.wsdl

In Solution Explorer -> right click on project -> Add Web Reference -> add the path in URL for Apex Class WSDL -> Web reference name = WebServiceWSDL -> click Add Reference.

For Example:
C:\Users\Administrator\Desktop\WebServiceWSDL.wsdl

Your Project in Visual Studio will look like below screenshot:




















7) Write C# Code to integrate with Force.com Apex Web Service using Force.com SOAP API

C# Code:
/********************************************************************************/
using System;
using System.Collections.Generic;
using System.Text;

namespace CustomWebService
{
    class Program
    {
        static void Main(string[] args)
        {
            /*
            When I add WebReferences I named Apex Class WSDL as "WebServiceWSDL"
            When I add WebReferences I named Enterprise WSDL as "EnterpriseWSDL"
            */

            //Salesforce.com Credentials
            string userName = "Your User Name";
            string userPassword = "Your Password + Your Security Token";

            //Apex Main Class WSDL Object
            WebServiceWSDL.SessionHeader webServiceWSDLSession = new WebServiceWSDL.SessionHeader();

            //Enterprise WSDL Object
            EnterpriseWSDL.LoginResult enterpriseWSDLLoginResult = new EnterpriseWSDL.LoginResult();
            EnterpriseWSDL.SforceService enterpriseWSDLSforceService = new EnterpriseWSDL.SforceService();

            //Make Sessions and Login successfully
            enterpriseWSDLLoginResult = enterpriseWSDLSforceService.login(userName, userPassword);
            enterpriseWSDLSforceService.Url = enterpriseWSDLLoginResult.serverUrl;
            webServiceWSDLSession.sessionId = enterpriseWSDLLoginResult.sessionId;

            //Number of records to process
            int numOfRecords = 1;

            //Apex Inner Class RequestClass object to fill the variables of RequestClass and send it to Salesforce.com
            WebServiceWSDL.RequestClass reqClass = new WebServiceWSDL.RequestClass();
            WebServiceWSDL.RequestClass[] reqClassList = new WebServiceWSDL.RequestClass[numOfRecords];
         
            //Traverse each record
            for (int i = 0; i < numOfRecords; i++)
            {
                reqClass = new WebServiceWSDL.RequestClass();
                reqClass.accountName = "Account Name from Dot Net";
                reqClassList[i] = reqClass;
            }

            //Apex Main Class Object
            WebServiceWSDL.SuperClassService mainWSDLClassObject = new WebServiceWSDL.SuperClassService();
            mainWSDLClassObject.SessionHeaderValue = new WebServiceWSDL.SessionHeader();
            mainWSDLClassObject.SessionHeaderValue.sessionId = webServiceWSDLSession.sessionId;
            //mainWSDLClassObject.behaviourOfWebService(reqClassList[0]);

            //Retrieve the Response of Apex class
            WebServiceWSDL.ResponseClass responseClassObject = new WebServiceWSDL.ResponseClass();
            responseClassObject = mainWSDLClassObject.behaviourOfWebService(reqClassList[0]);

            //Response Result
            Console.WriteLine("ID" + responseClassObject.responseResultID);
            Console.WriteLine("NAME" + responseClassObject.responseResultName);
            Console.WriteLine("RECORD TYPE" + responseClassObject.responseResultRecordType);
            Console.ReadLine();

        }
    }
}
/********************************************************************************/

Integrate Custom Apex WebService with Java Using Force.com Web Services Connector (WSC)

This blog post will describe that how to create an Apex web service and integrate it with Java App using Force.com Web Services Connector (WSC).

Below is an example of how to integrate Apex webservice with Force.com SOAP API  and Java Apps using Eclipse IDE.

To get started, we'll run through the following steps:

1) Create an Apex webService class

Apex Code:
/********************************************************************************/
global class SuperClass{
    global class RequestClass{
        webService String accountName;
    }
    global class ResponseClass{
        webService String responseResultID;
        webService String responseResultName;
        webService String responseResultRecordType;
    }
    webService static ResponseClass behaviourOfWebService(RequestClass reqClass){
        Account acct = new Account();
        acct.Name = reqClass.accountName;
        insert acct;
        ResponseClass resClass = new ResponseClass();
        resClass.responseResultID = acct.Id;
        resClass.responseResultName = acct.Name;
        resClass.responseResultRecordType = acct.RecordTypeId;
        return resClass;
    }
}
/********************************************************************************/

2) You need to install "Java JDK (6 or 7)" in your machine.
3) Second you need to check whether "Java JDK (6 or 7)" has installed successfully or not?

Just type in the command prompt:
java -version

You will the result same as below:
It will show you:
java version "1.6.0_35"
Java(TM) SE Runtime Environment (build 1.6.0_35-b10-428-10M3811)
Java HotSpot(TM) 64-Bit Server VM (build 20.10-b01-428, mixed mode)

Note: If you get error then change your command prompt directory to the Java bin directory and test it again.

For Example:
C:\Program Files\Java\jdk1.6.0_45\bin> java -version

4) Download the "Enterprise WSDL" or "Partner WSDL" from your salesforce.com organization.

Log in into salesforce.com organization -> User Name ->  Setup -> App Setup section -> Develop -> API -> click on Generate Enterprise or Partner WSDL -> Save file with extension ".wsdl" as "enterprise.wsdl" or "partner.wsdl".

5) Download the Apex class "SuperClass" wsdl.

Log in into salesforce.com organization -> User Name ->  Setup -> App Setup section -> Develop -> Apex Classes-> click on SuperClass -> click Generate WSDL -> Save file with extension ".wsdl" as "mywebservice.wsdl".

6) Download the Web Services Connector (WSC) "wsc-20.jar" file from the URL "http://code.google.com/p/sfdc-wsc/downloads/list".

7) Now create JAR (Java ARchive) files for "enterprise.wsdl", "partner.wsdl" and "mywebservice.wsdl" files.

8) Copy the "tools.jar" file from the path where you have installed "Java JDK (6 or 7)".

For Example:
"C:\Program Files\Java\jdk1.6.0_45\lib\tools.jar" and paste it in your "E:\" drive. I am doing this just for safe side so we will not have any conflicts.

Note: 
(a) "tools.jar" and "wsc-20.jar" file both must be in the same directory otherwise, you will get an error "classpath: java.io.FileNotFoundException".
(b) Just for safe side I would recommend that your "E:\" drive must have "tools.jar", "wsc-20.jar", "enterprise.wsdl", "partner.wsdl" and "mywebservice.wsdl".

9) Execute the following commands in Command Prompt to generate JAR File "JAR file (Java ARchive)" for "enterprise.wsdl" or "partner.wsdl" and "mywebservice.jar".

For Example:
java -classpath wsc-XX.jar com.sforce.ws.tools.wsdlc enterprise.wsdl enterprise.jar

Real Example:
Generate enterprise.jar
java -classpath E:\tools.jar;E:\wsc-20.jar com.sforce.ws.tools.wsdlc E:\enterprise.wsdl E:\enterprise.jar

Generate partner.jar
java -classpath E:\tools.jar;E:\wsc-20.jar com.sforce.ws.tools.wsdlc E:\partner.wsdl E:\partner.jar

Generate mywebservice.jar
java -classpath E:\tools.jar;E:\wsc-20.jar com.sforce.ws.tools.wsdlc E:\mywebservice.jar.wsdl E:\mywebservice.jar

Now these commands will generate "enterprise.wsdl" or "partner.wsdl" and "mywebservice.jar".

Generate Java Code in Eclipse (Creating an Enterprise WSDL Application)

Now that your environment is ready to go, it's time to build a test application to see how things are working. Most developers build client applications with the enterprise WSDL, so we’ll start with that one first.
In Eclipse, complete the following steps to build a Java application based on the enterprise WSDL.
  1. Create a new Java project named “WSC - Enterprise” (click File | New | Java Project).
  2. Add the wsc-XX.jar and enterprise.jar to the project (click Project | Properties | Java Build Path | Libraries or External Libraries, then add the JARs to the project.
  3. Add a new folder, wsc, to the src folder in your app (right-click src in Package Explorer, then click New | Folder).
  4. Create a new class src/wsc/CallWS.java and paste in the code from the code listing that follows.
  5. Replace the stub user credentials in the code with your own user name and password with security token for the appropriate static members, then save your source code.
  6. Run the application.
Your Project in Eclipse IDE will look like below screenshot:
















Java Code:
/********************************************************************************/
package wsc;

import com.sforce.soap.SuperClass.SoapConnection;
import com.sforce.soap.SuperClass.Connector;
import com.sforce.soap.SuperClass.RequestClass;
import com.sforce.soap.SuperClass.ResponseClass;

import com.sforce.ws.ConnectionException;
import com.sforce.ws.ConnectorConfig;
import com.sforce.soap.enterprise.*;


public class CallWS {


  static final String USERNAME = "Your User Name";
  static final String PASSWORD = "You User Password + Your Security Token";

  static SoapConnection MyWebserviceWSconnection;
  static EnterpriseConnection enterpriseConnection;

  public static void main(String[] args) {

    ConnectorConfig config = new ConnectorConfig();
    config.setUsername(USERNAME);
    config.setPassword(PASSWORD);


    try {

      //create a connection to Enterprise API -- authentication occurs
      enterpriseConnection = com.sforce.soap.enterprise.Connector.newConnection(config);    
      // display some current settings
      System.out.println("Auth EndPoint: "+config.getAuthEndpoint());
      System.out.println("Service EndPoint: "+config.getServiceEndpoint());
      System.out.println("Username: "+config.getUsername());
      System.out.println("SessionId: "+config.getSessionId());


      //create new connection to exportData webservice -- no authentication information is included
      MyWebserviceWSconnection = Connector.newConnection("","");
      //include session Id (obtained from enterprise api) in exportData webservice
      MyWebserviceWSconnection.setSessionHeader(config.getSessionId());
      
      RequestClass reqClass = new RequestClass();
      reqClass.setAccountName("Account Created By Java Program");
      
      ResponseClass resClass = new ResponseClass();
      resClass = MyWebserviceWSconnection.behaviourOfWebService(reqClass);
      System.out.println("Record ID ---"+resClass.getResponseResultID());
      System.out.println("Record Name ---"+resClass.getResponseResultName());
      System.out.println("Record Record Type ---"+resClass.getResponseResultRecordType());

      //String result = MyWebserviceWSconnection.receiveData("test");
      //System.out.println("Result: "+result);


    } catch (ConnectionException e1) {
        e1.printStackTrace();
    }  
  }
}
/********************************************************************************/

Helpful Links:
http://wiki.developerforce.com/page/Introduction_to_the_Force.com_Web_Services_Connector
http://code.google.com/p/sfdc-wsc/downloads/list

http://boards.developerforce.com/t5/Java-Development/Trying-to-call-a-simple-Apex-Web-Service-method-from-Java/td-p/206407
http://boards.developerforce.com/t5/General-Development/How-to-Call-WebService-Method-in-Java/td-p/261755
http://kperisetla.blogspot.com/2011/09/creating-custom-apex-web-service-in.html
http://forums.crmsuccess.com/t5/forums/forumtopicprintpage/board-id/JAVA_development/message-id/5930/print-single-message/false
http://boards.developerforce.com/t5/Java-Development/Problem-Calling-Apex-WebService-from-Java/td-p/206637
http://stackoverflow.com/questions/11204614/access-salesforce-apex-soap-webservice-from-java

Monday, June 3, 2013

Integrate Force.com SOAP API with Java Apps Using Force.com Web Services Connector (WSC)

Services This blog post will describe you that how to integrate Salesforce - Force.com SOAP API with Java Apps.

Below is an example of how to integrate Force.com SOAP API with Java Apps using Eclipse IDE.

Introduction to the Force.com Web Services Connector

The Force.com Web Services Connector (WSC) is a code-generation tool and runtime library for use with Force.com Web services. WSC uses a high-performing Web services client stack implemented with a streaming parser. It is the preferred tool for working with salesforce.com APIs. You can write Java applications with WSC that utilize the Force.com SOAP API, Bulk API, and Metadata API. There are even runtime libraries that let you access the Force.com SOAP API from applications running on Google App Engine.

Using WSC, you can perform operations with a few lines of code that would take many more lines of code with other Web services clients.

This article provides an introduction to WSC. The WSC can be used to invoke any doc-literal wrapped Web service, but in this article we’ll focus on the SOAP API with both the enterprise and partner WSDLs, and the Metadata API. Along the way, you’ll learn how to get started with WSC, and see an example of a console application that demonstrates WSC functionality.

Introduction to Force.com
Force.com has several API's including:
  • Force.com SOAP (Simple Object Access Protocol) API
  • Force.com Metadata API
  • Force.com Bulk API
  • Force.com Streaming API
  • Force.com REST (Representational State Transfer) API
  • Force.com Chatter API
Preparing to Integrate Java Apps with Force.com APIs:
I am assuming you have some experience building Java applications with Force.com APIs.

To get started, we'll run through the following steps:

1) First you need to install "Java JDK (6 or 7)" in your machine.
2) Second you need to check whether "Java JDK (6 or 7)" has installed successfully or not?

Just type in the command prompt:
java -version

You will the result same as below:
It will show you:
java version "1.6.0_35"
Java(TM) SE Runtime Environment (build 1.6.0_35-b10-428-10M3811)
Java HotSpot(TM) 64-Bit Server VM (build 20.10-b01-428, mixed mode)

Note: If you get error then change your command prompt directory to the Java bin directory and test it again.

For Example:
C:\Program Files\Java\jdk1.6.0_45\bin> java -version

3) Download the "Enterprise WSDL" or "Partner WSDL" from your salesforce.com organization.

Log in into salesforce.com organization -> User Name ->  Setup -> App Setup section -> Develop -> API -> click on Generate Enterprise or Partner WSDL -> Save file with extension ".wsdl" as "enterprise.wsdl" or "partner.wsdl".

4) Download the Web Services Connector (WSC) "wsc-20.jar" file from the URL "http://code.google.com/p/sfdc-wsc/downloads/list".

5) Now create JAR (Java ARchive) files for "enterprise.wsdl" and "partner.wsdl" files.

6) Copy the "tools.jar" file from the path where you have installed "Java JDK (6 or 7)".

For Example:
"C:\Program Files\Java\jdk1.6.0_45\lib\tools.jar" and paste it in your "E:\" drive. I am doing this just for safe side so we don't have any conflicts.

Note: 
(a) "tools.jar" and "wsc-20.jar" file both must be in the same directory otherwise, you will get an error "classpath: java.io.FileNotFoundException".
(b) Just for safe side I would recommend that your "E:\" drive must have "tools.jar", "wsc-20.jar", "enterprise.wsdl" and "partner.wsdl".

6) Execute the following command to generate JAR File "JAR file (Java ARchive)" for "enterprise.wsdl" or "partner.wsdl".

For Example:
java -classpath wsc-XX.jar com.sforce.ws.tools.wsdlc enterprise.wsdl enterprise.jar

Real Example:
Generate enterprise.jar
java -classpath E:\tools.jar;E:\wsc-20.jar com.sforce.ws.tools.wsdlc E:\enterprise.wsdl E:\enterprise.jar

Generate partner.jar
java -classpath E:\tools.jar;E:\wsc-20.jar com.sforce.ws.tools.wsdlc E:\partner.wsdl E:\partner.jar

Now these commands will generate "enterprise.wsdl" or "partner.wsdl".

Generate Java Code in Eclipse (Creating an Enterprise WSDL Application)

Now that your environment is ready to go, it's time to build a test application to see how things are working. Most developers build client applications with the enterprise WSDL, so we’ll start with that one first.
In Eclipse, complete the following steps to build a Java application based on the enterprise WSDL.
  1. Create a new Java project named “WSC - Enterprise” (click File | New | Java Project).
  2. Add the wsc-XX.jar and enterprise.jar to the project (click Project | Properties | Java Build Path | Libraries or External Libraries, then add the JARs to the project.
  3. Add a new folder, wsc, to the src folder in your app (right-click src in Package Explorer, then click New | Folder).
  4. Create a new class src/wsc/Main.java and paste in the code from the code listing that follows.
  5. Replace the stub user credentials in the code with your own user name and password with security token for the appropriate static members, then save your source code.
  6. Run the application.
Your Project in Eclipse IDE will look like below screenshot:










/********************************************************************************/
package wsc;

import com.sforce.soap.enterprise.Connector;
import com.sforce.soap.enterprise.DeleteResult;
import com.sforce.soap.enterprise.EnterpriseConnection;
import com.sforce.soap.enterprise.Error;
import com.sforce.soap.enterprise.QueryResult;
import com.sforce.soap.enterprise.SaveResult;
import com.sforce.soap.enterprise.sobject.Account;
import com.sforce.soap.enterprise.sobject.Contact;
import com.sforce.ws.ConnectionException;
import com.sforce.ws.ConnectorConfig;

public class Main {

static final String USERNAME = "YOUR-USERNAME";
static final String PASSWORD = "YOUR-PASSWORD&SECURITY-TOKEN";
  static EnterpriseConnection connection;

  public static void main(String[] args) {

    ConnectorConfig config = new ConnectorConfig();
    config.setUsername(USERNAME);
    config.setPassword(PASSWORD);
    //config.setTraceMessage(true);
 
    try {
   
      connection = Connector.newConnection(config);
   
      // display some current settings
      System.out.println("Auth EndPoint: "+config.getAuthEndpoint());
      System.out.println("Service EndPoint: "+config.getServiceEndpoint());
      System.out.println("Username: "+config.getUsername());
      System.out.println("SessionId: "+config.getSessionId());
   
      // run the different examples
      queryContacts();
      createAccounts();
      updateAccounts();
      deleteAccounts();
   
   
    } catch (ConnectionException e1) {
        e1.printStackTrace();
    }

  }

  // queries and displays the 5 newest contacts
  private static void queryContacts() {
 
    System.out.println("Querying for the 5 newest Contacts...");
 
    try {
     
      // query for the 5 newest contacts    
      QueryResult queryResults = connection.query("SELECT Id, FirstName, LastName, Account.Name " +
      "FROM Contact WHERE AccountId != NULL ORDER BY CreatedDate DESC LIMIT 5");
      if (queryResults.getSize() > 0) {
        for (int i=0;i<queryResults.getRecords().length;i++) {
          // cast the SObject to a strongly-typed Contact
          Contact c = (Contact)queryResults.getRecords()[i];
          System.out.println("Id: " + c.getId() + " - Name: "+c.getFirstName()+" "+
              c.getLastName()+" - Account: "+c.getAccount().getName());
        }
      }
   
    } catch (Exception e) {
      e.printStackTrace();
    }  
 
  }

  // create 5 test Accounts
  private static void createAccounts() {
 
    System.out.println("Creating 5 new test Accounts...");
    Account[] records = new Account[5];
 
    try {
     
      // create 5 test accounts
      for (int i=0;i<5;i++) {
        Account a = new Account();
        a.setName("Test Account "+i);
        records[i] = a;
      }
   
      // create the records in Salesforce.com
      SaveResult[] saveResults = connection.create(records);
   
      // check the returned results for any errors
      for (int i=0; i< saveResults.length; i++) {
        if (saveResults[i].isSuccess()) {
          System.out.println(i+". Successfully created record - Id: " + saveResults[i].getId());
        } else {
          Error[] errors = saveResults[i].getErrors();
          for (int j=0; j< errors.length; j++) {
            System.out.println("ERROR creating record: " + errors[j].getMessage());
          }
        }  
      }
   
    } catch (Exception e) {
      e.printStackTrace();
    }  
 
  }
}
/********************************************************************************/
Helpful Links:
http://wiki.developerforce.com/page/Introduction_to_the_Force.com_Web_Services_Connector
http://code.google.com/p/sfdc-wsc/downloads/list

http://boards.developerforce.com/t5/Java-Development/Trying-to-call-a-simple-Apex-Web-Service-method-from-Java/td-p/206407
http://boards.developerforce.com/t5/General-Development/How-to-Call-WebService-Method-in-Java/td-p/261755
http://kperisetla.blogspot.com/2011/09/creating-custom-apex-web-service-in.html
http://forums.crmsuccess.com/t5/forums/forumtopicprintpage/board-id/JAVA_development/message-id/5930/print-single-message/false
http://boards.developerforce.com/t5/Java-Development/Problem-Calling-Apex-WebService-from-Java/td-p/206637
http://stackoverflow.com/questions/11204614/access-salesforce-apex-soap-webservice-from-java

Sunday, June 2, 2013

How to write Batch Apex along with Visualforce page?

Batch Apex with Visualforce

Visualforce Controller Code:
public class RunBatchController{
public void RunBatch(){
// DeletionBatch is the batch you implemented.
DeletionBatch batch = new DeletionBatch();
Database.executeBatch(batch);
}
}

Visualforce Page Code:
<apex:page controller="RunBatchController">
   <apex:commandButton action="{!RunBatch}" value="Delete Records"/>
</apex:page>

Play with Pricebook & Products & Opportunity Objects

Play with Pricebook & Products & Opportunity Objects

There are 5 standard objects involved around it:
  • Pricebook
  • Product
  • PricebookEntry
  • Opportunity
  • Opportunity Product
Scenario without Multi-Currency enabled:
  • First you setup a Pricebook. If you have different prices based on customer type or deal then you would need to create custom Pricebooks (e.g. online tutoring, in-home tutoring, and school tutoring).
  • Second you would need to create a Product.
  • Third you would need to associate Product with the Pricebook. If you have only a standard Pricebook then you just need to associate it with the Product otherwise, you would need to associate with the custom Pricebook too. But in order to associate custom Pricebook you have to associate standard Pricebook with the Product first. This process create a PricebookEntry record.
  • Fourth you would need to create an Opportunity (Note: There is a field PricebookId on Opportunity that says this Opportunity has associated to whcih Pricebook?).
  • Fifth you would need to add Opportunity Products. First time when you add it will prompt you to choose the Pricebook (e.g. standard or custom Pricebooks will be shown in the picklist).
  • After that all the Products will be shown which are associated with the Pricebook which user has chosen already. After creating Opportunity Products, PricebookId field will be populated on Opportunity. Now going forward user will only add those Products which are associated with that Pricebook (Note: Opportunity can have only one PriceBook associated with it).
  • If in future user want to associate Opportunity with different Pricebook they need to click on the button "Choose Pricebook" (Note: it will delete all the previous Opportunity Product records.).
Note: You can not add 2 Products with different Pricebooks on the same Opportunity.

Scenario with Multi-Currency enabled:
  • Let's assume that your organization work on 2 currencies "USD" and "EUR".
  • First You setup standard and custom Pricebook in all currencies.
  • Second you would need to setup Products (Currency ISO on Product will not do anything so, you can remove from the page layout).
  • Third you would need to associate Product with the Pricebook.
  • Fourth you would need to create an Opportunity and select a Currency ISO field "USD" or "EUR" (Note: This will tell you that you will only Products which have been setup in that Currency ISO code).
  • Fifth you would need to add Opportunity Products.
Note: All currency amounts are displayed in the default currency of the record as well as in the personal currency of the user. When you change the default currency of a record, currency amounts are not converted; they simply display with the new currency code.

Tuesday, May 21, 2013

Delete Custom Button in List Views

Salesforce.com does not provide the functionality to delete records in batch / bulk size in List Views. Like in Salesforce.com we have standard / out-of-the box buttons "Change Owner", "Accept" and "Change Status". Unfortunately we do not have "Delete Lead" button in List Views but you can add it in List Views by just writing a small code snippet in JavaScript.

Sample Code:



{!REQUIRESCRIPT("/soap/ajax/27.0/connection.js")}
{!REQUIRESCRIPT("/soap/ajax/27.0/apex.js")}

//To show the alert pop up on the page
function log(message) {
alert(message);
}

//string for the URL of the current page
var url = parent.location.href;
//grabs the Lead records that the user is requesting to delete
var records = {!GETRECORDIDS($ObjectType.Lead)};
//if the button was clicked but there was no record selected
if (records[0] == null) {
//alert the user that they didn't make a selection
alert("Please select at least one record to update.");
} else {
//otherwise, there was a record selection
var delResult = sforce.connection.deleteIds(records);
if (delResult[0].getBoolean("success")) {
log(records.length+" record(s) have been deleted.");
}
else{
log("Failed to delete records.");
}
//refresh the page
parent.location.href = url;
}

Perform DML Using External ID

Perform DML Using External ID

If you want to create a Contact associate with an Account without using AccountId field then you must have an External Id on Account object in order to create a Contact record.

It is not Efficient Code:

The following code is creating a Contact record by querying an Account record. However, because it uses a SOQL query, it is not as efficient. If this code was called multiple times, it could reach the execution limit for the maximum number of SOQL queries.
Sample Code:
Account refAcct = [SELECT Id FROM Account WHERE externalId__c='12345' LIMIT 1];
Contact c = new Contact(Account = refAcct.Id);
insert c;

Efficient Code:
The following code is equivalent to the code above. However, because it does not use a SOQL query, it is efficient.
Sample Code:
Account refAcct = new Account(externalId__c = '12345');
Contact c = new Contact(Account = refAcct, LastName = 'Kay');
insert c;
Note: This inserts a new contact with the AccountId equal to the account with the external_id equal to ‘12345’. If there is no such account, the insert fails.

Sunday, May 5, 2013

Salesforce Mobile SDK

Visualforce Page Sample Code


<!--
        Salesforce Developer Meetup Karachi April 21-27, 2013.
        Salesforce Mobile Developer Week.
        Worldwide Developer Gathering.
        April 21-27, 2013
     
        Salesforce Mobile Web(HTML5) Apps
-->
<apex:page docType="html-5.0" sidebar="false" showHeader="false" standardStylesheets="false" cache="false" controller="SalesforceDevMeetupKarachiCtrl">
<html>
    <apex:stylesheet value="{!URLFOR($Resource.MobileSample_Resources_jQueryMobile, 'jquery.mobile-1.3.0.min.css')}"/>
    <apex:includeScript value="{!URLFOR($Resource.MobileSample_Resources_jQueryMobile, 'jquery-1.9.1.min.js')}"/>
    <apex:includeScript value="{!URLFOR($Resource.MobileSample_Resources_jQueryMobile, 'jquery.mobile-1.3.0.min.js')}"/>
    <apex:includeScript value="{!URLFOR($Resource.MobileSample_Resources_jQueryMobile, 'ForceTk.js')}"/>
<head>
    <title>Salesforce Developer Meetup Karachi April 21-27, 2013</title>
 
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />  
 
    <script type="text/javascript">
        var $j = jQuery.noConflict();
        var leadRecs = new Array();
     
        var restAPIClient = new forcetk.Client();
        restAPIClient.setSessionToken('{!$Api.Session_ID}');
     
        $j(document).ready(function(){
            getAllLeads();
        });
         
        //Query and get list of Leads to show        
        function getAllLeads(){
            $j.mobile.showPageLoadingMsg();
            var q = "select id, firstName, lastName, phone, email, company, LeadSource, Status from Lead where isconverted = false order by FirstName";
            restAPIClient.query(q ,
            function(response){
                showLeads(response.records);
            });                              
        }

        //Show list of Leads to convert it into Account, Contact and Opportunity                  
        function showLeads(records){  
            $j('#leadListView').empty();
            leadRecs.length = 0;                            
            for(var i = 0; i < records.length; i++) { leadRecs[records[i].Id] = records[i]; }
            var x = 0;
            $j.each(records,
            function() {
                var newLi = $j('<li></li>');                              
                var newLink = $j('<a id="' +this.Id+ '" data-transition="flip">' +this.FirstName+ ' '+this.LastName+ '</a>');
                newLink.click(function(e){
                    e.preventDefault();
                    $j.mobile.showPageLoadingMsg();
                    $j('#fNameHeader').val(leadRecs[this.id].FirstName);
                    $j('#lNameHeader').val(leadRecs[this.id].LastName);
                    $j('#fName').val(leadRecs[this.id].FirstName);
                    $j('#lName').val(leadRecs[this.id].LastName);
                    $j('#phone').val(leadRecs[this.id].Phone);
                    $j('#email').val(leadRecs[this.id].Email);
                    $j('#company').val(leadRecs[this.id].Company);
                    $j('#leadSource').val(leadRecs[this.id].LeadSource);
                    $j('#status').val(leadRecs[this.id].Status);
                    $j('#leadId').val(leadRecs[this.id].Id);
                    $j('#error').html('');                  
                    $j.mobile.changePage('#leaddetailpage', {changeHash: true});
                });
                newLi.append(newLink);          
                newLi.appendTo('#leadListView');
                x++;
            });                                
            $j.mobile.hidePageLoadingMsg();
            $j('#leadListView').listview('refresh');
        }
     
        //Convert Lead into Account, Contact and Opportunity
        function convertLead(){
            var leadToConvert =  $j('#leadId').val();
            //Invoke JavaScript Remoting function convertLead in Apex controller to convert Lead into Account, Contact and Opportunity
            SalesforceDevMeetupKarachiCtrl.convertLead(leadToConvert,
            //Show the details of new Account created by convet Lead process
            function(result, e){
                $j.mobile.showPageLoadingMsg("b", "Converting Lead ...", true);
                $j('#acctName').val(result.Name);
                $j('#acctType').val(result.Type);
                $j('#acctId').val(result.Id);
                $j.mobile.hidePageLoadingMsg();            
                $j.mobile.changePage('#newconvertedaccountcontactopportunity', {changeHash: true});
                getUser(result);
                getContact(result);
                getOpportunity(result);
            });                                                                                                  
        }
     
        //Get and show the User Name associated with the Account Owner Id
        function getUser(result){
            var acctOwnerId = result.OwnerId;
            //Invoke JavaScript Remoting function getUser in Apex controller to get the User Name associated with the Account Owner Id
            SalesforceDevMeetupKarachiCtrl.getUser(acctOwnerId,
            //Show the User Name associated with the Account Owner Id
            function(result, e){
                $j.mobile.showPageLoadingMsg("b", "Converting Lead ...", true);
                $j('#acctOwner').val(result.Name);
                $j.mobile.hidePageLoadingMsg();            
                $j.mobile.changePage('#newconvertedaccountcontactopportunity', {changeHash: true});
            });                                      
        }
     
        //Get and show the details of new Contact created by convet Lead process
        function getContact(result){
            var newacctId = result.Id;
            //Invoke JavaScript Remoting function getContact in Apex controller to get the details of new Contact created by convet Lead process
            SalesforceDevMeetupKarachiCtrl.getContact(newacctId,
            //Show the details of new Contact created by convet Lead process
            function(result, e){
                $j.mobile.showPageLoadingMsg("b", "Converting Lead ...", true);
                $j('#contFirstName').val(result.FirstName);
                $j('#contLastName').val(result.LastName);
                $j('#contTitle').val(result.Title);
                $j('#contPhone').val(result.Phone);
                $j('#contEmail').val(result.Email);
                $j('#contId').val(result.Id);
                $j.mobile.hidePageLoadingMsg();            
                $j.mobile.changePage('#newconvertedaccountcontactopportunity', {changeHash: true});
            });                                      
        }
     
        //Get and show the details of new Opportunity created by convet Lead process          
        function getOpportunity(result){
            var newacctId = result.Id;
            //Invoke JavaScript Remoting function getOpportunity in Apex controller to get the details of new Opportunity created by convet Lead process
            SalesforceDevMeetupKarachiCtrl.getOpportunity(newacctId,
            //Show the details of new Opportunity created by convet Lead process
            function(result, e){
                $j.mobile.showPageLoadingMsg("b", "Converting Lead ...", true);
                $j('#opptyName').val(result.Name);
                $j('#opptyStageName').val(result.StageName);
                $j('#opptyCloseDate').val(result.Close_Date__c);
                $j('#opptyAmount').val(result.Amount);
                $j('#opptyId').val(result.Id);
                $j.mobile.hidePageLoadingMsg();            
                $j.mobile.changePage('#newconvertedaccountcontactopportunity', {changeHash: true});
            });                                      
        }
     
        function refreshThePage(){
            $.mobile.loadPage("/apex/VFSalesforceDevMeetupKarachi");
        }

        function displayError(e){
            var error = JSON.parse(e.responseText);
            $j('#error').html(error[0].message);
        }                              
    </script>  

    <style type="text/css" media="screen">
    th {
        text-align: right;
    }
    td {
        text-align: left;
        padding: 5px; 10px; 5px; 5px;
        color: #6099c6;
        font-weight: bold;
     }
    </style>
     
</head>

<body>
    <div data-role="page" data-theme="b" id="listpage">              
        <div data-role="header" data-theme="b" data-position="fixed">
            <h2>Salesforce Mobile Developer Week</h2>
            <h4>Worldwide Developer Gathering</h4>
            <h4>April 21-27, 2013</h4>                          
        </div>
     
        <!-- Show list of Leads -->
        <div data-role="content" id="leadList">
            <h2>Search Salesforce Leads</h2>          
            <ul id="leadListView" data-filter="true" data-inset="true" data-role="listview"
            data-theme="c" data-dividertheme="b" data-autodividers="true" data-filter-placeholder="Search Salesforce Leads">
            </ul>
        </div>                      
    </div>  
 
    <!-- Show Lead Detail -->      
    <div data-role="page" data-theme="b" id="leaddetailpage">
        <div data-role="header" data-position="fixed" data-theme="b">
            <h1><output name="fNameHeader" id="fNameHeader"></output> <output name="lNameHeader" id="lNameHeader"></output>'s Detail</h1>
            <a href='#listpage' id="back2Home" data-role="button" class='ui-btn-left' data-icon="home" data-transition="slide" onClick="refreshThePage();">Back to Home</a>  
        </div>          

        <div data-role="content">
            <a href="#" data-role="button" data-icon="star" data-iconpos="left" style="background: #E29221; color: white;">Lead</a>
            <table>                            
            <tr>
            <th>First Name</th>
            <td><output name="fName" id="fName"></output></td>
            </tr>              
            <tr>
            <th>Last Name</th>
            <td><output name="lName" id="lName"></output></td>
            </tr>              
            <tr>
            <th>Company</th>
            <td><output name="company" id="company"></output></td>
            </tr>                            
            <tr>
            <th>Lead Source</th>
            <td><output name="leadSource" id="leadSource"></output></td>
            </tr>                            
            <tr>
            <th>Lead Status</th>
            <td><output name="status" id="status"></output></td>
            </tr>
            <tr>
            <th>Phone</th>                  
            <td><a href="tel:"><output name="phone" id="phone"></output></a></td>
            </tr>              
            <tr>
            <th>Email</th>
            <td><a href="mailto:"><output name="email" id="email"></output></a></td>
            </tr>                            
            <tr>
            <th></th>
            <td>
            <h2 style="color:red" id="error"></h2><br/>
            <input type="hidden" id="leadId" />
            </td>
            </tr>
            </table>
            <hr size="2"/>
            <center>
            <a href="#newconvertedaccountcontactopportunity" id="convertLead" data-theme="b" data-role="button" data-icon="star" data-direction="reverse" data-transition="flip" class='ui-btn-left' onClick="convertLead();" >Convert Lead</a>
            </center>
        </div>                        
    </div>
         
    <!-- Show detail of new Account, Contact and Opportunity created by convert Lead process -->
    <div data-role="page" data-theme="b" id="newconvertedaccountcontactopportunity">  
        <div data-role="header" data-position="fixed" data-theme="b">
            <h1>Converted Lead Detail</h1>
            <a href='#listpage' id="back2Home" data-role="button" class='ui-btn-left' data-icon="home" data-transition="slide" onClick="refreshThePage();">Back to Home</a>
        </div>
     
        <!-- Show detal of new Account created by convert Lead process -->
        <div data-role="content">
            <a href="#" data-role="button" data-icon="star" data-iconpos="left" style="background: #236EBC; color: white;">Account</a>
            <div data-role="fieldcontain">
                <table>
                <tr>
                <th>Account Owner</th>
                <td><output name="acctOwner" id="acctOwner"></output></td>
                </tr>
                <tr>
                <th>Account Name</th>
                <td><output name="acctName" id="acctName"></output></td>
                </tr>
                <tr>
                <th>Account Type</th>
                <td><output name="acctType" id="acctType"></output></td>
                </tr>
                </table>
            </div>
            <h2 style="color:red" id="error"></h2><br/>
            <input type="hidden" id="acctId" />
        </div>  
        <hr size="2"/>

        <!-- Show detal of new Contact created by convert Lead process -->        
        <div data-role="content">
            <a href="#" data-role="button" data-icon="star" data-iconpos="left" style="background: #55448B; color: white;">Contact</a>
            <div data-role="fieldcontain">
                <table>
                <tr>
                <th>First Name</th>
                <td><output name="contFirstName" id="contFirstName"></output></td>
                </tr>
                <tr>
                <th>Last Name</th>
                <td><output name="contLastName" id="contLastName"></output></td>
                </tr>
                <tr>
                <th>Title</th>
                <td><output name="contTitle" id="contTitle"></output></td>
                </tr>
                <tr>
                <th>Phone</th>
                <td><output name="contPhone" id="contPhone"></output></td>
                </tr>
                <tr>
                <th>Email</th>
                <td><output name="contEmail" id="contEmail"></output></td>
                </tr>
                </table>
            </div>
            <h2 style="color:red" id="error"></h2><br/>
            <input type="hidden" id="contId" />
        </div>  
        <hr size="2"/>
     
        <!-- Show detal of new Opportunity created by convert Lead process -->  
        <div data-role="content">
            <a href="#" data-role="button" data-icon="star" data-iconpos="left" style="background: #E3BF30; color: white;">Opportunity</a>
            <div data-role="fieldcontain">
                <table>
                <tr>
                <th>Opportunity Name</th>
                <td><output name="opptyName" id="opptyName"></output></td>
                </tr>
                <tr>
                <th>Opportunity Stage</th>
                <td><output name="opptyStageName" id="opptyStageName"></output></td>
                </tr>
                <tr>
                <th>Opportunity Close Date</th>
                <td><output name="opptyCloseDate" id="opptyCloseDate"></output></td>
                </tr>
                <tr>
                <th>Opportunity Amount</th>
                <td><output name="opptyAmount" id="opptyAmount"></output></td>
                </tr>
                </table>
            </div>
            <h2 style="color:red" id="error"></h2><br/>
            <input type="hidden" id="opptyId" />
        </div>          
        <center>
        <a href='#listpage' id="back2Home" data-role="button" class='ui-btn-left' data-icon="home" data-transition="slide" onClick="refreshThePage();">Back to Home</a>
        </center>
    </div>  
</body>
</html>  
</apex:page>




Visualforce Controller Sample Code
/*
* @Created Date : 4/26/2013
* @Modified Date : 4/26/2013
* @Modified By : Salesforce Developer Meetup Karachi User
* @Purpose: 
* (a) To show Salesforce Leads.
* (b) To show details of Leads.
* (c) To convert Lead into Account, Contact and Opportunity.
* (d) To show newly created Account, Contact and Opportunity created by Convert Lead process.
*/
public with sharing class SalesforceDevMeetupKarachiCtrl{

    //Controller constructor
    public SalesforceDevMeetupKarachiCtrl(){
    }
    
    //Remote Function to convert Lead into Account, Contact and Opportunity
    @RemoteAction
    public static Account convertLead(String leadId){
        //Query Lead record to convert Lead into Account, Contact and Opportunity
        Lead lead = [SELECT Id FROM Lead WHERE Id =:leadId LIMIT 1];

        
        //Query Lead status to convert Lead into Account, Contact and Opportunity
        LeadStatus convertStatus = [SELECT Id, MasterLabel FROM LeadStatus WHERE IsConverted=true LIMIT 1];

        //Convert Lead into Account, Contact and Opportunity
        Database.LeadConvert lc = new database.LeadConvert();
        lc.setLeadId(lead.id);        
        lc.setConvertedStatus(convertStatus.MasterLabel);        
        Database.LeadConvertResult lcr = Database.convertLead(lc);
        System.assert(lcr.isSuccess());

        //Query new Account record created by created by convert Lead process        
        Account newConvertedAccount = [SELECT Id, Name, Type, Owner.Name, OwnerId FROM Account WHERE Id =:lcr.getAccountId() LIMIT 1];                 
        return newConvertedAccount;
    }
    
    //Remote Function to get the User Name associated with the Account Owner Id
    @RemoteAction
    public static User getUser(String userId){
        //Query User record for Account Owner       
        User user = [SELECT Id, Name FROM User WHERE Id =:userId LIMIT 1];
        return user;
    }

    //Remote Function to get the details of new Contact created by convert Lead process
    @RemoteAction
    public static Contact getContact(String accountId){
        //Query new Contact record created by created by convert Lead process        
        Contact cont = [SELECT Id, FirstName, LastName, Title, Email, Phone FROM Contact WHERE AccountId =:accountId LIMIT 1];
        return cont;
    }

    //Remote Function to get the details of new Opportunity created by convert Lead process
    @RemoteAction
    public static Opportunity getOpportunity(String accountId){
        //Query new Opportunity record created by created by convert Lead process        
        Opportunity oppty = [SELECT Id, Name, StageName, Close_Date__c, Amount FROM Opportunity WHERE AccountId =:accountId LIMIT 1];
        oppty.Amount = 100.00;
        update oppty;
        return oppty;
    }
}

AccountShare Object


Recipe: Definition of AccountShare Object

Discussion:
At times, users may need to manage their record Sharing tables via the API.
For example, if an administrator wishes to remove all manually-assigned Territories from Accounts or Opportunities, it may be inconvenient to manage this change via the GUI.  Instead, the admin may prefer to delete these references in mass.  This would be done by accessing the Share tables through the API (for example, via the DataLoader).  The administrator would look for all records with a ROWCAUSE of TerritoryManual and delete these records.

Explanations of Fields of AccountShare object:
When accessing "Share" tables via the API, there are a number of columns that may not be immediately familiar.  Below are descriptions of each column and the possible values (the following refer to the AccountShare table, but the structure is very similar for other Share tables, such as OpportunityShare):

1. ID
This is the unique record ID for the AccountShare record.

2. ACCOUNTID
This column stores the IDs for the Account records being shared.  There may be multiple rows with the same AccountID value, as each Account record might be shared with more than one User or Group.

3. USERORGROUPID
This column stores the IDs for the User or Group to whom the Account record is shared.

4. ACCOUNTACCESSLEVEL
This column determines the access level granted for the Account record to the specified User or Group.
Values are:
A. All (Full access).
B. Edit (Read/Write).
C. Read (ReadOnly).

5. OPPORTUNITYACCESSLEVEL
This column determines the access level granted - for the Opportunities associated with this Account record - to the specified User or Group.
Values are:
A. Edit (Read/Write).
B. Read (ReadOnly).
C. None (No sharing granted).

6. CASEACCESSLEVEL
This column determines the access level granted - for the Cases associated with this Account record - to the specified User or Group.
Values are:
A. Edit (Read/Write).
B. Read (ReadOnly).
C. None (No sharing granted).

7. CONTACTACCESSLEVEL
This column determines the access level granted - for the Contacts associated with this Account record - to the specified User or Group.
Values are:
A. Edit (Read/Write)
B. Read (ReadOnly).
C. None (No sharing granted).

8. ROWCAUSE
This column explains *why* the Account record is shared to the specified User or Group.
Values are:
A. Owner (the specified User is the record owner).
B. ImplicitParent (a child record related to this Account is owned by the specified User).
C. Team (the specified User is an Account Team member).
D. Manual (sharing was manually granted to the specified User).
E. TerritoryManual (the Account record was manually assigned to a Territory).
F. Territory (a Territory assignment rule granted access for this Account record to the specified Group.  Note: records marked as Territory cannot be edited/deleted via DataLoader/API).

Reference Help Link for Account Share Object:
How do I interpret 'Share' tables?
http://help.salesforce.com/apex/HTViewSolution?id=000004856&language=en_US

Saturday, May 4, 2013

Territory Management in Apex


Recipe: Territory Management
Territory management is account sharing system that grants access to accounts based on the characteristics of the accounts. It enables your company to structure your Salesforce data and users the same way you structure your sales territories.
If you want to use territory management in Apex then you should read this document for limitations and territory functionality in Apex.

Relationships among Territory, Account, Opportunity and User objects:
a) A territory can have unlimited number of users, and a user can be assigned to an unlimited number of Territories.
b) A territory can have unlimited number of accounts, and an account can be assigned to an unlimited number of territories.
c) An Opportunity can only be assigned to a Territory.

Territories in Opportunity object:

Problem:
If you want to access the territory field of Opportunity in Apex you can access it easily. Because the field Territory on Opportunity object is exactly present in Opportunity object.

Solution with Example Code:
/*
Author: @Abrar Haq
Purpose:
    (a)To populate Parent Territory Name and Territory User Name of assigned Territory of Opportunity.
*/
trigger OpportunityTrg on Opportunity (before insert) {
    if(Trigger.isBefore){
        /* Declaration of collection data types */
        Set<Id> setOfTerritoryIds = new Set<Id>();

        for(Opportunity oppty : Trigger.New){
            if(Trigger.isInsert){
                if(oppty.TerritoryId != null){
                    setOfTerritoryIds.add(oppty.TerritoryId);
                }
            }
        }

        /*
            (b)To populate Parent Territory Name of assigned Territory of Opportunity.
        */    
        if(setOfTerritoryIds.size() > 0){
            Map<Id, Territory> mapOfTerritory = new Map<Id, Territory>([Select t.RestrictOpportunityTransfer, t.ParentTerritoryId, t.OpportunityAccessLevel, t.Name, t.MayForecastManagerShare, t.Id, t.ForecastUserId, t.Description, t.ContactAccessLevel, t.CaseAccessLevel, t.AccountAccessLevel From Territory t Where Id IN :setOfTerritoryIds]);
        }
    }
}


Territories in Account object:

Problem:
If you want to access to the Territories field on Account then you need to do extra work. Because Territories field on Account is not present in Account object. You have to query in different objects to get the assigned territory of Account.

Algorithm (Pseudo Code):
1) Query AccountShare object where AccountId = Trigger.New (Account Ids)
where RowCause = 'Territory' (Note: If Account has assigned via Territory Assignment Rules).
where RowCause = 'TerritoryManual' (Note: If Account has assigned via “Manually Assigned Accounts” related list on Territory detail page or AccountShare record has created in Apex Code).
2) Query Group object where Id = AccountShare.UserOrGroupId
3) Query Territory object where Id = Group.RelatedId
4) Query UserTerritory object where Id = Territory.UserId


Solution with Example Code:
/*
Author: @Abrar Haq
Purpose:
    (a)To populate Parent Territory Name and Territory User Name of assigned Territory of Account.
*/
trigger AccountTrg on Account (before update){
    if(Trigger.isBefore){
        /* Declaration of collection data types */
        Set<Id> setOfAccountIds = new Set<Id>();

        for(Account acct : Trigger.New){
            if(Trigger.isUpdate){
                if(acct.Id != null){
                    setOfAccountIds.add(acct.Id);
                }
            }
        }

        /*
            (b) To populate Parent Territory Name of assigned Territory of Opportunity.
        */    
        if(setOfAccountIds.size() > 0){

        /* Declaration of collection data types */
Map<Id, Id> mapOfAccountShare = new Map<Id, Id>();
       Map<Id, Id> mapOfGroup = new Map<Id, Id>();
       Map<Id, Territory> mapOfUserTerritory = new Map<Id, Territory>();


(1)

//Query in Account Share object
    /*
    Those Accounts which are assigned via Territory Assignment Rules.
You can query those Accounts by filtering RowCause = 'Territory' in AccountShare object query.
    */
    List<AccountShare> listOfAccountShare =
    [Select Id, UserOrGroupId, AccountId from AccountShare where RowCause = 'Territory' and AccountId IN :setOfAccountIds];

    //Query in Account Share object
    /*
    Those Accounts which are assigned via Manually Assigned Accounts related list on Territory detail page or create an AccountShare record in Apex code.
    You can query those Accounts by filtering RowCause = 'TerritoryManual' in AccountShare object query.
    */
    List<AccountShare> listOfAccountShare =
    [Select Id, UserOrGroupId, AccountId from AccountShare where RowCause = 'TerritoryManual' and AccountId IN :setOfAccountIds];

//Map of Account Share
    for(AccountShare acctShare : listOfAccountShare){
    mapOfAccountShare.put(acctShare.AccountId, acctShare.UserOrGroupId);        
    }      

(2)

    //Query in Group object            
    List<Group> listOfGroup = [Select Id, RelatedId from Group where Type='Territory' and Id IN :mapOfAccountShare.Values()];

//Map of Group object
    for(Group groupRecord : listOfGroup){
    mapOfGroup.put(groupRecord.Id, groupRecord.RelatedId);        
    }

(3)

    //Query in Territory object
    //Map<Id, Territory> mapOfTerritories =
    new Map<Id, Territory>([select id, name, ParentTerritoryId from Territory where Id IN:mapOfGroup.Values() ]);      

(4)

    //Query in User Territory object
    List<UserTerritory> listOfUserTerritory = [Select u.UserId, u.TerritoryId, u.IsActive, u.Id From UserTerritory u WHERE IsActive = true AND TerritoryId IN :mapOfTerritories.KeySet()];

//Map of User Territory object
    for(UserTerritory userTerritory : listOfUserTerritory){
    mapOfUserTerritory.put(userTerritory.TerritoryId, userTerritory);              
    }

}

}
}


Limitation(s):
1) You cannot edit / delete AccountShare record where ROWCAUSE = ‘Territory’ via Data Loader and API. If you do then you will get the following error.
“System.DmlException: Delete failed. First exception on row 0 with id 00rZ000001G3hDoIAJ; first error: INVALID_CROSS_REFERENCE_KEY, id does not exist: []”.