Automatically add custom order shipping item meta data in WooCommerce - php

I know how to add a meta on the order shipping method :
$shippingMethodItem->update_meta_data('num_packages', 0);
But I want to add/update this meta automatically when the order is placed OR when someone add/edit order items manually.
I already tried with no result :
add_action( 'woocommerce_checkout_create_order_shipping_item', [$this, 'actionCheckoutCreateOrderShippingItem'] );
...
public function actionCheckoutCreateOrderShippingItem ($shippingMethodItem, $package_key, $package, $order)
{
$shippingMethodItem->update_meta_data( 'num_packages', 0);
$shippingMethodItem->save();
}

The following will add custom specific order "shipping" item meta data on orders via checkout and/or manual admin orders too:
// For new orders via checkout
add_action( 'woocommerce_checkout_create_order_shipping_item', 'action_checkout_create_order_shipping_item', 10, 4 );
function action_checkout_create_order_shipping_item ( $item, $package_key, $package, $order ){
$item->update_meta_data( 'num_packages', 0 );
}
// For manual admin orders
add_action( 'woocommerce_saved_order_items', 'action_saved_order_items_callback', 10, 2 );
function action_saved_order_items_callback( $order_id, $items ) {
if ( isset( $items['shipping_method_id'] ) ) {
foreach( $items['shipping_method_id'] as $item_id ) {
$num_packages = wc_get_order_item_meta($item_id, 'num_packages');
if ( empty($num_packages) || $num_packages != 0 ) {
wc_update_order_item_meta( $item_id, 'num_packages', 0 );
}
}
}
}
Code goes in functions.php file of the active child theme (or active theme). Tested and works.
To use this code in a plugin with a class (OOP), add the following inside the constructor function:
// For checkout order
add_action( 'woocommerce_checkout_create_order_shipping_item', [$this, 'action_checkout_create_order_shipping_item'], 10, 4 );
// For checkout order
add_action( 'woocommerce_saved_order_items', [$this, 'action_saved_order_items_callback'], 10, 2 );
And the following outside the constructor function:
public function action_checkout_create_order_shipping_item ( $item, $package_key, $package, $order ){
$item->update_meta_data( 'num_packages', 0 );
}
public function action_saved_order_items_callback( $order_id, $items ) {
if ( isset( $items['shipping_method_id'] ) ) {
foreach( $items['shipping_method_id'] as $item_id ) {
$num_packages = wc_get_order_item_meta($item_id, 'num_packages');
if ( empty($num_packages) || $num_packages != 0 ) {
wc_update_order_item_meta( $item_id, 'num_packages', 0 );
}
}
}
}

Related

Display ACF custom field in WooCommerce product variations

I successfully created a ACF custom field in WooCommerce within my product variations in the backend. The field is properly shown within each variation.
But after editing this or other fields within the variations I can't save the whole variations tab anymore. The loading/saving indicator circle continues to rotate indefinitely. And the custom field is not showing in the single product variation in frontend.
What I did, was to add the following code to my functions.php:
/* ACF filter for Variations */
// Render fields at the bottom of variations - does not account for field group order or placement.
$GLOBALS['wc_loop_variation_id'] = null;
function is_field_group_for_variation($field_group, $variation_data, $variation_post) {
return (preg_match( '/Variation/i', $field_group['title'] ) == true);
}
add_action( 'woocommerce_product_after_variable_attributes', function( $loop_index, $variation_data, $variation_post ) {
$GLOBALS['wc_loop_variation_id'] = $variation_post->ID;
foreach ( acf_get_field_groups() as $field_group ) {
if ( is_field_group_for_variation( $field_group, $variation_data, $variation_post ) ) {
acf_render_fields( $variation_post->ID, acf_get_fields( $field_group ) );
}
}
$GLOBALS['wc_loop_variation_id'] = null;
}, 10, 3 );
add_action( 'woocommerce_save_product_variation', function( $variation_id, $loop_index ) {
if ( !isset( $_POST['acf_variation'][$variation_id] ) ) {
return;
}
$_POST['acf'] = $_POST['acf_variation'][$variation_id];
acf()->input->save_post( $variation_id );
}, 10, 2 );
add_filter( 'acf/prepare_field', function ( $field ) {
if ( !$GLOBALS['wc_loop_variation_id'] ) {
return $field;
}
$field['name'] = preg_replace( '/^acf\[/', 'acf_variation[' . $GLOBALS['wc_loop_variation_id'] . '][', $field['name'] );
return $field;
}, 10, 1);
//add ACF rule
add_filter('acf/location/rule_values/post_type', 'acf_location_rule_values_Post');
function acf_location_rule_values_Post( $choices ) {
$choices['product_variation'] = 'Product Variation';
//print_r($choices);
return $choices;
}
/* End */
Any help would be appreciated.
acf()->input->save_post( $variation_id );
Should be
do_action( 'acf/save_post', $variation_id );
Credit to onoweb from here https://support.advancedcustomfields.com/forums/topic/acf-on-product-variations-almost-works/

