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