Skip to content

Commit b1de35e

Browse files
committed
ACP2E-2756: [Cloud] Order Status changed to complete when partially refund of a partially shipped order
- refactored solution
1 parent 4969bbb commit b1de35e

File tree

4 files changed

+85
-73
lines changed

4 files changed

+85
-73
lines changed

app/code/Magento/Sales/Controller/Adminhtml/Order/Creditmemo/Save.php

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
use Magento\Framework\App\Action\HttpPostActionInterface as HttpPostActionInterface;
99
use Magento\Backend\App\Action;
1010
use Magento\Sales\Helper\Data as SalesData;
11-
use Magento\Sales\Model\Order;
11+
use Magento\Sales\Model\Order\Creditmemo;
1212
use Magento\Sales\Model\Order\Email\Sender\CreditmemoSender;
1313

1414
class Save extends \Magento\Backend\App\Action implements HttpPostActionInterface
@@ -85,6 +85,7 @@ public function execute()
8585
$this->creditmemoLoader->setCreditmemo($this->getRequest()->getParam('creditmemo'));
8686
$this->creditmemoLoader->setInvoiceId($this->getRequest()->getParam('invoice_id'));
8787
$creditmemo = $this->creditmemoLoader->load();
88+
$this->adjustCreditMemoItemQuantities($creditmemo);
8889
if ($creditmemo) {
8990
if (!$creditmemo->isValidGrandTotal()) {
9091
throw new \Magento\Framework\Exception\LocalizedException(
@@ -141,4 +142,33 @@ public function execute()
141142
$resultRedirect->setPath('sales/*/new', ['_current' => true]);
142143
return $resultRedirect;
143144
}
145+
146+
/**
147+
* Adjust credit memo parent item quantities with children quantities
148+
*
149+
* @param Creditmemo $creditMemo
150+
* @return void
151+
*/
152+
private function adjustCreditMemoItemQuantities(Creditmemo $creditMemo): void
153+
{
154+
$items = $creditMemo->getAllItems();
155+
$parentQuantities = [];
156+
foreach ($items as $item) {
157+
if ($parentId = $item->getOrderItem()->getParentItemId()) {
158+
if (empty($parentQuantities[$parentId])) {
159+
$parentQuantities[$parentId] = $item->getQty();
160+
} else {
161+
$parentQuantities[$parentId] += $item->getQty();
162+
}
163+
}
164+
}
165+
166+
foreach($parentQuantities as $parentId => $quantity) {
167+
foreach ($items as $item) {
168+
if ($item->getOrderItemId() == $parentId) {
169+
$item->setQty($quantity);
170+
}
171+
}
172+
}
173+
}
144174
}

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

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -861,7 +861,6 @@ public function canComment()
861861
* Retrieve order shipment availability
862862
*
863863
* @return bool
864-
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
865864
*/
866865
public function canShip()
867866
{
@@ -877,27 +876,28 @@ public function canShip()
877876
return false;
878877
}
879878

880-
foreach ($this->getAllItems() as $item) {
881-
$qtyToShip = !$item->getParentItem() || $item->getParentItem()->getProductType() !== Type::TYPE_BUNDLE ?
882-
$item->getQtyToShip() : $item->getSimpleQtyToShip();
883-
884-
if ($qtyToShip > 0 && !$item->getIsVirtual() &&
885-
!$item->getLockedDoShip() && !$this->isRefunded($item)) {
886-
return true;
887-
}
888-
}
889-
return false;
879+
return $this->checkItemShipping();
890880
}
891881

892882
/**
893-
* Check if item is refunded.
883+
* Check if at least one of the order items can be shipped
894884
*
895-
* @param OrderItemInterface $item
896885
* @return bool
897886
*/
898-
private function isRefunded(OrderItemInterface $item)
887+
private function checkItemShipping(): bool
899888
{
900-
return $item->getQtyRefunded() == $item->getQtyOrdered();
889+
foreach ($this->getAllItems() as $item) {
890+
if (!$item->getParentItem()) {
891+
$qtyToShip = !$item->getParentItem() || $item->getParentItem()->getProductType() !== Type::TYPE_BUNDLE ?
892+
$item->getQtyToShip() : $item->getSimpleQtyToShip();
893+
894+
if ($qtyToShip > 0 && !$item->getIsVirtual() && !$item->getLockedDoShip()) {
895+
return true;
896+
}
897+
}
898+
}
899+
900+
return false;
901901
}
902902

903903
/**

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ public function getQtyToShip()
232232
*/
233233
public function getSimpleQtyToShip()
234234
{
235-
$qty = $this->getQtyOrdered() - max($this->getQtyShipped(), $this->getQtyRefunded()) - $this->getQtyCanceled();
235+
$qty = $this->getQtyOrdered() - $this->getQtyShipped() - $this->getQtyRefunded() - $this->getQtyCanceled();
236236
return max(round($qty, 8), 0);
237237
}
238238

app/code/Magento/Sales/Model/ResourceModel/Order/Handler/State.php

