Email1 on Account is not populated by view.convertlead.php

Hi,

So I’ve already customized the convert-lead page and it is working fine with the exception of email1.
I understand that these fields are actually more complex behind the scenes and that email{1,2,3,n} is a proxy for underlying email module relationship.

However it works fine with contact.

e.g. in custom/modules/Leads/metadata/convertdefs.php, specifying ‘email1’ under the Contact section…

Contact definition extract in convertdefs.php
$viewdefs['Contacts']['ConvertLead'] = array(
    'copyData' => true,
    'required' => true,
    'select' => "report_to_name",
    'default_action' => 'create',
    'templateMeta' => array(
        'form'=>array(
            'hidden'=>array(
                '<input type="hidden" name="opportunity_id" value="{$smarty.request.opportunity_id}">',
                '<input type="hidden" name="case_id" value="{$smarty.request.case_id}">',
                '<input type="hidden" name="bug_id" value="{$smarty.request.bug_id}">',
                '<input type="hidden" name="email_id" value="{$smarty.request.email_id}">',
                '<input type="hidden" name="inbound_email_id" value="{$smarty.request.inbound_email_id}">'
            )
        ),
        'maxColumns' => '2',
        'widths' => array(
            array('label' => '10', 'field' => '30'),
            array('label' => '10', 'field' => '30'),
        ),
    ),
    'panels' =>array(
        'LNK_NEW_CONTACT' => array(
            array(
                'last_name',
                'email1',
            ),
        )
    ),
);

…works fine.

Trying the same thing with Account does NOT work

Account definition extract in convertdefs.php
$viewdefs['Accounts']['ConvertLead'] = array(
    'copyData' => true,
    'required' => true,
    'select' => "account_name",
    'default_action' => 'create',
    'relationship' => 'accounts_contacts',
    'templateMeta' => array(
        'form'=>array(
            'hidden'=>array(
                '<input type="hidden" name="opportunity_id" value="{$smarty.request.opportunity_id}">',
                '<input type="hidden" name="case_id" value="{$smarty.request.case_id}">',
                '<input type="hidden" name="bug_id" value="{$smarty.request.bug_id}">',
                '<input type="hidden" name="email_id" value="{$smarty.request.email_id}">',
                '<input type="hidden" name="inbound_email_id" value="{$smarty.request.inbound_email_id}">'
            )
        ),
        'maxColumns' => '2',
        'widths' => array(
            array('label' => '10', 'field' => '30'),
            array('label' => '10', 'field' => '30'),
        ),
    ),
    'panels' =>array(
        'LNK_NEW_ACCOUNT' => array(
            array(
                'email1',
            ),
        )
    ),
);

I’ve cleared the template cache (learned that last time). No errors. The convert form renders as expected with the email form in place for the Account…but the value is not populated like it is for Contact.

There is also some weird bug where if you have email1 in both Contact and Account on the same page, the javascript (or php template) freaks out and you end up with a ton of extra email input lines and some other mangling.

I’ve tried keeping email1 in both Contact and Account as well as removing it from Contact (only in Account) on the convert lead page. No luck.

So I guess there is some hard-coded, Contact-specific “hack” somewhere that causes email1 to populate from Lead for Contact but not Account. I’ve spent about 4 hours reading through various modules, includes and the SugarEmailAddress.{js,php} files as well as a fair bit of grepping trying to track down where this is happening.

