Remove specific meta_data from product on woocommerce checkout - woocommerce

so - I have a couple of meta_data which I don't want to get transmitted. Actually with this snippet I am able to do this on woocommerce order view. As I have connected an ERP this won't work there.
add_filter( "woocommerce_order_item_get_formatted_meta_data","unset_specific_order_item_meta_data", 10, 2);
function unset_specific_order_item_meta_data($formatted_meta, $item){
foreach( $formatted_meta as $key => $meta ){
if( in_array( $meta->key, array("length", "surface", "amount") ) )
unset($formatted_meta[$key]);
}
return $formatted_meta;
}
Is there a way to achieve this with woocommerce_checkout_update_order_meta? So that the specific meta_data (like length, surface, amount) isn't in the order in the first place?

The woocommerce_checkout_update_order_meta hook is meant to save custom meta data, unsetting meta data doesn't work here. However you can use delete_meta_data like this:
add_action("woocommerce_checkout_update_order_meta", "custom_woocommerce_checkout_update_order_meta", 10, 2);
function custom_woocommerce_checkout_update_order_meta($order_id, $data) {
$order = wc_get_order($order_id);
if ($order) {
$meta_data_keys = array("length", "surface", "amount");
foreach ($meta_data_keys as $meta_data_key) {
$order->delete_meta_data($meta_data_key);
}
}
}

Related

Can´t get custom attribute on WooCommerce product

I try to save custom meta data after creating an order in WooCommerce. I try it with the woocommerce_new_order_item hook and in general it works. But I need to store a custom attribute from the ordered product..but I can´t get it.
What I tried:
add_action('woocommerce_new_order_item','add_basic_meta_for_new_quote',10,3); // add extra order metas
function add_basic_meta_for_new_quote($item_id, $values, $cart_item_key)
{
$angebotstext = get_post_meta($item_id, 'angebotstext', false);
wc_add_order_item_meta($item_id, 'angebotstext', $angebotstext);
}
or
add_action('woocommerce_new_order_item','add_basic_meta_for_new_quote',10,3); // add extra order metas
function add_basic_meta_for_new_quote($item_id, $values, $cart_item_key)
{
global $product;
$angebotstext = $product->get_attribute( 'pa_angebotstext' );
wc_add_order_item_meta($item_id, 'angebotstext', $angebotstext);
}
The attribute is saved here:
... and the result for meta_value is always empty, NULL or a:0:{}
Do you have any idea?
-----EDIT----
It worked with this way:
add_action('woocommerce_new_order_item','add_basic_meta_for_new_quote',10,3); // add extra order metas
function add_basic_meta_for_new_quote($item_id, $item, $order_id )
{
if ($order_id) {
$order = wc_get_order( $order_id );
}
# Iterating through each order items (WC_Order_Item_Product objects in WC 3+)
if ($order) {
foreach ( $order->get_items() as $item_id => $item_values ) {
// Product_id
$product_id = $item_values->get_product_id();
$product = wc_get_product($product_id);
$angebotstext = $product->get_attribute('Angebotstext');
if ( !empty($angebotstext) && $angebotstext != NULL ) {
wc_add_order_item_meta($item_id, 'Angebotstext' , $angebotstext);
}
}
}
}
BUT: This code adds my custom attribute two times as meta.... why that? 🤔
The main issue here is if the order contains multiple products, so you're going to face the issue of multiple updates for the same order.
To avoid that or to choose to deal with it the right way, here are some ideas:
To make this works for multiple products:
Changed this part:
wc_add_order_item_meta($item_id, 'Angebotstext _'.$product_id , $angebotstext);
This will add a new custom meta for each product by adding the product ID at the end of each meta_key and the meta_value will be the product unique value for that attribute.
so you're going to find this custom fields on the order page:
Angebotstext_123 = product attribute.
Angebotstext_485 = product
attribute. Angebotstext_951 = product attribute.
the '123', '485' are the product id and the meta value for that meta_key will be that product attribute.
To prevent it from updating multiple times:
if ($order) {
$angebotstext ='';
foreach ( $order->get_items() as $item_id => $item_values ) {
// Product_id
$product_id = $item_values->get_product_id();
$product = wc_get_product($product_id);
$angebotstext = $product->get_attribute('Angebotstext');
$prev_angebotstext = $angebotstext;
if ( !empty($angebotstext) && $angebotstext != NULL && $angebotstext != $prev_angebotstext ) {
wc_add_order_item_meta($item_id, 'Angebotstext' , $angebotstext);
}
}
}
"$prev_angebotstext" is the previous item attribute.
if ( !empty($angebotstext) && $angebotstext != NULL && $angebotstext != $prev_angebotstext )
This condition will check if the current product attribute is equal to the previous product attribute, if yes it will not update the order again.
Thank you!

