Make Revenue Line Item 'Unit Price' editable after the Relationship is populated with Product Catalogue record?

Posting here (dev) after initially looking at this from admin and also found an admin page with the same question:

How can I make the unit price editable after a revenue line item is created? 

We do not just have one unit price for each product, instead we sell via Partners and each of those has their own price list - for the same product.  So each product can have many prices.  (Note that ERP system is SAP by Design and we dont have integration to that)

So, I am wondering if anybody else knows a solution for this?  Ideally I think the following would work nicely:

  • From Opportuinity> Add the Revenue Line Item (RLI) (from dashlet on side panel)..
  • In RLI the Product (catalogue record) is selected but the Unit Price is not editable. (Hoping it can be).
  • If Unit Price could be populated from the Catalogue, but then edited it would allow our sales folk the ability to alter the price per each project.I then do not need a unit price - it could be zero?  Though i do not know the impact that will have on Forecasts further down the road.

Note - with no product (catalogue item) relationship the Unit price can be manually added...

I am scared of breaking forecasts or quotes which we have not yet configured for this.

-We have looked at vardefs but see nothing which seems to make the Unit Price do what it does.

-I don't think the Unit Price field's formula in studio seems to effect this kind of desired change.

-We can Inspect the field in browser and remove disable for that fields input... but then it doesnt really work correctly with regard to Best/Likely/Worst etc..  and if you save and come back in again its still only looking at the Unit price from the relationship.

Any suggestions very welcome!   I am stuck.

Other options are:

- to import all our dealer price lists (200+!) into the catalogue... that's alot of duplication, what about price annual updates, and even then sales will still alter the prices by using discounts per project... but this not very slick either.

-Or I can import an MRSP price which they discount from... same un-slick approach. Plus they create these mrsp's per currency so that throws up its own questions too...

Thanks for any other advice on this one...!

Luke Ridgway     Admin

john Fieldsend    Dev

