| Current Path : /home/rtorresani/www/vendor/magento/module-catalog-inventory/Model/ |
| Current File : //home/rtorresani/www/vendor/magento/module-catalog-inventory/Model/StockStateProvider.php |
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Magento\CatalogInventory\Model;
use Magento\Catalog\Model\ProductFactory;
use Magento\CatalogInventory\Api\Data\StockItemInterface;
use Magento\CatalogInventory\Model\Spi\StockStateProviderInterface;
use Magento\Framework\DataObject\Factory as ObjectFactory;
use Magento\Framework\Locale\FormatInterface;
use Magento\Framework\Math\Division as MathDivision;
/**
* Provider stocks state
*/
class StockStateProvider implements StockStateProviderInterface
{
/**
* @var MathDivision
*/
protected $mathDivision;
/**
* @var FormatInterface
*/
protected $localeFormat;
/**
* @var ObjectFactory
*/
protected $objectFactory;
/**
* @var ProductFactory
*/
protected $productFactory;
/**
* @var bool
*/
protected $qtyCheckApplicable;
/**
* @param MathDivision $mathDivision
* @param FormatInterface $localeFormat
* @param ObjectFactory $objectFactory
* @param ProductFactory $productFactory
* @param bool $qtyCheckApplicable
*/
public function __construct(
MathDivision $mathDivision,
FormatInterface $localeFormat,
ObjectFactory $objectFactory,
ProductFactory $productFactory,
$qtyCheckApplicable = true
) {
$this->mathDivision = $mathDivision;
$this->localeFormat = $localeFormat;
$this->objectFactory = $objectFactory;
$this->productFactory = $productFactory;
$this->qtyCheckApplicable = $qtyCheckApplicable;
}
/**
* Validate stock
*
* @param StockItemInterface $stockItem
* @return bool
*/
public function verifyStock(StockItemInterface $stockItem)
{
if ($stockItem->getQty() === null && $stockItem->getManageStock()) {
return false;
}
if ($stockItem->getBackorders() == StockItemInterface::BACKORDERS_NO
&& $stockItem->getQty() <= $stockItem->getMinQty()
) {
return false;
}
return true;
}
/**
* Verify notification
*
* @param StockItemInterface $stockItem
* @return bool
*/
public function verifyNotification(StockItemInterface $stockItem)
{
return (float)$stockItem->getQty() < $stockItem->getNotifyStockQty();
}
/**
* Validate quote qty
*
* @param StockItemInterface $stockItem
* @param int|float $qty
* @param int|float $summaryQty
* @param int|float $origQty
* @return \Magento\Framework\DataObject
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @SuppressWarnings(PHPMD.NPathComplexity)
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function checkQuoteItemQty(StockItemInterface $stockItem, $qty, $summaryQty, $origQty = 0)
{
$result = $this->objectFactory->create();
$result->setHasError(false);
$qty = $this->getNumber($qty);
$quoteMessage = __('Please correct the quantity for some products.');
if ($stockItem->getMinSaleQty() && $qty < $stockItem->getMinSaleQty()) {
$result->setHasError(true)
->setMessage(__('The fewest you may purchase is %1.', $stockItem->getMinSaleQty() * 1))
->setErrorCode('qty_min')
->setQuoteMessage($quoteMessage)
->setQuoteMessageIndex('qty');
return $result;
}
if ($stockItem->getMaxSaleQty() && $qty > $stockItem->getMaxSaleQty()) {
$result->setHasError(true)
->setMessage(__('The requested qty exceeds the maximum qty allowed in shopping cart'))
->setErrorCode('qty_max')
->setQuoteMessage($quoteMessage)
->setQuoteMessageIndex('qty');
return $result;
}
$result->addData($this->checkQtyIncrements($stockItem, $qty)->getData());
$result->setItemIsQtyDecimal($stockItem->getIsQtyDecimal());
if (!$stockItem->getIsQtyDecimal() && (floor($qty) !== (float) $qty)) {
$result->setHasError(true)
->setMessage(__('You cannot use decimal quantity for this product.'))
->setErrorCode('qty_decimal')
->setQuoteMessage($quoteMessage)
->setQuoteMessageIndex('qty');
return $result;
}
if ($result->getHasError()) {
return $result;
}
if (!$stockItem->getManageStock()) {
return $result;
}
if (!$stockItem->getIsInStock()) {
$result->setHasError(true)
->setErrorCode('out_stock')
->setMessage(__('This product is out of stock.'))
->setQuoteMessage(__('Some of the products are out of stock.'))
->setQuoteMessageIndex('stock');
$result->setItemUseOldQty(true);
return $result;
}
if (!$this->checkQty($stockItem, $summaryQty) || !$this->checkQty($stockItem, $qty)) {
$message = __('The requested qty is not available');
$result->setHasError(true)
->setErrorCode('qty_available')
->setMessage($message)
->setQuoteMessage($message)
->setQuoteMessageIndex('qty');
return $result;
} else {
if ($stockItem->getQty() - $summaryQty < 0) {
if ($stockItem->getProductName()) {
if ($stockItem->getIsChildItem()) {
$backOrderQty = $stockItem->getQty() > 0 ? ($summaryQty - $stockItem->getQty()) * 1 : $qty * 1;
if ($backOrderQty > $qty) {
$backOrderQty = $qty;
}
$result->setItemBackorders($backOrderQty);
} else {
$orderedItems = (int)$stockItem->getOrderedItems();
// Available item qty in stock excluding item qty in other quotes
$qtyAvailable = ($stockItem->getQty() - ($summaryQty - $qty)) * 1;
if ($qtyAvailable > 0) {
$backOrderQty = $qty * 1 - $qtyAvailable;
} else {
$backOrderQty = $qty * 1;
}
if ($backOrderQty > 0) {
$result->setItemBackorders($backOrderQty);
}
$stockItem->setOrderedItems($orderedItems + $qty);
}
if ($stockItem->getBackorders() == \Magento\CatalogInventory\Model\Stock::BACKORDERS_YES_NOTIFY) {
if (!$stockItem->getIsChildItem()) {
$result->setMessage(
__(
'We don\'t have as many "%1" as you requested, '
. 'but we\'ll back order the remaining %2.',
$stockItem->getProductName(),
$backOrderQty * 1
)
);
} else {
$result->setMessage(
__(
'We don\'t have "%1" in the requested quantity, '
. 'so we\'ll back order the remaining %2.',
$stockItem->getProductName(),
$backOrderQty * 1
)
);
}
} elseif ($stockItem->getShowDefaultNotificationMessage()) {
$result->setMessage(
__('The requested qty is not available')
);
}
}
} else {
if (!$stockItem->getIsChildItem()) {
$stockItem->setOrderedItems($qty + (int)$stockItem->getOrderedItems());
}
}
}
return $result;
}
/**
* Check quantity
*
* @param StockItemInterface $stockItem
* @param int|float $qty
* @exception \Magento\Framework\Exception\LocalizedException
* @return bool
*/
public function checkQty(StockItemInterface $stockItem, $qty)
{
if (!$this->qtyCheckApplicable) {
return true;
}
if (!$stockItem->getManageStock()) {
return true;
}
if (!$stockItem->getIsInStock()) {
return false;
}
if ($stockItem->getQty() - $stockItem->getMinQty() - $qty < 0) {
switch ($stockItem->getBackorders()) {
case \Magento\CatalogInventory\Model\Stock::BACKORDERS_YES_NONOTIFY:
case \Magento\CatalogInventory\Model\Stock::BACKORDERS_YES_NOTIFY:
break;
default:
return false;
}
}
return true;
}
/**
* Returns suggested qty
*
* Returns suggested qty that satisfies qty increments and minQty/maxQty/minSaleQty/maxSaleQty conditions
* or original qty if such value does not exist
*
* @param StockItemInterface $stockItem
* @param int|float $qty
* @return int|float
*/
public function suggestQty(StockItemInterface $stockItem, $qty)
{
// We do not manage stock
if ($qty <= 0 || !$stockItem->getManageStock()) {
return $qty;
}
$qtyIncrements = (int)$stockItem->getQtyIncrements();
// Currently only integer increments supported
if ($qtyIncrements < 2) {
return $qty;
}
$minQty = max($stockItem->getMinSaleQty(), $qtyIncrements);
$divisibleMin = ceil($minQty / $qtyIncrements) * $qtyIncrements;
$maxQty = min($stockItem->getQty() - $stockItem->getMinQty(), $stockItem->getMaxSaleQty());
$divisibleMax = floor($maxQty / $qtyIncrements) * $qtyIncrements;
if ($qty < $minQty || $qty > $maxQty || $divisibleMin > $divisibleMax) {
// Do not perform rounding for qty that does not satisfy min/max conditions to not confuse customer
return $qty;
}
// Suggest value closest to given qty
$closestDivisibleLeft = floor($qty / $qtyIncrements) * $qtyIncrements;
$closestDivisibleRight = $closestDivisibleLeft + $qtyIncrements;
$acceptableLeft = min(max($divisibleMin, $closestDivisibleLeft), $divisibleMax);
$acceptableRight = max(min($divisibleMax, $closestDivisibleRight), $divisibleMin);
return abs($acceptableLeft - $qty) < abs($acceptableRight - $qty) ? $acceptableLeft : $acceptableRight;
}
/**
* Check Qty Increments
*
* @param StockItemInterface $stockItem
* @param float|int $qty
* @return \Magento\Framework\DataObject
*/
public function checkQtyIncrements(StockItemInterface $stockItem, $qty)
{
$result = new \Magento\Framework\DataObject();
if ($stockItem->getSuppressCheckQtyIncrements()) {
return $result;
}
$qtyIncrements = $stockItem->getQtyIncrements() * 1;
if ($qtyIncrements && $this->mathDivision->getExactDivision($qty, $qtyIncrements) != 0) {
$result->setHasError(true)
->setQuoteMessage(__('Please correct the quantity for some products.'))
->setErrorCode('qty_increments')
->setQuoteMessageIndex('qty');
if ($stockItem->getIsChildItem()) {
$result->setMessage(
__(
'You can buy %1 only in quantities of %2 at a time.',
$stockItem->getProductName(),
$qtyIncrements
)
);
} else {
$result->setMessage(__('You can buy this product only in quantities of %1 at a time.', $qtyIncrements));
}
}
return $result;
}
/**
* Retrieve stock qty whether product is composite or no
*
* @param StockItemInterface $stockItem
* @return float
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @SuppressWarnings(PHPMD.UnusedLocalVariable)
*/
public function getStockQty(StockItemInterface $stockItem)
{
if (!$stockItem->hasStockQty()) {
$stockItem->setStockQty(0);
$product = $this->productFactory->create();
$product->load($stockItem->getProductId());
// prevent possible recursive loop
if (!$product->isComposite()) {
$stockQty = $stockItem->getQty();
} else {
$stockQty = null;
$productsByGroups = $product->getTypeInstance()->getProductsToPurchaseByReqGroups($product);
foreach ($productsByGroups as $productsInGroup) {
$qty = 0;
foreach ($productsInGroup as $childProduct) {
$qty += $this->getStockQty($stockItem);
}
if (null === $stockQty || $qty < $stockQty) {
$stockQty = $qty;
}
}
}
$stockQty = (float)$stockQty;
if ($stockQty < 0 || !$stockItem->getManageStock() || !$stockItem->getIsInStock()
|| !$product->isSaleable()
) {
$stockQty = 0;
}
$stockItem->setStockQty($stockQty);
}
return (float)$stockItem->getData('stock_qty');
}
/**
* Get numeric qty
*
* @param string|float|int|null $qty
* @return float|null
*/
protected function getNumber($qty)
{
if (!is_numeric($qty)) {
$qty = $this->localeFormat->getNumber($qty);
return $qty;
}
return $qty;
}
}