Tuesday, September 16, 2014

Docusign integration with Salesforce

VF Page :

<apex:page standardController="Apttus_Proposal__Proposal__c" extensions="DocusignTestPageController" action="{!Send}">
<apex:pageMessages ></apex:pageMessages>
<apex:form >
<apex:iframe src="{!envelopeId}" scrolling="true"/>
</apex:form>
</apex:page>


Controller

public with sharing class DocusignTestPageController {
    public string quotename {get;set;}
    public Apttus_Proposal__Proposal__c quote {get;set;}
    public String envelopeId {get;set;}
    private String accountId = ' '; // Docusign account id
    private String userId = ' '; //Docusign user id
    private String password = ' '; // docusign password;
    private String integratorsKey = ''; docusign key
    private String webServiceUrl = 'https://demo.docusign.net/api/3.0/dsapi.asmx';
    
    public DocusignTestPageController(ApexPages.StandardController controller) {
        quote=(Apttus_Proposal__Proposal__c)controller.getrecord();
        quote=[Select Id,Name from Apttus_Proposal__Proposal__c where Id=:quote.Id];
        quotename=quote.Id;
    }
    
    public pagereference send()
    {
        DocusignWSDL.DSAPIServiceSoap dsApiSend = new DocusignWSDL.DSAPIServiceSoap();
        dsApiSend.endpoint_x = webServiceUrl;

        //Set Authentication
        String auth = '<DocuSignCredentials><Username>'+ userId 
            +'</Username><Password>' + password 
            + '</Password><IntegratorKey>' + integratorsKey 
            + '</IntegratorKey></DocuSignCredentials>';
        System.debug('Setting authentication to: ' + auth);
            
        dsApiSend.inputHttpHeaders_x = new Map<String, String>();
        dsApiSend.inputHttpHeaders_x.put('X-DocuSign-Authentication', auth);
 
        DocusignWSDL.Envelope envelope = new DocusignWSDL.Envelope();
        envelope.Subject    = 'Please Sign this Quote ';
        envelope.EmailBlurb = 'This is my new eSignature service, it allows me to get your signoff without having to fax, scan, retype, refile and wait forever';
        envelope.AccountId  = '4c65053a-b5b7-4a37-ba89-073f7275b556'; 
        

        // Render the contract
        System.debug('Rendering the contract');
        PageReference pageRef = new PageReference('/apex/Template');
        pageRef.getParameters().put('id',quotename);
        Blob pdfBlob = pageRef.getContent();  
        
        // Document
        DocusignWSDL.Document document = new DocusignWSDL.Document();
        document.ID = 1;
        document.pdfBytes = EncodingUtil.base64Encode(pdfBlob);
        document.Name = 'Contract';
        document.FileExtension = 'pdf';
        envelope.Documents = new DocusignWSDL.ArrayOfDocument();
        envelope.Documents.Document = new DocusignWSDL.Document[1];
        envelope.Documents.Document[0] = document;
        
        // Recipient
        System.debug('Building up the recipient');
        DocusignWSDL.Recipient recipient1 = new DocusignWSDL.Recipient();
        recipient1.ID = 1;
        recipient1.Type_x = 'Signer';
        recipient1.RoutingOrder = 1;
        recipient1.Email = ''; // recipient1 email id
        recipient1.UserName = 'SP Sign';
        DocusignWSDL.RecipientCaptiveInfo captiveinfo=new DocusignWSDL.RecipientCaptiveInfo();
        captiveinfo.ClientUserId='2482343';
        recipient1.CaptiveInfo=captiveinfo;
        // This setting seems required or you see the error:
        // "The string '' is not a valid Boolean value. at System.Xml.XmlConvert.ToBoolean(String s)" 
        recipient1.RequireIDLookup = false;      
        
        
        /*DocusignWSDL.Recipient recipient2 = new DocusignWSDL.Recipient();
        recipient2.ID = 1;
        recipient2.Type_x = 'Signer';
        recipient2.RoutingOrder = 2;
        recipient2.Email = ''; recipient2 email id
        recipient2.UserName = 'EBM Counter Sign';
            
        // This setting seems required or you see the error:
        // "The string '' is not a valid Boolean value. at System.Xml.XmlConvert.ToBoolean(String s)" 
        recipient2.RequireIDLookup = false;      
        */
        envelope.Recipients = new DocusignWSDL.ArrayOfRecipient();
        envelope.Recipients.Recipient = new DocusignWSDL.Recipient[1];
        envelope.Recipients.Recipient[0] = recipient1;
        //envelope.Recipients.Recipient[1] = recipient2;
        
        // Tab
        DocusignWSDL.Tab tab1 = new DocusignWSDL.Tab();
        tab1.Type_x = 'SignHere';
        tab1.RecipientID = 1;
        tab1.DocumentID = 1;
        tab1.AnchorTabItem = new DocusignWSDL.AnchorTab();
        tab1.AnchorTabItem.AnchorTabString = 'By:';
        tab1.AnchorTabItem.XOffset = 100;

        
        DocusignWSDL.Tab tab2 = new DocusignWSDL.Tab();
        tab2.Type_x = 'DateSigned';
        tab2.RecipientID = 1;
        tab2.DocumentID = 1;
        tab2.AnchorTabItem = new DocusignWSDL.AnchorTab();
        tab2.AnchorTabItem.AnchorTabString = 'Date Signed:';
        
       /* envelope.Tabs = new DocusignWSDL.ArrayOfTab();
        envelope.Tabs.Tab = new DocusignWSDL.Tab[1];
        envelope.Tabs.Tab[0] = tab1;        
        envelope.Tabs.Tab[0] = tab2;        
        */
        
        System.debug('Calling the API');
        try {
            DocusignWSDL.EnvelopeStatus es = dsApiSend.CreateAndSendEnvelope(envelope);
            DocusignWSDL.ArrayOfRecipientStatus arrayofrecieps=new DocusignWSDL.ArrayOfRecipientStatus();
            arrayofrecieps=es.RecipientStatuses;
            List<DocusignWSDL.RecipientStatus> reciepstatus=new List<DocusignWSDL.RecipientStatus>();
            reciepstatus=arrayofrecieps.RecipientStatus;
            
            
            DocusignWSDL.RequestRecipientTokenAuthenticationAssertion assertion = new DocusignWSDL.RequestRecipientTokenAuthenticationAssertion();
            assertion.AssertionID = '1983';
            assertion.AuthenticationInstant = DateTime.Now();
            assertion.AuthenticationMethod = 'Password';
            assertion.SecurityDomain = 'Request Recipient Token Test';
            // Construct the URLs based on username
            DocusignWSDL.ArrayOfRecipient recipient = envelope.Recipients;
            DocusignWSDL.RequestRecipientTokenClientURLs urls = new DocusignWSDL.RequestRecipientTokenClientURLs();
            String urlBase = 'https://demo.docusign.net/';
            urls.OnSigningComplete = urlBase + '?event=SignComplete&uname=' + reciepstatus[0].UserName;
            urls.OnViewingComplete = urlBase + '?event=ViewComplete&uname=' + reciepstatus[0].UserName;
            urls.OnCancel = urlBase + '?event=Cancel&uname=' + reciepstatus[0].UserName;
            urls.OnDecline = urlBase + '?event=Decline&uname=' + reciepstatus[0].UserName;
            urls.OnSessionTimeout = urlBase + '?event=Timeout&uname=' + reciepstatus[0].UserName;
            urls.OnTTLExpired = urlBase + '?event=TTLExpired&uname=' + reciepstatus[0].UserName;
            urls.OnIdCheckFailed = urlBase + '?event=IDCheck&uname=' + reciepstatus[0].UserName;
            urls.OnAccessCodeFailed = urlBase + '?event=AccessCode&uname=' + reciepstatus[0].UserName;
            urls.OnException = urlBase + '?event=Exception&uname=' + reciepstatus[0].UserName;
            string URL=dsApiSend.RequestRecipientToken(es.envelopeId,reciepstatus[0].ClientUserId,reciepstatus[0].UserName,reciepstatus[0].Email,assertion,urls);
            envelopeId = URL;
            System.debug('Returned successfully, envelope id = ' + envelopeId );
            return null;
            //return (new pagereference('https://demo.docusign.net/Member/EmailStart.aspx?m='+envelopeId));
        } catch ( CalloutException e) {
            System.debug('Exception - ' + e );
            envelopeId = 'Exception - ' + e;
            ApexPages.Message myMsg = new ApexPages.Message(ApexPages.Severity.ERROR,e.getMessage());
            ApexPages.addMessage(myMsg);
            return null;
        }
        
    }
}

