WooCommerce promotional discount: Buy 10 Get 1 Free - php

I'm trying to set up a specific discount for three variable products (464, 465 and 466). If a customer buys ten products they get one for free.
Based on WooCommerce discount: buy one get one 50% off answer code, I've come up with the following code:
add_action('woocommerce_cart_calculate_fees', 'add_custom_discount_11th_at_100', 10, 1 );
function add_custom_discount_11th_at_100( $wc_cart ){
if ( is_admin() && ! defined( 'DOING_AJAX' ) ) return;
$discount = 0;
$items_prices = array();
// Set HERE your targeted variable product ID
$targeted_product_id = 464;
foreach ( $wc_cart->get_cart() as $key => $cart_item ) {
if( $cart_item['product_id'] == $targeted_product_id ){
$qty = intval( $cart_item['quantity'] );
for( $i = 0; $i < $qty; $i++ )
$items_prices[] = floatval( $cart_item['data']->get_price());
}
}
$count_items_prices = count($items_prices);
if( $count_items_prices > 10 ) foreach( $items_prices as $key => $price )
if( $key % 11 == 1 ) $discount -= number_format($price / 1, 11 );
if( $discount != 0 ){
// Displaying a custom notice (optional)
wc_clear_notices();
wc_add_notice( __("Buy 10 Get 1 Free"), 'notice');
// The discount
$wc_cart->add_fee( 'Buy 10 Get 1 Free', $discount, true );
# Note: Last argument in add_fee() method is related to applying the tax or not to the discount (true or false)
}
}
But it only works for one product ID. How do I expand it to work for three product Ids?

To make it work for multiple products you could use in_array() php function as follow:
add_action('woocommerce_cart_calculate_fees', 'buy_ten_get_one_free', 10, 1 );
function buy_ten_get_one_free( $cart ){
if ( is_admin() && ! defined( 'DOING_AJAX' ) ) return;
// Set HERE your targeted variable products IDs
$targeted_product_ids = array( 464, 465, 466 );
$each_n_items = 10; // Number of items required to get a free one
$discount = 0; // Initializing
$items_prices = array(); // Initializing
foreach ( $cart->get_cart() as $cart_item ) {
if( in_array( $cart_item['product_id'], $targeted_product_ids ) ) {
$qty = intval( $cart_item['quantity'] );
for ( $i = 0; $i < $qty; $i++ ) {
$items_prices[] = floatval( $cart_item['data']->get_price() );
}
}
}
$count_items_prices = count($items_prices);
if ( $count_items_prices > $each_n_items ) {
foreach ( $items_prices as $key => $price ) {
if ( $key % ($each_n_items + 1) == 1 ) {
$discount += number_format($price, 2 );
}
}
}
if ( $discount > 0 ) {
// Displaying a custom notice (optional)
wc_clear_notices();
wc_add_notice( __("Buy 10 Get 1 Free"), 'notice');
// The discount
$cart->add_fee( __("Buy 10 Get 1 Free"), -$discount, true );
}
}
Code goes in function.php file of your active child theme (or active theme), tested and works.

Related

Remove all taxes in WooCommerce for a min cart total and specific countries

