Woocommerce: custom price based on user input - php

I did not want to post here but I could not find the answer I was looking for and I do not have enough reputation to comment on other VERY SIMILAR questions to get my exact answer.
I found an almost perfect answer from this post: WooCommerce: Add product to cart with price override?
using the code:
add_action( 'woocommerce_before_calculate_totals', 'add_custom_price');
function add_custom_price( $cart_object ) {
$custom_price = 10; // This will be your custome price
foreach ( $cart_object->cart_contents as $key => $value ) {
$value['data']->price = $custom_price;
}
}
and it works great...if you set a hard coded custom price.
My question is: How can I set a custom price based on user input?
I have tried every way I can think of to pass information (I even tried using cookies, globals, sessions) and none of them worked how I wanted and all of them were, at BEST, hacks.
The specific product in question is a donation and the customer wants the user to be able to set the donation price (rather than just creating a variable product with set price points).
On the donation page when the user submits the donation form I am adding the donation product to the cart like so:
$donation_success = $woocommerce->cart->add_to_cart($donation_id);
My donation product has a set price of 0.00 so when it is added to the cart it has a price of 0.00 (I don't know if the price is set at this point or later)
I have tried passing in information at this point using the last variable in the add_to_cart method which accepts an array of arguments but I couldn't seem to get that to work either.
I am sure the answer is simple but I have been trying for hours to do this right and I cannot get it to work. I am out of ideas.
The actual code I am using at the moment is slightly different than was suggested by the answerer of the above post but works basically the same:
add_action( 'woocommerce_before_calculate_totals', 'woo_add_donation');
function woo_add_donation() {
global $woocommerce;
$donation = 10;
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
if($cart_item['data']->id == 358 || $cart_item['data']->id == 360){
$cart_item['data']->set_price($donation);
}
}
}
Thanks in advance for any helpful advice!

I found a solution which is not elegant but works for my purposes.
I was trying to use cookies before but I didn't set the cookie path so it was only available to the page it was set on.
I am now setting a cookie with the donation price:
setcookie("donation", $_GET['donation'], 0, "/");
Then I am setting the price like so:
add_action( 'woocommerce_before_calculate_totals', 'woo_add_donation');
function woo_add_donation() {
global $woocommerce;
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
if($cart_item['data']->id == 358 && ! empty($_COOKIE['donation'])){
$cart_item['data']->set_price($_COOKIE['donation']);
}
}
}

I have been looking for exactly the same thing. I found this WooCommerce plugin (not free) for this
name your price plugin
Initially I wasn't sure what search terms to use to find plugins like this but it looks like "WooCommerce name your price" brings up links to other sources of similar plugins.

[this is a comment] Where do you set the cookie? My first guess is that the refreshes in the same page, using the GET method, and provides a PHP code-block with the $_GET['donation'] to set the cookie with.
And then, once the cookie is set, you redirect the page to Woocommerce Cart page to continue the shopping process. If you're doing it easier way, please let me know. Thanks.
Sorry, I couldn't post this as a comment to the selected answer due to the lack of points.

This code will create order with custom price:
$product = wc_get_product($product_id);
$order = wc_create_order();
try {
$order->add_product($product);
//$order->set_customer_id($user->ID);
$order->set_billing_email($customer_email);
} catch (WC_Data_Exception $e) {
wp_send_json_error("Failed to create order");
}
$order->calculate_totals();
try {
$order->set_total($custom_price); // $custom_price should be float value
} catch (WC_Data_Exception $e) {
wp_send_json_error("Failed to change order details");
}
$order->save();
$order->update_status( 'completed' );

Related

Currency conversion by plugin is wrong in Cart page

I hope someone can help me with this!
I´m using this plugin WooCommerce Currency Switcher by PluginUs.NET. everything works fine until I go to the cart page in there the price is a converter for a second time, first is converted before adding into the cart, and then inside cart page.
So I search in there support page and found this topic that suggests this solution but when I add the code everything is still the same, maybe its because the code was made three years ago and now is not supported in the new Woocommerce version, this is the code that is suggested:
add_action('woocommerce_before_calculate_totals', 'fix_currency_conversion');
function fix_currency_conversion($cart_object){
global $WOOCS;
$cur_currency = $WOOCS->current_currency;
$currencies = $WOOCS->get_currencies();
$conversion_rate = $currencies[$WOOCS->current_currency]['rate'];
if($cur_currency == 'HKD'){
foreach ( $cart_object->cart_contents as $cart_item_key => $cart_item ) {
$cart_item['data']->price = $cart_item['data']->price / $conversion_rate;
}
}
}
It is late but it will be helpful for other.
You need to change:
$cart_item['data']->price = $cart_item['data']->price / $conversion_rate;
WITH
$cart_item['data']->set_price($cart_item['data']->price / $conversion_rate);

Changing WooCommerce item name