Show or hide shipping methods based on user meta data in WooCommerce

I have a code to hide shipping for non-wholesale customers, please help me redo it, I need to hide the shipping option for wholesale customers.
/**
* Removes shipping methods for non-wholesale customers.
* Please be sure to clear your WooCommerce store's cache.
* Adjust 'flat_rate:2' to match that of your wholesale shipping method.
*/
function my_wcs_remove_shipping_non_wholesale( $rates, $package ){
global $current_user;
$is_wholesale = get_user_meta( $current_user->ID, 'wcs_wholesale_customer', true );
if ( ! $is_wholesale ) {
foreach( $rates as $method ) {
if ( $method->id == 'flat_rate:2' ) {
unset( $rates[$method->id] );
}
}
}
return $rates;
}
add_filter( 'woocommerce_package_rates', 'my_wcs_remove_shipping_non_wholesale', 10, 2 );
You don't need to have 2 functions, one for Wholesale customers and an other for non Wholesale customers… you can merge both in the same function as follows:
add_filter( 'woocommerce_package_rates', 'shipping_methods_based_on_wholesale_customer', 10, 2 );
function shipping_methods_based_on_wholesale_customer( $rates, $package ){
$is_wholesale = get_user_meta( get_current_user_id(), 'wcs_wholesale_customer', true );
// Set the shipping methods rate ids in the arrays:
if( $is_wholesale ) {
$shipping_rates_ids = array('flat_rate:1', 'flat_rate:4'); // To be removed for NON Wholesale users
} else {
$shipping_rates_ids = array('flat_rate:2'); // To be removed for Wholesale users
}
// Loop through shipping rates fro the current shipping package
foreach( $rates as $rate_key => $rate ) {
if ( in_array( $rate_key, $shipping_rates_ids) ) {
unset( $rates[$rate_key] );
}
}
return $rates;
}
Code goes in functions.php file of the active child theme (or active theme). It should works.
Don't forget to empty the cart after saving the code, to refresh cached shipping data
So for everybody, like me, where the code didn't work. With the help of #Howard E here is the adjusted code 2022 which now works:
add_filter( 'woocommerce_package_rates', 'shipping_methods_based_on_wholesale_customer', 10, 2 );
function shipping_methods_based_on_wholesale_customer( $rates, $package ) {
   $user  = wp_get_current_user();
   $roles = (array) $user->roles;
   // Set the shipping methods rate ids in the arrays.
   if ( ! in_array( 'wholesale_customer', $roles, true ) ) {
      $shipping_rates_ids = array( 'flat_rate:10', 'flat_rate:7' ); // To be removed for NON Wholesale users.
   } else {
      $shipping_rates_ids = array( 'flat_rate:13', 'flat_rate:15' ); // To be removed for Wholesale users.
   }
   // Loop through shipping rates from the current shipping package.
   foreach ( $rates as $rate_key => $rate ) {
      if ( in_array( $rate_key, $shipping_rates_ids, true ) ) {
         unset( $rates[ $rate_key ] );
      }
   }
   return $rates;
}

Problem with woocommerce_add_order_item_meta

i have spent the last 3 hours trying to fix a problem with a woocommerce deprecated hook and i'm going crazy because i have tried hundred different options to make it work, and it's not happening.
This is the actual code, it suppose to save the value of custom fields. Any idea about how to make it work with a non obsolete hook?
add_action('woocommerce_add_order_item_meta','save_in_order_item_meta', 10, 3 );
function save_in_order_item_meta( $item_id, $values, $cart_item_key ) {
if( isset( $values['custom_data'] ) ) {
woocommerce_new_order_item( $item_id, $values['custom_data']['label'], $values['custom_data']['value'] );
}
}
Any help is welcome. Thank you
Edit;
Already tried.
add_action( 'woocommerce_add_order_item_meta', 'custom_add_order_item_meta', 20, 3 ); function custom_add_order_item_meta(
$item_id, $values, $cart_item_key ) { // Get cart item custom data and update order item meta if( isset( $values['custom_data'] ) ) {
wc_add_order_item_meta( $item_id, $values['custom_data']['label'], $values['custom_data']['value'] ); } }
add_action( 'woocommerce_add_order_item_meta', 'custom_add_order_item_meta', 20, 3 );
function custom_add_order_item_meta( $item_id, $values, $cart_item_key ) {
$custom_field_value = $custom_field_value;
if ( ! empty( $custom_field_value ) ){
wc_add_order_item_meta( $item_id, $values['custom_data']['label'], $values['custom_data']['value'] );
}
}
Hook woocommerce_add_order_item_meta is replaced by woocommerce_checkout_create_order_line_item, so with your code (assuming that the cart object contains your custom cart item data):
add_action('woocommerce_checkout_create_order_line_item', 'save_custom_order_item_meta_data', 10, 4 );
function save_custom_order_item_meta_data( $item, $cart_item_key, $values, $order ) {
if( isset( $values['custom_data']['label'] ) && isset( $values['custom_data']['value'] ) ) {
$item->update_meta_data( $values['custom_data']['label'], $values['custom_data']['value'] );
}
}
Code goes in function.php file of your active child theme (or active theme). It should works.
Related:
Replace woocommerce_add_order_item_meta hook in Woocommerce 3.4
Woocommerce: Which hook to replace deprecated "woocommerce_add_order_item_meta"
Threads with: woocommerce_checkout_create_order_line_item action hook