we need to charge 0 VAT for UK orders that are greater than 150 euro including shipping and payment gateway fees but excluding the 20% normal VAT.
So if a British residential address orders something at 130 and shipping and payment gateway fees are 9 then we charge VAT so the customer pays 139+9+20% VAT, but, if the order is 130 and shipping and payment gateway fees are 23 so a total of 153 without VAT we charge no tax.
I created this but still, it's taxing shipping fees and PayPal added fees also are getting taxed, my head is minced really thought I'd reach out for suggestions.
add_action( 'woocommerce_before_calculate_totals','auto_add_tax_for_room', 10, 1 );
function auto_add_tax_for_room( $cart ) {
if ( is_admin() && ! defined('DOING_AJAX') ) return;
$shipping_country = WC()->customer->get_shipping_country();
$subtotal = 0;
foreach ( WC()->cart->get_cart() as $cart_item ) {
$subtotal += $cart_item['data']->get_price('edit') * $cart_item['quantity'];
}
$subtotal = intval($subtotal);
if($shipping_country!='GB' || $subtotal < 125) return;
$percent = 0;
// Calculation
$surcharge = ( $cart->cart_contents_total + $cart->shipping_total ) * $percent / 100;
// Add the fee (tax third argument disabled: false)
foreach ( $cart->get_cart() as $cart_item ) {
// get product price
$price = $cart_item['data']->get_price();
$cart_item['data']->set_tax_class( 'Zero rate' ); // Above 2500
}
return;
}
The hook woocommerce_before_calculate_totals is just for cart items and the tax class here only apply to products as $cart_item['data'] is the WC_Product Object.
If your product prices are set without taxes, there is another alternative much more simpler that will remove all taxes when your conditions are met.
Try the following instead:
add_action( 'woocommerce_calculate_totals', 'set_customer_tax_exempt' );
function set_customer_tax_exempt( $cart ) {
if ( is_admin() && ! defined('DOING_AJAX') )
return;
$min_amount = 150; // Minimal cart amount
$countries = array('GB'); // Defined countries array
$subtotal = floatval( $cart->cart_contents_total );
$shipping = floatval( $cart->shipping_total );
$fees = floatval( $cart->fee_total );
$total = $subtotal + $shipping + $fees; // cart total (without taxes including shipping and fees)
$country = WC()->customer->get_shipping_country();
$country = empty($country) ? WC()->customer->get_billing_country() : $country;
$vat_exempt = WC()->customer->is_vat_exempt();
$condition = in_array( $country, $countries ) && $total >= $min_amount;
if ( $condition && ! $vat_exempt ) {
WC()->customer->set_is_vat_exempt( true ); // Set customer tax exempt
} elseif ( ! $condition && $vat_exempt ) {
WC()->customer->set_is_vat_exempt( false ); // Remove customer tax exempt
}
}
add_action( 'woocommerce_thankyou', 'remove_customer_tax_exempt' );
function remove_customer_tax_exempt( $order_id ) {
if ( WC()->customer->is_vat_exempt() ) {
WC()->customer->set_is_vat_exempt( false );
}
}
Code goes in functions.php file of the active child theme (or active theme). Tested and works.
Thanks to LoicTheAztec help I made some changes ended up using the following and it worked to fix the UK taxes issues
add_action( 'woocommerce_before_calculate_totals','auto_add_tax_for_room', 10, 1 );
function auto_add_tax_for_room( $cart ) {
if ( is_admin() && ! defined('DOING_AJAX') ) return;
$shipping_country = WC()->customer->get_shipping_country();
$subtotal = 0;
foreach ( WC()->cart->get_cart() as $cart_item ) {
$subtotal += $cart_item['data']->get_price('edit') * $cart_item['quantity'];
}
$subtotal = intval($subtotal);
if($shipping_country!='GB' || $subtotal < 125) return;
$percent = 0;
// Calculation
$surcharge = ( $cart->cart_contents_total + $cart->shipping_total ) * $percent / 100;
// Add the fee (tax third argument disabled: false)
foreach ( $cart->get_cart() as $cart_item ) {
// get product price
$price = $cart_item['data']->get_price();
$cart_item['data']->set_tax_class( 'Zero rate' );
}
return;
}
function flat_rates_cost( $rates, $package ) {
$shipping_country = WC()->customer->get_shipping_country();
$subtotal = 0;
foreach ( WC()->cart->get_cart() as $cart_item ) {
$subtotal += $cart_item['data']->get_price('edit') * $cart_item['quantity'];
}
$subtotal = intval($subtotal);
if($shipping_country!='GB' || $subtotal < 125) return $rates;
foreach ( $rates as $rate_key => $rate ){
if ( 'free_shipping' !== $rate->method_id ) {
$has_taxes = false;
$taxes = [];
// Taxes rate cost (if enabled)
foreach ($rates[$rate_key]->taxes as $key => $tax){
if( $tax > 0 ){
$has_taxes = true;
$taxes[$key] = 0; // Set to 0 (zero)
}
}
if( $has_taxes )
$rates[$rate_key]->taxes = 0;
}
}
return $rates;
}
add_filter( 'woocommerce_package_rates', 'flat_rates_cost', 10, 2 );

WooCommerce buy one get one 50% off, excluding a product variation

