Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Catalog rule for discounted products applied incorrectly with grouped products #774

Closed
sedax90 opened this issue Feb 20, 2018 · 3 comments
Closed

Comments

@sedax90
Copy link

sedax90 commented Feb 20, 2018

This issue is direcly related to #689

I've some parent grouped products with 2 childs.
Each child has two different prices, one 9,02 € and another 1.000,00 €.
No one of this child or parent product has rule or fixed discounted price.
In magento index all prices are stored correcly, but in elasticsearch are stored like:

"price": [ { "price": "9.0200", "original_price": "1000.0000", "is_discount": true, "customer_group_id": "0" } , { "price": "9.0200", "original_price": "1000.0000", "is_discount": true, "customer_group_id": "1" } , { "price": "9.0200", "original_price": "1000.0000", "is_discount": true, "customer_group_id": "2" } , { "price": "9.0200", "original_price": "1000.0000", "is_discount": true, "customer_group_id": "3" } ],

As you can see the min price is placed as discounted price (but is the price of one product), and the max price is placed as original_price, and it's set as "is_discount".

@afoucret
Copy link
Contributor

The PR #776 will fix price indexing for grouped and bundled products.

For catalog rule and tier pricing, see #752

@sedax90
Copy link
Author

sedax90 commented Feb 22, 2018

I've developed this (orrible and absolutly barbaric) code, I don't know if it could be a possible solution, but for me it's working...

<?php
/**
 * DISCLAIMER
 *
 * Do not edit or add to this file if you wish to upgrade Smile Elastic Suite
 * to newer versions in the future.
 *
 * @category  Smile
 * @package   Smile\ElasticsuiteCatalog
 * @author    Aurelien FOUCRET <aurelien.foucret@smile.fr>
 * @copyright 2016 Smile
 * @license   Open Software License ("OSL") v. 3.0
 */

namespace Smile\ElasticsuiteCatalog\Model\Product\Indexer\Fulltext\Datasource;

use Smile\ElasticsuiteCore\Api\Index\DatasourceInterface;
use Smile\ElasticsuiteCatalog\Model\ResourceModel\Product\Indexer\Fulltext\Datasource\PriceData as ResourceModel;

/**
 * Datasource used to append prices data to product during indexing.
 *
 * @category Smile
 * @package  Smile\ElasticsuiteCatalog
 * @author   Aurelien FOUCRET <aurelien.foucret@smile.fr>
 */
class PriceData implements DatasourceInterface {

  /**
   * @var \Smile\ElasticsuiteCatalog\Model\ResourceModel\Product\Indexer\Fulltext\Datasource\PriceData
   */
  private $resourceModel;

  /**
   * @var PriceData\PriceDataReaderInterface[]
   */
  private $priceReaderPool = [];

  /**
   * @var \Magento\Framework\App\ResourceConnection
   */
  private $resource;

  /**
   * Constructor.
   *
   * @param ResourceModel $resourceModel Resource model
   * @param PriceData\PriceDataReaderInterface[] $priceReaderPool Price
   *   modifiers pool.
   */
  public function __construct(
    ResourceModel $resourceModel,
    $priceReaderPool = [],
    \Magento\Framework\App\ResourceConnection $resource
  ) {
    $this->resourceModel = $resourceModel;
    $this->priceReaderPool = $priceReaderPool;
    $this->resource = $resource;
  }

  /**
   * Add price data to the index data.
   *
   * {@inheritdoc}
   */
  public function addData($storeId, array $indexData) {
    $priceData = $this->resourceModel->loadPriceData($storeId,
      array_keys($indexData));

    foreach ($priceData as $priceDataRow) {
      $productId = (int) $priceDataRow['entity_id'];
      $productTypeId = $indexData[$productId]['type_id'];
      $priceModifier = $this->getPriceDataReader($productTypeId);

      $originalPrice = $priceModifier->getOriginalPrice($priceDataRow);
      $price = $priceModifier->getPrice($priceDataRow);

      $childrenIds = [];
      switch ($productTypeId) {
        case "configurable":
          $objectManager = \Magento\Framework\App\ObjectManager::getInstance();
          /** @var \Dexanet\Catalog\Model\Product $product */
          $product = $objectManager->create('Magento\Catalog\Model\Product')
            ->load($productId);

          $childrens = $product->getTypeInstance()->getUsedProducts($product);
          foreach ($childrens as $children) {
            $childrenIds[] = $children->getId();
          }
          break;

        case "grouped":
          $objectManager = \Magento\Framework\App\ObjectManager::getInstance();
          /** @var \Dexanet\Catalog\Model\Product $product */
          $product = $objectManager->create('Magento\Catalog\Model\Product')
            ->load($productId);

          $childrens = $product->getTypeInstance()->getAssociatedProducts($product);
          foreach ($childrens as $children) {
            $childrenIds[] = $children->getId();
          }
          break;
      }

      if (isset($childrenIds) && !empty($childrenIds)) {
        $priceInRule = $this->getPriceInRule($childrenIds,
          $priceDataRow['customer_group_id']);

        if (!empty($priceInRule) && $priceInRule != $price) {
          $price = $priceInRule;
        }
      }

      $indexData[$productId]['price'][] = [
        'price' => $price,
        'original_price' => $originalPrice,
        'is_discount' => $price < $originalPrice,
        'customer_group_id' => $priceDataRow['customer_group_id'],
      ];

      if (!isset($indexData[$productId]['indexed_attributes'])) {
        $indexData[$productId]['indexed_attributes'] = ['price'];
      }
      elseif (!in_array('price',
        $indexData[$productId]['indexed_attributes'])) {
        // Add price only one time.
        $indexData[$productId]['indexed_attributes'][] = 'price';
      }
    }

    return $indexData;
  }

