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 the 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 that can be sold separately or in bulk. Let’s take the example of soap. Users can purchase the soap single or in a pack of 8 to which they call family package, and there is also one more option: Users can purchase a pack of 24 to which they call a box.
To fulfill the client’s requirement, we’ve decided to override the tier price functionality and divide the solution into two parts.
- Add a secondary unit column in the 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 following 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 the code below to 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 in 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 an 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 to the grid and the database. Now, we have to override the handlers using the preference.
To do so, we’ve to create the SaveHandler.php in the Model directory. This file overrides the save functionality of the tier price, which will execute during the save of 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 updating 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 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 into 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 code below.
<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 all the necessary code to override the tier pricing functionality. Now, just execute the command below from the Magento root directory and verify your implementation.
php bin/magento setup:upgrade
php bin/magento setup:di:compileI 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!