Every order has a status 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 explore how to programmatically create a new order state and status 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 installation data class is as follows. For instance, two similar methods have been created:
- 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.

Thanks for reading!
