Display coupon usage count and limit in WooCommerce thank you page - php

I want to display the remaining number of the coupon on the WooCommerce thankyou page
For example: You used the test coupon 3 times and there are 10 more left.
This is my code:
add_action('woocommerce_after_cart_table', 'coupon_count');
function coupon_count() {
global $woocommerce;
if ( ! empty( $woocommerce->cart->applied_coupons ) ) {
$my_coupon = $woocommerce->cart->get_coupons() ;
foreach($my_coupon as $coupon){
if ( $post = get_post( $coupon->id ) ) {
$counter = $coupon->get_usage_count();
echo "<span class='name-coupon'><b>Total usage for coupon </b><b>'</b><b>".$coupon->code."</b><b>'</b><b>: </b></span>";
echo "<span class='coupon-counter'>".($counter)."</span>";
}
}
}
}
But I have two problems:
1.With this code, only the number of times used is displayed and the remaining number is not displayed.
2.Replacing woocommerce_after_cart_table with woocommerce_thankyou on the thankyou page does not execute the code.

Instead of using the cart object, the order object is used on the thankyou page. So we're going to use that object instead
You can use get_usage_limit_per_user() to get coupon usage limit per customer (for a single customer)
OR use get_usage_limit() to get coupon usage limit.
So you get:
function action_woocommerce_thankyou( $order_id ) {
// Get $order object
$order = wc_get_order( $order_id );
foreach( $order->get_coupon_codes() as $coupon_code ) {
// Get the WC_Coupon object
$coupon = new WC_Coupon( $coupon_code );
// Get usage count
$count = $coupon->get_usage_count();
// Get coupon usage limit per customer
$limit = $coupon->get_usage_limit_per_user();
// OR use this instead, to get coupon usage limit.
// $limit = $coupon->get_usage_limit();
// NOT empty
if ( ! empty ( $count ) && ! empty ( $limit ) ) {
// Calculate remaining
$remaining = $limit - $count;
// Output
echo sprintf( '<span class="coupon-class">You used the <strong>%s</strong> coupon <strong>%d</strong> times and there are <strong>%d</strong> more left</span>', $coupon_code, $count, $remaining );
}
}
}
add_action( 'woocommerce_thankyou', 'action_woocommerce_thankyou', 10, 1 );

add_action('woocommerce_after_cart_table', 'coupon_count');
function coupon_count() {
global $woocommerce;
if ( ! empty( $woocommerce->cart->applied_coupons ) ) {
$my_coupon = $woocommerce->cart->get_coupons() ;
foreach($my_coupon as $coupon){
if ( $post = get_post( $coupon->id ) ) {
$counter = $coupon->get_usage_count();
$remaining = $coupon->get_usage_limit()-$counter;
echo "<span class='name-coupon'><b>Total usage for coupon </b><b>'</b><b>".$coupon->code."</b><b>'</b><b>: </b></span>";
echo "<span class='coupon-counter'>".($counter)."</span>";
echo "<span class='coupon-counter'> there are ".($remaining)." more left</span>";
}
}
}
}

Related

Add column total order weight to WooCommerce My account Orders

