Magento 2:无需取消订单,如何编辑现有订单?
Hey there, let's break down how to edit existing Magento 2 orders without having to cancel them—covering all the scenarios you mentioned: updating item prices, adjusting quantities, canceling line items, and adding new products. First, let's fix and expand the code you shared for adding new items, then walk through each of the other use cases.
1. Adding New Products to an Existing Order
Your initial code was on the right track, but we need to fill in the missing attributes and sync the quote changes back to the order properly. Also, a quick heads-up: using ObjectManager directly works for testing, but always use dependency injection in production code to follow Magento's best practices.
Here's the completed code:
// Initialize ObjectManager (replace with DI in production) $objectManager = \Magento\Framework\App\ObjectManager::getInstance(); $orderId = 3445; $order = $objectManager->create('Magento\Sales\Model\Order')->load($orderId); $quote = $objectManager->create('Magento\Quote\Model\Quote')->load($order->getQuoteId()); // Load the product you want to add $productId = 4272; $productQty = 2; $product = $objectManager->create('Magento\Catalog\Model\Product')->load($productId); // Create and configure the quote item $quoteItem = $objectManager->create('Magento\Quote\Model\Quote\Item') ->setProduct($product) ->setQuote($quote) ->setQty($productQty) ->setStoreId($order->getStoreId()) ->setPrice($product->getFinalPrice()) // Override this for custom pricing ->setBasePrice($product->getFinalPrice()) ->setRowTotal($product->getFinalPrice() * $productQty) ->setBaseRowTotal($product->getFinalPrice() * $productQty); // Add the item to the quote and recalculate totals $quote->addItem($quoteItem); $quote->collectTotals()->save(); // Sync quote totals to the order $order->setQuoteId($quote->getId()) ->setSubtotal($quote->getSubtotal()) ->setBaseSubtotal($quote->getBaseSubtotal()) ->setGrandTotal($quote->getGrandTotal()) ->setBaseGrandTotal($quote->getBaseGrandTotal()); // Create the corresponding order item $orderItem = $objectManager->create('Magento\Sales\Model\Order\Item') ->setOrder($order) ->setProductId($productId) ->setName($product->getName()) ->setSku($product->getSku()) ->setQtyOrdered($productQty) ->setPrice($product->getFinalPrice()) ->setBasePrice($product->getFinalPrice()) ->setRowTotal($product->getFinalPrice() * $productQty) ->setBaseRowTotal($product->getFinalPrice() * $productQty); // Add the item to the order and save $order->addItem($orderItem); $order->collectTotals()->save();
2. Updating Existing Order Item Prices
To change the price of an existing line item, locate the item by ID, update its price attributes, then recalculate the order totals:
$orderId = 3445; $targetItemId = 123; // Replace with your order item ID $newItemPrice = 99.99; // Your desired new price $objectManager = \Magento\Framework\App\ObjectManager::getInstance(); $order = $objectManager->create('Magento\Sales\Model\Order')->load($orderId); foreach ($order->getAllItems() as $item) { if ($item->getId() === $targetItemId) { $item->setPrice($newItemPrice) ->setBasePrice($newItemPrice) ->setRowTotal($newItemPrice * $item->getQtyOrdered()) ->setBaseRowTotal($newItemPrice * $item->getQtyOrdered()) ->save(); break; } } // Recalculate and save the updated order totals $order->collectTotals()->save();
3. Adjusting Order Item Quantity (Increase/Decrease)
Increase Quantity
Add to the existing quantity of an item and update the order totals:
$orderId = 3445; $targetItemId = 123; $additionalQty = 1; // Number of units to add $objectManager = \Magento\Framework\App\ObjectManager::getInstance(); $order = $objectManager->create('Magento\Sales\Model\Order')->load($orderId); foreach ($order->getAllItems() as $item) { if ($item->getId() === $targetItemId) { $newQty = $item->getQtyOrdered() + $additionalQty; $item->setQtyOrdered($newQty) ->setRowTotal($item->getPrice() * $newQty) ->setBaseRowTotal($item->getBasePrice() * $newQty) ->save(); break; } } $order->collectTotals()->save();
Decrease Quantity
Reduce the quantity (ensuring it doesn't drop below 0) and optionally restock the product:
$orderId = 3445; $targetItemId = 123; $reduceQtyBy = 1; // Number of units to remove $objectManager = \Magento\Framework\App\ObjectManager::getInstance(); $order = $objectManager->create('Magento\Sales\Model\Order')->load($orderId); foreach ($order->getAllItems() as $item) { if ($item->getId() === $targetItemId) { $currentQty = $item->getQtyOrdered(); $newQty = max(0, $currentQty - $reduceQtyBy); $item->setQtyOrdered($newQty) ->setRowTotal($item->getPrice() * $newQty) ->setBaseRowTotal($item->getBasePrice() * $newQty) ->save(); // Restock the product (optional but recommended) $stockRegistry = $objectManager->create('Magento\CatalogInventory\Model\StockRegistry'); $stockItem = $stockRegistry->getStockItem($item->getProductId()); $stockItem->setQty($stockItem->getQty() + $reduceQtyBy); $stockItem->save(); break; } } $order->collectTotals()->save();
4. Canceling an Order Item
To cancel a line item, set its ordered quantity to 0, mark it as canceled, and restock the product:
$orderId = 3445; $itemIdToCancel = 123; $objectManager = \Magento\Framework\App\ObjectManager::getInstance(); $order = $objectManager->create('Magento\Sales\Model\Order')->load($orderId); foreach ($order->getAllItems() as $item) { if ($item->getId() === $itemIdToCancel) { $originalQty = $item->getQtyOrdered(); $item->setQtyOrdered(0) ->setQtyCanceled($originalQty) ->setRowTotal(0) ->setBaseRowTotal(0) ->save(); // Restock the canceled quantity $stockRegistry = $objectManager->create('Magento\CatalogInventory\Model\StockRegistry'); $stockItem = $stockRegistry->getStockItem($item->getProductId()); $stockItem->setQty($stockItem->getQty() + $originalQty); $stockItem->save(); break; } } $order->collectTotals()->save();
Key Notes to Remember
- Order State: Only edit orders in states that allow modifications (like
pendingorprocessing). Avoid editingcompletedorshippedorders unless you're certain it won't cause inventory or accounting issues. - Dependency Injection: Replace
ObjectManagerwith constructor injection in production. For example, injectMagento\Sales\Model\OrderFactoryinstead of instantiating it directly. - Taxes & Discounts: The code handles basic totals, but if your store uses complex taxes, discounts, or promotions, you may need to adjust the logic to reapply these correctly.
- Testing: Always test these changes in a staging environment first to avoid disrupting live orders.
内容的提问来源于stack exchange,提问作者Pankaj Pareek

