How to add Accounts sub-panel to Contacts view?

Hi Everyone,

I’d like to leverage the stock Accounts - Contacts many to many relationship. While the Accounts view has a Contacts sub-panel, the Contacts view has for some reason no Accounts sub-panel (but only an Account field that shows one of the related accounts - chosen randomly).

In an attempt to add an Accounts sub-panel to the Contacts view, I mimicked what is offered here https://suitecrm.com/suitecrm/forum/developer-help/11352-how-to-add-a-target-lists-subpanel-to-leads-or-contacts-etc#72345
replacing target lists with accounts. It did not work.

Any clue why it would not work? What should I try next?

I saw a suggestion on sugarcrm to create a new one to many relationship from Contacts to Accounts. This suggestion sounded weird to me as a many to many relationship already exists in the stock data model.

Thank you.

[quote]I saw a suggestion on sugarcrm to create a new one to many relationship from Contacts to Accounts. This suggestion sounded weird to me as a many to many relationship already exists in the stock data model.
[/quote]
If you do this the items in the newly created subpanel will be related to the new relation ship and not the out-of-the box accounts-contacts relationship

I don’t think it’s random. It should be the most recent. The others remain in a sort of limbo!

Maybe you can write a custom logic hook to display the subpanel.

Maybe there is one line of code somewhere that deletes such subpanel before it is displayed.

I remember that this topic had been discussed several times in the SugarCRM forum. Maybe you can google it and find something interesting.

Maybe you can try to edit:
modules/Contacts/metadata/subpaneldefs.php and add the subpanel inside that file.

I am not sure if you can put it in custom for an upgrade safe customisation

I managed to get it working by adding the subpanel in the file:

modules/Contacts/metadata/subpaneldefs.php

This is how the first part of the definition is now:

