Viewed   219 times

I am currently successfully adding a field to my WooCommerce product pages which is showing the value:

  • in the cart (front end),
  • on checkout page (front end),
  • on order page (front end),
  • and in admin individual order page (back end).

The problem: It isn't showing as a custom field in the admin order "custom fields" Metabox with the value inside it, but just as a text in the order page.

Here is my working code:

// Add the field to the product
add_action('woocommerce_before_add_to_cart_button', 'my_custom_checkout_field');

function my_custom_checkout_field() {
    echo '<div id="my_custom_checkout_field"><h3>'.__('My Field').'</h3>';
    echo  '<label>fill in this field</label> <input type="text" name="my_field_name">';
    echo '</div>';
}

// Store custom field
function save_my_custom_checkout_field( $cart_item_data, $product_id ) {
    if( isset( $_REQUEST['my_field_name'] ) ) {
        $cart_item_data[ 'my_field_name' ] = $_REQUEST['my_field_name'];
        /* below statement make sure every add to cart action as unique line item */
        $cart_item_data['unique_key'] = md5( microtime().rand() );
    }
    return $cart_item_data;
}
add_action( 'woocommerce_add_cart_item_data', 'save_my_custom_checkout_field', 10, 2 );

// Render meta on cart and checkout
function render_meta_on_cart_and_checkout( $cart_data, $cart_item = null ) {
    $custom_items = array();
    /* Woo 2.4.2 updates */
    if( !empty( $cart_data ) ) {
        $custom_items = $cart_data;
    }
    if( isset( $cart_item['my_field_name'] ) ) {
        $custom_items[] = array( "name" => 'My Field', "value" => $cart_item['my_field_name'] );
    }
    return $custom_items;
}
add_filter( 'woocommerce_get_item_data', 'render_meta_on_cart_and_checkout', 10, 2 );

// This is what I think needs changing?

function subscription_order_meta_handler( $item_id, $values, $cart_item_key ) {
    if( isset( $values['my_field_name'] ) ) {
        wc_add_order_item_meta( $item_id, "My Field", $values['my_field_name'] );
    }
}
add_action( 'woocommerce_add_order_item_meta', 'subscription_order_meta_handler', 1, 3 );

I think it is this last bit of the code that needs changing. It currently shows the text under the order item, so perhaps I need to adjust wc_add_order_item_meta to something else?

I've tried everything but it doesn't seem to work. I can get it to work when my field is on the checkout page but not when I pull it from the product page.

Perhaps I am missing a checkout process snippet?

 Answers

2

UPDATE 2017/11/02 (Works perfectly in Woocommerce 3+)

First I have get everything working has expected, except getting the value for my_field_name in back end "Custom fields" Metabox within Order pages.

Then after a real nightmare, I have found a pretty nice working solution, better than before. In back end you have now a Custom metabox with the custom field my_field_name displaying the right value, like in this screenshot:


My code is divided In 2 parts.

1) The backend Metabox in Order pages, with an editable field showing the correct value coming from a custom field on the product pages (in front end):

// Adding Meta container admin shop_order pages
add_action( 'add_meta_boxes', 'mv_add_meta_boxes' );
if ( ! function_exists( 'mv_add_meta_boxes' ) )
{
    function mv_add_meta_boxes()
    {
        add_meta_box( 'mv_other_fields', __('My Field','woocommerce'), 'mv_add_other_fields_for_packaging', 'shop_order', 'side', 'core' );
    }
}

// Adding Meta field in the meta container admin shop_order pages
if ( ! function_exists( 'mv_add_other_fields_for_packaging' ) )
{
    function mv_add_other_fields_for_packaging()
    {
        global $post;

        $meta_field_data = get_post_meta( $post->ID, '_my_field_slug', true ) ? get_post_meta( $post->ID, '_my_field_slug', true ) : '';

        echo '<input type="hidden" name="mv_other_meta_field_nonce" value="' . wp_create_nonce() . '">
        <p style="border-bottom:solid 1px #eee;padding-bottom:13px;">
            <input type="text" style="width:250px;";" name="my_field_name" placeholder="' . $meta_field_data . '" value="' . $meta_field_data . '"></p>';

    }
}