I want to add weight metadata to an order in the Frontend: My Account - Orders. I tried some things but it is not working.
I want to add is $order->get_weight(); as meta data to the order but I am getting an error.
I am already half way using this code to add a new column and show product description and quantity:
add_filter( 'woocommerce_my_account_my_orders_columns', 'additional_my_account_orders_column', 10, 1 );
function additional_my_account_orders_column( $columns ) {
$new_columns = [];
foreach ( $columns as $key => $name ) {
$new_columns[ $key ] = $name;
if ( 'order-status' === $key ) {
$new_columns['order-items'] = __( 'Descripción', 'woocommerce' );
}
}
return $new_columns;
}
add_action( 'woocommerce_my_account_my_orders_column_order-items', 'additional_my_account_orders_column_content', 10, 1 );
function additional_my_account_orders_column_content( $order ) {
$details = array();
foreach( $order->get_items() as $item )
$details[] = $item->get_name() . ' × ' . $item->get_quantity();
echo count( $details ) > 0 ? implode( '<br>', $details ) : '–';
}
Hope someone can help me get in the right direction.
This snippet inserts a new, custom column in the table of orders shown in My Account > Orders populated with the total weight of the order so the customer is aware how heavy their order was.
Specifically, this snippet has two blocks of code. The first block inserts the column. In this example, we have inserted this column between the Order Total and Order Actions column. This can be changed by changing the column key in the code. Your custom will appear after the column key you define.
The second block of code is where the magic happens. It first loops through each item in the order and gets it weight and times this by the quantity of this product. It then adds this weight of each product to a variable we have called $total_weight. The total weight is then output to the new column followed by the weight unit you have defined in your store settings under WordPress Dashboard > WooCommerce > Settings > Products > General > Measurements > Weight Unit.
/**
* Snippet Name: WooCommerce Show Order Weight Column In My Account Order View Table
* Snippet Author: ecommercehints.com
*/
// First, create the new table column between Total and Actions columns
add_filter( 'woocommerce_my_account_my_orders_columns', 'ecommercehints_weight_column_my_account_orders_table', 10, 1 );
function ecommercehints_weight_column_my_account_orders_table( $columns ) {
$weight_column = [];
foreach ( $columns as $key => $name ) {
$weight_column[ $key ] = $name;
if ( 'order-total' === $key ) { // Insert new column after Total column
$weight_column['order-items'] = __( 'Order Weight', 'woocommerce' );
}
}
return $weight_column;
}
// Second, insert the data from the order into the new column
add_action( 'woocommerce_my_account_my_orders_column_order-items', 'ecommercehints_get_order_weight', 10, 1 );
function ecommercehints_get_order_weight( $order ) {
$weight_unit = get_option('woocommerce_weight_unit');
$total_weight = 0;
foreach( $order->get_items() as $item_id => $item ){
$quantity = $item->get_quantity();
$product = $item->get_product();
$product_weight = $product->get_weight();
$total_weight += floatval( $product_weight * $quantity );
}
echo $total_weight . $weight_unit;
}
Just came across this, have you tried it? https://gist.github.com/kloon/5299119?permalink_comment_id=1415838
Here is a copy of the code in case the link eventually dies:
<?php
add_filter( 'manage_edit-shop_order_columns', 'woo_order_weight_column' );
function woo_order_weight_column( $columns ) {
$columns['total_weight'] = __( 'Weight', 'woocommerce' );
return $columns;
}
add_action( 'manage_shop_order_posts_custom_column', 'woo_custom_order_weight_column', 2 );
function woo_custom_order_weight_column( $column ) {
global $post, $woocommerce, $the_order;
if ( empty( $the_order ) || $the_order->get_id() !== $post->ID )
$the_order = new WC_Order( $post->ID );
if ( $column == 'total_weight' ) {
$weight = 0;
if ( sizeof( $the_order->get_items() ) > 0 ) {
foreach( $the_order->get_items() as $item ) {
if ( $item['product_id'] > 0 ) {
$_product = $item->get_product();
if ( ! $_product->is_virtual() ) {
$weight += $_product->get_weight() * $item['qty'];
}
}
}
}
if ( $weight > 0 ) {
print $weight . ' ' . esc_attr( get_option('woocommerce_weight_unit' ) );
} else {
print 'N/A';
}
}
}
?>

How to auto generate a coupon in WooCommerce and apply it to the cart?