I am using WooCommerce discount: buy one get one 50% off with a notice answer code, if customer purchases a specific product, customer get 50% discount off on the 2nd item.
Now If the targeted product id is a variable product with for example 3 variations "A", "B" and "C", how can I exclude for example the variation "A" from discount calculation? Where should put that condition?
The following will handle variation exclusion from the original answer code:
add_action('woocommerce_cart_calculate_fees', 'add_custom_discount_2nd_at_50', 10, 1 );
function add_custom_discount_2nd_at_50( $cart ){
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
// YOUR SETTINGS:
$variable_product_id = 40; // <== HERE your targeted variable product ID
$excl_variations_ids = array( 41, 42); // <== HERE your variations to be excluded
// Initializing variables
$discount = $qty_notice = 0;
$items_prices = array();
// Loop through cart items
foreach ( $cart->get_cart() as $key => $cart_item ) {
if( $variable_product_id == $cart_item['product_id'] && in_array( $cart_item['variation_id'], $excl_variations_ids ) ) {
$quantity = (int) $cart_item['quantity'];
$qty_notice += $quantity;
for( $i = 0; $i < $quantity; $i++ ) {
$items_prices[] = floatval( $cart_item['data']->get_price());
}
}
}
$count_items = count($items_prices); // Count items
rsort($items_prices); // Sorting prices descending order
if( $count_items > 1 ) {
foreach( $items_prices as $key => $price ) {
if( $key % 2 == 1 )
$discount -= number_format( $price / 2, 2 );
}
}
// Applying the discount
if( $discount != 0 ){
$cart->add_fee('Buy one get one 50% off' ,$discount ); // true
// Displaying a custom notice (optional)
wc_clear_notices(); // clear other notices on checkout page.
if( ! is_checkout() ){
wc_add_notice( __("You get 50% of discount on the 2nd item"), 'notice');
}
}
// Display a custom notice on cart page when quantity is equal to 1.
elseif( $qty_notice == 1 ){
wc_clear_notices(); // clear other notices on checkout page.
if( ! is_checkout() ){
wc_add_notice( __( "Add one more to get 50% off on 2nd item" ), 'notice');
}
}
}
Code goes in functions.php file of your active child theme (or active theme). Tested and works.

Add fee based on product category and shipping method WooCommerce