// Save the data of the Meta field
add_action( 'save_post', 'mv_save_wc_order_other_fields', 10, 1 );
if ( ! function_exists( 'mv_save_wc_order_other_fields' ) )
{

    function mv_save_wc_order_other_fields( $post_id ) {

        // We need to verify this with the proper authorization (security stuff).

        // Check if our nonce is set.
        if ( ! isset( $_POST[ 'mv_other_meta_field_nonce' ] ) ) {
            return $post_id;
        }
        $nonce = $_REQUEST[ 'mv_other_meta_field_nonce' ];

        //Verify that the nonce is valid.
        if ( ! wp_verify_nonce( $nonce ) ) {
            return $post_id;
        }

        // If this is an autosave, our form has not been submitted, so we don't want to do anything.
        if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
            return $post_id;
        }

        // Check the user's permissions.
        if ( 'page' == $_POST[ 'post_type' ] ) {

            if ( ! current_user_can( 'edit_page', $post_id ) ) {
                return $post_id;
            }
        } else {

            if ( ! current_user_can( 'edit_post', $post_id ) ) {
                return $post_id;
            }
        }
        // --- Its safe for us to save the data ! --- //

        // Sanitize user input  and update the meta field in the database.
        update_post_meta( $post_id, '_my_field_slug', $_POST[ 'my_field_name' ] );
    }
}

2) Front end / Back end:

• The product page custom field (front end).
• Displaying this data on cart, checkout pages and thank you order (front end).
• Displaying data on order page (back end)

// Add the field to the product
add_action('woocommerce_before_add_to_cart_button', 'my_custom_product_field');
function my_custom_product_field() {
    echo '<div id="my_custom_field">
        <label>' . __( 'My Field') . ' </label>
        <input type="text" name="my_field_name" value="">
    </div><br>';
}

// Store custom field
add_filter( 'woocommerce_add_cart_item_data', 'save_my_custom_product_field', 10, 2 );
function save_my_custom_product_field( $cart_item_data, $product_id ) {
    if( isset( $_REQUEST['my_field_name'] ) ) {
        $cart_item_data[ 'my_field_name' ] = $_REQUEST['my_field_name'];
        // below statement make sure every add to cart action as unique line item
        $cart_item_data['unique_key'] = md5( microtime().rand() );
        WC()->session->set( 'my_order_data', $_REQUEST['my_field_name'] );
    }
    return $cart_item_data;
}

// Add a hidden field with the correct value to the checkout
add_action( 'woocommerce_after_order_notes', 'my_custom_checkout_field' );
function my_custom_checkout_field( $checkout ) {
    $value = WC()->session->get( 'my_order_data' );
    echo '<div id="my_custom_checkout_field">
            <input type="hidden" class="input-hidden" name="my_field_name" id="my_field_name" value="' . $value . '">
    </div>';
}

// Save the order meta with hidden field value
add_action( 'woocommerce_checkout_update_order_meta', 'my_custom_checkout_field_update_order_meta' );
function my_custom_checkout_field_update_order_meta( $order_id ) {
    if ( ! empty( $_POST['my_field_name'] ) ) {
        update_post_meta( $order_id, '_my_field_slug', $_POST['my_field_name'] );
    }
}

// Display field value on the order edit page (not in custom fields metabox)
add_action( 'woocommerce_admin_order_data_after_billing_address', 'my_custom_checkout_field_display_admin_order_meta', 10, 1 );
function my_custom_checkout_field_display_admin_order_meta($order){
    $my_custom_field = get_post_meta( $order->id, '_my_field_slug', true );
    if ( ! empty( $my_custom_field ) ) {
        echo '<p><strong>'. __("My Field", "woocommerce").':</strong> ' . get_post_meta( $order->id, '_my_field_slug', true ) . '</p>';
    }
}

