Custom price on WooCommerce 5.5.2 - php

I've been stuck on this for far too long. I've tried everything I've found on stackoverflow and I feel like I'm going around in circles.
I have a form on a product page asking for an invoice number and an amount on the invoice. I can get product attributes to update and display on the Cart page using the following code:
function invoice_save_price( $cart_item_data, $product_id ) {
if( isset( $_POST['invoice_id'] ) ) {
$cart_item_data = array();
$cart_item_data[ "invoice_number" ] = $_POST['invoice_number'];
$cart_item_data[ "invoice_amount" ] = $_POST['invoice_amount'];
}
return $cart_item_data;
}
add_filter( 'woocommerce_add_cart_item_data', 'invoice_save_price', 99, 2 );
I'm using a custom product type - extending simple products. I'm not sure if that's the correct way to do this, as my product has to have a default price set ($1 for now) or it doesn't display.
function wcpt_register_invoice_type () {
class WC_Product_Invoice extends WC_Product {
public function __construct( $product ) {
$this->product_type = 'invoice';
parent::__construct( $product );
}
}
}
However, I can't get the price to update. My code for that is here:
add_action( 'woocommerce_before_calculate_totals', 'invoice_add_custom_price', 99 );
function invoice_add_custom_price($cart_object) {
if (isset($_POST['invoice_id'])) {
foreach ( $cart_object->get_cart() as $value ) {
$newValue = number_format($_POST['invoice_amount'],2);
$value['data']->set_price($newValue);
}
return $value;
}
}
I thought there might've been a conflict with another plugin, so I started testing on a clean install of Wordpress 5.8 and WooCommerce 5.5.2
I've found a lot of references to this in older versions of WooCommerce (3.x) but I'm not having much luck in later versions.
Can someone please tell me what I'm missing? Any help will be appreciated!
Thanks,

Related

How to add a custom WooCommerce product type that extends WC_Product_Variable to the cart

I've created a custom WooCommerce product type that's based on the built-in variable product type. I want to have both product variations and custom characteristics and fields specific to my product type. This is all working and displaying fine in the admin and my custom front-end product page template.
Here's my custom product, for reference:
add_action( 'init', 'register_book_type' );
function register_book_type () {
class WC_Product_Book extends WC_Product_Variable {
public function __construct( $product ) {
parent::__construct( $product );
}
public function get_type() {
return 'book';
}
}
}
However, when I post the product with its variation to the cart, the behavior messes up because the code $adding_to_cart->get_type() in add_to_cart_action() (in the WC_Form_Handler class) is identifying the product as my custom "book" type and not treating it as a "variable" product and falling back to treating it as a "simple" product type by default.
Here's that built-in WooCommerce area that's causing me trouble:
$add_to_cart_handler = apply_filters( 'woocommerce_add_to_cart_handler', $adding_to_cart->get_type(), $adding_to_cart );
if ( 'variable' === $add_to_cart_handler || 'variation' === $add_to_cart_handler ) {
$was_added_to_cart = self::add_to_cart_handler_variable( $product_id );
} elseif ( 'grouped' === $add_to_cart_handler ) {
$was_added_to_cart = self::add_to_cart_handler_grouped( $product_id );
} elseif ( has_action( 'woocommerce_add_to_cart_handler_' . $add_to_cart_handler ) ) {
do_action( 'woocommerce_add_to_cart_handler_' . $add_to_cart_handler, $url ); // Custom handler.
} else {
$was_added_to_cart = self::add_to_cart_handler_simple( $product_id );
}
The problem seems to be that my own get_type() method returns "book" when this code is expecting "variable." I need it to return "book" so the product edit page will recognize the type properly.
I know I can remove and replace the add_to_cart_action() function in my own code to override this behavior and add my custom type, but then I'm unable to call all the other private methods in the WC_Form_Handler class. Or is it okay to just override that entire class..?
Any other way to bypass all this to get my variable-based custom product into the shopping cart?
I believe you could change the handler to tell WooCommerce to use the variable product handler by filtering woocommerce_add_to_cart_handler like so:
function kia_custom_add_to_cart_handler( $handler, $adding_to_cart ){
if( $handler == 'book' ){
$handler = 'variable';
}
return $handler;
}
add_filter( 'woocommerce_add_to_cart_handler', 'kia_custom_add_to_cart_handler', 10, 2 );
OR
you could add the default variable product handler to the custom woocommerce_add_to_cart_handler_$type hook like so:
add_action( 'woocommerce_book_add_to_cart', 'woocommerce_variable_add_to_cart' );
This is how I solved this issue:
public function get_type()
{
return get_current_screen()->id === 'product' ? 'book' : parent::get_type();
}
This will return book on the edit product page however return variable everywhere else.

WooCommerce 502 Bad gateway when using add_to_cart function