There is a comment (modules/Leads/views/view.convertlead.php#L248) in the giant display() function that indicates email data is copied at that point. However looking into EditView2 class (setup and process) methods I don’t see anything obvious there.

Anyone else have experience with this and know where I should be looking?
I can’t believe it is this hard to tell the convert lead page to use the email1 from the source Lead to populate the email1 on the Account we are going to create.

@mattp

This problem is in field type email. The view form can has only one block of this field type.
I see alternative option for you to create special field of type flag. If the flag is on you copy email address to Account which actualy for Contact.
Or you should rewite the object include/SugarEmailAddress.

I definitely saw the form was mangled when email1 was included in both Contact and Account sections on the convert-lead page. So I removed email1 from Contact and only added it on the Account.

The thing I don’t understand is why the email1 auto-populates from Lead to Contact but not Lead to Account even though the fields are named the same and email1 was removed from Contact. Couldn’t figure out what part of the code was populating this field on Contact from Lead.

Make sense?

@mattp

I analyzed the logic for email address. There are several words before results of the analysis.
The object Lead has unconfirmed information about person and company. The information can be set as a rubbish and it can’t be converted to other object. If the information is correctly then the most of the information is describing person. We communicate with people which represent company. When we convert the Lead the first object is Contact. The logic included to object SugarEmailAddress of SuiteCRM.

Now about add email in both objects Contact and Account.

  1. add email1 into array of module Accounts and remove email1 from array of module Contacts in file modules/Leads/metadata/convertdefs.php
  2. change the method display in the file modules/Leads/views/view.convertlead.php
...
                    } elseif ($field == "id") {
                        //If it is not a contact, don't copy the ID from the lead
//                        if ($module == "Contacts") {
                        if ($module == "Contacts" || $module == "Accounts") {
                            $focus->$field = $this->focus->$field;
                        }
...
  1. change the function getEmailAddressWidget in the file include/SugarEmailAddress/getEmailAddressWidget.php
...
    if ($view == 'EditView' || $view == 'QuickCreate' || $view == 'ConvertLead') {
        $module = $focus->module_dir;
//     if ($view == 'ConvertLead' && $module == "Contacts") {
        if ($view == 'ConvertLead' && ($module == "Contacts"|| module == "Accounts") ) {
            $module = "Leads";
        }
...
  1. change the method getEmailAddressWidgetEditView in the file include/SugarEmailAddress/SugarEmailAddress.php
...
        if (!empty($id)) {
            $prefillDataArr = $this->getAddressesByGUID($id, $module);
            //When coming from convert leads, sometimes module is Contacts while the id is for a lead.
//         if (empty($prefillDataArr) && $module == "Contacts") {
            if (empty($prefillDataArr) && ($module == "Contacts" || $module == "Accounts") ) {
                $prefillDataArr = $this->getAddressesByGUID($id, "Leads");
            }
...

After the manipualtions you will get one email address in both objects Contact and Account.

P.S. If you want to get email address in object Account only you should remove module Contacts from the three conditions. This changing doesn’t support with upgrade.

Thank you very much for taking the time to check the code and reply.

I definitely get that and in most B2B sales it totally makes sense (account customer and you often have multiple contacts for an account). However I have a client that never has more than 1 contact for an account. They aren’t planning on using SuiteCRM for more than just basic lead/account/opportunity tracking and reporting. They asked that the main contact data be consolidated into Account rather than forcing their Sales people to think about and manage Accounts and Contacts separately. I tried to avoid going down this path but it is what they want and they see it as a UI/workflow simplification…which for their case it is.

I had already found and patched number 1, 2 and 4 from your list but missed 3. Thanks.

This might be the other problem I am having. I have been copying and editing these files from a custom/... path.

include/SugarEmailAddress/getEmailAddressWidget.php is included using relative path by include/SugarEmailAddress/SugarEmailAddress.php so not a problem. However it looks like include/SugarEmailAddress/SugarEmailAddress.php is loaded in a couple places using require_once and I don’t see a custom check (require file from custom if exists, otherwise from default location).

So it may be that my changes on the include/SugarEmailAddress/SugarEmailAddress.php had no impact either.

# grep -R 'SugarEmailAddress.php' *
files.md5:  './include/SugarEmailAddress/SugarEmailAddress.php' => 'e50ef2d9cc1ab28f13fe080f58d43a4c',
include/entryPoint.php:require_once 'include/SugarEmailAddress/SugarEmailAddress.php';
include/Smarty/plugins/modifier.default_date_value.php:- include/SugarEmailAddress/SugarEmailAddress.php
modules/Emails/EmailsControllerActionGetFromFields.php:require_once __DIR__ . '/../../include/SugarEmailAddress/SugarEmailAddress.php';

Would it be possible to abstract require_once and include into a SuiteCRM utility to offer more broad support for loading from the custom folder without having to add these checks in every file where we want support? Or is there some other logic/methodology for why some file mods are upgrade safe and others aren’t (eg APIs change regularly in certain code paths and so you don’t want people modifying these files because their changes likely won’t be upgrade safe in terms of the app working post-upgrade)?

Other than not being upgrade safe, this is great. Thanks for taking the time to help.

Often all that is needed is to change something like this

require_once 'include/SugarEmailAddress/SugarEmailAddress.php';

to

require_once get_custom_file_if_exists('include/SugarEmailAddress/SugarEmailAddress.php');

However, that is only one of the mechanisms SuiteCRM has for customization, and not the best one.

Since SugarEmailAddress is an object, and a core Bean, I suppose it can be extended in an upgrade-safe manner with the Extension mechanism, see modules.ext.php