Lines changed: 38 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -18,102 +18,84 @@ class State
1818
*
1919
* @param Order $order
2020
* @return $this
21-
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
22-
* @SuppressWarnings(PHPMD.NPathComplexity)
2321
*/
2422
public function check(Order $order)
2523
{
24+
if ($order->isCanceled() && $order->canUnhold() && $order->canInvoice()) {
25+
return $this;
26+
}
27+
2628
$currentState = $order->getState();
27-
if ($currentState == Order::STATE_NEW && $order->getIsInProcess()) {
29+
if ($this->canBeProcessingStatus($order, $currentState)) {
2830
$order->setState(Order::STATE_PROCESSING)
2931
->setStatus($order->getConfig()->getStateDefaultStatus(Order::STATE_PROCESSING));
3032
$currentState = Order::STATE_PROCESSING;
3133
}
3234

33-
if (!$order->isCanceled() && !$order->canUnhold() && !$order->canInvoice()) {
34-
if (in_array($currentState, [Order::STATE_PROCESSING, Order::STATE_COMPLETE])
35-
&& !$order->canCreditmemo()
36-
&& !$order->canShip()
37-
&& $order->getIsNotVirtual()
38-
) {
39-
$order->setState(Order::STATE_CLOSED)
40-
->setStatus($order->getConfig()->getStateDefaultStatus(Order::STATE_CLOSED));
41-
} elseif ($currentState === Order::STATE_PROCESSING
42-
&& (!$order->canShip() ||
43-
($this->isPartiallyRefundedOrderShipped($order) && !$this->hasPendingShipmentItems($order)))
44-
) {
45-
$order->setState(Order::STATE_COMPLETE)
46-
->setStatus($order->getConfig()->getStateDefaultStatus(Order::STATE_COMPLETE));
47-
} elseif ($order->getIsVirtual() && $order->getStatus() === Order::STATE_CLOSED) {
48-
$order->setState(Order::STATE_CLOSED);
49-
}
35+
if ($this->canBeClosedStatus($order, $currentState)) {
36+
$order->setState(Order::STATE_CLOSED)
37+
->setStatus($order->getConfig()->getStateDefaultStatus(Order::STATE_CLOSED));
38+
return $this;
39+
}
40+
41+
if ($this->canBeCompleteStatus($order, $currentState)) {
42+
$order->setState(Order::STATE_COMPLETE)
43+
->setStatus($order->getConfig()->getStateDefaultStatus(Order::STATE_COMPLETE));
44+
return $this;
5045
}
46+
5147
return $this;
5248
}
5349

5450
/**
55-
* Check if all items are remaining items after partially refunded are shipped
51+
* Check if order can be automatically switched to complete status
5652
*
5753
* @param Order $order
54+
* @param string $currentState
5855
* @return bool
5956
*/
60-
public function isPartiallyRefundedOrderShipped(Order $order): bool
57+
private function canBeCompleteStatus(Order $order, string $currentState): bool
6158
{
62-
$isPartiallyRefundedOrderShipped = false;
63-
if ($this->getShippedItems($order) > 0
64-
&& $order->getTotalQtyOrdered() <= $this->getRefundedItems($order) + $this->getShippedItems($order)) {
65-
$isPartiallyRefundedOrderShipped = true;
59+
if ($currentState === Order::STATE_PROCESSING && !$order->canShip()) {
60+
return true;
6661
}
6762

68-
return $isPartiallyRefundedOrderShipped;
63+
return false;
6964
}
7065

7166
/**
72-
* Check if order has items that haven't been shipped yet
67+
* Check if order can be automatically switched to closed status
7368
*
7469
* @param Order $order
70+
* @param string $currentState
7571
* @return bool
7672
*/
77-
private function hasPendingShipmentItems(Order $order): bool
73+
private function canBeClosedStatus(Order $order, string $currentState): bool
7874
{
79-
foreach ($order->getAllItems() as $item) {
80-
if ($item->canShip()) {
81-
return true;
82-
}
75+
if (in_array($currentState, [Order::STATE_PROCESSING, Order::STATE_COMPLETE])
76+
&& !$order->canCreditmemo()
77+
&& !$order->canShip()
78+
&& $order->getIsNotVirtual()
79+
) {
80+
return true;
8381
}
8482

8583
return false;
8684
}
8785

8886
/**
89-
* Get all refunded items number
87+
* Check if order can be automatically switched to processing status
9088
*
9189
* @param Order $order
92-
* @return int
90+
* @param string $currentState
91+
* @return bool
9392
*/
94-
private function getRefundedItems(Order $order): int
93+
private function canBeProcessingStatus(Order $order, string $currentState): bool
9594
{
96-
$numOfRefundedItems = 0;
97-
foreach ($order->getAllItems() as $item) {
98-
if ($item->getProductType() == 'simple') {
99-
$numOfRefundedItems += (int)$item->getQtyRefunded();
100-
}
95+
if ($currentState == Order::STATE_NEW && $order->getIsInProcess()) {
96+
return true;
10197
}
102-
return $numOfRefundedItems;
103-
}
10498

105-
/**
106-
* Get all shipped items number
107-
*
108-
* @param Order $order
109-
* @return int
110-
*/
111-
private function getShippedItems(Order $order): int
112-
{
113-
$numOfShippedItems = 0;
114-
foreach ($order->getAllItems() as $item) {
115-
$numOfShippedItems += (int)$item->getQtyShipped();
116-
}
117-
return $numOfShippedItems;
99+
return false;
118100
}
119101
}

0 commit comments

Comments
 (0)