/** * Initialize item option * * @param \Magento\Quote\Model\Quote\Item\Option $option * @param \Magento\Quote\Model\Quote\Item $quoteItem * @param int $qty * * @return \Magento\Framework\Object * @throws \Magento\Framework\Exception\LocalizedException */ public function initialize(\Magento\Quote\Model\Quote\Item\Option $option, \Magento\Quote\Model\Quote\Item $quoteItem, $qty) { $optionValue = $option->getValue(); $optionQty = $qty * $optionValue; $increaseOptionQty = ($quoteItem->getQtyToAdd() ? $quoteItem->getQtyToAdd() : $qty) * $optionValue; $qtyForCheck = $this->quoteItemQtyList->getQty($option->getProduct()->getId(), $quoteItem->getId(), $quoteItem->getQuoteId(), $increaseOptionQty); $stockItem = $this->getStockItem($option, $quoteItem); $result = $this->stockState->checkQuoteItemQty($option->getProduct()->getId(), $optionQty, $qtyForCheck, $optionValue, $option->getProduct()->getStore()->getWebsiteId()); if ($result->getItemIsQtyDecimal() !== null) { $option->setIsQtyDecimal($result->getItemIsQtyDecimal()); } if ($result->getHasQtyOptionUpdate()) { $option->setHasQtyOptionUpdate(true); $quoteItem->updateQtyOption($option, $result->getOrigQty()); $option->setValue($result->getOrigQty()); /** * if option's qty was updates we also need to update quote item qty */ $quoteItem->setData('qty', intval($qty)); } if ($result->getMessage() !== null) { $option->setMessage($result->getMessage()); $quoteItem->setMessage($result->getMessage()); } if ($result->getItemBackorders() !== null) { $option->setBackorders($result->getItemBackorders()); } $stockItem->unsIsChildItem(); return $result; }
/** * Initialize stock item * * @param \Magento\CatalogInventory\Api\Data\StockItemInterface $stockItem * @param \Magento\Quote\Model\Quote\Item $quoteItem * @param int $qty * * @return \Magento\Framework\DataObject * @throws \Magento\Framework\Exception\LocalizedException * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ public function initialize(\Magento\CatalogInventory\Api\Data\StockItemInterface $stockItem, \Magento\Quote\Model\Quote\Item $quoteItem, $qty) { /** * When we work with subitem */ if ($quoteItem->getParentItem()) { $rowQty = $quoteItem->getParentItem()->getQty() * $qty; /** * we are using 0 because original qty was processed */ $qtyForCheck = $this->quoteItemQtyList->getQty($quoteItem->getProduct()->getId(), $quoteItem->getId(), $quoteItem->getQuoteId(), 0); } else { $increaseQty = $quoteItem->getQtyToAdd() ? $quoteItem->getQtyToAdd() : $qty; $rowQty = $qty; $qtyForCheck = $this->quoteItemQtyList->getQty($quoteItem->getProduct()->getId(), $quoteItem->getId(), $quoteItem->getQuoteId(), $increaseQty); } $productTypeCustomOption = $quoteItem->getProduct()->getCustomOption('product_type'); if ($productTypeCustomOption !== null) { // Check if product related to current item is a part of product that represents product set if ($this->typeConfig->isProductSet($productTypeCustomOption->getValue())) { $stockItem->setIsChildItem(true); } } $stockItem->setProductName($quoteItem->getProduct()->getName()); $result = $this->stockState->checkQuoteItemQty($quoteItem->getProduct()->getId(), $rowQty, $qtyForCheck, $qty, $quoteItem->getProduct()->getStore()->getWebsiteId()); if ($stockItem->hasIsChildItem()) { $stockItem->unsIsChildItem(); } if ($result->getItemIsQtyDecimal() !== null) { $quoteItem->setIsQtyDecimal($result->getItemIsQtyDecimal()); if ($quoteItem->getParentItem()) { $quoteItem->getParentItem()->setIsQtyDecimal($result->getItemIsQtyDecimal()); } } /** * Just base (parent) item qty can be changed * qty of child products are declared just during add process * exception for updating also managed by product type */ if ($result->getHasQtyOptionUpdate() && (!$quoteItem->getParentItem() || $quoteItem->getParentItem()->getProduct()->getTypeInstance()->getForceChildItemQtyChanges($quoteItem->getParentItem()->getProduct()))) { $quoteItem->setData('qty', $result->getOrigQty()); } if ($result->getItemUseOldQty() !== null) { $quoteItem->setUseOldQty($result->getItemUseOldQty()); } if ($result->getMessage() !== null) { $quoteItem->setMessage($result->getMessage()); } if ($result->getItemBackorders() !== null) { $quoteItem->setBackorders($result->getItemBackorders()); } return $result; }
/** * Get items * * @return Item[] */ public function getItems() { $items = $this->getParentBlock()->getItems(); $oldSuperMode = $this->getQuote()->getIsSuperMode(); $this->getQuote()->setIsSuperMode(false); foreach ($items as $item) { // To dispatch inventory event sales_quote_item_qty_set_after, set item qty $item->setQty($item->getQty()); if (!$item->getMessage()) { //Getting product ids for stock item last quantity validation before grid display $stockItemToCheck = []; $childItems = $item->getChildren(); if (count($childItems)) { foreach ($childItems as $childItem) { $stockItemToCheck[] = $childItem->getProduct()->getId(); } } else { $stockItemToCheck[] = $item->getProduct()->getId(); } foreach ($stockItemToCheck as $productId) { $check = $this->stockState->checkQuoteItemQty($productId, $item->getQty(), $item->getQty(), $item->getQty(), $this->getQuote()->getStore()->getWebsiteId()); $item->setMessage($check->getMessage()); $item->setHasError($check->getHasError()); } } if ($item->getProduct()->getStatus() == ProductStatus::STATUS_DISABLED) { $item->setMessage(__('This product is disabled.')); $item->setHasError(true); } } $this->getQuote()->setIsSuperMode($oldSuperMode); return $items; }
public function testInitializeWhenResultNotDecimalGetBackordersMessageHasOptionQtyUpdate() { $optionValue = 5; $qtyForCheck = 50; $qty = 10; $this->optionMock->expects($this->once())->method('getValue')->will($this->returnValue($optionValue)); $this->quoteItemMock->expects($this->once())->method('getQtyToAdd')->will($this->returnValue(false)); $this->optionMock->expects($this->any())->method('getProduct')->will($this->returnValue($this->productMock)); $this->stockItemMock->expects($this->once())->method('setIsChildItem')->with(true); $this->stockItemMock->expects($this->once())->method('setSuppressCheckQtyIncrements')->with(true); $this->stockItemMock->expects($this->once())->method('getItemId')->will($this->returnValue(true)); $this->stockRegistry->expects($this->once())->method('getStockItem')->will($this->returnValue($this->stockItemMock)); $this->productMock->expects($this->any())->method('getId')->will($this->returnValue($this->productId)); $this->quoteItemMock->expects($this->any())->method('getId')->will($this->returnValue('quote_item_id')); $this->quoteItemMock->expects($this->once())->method('getQuoteId')->will($this->returnValue('quote_id')); $this->qtyItemListMock->expects($this->once())->method('getQty')->with($this->productId, 'quote_item_id', 'quote_id', $qty * $optionValue)->will($this->returnValue($qtyForCheck)); $this->stockState->expects($this->once())->method('checkQuoteItemQty')->with($this->productId, $qty * $optionValue, $qtyForCheck, $optionValue, $this->websiteId)->will($this->returnValue($this->resultMock)); $this->resultMock->expects($this->once())->method('getItemIsQtyDecimal')->will($this->returnValue(null)); $this->optionMock->expects($this->never())->method('setIsQtyDecimal'); $this->resultMock->expects($this->once())->method('getHasQtyOptionUpdate')->will($this->returnValue(null)); $this->optionMock->expects($this->never())->method('setHasQtyOptionUpdate'); $this->resultMock->expects($this->once())->method('getMessage')->will($this->returnValue(null)); $this->resultMock->expects($this->once())->method('getItemBackorders')->will($this->returnValue(null)); $this->optionMock->expects($this->never())->method('setBackorders'); $this->stockItemMock->expects($this->once())->method('unsIsChildItem'); $this->validator->initialize($this->optionMock, $this->quoteItemMock, $qty); }
public function testIsSalableNoManageStock() { $option1 = $this->getRequiredOptionMock(10, 10); $option2 = $this->getRequiredOptionMock(20, 10); $stockItem = $this->getStockItem(true); $this->stockRegistry->method('getStockItem')->willReturn($stockItem); $this->stockState->expects($this->at(0))->method('getStockQty')->with(10)->willReturn(10); $this->stockState->expects($this->at(1))->method('getStockQty')->with(20)->willReturn(10); $optionCollectionMock = $this->getOptionCollectionMock([$option1, $option2]); $selectionCollectionMock = $this->getSelectionCollectionMock([$option1, $option2]); $product = new \Magento\Framework\Object(['is_salable' => true, '_cache_instance_options_collection' => $optionCollectionMock, 'status' => \Magento\Catalog\Model\Product\Attribute\Source\Status::STATUS_ENABLED, '_cache_instance_selections_collection10_20' => $selectionCollectionMock]); $this->assertTrue($this->model->isSalable($product)); }
public function te1stGetStockQtyLeft() { $productId = 1; $minQty = 0; $websiteId = 1; $stockQty = 2; $storeMock = $this->getMockBuilder('Magento\\Store\\Model\\Store')->disableOriginalConstructor()->getMock(); $storeMock->expects($this->once())->method('getWebsiteId')->willReturn($websiteId); $product = $this->getMock('Magento\\Catalog\\Model\\Product', [], [], '', false); $product->expects($this->any())->method('getId')->willReturn($productId); $product->expects($this->once())->method('getStore')->willReturn($storeMock); $this->registryMock->expects($this->once())->method('registry')->with('current_product')->will($this->returnValue($product)); $stockItemMock = $this->getMockBuilder('Magento\\CatalogInventory\\Api\\Data\\StockItemInterface')->disableOriginalConstructor()->getMock(); $stockItemMock->expects($this->once())->method('getMinQty')->willReturn($minQty); $this->stockRegistryMock->expects($this->once())->method('getStockItem')->with($productId)->willReturn($stockItemMock); $this->stockState->expects($this->once())->method('getStockQty')->with($productId, $storeMock)->willReturn($stockQty); $this->assertEquals($stockQty, $this->block->getStockQtyLeft()); }
/** * Returns suggested quantities for items. * Can be used to automatically fix user entered quantities before updating cart * so that cart contains valid qty values * * The $data is an array of ($quoteItemId => (item info array with 'qty' key), ...) * * @param array $data * @return array */ public function suggestItemsQty($data) { foreach ($data as $itemId => $itemInfo) { if (!isset($itemInfo['qty'])) { continue; } $qty = (double) $itemInfo['qty']; if ($qty <= 0) { continue; } $quoteItem = $this->getQuote()->getItemById($itemId); if (!$quoteItem) { continue; } $product = $quoteItem->getProduct(); if (!$product) { continue; } $data[$itemId]['before_suggest_qty'] = $qty; $data[$itemId]['qty'] = $this->stockState->suggestQty($product->getId(), $qty, $product->getStore()->getWebsiteId()); } return $data; }
/** * Retrieve product stock qty * * @param Product $product * @return float */ public function getProductStockQty($product) { return $this->stockState->getStockQty($product->getId(), $product->getStore()->getWebsiteId()); }
/** * Checking if we can sale this bundle * * @param \Magento\Catalog\Model\Product $product * @return bool * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ public function isSalable($product) { if (!parent::isSalable($product)) { return false; } if ($product->hasData('all_items_salable')) { return $product->getData('all_items_salable'); } $optionCollection = $this->getOptionsCollection($product); if (!count($optionCollection->getItems())) { return false; } $requiredOptionIds = []; foreach ($optionCollection->getItems() as $option) { if ($option->getRequired()) { $requiredOptionIds[$option->getId()] = 0; } } $selectionCollection = $this->getSelectionsCollection($optionCollection->getAllIds(), $product); if (!count($selectionCollection->getItems())) { return false; } $salableSelectionCount = 0; foreach ($selectionCollection as $selection) { /* @var $selection \Magento\Catalog\Model\Product */ if ($selection->isSalable()) { $selectionEnoughQty = $this->_stockRegistry->getStockItem($selection->getId())->getManageStock() ? $selection->getSelectionQty() <= $this->_stockState->getStockQty($selection->getId()) : $selection->isInStock(); if (!$selection->hasSelectionQty() || $selection->getSelectionCanChangeQty() || $selectionEnoughQty) { $requiredOptionIds[$selection->getOptionId()] = 1; $salableSelectionCount++; } } } $isSalable = array_sum($requiredOptionIds) == count($requiredOptionIds) && $salableSelectionCount; $product->setData('all_items_salable', $isSalable); return $isSalable; }
/** * Check product inventory data when quote item quantity declaring * * @param \Magento\Framework\Event\Observer $observer * * @return void * @throws \Magento\Framework\Exception\LocalizedException * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function validate(\Magento\Framework\Event\Observer $observer) { /* @var $quoteItem \Magento\Quote\Model\Quote\Item */ $quoteItem = $observer->getEvent()->getItem(); if (!$quoteItem || !$quoteItem->getProductId() || !$quoteItem->getQuote() || $quoteItem->getQuote()->getIsSuperMode()) { return; } $qty = $quoteItem->getQty(); /** @var \Magento\CatalogInventory\Model\Stock\Item $stockItem */ $stockItem = $this->stockRegistry->getStockItem($quoteItem->getProduct()->getId(), $quoteItem->getProduct()->getStore()->getWebsiteId()); /* @var $stockItem \Magento\CatalogInventory\Api\Data\StockItemInterface */ if (!$stockItem instanceof \Magento\CatalogInventory\Api\Data\StockItemInterface) { throw new \Magento\Framework\Exception\LocalizedException(__('The stock item for Product is not valid.')); } $parentStockItem = false; /** * Check if product in stock. For composite products check base (parent) item stock status */ if ($quoteItem->getParentItem()) { $product = $quoteItem->getParentItem()->getProduct(); $parentStockItem = $this->stockRegistry->getStockItem($product->getId(), $product->getStore()->getWebsiteId()); } if ($stockItem) { if (!$stockItem->getIsInStock() || $parentStockItem && !$parentStockItem->getIsInStock()) { $quoteItem->addErrorInfo('cataloginventory', \Magento\CatalogInventory\Helper\Data::ERROR_QTY, __('This product is out of stock.')); $quoteItem->getQuote()->addErrorInfo('stock', 'cataloginventory', \Magento\CatalogInventory\Helper\Data::ERROR_QTY, __('Some of the products are out of stock.')); return; } else { // Delete error from item and its quote, if it was set due to item out of stock $this->_removeErrorsFromQuoteAndItem($quoteItem, \Magento\CatalogInventory\Helper\Data::ERROR_QTY); } } /** * Check item for options */ if (($options = $quoteItem->getQtyOptions()) && $qty > 0) { $qty = $quoteItem->getProduct()->getTypeInstance()->prepareQuoteItemQty($qty, $quoteItem->getProduct()); $quoteItem->setData('qty', $qty); if ($stockItem) { $result = $this->stockState->checkQtyIncrements($quoteItem->getProduct()->getId(), $qty, $quoteItem->getProduct()->getStore()->getWebsiteId()); if ($result->getHasError()) { $quoteItem->addErrorInfo('cataloginventory', \Magento\CatalogInventory\Helper\Data::ERROR_QTY_INCREMENTS, $result->getMessage()); $quoteItem->getQuote()->addErrorInfo($result->getQuoteMessageIndex(), 'cataloginventory', \Magento\CatalogInventory\Helper\Data::ERROR_QTY_INCREMENTS, $result->getQuoteMessage()); } else { // Delete error from item and its quote, if it was set due to qty problems $this->_removeErrorsFromQuoteAndItem($quoteItem, \Magento\CatalogInventory\Helper\Data::ERROR_QTY_INCREMENTS); } } foreach ($options as $option) { $result = $this->optionInitializer->initialize($option, $quoteItem, $qty); if ($result->getHasError()) { $option->setHasError(true); $quoteItem->addErrorInfo('cataloginventory', \Magento\CatalogInventory\Helper\Data::ERROR_QTY, $result->getMessage()); $quoteItem->getQuote()->addErrorInfo($result->getQuoteMessageIndex(), 'cataloginventory', \Magento\CatalogInventory\Helper\Data::ERROR_QTY, $result->getQuoteMessage()); } else { // Delete error from item and its quote, if it was set due to qty lack $this->_removeErrorsFromQuoteAndItem($quoteItem, \Magento\CatalogInventory\Helper\Data::ERROR_QTY); } } } else { $result = $this->stockItemInitializer->initialize($stockItem, $quoteItem, $qty); if ($result->getHasError()) { $quoteItem->addErrorInfo('cataloginventory', \Magento\CatalogInventory\Helper\Data::ERROR_QTY, $result->getMessage()); $quoteItem->getQuote()->addErrorInfo($result->getQuoteMessageIndex(), 'cataloginventory', \Magento\CatalogInventory\Helper\Data::ERROR_QTY, $result->getQuoteMessage()); } else { // Delete error from item and its quote, if it was set due to qty lack $this->_removeErrorsFromQuoteAndItem($quoteItem, \Magento\CatalogInventory\Helper\Data::ERROR_QTY); } } }
public function testCheckQuoteItemQty() { $this->assertEquals($this->objectResult, $this->stockState->checkQuoteItemQty($this->productId, $this->qty, $this->qty, $this->qty, $this->websiteId)); }