Monday, March 18, 2013

JavaScript Remoting for Apex Controllers



Use JavaScript remoting in Visualforce to call methods in Apex controllers from JavaScript. Create pages with complex, dynamic behavior that isn’t possible with the standard Visualforce AJAX components

Adding JavaScript Remoting to a Visualforce Page

To use JavaScript remoting in a Visualforce page, add the request as a JavaScript invocation with the following form:

[namespace.]controller.method(
                [parameters...,]
                callbackFunction,
                [configuration]
);

namespace: The namespace of the controller class. This is required if your organization has a namespace defined, or if the class comes from an installed package.
controller: The name of your Apex controller.
method: The name of the Apex method you’re calling.
parameters: The comma-separated list of parameters that your method takes.
callbackFunction: The name of the JavaScript function that will handle the response from the controller. You can also declare an anonymous function inline. callbackFunction receives the status of the method call and the result as parameters.
configuration: configures the handling of the remote call and response. Use this to specify whether or not to escape the Apex method’s response. The default value is {escape: true}.

Namespaces and JavaScript Remoting:
To make it easier to work with namespaces, especially for pages that make remoting calls to methods provided in packages, you can use the $RemoteAction global to automatically resolve the correct namespace, if any, for your remote action. To use this facility, you must explicitly invoke JavaScript remoting. 

