Posting in the Magento forums has been disabled pending the implementation of a new and improved forum solution which should better serve the community.

For new questions please post at magento.stackexchange.com, the community-run support site for the Magento community. We will be providing updates on the new forum solution soon. For questions or concerns please email community@magento.com.

Magento Forum

Page 1 of 10
Trouble creating a quote/order through custom code (programmatically for use with API)
 
petemcw
Jr. Member
 
Total Posts:  24
Joined:  2007-08-31
 

I am wondering if anyone has figured out how to create orders programmatically.  I have found a number of threads where people are asking how to do it and it doesn’t seem that anyone has figured this out yet.

For a client, we have to built a system that reads XML “purchase orders” and in turn, create an order within Magento containing details from the XML file.  We are having a heck of a time getting this to work.  I believe we have it narrowed down to the point where we think the following is true:

- first you need to create a quote
- populate the quote
- convert the quote to an order
- place the order

Here is a rough example of some code that will give you an idea of where we’re at:

$checkout Mage::getSingleton('checkout/session');
$quote    $checkout->getQuote();

// look up customer
$customer Mage::getModel('customer/customer')
    ->
setWebsiteId(Mage::app()->getStore()->getWebsiteId())
    ->
loadByEmail($xml->getEmail());

$quote->assignCustomer($customer);
$quote->setIsMultiShipping(false);
$quote->save();

$address     $quote->getShippingAddress();
$address->setCollectShippingRates(true);
$quote->collectTotals()->save();

// set shipping method
$quote->getShippingAddress()->setShippingMethod($method);
$quote->collectTotals()->save();

// set payment
$payment $quote->getPayment();
$payment->importData(array(
    
'method' => 'purchaseorder'
    
'po_number' => $xml->getPoNumber()
));
$quote->getShippingAddress()->setPaymentMethod($payment->getMethod());
$quote->collectTotals()->save();

