Magento – Magento 2 Catalog price rule on special price

catalogmagento2special-price

Currently Magento is using the special price if it is lower than the applied catalog price rule. If the catalog price rule makes the product cheaper than the special price, then the catalog price rule defines the shop price.

I am looking for an elegant way to make catalog price rules be applied to the special price (additionally). Maybe there is some store config for it? Maybe there is some neat observer way?


EDIT
I thought about editing the rule.php inside vendor/magento/module-catalog-rule/Model/RessourceModel to check the already placed special price (there is a solution like that for magento 1.8) but im kinda stuck there

public function getRulePrices(\DateTimeInterface $date, $websiteId, $customerGroupId, $productIds)
{
    $connection = $this->getConnection();
    $select = $connection->select()
        ->from($this->getTable('catalogrule_product_price'), ['product_id', 'rule_price'])
        ->where('rule_date = ?', $date->format('Y-m-d'))
        ->where('website_id = ?', $websiteId)
        ->where('customer_group_id = ?', $customerGroupId)
        ->where('product_id IN(?)', $productIds);

    return $connection->fetchPairs($select);
}


public function getRulesFromProduct($date, $websiteId, $customerGroupId, $productId)
{
    $connection = $this->getConnection();
    if (is_string($date)) {
        $date = strtotime($date);
    }
    $select = $connection->select()
        ->from($this->getTable('catalogrule_product'))
        ->where('website_id = ?', $websiteId)
        ->where('customer_group_id = ?', $customerGroupId)
        ->where('product_id = ?', $productId)
        ->where('from_time = 0 or from_time < ?', $date)
        ->where('to_time = 0 or to_time > ?', $date);

    return $connection->fetchAll($select);
}

Best Answer

I do not know if this is the best solution - but it works for me (I just needed special prices for customer groups without quantities):

Write a plugin with a before method for https://github.com/magento/magento2/blob/2.3.0/app/code/Magento/CatalogRule/Model/Indexer/ProductPriceCalculator.php#L34

public function beforeCalculate(
    \Magento\CatalogRule\Model\Indexer\ProductPriceCalculator $subject,
    $ruleData,
    $productData = null
  )
  {
    if (isset($ruleData['product_id']) && isset($ruleData['customer_group_id'])) {
      try {
        $specialPrice = $this->getSpecialPrice($ruleData['product_id'], $ruleData['customer_group_id']);
        if ($specialPrice <= 0) {
          return [
            $ruleData,
            $productData,
          ];
        }

        if ($productData !== null && isset($productData['rule_price'])) {
          $productData['rule_price'] = $specialPrice;
        } else {
          $ruleData['default_price'] = $specialPrice;
        }
      } catch (\Exception $noSuchEntityException) {
        // Do nothing
      }
    }

    return [
      $ruleData,
      $productData,
    ];
  }

getSpecialPrice() is a modified version of https://magento.stackexchange.com/a/176738/77389

private function getSpecialPrice($productId, $customerGroupId)
  {
    $product = $this->_productRepository->getById($productId, ['edit_mode' => true]);

    $priceKey = 'website_price';
    $value = $this->_config->getValue('catalog/price/scope', \Magento\Store\Model\ScopeInterface::SCOPE_WEBSITE);
    if ($value == 0) {
      $priceKey = 'price';
    }

    $cgi = ($customerGroupId === 'all'
      ? $this->_groupManagement->getAllCustomersGroup()->getId()
      : $customerGroupId);

    $prices = [];
    foreach ($product->getData('tier_price') as $price) {
      if ((is_numeric($customerGroupId) && intval($price['cust_group']) === intval($customerGroupId))
        || ($customerGroupId === 'all' && $price['all_groups'])
      ) {
        /** @var \Magento\Catalog\Api\Data\ProductTierPriceInterface $tierPrice */
        $tierPrice = $this->_priceFactory->create();
        $tierPrice->setValue($price[$priceKey])
          ->setQty($price['price_qty'])
          ->setCustomerGroupId($cgi);
//        $prices[] = $tierPrice->getValue();
        return $tierPrice->getValue();
      }
    }
//    echo '<pre>';print_r($prices);
    return 0;
  }

I have not yet figured out how to get special pricing for quantities - I'm not sure, but I think if you need special pricing with quantities, this solution will not work for you.

Related Topic