Disable carriers on Specific Days in PrestaShop

If you offer same day shipping, you might want to disable your carriers depending on the current day. In this tutorial we will see how to set up carriers so that they can be disabled on specific days of the week.

  • Compatible PrestaShop versions: 1.5.5+, 1.6, 1.7
  • Compatible ThirtyBees versions: 1.0+

NOTE: this tutorial is compatible with ThirtyBees, the PrestaShop 1.6 fork! If you haven’t checked it out yet, have a look at the list of improvements compared to the original PrestaShop 1.6 on the ThirtyBees Official Website

To achieve the result we want, we could simply hardcode dates and carrier ids in the function that retrieves all available carriers, and disable accordingly. However, this is not maintainable in the long term, as we would have to amend the Carrier Class every time we need to change the days.
Instead, we will add a new field in the back office, for each carrier’s configuration page, that we can easily access and modify when needed. Let’s get started!

Preparing the Database and carrier object

To keep things simple, we are going to save the days names as a string, in the following format “Tue, Wed, Thu”, and then check against this value in the getAvailableCarrierList method, when retrieving carriers.
Thus, the first thing we want to do is extend the Carrier object, by adding a new column to the database table.

Login to phpMyadmin, browse to the *prefix*carrier table (where *prefix* is ps_ by default), and add a new column as VARCHAR, 64 length, named disable_days.

Prestashop: Disable carriers on specific days phpmyadmin

Now that the column has been added, we must tell the Carrier class that it has a new field available.

Navigate to override/classes and create a new file named Carrier.php
Inside PHP tags, add the following:

Class Carrier extends CarrierCore
{
	public $disable_days;

    public static $definition = array(
        'table' => 'carrier',
        'primary' => 'id_carrier',
        'multilang' => true,
        'multilang_shop' => true,
        'fields' => array(
            /* Classic fields */
            'id_reference' =>            array('type' => self::TYPE_INT),
            'name' =>                    array('type' => self::TYPE_STRING, 'validate' => 'isCarrierName', 'required' => true, 'size' => 64),
            'active' =>                array('type' => self::TYPE_BOOL, 'validate' => 'isBool', 'required' => true),
            'is_free' =>                array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
            'url' =>                    array('type' => self::TYPE_STRING, 'validate' => 'isAbsoluteUrl'),
            'shipping_handling' =>        array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
            'shipping_external' =>        array('type' => self::TYPE_BOOL),
            'range_behavior' =>        array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
            'shipping_method' =>        array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'),
            'max_width' =>                array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'),
            'max_height' =>            array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'),
            'max_depth' =>                array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt'),
            'max_weight' =>            array('type' => self::TYPE_FLOAT, 'validate' => 'isFloat'),
            'grade' =>                    array('type' => self::TYPE_INT, 'validate' => 'isUnsignedInt', 'size' => 1),
            'external_module_name' =>    array('type' => self::TYPE_STRING, 'size' => 64),
            'is_module' =>                array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),
            'need_range' =>            array('type' => self::TYPE_BOOL),
            'position' =>                array('type' => self::TYPE_INT),
            'deleted' =>                array('type' => self::TYPE_BOOL, 'validate' => 'isBool'),

            /* Lang fields */
            'delay' =>                    array('type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'required' => true, 'size' => 128),

            /* By Nemo */
            'disable_days' =>                array('type' => self::TYPE_STRING, 'validate' => 'isCleanHtml'),
        ),
    );	 
}

As you can see, I added a new property for the object, as well as a new definition entry. There is another way to do this as explained in my other tutorial on Extending PrestaShop Objects, feel free to use that instead.

Make sure you clar the class index at this point, so reach out the cache/ folder and erase class_index.php.

So far, so good. We have a new field to play with, so the next step is creating the back office input so that we can add values.

Modifying the PrestaShop carrier Wizard

We will need another override for the next step, so let’s create inside override/controllers/admin, naming it AdminCarrierWizardController.php.

Class AdminCarrierWizardController extends AdminCarrierWizardControllerCore
{

}

We need to copy 2 methods from the original file, so make sure you grab them from your AdminCarrierWizardController, located in the controllers/admin folder.
Copy and paste them in our new file, they are renderStepOne and getStepOneFieldsValues:

    public function renderStepOne($carrier)
    {
        $this->fields_form = array(
            'form' => array(
                'id_form' => 'step_carrier_general',
                'input' => array(
                    array(
                        'type' => 'text',
                        'label' => $this->l('Carrier name'),
                        'name' => 'name',
                        'required' => true,
                        'hint' => array(
                            sprintf($this->l('Allowed characters: letters, spaces and "%s".'), '().-'),
                            $this->l('The carrier\'s name will be displayed during checkout.'),
                            $this->l('For in-store pickup, enter 0 to replace the carrier name with your shop name.')
                        )
                    ),
                    array(
                        'type' => 'text',
                        'label' => $this->l('Transit time'),
                        'name' => 'delay',
                        'lang' => true,
                        'required' => true,
                        'maxlength' => 128,
                        'hint' => $this->l('The estimated delivery time will be displayed during checkout.')
                    ),
                    array(
                        'type' => 'text',
                        'label' => $this->l('Speed grade'),
                        'name' => 'grade',
                        'required' => false,
                        'size' => 1,
                        'hint' => $this->l('Enter "0" for a longest shipping delay, or "9" for the shortest shipping delay.')
                    ),
                    array(
                        'type' => 'logo',
                        'label' => $this->l('Logo'),
                        'name' => 'logo'
                    ),
                    array(
                        'type' => 'text',
                        'label' => $this->l('Tracking URL'),
                        'name' => 'url',
                        'hint' => $this->l('Delivery tracking URL: Type \'@\' where the tracking number should appear. It will be automatically replaced by the tracking number.'),
                        'desc' => $this->l('For example: \'http://example.com/track.php?num=@\' with \'@\' where the tracking number should appear.')
                    )
                )
            )
        );

        $tpl_vars = array('max_image_size' => (int)Configuration::get('PS_PRODUCT_PICTURE_MAX_SIZE') / 1024 / 1024);
        $fields_value = $this->getStepOneFieldsValues($carrier);
        return $this->renderGenericForm(array('form' => $this->fields_form), $fields_value, $tpl_vars);
    }

    public function getStepOneFieldsValues($carrier)
    {
        return array(
            'id_carrier' => $this->getFieldValue($carrier, 'id_carrier'),
            'name' => $this->getFieldValue($carrier, 'name'),
            'delay' => $this->getFieldValue($carrier, 'delay'),
            'grade' => $this->getFieldValue($carrier, 'grade'),
            'url' => $this->getFieldValue($carrier, 'url'),
        );
    }    