Set a custom field value in cart and save it as order item data in Woocommerce 3

In wooCommerce plugin I want to send $item['_custom_sizer'] this value to check out page and email template but i am not able to pass the value. please help me out.
add_filter( 'woocommerce_add_cart_item_data', 'add_cart_item_custom_data_vase', 10, 2 );
function add_cart_item_custom_data_vase( $cart_item_meta, $product_id ) {
global $woocommerce;
$new_value = array();
$new_value['_custom_sizer'] = $_POST['sizer'];
if(empty($cart_item_data)) {
return $new_value;
} else {
return array_merge($cart_item_data, $new_value);
}
}
//Get it from the session and add it to the cart variable
function get_cart_items_from_session( $item, $values, $key ) {
if (array_key_exists( '_custom_sizer', $values ) ) {
$item['_custom_sizer'] = $values['_custom_sizer'];
}
return $item;
}
add_filter( 'woocommerce_get_cart_item_from_session', 'get_cart_items_from_session', 1, 3 );
Updated… Try the following instead:
// Add "Sizer" as custom cart item data
add_filter( 'woocommerce_add_cart_item_data', 'add_cart_item_custom_data', 20, 2 );
function add_cart_item_custom_data( $cart_item_data, $product_id ) {
if( isset($_POST['sizer']) && ! empty($_POST['sizer']) ){
$cart_item_data['custom_sizer'] = sanitize_text_field( $_POST['sizer'] );
}
return $cart_item_data;
}
// Display "Sizer" in cart and checkout
add_filter( 'woocommerce_get_item_data', 'display_sizer_in_cart_checkout', 20, 2 );
function display_sizer_in_cart_checkout( $cart_item_data, $cart_item ) {
if( isset($cart_item['custom_sizer']) ){
$cart_item_data[] = array(
'name' => __('Metal Type'),
'value' => $cart_item['custom_sizer'],
);
}
return $cart_item_data;
}
// Save "sizer" as custom order item meta data
add_action( 'woocommerce_checkout_create_order_line_item', 'update_order_item_meta', 20, 4 );
function update_order_item_meta( $item, $cart_item_key, $values, $order ) {
if( isset($values['custom_sizer']) )
$item->update_meta_data( 'Sizer', $values['custom_sizer'] );
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.

Remove add cart button in Woocommerce for a specific product category

I have an issue how to remove a cart from a category product. It works just fine if I apply it to a specific id or all in general, but I am unable to do it for a category. Below is my code I have done regarding it.
Also, I am struggling to apply this same pattern to Related Articles section, so any help would be appreciated.
Thank you.
//function for deleting ....
function remove_product_description_add_cart_button(){
global $product;
//Remove Add to Cart button from product description of product with id 1234
if ($product->id == 188){
remove_action( 'woocommerce_single_product_summary', 'woocommerce_template_single_add_to_cart', 30 );
}
add_action('wp','remove_product_description_add_cart_button');
}
Nov 2020 Update
To make it work with a product category you can use the WordPress conditional function has_term() this way:
add_action('woocommerce_single_product_summary', 'remove_product_description_add_cart_button', 1 );
function remove_product_description_add_cart_button() { // function for deleting ...
// Set HERE your category ID, slug or name (or an array)
$categories = array('your-category-1');
//Remove Add to Cart button from product description of product with id 1234
if ( has_term( $categories, 'product_cat', get_the_id() ) ) {
remove_action( 'woocommerce_single_product_summary', 'woocommerce_template_single_add_to_cart', 30 );
}
}
Code goes in function.php file of your active child theme (or active theme). Or also in any plugin php file. Tested and works.
You can try something like this
function western_custom_buy_buttons(){
$product = get_product();
if ( has_term( 'category1', 'product_cat') ){
// removing the purchase buttons
remove_action( 'woocommerce_after_shop_loop_item', 'woocommerce_template_loop_add_to_cart' );
remove_action( 'woocommerce_single_product_summary', 'woocommerce_template_single_add_to_cart', 30 );
remove_action( 'woocommerce_simple_add_to_cart', 'woocommerce_simple_add_to_cart', 30 );
remove_action( 'woocommerce_grouped_add_to_cart', 'woocommerce_grouped_add_to_cart', 30 );
remove_action( 'woocommerce_variable_add_to_cart', 'woocommerce_variable_add_to_cart', 30 );
remove_action( 'woocommerce_external_add_to_cart', 'woocommerce_external_add_to_cart', 30 );
}
}
add_action( 'wp', 'western_custom_buy_buttons' );
Reference : https://www.themelocation.com/how-to-remove-add-to-cart-button-from-a-certain-category-woocommerce/
you can add your own category name in line 3
add_filter('woocommerce_is_purchasable', 'set_catalog_mode_on_for_category', 10, 2 );
function set_catalog_mode_on_for_category( $is_purchasable, $product ) {
if( has_term( 'Category Name', 'product_cat', $product->get_id() ) ) { //Change Category name here
return false; }
return $is_purchasable;
}
Source: https://webtalkhub.com/how-to-remove-add-to-cart-button-in-woocommerce/
You have try something like this:
function remove_product_description_add_cart_button(){
global $product;
$termsOfProduct = wp_get_post_terms( $product->id, 'product_cat' );
if (in_array("CatToFind", $termsOfProduct)) {
remove_action( 'woocommerce_single_product_summary', 'woocommerce_template_single_add_to_cart', 30 );
}
add_action('wp','remove_product_description_add_cart_button');
}
Category products in Woocommerce are simply terms. Wp_get_post_terms allow you to find any categories associated to post (product id).
Reference: https://codex.wordpress.org/Function_Reference/wp_get_post_terms
Use this code
/*
Plugin Name: Remove 'Add to cart' conditionally
Plugin URI: https://www.damiencarbery.com/2020/03/remove-add-to-cart-conditionally/
Description: Conditionally remove the 'Add to cart' button in WooCommerce.
Author: Damien Carbery
Version: 0.2
*/
class IsPurchasableConditionalFiltering {
// A reference to an instance of this class.
private static $instance;
// Store whether 'Add to cart' button should be displayed.
private $purchasable;
// Returns an instance of this class.
public static function get_instance() {
if ( null == self::$instance ) {
self::$instance = new IsPurchasableConditionalFiltering;
}
return self::$instance;
}
// Initialize the plugin variables.
public function __construct() {
$this->purchasable = array();
$this->init();
}
// Set up WordPress specfic actions.
public function init() {
add_filter( 'woocommerce_is_purchasable', array( $this, 'is_purchasable_conditionals' ), 10, 2 );
add_filter( 'woocommerce_variation_is_purchasable', array( $this, 'variation_is_purchasable_conditionals' ), 10, 2 );
// Remove variations dropdown and 'Add to cart' button for variable products.
add_action( 'woocommerce_before_single_product_summary', array( $this, 'before_single_product_summary' ) );
}
public function is_purchasable_conditionals( $whether_purchasable, $product ) {
// Return cached result.
if ( array_key_exists( $product->get_id(), $this->purchasable ) ) {
return $this->purchasable[ $product->get_id() ];
}
// Only do our conditional checks if WooCommerce deems the item to be purchasable.
if ( $whether_purchasable ) {
$result = true; // Default to allowing purchase.
// Check our specific conditions - some examples.
/* // Product over a certain price.
if ( $product->get_price() > 2 ) {
$result = false;
}*/
// Check if product in a certain categores.
if ( has_term( array( 'hoodies', 'accessories' ), 'product_cat', $product->get_id() ) ) {
$result = false;
}
$this->purchasable[ $product->get_id() ] = $result;
}
else {
// Store that this item cannot be purchased.
$this->purchasable[ $product->get_id() ] = false;
}
return $this->purchasable[ $product->get_id() ];
}
public function variation_is_purchasable_conditionals( $whether_purchasable, $product ) {
return $whether_purchasable;
}
public function before_single_product_summary() {
$product_id = get_the_ID();
if ( array_key_exists( $product_id, $this->purchasable ) && !$this->purchasable[ $product_id ] ) {
remove_action( 'woocommerce_single_variation', 'woocommerce_single_variation_add_to_cart_button', 20 );
remove_action( 'woocommerce_variable_add_to_cart', 'woocommerce_variable_add_to_cart', 30 );
}
}
}
$IsPurchasableConditionalFiltering = new IsPurchasableConditionalFiltering;
Reference : https://www.damiencarbery.com/2020/03/remove-add-to-cart-button-conditionally/

Categories