jueves, 1 de agosto de 2013

How to produce PDFs on Magento orders using a grid bulk action

We recently worked with a client who wanted to be able to print a pick list for orders placed in their Magento Store. Magento has a number of features that enable quick, printable versions of order information to be saved, or printed. However, it appeared that all of the options required the order to reach a certain point prior to being able to use them; for example, "Printing Packing Slips" or "Print Shipping Labels". Both require a shipment to be raised, and as the client wanted to use this as a picking slip, we didn't want this to have to happen.

Whilst these solutions didn't offer exactly what we wanted, they did provide a template and an idea of the functionality we required. We wanted PDF downloads and we wanted to be able to select multiple orders at once and not have to click into each separate order to create the PDF.

The only change required to the invoice template was that we weren't interested about billing information, and we required comments written by the customer whilst going through the checkout process.


So, we faced two challenges. Firstly, we needed to add a mass action to the orders grid. Secondly, we needed to create a multi-page PDF – depending on how many orders had been selected on the orders grid.

As mentioned, Magento already has a series of mass actions that can be applied to orders from the orders grid page.

As with all plugins, we need to start by creating a new folder in app/code/local/

We tend to keep all of our custom extensions together and use the Branded3 namespace, so we start with /app/code/local/Branded3/

Next, we're going to add the folder for our extension. Let's call it PrintPicking, and place it in the Branded3 namespace at /app/code/local/Branded3/PrintPicking/

Suffice to say, you can call it whatever you want.

We'll split things down a little from now on for better understanding…

Adding a new mass action

There are two ways to add a new mass action to the orders grid. The first way is to extend the Sales Order Grid (Mage_Adminhtml_Block_Sales_Order_Grid). We'd do this by doing something like the following: /app/code/local/Branded3/PrintPicking/etc/config.xml

Extending the Sales Order Grid is quick and easy; it's also pretty dirty. The reason being – other plugins could already be extending the Sales Order Grid and adding new features. Features you might want to be using. So, you might need to extend their class and this then leads to dependency in your plugin which isn't really something we want.

As with many programming solutions; there is always more than one way, and this solution will also leave other plugins to take control/add to the Sales Order Grid. We achieve this by using a Magento event instead. Specifically, core_block_abstract_to_html_before.

It's worth mentioning that it's always best practice to use event/observers whenever you can because it's future proof. Whilst a core class such as Mage_Adminhtml_Block_Sales_Order_Grid is probably unlikely to change, the core event won't, and we can keep adding events on top of events.

So let's go ahead and look at the code that we need to control this. As suggested above, we're looking at adding an event hook and pointing to our own observer function. Firstly, the config.xml for our extension – /app/code/local/Branded3/PrintPicking/etc/config.xml

As you can see, we tell Magento that we want to bind to an event. Specifically the core_block_abstract_to_html_before event. We enter the observers block, and define a new class – Branded3_Printpicking_Model_Observer, and hook up the method addActions. We also define that the model exists so that we can add an Observer (referenced in the event observer).

This means we can go ahead and make our observer next /app/code/local/Branded3/PrintPicking/Model/Observer.php

Here, we define our new Observer Class and our method addAction. We get the Observer Event Block and check for the MassAction widget, and check that it is the MassAction for the Sales Order Grid. If it is, we add a new item to it – our new mass action. As we're adding a block, it's worth using a unqiue name (first parameter of the $block->addItem call).

This gives us a new item in our drop down – "Print Picking Labels" – and sets its URL to route through to our eventual new controller. The controller is going to provide the action – i.e. creating a PDF for us to view / print / download.

Hooking up our mass action to actually do something is the next step. We've already defined a path, so let's put the correct files in place.

Creating a PDF action

We need to create our new controller and action.

At /app/code/local/Branded3/PrintPicking/controllers/adminhtml/ lets create an IndexController.php file.

Here, we create a new class that extends Mage_Adminhtml_Controller_Action. We call this Branded3_Printpicking_Adminhtml_IndexController and we can define a new action. Based on our route, this needs to be getPickingAction().

Since we have the ability to select multiple orders, the first thing we need to do is get all the order ID's we've passed through to this function and find the relevant orders.

From here, we pass off the order information to a PDF model. As we're not dealing with Shipments, some information isn't available if we used Mage_Sales_Model_Order_Pdf_Abstract & Mage_Sales_Model_Order_Pdf_Items_Abstract – as we'd need to be looking for the order/invoice that the shipment had been created on, so we'll go ahead and override these with our own methods.

Create the following two files: /app/code/local/Branded3/Printpicking/Model/Pdf.php and /app/code/local/Branded3/Printpicking/Model/Pdfitem.php

Here we can go ahead and override the two classes mentioned, so we end up with class
class Branded3_Printpaicking_Model_Pdf extends Mage_Sales_Model_Order_Pdf_Abstract
class Branded3_Printpicking_Model_Pdfitem extends Mage_Sales_Model_Order_Pdf_Items_Abstract

We can then call these from our controller.

This means that we can override the _drawHeader method (for example if we want to add or remove any information) and we can also override the getPdf() method in order to change complete layout.

We need to also tell the PDF class to use the Pdf class as its Renderer.

As mentioned above, there are some issues around how we can get order item information from an order. To do this, we can override the getSku($item) function found within Pdfitem. It should look like:

As stated in my introduction, we needed to add customer comments to the PDF. This added an additional issue, as we noticed that the site had the One Step Checkout extension running. Luckily, One Step Checkout makes this easy. You can load up an order object and call the function getOnestepcheckoutCustomercomment().

Now you should hopefully have all the component parts to put together your new module. Moving on from here, you can now look at changing Branded3_Printpaicking_Model_Pdf & Branded3_Printpicking_Model_Pdfitem to update the information required in your PDF.

If you find yourself up against any problems implementing any of the above, simply leave me a comment below and I'll get back to you.

BY Douglas Radburn AT 1:24pm ON Wednesday, 31 July 2013

Doug is our Senior Web Developer, and all round development expert. Having gained some informative insight and technical experience at two major digital agencies after graduating; Doug brought his knowledge and skills to Branded3 in 2009, and has been solving our development dilemmas ever since.

No hay comentarios:

Publicar un comentario