Easily Set a Custom Order Number in Magento With My Free SetStartOrderNumber Extension
TweetEvery 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 numbers – woops.
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:
- how we make the button take the user to a special URL:
- and lastly, where it takes you:
$this->_getAddRowButtonHtml($this->__('Run Update'))
setOnClick("window.location.href='".$url."'")
$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.
You might also be interested in:
- Improved Custom Magento Order Number Functionality for SetStartOrderNumber extension – v0.3
- Google Apps Email/Gmail Magento Extension v0.5 released
- Sitemap Submit – Magento Extension to Submit your Google Sitemap
- New Magento SMTP features: Email logging and email sending events
- SMTP Pro Magento extension, free and open SMTP support for Magento
Tagged as custom order number, extension, Magento, set order number + Categorized as Magento, Set Start Order Number Extension 'ers make great lovers Tweet
![Email Me: ashley.schroder[at]gmail Email Me: ashley.schroder[at]gmail](/wp-content/themes/aschroder/images/email.png)










Nice! It’s shocking that even with the high-end Magento developers who build really good extensions, I go buy them and get order 10000000101 and then a week later when I buy their next great thing, I get order 100000000112. Not only does it look Mickey Mouse, I also know roughly their turnover.
And imagine, if you take an enquiry over the phone… “Order number one zero zero zero zero zero zero, hold on, was that 6, or 7 zero’s…”
Having said that, if you know the tables and fields, the SQL query to change the increments is quite easy but for the DB shy, this extension is great, and even better value
As one to rarely be immediately satisfied by a 1.0 release of anything, may I suggest something for 1.1? Using Mage’s cron system, I’d like to say “increase the order/invoice/shipping/credit memo numbers, skipping N, every X days”.
That way, if a competitor were to place a bogus not-yet-paid order and again a couple days later, if he tries to be smart and subtract the latter with the former, he will get an artificially inflated number to scare him off nicely and drive him to despair.
(OK, maybe not increase the credit memo’s then
)
Hi, JT
Thanks for stopping by – it’s interesting to hear your thoughts on the order numbers, and that you can use them as a gauge of turnover/sales – that cron increment idea is a great one, definitely going on my v0.2 list!
Stay tuned for an update.
J.T., I’m totally with you on the criticism of order numbers. Over the years you wouldn’t believe the number of systems I’ve seen installed where the transactions start at number ‘1′. Or maybe you would. Maybe, like me, you’re the guy who feels like you’re nagging everyone to change the sequence, and can’t understand why some programmers never seem to get it?
Having sequence numbers start at 100000001 might not be ideal, but at least it’s one step forward, all the numbers will be the same length for a while. It’s the numbers that are visible to customers that are the most important, and with Magento, at least the way we use it, that means the order number, which shows up in email subject lines and RSS feeds and so on.
An alternative to randomisation is to add a suffix, such as a checksum suffix, banking-style. Modulo-13 I think is one of the common checksum/validation algorithms. That way the sequence is obscured to customers, yet remains apparent to those in the know running the systems. And there often is value in having a true sequence, as anyone who’s tried to reconcile a set of transactions for an auditor or accountant will attest.
I just remembered, it’s not just the order/shipping/invoice/credit number that’s public. If you want to also impress fellow techies, you actually have to increment the real row ID’s too.
Take this URL of a current order of ours…
https://mage.ourcompany.com/1.3/index.php/atadmin/sales_order/view/order_id/274929/key/297f5aa9817f9dd11f00814c5c357/
In the customer’s account it’s like https://www.ourshop.co.uk/sales/order/view/order_id/274929/
If I remember correctly, the 274929 number is the real order ID in Mage’s tables. The 9 digit order number is only a reference to it. This real order id also shows in the front end. Same with customer IDs. So if you extend my suggestion, these numbers should ideally be included too.
Just checked, the customer ID seems hidden by the session though I seem to remember seeing it somewhere publicly.
So it looks this can be taken to extreme levels with even more incremental data to artificially inflate or at least change from 1 to something a little bit more professional.
PS, for the 7 shops I run in the one Mage installation, we use different start digits so for the first 100 million orders on each, they keep their start digit so we can easily remember which shop it was just from looking at the number. If this extension can discriminate between ‘websites’ for its numbering, that would be even better. On the day of the one hundred millionth and first order, I’ll be sad that the system didn’t hold up
Final comment on this topic, I was surprised to see Fooman list that order number = invoice number extension. Surely, you have to have the ability to raise multiple invoices in order to make up an order total? I don’t get why people of that caliber want to limit the quite unique flexibility of Magento like that. Unless of course when he says the invoice number is “using” the order number, he means as a prefix.
Hi JT,
My order number = invoice number extension was initially targeted at the bulk of all online stores, which process one payment transaction with each order – if you needed the default Magento functionality you simply wouldn’t use the extension. The other pain point I find of the default Magento implementation is that once a customer rings up you are never quite sure which number (order, shipment or invoice) they give you.
Also you’d be pleased to hear that version 2.0 now supports multiple invoices, etc by appending a suffix.
Hi there.
It’s a nice extension but I couldn’t make it work. I debugged and finally see that getStoreConfig parts like Mage::getStoreConfig(’sales/setstartordernumber/override’); comes as null. What may be the problem?
Hi – it’s a little clunky in that you have to Save the config before you run the update. Otherwise the values are not in there. I promise promise if I get 100+ downloads I’ll figure out how to make it an ’save and update’ button because I know it’s not the best currently.
You are right. I did not think that I should press “Save the config” button first. Thank you.
I can’t use the extension. The buttom doesn’t appears in my admin menu. I’m running on 4.1.1.1 version
Hi J.M – you should see the config section in the Sales section of the admin. Is it not there?
Hi Ashley , I finally found the buttom
. Thanks
Hi everybody. I put the numbers on the fields, press “Save config buttom” and press “Run” buttom. But when I place an order, the number is not specified in the field and this happnes with the number invoice.I don’t know what is wrong
Thanks. It just works!
If you ever get around to implement the custom length of the custom order numbers, I would for sure be one of the first ones to test it.
So, Now I am testing 2 of your extensions: the smtp pro, and this custom order number.
Thanks so much.
/Jonas
I got this when downloading from Magento Connect:
Failed to download magento-community/Aschroder_SetStartOrderNumber within preferred state “stable”, latest release is version 0.2, stability “beta”, use “channel://connect.magentocommerce.com/community/Aschroder_SetStartOrderNumber-0.2″ to install
Cannot initialize ‘channel://connect.magentocommerce.com/community/Aschroder_SetStartOrderNumber’, invalid or missing package file
Install Errors
Package “channel://connect.magentocommerce.com/community/Aschroder_SetStartOrderNumber” is not valid
PEAR ERROR: install failed
Worked just fine, by saving the config first and the running the update.
You have to set your stability to beta to use the extension (you do that in your magento connect admin).
Hi, at first, thank you for your great work!
I have one Problem with my stores:
Is it possible to define one and the same Incremetation Rule for all Shops and views?
we have 3 shops ( 2 websites – first website with 2 stores and two views in each of them plus another website with 1 store an 2 storeviews ).
It means that we have 6 types of Invoice numbers.
100…..01
200….01
300….01
..
600….01
But we need to have only one Invoice number per website. Is there a way to make that possible with your extension?
Greetings from Austria ( not Australia..)
Hi, I haven’t tested it on multi-store setups, if you fancy helping out, you could try it out and let me know?
hi!
after installation of your extension i am experiencing following:
I am able to configure the order, invoice … – numbers per website.
My Setup:
website 1 store1 store view 1.1
store view 1.2
website1 store2 store view 2.1
sore view 2.2
website2 store3 store view 3.1
store view 3.2
With your extension I am able to configure the numbers for store view 1.1 on website 1 and store view 3.1 on website 2.
It would be great, if it was possible to get one increasing order, invoice.. – number per website.
Is there a chance for that?
Hi, Wolfi – I think I can change it so the scope of the order numbers etc is store not website, that might achieve what you are after. If you want to test it out yourself (in a test environment!) then try changing the etc/system.xml file where it says for the various different fields. Let me know how you get on, if it works I’ll update the extension. Cheers
Hi Asley…I’ve installed the extension and configured it successful. i then went to test it and place an order; however, when I clicked the “Proceed to Checkout” button, a page not found button appears. Any suggestions on how to fix that?
Thanks,
Natalie
Hi, Natalie – what is the URL of the page that is 404′ing? It’s very hard to imagine a situation where changing your order increment ID’s would cause an error like you are describing, are you certain nothing else changed?
My mistake…the problem was not related. I’ve got it working fine now. Thank you for your work
I will start testing it out live and provide feedback later. Is there a chance you will be able to add characters?