Inconsistent bundle currency/empty "default" bundle

I have heavily customized Quotes 7.9.3

We do not use the pricing from Product Catalog or currency conversion.

We use an external price catalog and price items in the appropriate currency (matching the quote currency) but the bundle currency is not always consistent and sometimes defaults to -99 (USD) for quotes in foreign currencies.

I am finding that the behavior seems to affect some users more than others, but inconsistently so even for the exact same quote by the same exact user.

I have tracked this down to a problem with the default product bundle but still cannot understand why it is so inconsistent.

Note that I force users to save the Quote before adding line items (I have some custom steps defined behind the save button on the line item so the items need to be saved from the line itself).

If the users create a group and then add products the group's totals currency is consistently correct. But if they add any items outside the group the currency for the bundle is sometimes set to the default currency.

In the back end I noticed that when currencies are inconsistent there is always an empty bundle (no products) with the correct currency and a separate "default" bundle (no group was added by the user) with the system currency.

In these same cases, in the front end we see one or more of the "Use the + create to add line Item, comment or group to Quote" even after the items have been added.

I can't reproduce this consistently and I can't help but think that it's a timing issue.

One user reports seeing an alert flash: "Saving default group"

I tracked that message to:

modules/Quotes/clients/base/layouts/quote-data-list-groups/quote-data-list-groups.js

_onSaveDefaultQuoteGroup: function

 

And wondered if some javascript timing issue may be creating the second default group, but even if so, why would it not have the correct currency? Is the default bundle currency set by an on-change event?

 

Any suggestions on how to fix this issue?

FrancescaS

  • I can't quite pin it down but it appears that in 

    modules/Quotes/clients/base/layouts/quote-data-list-groups/quote-data-list-groups.js

     

    it gets to the  if (!hasDefaultGroup) before the check to set that var has completed, thus creating a second default group.

     

     

        _onProductBundleChange: function(productBundles) {
            var hasDefaultGroup = false;
            var defaultGroupModel;

            // after adding and deleting models, the change event is like its change for the model, where the
            // model is the first param and not the actual value it's self.
            if (productBundles instanceof Backbone.Model) {
                productBundles = productBundles.get('bundles');
            }

            // check to see if there's a default group in the bundle
            if (productBundles && productBundles.length > 0) {
                hasDefaultGroup = _.some(productBundles.models, function(bundle) {
                    return bundle.get('default_group');
                });
            }

            if (!hasDefaultGroup) {
                defaultGroupModel = this._getDefaultGroupModel();
                // calling unshift on the collection with silent so it doesn't
                // cause this function to be triggered again halfway thru
                productBundles.unshift(defaultGroupModel);
            } else {
                // default group exists, get the ID
                defaultGroupModel = _.find(productBundles.models, function(bundle) {
                    return bundle.get('default_group');
                });
                this.defaultGroupId = defaultGroupModel.cid;
            }

            productBundles.each(function(bundle) {
                if (!_.contains(this.groupIds, bundle.cid)) {
                    this.groupIds.push(bundle.cid);
                    this._addQuoteGroupToLayout(bundle);
                }
            }, this);

            this.render();
        },

    This suggests that the 

            hasDefaultGroup = _.some(productBundles.models, function(bundle) {
              return bundle.get('default_group');
            });

    is taking too long to set the hasDefaultGroup. But why?

     

    It seems the programmer had seen the problem before and tried to mitigate it by forcing the new defaultGroup model into the productBundles, but maybe the unshift is happening before the this._getDefaultGroupModel(); returns a value.

     

        if (!hasDefaultGroup) {
          defaultGroupModel = this._getDefaultGroupModel();
          // calling unshift on the collection with silent so it doesn't
          // cause this function to be triggered again halfway thru
          productBundles.unshift(defaultGroupModel);

    Drew McDaniel, have you or your team seen this problem before?

    FrancescaS

  • Hi Francesca Shiekh,

    This issue doesn't look familiar to me, but I'll run it by my team and get back to you.  Thanks for bringing it to my attention.

  • Drew McDaniel

    It turns out this happens when the list view gets its data from the slave database.

    We are on Professional and I know just enough to be dangerous... 

    in 

    include/database/DBManagerFactory.php

    I changed the getInstance function to read reports and list slave server from the config

     

        /**
             * Returns a reference to the DB object for instance $instanceName, or the default
         * instance if one is not specified
         *
         * @param  string $instanceName optional, name of the instance
         * @return DBManager Database instance
         */

            public static function getInstance($instanceName = '')
        {
            global $sugar_config;
            static $count = 0, $old_count = 0;
            if(empty($sugar_config['dbconfig'])) {
                return false;
            }
            //fall back to the default instance name
            if(empty($sugar_config['db'][$instanceName])){
                    $instanceName = '';
            }
            if(!isset(self::$instances[$instanceName])){
                //WR Note: original assignment
                //$config = $sugar_config['dbconfig'];
                //custom code starts
                if(!empty($sugar_config['db'][$instanceName])){
                  $config = $sugar_config['db'][$instanceName];
                }else{
                  $config = $sugar_config['dbconfig'];
                }
                //custom code ends
                $count++;
                    self::$instances[$instanceName] = self::getTypeInstance($config['db_type'], $config);
                    if(!empty($sugar_config['dbconfigoption'])) {
                        self::$instances[$instanceName]->setOptions($sugar_config['dbconfigoption']);
                    }
                    self::$instances[$instanceName]->connect($config, true);
                    self::$instances[$instanceName]->count_id = $count;
                    self::$instances[$instanceName]->references = 0;
                    if (empty($instanceName) && empty($GLOBALS['db'])) {
                        $GLOBALS['db'] = self::$instances[$instanceName];
                    }
                    if (empty($instanceName) && !empty($GLOBALS['system_config']) && $GLOBALS['system_config'] instanceof Administration && empty($GLOBALS['system_config']->db)) {
                        $GLOBALS['system_config']->db = self::$instances[$instanceName];
                    }
            } else {
                $old_count++;
                self::$instances[$instanceName]->references = $old_count;
            }
            return self::$instances[$instanceName];
        }

    I suspect that the quote gets the bundle data as list view data and when the replication of the data to the slave is not fast enough it ends up recreating the bundle because it is not found on the slave.

    Let me know if you think this makes sense.

    FrancescaS