Add a Custom Order Status to WooCommerce

Intermediate

PHP

Details

This little code snippet adds a new dimension to your WooCommerce order management by allowing you to create, manage, and use custom order statuses. When added to your child theme’s functions.php file, it provides the following features:

  1. A new ‘Custom Order Status’ menu item under WooCommerce in your WordPress admin panel.
  2. An interface to easily add and delete custom order statuses.
  3. Seamless integration of your custom statuses into the WooCommerce order system.
  4. Custom statuses appear in order lists, filters, and bulk actions.

Perfect for stores with unique workflows, this code gives you the flexibility to tailor your order statuses to your specific business needs, all without the need for a separate plugin.

Snippets Demo

Add a custom order status to WooCommerce

Code

1

Description:
Add this code to your Child Themes functions.php file to add a new menu item under WooCommerce where you can maintain custom order statuses for WooCommerce.

Language: PHP

Where to add: functions.php

                        /*
 * Snippet Name:     Create and Manage Custom WooCommerce Order Statuses.
 * Snippet Author:   diviengine.com
 */

// Add menu item under WooCommerce
add_action('admin_menu', 'add_custom_order_status_menu');
function add_custom_order_status_menu() {
    add_submenu_page(
        'woocommerce',
        'Custom Order Status',
        'Custom Order Status',
        'manage_woocommerce',
        'custom-order-status',
        'custom_order_status_page'
    );
}

// Custom Order Status page content
function custom_order_status_page() {
    ?>
    <div class="wrap">
        <h1>Custom Order Status Manager</h1>
		<!-- Info Box Start -->
        <div class="de-info-box" style="border: 1px solid #ccd0d4; background-color: #f1f1f1; padding: 15px; margin-bottom: 20px; display: flex; align-items: center;">
            <img src="https://diviengine.com/wp-content/uploads/2023/12/logo.png" alt="Divi Engine Logo" style="max-width: 150px; height: auto; margin-right: 20px;">
            <p style="margin: 0; font-size: 14px; color: #23282d;">
                <?php esc_html_e( 'Get 10% off any plugin or All-Access Pass with the coupon code ', 'mytheme' ); ?>
                <strong>snippetengine</strong>
                <?php esc_html_e( ' at ', 'mytheme' ); ?>
                <a href="https://diviengine.com" target="_blank" style="color: #0073aa; text-decoration: none;"><?php esc_html_e( 'diviengine.com', 'mytheme' ); ?></a>.
            </p>
        </div>
        <!-- Info Box End -->
        <?php
        // Handle form submissions
        if ($_SERVER['REQUEST_METHOD'] == 'POST') {
            if (isset($_POST['add_status'])) {
                add_custom_order_status(sanitize_text_field($_POST['status_name']), sanitize_text_field($_POST['status_label']));
            } elseif (isset($_POST['edit_status'])) {
                edit_custom_order_status(sanitize_text_field($_POST['status_id']), sanitize_text_field($_POST['status_name']), sanitize_text_field($_POST['status_label']));
            } elseif (isset($_POST['delete_status'])) {
                delete_custom_order_status(sanitize_text_field($_POST['status_id']));
            }
        }

        // Display existing custom statuses
        $custom_statuses = get_option('custom_order_statuses', array());
        ?>
        <table class="wp-list-table widefat fixed striped">
            <thead>
                <tr>
                    <th>ID</th>
                    <th>Name</th>
                    <th>Label</th>
                    <th>Actions</th>
                </tr>
            </thead>
            <tbody>
                <?php foreach ($custom_statuses as $id => $status): ?>
                <tr>
                    <td><?php echo esc_html($id); ?></td>
                    <td>
                        <span class="status-name-display" style="display: <?php echo isset($_POST['edit_status']) && $_POST['status_id'] == $id ? 'none' : 'inline'; ?>;">
                            <?php echo esc_html($status['name']); ?>
                        </span>
                        <form method="post" style="display: <?php echo isset($_POST['edit_status']) && $_POST['status_id'] == $id ? 'inline' : 'none'; ?>;">
                            <input type="text" name="status_name" value="<?php echo esc_attr($status['name']); ?>" required>
                            <input type="hidden" name="status_id" value="<?php echo esc_attr($id); ?>">
                    </td>
                    <td>
                        <span class="status-label-display" style="display: <?php echo isset($_POST['edit_status']) && $_POST['status_id'] == $id ? 'none' : 'inline'; ?>;">
                            <?php echo esc_html($status['label']); ?>
                        </span>
                            <input type="text" name="status_label" value="<?php echo esc_attr($status['label']); ?>" style="display: <?php echo isset($_POST['edit_status']) && $_POST['status_id'] == $id ? 'inline' : 'none'; ?>;" required>
                    </td>
                    <td>
                            <?php if (isset($_POST['edit_status']) && $_POST['status_id'] == $id): ?>
                                <input type="submit" name="edit_status" value="Save" class="button button-small">
                                <a href="<?php echo esc_url(admin_url('admin.php?page=custom-order-status')); ?>" class="button button-small">Cancel</a>
                            </form>
                            <?php else: ?>
                                <form method="post" style="display: inline;">
                                    <input type="hidden" name="status_id" value="<?php echo esc_attr($id); ?>">
                                    <input type="submit" name="edit_status" value="Edit" class="button button-small">
                                    <input type="submit" name="delete_status" value="Delete" class="button button-small" onclick="return confirm('Are you sure you want to delete this status?');">
                                </form>
                            <?php endif; ?>
                    </td>
                </tr>
                <?php endforeach; ?>
            </tbody>
        </table>

        <h2><?php esc_html_e('Add New Custom Status', 'mytheme'); ?></h2>
        <form method="post">
            <input type="text" name="status_name" placeholder="<?php esc_attr_e('Status Name', 'mytheme'); ?>" required>
            <input type="text" name="status_label" placeholder="<?php esc_attr_e('Status Label', 'mytheme'); ?>" required>
            <input type="submit" name="add_status" value="<?php esc_attr_e('Add Status', 'mytheme'); ?>" class="button button-primary">
        </form>
    </div>
    <?php
}