The pattern for doing this is:

Visualforce.remoting.Manager.invokeAction(
'fully_qualified_remote_action', 
 invocation_parameters
);

The fully qualified remote action is a string that represents the complete path to the remote action method, including namespace, base class, and so on: namespace[.BaseClass][.ContainingClass].ConcreteClass.Method. Use $RemoteAction in an expression to automatically resolve the namespace

Below is the simple working example of Javascript Remoting,

VF Page : 

<apex:page standardController="Account" extensions="VFRemoting_Con">
<apex:form >
<input type="text" id="acctSearch"/> &nbsp;&nbsp;&nbsp;&nbsp;
<Button onclick="getRemoteAccount();return false;">Search</Button>
</apex:form>

<script type="text/javascript">
function getRemoteAccount() 
{   
    var accountName = document.getElementById('acctSearch').value;
    Visualforce.remoting.Manager.invokeAction('{!$RemoteAction.VFRemoting_Con.getaccount}', accountName, function(result, event){
            if (event.status) {
                alert("Id : "+result.Id+"\nName : "+result.Name);
            } else if (event.type === 'exception') {
                alert("Exception : "+event.message);
            } else {
                alert("Exception : "+event.message);
            }
        }, 
        {escape: true}
    );  
}
</script>
</apex:page>

Controller :

public class VFRemoting_Con 
{
    public VFRemoting_Con(ApexPages.StandardController controller) 
    {

    }
    @RemoteAction
    public static account getaccount(string accname)
    {
        Account testacc=new Account();
        testacc=[Select Id,Name from Account where Name=:accname];
        return testacc;
    }
}

JavaScript Remoting and <apex:actionFunction>:

The <apex:actionFunction> component also lets you call controller action methods through JavaScript. Here are some differences between the two:

    The <apex:actionFunction> tag:
  1. lets you specify rerender targets
  2. submits the form
  3. does not require you to write any JavaScript
    JavaScript remoting:
  1. lets you pass parameters
  2. provides a callback
  3. requires you to write some JavaScript
In general, <apex:actionFunction> is easier to use and requires less code, while JavaScript remoting offers more flexibility.


Thursday, January 10, 2013

Trigger Context Variables Considerations



