Referring to the answer found here - Customize total rows from WooCommerce order table
I would like to change the items order in Order details table on the /checkout/order-received/ page and in email that the customer receives
I have added an extra line in the Order details table "Total excl. VAT:" which I got from this code
add_filter( 'woocommerce_get_order_item_totals', 'add_order_total_excl_vat_row', 10, 3 );
function add_order_total_excl_vat_row( $total_rows, $order, $tax_display ) {
// Only on emails notifications
if( ! is_wc_endpoint_url() || ! is_admin() ) {
// Set last total row in a variable and remove it.
$gran_total = $total_rows['order_total'];
unset( $total_rows['order_total'] );
// Insert our new row
$total_rows['order_total_ev'] = array(
'label' => __( 'Total excl. VAT: ', 'woocommerce' ),
'value' => wc_price( $order->get_total() - $order->get_total_tax() ),
);
// Set back last total row
$total_rows['order_total'] = $gran_total;
}
return $total_rows;
}
I have managed to rearrange the order of the items in the Order details table with this code:
add_filter( 'woocommerce_get_order_item_totals', 'reordering_order_item_totals', 10, 3 );
function reordering_order_item_totals( $total_rows, $order, $tax_display = '' ){
$sorted_items_end = array('order_total_ev', 'order_total', 'payment_method');
$sorted_total_rows = array(); // Initializing
// Loop through sorted totals item keys
foreach( $sorted_items_end as $item_key ) {
if( isset($total_rows[$item_key]) ) {
$sorted_total_rows[$item_key] = $total_rows[$item_key]; // Save sorted data in a new array
unset($total_rows[$item_key]); // Remove sorted data from default array
}
}
return array_merge( $total_rows, $sorted_total_rows); // merge arrays
}
What I want to achieve is to add Tax value after the 'order_total_ev' (Price excl. VAT) and before 'order_total'
My PHP knowledge is very limited and I appreciate any help.
Related
In Woocommerce, I used jQuery to calculate a custom price on a single product pages, and now need to pass this value to the cart.
The desired behavior is to pass the new price retrieved from the hidden field to the cart item price.
Here is my actual code:
// Hidden input field in single product page
add_action( 'woocommerce_before_add_to_cart_button', 'custom_hidden_product_field', 11, 0 );
function custom_hidden_product_field() {
echo '<input type="hidden" id="hidden_field" name="custom_price" class="custom_price" value="">';
}
// The code to pass this data to the cart:
add_action( 'woocommerce_add_cart_item_data', 'save_custom_fields_data_to_cart', 10, 2 );
function save_custom_fields_data_to_cart( $cart_item_data, $product_id ) {
if( ! empty( $_REQUEST['custom_price'] ) ) {
// Set the custom data in the cart item
$cart_item_data['custom_data']['custom_price'] = $_REQUEST['custom_price'];
$data = array( 'custom_price' => $_REQUEST['custom_price'] );
// below statement make sure every add to cart action as unique line item
$cart_item_data['custom_data']['unique_key'] = md5( microtime().rand() );
WC()->session->set( 'custom_data', $data );
}
return $cart_item_data;
}
And check both $data and $cart_item_data to see that they both return the custom_price data that is calculated on the page.
However, I go to view cart, and the value of the line item is still 0.
I set a var equal to the WC()->session->set( 'custom_data', $data ); and then var_dump to check it, but this returns NULL which might just be what it returns, I'm not entirely sure because I've never used it.
I should also add that I have the regular_price in the product backend set to 0. When I erase this (and leave it blank) I get back the error:
Warning: A non-numeric value encountered in
C:\xampp\htdocs\my-transfer-source\wp-content\plugins\woocommerce\includes\class-wc-discounts.php on line 85
I'm wondering if I've missed something here, and if someone could lend some light onto this? Thanks
Update 2021 - Handling custom price item in mini cart
First for testing purpose we add a price in the hidden input field as you don't give the code that calculate the price:
// Add a hidden input field (With a value of 20 for testing purpose)
add_action( 'woocommerce_before_add_to_cart_button', 'custom_hidden_product_field', 11 );
function custom_hidden_product_field() {
echo '<input type="hidden" id="hidden_field" name="custom_price" class="custom_price" value="20">'; // Price is 20 for testing
}
Then you will use the following to change the cart item price (WC_Session is not needed):
// Save custom calculated price as custom cart item data
add_filter( 'woocommerce_add_cart_item_data', 'save_custom_fields_data_to_cart', 10, 2 );
function save_custom_fields_data_to_cart( $cart_item_data, $product_id ) {
if( isset( $_POST['custom_price'] ) && ! empty( $_POST['custom_price'] ) ) {
// Set the custom data in the cart item
$cart_item_data['custom_price'] = (float) sanitize_text_field( $_POST['custom_price'] );
// Make each item as a unique separated cart item
$cart_item_data['unique_key'] = md5( microtime().rand() );
}
return $cart_item_data;
}
// For mini cart
add_action( 'woocommerce_cart_item_price', 'filter_cart_item_price', 10, 2 );
function filter_cart_item_price( $price, $cart_item ) {
if ( isset($cart_item['custom_price']) ) {
$args = array( 'price' => floatval( $cart_item['custom_price'] ) );
if ( WC()->cart->display_prices_including_tax() ) {
$product_price = wc_get_price_including_tax( $cart_item['data'], $args );
} else {
$product_price = wc_get_price_excluding_tax( $cart_item['data'], $args );
}
return wc_price( $product_price );
}
return $price;
}
// Updating cart item price
add_action( 'woocommerce_before_calculate_totals', 'change_cart_item_price', 30, 1 );
function change_cart_item_price( $cart ) {
if ( ( is_admin() && ! defined( 'DOING_AJAX' ) ) )
return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
// Loop through cart items
foreach ( $cart->get_cart() as $cart_item ) {
// Set the new price
if( isset($cart_item['custom_price']) ){
$cart_item['data']->set_price($cart_item['custom_price']);
}
}
}
Code goes in functions.php file of your active child theme (or active theme). Tested and works.
I'm trying to get the WooCommerce cart calculations to multiply the shipping rates by the number of different custom taxonimies terms in cart.
For example, my custom taxonomy name/slug is 'city'. If there are 3 different product taxonomies terms present (eg. Boston, Washington and New York), the current cart rate should be multiplied by 3.
This is my current code:
add_filter( 'woocommerce_package_rates', 'funkcija');
function funkcija ( $rates ) {
$cart = WC()->cart;
// here I define the array where custom taxonomies will be saved in the foreach loop
$mojarray = array();
// the foreach loop that iterates through cart items and saves every taxonomy term in the array
foreach ( $cart->get_cart() as $cart_item_key => $cart_item ) {
$product_id = $cart_item['product_id'];
$terms = wp_get_post_terms( $product_id, 'city' );
$mojarray[] = $terms;
}
//now here the counter is defined, the array is set to contain only unique values of the taxonomies
$counter = 0;
$noviarray = array_unique($mojarray);
foreach ($noviarray as $value) {
// for each unique taxonomy present in cart, increase the counter by 1
++$counter;
}
//here the rates totals are taken and multiplied with $counter
foreach($rates as $key => $rate ) {
$currenttotal = $rates[$key]->cost;
$multipliedtotal = $currenttotal * $counter;
$rates[$key]->cost = $multipliedtotal;
}
return $rates;
}
Unfortunately, this code multiplies the rates for every product in the cart. I've gone through the code multiple times and do not understand why it's not working as intended for unique taxonomy terms.
I believe this is testable on any WooCommerce store with any custom taxonomy. Any advice?
Your code contains some mistakes
The use of WC()->cart is not necessary, since the woocommerce_package_rates hook contains not 1 but 2 parameters, and from the 2nd, namely $package you can get the necessary information
wp_get_post_terms() contains a third parameter, namely $args, so array( 'fields' => 'names' ) has been added
Since you currently apply this to all $rates, I added an if condition in my answer where you can specify 1 or more methods. If you don't want this, you can just remove the if condition
So you get:
function filter_woocommerce_package_rates( $rates, $package ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) ) return $rates;
// Initialize
$counter = 0;
$my_array = array();
// Loop through line items
foreach ( $package['contents'] as $line_item ) {
// Get product id
$product_id = $line_item['product_id'];
// Get terms
$term_names = wp_get_post_terms( $product_id, 'city', array( 'fields' => 'names' ) );
// Loop through (unique values)
foreach ( $term_names as $term_name ) {
// Checks if a value NOT exists in an array
if ( ! in_array( $term_name, $my_array, true ) ) {
// Push one or more elements onto the end of array
array_push( $my_array, $term_name );
}
}
}
// Counts all elements in an array
$counter = count( $my_array );
// Greater than
if ( $counter > 0 ) {
// Loop through rates
foreach ( $rates as $rate_key => $rate ) {
// Target specific methods, multiple can be added, separated by a comma
if ( in_array( $rate->method_id, array( 'flat_rate', 'table_rate' ) ) ) {
// Get rate cost
$cost = $rates[$rate_key]->cost;
// Greater than
if ( $cost > 0 ) {
// Set rate cost
$rates[$rate_key]->cost = $cost * $counter;
}
}
}
}
return $rates;
}
add_filter( 'woocommerce_package_rates', 'filter_woocommerce_package_rates', 10, 2 );
I used Add the Coupon Code names to Woocommerce View Order details and email notifications to inspire my snippet.
I'm trying to expand the snippet to also include the coupon percentage amount in brackets, if the coupon type is a percentage discount.
This is what it should look like:
Here is my attempt. Any ideas if this is correct:
add_filter( 'woocommerce_get_order_item_totals', 'add_coupons_codes_line_to_order_totals_lines', 10, 3 );
function add_coupons_codes_line_to_order_totals_lines( $total_rows, $order, $tax_display ) {
// Exit if there is no coupons applied
if( sizeof( $order->get_used_coupons() ) == 0 )
return $total_rows;
$new_total_rows = []; // Initializing
foreach($total_rows as $key => $total ){
$new_total_rows[$key] = $total;
if( $key == 'discount' ){
// Get applied coupons
$applied_coupons = $order->get_used_coupons();
if( $applied_coupons->discount_type == 'percent'){
// Get applied coupon percentge
$applied_coupons_percentage = $applied_coupons->coupon_amount;
}
// Insert applied coupon codes in total lines after discount line
$new_total_rows['coupon_codes'] = array(
'label' => __('Applied coupons:', 'woocommerce'),
'value' => implode( ', ', $applied_coupons '<p> ({$applied_coupons_percentage}%)</p>' ),
);
}
}
return $new_total_rows;
}
I use this to display the coupon percentage on the cart page. I tried to incorporate this into my snippet above:
function my_coupon_percentage_cart($value, $coupon)
{
if($coupon->discount_type == 'percent' && !empty($coupon->coupon_amount))
{
$amt = "<br><br><p><em><strong>{$coupon->coupon_amount}% OFF on Your Order</strong></em></p>";
}
return $value.$amt;
}
add_filter('woocommerce_cart_totals_coupon_html','my_coupon_percentage_cart',10,2);
Besides the fact that your code contains mistakes, it also contains some outdated code
get_used_coupons() is deprecated since 3.7.0 - Replaced with better named method to reflect the actual data being returned.
$coupon->discount_type has been replaced with $coupon->get_discount_type()
$coupon->coupon_amount has been replaced with $coupon->get_amount()
So you get:
function filter_woocommerce_get_order_item_totals( $total_rows, $order, $tax_display ) {
// Exit if there is no coupons applied
if ( sizeof( $order->get_coupon_codes() ) == 0 ) return $total_rows;
$new_total_rows = []; // Initializing
foreach( $total_rows as $key => $total ) {
$new_total_rows[$key] = $total;
if ( $key == 'discount' ) {
// Get used coupon codes only
$coupon_codes = $order->get_coupon_codes();
// Loop through WC_Order_Item_Coupon objects
foreach ( $coupon_codes as $index => $coupon_code ) {
// Get an instance of the WC_Coupon Object
$coupon = new WC_Coupon( $coupon_code );
// Discount type = percent & amount NOT empty
if ( $coupon->get_discount_type() == 'percent' && ! empty ( $coupon->get_amount() ) ) {
$coupon_codes[$index] = $coupon_code . ' (' . $coupon->get_amount() . '%)';
}
}
// Insert applied coupon codes in total lines after discount line
$new_total_rows['coupon_codes'] = array(
'label' => __( 'Applied coupons:', 'woocommerce' ),
'value' => implode( ', ', $coupon_codes ),
);
}
}
return $new_total_rows;
}
add_filter( 'woocommerce_get_order_item_totals', 'filter_woocommerce_get_order_item_totals', 10, 3 );
This question already has answers here:
Sorting order items by SKU in Woocommerce
(2 answers)
Closed 2 years ago.
I'm stumped if it's possible. I have scouted to only seeing results to sort by SKU on the front end. not the backend. When you edit an order, I want there to be an SKU column that I can sort by ASC/DESC. Is there a plugin out there that achieves this, or a snippet of code that can add in an additional column? Any help would be greatly appreciated. Below is an image illustrating the SKU I'm talking about and would love for it to be moved/duplicated into its own column
add_filter( 'woocommerce_order_get_items', 'filter_order_get_items', 10, 3 );
function filter_order_get_items( $items, $order, $types = 'line_item' ) {
if(count($items) < 2) return $items;
if( $types != 'line_item') return $items;
$item_skus = $sorted_items = array();
// Loop through order line items
foreach( $items as $items_id => $item ){
// Check items type: for versions before Woocommerce 3.3
if($item->is_type('line_item')){
$product = $item->get_product(); //
$item_skus[$product->get_sku()] = $items_id;
}
}
// Check items type: for versions before Woocommerce 3.3 (2)
if( count($item_skus) == 0 ) return $items;
// Sorting in ASC order based on SKUs;
ksort($item_skus); // or use krsort() for DESC order
// Loop through sorted $item_skus array
foreach( $item_skus as $sku => $item_id ){
// Set items in the correct order
$sorted_items[$item_id] = $items[$item_id];
}
return $sorted_items;
}
#mujuonly has a great answer. I was using it until I found a conflict with woocommerce subscription renewals. The original orders were fine, but renewals orders were missing their shipping method.
Instead I opted to sort the basket items by sku so that they would be saved in the database that way.
add_action( 'woocommerce_cart_loaded_from_session', 'sort_cart_items_sku' );
function sort_cart_items_sku() {
// READ CART ITEMS
$products_in_cart = array();
foreach ( WC()->cart->get_cart_contents() as $key => $item ) {
$products_in_cart[ $key ] = $item['data']->get_sku();
}
// SORT CART ITEMS
natsort( $products_in_cart );
// ASSIGN SORTED ITEMS TO CART
$cart_contents = array();
foreach ( $products_in_cart as $cart_key => $product_title ) {
$cart_contents[ $cart_key ] = WC()->cart->cart_contents[ $cart_key ];
}
WC()->cart->cart_contents = $cart_contents;
}
Credit: https://businessbloomer.com/woocommerce-sort-cart-items-alphabetically-az/
I am trying to display chained products meta on admin product column but it just seems to display array. What is the bug? Thanks guys!
// 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['chain_product'] = __( 'Custom','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;
$chained_parent_id = empty( $variation ) ? $post->ID : $variation->ID;
// HERE get the data from your custom field (set the correct meta key below)
$chain_product = get_post_meta( $chained_parent_id, '_chained_product_detail', true );
switch ( $column )
{
case 'chain_product' :
echo $chain_product; // display the data
break;
}
}