Here in Australia we have AusPost who will not ship anything above 20kg. On this particular site, there are often shipments much larger comprising of a lot of smaller products, say 1kg each.

I would like to know if it is possible to extend Magento so that based on a maximum weight threshold, the shipments are automatically split into lots of 20kg packages (versus the client having to work out the QTY for each item to make the shipment match as close to the threshold weight (20kg in this example) as possible.

Ideally the solution should work with bulk order management extensions such as Better Order Management by Amasty or Simplify Bulk Order Processing by Xtento - preferably the former.

  • what do you mean by splitting exactly. Is it purely price based? (per 20kg $10) or should a different shipping method be displayed when you go over 20kg? – Sander Mangel Jan 10 '14 at 7:52
  • So you know how Magento can create multiple shipments per order - my client needs a programmatic way to set a maximum value and Magento needs to create multiple shipments if it goes over the threshold. The process after that is of course, they print out the picking slips, package it up and ship it. – Francis Kim Jan 10 '14 at 10:41
up vote 9 down vote accepted
+250

OK, your request is not difficult to implement, see my solution. It works only when you create shipment using Magento admin panel. I supposed that shipments are created manually.

1. Create local module (ex. ShipmentSplit) and rewrite Shipment controller, config.xml:

<?xml version="1.0"?>
<config>
    <modules>
        <Some_ShipmentSplit>
            <version>0.1.0</version>
        </Some_ShipmentSplit>
    </modules>
    <admin>
        <routers>
            <adminhtml>
                <args>
                    <modules>
                        <Some_ShipmentSplit before="Mage_Adminhtml">Some_ShipmentSplit_Adminhtml</Some_ShipmentSplit>
                    </modules>
                </args>
            </adminhtml>
        </routers>
    </admin>
    <global>
        <helpers>
            <some_shipmentsplit>
                <class>Some_ShipmentSplit_Helper</class>
            </some_shipmentsplit>
        </helpers>
    </global>
</config>

2. Create Configuration settings to enable/disable splitting functionality and store weight threshold parametr, system.xml.

<?xml version="1.0"?>
<config>
    <sections>
        <shipping>
            <groups>
                <splitting translate="label">
                    <label>Shipment Splitting</label>
                    <frontend_type>text</frontend_type>
                    <sort_order>10</sort_order>
                    <show_in_default>1</show_in_default>
                    <show_in_website>0</show_in_website>
                    <show_in_store>0</show_in_store>
                    <fields>
                        <enable translate="label">
                            <label>Allow Shipping to Split</label>
                            <frontend_type>select</frontend_type>
                            <source_model>adminhtml/system_config_source_yesno</source_model>
                            <sort_order>1</sort_order>
                            <show_in_default>1</show_in_default>
                        </enable>
                        <weight translate="label">
                            <label>Maximum Weight</label>
                            <frontend_type>text</frontend_type>
                            <validate>validate-number</validate>
                            <sort_order>2</sort_order>
                            <show_in_default>1</show_in_default>
                            <depends>
                                <enable>1</enable>
                            </depends>
                        </weight>
                    </fields>
                </splitting>
            </groups>
        </shipping>
    </sections>
</config>

3. Rewrite Shipment controller:

<?php
require_once 'Mage/Adminhtml/controllers/Sales/Order/ShipmentController.php';
class Some_ShipmentSplit_Adminhtml_Sales_Order_ShipmentController extends Mage_Adminhtml_Sales_Order_ShipmentController
{

    public function saveAction()
    {
        $enableSplitting = Mage::getStoreConfig('shipping/splitting/enable');
        $isNeedCreateLabel = isset($data['create_shipping_label']) && $data['create_shipping_label'];

        //if splitting not enabled use standard magento logic
        if (!$enableSplitting || $isNeedCreateLabel) return parent::saveAction();

        $data = $this->getRequest()->getPost('shipment');
        if (!empty($data['comment_text'])) {
            Mage::getSingleton('adminhtml/session')->setCommentText($data['comment_text']);
        }

        try {
            $shipment = $this->_initShipment();
            if (!$shipment) {
                $this->_forward('noRoute');
                return;
            }

            $comment = '';
            if (!empty($data['comment_text'])) {
                $shipment->addComment(
                    $data['comment_text'],
                    isset($data['comment_customer_notify']),
                    isset($data['is_visible_on_front'])
                );
                if (isset($data['comment_customer_notify'])) {
                    $comment = $data['comment_text'];
                }
            }

            $shipmentCreatedMessage = $this->__('The shipment has been created.');
            $labelCreatedMessage = $this->__('The shipping label has been created.');

            //split to groups
            $shipments = $this->_initShipmentGroups($shipment);

            foreach ($shipments as $shipment) {
                $shipment->register();
                if (!empty($data['send_email'])) {
                    $shipment->setEmailSent(true);
                }
                $shipment->getOrder()->setCustomerNoteNotify(!empty($data['send_email']));

                $this->_saveShipment($shipment);
                $shipment->sendEmail(!empty($data['send_email']), $comment);

                $this->_getSession()->addSuccess($isNeedCreateLabel ? $shipmentCreatedMessage . ' ' . $labelCreatedMessage
                    : $shipmentCreatedMessage);
                Mage::getSingleton('adminhtml/session')->getCommentText(true);
            }
        } catch (Mage_Core_Exception $e) {
            $this->_getSession()->addError($e->getMessage());
            $this->_redirect('*/*/new', array('order_id' => $this->getRequest()->getParam('order_id')));
        } catch (Exception $e) {
            Mage::logException($e);
            $this->_getSession()->addError($this->__('Cannot save shipment.'));
            $this->_redirect('*/*/new', array('order_id' => $this->getRequest()->getParam('order_id')));
        }
        $this->_redirect('*/sales_order/view', array('order_id' => $shipment->getOrderId()));
    }

    /**
     * this function splits shipment into groups
     * @param Mage_Sales_Model_Order_Shipment $shipment
     * @return array
     */
    protected function _initShipmentGroups(Mage_Sales_Model_Order_Shipment $shipment)
    {
        /**
         * @var $items Mage_Sales_Model_Resource_Order_Item_Collection
         */
        if ($maxKg = (int)Mage::getStoreConfig('shipping/splitting/weight')) {
            $qtys = $this->_getItemQtys();
            $itemWeights = array();
            $allItems = array();

            $order = $shipment->getOrder();
            $items = $shipment->getOrder()->getItemsCollection();
            /**
             * fetching items weight
             */
            foreach ($qtys as $itemId => $qty) {
                $itemWeights[$itemId] = $items->getItemById($itemId)->getWeight();
                while ($qty--) $allItems[] = $itemId;
            }

            $groups = array();
            $i = 0;
            /**
             * create item groups which every group weight up to $maxKg threshold
             */
            while (count($allItems) > 0) {
                $i++;
                $groups[$i] = array(
                    'items' => array(),
                    'weight' => 0,
                );
                foreach ($allItems as $index => $itemId) {
                    $itemWeight = $itemWeights[$itemId];
                    if ($groups[$i]['weight'] + $itemWeight <= $maxKg) {
                        $groups[$i]['weight'] += $itemWeight;
                        $groups[$i]['items'][] = $itemId;
                        unset($allItems[$index]);
                    }
                }
                /**
                 * check if an item is added to the group,
                 * if not added, it means some product weight is over threshold
                 * you can throw within if or add this product to the group
                 * or you can remove this IF block
                 * if you are sure that all products have appropriate weight
                 */
                if (count($groups[$i]['items']) == 0) {
                    $itemWeight = $itemWeights[$itemId];
                    $groups[$i]['weight'] += $itemWeight;
                    $groups[$i]['items'][] = $itemId;
                    unset($allItems[$index]);
                }
            }

            //print_r($groups);
            $shipments = array();
            if (count($groups) > 1) {
                foreach ($groups as $data) {
                    $groupItems = array();
                    foreach ($data['items'] as $itemId) {
                        if (isset($groupItems[$itemId])) {
                            /**
                             * if same item then only increase quantity
                             */
                            $groupItems[$itemId]++;
                        } else {
                            $groupItems[$itemId] = 1;
                        }
                    }
                    //create shipment for each group
                    $shipments[] = Mage::getModel('sales/service_order', $order)->prepareShipment($groupItems);
                }
            } else {
                $shipments[] = $shipment;
            }
            return $shipments;
        } else {
            Mage::throwException('Shipment package weight not defined');
        }
    }
}

Enter to admin order management page, open order, click [ship] button, click [submit shipment] button (you can except some items giving 0 qty to ship) and our logic automatically create shipments based on weight threshold (minimum one shipment). See my results:

enter image description here

  • Nice one! The code seems solid :) – Sander Mangel Jan 11 '14 at 9:22
  • Thanks for the code - seems very solid as @SanderMangel points out. I've expanded the question a little - as it was missing some background information. – Francis Kim Jan 11 '14 at 11:58
  • 1
    @FrancisKim I cannot test this solution with modules which are given in your question, because of they are not free and I don't have these modules. But you can use my logic in these modules, if they mekes some shipment related functionality. – mageUz Jan 15 '14 at 22:40

Basically we have a frontend part (the checkout) and a backend part. Now for the frontend I would suggest to use the WebshopApps matrixrate extension. This allows you to set price of the shipping based on weight. This way you can charge a shipment fee per 20kg.

Now for the backend you will need to split the shipment when it's created in the backend. The request to create a shipment is processed in the Mage_Adminhtml_Sales_Order_ShipmentController controller. Here, in the method _initShipment the Mage_Sales_Model_Service_Order model, method prepareShipment is called.

In this function you can start writing the custom code. I would advice you to create your own extension that overwrites this model or at least copy the file to the local code pool. More on overwriting models you can find on the Inchoo blokg.

Basically instead of the method creating 1 shipment you will have to loop through all the quote items splitting them up in blocks of max 20kg and creating a shipment for each. The method uses the code

$shipment = $this->_convertor->toShipment($this->_order);

to create a shipment. Instead of calling it once call it per new shipment.

Ultimately HOW you create the shipment there won't matter I guess, more on creating them you can find in this Mage SE answer.

I didn't find any ready to go extensions for this. If you need any further help on specific features in this extension make sure to give a shout.

Your Answer

 
discard

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.