Magento Quotes

A quote in Magento is effectively the contents of a customer’s cart before it is converted into an order.

The quote model class used for Magento quotes is the Mage_Sales_Model_Quote class.

Quotes are saved in Magento in the sales_flat_quote table and are first saved when the customer adds their first product to the cart.

The first method that gets executed when a product gets added to the cart is the addAction() method in the Mage_Checkout_CartController controller class file. The key line to look at here is the $cart->save() line.

public function addAction()
{
    if (!$this->_validateFormKey()) {
        $this->_goBack();
        return;
    }
    $cart   = $this->_getCart();
    $params = $this->getRequest()->getParams();
    try {
        if (isset($params['qty'])) {
            $filter = new Zend_Filter_LocalizedToNormalized(
                array('locale' => Mage::app()->getLocale()->getLocaleCode())
            );
            $params['qty'] = $filter->filter($params['qty']);
        }

        $product = $this->_initProduct();
        $related = $this->getRequest()->getParam('related_product');

        /**
         * Check product availability
         */
        if (!$product) {
            $this->_goBack();
            return;
        }

        $cart->addProduct($product, $params);
        if (!empty($related)) {
            $cart->addProductsByIds(explode(',', $related));
        }

        $cart->save();
        ....
}

The save() method exists in the Mage_Checkout_Model_Cart class.

public function save()
{
    Mage::dispatchEvent('checkout_cart_save_before', array('cart'=>$this));

    $this->getQuote()->getBillingAddress();
    $this->getQuote()->getShippingAddress()->setCollectShippingRates(true);
    $this->getQuote()->collectTotals();
    $this->getQuote()->save();
    $this->getCheckoutSession()->setQuoteId($this->getQuote()->getId());
    /**
     * Cart save usually called after changes with cart items.
     */
    Mage::dispatchEvent('checkout_cart_save_after', array('cart'=>$this));
    return $this;
}

The line $this->getQuote()->save() will save the quote to the database.

The sales_flat_quote table contains generic information about the quote, and has columns like is_active, items_count and created_at.

By default, if you’re not a registered customer, Magento will also save null address information in the sales_flat_quote_address table. There will be two rows created; one for the billing address and one for the shipping address. These get populated later on when at the checkout page.

If you are a registered customer, the addresses used are those that are saved as the ‘default’ addresses.

The item information get saved in the table sales_flat_quote_item. If the items have options, these get saved in sales_flat_quote_item_option. Some of these options include:

– Custom options within the Custom Options tab of the product add/edit screen.
– Configurable product options.

The info_buyRequest column contains data that is transferred to the field product_options in the sales_flat_order_item table.

When you head to the checkout page, a new row will be created in sales_flat_quote_payment that will eventually hold your payment data when you complete the payment step.

When we save our billing and shipping address information at the checkout stage, the saveBilling() and saveShipping() methods are called respectively. These methods both belong in the Mage_Checkout_Model_Onepage class and are executed in the saveBillingAction() and saveShippingAction() methods of the OnepageController.php file.

There are also two other quote tables. The sales_flat_quote_shipping_rate.

All available shipping methods on the checkout page get populated when on the method step

When you choose the method, the method saves against the the shipping_method column in the quote address table.

The quote will eventually get converted into an order when you click Place Order on the checkout page. The steps are as follows.

Within the submitOrder() method of Mage_Sales_Model_Service_Quote, the billing and shipping addresses are converted first.

$order->setBillingAddress($this->_convertor->addressToOrderAddress($quote->getBillingAddress()));
if ($quote->getBillingAddress()->getCustomerAddress()) {
    $order->getBillingAddress()->setCustomerAddress($quote->getBillingAddress()->getCustomerAddress());
}
if (!$isVirtual) {
    $order->setShippingAddress($this->_convertor->addressToOrderAddress($quote->getShippingAddress()));
    if ($quote->getShippingAddress()->getCustomerAddress()) {
        $order->getShippingAddress()->setCustomerAddress($quote->getShippingAddress()->getCustomerAddress());
    }
}

The conversion of quote data to order data takes place in Mage_Sales_Model_Convert_Quote.

public function addressToOrderAddress(Mage_Sales_Model_Quote_Address $address)
{
    $orderAddress = Mage::getModel('sales/order_address')
        ->setStoreId($address->getStoreId())
        ->setAddressType($address->getAddressType())
        ->setCustomerId($address->getCustomerId())
        ->setCustomerAddressId($address->getCustomerAddressId());

    Mage::helper('core')->copyFieldset('sales_convert_quote_address', 'to_order_address', $address, $orderAddress);

    Mage::dispatchEvent('sales_convert_quote_address_to_order_address',
        array('address' => $address, 'order_address' => $orderAddress));

    return $orderAddress;
}

The method copyFieldset() method belongs in Mage_Core_Helper_Data.

public function copyFieldset($fieldset, $aspect, $source, $target, $root='global')
{
    if (!(is_array($source) || $source instanceof Varien_Object)
        || !(is_array($target) || $target instanceof Varien_Object)) {

        return false;
    }
    $fields = Mage::getConfig()->getFieldset($fieldset, $root);
    if (!$fields) {
        return false;
    }

    $sourceIsArray = is_array($source);
    $targetIsArray = is_array($target);

    $result = false;
    foreach ($fields as $code=>$node) {
        if (empty($node->$aspect)) {
            continue;
        }

        if ($sourceIsArray) {
            $value = isset($source[$code]) ? $source[$code] : null;
        } else {
            $value = $source->getDataUsingMethod($code);
        }

        $targetCode = (string)$node->$aspect;
        $targetCode = $targetCode == '*' ? $code : $targetCode;

        if ($targetIsArray) {
            $target[$targetCode] = $value;
        } else {
            $target->setDataUsingMethod($targetCode, $value);
        }

        $result = true;
    }

    $eventName = sprintf('core_copy_fieldset_%s_%s', $fieldset, $aspect);
    Mage::dispatchEvent($eventName, array(
        'target' => $target,
        'source' => $source,
        'root'   => $root
    ));

    return $result;
}

The <fieldsets> tag is mainly used to define which fields (attributes) are to be copied to where while converting objects, e.g. in a quote to order conversion.

We save the transaction and set the is_active column value of our quote is sales_flat_quote to 0. If cron is configured correctly, Magento will then clear these inactive quotes from the database after a certain period of time.

public function submitOrder()
{
    ....
    try {
        $transaction->save();
        $this->_inactivateQuote();
        Mage::dispatchEvent('sales_model_service_quote_submit_success', array('order'=>$order, 'quote'=>$quote));
    }
    ....
}

Note: This articles is based on Magento Community/Open Source version 1.9.