// Render meta on cart and checkout
add_filter( 'woocommerce_get_item_data', 'render_meta_on_cart_and_checkout', 10, 2 );
function render_meta_on_cart_and_checkout( $cart_data, $cart_item = null ) {
    $custom_items = array();
    if( !empty( $cart_data ) ) $custom_items = $cart_data;

    if( isset( $cart_item['my_field_name'] ) )
        $custom_items[] = array( "name" => 'My Field', "value" => $cart_item['my_field_name'] );

    return $custom_items;
}

// Add the information as meta data so that it can be seen as part of the order
add_action('woocommerce_add_order_item_meta','add_values_to_order_item_meta', 10, 3 );
function add_values_to_order_item_meta( $item_id, $cart_item, $cart_item_key ) {
    // lets add the meta data to the order (with a label as key slug)
    if( ! empty( $cart_item['my_field_name'] ) )
        wc_add_order_item_meta($item_id, __('My field label name'), $cart_item['my_field_name'], true);
}

Everything is working as expected now.

Sunday, September 4, 2022
5

New Update for WC 3.3+: Custom action button in admin orders list on Woocommerce 3.3+

Here is the way to add an action button in admin order list with a custom link related to tracking (opening the link in a new window as requested):

// Add your custom order action button
add_action( 'woocommerce_admin_order_actions_end', 'add_custom_order_actions_button', 100, 1 );
function add_custom_order_actions_button( $order ) {

    // Get the tracking number
    $traking_number = get_post_meta( $order->get_id(), '_aftership_tracking_number', true );
    if( empty($traking_number) ) return;

    // Prepare the button data
    $url    = esc_url('https://track.aftership.com/'.$traking_number.'?');
    $name   = esc_attr( __('Tracking', 'woocommerce' ) );
    $action = esc_attr( 'view tracking' ); // keep "view" class for a clean button CSS

    // Set the action button
    printf( '<a class="button tips %s" href="%s" data-tip="%s" target="_blank">%s</a>', $action, $url, $name, $name );
}

// The icon of your action button (CSS)
add_action( 'admin_head', 'add_custom_order_actions_button_css' );
function add_custom_order_actions_button_css() {
    echo '<style>.view.tracking::after { font-family: woocommerce; content: "e005" !important; }</style>';
}

Code goes in function.php file of your active child theme (or theme) or also in any plugin file.

Tested and works. You will get something like:


Now To make your tracking number clickable you will replace this function in your code:

add_action( 'manage_shop_order_posts_custom_column', 'custom_order_list_column_content', 10, 2 );
function custom_order_list_column_content( $column, $post_id )
{

    // HERE get the data from your custom field (set the correct meta key below)
    $astracking = get_post_meta( $post_id, '_aftership_tracking_number', true );
    if( empty($astracking)) $astracking = '';

    switch ( $column )
    {
        case 'order_astracking' :
            echo '<span><a href="https://track.aftership.com/'.$astracking.'?" target="_blank">'.$astracking . '</a></span>'; // display the data
            break;
    }
}
Sunday, October 9, 2022
 
naaff
 
1

Update 3

For manual backend orders, you can try to use woocommerce_before_save_order_item dedicated action hook as follow (code based on your question code):

add_action( 'woocommerce_before_save_order_item', 'action_before_save_order_item_callback' );
function action_before_save_order_item_callback( $item ) {
    $cost_centre = $item->get_meta('_cost_centre');
    // If custom meta data is not saved as order item
    if ( empty($cost_centre) ) {
        // Get custom meta data from the product
        $cost_centre = get_post_meta( $item->get_product_id(), '_cost_centre', true );
        $cost_centre = empty($cost_centre) ? 'MFEG' : $cost_centre;
        
        // Save it as custom order item (if defined)
        $item->update_meta_data( '_cost_centre', $cost_centre );
    }
}

Code goes in functions.php file of your active child theme (or active theme). Tested and works.


Addition: Make the order item custom meta data visible to customer

If you want this order item meta data to be visible on customer orders and email notifications, you will replace the order item meta key from '_cost_centre' to 'Cost centre' as follow:

