Saving Contact $bean deletes email addresses

In the after_save logic hook for Accounts I wrote some code that would change the primary address of all Contacts linked to that account to the billing address of that account.

The code works, but we eventually noticed, the the mystery of vanishing email-addresses was directly related to that code I wrote:

$bean->load_relationship('contacts');
foreach ($bean->contacts->getBeans() as $contact){
   ...
  $contact->primary_address_street = $bean->billing_address_street;
  ...
  $contact->save();
}

The intended changes appear, but all linked contacts lose their email addresses.


Any ideas on how to convince $contact->save() to indeed save everything?
  • Instead of using the getBeans method use get()

    $bean->load_relationship('contacts');
    foreach ($bean->contacts->get() as $contact_id){       $contact = BeanFactory::getBean('Contacts', $contact_id);    if (! empty($contact->id)) {
        $contact->primary_address_street = $bean->billing_address_street;
        ...
        $contact->save();    }
    }

    That should resolve the issue. 
  • Thanks. I tried it and it doesn't make a difference.

    I checked the database and discovered, that $contact->save() doesn't delete the email addresses, but it deletes the relationship in the table email_addr_bean_rel.

    Ok, I can undelete it by setting the "deleted" column back to 0, but I'd rather that saving doesn't delete anything in the first place.
  • Also, I inspected the $contact->emailAddress object before and after $contact->save(). Apart from the Emails being gone, I noticed the lastsql value in $contact->emailAddress->db->lastsql

    Before $contact->save():
    SELECT ea.email_address, ea.email_address_caps, ea.invalid_email, ea.opt_out, ea.date_created, ea.date_modified, ear.id, ear.email_address_id, ear.bean_id, ear.bean_module, ear.primary_address, ear.reply_to_address, ear.deleted
    FROM email_addresses ea LEFT JOIN email_addr_bean_rel ear ON ea.id = ear.email_address_id
    WHERE ear.bean_module = 'Contacts'
    AND ear.bean_id = '40d72dd3-251d-6c84-9ec6-508fd561cccb'
    AND ear.deleted = 0
    ORDER BY ear.reply_to_address, ear.primary_address DESC

    After $contact->save():
    update email_addr_bean_rel set deleted=1 where id in ('47208175-a510-31c5-e28a-508fd5530b13' ,'d0a75bcd-3ba3-db6f-faaa-51c7eabcc2a5') 


    So, it looks like $contact->save() actively deletes email address relationships down the line for some reason ...
  • Ok, I worked around this obvious bug.

    The solution is: *** beans! Use SQL instead:

    $bean->load_relationship('contacts');
    foreach ($bean->contacts->getBeans() as $contact) {
        if (!empty($contact->id)){     
            $email_addresses = $contact->emailAddress->addresses;
            $sql = "
                UPDATE contacts
                SET
                    primary_address_city       = '" . $bean->billing_address_city         . "',
                    primary_address_street     = '" . $bean->billing_address_street     . "',
                    primary_address_state      = '" . $bean->billing_address_state     . "',
                    primary_address_postalcode = '" . $bean->billing_address_postalcode. "',
                    primary_address_country    = '" . $bean->billing_address_country     . "'
                WHERE id = '". $contact->id . "'";
            $GLOBALS['db']->query($sql);
        }
    }

  • Solution (hack) for Sugar7 :

    $bean_contact->emailAddress->handleLegacyRetrieve($bean_contact);
    $adresses = $bean_contact->emailAddress->getAddressesForBean($bean_contact);
    $contact_email = $adresses[0]["email_address"];
    $bean_contact->save();
    $bean_contact->emailAddress->addAddress($contact_email,true,false,false,false,null);
    $bean_contact->emailAddress->save($bean_contact->id, "Contacts");

    Get the email, save the bean, Set and save the email.
  • Hi All,

    I just came across this issue today and I was trying to figure out where the email address is being overridden.

    I found the below code under include/SugarObjects/templates/Person/Person.php under save method (checked in sugar pro 792).  You can find similar snippet under include/SugarObjects/templates/Company/Company.php as well.

    $ori_in_workflow = empty($this->in_workflow) ? false : true;
    $this->emailAddress->handleLegacySave($this, $this->module_dir);
    // bug #39188 - store emails state before workflow make any changes
    $this->emailAddress->stash($this->id, $this->module_dir);
    parent::save($check_notify);
    $override_email = array();
    if (!empty($this->email1_set_in_workflow)) {
        $override_email['emailAddress0'] = $this->email1_set_in_workflow;
    }
    if (!empty($this->email2_set_in_workflow)) {
        $override_email['emailAddress1'] = $this->email2_set_in_workflow;
    }
    if (!isset($this->in_workflow)) {
        $this->in_workflow = false;
    }

    if ($ori_in_workflow === false || !empty($override_email)) {
        $this->emailAddress->save($this->id, $this->module_dir, $override_email, '', '', '', '', $this->in_workflow);
    }
    I change the above condition (in italic) to below and the issue got fixed.

    if ($ori_in_workflow && !empty($override_email)) {
        $this->emailAddress->save($this->id, $this->module_dir, $override_email, '', '', '', '', $this->in_workflow);
    }

    $this->in_workflow   will be set to true if the bean is being dealt with in a workflow
    I have not tested the impact when bean is involved in workflow, but I feel the condition was written wrongly which was causing the issue.

    I know it's an old thread. But just thought of sharing what I found

    Thanks!