I am trying to achieve an order prefix between "Parent Order" and "Sub Orders". Currently I have the order prefix's working for products in specific categories, so 'Merchandise' Categories and products within have the order number prefix 'M' and the Categories and products within 'Coffee' have the prefix 'C', this all works fine, brilliantly see the image attached.
I do have subscription products and single products within the store but I mainly just want the one 'P' to show up for the main order regardless of the category the product is within.
I need and would like help adding for the parent order a 'P' so that I can distinguish between Parent and sub orders, (please see attached image) I cannot for the life of me figure out this function currently my code is this:
add_filter( 'woocommerce_order_number', 'change_woocommerce_order_number' );
function change_woocommerce_order_number( $order_id ) {
$order = wc_get_order( $order_id );
$category = array();
if ( ! empty($order) ) {
foreach ( $order->get_items() as $item_id => $item ) {
$product_id = $item['product_id'];
$terms = get_the_terms ( $product_id, 'product_cat' );
$parent_id = $order->get_parent_id( $parent, 'product_id' );
if ( ! empty($terms) ) {
foreach ( $terms as $term ) {
$category[] = $term->term_id;
}
}
}
}
if ( $parent ) {
$order_id = 'P - '. $order_id;
} else {
if ( in_array( 23, $category ) ) {
$order_id = 'M - '. $order_id;
} else {
if ( in_array( 16, $category ) ) {
$order_id = 'C - '. $order_id;
}
}
return $order_id;
}
}
The 'M' & 'C' work perfectly... just the parent order 'P' does not so instead of a 'P' I have what seems to be a random M or C in the parent order section. All my orders split in categories and this still works as expected (please see image). I mainly just need the 'P' Main Parent order on the left from the image attached
Parent Order Prefix 'P' Location
Thanks in advance.
You are currently returning the order number only within the else statement.
A value should always be returned at the end of the function.
Also the $parent variable is not set and you are getting the parent id the wrong way.
Try this, hope it works.
The get_parent_id() method returns 0 if the function has no parent order.
add_filter( 'woocommerce_order_number', 'change_woocommerce_order_number' );
function change_woocommerce_order_number( $order_id ) {
$order = wc_get_order( $order_id );
$category = array();
if ( ! empty($order) ) {
foreach ( $order->get_items() as $item_id => $item ) {
$product_id = $item['product_id'];
$terms = get_the_terms ( $product_id, 'product_cat' );
if ( ! empty($terms) ) {
foreach ( $terms as $term ) {
$category[] = $term->term_id;
}
}
}
}
$parent_id = $order->get_parent_id();
if ( $parent_id > 0 ) {
if ( in_array( 23, $category ) ) {
$order_id = 'M - '. $order_id;
} else {
if ( in_array( 16, $category ) ) {
$order_id = 'C - '. $order_id;
}
}
} else {
$order_id = 'P - '. $order_id;
}
return $order_id;
}
Also you could optimize the function like this:
// change the WooCommerce order number
add_filter( 'woocommerce_order_number', 'change_woocommerce_order_number' );
function change_woocommerce_order_number( $order_id ) {
$order = wc_get_order( $order_id );
$prefix = '';
$has_cat_id_23 = false;
$has_cat_id_16 = false;
// get the parent order id
$parent_id = $order->get_parent_id();
// if it does not have a parent order it returns the order number with the prefix "P - "
if ( $parent_id == 0 ) {
$prefix = 'P - ';
return $prefix . $order_id;
// otherwise set the prefix to the child orders
} else {
foreach ( $order->get_items() as $item_id => $item ) {
// if the product belongs to the product category with ID 23
if ( has_term( 23, 'product_cat', $item['product_id'] ) ) {
$has_cat_id_23 = true;
break;
}
// if the product belongs to the product category with ID 16
if ( has_term( 16, 'product_cat', $item['product_id'] ) ) {
$has_cat_id_16 = true;
break;
}
}
// if the product belongs to the product category with ID 23 set the prefix "M - "
if ( $has_cat_id_23 ) {
$prefix = 'M - ';
}
// if the product belongs to the product category with ID 16 set the prefix "C - "
if ( $has_cat_id_16 ) {
$prefix = 'C - ';
}
}
return $prefix . $order_id;
}
The code has been tested and works (I didn't install the plugin but actually only used Wordpress and WooCommerce functions and methods). The result will look like:
Try it out and let me know if it works for you too.
Related
As you can tell from the title I try to "automatically pre select the first in stock value in WooCommerce variable product dropdown".
Currently, I'm using the code below to pre-select the first variant, but I need this code to select the first IN STOCK variant. Any advice?
function fun_select_default_option( $args ) {
// Check the count of available options in dropdown
if ( count($args['options']) > 0 ) {
$args['selected'] = $args['options'][0];
}
return $args;
}
add_filter( 'woocommerce_dropdown_variation_attribute_options_args', 'fun_select_default_option', 10, 1 );
The callback function only contains $args, via $args['product'] however, you have access to the product variable object.
Based on that you can loop through the visible children. By the variation ID's you can get the product variation object(s).
With the use of get_stock_status() you can then determine the status.
So you get:
function filter_woocommerce_dropdown_variation_attribute_options_args( $args ) {
// Check the count of available options in dropdown
if ( count( $args['options'] ) > 0 ) {
// Initialize
$option_key = '';
// Get WC_Product_Variable Object
$product = $args['product'];
// Is a WC Product Variable
if ( is_a( $product, 'WC_Product_Variable' ) ) {
// Loop through children
foreach ( $product->get_visible_children() as $key => $variation_id ) {
// Get product variation object
$variation = wc_get_product( $variation_id );
// Is a WC Product Variation
if ( is_a( $variation, 'WC_Product_Variation' ) ) {
// Get stock status
$product_stock_status = $variation->get_stock_status();
// In stock
if ( $product_stock_status == 'instock' ) {
// Set key
$option_key = $key;
// Break
break;
}
}
}
}
// Finds whether a variable is a number
if ( is_numeric( $option_key ) ) {
// Selected
$args['selected'] = $args['options'][$option_key];
}
}
return $args;
}
add_filter( 'woocommerce_dropdown_variation_attribute_options_args', 'filter_woocommerce_dropdown_variation_attribute_options_args', 10, 1 );
Alternatively you can also use get_available_variations() and $variation['is_in_stock'].
The big difference with the above answer is that this answer will also see backorders, where backorder is allowed as instock because it does not take the specific stock status into account.
So then you get:
function filter_woocommerce_dropdown_variation_attribute_options_args( $args ) {
// Check the count of available options in dropdown
if ( count( $args['options'] ) > 0 ) {
// Initialize
$option_key = '';
// Get WC_Product_Variable Object
$product = $args['product'];
// Is a WC Product Variable
if ( is_a( $product, 'WC_Product_Variable' ) ) {
// Get an array of available variations for the current product
foreach ( $product->get_available_variations() as $key => $variation ) {
// Is in stock
$is_in_stock = $variation['is_in_stock'];
// True
if ( $is_in_stock ) {
// Set key
$option_key = $key;
// Break
break;
}
}
}
// Finds whether a variable is a number
if ( is_numeric( $option_key ) ) {
// Selected
$args['selected'] = $args['options'][$option_key];
}
}
return $args;
}
add_filter( 'woocommerce_dropdown_variation_attribute_options_args', 'filter_woocommerce_dropdown_variation_attribute_options_args', 10, 1 );
ACF is set up for post type on WooCommerce products. However, I am trying to add a custom column to WooCommerce orders list within Admin dashboard and add the products ACF field.
I have added the column to display after order_status, but I'm having problems getting the ACF field to display.
// ADD NEW COLUMN
add_filter( 'manage_edit-shop_order_columns', 'custom_shop_order_column', 20 );
function custom_shop_order_column($columns)
{
$reordered_columns = array();
foreach( $columns as $key => $column){
$reordered_columns[$key] = $column;
if( $key == 'order_status' ){
$reordered_columns['my-column'] = __( 'Location','theme_domain');
}
}
return $reordered_columns;
}
Here, adding ACF to new colum.
// ADD ACF FIELD TO COLUMN
add_action( 'manage_shop_order_posts_custom_column' , 'custom_orders_list_column_content', 20, 2 );
function custom_orders_list_column_content( $column, $post_id )
{
if ( 'Location' == $column_name ){
$product_id = method_exists( $product, 'get_id' ) ? $product->get_id() : $product->id;
echo get_field( 'location', $product_id );
}
return true;
}
Still learning and not sure how to do this, any advice?
An order generally consists of several products, therefore you cannot use $product_id directly, but you have to loop through the order items.
So you get:
/**
* Add columns
*/
function filter_manage_edit_shop_order_columns( $columns ) {
$reordered_columns = array();
foreach ( $columns as $key => $column ) {
$reordered_columns[$key] = $column;
if ( $key == 'order_status' ) {
$reordered_columns['my-column'] = __( 'Location','theme_domain' );
}
}
return $reordered_columns;
}
add_filter( 'manage_edit-shop_order_columns', 'filter_manage_edit_shop_order_columns', 10, 1 );
/**
* Populate columns
*/
function filter_manage_shop_order_posts_custom_column( $column, $post_id ) {
// Compare
if ( $column == 'my-column' ) {
// Get order
$order = wc_get_order( $post_id );
// Is a WC_Order
if ( is_a( $order, 'WC_Order' ) ) {
// Get items
$items = $order->get_items();
// Loop through
foreach ( $items as $key => $item ) {
// Product ID
$product_id = $item->get_variation_id() > 0 ? $item->get_variation_id() : $item->get_product_id();
// Get field
$address = get_field( 'location', $product_id );
// Output
echo ($address) ? '<div>Address: ' . $address . '</div>' : '<div>Address: No address found!</div>';
}
}
}
}
add_filter( 'manage_shop_order_posts_custom_column', 'filter_manage_shop_order_posts_custom_column', 10, 2 );
I need to automatically set a certain order status (different than processing) when getting a new order.
This is achieved by this function:
add_action('woocommerce_thankyou','change_order_status');
function change_order_status( $order_id ) {
if ( ! $order_id ) { return; }
$order = wc_get_order( $order_id );
if( 'processing'== $order->get_status() ) {
$order->update_status( 'wc-custom-status' );
}
}
This totally works. Now I only need this to happen when a product has a customization.
The way to customize a product is filling an input field before adding to cart. The input is attached to the item data:
// Add custom cart item data
add_filter( 'woocommerce_add_cart_item_data', 'add_custom_cart_item_data', 10, 2 );
function add_custom_cart_item_data( $cart_item_data, $product_id ){
if( isset($_POST['custom_text']) ) {
$cart_item_data['custom_text'] = sanitize_text_field( $_POST['custom_text'] );
$cart_item_data['unique_key'] = md5( microtime().rand() ); // Make each item unique
}
return $cart_item_data;
}
Then the custom text is retrieved and displayed in cart and in the order data using this:
// Display custom cart item data on cart and checkout
add_filter( 'woocommerce_get_item_data', 'display_custom_cart_item_data', 10, 2 );
function display_custom_cart_item_data( $cart_item_data, $cart_item ) {
if ( !empty( $cart_item['custom_text'] ) ){
$cart_item_data[] = array(
'name' => __('Customization', 'woocommerce'),
'value' => $cart_item['custom_text'] // Already sanitized field
);
}
return $cart_item_data;
}
// Save and display custom item data everywhere on orders and email notifications
add_action( 'woocommerce_checkout_create_order_line_item', 'add_product_custom_field_as_order_item_meta', 10, 4 );
function add_product_custom_field_as_order_item_meta( $item, $cart_item_key, $values, $order ) {
if ( isset($values['custom_text']) ) {
$item->update_meta_data('Add on', $values['custom_text'] );
}
}
I'm trying using the if ( isset($values['custom_text']) ) part as a trigger of the function to change the order status only if the product add on is set and other similar methods (like if ( !empty( $cart_item['custom_text'] ) ) but I'm not sure this is the way to go:
add_action('woocommerce_thankyou','change_order_status');
function change_order_status( $order_id ) {
if ( ! $order_id ) {return;}
$order = wc_get_order( $order_id );
if ( isset($values['custom_text']) ) {
if( 'processing'== $order->get_status() ) {
$order->update_status( 'wc-custom-status' );
}
}
}
This above does nothing. Am I anywhere near it with this approach?
EDIT: I tried this too
add_action('woocommerce_thankyou','change_order_status');
function change_order_status( $order_id ) {
if ( ! $order_id ) {return;}
$order = wc_get_order( $order_id );
foreach ( $order->get_items() as $item_id => $item ) {
$allmeta = $item->get_meta_data();
if ( isset($values['custom_text']) ) {
if( 'processing'== $order->get_status() ) {
$order->update_status( 'wc-custom-status' );
}
}
}
}
Your code contains some unnecessary steps as well as some shortcomings
For example, the woocommerce_add_cart_item_data hook contains 3 arguments versus 2
So you get:
// Add custom cart item data
function filter_add_cart_item_data( $cart_item_data, $product_id, $variation_id ) {
// Isset and NOT empty
if ( isset ( $_POST[ 'custom_text' ] ) && ! empty ( $_POST[ 'custom_text' ] ) ) {
$cart_item_data['custom_text'] = sanitize_text_field( $_POST['custom_text'] );
}
return $cart_item_data;
}
add_filter( 'woocommerce_add_cart_item_data', 'filter_add_cart_item_data', 10, 3 );
// Display custom cart item data on cart and checkout
function filter_woocommerce_get_item_data( $cart_data, $cart_item = null ) {
if ( isset ( $cart_item['custom_text'] ) ) {
$cart_data[] = array(
'name' => __( 'Customization', 'woocommerce' ),
'value' => $cart_item['custom_text']
);
}
return $cart_data;
}
add_filter( 'woocommerce_get_item_data', 'filter_woocommerce_get_item_data', 10, 2 );
// Add the information as meta data so that it can be seen as part of the order
// Save and display custom item data everywhere on orders and email notifications
function action_woocommerce_checkout_create_order_line_item( $item, $cart_item_key, $values, $order ) {
if ( isset ( $values['custom_text'] ) ) {
$item->update_meta_data( 'custom_text', $values['custom_text'] );
}
}
add_action( 'woocommerce_checkout_create_order_line_item', 'action_woocommerce_checkout_create_order_line_item', 10, 4 );
// On thankyou page
function action_woocommerce_thankyou( $order_id ) {
// Get $order object
$order = wc_get_order( $order_id );
// Is a WC_Order
if ( is_a( $order, 'WC_Order' ) ) {
// Only when the current order status is 'processing'
if ( $order->get_status() == 'processing' ) {
// Loop trough
foreach ( $order->get_items() as $item ) {
// Get meta
$value = $item->get_meta( 'custom_text' );
// NOT empty
if ( ! empty ( $value ) ) {
// Update status (change to desired status)
$order->update_status( 'cancelled' );
// Stop loop
break;
}
}
}
}
}
add_action( 'woocommerce_thankyou', 'action_woocommerce_thankyou', 10, 1 );
Currently it is just checking if the value exists, to compare it effectively
Change
// NOT empty
if ( ! empty ( $value ) ) {
// Update status
$order->update_status( 'cancelled' );
// Stop loop
break;
}
To
// Compare
if ( $value == 'some value' ) ) {
// Update status
$order->update_status( 'cancelled' );
// Stop loop
break;
}
I have the following code that adds a custom column on WooCommerce admin orders list
add_action( 'manage_shop_order_posts_custom_column', 'inforder_add_new_order_admin_list_column_content' );
function inforder_add_new_order_admin_list_column_content( $column ) {
global $post;
if ( 'product_order' === $column ) {
$order = wc_get_order( $post->ID );
$items = $order->get_items();
$count = count($items);
$i = 1;
foreach($items as $item) {
if($i==$count)
echo $item->get_name();
else
echo $item->get_name() .', ';
$i++;
}
}
}
But this shows duplicate values. I've tried to avoid this using
array_unique(), but unfortunately without the desired result.
Any advice to avoid this?
Your code contains some mistakes
The manage_edit-shop_order_columns is missing, which is for adding columns
The manage_shop_order_posts_custom_column has not 1 but 2 parameters, the 2nd contains the $post_id so the use of global $post is not necessary
You can use in_array(), which checks if a value exists in an array. If this is not the case, we will add this value to the array, this way we avoid duplicate values
So you get:
// Add header
function filter_manage_edit_shop_order_columns( $columns ) {
$columns['product_order'] = __( 'Product', 'woocommerce' );
return $columns;
}
add_filter( 'manage_edit-shop_order_columns', 'filter_manage_edit_shop_order_columns', 10, 1 );
// Display (populate the column)
function action_manage_shop_order_posts_custom_column( $column, $post_id ) {
// Compare
if ( $column == 'product_order' ) {
// Get order
$order = wc_get_order( $post_id );
// Initialize
$names = array();
// Is a WC_Order
if ( is_a( $order, 'WC_Order' ) ) {
// Get items
$items = $order->get_items();
// Loop through
foreach ( $items as $key => $item ) {
// Get name
$name = $item->get_name();
// NOT in array
if ( ! in_array( $name, $names, true ) ) {
// Push one or more elements onto the end of array
array_push( $names, $name );
}
}
// NOT empty, print result
if ( ! empty( $names ) ) {
// Use implode() function to join comma in the array
$list = implode( ', ', $names );
// Output
echo $list;
}
}
}
}
add_action( 'manage_shop_order_posts_custom_column' , 'action_manage_shop_order_posts_custom_column', 10, 2 );
I am trying to sort my taxonomy terms table by a custom field in Wordpress. But I am missing something.
I have a custom taxonomy. Let’s call it education in this example.
I added a field sort-order and save a value to that field using update_term_meta().
This works.
Next I added a column to the admin table for the taxonomy:
// Add the column to the table with terms
add_filter('manage_edit-education_columns', array( $this, 'addSortOrderColumn' ));
function addSortOrderColumn( $columns ){
$columnsBefore = array_slice( $columns, 0, 2, true); // NOTE: First column is the checkbox
$columnsAfter = array_slice( $columns, 2, count( $columns ), true);
$columnsInsert = array('sort-order' => ‘Sort’ );
$columns = array_merge($columnsBefore, $columnsInsert, $columnsAfter);
return $columns;
}
// Display the contents for the column
add_filter('manage_education_custom_column',array( $this, 'addSortOrderColumnContent'), 10, 3 );
function addSortOrderColumnContent( $content, $columnName, $termId ){
if( $columnName !== 'sort-order' ){
return $content;
}
$termId = absint( $termId );
$order = get_term_meta( $termId, 'sort-order', true );
if( !empty( $order ) ){
$content .= esc_attr( $order );
}
return $content;
}
// Make the column sortable
add_filter( 'manage_edit-education_sortable_columns', array( $this, 'makeSortOrderColumnSortable' ));
function makeSortOrderColumnSortable( $sortable ){
$sortable[ 'sort-order' ] = 'sort-order';
return $sortable;
}
I see the column, I can click its header and it flips the table rows on click ( a c z e <-> e z c a ), but it does not sort it based on the contents.
What do I need to add to make the sorting aware of the column contents?
You need to tweak the underlying sql that the get_terms function runs to retrieve the taxonomy terms list. It defaults to the term name, clicking & changing the sort will change the orderby=xxx in the url, but will flow through the switch statement in the code and return to the default. The way to do it is through the terms_clauses filter:
add_filter( 'terms_clauses', 'filter_terms_clauses', 10, 3 );
/**
* Filter WP_Term_Query meta query
*
* #param object $query WP_Term_Query
* #return object
*/
function filter_terms_clauses( $pieces, $taxonomies, $args ) {
global $pagenow, $wpdb;
// Require ordering
$orderby = ( isset( $_GET['orderby'] ) ) ? trim( sanitize_text_field( $_GET['orderby'] ) ) : '';
if ( empty( $orderby ) ) { return $pieces; }
// set taxonomy
$taxonomy = $taxonomies[0];
// only if current taxonomy or edit page in admin
if ( !is_admin() || $pagenow !== 'edit-tags.php' || !in_array( $taxonomy, [ 'education' ] ) ) { return $pieces; }
// and ordering matches
if ( $orderby === 'sort-order' ) {
$pieces['join'] .= ' INNER JOIN ' . $wpdb->termmeta . ' AS tm ON t.term_id = tm.term_id ';
$pieces['where'] .= ' AND tm.meta_key = "sort-order"';
$pieces['orderby'] = ' ORDER BY tm.meta_value ';
}
return $pieces;
}
The WP_Term_Query is only from WP4.6 so this functionality is pretty fresh.