Woocommerce: How to Add extra fee for First Order - php

I am trying to add Extra Fee in the total of cart/order amount only if this is first order of Customer.
I searched a lot online but I still did not find any specific solution yet.
Please someone suggest/guide the best solution to perferm this.
Thanks

You use the cart class' add_fee() method to add a fee. There's no built-in way to know how many orders a customer has made, so we can try to track that via a user meta field called _number_order.
function so_27969258_add_cart_fee() {
$orders = intval( get_user_meta( get_current_user_id(), '_number_orders', true ) );
if( $orders < 1 ){
WC()->cart->add_fee( __( 'First time fee', 'your-plugin'), 100 );
}
}
add_action( 'woocommerce_before_calculate_totals', 'so_27969258_add_cart_fee' );
If we don't actually update the _number_orders key, then it will always be null/empty/zero and the user will always be charged the first time fee. So we can try to update that key when the user completes payment.
function so_27969258_track_orders_per_customer(){
$orders = intval( get_user_meta( get_current_user_id(), '_number_orders', true ) );
$orders = $orders + 1;
update_user_meta( get_current_user_id(), '_number_orders', $orders );
}
add_action( 'woocommerce_payment_complete', 'so_27969258_track_orders_per_customer' );
This is totally untested, so use at your own risk. Also, you might want to look into changing the number of orders total in case of refunds/cancellations, etc, but this seems like the general gist.

Related

Saving a product meta field to order item meta in WooCommerce

First time posting...so hopefully I've done this correctly. :)
I'm currently working on a snippet to save a custom product field (purchase price) into the order item meta. The reason that we want to do this is that the purchase price changes during the time where orders are accepted. In order to produce accurate reporting - we need to know what the purchase price field (custom product field) was set to when the order was placed. Simply exporting the custom field (_wcj_purchase_price) gives the current value, which may not be current.
The custom field that stores the purchase price is called _wcj_purchase_price and I am trying to create a custom order item meta field called _purchase_price which is added at the time the order is created.
This is the code that I've got so far, from my attempts, but it's not working quite right. I am sure the answer is obvious to someone, but I'm very stuck at the moment!
add_action( 'wc_add_order_item_meta', 'save_item_sku_order_itemmeta', 10, 3 );
function save_item_sku_order_itemmeta( $item_id, $values, $cart_item_key ) {
$purchasep = get_post_meta( $values[ 'product_id' ], '_wcj_purchase_price', true );
wc_add_order_item_meta( $item_id, '_purchase_price', $purchasep , false );
}
Any ideas or advice? Please let me know if anything is unclear.
Thank you very much!
Are you sure that there is a filter called "wc_add_order_item_meta"?
Correct filter for this case can be woocommerce_new_order.
add_action('woocommerce_new_order', function ($order_id) {
// your code here
}, 10, 1);
For exampe, you can use such code:
add_action('woocommerce_new_order', function ($order_id) {
$order = wc_get_order( $order_id );
foreach( $order->get_items() as $item_id => $item ){
$product_id = $item->get_product_id();
$purchasep = get_post_meta( $product_id, '_wcj_purchase_price', true );
wc_add_order_item_meta($item_id, '_purchase_price', $purchasep , false );
}
}, 10, 1);

WooCommerce Subscriptions: Is there a hook for a manual payment retry?

