Skip to content

Commit 2f0ff41

Browse files
committed
Merge branch 'ACP2E-1918' of https://github.com/magento-l3/magento2ce into PR-L3-2023-07-14
2 parents 87652b1 + 0d994b0 commit 2f0ff41

File tree

9 files changed

+315
-99
lines changed

9 files changed

+315
-99
lines changed

app/code/Magento/Checkout/Test/Fixture/SetPaymentMethod.php

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,17 +38,21 @@ public function __construct(
3838

3939
/**
4040
* {@inheritdoc}
41-
* @param array $data Parameters
41+
* @param array $data Parameters. Same format as SetPaymentMethod::DEFAULT_DATA.
4242
* <pre>
4343
* $data = [
4444
* 'cart_id' => (int) Cart ID. Required
4545
* 'method' => (array) Payment method. Optional
4646
* ]
4747
* </pre>
48+
* Fields structure:
49+
* - $data['method']: can be supplied in following formats:
50+
* - array ["method" => "checkmo", "po_number" => null, "additional_data" => null]
51+
* - string "checkmo"
4852
*/
4953
public function apply(array $data = []): ?DataObject
5054
{
51-
$data = array_merge(self::DEFAULT_DATA, $data);
55+
$data = $this->prepareData($data);
5256
$service = $this->serviceFactory->create(PaymentMethodManagementInterface::class, 'set');
5357
$service->execute(
5458
[
@@ -59,4 +63,19 @@ public function apply(array $data = []): ?DataObject
5963

6064
return null;
6165
}
66+
67+
/**
68+
* Prepare payment data
69+
*
70+
* @param array $data
71+
* @return array
72+
*/
73+
private function prepareData(array $data): array
74+
{
75+
if (isset($data['method']) && is_string($data['method'])) {
76+
$data['method'] = ['method' => $data['method']];
77+
}
78+
79+
return array_merge(self::DEFAULT_DATA, $data);
80+
}
6281
}

app/code/Magento/Sales/Model/Order.php

Lines changed: 65 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,34 +6,49 @@
66
namespace Magento\Sales\Model;
77

88
use Magento\Catalog\Model\Product\Type;
9+
use Magento\Catalog\Api\ProductRepositoryInterface;
10+
use Magento\Catalog\Model\Product\Visibility;
911
use Magento\Config\Model\Config\Source\Nooptreq;
1012
use Magento\Directory\Model\Currency;
13+
use Magento\Directory\Model\CurrencyFactory;
1114
use Magento\Directory\Model\RegionFactory;
1215
use Magento\Directory\Model\ResourceModel\Region as RegionResource;
1316
use Magento\Framework\Api\AttributeValueFactory;
17+
use Magento\Framework\Api\ExtensionAttributesFactory;
1418
use Magento\Framework\Api\SearchCriteriaBuilder;
1519
use Magento\Framework\App\Config\ScopeConfigInterface;
1620
use Magento\Framework\App\ObjectManager;
21+
use Magento\Framework\Data\Collection\AbstractDb;
1722
use Magento\Framework\Exception\LocalizedException;
1823
use Magento\Framework\Locale\ResolverInterface;
24+
use Magento\Framework\Model\Context;
25+
use Magento\Framework\Model\ResourceModel\AbstractResource;
1926
use Magento\Framework\Pricing\PriceCurrencyInterface;
27+
use Magento\Framework\Registry;
28+
use Magento\Framework\Stdlib\DateTime\TimezoneInterface;
2029
use Magento\Sales\Api\Data\OrderInterface;
2130
use Magento\Sales\Api\Data\OrderItemInterface;
2231
use Magento\Sales\Api\Data\OrderStatusHistoryInterface;
32+
use Magento\Sales\Api\InvoiceManagementInterface;
2333
use Magento\Sales\Api\OrderItemRepositoryInterface;
34+
use Magento\Sales\Model\Order\Config;
35+
use Magento\Sales\Model\Order\CreditmemoValidator;
2436
use Magento\Sales\Model\Order\Payment;
2537
use Magento\Sales\Model\Order\ProductOption;
38+
use Magento\Sales\Model\Order\Status\HistoryFactory;
2639
use Magento\Sales\Model\ResourceModel\Order\Address\Collection;
2740
use Magento\Sales\Model\ResourceModel\Order\Creditmemo\Collection as CreditmemoCollection;
2841
use Magento\Sales\Model\ResourceModel\Order\Invoice\Collection as InvoiceCollection;
2942
use Magento\Sales\Model\ResourceModel\Order\Item\Collection as ItemCollection;
43+
use Magento\Sales\Model\ResourceModel\Order\Item\CollectionFactory;
3044
use Magento\Sales\Model\ResourceModel\Order\Payment\Collection as PaymentCollection;
3145
use Magento\Sales\Model\ResourceModel\Order\Shipment\Collection as ShipmentCollection;
3246
use Magento\Sales\Model\ResourceModel\Order\Shipment\Track\Collection as TrackCollection;
3347
use Magento\Sales\Model\ResourceModel\Order\Status\History\Collection as HistoryCollection;
3448
use Magento\Store\Model\ScopeInterface;
3549
use Magento\Framework\App\Area;
3650
use 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
*

app/code/Magento/Sales/Model/Order/CreditmemoFactory.php

Lines changed: 21 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
use Magento\Framework\Locale\FormatInterface;
1111
use Magento\Framework\Serialize\Serializer\Json as JsonSerializer;
1212
use Magento\Sales\Api\Data\OrderItemInterface;
13+
use Magento\Sales\Model\Convert\OrderFactory;
14+
use Magento\Tax\Model\Config;
1315

1416
/**
1517
* Factory class for @see \Magento\Sales\Model\Order\Creditmemo
@@ -48,24 +50,33 @@ class CreditmemoFactory
4850
*/
4951
private $serializer;
5052

53+
/**
54+
* @var CreditmemoValidator
55+
*/
56+
private CreditmemoValidator $creditmemoValidator;
57+
5158
/**
5259
* Factory constructor
5360
*
54-
* @param \Magento\Sales\Model\Convert\OrderFactory $convertOrderFactory
55-
* @param \Magento\Tax\Model\Config $taxConfig
56-
* @param JsonSerializer $serializer
57-
* @param FormatInterface $localeFormat
61+
* @param OrderFactory $convertOrderFactory
62+
* @param Config $taxConfig
63+
* @param JsonSerializer|null $serializer
64+
* @param FormatInterface|null $localeFormat
65+
* @param CreditmemoValidator|null $creditmemoValidator
5866
*/
5967
public function __construct(
6068
\Magento\Sales\Model\Convert\OrderFactory $convertOrderFactory,
6169
\Magento\Tax\Model\Config $taxConfig,
6270
JsonSerializer $serializer = null,
63-
FormatInterface $localeFormat = null
71+
FormatInterface $localeFormat = null,
72+
CreditmemoValidator $creditmemoValidator = null
6473
) {
6574
$this->convertor = $convertOrderFactory->create();
6675
$this->taxConfig = $taxConfig;
6776
$this->serializer = $serializer ?: ObjectManager::getInstance()->get(JsonSerializer::class);
6877
$this->localeFormat = $localeFormat ?: ObjectManager::getInstance()->get(FormatInterface::class);
78+
$this->creditmemoValidator = $creditmemoValidator ?
79+
: ObjectManager::getInstance()->get(CreditmemoValidator::class);
6980
}
7081

7182
/**
@@ -152,55 +163,25 @@ public function createByInvoice(\Magento\Sales\Model\Order\Invoice $invoice, arr
152163
* @param \Magento\Sales\Model\Order\Item $item
153164
* @param array $qtys
154165
* @param array $invoiceQtysRefundLimits
166+
*
155167
* @return bool
156-
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
157168
*/
158169
protected function canRefundItem($item, $qtys = [], $invoiceQtysRefundLimits = [])
159170
{
160-
if ($item->isDummy()) {
161-
if ($item->getHasChildren()) {
162-
foreach ($item->getChildrenItems() as $child) {
163-
if (empty($qtys) || (count(array_unique($qtys)) === 1 && (int)end($qtys) === 0)) {
164-
if ($this->canRefundNoDummyItem($child, $invoiceQtysRefundLimits)) {
165-
return true;
166-
}
167-
} else {
168-
if (isset($qtys[$child->getId()]) && $qtys[$child->getId()] > 0) {
169-
return true;
170-
}
171-
}
172-
}
173-
return false;
174-
} elseif ($item->getParentItem()) {
175-
$parent = $item->getParentItem();
176-
if (empty($qtys)) {
177-
return $this->canRefundNoDummyItem($parent, $invoiceQtysRefundLimits);
178-
} else {
179-
return isset($qtys[$parent->getId()]) && $qtys[$parent->getId()] > 0;
180-
}
181-
}
182-
return false;
183-
} else {
184-
return $this->canRefundNoDummyItem($item, $invoiceQtysRefundLimits);
185-
}
171+
return $this->creditmemoValidator->canRefundItem($item, $qtys, $invoiceQtysRefundLimits);
186172
}
187173

188174
/**
189-
* Check if no dummy order item can be refunded
175+
* Check if no dummy order item can be refunded.
190176
*
191177
* @param \Magento\Sales\Model\Order\Item $item
192178
* @param array $invoiceQtysRefundLimits
179+
*
193180
* @return bool
194181
*/
195182
protected function canRefundNoDummyItem($item, $invoiceQtysRefundLimits = [])
196183
{
197-
if ($item->getQtyToRefund() < 0) {
198-
return false;
199-
}
200-
if (isset($invoiceQtysRefundLimits[$item->getId()])) {
201-
return $invoiceQtysRefundLimits[$item->getId()] > 0;
202-
}
203-
return true;
184+
return $this->creditmemoValidator->canRefundNoDummyItem($item, $invoiceQtysRefundLimits);
204185
}
205186

206187
/**

0 commit comments

Comments
 (0)