How to update similar variation stock after purchase in Woocommerce

I struggle to find a way to update the stock of the similar variation of a product.
All my product have, say, a main variation, "Black" (30$) or "white" (250$) but sometimes they have another variation which is a "date start", so date_start:"12th june", date_start: "30th july", etc. I need to update the stock of the "date" variation when it's present (if there is no date, woocommerce update the main stock, no problem).
If someone choose "Black"+"12th June" I need the stock of "12 June" to be decreased also for "white"...
Before someone ask, "black" and "white" have different price per product... And "date" change also per product, that's why I need to use variation (and not addon attribute with a plugin).
Maybe someone have a better idea for organising this, but I try many other solution, always a caveat. This one seems the simpler, just have to find the good "hook" and "code"
Here is some pseudo code I made for this:
if(Product is sold):
VarSoldVariation = getSoldVariation(product->ID);
OtherVariationWithSameDate = getVariations (VarSoldVariation->pa_dates);
foreach(OtherVariationWithSameDate)updateStockVariation();
endif;
OK, it seems a little weird for not using metadata/attributes for this case, instead of variations. However, I've done more unusual stuff with variations than this one, so without judging your decision:
At first, you have to find a suitable action hook which fires after an order takes place. Some of them are:
woocommerce_order_status_$STATUS_TRANSITION[from]_to_$STATUS_TRANSITION[to]
woocommerce_order_status_$STATUS_TRANSITION[to] (e.g. woocommerce_order_status_completed)
woocommerce_order_status_changed
woocommerce_payment_complete
woocommerce_thankyou
Update 2:
I rewrite the code with some improvements:
In my initial answer, I used a WordPress get_posts function (which uses WP_Query) with a meta_query parameter, which you should definitely change to tax_query in this case, due to performance considerations. And we also know that it's better practice to use wc_get_products and WC_Product_Query where it's possible. However in this case it's not even needed to do a direct post query on db and it's possible to get the variations from get_available_variations method of WC_Product_Variable
Now it checks for quantity on order item and uses it on other date variations stock update.
Now it uses WC classes and functions wherever is possible, instead of direct updating of metadata, stock quantity, stock status, etc.
The Updated Code:
add_action('woocommerce_order_status_processing', 'sync_variations_stock');
/**
* Update same date variations on customer order place
* #param $order_id
* #return void
*/
function sync_variations_stock($order_id)
{
if (is_admin()) return; // make sure it's a user order and we aren't on admin dashboard
$order = wc_get_order( $order_id );
foreach( $order->get_items() as $item ) {
if ($item->get_type() !== 'line_item') continue; //if $item is not a product or variation
$order_variation_count = $item->get_quantity();
$order_product_id = $item->get_product_id();
$order_variation_id = $item->get_variation_id();
if ( ! $order_variation_id ) continue; // if the item isn't a variation
$order_variation = wc_get_product($order_variation_id);
$order_variation_attribs = $order_variation->get_variation_attributes();
if ( isset($order_variation_attribs['attribute_pa_date']) ) {
$current_date_attrib = $order_variation_attribs['attribute_pa_date'];
} else {
continue; // stop if the variation in the order doesn't have 'pa_date' attrib
}
$product = wc_get_product( $order_product_id );
$variations = $product->get_available_variations();
foreach ( $variations as $variation ) {
if ( $variation['variation_id'] == $order_variation_id ) {
continue; //if this variation is the one we have in our order
}
if ( ! isset( $variation['attributes']['attribute_pa_color'] ) || !isset( $variation['attributes']['attribute_pa_date'] ) ) {
continue; //if this variation does not have the color or date attrib
}
if ( $variation['attributes']['attribute_pa_date'] == $current_date_attrib ) {
/*
* wc_update_product_stock function sets the stock quantity if the variation stock management is enabled
* NOTE: This function may cause a negative stock even if the variation backorder is set to false
*/
wc_update_product_stock( $variation['variation_id'], $order_variation_count, 'decrease' );
wc_delete_product_transients($variation['variation_id']); // Clear/refresh the variation cache (optionally if needed)
}
}
}
}
Tested and It's Working!
My First Answer:
For this example, I will use the last one. However you should be careful about this hook, since it fires on every page load of the 'WC Thank You page'. It would be a good idea to use one of these hooks instead:
woocommerce_order_status_processing
woocommerce_order_status_completed
woocommerce_payment_complete
Final code would be something like this:
add_action('woocommerce_thankyou', 'sync_variations_stock');
function sync_variations_stock($order_id)
{
$order = wc_get_order( $order_id );
foreach( $order->get_items() as $item ){
$product_id = $item->get_product_id();
$product_variation_id = $item->get_variation_id();
if (!$product_variation_id) return; // if the item isn't a variation
$date_variation = get_post_meta( $product_variation_id, 'attribute_pa_date', true);
$color_variation = get_post_meta( $product_variation_id, 'attribute_pa_color', true);
if ( ! $date_variation && ! $color_variation ) return; //if the variation doesn't have date and color attributes
$args = array(
'post_parent' => $product_id,
'post_type' => 'product_variation',
'posts_per_page' => -1,
'meta_query' => array(
array(
'key' => 'attribute_pa_date',
'value' => $date_variation,
'compare' => '='
),
array(
'key' => 'attribute_pa_color',
'value' => $color_variation,
'compare' => '!='
)
),
);
$other_date_variations = get_posts($args);
if( is_array($other_date_variations) && !empty($other_date_variations) ){
foreach ($other_date_variations as $date_variation) {
// do your stock updating proccess here. (updateStockVariation() as you write in your code)
$variation_id = $date_variation->ID;
$date_variation_stock = (int) get_post_meta( $variation_id, '_stock', true);
if ($date_variation_stock > 0) { //to prevent backorders
$date_variation_stock = $date_variation_stock - 1;
update_post_meta($variation_id, '_stock', $date_variation_stock);
// if the variation is now out-of-stock, set it as so
if ($date_variation_stock === 0) {
update_post_meta($variation_id, '_stock_status', 'outofstock');
wp_set_post_terms( $variation_id, 'outofstock', 'product_visibility', true );
}
}
}
}
}
}
Note: You have to replace attribute_pa_date & attribute_pa_color to match your attribute slugs.
Update 1
There are other consideration in this topic. WC Variation stock quantities may be changed in other senarios and circumstances, such as order edit on dashboard, order refunds, direct product edit, etc. Before going live, you have to think about these too.
Whoever as I said, there may be other ways to do what you are trying to. But I couldn't understand your setup and the relation between your variations and the dates. I think it's better to ask a approach related question for this, on WB.SE
I also just made a small change. In your code, if people refresh the page, the stock of the other variation are decreased... As woocommerce will always decrease the stock of the bought variation first, I go get this stock variation number and update other one with it. So I'm sure everything stays the same. :)
Here is the code updated:
function sync_variations_stock($order_id)
{
if (is_admin()) return; // make sure it's a user order and we aren't on admin dashboard
$order = wc_get_order( $order_id );
foreach( $order->get_items() as $item ) {
if ($item->get_type() !== 'line_item') continue; //if $item is not a product or variation
$order_variation_count = $item->get_quantity();
$order_product_id = $item->get_product_id();
$order_variation_id = $item->get_variation_id();
if ( ! $order_variation_id ) continue; // if the item isn't a variation
$order_variation = wc_get_product($order_variation_id);
$order_variation_attribs = $order_variation->get_variation_attributes();
if ( isset($order_variation_attribs['attribute_pa_dates']) ) {
$current_date_attrib = $order_variation_attribs['attribute_pa_dates'];
//Get the stock of the current variation for updating others.
$new_stock = $order_variation->get_stock_quantity();
} else {
continue; // stop if the variation in the order doesn't have 'pa_dates' attrib
}
$product = wc_get_product( $order_product_id );
$variations = $product->get_available_variations();
foreach ( $variations as $variation ) {
if ( $variation['variation_id'] == $order_variation_id ) {
continue; //if this variation is the one we have in our order
}
if ( ! isset( $variation['attributes']['attribute_pa_admissible-emploi-quebec'] ) || !isset( $variation['attributes']['attribute_pa_dates'] ) ) {
continue; //if this variation does not have the color or date attrib
}
if ( $variation['attributes']['attribute_pa_dates'] == $current_date_attrib ) {
/*
* wc_update_product_stock function sets the stock quantity if the variation stock management is enabled
* NOTE: This function may cause a negative stock even if the variation backorder is set to false
*/
//wc_update_product_stock( $variation['variation_id'], $order_variation_count, 'decrease' );
//Update stock of other variation with the stock number of the one just bought
wc_update_product_stock( $variation['variation_id'], $new_stock, 'set' );
wc_delete_product_transients($variation['variation_id']); // Clear/refresh the variation cache (optionally if needed)
}
}
}
}

