66namespace Magento \Sales \Model ;
77
88use Magento \Catalog \Model \Product \Type ;
9+ use Magento \Catalog \Api \ProductRepositoryInterface ;
10+ use Magento \Catalog \Model \Product \Visibility ;
911use Magento \Config \Model \Config \Source \Nooptreq ;
1012use Magento \Directory \Model \Currency ;
13+ use Magento \Directory \Model \CurrencyFactory ;
1114use Magento \Directory \Model \RegionFactory ;
1215use Magento \Directory \Model \ResourceModel \Region as RegionResource ;
1316use Magento \Framework \Api \AttributeValueFactory ;
17+ use Magento \Framework \Api \ExtensionAttributesFactory ;
1418use Magento \Framework \Api \SearchCriteriaBuilder ;
1519use Magento \Framework \App \Config \ScopeConfigInterface ;
1620use Magento \Framework \App \ObjectManager ;
21+ use Magento \Framework \Data \Collection \AbstractDb ;
1722use Magento \Framework \Exception \LocalizedException ;
1823use Magento \Framework \Locale \ResolverInterface ;
24+ use Magento \Framework \Model \Context ;
25+ use Magento \Framework \Model \ResourceModel \AbstractResource ;
1926use Magento \Framework \Pricing \PriceCurrencyInterface ;
27+ use Magento \Framework \Registry ;
28+ use Magento \Framework \Stdlib \DateTime \TimezoneInterface ;
2029use Magento \Sales \Api \Data \OrderInterface ;
2130use Magento \Sales \Api \Data \OrderItemInterface ;
2231use Magento \Sales \Api \Data \OrderStatusHistoryInterface ;
32+ use Magento \Sales \Api \InvoiceManagementInterface ;
2333use Magento \Sales \Api \OrderItemRepositoryInterface ;
34+ use Magento \Sales \Model \Order \Config ;
35+ use Magento \Sales \Model \Order \CreditmemoValidator ;
2436use Magento \Sales \Model \Order \Payment ;
2537use Magento \Sales \Model \Order \ProductOption ;
38+ use Magento \Sales \Model \Order \Status \HistoryFactory ;
2639use Magento \Sales \Model \ResourceModel \Order \Address \Collection ;
2740use Magento \Sales \Model \ResourceModel \Order \Creditmemo \Collection as CreditmemoCollection ;
2841use Magento \Sales \Model \ResourceModel \Order \Invoice \Collection as InvoiceCollection ;
2942use Magento \Sales \Model \ResourceModel \Order \Item \Collection as ItemCollection ;
43+ use Magento \Sales \Model \ResourceModel \Order \Item \CollectionFactory ;
3044use Magento \Sales \Model \ResourceModel \Order \Payment \Collection as PaymentCollection ;
3145use Magento \Sales \Model \ResourceModel \Order \Shipment \Collection as ShipmentCollection ;
3246use Magento \Sales \Model \ResourceModel \Order \Shipment \Track \Collection as TrackCollection ;
3347use Magento \Sales \Model \ResourceModel \Order \Status \History \Collection as HistoryCollection ;
3448use Magento \Store \Model \ScopeInterface ;
3549use Magento \Framework \App \Area ;
3650use Magento \Sales \Model \Order \StatusLabel ;
51+ use Magento \Store \Model \StoreManagerInterface ;
3752
3853/**
3954 * Order model
@@ -334,20 +349,25 @@ class Order extends AbstractModel implements EntityInterface, OrderInterface
334349 private $ statusLabel ;
335350
336351 /**
337- * @param \Magento\Framework\Model\Context $context
338- * @param \Magento\Framework\Registry $registry
339- * @param \Magento\Framework\Api\ExtensionAttributesFactory $extensionFactory
352+ * @var ?CreditmemoValidator
353+ */
354+ private $ creditmemoValidator ;
355+
356+ /**
357+ * @param Context $context
358+ * @param Registry $registry
359+ * @param ExtensionAttributesFactory $extensionFactory
340360 * @param AttributeValueFactory $customAttributeFactory
341- * @param \Magento\Framework\Stdlib\DateTime\ TimezoneInterface $timezone
342- * @param \Magento\Store\Model\ StoreManagerInterface $storeManager
343- * @param Order\ Config $orderConfig
344- * @param \Magento\Catalog\Api\ ProductRepositoryInterface $productRepository
345- * @param \Magento\Sales\Model\ResourceModel\Order\Item\ CollectionFactory $orderItemCollectionFactory
346- * @param \Magento\Catalog\Model\Product\ Visibility $productVisibility
347- * @param \Magento\Sales\Api\ InvoiceManagementInterface $invoiceManagement
348- * @param \Magento\Directory\Model\ CurrencyFactory $currencyFactory
361+ * @param TimezoneInterface $timezone
362+ * @param StoreManagerInterface $storeManager
363+ * @param Config $orderConfig
364+ * @param ProductRepositoryInterface $productRepository
365+ * @param CollectionFactory $orderItemCollectionFactory
366+ * @param Visibility $productVisibility
367+ * @param InvoiceManagementInterface $invoiceManagement
368+ * @param CurrencyFactory $currencyFactory
349369 * @param \Magento\Eav\Model\Config $eavConfig
350- * @param Order\Status\ HistoryFactory $orderHistoryFactory
370+ * @param HistoryFactory $orderHistoryFactory
351371 * @param \Magento\Sales\Model\ResourceModel\Order\Address\CollectionFactory $addressCollectionFactory
352372 * @param \Magento\Sales\Model\ResourceModel\Order\Payment\CollectionFactory $paymentCollectionFactory
353373 * @param \Magento\Sales\Model\ResourceModel\Order\Status\History\CollectionFactory $historyCollectionFactory
@@ -358,8 +378,8 @@ class Order extends AbstractModel implements EntityInterface, OrderInterface
358378 * @param ResourceModel\Order\CollectionFactory $salesOrderCollectionFactory
359379 * @param PriceCurrencyInterface $priceCurrency
360380 * @param \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $productListFactory
361- * @param \Magento\Framework\Model\ResourceModel\ AbstractResource|null $resource
362- * @param \Magento\Framework\Data\Collection\ AbstractDb|null $resourceCollection
381+ * @param AbstractResource|null $resource
382+ * @param AbstractDb|null $resourceCollection
363383 * @param array $data
364384 * @param ResolverInterface|null $localeResolver
365385 * @param ProductOption|null $productOption
@@ -369,8 +389,10 @@ class Order extends AbstractModel implements EntityInterface, OrderInterface
369389 * @param RegionFactory|null $regionFactory
370390 * @param RegionResource|null $regionResource
371391 * @param StatusLabel|null $statusLabel
392+ * @param CreditmemoValidator|null $creditmemoValidator
372393 * @SuppressWarnings(PHPMD.ExcessiveParameterList)
373394 * @SuppressWarnings(PHPMD.NPathComplexity)
395+ * @SuppressWarnings(PHPMD.CyclomaticComplexity)
374396 */
375397 public function __construct (
376398 \Magento \Framework \Model \Context $ context ,
@@ -407,7 +429,8 @@ public function __construct(
407429 ScopeConfigInterface $ scopeConfig = null ,
408430 RegionFactory $ regionFactory = null ,
409431 RegionResource $ regionResource = null ,
410- StatusLabel $ statusLabel = null
432+ StatusLabel $ statusLabel = null ,
433+ CreditmemoValidator $ creditmemoValidator = null
411434 ) {
412435 $ this ->_storeManager = $ storeManager ;
413436 $ this ->_orderConfig = $ orderConfig ;
@@ -440,6 +463,8 @@ public function __construct(
440463 $ this ->regionResource = $ regionResource ?: ObjectManager::getInstance ()->get (RegionResource::class);
441464 $ this ->regionItems = [];
442465 $ this ->statusLabel = $ statusLabel ?: ObjectManager::getInstance ()->get (StatusLabel::class);
466+ $ this ->creditmemoValidator = $ creditmemoValidator ?:
467+ ObjectManager::getInstance ()->get (CreditmemoValidator::class);
443468 parent ::__construct (
444469 $ context ,
445470 $ registry ,
@@ -745,23 +770,43 @@ private function canCreditmemoForZeroTotalRefunded($totalRefunded)
745770 */
746771 private function canCreditmemoForZeroTotal ($ totalRefunded )
747772 {
773+ if ($ this ->areThereRefundableItems ()) {
774+ return true ;
775+ }
776+
748777 $ totalPaid = $ this ->getTotalPaid ();
749778 //check if total paid is less than grandtotal
750779 $ checkAmtTotalPaid = $ totalPaid <= $ this ->getGrandTotal ();
751780 //case when amount is due for invoice
752781 $ hasDueAmount = $ this ->canInvoice () && ($ checkAmtTotalPaid );
753782 //case when paid amount is refunded and order has creditmemo created
754- $ creditmemos = ( $ this ->getCreditmemosCollection () === false ) ?
755- true : ( $ this ->_memoCollectionFactory ->create ()->setOrderFilter ($ this )->getTotalCount () > 0 ) ;
783+ $ creditmemos = $ this ->getCreditmemosCollection () === false ||
784+ $ this ->_memoCollectionFactory ->create ()->setOrderFilter ($ this )->getTotalCount () > 0 ;
756785 $ paidAmtIsRefunded = $ this ->getTotalRefunded () == $ totalPaid && $ creditmemos ;
757- if (( $ hasDueAmount || $ paidAmtIsRefunded ) ||
758- (! $ checkAmtTotalPaid &&
759- abs ($ totalRefunded - $ this ->getAdjustmentNegative ()) < .0001 )) {
786+ if ($ hasDueAmount ||
787+ $ paidAmtIsRefunded ||
788+ (! $ checkAmtTotalPaid && abs ($ totalRefunded - $ this ->getAdjustmentNegative ()) < .0001 )) {
760789 return false ;
761790 }
762791 return true ;
763792 }
764793
794+ /**
795+ * Check if there are order items available for refund.
796+ *
797+ * @return bool
798+ */
799+ private function areThereRefundableItems (): bool
800+ {
801+ foreach ($ this ->getAllItems () as $ orderItem ) {
802+ if ($ this ->creditmemoValidator ->canRefundItem ($ orderItem )) {
803+ return true ;
804+ }
805+ }
806+
807+ return false ;
808+ }
809+
765810 /**
766811 * Retrieve order hold availability
767812 *
0 commit comments