When duplicating a record also duplicate related records

When I duplicate a record I also want to duplicate related records

In 6.5 this was fairly simple

I did it using an after save logig hook

I checked  for $_REQUEST['duplicateId']  and $_REQUEST['module'] == 'zz_expenses_head')

But this doesn't work in 7

I can't see how to identify a duplicated record in 7

My 6.5 after save code is:

require_once('data/SugarBean.php');
require_once('include/utils.php');

class zz_expenses_headAfterSave {
    function zz_expenses_headAfterSave (&$bean, $event, $arguments)
    {
        #duplicate expenses & detail
        if ( $_REQUEST['duplicateId'] && $_REQUEST['module'] == 'zz_expenses_head') {

            $eh = new zz_expenses_head;
            $eh->retrieve($_REQUEST['duplicateId']);

            $details = $eh->get_linked_beans('zz_expenses_detail','zz_expenses_detail');

            foreach($details as $detail) {

                $ndetail = new zz_expenses_detail;
                $ndetail->zz_expenses_head_id = $bean->id;
                $ndetail->name = $detail->name;
                $ndetail->description = $detail->description;
                $ndetail->cost = $detail->cost;
                $ndetail->passenger = $detail->passenger;
                  $ndetail->total = $detail->total;
                  $ndetail->vat = $detail->vat;
                  $ndetail->distance = $detail->distance;
                  $ndetail->distance_type = $detail->distance_type;
                  $ndetail->zz_vat_codes_id = $detail->zz_vat_codes_id;
                  $ndetail->zz_exp_analysis_id = $detail->zz_exp_analysis_id;
                 $ndetail->parent_type = $detail->parent_type;
                 $ndetail->parent_id = $detail->parent_id;
                 $ndetail->assigned_user_id = $bean->assigned_user_id;
                 $ndetail->skip = 'Y';
                $ndetail->save();

            } #end foreach
        } #end if
    } #end function
} #end class
  • I use $bean->module_name instead of $_REQUEST['module']  in 7.x and do a my own duplicate or $arguments['isUpdate'] record checks. Not sure if this helps any...
    [both $_REQUEST['duplicateId'] && $_REQUEST['module']  return empty]

    Also when a duplicate is created $arguments[check_notify] = 1  and $_SERVER['REQUEST_URI'] will have &picture_duplicateBeanId=11111111-2222-3333-4444-555555555555 parameter.
  • That did the trick :)

    My code is:

    if (preg_match('/picture_duplicateBeanId/', $_SERVER['QUERY_STRING'])){
       $split = explode('&',$_SERVER['QUERY_STRING']);
       foreach($split as $k) {
           if (preg_match('/picture_duplicateBeanId/', $k)){
              $aid = explode('=',$k);
              $id = $aid[1];
              continue;
           }
        }
       $eh = new zz_expenses_head;
       $eh->retrieve($id);
       $details = $eh->get_linked_beans('zz_expenses_detail','zz_expenses_detail');
        foreach($details as $detail) {
           $ndetail = new zz_expenses_detail;
           $ndetail->zz_expenses_head_id = $bean->id;
            $ndetail->name = $detail->name;
            $ndetail->description = $detail->description;
       .....
           $ndetail->save();
        }
    }
                                                



  • Note: Careful when relating records via relationship rather than just adding parent_id.
    When you save a relationship the after_save logic hook triggers again getting into an infinite loop.
    The way out is checking isUpdate as Paperless suggested, the isUpdate will be true when adding the relationship.

    Here is my example, in this example, when Copy is used on an Opportunity I duplicate related Opportunity Products and I relate all Contacts that were related to that Opportunity to the new one that I just created via the copy.

            function copyOppProducts($bean, $event, $arguments){
            
                    //bean = the Opportunity we just create
                    //o = the Opportunity we copied to create this one
                    //product = the product/s linked to o
                    //op = the new products we created from copies of product  to be linked to the bean we just created
                    $GLOBALS['log']->fatal("copyOppProducts::" . print_r($arguments, true));
                    include_once('custom/entry_points/Utils/getRelationshipByModules.php');
                    //need to check if isUpdate, the addition of the relationship triggers the after_save causing a loop
                    //but the relationship add is an update, so skip this if you're updating.
                    if (!$arguments['isUpdate'] && preg_match('/picture_duplicateBeanId/', $_SERVER['QUERY_STRING'])){ //we are copying an existing Opp
                            $GLOBALS['log']->fatal("copyOppProducts -- it's a copy:: " . $_SERVER['QUERY_STRING']);
                            $split = explode('&',$_SERVER['QUERY_STRING']);
                            foreach($split as $k) {
                                    $GLOBALS['log']->fatal("copyOppProducts -- k " . $k);
                                    if (preg_match('/picture_duplicateBeanId/', $k)){
                                            $aid = explode('=',$k);
                                            $id = $aid[1]; //the id of the original record
                                            continue;
                                    }
                            }
                            //get the opp we just copied
                            $o = BeanFactory::retrieveBean('Opportunities',$id);
                            //Copy Opportunity Products
                            list ($rel,$m1)= getRelationshipByModules('oppp_Opportunity_Products','Opportunities');
                            if($o->load_relationship($rel)){
                                    //loop through the products linked to the original Opp
                                    foreach($o->$rel->getBeans()  as $product) {
                                            $op = BeanFactory::newBean('oppp_Opportunity_Products');
                                            //copy the product 
                                            $op->name=$product->name;
                                            $op->description=$product->description;
                                            $op->license_type=$product->license_type;
                                            $op->site=$product->site;
                                            $op->subscription=$product->subscription;
                                            $op->product=$product->product;
                                            $op->value=$product->value;
                                            $op->save();
                                            //relate the product to the new Opp we just created
                                            if($op->load_relationship($rel)){;
                                                    $GLOBALS['log']->fatal("Relate Opp Product to Bean");
                                                    $op->$rel->add($bean->id);
                                            }
                                    }
                            }
                            //Relate Contacts from copy to this bean
                            list ($crel,$cm1)= getRelationshipByModules('Contacts','Opportunities');
                            $GLOBALS['log']->fatal("Relate Contact to Opp::{$crel}");
                            if($o->load_relationship($crel)){
                                    $GLOBALS['log']->fatal("Relate Contact to Opp::{$crel} Loaded");
                                    foreach($o->$crel->getBeans() as $contact){
                                            $GLOBALS['log']->fatal("About to Relate Contact {$contact->first_name}  to Opp");
                                            if($bean->load_relationship($crel)){
                                                    $GLOBALS['log']->fatal("Relate Contact {$contact->first_name}  to Opp");
                                                    $bean->$crel->add($contact->id);
                                            }
                                    }
                            }
                    }
            }
                                                            


    Thank you Mike and Paperless!

    FrancescaS

  • Another way to detect the copy action (and the source ID) is to check $_REQUEST['after_create']['copy_rel_from'] for the ID of the source record.  This is the parameter set by clients/base/views/create/create.js that signals clients/base/api/ModuleApi.php's processAfterCreateOperations() to copy relationships from the source record.  In theory this could be more reliable, as it is not dependent on specific field names, though I'm not sure if it makes much difference in practice.
  • I am trying to only perform code during the first before_save...only during the create of a record. I thought that checking isUpdate would allow me to do this but I never go into the code. Here is the code in question. Any help would be appreciated:

    <?phpclass LinkMCAData_class
    {
     function LinkMCAData_method($bean, $events, $arguments)
     {

       //check if this is the first save
       if (isset($arguments['isUpdate']) && $arguments['isUpdate'] == false)
       {
     
     $GLOBALS['log']->info("LinkMCAData:: New Rec");
         //This is the first save, do this stuff...
     .
     .
     .
       }
       else
        //This is a subsequent save, fall out of the code
       {
        $GLOBALS['log']->info("LinkMCAData:: Existing Rec");
       }
      }

    }
    ?>
  • Hi Dianna,

    Here is the code you can try

    //check if this is the first save
    if(empty($bean->fetched_row)) {
    {

    $GLOBALS['log']->info("LinkMCAData:: New Rec");
     //This is the first save, do this stuff...
    .
    }
    else
    //This is a subsequent save, fall out of the code
    {
    $GLOBALS['log']->info("LinkMCAData:: Existing Rec");
    }
    Let us know if this helps.

    Regards.
    Hats
  • Thanks, hats! I had already discovered this worked myself but your response helps to reinforce that this is the correct logic.