Save woocommerce ITEM meta data

I the cart i dynamically create some meta data for each item from a function located in functions.php.
IN CHECKOUT, I would like to save each meta data for each item of the current order.
In this way, once order completed, i would need to display these data in woo commerce admin and woocommerce email.
Basically, i need to save $date_start,$duration,$end_date
when the order is completed and receive this data in woocomerce admin and emails.
function get_infos_order ($date_start,$duration){
$end_date = strtotime('+ '.$duration, $date_start);
}
Could someone please give some advices how to do that please ?
Thanks a lot.
Muhammad Muazzam solution is OK, but woocommerce_add_order_item_meta is deprecated, you have to use wc_add_order_item_meta
function add_order_item_meta($item_id, $values) {
$key = ''; // Define your key here
$value = filter_input(INPUT_POST, 'key_name'); // Safer retrieval
wc_add_order_item_meta( $item_id, $meta_key, $meta_value);
}
add_action('woocommerce_add_order_item_meta', 'add_order_item_meta', 10, 2);
Source: https://docs.woocommerce.com/wc-apidocs/source-function-woocommerce_add_order_item_meta.html#428-433
Save them using this function:
function add_order_item_meta($item_id, $values) {
$key = ''; // Define your key here
$value = $_POST['key_name']; // Get your value here
woocommerce_add_order_item_meta($item_id, $key, $value);
}
add_action('woocommerce_add_order_item_meta', 'add_order_item_meta', 10, 2);

