I am developing a Woocommerce shop and want to offer all members (users logged in) 20% off their Total.
I have created a coupon called "twentypercent_off" and I am programmatically applying this using a custom function and a hook in my functions.php.
This works well.
In WC settings I have checked "Calculate coupon discounts sequentially" and this works almost as I would expect.
However what I would like is that my "twentypercent_off" is always applied last in cases where there are multiple coupons.
No matter what I have attempted to do (loop through all coupons on current order, remove all coupons, add them back in the order I desire) I find that Woo-commerce seems to always apply them in the order of monetary savings.
For example if the customer orders $100 worth of products and they have a coupon for $25 and also qualified to receive 20% off total.
I would like the total to calculate as (100 -25) * 0.8 = $60
but WC is calculating as such (100 * 0.8) - 25 = $55
I tried the following but it made no difference:
if ( is_user_logged_in() ) {
$Coupon_code = 'twentypercent_off';
$Current_Coupons = WC()->cart->get_coupons();
$Non_Prog_Coupons=array();
// loop through all current coupons
foreach( $Current_Coupons as $code => $coupon ){
if($code==$coupon_code){
WC()->cart->remove_coupon( $code );
} else {
WC()->cart->remove_coupon( $code );
$Non_Prog_Coupons[]=$code; // array of non-programmatically added coupons
}
}
// loop through non-programmatically added coupons
foreach( $Non_Prog_Coupons as $x => $code ){
WC()->cart->add_discount( $code ); // add them again
}
// add programmatically added coupon last !
WC()->cart->add_discount($Coupon_code);
}
return;
The only possible solution I have come across is to apply the 20% discount as a negative fee, which is not suitable for our purposes and would be an absolute last resort
Any ideas on how to achieve this would be greatly appreciated
Related
I am trying to display the flat rate shipping cost for a product on its single product page.
From searching on Stack Overflow, I did find some solutions, but none worked quite as expected.
Currently, the Geo Location is being used, so I want to base the displayed shipping cost off of that location. Each product may have more than one shipping option, but I only want to display the flat rate fee for the individual product. E.g.
In the US - Product X - Shipping Cost $Y
In the UK - Product X - Shipping Cost £Z
Currently I have the following code:
function shipping_cost_display_1() {
$rates = WC()->session->get("shipping_for_package_0")['rates'];
if (! empty($rates) ) {
foreach ( $rates as $rate_key => $rate ) {
$method_id = $rate->method_id;
$instance_id = $rate->instance_id;
$rate_id = $rate_key;
$label = $rate->label;
$cost = $rate->cost;
$taxes_array = $rate->taxes;
$update_cost = ceil($cost *= (1 + display_tax_rate_on_single_product() / 100));
if ($label == 'Shipping cost') {
//'Shipping cost' is the Flat Rate
echo $label;
echo number_format($update_cost, 2);
}
}
}
}
It is displaying on the product page, but there are some issues. It will display the correct price on one page, but then if I go to another page the same price for the previous product will be displayed. If I add something to the cart then the price displayed on all product pages will change to that, so it seems to be related to the cart somehow.
Is there a way to get the direct flat rate for the individual product rather than have it related to the cart? Since Geo Location is already active, it should be possible to simply pull the flat rate for the product based on the current location.
I assume this current issue is happening somehow due to WC()->session , but any input would be appreciated.
I got a Problem. In Woocommerce I added a custom product type (abo), where the user can choose a area size (in square meters) and there is a price per square meter and a dosage information where it says e.g. "You need 0.165kg per squaremeter" (it´s a fertilizer).
So for example if the user chooses 100 square meters and the price per square meter is 2.30€ and the dosage is 0.165kg/sqm the total price of the abo is 37.95€ and at a tax rate of 10.7% the tax amount should be 4.06€.
My Problem now is, I have normal products in the shop as well, where the standard calculation of the price and tax etc. works perfectly fine and you can buy the abo and a normal product.
So my question is, how can I recalculate the tax amount if I got an abo in cart?
I tried a few things for testing so far:
Adding a custom fee (that isn´t what I´m looking for, cause the tax has to be the same on cart, checkout and receipt):
add_action( 'woocommerce_cart_calculate_fees','custom_tax_surcharge_for_swiss', 10, 1 );
function custom_tax_surcharge_for_swiss( $cart ) {
if(current_user_can('administrator')) :
$percent = 10.7;
// Calculation
$surcharge = ( $cart->cart_contents_total + $cart->shipping_total ) * $percent / 100;
// Add the fee (tax third argument disabled: false)
$cart->add_fee( __( 'TAX', 'woocommerce')." ($percent%)", $surcharge, false );
endif;
}
calc_tax function (I think this one is the one I need) :
/ define the woocommerce_calc_tax callback
function filter_woocommerce_calc_tax( $taxes, $price, $rates, $price_includes_tax, $suppress_rounding ) {
if(current_user_can('administrator')) :
foreach(WC()->cart->cart_contents as $item) :
if($item['data']->get_type() == 'abo') :
$qty = $item['quantity'];
$ppqm = str_replace(',', '.', get_post_meta($item['product_id'], 'abo_product_price_per_kg')[0]);
$kgpqm = str_replace(',', '.', get_post_meta($item['product_id'], 'abo_product_kg_qm')[0]);
$realQty = floatval($kgpqm) * floatval($qty);
$priceCustom = $priceCustom + $realQty * floatval($ppqm);
endif;
endforeach;
return $taxes;
};
// add the filter
add_filter( 'woocommerce_calc_tax', 'filter_woocommerce_calc_tax', 10, 5 );
With the above function I got the problem, that I don´t have the cart contents directly so it could be, that the order of the items isn´t the same as the Tax order which I get from the function.
I would also note, that the woocommerce_calc_tax fires twice. I had for debug reasons an output in the console and if I have 2 items in cart, I got 4 outputs.
Hope my Problem is clear and that anyone could help me with that problem.
Thanks in advance.
I would look into the tax class for WooCommerce. You can specific target a item with a different tax rate. Unless I'm missing something this should actually work how you need it to out of the box.
When checking out it will also separate the taxes.
I have set a threshold of 3 items for someone in the UK to get Free Shipping. Every time someone adds a product from a specific category, I automatically add a product to their cart which is a free gift.
My issue is that I am trying to exclude this free gift from the threshold count as currently this is being counted and people are getting free shipping without having 3 actual chargeable items in their cart.
I am unsure to how I can exclude product id 29 from counting, so any help would be greatly appreciated.
add_action( 'woocommerce_before_cart', 'ds_free_shipping_cart_notice' );
function ds_free_shipping_cart_notice() {
$threshold = 3;
$current = WC()->cart->get_cart_contents_count();
$billing_country = WC()->customer->get_billing_country();
if ( $current < $threshold && WC()->customer->get_billing_country() == 'GB' ) {
wc_print_notice( 'Nearly there! Order ' . ( $threshold - $current ) . ' more and shipping is on us', 'notice' );
}
}
I don't have woocommerce set up to be able to test this, but this is the type of logic you need:
$cart_items = WC()->cart->get_cart();
$current = WC()->cart->get_cart_contents_count();
foreach($cart_items as $item => $values) {
if ($values['product_id'] == 29) {
--$current;
}
}
This answer should help as well.
In general, though, this is a pretty fragile approach as you will need to change the hard-coded product ID every time you offer a new gift. It would be better to add a custom field to your products called "Free Gift" or something similar, set that value as needed for the items offered as free gifts, and then set up the code to exclude any of those items from the free shipping.
I have a php code that sets a 100 minimum order site wide, that works good.
I want to set a different minimum order of 250 for a specific role 'company' using the same code or by cloning it and wrapping it in an if() statement, only I have no idea how to write it. Would appreciate any kind of help.
This it the currant code (don't mind the Hebrew text, its just the error messages, when the condition is not met):
// Set a minimum dollar amount per order
add_action( 'woocommerce_check_cart_items', 'spyr_set_min_total' );
function spyr_set_min_total() {
// Only run in the Cart or Checkout pages
if( is_cart() || is_checkout() ) {
global $woocommerce;
// Set minimum cart total
$minimum_cart_total = 100;
// Total we are going to be using for the Math
// This is before taxes and shipping charges
$total = WC()->cart->subtotal;
// Compare values and add an error is Cart's total
// happens to be less than the minimum required before checking out.
// Will display a message along the lines of
// A Minimum of 10 USD is required before checking out. (Cont. below)
// Current cart total: 6 USD
if( $total <= $minimum_cart_total ) {
// Display our error message
wc_add_notice( sprintf( '<strong>לקוח יקר, יש צורך במינימום הזמנה
של %s %s₪ על מנת לבצע רכישה באתר.</strong>'
.'<br />סכום הביניים בעגלה הינו: %s %s₪',
$minimum_cart_total,
get_option( 'woocommerce_currency_symbol'),
$total,
get_option( 'woocommerce_currency_symbol') ),
'error' );
}
}
}
Thanks!
Try the following using current_user_can(), so in your code:
// Set a minimum amount per order (and user role)
add_action( 'woocommerce_check_cart_items', 'set_min_total_per_user_role' );
function set_min_total_per_user_role() {
// Only run in the Cart or Checkout pages
if( is_cart() || is_checkout() ) {
// Set minimum cart total
$minimum_cart_total = current_user_can('company') ? 250 : 100;
// Total (before taxes and shipping charges)
$total = WC()->cart->subtotal;
// Add an error notice is cart total is less than the minimum required
if( $total <= $minimum_cart_total ) {
// Display our error message
wc_add_notice( sprintf( '<strong>Dear customer, minimum order of %s is required to make a purchase on your site.</strong> <br>
Your actual cart amount is: %s',
wc_price($minimum_cart_total),
wc_price($total)
), 'error' );
}
}
}
Code goes on function.php file of your active child theme (or active theme). It should works.
To format prices for display you can use the dedicated wc_price() formatting function.
Related: Apply a discount for a specific user role in Woocommerce
First you have to retrieve the roles of the current user.
$roles = is_user_logged_in() ? (array) wp_get_current_user()->roles : [];
Now you want to check if the role company is in the user's roles to determine the minimum.
$minimum_cart_total = in_array('company', $roles) ? 250 : 100;
I have one little problem that dont know how to fix myself. I want to use this logic into my Woocommerce store just for one product.
I have use link like this to autmatically apply coupon code and add to cart:
https://testsite.com/checkout/?add-to-cart=Product_ID&quantity=1&coupon=Coupon_Code
and this seems to work, when quantity is 1. But i want discount (30%) that is automatically placed when click on direct link from before, to make dynamic, for ex:
Increase quantity to 2 in cart page, and coupon automatically to calculate 2 x 30$ = 60$ discount,
buy 3 from same product, and calculate 3 X 30$ = 90$ coupon discount
and so on..
I searched, and found this usefull thread, but there situation is little different then mine.
How can I make to have a specific coupon? Some advice or start point. Thanks
This is possible with that 2 steps:
1) Add a unique coupon with:
In General settings > Type = Fixed Product discount
In General settings > Amount = 30
In Usage restrictions > Products ==> set your desired product(s)
2) Add this code (where you will set your coupon code in the function (in lowercase)):
add_filter( 'woocommerce_coupon_get_discount_amount', 'custom_coupon_get_discount_amount', 10, 5 );
function custom_coupon_get_discount_amount( $rounded_discount, $discounting_amount, $cart_item, $single, $coupon ){
## ---- Your settings ---- ##
// Related coupons codes to be defined in this array (you can set many)
$coupon_codes = array('30perqty');
## ------ The code ------- ##
if ( $coupon->is_type('fixed_product') && in_array( $coupon->get_code(), $coupon_codes ) && $cart_item['quantity'] > 1 ) {
if( in_array( $cart_item['product_id'], $coupon->get_product_ids() ) ){
$discount = (float) $coupon->get_amount() * (int) $cart_item['quantity'];
$round = round( min( $discount, $discounting_amount ), wc_get_rounding_precision() );
}
}
return $rounded_discount;
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.