  /**
   * Retur
   *
   * @param string $typeId Product type id.
   *
   * @return PriceData\PriceDataReaderInterface
   */
  private function getPriceDataReader($typeId) {
    $priceModifier = $this->priceReaderPool['default'];

    if (isset($this->priceReaderPool[$typeId])) {
      $priceModifier = $this->priceReaderPool[$typeId];
    }

    return $priceModifier;
  }

  protected function getPriceInRule($childrenIds, $customerGroupId) {
    $connection = $this->resource->getConnection();
    $indexTable = $this->resource->getTableName('catalogrule_product_price_replica');

    $query = "SELECT min(i.rule_price) as min_price FROM {$indexTable} i WHERE i.product_id IN (" . implode(",",
        $childrenIds) . ") AND i.customer_group_id = '{$customerGroupId}' and DATE(NOW()) BETWEEN i.latest_start_date AND i.earliest_end_date";

    $result = $connection->fetchCol($query, "rule_price");
    return (!empty($result)) ? reset($result) : NULL;
  }
}

@cgsoratto
Copy link

cgsoratto commented Aug 21, 2018

Hi @CRYX2!

Thank you for your help! But, I made other solution! See:

1º. I creating module for override class PriceData.

  <preference for="Smile\ElasticsuiteCatalog\Model\Product\Indexer\Fulltext\Datasource\PriceData"
                type="Vendor\Module\Model\Product\Indexer\Fulltext\Datasource\PriceData" />

2º Look the new class PriceData:

<?php

namespace Vendor\Module\Model\Product\Indexer\Fulltext\Datasource;

use Smile\ElasticsuiteCore\Api\Index\DatasourceInterface;
use Smile\ElasticsuiteCatalog\Model\ResourceModel\Product\Indexer\Fulltext\Datasource\PriceData as ResourceModel;
use Smile\ElasticsuiteCatalog\Model\Product\Indexer\Fulltext\Datasource\PriceData as nativePriceData;
use Magento\Catalog\Api\ProductRepositoryInterface;

class PriceData extends nativePriceData implements DatasourceInterface
{
    private $resourceModel;

    private $priceReaderPool = [];

    private $productAPI;

    public function __construct(
        ResourceModel $resourceModel,
        ProductRepositoryInterface $productAPI,
        $priceReaderPool = []
    ) {
        $this->resourceModel     = $resourceModel;
        $this->productAPI = $productAPI;
        $this->priceReaderPool = $priceReaderPool;
    }

    public function addData($storeId, array $indexData)
    {
        $priceData = $this->resourceModel->loadPriceData($storeId, array_keys($indexData));

        foreach ($priceData as $priceDataRow) {
            $productId     = (int) $priceDataRow['entity_id'];
            $productTypeId = $indexData[$productId]['type_id'];
            $priceModifier = $this->getPriceDataReader($productTypeId);

            $originalPrice = $priceModifier->getOriginalPrice($priceDataRow);
            $price         = $priceModifier->getPrice($priceDataRow);

            if ($productTypeId == "configurable") {
                $configProduct = $this->productAPI->getById($productId);
                $_childrens = $configProduct->getTypeInstance()->getUsedProducts($configProduct);

                $today = time();
                $max_price = 0;
                $min_price = 0;

                foreach ($_childrens as $_children) {
                    $price = $_children->getPrice();
                    $specialprice = $_children->getSpecialPrice();
                    if ($specialprice) {
                        $specialPriceFromDate = $_children->getSpecialFromDate();
                        $specialPriceToDate = $_children->getSpecialToDate();
                        if ($today >= strtotime($specialPriceFromDate)
                            && $today <= strtotime($specialPriceToDate) || $today >= strtotime($specialPriceFromDate)
                            && is_null($specialPriceToDate)) {
                            $specialprice = $_children->getSpecialPrice();
                        }

                        if ($specialprice < $min_price || $min_price == 0) {
                            $min_price = $specialprice;
                        }
                    }

                    if ($price > $max_price) {
                        $max_price = $price;
                    }
                }

                $originalPrice = $max_price ?: $priceModifier->getOriginalPrice($priceDataRow);
                $price = $min_price ?: $priceModifier->getPrice($priceDataRow);
            }

            $indexData[$productId]['price'][] = [
                'price'             => $price,
                'original_price'    => $originalPrice,
                'is_discount'       => $price < $originalPrice,
                'customer_group_id' => $priceDataRow['customer_group_id'],
            ];

            if (!isset($indexData[$productId]['indexed_attributes'])) {
                $indexData[$productId]['indexed_attributes'] = ['price'];
            } elseif (!in_array('price', $indexData[$productId]['indexed_attributes'])) {
                // Add price only one time.
                $indexData[$productId]['indexed_attributes'][] = 'price';
            }
        }

        return $indexData;
    }

    private function getPriceDataReader($typeId)
    {
        $priceModifier = $this->priceReaderPool['default'];

        if (isset($this->priceReaderPool[$typeId])) {
            $priceModifier = $this->priceReaderPool[$typeId];
        }

        return $priceModifier;
    }
}

With this, all product with 'type' in 'configurable', will get maximum price and also minimum price between all childrens. Of course, if the Special Price for true.

Bye!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants