Create new order state and status programmatically in Magento 2

All orders have an order status that is associated with a stage in the order processing workflow.
The state describes the position of an order in the workflow. By default, Magento store has a set of predefined order statuses and order state settings. All the available order statuses can be found in admin panel under Stores -> Settings -> Order Status. Sometimes we need to create a new order status and state. Let’s figure out how to create a new order state and status programmatically in Magento 2.

The order statuses are stored in sales_order_status database table while the order states and their bindings to statuses are defined in sales_order_status_state table. We will need to create a simple setup script in order to add a new order state and status.

First of all we need to create a new custom extension, for example Atwix_OrderFlow. Create a registration.php file in the app/code/Atwix/OrderFlow:

<?php
/* File: app/code/Atwix/OrderFlow/registration.php */

use \Magento\Framework\Component\ComponentRegistrar;

ComponentRegistrar::register(
    ComponentRegistrar::MODULE,
    'Atwix_OrderFlow',
    __DIR__
);

And create the module.xml configuration file in app/code/Atwix/OrderFlow/etc folder with the following code:

<?xml version="1.0"?>
<!-- File: app/code/Atwix/OrderFlow/etc/module.xml -->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="Atwix_OrderFlow" setup_version="1.0.0">
        <sequence>
            <module name="Magento_Sales"/>
        </sequence>
    </module>
</config>

The install data class is the following. There are two similar methods created for example:

  • addNewOrderProcessingStatus
  • addNewOrderStateAndStatus

The first method will add a new order status to the existent order state. The second one will create a new order status and new order state.

<?php
/* File: app/code/Atwix/OrderFlow/Setup/InstallData.php */

namespace Atwix\OrderFlow\Setup;

use Exception;
use Magento\Framework\Exception\AlreadyExistsException;
use Magento\Framework\Setup\InstallDataInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\ModuleDataSetupInterface;
use Magento\Sales\Model\Order;
use Magento\Sales\Model\Order\Status;
use Magento\Sales\Model\Order\StatusFactory;
use Magento\Sales\Model\ResourceModel\Order\Status as StatusResource;
use Magento\Sales\Model\ResourceModel\Order\StatusFactory as StatusResourceFactory;

/**
 * Class InstallData
 */
class InstallData implements InstallDataInterface
{
    /**
     * Custom Processing Order-Status code
     */
    const ORDER_STATUS_PROCESSING_FULFILLMENT_CODE = 'processing_custom';

    /**
     * Custom Processing Order-Status label
     */
    const ORDER_STATUS_PROCESSING_FULFILLMENT_LABEL = 'Processing Custom';

    /**
     * Custom Order-State code
     */
    const ORDER_STATE_CUSTOM_CODE = 'some_custom_state';

    /**
     * Custom Order-Status code
     */
    const ORDER_STATUS_CUSTOM_CODE = 'some_custom_status';

    /**
     * Custom Order-Status label
     */
    const ORDER_STATUS_CUSTOM_LABEL = 'Some Custom Status';

    /**
     * Status Factory
     *
     * @var StatusFactory
     */
    protected $statusFactory;

    /**
     * Status Resource Factory
     *
     * @var StatusResourceFactory
     */
    protected $statusResourceFactory;

    /**
     * InstallData constructor
     *
     * @param StatusFactory $statusFactory
     * @param StatusResourceFactory $statusResourceFactory
     */
    public function __construct(
        StatusFactory $statusFactory,
        StatusResourceFactory $statusResourceFactory
    ) {
        $this->statusFactory = $statusFactory;
        $this->statusResourceFactory = $statusResourceFactory;
    }

    /**
     * Installs data for a module
     *
     * @param ModuleDataSetupInterface $setup
     * @param ModuleContextInterface $context
     *
     * @return void
     *
     * @throws Exception
     */
    public function install(ModuleDataSetupInterface $setup, ModuleContextInterface $context)
    {
        $this->addNewOrderProcessingStatus();
        $this->addNewOrderStateAndStatus();
    }

    /**
     * Create new order processing status and assign it to the existent state
     *
     * @return void
     *
     * @throws Exception
     */
    protected function addNewOrderProcessingStatus()
    {
        /** @var StatusResource $statusResource */
        $statusResource = $this->statusResourceFactory->create();
        /** @var Status $status */
        $status = $this->statusFactory->create();
        $status->setData([
            'status' => self::ORDER_STATUS_PROCESSING_FULFILLMENT_CODE,
            'label' => self::ORDER_STATUS_PROCESSING_FULFILLMENT_LABEL,
        ]);

        try {
            $statusResource->save($status);
        } catch (AlreadyExistsException $exception) {

            return;
        }

        $status->assignState(Order::STATE_PROCESSING, false, true);
    }

    /**
     * Create new custom order status and assign it to the new custom order state
     *
     * @return void
     *
     * @throws Exception
     */
    protected function addNewOrderStateAndStatus()
    {
        /** @var StatusResource $statusResource */
        $statusResource = $this->statusResourceFactory->create();
        /** @var Status $status */
        $status = $this->statusFactory->create();
        $status->setData([
            'status' => self::ORDER_STATUS_CUSTOM_CODE,
            'label' => self::ORDER_STATUS_CUSTOM_LABEL,
        ]);

        try {
            $statusResource->save($status);
        } catch (AlreadyExistsException $exception) {

            return;
        }

        $status->assignState(self::ORDER_STATE_CUSTOM_CODE, true, true);
    }
}

As you may have noticed, each method performs two steps. First of all, it creates a new order status and saves it, so it will appear in sales_order_status database table. Then it links the created status to an order state by adding a new record to the sales_order_status_state database table.

There are no separate table for order states, so each unique value in state column of sales_order_status_state table is considered as a separate order state.

Run the Setup Upgrade command in order to activate the module and execute the setup script:

php bin/magento setup:upgrade

Now you can check the results in Stores -> Settings -> Order Status.

How to create new order state and status programmatically in Magento 2

Thanks for reading!

Clients

Smart Brands Choose Us.

From startups backed by Mark Cuban and Sir Richard Branson, to some of the biggest eCommerce operations in the world, Atwix helps our clients deliver unparalleled eСommerce experiences. We’re proud to work with the following companies: