Blog / WooCommerce/ WooCommerce Development: Bulk Custom Order Status & Form Field Customization

WooCommerce Development: Bulk Custom Order Status & Form Field Customization

WooCommerce 开发实战:批量处理自定义订单状态与表单字段定制

Introduction

When operating a WooCommerce-based e-commerce site, deep customization is key to improving management efficiency and user experience. This article explains how to implement two core features via code: 1) Adding bulk action functionality for custom order statuses to manage orders quickly, and 2) Adding custom dropdown fields to the user registration and 'My Account' pages to collect richer user information.

Adding Bulk Actions for Custom Order Statuses

WooCommerce's admin area supports bulk changes for core order statuses (e.g., Processing, Completed) by default. To add a bulk action option for a custom status (e.g., 'Awaiting Shipment'), you need to extend functionality via hooks.

Implementation Code

Add the following code to your theme's functions.php file or a custom functionality plugin.

// 1. Register bulk action option
add_filter( 'bulk_actions-edit-shop_order', 'custom_register_bulk_action' );
function custom_register_bulk_action( $bulk_actions ) {
    $bulk_actions['mark_awaiting_shipment'] = 'Mark as Awaiting Shipment';
    return $bulk_actions;
}

// 2. Process bulk action request
add_action( 'admin_action_mark_awaiting_shipment', 'custom_bulk_process_status' );
function custom_bulk_process_status() {
    if ( ! isset( $_REQUEST['post'] ) || ! is_array( $_REQUEST['post'] ) ) {
        return;
    }
    foreach ( $_REQUEST['post'] as $order_id ) {
        $order = wc_get_order( $order_id );
        if ( $order ) {
            $order->update_status( 'wc-awaiting-shipment', 'Status updated via bulk action', true );
        }
    }
    $location = add_query_arg( array(
        'post_type' => 'shop_order',
        'marked_awaiting_shipment' => 1,
        'changed' => count( $_REQUEST['post'] ),
        'ids' => join( ',', $_REQUEST['post'] ),
        'post_status' => 'all'
    ), 'edit.php' );
    wp_redirect( admin_url( $location ) );
    exit;
}

// 3. Display success notice
add_action( 'admin_notices', 'custom_bulk_action_admin_notice' );
function custom_bulk_action_admin_notice() {
    global $pagenow, $typenow;
    if ( $typenow == 'shop_order' && $pagenow == 'edit.php' && isset( $_REQUEST['marked_awaiting_shipment'] ) ) {
        $count = intval( $_REQUEST['changed'] );
        echo '<div class="updated"><p>Successfully updated status for ' . $count . ' order(s).</p></div>';
    }
}

Key Points

  • Hook Match: The edit-shop_order in bulk_actions-edit-shop_order is the screen ID for the order list page.
  • Action Naming: The second part of admin_action_mark_awaiting_shipment must match the registered array key (mark_awaiting_shipment).
  • Status Identifier: The status identifier used in update_status() (e.g., wc-awaiting-shipment) must be a valid, registered status in the system.

Adding a Custom Dropdown Field to the Registration Form

To collect user attributes (e.g., customer type), we need to add a dropdown selection to the registration form and ensure data is saved and displayed correctly.

Complete Implementation Code

The following code demonstrates adding a 'Customer Type' field, covering frontend display, data saving, and backend/frontend edit display.

// 1. Add dropdown field to registration form
add_action( 'woocommerce_register_form', 'add_extra_register_select_field' );
function add_extra_register_select_field() {
    ?>
    <p class="form-row form-row-wide">
        <label for="customer_type">Customer Type <span class="required">*</span></label>
        <select id="customer_type" name="customer_type" required>
            <option value="">Please select...</option>
            <option value="individual">Individual</option>
            <option value="company">Company</option>
            <option value="reseller">Reseller</option>
        </select>
    </p>
    <?php
}

// 2. Save field value on registration
add_action( 'woocommerce_created_customer', 'save_extra_register_fields' );
function save_extra_register_fields( $customer_id ) {
    if ( isset( $_POST['customer_type'] ) ) {
        update_user_meta( $customer_id, 'customer_type', sanitize_text_field( $_POST['customer_type'] ) );
    }
}