I have scoured the net and many pages here on stackoverflow. I’m trying to set up a WooCommerce abandoned cart flow where an auto generated coupon in WooCommerce can be added to the cart. I have butchered various code that I’ve found. I have it pretty rough but kinda working. I’m not that familiar with PHP so struggling to get this to behave correctly.
I have the coupon auto generating in WooCommerce admin when a product is added to the cart, its also being applied on the cart page. However every time the page is refreshed more coupons are being generated and added to the cart. Also coupons are generated every time a product is added to the cart.
Is there anyway to limit the coupon so that it is only generated and applied once?
I was planning to direct to the WooCommerce shopping cart from a recreated abandoned cart flow in Klaviyo, where all products from a customers abandoned cart will be added. I was planning to append the URL with something like https://example.com/cart?coupon-code=mycoupon
Would it at all be possible to trigger the coupon creation from the URL variable (eg/ mycoupon) ?
Please let me know if this is even possible? Before I waste another day of my life on this project...lol
Heres my code so far.
function coupon_exists($coupon_code) {
global $wpdb;
$sql = $wpdb->prepare( "SELECT post_name FROM $wpdb->posts WHERE post_type = 'shop_coupon' AND post_name = '%s'", $coupon_code );
$coupon_codes = $wpdb->get_results($sql);
if (count($coupon_codes)> 0) {
return true;
}
else {
return false;
}
}
// Utility function that generate a non existing coupon code (as each coupon code has to be unique)
function random_coupon_code() {
global $wpdb;
// Get an array of all existing coupon codes
$coupon_codes_check = $wpdb->get_col("SELECT post_name FROM $wpdb->posts WHERE post_type = 'shop_coupon'");
for ( $i = 0; $i < 1; $i++ ) {
$generated_code = strtolower( wp_generate_password( 15, false ) );
// Check if the generated code doesn't exist yet
if( in_array( $generated_code, $coupon_codes_check ) ) {
$i--; // continue the loop and generate a new code
} else {
break; // stop the loop: The generated coupon code doesn't exist already
}
}
return $generated_code;
}
function generate_random_coupon($coupon_generated) {
// Set some coupon data by default
$date_expires = date('Y-m-d', strtotime('+1 days'));
$discount_type = 'fixed_cart'; // 'store_credit' doesn't exist
$coupon_amount = '10';
$coupon = new WC_Coupon();
// Generate a non existing coupon code name
$coupon_code = random_coupon_code();
$coupon->set_code($coupon_generated);
//the coupon discount type can be 'fixed_cart', 'percent' or 'fixed_product', defaults to 'fixed_cart'
$coupon->set_discount_type($discount_type);
//the discount amount, defaults to zero
$coupon->set_amount($coupon_amount );
$coupon->set_date_expires( $date_expires );
//save the coupon
$coupon->save();
return $coupon_generated;
}
function hwn_add_programmatically_created_coupon_to_basket( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
$coupon_code = random_coupon_code();
if (!coupon_exists($coupon_code)) {
generate_random_coupon($coupon_code);
}
$applied_coupons = $cart->get_applied_coupons();
if( ! in_array($coupon_code, $applied_coupons)){
$cart->add_discount( $coupon_code );
wc_print_notices();
}
elseif( in_array($coupon_code, $applied_coupons)){
$cart->remove_coupon( $coupon_code );
}
}
add_action('woocommerce_before_calculate_totals', 'hwn_add_programmatically_created_coupon_to_basket');
https://i.imgur.com/hzGfDkz.png
https://i.imgur.com/2yIlz1a.png
https://i.imgur.com/hfligL9.png
You can get a coupon code from the URL using the GET method. try the below code. You remove the function random_coupon_code because now you generate coupon code based on URL.
function coupon_exists( $coupon_code ) {
global $wpdb;
$sql = $wpdb->prepare( "SELECT post_name FROM $wpdb->posts WHERE post_type = 'shop_coupon' AND post_name = '%s'", $coupon_code );
$coupon_codes = $wpdb->get_results($sql);
if ( count( $coupon_codes ) > 0 ) {
return true;
} else {
return false;
}
}
function generate_coupon($coupon_generated) {
// Set some coupon data by default
$date_expires = date('Y-m-d', strtotime('+1 days'));
$discount_type = 'fixed_cart'; // 'store_credit' doesn't exist
$coupon_amount = '10';
$coupon = new WC_Coupon();
$coupon->set_code($coupon_generated);
//the coupon discount type can be 'fixed_cart', 'percent' or 'fixed_product', defaults to 'fixed_cart'
$coupon->set_discount_type($discount_type);
//the discount amount, defaults to zero
$coupon->set_amount($coupon_amount );
$coupon->set_date_expires( $date_expires );
//save the coupon
$coupon->save();
return $coupon_generated;
}
function hwn_add_programmatically_created_coupon_to_basket( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
$coupon_code = ( isset( $_GET['coupon-code'] ) && $_GET['coupon-code'] != '' ) ? $_GET['coupon-code'] : '' ;
if( $coupon_code == '' ){
return;
}
$applied_coupons = $cart->get_applied_coupons();
if( empty( $applied_coupons ) || ! in_array( $coupon_code, $applied_coupons ) ){
if ( !coupon_exists( $coupon_code ) ) {
generate_coupon( $coupon_code );
}
$cart->add_discount( $coupon_code );
}
}
add_action('woocommerce_before_calculate_totals', 'hwn_add_programmatically_created_coupon_to_basket');
Tested and works

