I would like to set a 50% discount when and only when the admin adds a product to the existing order via the admin area.
I've tried with this:
function admin_set_custom_price( $item, $item_id ) {
$item->set_subtotal( ( ( ( 100 - 50 ) * $item->get_subtotal() ) / 100 ) );
$item->set_total( ( ( ( 100 - 50 ) * $item->get_total() ) / 100 ) );
$item->apply_changes();
$item->save();
return $item;
}
add_filter( 'woocommerce_ajax_order_item', 'admin_set_custom_price', 10, 2 );
And the result is that when the item is added the price is the original price.
If I then simply refresh the page, it shows the prices with 50% discount.
What else do I need to do show the price with discount right away when it is added without the need to refresh the page?
Looks like something is overriding it as it is saved I would guess since the price is correct on refresh.
Talking about simple/variation product types.
So I've used this hook instead:
woocommerce_ajax_added_order_items
And then in the function:
foreach ( $order->get_items() as $order_item_id => $order_item_data ) {
// Set custom price.
}
Seems to work just fine.
UPDATE:
It turns out that the above hook only get the last item in case you want to add multiple items at once.
A better hook which is executed in the loop ONLY for the items added via ajax (not affecting the existing ones) is:
woocommerce_ajax_add_order_item_meta
Then in the loop you can do a loop over the items in the cart and if the cart id matches, you can change the product.
function update_order_prices_on_admin_ajax( $item_id, $item, $order )
foreach ( $order->get_items() as $order_item_id => $order_item_data ) {
if ( $order_item_id == $item_id ) {
// Do changes here.
// Runs this after making a change to $order_item_data
$order->apply_changes();
$order->save();
}
}
}
add_action( 'woocommerce_ajax_add_order_item_meta', 'update_order_prices_on_admin_ajax', 99, 3 );
Related
So I need a little help. I have a wooocommerce webshop where there are two main products, and then 3 products, that visitors only will be able to buy, if one of the two main products are in the cart. I have found some code, that works like this: If I remove a main product, the additional product will be removed automatic, which it should. My problem is, i dont know how to modify the code, so it includes 3 products id's (one id, for each of the additional products). I have tried plenty of things, but cant get it to work. Can you maybe help me? This it the code I have (which now contains one product id, and it should contain three id's):
// Add to cart validation for the discounted product
add_filter( 'woocommerce_add_to_cart_validation', 'check_specific_discounted_product', 10, 3 );
function check_specific_discounted_product( $passed, $product_id, $quantity ) {
// Settings
$discounted_product_id = 3008;
if( WC()->cart->is_empty() && $discounted_product_id == $product_id ) {
wc_add_notice( __("This product can't be purchased alone."), 'notice' );
return false;
}
return $passed;
}
// Removing the discounted product if it's alone in cart
add_action( 'woocommerce_before_calculate_totals', 'conditionally_remove_a_discounted_product' );
function conditionally_remove_a_discounted_product( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
// Settings
$discounted_product_id = 3008;
// Initializing variables
$discounted_item_key = false;
// Loop through cart items (first loop)
foreach ( $cart->get_cart() as $cart_item_key => $cart_item ){
// When free productis is cart
if ( in_array( $discounted_product_id, array($cart_item['product_id'], $cart_item['variation_id']) ) ) {
$discounted_item_key = $cart_item_key;
}
// if any other product is in cart: EXIT
else {
return;
}
}
// When the discounted product is alone in cart, remove it
if( $discounted_item_key ) {
// display notice on removal (optional)
wc_clear_notices();
wc_add_notice( __("The discounted product can't be purchased alone and has been removed."), 'notice' );
$cart->remove_cart_item( $discounted_item_key ); // Remove
}
}
I have tried to modify the code like this:
$discounted_product_id = 3004, 3008, 3157;
And like this:
$discounted_product_id = 3004;$discounted_product_id = 3008;$discounted_product_id = 3157;
Also like this:
$discounted_product_id = array('3004','3008','3157');
But it's not working :(
I would like to add a function that is triggered every time that the stock quantity of a product will be changed in the admin product page, such that this function will not allow any reduce of the stock value - but only increase.
This is to prevent an admin user to reduce the stock quantity of the products.
Of course, this function should not be triggered if a product will be in an order, since then of course I would like the stock quantity to be reduced.
I tried the following function in the functions.php but unfortunately did not work.
Since I'm new to woocommerce and php, any ideas that could provide a solid solution to the problem?
// get old and new product stock quantity
function get_old_and_new_product_quantity_stock( $sql, $product_id_with_stock, $new_stock, $operation ) {
$product = wc_get_product( $product_id_with_stock );
$old_stock_quantity = $product->get_stock_quantity();
$new_stock_quantity = $new_stock;
echo $old_stock_quantity, $new_stock_quantity;
if ($new_stock_quantity < $old_stock_quantity) {
$new_stock = $old_stock_quantity;
$new_stock_quantity = $old_stock_quantity;
}
return $sql;
}
add_filter( 'woocommerce_update_product_stock_query', 'get_old_and_new_product_quantity_stock', 10, 4 );
You can use the update_post_meta action hook to check if the new value is less than the previous value and display error message.
This will work for quick edit and for product edit page. But the wp_die on product page will look bad so use the javascript to prevent submitting on product edit page (there was another question about it yesterday)
Be sure to test this snippet and create some orders that will reduce the stock automatically. I added is_admin() check but please do a good test.
add_action( 'update_post_meta', 'prevent_reducing_stock_metadata', 10, 4 );
function prevent_reducing_stock_metadata( $meta_id, $post_id, $meta_key, $meta_value ) {
// Check if the meta key is _stock and the new value is less than the previous value
if ( '_stock' == $meta_key && $meta_value < get_post_meta( $post_id, '_stock', true ) ) {
// Check if this is an update from the WordPress admin area
if ( is_admin() ) {
wp_die( __( 'Error: You cannot reduce the stock level for this product.' ), 'error' );
}
}
}
I am trying to change product price in cart using the following function:
add_action( 'woocommerce_before_shipping_calculator', 'add_custom_price'
);
function add_custom_price( $cart_object ) {
foreach ( $cart_object->cart_contents as $key => $value ) {
$value['data']->price = 400;
}
}
It was working correctly in WooCommerce version 2.6.x but not working anymore in version 3.0+
How can I make it work in WooCommerce Version 3.0+?
Thanks.
Update 2021 (Handling mini cart custom item price)
With WooCommerce version 3.0+ you need:
To use woocommerce_before_calculate_totals hook instead.
To use WC_Cart get_cart() method instead
To use WC_product set_price() method instead
Here is the code:
// Set custom cart item price
add_action( 'woocommerce_before_calculate_totals', 'add_custom_price', 1000, 1);
function add_custom_price( $cart ) {
// This is necessary for WC 3.0+
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
// Avoiding hook repetition (when using price calculations for example | optional)
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
// Loop through cart items
foreach ( $cart->get_cart() as $cart_item ) {
$cart_item['data']->set_price( 40 );
}
}
And for mini cart (update):
// Mini cart: Display custom price
add_filter( 'woocommerce_cart_item_price', 'filter_cart_item_price', 10, 3 );
function filter_cart_item_price( $price_html, $cart_item, $cart_item_key ) {
if( isset( $cart_item['custom_price'] ) ) {
$args = array( 'price' => 40 );
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).
This code is tested and works (still works on WooCommerce 5.1.x).
Note: you can increase the hook priority from 20 to 1000 (or even 2000) when using some few specific plugins or others customizations.
Related:
Set cart item price from a hidden input field custom price in Woocommerce 3
Change cart item prices based on custom cart item data in Woocommerce
Set a specific product price conditionally on Woocommerce single product page & cart
Add a select field that will change price in Woocommerce simple products
With WooCommerce version 3.2.6, #LoicTheAztec's answer works for me if I increase the priority to 1000.
add_action( 'woocommerce_before_calculate_totals', 'add_custom_price', 1000, 1);
I tried priority values of 10,99 and 999 but the price and total in my cart did not change (even though I was able to confirm with get_price() that set_price() had actually set the price of the item.
I have a custom hook that adds a fee to my cart and I'm using a 3rd party plugin that adds product attributes. I suspect that these WooCommerce "add-ons" introduce delays that require me to delay my custom action.
In WooCommerce, I understand well that woocommerce_get_order_item_totals filter kook is used to customize order total rows like reordering them.
add_filter( 'woocommerce_get_order_item_totals', 'custom_order_of_from_order_table', 10, 2 );
function woocommerce_get_order_item_totals( $total_rows, $order ) {
// code here
return $total_rows;
}
I have tried to reorder the subtotal over the total, and the payment method below the total without success on WooCommerce ThankYou page. My PHP knowledge is very limited and I appreciate any help.
How to customize total rows from order table, reordering them on WooCommerce thankyou page?
The following will reorder items totals as desired on Woocommerce thankyou (order received) page only:
add_filter( 'woocommerce_get_order_item_totals', 'reordering_order_item_totals', 10, 3 );
function reordering_order_item_totals( $total_rows, $order, $tax_display = '' ){
// Only on "order received" thankyou page
if ( ! is_wc_endpoint_url('order-received') )
return $total_rows;
$sorted_items_end = array('cart_subtotal', 'order_total', 'payment_method');
$sorted_total_rows = array(); // Initializing
// Loop through sorted totals item keys
foreach( $sorted_items_end as $item_key ) {
if( isset($total_rows[$item_key]) ) {
$sorted_total_rows[$item_key] = $total_rows[$item_key]; // Save sorted data in a new array
unset($total_rows[$item_key]); // Remove sorted data from default array
}
}
return array_merge( $total_rows, $sorted_total_rows); // merge arrays
}
Code goes in functions.php file of your active child theme (or active theme). Tested and works.
To make that work everywhere for customer orders and email notifications, just remove:
// Only on "order received" thankyou page
if ( ! is_wc_endpoint_url('order-received') )
return $total_rows;
Basically I am interested in using woocommerce to sell a product . This product is a Print Order of a external printing service that I have implemented in a brand new plugin.
What I want now is after the order, is to be able to put that "order" in the buy cart, and buy it normally as just another woocommerce product.
The product has to be created on the fly, manually by a way of some function that I can use to create a product during a certain workflow point.
Can you help me to find a solution?
Using woocommerce or not!
What i understand from your requirement/comments is that you want to be able to dynamically create a product, which is bad idea. But here is my suggestion.
Lets say you have a simple product called "Print Job" with a price of $1 and with an id of 10. And i am supposing that your external printing service would send back a response with the order and the price of printing, lets say $64.
Once you recieve the response you can simply call add_product_to_cart(10), this will automatically add the print job product to your cart.
/************* functions.php ***************/
function add_product_to_cart($product_id) {
if ( ! is_admin() ) {
$found = false;
//check if product already in cart
if ( sizeof( WC()->cart->get_cart() ) > 0 ) {
foreach ( WC()->cart->get_cart() as $cart_item_key => $values ) {
$_product = $values['data'];
if ( $_product->id == $product_id )
$found = true;
}
// if product not found, add it
if ( ! $found )
WC()->cart->add_to_cart( $product_id );
} else {
// if no products in cart, add it
WC()->cart->add_to_cart( $product_id );
}
}
}
You also need to add this function in your functions.php, this function will override the price of the product i.e "Print Job" with the actual price of $64.
/******************* Functions.php ****************/
add_action( 'woocommerce_before_calculate_totals', 'override_printing_price' );
function override_printing_price( $cart_object ) {
$custom_price = 64;
foreach ( $cart_object->cart_contents as $key => $value ) {
$value['data']->price = $custom_price;
}
}