$layout_defs['Contacts'] = array(
    // list of what Subpanels to show in the DetailView
    'subpanel_setup' => array(

        'accounts' => array(
            'order' => 30,
            'module' => 'Accounts',
            'sort_order' => 'asc',
            'sort_by' => 'name',
            'subpanel_name' => 'default',
            'get_subpanel_data' => 'accounts',
            'add_subpanel_data' => 'account_id',
            'title_key' => 'LBL_ACCOUNTS_SUBPANEL_TITLE',
            'top_buttons' => array(
                array('widget_class' => 'SubPanelTopCreateAccountNameButton'),
                array('widget_class' => 'SubPanelTopSelectButton', 'mode' => 'MultiSelect')
            ),
        ),

The above works. I did a Quick Repair and Rebuild but probably Ctrl+F5 is sufficient.

Next step would be to create a specific subpanel definition to use (ForContacts.php instead of default.php).

1 Like

Thanks for all. I’ll try this on my side tonight and will let you know of the outcome. I may come back with a further questions as I am a beginner with SuiteCRM.

So I tried what you proposed and it works pretty well :slight_smile: Thanks again.

A few things though:

  • The Quick Repair and Rebuild was necessary to see the change
  • The ‘Create’ button at the top left of the new Account sub-panel creates a Contact rather than an Account. (while the down-arrow Select selects an Account as expected). Any clue why?
  • I could customize the sub-panel definition in Studio but adding a ForContacts.php definition in modules/Accounts/metadata/subpanels/ would have no effect. Perhaps I misunderstood what I should do exactly to ddo it programmatically.
  • I guess such a customization would have to be re-done after each upgrade as you pointed out. Any suggestion to make it upgrade-safe?

I will look into it and let you know. By the way: I remember that, when I posted the code I had noticed that in some places the word Contacts or Accounts seemed inconsistent so I edited it a few times before posting so that could be the cause.

If you create ForContacts.php then you should edit the subpanels list definition to let it know where to go and get the definition.
I will try to check this too.

Probably copying the necessary files to custom folder is sufficient. I will check that too.

Quick Create button:
I replaced the line:

                array('widget_class' => 'SubPanelTopCreateAccountNameButton'),

with:

                array('widget_class' => 'SubPanelTopButtonQuickCreate'),

and now it creates an account instead of a contact.

Custom folder:
I placed the original file modules/Contacts/metadata/subpaneldefs.php (without modifications) into custom/modules/Contacts/metadata/subpaneldefs.php and then added the modifications we made.

Unfortunately this doesn’t work.

So I digged into the SuiteCRM and I found a bug in the file:
include/SubPanel/SubPanelDefinitions.php

At around line 781 there is a function called open_layout_defs. The code of this function is:

    function open_layout_defs ( $reload = false , $layout_def_key = '' , $original_only = false )
    {
        $layout_defs [ $this->_focus->module_dir ] = array ( ) ;
        $layout_defs [ $layout_def_key ] = array ( ) ;

        if (empty ( $this->layout_defs ) || $reload || (! empty ( $layout_def_key ) && ! isset ( $layout_defs [ $layout_def_key ] )))
        {
            if (file_exists ( 'modules/' . $this->_focus->module_dir . '/metadata/subpaneldefs.php' ))
                require ('modules/' . $this->_focus->module_dir . '/metadata/subpaneldefs.php') ;

            if (! $original_only && file_exists ( 'custom/modules/' . $this->_focus->module_dir . '/Ext/Layoutdefs/layoutdefs.ext.php' ))
                require ('custom/modules/' . $this->_focus->module_dir . '/Ext/Layoutdefs/layoutdefs.ext.php') ;

            if (! empty ( $layout_def_key ))
                $this->layout_defs = $layout_defs [ $layout_def_key ] ;
            else
                $this->layout_defs = $layout_defs [ $this->_focus->module_dir ] ;

        }

    }

As you can see it is not checking for the custom version of subpaneldefs.php, so I modified it into:

    function open_layout_defs ( $reload = false , $layout_def_key = '' , $original_only = false )
    {
        $layout_defs [ $this->_focus->module_dir ] = array ( ) ;
        $layout_defs [ $layout_def_key ] = array ( ) ;

        if (empty ( $this->layout_defs ) || $reload || (! empty ( $layout_def_key ) && ! isset ( $layout_defs [ $layout_def_key ] )))
        {
            if (file_exists ( 'modules/' . $this->_focus->module_dir . '/metadata/subpaneldefs.php' ))
                require ('modules/' . $this->_focus->module_dir . '/metadata/subpaneldefs.php') ;

            if (file_exists ( 'custom/modules/' . $this->_focus->module_dir . '/metadata/subpaneldefs.php' ))
                require ('custom/modules/' . $this->_focus->module_dir . '/metadata/subpaneldefs.php') ;

            if (! $original_only && file_exists ( 'custom/modules/' . $this->_focus->module_dir . '/Ext/Layoutdefs/layoutdefs.ext.php' ))
                require ('custom/modules/' . $this->_focus->module_dir . '/Ext/Layoutdefs/layoutdefs.ext.php') ;

            if (! empty ( $layout_def_key ))
                $this->layout_defs = $layout_defs [ $layout_def_key ] ;
            else
                $this->layout_defs = $layout_defs [ $this->_focus->module_dir ] ;

        }

    }

And now it works on any module if we place subpaneldefs.php in the custom folder.

For the sake of clarity I added the following lines after it tried to include the original subpaneldefs.php file:


            if (file_exists ( 'custom/modules/' . $this->_focus->module_dir . '/metadata/subpaneldefs.php' ))
                require ('custom/modules/' . $this->_focus->module_dir . '/metadata/subpaneldefs.php') ;

This is a bug. When I have time I will add it in gitHub, but unfortunately I am not sure if I can test it sufficiently against potential derived issues (although I believe it should be fine as it doesn’t do anything potentially harmful.

ForAccounts.php
I haven’t had the time to check yet. I will do later. Sorry

1 Like

Fantastic job! You’re great. Thank you so much. :slight_smile:

@amariussi are you sure it’s a bug? It could be intentional, because people are supposed to use the Extension mechanism, not override this file directly.

I am not 100% sure about this, but I guess it could be intentional. The Extension mechanism combines several different changes more easily, so it’s more upgrade-safe, even when considering several different add-ons could be changing the same subpanel.

Not at all sure.

However the way in which it is now via the Extension mechanism is way more complicated.

How can we find out if it actually is intentional? It would be interesting to know.

I wonder if this could be implemented by adding extra information via the config array, or check the existence of other potentially conflicting stuff.

On my side I will investigate further and wait until I post something on gitHub on this.

Ok, and I’ll try to get one of our developers who works on custom projects to have a look at this.

1 Like

@pgr
I found the file include/SubPanel/subpanels.tx (I am attaching it) which explains how it should work.

With respect to subpanel definitions it says at line 105:

			- gets subpanel definitions using a search path (custom, then module-specific, then global)

I am not sure if this applies to subpaneldefs.php or other.

Furthermore, I understand that this is supposed to be done in a different file (SubPanel.php) and not the one I have used (SubPanelDefinitions.php).

I searched in SubPanel.php but I couldn’t find where this check takes place.

Then I commented in the function open_layout_defs within SubPanelDefinitions.php the lines of code that include subpaneldefs.php and no subpanel is displayed.

Having said this I come to the possiple conclusion that, if the the fact that the check against a custom version of subpaneldefs.php is missing is not intentional, then the check may have to be done in there. This conclusion, however, may not be sufficient to define this omission as a bug!

I digged further.

SubPanel.php requires once SubPanelDefinitions.php (there is a class inside) and a new object of the class is created. The construct of the class calls the function I modified to include the custom subpaneldefs.php.

However this is done after verifying that the variable (local to the function) $layout_def_override is not empty.

This variable is one of the construct parameters but is is never used elsewhere in the whole package as is the function open_layout_defs.

Furthermore: after checking for the standard subpaneldefs.php the function open_layout_defs checks the Extension mechanism.

So my conclusion, this time is that I am 100% sure that this is a bug!!! So I would add the check in the custom folder. There are two possible ways to do this check: if then else (first check the module else custom) or two separate checks: module followed by custom. I am for the second option because it follows the same way the function is written. However the function could be optimised and rewritten to include only what is necessary.

code within SubPanel.php:

		if (empty($subpanelDef)) {
			//load the subpanel by name.
			require_once 'include/SubPanel/SubPanelDefinitions.php' ;
			$panelsdef=new SubPanelDefinitions($result,$layout_def_key);
			$subpanelDef=$panelsdef->load_subpanel($subpanel_id, false, false, $this->search_query,$collections);
			$this->subpanel_defs=$subpanelDef;
		}

construct of SubPanelDefinitions:

    function __construct ( $focus , $layout_def_key = '' , $layout_def_override = '' )
    {
        $this->_focus = $focus ;
        if (! empty ( $layout_def_override ))
        {
            $this->layout_defs = $layout_def_override ;

        } else
        {
            $this->open_layout_defs ( false, $layout_def_key ) ;
        }
    }

Code of function open_layout_defs:

    function open_layout_defs ( $reload = false , $layout_def_key = '' , $original_only = false )
    {
        $layout_defs [ $this->_focus->module_dir ] = array ( ) ;
        $layout_defs [ $layout_def_key ] = array ( ) ;

        if (empty ( $this->layout_defs ) || $reload || (! empty ( $layout_def_key ) && ! isset ( $layout_defs [ $layout_def_key ] )))
        {
            if (file_exists ( 'modules/' . $this->_focus->module_dir . '/metadata/subpaneldefs.php' ))
                require ('modules/' . $this->_focus->module_dir . '/metadata/subpaneldefs.php') ;

            if (! $original_only && file_exists ( 'custom/modules/' . $this->_focus->module_dir . '/Ext/Layoutdefs/layoutdefs.ext.php' ))
                require ('custom/modules/' . $this->_focus->module_dir . '/Ext/Layoutdefs/layoutdefs.ext.php') ;

            if (! empty ( $layout_def_key ))
                $this->layout_defs = $layout_defs [ $layout_def_key ] ;
            else
                $this->layout_defs = $layout_defs [ $this->_focus->module_dir ] ;

        }

    }