I was trying to create orders and quotes programmatically, in an API resource that is consumed by a Mobile Application, and i was successful. However every now and then I got a UNQ_SALES_FLAT_ORDER_INCREMENT_ID error. I google around and realized that:
- The (increment_last_id) field in the (eav_entity_store) table is incremented by 1on each order as it normally should
- The (increment_id) field in the (sales_flat_order) table seems to jump/skip several number from the (increment_last_id), sometimes the difference is 20, sometimes the difference is 50, very random
So I digged deeper on how Magento assigns the order increment id and observed the following:
app\code\core\Mage\Sales\Model\Service\Quote.php
The $quote->reserveOrderId() line reserve the order id of this quote:
public function submitOrder()
{
$this->_deleteNominalItems();
$this->_validate();
$quote = $this->_quote;
$isVirtual = $quote->isVirtual();
$transaction = Mage::getModel('core/resource_transaction');
if ($quote->getCustomerId()) {
$transaction->addObject($quote->getCustomer());
}
$transaction->addObject($quote);
$quote->reserveOrderId();
app\code\local\Mage\Sales\Model\Quote.php
The Quote model then checks if the quote already have a reserved order id or not, if it does not have one, it assigns it automatically, and this is the part that is really causing the issue. I don't know why if a quote doesn't have a reserved order id, the id is automatically assigned, by adding the current store increment id + a random value:
/**
* Generate new increment order id and associate it with current quote
*
* @return Mage_Sales_Model_Quote
*/
public function reserveOrderId()
{
if (!$this->getReservedOrderId()) {
$this->setReservedOrderId($this->_getResource()->getReservedOrderId($this) + $this->getRandInt() );
} else {
//checking if reserved order id was already used for some order
//if yes reserving new one if not using old one
if ($this->_getResource()->isOrderIncrementIdUsed($this->getReservedOrderId())) {
$this->setReservedOrderId($this->_getResource()->getReservedOrderId($this) + $this->getRandInt() );
}
}
return $this;
}
app\code\core\Mage\Sales\Model\Resource\Quote.php
And just for reference, this is where $this->_getResource()->getReservedOrderId($this) generates the next increment:
public function getReservedOrderId($quote)
{
$storeId = (int)$quote->getStoreId();
return Mage::getSingleton('eav/config')->getEntityType(Mage_Sales_Model_Order::ENTITY)
->fetchNewIncrementId($storeId);
}
So after all the above I came to the conclusion that I need to set the quote reserved order id manually before submitting the order, something like:
<!-- ... creating quote and adding products -->
$quote->getPayment()->importData(array('method' => 'cashondelivery'));
$quote->collectTotals();
<!-- do i need to manually assign it this way -->
$quote->setReservedOrderId($quote->getResource()->getReservedOrderId($quote));
$quote->save();
$service = Mage::getModel('sales/service_quote', $quote);
$service->submitAll();
$increment_id = $service->getOrder()->getRealOrderId();
So my question right now is:
- should i assign the reserved order id to the quote the way im doing it above, so i don't get a random order increment id? Most of the answers on stackoverflow about creating an order by code doesn't mention this line, so not sure whether it has consequences or not.
- why Magento is assigning the order incrementn id the way it does randomly, if it already does not have one.
Thanks and sorry for the lengthy post.
setReservedOrderId
code you're referring to never existed in any revision of Magento 1x and so must have been alocal
override.