Pages

Friday, March 30, 2012

Logic Hooks: Lead History Transfer

Have you noticed what happens to history information associated with a lead when one converts that lead into a contact within SugarCRM? 

If not, give it a try. Enter a lead record named John Doe, add a Note under its History section. Next, convert the lead into a contact. Now, take a look at the History section on the new contact record for John Doe that was created via the conversion process and compare it to the History section on the lead record. You will notice that the Note entry is nowhere to be found on the contact record and instead, remains associated with the lead record.

This separation allows us to see which interactions were completed when the individual was a lead and which were completed after it was converted into a contact. In turn, it helps us identify processes and techniques that led us to converting it. 

All future activity relating to that individual would be recorded against the contact record, not the lead. However, many users find that the distinction between lead and contact history causes too much confusion.

One problem that is created by this separation of data is that looking at the History section of a contact does not give us a full picture of everything that has occurred over time, as it relates to the individual. One must toggle between the contact and lead record in order to gather that insight. 

How can we address this problem?

One school of thought would bring us to the conclusion that one should not use the Leads module at all and simply enter everyone as a contact. This would solve the History problem, but creates others, such as the inability to track lead conversion rates. 

A second approach would be to carry over the History data from the lead over to the contact record during the conversion. Doing so would solve the problem as all the History would appear on the contact record, but a facility for this does not exist in SugarCRM. Fortunately, it can be easily accomplished via a logic hook.

Let us take a look at the logic hook we would use.



To begin with, we need the standard logic hook definition which belongs in our logic_hooks.php file. In this case, it will be defined as a before_save hook, with the following code:


<?php

$hook_version = 1; 
$hook_array = Array(); 
$hook_array['before_save'] = Array(); 
$hook_array['before_save'][] = Array(1, 'DataTransfer', 'custom/modules/Leads/DataTransfer.php','DataTransfer', 'doDataTransfer'); 

?>

Alright, but where do we place this file? 

After all, when one converts a lead, more than one module is involved in the process. For example, the process creates a contact and an account, in addition to providing options for creating an opportunity, call and other type of records.

As it turns out, the best place for our logic hook is within the Leads module, thus, we will place the logic_hooks.php file in the custom/modules/Leads folder.

Lastly, we need to create the custom code file that performs the action of unlinking the History entry from the lead record and then links it to the contact. We have named the file DataTransfer.php within our logic hook definition, so this is the name we will use. Here is the content of DataTransfer.php:


<?php

/*************************************
Project: Relation Transfer
Original Dev: Angel Magaña, March 2012
@2012 Angel Magaña
cheleguanaco[at]cheleguanaco.com

Desc: Logic Hook for Transferring Relation

The contents of this file are governed by the GNU General Public License (GPL).
A copy of said license is available here: http://www.gnu.org/copyleft/gpl.html
This code is provided AS IS and WITHOUT WARRANTY OF ANY KIND.
*************************************/

class DataTransfer
{
function doDataTransfer($bean, $events, $arguments)
{
$action = $_REQUEST['action'];
if ($action == 'ConvertLead') //Must confirm it only triggers on conversion!!
{
$lead_id = $bean->id;
$contact_id = $bean->contact_id;
$bean->load_relationship('notes');
//Remove relationship 
$notes = array();
foreach ($bean->notes->getBeans() as $note)
{
$note_id = $note->id;
$notes[] = $note_id;
$bean->notes->delete($lead_id, $note_id);
}
$bean->save();
//Transfer relationship to Contact record
$contact = new Contact();
$contact->retrieve($contact_id);
$contact->load_relationship('notes');
foreach ($notes as $note)
{
$contact->notes->add($note);
}
}
}
}

?>


Note that for the purposes of our example, the code has been simplified a bit so as to only transfer Notes entries. In addition, notice the following lines from above:

$action  = $_REQUEST['action'];
if ($action == 'ConvertLead')

Because the logic hook is defined to trigger upon a save occurring on a lead record, we must be careful to not execute the code, and in turn unlink the Notes records, every time the save button is clicked on a lead. To do this, we must explicitly limit the transfer of the Notes data to only those situations where the save is the result of a conversion. The above referenced lines are used to confirm that by first obtaining the action that is occurring within SugarCRM and secondly, verifying it is the ConvertLead action, which is the one associated with the process we are concerned with.

We will want to place this file in the custom/modules/Leads folder as well. 

Once in place, give it a try by converting a lead with a related note entry in its History section and then check the resulting contact record. 