I don't have 50 reputation on stack overflow, so I can't comment on post. To ask a question I gotta make a new question unfortunately :/
So, from here :
Changing WooCommerce cart item names
OP asked about changing WooCommerce cart item names.
The answer below, which
#https://stackoverflow.com/users/3730754/loictheaztec
reply helped me but it isn't complete enough..
I've tested the code he gave,
add_filter( 'woocommerce_before_calculate_totals', 'custom_cart_items_prices', 10, 1 );
function custom_cart_items_prices( $cart_object ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
// Iterating through cart items
foreach ( $cart_object->get_cart() as $cart_item ) {
// Get the product name (item name)
$id = $cart_item['data']->get_name();
// THE NEW NAME
$new_name = 'mydesiredproductname';
// Set your cart item name
$cart_item['data']->set_name( $new_name );
}
}
it works great, for the code above on the cart page, checkout page customer invoice page and admin order page is changed to "mydesiredproductname" but I want the Cart Page, the Checkout Page, the customer invoice page and the admin order page to remain the same, only thing is changed is the item name that is submitted to payment gateway. Wonder if that is possible? Or what is the closest that I can achieve?
ps: I tested is_cart and also is_checkout function, for is_cart the cart is unchanged, for is_checkout the checkout page is unchanged.
Any help would be greatly appreciated
Thanks !

Woocommerce after product update get stock

I have a problem with adding action to 'save_post_product' hook.
I want to send stock data to my external API when product is changed(updated) in woocommerce admin.
The problem is that the stock values i get from simple product after the action is called are one 'iteration' old. By that i mean that the stock data i get seem to be the data before update. so if i call the update product 2 times, first time i get old data, second time i get the new ones.
add_action('save_post_product', array($this, 'product_changed'), 99, 3);
function product_changed($post_id, $post, $update)
{
if ('product' != $post->post_type || $update != true) {
return;
}
$_pf = new WC_Product_Factory();
$product = $_pf->get_product($post_id);
$items = array();
if ($product->product_type == 'simple') {
$product->get_total_stock();
$inStock = $product->is_in_stock();
$qty = $product->get_stock_quantity();
$managing = $product->managing_stock();
$items[$product->id] = [
...
];
} elseif ($product->product_type == 'variable') {
$variations = $product->get_available_variations();
/*For variations it works properly*/
}
}
$itemsJson = json_encode($items);
$this->sendData($itemsJson, '/products-changed/');
}
TLDR (example):
Lets say that product is set to manage stock, stock quantity 500 and is in stock.
Now i change the product not to manage stock, and set that it is out of stock.
I hit update.
Everything runs and wordpress gets to my code. When i get the values i still get
$product->is_in_stock(); //true
$product->get_stock_quantity(); //500
$product->managing_stock(); //yes
Now, when i hit the update again, everything runs the second time, but now i get the correct values.
$product->is_in_stock(); //false
$product->get_stock_quantity(); //0
$product->managing_stock(); //no
I assume that the product stock update runs after 'save_post_product' hook however, i was not able to find any other hook that might solve my problem.
NOTE: It works well with variations in the first 'iteration'. I think it has to do something with the $product->get_available_variations() code.
Seems like hooking the action to wp_insert_post hook did the trick :
add_action('wp_insert_post', array($this, 'product_changed'), 99, 3);
save_post is triggered BEFORE the update. So you would have to use the $_POST, $_GET or the global $post_data, as specified in https://codex.wordpress.org/Plugin_API/Action_Reference/save_post
You already resolved this by using wp_insert_post, so that's okay. Just a clarification for any one seeing this

Woocommerce: Programmatically Update Item In Cart

I need to programmatically and dynamically change the price of an item in the cart.
I’ve tried varying combinations of Woocommerce action hooks, the cart and session objects, but nothing quite seems to do the trick. I thought this wouldn’t be so challenging.
add_action( 'woocommerce_before_calculate_totals', 'change_cart_item_price' );
function change_cart_item_price( $cart_object ) {
foreach ( $cart_object->cart_contents as $key => $value ) {
if( 123 == $value['data']->id ) {
$new_price = function_to_get_new_price( $value, get_current_user_id( ) );
$value['data']->price = $new_price;
}
}
}
The above code changes the price for each item only on the checkout page, or when updating the cart (ie: the hook is called when removing an item from the cart), but not indefinitely.
I'm using the Woocommerce Gravity Forms add-on. I have one product in particular, which will be ordered multiple times by a given user. The user will be allowed 5x free with only shipping fees, and each above 5 will be $20. I have this much coded and functional with Gravity Forms hooks that dynamically populate fields. Shipping is specific to fields within the gravity form, therefore I am leaving that calculation to Gravity Forms.
My issue is that if a user reduces the quantity of this product from their order (removes one of the items from the cart), it should re-calculate the price of each item of the same product within the cart, otherwise they could be over-charged (an item that used to be the 6th is now the 4th, but the price remains the same, which it shouldn't)
Therefore, I would like to re-calculate the price of each item in the cart, based on the quantity of this particular product, every time something is removed from the cart.
--- EDIT ---
The above code works, but I'm realizing the issue must be a custom loop I'm using to display the prices...
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
$_product = $cart_item['data'];
if( 123 == $_product->post->ID ) {
$price_not_updated = $cart_item['data']->price;
}
}
I figured it out... I looked at the woocommerce cart docs and essentially realized that the prices I was getting had yet to be calculated. So, before running the loop, I had to do the action that I was initially hooking into to change the prices.
Thanks for your help!
function getUpdatedCartPrices() {
do_action( 'woocommerce_before_calculate_totals', WC()->cart );
$horray_updated_prices_works = array();
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
$_product = $cart_item['data'];
if( 123 == $_product->post->ID ) {
$horray_updated_prices_works[] = $cart_item['data']->price;
}
}
}
I changed the function to have a second parameter so that you can use it dynamically with any product as long as you call the proper id. I also set the function return value to either a success or error message. This way you can know if it was changed, and if so, to what price. Hope this helps.
by the way, having this function called again when the cart is updated should solve the recalculation issue. It would be great if i could see the code for your $function_to_get_new_price() function.
add_action( 'woocommerce_before_calculate_totals', 'change_cart_item_price' );
function change_cart_item_price($id, $cart_object ) {
foreach ( $cart_object->cart_contents as $key => $value ) {
if( $id == $value['data']->id ) {
$new_price = function_to_get_new_price( $value, get_current_user_id( ) );
$value['data']->price = $new_price;
$successMsg = "The price of item $value['data']->id was set to $value['data']->price".;
return $successMsg;
}else{
$errorMsg = "Price was not changed!";
return $errorMsg;
}
}
}

