I am trying to add ordered products (quantity per product per line) in customer added notes field for new WooCommerce orders. I introduced the following code in my functions.php but it was not working.
/* Add ordered products to the customer notes field on order creation. */
add_action( ‘woocommerce_new_order’, ‘products_in_customer_notes’, 10, 1 );
function products_in_customer_notes( $order_id ) {
$order = wc_get_order( $order_id );
// Verify it's a WC Order
if ( is_a( $order, 'WC_Order' ) ) {
$customer_note = $order->get_customer_note();
foreach ( $order->get_items() as $item_id => $item ) {
$product_name = $item->get_name();
$quantity = $item->get_quantity();
$customer_note .= "<br>";
$customer_note .= $quantity . 'x ' . $product_name;
}
// Add the note
$order->set_customer_note($customer_note);
// $order->add_order_note($customer_note);
// Save the data
$order->save();
}
}
When that didn't work, I tried to simplify my code by hardcoding a comment instead, but that didn't work either.
/* Add ordered products to the customer notes field on order creation. */
add_action( ‘woocommerce_new_order’, ‘products_in_customer_notes’, 10, 1 );
function products_in_customer_notes( $order_id ) {
$order = wc_get_order( $order_id );
// Verify it's a WC Order
if ( is_a( $order, 'WC_Order' ) ) {
$customer_note = $order->get_customer_note();
$customer_note .= '<br>';
$customer_note .= 'Hardcoded Test';
// foreach ( $order->get_items() as $item_id => $item ) {
// $product_name = $item->get_name();
// $quantity = $item->get_quantity();
// $customer_note .= "<br>";
// $customer_note .= $quantity . 'x ' . $product_name;
// }
// Add the note
$order->set_customer_note($customer_note);
// $order->add_order_note($customer_note);
// Save the data
$order->save();
}
}
What is it that I am missing that is preventing this from working?
The desired output would be something like the following:
Any customer notes if they were inserted at the time of order placement.
1x product A
3x product B
8x product C
etc.
add_action( 'woocommerce_new_order', 'products_in_customer_notes', 10, 2 );
function products_in_customer_notes( $order_id, $order ) {
// Verify it's a WC Order
if ( is_a( $order, 'WC_Order' ) ) {
$customer_note = $order->get_customer_note();
foreach ( $order->get_items() as $item_id => $item ) {
$product_name = $item->get_name();
$quantity = $item->get_quantity();
$customer_note .= "<br>";
$customer_note .= $quantity . 'x ' . $product_name;
}
// Add the note
$order->set_customer_note($customer_note);
// Save the data
$order->save();
}
}
You were missing the parameter count(2), $order_id, $order
Related
I'm trying to show stock remaining per product in a custom column on WooCommerce admin orders list, without success.
I would like to show it next to 'quantity'.
Ej. Product A x 1 (3)
My code attempt:
add_filter('manage_edit-shop_order_columns', 'new_order_items_column' );
function new_order_items_column( $order_columns ) {
$order_columns['order_products'] = "Productos";
return $order_columns;
}
add_action( 'manage_shop_order_posts_custom_column' , 'new_order_items_column_cnt' );
function new_order_items_column_cnt( $colname ) {
global $the_order; // the global order object
if( $colname == 'order_products' ) {
// get items from the order global object
$order_items = $the_order->get_items();
if ( !is_wp_error( $order_items ) ) {
foreach( $order_items as $order_item ) {
echo '▪️ ' . $order_item['name'] .' × '. $order_item['quantity'] .'<br />';
}
}
}
}
Any advice?
You can use get_stock_quantity(), note that products with variations and products that do not contain stock are also taken into account
So you get:
// Add a Header
function filter_manage_edit_shop_order_columns( $columns ) {
// Add new column
$columns['order_products'] = __( 'Products', 'woocommerce' );
return $columns;
}
add_filter( 'manage_edit-shop_order_columns', 'filter_manage_edit_shop_order_columns', 10, 1 );
// Populate the Column
function action_manage_shop_order_posts_custom_column( $column, $post_id ) {
// Compare
if ( $column == 'order_products' ) {
// Get an instance of the WC_Order object from an Order ID
$order = wc_get_order( $post_id );
// Is a WC_Order
if ( is_a( $order, 'WC_Order' ) ) {
foreach( $order->get_items() as $item ) {
// Product ID
$product_id = $item->get_variation_id() > 0 ? $item->get_variation_id() : $item->get_product_id();
// Get product
$product = wc_get_product( $product_id );
// Get stock quantity
$get_stock_quantity = $product->get_stock_quantity();
// NOT empty
if ( ! empty ( $get_stock_quantity ) ) {
$stock_output = ' (' . $get_stock_quantity . ')';
} else {
$stock_output = '';
}
// Output
echo '▪ '. $item->get_name() . ' × ' . $item->get_quantity() . $stock_output . '<br />';
}
}
}
}
add_action( 'manage_shop_order_posts_custom_column' , 'action_manage_shop_order_posts_custom_column', 10, 2 );
I want to display the category & attribute (brand) of each product under the product name in the Cart/checkout page
Example:
"Name of product"
"Category | Brand"
As shown in this image from shop
I would like to display it the same way on the cart page
(and also Thank you page + Order details, but these are lower priority)
I have this code which adds the Category to the cart page, but how can i add the attribute/brand next to it? Refer to this image
add_filter( 'woocommerce_cart_item_name', 'category_under_at_cart', 99, 3);
function category_under_at_cart( $name, $cart_item, $cart_item_key ) {
$product_item = $cart_item['data'];
// make sure to get parent product if variation
if ( $product_item->is_type( 'variation' ) ) {
$product_item = wc_get_product( $product_item->get_parent_id() );
}
$cat_ids = $product_item->get_category_ids();
$attributes = $product_item->get_attributes();
// if product has categories, concatenate cart item name with them
if ( $cat_ids ) $name .= '</br>' . wc_get_product_category_list( $product_item->get_id(), ', ', '<span class="posted_in">' . _n( count( $cat_ids )) . ' ', ' | ','</span>');
return $name;
}
The following will display the formatted product category(ies) and product attribute "brand" term names on minicart, cart, checkout, customer order and email notifications:
// Custom funtion that return the formatted category(ies) and attribute 'brand' term names
function get_categories_and_brand_html( $product_id ){
$product = wc_get_product($product_id);
$cat_names = (array) wp_get_post_terms( $product_id, 'product_cat', ['fields' => 'names'] );
$brand_name = $product->get_attribute('brand');
$output = '';
if ( ! empty($cat_names) || ! empty($brand_name) ) {
$output .= '</br><span class="posted_in">';
if ( ! empty($cat_names) ) {
$output .= implode(', ', $cat_names);
}
if ( ! empty($cat_names) && ! empty($brand_name) ) {
$output .= ' | ';
}
if ( ! empty($brand_name) ) {
$output .= $brand_name;
}
$output .= '</span>';
}
return $output;
}
// Display term names in minicart and cart page
add_filter( 'woocommerce_cart_item_name', 'category_brand_after_cart_item_name', 100, 3 );
function category_brand_after_cart_item_name( $item_name, $cart_item, $cart_item_key ) {
$terms_html = get_categories_and_brand_html( $cart_item['product_id'] );
if ( ! is_checkout() && ! empty($terms_html) ) {
$item_name .= $terms_html;
}
return $item_name;
}
// Display term names in checkout page
add_filter( 'woocommerce_checkout_cart_item_quantity', 'category_brand_after_checkout_item_name', 100, 3 );
function category_brand_after_checkout_item_name( $quantity, $cart_item, $cart_item_key ) {
$terms_html = get_categories_and_brand_html( $cart_item['product_id'] );
if ( is_checkout() && ! empty($terms_html) ) {
$quantity .= $terms_html;
}
return $quantity;
}
// Display term names on customer orders
add_filter( 'woocommerce_order_item_quantity_html', 'category_brand_after_order_item_name', 100, 2 );
function category_brand_after_order_item_name( $item_name, $item ) {
$terms_html = get_categories_and_brand_html( $item->get_product_id() );
if ( is_wc_endpoint_url() && ! empty($terms_html) ) {
$item_name .= $terms_html;
}
return $item_name;
}
// Display term names on email notifications
add_filter( 'woocommerce_order_item_name', 'category_brand_after_email_item_name', 100, 3 );
function category_brand_after_email_item_name( $item_name, $item, $is_visible ) {
$terms_html = get_categories_and_brand_html( $item->get_product_id() );
if ( ! is_wc_endpoint_url() && ! empty($terms_html) ) {
$item_name .= $terms_html;
}
return $item_name;
}
Code goes in functions.php file of your active child theme (or active theme). Tested and works.
On the checkout page of my WooCommerce based site, users will have a list of shipping methods to choose from depending on what they are purchasing
This is the list I want to capture:
Things like
“Free Shipping” for orders over a certain price
“Freight Shipping” for certain items, and so on.
My goal, is to display ALL available methods for each specific order, and display it on the “Edit Order / Order Details” page in the Admin view.
A small feature that would help us be able to quickly identify what option people are choosing more often, depending on the choices that they have available.
This is what I have so far:
add_action( 'woocommerce_checkout_update_order_meta', 'save_available_shipping_methods' );
function save_available_shipping_methods( $order_id ) {
$shippingmethods = WC()->cart->get_shipping_methods();
update_post_meta( $order_id, '_shipping_methods', $shippingmethods );
}
add_action( 'woocommerce_admin_order_data_after_shipping_address', 'get_available_shipping_methods', 10, 1 );
function get_available_shipping_methods($order){
$order = wc_get_order( $order_id );
if ( $order ) {
echo '<p><strong>'.__('Available Shipping Methods: ').'</strong> ' . get_post_meta($order->get_shipping_methods(), '_shipping_field_value', true ) . '</p>';
}
}
I'll attach a picture as well to maybe make things a little easier to follow.
This is where I would like to put it. The current hook places the field underneath the shipping address
In the woocommerce_checkout_update_order_meta hook, the available shipping packages are stored via update_post_meta
Comments with explanation added in the code
function action_woocommerce_checkout_update_order_meta( $order_id ) {
// Get shipping packages
$packages = WC()->shipping()->get_packages();
// Set array
$rate_labels = array();
// Loop trough packages
foreach ( $packages as $key => $package ) {
// Loop through package rates
foreach( $package['rates'] as $rate_id => $rate ) {
// Push to array
$rate_labels[] = $rate->get_label();
}
}
// NOT empty
if ( ! empty ( $rate_labels ) ) {
// Update post meta
update_post_meta( $order_id, '_available_shipping_methods', $rate_labels );
}
}
add_action( 'woocommerce_checkout_update_order_meta', 'action_woocommerce_checkout_update_order_meta', 10, 1 );
// Display on the order edit page (backend)
function action_woocommerce_admin_order_data_after_shipping_address( $order ) {
// Get meta
$rate_labels = $order->get_meta( '_available_shipping_methods' );
// True
if ( $rate_labels ) {
// Loop trough rate labels
foreach( $rate_labels as $rate_label ) {
// Output
echo '<p>' . $rate_label . '</p>';
}
}
}
add_action( 'woocommerce_admin_order_data_after_shipping_address', 'action_woocommerce_admin_order_data_after_shipping_address', 10, 1 );
In case anyone is wondering, this is the final result. It will loop through each order to find the available shipping methods at the time, along with how much it was quoted for.
// Capture the available shipping methods, and costs:
function action_woocommerce_checkout_update_order_meta( $order_id ) {
// Get shipping packages
$packages = WC()->shipping()->get_packages();
// Set array
$rate_labels = array();
$rate_costs = array();
// Loop trough packages
foreach ( $packages as $key => $package ) {
// Loop through package rates
foreach( $package['rates'] as $rate_id => $rate ) {
// Push to array
$rate_labels[] = $rate->get_label();
$rate_costs[] = $rate->get_cost();
}
}
// NOT empty
if ( ! empty ( $rate_labels ) ) {
// Update post meta
update_post_meta( $order_id, '_available_shipping_methods', $rate_labels );
update_post_meta( $order_id, '_available_shipping_method_cost', $rate_costs );
}
}
add_action( 'woocommerce_checkout_update_order_meta', 'action_woocommerce_checkout_update_order_meta', 10, 1 );
// Make it display on the edit order page:
function action_woocommerce_admin_order_data_after_shipping_address( $order ) {
// Get meta
$rate_labels = $order->get_meta( '_available_shipping_methods' );
$rate_costs = $order->get_meta( '_available_shipping_method_cost' );
$methods = array ( $rate_labels, $rate_costs );
// True
if ( $rate_labels ) {
// Loop
echo '<p><strong>Shipping Methods: </strong>';
foreach(array_combine($rate_labels, $rate_costs) as $rate_label => $rate_cost) {
echo '<p>' . $rate_label . ' - $' . $rate_cost . '</p>';
}
}
}
add_action( 'woocommerce_admin_order_data_after_shipping_address', 'action_woocommerce_admin_order_data_after_shipping_address', 10, 1 );
Attempting to pull product data from the last order such as [key] => pa_size
Using automatewoo_update_print_file to call file.php where the two function is located, it's called when a new note is added to an order:
function automatewoo_update_print_file( $workflow ) {
include '/home/***/public_html/wp-content/themes/***-child/woocommerce/checkout/file.php';
}
Update This worked well to pull the most recent order ID and get the product id, then the meta data from the ID and also the rest of the order data. But I still need to pull [key] => pa_size
function get_last_order_id(){
global $wpdb;
$statuses = array_keys(wc_get_order_statuses());
$statuses = implode( "','", $statuses );
// Getting last Order ID (max value)
$results = $wpdb->get_col( "
SELECT MAX(ID) FROM {$wpdb->prefix}posts
WHERE post_type LIKE 'shop_order'
AND post_status IN ('$statuses')
" );
return reset($results);
}
$latest_order_id = get_last_order_id(); // Last order ID
$order = wc_get_order( $latest_order_id ); // Get an instance of the WC_Order oject
$order_details = $order->get_data(); // Get the order data in an array
$order_status = $order_details['status'];
foreach ($order->get_items() as $item_key => $item ):
$product_id = $item->get_product_id();
$variation_id = $item->get_variation_id();
$item_name = $item->get_name(); // Name of the product
$quantity = $item->get_quantity();
$product = $item->get_product(); // Get the WC_Product object
$product_price = $product->get_price();
endforeach;
$print_file = get_post_meta( $product_id, 'print_file_url', true );
// Raw output for testing
echo 'Product Price<pre> '; print_r( $product_price ); echo '</pre>';
echo 'Product Name<pre> '; print_r( $item_name ); echo '</pre>';
echo 'Product Quantity<pre> '; print_r( $quantity ); echo '</pre>';
echo 'Product ID<pre> '; print_r( $product_id ); echo '</pre>';
echo 'Variation ID<pre> '; print_r( $variation_id ); echo '</pre>';
echo 'Print File Url<pre> '; print_r( $print_file ); echo '</pre>';
echo 'Order Status<pre>'; print_r( $order_status ); echo '</pre>';
echo 'Latest Order ID<pre>'; print_r( $latest_order_id ); echo '</pre>';
echo 'Order Details<pre>'; print_r( $order_details ); echo '</pre>';
Use this function for get last order id
$last_order_id = wc_get_customer_last_order($user_id);
$order = wc_get_order( $order_id );
$order->get_items();
foreach ($order->get_items() as $item_key => $item ){
// Item ID is directly accessible from the $item_key in the foreach loop or
$item_id = $item->get_id();
## Using WC_Order_Item_Product methods ##
$product = $item->get_product(); // Get the WC_Product object
$product_id = $item->get_product_id(); // the Product id
$variation_id = $item->get_variation_id(); // the Variation id
$item_type = $item->get_type(); // Type of the order item ("line_item")
$item_name = $item->get_name(); // Name of the product
$quantity = $item->get_quantity();
$tax_class = $item->get_tax_class();
$line_subtotal = $item->get_subtotal(); // Line subtotal (non discounted)
$line_subtotal_tax = $item->get_subtotal_tax(); // Line subtotal tax (non discounted)
$line_total = $item->get_total(); // Line total (discounted)
$line_total_tax = $item->get_total_tax(); // Line total tax (discounted)
$product = $item->get_product(); // Get the WC_Product object
$product_type = $product->get_type();
$product_sku = $product->get_sku();
$product_price = $product->get_price();
$stock_quantity = $product->get_stock_quantity();
}
Try this
function get_names( $order_id ) {
$order = wc_get_order( $order_id );
if (empty($order)) return false;
$items = $order->get_items();
foreach ( $items as $item ) {
$item_name = $item->get_name();
}
return $item_name;
}
Thanks
I got the following code in my woocommerce thankyou.php, this works if only products from ONE category is bought. When products from both the 'ebook' and the 'ticket' category are bought, 'ticket' will be added to the $productname variable.
How to determine if the products in the list are of one or multiple categories?
<?php
$email = $order->billing_email;
$order_info = wc_get_order( $order );
$productname = "";
foreach( $order_info->get_items() as $item ) {
// check if a product is in specific category
if ( has_term( 'ebook', 'product_cat', $item['product_id'] ) ) {
$productname = ' ebook';
} elseif (has_term( 'ticket', 'product_cat', $item['product_id'] )) {
$productname = 'ticket';
} else {
$productname = 'order details';
}
}
echo '<p> Your ' . $productname . ' will be send to <strong>' . $email . '</strong> as soon as the payment is received.<br>;
?>
Try the following instead that will store your product names in an array, removing duplicated value and displaying the product names in your message:
// Get the instance of the WC_Order Object from the $order_id variable
$order = wc_get_order( $order_id );
$product_names = array(); // Initializing
// Loop through order items
foreach( $order->get_items() as $item ) {
// check if a product is in specific category
if ( has_term( 'ebook', 'product_cat', $item['product_id'] ) )
{
$product_names[] = '"Ebook"';
}
elseif ( has_term( 'ticket', 'product_cat', $item['product_id'] ) )
{
$product_names[] = '"Ticket"';
}
else
{
$product_names[] = '"Others"';
}
}
$product_names = array_unique( $product_names );
echo sprintf( '<p>%s %s %s <strong>%s</strong> %s</p><br>',
_n( "Your", "Yours", sizeof( $product_names ), "woocommerce-bookings" ),
implode( ', ', $product_names ),
__("will be sent to", "woocommerce-bookings"),
$order->get_billing_email(),
__("as soon as the payment is received.", "woocommerce-bookings")
);
Tested and works