Be aware of the following considerations for trigger context variables:

1)       trigger.new and trigger.old cannot be used in Apex DML operations.
2)       You can use an object to change its own field values using trigger.new, but only in before triggers. In all after triggers, trigger.new is not saved, so a runtime exception is thrown.
3)       trigger.old is always read-only.
4)       You cannot delete trigger.new.
The following table lists considerations about certain actions in different trigger events:

Trigger Event
Can change fields using trigger.new
Can update original object using an update DML operation
Can delete original object using a delete DML operation
Before insert
Allowed.
Not applicable. The original object has not been created; nothing can reference it, so nothing can update it.
Not applicable. The original object has not been created; nothing can reference it, so nothing can update it.
After insert
Not allowed. A runtime error is thrown, as trigger.new is already saved.
Allowed.
Allowed, but unnecessary. The object is deleted immediately after being inserted.
Before update
Allowed.
Not allowed. A runtime error is thrown.
Not allowed. A runtime error is thrown.
After update
Not allowed. A runtime error is thrown, as trigger.new is already saved.
Allowed. Even though bad code could cause an infinite recursion doing this incorrectly, the error would be found by the governor limits.
Allowed. The updates are saved before the object is deleted, so if the object is undeleted, the updates become visible.
Before delete
Not allowed. A runtime error is thrown. trigger.new is not available in before delete triggers.
Allowed. The updates are saved before the object is deleted, so if the object is undeleted, the updates become visible.
Not allowed. A runtime error is thrown. The deletion is already in progress.
After delete
Not allowed. A runtime error is thrown. trigger.new is not available in after delete triggers.
Not applicable. The object has already been deleted.
Not applicable. The object has already been deleted.
After undelete
Not allowed. A runtime error is thrown. trigger.old is not available in after undelete triggers.
Allowed.
Allowed, but unnecessary. The object is deleted immediately after being inserted.

All triggers define implicit variables that allow developers to access runtime context. These variables are contained in the System.Trigger class:

Variable
Usage
isExecuting
Returns true if the current context for the Apex code is a trigger, not a Visualforce page, a Web service, or an executeanonymous() API call.
isInsert
Returns true if this trigger was fired due to an insert operation, from the Salesforce user interface, Apex, or the API.
isUpdate
Returns true if this trigger was fired due to an update operation, from the Salesforce user interface, Apex, or the API.
isDelete
Returns true if this trigger was fired due to a delete operation, from the Salesforce user interface, Apex, or the API.
isBefore
Returns true if this trigger was fired before any record was saved.
isAfter
Returns true if this trigger was fired after all records were saved.
isUndelete
Returns true if this trigger was fired after a record is recovered from the Recycle Bin (that is, after an undelete operation from the Salesforce user interface, Apex, or theAPI.)
new
Returns a list of the new versions of the sObject records.
Note that this sObject list is only available in insert and update triggers, and the records can only be modified in before triggers.
newMap
A map of IDs to the new versions of the sObject records.
Note that this map is only available in before update, after insert, and after update triggers.
old
Returns a list of the old versions of the sObject records.
Note that this sObject list is only available in update and delete triggers.
oldMap
A map of IDs to the old versions of the sObject records.
Note that this map is only available in update and delete triggers.
size
The total number of records in a trigger invocation, both old and new.

Monday, January 7, 2013

Next Birthday formula field that respects Leap Day

The following formula is calculated from a custom Birthday field and will display this year's Birthday if it has not passed, next year's Birthday if it has passed, and takes Birthdays on Leap day into consideration.


IF ( MONTH (Birthday__c)=2 && DAY (Birthday__c)=29,
    IF ((DATE (YEAR(TODAY()),1,1) + 59)  > TODAY(),
         (DATE (YEAR(TODAY()),1,1)) + 59,
         (DATE (YEAR(TODAY())+1,1,1)) + 59),
    IF (DATE (YEAR(TODAY()),MONTH(Birthday__c),DAY(Birthday__c)) > TODAY(),
         DATE (YEAR(TODAY()),MONTH(Birthday__c),DAY(Birthday__c)),
         DATE (YEAR(TODAY())+1,MONTH(Birthday__c),DAY(Birthday__c))
       )
)
 