// Add custom order status
function add_custom_order_status($name, $label) {
    $custom_statuses = get_option('custom_order_statuses', array());
    $id = 'wc-' . sanitize_title($name);
    if (!isset($custom_statuses[$id])) {
        $custom_statuses[$id] = array(
            'name' => $name,
            'label' => $label
        );
        update_option('custom_order_statuses', $custom_statuses);
    } else {
        // Handle duplicate status error
        add_action('admin_notices', function() use ($name) {
            echo '<div class="notice notice-error"><p>' . sprintf(esc_html__('Order status "%s" already exists.', 'mytheme'), esc_html($name)) . '</p></div>';
        });
    }
}

// Edit custom order status
function edit_custom_order_status($id, $name, $label) {
    $custom_statuses = get_option('custom_order_statuses', array());
    if (isset($custom_statuses[$id])) {
        $custom_statuses[$id]['name'] = $name;
        $custom_statuses[$id]['label'] = $label;
        update_option('custom_order_statuses', $custom_statuses);
    }
}

// Delete custom order status
function delete_custom_order_status($id) {
    $custom_statuses = get_option('custom_order_statuses', array());
    if (isset($custom_statuses[$id])) {
        unset($custom_statuses[$id]);
        update_option('custom_order_statuses', $custom_statuses);
    }
}

// Register custom order statuses
add_filter('woocommerce_register_shop_order_post_statuses', 'register_custom_order_statuses');
function register_custom_order_statuses($order_statuses) {
    $custom_statuses = get_option('custom_order_statuses', array());
    foreach ($custom_statuses as $id => $status) {
        $order_statuses[$id] = array(
            'label' => $status['label'],
            'public' => false,
            'exclude_from_search' => false,
            'show_in_admin_all_list' => true,
            'show_in_admin_status_list' => true,
            'label_count' => _n_noop($status['label'] . ' <span class="count">(%s)</span>', $status['label'] . ' <span class="count">(%s)</span>', 'woocommerce'),
        );
    }
    return $order_statuses;
}