// 3. Display field in admin user edit page
add_action( 'show_user_profile', 'show_extra_register_fields_admin' );
add_action( 'edit_user_profile', 'show_extra_register_fields_admin' );
function show_extra_register_fields_admin( $user ) {
    $value = get_user_meta( $user->ID, 'customer_type', true );
    ?>
    <h3>Additional Information</h3>
    <table class="form-table">
        <tr>
            <th><label for="customer_type">Customer Type</label></th>
            <td>
                <select id="customer_type" name="customer_type">
                    <option value="">Please select...</option>
                    <option value="individual" <?php selected( $value, 'individual' ); ?>>Individual</option>
                    <option value="company" <?php selected( $value, 'company' ); ?>>Company</option>
                    <option value="reseller" <?php selected( $value, 'reseller' ); ?>>Reseller</option>
                </select>
            </td>
        </tr>
    </table>
    <?php
}

// 4. Display field in frontend 'My Account' edit page
add_action( 'woocommerce_edit_account_form', 'show_extra_register_fields_frontend' );
function show_extra_register_fields_frontend() {
    $user_id = get_current_user_id();
    $value = get_user_meta( $user_id, 'customer_type', true );
    ?>
    <p class="form-row form-row-wide">
        <label for="customer_type">Customer Type <span class="required">*</span></label>
        <select id="customer_type" name="customer_type">
            <option value="">Please select...</option>
            <option value="individual" <?php selected( $value, 'individual' ); ?>>Individual</option>
            <option value="company" <?php selected( $value, 'company' ); ?>>Company</option>
            <option value="reseller" <?php selected( $value, 'reseller' ); ?>>Reseller</option>
        </select>
    </p>
    <?php
}

// 5. Save updates from admin and frontend edit pages
add_action( 'personal_options_update', 'save_extra_register_fields_admin' );
add_action( 'edit_user_profile_update', 'save_extra_register_fields_admin' );
add_action( 'woocommerce_save_account_details', 'save_extra_register_fields_admin' );
function save_extra_register_fields_admin( $customer_id ) {
    if ( isset( $_POST['customer_type'] ) ) {
        update_user_meta( $customer_id, 'customer_type', sanitize_text_field( $_POST['customer_type'] ) );
    }
}

Functional Logic

  1. Frontend Registration: Uses the woocommerce_register_form hook to insert HTML below the standard form.
  2. Data Storage: Uses update_user_meta to store data in the wp_usermeta table, with sanitization for security.
  3. Multi-end Display: Hooks into backend and frontend edit pages to ensure the field is visible and editable everywhere.
  4. Data Synchronization: Save logic is hooked to corresponding update actions to ensure changes take effect.

Modifying Payment Gateway Title and Description

Sometimes, to improve conversion or align with brand tone, you need to modify the text displayed for a payment gateway. For example, changing 'Cash on Delivery' to a more descriptive phrase.

// Modify payment gateway title
add_filter( 'woocommerce_gateway_title', 'custom_payment_gateway_title', 25, 2 );
function custom_payment_gateway_title( $title, $gateway_id ) {
    if ( 'cod' === $gateway_id ) { // 'cod' is the default ID for Cash on Delivery
        $title = 'Pay with Cash or Card on Delivery';
    }
    return $title;
}

// Modify payment gateway description
add_filter( 'woocommerce_gateway_description', 'custom_payment_gateway_description', 25, 2 );
function custom_payment_gateway_description( $description, $gateway_id ) {
    if ( 'cod' === $gateway_id ) {
        $description = 'Pay when your order is delivered. Supports cash and mobile payments.';
    }
    return $description;
}

Summary and Considerations

  • Code Placement: Place code in a child theme's functions.php or a custom plugin to avoid loss during theme updates.
  • Security: Always sanitize user input (e.g., $_POST) using functions like sanitize_text_field to prevent XSS attacks.
  • Testing Environment: Changes to order status and payment logic affect core operations. Test thoroughly on a staging site before deploying to production.
  • Extensibility: The provided code is a template. Adjust field names, option values, and logic based on your specific business needs.

Post a Comment

Your email will not be published. Required fields are marked with *.