update woocommerce cart after changing shipping method

I am currently working on an online shop with WooCommerce. I faced the problem that I want to grant a discount to customers who chose a specific shipping method. The discount is 0.50 for every single product. I basically solved this problem with the following code in my "functions.php":
add_action('woocommerce_before_calculate_totals', 'woo_add_cart_fee');
function woo_add_cart_fee() {
global $woocommerce;
$cart = $woocommerce->cart->get_cart();
//Calculating Quantity
foreach ($cart as $cart_val => $cid) {
$qty += $cid['quantity'];
}
if ($woocommerce->cart->shipping_label == "specific shipping method") {
$woo_fee = $qty * (-0.5);
$woo_name = "discount for specific shipping method";
}
$woocommerce->cart->add_fee(__($woo_name, 'woocommerce'), $woo_fee, true);
}
The code technically works, the only problem I have now is that if a customer changes the shipping method i.e. from the "specific shipping method" to a "normal one" (without any discount) or the other way round, it always displays and calculates the discount value from the previously chosen shipping method. In other words it is always one step back and therefore displays the customer the wrong total amount.
Does anyone has an idea to solve this problem?
This solutions is for Woocommerce 2.1.X!
I am not sure if this might help. I was facing a similar problem, where I needed to retrieve the chosen shipping method. In the file \wp-content\plugins\woocommerce\includes\wc-cart-functions.php I found a method called wc_cart_totals_shipping_html().
Within this method there is a check of the current selected shipping method that contains the following code:
$packages = WC()->shipping->get_packages();
foreach ( $packages as $i => $package ) {
$chosen_method = isset( WC()->session->chosen_shipping_methods[ $i ] ) ? WC()->session->chosen_shipping_methods[ $i ] : '';
}
I used this code in my own functions.php to check for the currently selected shipping method and it works. Example:
add_filter( 'woocommerce_billing_fields', 'wc_change_required_fields');
function wc_change_required_fields($address_fields) {
$packages = WC()->shipping->get_packages();
foreach ( $packages as $i => $package ) {
$chosen_method = isset( WC()->session->chosen_shipping_methods[ $i ] ) ? WC()->session->chosen_shipping_methods[ $i ] : '';
}
if ($chosen_method == 'local_delivery') {
$address_fields['billing_address_1']['required'] = true;
// place your changes that depend on the shipping method here...
}
}
Hope that helps!
This is very old, but I ran into this issue myself and had to work out the solution.
Woocommerce stores pre-calculated cart totals in the database, rather than calculating them on the fly. But the shipping method choice is stored as a session variable. So shipping changes are not reflected immediately at checkout without a visit or refresh of a cart page.
With the original posted code, the shipping changes were not reflected because they aren't recalculated and stored. To do this, the function needs to be tricked into thinking it's a cart page first, and then recalculating the totals to be stored.
GLOBAL $woocommerce;
if ( ! defined('WOOCOMMERCE_CART') ) {
define( 'WOOCOMMERCE_CART', true );
}
And then at the end of the function, after all the desired changes have been made refresh and store.
WC()->cart->calculate_totals();
See also CODEX for WC_AJAX::update_shipping_method()
http://docs.woothemes.com/wc-apidocs/source-class-WC_AJAX.html#148-174
Mark's answer worked for me, however I had to delete all transient values prior to running the code. Otherwise, it would simply restore the saved values.
public function clear_shipping_transients() {
global $wpdb;
$wpdb->query( "DELETE FROM `$wpdb->options` WHERE `option_name` LIKE ('_transient_cp_quote_%') OR `option_name` LIKE ('_transient_timeout_cp_quote_%') OR `option_name` LIKE ('_transient_wc_ship_%')" );
}

Categories