I created a custom product type for WooCommerce. With this product type it is possible to connect an other product that is exist by ID.
WooCommerce reduce the stock quantity automatically if an order is placed and the payment is successful.
For example I added a product with ID 4082 to the cart with a quantity from 3.
After place this order WooCommerce updated the stock from product 4082 with -3.
Ok back to my custom product type. As I said it is possible to connect another product by ID.
For example I connect product 4082 with product ID 10988.
If a customer add product 4082 to the cart and placed the order I want reduce the stock quantity from product ID 10988 and not from 4082.
<?php
add_action('woocommerce_checkout_order_processed', 'stocktest');
function stocktest($order_id){
$order = wc_get_order( $order_id );
$order_item = $order->get_items();
foreach( $order_item as $product ) {
//for the topic I programmed the IDs hardcoded
if($product->ID == '4082'){
wc_update_product_stock( 10998, $product['qty'], 'decrease', '' );
}
}
}
?>
I tried the code above and the stock from ID 10998 is correctly decreased but also the stock from ID 4082 is decreased.
Do I use the wrong hook? And how can I make the function correctly?
Hope somebody can help me with this.
Thanks a lot
Does wc_update_product_stock( 4082, $product['qty'], 'increase', '' ); not work?
That way, the stock should be adjusted for the 4082 product.
Also, there might be a potential issue with your snippet. The $product variable refers to the product object of 4082 product, not the 10998 product.
Maybe try something like this?
<?php
add_action('woocommerce_checkout_order_processed', 'stocktest');
function stocktest($order_id){
$order = wc_get_order( $order_id );
$order_item = $order->get_items();
foreach( $order_item as $product ) {
//for the topic I programmed the IDs hardcoded
if($product->ID == '4082'){
$connected_qty = get_post_meta( 10988, '_stock', true );
wc_update_product_stock( 10998, $connected_qty, 'decrease', '' );
wc_update_product_stock( 4082, $product['qty'], 'increase', '' );
}
}
}
?>
Instead of using the custom field query, you could use the WC_Product class:
$connected_product = wc_get_product(10998);
$connected_qty = $connected_product->get_stock_quantity();
I did some research on your question. WooCommerce called a lot of functions to update the stock af order / payment. If an order is placed the items become an status reduce_stock yes or no.
If is yes go further and update the stock of this item. After this WooCommerce created the e-mails and ordernotes.
All this functions worked together please be careful with changing the code in the file wc-stock-functions.php
If you want change and try something do this for example on a local testserver.
This are some functions in the file
<?php
wc_maybe_reduce_stock_levels();
wc_maybe_increase_stock_levels();
wc_reduce_stock_levels();
wc_trigger_stock_change_notifications();
?>
Related
I have a website with few WooCommerce products, and the product page is created with Elementor Pro. I want users to be able to click Add to Cart and buy the product normally.
But then when the user goes back to the product page (after buying the product), they would not see the the Add to Cart button (if will be hidden for those that purchased the product) and they will see another extra section.
How can I do this?
I also have Crocoblock and JetEngine that have a Dynamic Visibility functionality built in Elementor.
The easiest way to achieve this would be by creating a child theme. The child theme should contain 2 added functionalities.
For this to fully work you will need all users to be registered and NOT have checkout as a guest enabled. Also, this will only work for new orders. If you want to add compatibility for old orders you'd have to create a script that sets the meta value for all users that have purchased this product before the code was implemented.
First of we create a function that adds metadata to the purchasing user account if the product was in the cart. Add the following code to your child theme functions.php
add_action( 'woocommerce_thankyou', 'custom_add_user_meta');
function custom_add_user_meta( $order_id ){
$user_id = get_current_user_id();
$order = wc_get_order( $order_id );
$items = $order->get_items();
foreach ( $items as $item_id => $item ) {
$product_id = $item->get_variation_id() ? $item->get_variation_id() : $item->get_product_id();
if ( $product_id == 12345 ) {
add_user_meta( $user_id, '_product_xyz_purchased', "true");
}
}
}
Now we add the following code to the same functions.php file to render additional content on the single product page of WooCommerce. NOTE: This step is for a more complete overview that can be implemented outside of Crocoblocks/Jetegine. The following step can be skipped because Jetengine allows setting conditions based on user_meta
add_action("woocommerce_after_single_product", 'render_additional_content', 50);
function render_additional_content() {
$user_id = get_current_user_id();
$product_purchased = get_user_meta( $user_id, '_product_xyz_purchased', true);
if($product_purchased == "true"){
echo "here you can put the HTML you want to display";
}
}
I am using woocommerce bookings plugin and I am looking for a solution for customers who have already booked a time slot to be able to add more time to their booking. In theory, it would be great if they could just edit their booking and pay the difference but from what I can tell that would require an extensive amount of custom code which I am not capable of doing. So as an alternative solution, perhaps I can create a unique button on the customer's order detail page at /my-account/booking-details/OrderID/ where they can go to a specific product based on the product of that order.
I have created a custom hook on the /order-details.php file called woocommerce_extra_time_button and added the below code to my theme's function.php file but I am not able to pull the product URL as I thought I would.
add_action( 'woocommerce_extra_time_button', 'booking_extratime_button', 10, 3 );
function booking_extratime_button( $item_id, $item ){
$url = get_permalink( $item['product_id'] ) ;
return ''. $item_id .'';
}
You can find the product detail from the order of current user and use inside your custom hook. for example :
$order = wc_get_order( $order_id );
$items = $order->get_items()
foreach ( $items as $item ) {
$product_name = $item->get_name();
$product_id = $item->get_product_id();
$product_variation_id = $item->get_variation_id();
}
I need to update the order item meta in a woocommerce oder on checkout page or while woocommerce creates the order.
I'm using the plugin visual product configurator and it is not passing the right quantity of some items of the order to woocommerce order meta, especially when I use multiple variations on the same product.
Is there a hook for me to use to update the item quantity for a certain order item and how can I use it?
The plugin returns me an array with all the cart information and I can only check if an item of the order appears multiple times - if yes I need to change the quantity of that item to that number in the woocommerce order/database.
I was thinking of adding the following hook to my functions.php
add_action('woocommerce_checkout_create_order', 'change_qty', 1,1);
function change_qty($item_qty){
foreach($item_qty as $qty) {
$qty['product_id'] = $id;
$qty['qty'] = $new_qty
$order->update_meta_data('quantity', $new_qty, $id)
}
}
Whereas $item_qty is be an multi-dimensional array containing the item_ids and adjusted quantities.
Another problem I'm facing is that I dont know when I need to call that function because I get the array from the plugin on the checkout page, but I think WooCommerce has not yet created an order at that moment?
The result should be an adjusted item quantity in the woocommerce order summary in the backend.
To update the order item quantity, you can use WC_Order_Item_Product set_quantity() method.
The correct hook to update order items (line items) is woocommerce_checkout_create_order_line_item action hook, that is triggered during order creation, before data is saved to databased.
add_action('woocommerce_checkout_create_order_line_item', 'change_order_line_item_quantity', 10, 4 );
function change_order_line_item_quantity( $item, $cart_item_key, $cart_item, $order ) {
// Your code goes below
// Get order item quantity
$quantity = $item->get_quantity();
$new_qty = $quantity + 2;
// Update order item quantity
$item->set_quantity( $new_qty );
}
The function arguments (variables) are defined and usable:
$item is the WC_Order_Item_Product Object (not saved yet to database)
$cart_item_key is the related cart item key
$cart_item is the related cart item data
$order is the WC_Order Object (not saved yet to database)
Related:
Get Order items and WC_Order_Item_Product in Woocommerce 3
WC_Order_Item_Product Class and methods API Documentation
WC_Checkout and woocommerce_checkout_create_order_line_item action hook located in the create_order_line_items() method
This can help you (we hook into payment completed notification from the payment provider). If you want to update the _qty just after the order was created, I can change my function. But for now I would update it only when the payment was successful.:
/**
* Update order item qty after payment successful
*/
add_filter( 'woocommerce_payment_complete_order_status', 'update_order_item_qty', 10, 2 );
function update_order_item_qty( $order_status, $order_id ) {
//Get the order and items
$order = new WC_Order( $order_id );
$items = $order->get_items();
//New qty
$new_qty = 0;
foreach ( $items as $item_id => $item_data ) {
update_meta_data( '_qty', $new_qty, $item_id );
}
}
Please try if this is what you'r looking for.
I need to change the item price in a woocommerce order but everything I found is to changing the price in the cart but this is not what I need because I need to change after the checkout process.
Does somebody can give me a clue on how to do that?
You need to use the new CRUD setters methods introduced with Woocommerce 3:
For order object you will use WC_Order methods,
For order "line item" you will use WC_Order_Item_Product methods,
For both of them you could be also use some WC_Data methods like save()…
Here is a working basic example with a static price and a static order ID:
$order_id = 809; // Static order Id (can be removed to get a dynamic order ID from $order_id variable)
$order = wc_get_order( $order_id ); // The WC_Order object instance
// Loop through Order items ("line_item" type)
foreach( $order->get_items() as $item_id => $item ){
$new_product_price = 50; // A static replacement product price
$product_quantity = (int) $item->get_quantity(); // product Quantity
// The new line item price
$new_line_item_price = $new_product_price * $product_quantity;
// Set the new price
$item->set_subtotal( $new_line_item_price );
$item->set_total( $new_line_item_price );
// Make new taxes calculations
$item->calculate_taxes();
$item->save(); // Save line item data
}
// Make the calculations for the order and SAVE
$order->calculate_totals();
Then you will have to replace the static price by your submitted new price in your custom page, which is not so simple, as you will need to target the correct $item_id…
Thank you very much, I spent 4 hours looking for how to change the quantity of the product in the order and based on your code (I rewrote the necessary part) I finally got it! that's if someone needs to change the quantity product in the order `
$order = wc_get_order( $_POST['orderID'] );
foreach( $order->get_items() as $item_id => $item ){
$product = $item->get_product();
$product_price = (int) $product->get_price(); // A static replacement product price
$new_quantity = (int) $_POST['productQty'] // product Quantity
// The new line item price
$new_line_item_price = $product_price * $new_quantity;
// Set the new price
$item->set_quantity($_POST['orderQty']);
$item->set_subtotal( $new_line_item_price );
$item->set_total( $new_line_item_price );
// Make new taxes calculations
$item->calculate_taxes();
$item->save(); // Save line item data
}
// Make the calculations for the order and SAVE
$order->calculate_totals();`
#LoicTheAztec
Completing an automated woo-commerce Payment by manually inputting an identifier
The shopper goes online, creates an account , adds a payment method, and fills their cart .
We hold the amount plus 15% when they check out.
woocommerce sends order details to the delivery team that takes the gig .
They go to the store and shop
After checking out at the physical store, the new invoice total is uploaded to woo-commerce via the shopping app .
This manually entered amount will be the IDENTIFIER in the stripe that TRIGGERS the order completion
I tried using the 'Hide if sold out' option in Woocommerce. But I am using a different plugin to display my products (GridFX Masonry Gallery) and it is still showing the sold out products. Is there a way to change the product to draft when the last item has been purchased and is sold out? Is there a snippet to do this?
The previous answer by Maha Dev cannot work.
In a theme functions file the get_stock_quantity() method doesn't exist unless you call the product data using wc_get_product.
Using set_post_type will change the actual type of the post, not it's status, you need to use wp_update_post and set the post status.
This is the code I ended up using:
/*
* Unpublish products after purchase
*/
function lbb_unpublish_prod($order_id) {
$order = new WC_Order( $order_id );
$all_products = $order->get_items();
foreach ($all_products as $product){
$product_object = wc_get_product($product['product_id']);
// This will only work if stock management has been enabled
if( ! $product_object->get_stock_quantity() ) {
wp_update_post(array(
'ID' => $product['product_id'],
'post_status' => 'draft'
));
}
}
}
add_action( 'woocommerce_thankyou', 'lbb_unpublish_prod', 10, 1 );
Better way is to set product stock (1 in your case). Select 'Hide out of stock products'. It will hide out of stock products. But if you actually want to hide ordered products then see my code below:
//functions.php
add_action( 'woocommerce_thankyou', 'your_func', 10, 1 );
function your_func($order_id) {
$order = new WC_Order( $order_id );
$all_products = $order->get_items();
foreach ($all_products as $product){
// This will only work if stock management has been enabled
if( ! $product->get_stock_quantity() )
set_post_type ($product['product_id'], 'draft');
}
}
This will hide all the products added in the cart for order. You can customize this function as you want.