Easily Set a Custom Order Number in Magento With My Free SetStartOrderNumber Extension

July 24, 2010

Every time I launch a new Magento store I have to go and hack the database to change the order, shipment, invoice and credit memo numbers. This just doesn’t seem right, and seeing as I was about to launch two stores (in one week!) I thought it’d be better to make the order increment ID’s custom from within the Magento admin panel. Now what I didn’t realize before I set about doing this was that someone had already made a very good extension to customize order numberswoops.

So not knowing I could just buy an extension for $60, I went ahead and coded one up on my train ride home Friday afternoon.

This is a blog post about what the extension does (and does not) do and how it’s made. I’m going to release it for free, if you need full order customizing functionality, you should still buy Adjustware’s one, it has some great features.

If you’re just here for the free software, you can get it on Magento Connect, if you’d like to learn more about it and how it’s made, please read on.

What it does (and does not)

This is a very simple extension all it does is allow you to set the last_increment_id for 4 different magento entities; order, invoice, shipment, credit memo. In doing that, you are basically overriding the default numbers which look like 100000001,100000002,… to make them more like 253348713,253348714,… (or something like that). This allows you to encode in the number a specific store prefix (if you aggregate order numbers into external systems) and also to mask the number of orders you have processed from your customers – no one likes to be order number 100000001!

The other thing the extension does is make it hard to set a value lower than what the current one is. The reason I added this validation is because if you allow your order number space to clash, then you end up with duplicate order numbers, and that’s a customer service nightmare. Don’t worry, experts can manually override the check from within Magento.

Probably more important to the people who want a quick (free) way to set their order numbers in Magento, is what this extension does not do. It does not allow you to change the length, increment amount or use letters in your order related numbers. So for example if you choose to use number 3456 as your starting order number, what you’ll actually get is 300000456 – the Magento will pad to 9 digits in it’s core code, and for simplicity, I didn’t bother overriding that – I can, but it’d take me longer than hour long train ride, and that was the goal!

You also cannot have the order numbers go up by more than 1 each time, and you cannot have a prefix like ‘ORD12345′. These are features you do get if you buy the pro extension, and if you wait around long enough and there’s demand, I might override the increment model and add those for my extension too – or better yet, someone might contribute a patch!

So that’s it, there’s almost more things this extension doesn’t do, than does – but I made it to save myself hacking the Magento DB (in keeping with the let’s not hack Magento theme) if it saves others some time, great.

How it’s made

Part of the reason this extension is so simple is because it’s just one PHP class that actually does anything (and a bunch of other cruft required to make a Magento extension work). I’ll show you what it does and explain the various parts of it, because I think it’s valuable as a way to learn a few things about how Magento works.

Structure
Firstly a quick note about how it’s put together. Basically the extension puts a button in the Magento admin area, that when clicked, redirects the users browser to a special URL that has been mapped to a customer controller. The custom controller then runs the code that updates the order numbers and will report errors and successes back to the user. The MVC purist in me doesn’t like doing this, as the update functionality should be separated into a model that actually does the work, but sometimes conforming strictly to a software pattern loses out to the number of stops remaining before you have to get off a train!

Admin interface – the button
This is achieved through several pieces of configuration and code, firstly the system.xml file tells Magento what fields to show in the backend admin screen. In my case that’s 4 text fields (for the numbers) a button and an override switch (drop down). The XML for that looks like this:

<order translate="label">
	<label>Starting Order Number</label>
	<frontend_type>text</frontend_type>
	<sort_order>20</sort_order>
	<show_in_default>1</show_in_default>
	<show_in_website>1</show_in_website>
	<show_in_store>0</show_in_store>
</order>
... etc ...
<update translate="label">
    <comment>This will update your...*snip*</comment>
    <frontend_type>select</frontend_type>
    <frontend_model>Aschroder_SetStartOrderNumber_Block_Adminhtml_Update</frontend_model>
    <sort_order>220</sort_order>
    <show_in_default>1</show_in_default>
    <show_in_website>1</show_in_website>
    <show_in_store>0</show_in_store>
</update>
<override translate="label">
	<label>Allow me to set my increment ID's below their current values - Trust me, I know what I'm doing.</label>
	<frontend_type>select</frontend_type>
	<source_model>adminhtml/system_config_source_yesno</source_model>
	<sort_order>300</sort_order>
	<show_in_default>1</show_in_default>
	<show_in_website>1</show_in_website>
	<show_in_store>0</show_in_store>