We are adding the days fields to the first screen (managed by the first method we pasted), and using the other to retrieve the correct value. Let’s add the field right after the name:

   public function renderStepOne($carrier)
   {
        $this->fields_form = array(
            'form' => array(
                'id_form' => 'step_carrier_general',
                'input' => array(
                    array(
                        'type' => 'text',
                        'label' => $this->l('Carrier name'),
                        'name' => 'name',
                        'required' => true,
                        'hint' => array(
                            sprintf($this->l('Allowed characters: letters, spaces and "%s".'), '().-'),
                            $this->l('The carrier\'s name will be displayed during checkout.'),
                            $this->l('For in-store pickup, enter 0 to replace the carrier name with your shop name.')
                        )
                    ),
                    // Nemo
                    array(
                        'type' => 'text',
                        'label' => $this->l('Disable the following days of the week'),
                        'name' => 'disable_days',
                        'hint' => $this->l('Enter days as follows: Mon, Tue, Wed etc... Separated by comma')
                    ),
                    array(
                        'type' => 'text',
                        'label' => $this->l('Transit time'),
                        'name' => 'delay',
                        'lang' => true,
                        'required' => true,
                        'maxlength' => 128,
                        'hint' => $this->l('The estimated delivery time will be displayed during checkout.')
                    ),
                    array(
                        'type' => 'text',
                        'label' => $this->l('Speed grade'),
                        'name' => 'grade',
                        'required' => false,
                        'size' => 1,
                        'hint' => $this->l('Enter "0" for a longest shipping delay, or "9" for the shortest shipping delay.')
                    ),
                    array(
                        'type' => 'logo',
                        'label' => $this->l('Logo'),
                        'name' => 'logo'
                    ),
                    array(
                        'type' => 'text',
                        'label' => $this->l('Tracking URL'),
                        'name' => 'url',
                        'hint' => $this->l('Delivery tracking URL: Type \'@\' where the tracking number should appear. It will be automatically replaced by the tracking number.'),
                        'desc' => $this->l('For example: \'http://example.com/track.php?num=@\' with \'@\' where the tracking number should appear.')
                    )
                )
            )
        );

        $tpl_vars = array('max_image_size' => (int)Configuration::get('PS_PRODUCT_PICTURE_MAX_SIZE') / 1024 / 1024);
        $fields_value = $this->getStepOneFieldsValues($carrier);
        return $this->renderGenericForm(array('form' => $this->fields_form), $fields_value, $tpl_vars);
    }

I chose the Mon, Tue, Wed format, but you can also add the full names as long as their first letter is capital and they are english names. We will be checking against a php date for it.
Then, let’s grab the value with the second method we pasted:

public function getStepOneFieldsValues($carrier)
    {
        return array(
            'id_carrier' => $this->getFieldValue($carrier, 'id_carrier'),
            'name' => $this->getFieldValue($carrier, 'name'),
            'delay' => $this->getFieldValue($carrier, 'delay'),
            'grade' => $this->getFieldValue($carrier, 'grade'),
            'url' => $this->getFieldValue($carrier, 'url'),
            //nemo
            'disable_days' => $this->getFieldValue($carrier, 'disable_days'),
        );
    }

Good!
Clear the class index again, then reach a carrier’s configuration page and try to save the new field.

Prestashop: Disable carriers on specific days phpmyadmin

Prestashop: Disable carriers on specific days back office

NOTE: If you don’t see the field or it doesn’t save, chances are you still need to clear the class index. Also, make sure overrides are not disabled in the back office Performance options.

We are almost done, the last step is to actually disable carriers when needed, so let’s do it!

Disable carriers on specific days

We already have everything we need, we just have to clone one last method from the original Carrier class. Go ahead and paste getAvailableCarrierList in the override we created earlier.
Then, right before the last return $carrier_list;, add the following:

            foreach ($carrier_list as $key => $id_carrier) {
                $carrier = new Carrier($id_carrier);
                // let's disable carrier #3 on mondays
                if(strstr($carrier->disable_days, date('D')))
                    unset($carrier_list[$key]);
            }      

As you can see it’s pretty easy, just a string check to see if the saved value contains the current day. Once more, since it’s a PHP day, you must save values in the back office like Mon, Tue, Wed etc.
We are done! Save and try to checkout, then change the days, and you should see your carrier being disabled as needed!

You like the tuts and want to say "thank you"? Well, you can always feel free to donate:

You like the tuts and want to say "thank you"? Well, you can always feel free to donate:

Need Help?

Hire me