I'm building a woocommerce shop to sell antique maps, both originals and digital prints. The maps therefore are set as two variations of the same product: "Original" and "Print". "Original" is just the original antique map but "Print" has a load of options such as framing, mounting and print size and these are set by the user in the single-product page using jquery.
I've succeeded in allowing users to choose from a series of options in the single product page and thereby dynamically generate a price for the product they're buying based on what size, quantity, framing and mounting options they select.
This is done using jquery and the options (including the price) are passed to the cart using hidden fields and custom data as follows, where isset($_REQUEST['price']) refers to the hidden price field set by jquery (thwre are a load of these for framing, mounting, size etc but this gives an idea:
if(isset($_REQUEST['price']) && ! empty( 'price' )) {
$order_item_price = sanitize_text_field($_REQUEST['price']);
$cart_item_data['custom_data']['order_price'] = array(
'label' => 'Total this item',
'value' => '£' . $order_item_price
);
}
...this works fine. The price and total price (along with other custom information about the product) are displayed in the cart and the total at the bottom of the cart tallies with the product prices. I use this function to achieve this:
function update_price_in_cart( $cart_obj ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) ) {
return;
}
if( ! is_cart() ) {
return;
}
// Iterate through each cart item
foreach( $cart_obj->get_cart() as $item_key=>$cart_item ) {
if( isset( $cart_item['total_price'] ) || isset( $cart_item['total_price_original'] ) ) {
$price = $cart_item['total_price'] + $cart_item['total_price_original'];
$cart_item['data']->set_price( ( $price ) );
}
}
}
add_action( 'woocommerce_before_calculate_totals', 'update_price_in_cart', 10, 1 );
The problem is when the user proceeds to the checkout. All the data created using hidden fields (frame type, mount type, size, price) displays on the left side of the page under where it says product but the totals on the right next to each product ordered are set to the original price set for the variation 'Print' set in the woocommerce product in wordpress.
I've tried up dating using something like this (the below is a test using static values):
function update_price_in_checkout($cart_obj) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
var_dump($cart_item_data['custom_data']['order_price']);
if( isset( $cart_item['total_price'] ) || isset( $cart_item['total_price_original'] ) ) {
WC()->cart->total = 597;
} else {
WC()->cart->total = 1;
}
}
add_action( 'woocommerce_review_order_before_order_total', 'update_price_in_checkout' );
...but a. this only accepts a static number as it doesn't pick up the hidden fields and b. it doesn't touch the individual product prices on the right of the checkout.
In summary - I need to find a way to replace the base variation product price on which is appearing on the right with the calculated totals based on user input. It all works fine in the cart page but doesn't transfer to the checkout page.
Delving through stackoverflow I think setting it in woocommerce_checkout_create_order_line_item might be an idea but I can't work out how to do this.
Any ideas?
The following code will add as custom to cart item data the price of your product select options (from the hidden product field) and will update the cart item price:
// Get custom field value, calculate new item price, save it as custom cart item data
add_filter( 'woocommerce_add_cart_item_data', 'add_custom_field_data', 20, 3 );
function add_custom_field_data( $cart_item_data, $product_id, $variation_id ) {
if (isset($_REQUEST['price']) && !empty($_REQUEST['price'])) {
$cart_item_data['custom_price'] = sanitize_text_field($_REQUEST['price']);
$cart_item_data['unique_key'] = md5(microtime().rand());
}
return $cart_item_data;
}
// Update price
add_action( 'woocommerce_before_calculate_totals', 'update_price_in_cart', 10, 1 );
function update_price_in_cart( $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( isset( $cart_item['custom_price'] ) ) {
// Add options to the product price
$cart_item['data']->set_price( $cart_item['data']->get_price() + $cart_item['custom_price'] );
}
}
}
Code goes in function.php file of your active child theme (or active theme). Tested and work.
Related
I'm stuck in a critical situation actually i am using custom price on my product which is calculated by the requirements of the product and on add to cart it works fine. But when the order is placed and customer wants to order it again...here order again functionality of woocommerce do not post the calculated price that has been paid by the customer before..after clicking on order again button it redirects to the cart page and set the regular price of the product
The question is woocommerce let us to use the custom price instead of regular price of the product but the Order Again functionality use regular price instead of custom price.
Thanks
global $product;
add_action( 'woocommerce_before_calculate_totals', 'quantity_based_bulk_pricing', 9999, 1 );
function quantity_based_bulk_pricing( $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_key => $cart_item ) {
// Get the bulk price from product (variation) custom field
$bulk_price = $cart_item['wapf']['8']['values']['0']['label'];
$bulk_price_reorder = $cart_item['wapf']['6']['values']['0']['label'];
if($cart_item['data']->get_name() == "Print Custom Stickers"){
if($bulk_price != null){
$cart_item['data']->set_price( $bulk_price );
}
if($bulk_price_reorder != null){
$cart_item['data']->set_price( $bulk_price_reorder );
}
}
}
}
I've tried this hook in which i am saving custom price of product in a custom field and getting it from $cart_item array and it works for me but there are lot of orders that has been placed before so i want to get the price on cart from which customer has ordered this product before
I am developing a wordpress and woocommerce-based website where information on cooking-related training is provided and various kitchen materials are sold.
Those who want to participate in the trainings apply by filling out a form. Kitchen supplies are also sold through woocommerce.
Trainings are added to the website with a type of content called training.
Some trainings are requested to be sold over the woocommerce structure. However, these "Trainings" that want to be sold are wanted to remain in the form of educational content. In addition, it is requested not to be added or moved as a product.
First of all, I created a virtual product called Education. I hid the product in the store.
Then I added a custom field for Tutorials called price. The price of each training to be sold will be entered here.
I have a button "Register for Training" on the training detail page, I changed it to "Buy" for the trainings wanted to sell and the link
?add-to-cart=340&custom_price=600&quantity=1
I gave in the form.
Here 340 is the id of the virtual product I created.
When the Buy button is clicked, the virtual product called Education is added to the basket. But I want to update the name and price of this training according to which training detail page is printed.
The codes I added to functions.php.
add_action( 'woocommerce_before_calculate_totals', 'before_calculate_totals' );
function before_calculate_totals( $_cart ){
// loop through the cart_contents
foreach ( $_cart->cart_contents as $cart_item_key => &$item ) {
// you will need to determine the product id you want to modify, only when the "donation_amount" is passed
if ( $item['product_id'] == 340 && isset( $_GET['custom_price'] ) ){
// custom price from POST
$custom_price = $_GET['custom_price'] > 0 ? $_GET['custom_price'] : 0;
// save to the cart data
//$item['data']->price = $custom_price;
// new versions of WooCommerce may require (instead of line above)...
$item['data']->set_price($custom_price);
}
}
}
function ipe_product_custom_price( $cart_item_data, $product_id ) {
if( isset( $_POST['custom_price'] ) && !empty($_POST['custom_price'])) {
$cart_item_data[ "custom_price" ] = $_POST['custom_price'];
}
return $cart_item_data;
}
add_filter( 'woocommerce_add_cart_item_data', 'ipe_product_custom_price', 99, 2 );
I wanted to update the price with these codes, but it didn't work.
How do I dynamically update the information of the virtual product? Or what different method would you suggest?
There are some mistakes in your code and some missing things to make it work. Try the following:
// Set custom data as custom cart data in the cart item
add_filter( 'woocommerce_add_cart_item_data', 'add_custom_price_as_custom_cart_item_data', 30, 3 );
function add_custom_price_as_custom_cart_item_data( $cart_item_data, $product_id, $variation_id ) {
if( ! isset($_GET['custom_price']) ) {
$cart_item_data['custom_price'] = (float) esc_attr( $_GET['custom_price'] );
$cart_item_data['unique_key'] = md5( microtime().rand() ); // Make each item unique
}
return $cart_item_data;
}
// Change cart item price
add_action( 'woocommerce_before_calculate_totals', 'change_cart_item_price' );
function change_cart_item_price( $cart ){
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
// loop through the cart_contents
foreach ( $cart->get_cart() as $cart_item ) {
// you will need to determine the product id you want to modify, only when the "donation_amount" is passed
if ( isset($cart_item['custom_price']) ) {.
$item['data']->set_price($cart_item['custom_price']);
}
}
}
// Display the custom price on minicart too
add_filter( 'woocommerce_cart_item_price', 'change_minicart_item_price', 10, 3 );
function change_minicart_item_price( $price_html, $cart_item, $cart_item_key ) {
if( ! is_cart() && 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_html;
}
Code goes in functions.php file of your active child theme (or active theme). It should works.
I'm trying to append text to WooCommerce product title in the order meta - if products has a specific tag. I'm working from
"Append text to product title if product has product-tag on cart in WooCommerce"
"Display custom payment field in Woocommerce Admin, Orders and emails"
This is what I have so fare:
add_filter( 'woocommerce_get_order_item_totals', 'add_udstilling_below_cart_item_name', 10, 3 );
function add_udstilling_below_cart_item_name( $total_rows, $order, $tax_display ) {;
$new_total_rows = [];
foreach($total_rows as $key => $total ){
$new_total_rows[$key] = $total;
}
}
return $new_total_rows;
}
Based on your previous question and this new question, I assume that you would like to see the product title adjusted 'everywhere'.
IMPORTANT!! You should definitely read the following post to
understand that this is a general adjustment, so that you ultimately
no longer need the code from your previous post / question
Changing WooCommerce cart item names
In other words, you can start using the code below to see the adjustment in the following places
Order-receive (Thank you) page,
email notifications
My Account Orders> Single order details
Cart & Checkout and Backend Order edit pages.
Now you have change the names everywhere except on Shop archives and product pages…
function add_udstilling_order_item_name( $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 an instance of the WC_Product object
$product = $cart_item['data'];
// Get product id
$product_id = $cart_item['product_id'];
if( method_exists( $product, 'set_name' ) && has_term( 'udstillingsmodel', 'product_tag', $product_id ) ) {
$product->set_name( $product->get_name() . '(test)' );
}
}
}
add_action( 'woocommerce_before_calculate_totals', 'add_udstilling_order_item_name', 10, 1 );
I am creating a membership site and totally created static pages for each Membership plans (have only 3 plans). However, I have added products for each plan and when I hit SELECT PLAN button I redirect to some custom form where I ask users range of info we are going to use to fulfil the plan (same as sneakertub.com).
I have written code into the PHP page which will handle SUBMIT action of the form. This PHP file, infopage.php, will process POST data I sent via POST call and stores these all data into WC session.
$customer_name = $_POST["customer_name"];
$customer_email = $_POST["customer_email"];
$customer_sex = $_POST["customer_sex"];
$customer_age = $_POST["customer_age"];
$product_id = $_POST["product_id"];
global $wp_session;
$data = array(
'customer_name' => $customer_name,
'customer_email' => $customer_email,
'customer_sex' => $customer_sex,
'customer_age' => $customer_age);
$wp_session['custom_SESSION_child']=$data;
WC()->session->set('custom_data_child', $data);
//Add product to WooCommerce cart.
WC()->cart->add_to_cart( $product_id )
However, I don't think the above code works. As I don't find values into session with any of the above technique. I have used wp_session, WC()->session and $_SESSION but no approach is working.
I am trying to access these values into functions.php this way,
add_action( 'woocommerce_before_calculate_totals', 'twf_additional_price', 1, 3 );
function twf_additional_price( $cart_object ) {
global $wpdb;
global $wp_session;
$session_data_2 = $wp_session['custom_SESSION_child'];
$session_data = WC()->session->get('custom_data_child');
var_dump($session_data);
var_dump($session_data2);
foreach ( $cart_object->cart_contents as $key => $value ) {
$extra_charge = 0;
if(isset($value['twf_user_custom_datas'])){
$extra_charge = 100;
}
$value['data']->set_price($value['data']->price + $extra_charge);
}
}
For now ignore the for loop. Main thing is
var_dump($session_data);
var_dump($session_data2);
both dumps only NULL.
My main goal is to add the all above fields into Woocommerce checkout and order pages.
Please let me know what is wrong here. I know I might be working on very bad approach but I want Plan selection to checkout process same as sneakertub.com. Please let me know if there is any tutorial on this or proper way to do this. I prefer doing this without plugins but I am ready to use plugins as well.
I appreciate your attention.
Updated - Instead of using sessions, you should use the last available argument in WC_Cart add_to_cart() method, which will allow you to add any custom cart item data.
For cart item price change based on calculations, is better to make the new price calculation before and to set it in that custom cart item data.
Try the following instead:
1) For your code in the php page:
$custom_data = array(); // Initializing
// Set the posted data as cart item custom data
if( isset($_POST['customer_name']) && ! empty($_POST['customer_name']) )
$custom_data['custom_data']['name'] = sanitize_text_field( $_POST['customer_name'] );
if( isset($_POST['customer_email']) && ! empty($_POST['customer_email']) )
$custom_data['custom_data']['email'] = sanitize_text_field( $_POST['customer_email'] );
if( isset($_POST['customer_sex']) && ! empty($_POST['customer_sex']) )
$custom_data['custom_data']['sex'] = sanitize_text_field( $_POST['customer_sex'] );
if( isset($_POST['customer_age']) && ! empty($_POST['customer_age']) )
$custom_data['custom_data']['age'] = sanitize_text_field( $_POST['customer_age'] );
// Set the calculated item price as custom cart item data
if( isset($custom_data['custom_data']) && sizeof($custom_data['custom_data']) > 0 && $product_id > 0 ) {
// Get an instance of the WC_Product object
$product = wc_get_product( $product_id );
// Save the new calculated price as custom cart item data
$custom_data['custom_data']['new_price'] = $product->get_price() + 100;
}
// Add product to cart with the custom cart item data
WC()->cart->add_to_cart( $product_id, '1', '0', array(), $custom_data );
Code goes in function.php file of your active child theme (or active theme). Tested and works.
2) Your revisited function that will change the cart item price:
add_action( 'woocommerce_before_calculate_totals', 'custom_cart_item_price', 30, 1 );
function custom_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']['new_price']) )
$cart_item['data']->set_price( $cart_item['custom_data']['new_price'] );
}
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
All other custom cart item data is available under the cart item key 'custom_data' as an indexed array… So you will be able to get that data easily from the cart object, to save it in the order.
I have a function on my Woocommerce website that enables customers to set a custom amount to pay for a specific product, based on a value I'm passing through the URL.
I'm using the woocommerce_before_calculate_totals hook, and up until I upgraded to WC 3.3.5, it was working fine. Now, when I run the code, the checkout initially shows the custom amount.
However, once the loader has finished updating, it resets the price to '0' (i.e. displaying £0.00 the checkout page's total fields).
Here's that code:
add_action( 'woocommerce_before_calculate_totals', 'pay_custom_amount', 99);
function pay_custom_amount() {
$payment_value = $_GET['amount'];
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
if($cart_item['data']->id == 21 ){
$cart_item['data']->set_price($payment_value);
}
}
}
Well, colour me baffled. I've trawled Stack Overflow for solutions but can't see any similar problems. I see the hook runs multiple times but gather this is normal.
If anyone know what might be happening here, it would be great if you could share.
You can't get a price from an URL and set it in woocommerce_before_calculate_totals action hook. This needs to be done differently.
In the below code:
the first hooked function will get that "amount" from the URl and will set it (register it) in cart item object as custom data.
the 2nd hooked function will read that amount from custom cart item data and will set it as the new price.
Now your the target product ID in your code need to be the same ID that the added to cart product.
The code:
// Get the custom "amount" from URL and save it as custom data to the cart item
add_filter( 'woocommerce_add_cart_item_data', 'add_pack_data_to_cart_item_data', 20, 2 );
function add_pack_data_to_cart_item_data( $cart_item_data, $product_id ){
if( ! isset($_GET['amount']) )
return $cart_item_data;
$amount = esc_attr( $_GET['amount'] );
if( empty($amount) )
return $cart_item_data;
// Set the custom amount in cart object
$cart_item_data['custom_price'] = (float) $amount;
$cart_item_data['unique_key'] = md5( microtime() . rand() ); // Make each item unique
return $cart_item_data;
}
// Alter conditionally cart item price based on product ID and custom registered "amount"
add_action( 'woocommerce_before_calculate_totals', 'change_conditionally_cart_item_price', 30, 1 );
function change_conditionally_cart_item_price( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
// HERE set your targeted product ID
$targeted_product_id = 21;
foreach ( $cart->get_cart() as $cart_item ) {
// Checking for the targeted product ID and the registered "amount" cart item custom data to set the new price
if($cart_item['data']->get_id() == $targeted_product_id && isset($cart_item['custom_price']) )
$cart_item['data']->set_price($cart_item['custom_price']);
}
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.