diff --git a/vendor/magento/module-sales/Model/ResourceModel/Report/Bestsellers.php b/vendor/magento/module-sales/Model/ResourceModel/Report/Bestsellers.php
index e37e8ab843e..a4d2d3cbb94 100644
--- a/vendor/magento/module-sales/Model/ResourceModel/Report/Bestsellers.php
+++ b/vendor/magento/module-sales/Model/ResourceModel/Report/Bestsellers.php
@@ -5,25 +5,39 @@
*/
namespace Magento\Sales\Model\ResourceModel\Report;
+use Magento\Catalog\Model\ResourceModel\Product;
+use Magento\Framework\App\ObjectManager;
+use Magento\Framework\DB\Select;
+use Magento\Framework\Exception\LocalizedException;
+use Magento\Framework\Model\ResourceModel\Db\Context;
+use Magento\Framework\Stdlib\DateTime\DateTime;
+use Magento\Framework\Stdlib\DateTime\Timezone\Validator;
+use Magento\Framework\Stdlib\DateTime\TimezoneInterface;
+use Magento\Reports\Model\FlagFactory;
+use Magento\Sales\Model\ResourceModel\Helper;
+use Magento\Store\Model\Store;
+use Magento\Store\Model\StoreManagerInterface;
+use Psr\Log\LoggerInterface;
+
/**
* Bestsellers report resource model
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class Bestsellers extends AbstractReport
{
- const AGGREGATION_DAILY = 'daily';
+ public const AGGREGATION_DAILY = 'daily';
- const AGGREGATION_MONTHLY = 'monthly';
+ public const AGGREGATION_MONTHLY = 'monthly';
- const AGGREGATION_YEARLY = 'yearly';
+ public const AGGREGATION_YEARLY = 'yearly';
/**
- * @var \Magento\Catalog\Model\ResourceModel\Product
+ * @var Product
*/
protected $_productResource;
/**
- * @var \Magento\Sales\Model\ResourceModel\Helper
+ * @var Helper
*/
protected $_salesResourceHelper;
@@ -37,29 +51,36 @@ class Bestsellers extends AbstractReport
];
/**
- * @param \Magento\Framework\Model\ResourceModel\Db\Context $context
- * @param \Psr\Log\LoggerInterface $logger
- * @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate
- * @param \Magento\Reports\Model\FlagFactory $reportsFlagFactory
- * @param \Magento\Framework\Stdlib\DateTime\Timezone\Validator $timezoneValidator
- * @param \Magento\Framework\Stdlib\DateTime\DateTime $dateTime
- * @param \Magento\Catalog\Model\ResourceModel\Product $productResource
- * @param \Magento\Sales\Model\ResourceModel\Helper $salesResourceHelper
+ * @var StoreManagerInterface
+ */
+ protected $storeManager;
+
+ /**
+ * @param Context $context
+ * @param LoggerInterface $logger
+ * @param TimezoneInterface $localeDate
+ * @param FlagFactory $reportsFlagFactory
+ * @param Validator $timezoneValidator
+ * @param DateTime $dateTime
+ * @param Product $productResource
+ * @param Helper $salesResourceHelper
+ * @param string|null $connectionName
* @param array $ignoredProductTypes
- * @param string $connectionName
+ * @param StoreManagerInterface|null $storeManager
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
public function __construct(
- \Magento\Framework\Model\ResourceModel\Db\Context $context,
- \Psr\Log\LoggerInterface $logger,
- \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate,
- \Magento\Reports\Model\FlagFactory $reportsFlagFactory,
- \Magento\Framework\Stdlib\DateTime\Timezone\Validator $timezoneValidator,
- \Magento\Framework\Stdlib\DateTime\DateTime $dateTime,
- \Magento\Catalog\Model\ResourceModel\Product $productResource,
- \Magento\Sales\Model\ResourceModel\Helper $salesResourceHelper,
- $connectionName = null,
- array $ignoredProductTypes = []
+ Context $context,
+ LoggerInterface $logger,
+ TimezoneInterface $localeDate,
+ FlagFactory $reportsFlagFactory,
+ Validator $timezoneValidator,
+ DateTime $dateTime,
+ Product $productResource,
+ Helper $salesResourceHelper,
+ ?string $connectionName = null,
+ array $ignoredProductTypes = [],
+ ?StoreManagerInterface $storeManager = null
) {
parent::__construct(
$context,
@@ -73,6 +94,7 @@ class Bestsellers extends AbstractReport
$this->_productResource = $productResource;
$this->_salesResourceHelper = $salesResourceHelper;
$this->ignoredProductTypes = array_merge($this->ignoredProductTypes, $ignoredProductTypes);
+ $this->storeManager = $storeManager ?: ObjectManager::getInstance()->get(StoreManagerInterface::class);
}
/**
@@ -92,123 +114,162 @@ class Bestsellers extends AbstractReport
* @param string|int|\DateTime|array|null $to
* @return $this
* @throws \Exception
- * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/
public function aggregate($from = null, $to = null)
{
$connection = $this->getConnection();
- //$this->getConnection()->beginTransaction();
-
- try {
- if ($from !== null || $to !== null) {
- $subSelect = $this->_getTableDateRangeSelect(
- $this->getTable('sales_order'),
- 'created_at',
- 'updated_at',
- $from,
- $to
- );
- } else {
- $subSelect = null;
- }
-
- $this->_clearTableByDateRange($this->getMainTable(), $from, $to, $subSelect);
- // convert dates to current admin timezone
- $periodExpr = $connection->getDatePartSql(
- $this->getStoreTZOffsetQuery(
- ['source_table' => $this->getTable('sales_order')],
- 'source_table.created_at',
- $from,
- $to
- )
- );
- $select = $connection->select();
-
- $select->group([$periodExpr, 'source_table.store_id', 'order_item.product_id']);
-
- $columns = [
- 'period' => $periodExpr,
- 'store_id' => 'source_table.store_id',
- 'product_id' => 'order_item.product_id',
- 'product_name' => new \Zend_Db_Expr('MIN(order_item.name)'),
- 'product_price' => new \Zend_Db_Expr(
- 'MIN(IF(order_item_parent.base_price, order_item_parent.base_price, order_item.base_price))' .
- '* MIN(source_table.base_to_global_rate)'
- ),
- 'qty_ordered' => new \Zend_Db_Expr('SUM(order_item.qty_ordered)'),
- ];
-
- $select->from(
- ['source_table' => $this->getTable('sales_order')],
- $columns
- )->joinInner(
- ['order_item' => $this->getTable('sales_order_item')],
- 'order_item.order_id = source_table.entity_id',
- []
- )->joinLeft(
- ['order_item_parent' => $this->getTable('sales_order_item')],
- 'order_item.parent_item_id = order_item_parent.item_id',
- []
- )->where(
- 'source_table.state != ?',
- \Magento\Sales\Model\Order::STATE_CANCELED
- )->where(
- 'order_item.product_type NOT IN(?)',
- $this->ignoredProductTypes
- );
+ $this->clearByDateRange($from, $to);
+ foreach ($this->storeManager->getStores(true) as $store) {
+ $this->processStoreAggregate($store->getId(), $from, $to);
+ }
- if ($subSelect !== null) {
- $select->having($this->_makeConditionFromDateRangeSelect($subSelect, 'period'));
- }
-
- $select->useStraightJoin();
- // important!
- $insertQuery = $select->insertFromSelect($this->getMainTable(), array_keys($columns));
- $connection->query($insertQuery);
-
- $columns = [
- 'period' => 'period',
- 'store_id' => new \Zend_Db_Expr(\Magento\Store\Model\Store::DEFAULT_STORE_ID),
- 'product_id' => 'product_id',
- 'product_name' => new \Zend_Db_Expr('MIN(product_name)'),
- 'product_price' => new \Zend_Db_Expr('MIN(product_price)'),
- 'qty_ordered' => new \Zend_Db_Expr('SUM(qty_ordered)'),
- ];
-
- $select->reset();
- $select->from(
- $this->getMainTable(),
- $columns
- )->where(
- 'store_id <> ?',
- \Magento\Store\Model\Store::DEFAULT_STORE_ID
- );
+ $columns = [
+ 'period' => 'period',
+ 'store_id' => new \Zend_Db_Expr(Store::DEFAULT_STORE_ID),
+ 'product_id' => 'product_id',
+ 'product_name' => new \Zend_Db_Expr('MIN(product_name)'),
+ 'product_price' => new \Zend_Db_Expr('MIN(product_price)'),
+ 'qty_ordered' => new \Zend_Db_Expr('SUM(qty_ordered)'),
+ ];
- if ($subSelect !== null) {
- $select->where($this->_makeConditionFromDateRangeSelect($subSelect, 'period'));
- }
-
- $select->group(['period', 'product_id']);
- $insertQuery = $select->insertFromSelect($this->getMainTable(), array_keys($columns));
- $connection->query($insertQuery);
-
- // update rating
- $this->_updateRatingPos(self::AGGREGATION_DAILY);
- $this->_updateRatingPos(self::AGGREGATION_MONTHLY);
- $this->_updateRatingPos(self::AGGREGATION_YEARLY);
- $this->_setFlagData(\Magento\Reports\Model\Flag::REPORT_BESTSELLERS_FLAG_CODE);
- } catch (\Exception $e) {
- throw $e;
+ $select = $connection->select();
+ $select->reset();
+ $select->from(
+ $this->getMainTable(),
+ $columns
+ )->where(
+ 'store_id <> ?',
+ Store::DEFAULT_STORE_ID
+ );
+ $subSelect = $this->getRangeSubSelect($from, $to);
+ if ($subSelect !== null) {
+ $select->where($this->_makeConditionFromDateRangeSelect($subSelect, 'period'));
}
+ $select->group(['period', 'product_id']);
+ $insertQuery = $select->insertFromSelect($this->getMainTable(), array_keys($columns));
+ $connection->query($insertQuery);
+
+ $this->_updateRatingPos(self::AGGREGATION_DAILY);
+ $this->_updateRatingPos(self::AGGREGATION_MONTHLY);
+ $this->_updateRatingPos(self::AGGREGATION_YEARLY);
+ $this->_setFlagData(\Magento\Reports\Model\Flag::REPORT_BESTSELLERS_FLAG_CODE);
+
return $this;
}
+ /**
+ * Clear aggregate existing data by range
+ *
+ * @param string|int|\DateTime|array|null $from
+ * @param string|int|\DateTime|array|null $to
+ * @return void
+ * @throws LocalizedException
+ */
+ private function clearByDateRange($from = null, $to = null): void
+ {
+ $subSelect = $this->getRangeSubSelect($from, $to);
+ $this->_clearTableByDateRange($this->getMainTable(), $from, $to, $subSelect);
+ }
+
+ /**
+ * Get report range sub-select
+ *
+ * @param string|int|\DateTime|array|null $from
+ * @param string|int|\DateTime|array|null $to
+ * @return Select|null
+ */
+ private function getRangeSubSelect($from = null, $to = null): ?Select
+ {
+ if ($from !== null || $to !== null) {
+ $subSelect = $this->_getTableDateRangeSelect(
+ $this->getTable('sales_order'),
+ 'created_at',
+ 'updated_at',
+ $from,
+ $to
+ );
+ } else {
+ $subSelect = null;
+ }
+
+ return $subSelect;
+ }
+
+ /**
+ * Calculate report aggregate per store
+ *
+ * @param int|null $storeId
+ * @param string|int|\DateTime|array|null $from
+ * @param string|int|\DateTime|array|null $to
+ * @return void
+ * @throws LocalizedException
+ */
+ private function processStoreAggregate(?int $storeId, $from = null, $to = null): void
+ {
+ $connection = $this->getConnection();
+
+ // convert dates to current admin timezone
+ $periodExpr = $connection->getDatePartSql(
+ $this->getStoreTZOffsetQuery(
+ ['source_table' => $this->getTable('sales_order')],
+ 'source_table.created_at',
+ $from,
+ $to
+ )
+ );
+ $select = $connection->select();
+ $subSelect = $this->getRangeSubSelect($from, $to);
+
+ $select->group([$periodExpr, 'source_table.store_id', 'order_item.product_id']);
+
+ $columns = [
+ 'period' => $periodExpr,
+ 'store_id' => 'source_table.store_id',
+ 'product_id' => 'order_item.product_id',
+ 'product_name' => new \Zend_Db_Expr('MIN(order_item.name)'),
+ 'product_price' => new \Zend_Db_Expr(
+ 'MIN(IF(order_item_parent.base_price, order_item_parent.base_price, order_item.base_price))' .
+ '* MIN(source_table.base_to_global_rate)'
+ ),
+ 'qty_ordered' => new \Zend_Db_Expr('SUM(order_item.qty_ordered)'),
+ ];
+
+ $select->from(
+ ['source_table' => $this->getTable('sales_order')],
+ $columns
+ )->joinInner(
+ ['order_item' => $this->getTable('sales_order_item')],
+ 'order_item.order_id = source_table.entity_id',
+ []
+ )->joinLeft(
+ ['order_item_parent' => $this->getTable('sales_order_item')],
+ 'order_item.parent_item_id = order_item_parent.item_id',
+ []
+ )->where(
+ "source_table.entity_id IN (SELECT entity_id FROM " . $this->getTable('sales_order') .
+ " WHERE store_id = " . $storeId .
+ " AND state != '" . \Magento\Sales\Model\Order::STATE_CANCELED . "'" .
+ ($subSelect !== null ?
+ " AND " . $this->_makeConditionFromDateRangeSelect($subSelect, $periodExpr) :
+ '') . ")"
+ )->where(
+ 'order_item.product_type NOT IN(?)',
+ $this->ignoredProductTypes
+ );
+
+ $select->useStraightJoin();
+ // important!
+ $insertQuery = $select->insertFromSelect($this->getMainTable(), array_keys($columns));
+ $connection->query($insertQuery);
+ }
+
/**
* Update rating position
*
* @param string $aggregation
* @return $this
+ * @throws LocalizedException
*/
protected function _updateRatingPos($aggregation)
{
diff --git a/vendor/magento/module-sales/etc/db_schema.xml b/vendor/magento/module-sales/etc/db_schema.xml
index 491772e7e65..2f1cf0904ac 100644
--- a/vendor/magento/module-sales/etc/db_schema.xml
+++ b/vendor/magento/module-sales/etc/db_schema.xml
@@ -294,6 +294,11 @@