Limit customers to buy a particular product multiple times within a certain time frame in WooCommerce

I'm looking for a restriction if a guest user has bought a specific product 2 times in a week he must not be able to make another purchase of same product.
Im looking to apply this restriction based on guest user's email.
I've seen many posts related to that but all focus on registered users however i want to apply this restriction for guest users.
This is the code I am currently using, unfortunately without the desired result
function my_ip_checker() {
$last_order = get_posts(array(
//'date_created' => '>=' . (time() - 86400), time in seconds
'meta_key' => '_billing_email',
'meta_value' => sanitize_email( $_POST['cb_email'] ),
'post_type' => 'shop_order',
'post_status' => array('wc-processing', 'wc-completed')
));
if($last_order->total > 1) {
wc_add_notice('Too many orders in the last 24 hours. Please return later.', 'error');
}
}
add_action('woocommerce_checkout_process', 'my_ip_checker', 10, 0);
Any help is appreciated.
For a restriction for a specific product per week you could use:
Based on the billing email address
wc_get_orders provide a standard way of retrieving orders - wc_get_orders and WC_Order_Query
Will work for guest users or logged in users
function action_woocommerce_checkout_process() {
// Initialize
$customer_email = '';
// Get email
if ( is_user_logged_in() ) {
// Get current user
$user = wp_get_current_user();
// Get email
$customer_email = $user->user_email;
} elseif ( isset( $_POST['billing_email'] ) && ! empty ( $_POST['billing_email'] ) ) {
$customer_email = $_POST['billing_email'];
} else {
// Get billing_email
$customer_email = WC()->customer->get_billing_email();
}
// NOT empty
if ( ! empty ( $customer_email ) ) {
// Time in seconds (1 week)
$time_in_seconds = 604800;
// Set limit per week
$limit = 2;
// Specific product id
$specific_product_id = 30;
// Get orders from last week from customer by email
$orders_last_week_by_customer_email = wc_get_orders( array(
'date_created' => '>' . (time() - $time_in_seconds ),
'customer' => $customer_email,
));
// Total (counter)
$total = 0;
// Iterating through each order
foreach ( $orders_last_week_by_customer_email as $order ) {
// Going through order items
foreach ( $order->get_items() as $item ) {
// Get product ID
$product_id = $item->get_product_id();
// Compare
if ( $specific_product_id == $product_id ) {
// Get quantity
$quantity = $item->get_quantity();
// Add to total
$total += $quantity;
}
}
}
// Show error when total >= limit
if ( $total >= $limit ) {
wc_add_notice( sprintf( __( 'You are not allowed to buy more than %d pieces of product with ID = %d in one week', 'woocommerce' ), $limit, $specific_product_id ), 'error' );
}
}
}
add_action( 'woocommerce_checkout_process', 'action_woocommerce_checkout_process', 10, 0 );
To apply this only for guest users:
Replace
// Get email
if ( is_user_logged_in() ) {
// Get current user
$user = wp_get_current_user();
// Get email
$customer_email = $user->user_email;
} elseif ( isset( $_POST['billing_email'] ) && ! empty ( $_POST['billing_email'] ) ) {
$customer_email = $_POST['billing_email'];
}
With
// Only for guests
if ( ! is_user_logged_in() ) return;
if ( isset( $_POST['billing_email'] ) && ! empty ( $_POST['billing_email'] ) ) {
$customer_email = $_POST['billing_email'];
}
For a restriction for a specific variable product per week i used:
I used parent product ID for guest user restricion:
Based on the billing email address
Based on phone number
wc_get_orders provide a standard way of retrieving orders - wc_get_orders and WC_Order_Query
function action_woocommerce_checkout_process() {
$specific_product_name = 'Buy iPhone 12 with Z Protect+ included';
// Only for guests
if ( is_user_logged_in() ) return;
//product check start
global $woocommerce;
$parent_id;
$_product = '';
$items = $woocommerce->cart->get_cart();
foreach($items as $item => $values) {
$_product = wc_get_product( $values['data']->get_id());
}
$parent_id = $_product->get_parent_id();
if( $parent_id == 389 ){
//product check end
// Isset
if ( isset( $_POST['billing_phone'] ) ) {
// NOT empty
if ( ! empty ( $_POST['billing_phone'] ) ) {
$customer_phone = $_POST['billing_phone'];
}
}
// Isset
if ( isset ( $customer_phone ) ) {
// Time in seconds (1 week)
$time_in_seconds = 604800;
// Set limit per week
$limit = 2;
// Specific product name
$specific_product_name = 'Buy iPhone 12 with Z Protect+ included';
// Get orders from last week from customer by phone
$orders_last_week_by_customer_phone = wc_get_orders( array(
'date_created' => '>' . (time() - $time_in_seconds ),
'billing_phone' => $customer_phone,
));
$total = 0;
// Iterating through each order
foreach ( $orders_last_week_by_customer_phone as $order ) {
// Going through order items
foreach ( $order->get_items() as $item ) {
// Name of the producte
$product_name = $item->get_name();
// Compare
if ( $specific_product_name == $product_name ) {
// Add to total
$total += 1;
}
}
}
// Show error when total >= limit
if ( $total >= $limit ) {
wc_add_notice( sprintf( __( 'You are not allowed to buy more than %d iPhones. For more information please contact support.', 'woocommerce' ), $limit ), 'error' );
}
}
// Isset
if ( isset( $_POST['billing_email'] ) ) {
// NOT empty
if ( ! empty ( $_POST['billing_email'] ) ) {
$customer_email = $_POST['billing_email'];
}
}
// Email NOT empty
if ( ! empty ( $customer_email ) ) {
// Time in seconds (1 week)
$time_in_seconds = 604800;
// Set limit per week
$limit = 2;
// Specific product id
$specific_product_id = 'Buy iPhone 12 with Z Protect+ included';
// Get orders from last week from customer by email
$orders_last_week_by_customer_email = wc_get_orders( array(
'date_created' => '>' . (time() - $time_in_seconds ),
'customer' => $customer_email,
));
// Total (counter)
$total = 0;
// Iterating through each order
foreach ( $orders_last_week_by_customer_email as $order ) {
// Going through order items
foreach ( $order->get_items() as $item ) {
// Get product ID
$product_id = $item->get_name();
// Compare
if ( $specific_product_id == $product_id ) {
// Get quantity
$quantity = $item->get_quantity();
// Add to total
$total += $quantity;
}
}
}
// Show error when total >= limit
if ( $total >= $limit ) {
wc_add_notice( sprintf( __( 'You are not allowed to buy more than %d iPhones. For more information please contact support.', 'woocommerce' ), $limit), 'error' );
}
}
//product check start
}
//product check start
}
add_action( 'woocommerce_checkout_process', 'action_woocommerce_checkout_process', 10, 0 );
Thanks to the 1st answer above for providing me the base to start implementation.
Any further improvements in my code are welcomed.

