Discount based on WooCommerce product custom input fields - php

I'm trying to give the user a discount based on a quantity field in the single product page.
Basically, the website sells tickets and I have a different price for adults and for children. So I created input fields in the single product page so the user would type how many adults and how many children he is buying for.
In the product admin I have a ACF (advanced custom field) for "children discount", so in the cart I want to give this discount based on the amount of children. For example, let's say that for this product the user is buying 5 tickets, 3 for adults and 2 for children, I want to calculate a discount for these 2 children.
What I've tried so far:
add_action( 'woocommerce_before_add_to_cart_button', 'custom_product_price_field', 5 );
function custom_product_price_field(){
echo '<div class="custom-text text">
<p>Quantity of adults:</p>
<input type="text" name="qtty_adults" value="" title="Quantity Adults" class="qtty-field">
</div>
<div class="custom-text text">
<p>Quantity of children:</p>
<input type="text" name="qtty_kids" value="" title="Quantity Kids" class="qtty-field">
</div>';
}
add_action('woocommerce_cart_calculate_fees' , 'add_user_discounts');
function add_user_discounts( WC_Cart $cart ){
global $product;
$qtty_kids = (float) sanitize_text_field( $_POST['qtty_kids'] );
$discount_per_kid = (float) get_field('children_discount', $product->id);
$discount = $qtty_kids * $discount_per_kid;
$cart->add_fee( 'Discount for children', -$discount);
}
Doing this the discount is always $0
Can anyone give me some help on how to make this happen?

Your code is a bit outdated, with some mistakes and there is a lot of missing things to get what you expect…
Try the following instead (updated):
add_action( 'woocommerce_before_add_to_cart_button', 'custom_product_price_field', 5 );
function custom_product_price_field(){
echo '<div class="custom-text text">
<p>'.__("Quantity of adults:").'</p>
<input type="text" name="qtty_adults" value="" title="'.__("Quantity Adults").'" class="qtty-field">
</div>
<div class="custom-text text">
<p>'.__("Quantity of children:").'</p>
<input type="text" name="qtty_kids" value="" title="'.__("Quantity Kids").'" class="qtty-field">
</div>';
}
// Add selected add-on option as custom cart item data
add_filter( 'woocommerce_add_cart_item_data', 'filter_add_cart_item_data_callback', 10, 3 );
function filter_add_cart_item_data_callback( $cart_item_data, $product_id, $variation_id ) {
if ( isset( $_POST['qtty_kids'] ) && $children_discount = get_field( 'children_discount', $product_id ) ) {
$cart_item_data['children_discount'] = (float) $children_discount - (float) sanitize_text_field( $_POST['qtty_kids'] );
$cart_item_data['unique_key'] = md5( microtime().rand() ); // Make each item unique
}
return $cart_item_data;
}
// Set a discount based a product custom field(s)
add_action('woocommerce_cart_calculate_fees' , 'add_children_discount', 10, 1 );
function add_children_discount( $cart ){
if ( is_admin() && ! defined('DOING_AJAX') )
return;
if ( did_action('woocommerce_cart_calculate_fees') >= 2 )
return;
$discount = 0; // Initialising
// Loop through cart items
foreach ( $cart->get_cart() as $cart_item ) {
if( isset( $cart_item['children_discount'] ) ) {
$discount += $cart_item['children_discount'];
}
}
if ( $discount > 0 )
$cart->add_fee( __("Discount for children", "woocommerce"), -$discount );
}
Tested and works.
Related:
Cart item discount based on quantity in Woocommerce 3

Related

Woocommerce How to Display Sale Price of Products in Cart

