Question

I have added an extra column 'title' to the catalog_product_entity_tier_price table using Setup/UpdateSchema.php and an extra field to the tier price backend by overwriting Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\AdvancedPricing::getTierPriceStructure:

class InstallSchema implements InstallSchemaInterface
{

    /**
     * {@inheritdoc}
     * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
     */
    public function install(SchemaSetupInterface $setup, ModuleContextInterface $context)
    {
        $setup->startSetup();

        /** Tier Price Label **/
        $setup->getConnection()->addColumn(
            $setup->getTable('catalog_product_entity_tier_price'),
            'title',
            [
                'type' => \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
                'length' => 255,
                'nullable' => true,
                'comment' => 'Tier Price Title'
            ]
        );

        $setup->endSetup();
    }

}

enter image description here

database

product- loading=advanced pricing">

I've added the database column by overwriting Magento\Catalog\Model\ResourceModel\Product\Attribute\Backend\Tierprice::_loadPriceDataColumns, so my data from the database is loaded into the corresponding field - as seen above.

class Tierprice extends \Magento\Catalog\Model\ResourceModel\Product\Attribute\Backend\Tierprice
{
    /**
     * Initialize connection and define main table
     *
     * @return void
     */
    protected function _construct()
    {
        $this->_init('catalog_product_entity_tier_price', 'value_id');
    }

    /**
     * Add qty column & title column
     *
     * @param array $columns
     * @return array
     */
    protected function _loadPriceDataColumns($columns)
    {
        $columns = parent::_loadPriceDataColumns($columns);
        $columns['price_qty'] = 'qty';
        $columns['percentage_value'] = 'percentage_value';

        /* Start TierTitle */
        $columns['title'] = 'title';
        /* End TierTitle */

        return $columns;
    }

    /**
     * Order by qty
     *
     * @param \Magento\Framework\DB\Select $select
     * @return \Magento\Framework\DB\Select
     */
    protected function _loadPriceDataSelect($select)
    {
        $select->order('qty');
        return $select;
    }
}

I've added an extension_attribute so that my getters / setters are created:

<extension_attributes for="Magento\Catalog\Api\Data\ProductTierPriceInterface">
    <attribute code="title" type="string">
        <join reference_table="catalog_product_entity_tier_price" reference_field="product_id" join_on_field="entity_id">
            <field>title</field>
        </join>
    </attribute>
</extension_attributes>

These are created in generated/code/Magento/Catalog/Api/Data/ProductTierPriceExtension.php and in generated/code/Magento/Catalog/Api/Data/ProductTierPriceExtensionInterface.php.

As per Jonas Chen's answer below I've overwritten Magento\Catalog\Model\Product\Type\Price::setTierPrices and used $price->getExtensionAttributes()->getTitle():

class Price extends \Magento\Catalog\Model\Product\Type\Price
{
    /**
     * Sets list of product tier prices
     *
     * @param \Magento\Catalog\Model\Product $product
     * @param \Magento\Catalog\Api\Data\ProductTierPriceInterface[] $tierPrices
     * @return $this
     */
    public function setTierPrices($product, array $tierPrices = null)
    {
        // null array means leave everything as is
        if ($tierPrices === null) {
            return $this;
        }

        $allGroupsId = $this->getAllCustomerGroupsId();
        $websiteId = $this->getWebsiteForPriceScope();

        // build the new array of tier prices
        $prices = [];
        foreach ($tierPrices as $price) {
            $extensionAttributes = $price->getExtensionAttributes();
            $priceWebsiteId = $websiteId;
            $priceTitle = "";
            if (isset($extensionAttributes) && is_numeric($extensionAttributes->getWebsiteId())) {
                $priceWebsiteId = (string)$extensionAttributes->getWebsiteId();
            }
            if (isset($extensionAttributes) && $extensionAttributes->getTitle()) {
                $priceTitle = (string)$extensionAttributes->getTitle();
            }
            $prices[] = [
                'website_id' => $priceWebsiteId,
                'cust_group' => $price->getCustomerGroupId(),
                'website_price' => $price->getValue(),
                'price' => $price->getValue(),
                'all_groups' => ($price->getCustomerGroupId() == $allGroupsId),
                'price_qty' => $price->getQty(),
                'percentage_value' => $extensionAttributes ? $extensionAttributes->getPercentageValue() : null,
                'title' => $priceTitle
            ];
        }

        $product->setData('tier_price', $prices);

        return $this;
    }
}

