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 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.
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?
I´ve the same problem like wolfi…regarding 2 websites in one store. Do you have any solutions of this problem?
I haven’t had a chance to test the extension with multiple store views – I do not think it works properly though as I have had several people with similar feedback.
It’s ticked over 300 downloads so I am planning a bit of development work on it. I’ll take a look as soon as I can.
I’m also interested for a solution on 2+ website views.
Hi Ashley,
Another great one that I have used… Still, I can join to the comment about the store view issue: I am using different “mag websites” (as subfolders of the main store) for currencies and payment methods. When i got the first order in other “website” than the default it gave me: 200000001 while the “2” is for the website I think…
Cheers,
Sharon
Hi, thanks for the report. This extension is due an update, when I get a free moment I’ll send one out – and it’ll include multi-store support, promise.
Ash,
Magento should sponsor that train you ride on !
Yet-another-Ashley-extension-that-works-does-what-it-says-it-will-and-saves-my-time
Thank you,
Matt.
No Worries Matt, glad to hear it.
Outstanding job however please, please, please multiple storefronts version – yes, that’s officially begging!
Hey Ashley,
Great extension!
I have a feature idea, how about being able to add the date dmy+increment for example?
hei,ashley, is this extension support magento version 1.5.0.1 yet? the magento connect still store the 1.4version. thanks.
Hi, That’s great extension. But I would like to known how could i prefix with current date time like.
201105202459ID
Thanks
Hi Ashley,
I will install and test your extension, it seems like it is what i am looking for.
I wonder if it is magento 1.5 ready and if it is possible to put a alphanumeric prefix to the ordernumber?
Kind regards.
Freek.
I wonder if it is magento 1.5 ready?
Hi Ashley
Many thanx for this great extension. I’ve successfully installed version 3.0 on my Magento Wbsite (1.5.0.1). However, when I am trying to click on “sales” under the setting menu, I am getting a blank page. Uninstalling the extension solves the problem.
Any idea on that issue.
Thanx in advance.
Regards
Mohamed
Hi,
have just installed the actual extension from http://www.magentocommerce.com/magento-connect/ASchroder/extension/4088/aschroder_setstartordernumber, but seems not to work properly on my system.
We are running on magento v1.4.0.1. The extension settings are: disable_padding sim, disable_prefix sim, extra increment 53, order number 12345. New orders however continue to be sth. like 2000000123, whereas the order id is the id of the previous order + the extra increment. Am I missing sth.? Would like to have order ids like “2123456789”
THX
@marcel – might be mutli-store setup you have? I haven’t had time to check this works on all multi-store setups.
Hi,
yes, our shop runs beside the Default-Store. I.e. we just running one store, but installation was made like this :/
I tried to install your extension through magento connect but it won’t work. Is it possible for you to email me the files so I can manually install it?
You might make this available for Magento 1.6.0.0?
I would use it untested ;P
If anyone is out there listening?
I feel as though I am the only person here who can’t seem to make this work…
I’ve installed the extension successfully, configured it as I believe to be correct, cleared my caches and still when I place orders I don’t see the desired results… Am I missing something that you all have seemed to figure out ?! I’m getting a bit annoyed that I can’t make this work…
PS, I am a dummy when it comes to database stuff, hence I tried this extension rather than hack up my database and risk screwing it all up royally…
Any suggestions on how to troubleshoot this?
Thanks anyone,
Jo
Ashley, can I ask you a little improvements to your module? Can’t you make your module work with
PREFIX: 2012/
in order to have invoice number as
2012/00001
?!
I think you’ll made happy ALL the developer of Magento in Italy and also in other countries… I will try do see if I can manage to edit your plugin for me, but you actually know how everything is working and for you will be of course, quickly!!! 😉
Hi.
Still can’t reset the invoice number. I’m afraid of enabling the “set my increment ids below their current values” options, even after resetting all orders.
Any hint on why magento keeps generating invoice from last one instead of the n.0000001 like the order?
Works great in my Spanish flowershop!
thanks for this great free module!
Lysa Flores
Do you have this for 1.7 i really need some help using this
how can save the order in the custom payment.
Is this correct?Order is not stored and i used the code in my response function.
$order = Mage::getModel(‘sales/order’);
$order_id = Mage::getSingleton(‘checkout/session’)->getLastRealOrderId();
$order->loadByIncrementId($order_id);
$order->setState(Mage_Sales_Model_Order::STATE_PROCESSING, true, ‘Gateway has authorized the payment.’);
$order->sendNewOrderEmail();
$order->setEmailSent(true);
🙁 please give me solun
Thnx
vasa
I love your blog, I recommend to all people who love this kind of post to visit this site, I recommend it.
Hi Ashley,
first of all, thank you very much for the great work you offer for the magento community. I already use your cron-scheduler extension and I couldn’ be happier with it.
I already contacted you and although you answered my mail quickly and in a very nice way, I’m still struggling to solve that problem, so I thought that maybe somebody around here can help me out a bit.
I installed this extension but unfortunaley it doesn’t work. Maybe I did something wrong, but I can’t see, what that would be.
I configured the sales option, saved it and “ran update”, but when I buy I product at the frontend of my shop, it still shows the old default invoice number, e.g. 200000026 or something like that.
I’m using magento 1.7.0.2. and the absolute theme (f001) – could this be the reason for the problem?
Of course I cleared caches and rebuild the indexes. I tried both with firefox and safari, the problem’s the same.
You mentioned that it could be because of a multistore setup, but I’m only using one store.
Do you have any ideas what the problem could be?
Thanks a lot!