// add products
$items $xml->getLines();
foreach (
$items as $item{
    $collection 
Mage::getModel('catalog/product')->getCollection()
        ->
setStoreId(Mage::app()->getStore()->getId())
        ->
addAttributeToFilter(array(
        array(
'attribute' => 'part_number1''like' => $item->getPartNumber()),
        ));
    
$quote->addProduct($collection->getFirstItem());
}

// save quote
$quote->reserveOrderId();
$convertQuote Mage::getModel('sales/convert_quote');
$order $convertQuote->addressToOrder($quote->getShippingAddress());
$order->setBillingAddress($convertQuote->addressToOrderAddress($quote->getBillingAddress()));
$order->setShippingAddress($convertQuote->addressToOrderAddress($quote->getShippingAddress()));
$order->setPayment($convertQuote->paymentToOrderPayment($quote->getPayment()));
foreach (
$quote->getAllItems() as $item{
    $orderItem 
$convertQuote->itemToOrderItem($item);
    if (
$item->getParentItem()) {
        $orderItem
->setParentItem($order->getItemByQuoteItemId($item->getParentItem()->getId()));
    
}
    $order
->addItem($orderItem);
}
$order
->place();
$order->sendNewOrderEmail();
$quote->setIsActive(false);
$quote->save();

The code above is basically run via a controller action for now and validate and other checks have all been stripped out (get it working simple first, right?).  The code above actually runs - doesn’t through any exceptions or errors - yet NO ORDER!

Anyone want to take a stab and helping to figure this out until Magento gets this functionality via their API?  Anyone from Varien/Magento that could take a few minutes to help us out?  Thanks so much!

 
Magento Community Magento Community
Magento Community
Magento Community
 
smarteeformee
Jr. Member
 
Total Posts:  4
Joined:  2008-08-05
 

Hi there,

I am about to attempt to create a similar script.  We are working a subscription feature and need to run a cron job each month to create orders automatically. 

Curious, what version of Magento are you writing this code?

Also, since last week, have you made any progress on getting orders successfully created?

Thanks!
Candace Lapp
Spitball Media (http://www.spitballmedia.com)

 
Magento Community Magento Community
Magento Community
Magento Community
 
petemcw
Jr. Member
 
Total Posts:  24
Joined:  2007-08-31
 
smarteeformee - 22 January 2009 01:39 PM

Also, since last week, have you made any progress on getting orders successfully created?

Actually, I am further along. I had a half written post ready but haven’t gotten back to it - so you gave me an excuse to post it. I’m pretty sure our end result is not the best way to get this done, as it involves basically copying an admin controller and making your adjustments to it.  It also has to be called via web request and isn’t a model-based solution. It would have been preferable to figure this out in the model so that it could be instantiated and run from a variety of sources, but due to our time constraints couldn’t make it happen.

We started out by creating a custom module to hold all our code. Create a frontend controller that will intercept your web request and activate the order creation process. We made an obscure controller/action name for this process to be called from (Ex: /module/controller/action).  In our case, the end result was a request that scanned a directory for XML purchase orders and placed actual Magento orders from the data within each XML file.

To get this working, I followed the process of creating an order from the administration area.  We modeled our solution after this class:

Mage_Adminhtml_Sales_Order_CreateController

The basics of this approach go like this:
- Do some initialization
- Create an array with all your order information populated
- For each “order” in your array, run it through a “process” function
- The process function creates a new “adminhtml/sales_order_create” singleton
- Populate the singleton with your array data
- Create an order via the singleton model

Here is an trimmed down version of our controller:

<?php
class Yournamespace_Yourmodule_IndexController extends Mage_Core_Controller_Front_Action
{
    
/**
     * Retrieve order create model
     *
     * @return  Mage_Adminhtml_Model_Sales_Order_Create
     */
    
protected function _getOrderCreateModel()
    
{
        
return Mage::getSingleton('adminhtml/sales_order_create');
    
}

    
/**
     * Retrieve session object
     *
     * @return  Mage_Adminhtml_Model_Session_Quote
     */
    
protected function _getSession()
    
{
        
return Mage::getSingleton('adminhtml/session_quote');
    
}

    
/**
     * Initialize order creation session data
     *
     * @param   array $data
     * @return  Mage_Adminhtml_Sales_Order_CreateController
     */
    
protected function _initSession($data)
    
{
        
/**
         * Identify customer
         */
        
if (!empty($data['customer_id'])) {
            $this
->_getSession()->setCustomerId((int) $data['customer_id']);
        
}

        
/**
         * Identify store
         */
        
if (!empty($data['store_id'])) {
            $this
->_getSession()->setStoreId((int) $data['store_id']);
        
}

        
return $this;
    
}

    
/**
     * This action would normally be called by cron.
     *
     * @return  void
     */
    
public function processAction()
    
{
        
/* @var $collection Yournamespace_Yourmodule_Model_Yourmodel_Collection */
        
$collection Mage::getModel('yourmodule/yourmodel')->getCollection();

        foreach(
$collection as $object{
            
/* @var $object Yournamespace_Yourmodule_Model_Yourmodel */
            
$orderData $object->getOrderDataArray();

            if (!empty(
$orderData)) {
                
// we have valid order data
                
$this->_initSession($orderData['session']);
                try 
{
                    $this
->_processQuote($orderData);
                    if (!empty(
$orderData['payment'])) {
                        $this
->_getOrderCreateModel()->setPaymentData($orderData['payment']);
                        
$this->_getOrderCreateModel()->getQuote()->getPayment()->addData($orderData['payment']);
                    
}

                    $this
->_getOrderCreateModel()
                         ->
importPostData($orderData['order'])
                         ->
createOrder();

                    
$this->_getSession()->clear();
                    
Mage::unregister('rule_data');
                    
Mage::log('Order Successfull'Zend_Log::INFO);
                
}
                
catch (Exception $e){
                    Mage
::log(sprintf('Order saving error: %s'$e->getMessage()), Zend_Log::ERR);
                
}
            }
        }
    }

    
/**
     * Processing quote data
     *
     * @param   array $data
     * @return  Yournamespace_Yourmodule_IndexController
     */
    
protected function _processQuote($data = array())
    
{
        
/**
         * Saving order data
         */
        
if (!empty($data['order'])) {
            $this
->_getOrderCreateModel()->importPostData($data['order']);
        
}

        
/**
         * init first billing address, need for virtual products
         */
        
$this->_getOrderCreateModel()->getBillingAddress();
        
$this->_getOrderCreateModel()->setShippingAsBilling(true);

        
/**
         * Adding products to quote from special grid and
         */
        
if (!empty($data['add_products'])) {
            $this
->_getOrderCreateModel()->addProducts($data['add_products']);
        
}

        
/**
         * Collecting shipping rates
         */
        
$this->_getOrderCreateModel()->collectShippingRates();

        
/**
         * Adding payment data
         */
        
if (!empty($data['payment'])) {
            $this
->_getOrderCreateModel()->getQuote()->getPayment()->addData($data['payment']);
        
}

        $this
->_getOrderCreateModel()
             ->
initRuleData()
             ->
saveQuote();

        if (!empty(
$data['payment'])) {
            $this
->_getOrderCreateModel()->getQuote()->getPayment()->addData($data['payment']);
        
}

        
return $this;
    
}
}
 
Magento Community Magento Community
Magento Community
Magento Community
 
petemcw
Jr. Member
 
Total Posts:  24
Joined:  2007-08-31
 

The most important part we found to the solution above is the format of the $orderData array above (around line 61 of the controller code above).

If the data in the array is formatted correctly, the Mage_Adminhtml_Model_Sales_Order_Create::importPostData() method will correctly create an order.

Here is an example of the format the array should be in (as in it works for us!):

$orderData = array(
    
'session'       => array(
        
'customer_id'   => $customer->getId(),
        
'store_id'      => Mage::app()->getStore()->getStoreId(),
    ),
    
'payment'       => array(
        
'method'    => 'purchaseorder',
        
'po_number' => (string) $this->getPoNumber(),
    ),
    
// 123456 denotes the product's ID value
    
'add_products'  =>array(
        
'123456'    => array('qty' => 1),
        
'987654'    => array('qty' => 2),
    ),
    
'order'         => array(
        
'currency'  => 'USD',
        
'account'   => array(
            
'group_id'  => $customer->getGroupId(),
            
'email'     => (string) $this->getEmail(),
        ),
        
'comment'           => array('customer_note' => 'API ORDER'),
        
'send_confirmation' => 1,
        
'shipping_method'   => 'flatrate_flatrate',
        
'billing_address'   => array(
            
'customer_address_id' => $this->getCustomer()->getDefaultBillingAddress()->getEntityId(),
            
'prefix'             => $this->getCustomer()->getDefaultBillingAddress()->getPrefix(),
            
'firstname'           => $this->getCustomer()->getDefaultBillingAddress()->getFirstname(),
            
'middlename'          => $this->getCustomer()->getDefaultBillingAddress()->getMiddlename(),
            
'lastname'            => $this->getCustomer()->getDefaultBillingAddress()->getLastname(),
            
'suffix'             => $this->getCustomer()->getDefaultBillingAddress()->getSuffix(),
            
'company'              => $this->getCustomer()->getDefaultBillingAddress()->getCompany(),
            
'street'               => $this->getCustomer()->getDefaultBillingAddress()->getStreet(),
            
'city'                   => $this->getCustomer()->getDefaultBillingAddress()->getCity(),
            
'country_id'           => $this->getCustomer()->getDefaultBillingAddress()->getCountryId(),
            
'region'               => $this->getCustomer()->getDefaultBillingAddress()->getRegion(),
            
'region_id'           => $this->getCustomer()->getDefaultBillingAddress()->getRegionId(),
            
'postcode'               => $this->getCustomer()->getDefaultBillingAddress()->getPostcode(),
            
'telephone'           => $this->getCustomer()->getDefaultBillingAddress()->getTelephone(),
            
'fax'                   => $this->getCustomer()->getDefaultBillingAddress()->getFax(),
        ),
        
'shipping_address'  => array(
            
'customer_address_id' => $this->getCustomer()->getDefaultShippingAddress()->getEntityId(),
            
'prefix'               => $this->getCustomer()->getDefaultShippingAddress()->getPrefix(),
            
'firstname'           => $this->getCustomer()->getDefaultShippingAddress()->getFirstname(),
            
'middlename'           => $this->getCustomer()->getDefaultShippingAddress()->getMiddlename(),
            
'lastname'               => $this->getCustomer()->getDefaultShippingAddress()->getLastname(),
            
'suffix'               => $this->getCustomer()->getDefaultShippingAddress()->getSuffix(),
            
'company'               => $this->getCustomer()->getDefaultShippingAddress()->getCompany(),
            
'street'               => $this->getCustomer()->getDefaultShippingAddress()->getStreet(),
            
'city'                   => $this->getCustomer()->getDefaultShippingAddress()->getCity(),
            
'country_id'           => $this->getCustomer()->getDefaultShippingAddress()->getCountryId(),
            
'region'               => $this->getCustomer()->getDefaultShippingAddress()->getRegion(),
            
'region_id'           => $this->getCustomer()->getDefaultShippingAddress()->getRegionId(),
            
'postcode'               => $this->getCustomer()->getDefaultShippingAddress()->getPostcode(),
            
'telephone'           => $this->getCustomer()->getDefaultShippingAddress()->getTelephone(),
            
'fax'                   => $this->getCustomer()->getDefaultShippingAddress()->getFax(),
        ),
    ),
);

It would also be worth noting that our implementation assumes that there is already a customer created, and they have address information entered.  Hopefully this is helpful!

 
Magento Community Magento Community
Magento Community
Magento Community
 
smarteeformee
Jr. Member
 
Total Posts:  4
Joined:  2008-08-05
 

Thanks so much for this!  It will really help us get a head start on solving this problem.  grin

 
Magento Community Magento Community
Magento Community
Magento Community
 
Polo Magento
Jr. Member
 
Total Posts:  21
Joined:  2008-12-11
 

Hi,

i’m trying to import Orders from an old website (very very custom version of oscommerce) to the new one.

Almost suceed. But i have one “pb” : the stock of the item that is updated after the order save :/

Is there any options to not update the stock ?

I know how to deal with this pb, with reupdating the stock, or simply comment the line 107 in cataloginventory/model/stock, but i want to find a “clean” solution.

 
Magento Community Magento Community
Magento Community
Magento Community
 
battarro
Jr. Member
 
Total Posts:  3
Joined:  2009-01-28
 

Do anyone has figure it out yet?

I am trying to create a paid new order from an outside page.
So far i have the following

Mage::app();
    
    
$customer Mage::getModel('customer/customer')
    ->
setWebsiteId(Mage::app()->getStore()->getWebsiteId())
   ->
loadByEmail("customer@email");

    
$quote Mage::getSingleton('sales/quote') ;

    
$quote->assignCustomer($customer);
    
$quote->setIsMultiShipping(false);
    
$customer_billing_address $customer ->getDefaultBillingAddress();
    
$customer_shipping_address $customer ->getDefaultShippingAddress();
    
$quote_billing_address Mage::getModel('sales/quote_address')
                    ->
importCustomerAddress($customer_billing_address);
    
$quote_shipping_address Mage::getModel('sales/quote_address')
                    ->
importCustomerAddress($customer_shipping_address);
    
$quote->setBillingAddress ($quote_billing_address);
    
$quote->setBillingAddress ($quote_shipping_address);
    
    
    
$catalogProduct Mage::getModel('catalog/product')->load(161);    
    
$quote -> addProduct($catalogProduct);
    
$quote -> addMessage('Auto Order Code');
    
$quote->save();

What other steps am i missing?
How can i add a shipping method to the quote or order?
How can i add a paid payment to the order?

Any help will be appreciated

 
Magento Community Magento Community
Magento Community
Magento Community
 
jesse_dev
Jr. Member
 
Total Posts:  27
Joined:  2009-03-03
 

here’s some code for creating an order ‘outside of magento’
for example, i have a custom soap implementation in /soap/
setting a payment method in the quote before you save() seems to cause errors
i’ve been working on this for about a week..

require_once '../app/Mage.php';
    
$app Mage::app();
    
Mage::register('isSecureArea'true);
    
$customerObj=Mage::getModel('customer/customer')->load($AccountNo);
    
$quoteObj=Mage::getModel('sales/quote')->assignCustomer($customerObj);
    
$quoteObj->reserveOrderId();
    
    
$productModel=Mage::getModel('catalog/product');
    foreach(
$PartCart as $part{
        
foreach($part as $k=>$v$$k=$v}
        $productObj
=$productModel->load($PartId);
        
$quoteObj->addProduct($productObj);
    
}
    $quoteObj
->collectTotals();
    
$quoteObj->save();

//$quoteId=$quoteObj->getId();
//$quoteObj = Mage::getModel('sales/quote')->load($quoteId);
//$orderCreateModel=Mage::getSingleton('adminhtml/sales_order_create');

    
$quotePaymentObj=$quoteObj->getPayment();
    
//methods: authorizenet, paypal_express, googlecheckout, purchaseorder
    
$quotePaymentObj->setMethod('purchaseorder');
    
$quoteObj->setPayment($quotePaymentObj);
    
$convertQuoteObj=Mage::getSingleton('sales/convert_quote');
    
$orderObj $convertQuoteObj->addressToOrder($quoteObj->getShippingAddress());
    
$orderPaymentObj=$convertQuoteObj->paymentToOrderPayment($quotePaymentObj);

    
$orderObj->setBillingAddress($convertQuoteObj->addressToOrderAddress($quoteObj->getBillingAddress()));
    
$orderObj->setPayment($convertQuoteObj->paymentToOrderPayment($quoteObj->getPayment()));
    
$orderObj->setShippingAddress($convertQuoteObj->addressToOrderAddress($quoteObj->getShippingAddress()));
    
    
$qty=1;
    foreach (
$quoteObj->getShippingAddress()->getAllItems() as $item{
        
//@var $item Mage_Sales_Model_Quote_Item
        
$item->setQty($qty);
        
$orderItem $convertQuoteObj->itemToOrderItem($item);
        if (
$item->getParentItem()) {
            $orderItem
->setParentItem($orderObj->getItemByQuoteItemId($item->getParentItem()->getId()));
        
}
        $orderObj
->addItem($orderItem);
    
}
    
    $orderObj
->setCanShipPartiallyItem(false);
    
    
$totalDue=$orderObj->getTotalDue();
    echo 
"<p>total due: $totalDue</p>";
    
$orderObj->place(); //calls _placePayment
    
$orderObj->save();
    
$orderId=$orderObj->getId();
    echo 
"<p>orderId: $orderId</p>";
 
Magento Community Magento Community
Magento Community
Magento Community
 
emislive
Jr. Member
 
Total Posts:  3
Joined:  2009-04-03
 

To use jesse_dev’s code inside of Magento (in a module), remove the first three lines. Looks good all in all. Maybe this will make into Magento’s “documentation.”

 
Magento Community Magento Community
Magento Community
Magento Community
 
jesse_dev
Jr. Member
 
Total Posts:  27
Joined:  2009-03-03
 

here’s another set of code, adding items as quote_items rather than products
also the functions are split in 2, one to create the quote and the other converts it to an order
in my system, these are set up as soap functions, so it loads Mage separately in each function.
the top 3 lines in each function could be taken out and called just once at the top of the file

function PrepareOrder($params{
    
require_once '../app/Mage.php';
    
$app Mage::app();
    
Mage::register('isSecureArea'true); //no output before here, will get a session header error
    
foreach($params as $k=>$v$$k=$v}
    
    $customerObj
=Mage::getModel('customer/customer')->load($AccountNo);
    
$storeId=$customerObj->getStoreId();
    
$quoteObj=Mage::getModel('sales/quote')->assignCustomer($customerObj); //sets ship/bill address
    
$storeObj=$quoteObj->getStore()->load($storeId);
    
$quoteObj->setStore($storeObj);
    
$productModel=Mage::getModel('catalog/product');

    foreach(
$PartCart as $part{
        
foreach($part as $k=>$v$$k=$v}
        $productObj
=$productModel->load($PartId);
        
$quoteItem=Mage::getModel('sales/quote_item')->setProduct($productObj);
        
$quoteItem->setQuote($quoteObj);
        
$quoteItem->setQty($Quantity);
        
$quoteObj->addItem($quoteItem);
    
}
  $quoteObj
->collectTotals();
    
$quoteObj->save();
    
$quoteId=$quoteObj->getId();
    return 
$quoteId;
}

function ConfirmOrder($quoteId{
    
require_once '../app/Mage.php';
    
$app Mage::app();
    
Mage::register('isSecureArea'true);
    
$quoteObj=Mage::getModel('sales/quote')->load($quoteId);
    
$items=$quoteObj->getAllItems();
    
$quoteObj->collectTotals();
    
$quoteObj->reserveOrderId();

    
$quotePaymentObj=$quoteObj->getPayment();
    
//methods: authorizenet, paypal_express, googlecheckout, purchaseorder
    
$quotePaymentObj->setMethod('purchaseorder');
    
$quoteObj->setPayment($quotePaymentObj);
    
$convertQuoteObj=Mage::getSingleton('sales/convert_quote');
    
$orderObj $convertQuoteObj->addressToOrder($quoteObj->getShippingAddress());
    
$orderPaymentObj=$convertQuoteObj->paymentToOrderPayment($quotePaymentObj);

    
$orderObj->setBillingAddress($convertQuoteObj->addressToOrderAddress($quoteObj->getBillingAddress()));
    
$orderObj->setShippingAddress($convertQuoteObj->addressToOrderAddress($quoteObj->getShippingAddress()));
    
$orderObj->setPayment($convertQuoteObj->paymentToOrderPayment($quoteObj->getPayment()));
    
    foreach (
$items as $item{
        
//@var $item Mage_Sales_Model_Quote_Item
        
$orderItem $convertQuoteObj->itemToOrderItem($item);
        if (
$item->getParentItem()) {
            $orderItem
->setParentItem($orderObj->getItemByQuoteItemId($item->getParentItem()->getId()));
        
}
        $orderObj
->addItem($orderItem);
    
}
    
    $orderObj
->setCanShipPartiallyItem(false);
    
    
$totalDue=$orderObj->getTotalDue();
    
$orderObj->place(); //calls _placePayment
    
$orderObj->save(); 
    
$orderId=$orderObj->getId();
    
//$orderObj->sendNewOrderEmail(); //*/
    
return $orderId;
    
}
 
Magento Community Magento Community
Magento Community
Magento Community
 
faisal2010
Jr. Member
 
Avatar
Total Posts:  21
Joined:  2009-02-03
Karachi
 

Jesse_dev, Thanks for posting two functions . what will be the value of $params in the PrepareOrder($params) function.

These functions are working working

thanks

 
Magento Community Magento Community
Magento Community
Magento Community
 
Flopy69
Sr. Member
 
Avatar
Total Posts:  124
Joined:  2008-06-04
Lyon
 

can you make zip for this ?

 
Magento Community Magento Community
Magento Community
Magento Community
 
freshwebs
Sr. Member
 
Total Posts:  153
Joined:  2008-05-27
Leicestershire, UK
 

Hi,
My attempt fails at the $quoteObj->collectTotals(); point: I can var_dump() & return data up to this point in the code.

Would this fail because of data that I’m passing over or does collectTotals() merely calculate product price, etc?

Any input would be appreciated,
Thanks
Eddie

 
Magento Community Magento Community
Magento Community
Magento Community
 
freshwebs
Sr. Member
 
Total Posts:  153
Joined:  2008-05-27
Leicestershire, UK
 

Hi,
My attempt fails at the $quoteObj->collectTotals(); point: I can var_dump() & return data up to this point in the code.

Would this fail because of data that I’m passing over or does collectTotals() merely calculate product price, etc?

Any input would be appreciated,
Thanks
Eddie

 
Magento Community Magento Community
Magento Community
Magento Community
 
jesse_dev
Jr. Member
 
Total Posts:  27
Joined:  2009-03-03
 

I’m not receiving email from this thread for some reason, sorry for the delay.
i just checked the email notification so i should get notified of every post on here now.

@faisal - the params array is basically an array of product ids and quantities, like a shopping cart

it looks like this

$shopping_cart=array();

$part=array("PartId"=>x"Quantity"=>y);
$shopping_cart[]=$part//repeat as necessary

$params=array("AccountNo"=>$customer_id"PartCart"=>$shopping_cart);

@freshwebservices - the code you sent me in msg is quite a bit different than what you’re describing here. the code works as is. any small change can break it.

 
Magento Community Magento Community
Magento Community
Magento Community
 
freshwebs
Sr. Member
 
Total Posts:  153
Joined:  2008-05-27
Leicestershire, UK
 

Can I thank jesse_dev for posting their code & also for helping me out on an issue I had.

For the record, I was able to use jesse_dev’s code as is. My problem arose from having a few var_dump statements in my programme - this caused ‘headers already sent’ issues, unrelated to jesse_devs code.

I only found this out when I put a

try{}catch{Exception $e)echo $e}
around where I thought it was falling over (collectTotals();) Only then did the ‘headers already sent’ issue come to my attention.

So, thanks again jesse_dev

Eddie

 
Magento Community Magento Community
Magento Community
Magento Community
Magento Community
Magento Community
Back to top
Page 1 of 10