And I've overwritten Model\Product\Attribute\Backend\Tierprice:

class Tierprice extends \Magento\Catalog\Model\Product\Attribute\Backend\Tierprice
{
    protected function getAdditionalFields($objectArray)
    {
        $percentageValue = $this->getPercentage($objectArray);
        $titleValue = $this->getTierTitle($objectArray);
        return [
            'value' => $percentageValue ? null : $objectArray['price'],
            'percentage_value' => $percentageValue ?: null,
            'title' => $titleValue ? $titleValue : null,
        ];
    }

    /**
     * @param array $valuesToUpdate
     * @param array $oldValues
     * @return boolean
     */
    protected function updateValues(array $valuesToUpdate, array $oldValues)
    {
        $isChanged = false;
        foreach ($valuesToUpdate as $key => $value) {

            if ((!empty($value['value']) && $oldValues[$key]['price'] != $value['value'])
                || $this->getPercentage($oldValues[$key]) != $this->getPercentage($value)
                || $this->getTierTitle($oldValues[$key]) != $this->getTierTitle($value)
            ) {
                $price = new \Magento\Framework\DataObject(
                    [
                        'value_id' => $oldValues[$key]['price_id'],
                        'value' => $value['value'],
                        'percentage_value' => $this->getPercentage($value),
                        'title' => $this->getTierTitle($value)
                    ]
                );
                $this->_getResource()->savePriceData($price);

                $isChanged = true;
            }
        }
        return $isChanged;
    }

    /**
     * Check whether price has percentage value.
     *
     * @param array $priceRow
     * @return null
     */
    private function getPercentage($priceRow)
    {
        return isset($priceRow['percentage_value']) && is_numeric($priceRow['percentage_value'])
            ? $priceRow['percentage_value']
            : null;
    }

    /**
     * Check whether price has title value.
     *
     * @param $priceRow
     * @return null
     */
    private function getTierTitle($priceRow)
    {
        return isset($priceRow['title'])
            ? $priceRow['title']
            : null;
    }

}

Following this the backend values are saved into the database, however now the the tierprice object does not contain the title.

I've tried overwriting Magento\Catalog\Model\Product\Type\Price::getTierPrices, but calling $tierPrices = $this->getExistingPrices($product, 'tier_price'); is already missing the title.

Answer

I had to also use a plugin on Magento\Catalog\Model\ResourceModel\Product\Attribute\Backend\GroupPrice\AbstractGroupPrice and had to add my custom database column on top of setting my title in my overwrite of Magento\Catalog\Model\Product\Type\Price::getTierPrices:

class AbstractGroupPricePlugin
{
    public function afterGetSelect(
        \Magento\Catalog\Model\ResourceModel\Product\Attribute\Backend\GroupPrice\AbstractGroupPrice $subject,
        $result
    )
    {
        //add title column to select
        $result->columns('title');

        return $result;
    }
}

I hope this helps someone else also.

Was it helpful?

Solution

for Magento\Catalog\Model\Product\Type\Price::setTierPrices get value from $extensionAttributes->getTitle()

