New quotes modules How to update the current QLI based on field change?

Hi All,

We are moving old quote customisation to new quotes module in 7.9.4.

When quantity is changed, we need to update a custom filed for that specific QLI.

How/where to update the specific QLI only, based on the field change?

Any body has any idea how we can achieve that.

Regards,

Usman

  • I make some field dependent changes in:

    <mysugar>/custom/modules/Products/clients/base/fields/quote-data-relate/quote-data-relate.js

     

    ({
      extendsFrom:'ProductsQuoteDataRelateField',
      initialize: function(options){
        this._super('initialize', [options]);
        this.model.on('change:mft_part_num', this.triggerERPLookup,this); //takes care of user-added/modified lines
      },
      triggerERPLookup: function(){
        //my ERP price lookup updates discount_price when the mft_part_num changes
      },
    })

    Maybe this will work for you too.

    FrancescaS

  • hey Francesca Shiekh,

    really appreciate your generously sharing your knowledge. This is exactly what make the community helpful.

    as you know by default, when quantity / product template / unit price /  discount those fields change, there is already some event triggered to render the page. my question is, do you know what and where is that event ? and how to extend that event to add some custom handling? If i add 

    this.model.on('change:mft_part_num', this.triggerERPLookup,this);

    for each fields, would it override the default event?  I have also found another related article: How/where to detect this change event for product change in new quote module 7.9.4  which seems have totally different solution. however, i could not get it. what do you think about that?

    our user case is, when a product template get picked/changed, we would like to call custom API to calculate the UNIT price and send back to UI. Could you please give any advise? 

    looking forward to hear back from you.

    cheers,

    Rob

  • Hi Rob, 

    It's been a while since I looked at my Quotes code but in my instance I use an ERPLookup API to to look up the unit price on a different system when the mft_part_num changes (which is when the product is selected) and set it as the discount_price (aka unit price) on the Quote Line item.

    The core is what I described above but  let me see if I can explain my process:

    in 

    custom/clients/base/api/ERPLookupApi.php

    I have a custom API that gets the Price from our ERP based on mft_part_num (which is stored in the Sugar product catalog)

    in 

    custom/modules/Products/clients/base/fields/quote-data-relate/quote-data-relate.js

    I look up the ERP price when the mft_part_num changes (notice that the mft_part_num is retrieved when the product changes on the Quoted Line Item)

    ({
      extendsFrom:'ProductsQuoteDataRelateField',
      initialize: function(options){
        this._super('initialize', [options]);
        this.model.on('change:mft_part_num', this.triggerERPLookup,this); //takes care of user-added/modified lines
      },
      triggerERPLookup: function(){
        var attr = this.model.attributes,
              part_num = attr.mft_part_num;
        //use the part_num in the API call
       //use an alert to let the user know it case it takes a little time to get the price back
        app.alert.show('erp_price', {
          level: 'process',
          title: 'Looking up Price in ERP...'
        });
        var self = this,
            price_url = app.api.buildURL('ERPGetPrice/'+part_num);
        app.api.call('GET', price_url, null, {
          success: function (price){
            self.model.set('discount_price', price);
            app.alert.dismiss('erp_price'); //dismiss the alert
          },
          error: function(e){
            app.alert.dismiss('erp_price');
            console.log(e);
          },
        });
      },

    I also have an override for the discount_price readonly dependency in

    custom/modules/Products/metadata/dependencydefs.php

    note that in the original the fields array includes list_price and cost_price which I do not care to have as read-only so I removed them. There may be a better way to do this.

    <?php
    //override discount_price readonly dependency
    $dependencies['Products']['read_only_fields'] = array();

    $fields = array(
        'category_name',
        'tax_class',
        'mft_part_num',
        'weight'
    );

    $dependencies['Products']['read_only_fields'] = array(
        'hooks' => array("edit"),
        //Trigger formula for the dependency. Defaults to 'true'.
        'trigger' => 'true',
        'triggerFields' => array('product_template_id'),
        'onload' => true,
        //Actions is a list of actions to fire when the trigger is true
        'actions' => array(),
    );

    foreach ($fields as $field) {
        $dependencies['Products']['read_only_fields']['actions'][] = array(
            'name' => 'ReadOnly', //Action type
            //The parameters passed in depend on the action type
            'params' => array(
                'target' => $field,
                'label' => $field . '_label', //normally <field>_label
                'value' => 'not(equal($product_template_id,""))', //Formula
            ),
        );
    }

    I think that's all the pieces, there is so much more going on in my Quotes and it's been a while since I worked on this so I may have forgotten something, but it should put you on the right track.

    Which fields are copied from the catalog to the Quoted Line Item fields is determined by the populate_list on the product_template_name field.

     

    You will notice that in 

    modules/Products/vardefs.php

    'product_template_name' includes discount_price in the populate_list, this is what takes the price from the catalog and includes it in the quoted line item. Perhaps you could try to remove that for the fields you don't want populated by the Sugar Product Catalog by extending the vardefs. I honestly don't recall why I did not do that.

     

    To add a custom field to that list, use the dictionary Extension, e.g.:

    in custom/Extension/modules/Products/Ext/Vardefs/vardefs.ext.php

    I added a custom catalog field for available platforms:

    $dictionary['Product']['fields']['product_template_name']['populate_list']['platforms_available_c'] 'platforms_available_c';

    where platforms_available_c is a custom field in ProductTemplates

     

     

    HTH

    FrancescaS

  • thank you very much Francesca! you are the best!!!

    i will try it out and let you know how i go.

    many many thanks!

    Ron (Rob was a typo )

  • Hey Francesca Shiekh,

    thanks to your help, i have managed to complete the custom calculation when any product item changes. however, because our business special need, each time event triggers, we need to get the entire product bundle items:

    this.context.parent.get('model').attributes.bundles.models[0].attributes.product_bundle_items.models

    and send to back end for calculation, and then we need to update some values for each product item line. e.g unit price, discount etc.

    this added another complication when try to update value for each product. in your case, you just need to update the current item 

    self.model.set('discount_price', price);

    Do you know how could i iterate all items in current product bundle and update for each of them? given the fact that my response from calculation contains the unique key of each product item. you know the cid.

    hey Francesca, don't worry. i think i have worked out

    _.mapObject(calculated_price.prices, function(item_price,key) {
        _.each(product_bundle_items, function(product_model) {
            if(product_model.cid == key){
                product_model.set('discount_price',item_price.price);
                product_model.set('discount_amount',item_price.discount_amount);
            }
        });
    });
  • hey Francesca Shiekh,

    i need your help again. got stuck in a very simple but strange issue. 

    the purpose is try update the quote currency  and tax rate when selected a product template. (* in our system, we have product templates for different country)

    this suppose be very easy as i can see the new currency id and tax class each time when the change product template event triggered. so the code i wrote is:

    if(this.model.changed.hasOwnProperty('product_template_id')){
        //update GST Rate
        console.log("product template change detected");
        console.log("Print this model attribute:");
        console.log(this.model.attributes);
        console.log("Print this model product_template_name:");
        console.log(this.model.attributes.product_template_name);
        console.log("Print this model currency_id:");
        console.log(this.model.attributes.currency_id);
        console.log("Print this model tax_class:");
        console.log(this.model.attributes.tax_class);
        console.log(this.model.get("tax_class"));
        console.log("going to set currency id to "+ this.model.attributes.currency_id);

        console.log("this model cid: "+this.model.cid);

        var test = this.context.parent.get('model').attributes.bundles.models[0].attributes.product_bundle_items.models[0];
        console.log("from quote get first item in product bundle ===> "+test.attributes.currency_id);

        this.context.parent.get('model').set("currency_id",this.model.attributes.currency_id);
        this.updateGSTRate(this.model.attributes.tax_class);
    }

    HOWEVER, what blow my mind is:
    although i did print currency id and tax class from this.model, the value is DIFFERENT from when i print entire model!!!
    in other word: On change product template, some attribute value are not from the current selected product template!
    please refer the screen shot below:
    screen shot of the fact that on change product template, some attribute value is not from the current selected template

    Could you please help me to tell what is the problem here. and do you know if there is any other way to achieve what i want?

    looking forward to hear back from you.

    cheers,

    Ron

  • I am a bit confused too, and unfortunately I have a bunch of work to get done before I take off for some much needed vacation time...

    Try putting these two in the log:

    is there a difference between

    this.model.get('currency_id')

    and

    this.model.attributes.currency_id

  • hey Francesca Shiekh,

    thanks for your reply. i have checked that on above screen shot on "tax_class" field. But there is no difference.

    hope you finish your work soon and really appreciate your time. your previous very detailed reply already helped me very much! May be come to Melbourne for holiday one day and let me know lol~ i would be super excited and could drive u around if you need.

    anyway, for this issue. i have made a work around: by get the product template bean again and then use the value. it is a bit silly, but at least it seems working lol. i will skip this headache for now

    var self = this;
    var productTemplateBean = app.data.createBean('ProductTemplates', {id: this.model.attributes.product_template_id});
    productTemplateBean.fetch({
        success: function() {
            console.log("++++++++"+productTemplateBean.get("currency_id"));
            self.context.parent.get('model').set("currency_id",productTemplateBean.get("currency_id"));
            self.updateGSTRate(productTemplateBean.get("tax_class"));
        }
    }, this);
  • I am glad you found a way to make it work.


    FrancescaS