I'm creating a WooCommerce add-on to customize the product, based on selected options by the visitor and custom price is calculated and stored into session table also.
I am able to get that value in cart page also.
My problem: I would like to change the default price of the product and replace it with new calculated value in the WooCommerce process as cart, checkout, payment, mail notifications, order...
Any advice please?
Thanks
Ce right hook to get it working is woocommerce_before_calculate_totals. But you will have to complete (replace) the code to get the new price in the hooked function below:
add_action( 'woocommerce_before_calculate_totals', 'custom_cart_items_prices', 10, 1 );
function custom_cart_items_prices( $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 ) {
// Get the product id (or the variation id)
$product_id = $cart_item['data']->get_id();
// GET THE NEW PRICE (code to be replace by yours)
$new_price = 500; // <== Add your code HERE
// Updated cart item price
$cart_item['data']->set_price( $new_price );
}
}
Code goes in function.php file of your active child theme (or theme) or also in any plugin file.
This code is tested and works on WooCommerce versions 3+. But as you don't give any code I can't test it for real getting the new price from session…
function save_subscription_wrap_data( $cart_item_data, $product_id ) {
$include_as_a_addon_subscription = get_field('include_as_a_addon_subscription',$product_id);
$subscricption_product_data = get_field('subscricption_product',$product_id);
$current_user = is_user_logged_in() ? wp_get_current_user() : null;
$subscriptions = wcs_get_users_subscriptions( $current_user->ID );
if($include_as_a_addon_subscription == "yes")
{
foreach ( $subscriptions as $subscription_id => $subscription ) {
$subscription_status = $subscription->get_status();
}
if($subscription_status == 'active')
{
$cart_item_data[ "subscribe_product" ] = "YES";
}
}
return $cart_item_data;
}
add_filter( 'woocommerce_add_cart_item_data', 'save_subscription_wrap_data', 99, 2 );
function render_meta_on_cart_and_checkout1( $cart_data, $cart_item = null ) {
$meta_items = array();
if( !empty( $cart_data ) ) {
$meta_items = $cart_data;
}
if( isset( $cart_item["subscribe_product"] ) ) {
$meta_items[] = array( "name" => "Product Type", "value" => "Package Addon" );
}
return $meta_items;
}
add_filter( 'woocommerce_get_item_data', 'render_meta_on_cart_and_checkout1', 100, 2 );
function calculate_gift_wrap_fee( $cart_object ) {
if( !WC()->session->__isset( "reload_checkout" )) {
$additionalPrice = 100;
foreach ( WC()->cart->get_cart() as $key => $value ) {
if( isset( $value["subscribe_product"] ) ) {
if( method_exists( $value['data'], "set_price" ) ) {
$orgPrice = floatval( $value['data']->get_price() );
//$value['data']->set_price( $orgPrice + $additionalPrice );
$value['data']->set_price(0);
} else {
$orgPrice = floatval( $value['data']->price );
//$value['data']->price = ( $orgPrice + $additionalPrice );
$value['data']->price = (0);
}
}
}
}
}
add_action( 'woocommerce_before_calculate_totals', 'calculate_gift_wrap_fee', 99 );
Related
I'm trying to overwrite the price for a product in the WooCommerce cart.
The problem is that I'm using Divi which includes an ajax cart update. So when I reload the checkout page I can see my overwritten changes for 1-2 seconds (during the ajax loading) and after this the cart details get overwritten again by the default values. I've tried everythink, different hooks, delays, priority changes but nothing works.
This is my initial code:
add_filter( 'woocommerce_calculate_totals', 'custom_cart_items_prices', 1000, 1 )
function custom_cart_items_prices( $cart_object ) {
$title = $_GET['form-title'];
$money = $_GET['money'];
if ($title && $money) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
// Iterating through cart items
foreach ( $cart_object->get_cart() as $cart_item ) {
// Get an instance of the WC_Product object
$wc_product = $cart_item['data'];
// Get the product name (WooCommerce versions 2.5.x to 3+)
$original_name = method_exists( $wc_product, 'get_name' ) ? $wc_product->get_name() : $wc_product->post->post_title;
// SET THE NEW NAME
$new_name = $title;
// Set the new name (WooCommerce versions 2.5.x to 3+)
if( method_exists( $wc_product, 'set_name' ) )
$wc_product->set_name( $new_name );
else
$wc_product->post->post_title = $new_name;
}
// Updated cart item price
$cart_item['data']->set_price( $money );
}}
Picture during the loading (Everything looks great):
When the loading is completed:
Update - I've also tried a different approach as I am using a custom form made with CRED Toolset and Toolset Woocommerce plugins where customer create a product with a custom title and price, adding to cart a default existing product (and redirected to checkout page). I am trying to replace the cart item title and price by the submitted form data.
Here is my code:
// Change the product ID
add_filter('cred_commerce_add_product_to_cart', function( $product_id, $form_id, $post_id ) {
error_log($form_id);
if( $form_id == 55 ) {
if (!session_id()) {
session_start();
}
$_SESSION['target_post_id'] = $post_id;
if(isset($_POST['product-id'])){
$product_id = $_POST['product-id'];
$_SESSION['target_post_id'] = $post_id;
}
}
return $product_id;
}, 20, 3);
// change the price in cart
add_action('woocommerce_before_calculate_totals', function(){
session_start();
if($form_id != 55 && !isset($_SESSION['target_post_id']))return;
global $woocommerce;
$post_id = $_SESSION['target_post_id'];
$price = 0;
foreach ( $woocommerce->cart->get_cart() as $key => $cart_item ) {
$product_id = $cart_item['data']->get_id();
$adult_price = get_post_meta($product_id, 'wpcf-prezzo-1', true);
$adult_num = get_post_meta($post_id, 'wpcf-adult-number', true);
$child_price = get_post_meta($product_id, 'wpcf-prezzo-2', true);
$child_num = get_post_meta($post_id, 'wpcf-children-number', true);
$price = $adult_price * $adult_num + $child_price * $child_num;
$cart_item['data']->set_price($price);
}
}, 999);
But it doesn't work either.
How can I get the submitted form data (custom title and price) and to overwrite the cart item title and price in Woocommerce?
Try the following instead as you are using the toolset plugin with a custom form, where you will use WC_Sessions to store the product ID and change the price:
add_filter( 'cred_commerce_add_product_to_cart', 'cred_commerce_add_product_to_cart', 20, 3 );
function cred_commerce_add_product_to_cart( $product_id, $form_id, $post_id ) {
if( $form_id == 55 && $post_id > 0 ) {
WC()->session->set( 'cp_id', $post_id );
}
return $product_id;
}
add_filter( 'woocommerce_add_cart_item_data', 'filter_add_cart_item_data', 30, 4 );
function filter_add_cart_item_data( $cart_item_data, $product_id, $variation_id ){
if ( WC()->session->get('cp_id') ) {
$cp_id = WC()->session->get('cp_id');
$product = wc_get_product($cp_id);
$cart_item_data['cp_price'] = $product->get_regular_price();
$cart_item_data['cp_title'] = $product->get_title();
}
return $cart_item_data;
}
add_action( 'woocommerce_before_calculate_totals', 'custom_cart_items_price', 30, 1 );
function custom_cart_items_price( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
// Iterating through cart items
foreach ( $cart->get_cart() as $cart_item ) {
if( isset($cat_item['cp_price']) && isset($cat_item['cp_title']) ){
$cart_item['data']->set_price( $cat_item['cp_price'] );
$cart_item['data']->set_name( $cat_item['cp_title'] );
}
}
}
Code goes in function.php file of your active child theme (or active theme). It should works.
Some related answers:
Set cart item price from a hidden input field custom price in Woocommerce 3
Custom cart item price based on user input in Woocommerce
Conditionally alter specific product price in Woocommerce
Change cart item prices in Woocommerce 3
Deactivate all of your plugins and voila!
You will suprisingly understand that its a cause of one.
Enabling them one by one back, will let you know which one is conflicting.
And.. Please consider using WordPress stack exchange for WordPress-specific questions.
1 woocommerce_add_cart_item_data
add_filter( 'woocommerce_add_cart_item_data', function($cart_item_data, $product_id, $variation_id ){
if ( empty( $_GET['form-title'] ) || empty( $_GET['money'] ) ) {
return;
}
$title = $_GET['form-title'];
$money = $_GET['money'];
$cart_item_data['form-title'] = $title ;
$cart_item_data['money'] = $money ;
return $cart_item_data;
} ,100,4);
2 woocommerce_before_calculate_totals
add_action( 'woocommerce_before_calculate_totals', 'update_custom_price', 10, 1 );
function update_custom_price( $cart_object ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) ) {
return;
}
foreach ( $cart_object->cart_contents as $cart_item_key => $cart_item ) {
if(isset($cart_item["money"]) ){
$money = $cart_item["money"] ;
$cart_item['data']->set_price( $money);
}
if(isset($cart_item["form-title"]) ){
$title = $cart_item["form-title"] ;
}
$wc_product = $cart_item['data'];
// Get the product name (WooCommerce versions 2.5.x to 3+)
$original_name = method_exists( $wc_product, 'get_name' ) ? $wc_product->get_name() : $wc_product->post->post_title;
// SET THE NEW NAME
$new_name = $title;
// Set the new name (WooCommerce versions 2.5.x to 3+)
if( method_exists( $wc_product, 'set_name' ) )
$wc_product->set_name( $new_name );
else
$wc_product->post->post_title = $new_name;
}
}
return $cart_object ;
}
Hope this will help.
We have some products in shop , and we are giving some coupons to customer .
product -> ABC price 10
coupon code is 'newcp' discount 20%;
so when people add the product to cart price will be 10 .
Then they apply coupon then original product price shown as 10 and calculate 20% from that and at the end the total will be 8
But now we need to change this as per specific condition
When people apply product coupon newbc
1)if coupon is newcp , then change order_item_price as order_item_price +3[ only if category is Fruit] , and this price should be shown in cart page, checkout page , order email
2)Calculate discount from the new price calculate discouunt from 13
3)If people remove coupon then price will again return to 10
I made 2 solutions , but not working.
Solution 1
add_action('woocommerce_before_calculate_totals', 'add_custom_price', 10, 1);
function add_custom_price($cart_obj)
{
if (is_admin() && !defined('DOING_AJAX')) return;
foreach($cart_obj->get_cart() as $key => $value)
{
$product_id = $value['product_id'];
$coupon_code = $value['coupon_code'];
if ($coupon_code != '' && $coupon_code == "newcp")
{
global $woocommerce;
if (WC()->cart->has_discount($coupon_code)) return;
else
{
if (has_term('fruits', 'product_cat', $product_id))
{
$value['data']->set_price(CURRENT_CART_PRICE + 3);
}
}
}
}
}
Solution 2
add_action( 'woocommerce_before_calculate_totals', 'add_custom_price', 10, 1);
function add_custom_price( $cart_object) {
global $woocommerce;
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
$coupon = False;
if ($coupons = WC()->cart->get_applied_coupons() == False )
$coupon = False;
else {
foreach ( WC()->cart->get_applied_coupons() as $code ) {
$coupon = $code;
}
}
if ($coupon == "newcp"){
foreach ( $cart_object->get_cart() as $cart_item )
{
$price = $cart_item['data']->price+3;
$cart_item['data']->set_price( $price );
}
}
}
Please help .
Here is a possible way to achieve that:
// Add custom calculated price conditionally as custom data to cart items
add_filter( 'woocommerce_add_cart_item_data', 'custom_add_cart_item_data', 20, 2 );
function custom_add_cart_item_data( $cart_item_data, $product_id ){
// Your settings below
$product_categories = array('fruits');
$addition = 3;
$product = wc_get_product($product_id);
$the_id = $product->is_type('variation') ? $product->get_parent_id() : $product_id;
if ( has_term( $product_categories, 'product_cat', $the_id ) )
$cart_item['custom_price'] = $product->get_price() + $addition;
return $cart_item;
}
// Set conditionally a custom item price
add_action('woocommerce_before_calculate_totals', 'add_custom_price', 20, 1);
function add_custom_price( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
// Only for a DEFINED coupon code ( to be set below )
$coupon_code = 'newcp';
if ( ! $cart->has_discount( $coupon_code ) ) return;
foreach( $cart->get_cart() as $cart_item ) {
if ( isset($cart_item['custom_price']) ) {
$cart_item['data']->set_price( (float) $cart_item['custom_price'] );
}
}
}
Code goes in function.php file of the active child theme (or active theme). Tested and works.
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.
I have set up a hidden input item using woocommerce_before_add_to_cart_button hook
function add_gift_wrap_field() {
?>`<input type="hidden" id="price_val" name="added_price" value="100.34">`<?php
}
add_action( 'woocommerce_before_add_to_cart_button', 'add_gift_wrap_field' );
Saving fields against the product:
function save_gift_wrap_fee( $cart_item_data, $product_id ) {
if( isset( $_POST['added_price'] ) ) {
$cart_item_data = array();
$cart_item_data[ "gift_wrap_fee" ] = "YES";
$cart_item_data[ "gift_wrap_price" ] = 100;
}
return $cart_item_data;
}
add_filter( 'woocommerce_add_cart_item_data', 'save_gift_wrap_fee', 99, 2 );
Now I can echo out the $_POST['added_price'] inside this woocommerce_before_calculate_totals hook.
The code I written is shown below:
function calculate_gift_wrap_fee( $cart_object ) {
if( !WC()->session->__isset( "reload_checkout" )) {
/* Gift wrap price */
$additionalPrice = number_format($_POST['added_price'], 2);
$additionalPrice = floatval($additionalPrice);
//echo $additionalPrice; exit(); shows the value
foreach ( $cart_object->cart_contents as $key => $value ) {
if( isset( $value["gift_wrap_fee"] ) ) {
/* Woocommerce 3.0 + */
$orgPrice = floatval( $value['data']->get_price() );
$value['data']->set_price( $orgPrice + $additionalPrice ); //not adding the $additionalPrice here.
}
}
}
}
add_action( 'woocommerce_before_calculate_totals', 'calculate_gift_wrap_fee', 99 );
What am I doing wrong here?
Here is the complete clean, tested and working solution based on your code question.
Here I have included your custom field key/value in the cart object for the related cart item, instead of getting the Post value in your calculate_gift_wrap_fee() function.
This way it's not possible to loose the custom field value and the new price calculation is reliable.
I have commented 'gift_wrap_fee' and 'gift_wrap_price' as they are not really needed now (but you can uncomment them if you like).
Here is this code:
// The hidden product custom field
add_action( 'woocommerce_before_add_to_cart_button', 'add_gift_wrap_field' );
function add_gift_wrap_field() {
?>
<input type="hidden" id="price_val" name="added_price" value="100.34">
<?php
}
// Adding the custom field to as custom data for this cart item in the cart object
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 ) {
$bool = false;
$data = array();
if( ! empty( $_REQUEST['added_price'] ) ) {
$cart_item_data['custom_data']['added_price'] = $_REQUEST['added_price'];
// Below this 2 values are not really needed (I think)
// (You can uncomment them if needed)
## $cart_item_data['custom_data']['gift_wrap_fee'] = 'YES';
## $cart_item_data['custom_data']['gift_wrap_price'] = 100;
// 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;
}
// Changing the cart item price based on custom field calculation
add_action( 'woocommerce_before_calculate_totals', 'calculate_gift_wrap_fee', 10, 1 );
function calculate_gift_wrap_fee( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
// Iterating though cart items
foreach ( $cart->get_cart() as $cart_item ) {
// Continue if we get the custom data for the current cart item
if( ! empty( $cart_item['custom_data'] ) ){
// Get the custom field "added price" value
$added_price = number_format( $cart_item['custom_data']['added_price'], 2 );
// The WC_Product object
$product = $cart_item['data'];
// Get the price (WooCommerce versions 2.5.x to 3+)
$product_price = method_exists( $product, 'get_price' ) ? floatval(product->get_price()) : floatval($product->price);
// New price calculation
$new_price = $product_price + $added_price;
// Set the calculeted price (WooCommerce versions 2.5.x to 3+)
method_exists( $product, 'set_price' ) ? $product->set_price( $new_price ) : $product->price = $new_price;
}
}
}
Code goes in function.php file of your active child theme (or theme) or also in any plugin file.
This code is tested and works on WooCommerce versions from 2.5.x to 3+.
I have set up a hidden input item using woocommerce_before_add_to_cart_button hook
function add_gift_wrap_field() {
?>`<input type="hidden" id="price_val" name="added_price" value="100.34">`<?php
}
add_action( 'woocommerce_before_add_to_cart_button', 'add_gift_wrap_field' );
Saving fields against the product:
function save_gift_wrap_fee( $cart_item_data, $product_id ) {
if( isset( $_POST['added_price'] ) ) {
$cart_item_data = array();
$cart_item_data[ "gift_wrap_fee" ] = "YES";
$cart_item_data[ "gift_wrap_price" ] = 100;
}
return $cart_item_data;
}
add_filter( 'woocommerce_add_cart_item_data', 'save_gift_wrap_fee', 99, 2 );
Now I can echo out the $_POST['added_price'] inside this woocommerce_before_calculate_totals hook.
The code I written is shown below:
function calculate_gift_wrap_fee( $cart_object ) {
if( !WC()->session->__isset( "reload_checkout" )) {
/* Gift wrap price */
$additionalPrice = number_format($_POST['added_price'], 2);
$additionalPrice = floatval($additionalPrice);
//echo $additionalPrice; exit(); shows the value
foreach ( $cart_object->cart_contents as $key => $value ) {
if( isset( $value["gift_wrap_fee"] ) ) {
/* Woocommerce 3.0 + */
$orgPrice = floatval( $value['data']->get_price() );
$value['data']->set_price( $orgPrice + $additionalPrice ); //not adding the $additionalPrice here.
}
}
}
}
add_action( 'woocommerce_before_calculate_totals', 'calculate_gift_wrap_fee', 99 );
What am I doing wrong here?
Here is the complete clean, tested and working solution based on your code question.
Here I have included your custom field key/value in the cart object for the related cart item, instead of getting the Post value in your calculate_gift_wrap_fee() function.
This way it's not possible to loose the custom field value and the new price calculation is reliable.
I have commented 'gift_wrap_fee' and 'gift_wrap_price' as they are not really needed now (but you can uncomment them if you like).
Here is this code:
// The hidden product custom field
add_action( 'woocommerce_before_add_to_cart_button', 'add_gift_wrap_field' );
function add_gift_wrap_field() {
?>
<input type="hidden" id="price_val" name="added_price" value="100.34">
<?php
}
// Adding the custom field to as custom data for this cart item in the cart object
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 ) {
$bool = false;
$data = array();
if( ! empty( $_REQUEST['added_price'] ) ) {
$cart_item_data['custom_data']['added_price'] = $_REQUEST['added_price'];
// Below this 2 values are not really needed (I think)
// (You can uncomment them if needed)
## $cart_item_data['custom_data']['gift_wrap_fee'] = 'YES';
## $cart_item_data['custom_data']['gift_wrap_price'] = 100;
// 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;
}
// Changing the cart item price based on custom field calculation
add_action( 'woocommerce_before_calculate_totals', 'calculate_gift_wrap_fee', 10, 1 );
function calculate_gift_wrap_fee( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
// Iterating though cart items
foreach ( $cart->get_cart() as $cart_item ) {
// Continue if we get the custom data for the current cart item
if( ! empty( $cart_item['custom_data'] ) ){
// Get the custom field "added price" value
$added_price = number_format( $cart_item['custom_data']['added_price'], 2 );
// The WC_Product object
$product = $cart_item['data'];
// Get the price (WooCommerce versions 2.5.x to 3+)
$product_price = method_exists( $product, 'get_price' ) ? floatval(product->get_price()) : floatval($product->price);
// New price calculation
$new_price = $product_price + $added_price;
// Set the calculeted price (WooCommerce versions 2.5.x to 3+)
method_exists( $product, 'set_price' ) ? $product->set_price( $new_price ) : $product->price = $new_price;
}
}
}
Code goes in function.php file of your active child theme (or theme) or also in any plugin file.
This code is tested and works on WooCommerce versions from 2.5.x to 3+.