This says that:
 
1.        if the B-Day is a leap day
a.       then if TODAY() is less than the 60th day of the year (which is 2/29 in a leap year and 3/1 in a non leap year)
i.      then display Jan 1st + 59 (the 60th day) of this year
b.      else
i.      display Jan 1st + 59 of next year
2.       else
a.       then if TODAY is less than the custom date field’s month and day of this year
i.      then display the custom month and day of this year
b.      else
 i.      display the custom month and day of next year

Thursday, December 6, 2012

Standard pop up box in Salesforce using JavaScript

We can make use of alert() in javascript to display the pop in Visualforce page. But there another pop up box which looks pretty good and it is providing by Salesforce.

Below is the code to create a pop up box in Visualforce :

<apex:page >
<script type="text/javascript">
document.body.style.cursor="auto";
var box=new SimpleDialog("Test", true);
box.setTitle("Test Pop up");
box.createDialog();
    box.setContentInnerHTML("<b>Praveen</b><input type=\"button\" value=\"Ok\"  onclick=\"javascript:box.hide();\"/>");
box.show();
</script>
</apex:page>


Calling Force.com REST API from Javascript in Visualforce


Download the Forcetk.js from here and upload it in Static resources. Your Name > App Setup > Develop > Static Resources

 Include the forcetk.js and jquery.js in the Visualforce page in which you would like to call the REST API.


Here is the example code to display the Accounts list using REST API from java script in Visualforce page.


<apex:page standardController="Account">
<apex:includeScript value="https://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"/>
<apex:includeScript value="{!URLFOR($Resource.forcetk)}"/>
<script type="text/javascript">
// Get a reference to jQuery that we can work with
$j = jQuery.noConflict();
// Get an instance of the REST API client and set the session ID
var client = new forcetk.Client();
client.setSessionToken('{!$Api.Session_ID}');
var temp="";
client.query("SELECT Name FROM Account", function(response){
for (var i=0;i<response.records.length;i++)
{
    temp=temp+"<br/>"+response.records[i].Name;
}
$j('#accountname').html(temp);  
});
</script>
<p><u><b>Accounts:</b></u><br/><span id="accountname"></span>.</p>
</apex:page>


However it is good, but still this will increase the API calls.

Click here to see more information about the rest calls.

Tuesday, October 16, 2012

Google Translator in Visualforce






2)      Enter your site URL
3)      Click on Next



4)      Click on “Get Code” button


Copy and Paste the above code in the Visualforce page in which you to put the Google Translate languages drop down list.

Treeview in Visualforce using JQuery


Visualforce page code:

<apex:page controller="treeviewcon" sidebar="false" showHeader="false">
<html>
<head>
    <apex:includeScript value="{!URLFOR($Resource.jqueryminjs)}"  />
    <apex:stylesheet value="{!URLFOR($Resource.jquerycss)}"/>
     <apex:stylesheet value="http://jquery.bassistance.de/treeview/jquery.treeview.css"/>
    <apex:includeScript value="http://jquery.bassistance.de/treeview/lib/jquery.js"/>
    <apex:includeScript value="http://jquery.bassistance.de/treeview/lib/jquery.cookie.js"/>
    <apex:includeScript value="http://jquery.bassistance.de/treeview/jquery.treeview.js"/>
    <apex:includeScript value="http://jquery.bassistance.de/treeview/demo/demo.js"/>
  <style type="text/css">
  #browser {
    font-family: Verdana, helvetica, arial, sans-serif;
    font-size: 120%;
    font-weight:bold;
  }
  </style>
  <script>
  $(document).ready(function(){
    $("#browser").treeview();
  });
  </script>
</head>
<body><br/><br/>
<div style="width:300px;margin-left:50px;">
<apex:pageBlock >
  <ul id="browser" class="filetree">
    <apex:repeat value="{!accnts}" var="acc">
    <li class="closed"><span class="folder">{!acc.name}</span>
        <ul>
            <apex:repeat value="{!acc.contacts}" var="c">
            <li><span class="file"><a href="/{!c.id}" target="_blank">{!c.name}</a></span></li>
            </apex:repeat>
        </ul>
    </li>
    </apex:repeat>
   </ul>
   </apex:pageBlock>
   </div>