8 comments:

  1. This comment has been removed by the author.

    ReplyDelete
  2. Hi,
    Love your articles, starting a career with SugarCRM and I find your articles very helpful.

    I used the same method as described here and an older post by you to implement a logic hook onto a deployed custom module that was created in the module builder.

    The purpose of it is to add a date and time to when in was last modified in the "description" field.

    my logic_hook.php is this

    $hook_version = 1;

    $hook_array = Array();

    // position, file, function

    $hook_array['before_save'] = Array();

    $hook_array['before_save'][] = Array(1, 'AddTimeStamp', 'custom/modules/soh_Media_Inquiries/AddTimeStamp.php ','AddTimeStamp', 'StampIt');

    and AddTimeStamp.php is:

    //prevents directly accessing this file from a web browser

    if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');

    class AddTimeStamp {

    function StampIt(& $focus, $event){

    global $current_user;

    $focus->description .= “\nSaved on ”. date(“Y-m-d g:i a”). “ by ”. $current_user->user_name;

    }

    }

    I have placed both of the newly created files in /custom/modules/soh_Media_Inquiries/

    Help would highly appreciated

    P.S. i'm using Sugar on a a linux VM

    ReplyDelete
    Replies
    1. Hi,

      Thanks for the feedback, I am happy to hear you have found it helpful.

      You don't specify what it is you are having a problem with, but this line might be a problem:

      function StampIt(& $focus, $event){

      Try:

      function StampIt($bean, $events, $arguments) {

      and consequently, $focus->description should be changed to $bean->description.

      Please elaborate on your problem if you require further assistance.

      Delete
  3. thank you that fixes it :)

    could you tell me the difference between using $focus and $bean ?

    off-topic: could you point me towards a more in-depth and example based resource/meta-tutorial/book that is updated for v6.4 or 6.5 of sugar CRM, as some of the examples in the official developer guide are not working even when using the same version as the guide is, besides that the developer guide is lacking in other areas such as blurred diagrams and more focus on extending Sugar without giving a solid understanding in its actual working.

    ReplyDelete
    Replies
    1. Cool.

      I am not entirely sure what the differences are between the two. I've never compared them, but based on my usage of both, it seems they are very similar. However, they are used in different context. As you noticed, $focus doesn't work in a logic hook, but would work in other areas and vice-versa.

      As for documentation/examples, the only suggestion I can give you is to check out the developer site and blog: http://developer.sugarcrm.com

      Delete
  4. I'm trying to copy an opportunity field to a project field as soon as the project is created. I'm doing an After_Save and I'm stuck on getting the ID from the relationship so I can set the field. Your assistance would be greatly appreciated.

    function CopyFields(&$bean, $event, $arguments) {
    global $current_user;
    //GET Project-ID





    $projectID = $bean->id;
    //RELOAD BEAN TO GET RELATIONSHIP DATA FROM LINK (WITHOUT YOU NEED TO SAVE TWICE IN ORDER TO GET THE RELATIONSHIP DATA)
    $bean->retrieve($projectID);





    //Get Opportunity-ID
    $bean->load_relationship('Project'); //Also tried projects_opportunities

    $opportunityID = implode($bean->projects_opportunities->get());


    //GET Opportunity-ID-BEAN
    $opportunity = new Opportunity();
    $opportunity->retrieve($opportunityID);

    //Set the Project Fields from the Opportunity Fields

    $bean->number_of_ac_units_c = $opportunity->no_of_ac_units_c;


    $bean->save();

    ReplyDelete
    Replies
    1. Hmmm...

      I am a bit confused about what it is you are trying to do. One doesn't have the ability to link an opportunity to a project until after the project has already been added and the DetailView is displayed.

      You could execute an after_relationship_add logic hook (triggered when Opportunity is linked via subpanel) and you should be able to grab the opportunity id via $bean->opportunity_id and then load the opportunity record, read the field and update the project.

      Alternatively, calling getBeans(), as illustrated in this article should also give you the opportunity ID values.

      Delete
  5. Hey there,

    Wanted your help again.

    I've created a 1-M relationship Primary Module = Contacts; Related Module = Leads.

    This creates a sub-panel in Contacts from Leads with Contacts as a field in Leads.
    I wanted to move this field to Contacts when the Lead is converted.

    There are the foll. tables that get created.

    contacts_leads_1_c
    +------------------------------+-------------+------+-----+---------+-------+
    | Field | Type | Null | Key | Default | Extra |
    +------------------------------+-------------+------+-----+---------+-------+
    | id | varchar(36) | NO | PRI | NULL | |
    | date_modified | datetime | YES | | NULL | |
    | deleted | tinyint(1) | YES | | 0 | |
    | contacts_leads_1contacts_ida | varchar(36) | YES | MUL | NULL | |
    | contacts_leads_1leads_idb | varchar(36) | YES | MUL | NULL | |
    +------------------------------+-------------+------+-----+---------+-------+

    AND

    desc contacts_contacts_1_c;
    +---------------------------------+-------------+------+-----+---------+-------+
    | Field | Type | Null | Key | Default | Extra |
    +---------------------------------+-------------+------+-----+---------+-------+
    | id | varchar(36) | NO | PRI | NULL | |
    | date_modified | datetime | YES | | NULL | |
    | deleted | tinyint(1) | YES | | 0 | |
    | contacts_contacts_1contacts_ida | varchar(36) | YES | MUL | NULL | |
    | contacts_contacts_1contacts_idb | varchar(36) | YES | MUL | NULL | |
    +---------------------------------+-------------+------+-----+---------+-------+

    So basically, on conversion, contacts_leads_1contacts_ida needs to move over to contacts_contacts_1contacts_idb

    Thanx in advance.

    ReplyDelete

Your comments, feedback and suggestions are welcome, but please refrain from using offensive language and/or berating others. Thank you in advance.