Magento – Calculating Product Price Dynamically adminhtml – Magento 2

magento2product-attributeproduct-prices

I added some custom attributes to my product such as cost and markup. By default markup will be set to 1.5 but the admin can adjust this.

When I am in the admin panel, I want to be able to observe a change on the cost field and have the price field populated automatically based on the formula:

price = cost * markup

If anyone can point me in the right direction on where this code may need to be updated or provide any examples that would be awesome.

Best Answer

First off, I would suggest updating to Magento 2.1.3 first, since this changes some fundamental ways on how products are saved.

Secondly, you need to catch a hook on where the product is saved. An event or a plugin (interceptor) could be used for this.

You could add a plugin before the save method of the product repository:

public function beforeSave(
    \Magento\Catalog\Model\ProductRepository $subject,
    \Magento\Catalog\Api\Data\ProductInterface $product,
    bool $saveOptions
) {
    ...

    return [$product, $saveOptions];
}

Or a plugin for the save()-method on the resource model:

public function beforeSave(
    \Magento\Catalog\Model\ResourceModel\Product $subject,
    \Magento\Framework\Model\AbstractModel $object
) {
    ...

    return [$object];
}

Or catch the event (I believe in 2.1.2 you can still use catalog_product_save_before, but in 2.1.3 the saving of the product is done with the entity manager. I'm not sure if this event still exists in 2.1.3). Haven't had time to test this out yet.

For any of these options I would suggest looking at the Price Modifier (Magento\Catalog\Model\Product\PriceModifierInterface). This has a modifyPrice()-method that allows you to set a new price for a product.

protected $priceModifier;

public function __construct(
    \Magento\Catalog\Model\Product\PriceModifierInterface $priceModifier
) {
    $this->priceModifier = $priceModifier;
}

public function changePrice()
{
    ... get price and product ...

    $this->priceModifier->modifyPrice($price, $product);
}

Haven't had the time to test any of the above yet, but perhaps it helps you find your solution.

JavaScript

Since you stated in your comment that you want the price to be updated realtime, you need to add some JavaScript. Once again, there are various ways to do this. The most elegant way would require you to create a new UI Component that does your magic on change and apply it to the cost-input-field.

There also is a more 'hacky' way to do is by extending the JavaScript Magento_Ui/js/form/element/abstract and manipulate the onUpdate()-method. This method is fired when the user changes input on any input field so we need to do an additional check:

/**
 * Callback that fires when 'value' property is updated.
 */
onUpdate: function () {
  this.bubble('update', this.hasChanged());

  if (this.namespace === 'product_form' && this.index === 'cost') {
    // The most hacky solution I've ever seen ...
    var priceField = document.querySelector('[name="product\\[price\\]"]');
    priceField.value = this.value() * 1.5;

    // Force trigger the 'change'-event on this field:
    var event = new Event('change');
    priceField.dispatchEvent(event);
  }

  this.validate();
}

Needless to say, this is very ugly. But if you look at the code, you'll see that the update-event bubbles. So a more proper way would be to utilise this event and leave the original class intact. Unfortunately for you I don't have too much experience on this specific area, but perhaps it gives you a good starting point.

In Conclusion

To achieve your goal you need to do the following:

  • Update on save (in case the product is saved somewhere other than the form).
  • Extend an existing JS Component to manipulate the onUpdate method - OR - utilize the event bubbling in UI Components.
Related Topic