In WooCommerce I basically need to set a stock quantity of 10 for 4 separate products so when someone purchases 1 of these products the overall stock level goes down to 9.
The stock quantity for these products has to be linked so when 1 is purchased the quantity for all of them goes down.
I can't simply set 1 product up as a 'variable product' to achive this as each product has to be a 'simple product'.
Does anyone know of a plugin or way of setting up WooCommerce to achieve this?
You could try this custom function that is using the woocommerce_thankyou hook, to get first, the smallest stock value of the bought products in the customer order and then update all products with this stock value. This is untested, so you will have to give me some feed back on it.
Here is this code:
add_action( 'woocommerce_thankyou', 'updating_product_unified_stock', 10, 1 );
function unifying_product_stock($order_id){
$stock_updated = get_post_meta($order_id, 'stock_updated', true);
if(empty($stock_updated)):
// Inserting in the order meta data a custom field value to avoid repetition of this code,
// if the customer reload the "Order received" page…
update_post_meta($order_id, 'stock_updated', 'yes');
$products_stock_arr = array();
$products_ids = array();
// Getting the Order Object post data
$order = wc_get_order( $order_id );
foreach ( $order->get_items() as $item_id => $item ) {
$product_stock = get_post_meta($item_id, '_stock', true);
$products_stock_arr[] = $product_stock; // adding the product stock in the array
}
// Get the smallest stock value in the array of stock values
$new_stock_number = min($products_stock_arr);
// get all published simple products
$all_products = get_posts( array(
'post_type' => 'product',
'posts_per_page' => -1,
'post_status' => 'publish'
));
// Iterating through each published product
// UPDATING PRODUCTS WITH THE NEW STOCK VALUE:
foreach( $all_products as $product)
update_post_meta( $product_id, '_stock', $new_stock_number );
endif;
}
This code goes in function.php file of your active child theme (or theme) or also in any plugin php files.
Related
I am creating a 'Build Your Own' page. After selecting 4 products from the options, 1 main product gets added to the basket with 4 of the selected products being added as meta_data under that main product.
You can see below the main product with 4 selected products (IDs).
After paying for this item, I need to add each selected product to the order, so that they are within the order on the backend. I'm having to do it like this, because I need the stock of the selected product to go down, eventually pulling into a stock management system we use (veeqo)
Any help is appreciated. The code below allows me get the some meta_data for woocommerce_thankyou but I am not sure if it will work then...
add_action('woocommerce_thankyou', 'BuildYourOwn', 10, 1);
function BuildYourOwn( $order_id ) {
if ( !$order_id ){
return;
}
$firstTime = get_post_meta( $order_id, '_thankyou_action_done', true );
// Allow code execution only once
if( !$firstTime ) {
// Get an instance of the WC_Order object
$order = wc_get_order( $order_id );
$exItems = '';
// Loop through order items
foreach ( $order->get_items() as $item_id => $item ) {
//print_r($item);
// Get the product object
$product = $item->get_product();
// Get the product sku
$product_sku = $product->get_sku();
// Get the product name
$product_id = $product->get_name();
$extras = $item->get_formatted_meta_data('_', true);
$exItems.=$product_sku;
if(!empty($extras)){
$exItems.=$product_sku.' -';
foreach($extras as $extra){
$exItems.= ' ['.$extra->key.' : '. preg_replace("/[^A-Za-z0-9?#,.&%!\s]/","",$extra->value).'] ';
}
}
$exItems.="\n";
}
var_dump($exItems);
Maybe I worded the question wrong - But I figured it out:
I used the code below to get the meta_data which I then looped through and got individual item id and added it to the basket this way.
// Get the product meta data
$extras = $item->get_formatted_meta_data('_', true);
if ($product_id == 60023){
if(!empty($extras)){
$args = array(
'subtotal' => 0,
'total' => 0,
);
foreach($extras as $extra){
$mini_name = $extra->value;
$mini = get_page_by_title( $mini_name, OBJECT, 'product' );
$mini_id = $mini->ID;
$order->add_product( wc_get_product($mini_id), 1, $args); // Add Minis
}
}
}
The only slight issue is woocommerce_thankyou hook not firing if paypal users don't come back to the site after paying.
I am trying to send out a maximum of 3 free samples to customers with free shipping.
I have created some 0 priced product and set them in "samples" product category.
This specific products have option "sold individually" so customers can only have one of each sample.
I cannot figure out how to allow only a maximum of 3 samples products from this product category on cart.
Any help is appreciated.
This can be done easily using woocommerce_add_to_cart_validation action hook to limit customers to a maximum of 3 items from "sample" product category, this way:
add_action('woocommerce_add_to_cart_validation', 'max_3_items_for_samples', 20, 3 );
function max_3_items_for_samples( $passed, $product_id, $quantity ) {
// HERE define your product category ID, slug or name.
$category = 'clothing';
$limit = 3; // HERE set your limit
$count = 0;
// Loop through cart items checking for specific product categories
foreach ( WC()->cart->get_cart() as $cat_item ) {
if( has_term( $category, 'product_cat', $cat_item['product_id'] ) )
$count++;
}
if( has_term( $category, 'product_cat', $product_id ) && $count == $limit )
$count++;
// Total item count for the defined product category is more than 3
if( $count > $limit ){
// HERE set the text for the custom notice
$notice = __('Sorry, you can not add to cart more than 3 free samples.', 'woocommerce');
// Display a custom notice
wc_add_notice( $notice, 'error' );
// Avoid adding to cart
$passed = false;
}
return $passed;
}
Code goes in function.php file of your active child theme (or theme).
Tested and works.
The above solution does not work. 1) It allows you to initially add more than the limit the first time the product is added. I believe this is because the validation comes before the items are added to the cart, yet the function above loops through items that are already in the cart and 2) it lets the user enter as many of a product that they want, on the the /cart/ page because the woocommerce_update_cart_validation filter is not used.
I am trying to update wp_postmeta table for all products sale price.
I'm struggling with how to work this, due to both fields being meta_key / meta_value pairs in this wp_postmeta table.
How can I write a query that will update all '_sale_price' = 0 WHERE '_sale_price' > '_price'?
A different approach with a custom function that should do the same job. You will have to use this function just one time, and then remove it after the job is done (at first site load, the process will depend on number of products you have).
Here is the code:
function update_products_sale_price(){
$args = array(
'posts_per_page' => -1,
'post_type' => 'product',
'post_status' => 'publish'
);
// getting all products
$products = get_posts( $args );
// Going through all products
foreach ( $products as $key => $value ) {
// the product ID
$product_id = $value->ID;
// Getting the product sale price
$sale_price = get_post_meta($product_id, '_sale_price', true);
// if product sale price is not defined we give to the variable a 0 value
if (empty($sale_price))
$sale_price = 0;
// Getting the product sale price
$price = get_post_meta($product_id, '_price', true);
// udate sale_price to 0 if sale price is bigger than price
if ($sale_price > $price)
update_post_meta($product_id, '_sale_price', '0');
}
}
// Here the function we will do the job.
update_products_sale_price();
You can also embed the code function in a hook…
This code goes on function.php file of your active child theme or theme
This code is tested and fully functional
References:
WordPress Code Reference - Function get_post_meta()
WordPress Code Reference - Function update_post_meta()
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.
I'm creating a Plugin in WooCommerce and have a small issue with adding custom discounts to the CART / CHECKOUT page.
How can I apply custom discount to the cart without creating coupons?
Say I want to give some discount of 5 dollars on the cart page. How can I do that?
Below is my code from the plugin file where I have used a coupon to apply discount, but I want to add another custom discount without the use of coupon.
Action Hook in the plugin file :
add_action('woocommerce_calculate_totals',array(&$this,'cart_order_total_action'));
and its function in the plugin file is :
public function cart_order_total_action(){
if ( is_user_logged_in() ){
global $woocommerce;
global $current_user;
global $wpdb;
$u_id = $current_user->ID;
$table_name = $wpdb->prefix."woocommerce_customer_reward_ms";
$thetable2 = $wpdb->prefix . "woocommerce_customer_reward_cart_ms";
$table_name3 = $wpdb->prefix."woocommerce_customer_reward_points_log_ms";
$data = $wpdb->get_row("SELECT * from $table_name where id=$u_id");
$data2 = $wpdb->get_row("SELECT * from $thetable2");
/* Order Id goes here */
$orders=array();//order ids
$args = array(
'numberposts' => -1,
'meta_key' => '_customer_user',
'meta_value' => $current_user->ID,
'post_type' => 'shop_order',
'post_status' => 'publish',
'tax_query'=>array(
array(
'taxonomy' =>'shop_order_status',
'field' => 'slug',
'terms' =>'on-hold'
)
)
);
$posts=get_posts($args);
$orders=wp_list_pluck( $posts, 'ID' );
$order = $orders[0];
/* Order Id ends here */
if($data){
$user_points = $data->points;
$points_set = $data2->woo_pts_set;
$coupon_code = 'wooreward_discount';
if($user_points>=$points_set){
// this following Code is optional and can be removed......as there is no need of if statement here
if ( $woocommerce->cart->has_discount( $coupon_code ) ) {
/*$woocommerce->add_error( __('Coupon Code Already Applied.!!','woocommerce'));*/
return false;
}else{
$woocommerce->cart->add_discount(sanitize_text_field($coupon_code));
$woocommerce->add_message( __('Taxco925 Reward Discount Applied.!!','woocommerce'));
}
}else{
$woocommerce->add_error( __('Not Enough Taxco925 Points.!!','woocommerce'));
}
}else{
$woocommerce->add_error( __('You have have not earned any Taxco925 Points yet.!!','woocommerce'));
}
}
}
As you can see this line $woocommerce->cart->add_discount(sanitize_text_field($coupon_code));
adds my discount to the cart. But it uses coupon in the background to do so . Is there any way I can add a custom discount without the use of coupon.
add_action('woocommerce_checkout_order_processed','custom_disount',10,1);
function custom_disount($order_id){
$order = wc_get_order($order_id);
$order_items = $order->get_items();
foreach ($order_items as $order_item_key => $order_item) {
$product = new WC_Product((int) $order_item['product_id']);
$quantity = (int) $order_item['qty'];
$discount=($product->regular_price*30)/100; //30% disount.
wc_update_order_item_meta($order_item_key,'_line_total',($product->regular_price*$quantity)-($discount*$quantity));
}
}
You can add discount to each and every product in the cart using "woocommerce_get_discounted_price" hook.
For Eg.:
function filter_woocommerce_get_discounted_price( $price, $values, $instance ) {
//$price represents the current product price without discount
//$values represents the product object
//$instance represent the cart object
$discount = 300; // add custom discount rule , This is just an example
return ($price - $discount);
};
add_filter('woocommerce_get_discounted_price','filter_woocommerce_get_discounted_price', 10, 3 );
Maybe too late, but If someone have another solution tell me.
I use something like:
$discount = floatval(10);
if(!empty($discount) || $discount != 0){
$discount *= -1; // convert positive to negative fees
$woocommerce->cart->add_fee('discount', $discount, true, '' ); // add negative fees
}
If you use paypal standard payment, you got an error because you can't submit a product with negative pricing.
You just need to edit the paypal woocommerce plugin to pass this value.
But other Payment method is ok!
Best Regards,
Add fee with negative value will not produce the right total fee.
Tax is added on the fee amount resulting in higher total fee than expected.
You need to create a "coupon" and apply it to the cart before you create the order from the cart (it will not calculate right if you apply it on $order directly). Then recalculate the cart->total and finally create an order from the cart, after you have saved the order you can remove the "dynamic" created "coupon" if you want. You can create dynamic coupons with any dynamic $value and of any type (fixed, percent etc etc).
This is the only way to add discounts in woo3+.
Fee is doing it wrong when it comes to discounts. Also woo say about fee "Not use negative values here!".
I guessed you wanted some example?
here....
<?php
// this code inside wordpress and with woo3+ of course......
// you have to figure out the rest yourself, how to implement it. but here it is...
$order_data = array (
'status' => 'on-hold' // or whatever order staus
// can have more data if need here...
);
// below creates a coupon with discount_type = fixed_cart, default.
$coupon = array (
'post_title' => 'coupon_discount',
'post_status' => 'publish',
'post_type' => 'shop_coupon'
);
// can be modified with update_post_meta discount_type = percent and so on....
$dynamic_discount = 20; // yes, just a number can be from another dynamic input source....
$new_coupon_id = wp_insert_post( $coupon ); // add the coupon to the cart
add_post_meta( $new_coupon_id , 'coupon_amount' , $dynamic_discount , true ); // add the "discount" value ($dynamic_discount)..... depends on discount_type... in this case fixed_cart
WC()->cart->add_to_cart( 2122 , 2 ); // add products, product_id , quantity ..... can be in a loop.
WC()->cart->add_discount( 'coupon_discount' ); // APPLY THE COUPON WITH DISCOUNT -> This is the trick....
WC()->cart->calculate_totals(); // do some math on the "cart"
WC()->checkout(); // yes, checkout the "cart", now with the discount....
$order_id = WC()->checkout()->create_order( $order_data ); // basic order data, see the top in this script.. get new created order_id.
$order = wc_get_order( $order_id ); // get the order...
// can do more with $order here if want, but NOT any coupons... it just not work in $order as is...
$order->calculate_totals(); // math
WC()->cart->empty_cart(); // empty cart....
$order->save(); // save the order...
wp_delete_post( $new_coupon_id , true ); // IF you want to delete the "dynamic" coupon created above... up 2 u, if not you will end up with a lot of coupons
// sorry, a bad example, uggly code, but at least it work.... :)
// btw, i like Pattaya, send bitcoins :)
// Again, sorry for uggly code...
?>