Parents
  • indeed I had forgotten that some fields are managed by the RLI class itself.

    Instead of modifying core code you can create a custom RLI class which extends the core one and modify it accordingly.

    Find below a working example on how to create a custom bean class:

    custom/Extension/application/Ext/Include/some_file.php

    $moduleList[] = 'ProductTemplates';
    $objectList['ProductTemplates'] = 'ProductTemplate';
    $beanList['ProductTemplates'] = 'CustomProductTemplate';
    $beanFiles['CustomProductTemplate'] = 'custom/modules/ProductTemplates/ProductTemplate.php';
    if (isset($modInvisList) && is_array($modInvisList)) {
        foreach ($modInvisList as $key => $mod) {
            if ($mod === 'ProductTemplates') {
                unset($modInvisList[$key]);
            }
        }
    }
    
    

    custom/modules/ProductTemplates/ProductTemplate.php

    require_once('modules/ProductTemplates/ProductTemplate.php');
    
    class CustomProductTemplate extends ProductTemplate {
    	public function __construct() {
    		parent::__construct();
    		$this->disable_row_level_security = false;
    	}
    }
    

    Remember to run QRR after saving your custom files.

    Regards

    André Lopes
    Lampada Global
    Skype: andre.lampada
  • Thanks for the quick response and confirming that we indeed need to extend the base class. For completeness I have added the changes I made:

    First we add custom/Extension/application/Ext/Include/customRLI.php to map the new class to the rli object

    <?php
    //
    /**
    * The $objectList array, maps the module name to the Vardef property
    * By default only a few core modules have this defined, since their Class/Object names differs from their Vardef Property
    **/
    $objectList['RevenueLineItems'] = 'RevenueLineItem';
    
    // $beanList maps the Bean/Module name to the Class name
    $beanList['RevenueLineItems'] = 'CustomRevenueLineItem';
    
    // $beanFiles maps the Class name to the PHP Class file
    $beanFiles['CustomRevenueLineItem'] = 'custom/modules/RevenueLineItems/CustomRevenueLineItem.php';

    next we create a new class which extends default Rli class. In this case I overwritten the mapFieldsFromProductTemplate function since I don't want the discount price from the template when we create or change the rli

    <?php
    
    require_once('modules/RevenueLineItems/RevenueLineItem.php');
    
    
    class CustomRevenueLineItem extends RevenueLineItem
    {
        public function __construct()
        {
            parent::__construct();
        }
    
        protected function mapFieldsFromProductTemplate(){
            $GLOBALS['log']->fatal('Custom RLI -> mapFieldsFromProductTemplate called');
            if ($this->product_template_id && (
                    $this->fetched_row === false || $this->fetched_row['product_template_id'] != $this->product_template_id
                )) {
                /* @var $pt ProductTemplate */
                $pt = BeanFactory::getBean('ProductTemplates', $this->product_template_id);
    
                $this->category_id = $pt->category_id;
                $this->mft_part_num = $pt->mft_part_num;
                $this->list_price = SugarCurrency::convertAmount($pt->list_price, $pt->currency_id, $this->currency_id);
                $this->cost_price = SugarCurrency::convertAmount($pt->cost_price, $pt->currency_id, $this->currency_id);
    //            $this->discount_price = SugarCurrency::convertAmount($pt->discount_price, $pt->currency_id, $this->currency_id); // discount_price = unit price on the front end...
                $this->list_usdollar = $pt->list_usdollar;
                $this->cost_usdollar = $pt->cost_usdollar;
    //            $this->discount_usdollar = $pt->discount_usdollar;
                $this->tax_class = $pt->tax_class;
                $this->weight = $pt->weight;
            }
        }
    
    }

    Also we need to prevent the discount price from populating to from the catalogue. Create the following file: 

    custom/Extension/modules/RevenueLineItems/Ext/Vardefs/sugarfield_product_template_name.php. As you can see I rmoved the discount_price fields from the popuplate list.

    <?php
    $dictionary['RevenueLineItem']['fields']['product_template_name']['audited']=false;
    $dictionary['RevenueLineItem']['fields']['product_template_name']['hidemassupdate']=false;
    $dictionary['RevenueLineItem']['fields']['product_template_name']['duplicate_merge']='disabled';
    $dictionary['RevenueLineItem']['fields']['product_template_name']['duplicate_merge_dom_value']=0;
    $dictionary['RevenueLineItem']['fields']['product_template_name']['merge_filter']='disabled';
    $dictionary['RevenueLineItem']['fields']['product_template_name']['calculated']=false;
    $dictionary['RevenueLineItem']['fields']['product_template_name']['populate_list']= array(
        'name' => 'name',
        'category_id' => 'category_id',
        'category_name' => 'category_name',
        'mft_part_num' => 'mft_part_num',
        'list_price' => 'list_price',
        'cost_price' => 'cost_price',
        'list_usdollar' => 'list_usdollar',
        'cost_usdollar' => 'cost_usdollar',
        'currency_id' => 'currency_id',
        'base_rate' => 'base_rate',
        'tax_class' => 'tax_class',
        'weight' => 'weight',
        'manufacturer_id' => 'manufacturer_id',
        'manufacturer_name' => 'manufacturer_name',
        'type_id' => 'type_id',
        'type_name' => 'type_name',
        'service_start_date' => 'service_start_date',
        'service_end_date' => 'service_end_date',
        'service_duration_value' => ['service_duration_value', 'catalog_service_duration_value'],
        'service_duration_unit' => ['service_duration_unit', 'catalog_service_duration_unit'],
        'renewable' => 'renewable',
        'service' => 'service',
    );
    ?>

    Hit Q&R and all should be working. Just be sure that in the custom bean class you don't use namespaces since for some reason which namespaces sugar can't find the class and throws 500 errors all over te place.