As the title says, I'm getting Error 502 Bad Gateway when I try to use the standard function add_to_cart() from WooCommerce
WC()->cart->add_to_cart( 8622 );
Any ideas what's going on? I also tried adding more arguments to the function such as the quantity and etc but doesn't seems to change anything...
WooCommerce documentation: https://docs.woocommerce.com/wc-apidocs/class-WC_Cart.html
Okay, I have managed to fix this by making another function to be called in the function.php file. I am not sure why this works now since it's the same concept, except that the function is now called from the functions.php file from the theme.
Here's the code for anyone that needs this:
// Add item to cart
function add_id_to_cart( $product_id ) {
$flag = true;
//check if product already in cart
foreach(WC()->cart->get_cart() as $key => $val ) {
$_product = $val['data'];
if($product_id == $_product->id ) {
$flag = false;
}
}
// if product not in cart, add it
if ( $flag ) {
WC()->cart->add_to_cart( $product_id );
}
}

Conditionally making WooCommerce product non purchasable

I've been struggling with a snippet for WooCommerce that I'd like to add to my child's theme functions but I am reaching the limits of my PHP knowledge.
I have set a custom attribute called pdt_volume, that I will fill everytime I add a product. I would like to remove the Add_to_cart button when the sum of this attribute for all the items already in the cart have reach a max_volume limit.
For example, the Max_volume is 100, I have 4 products of pdt_volume = 20, therefore, the add_to_cart button for a product with a pdt_volume of 25 should be removed so that I cannot add it to the cart.
So I have come up with this snippet, with bits of code found here and there.
But the functions.php won't save it, and others variations of this code has succeeded to register in the functions.php but the website would then give a 500 error...
Please, does someone has any idea how to achieve this?
What am I doing wrong?
Thanks.
EDIT 1 : Alright, so I got this code to actually be registered in the functions.php by the editor without breaking, but on the site, I still get the internal server error. Like something is not computing or something.
EDIT 2 (29/01/2017)
I used the ACF plugin I had purchased a long time ago but didn't find any suitable purpose... so far.
Here is the working code I was able to come up with. This is not a code masterpiece and I won't get any award for this, but it seems to be working so far. At least, it allows me to get a TRUE/FALSE statement that I can use in a if condition to change the add_to_cart button to a Your_box_is_full button.
Indeed, I didn't need any global $woocommerce or $product !
function get_cart_volume() {
$cart_volume = 0;
foreach( WC()->cart->get_cart() as $cart_item ) {
$item_id = $cart_item['product_id'];
$item_volume = get_field('product_volume', $item_id);
$item_qty = $cart_item['quantity'];
$vol_by_qty = $item_volume * $item_qty;
$cart_volume += $vol_by_qty;
}
return $cart_volume;
}
function check_if_full($candidate) {
$max_volume = 100;
$candidate = $product->id;
$a_volume = get_field('product_volume', $candidate);
$b_volume = get_cart_volume();
$check_volume = $a_volume + $b_volume;
if ($check_volume > $max_volume)
return true;
else
return false;
}
//Just a function to see if it's working on the cart page fur debugging purpose
add_action( 'woocommerce_check_cart_items', 'current_volume');
function current_volume() {
if (is_cart() || is_checkout()) {
global $woocommerce;
$current_volume = get_cart_volume();
wc_add_notice( sprintf( '<strong>Volume is %s.</strong>', $current_volume ),
'error' );
}
}
As Helgatheviking says, you get already, with woocommerce_is_purchasable hook in your hooked function, the $product object as 2nd argument. So you don't need and you have to remove global $woocommerce, $product; to make it work without throwing an error.
Your code is going to be:
add_filter('woocommerce_is_purchasable', 'if_room_is_purchasable', 10, 2);
function if_room_is_purchasable ($is_purchasable, $product){
$cart_volume = 0;
$max_volume = 100;
foreach( WC()->cart->get_cart() as $cart_item ){
$item_id = $cart_item['product_id'];
$terms = get_the_terms( $item_id, 'pa_pdt_volume');
foreach($terms as $key => $term){
$cart_volume += $term->name; // May be better with $term->slug;
}
}
$candidate_volume = $cart_volume + $product->get_attribute( 'pa_pdt_volume' );
if ( $candidate_volume > $max_volume )
return false;
else
return true;
}
This should work now without error.

How to find out product id from "woocommerce_cart_item_removed" hook?

