Preconditions and environment
- Magento version: 2.4.8-p5
- PHP 8.3
Steps to reproduce
- Add a configurable product to the cart.
- Trigger a second
Quote::updateItem($parentItemId, $buyRequest) call for that
same item — in our case this happened naturally via CartItemPersister::save()
(triggered by a third-party module setting a custom price on a cart item, which
causes Magento core to re-save/re-sync the quote a few seconds after the initial
add), but any caller that invokes updateItem() a second time on an already
fully-populated configurable item with a changed buyRequest can trigger it.
- Place the order.
Expected result
Exactly one parent + one child quote/order item for the configurable product.
Actual result
Two child items exist under the same parent, one of them with no price. Credit
memo creation later fails on that order with "invalid quantity to refund".
Additional information
Quote::updateItem() merges duplicate parent items after calling addProduct()
(see the if ($resultItem->getId() != $itemId) block), but it never checks for
duplicate child items. If addProduct()'s internal getItemByProduct() fails to
match an already-existing child against the freshly regenerated candidate — which
can happen when the parent's stored buyRequest/options differ slightly from the
state at initial add-to-cart — a second child quote item is created under the same
parent instead of the existing one being reused.
This results in an order with two child items for the same configurable product
(one legitimate, one with price/row_total = 0/NULL), and later breaks credit memo
creation with "We found an invalid quantity to refund item ...".
Release note
No response
Triage and priority
Preconditions and environment
Steps to reproduce
Quote::updateItem($parentItemId, $buyRequest)call for thatsame item — in our case this happened naturally via
CartItemPersister::save()(triggered by a third-party module setting a custom price on a cart item, which
causes Magento core to re-save/re-sync the quote a few seconds after the initial
add), but any caller that invokes
updateItem()a second time on an alreadyfully-populated configurable item with a changed buyRequest can trigger it.
Expected result
Exactly one parent + one child quote/order item for the configurable product.
Actual result
Two child items exist under the same parent, one of them with no price. Credit
memo creation later fails on that order with "invalid quantity to refund".
Additional information
Quote::updateItem()merges duplicate parent items after callingaddProduct()(see the
if ($resultItem->getId() != $itemId)block), but it never checks forduplicate child items. If
addProduct()'s internalgetItemByProduct()fails tomatch an already-existing child against the freshly regenerated candidate — which
can happen when the parent's stored buyRequest/options differ slightly from the
state at initial add-to-cart — a second child quote item is created under the same
parent instead of the existing one being reused.
This results in an order with two child items for the same configurable product
(one legitimate, one with price/row_total = 0/NULL), and later breaks credit memo
creation with "We found an invalid quantity to refund item ...".
Release note
No response
Triage and priority