</body>
</html>
</apex:page>

Controller:

public with sharing class treeviewcon {
    public List<Account> accnts{get; set;}
    public treeviewcon(){
        accnts = [Select id, name,(Select id, name from Contacts) from account];
    }
}

TreeView : 



Wednesday, October 3, 2012

Triggers and Order of Execution



When you save a record with an insert, update, or upsert statement, Salesforce performs the following events in order.

On the server, Salesforce:

1.       Loads the original record from the database or initializes the record for an upsert statement.
2.       Loads the new record field values from the request and overwrites the old values.
If the request came from a standard UI edit page, Salesforce runs system validation to check the record for:
o    Compliance with layout-specific rules
o    Required values at the layout level and field-definition level
§  Valid field formats
§  Maximum field length
Salesforce doesn't perform system validation in this step when the request comes from other sources, such as an Apex application or a SOAP API call.
3.       Executes all before triggers.
4.       Runs most system validation steps again, such as verifying that all required fields have a non-null value, and runs any user-defined validation rules. The only system validation that Salesforce doesn't run a second time (when the request comes from a standard UI edit page) is the enforcement of layout-specific rules.
5.       Saves the record to the database, but doesn't commit yet.
6.       Executes all after triggers.
7.       Executes assignment rules.
8.       Executes auto-response rules.
9.       Executes workflow rules.
10.   If there are workflow field updates, updates the record again.
11.   If the record was updated with workflow field updates, fires before and after triggers one more time (and only one more time), in addition to standard validations. Custom validation rules are not run again.
12.   Executes escalation rules.
13.   If the record contains a roll-up summary field or is part of a cross-object workflow, performs calculations and updates the roll-up summary field in the parent record. Parent record goes through save procedure.
14.   If the parent record is updated, and a grand-parent record contains a roll-up summary field or is part of a cross-object workflow, performs calculations and updates the roll-up summary field in the parent record. Grand-parent record goes through save procedure.
15.   Executes Criteria Based Sharing evaluation.
16.   Commits all DML operations to the database.
17.   Executes post-commit logic, such as sending email.

Additional Considerations
Please note the following when working with triggers:
·         When Enable Validation and Triggers from Lead Convert is selected, if the lead conversion creates an opportunity and the opportunity has Apex before triggers associated with it, the triggers run immediately after the opportunity is created, before the opportunity contact role is created. For more information, see “Customizing Lead Settings” in the Salesforce online help.
·         If you are using before triggers to set Stage and Forecast Category for an opportunity record, the behavior is as follows:
o    If you set Stage and Forecast Category, the opportunity record contains those exact values.
o    If you set Stage but not Forecast Category, the Forecast Category value on the opportunity record defaults to the one associated with trigger Stage.
o    If you reset Stage to a value specified in an API call or incoming from the user interface, the Forecast Category value should also come from the API call or user interface. If no value for Forecast Category is specified and the incoming Stage is different than the trigger Stage, the Forecast Category defaults to the one associated with trigger Stage. If the trigger Stage and incoming Stage are the same, the Forecast Category is not defaulted.
·         If you are cloning an opportunity with products, the following events occur in order:
o    The parent opportunity is saved according to the list of events shown above.
o    The opportunity products are saved according to the list of events shown above.
If any opportunity products contain unique custom fields, you must null them out before cloning the opportunity.
·         Trigger.old contains a version of the objects before the specific update that fired the trigger. However, there is an exception. When a record is updated and subsequently triggers a workflow rule field update, Trigger.old in the last update trigger won’t contain the version of the object immediately prior to the workflow update, but the object before the initial update was made. For example, suppose an existing record has a number field with an initial value of 1. A user updates this field to 10, and a workflow rule field update fires and increments it to 11. In the update trigger that fires after the workflow field update, the field value of the object obtained from Trigger.old is the original value of 1, rather than 10, as would typically be the case.