I'm trying to add a fee of £3 per product if any of the products in the basket have the category ID "160".
This is working great but I also only want this fee to apply if certain shipping methods are chosen.
What I have so far:
function df_add_ticket_surcharge( $cart_object ) {
global $woocommerce;
$specialfeecat = 160; // category id for the special fee
$spfee = 3.00; // initialize special fee
$spfeeperprod = 3.00; //special fee per product
foreach ( $cart_object->cart_contents as $key => $value ) {
$proid = $value['product_id']; //get the product id from cart
$quantiy = $value['quantity']; //get quantity from cart
$itmprice = $value['data']->price; //get product price
$terms = get_the_terms( $proid, 'product_cat' ); //get taxonamy of the prducts
if ( $terms && ! is_wp_error( $terms ) ) :
foreach ( $terms as $term ) {
$catid = $term->term_id;
if($specialfeecat == $catid ) {
$spfee = $spfee * $quantiy;
}
}
endif;
}
if($spfee > 0 ) {
$woocommerce->cart->add_fee( 'Ticket Concierge Charge', $spfee, true, 'standard' );
}
}
add_action( 'woocommerce_cart_calculate_fees', 'df_add_ticket_surcharge' );
I have tried integrating the function from this post: https://stackoverflow.com/a/47732732/12019310 but can't seem to get it functioning without a critical notice.
Any advice would be greatly appreciated!
The following code
add A fee of 3 per product in the basket if it belongs to the category ID "160"
If "local_pickup" is chosen
function df_add_ticket_surcharge( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
$chosen_shipping_method_id = WC()->session->get( 'chosen_shipping_methods' )[0];
$chosen_shipping_method = explode(':', $chosen_shipping_method_id)[0];
/* SETTINGS */
$special_fee_cat = 160; // category id for the special fee
$fee_per_prod = 3; //special fee per product
/* END SETTINGS */
// Total
$total = 0;
// Only for Local pickup chosen shipping method
if ( strpos( $chosen_shipping_method_id, 'local_pickup' ) !== false ) {
// Loop though each cart items and set prices in an array
foreach ( $cart->get_cart() as $cart_item ) {
// Get product id
$product_id = $cart_item['product_id'];
// Quantity
$product_quantity = $cart_item['quantity'];
// Check for category
if ( has_term( $special_fee_cat, 'product_cat', $product_id ) ) {
$total += $fee_per_prod * $product_quantity;
}
}
// Add the discount
$cart->add_fee( __('Ticket Concierge Charge', 'woocommerce'), $total );
}
}
add_action( 'woocommerce_cart_calculate_fees', 'df_add_ticket_surcharge', 10, 1 );
This post served me fully, but the script did not work for me if I have other cities with fixed shipping costs.
so this worked for me!
add_action( 'woocommerce_cart_calculate_fees','custom_pcat_fee');
function custom_pcat_fee( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
$chosen_shipping_method_id = WC()->session->get( 'chosen_shipping_methods' )[0];
$chosen_shipping_method = explode(':', $chosen_shipping_method_id)[0];
// Set HERE your categories (can be term IDs, slugs or names) in a coma separated array
$categories = array('181');
$fee_amount = 0;
// Loop through cart items
foreach( $cart->get_cart() as $cart_item ){
if( has_term( $categories, 'product_cat', $cart_item['product_id']) )
$fee_amount = 35000;
}
// Only for Local pickup chosen shipping method
if ( strpos( $chosen_shipping_method_id, 'flat_rate' ) !== false || strpos( $chosen_shipping_method_id, 'filters_by_cities_shipping_method' ) !== false ) {
// Loop though each cart items and set prices in an array
foreach ( $cart->get_cart() as $cart_item ) {
// Get product id
$product_id = $cart_item['product_id'];
// Quantity
$product_quantity = $cart_item['quantity'];
// Check for category
if ( has_term( $special_fee_cat, 'product_cat', $product_id ) ) {
$total += $fee_per_prod * $product_quantity;
}
}
// Adding the fee
if ( $fee_amount > 0 ){
// Last argument is related to enable tax (true or false)
WC()->cart->add_fee( __( "Costo de trasteo de mobiliario", "woocommerce" ), $fee_amount, false );
}
}
}

Set product variation custom calculated price dynamically in WooCommerce

I am trying to dynamically add to a products price when a user checks out based on what they have entered in the product page. The setting of the price is only working on a non variation product.
I need to be able to set the price on the variations of the products as well.
The code that I am using:
function add_cart_item_data( $cart_item_meta, $product_id, $variation_id ) {
$product = wc_get_product( $product_id );
$price = $product->get_price();
$letterCount = strlen($_POST['custom_name']);
$numberCount = strlen($_POST['custom_number']);
if($letterCount != '0') {
$letterPricing = 20 * $letterCount;
$numberPricing = 10 * $numberCount;
$additionalPrice = $letterPricing + $numberPricing;
$cart_item_meta['custom_price'] = $price + $additionalPrice;
}
return $cart_item_meta;
}
function calculate_cart_total( $cart_object ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) ) {
return;
}
foreach ( $cart_object->cart_contents as $key => $value ) {
if( isset( $value['custom_price'] ) ) {
$price = $value['custom_price'];
$value['data']->set_price( ( $price ) );
}
}
}
I have completely revisited your code:
// Set custom data as custom cart data in the cart item
add_filter( 'woocommerce_add_cart_item_data', 'add_custom_data_to_cart_object', 30, 3 );
function add_custom_data_to_cart_object( $cart_item_data, $product_id, $variation_id ) {
if( ! isset($_POST['custom_name']) || ! isset($_POST['custom_number']) )
return $cart_item_data; // Exit
if( $variation_id > 0)
$product = wc_get_product( $variation_id );
else
$product = wc_get_product( $product_id );
$price = $product->get_price();
// Get the data from the POST request and calculate new custom price
$custom_name = sanitize_text_field( $_POST['custom_name'] );
if( strlen( $custom_name ) > 0 )
$price += 20 * strlen( $custom_name );
$custom_number = sanitize_text_field( $_POST['custom_number'] );
if( strlen( $custom_number ) > 0 )
$price += 10 * strlen( $custom_number );
// Set new calculated price as custom cart item data
$cart_item_data['custom_data']['price'] = $price;
return $cart_item_data;
}
// Set the new calculated price of the cart item
add_action( 'woocommerce_before_calculate_totals', 'set_new_cart_item_price', 50, 1 );
function set_new_cart_item_price( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
foreach ( $cart->get_cart() as $cart_item ) {
if( isset( $cart_item['custom_data']['price'] ) ) {
// Get the new calculated price
$new_price = (float) $cart_item['custom_data']['price'];
// Set the new calculated price
$cart_item['data']->set_price( $new_price );
}
}
}
This code goes on function.php file of your active child theme (or theme).
Tested and works as well with product variations.