public function setTierPrices($product, array $tierPrices = null)
{
    // null array means leave everything as is
    if ($tierPrices === null) {
        return $this;
    }

    $allGroupsId = $this->getAllCustomerGroupsId();
    $websiteId = $this->getWebsiteForPriceScope();

    // build the new array of tier prices
    $prices = [];
    foreach ($tierPrices as $price) {
        $extensionAttributes = $price->getExtensionAttributes();
        $priceWebsiteId = $websiteId;
        $priceTitle = "";
        if (isset($extensionAttributes) && is_numeric($extensionAttributes->getWebsiteId())) {
            $priceWebsiteId = (string)$extensionAttributes->getWebsiteId();
        }
        if (isset($extensionAttributes) && $extensionAttributes->getTitle()) {
            $priceTitle = (string)$extensionAttributes->getTitle();
        }
        $prices[] = [
            'website_id' => $priceWebsiteId,
            'cust_group' => $price->getCustomerGroupId(),
            'website_price' => $price->getValue(),
            'price' => $price->getValue(),
            'all_groups' => ($price->getCustomerGroupId() == $allGroupsId),
            'price_qty' => $price->getQty(),
            'percentage_value' => $extensionAttributes ? $extensionAttributes->getPercentageValue() : null,

            'price_title' => $priceTitle
        ];
    }

    $product->setData('tier_price', $prices);

    return $this;
}

when save value in backend, try overwrite \Magento\Catalog\Model\Product\Attribute\Backend\Tierprice updateValues and getAdditionalFields functions

protected function getAdditionalFields($objectArray)
{
    $percentageValue = $this->getPercentage($objectArray);
    $titleValue = $objectArray['title'];
    return [
        'value' => $percentageValue ? null : $objectArray['price'],
        'percentage_value' => $percentageValue ?: null,
        'title' => $titleValue ? $titleValue : "",
    ];
}

/**
 * @param array $valuesToUpdate
 * @param array $oldValues
 * @return boolean
 */
protected function updateValues(array $valuesToUpdate, array $oldValues)
{
    $isChanged = false;
    foreach ($valuesToUpdate as $key => $value) {
        //check if "title" changed
        if ((!empty($value['value']) && $oldValues[$key]['price'] != $value['value'])
            || (!empty($value['title']) && $oldValues[$key]['title'] != $value['title'])
            || $this->getPercentage($oldValues[$key]) != $this->getPercentage($value)
        ) {
            $price = new \Magento\Framework\DataObject(
                [
                    'value_id' => $oldValues[$key]['price_id'],
                    'value' => $value['value'],
                    'percentage_value' => $this->getPercentage($value),
                    'title' => $value['title']
                ]
            );
            $this->_getResource()->savePriceData($price);

            $isChanged = true;
        }
    }
    return $isChanged;
}

OTHER TIPS

First of all, I thank you for sharing this dev .

I have the same need as you, I have to add on "catalog_product_entity_tier_price" a special price that will be applied only on particular customer groups, so I followed the steps you marked on the page, but I can't set the value on the custom filed

Could you confirm me if there is a part of dev missing, here is my di.xml: <preference for="Magento\Catalog\Model\ResourceModel\Product\Attribute\Backend\Tierprice" type="Klp\Catalog\Preference\Model\ResourceModel\Product\Attribute\Backend\TierPrice"/>

<type name="Magento\Catalog\Model\ResourceModel\Product\Attribute\Backend\GroupPrice\AbstractGroupPrice">
    <plugin name="klp_get_tier_prices" type="Klp\Catalog\Plugin\Model\ResourceModel\Product\Attribute\Backend\GroupPrice\AbstractGroupPrice"/>
</type>

    <preference for="Magento\Catalog\Model\Product\Type\Price" type="Klp\Catalog\Preference\Model\Product\Type\Price"/>
<preference for="Magento\Catalog\Model\Product\Attribute\Backend\Tierprice" type="Klp\Catalog\Preference\Model\Product\Attribute\Backend\Tierprice"/>

and in adminhtml , I add :

   <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="advanced-pricing" xsi:type="array">
                <item name="class" xsi:type="string">Klp\Catalog\Ui\DataProvider\Product\Form\Modifier\AdvancedPricing</item>
                <item name="sortOrder" xsi:type="number">90</item>
            </item>
        </argument>
    </arguments>
</virtualType>
Licensed under: CC-BY-SA with attribution
Not affiliated with magento.stackexchange
scroll top