Dynamically filter Contacts by Account in Quotes module

Hello,

We have a requirement in the Quotes module to filter Contact records in the select2 relate field so that they are filtered by the currently selected Account. In other words, if a billing account is selected in Quotes, we should only see contacts related to that Account. If no billing account is selected, we should see all of them.

We have this partially working by way of the initial filters referenced here .

The problem we have, is that it works correctly when an Account is selected, but cannot find any contacts when no account is selected.

My filter code is here:

$viewdefs["Contacts"]["base"]["filter"]["basic"]["filters"][]=array(
  'id' => 'filterContactsByAccountId',
  'name' => 'LBL_ACCOUNT_ID_CONTACT',
  'filter_definition' => array(
    array(
      'account_id' => ''
    )
  ),
  'editable' => true,
  'is_template' => true,
);

The relevant bit of record.php is here (I know that I ideally want to have this in an extension, but for easy of replication, here is the below:

//...
2 => array(
    "name"=>"billing_contact_name",
    "initial_filter"=>"filterContactsByAccountId",
    "initial_filter_label"=>"LBL_ACCOUNT_ID_CONTACT",
    "filter_relate"=>array(
        "billing_account_name"=>"account_id"
    )
),
//...

And finally, I have put the below into custom/modules/Quotes/clients/base/fields/relate/relate.js . The docs don't say what area to put the JS excerpts in, so I'm not sure if this is the best place to put it, but in lieu of anything else, that made the most sense.

({
    extendsFrom: 'RelateField',
    initialize: function (options) {
        this._super('initialize', [options]);
    },
    //Set the filter specifically for a_address to limit to its account
    getFilterOptions: function (force) {
        if (this._filterOptions && !force) {
            return this._filterOptions;
        }
        if (this.getSearchModule() == "Contacts" && this.def.name=="billing_contact_name") {
            this._filterOptions = new app.utils.FilterOptions()
                .config({
                    'initial_filter': 'filterContactsByAccountId',
                    'initial_filter_label': 'Contacts for Account',
                    'filter_populate': {
                        'account_id': this.model.get('billing_account_id'),
                    },
                    'filter_relate': {
                        'account_id': this.model.get('billing_account_id'),
                    }
                })
                .populateRelate(this.model)
                .format();
        } else {
            this._filterOptions = new app.utils.FilterOptions()
                .config(this.def)
                .setInitialFilter(this.def.initial_filter || '$relate')
                .populateRelate(this.model)
                .format();
        }
        return this._filterOptions;
    },
})

Is there a way I can set the filter dynamically based on whether the billing_account_id is populated?

  • You should be able to add a simple check to see if the account id is empty and back off to the default filter in that case.

    in your if statement:

    if (this.getSearchModule() == "Contacts" 
    && this.def.name=="billing_contact_name"
    && !_.isEmpty(this.model.get('billing_account_id')))

     

    FrancescaS

  • Very close!

    So if I add an Account "Account", and two Contacts "Contact for Account" (related to Account) and "No Account Contact" (not related to any Account) here's what happens:

    If I've added "Account" as the billing account, it will correctly filter to only "Contact for Account".

    If I haven't added any Account, No contacts show in the list.

    Looking at the code, it looks like the problem is that the "default" from this.def.initial_filter is actually the one in record.php. I've even tried making it so it runs 

    .setInitialFilter('$relate')

    as opposed to this.def.initial_filter, but it's either All of them, or None of them.

    Any other ideas? Or shall I admit defeat?

  • Never admit defeat! It's the only way to learn... being stubborn!  

    I am far from an expert in filters...

    But what if you check for billing_account_id and if you don't have one just defer to the original, no questions asked.

    ({
        extendsFrom: 'RelateField',
        initialize: function (options) {
            this._super('initialize', [options]);
        },
        getFilterOptions: function(force){
            //Set the filter specifically for a_address to limit to its account
            var billing_account_id = this.model.get('billing_account_id') || null;
            if (this.getSearchModule() == "Contacts" && this.def.name=="billing_contact_name" && !_.isEmpty(billing_account_id)) {
                if (this._filterOptions && !force) {
                    return this._filterOptions;
                }
                this._filterOptions = new app.utils.FilterOptions()
                    .config({
                        'initial_filter': 'filterContactsByAccountId',
                        'initial_filter_label': 'Contacts for Account',
                        'filter_populate': {
                            'account_id': this.model.get('billing_account_id'),
                        },
                        'filter_relate': {
                            'account_id': this.model.get('billing_account_id'),
                        }
                    })
                    .populateRelate(this.model)
                    .format();
                return this._filterOptions;
            }else{
                //defer to the original
                this._super('getFilterOptions', [force]);
            }
        }      
    })
              

    FrancescaS

  • My tenacity is pretty well known, but even I have limits (usually down to sanity).

    Very very close now...

    So if I think of four cases:

    Existing Quote with Account saved - Works correctly! (Only Related shows)

    Existing Quote with Account removed - Works correctly! (Both show)

    New Quote, No Account - Works correctly! (Both show)

    New Quote, Account Populated - Shows both related, and non-related Contact 

    It's truly crazy... I've been debugging the code you've done, and everything looks to go the correct paths. The correct javascript is executed if the billing Account is populated, and yet the call to the filters at the server doesn't include the filter!

    3/4 of the way certainly isn't a bad place to be. I feel like this should be easier, but seems that's not the case.