I'm trying to implement a feature where customers receive a new user role after a certain amount of orders have been made, but all those orders must have been made within the same year.
I was able to assign the user role based on nth number of orders but can't seem to go beyond to where the date needs to be taken into consideration, can someone point me in the right direction or point out what I might be missing.
This is what I have tried so far.
function change_user_role_on_order_status_completed( $order_id ) {
$order = new WC_Order( $order_id );
$user_id = $order->user_id;
$order_this_year = false;
$current_date = date('Y');
$total_orders = get_posts( array(
'numberposts' => -1,
'meta_key' => '_customer_user',
'meta_value' => $user_id,
'post_type' => 'shop_order',
) );
if ( $total_orders > 1 ) {
foreach ($order->get_items() as $item_key => $item_values):
// Get the item date
if ($item_date = $item_values->get_date_completed()->format ('Y') == $current_date) {
$order_this_year = true;
}
endforeach;
if ($order_this_year) {
$user = new WP_User( $order->user_id );
// Set role editor
$user->set_role( 'customer_club' );
}
}
}
add_action( 'woocommerce_order_status_completed', 'change_user_role_on_order_status_completed', 10, 1 );
Using WC_Order_Query here is a much lighter and simple way to change the user role based on yearly orders count:
add_action( 'woocommerce_order_status_completed', 'change_user_role_on_order_status_completed', 10, 2 );
function change_user_role_on_order_status_completed( $order_id, $order ) {
// Here set the minimal order count
$min_orders_count = 3;
// The WC_Order_Query
$queried_orders = wc_get_orders( array(
'limit' => -1,
'customer_id' => $order->get_customer_id(),
'date_paid' => '>=' . date('Y') . '-01-01', // or 'date_created'
'return' => 'ids'
) );
if ( sizeof($queried_orders) >= $min_orders_count ) {
// Get an instance of the customer WP_User Object
$user = $order->get_user();
// Change the user role
$user->set_role( 'customer_club' );
}
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
Related
I am trying to get all orders from woocommerce. Following the instruction on https://github.com/woocommerce/woocommerce/wiki/wc_get_orders-and-WC_Order_Query
I put the following code in my function.php
// Get latest 3 orders.
$args = array(
'limit' => 3,
);
$orders = wc_get_orders( $args );
var_dump($orders);
However, it outputs an empty array.
I checked my code and found I actually used wc_get_orders in a hook as below
add_action( 'woocommerce_order_status_changed', 'change_role_on_first_purchase',10,4 );
function change_role_on_first_purchase( $order_id,$old_status, $new_status, $order ) {
$userID = $order->user_id;
$user = new WP_User( $userID );
if ( in_array('subscriber',$user->roles) ){
$args = array(
'customer' => $userID,
'exclude' => array( $order->get_id() ),
'status' => array('completed')
);
$orders = wc_get_orders($args);
if (!$orders && $new_status == "completed"){
$user->set_role('customer');
}
}
}
This is used to change the user's role from subscriber to customer after he places the first order. This function works on my site. So wc_get_orders works here. Why then it is not working in my function.php?
It outputs an empty array because Woocommerce has not yet registered order types/statuses when you're calling wc_get_orders()
Try adding it after init event:
add_action( 'init', 'test_init' );
function test_init() {
// Get latest 3 orders.
$args = array(
'limit' => 3,
);
$orders = wc_get_orders( $args );
var_dump($orders);
}
https://developer.wordpress.org/reference/hooks/init/
Is is possible to get current and previous customer order? Namely I need Billing Address of current order and Billing Address of previous order per customer.
For example I need to get array of every Customer Order, namely I need Billing Address from every customer order.
So I have some code that print any text at "Edit Order page" in admin panel.
add_action( 'woocommerce_admin_order_data_after_shipping_address', 'edit_woocommerce_order_page', 10, 1 );
function edit_woocommerce_order_page($order){
global $post_id;
$order = new WC_Order( $post_id );
echo '<p><strong>Some text here</strong> ' . get_post_meta($order->get_id(), '_shipping_field_value', true ) . '</p>';
}
As you can see it displays Some text for every user. I guess I should get some array of every customer and display Order array with order ID and Billing Address 1. Check screenshot please
Code above adds text in Edit Order page
Is it possible?
The following function will grab all completed orders if customer is not a guest.
add_action( 'woocommerce_admin_order_data_after_shipping_address', 'edit_woocommerce_order_page', 10, 1 );
function edit_woocommerce_order_page($order){
$customer_id = $order->get_user_id();
//Check if its guest or not
if($customer_id != 0):
$args = array(
'customer_id' => $customer_id,
'status' => array('wc-completed'), //Change if needed
'exclude' => array( $order->get_id() ), // We dont need current order
);
$orders = wc_get_orders( $args );
if($orders):
foreach($orders as $k=>$order):
echo '<p><strong>Some text here</strong>'.$order->get_billing_address_1().'</p>';
endforeach;
endif;
endif;
}
Limit to last 2 orders
add_action( 'woocommerce_admin_order_data_after_shipping_address', 'edit_woocommerce_order_page', 10, 1 );
function edit_woocommerce_order_page($order){
$customer_id = $order->get_user_id();
//Check if its guest or not
if($customer_id != 0):
$args = array(
'customer_id' => $customer_id,
'status' => array('wc-completed'), //Change if needed
'limit' => 2,
);
$orders = wc_get_orders( $args );
if($orders):
foreach($orders as $k=>$order):
echo '<p><strong>Some text here</strong>'.$order->get_billing_address_1().'</p>';
endforeach;
endif;
endif;
}
Getting current order and previous one only
function edit_woocommerce_order_page($order){
$customer_id = $order->get_user_id();
//We need current order id to know where we start
$order_id = $order->get_id();
if($customer_id != 0):
$args = array(
'customer_id' => $customer_id,
'status' => array('wc-completed'), //Change if needed
'return' => 'ids', // Grab all order ids for customer
'posts_per_page' => -1
);
$all_order_ids = wc_get_orders( $args );
//Find current order key
$all_order_id_keys = array_flip(array_keys($all_order_ids));
$current_order_key = array_keys($all_order_ids, $order_id);
//Grab all values from our array
$all_order_id_values = array_values($all_order_ids);
//From all values we look for current order key and we increase that key with 1 to grab prev order id by key
$previous_order_id = $all_order_id_values[$all_order_id_keys[$current_order_key[0]]+1];
$order_args = array(
'post__in' => array($order_id,$previous_order_id),
);
$orders = wc_get_orders( $order_args );
if($orders):
foreach($orders as $k=>$order):
echo $order->get_id(); // For testing
echo '<p><strong>Some text here</strong>'.$order->get_billing_address_1().'</p>';
endforeach;
endif;
endif;
}
add_action( 'woocommerce_admin_order_data_after_shipping_address', 'edit_woocommerce_order_page', 10, 1 );
I am looking for a way, to limit coupon usage and to display an error message if a customer have used related coupons in previous orders before in WooCommerce.
By related coupons, I mean: couponcodes that appear in a predefined array along with the currently inserted coupon on the cart/checkout page. On that basis, compare.
My code attempt:
add_filter( 'woocommerce_coupon_is_valid', 'specific_coupons_valid', 10,
3 );
function specific_coupons_valid( $is_valid, $coupon, $discount ){
$coupon_codes = array( 'free10', 'free20', 'free30' );
$args = array(
'posts_per_page' => -1,
'orderby' => 'title',
'order' => 'asc',
'post_type' => 'shop_coupon',
'post_status' => 'publish',
);
$coupons = get_posts( $args );
if( in_array( $coupons, $coupon_codes ) ) {
$is_valid = false;
}
return $is_valid;
}
Any help is appreciated
To limit coupon usage if a customer have used related coupons in previous orders in WooCommerce before, you can use:
// Compare & return true OR false
function compare_related_coupon_codes( $current_coupon_code ) {
// Add multiple coupon codes to compare, seperated by a comma
$compare_coupon_codes = array( 'coupon1', 'coupon2', 'coupon3' );
// Default
$valid = true;
// When the current coupon code has to be compared with other coupon codes
if ( in_array( $current_coupon_code, $compare_coupon_codes ) ) {
// Get user ID
$user_id = get_current_user_id();
// Get the WC_Customer instance Object
$customer = New WC_Customer( $user_id );
// Billing email
$email = $customer->get_billing_email();
// Loop through
foreach ( $compare_coupon_codes as $coupon_code ) {
// Get the WC_Coupon instance Object
$coupon_obj = New WC_Coupon( $coupon_code );
// If one of the coupons has already been used by the customer
if ( array_intersect( array( $user_id, $email ), $coupon_obj->get_used_by() ) ) {
$valid = false;
break;
}
}
}
// Return
return $valid;
}
// Valid
function filter_woocommerce_coupon_is_valid( $valid, $coupon, $discount ) {
if ( ! is_user_logged_in() ) return $valid;
// Get current applied coupon code
$current_coupon_code = strtolower( $coupon->get_code() );
// Call function, true OR false
$valid = compare_related_coupon_codes( $current_coupon_code );
// NOT valid
if ( ! $valid ) {
throw new Exception( __( 'My error', 'woocommerce' ), 109 );
}
return $valid;
}
add_filter( 'woocommerce_coupon_is_valid', 'filter_woocommerce_coupon_is_valid', 10, 3 );
Explanation via comment tags added in my answer
Related: Coupon daily time range in WooCommerce
I previously implemented a solution to change the user role on registration, but seem to be missing something this time around.
Does anyone out there have a solution for this that still works or be able to point me in the right direction as to what I'm missing or doing wrong?
Does is what I have tried so far.
add_action('woocommerce_thankyou', 'change_user_role_on_order_success');
function change_user_role_on_order_success($order_id ) {
// Get all customer orders
$customer_orders = get_posts( array(
'numberposts' => -1,
'meta_key' => '_customer_user',
'meta_value' => get_current_user_id(),
'post_type' => 'shop_order', // WC orders post type
'post_status' => 'wc-completed' // Only orders with status "completed"
) );
// Count number of orders
$count = count( $customer_orders );
// return "true" when customer has already one order
if ( $count >= 1 ) {
$user = new WP_User( $order->get_user_id() );
// our new role name
$role = 'editor';
//set the new role to our customer
$user->set_role($role);
}
}
Maybe you could try to use the hook woocommerce_order_status_completed instead. It should fire as soon as your customer really completed the order and removes the need for you to check for the status.
function change_user_role_on_order_status_completed( $order_id ) {
$order = new WC_Order( $order_id );
$user_id = $order->user_id;
$total_orders = get_posts( array(
'numberposts' => -1,
'meta_key' => '_customer_user',
'meta_value' => $user_id,
'post_type' => 'shop_order',
) );
if ( $total_orders > 1 ) {
$user = new WP_User( $order->user_id );
// Set role editor
$user->set_role( 'editor' );
}
}
add_action( 'woocommerce_order_status_completed', 'change_user_role_on_order_status_completed', 10, 1 );
I have a product loop in Woocommerce. If a customer has purchased a product before, I would like to replace the purchase product button with a message and the date of the order. For example.
"You purchased this product on Jan. 15, 2016"
I can get the product id and I can get the current user id, but can't figure out how to use those pieces of info to pull order ids.
$postid = get_the_ID();
$current_user = wp_get_current_user();
$has_product = wc_customer_bought_product( $current_user->user_email, $current_user->ID, $product->id);
Ideas?
You can use this function below
function _cmk_check_ordered_product( $id ) {
// Get All order of current user
$orders = get_posts( array(
'numberposts' => -1,
'meta_key' => '_customer_user',
'meta_value' => get_current_user_id(),
'post_type' => wc_get_order_types( 'view-orders' ),
'post_status' => array_keys( wc_get_order_statuses() )
) );
if ( !$orders ) return false; // return if no order found
$all_ordered_product = array(); // store products ordered in an array
foreach ( $orders as $order => $data ) { // Loop through each order
$order_data = new WC_Order( $data->ID ); // create new object for each order
foreach ( $order_data->get_items() as $key => $item ) { // loop through each order item
// store in array with product ID as key and order date a value
$all_ordered_product[ $item['product_id'] ] = $data->post_date;
}
}
if ( isset( $all_ordered_product[ $id ] ) ) { // check if defined ID is found in array
return 'You purchased this product on '. date('M. d, Y', strtotime( $all_ordered_product[ $id ] ) );
} else {
return 'Product Never Purchased';
}
}
e.g.
Showing message on single product page echo _cmk_check_ordered_product( get_the_ID() );