Stock.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\CatalogInventory\Model\ResourceModel;
  7. use Magento\CatalogInventory\Api\StockConfigurationInterface;
  8. use Magento\Store\Model\StoreManagerInterface;
  9. /**
  10. * Stock resource model
  11. */
  12. class Stock extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb implements QtyCounterInterface
  13. {
  14. /**
  15. * @var StockConfigurationInterface
  16. */
  17. protected $stockConfiguration;
  18. /**
  19. * Is initialized configuration flag
  20. *
  21. * @var bool
  22. */
  23. protected $_isConfig;
  24. /**
  25. * Manage Stock flag
  26. *
  27. * @var bool
  28. */
  29. protected $_isConfigManageStock;
  30. /**
  31. * Backorders
  32. *
  33. * @var bool
  34. */
  35. protected $_isConfigBackorders;
  36. /**
  37. * Minimum quantity allowed in shopping card
  38. *
  39. * @var int
  40. */
  41. protected $_configMinQty;
  42. /**
  43. * Product types that could have quantities
  44. *
  45. * @var array
  46. */
  47. protected $_configTypeIds;
  48. /**
  49. * Notify for quantity below _configNotifyStockQty value
  50. *
  51. * @var int
  52. */
  53. protected $_configNotifyStockQty;
  54. /**
  55. * Core store config
  56. *
  57. * @var \Magento\Framework\App\Config\ScopeConfigInterface
  58. */
  59. protected $_scopeConfig;
  60. /**
  61. * @var \Magento\Framework\Stdlib\DateTime\DateTime
  62. */
  63. protected $dateTime;
  64. /**
  65. * @var StoreManagerInterface
  66. * @deprecated 100.1.0
  67. */
  68. protected $storeManager;
  69. /**
  70. * @param \Magento\Framework\Model\ResourceModel\Db\Context $context
  71. * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
  72. * @param \Magento\Framework\Stdlib\DateTime\DateTime $dateTime
  73. * @param StockConfigurationInterface $stockConfiguration
  74. * @param StoreManagerInterface $storeManager
  75. * @param string $connectionName
  76. */
  77. public function __construct(
  78. \Magento\Framework\Model\ResourceModel\Db\Context $context,
  79. \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
  80. \Magento\Framework\Stdlib\DateTime\DateTime $dateTime,
  81. StockConfigurationInterface $stockConfiguration,
  82. StoreManagerInterface $storeManager,
  83. $connectionName = null
  84. ) {
  85. parent::__construct($context, $connectionName);
  86. $this->_scopeConfig = $scopeConfig;
  87. $this->dateTime = $dateTime;
  88. $this->stockConfiguration = $stockConfiguration;
  89. $this->storeManager = $storeManager;
  90. }
  91. /**
  92. * Define main table and initialize connection
  93. *
  94. * @return void
  95. */
  96. protected function _construct()
  97. {
  98. $this->_init('cataloginventory_stock', 'stock_id');
  99. }
  100. /**
  101. * Lock Stock Item records.
  102. *
  103. * @param int[] $productIds
  104. * @param int $websiteId
  105. * @return array
  106. */
  107. public function lockProductsStock(array $productIds, $websiteId)
  108. {
  109. if (empty($productIds)) {
  110. return [];
  111. }
  112. $itemTable = $this->getTable('cataloginventory_stock_item');
  113. $select = $this->getConnection()->select()->from(['si' => $itemTable])
  114. ->where('website_id = ?', $websiteId)
  115. ->where('product_id IN(?)', $productIds)
  116. ->forUpdate(true);
  117. $productTable = $this->getTable('catalog_product_entity');
  118. $selectProducts = $this->getConnection()->select()->from(['p' => $productTable], [])
  119. ->where('entity_id IN (?)', $productIds)
  120. ->columns(
  121. [
  122. 'product_id' => 'entity_id',
  123. 'type_id' => 'type_id',
  124. ]
  125. );
  126. $items = [];
  127. foreach ($this->getConnection()->query($select)->fetchAll() as $si) {
  128. $items[$si['product_id']] = $si;
  129. }
  130. foreach ($this->getConnection()->fetchAll($selectProducts) as $p) {
  131. $items[$p['product_id']]['type_id'] = $p['type_id'];
  132. }
  133. return $items;
  134. }
  135. /**
  136. * {@inheritdoc}
  137. */
  138. public function correctItemsQty(array $items, $websiteId, $operator)
  139. {
  140. if (empty($items)) {
  141. return;
  142. }
  143. $connection = $this->getConnection();
  144. $conditions = [];
  145. foreach ($items as $productId => $qty) {
  146. $case = $connection->quoteInto('?', $productId);
  147. $result = $connection->quoteInto("qty{$operator}?", $qty);
  148. $conditions[$case] = $result;
  149. }
  150. $value = $connection->getCaseSql('product_id', $conditions, 'qty');
  151. $where = ['product_id IN (?)' => array_keys($items), 'website_id = ?' => $websiteId];
  152. $connection->beginTransaction();
  153. $connection->update($this->getTable('cataloginventory_stock_item'), ['qty' => $value], $where);
  154. $connection->commit();
  155. }
  156. /**
  157. * Load some inventory configuration settings
  158. *
  159. * @return void
  160. */
  161. protected function _initConfig()
  162. {
  163. if (!$this->_isConfig) {
  164. $configMap = [
  165. '_isConfigManageStock' => \Magento\CatalogInventory\Model\Configuration::XML_PATH_MANAGE_STOCK,
  166. '_isConfigBackorders' => \Magento\CatalogInventory\Model\Configuration::XML_PATH_BACKORDERS,
  167. '_configMinQty' => \Magento\CatalogInventory\Model\Configuration::XML_PATH_MIN_QTY,
  168. '_configNotifyStockQty' => \Magento\CatalogInventory\Model\Configuration::XML_PATH_NOTIFY_STOCK_QTY,
  169. ];
  170. foreach ($configMap as $field => $const) {
  171. $this->{$field} = (int) $this->_scopeConfig->getValue(
  172. $const,
  173. \Magento\Store\Model\ScopeInterface::SCOPE_STORE
  174. );
  175. }
  176. $this->_isConfig = true;
  177. $this->_configTypeIds = array_keys($this->stockConfiguration->getIsQtyTypeIds(true));
  178. }
  179. }
  180. /**
  181. * Set items out of stock basing on their quantities and config settings
  182. *
  183. * @deprecated 100.2.5
  184. * @see \Magento\CatalogInventory\Model\ResourceModel\Stock\Item::updateSetOutOfStock
  185. * @param string|int $website
  186. * @SuppressWarnings(PHPMD.UnusedFormalParameter)
  187. * @return void
  188. */
  189. public function updateSetOutOfStock($website = null)
  190. {
  191. $websiteId = $this->stockConfiguration->getDefaultScopeId();
  192. $this->_initConfig();
  193. $connection = $this->getConnection();
  194. $values = ['is_in_stock' => 0, 'stock_status_changed_auto' => 1];
  195. $select = $connection->select()->from($this->getTable('catalog_product_entity'), 'entity_id')
  196. ->where('type_id IN(?)', $this->_configTypeIds);
  197. $where = sprintf(
  198. 'website_id = %1$d' .
  199. ' AND is_in_stock = 1' .
  200. ' AND ((use_config_manage_stock = 1 AND 1 = %2$d) OR (use_config_manage_stock = 0 AND manage_stock = 1))' .
  201. ' AND ((use_config_backorders = 1 AND %3$d = %4$d) OR (use_config_backorders = 0 AND backorders = %3$d))' .
  202. ' AND ((use_config_min_qty = 1 AND qty <= %5$d) OR (use_config_min_qty = 0 AND qty <= min_qty))' .
  203. ' AND product_id IN (%6$s)',
  204. $websiteId,
  205. $this->_isConfigManageStock,
  206. \Magento\CatalogInventory\Model\Stock::BACKORDERS_NO,
  207. $this->_isConfigBackorders,
  208. $this->_configMinQty,
  209. $select->assemble()
  210. );
  211. $connection->update($this->getTable('cataloginventory_stock_item'), $values, $where);
  212. }
  213. /**
  214. * Set items in stock basing on their quantities and config settings
  215. *
  216. * @deprecated 100.2.5
  217. * @see \Magento\CatalogInventory\Model\ResourceModel\Stock\Item::updateSetInStock
  218. * @param int|string $website
  219. * @SuppressWarnings(PHPMD.UnusedFormalParameter)
  220. * @return void
  221. */
  222. public function updateSetInStock($website)
  223. {
  224. $websiteId = $this->stockConfiguration->getDefaultScopeId();
  225. $this->_initConfig();
  226. $connection = $this->getConnection();
  227. $values = ['is_in_stock' => 1];
  228. $select = $connection->select()->from($this->getTable('catalog_product_entity'), 'entity_id')
  229. ->where('type_id IN(?)', $this->_configTypeIds);
  230. $where = sprintf(
  231. 'website_id = %1$d' .
  232. ' AND is_in_stock = 0' .
  233. ' AND stock_status_changed_auto = 1' .
  234. ' AND ((use_config_manage_stock = 1 AND 1 = %2$d) OR (use_config_manage_stock = 0 AND manage_stock = 1))' .
  235. ' AND ((use_config_min_qty = 1 AND qty > %3$d) OR (use_config_min_qty = 0 AND qty > min_qty))' .
  236. ' AND product_id IN (%4$s)',
  237. $websiteId,
  238. $this->_isConfigManageStock,
  239. $this->_configMinQty,
  240. $select->assemble()
  241. );
  242. $connection->update($this->getTable('cataloginventory_stock_item'), $values, $where);
  243. }
  244. /**
  245. * Update items low stock date basing on their quantities and config settings
  246. *
  247. * @deprecated 100.2.5
  248. * @see \Magento\CatalogInventory\Model\ResourceModel\Stock\Item::updateLowStockDate
  249. * @param int|string $website
  250. * @SuppressWarnings(PHPMD.UnusedFormalParameter)
  251. * @return void
  252. */
  253. public function updateLowStockDate($website)
  254. {
  255. $websiteId = $this->stockConfiguration->getDefaultScopeId();
  256. $this->_initConfig();
  257. $connection = $this->getConnection();
  258. $condition = $connection->quoteInto(
  259. '(use_config_notify_stock_qty = 1 AND qty < ?)',
  260. $this->_configNotifyStockQty
  261. ) . ' OR (use_config_notify_stock_qty = 0 AND qty < notify_stock_qty)';
  262. $currentDbTime = $connection->quoteInto('?', $this->dateTime->gmtDate());
  263. $conditionalDate = $connection->getCheckSql($condition, $currentDbTime, 'NULL');
  264. $value = ['low_stock_date' => new \Zend_Db_Expr($conditionalDate)];
  265. $select = $connection->select()->from($this->getTable('catalog_product_entity'), 'entity_id')
  266. ->where('type_id IN(?)', $this->_configTypeIds);
  267. $where = sprintf(
  268. 'website_id = %1$d' .
  269. ' AND ((use_config_manage_stock = 1 AND 1 = %2$d) OR (use_config_manage_stock = 0 AND manage_stock = 1))' .
  270. ' AND product_id IN (%3$s)',
  271. $websiteId,
  272. $this->_isConfigManageStock,
  273. $select->assemble()
  274. );
  275. $connection->update($this->getTable('cataloginventory_stock_item'), $value, $where);
  276. }
  277. /**
  278. * Add low stock filter to product collection
  279. *
  280. * @param \Magento\Catalog\Model\ResourceModel\Product\Collection $collection
  281. * @param array $fields
  282. * @return $this
  283. */
  284. public function addLowStockFilter(\Magento\Catalog\Model\ResourceModel\Product\Collection $collection, $fields)
  285. {
  286. $this->_initConfig();
  287. $connection = $collection->getSelect()->getConnection();
  288. $qtyIf = $connection->getCheckSql(
  289. 'invtr.use_config_notify_stock_qty > 0',
  290. $this->_configNotifyStockQty,
  291. 'invtr.notify_stock_qty'
  292. );
  293. $conditions = [
  294. [
  295. $connection->prepareSqlCondition('invtr.use_config_manage_stock', 1),
  296. $connection->prepareSqlCondition($this->_isConfigManageStock, 1),
  297. $connection->prepareSqlCondition('invtr.qty', ['lt' => $qtyIf]),
  298. ],
  299. [
  300. $connection->prepareSqlCondition('invtr.use_config_manage_stock', 0),
  301. $connection->prepareSqlCondition('invtr.manage_stock', 1)
  302. ],
  303. ];
  304. $where = [];
  305. foreach ($conditions as $k => $part) {
  306. $where[$k] = join(' ' . \Magento\Framework\DB\Select::SQL_AND . ' ', $part);
  307. }
  308. $where = $connection->prepareSqlCondition(
  309. 'invtr.low_stock_date',
  310. ['notnull' => true]
  311. ) . ' ' . \Magento\Framework\DB\Select::SQL_AND . ' ((' . join(
  312. ') ' . \Magento\Framework\DB\Select::SQL_OR . ' (',
  313. $where
  314. ) . '))';
  315. $collection->joinTable(
  316. ['invtr' => 'cataloginventory_stock_item'],
  317. 'product_id = entity_id',
  318. $fields,
  319. $where
  320. );
  321. return $this;
  322. }
  323. }