Reply
  • Thanks for the quick response and confirming that we indeed need to extend the base class. For completeness I have added the changes I made:

    First we add custom/Extension/application/Ext/Include/customRLI.php to map the new class to the rli object

    <?php
    //
    /**
    * The $objectList array, maps the module name to the Vardef property
    * By default only a few core modules have this defined, since their Class/Object names differs from their Vardef Property
    **/
    $objectList['RevenueLineItems'] = 'RevenueLineItem';
    
    // $beanList maps the Bean/Module name to the Class name
    $beanList['RevenueLineItems'] = 'CustomRevenueLineItem';
    
    // $beanFiles maps the Class name to the PHP Class file
    $beanFiles['CustomRevenueLineItem'] = 'custom/modules/RevenueLineItems/CustomRevenueLineItem.php';

    next we create a new class which extends default Rli class. In this case I overwritten the mapFieldsFromProductTemplate function since I don't want the discount price from the template when we create or change the rli

    <?php
    
    require_once('modules/RevenueLineItems/RevenueLineItem.php');
    
    
    class CustomRevenueLineItem extends RevenueLineItem
    {
        public function __construct()
        {
            parent::__construct();
        }
    
        protected function mapFieldsFromProductTemplate(){
            $GLOBALS['log']->fatal('Custom RLI -> mapFieldsFromProductTemplate called');
            if ($this->product_template_id && (
                    $this->fetched_row === false || $this->fetched_row['product_template_id'] != $this->product_template_id
                )) {
                /* @var $pt ProductTemplate */
                $pt = BeanFactory::getBean('ProductTemplates', $this->product_template_id);
    
                $this->category_id = $pt->category_id;
                $this->mft_part_num = $pt->mft_part_num;
                $this->list_price = SugarCurrency::convertAmount($pt->list_price, $pt->currency_id, $this->currency_id);
                $this->cost_price = SugarCurrency::convertAmount($pt->cost_price, $pt->currency_id, $this->currency_id);
    //            $this->discount_price = SugarCurrency::convertAmount($pt->discount_price, $pt->currency_id, $this->currency_id); // discount_price = unit price on the front end...
                $this->list_usdollar = $pt->list_usdollar;
                $this->cost_usdollar = $pt->cost_usdollar;
    //            $this->discount_usdollar = $pt->discount_usdollar;
                $this->tax_class = $pt->tax_class;
                $this->weight = $pt->weight;
            }
        }
    
    }

    Also we need to prevent the discount price from populating to from the catalogue. Create the following file: 

    custom/Extension/modules/RevenueLineItems/Ext/Vardefs/sugarfield_product_template_name.php. As you can see I rmoved the discount_price fields from the popuplate list.

    <?php
    $dictionary['RevenueLineItem']['fields']['product_template_name']['audited']=false;
    $dictionary['RevenueLineItem']['fields']['product_template_name']['hidemassupdate']=false;
    $dictionary['RevenueLineItem']['fields']['product_template_name']['duplicate_merge']='disabled';
    $dictionary['RevenueLineItem']['fields']['product_template_name']['duplicate_merge_dom_value']=0;
    $dictionary['RevenueLineItem']['fields']['product_template_name']['merge_filter']='disabled';
    $dictionary['RevenueLineItem']['fields']['product_template_name']['calculated']=false;
    $dictionary['RevenueLineItem']['fields']['product_template_name']['populate_list']= array(
        'name' => 'name',
        'category_id' => 'category_id',
        'category_name' => 'category_name',
        'mft_part_num' => 'mft_part_num',
        'list_price' => 'list_price',
        'cost_price' => 'cost_price',
        'list_usdollar' => 'list_usdollar',
        'cost_usdollar' => 'cost_usdollar',
        'currency_id' => 'currency_id',
        'base_rate' => 'base_rate',
        'tax_class' => 'tax_class',
        'weight' => 'weight',
        'manufacturer_id' => 'manufacturer_id',
        'manufacturer_name' => 'manufacturer_name',
        'type_id' => 'type_id',
        'type_name' => 'type_name',
        'service_start_date' => 'service_start_date',
        'service_end_date' => 'service_end_date',
        'service_duration_value' => ['service_duration_value', 'catalog_service_duration_value'],
        'service_duration_unit' => ['service_duration_unit', 'catalog_service_duration_unit'],
        'renewable' => 'renewable',
        'service' => 'service',
    );
    ?>

    Hit Q&R and all should be working. Just be sure that in the custom bean class you don't use namespaces since for some reason which namespaces sugar can't find the class and throws 500 errors all over te place.

Children
No Data