LowQuantityCollection.php 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. declare(strict_types=1);
  7. namespace Magento\InventoryLowQuantityNotification\Model\ResourceModel;
  8. use Magento\Catalog\Api\Data\ProductInterface;
  9. use Magento\CatalogInventory\Api\StockConfigurationInterface;
  10. use Magento\Eav\Api\AttributeRepositoryInterface;
  11. use Magento\Framework\Data\Collection\Db\FetchStrategyInterface;
  12. use Magento\Framework\Data\Collection\EntityFactoryInterface;
  13. use Magento\Framework\DB\Adapter\AdapterInterface;
  14. use Magento\Framework\EntityManager\MetadataPool;
  15. use Magento\Framework\Event\ManagerInterface;
  16. use Magento\Framework\Model\ResourceModel\Db\AbstractDb;
  17. use Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection;
  18. use Magento\Inventory\Model\ResourceModel\Source;
  19. use Magento\Inventory\Model\ResourceModel\SourceItem as SourceItemResourceModel;
  20. use Magento\Inventory\Model\SourceItem as SourceItemModel;
  21. use Magento\InventoryApi\Api\Data\SourceInterface;
  22. use Magento\InventoryApi\Api\Data\SourceItemInterface;
  23. use Magento\InventoryConfigurationApi\Model\GetAllowedProductTypesForSourceItemManagementInterface;
  24. use Magento\InventoryLowQuantityNotificationApi\Api\Data\SourceItemConfigurationInterface;
  25. use Magento\Store\Model\Store;
  26. use Psr\Log\LoggerInterface;
  27. /**
  28. * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  29. */
  30. class LowQuantityCollection extends AbstractCollection
  31. {
  32. /**
  33. * @var StockConfigurationInterface
  34. */
  35. private $stockConfiguration;
  36. /**
  37. * @var GetAllowedProductTypesForSourceItemManagementInterface
  38. */
  39. private $getAllowedProductTypesForSourceItemManagement;
  40. /**
  41. * @var AttributeRepositoryInterface
  42. */
  43. private $attributeRepository;
  44. /**
  45. * @var MetadataPool
  46. */
  47. private $metadataPool;
  48. /**
  49. * @var int
  50. */
  51. private $filterStoreId;
  52. /**
  53. * @param EntityFactoryInterface $entityFactory
  54. * @param LoggerInterface $logger
  55. * @param FetchStrategyInterface $fetchStrategy
  56. * @param ManagerInterface $eventManager
  57. * @param AttributeRepositoryInterface $attributeRepository
  58. * @param StockConfigurationInterface $stockConfiguration
  59. * @param GetAllowedProductTypesForSourceItemManagementInterface $getAllowedProductTypesForSourceItemManagement
  60. * @param MetadataPool $metadataPool
  61. * @param AdapterInterface|null $connection
  62. * @param AbstractDb|null $resource
  63. *
  64. * @SuppressWarnings(PHPMD.ExcessiveParameterList)
  65. */
  66. public function __construct(
  67. EntityFactoryInterface $entityFactory,
  68. LoggerInterface $logger,
  69. FetchStrategyInterface $fetchStrategy,
  70. ManagerInterface $eventManager,
  71. AttributeRepositoryInterface $attributeRepository,
  72. StockConfigurationInterface $stockConfiguration,
  73. GetAllowedProductTypesForSourceItemManagementInterface $getAllowedProductTypesForSourceItemManagement,
  74. MetadataPool $metadataPool,
  75. AdapterInterface $connection = null,
  76. AbstractDb $resource = null
  77. ) {
  78. parent::__construct(
  79. $entityFactory,
  80. $logger,
  81. $fetchStrategy,
  82. $eventManager,
  83. $connection,
  84. $resource
  85. );
  86. $this->attributeRepository = $attributeRepository;
  87. $this->stockConfiguration = $stockConfiguration;
  88. $this->getAllowedProductTypesForSourceItemManagement = $getAllowedProductTypesForSourceItemManagement;
  89. $this->metadataPool = $metadataPool;
  90. }
  91. /**
  92. * @inheritdoc
  93. */
  94. protected function _construct()
  95. {
  96. $this->_init(SourceItemModel::class, SourceItemResourceModel::class);
  97. $this->addFilterToMap('source_code', 'main_table.source_code');
  98. $this->addFilterToMap('sku', 'main_table.sku');
  99. $this->addFilterToMap('product_name', 'product_entity_varchar.value');
  100. }
  101. /**
  102. * @param int $storeId
  103. * @return void
  104. */
  105. public function addStoreFilter(int $storeId)
  106. {
  107. $this->filterStoreId = $storeId;
  108. }
  109. /**
  110. * @inheritdoc
  111. */
  112. protected function _renderFilters()
  113. {
  114. if (false === $this->_isFiltersRendered) {
  115. $this->joinInventoryConfiguration();
  116. $this->joinCatalogProduct();
  117. $this->addProductTypeFilter();
  118. $this->addNotifyStockQtyFilter();
  119. $this->addEnabledSourceFilter();
  120. $this->addSourceItemInStockFilter();
  121. }
  122. return parent::_renderFilters();
  123. }
  124. /**
  125. * @inheritdoc
  126. */
  127. protected function _renderOrders()
  128. {
  129. if (false === $this->_isOrdersRendered) {
  130. $this->setOrder(SourceItemInterface::QUANTITY, self::SORT_ORDER_ASC);
  131. }
  132. return parent::_renderOrders();
  133. }
  134. /**
  135. * joinCatalogProduct depends on dynamic condition 'filterStoreId'
  136. *
  137. * @return void
  138. */
  139. private function joinCatalogProduct()
  140. {
  141. $productEntityTable = $this->getTable('catalog_product_entity');
  142. $productEavVarcharTable = $this->getTable('catalog_product_entity_varchar');
  143. $nameAttribute = $this->attributeRepository->get('catalog_product', 'name');
  144. $metadata = $this->metadataPool->getMetadata(ProductInterface::class);
  145. $linkField = $metadata->getLinkField();
  146. $this->getSelect()->joinInner(
  147. ['product_entity' => $productEntityTable],
  148. 'main_table.' . SourceItemInterface::SKU . ' = product_entity.' . ProductInterface::SKU,
  149. []
  150. );
  151. $this->getSelect()->joinInner(
  152. ['product_entity_varchar' => $productEavVarcharTable],
  153. 'product_entity_varchar.' . $linkField . ' = product_entity.' . $linkField . ' ' .
  154. 'AND product_entity_varchar.store_id = ' . Store::DEFAULT_STORE_ID. ' ' .
  155. 'AND product_entity_varchar.attribute_id = ' . (int)$nameAttribute->getAttributeId(),
  156. []
  157. );
  158. if (null !== $this->filterStoreId) {
  159. $this->getSelect()->joinLeft(
  160. ['product_entity_varchar_store' => $productEavVarcharTable],
  161. 'product_entity_varchar_store.' . $linkField . ' = product_entity.' . $linkField . ' ' .
  162. 'AND product_entity_varchar_store.store_id = ' . (int)$this->filterStoreId . ' ' .
  163. 'AND product_entity_varchar_store.attribute_id = ' . (int)$nameAttribute->getAttributeId(),
  164. [
  165. 'product_name' => $this->getConnection()->getIfNullSql(
  166. 'product_entity_varchar_store.value',
  167. 'product_entity_varchar.value'
  168. )
  169. ]
  170. );
  171. } else {
  172. $this->getSelect()->columns(['product_name' => 'product_entity_varchar.value']);
  173. }
  174. }
  175. /**
  176. * @return void
  177. */
  178. private function joinInventoryConfiguration()
  179. {
  180. $sourceItemConfigurationTable = $this->getTable('inventory_low_stock_notification_configuration');
  181. $this->getSelect()->joinInner(
  182. ['notification_configuration' => $sourceItemConfigurationTable],
  183. sprintf(
  184. 'main_table.%s = notification_configuration.%s AND main_table.%s = notification_configuration.%s',
  185. SourceItemInterface::SKU,
  186. SourceItemConfigurationInterface::SKU,
  187. SourceItemInterface::SOURCE_CODE,
  188. SourceItemConfigurationInterface::SOURCE_CODE
  189. ),
  190. []
  191. );
  192. }
  193. /**
  194. * @return void
  195. */
  196. private function addProductTypeFilter()
  197. {
  198. $this->addFieldToFilter(
  199. 'product_entity.type_id',
  200. $this->getAllowedProductTypesForSourceItemManagement->execute()
  201. );
  202. }
  203. /**
  204. * @return void
  205. */
  206. private function addNotifyStockQtyFilter()
  207. {
  208. $notifyStockExpression = $this->getConnection()->getIfNullSql(
  209. 'notification_configuration.' . SourceItemConfigurationInterface::INVENTORY_NOTIFY_QTY,
  210. (float)$this->stockConfiguration->getNotifyStockQty()
  211. );
  212. $this->getSelect()->where(
  213. SourceItemInterface::QUANTITY . ' < ?',
  214. $notifyStockExpression
  215. );
  216. }
  217. /**
  218. * @return void
  219. */
  220. private function addEnabledSourceFilter()
  221. {
  222. $this->getSelect()->joinInner(
  223. ['inventory_source' => $this->getTable(Source::TABLE_NAME_SOURCE)],
  224. sprintf(
  225. 'inventory_source.%s = 1 AND inventory_source.%s = main_table.%s',
  226. SourceInterface::ENABLED,
  227. SourceInterface::SOURCE_CODE,
  228. SourceItemInterface::SOURCE_CODE
  229. ),
  230. []
  231. );
  232. }
  233. /**
  234. * @return void
  235. */
  236. private function addSourceItemInStockFilter()
  237. {
  238. $this->addFieldToFilter('main_table.status', SourceItemInterface::STATUS_IN_STOCK);
  239. }
  240. }