WooCommerce discount: buy one get one 50% off with a notice

After my previous question WooCommerce discount: buy one get one 50% off I want to add custom notice to the cart, whenever a particular product(not all the products) gets added to the cart.
I want to check the quantity first, if it's 1 then I want to display a notice to increase the quantity of that product. I have figured something by myself from the internet and I don't think my solution is right:
add_action( 'wp', 'sp_custom_notice' );
function sp_custom_notice() {
if ( is_admin() && ! defined( 'DOING_AJAX' ) ) return;
//to display notice only on cart page
if ( ! is_cart() ) {
return;
}
global $product;
$product_id = method_exists( $product, 'get_id' ) ? $product->get_id() : $product->id;
if($product_id == 15730){
//check for quantify if equal to 1 in cart
wc_clear_notices();
wc_add_notice( __("Add one more to get 50% off on 2nd product"), 'notice');
}
}
It will be great if anyone can help me on this.
Updated July 2020
If you are still using the code from my answer to your previous question, you can change it a bit to get this working too:
add_action('woocommerce_cart_calculate_fees', 'add_custom_discount_2nd_at_50', 10, 1 );
function add_custom_discount_2nd_at_50( $cart ){
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
// YOUR SETTINGS:
$targeted_product_id = 40; // Set HERE your targeted product ID
// Initializing variables
$discount = $qty_notice = 0;
$items_prices = array();
// Loop through cart items
foreach ( $cart->get_cart() as $key => $cart_item ) {
if( in_array( $targeted_product_id, [$cart_item['product_id'], $cart_item['variation_id']] ) ){
$quantity = (int) $cart_item['quantity'];
$qty_notice += $quantity;
for( $i = 0; $i < $quantity; $i++ ) {
$items_prices[] = floatval( $cart_item['data']->get_price());
}
}
}
$count_items = count($items_prices); // Count items
rsort($items_prices); // Sorting prices descending order
if( $count_items > 1 ) {
foreach( $items_prices as $key => $price ) {
if( $key % 2 == 1 )
$discount -= number_format( $price / 2, 2 );
}
}
// Applying the discount
if( $discount != 0 ){
$cart->add_fee('Buy one get one 50% off', $discount );
// Displaying a custom notice (optional)
wc_clear_notices(); // clear other notices on checkout page.
if( ! is_checkout() ){
wc_add_notice( __("You get 50% of discount on the 2nd item"), 'notice');
}
}
// Display a custom notice on cart page when quantity is equal to 1.
elseif( $qty_notice == 1 ){
wc_clear_notices(); // clear other notices on checkout page.
if( ! is_checkout() ){
wc_add_notice( __( "Add one more to get 50% off on 2nd item" ), 'notice');
}
}
}
Code goes in functions.php file of your active child theme (or active theme) Tested and works.
Note: The wc_add_notice() function absolutely don't need to be echoed
You did pretty good code some minor changes are required try below code :
add_action('woocommerce_before_cart', 'sp_custom_notice');
function sp_custom_notice() {
if ( is_admin() && ! defined( 'DOING_AJAX' ) ) return;
//to display notice only on cart page
if ( ! is_cart() ) {
return;
}
global $woocommerce;
$items = $woocommerce->cart->get_cart();
foreach($items as $item => $values) {
$_product = wc_get_product( $values['data']->get_id());
if($values['data']->get_id() == 190 && $values['quantity'] == 1 ){
wc_clear_notices();
echo wc_add_notice( __("Add one more to get 50% off on 2nd product"), 'notice');
}
}
}

Categories