Here, In this blog, I’ll guide you to add columns in the tier price grid of the product. Sometimes it’s essential to understand client’s Magento web development requirements by overriding the core modules. Here, I am sharing a use case in which we’ve to override the tier price functionality.
Use Case: Our client has a few products which can be sold separately or in a bulk. Let’s take the example of soap. Users can purchase the soap single or in a pack of 8 to which they called family package and also there is one more option, Users can also purchase a pack of 24 to which they called Box.
To fulfill the client requirement we’ve decided to override the tier price functionality and divided the solution into the two parts.
- Add a secondary unit column in tier price
- Display units on the frontend by using the dropdown.
This blog consists of a first solution which is, to add a column in the tier price grid. To do so, you have to do the below things in your extension.
- Add a required field in the tier price table
- Add a field in the grid
- Override save & update handlers
Let’s follow the above steps and customize the tier price.
1. Add a required field in the tier price table:
Here, we’ll add the field in the tier price table using the database schema. To do so, create the db_schema.xml in the etc folder and add a below code in it.
<schema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd">
<table name="catalog_product_entity_tier_price" resource="default" engine="innodb" comment="Tier Pricing Table">
<column xsi:type="varchar" name="{KEY}" nullable="true" length="255" comment="comment" />
</table>
</schema>
2. Add a field in the grid:
We’ll add a field in a tier price grid using the modifiers by the following way. First we’ll create a di.xml in the etc/adminhtml and add a modifier pool in it.
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<virtualType name="Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\Pool" type="Magento\Ui\DataProvider\Modifier\Pool">
<arguments>
<argument name="modifiers" xsi:type="array">
<item name="{identifier}" xsi:type="array">
<item name="class" xsi:type="string">{vendor}\{module}\Ui\DataProvider\Product\Form\Modifier\UpdateTierPricing</item>
<item name="sortOrder" xsi:type="number">200</item>
</item>
</argument>
</arguments>
</virtualType>
<type name="{vendor}\{module}\Ui\DataProvider\Product\Form\Modifier\UpdateTierPricing">
<arguments>
<argument name="scopeName" xsi:type="string">product_form.product_form</argument>
</arguments>
</type>
</config>
Now, create a UpdateTierPricing.php at the Ui\DataProvider\Product\Form\Modifier and add the below code.
class UpdateTierPricing extends AbstractModifier
{
/**
* @var ArrayManager
* @since 101.0.0
*/
protected $arrayManager;
/**
* @var string
* @since 101.0.0
*/
protected $scopeName;
/**
* @var array
* @since 101.0.0
*/
protected $meta = [];
/**
* UpdateTierPricing constructor.
* @param ArrayManager $arrayManager
*/
public function __construct(
ArrayManager $arrayManager
) {
$this->arrayManager = $arrayManager;
}
/**
* @param array $data
* @return array
* @since 100.1.0
*/
public function modifyData(array $data)
{
// TODO: Implement modifyData() method.
return $data;
}
/**
* @param array $meta
* @return array
* @since 100.1.0
*/
public function modifyMeta(array $meta)
{
// TODO: Implement modifyMeta() method.
$this->meta = $meta;
$this->customizeTierPrice();
return $this->meta;
}
/**
* @return $this
*/
private function customizeTierPrice()
{
$tierPricePath = $this->arrayManager->findPath(
ProductAttributeInterface::CODE_TIER_PRICE,
$this->meta,
null,
'children'
);
if ($tierPricePath) {
$this->meta = $this->arrayManager->merge(
$tierPricePath,
$this->meta,
$this->getTierPriceStructure()
);
}
return $this;
}
/**
* @return array
*/
private function getTierPriceStructure()
{
return [
'children' => [
'record' => [
'children' => [
{KEY} => [
'arguments' => [
'data' => [
'config' => [
'formElement' => Input::NAME,
'componentType' => Field::NAME,
'dataType' => Number::NAME,
'label' => __('Label'),
'dataScope' => '{KEY},
'sortOrder' => 25,
],
],
],
],
],
],
],
];
}
}
3. Override save & update handlers:
After performing the above steps we’ve added the field in the grid and the database. Now, we have to override the handlers using the preference.
To do so, we’ve to first create the SaveHandler.php in the Model directory. This file overrides the save functionality of tier price which will execute during the save the new product.
use Magento\Catalog\Model\Product\Attribute\Backend\TierPrice\SaveHandler;
class SavePriceHandler extends SaveHandler
{
/**
* Get additional tier price fields.
*
* @param array $objectArray
* @return array
*/
public function getAdditionalFields(array $objectArray): array
{
$percentageValue = $this->getPercentage($objectArray);
return [
'value' => $percentageValue ? null : $objectArray['price'],
'percentage_value' => $percentageValue ?: null,
{KEY} => $this->getSecondaryUnit($objectArray),
];
}
/**
* @param array $priceRow
* @return mixed|null
*/
public function getSecondaryUnit(array $priceRow)
{
return isset($priceRow[{KEY}]) && !empty($priceRow[{KEY}])
? $priceRow[{KEY}]
: null;
}
}
To update the tier price data while update the product need to create the UpdateHandler.php in the Model directory and copy the code from vendor/magento/module-catalog/Model/Product/Attribute/Backend/TierPrice/UpdateHandler.php and remove the updateValues function from the code and add below code in the file.
Note: Here, don’t forget to extend the UpdateHandler.
/**
* Update existing tier prices for processed product
*
* @param array $valuesToUpdate
* @param array $oldValues
* @return bool
*/
public function updateValues(array $valuesToUpdate, array $oldValues): bool
{
$isChanged = false;
foreach ($valuesToUpdate as $key => $value) {
if ((!empty($value['value']) && (float)$oldValues[$key]['price'] !== (float)$value['value'])
|| $this->getPercentage($oldValues[$key]) !== $this->getPercentage($value)
|| $this->getSecondaryUnit($oldValues[$key]) !== $this->getSecondaryUnit($value)
) {
$price = new \Magento\Framework\DataObject(
[
'value_id' => $oldValues[$key]['price_id'],
'value' => $value['value'],
'percentage_value' => $this->getPercentage($value),
{KEY} => $this->getSecondaryUnit($value),
]
);
$this->tierPriceResource->savePriceData($price);
$isChanged = true;
}
}
return $isChanged;
}
/**
* Get additional tier price fields.
*
* @param array $objectArray
* @return array
*/
public function getAdditionalFields(array $objectArray): array
{
$percentageValue = $this->getPercentage($objectArray);
return [
'value' => $percentageValue ? null : $objectArray['price'],
'percentage_value' => $percentageValue ?: null,
{KEY} => $this->getSecondaryUnit($objectArray),
];
}
/**
* @param array $priceRow
* @return mixed|null
*/
public function getSecondaryUnit(array $priceRow)
{
return isset($priceRow[{KEY}]) && !empty($priceRow[{KEY}])
? $priceRow[{KEY}]
: null;
}
Create DataColumnUpdate.php in the Model directory. This file will load the saved data in the database.
class DataColumnUpdate extends Tierprice
{
/**
* @param array $columns
* @return array
*/
protected function _loadPriceDataColumns($columns)
{
$columns = parent::_loadPriceDataColumns($columns);
$columns[{KEY}] = {KEY};
return $columns;
}
}
Now, you have to add the di.xml in the etc directory and add below code.
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<preference for="Magento\Catalog\Model\Product\Attribute\Backend\TierPrice\UpdateHandler"
type="{vendor}\{module}\Model\UpdatePriceHandler" />
<preference for="Magento\Catalog\Model\Product\Attribute\Backend\TierPrice\SaveHandler"
type="{vendor}\{module}\Model\Product\Attribute\Backend\TierPrice\SavePriceHandler" />
<preference for="Magento\Catalog\Model\ResourceModel\Product\Attribute\Backend\Tierprice"
type="{vendor}\{module}\Model\ResourceModel\Product\Attribute\Backend\DataColumnUpdate" />
</config>
Here, we’ve added the all necessary code to override the tier pricing functionality. Now, just execute the below command from the Magento root directory and verify your implementation.
php bin/magento setup:upgrade
php bin/magento setup:di:compile
I hope this solution will resolve your problem. Let me know your thoughts in the comment section. And if you have any queries with this blog, I will be happy to solve that. Thanks and cheers … 🙂
Also read: Convert the price from one current to another in Magento 2
Hi I am getting following error when saving the tier price
Type Error occurred when creating object: Vendor\Module\Model\Product\Attribute\Backend\Tierprice\UpdatePriceHandler
Nice tutorial. I tried to follow and add column in M2 but I get following error after I run
php bin/magento setup:upgrade
PHP Fatal error: Class ‘AbstractModifier’ not found in /var/www/vhosts/{site}/httpdocs/app/code/{MOD}/TierPriceMod/Ui/DataProvider/Product/Form/Modifier/UpdateTierPricing.php on line 2
I have followed this tutorial but I’m getting an error while updating the value:
Fatal error: Uncaught Error: Call to a member function getAllCustomersGroup() on null in
Great tutorial!
How retrive the custom value programmatically (in phtml page, controller… for example)?
Thanks!