</override>

The really interesting part of that is the button, you can see it’s mapped to a special (but very simple) front end model Aschroder_SetStartOrderNumber_Block_Adminhtml_Update which basically tells Magento what to do when the button is pushed. Let’s take a look at the code for that.

class Aschroder_SetStartOrderNumber_Block_Adminhtml_Update
    extends Mage_Adminhtml_Block_System_Config_Form_Field {
 
 protected function _getElementHtml(Varien_Data_Form_Element_Abstract $element) {
        $this->setElement($element);
        return $this->_getAddRowButtonHtml($this->__('Run Update'));
    }
 
  protected function _getAddRowButtonHtml($title) {
 
	$buttonBlock = $this->getElement()->getForm()->getParent()->getLayout()->createBlock('adminhtml/widget_button');
	$params = array(
        'website' => $buttonBlock->getRequest()->getParam('website')
    );
 
	$url = Mage::helper('adminhtml')->getUrl("setstartordernumber/start/update", $params);
 
	return $this->getLayout()->createBlock('adminhtml/widget_button')
                ->setType('button')
                ->setLabel($this->__($title))
                ->setOnClick("window.location.href='".$url."'")
                ->toHtml();
    }
}

The most important things here are;

  • What the button says:
  • $this->_getAddRowButtonHtml($this->__('Run Update'))

  • how we make the button take the user to a special URL:
  • setOnClick("window.location.href='".$url."'")

  • and lastly, where it takes you:
  • $url = Mage::helper('adminhtml')->getUrl("setstartordernumber/start/update", $params);.

Notice how we grab the current website and append the website parameter to the URL, so that the controller can know and use the website context if it has been set:
'website' => $buttonBlock->getRequest()->getParam('website').

So that’s how to create an admin button and make it do something in Magento. Let’s now move on to the controller – where the magic happens.

The Controller
So now we can send the user to a particular URL, we have to make something happen when they get there. A controller is how we do that.

The first part is creating the controller mapping in the config.xml file like this:

<admin>
	<routers>
		<setstartordernumber>
			<use>admin</use>
			<args>
				<module>Aschroder_SetStartOrderNumber</module>
				<frontName>setstartordernumber</frontName>
			</args>
		</setstartordernumber>
	 </routers>
</admin>

Now in a directory called controllers we make a Controller class called Aschroder_SetStartOrderNumber_StartController. The class declaration looks like this:

class Aschroder_SetStartOrderNumber_StartController
	extends Mage_Adminhtml_Controller_Action

Just to be clear, the URL we mapped in the previous section was: setstartordernumber/start/update. The first part of that url is mapped in the config.xml, the second part, is the controller name (start) and the last part is the action in that controller (update). If you omit the action part, then the index action is the default. The action is a function like this: public function updateAction().

So now we can send the user to the URL, and make the URL do something, finally we get to roll up our sleeves and write some code!

The Actual Application Logic

It feels like a lot of overhead to actually get to the point where you’re writing real code when making extensions doesn’t it? Well we’re finally there now – here’s how it looks – I’ll break it into bite-sized peices to make it easy to understand.

Firstly we set the collection of objects we’re interested in – the entity_type model and then set a few other variables we’ll be using during the function. $override is the value for the switch in the admin, and the $objects array is a mapping of entity type to desired value (also from the admin UI).

$collection = Mage::getModel('eav/entity_type')->getCollection();
$override = Mage::helper('setstartordernumber')->isOverrideEnabled();
 
$objects = array(
'order' => Mage::helper('setstartordernumber')->getOrderNumber(),
'invoice' => Mage::helper('setstartordernumber')->getInvoiceNumber(),
'shipment' => Mage::helper('setstartordernumber')->getShipmentNumber(),
'creditmemo' => Mage::helper('setstartordernumber')->getCreditNumber());

Now we also get the website code from the request, and set it to the default if we cannot find one.

$store = $this->getRequest()->getParam('website');
if (!$store) { 
$storeID = 1; //use default store if none was given	
} else {
$storeID = Mage::app()->getWebsite($store)->getId();
}

We’re going to iterate the collection of Magento entities in a big loop.