// Add custom order statuses to order status list
add_filter('wc_order_statuses', 'add_custom_order_statuses_to_list');
function add_custom_order_statuses_to_list($order_statuses) {
    $custom_statuses = get_option('custom_order_statuses', array());
    foreach ($custom_statuses as $id => $status) {
        $order_statuses[$id] = $status['label'];
    }
    return $order_statuses;
}

// Add custom order statuses to bulk actions
add_filter('bulk_actions-edit-shop_order', 'add_custom_order_statuses_to_bulk_actions');
function add_custom_order_statuses_to_bulk_actions($bulk_actions) {
    $custom_statuses = get_option('custom_order_statuses', array());
    foreach ($custom_statuses as $id => $status) {
        $bulk_actions['mark_' . substr($id, 3)] = 'Change status to ' . $status['label'];
    }
    return $bulk_actions;
}

// Add custom statuses to order list actions dropdown
add_action('woocommerce_admin_order_actions_end', 'add_custom_order_statuses_to_order_actions', 20, 1);
function add_custom_order_statuses_to_order_actions($order) {
    $custom_statuses = get_option('custom_order_statuses', array());
    foreach ($custom_statuses as $id => $status) {
        if ($order->get_status() !== substr($id, 3)) {
            echo '<a class="button tips custom-order-status-button" href="' . wp_nonce_url(admin_url('post.php?post=' . $order->get_id() . '&action=edit&mark_order_status=' . $id), 'woocommerce-mark-order-status') . '" aria-label="' . esc_attr__('Change order status to ', 'woocommerce') . esc_html($status['label']) . '" data-tip="' . esc_attr($status['label']) . '">' . esc_html($status['label']) . '</a>';
        }
    }
}
                    

Related Snippets

Explore more from Divi Engine

Divi Form Builder

Divi Form Builder

From simple contact forms to complex frontend post or product creation, Divi Form Builder has you covered.

Divi Form Builder
Find out more
Divi Machine

Divi Machine

Build complex websites that displays dynamic fields you can filter, search and so much more with the Divi Builder.

Divi Machine
Find out more
Divi BodyCommerce

Divi BodyCommerce

A versatile toolkit for developers using Divi and WooCommerce together, designed to boost your e-commerce site and achieve greater conversion rates.

Divi BodyCommerce
Find out more
Divi Loop Extender

Divi Loop Extender

Unlock the Full Power of Divi 5 Loop Builder Add advanced sorting, filtering, and relationship logic right inside the Visual Builder.

Divi Loop Extender
Find out more
Divi Membership

Divi Membership

Monetize your Divi websites by transforming them into membership sites with seamless subscription management, user-friendly interfaces, and customizable membership tiers.

Divi Membership
Find out more
Divi Machine Accounts

Divi Machine Accounts

Build an account area for your customers to edit their details, access wishlist, submitted posts and more. *Note: Requires Divi Machine installed and active

Divi Machine Accounts
Find out more
Divi Ajax Filter

Divi Ajax Filter

Filter WooCommerce, Posts & Custom Posts without reloading the page.

Divi Ajax Filter
Find out more
Divi Mobile

Divi Mobile

Divi Mobile helps you create beautiful looking mobile menus without having to code.

Divi Mobile
Find out more
Divi Nitro

Divi Nitro

Give your Divi website that extra boost of speed with our Divi Nitro plugin to enhance your customer's experience.

Divi Nitro
Find out more
Divi Protect

Divi Protect

Password protect the content of your Divi website with our Divi Protect plugin. Keep unwanted eyes out!

Divi Protect
Find out more
Divi Mega Menu

Divi Mega Menu

Improve the user experience of your Divi website with our Divi Mega Menu plugin by creating dynamic menus using the Divi Builder.

Divi Mega Menu
Find out more