I have Woocommerce orders that are pending payment. At the moment when an order hits the automatic retry payment schedule it pulls the payment information from the subscription, which works great as the customer may have corrected their wrong details by then.
But in some cases, we need to manually retry the payment. When this is done, it doesn't pull the payment information from the subscription (as the customer may have corrected their details for it to go through).
Is there a hook/action that I can use to fire the following code? woocommerce_subscriptions_before_payment_retry doesn't seem to work.
add_action('woocommerce_subscriptions_before_payment_retry', 'remove_payment_details', 10, 2 );
function remove_payment_details( $order_id ){
$order = wc_get_order( $order_id ); // Order Object
$subscriptions = wcs_get_subscriptions_for_order( $order_id, array( 'order_type' => 'any' ) ); // Array of subscriptions Objects
foreach( $subscriptions as $subscription_id => $subscription ){
$stripe_cust_id = $subscription->get_meta( '_stripe_customer_id');
$stripe_src_id = $subscription->get_meta( '_stripe_source_id' );
$order->update_meta_data( '_stripe_customer_id', $stripe_cust_id );
$order->update_meta_data( '_stripe_source_id', $stripe_src_id );
$order->save();
}
}
Please check woocommerce_order_action_wcs_retry_renewal_payment hook.
The hook is for "Retry Renewal Payment" handling, and you can use it.
add_action( 'woocommerce_order_action_wcs_retry_renewal_payment', 'custom_process_retry_renewal_payment_action_request', 20, 1 );
function custom_process_retry_renewal_payment_action_request( $order ) {
// your code is here
}

Hide orders with status (Pending Payment) on WooCommerce My Account page

Inspired by the thread 'Hiding order status in My Account recent orders list page'
I tried to hide orders with status (Pending Payment) on the 'My Account' page.
I modified the code slightly, but I can't get it to work.
add_filter('woocommerce_my_account_my_orders_actions', 'custom_removing_order_status_pending_payment', 10, 1);
function custom_removing_order_status_pending_payment( $order ){
unset($order['wc-pending']);
return $order;
}
I really appreciate any help. Thank you.
Right now you are using the woocommerce_my_account_my_orders_actions filter which will let you filter the buttons in the 'Action' column on the 'My Account' page.
If you want to filter out certain order statuses from the order list you will have to use the woocommerce_my_account_my_orders_query filter.
add_filter( 'woocommerce_my_account_my_orders_query', 'unset_pending_payment_orders_from_my_account', 10, 1 );
function unset_pending_payment_orders_from_my_account( $args ) {
$statuses = wc_get_order_statuses();
unset( $statuses['wc-pending'] );
$args['post_status'] = array_keys( $statuses );
return $args;
}
The accepted answer is mostly, OK, however, it relies on WooCommerce saving Orders as a WordPress custom post type, and there's been for a while a conversation about moving orders to their own custom table to improve performance and scalability of WooCommerce-powered stores.
As a general rule, it is better to use WooCommerce's specific methods and parameters.
The filter woocommerce_my_account_my_orders_query uses wc_get_orders which says to use 'status' as parameter, and not 'post_status'
The updated answer using WC_Order_Query would be
add_filter( 'woocommerce_my_account_my_orders_query', 'unset_pending_payment_orders_from_my_account', 10, 1 );
function unset_pending_payment_orders_from_my_account( $args ) {
$statuses = wc_get_order_statuses();
unset( $statuses['wc-pending'] );
$args['status'] = array_keys( $statuses );
return $args;
}

Woocommerce Minimum Order by User Role