I have set up my cart to display the product price with a strikethrough next to the sale price of the discounted item (see first product in cart in photo).
checkout screenshot
This was achieved using this code
function my_custom_show_sale_price_at_cart( $old_display, $cart_item, $cart_item_key ) {
$product = $cart_item['data'];
if ( $product ) {
return $product->get_price_html();
}
return $old_display;
}
add_filter( 'woocommerce_cart_item_price', 'my_custom_show_sale_price_at_cart', 10, 3 );
The issue as you can see in the photo is that this only works for products on sale (the $12.00 product on sale for 0.00). However, a coupon code is applied to the other two items.
I followed this thread to display the total savings as "You Saved" in the checkout summary, including sale and coupon discounts.
How can I display the discounted price of the items in the cart that have the coupon applied to them?
So this will update the cart item totals and the line item totals based on what I interpreted your comments to be.
This would be added to functions.php
function my_custom_show_sale_price_at_cart( $old_display, $cart_item, $cart_item_key ) {
$product = $cart_item['data'];
// If you just want this to show in checkout vs cart + checkout - add && is_checkout() below.
if ( $product ) {
// This item has a coupon applied.
if ( floatval( $product->get_price() ) !== number_format( $cart_item['line_total'], 2 ) / $cart_item['quantity'] ) {
// This updates the item total.
$price_html = wc_format_sale_price( $product->get_regular_price(), number_format( $cart_item['line_total'], 2 ) ) . $product->get_price_suffix();
} else {
$price_html = $product->get_price_html();
}
// This updates the line item sub-total.
add_filter(
'woocommerce_cart_item_subtotal',
function() use ( $cart_item ) {
return wc_price( $cart_item['line_total'] + $cart_item['line_tax'] );
}
);
return $price_html;
}
return $old_display;
}
add_filter( 'woocommerce_cart_item_price', 'my_custom_show_sale_price_at_cart', 10, 3 );

Woocommerce Discount only first of product type

I am trying to find a way to make a discount only happen for the first product with the attribute gallon in Woocommerce. So for example I add a paint product with the attribute gallon, that first product will be 10% off with coupon code test123. But if someone adds another paint with the attribute gallon, it should still only affect the first gallon added with a 10% off discount. Any other gallons of paint shouldn't receive the 10% off discount. Only the first one added.
Here is my code so far but I can't figure out how to get only one of the products with the attribute gallon with the discount.
add_filter( 'woocommerce_cart_item_subtotal', 'coupon_gallon', 99, 3 );
function coupon_gallon( $subtotal, $cart_item ){
global $woocommerce;
global $product;
$coupon_code = 'test123';
$item_data = $cart_item['data'];
$gallon = $item_data->get_attribute('pa_size');
if ( $woocommerce->cart->has_discount( $coupon_code )) {
if( $gallon == "Gallon") {
//testing if triggered
echo '<div class="">YES!</div>';
$newsubtotal = wc_price( $cart_item['data']->get_price() * 0.90 * 1 );
$subtotal = sprintf( '<s>%s</s> %s', $subtotal, $newsubtotal );
} else {
echo '<div class="">NO!</div>';
}
}
return $subtotal;
}

Allow cart item quantity update for only one product category in Woocommerce