Auto apply coupon only one time per user based on total spent in WooCommerce

I would like to apply a coupon automatically based on customer total spent amount. This coupon needs to be applied only one time by customer.
This is what I've tried so far but I'm getting a blank screen:
add_action( 'woocommerce_before_calculate_totals', 'loyalty_order_discount', 10, 1 );
function loyalty_order_discount( $order_id ) {
global $woocommerce;
$coupon = 'loyaltydiscount';
$customer = new WC_Customer(get_current_user_id());
$total_spent = 30;
$order = wc_get_order( $order_id );
foreach( $order->get_used_coupons( $customer ) as $coupon_name ){
// Retrieving the coupon ID
$coupon_post_obj = get_page_by_title($coupon_name, OBJECT, 'shop_coupon');
$coupon_id = $coupon_post_obj->ID;
$coupons_obj = new WC_Coupon($coupon_id);
if( $coupons_obj == $coupon && $customer->get_total_spent() < $total_spent ){
$woocommerce->cart->remove_coupon( $coupon );
}
elseif ( ! $coupons_obj == $coupon && $customer->get_total_spent() >= $total_spent){
$woocommerce->cart->add_discount( $coupon );
}
}
}
Any help is appreciated.
First you need to set, for your coupon, the "Usage limit per user" option to 1 (and save) like:
Now the following revisited code will auto apply a specific coupon only once when the customer total spent has reached a defined amount:
add_action( 'woocommerce_before_calculate_totals', 'enable_customer_loyalty_discount', 10, 1 );
function enable_customer_loyalty_discount( $cart ) {
if ( ( is_admin() && ! defined( 'DOING_AJAX' ) ) || ! is_user_logged_in() )
return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
//Your settings below
$coupon_code = 'loyaltydiscount';
$coupon_code = 'summer';
$total_spent = 30;
if( ! in_array( $coupon_code, $cart->get_applied_coupons() ) ) {
$user_id = get_current_user_id(); // User ID
// Get the WC_Customer instance Object
$customer = New WC_Customer( $user_id );
$email = $customer->get_billing_email(); // Billing email
// If the user total spent has not reached the define amount, we exit
if( $customer->get_total_spent() < $total_spent ) {
return;
}
// Get the WC_Coupon instance Object
$coupon = New WC_Coupon( $coupon_code );
// If the coupon has already been used by the customer, we exit
if( array_intersect( array($user_id, $email), $coupon->get_used_by() ) {
return;
}
$cart->apply_coupon( $coupon_code );
}
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.

Hook woocommerce price in backend order edition

I need to change item product prices in Woocommerce Backend Order. I tried tu use the following hook, but I have a problem trying to obtain the order id. Any suggestion? Thanks in advance!
function my_custom_prices ($price, $product)
{
if ( !is_admin() || ( is_admin() && is_post_type_archive() ) ) return $price;
global $post, $woocommerce;
$order = new WC_Order( $post_id );
$user_id = $order->user_id;
$user = get_user_by('id', $user_id);
if ( in_array( 'my_role', (array) $user->roles ) ) {
return $price * 2;
}
else {
return $price;
}
}
add_filter('woocommerce_get_price', 'my_custom_prices ', 10, 2);
Complete problem:
The complete problem is as follows. I am using a plugin that adds a field to the product called wholesale price. If the customer has the wholesale customer role, the order uses those prices. The plugin works fine, but it does not take the price in the backend. I talked to the author and it's something they do not plan to change yet. My client needs to modify the orders. But when it enters the backend, it takes the common price, not the wholesaler. I need to do something in the backend that allows me to detect if the order is from a client with a wholesale customer role. If yes, take the correct price when adding products. There is more information on the discussion with the author here. https://wordpress.org/support/topic/wholesale-prices-in-backend-editing-orders/ Thank you very much for the help you can give me.
Options:
woocommerce_get_price: does not work because I cannot obtain the customer id
woocommerce_ajax_add_order_item_meta: nice option, but I could not find a sample
Button: nice option, but I do not know how can I change the price. I tryed the follow:
add_action( 'woocommerce_order_item_add_action_buttons', 'action_aplicar_mayoristas', 10, 1);
function action_aplicar_mayoristas( $order )
{
echo '<button type="button" onclick="document.post.submit();" class="button button-primary generate-items">Aplicar precios mayoristas</button>';
echo '<input type="hidden" value="1" name="aplicar_mayoristas" />';
};
add_action('save_post', 'aplicar_mayoristas', 10, 3);
function aplicar_mayoristas($post_id, $post, $update){
$slug = 'shop_order';
if(is_admin()){
if ( $slug != $post->post_type ) {
return;
}
if(isset($_POST['aplicar_mayoristas']) && $_POST['aplicar_mayoristas']){
$order = wc_get_order( $post_id);
//$order_id = $order->get_user_id();
// Iterating through each "line" items in the order
foreach ($order->get_items() as $item_id => $item_data) {
//$item_data->set_subtotal("798");
$item_data->set_price("798");
//->set_price($custom_price);
}
}
}
}
Updated
The hook that you are using is not for orders, but only for products, and is made to change the displayed prices only. So you will not get the order ID with it.
You could change the price display in many hooks, but if you want to change order item prices for real (not only the displayed formatted prices), you should trigger this prices changes when order is updated for example.
In this case you can use a custom function hooked in save_post action hook:
add_action( 'save_post', 'change_order_item_prices', 11, 1 );
function change_order_item_prices( $post_id ) {
// If this is an autosave (has not been submitted).
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
return $post_id;
// Check the user's permissions.
if ( 'shop_order' == $_POST[ 'post_type' ] ){
if ( ! current_user_can( 'edit_shop_order', $post_id ) )
return $post_id;
} else {
if ( ! current_user_can( 'edit_post', $post_id ) )
return $post_id;
}
## ------------------- Changing prices Start code ------------------- ##
## ===> HERE define the targeted user role
$user_role = 'my_role';
## ===> HERE define the rate multiplier (for price calculations)
$multiplier = 2;
// If this Order items prices have already been updated, we exit
$items_prices_updated = get_post_meta( $post_id, 'line_item_updated', true );
if( ! empty( $items_prices_updated ) ) return $post_id; // Exit
$order = new WC_Order( $post_id ); // Get the order object
$user_id = $order->get_user_id(); // Get the user ID
$user_data = get_userdata( $user_id ); // Get the user data
// Check the user role
if ( ! in_array( $user_role, $user_data->roles ) ) return;
// Loop through order items
foreach( $order->get_items() as $item_id => $item ){
$item_data = $item->get_data(); // The item data
$taxes = array();
foreach( $item_data['taxes'] as $key_tax => $values ){
if( ! empty( $values ) ){
foreach( $values as $key => $tax_price ){
$taxes[$key_tax][$key] = floatval($tax_price) * $multiplier;
}
}
}
$new_line_subtotal = floatval( $item_data['subtotal'] ) * $multiplier;
$new_line_subt_tax = floatval( $item_data['subtotal_tax'] ) * $multiplier;
$new_line_total = floatval( $item_data['total'] ) * $multiplier;
$new_line_total_tax = floatval( $item_data['total_tax'] ) * $multiplier;
// Update Order item prices
$item->set_subtotal($new_line_subtotal);
$item->set_subtotal_tax($new_line_subt_tax);
$item->set_total($new_line_total);
$item->set_total_tax($new_line_total_tax);
$item->set_taxes($taxes);
// Save the updated data
$item->save();
}
// Udpate order totals and cache
$order->calculate_totals();
// We mark the order as updated (to avoid repetition)
update_post_meta( $post_id, 'line_item_updated', true );
}
Code goes in function.php file of your active child theme (or theme) or also in any plugin file.
Tested and and finally works.
I have added a security to avoid the order items to be updated twice.
The method $order->calculate_totals(); slow down the process a little bit… It's normal as it will calculate totals, update data and refresh caches.
Your code needs to be debugged.
$post_id - there is not such variable or parameter inside your function. So, use $post->ID instead.
do
var_dump( (array) $user->roles);
before the
if (in_array( 'my_role', (array) $user->roles ) )
line. And make sure that my_role exists in that array.
Temporary comment this line for debugging purpose:
// if (is_admin() && is_post_type_archive())
Then you will see the reason and able to be fix it.

Categories