foreach ($collection->getItems() as $item) {

Each time we find an entity we care about and we have a desired new value for it, we get to work. The indexes of the $objects array above were cleverly chosen to match the entity codes – that helps make this code quite simple.

if(isset($objects[$item->getEntityTypeCode()]) && 
	$new_number = $objects[$item->getEntityTypeCode()]) {

Now we have an entity we care about, let’s load it’s corresponding entity_store model for the chosen website/store:

$store = Mage::getModel('eav/entity_store')->loadByEntityStore($item->getEntityTypeId(), $storeID);

This is how to check a model actually got returned in Magento – we check that the object is set, and that it has an Id like so:

if ($store && $store->getId()) {

If we do have one, we need to check if we are in override mode or that the new number is greater than the old one.

if($override || $store->getIncrementLastId() < $new_number) {

If that’s true, we can go ahead and update the entity, then save it:

$old = $store->getIncrementLastId();
$store->setIncrementPrefix(substr($new_number."",0,1));
$store->setIncrementLastId($new_number);
$store->save();

And once we save it, it’s nice to add a success message to the user’s session, so that when they next load an admin screen (which we’ll be forcing soon) they get one of those green message boxes up the top of the screen:

$this->_getSession()->addSuccess(
		"Updated: " . $item->getEntityTypeCode() . 
		" from " . $old . 
		" to " .  $new_number );

Likewise if the override is not set, or the new number is less than the old one, we report an error to the user, so they don’t think the extension isn’t working!

$this->_getSession()->addError(
	"Skipped: " . $item->getEntityTypeCode() . 
	" because " . $store->getIncrementLastId() . 
	" is greater than or equal to " .  $new_number . 
	" (and you are not in override mode)");

If the entity_store does not exist, we actually just go ahead and save a new one – which is pretty much the same code as updating, I won’t repeat it here.

Lastly, we send the user back to the same place they came from using a redirect. The will ensure they see the messages we set for them in the admin session, and show them the same admin UI screen.

$this->_redirectReferer();

So that’s it – the entire extension (pretty much) – you can get all the actual code here. Hope this little mini-howto and code explanation comes in handy for some one trying to struggle through creating a Magento extension.

How you can help

This is a pretty basic little extension, but I’d really love for some contributions that increase the capability – or even just feature ideas, bug reports and feedback, so please don’t be shy.

Ashley

Posts

Hi! I'm Ashley Schroder, a Software Engineer from New Zealand - this is a collection of notes on my experiences with Ecommerce Web Development, particularly Magento Development.

Are Your Customers Getting The Magento Emails You Send?

MageSend Magento Email sending with Amazon SES

I have made a premium Magento Extension called MageSend. MageSend makes it simple and easy to send using Amazon's highly reliable SES email service.

The extension is $99, and comes with a complete money back guarantee, please check it out, I am confident it will resolve any email sending problems with your Magento store.

61 responses to Easily Set a Custom Order Number in Magento With My Free SetStartOrderNumber Extension

  1. You may use our Magento Random Order ID extension – it randomizes order, invoice, shipment and creditmemo ids. http://www.magalter.com/random-order-id.html

  2. Hello, cool that you made this extension! I am thinking about installing it but I have Magento 1.7.0.2 (the latest version). Will it work on my version??

    Thanks a lot!

  3. Hi. First of all, thanks for sharing this with us all! I was wondering if there is a way to use this extension only with shipping numbers. This is my situation: I have different websites and I do need the order numbers to be separated for each store (starting with 1, 2, 3, etc.). Yet, it would be really great if I could have all my shipping numbers starting with the same number, so I can keep track of my shipments in a better way. Can you think of a way to achieve this with your module?

    Thanks again!

  4. When you plan a trip with your family, there are many details that you need to get right to ensure a hassle free trip.

  5. We provide travel guides and holiday reviews that all collected from famous travel site. Experience a different journey!

  6. Learn about the best destinations, the best ways to get there, the travel business, vacation tips from vacationol.com Travel

  7. Travel guides, trip ideas, expert vacation advice, travel articles, anddomestic destinations from glotrips.com

  8. Travel tips and inspiration on how to travel around the world.

  9. Travel advice and travel guide – articles, news and tips from internet at visplace.com to help plan your trip.

  10. traguide.com is a site to provide you best travel tips and trip ideas to help you travel smarter and faster.

  11. Find the best travel advice, travel news, and travel tips. Get all the travel information you need for your next trip.