I am trying to disable quantity changes in the Woocommerce cart to every product except a single category.
I have tried a custom function to do this but it disables it for all items. Looking to only allow one category product quantity to be changed. I have used this code to disable it on all products so far:
add_filter( 'woocommerce_cart_item_quantity', 'wc_cart_item_quantity', 10, 3 );
function wc_cart_item_quantity( $product_quantity, $cart_item_key, $cart_item
){
if( is_cart() ){
$product_quantity = sprintf( '%2$s <input type="hidden"
name="cart[%1$s][qty]" value="%2$s" />', $cart_item_key,
$cart_item['quantity'] );
}
return $product_quantity;
}
You can use has_tem() conditional function to target only specific product category(ies) set for your cart items like:
add_filter( 'woocommerce_cart_item_quantity', 'wc_cart_item_quantity', 10, 3 );
function wc_cart_item_quantity( $product_quantity, $cart_item_key, $cart_item ){
$categories = array('disc'); // Define your allowed product category(ies)
if( is_cart() && ! has_term( $categories, $taxonomy, $cart_item['product_id'] ){
$product_quantity = sprintf( '%2$s <input type="hidden"
name="cart[%1$s][qty]" value="%2$s" />', $cart_item_key,
$cart_item['quantity'] );
}
return $product_quantity;
}
Code goes in functions.php file of your active child theme (or active theme). It should work.

Set cart item price from a hidden input field custom price in Woocommerce 3

In Woocommerce, I used jQuery to calculate a custom price on a single product pages, and now need to pass this value to the cart.
The desired behavior is to pass the new price retrieved from the hidden field to the cart item price.
Here is my actual code:
// Hidden input field in single product page
add_action( 'woocommerce_before_add_to_cart_button', 'custom_hidden_product_field', 11, 0 );
function custom_hidden_product_field() {
echo '<input type="hidden" id="hidden_field" name="custom_price" class="custom_price" value="">';
}
// The code to pass this data to the cart:
add_action( 'woocommerce_add_cart_item_data', 'save_custom_fields_data_to_cart', 10, 2 );
function save_custom_fields_data_to_cart( $cart_item_data, $product_id ) {
if( ! empty( $_REQUEST['custom_price'] ) ) {
// Set the custom data in the cart item
$cart_item_data['custom_data']['custom_price'] = $_REQUEST['custom_price'];
$data = array( 'custom_price' => $_REQUEST['custom_price'] );
// below statement make sure every add to cart action as unique line item
$cart_item_data['custom_data']['unique_key'] = md5( microtime().rand() );
WC()->session->set( 'custom_data', $data );
}
return $cart_item_data;
}
And check both $data and $cart_item_data to see that they both return the custom_price data that is calculated on the page.
However, I go to view cart, and the value of the line item is still 0.
I set a var equal to the WC()->session->set( 'custom_data', $data ); and then var_dump to check it, but this returns NULL which might just be what it returns, I'm not entirely sure because I've never used it.
I should also add that I have the regular_price in the product backend set to 0. When I erase this (and leave it blank) I get back the error:
Warning: A non-numeric value encountered in
C:\xampp\htdocs\my-transfer-source\wp-content\plugins\woocommerce\includes\class-wc-discounts.php on line 85
I'm wondering if I've missed something here, and if someone could lend some light onto this? Thanks
Update 2021 - Handling custom price item in mini cart
First for testing purpose we add a price in the hidden input field as you don't give the code that calculate the price:
// Add a hidden input field (With a value of 20 for testing purpose)
add_action( 'woocommerce_before_add_to_cart_button', 'custom_hidden_product_field', 11 );
function custom_hidden_product_field() {
echo '<input type="hidden" id="hidden_field" name="custom_price" class="custom_price" value="20">'; // Price is 20 for testing
}
Then you will use the following to change the cart item price (WC_Session is not needed):
// Save custom calculated price as custom cart item data
add_filter( 'woocommerce_add_cart_item_data', 'save_custom_fields_data_to_cart', 10, 2 );
function save_custom_fields_data_to_cart( $cart_item_data, $product_id ) {
if( isset( $_POST['custom_price'] ) && ! empty( $_POST['custom_price'] ) ) {
// Set the custom data in the cart item
$cart_item_data['custom_price'] = (float) sanitize_text_field( $_POST['custom_price'] );
// Make each item as a unique separated cart item
$cart_item_data['unique_key'] = md5( microtime().rand() );
}
return $cart_item_data;
}
// For mini cart
add_action( 'woocommerce_cart_item_price', 'filter_cart_item_price', 10, 2 );
function filter_cart_item_price( $price, $cart_item ) {
if ( isset($cart_item['custom_price']) ) {
$args = array( 'price' => floatval( $cart_item['custom_price'] ) );
if ( WC()->cart->display_prices_including_tax() ) {
$product_price = wc_get_price_including_tax( $cart_item['data'], $args );
} else {
$product_price = wc_get_price_excluding_tax( $cart_item['data'], $args );
}
return wc_price( $product_price );
}
return $price;
}
// Updating cart item price
add_action( 'woocommerce_before_calculate_totals', 'change_cart_item_price', 30, 1 );
function change_cart_item_price( $cart ) {
if ( ( is_admin() && ! defined( 'DOING_AJAX' ) ) )
return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
// Loop through cart items
foreach ( $cart->get_cart() as $cart_item ) {
// Set the new price
if( isset($cart_item['custom_price']) ){
$cart_item['data']->set_price($cart_item['custom_price']);
}
}
}
Code goes in functions.php file of your active child theme (or active theme). Tested and works.

Custom cart item price based on user input in Woocommerce

in our Woocommerce store we have some minimum price for any products . And in every product inner page there is two filed where customer can type width , height of the product . And then they can add this product to cart, then price is changed based on given width and height.
For example if the minimum price for a product is 50 . And customer add width =2, height=3 , then the price for this product is going to 50*2*3=300
So to arrange this we add this code to function.php
add_filter( 'woocommerce_add_cart_item', 'add_custom_cart_item_data', 10, 2 );
function add_custom_cart_item_data( $cart_item_data, $cart_item_key ) {
$result=$_POST['width']*$_POST['height']*$cart_item_data['data']->price;
$cart_item_data['new-price'] =$result;
$cart_item_data['data']->set_price($result);
return $cart_item_data;
}
So this is working correctly .
But the problem is once it is coming to checkout page then the product price is showing as 50 , but it is 300 actually.
so to overcome this i use the following code
add_filter( 'woocommerce_cart_item_subtotal', 'update_with_custom_details', 10, 3 );
function update_with_custom_details( $subtotal, $cart_item, $cart_item_key ) {
$subtotal =" £".$cart_item['line_total'];
return $subtotal;
}
now it is showing £300, but i know this code is wrong.
The code is wrong because when customer apply a 50% coupon code for this product then the discount is coming as 25 , Because it calculating based on 50*.5=25; But actually product new price is 300, so the discount should be 300*5=150;
So how i can update the product price in cart page, checkout page ,mini cart etc ?
Please help .
Please see this also
Woocommerce product custom price is not accepted by wocommerce coupon
Update 2: The correct way to do it need to be done in two steps:
We calculate the price on add to cart event hook and save it as custom cart item data.
We set that calculated price in woocommerce_before_calculate_totals hook
The code:
// Save custom field value in cart item as custom data
add_filter( 'woocommerce_add_cart_item_data', 'calculate_custom_cart_item_prices', 30, 3 );
function calculate_custom_cart_item_prices( $cart_item_data, $product_id, $variation_id ) {
if ( isset($_POST['width']) && isset($_POST['height']) ) {
// Get the correct Id to be used (compatible with product variations)
$the_id = $variation_id > 0 ? $variation_id : $product_id;
$product = wc_get_product( $the_id ); // Get the WC_Product object
$product_price = (float) $product->get_price(); // Get the product price
// Get the posted data
$width = (float) sanitize_text_field( $_POST['width'] );
$height = (float) sanitize_text_field( $_POST['height'] );
$new_price = $width * $height * $product_price; // Calculated price
$cart_item_data['calculated-price'] = $new_price; // Save this price as custom data
}
return $cart_item_data;
}
// Set custom calculated price in cart item price
add_action( 'woocommerce_before_calculate_totals', 'set_calculated_cart_item_price', 20, 1 );
function set_calculated_cart_item_price( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
// Loop through cart items
foreach ( $cart->get_cart() as $cart_item ){
if( ! empty( $cart_item['calculated-price'] ) ){
// Set the calculated item price (if there is one)
$cart_item['data']->set_price( $cart_item['calculated-price'] );
}
}
}
Code goes in function.php file of your active child theme (or theme). Tested and works.
For testing purpose, I have used the following code that output 2 custom fields on single product pages:
// The hidden product custom field
add_action( 'woocommerce_before_add_to_cart_button', 'add_gift_wrap_field' );
function add_gift_wrap_field() {
global $product;
// The fake calculated price
?>
<div>
<label class="product-custom-text-label" for="servicetime"><?php _e( 'Custom text:', 'woocommerce'); ?><br>
<input type="text" id="user-width" name="width" value=""><br>
<input type="text" id="user-height" name="height" value="">
</label>
</div><br>
<?php
}

Categories