I have code
add_action( 'woocommerce_cart_item_removed', 'after_remove_product_from_cart' );
function after_remove_product_from_cart($removed_cart_item_key, $instance) {
$product_id = $removed_cart_item_key['product_id'];
}
I want to find out a way to get product id or actual product object itself using $removed_cart_item_key. How do you do it? I cannot find any references, thanks.
should be something like this...
add_action( 'woocommerce_cart_item_removed', 'after_remove_product_from_cart', 10, 2 );
function after_remove_product_from_cart($removed_cart_item_key, $cart) {
$line_item = $cart->removed_cart_contents[ $removed_cart_item_key ];
$product_id = $line_item[ 'product_id' ];
}
Because it happen before the cart item is removed, you need to use woocommerce_remove_cart_item instead of woocommerce_cart_item_removed, to retrieve this product item.
add_action( 'woocommerce_remove_cart_item', 'after_remove_product_from_cart', 10, 2 );
function after_remove_product_from_cart($removed_cart_item_key, $cart) {
$product_id = $cart->cart_contents[ $removed_cart_item_key ]['product_id'];
}
See this source from helgatheviking
You can use this method to fetch the product id of item removed from cart, It worked for me
$product_id = $cart->cart_contents[$cart_item_key]['product_id'];

WooCommerce: Add product to cart with price override?

$replace_order = new WC_Cart();
$replace_order->empty_cart( true );
$replace_order->add_to_cart( "256", "1");
The above code add product 256 to the Cart 1 time. But the issue I'm having is that I want to be able to completely override the product price... as far as I can tell, the only thing I can do it apply a coupon to the Cart.
Is there a way to completely override the price to something totally custom?
Here is the code for overriding price of product in cart
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;
// for WooCommerce version 3+ use:
// $value['data']->set_price($custom_price);
}
}
Hope it will be useful...
You need to introduce an if statement for checking product id, in above 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
$target_product_id = 598;
foreach ( $cart_object->cart_contents as $value ) {
if ( $value['product_id'] == $target_product_id ) {
$value['data']->price = $custom_price;
}
/*
// If your target product is a variation
if ( $value['variation_id'] == $target_product_id ) {
$value['data']->price = $custom_price;
}
*/
}
}
Add this code anywhere and make sure that this code is always executable.
After adding this code, when you'll call:
global $woocommerce;
$woocommerce->cart->add_to_cart(598);
Only this product will be added with overridden price, other products added to cart will be ignored for overriding prices.
Hope this will be helpful.
I have tried all above code samples and latest woocommerce 3.0 is not support any of the above example. Use below code and working perfectly for me.
add_action( 'woocommerce_before_calculate_totals', 'add_custom_price' );
function add_custom_price( $cart_object ) {
$custom_price = 10; // This will be your custom price
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
$cart_item['data']->set_price($custom_price);
}
}
After release of woocommerce version 3.0.0 product price is update on add to cart using set_price($price) function. The example is given as below :
add_action( 'woocommerce_before_calculate_totals', 'mj_custom_price' );
function mj_custom_price( $cart_object ) {
$woo_ver = WC()->version;
$custom_price = 10;
foreach ( $cart_object->cart_contents as $key => $value )
{
if($woo_ver < "3.0.0" && $woo_ver < "2.7.0")
{
$value['data']->price = $custom_price;
}
else
{
$value['data']->set_price($custom_price);
}
}
}
Many Thanks
For the Wordpress and Woocommerce latest version,Please use like this
add_action( 'woocommerce_before_calculate_totals', 'add_custom_price' );
function add_custom_price( $cart_object ) {
foreach ( $cart_object->cart_contents as $key => $value ) {
$custom_price = 5;
$value['data']->set_price($custom_price);
}
}
For eveeryone that got here from Google. The above is now deprecated as i found out updating to WooCommerce 3.0.1.
Instead of the above you now need to use set_price instead of price
Here is an example:
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']->set_price = $custom_price;
}
}
I hope this helps people in the future :)
This is how i did it, first i add my custom price to cart_item_data witch can save custom data to cart items, then i use woocommerce_before_calculate_totals, loop the cart and add the previously added price.
function add_donation_to_cart() {
$cart_item_data = array('price' => $_REQUEST['donate_amount']);
$woocommerce->cart->add_to_cart( 5395, 1, '', array(), $cart_item_data);
}
add_action( 'woocommerce_before_calculate_totals', 'add_custom_price' );
function add_custom_price( $cart ) {
foreach ( $cart->cart_contents as $key => $value ) {
$value['data']->price = $value['price'];
}
}
With WooCommerce 2.5 I found this to be a 2-part process. The first step is to change the run-time display of pricing when added to the cart via the woocommerce_add_cart_item filter. The second part is to set the persistent session data which is read during checkout via the woocommerce_get_cart_item_from_session filter. This seems to be faster than hooking the calculate totals filters (such as woocommerce_before_calculate_totals) as they are run very frequently in WooCommerce.
More details here:
woocommerce change price while add to cart
To make it dynamic ( override price for each item in cart separately ), you need to save the override product price in session with cart item key as session key using woocommerce_add_to_cart hook.
by using these session values you can calculate correct Cart Total and make the altered price appear in the Order Item as well
You can use the following
add_filter( 'woocommerce_cart_item_price', 'kd_custom_price_message' );
function kd_custom_price_message( $price ) {
$textafter = ' USD';
return $price . $textafter;
}

Categories