Skip to content

Commit cc0ec25

Browse files
Merge pull request #10206 from magento-gl/spartans_pr_11112025
[Spartans] BugFixes Delivery
2 parents d9a883c + 74466ba commit cc0ec25

File tree

7 files changed

+1631
-404
lines changed

7 files changed

+1631
-404
lines changed

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

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
use Magento\Sales\Model\Order;
1010
use Magento\Sales\Model\Order\Invoice;
11+
use Magento\Catalog\Model\Product\Type;
12+
use Magento\Catalog\Model\Product\Type\AbstractType;
1113

1214
/**
1315
* Checking order status and adjusting order status before saving
@@ -60,7 +62,9 @@ public function check(Order $order)
6062
*/
6163
private function checkForCompleteState(Order $order, ?string $currentState): bool
6264
{
63-
if ($currentState === Order::STATE_PROCESSING && !$order->canShip()) {
65+
if ($currentState === Order::STATE_PROCESSING
66+
&& (!$order->canShip() || $this->areAllItemsFulfilled($order))
67+
) {
6468
return true;
6569
}
6670

@@ -96,7 +100,7 @@ private function checkForClosedState(Order $order, ?string $currentState): bool
96100
{
97101
if (in_array($currentState, [Order::STATE_PROCESSING, Order::STATE_COMPLETE])
98102
&& !$order->canCreditmemo()
99-
&& !$order->canShip()
103+
&& (!$order->canShip() || $this->areAllItemsFulfilled($order))
100104
&& $order->getIsNotVirtual()
101105
) {
102106
return true;
@@ -109,6 +113,49 @@ private function checkForClosedState(Order $order, ?string $currentState): bool
109113
return false;
110114
}
111115

116+
/**
117+
* Determine whether all shippable items have been fulfilled by shipment, refund, or cancellation.
118+
*
119+
* @param Order $order
120+
* @return bool
121+
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
122+
*/
123+
private function areAllItemsFulfilled(Order $order): bool
124+
{
125+
foreach ($order->getAllItems() as $item) {
126+
if ($item->getIsVirtual() || $item->getLockedDoShip()) {
127+
continue;
128+
}
129+
130+
// For bundle shipped together, evaluate fulfillment using the parent only
131+
$parentItem = $item->getParentItem();
132+
if ($parentItem && $parentItem->getProductType() === Type::TYPE_BUNDLE) {
133+
$parentProduct = $parentItem->getProduct();
134+
if ($parentProduct && $parentProduct->getShipmentType() == AbstractType::SHIPMENT_TOGETHER) {
135+
continue;
136+
}
137+
}
138+
139+
$subject = $parentItem && $parentItem->getProductType() === Type::TYPE_BUNDLE
140+
&& $parentItem->getProduct()
141+
&& $parentItem->getProduct()->getShipmentType() == AbstractType::SHIPMENT_TOGETHER
142+
? $parentItem
143+
: $item;
144+
145+
$qtyOrdered = (int) $subject->getQtyOrdered();
146+
$qtyCanceled = (int) $subject->getQtyCanceled();
147+
$qtyShipped = (int) $subject->getQtyShipped();
148+
$qtyRefunded = (int) $subject->getQtyRefunded();
149+
150+
$openQty = $qtyOrdered - $qtyCanceled - $qtyShipped - $qtyRefunded;
151+
if ($openQty > 0) {
152+
return false;
153+
}
154+
}
155+
156+
return true;
157+
}
158+
112159
/**
113160
* Check if order can be automatically switched to processing state
114161
*

0 commit comments

Comments
 (0)