Somebody previously asked a question similar to mine, but the solutions offered do not seem to help me.
I am working on a wholesale website. For first time buyers, the minimum order is $500. For returning buyers / reorders there is no minimum. I am assuming I need to make 2 different wholesale user roles - one for first time buyers and the other for returning / reorder buyers. I was thinking Wholesale New and Wholesale Reorder would work. How do I apply this to the functions.php? This is the code I am currently using:
add_action( 'woocommerce_checkout_process', 'wc_minimum_order_amount' );
add_action( 'woocommerce_before_cart' , 'wc_minimum_order_amount' );
function wc_minimum_order_amount() {
// Set this variable to specify a minimum order value
$minimum = 500;
if ( WC()->cart->total < $minimum ) {
if( is_cart() ) {
wc_print_notice(
sprintf( 'You must have an order with a minimum of %s to place your order, your current order total is %s.' ,
wc_price( $minimum ),
wc_price( WC()->cart->total )
), 'error'
);
} else {
wc_add_notice(
sprintf( 'You must have an order with a minimum of %s to place your order, your current order total is %s.' ,
wc_price( $minimum ),
wc_price( WC()->cart->total )
), 'error'
);
}
}
Any help would be greatly appreciated.
You need current_user_can() to test the current user's capability to do something.
Create a Role
First, you would need to create a new role for users who don't have any restrictions. The Members plugin is genius for creating roles and editing their capabilities.
For example, you could create a Returning Wholesaler role.
Define the new Role's capabilities
You should duplicate the "Customer" role's capabilities... so read , edit_posts, and delete_posts.
And then, add a new capability called wholesale_reorder
Modify your conditional logic
Then you can convert your if statement from:
if ( WC()->cart->total < $minimum )
to:
if ( WC()->cart->total < $minimum && ! current_user_can( 'wholesale_reorder' ) )
Simplification in the case of no new roles
If you don't have any existing customers, don't need multiple levels of wholesale, don't have wholesale in conjunction with regular customers, and you don't have a way to register without ordering, then ostensibly you could skip adding new roles and just test current_user_can('customer') as the only people who would be customers would be those who have ordered something already.

How to create custom discount for the cart in woocommerce

I'm creating a Plugin in WooCommerce and have a small issue with adding custom discounts to the CART / CHECKOUT page.
How can I apply custom discount to the cart without creating coupons?
Say I want to give some discount of 5 dollars on the cart page. How can I do that?
Below is my code from the plugin file where I have used a coupon to apply discount, but I want to add another custom discount without the use of coupon.
Action Hook in the plugin file :
add_action('woocommerce_calculate_totals',array(&$this,'cart_order_total_action'));
and its function in the plugin file is :
public function cart_order_total_action(){
if ( is_user_logged_in() ){
global $woocommerce;
global $current_user;
global $wpdb;
$u_id = $current_user->ID;
$table_name = $wpdb->prefix."woocommerce_customer_reward_ms";
$thetable2 = $wpdb->prefix . "woocommerce_customer_reward_cart_ms";
$table_name3 = $wpdb->prefix."woocommerce_customer_reward_points_log_ms";
$data = $wpdb->get_row("SELECT * from $table_name where id=$u_id");
$data2 = $wpdb->get_row("SELECT * from $thetable2");
/* Order Id goes here */
$orders=array();//order ids
$args = array(
'numberposts' => -1,
'meta_key' => '_customer_user',
'meta_value' => $current_user->ID,
'post_type' => 'shop_order',
'post_status' => 'publish',
'tax_query'=>array(
array(
'taxonomy' =>'shop_order_status',
'field' => 'slug',
'terms' =>'on-hold'
)
)
);
$posts=get_posts($args);
$orders=wp_list_pluck( $posts, 'ID' );
$order = $orders[0];
/* Order Id ends here */
if($data){
$user_points = $data->points;
$points_set = $data2->woo_pts_set;
$coupon_code = 'wooreward_discount';
if($user_points>=$points_set){
// this following Code is optional and can be removed......as there is no need of if statement here
if ( $woocommerce->cart->has_discount( $coupon_code ) ) {
/*$woocommerce->add_error( __('Coupon Code Already Applied.!!','woocommerce'));*/
return false;
}else{
$woocommerce->cart->add_discount(sanitize_text_field($coupon_code));
$woocommerce->add_message( __('Taxco925 Reward Discount Applied.!!','woocommerce'));
}
}else{
$woocommerce->add_error( __('Not Enough Taxco925 Points.!!','woocommerce'));
}
}else{
$woocommerce->add_error( __('You have have not earned any Taxco925 Points yet.!!','woocommerce'));
}
}
}
As you can see this line $woocommerce->cart->add_discount(sanitize_text_field($coupon_code));
adds my discount to the cart. But it uses coupon in the background to do so . Is there any way I can add a custom discount without the use of coupon.
add_action('woocommerce_checkout_order_processed','custom_disount',10,1);
function custom_disount($order_id){
$order = wc_get_order($order_id);
$order_items = $order->get_items();
foreach ($order_items as $order_item_key => $order_item) {
$product = new WC_Product((int) $order_item['product_id']);
$quantity = (int) $order_item['qty'];
$discount=($product->regular_price*30)/100; //30% disount.
wc_update_order_item_meta($order_item_key,'_line_total',($product->regular_price*$quantity)-($discount*$quantity));
}
}
You can add discount to each and every product in the cart using "woocommerce_get_discounted_price" hook.
For Eg.:
function filter_woocommerce_get_discounted_price( $price, $values, $instance ) {
//$price represents the current product price without discount
//$values represents the product object
//$instance represent the cart object
$discount = 300; // add custom discount rule , This is just an example
return ($price - $discount);
};
add_filter('woocommerce_get_discounted_price','filter_woocommerce_get_discounted_price', 10, 3 );
Maybe too late, but If someone have another solution tell me.
I use something like:
$discount = floatval(10);
if(!empty($discount) || $discount != 0){
$discount *= -1; // convert positive to negative fees
$woocommerce->cart->add_fee('discount', $discount, true, '' ); // add negative fees
}
If you use paypal standard payment, you got an error because you can't submit a product with negative pricing.
You just need to edit the paypal woocommerce plugin to pass this value.
But other Payment method is ok!
Best Regards,
Add fee with negative value will not produce the right total fee.
Tax is added on the fee amount resulting in higher total fee than expected.
You need to create a "coupon" and apply it to the cart before you create the order from the cart (it will not calculate right if you apply it on $order directly). Then recalculate the cart->total and finally create an order from the cart, after you have saved the order you can remove the "dynamic" created "coupon" if you want. You can create dynamic coupons with any dynamic $value and of any type (fixed, percent etc etc).
This is the only way to add discounts in woo3+.
Fee is doing it wrong when it comes to discounts. Also woo say about fee "Not use negative values here!".
I guessed you wanted some example?
here....
<?php
// this code inside wordpress and with woo3+ of course......
// you have to figure out the rest yourself, how to implement it. but here it is...
$order_data = array (
'status' => 'on-hold' // or whatever order staus
// can have more data if need here...
);
// below creates a coupon with discount_type = fixed_cart, default.
$coupon = array (
'post_title' => 'coupon_discount',
'post_status' => 'publish',
'post_type' => 'shop_coupon'
);
// can be modified with update_post_meta discount_type = percent and so on....
$dynamic_discount = 20; // yes, just a number can be from another dynamic input source....
$new_coupon_id = wp_insert_post( $coupon ); // add the coupon to the cart
add_post_meta( $new_coupon_id , 'coupon_amount' , $dynamic_discount , true ); // add the "discount" value ($dynamic_discount)..... depends on discount_type... in this case fixed_cart
WC()->cart->add_to_cart( 2122 , 2 ); // add products, product_id , quantity ..... can be in a loop.
WC()->cart->add_discount( 'coupon_discount' ); // APPLY THE COUPON WITH DISCOUNT -> This is the trick....
WC()->cart->calculate_totals(); // do some math on the "cart"
WC()->checkout(); // yes, checkout the "cart", now with the discount....
$order_id = WC()->checkout()->create_order( $order_data ); // basic order data, see the top in this script.. get new created order_id.
$order = wc_get_order( $order_id ); // get the order...
// can do more with $order here if want, but NOT any coupons... it just not work in $order as is...
$order->calculate_totals(); // math
WC()->cart->empty_cart(); // empty cart....
$order->save(); // save the order...
wp_delete_post( $new_coupon_id , true ); // IF you want to delete the "dynamic" coupon created above... up 2 u, if not you will end up with a lot of coupons
// sorry, a bad example, uggly code, but at least it work.... :)
// btw, i like Pattaya, send bitcoins :)
// Again, sorry for uggly code...
?>

Categories