add_action( 'woocommerce_before_save_order_item', 'action_before_save_order_item_callback' );
function action_before_save_order_item_callback( $item ) {
    $cost_centre = $item->get_meta('_cost_centre');
    // If custom meta data is not saved as order item
    if ( empty($cost_centre) ) {
        // Get custom meta data from the product
        $cost_centre = get_post_meta( $item->get_product_id(), 'Cost centre', true );
        $cost_centre = empty($cost_centre) ? 'MFEG' : $cost_centre;
        
        // Save it as custom order item (if defined)
        $item->update_meta_data( 'Cost centre', $cost_centre );
    }
}

This time it will be visible on customer orders and emails.

You will need also to change your last function on your question code to:

// Order items: Save product "Cost centre" as visible order item meta data
add_action('woocommerce_checkout_create_order_line_item', 'save_file_type_as_order_item_meta', 20, 4);
function save_file_type_as_order_item_meta($item, $cart_item_key, $values, $order) {
    if ( $cost_centre = $values['data']->get_meta('_cost_centre') ) {
        $item->update_meta_data( 'Cost centre', $cost_centre ); // Save as order item (visible everywhere)
    }
}

Note: When the order item custom meta key starts with an underscore, it's hidden.

Sunday, September 4, 2022
 
2

The following will get the parent variable product custom field if it is a product variation with no custom field set for it:

add_action( 'woocommerce_before_order_itemmeta', 'add_admin_order_item_custom_fields', 10, 2 );
function add_admin_order_item_custom_fields( $item_id, $item ) {
    // Targeting line items type only
    if( $item->get_type() !== 'line_item' ) return;

    $product       = $item->get_product();
    $model_number  = $product->get_meta('model_number');
    
    // Get the parent variable product custom field if empty value
    if( $item->get_variation_id() > 0 && empty($model_number) ) {
        $parent_product = wc_get_product( $item->get_product_id() );
        $model_number   = $parent_product->get_meta('model_number');
    }

    if ( ! empty($model_number) ) {
        echo '<table cellspacing="0" class="display_meta"><tr>
            <th>' . __("Model number", "woocommerce") . ': </th>
            <td>' . $model_number . '</td>
        </tr></table>';
    }
}

Code goes in functions.php file of the active child theme (or active theme). Tested and works.

Thursday, September 1, 2022
4

Here is the way to do it with that 2 custom functions hooked. The first one create the column with the title, the second one populate the column with the products data. But you will need to set in that second function, the correct corresponding meta_key to get the data.

Here is that code:

// ADDING A CUSTOM COLUMN TITLE TO ADMIN PRODUCTS LIST
add_filter( 'manage_edit-product_columns', 'custom_product_column',11);
function custom_product_column($columns)
{
   //add columns
   $columns['delivery'] = __( 'Delivery time','woocommerce'); // title
   return $columns;
}

// ADDING THE DATA FOR EACH PRODUCTS BY COLUMN (EXAMPLE)
add_action( 'manage_product_posts_custom_column' , 'custom_product_list_column_content', 10, 2 );
function custom_product_list_column_content( $column, $product_id )
{
    global $post;

    // HERE get the data from your custom field (set the correct meta key below)
    $delivery_time = get_post_meta( $product_id, '_delivery_time', true );

    switch ( $column )
    {
        case 'delivery' :
            echo $delivery_time; // display the data
            break;
    }
}

Code goes in function.php file of your active child theme (or theme) or also in any plugin file.

Tested and works.


How to get the correct meta_key slug:

To find the correct meta_key slug corresponding to the "delivery time", you should need to make search in your database using PhpMyAdmin. You will have to search for delivery term in wp_postmeta table this way:

Then you will get this kind of results (here there is just 1 line with a fake slug):

So now you should be able to get the correct slug name (like this fake "_delivery_date" one)


Related answer (for orders): Add custom columns to admin orders list in WooCommerce backend

Tuesday, September 27, 2022
 
eder
 
Only authorized users can answer the search term. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :