Skip to content
intermediate Phase 49 · Order Lifecycle

Quote to Order Conversion

Order creation from quote, order items, order totals, and the conversion process

45m
0 problems
Topic Progress 0%

Conversion Process

Quote to Order Converter

namespace Magento\Quote\Model\Quote\Order\Convert;

class Converter
{
    public function __construct(
        private \Magento\Sales\Api\OrderRepositoryInterface $orderRepository,
        private \Magento\Sales\Api\Data\OrderItemInterfaceFactory $orderItemFactory,
        private \Magento\Quote\Api\Data\CartInterface $quote
    ) {}

    public function convert(
        \Magento\Quote\Api\Data\CartInterface $quote
    ): \Magento\Sales\Api\Data\OrderInterface {
        // Create order from quote data
        $order = $this->orderFactory->create();

        // Transfer quote data to order
        $order->setEntityId($quote->getId());
        $order->setIncrementId($quote->getReservedOrderId());
        $order->setStoreId($quote->getStoreId());
        $order->setCustomerId($quote->getCustomerId());
        $order->setCustomerEmail($quote->getCustomerEmail());
        $order->setCustomerGroupId($quote->getCustomerGroupId());

        // Transfer addresses
        $order->setBillingAddress($this->convertAddress($quote->getBillingAddress()));
        $order->setShippingAddress($this->convertAddress($quote->getShippingAddress()));

        // Transfer payment
        $order->setPayment($this->convertPayment($quote->getPayment()));

        // Convert items
        foreach ($quote->getItems() as $quoteItem) {
            $orderItem = $this->convertItem($quoteItem);
            $order->addItem($orderItem);
        }

        // Transfer totals
        $this->transferTotals($quote, $order);

        return $order;
    }
}

Order Item Mapping

Quote Item to Order Item

private function convertItem(
    \Magento\Quote\Api\Data\CartItemInterface $quoteItem
): \Magento\Sales\Api\Data\OrderItemInterface {
    $orderItem = $this->orderItemFactory->create();

    // Map quote item fields to order item
    $orderItem->setProductId($quoteItem->getProductId());
    $orderItem->setSku($quoteItem->getSku());
    $orderItem->setName($quoteItem->getName());
    $orderItem->setQtyOrdered($quoteItem->getQty());
    $orderItem->setPrice($quoteItem->getPrice());
    $orderItem->setBasePrice($quoteItem->getBasePrice());
    $orderItem->setRowTotal($quoteItem->getRowTotal());
    $orderItem->setBaseRowTotal($quoteItem->getBaseRowTotal());
    $orderItem->setDiscountAmount($quoteItem->getDiscountAmount());
    $orderItem->setBaseDiscountAmount($quoteItem->getBaseDiscountAmount());
    $orderItem->setTaxAmount($quoteItem->getTaxAmount());
    $orderItem->setBaseTaxAmount($quoteItem->getBaseTaxAmount());

    // Store quote item ID for reference
    $orderItem->setQuoteItemId($quoteItem->getId());

    // Copy options
    $orderItem->setProductOptions($quoteItem->getProductOptions());

    // Parent item for configurable/bundle
    if ($quoteItem->getParentItem()) {
        $orderItem->setParentItem($this->convertItem($quoteItem->getParentItem()));
    }

    return $orderItem;
}

Item Type Mapping

// Product type handling during conversion
$itemType = $quoteItem->getProductType();

switch ($itemType) {
    case 'configurable':
        // Parent item stores configurable options
        // Children are the actual simple products
        $orderItem->setProductType('configurable');
        break;
    case 'bundle':
        // Bundle options stored on parent
        $orderItem->setProductType('bundle');
        break;
    case 'grouped':
        // Each associated product is a separate item
        $orderItem->setProductType('grouped');
        break;
    default:
        $orderItem->setProductType('simple');
}

Order Totals Transfer

Totals Transfer

