Bestsellers.php 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Sales\Model\ResourceModel\Report;
  7. /**
  8. * Bestsellers report resource model
  9. * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  10. */
  11. class Bestsellers extends AbstractReport
  12. {
  13. const AGGREGATION_DAILY = 'daily';
  14. const AGGREGATION_MONTHLY = 'monthly';
  15. const AGGREGATION_YEARLY = 'yearly';
  16. /**
  17. * @var \Magento\Catalog\Model\ResourceModel\Product
  18. */
  19. protected $_productResource;
  20. /**
  21. * @var \Magento\Sales\Model\ResourceModel\Helper
  22. */
  23. protected $_salesResourceHelper;
  24. /**
  25. * Ignored product types list
  26. *
  27. * @var array
  28. */
  29. protected $ignoredProductTypes = [
  30. \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE => \Magento\Catalog\Model\Product\Type::TYPE_BUNDLE,
  31. ];
  32. /**
  33. * @param \Magento\Framework\Model\ResourceModel\Db\Context $context
  34. * @param \Psr\Log\LoggerInterface $logger
  35. * @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate
  36. * @param \Magento\Reports\Model\FlagFactory $reportsFlagFactory
  37. * @param \Magento\Framework\Stdlib\DateTime\Timezone\Validator $timezoneValidator
  38. * @param \Magento\Framework\Stdlib\DateTime\DateTime $dateTime
  39. * @param \Magento\Catalog\Model\ResourceModel\Product $productResource
  40. * @param \Magento\Sales\Model\ResourceModel\Helper $salesResourceHelper
  41. * @param array $ignoredProductTypes
  42. * @param string $connectionName
  43. * @SuppressWarnings(PHPMD.ExcessiveParameterList)
  44. */
  45. public function __construct(
  46. \Magento\Framework\Model\ResourceModel\Db\Context $context,
  47. \Psr\Log\LoggerInterface $logger,
  48. \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate,
  49. \Magento\Reports\Model\FlagFactory $reportsFlagFactory,
  50. \Magento\Framework\Stdlib\DateTime\Timezone\Validator $timezoneValidator,
  51. \Magento\Framework\Stdlib\DateTime\DateTime $dateTime,
  52. \Magento\Catalog\Model\ResourceModel\Product $productResource,
  53. \Magento\Sales\Model\ResourceModel\Helper $salesResourceHelper,
  54. $connectionName = null,
  55. array $ignoredProductTypes = []
  56. ) {
  57. parent::__construct(
  58. $context,
  59. $logger,
  60. $localeDate,
  61. $reportsFlagFactory,
  62. $timezoneValidator,
  63. $dateTime,
  64. $connectionName
  65. );
  66. $this->_productResource = $productResource;
  67. $this->_salesResourceHelper = $salesResourceHelper;
  68. $this->ignoredProductTypes = array_merge($this->ignoredProductTypes, $ignoredProductTypes);
  69. }
  70. /**
  71. * Model initialization
  72. *
  73. * @return void
  74. */
  75. protected function _construct()
  76. {
  77. $this->_init('sales_bestsellers_aggregated_' . self::AGGREGATION_DAILY, 'id');
  78. }
  79. /**
  80. * Aggregate Orders data by order created at
  81. *
  82. * @param string|int|\DateTime|array|null $from
  83. * @param string|int|\DateTime|array|null $to
  84. * @return $this
  85. * @throws \Exception
  86. * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
  87. */
  88. public function aggregate($from = null, $to = null)
  89. {
  90. $connection = $this->getConnection();
  91. //$this->getConnection()->beginTransaction();
  92. try {
  93. if ($from !== null || $to !== null) {
  94. $subSelect = $this->_getTableDateRangeSelect(
  95. $this->getTable('sales_order'),
  96. 'created_at',
  97. 'updated_at',
  98. $from,
  99. $to
  100. );
  101. } else {
  102. $subSelect = null;
  103. }
  104. $this->_clearTableByDateRange($this->getMainTable(), $from, $to, $subSelect);
  105. // convert dates to current admin timezone
  106. $periodExpr = $connection->getDatePartSql(
  107. $this->getStoreTZOffsetQuery(
  108. ['source_table' => $this->getTable('sales_order')],
  109. 'source_table.created_at',
  110. $from,
  111. $to
  112. )
  113. );
  114. $select = $connection->select();
  115. $select->group([$periodExpr, 'source_table.store_id', 'order_item.product_id']);
  116. $columns = [
  117. 'period' => $periodExpr,
  118. 'store_id' => 'source_table.store_id',
  119. 'product_id' => 'order_item.product_id',
  120. 'product_name' => new \Zend_Db_Expr('MIN(order_item.name)'),
  121. 'product_price' => new \Zend_Db_Expr(
  122. 'MIN(IF(order_item_parent.base_price, order_item_parent.base_price, order_item.base_price))' .
  123. '* MIN(source_table.base_to_global_rate)'
  124. ),
  125. 'qty_ordered' => new \Zend_Db_Expr('SUM(order_item.qty_ordered)'),
  126. ];
  127. $select->from(
  128. ['source_table' => $this->getTable('sales_order')],
  129. $columns
  130. )->joinInner(
  131. ['order_item' => $this->getTable('sales_order_item')],
  132. 'order_item.order_id = source_table.entity_id',
  133. []
  134. )->joinLeft(
  135. ['order_item_parent' => $this->getTable('sales_order_item')],
  136. 'order_item.parent_item_id = order_item_parent.item_id',
  137. []
  138. )->where(
  139. 'source_table.state != ?',
  140. \Magento\Sales\Model\Order::STATE_CANCELED
  141. )->where(
  142. 'order_item.product_type NOT IN(?)',
  143. $this->ignoredProductTypes
  144. );
  145. if ($subSelect !== null) {
  146. $select->having($this->_makeConditionFromDateRangeSelect($subSelect, 'period'));
  147. }
  148. $select->useStraightJoin();
  149. // important!
  150. $insertQuery = $select->insertFromSelect($this->getMainTable(), array_keys($columns));
  151. $connection->query($insertQuery);
  152. $columns = [
  153. 'period' => 'period',
  154. 'store_id' => new \Zend_Db_Expr(\Magento\Store\Model\Store::DEFAULT_STORE_ID),
  155. 'product_id' => 'product_id',
  156. 'product_name' => new \Zend_Db_Expr('MIN(product_name)'),
  157. 'product_price' => new \Zend_Db_Expr('MIN(product_price)'),
  158. 'qty_ordered' => new \Zend_Db_Expr('SUM(qty_ordered)'),
  159. ];
  160. $select->reset();
  161. $select->from(
  162. $this->getMainTable(),
  163. $columns
  164. )->where(
  165. 'store_id <> ?',
  166. \Magento\Store\Model\Store::DEFAULT_STORE_ID
  167. );
  168. if ($subSelect !== null) {
  169. $select->where($this->_makeConditionFromDateRangeSelect($subSelect, 'period'));
  170. }
  171. $select->group(['period', 'product_id']);
  172. $insertQuery = $select->insertFromSelect($this->getMainTable(), array_keys($columns));
  173. $connection->query($insertQuery);
  174. // update rating
  175. $this->_updateRatingPos(self::AGGREGATION_DAILY);
  176. $this->_updateRatingPos(self::AGGREGATION_MONTHLY);
  177. $this->_updateRatingPos(self::AGGREGATION_YEARLY);
  178. $this->_setFlagData(\Magento\Reports\Model\Flag::REPORT_BESTSELLERS_FLAG_CODE);
  179. } catch (\Exception $e) {
  180. throw $e;
  181. }
  182. return $this;
  183. }
  184. /**
  185. * Update rating position
  186. *
  187. * @param string $aggregation
  188. * @return $this
  189. */
  190. protected function _updateRatingPos($aggregation)
  191. {
  192. $aggregationTable = $this->getTable('sales_bestsellers_aggregated_' . $aggregation);
  193. $aggregationAliases = [
  194. 'daily' => self::AGGREGATION_DAILY,
  195. 'monthly' => self::AGGREGATION_MONTHLY,
  196. 'yearly' => self::AGGREGATION_YEARLY,
  197. ];
  198. $this->_salesResourceHelper->getBestsellersReportUpdateRatingPos(
  199. $aggregation,
  200. $aggregationAliases,
  201. $this->getMainTable(),
  202. $aggregationTable
  203. );
  204. return $this;
  205. }
  206. }