123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251 |
- <?php
- /**
- * Copyright © Magento, Inc. All rights reserved.
- * See COPYING.txt for license details.
- */
- namespace Magento\Sales\Model\Order\Creditmemo\Validation;
- use Magento\Sales\Api\Data\CreditmemoInterface;
- use Magento\Sales\Api\Data\OrderInterface;
- use Magento\Sales\Api\Data\OrderItemInterface;
- use Magento\Sales\Api\InvoiceRepositoryInterface;
- use Magento\Sales\Api\OrderRepositoryInterface;
- use Magento\Sales\Model\Order;
- use Magento\Sales\Model\Order\Creditmemo;
- use Magento\Sales\Model\Order\Item;
- use Magento\Sales\Model\ValidatorInterface;
- /**
- * Class QuantityValidator
- */
- class QuantityValidator implements ValidatorInterface
- {
- /**
- * @var OrderRepositoryInterface
- */
- private $orderRepository;
- /**
- * @var InvoiceRepositoryInterface
- */
- private $invoiceRepository;
- /**
- * @var \Magento\Framework\Pricing\PriceCurrencyInterface
- */
- private $priceCurrency;
- /**
- * InvoiceValidator constructor.
- *
- * @param OrderRepositoryInterface $orderRepository
- * @param InvoiceRepositoryInterface $invoiceRepository
- * @param \Magento\Framework\Pricing\PriceCurrencyInterface $priceCurrency
- */
- public function __construct(
- OrderRepositoryInterface $orderRepository,
- InvoiceRepositoryInterface $invoiceRepository,
- \Magento\Framework\Pricing\PriceCurrencyInterface $priceCurrency
- ) {
- $this->orderRepository = $orderRepository;
- $this->invoiceRepository = $invoiceRepository;
- $this->priceCurrency = $priceCurrency;
- }
- /**
- * @inheritdoc
- */
- public function validate($entity)
- {
- /**
- * @var $entity CreditmemoInterface
- */
- if ($entity->getOrderId() === null) {
- return [__('Order Id is required for creditmemo document')];
- }
- $messages = [];
- $order = $this->orderRepository->get($entity->getOrderId());
- $orderItemsById = $this->getOrderItems($order);
- $invoiceQtysRefundLimits = $this->getInvoiceQtysRefundLimits($entity, $order);
- $totalQuantity = 0;
- foreach ($entity->getItems() as $item) {
- if (!isset($orderItemsById[$item->getOrderItemId()])) {
- $messages[] = __(
- 'The creditmemo contains product SKU "%1" that is not part of the original order.',
- $item->getSku()
- );
- continue;
- }
- $orderItem = $orderItemsById[$item->getOrderItemId()];
- if (!$this->canRefundItem($orderItem, $item->getQty(), $invoiceQtysRefundLimits) ||
- !$this->isQtyAvailable($orderItem, $item->getQty())
- ) {
- $messages[] =__(
- 'The quantity to creditmemo must not be greater than the unrefunded quantity'
- . ' for product SKU "%1".',
- $orderItem->getSku()
- );
- } else {
- $totalQuantity += $item->getQty();
- }
- }
- if ($entity->getGrandTotal() <= 0) {
- $messages[] = __('The credit memo\'s total must be positive.');
- } elseif ($totalQuantity <= 0 && !$this->canRefundShipping($order)) {
- $messages[] = __('You can\'t create a creditmemo without products.');
- }
- return $messages;
- }
- /**
- * We can have problem with float in php (on some server $a=762.73;$b=762.73; $a-$b!=0)
- * for this we have additional diapason for 0
- * TotalPaid - contains amount, that were not rounded.
- *
- * @param OrderInterface $order
- * @return bool
- */
- private function canRefundShipping(OrderInterface $order)
- {
- return !abs($this->priceCurrency->round($order->getShippingAmount()) - $order->getShippingRefunded()) < .0001;
- }
- /**
- * @param CreditmemoInterface $creditmemo
- * @param OrderInterface $order
- * @return array
- */
- private function getInvoiceQtysRefundLimits(CreditmemoInterface $creditmemo, OrderInterface $order)
- {
- $invoiceQtysRefundLimits = [];
- if ($creditmemo->getInvoiceId() !== null) {
- $invoiceQtysRefunded = [];
- $invoice = $this->invoiceRepository->get($creditmemo->getInvoiceId());
- foreach ($order->getCreditmemosCollection() as $createdCreditmemo) {
- if ($createdCreditmemo->getState() != Creditmemo::STATE_CANCELED &&
- $createdCreditmemo->getInvoiceId() == $invoice->getId()
- ) {
- foreach ($createdCreditmemo->getAllItems() as $createdCreditmemoItem) {
- $orderItemId = $createdCreditmemoItem->getOrderItem()->getId();
- if (isset($invoiceQtysRefunded[$orderItemId])) {
- $invoiceQtysRefunded[$orderItemId] += $createdCreditmemoItem->getQty();
- } else {
- $invoiceQtysRefunded[$orderItemId] = $createdCreditmemoItem->getQty();
- }
- }
- }
- }
- foreach ($invoice->getItems() as $invoiceItem) {
- $invoiceQtyCanBeRefunded = $invoiceItem->getQty();
- $orderItemId = $invoiceItem->getOrderItem()->getId();
- if (isset($invoiceQtysRefunded[$orderItemId])) {
- $invoiceQtyCanBeRefunded = $invoiceQtyCanBeRefunded - $invoiceQtysRefunded[$orderItemId];
- }
- $invoiceQtysRefundLimits[$orderItemId] = $invoiceQtyCanBeRefunded;
- }
- }
- return $invoiceQtysRefundLimits;
- }
- /**
- * @param OrderInterface $order
- * @return OrderItemInterface[]
- */
- private function getOrderItems(OrderInterface $order)
- {
- $orderItemsById = [];
- foreach ($order->getItems() as $item) {
- $orderItemsById[$item->getItemId()] = $item;
- }
- return $orderItemsById;
- }
- /**
- * @param Item $orderItem
- * @param int $qty
- * @return bool
- */
- private function isQtyAvailable(Item $orderItem, $qty)
- {
- return $qty <= $orderItem->getQtyToRefund() || $orderItem->isDummy();
- }
- /**
- * Check if order item can be refunded
- *
- * @param \Magento\Sales\Model\Order\Item $item
- * @param double $qty
- * @param array $invoiceQtysRefundLimits
- * @return bool
- */
- private function canRefundItem(\Magento\Sales\Model\Order\Item $item, $qty, array $invoiceQtysRefundLimits)
- {
- if ($item->isDummy()) {
- return $this->canRefundDummyItem($item, $qty, $invoiceQtysRefundLimits);
- }
- return $this->canRefundNoDummyItem($item, $invoiceQtysRefundLimits);
- }
- /**
- * Check if no dummy order item can be refunded
- *
- * @param \Magento\Sales\Model\Order\Item $item
- * @param array $invoiceQtysRefundLimits
- * @return bool
- */
- private function canRefundNoDummyItem(\Magento\Sales\Model\Order\Item $item, array $invoiceQtysRefundLimits = [])
- {
- if ($item->getQtyToRefund() < 0) {
- return false;
- }
- if (isset($invoiceQtysRefundLimits[$item->getId()])) {
- return $invoiceQtysRefundLimits[$item->getId()] > 0;
- }
- return true;
- }
- /**
- * @param Item $item
- * @param int $qty
- * @param array $invoiceQtysRefundLimits
- * @return bool
- */
- private function canRefundDummyItem(\Magento\Sales\Model\Order\Item $item, $qty, array $invoiceQtysRefundLimits)
- {
- if ($item->getHasChildren()) {
- foreach ($item->getChildrenItems() as $child) {
- if ($this->canRefundRequestedQty($child, $qty, $invoiceQtysRefundLimits)) {
- return true;
- }
- }
- } elseif ($item->getParentItem()) {
- return $this->canRefundRequestedQty($item->getParentItem(), $qty, $invoiceQtysRefundLimits);
- }
- return false;
- }
- /**
- * @param Item $item
- * @param int $qty
- * @param array $invoiceQtysRefundLimits
- * @return bool
- */
- private function canRefundRequestedQty(
- \Magento\Sales\Model\Order\Item $item,
- $qty,
- array $invoiceQtysRefundLimits
- ) {
- return $qty === null ? $this->canRefundNoDummyItem($item, $invoiceQtysRefundLimits) : $qty > 0;
- }
- }
|