private function transferTotals(
    \Magento\Quote\Api\Data\CartInterface $quote,
    \Magento\Sales\Api\Data\OrderInterface $order
): void {
    $shippingAddress = $quote->getShippingAddress();

    // Transfer all totals
    $order->setSubtotal($shippingAddress->getSubtotal());
    $order->setBaseSubtotal($shippingAddress->getBaseSubtotal());
    $order->setDiscountAmount($shippingAddress->getDiscountAmount());
    $order->setBaseDiscountAmount($shippingAddress->getBaseDiscountAmount());
    $order->setShippingAmount($shippingAddress->getShippingAmount());
    $order->setBaseShippingAmount($shippingAddress->getBaseShippingAmount());
    $order->setTaxAmount($shippingAddress->getTaxAmount());
    $order->setBaseTaxAmount($shippingAddress->getBaseTaxAmount());
    $order->setGrandTotal($shippingAddress->getGrandTotal());
    $order->setBaseGrandTotal($shippingAddress->getBaseGrandTotal());

    // Shipping details
    $order->setShippingDescription($shippingAddress->getShippingDescription());
    $order->setShippingMethod($shippingAddress->getShippingMethod());

    // Coupon information
    $order->setCouponCode($quote->getCouponCode());
    $order->setDiscountDescription($shippingAddress->getDiscountDescription());
}

Currency Handling

// Base vs store currency
$order->setCurrencyCode($quote->getStore()->getCurrentCurrencyCode());
$order->setBaseCurrencyCode($quote->getStore()->getBaseCurrencyCode());
$order->setStoreCurrencyCode($quote->getStore()->getBaseCurrencyCode());

// Exchange rate
$order->setExchangeRate($quote->getStore()->convertPrice(1));

Conversion Errors and Rollback

Error Handling

try {
    $order = $this->converter->convert($quote);
    $this->orderRepository->save($order);
} catch (\Exception $e) {
    // Rollback: restore inventory
    $this->inventoryReservation->cancelReservation($order);

    // Log error
    $this->logger->critical('Quote to order conversion failed:', [
        'quote_id' => $quote->getId(),
        'error' => $e->getMessage(),
        'trace' => $e->getTraceAsString(),
    ]);

    // Restore quote state
    $quote->setIsActive(1);
    $quote->save();

    throw new \Magento\Framework\Exception\LocalizedException(
        __('Order placement failed. Please try again.')
    );
}

Quote State After Conversion

// After successful conversion
$quote->setIsActive(0);
$quote->setReservedOrderId(null);
$quote->save();

// Quote is kept for reference but marked inactive
// Can be used for reorder or history

Quiz

1. What data transfers from quote to order?

Question 1 options

2. What happens to the quote after conversion?

Question 2 options

3. How are configurable product options preserved?

Question 3 options

Flashcards

Question

Quote to order conversion steps?

Answer

Create order → transfer data → convert items → transfer totals → save

Question

What preserves product options?

Answer

Order item product_options field stores configurable/bundle options

Question

Quote state after conversion?

Answer

is_active = 0 (inactive), reserved_order_id cleared

Question

Conversion failure handling?

Answer

Rollback inventory, restore quote, throw exception

Revision Notes

Key Takeaways

  • 1. Quote to order transfers all data: items, addresses, payment, totals
  • 2. Each quote item maps to an order item with full price/tax details
  • 3. Totals include subtotal, discounts, shipping, tax, and grand total
  • 4. Quote is deactivated after successful conversion but retained
  • 5. Conversion errors trigger inventory rollback and quote restoration

Interview Tips

  • Describe the complete data flow from quote to order
  • Explain how configurable options are preserved in order items
  • Discuss error handling and rollback strategies during conversion

Cheat Sheet

Quote → Order Conversion:
  Order data: store, customer, addresses
  Items: product_id, sku, qty, price, options
  Totals: subtotal, discount, shipping, tax, grand

Post-Conversion:
  Quote: is_active=0, reserved_order_id=null
  Order: created with incremented ID

Error Handling:
  Inventory rollback + quote restoration