Wordpress variable, user created, woocommerce product

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;
}
}

Woocommerce add to cart with custom price

I've seen many examples of adding an item to the WC cart with a customer price, but none dynamically. I am trying to do in a shortcode function that receives a POST variables....
if (isset($_POST['wmnf_add_donation'])) {
global $woocommerce;
$cart_object = $woocommerce->cart;
$custom_price = ($_POST['donation_amount'] > 0 ? $_POST['donation_amount'] : 0);
$target_product_id = 65986;
$cart_object->add_to_cart($target_product_id, "1");
foreach ( $cart_object->cart_contents as $key => $value ) {
if ( $value['product_id'] == $target_product_id ) {
$value['data']->price = $custom_price;
}
}
}
This adds the item to the cart of course, but the price is zero and I realize I need to somehow save this array back to the WC cart data. Is this method even possible or can it only be done via a filter or action hook? If so, how can I save the changed array back to the cart contents or make it work to add the one item with its posted price? Any guidance greatly appreciated.
Thanks for that answer doublesharp, I was not able to get it to work as described because the form was posting to the page with my shortcode, which has my form, instead of posting directly to the cart. The $_POST was not being seen and the product ended up zero. I did find another approach, but having a problem using wp_redirect. I changed the above shortcode to this:
if (isset($_POST['wmnf_add_donation'])) {
global $woocommerce;
$custom_price = ($_POST['donation_amount'] > 0 ? $_POST['donation_amount'] : 0);
$target_product_id = 65986;
$_SESSION['donation_amount'] = $custom_price;
$woocommerce->cart->add_to_cart($target_product_id, "1");
wp_redirect( site_url() . '/gifts/swag-bag/');
}
Then I added the following filters to functions.php:
add_filter('woocommerce_get_price','donation_price', 10, 2);
add_filter('woocommerce_get_regular_price','donation_price', 10, 2);
add_filter('woocommerce_get_sale_price','donation_price', 10, 2);
function donation_price($price, $productd){
if($productd->id == '65986'){
$price = $_SESSION['donation_amount'];
}
return $price;
}
This does not work except when wp_redirect is commented out, hence, not redirecting. The above redirects to the cart, but its empty. If I comment out the wp_redirect line and then manually go to the cart, the product is there with my custom price. Actually, I would like to apply a custom price and redirect directly to the checkout page instead of the cart, if possible?
You can use the woocommerce_before_calculate_totals action hook to modify the contents of the cart, including the product prices.
add_action( 'woocommerce_before_calculate_totals', 'before_calculate_totals' );
function before_calculate_totals( $_cart ){
// loop through the cart_contents
foreach ( $_cart->cart_contents as $cart_item_key => &$item ) {
// you will need to determine the product id you want to modify, only when the "donation_amount" is passed
if ( $item['id'] == 65986 && isset( $_POST['donation_amount'] ) ){
// custom price from POST
$custom_price = $_POST['donation_amount'] > 0 ? $_POST['donation_amount'] : 0;
// save to the cart data
$item['data']->price = $custom_price;
// new versions of WooCommerce may require (instead of line above)...
// $item['data']->set_price($custom_price);
}
}
}

Resources