Dynamically added custom fields not displayed on WooCommerce email notifications - php

I have a different kind of scenario then the typical custom fields (I suppose). I am not getting custom values (fields) from user in the form rather I have an implementation which adds:
ColorName
Size
City
These are from a custom product flow which adds custom attributes to the cart, here is how I am doing that:
add_action('wp_ajax_wdm_add_user_custom_data_options', 'wdm_add_user_custom_data_options_callback');
add_action('wp_ajax_nopriv_wdm_add_user_custom_data_options', 'wdm_add_user_custom_data_options_callback');
function wdm_add_user_custom_data_options_callback()
{
// print_r($_POST);
$productIDM = $_POST['product_id'];
// case swith
switch ($productIDM) {
case "Ace Dura Silk":
$productID = 3254;
break;
case "Ace Matt Finish":
$productID = 3232;
break;
case "Ace Plastic Emulsion":
$productID = 3276;
break;
case "Ace Weather Defender":
$productID = 2991;
break;
case "Overall Plasticoat":
$productID = 3112;
break;
}
$colorname = $_POST['colorname'];
$cityname = $_POST['cityname'];
$size = $_POST['size'];
$price = $_POST['price'];
global $woocommerce;
$woocommerce->cart->add_to_cart( $productID, 1 );
// die();
// echo 'I am in...';
$result = array(
'status' => true
);
echo json_encode($result);
}
add_filter('woocommerce_add_cart_item_data','wdm_add_item_data',1,10);
function wdm_add_item_data($cart_item_data, $product_id) {
global $woocommerce;
$new_value = array();
$new_value['_custom_options'] = $_POST['custom_options'];
if(empty($cart_item_data)) {
return $new_value;
} else {
return array_merge($cart_item_data, $new_value);
}
}
add_filter('woocommerce_get_cart_item_from_session', 'wdm_get_cart_items_from_session', 1, 3 );
function wdm_get_cart_items_from_session($item,$values,$key) {
if (array_key_exists( '_custom_options', $values ) ) {
$item['_custom_options'] = $values['_custom_options'];
}
return $item;
}
add_filter('woocommerce_cart_item_name','add_usr_custom_session',1,3);
function add_usr_custom_session($product_name, $values, $cart_item_key ) {
$return_string = $product_name . "<div class='cart_subitems_custom'><br /><small>".$values['_custom_options']['colorname']."</small><br /><small>".$values['_custom_options']['cityname']."</small><br /><small>".$values['_custom_options']['size']."</small></div>" ; //. "<br />" . print_r($values['_custom_options']);
return $return_string;
}
add_action('woocommerce_add_order_item_meta','wdm_add_values_to_order_item_meta',1,2);
function wdm_add_values_to_order_item_meta($item_id, $values) {
global $woocommerce,$wpdb;
wc_add_order_item_meta($item_id,'_colorname',$values['_custom_options']['colorname']);
wc_add_order_item_meta($item_id,'_cityname',$values['_custom_options']['cityname']);
wc_add_order_item_meta($item_id,'_size',$values['_custom_options']['size']);
}
add_action( 'woocommerce_before_calculate_totals', 'update_custom_price', 1, 1 );
function update_custom_price( $cart_object ) {
foreach ( $cart_object->cart_contents as $cart_item_key => $value ) {
// Version 2.x
//$value['data']->price = $value['_custom_options']['custom_price'];
// Version 3.x / 4.x
if($value['_custom_options']['price'] == null){
echo"";
}else{
$value['data']->set_price($value['_custom_options']['price']);
}
}
}
I am getting these custom values almost everywhere except Email Notification.
Here is what normal product order edit shows:
Here is how I am getting the custom product in order edit page:
I have tried all the solution I can possibly find (filter & action hooks) but nothing works for me.
I have tried first answer from this:
add_action( 'woocommerce_checkout_create_order_line_item', 'custom_checkout_create_order_line_item', 20, 4 );
function custom_checkout_create_order_line_item( $item, $cart_item_key, $values, $order ) {
if( isset( $values['colorname'] ) )
$item->add_meta_data( __('DCM Shade'), $values['_colorname'] );
}
Also the common method I found everywhere:
function custom_woocommerce_email_order_meta_fields( $fields, $sent_to_admin, $order ) {
// Get meta
$color = $order->get_meta( 'colorname', true );
// NOT empty
if( ! empty( $color ) ) {
$fields['colorname'] = array(
'label' => __( 'Shade' ),
'value' => $color,
);
}
// Get (other) meta
$shipping_email = $order->get_meta( '_cityname', true );
// NOT empty
if ( ! empty( $shipping_email ) ) {
$fields['_cityname'] = array(
'label' => __( 'City' ),
'value' => $shipping_email,
);
}
return $fields;
}
add_filter( 'woocommerce_email_order_meta_fields', 'custom_woocommerce_email_order_meta_fields', 10, 3 );
But I can't get the custom fields.
What am I doing wrong can please anyone please guide me.

Related

Remove Component/Variant in Order Invoice PDF in Woocommerce

I Hope are you okay.
Today I have problems with I open order invoice pdf in admin after products list have components.
I Remove all components as well
I am using woocommerce-pdf-invoices-packing-slips plugin.
Below is my product URL :
https://www.pcguru.lt/produktas/amd-home-guru/
My Invoice PDF screenshot as well.
How can I remove it?
I Searching Google but couldn't Found Anything.
After Code Debugging Step by Step, That's are Found Successfully.
File Path:
/wp-content/plugins/woocommerce-pdf-invoices-packing-slips/includes/documents/abstract-wcpdf-order-document-methods.php
public function get_order_items() {
$items = $this->order->get_items();
$data_list = array();
if( sizeof( $items ) > 0 ) {
foreach ( $items as $item_id => $item ) {
//Find Child Product Of Composite Product
global $wpdb;
$post_id = $wpdb->get_row("SELECT meta_value FROM pcg_woocommerce_order_itemmeta WHERE (meta_key = 'wooco_ids' AND order_item_id = '". $item_id ."')");
if($post_id->meta_value !=''){
$main_id = str_replace('/1/', '', $post_id->meta_value);
$data_ids = explode(",",$main_id);
}
//END Custom Code
$data = array();
// Set the item_id
$data['item_id'] = $item_id;
// Set the id
$data['product_id'] = $item['product_id'];
$data['variation_id'] = $item['variation_id'];
// Compatibility: WooCommerce Composit Products uses a workaround for
// setting the order before the item name filter, so we run this first
if ( class_exists('WC_Composite_Products') ) {
$order_item_class = apply_filters( 'woocommerce_order_item_class', '', $item, $this->order );
}
// Set item name
$data['name'] = apply_filters( 'woocommerce_order_item_name', $item['name'], $item, false );
// Set item quantity
$data['quantity'] = $item['qty'];
//$data['product_desc'] = $item->get_product(); // Get the WC_Product objec
//echo '<pre>'; print_r($product); echo '</pre>'; die;
// Set the line total (=after discount)
$data['line_total'] = $this->format_price( $item['line_total'] );
$data['single_line_total'] = $this->format_price( $item['line_total'] / max( 1, abs( $item['qty'] ) ) );
$data['line_tax'] = $this->format_price( $item['line_tax'] );
$data['single_line_tax'] = $this->format_price( $item['line_tax'] / max( 1, abs( $item['qty'] ) ) );
$data['tax_rates'] = $this->get_tax_rate( $item, $this->order, false );
$data['calculated_tax_rates'] = $this->get_tax_rate( $item, $this->order, true );
// Set the line subtotal (=before discount)
$data['line_subtotal'] = $this->format_price( $item['line_subtotal'] );
$data['line_subtotal_tax'] = $this->format_price( $item['line_subtotal_tax'] );
$data['ex_price'] = $this->get_formatted_item_price( $item, 'total', 'excl' );
$data['price'] = $this->get_formatted_item_price( $item, 'total' );
$data['order_price'] = $this->order->get_formatted_line_subtotal( $item ); // formatted according to WC settings
// Calculate the single price with the same rules as the formatted line subtotal (!)
// = before discount
$data['ex_single_price'] = $this->get_formatted_item_price( $item, 'single', 'excl' );
$data['single_price'] = $this->get_formatted_item_price( $item, 'single' );
// Pass complete item array
$data['item'] = $item;
// Get the product to add more info
if ( is_callable( array( $item, 'get_product' ) ) ) {
$product = $item->get_product();
} else {
$product = $this->order->get_product_from_item( $item );
}
// Checking fo existance, thanks to MDesigner0
if( !empty( $product ) ) {
// Thumbnail (full img tag)
$data['thumbnail'] = $this->get_thumbnail( $product );
$data['get_parent_id'] = is_callable( array( $product, 'get_parent_id' ) ) ? $product->get_parent_id() : '';
// Set item SKU
$data['sku'] = is_callable( array( $product, 'get_sku' ) ) ? $product->get_sku() : '';
// Set item weight
$data['weight'] = is_callable( array( $product, 'get_weight' ) ) ? $product->get_weight() : '';
// Set item dimensions
$data['dimensions'] = $product instanceof \WC_Product ? WCX_Product::get_dimensions( $product ) : '';
// Pass complete product object
$data['product'] = $product;
} else {
$data['product'] = null;
}
// Set item meta
if (function_exists('wc_display_item_meta')) { // WC3.0+
$data['meta'] = wc_display_item_meta( $item, array(
'echo' => false,
) );
} else {
if ( version_compare( WOOCOMMERCE_VERSION, '2.4', '<' ) ) {
$meta = new \WC_Order_Item_Meta( $item['item_meta'], $product );
} else { // pass complete item for WC2.4+
$meta = new \WC_Order_Item_Meta( $item, $product );
}
$data['meta'] = $meta->display( false, true );
}
if (!in_array($data['product_id'], $data_ids)) {
$data_list[$item_id] = apply_filters( 'wpo_wcpdf_order_item_data', $data, $this->order, $this->get_type());
}
}
}
//echo '<pre>'; print_r($data_list); echo '</pre>'; die;
return apply_filters( 'wpo_wcpdf_order_items_data', $data_list, $this->order, $this->get_type() );
}
Solution Screenshot:
I am very happy because solution are completed.
Also Thanks to all helping guys

Using ACF number field as price for WooCommerce custom product type

I've seen online that you can add all Custom Post Types to a WooCommerce cart as long as the CPT has a price field. The only issue is that you have to tell WooCommerce what CPT field contains the price.
Afterwords you can easily create an add-to-cart url like this:
https://yourdomain.com/?add-to-cart=XXX (XXX being the Custom Post Type post ID)
Why is this handy?
I have an online menu (just to inform my guest what we serve) but because of Corona we have to close our doors so I want these dishes to be ordered online.
The code should be something like this but the CPT is not added to cart:
add_filter('woocommerce_get_price', 'yl_get_dish_price', 20,2);
function yl_get_dish_price($price,$post) {
if ($post->post->post_type === 'dish') {
$price = get_field('price', $post->ID);
}
return $price;
}
UPDATE
I took this answer from here https://stackoverflow.com/a/60320662/10291365
class YL_Dish_Product extends WC_Product {
protected $post_type = 'dish';
public function get_type() {
return 'dish';
}
public function __construct( $product = 0 ) {
$this->supports[] = 'ajax_add_to_cart';
parent::__construct( $product );
}
// maybe overwrite other functions from WC_Product
}
class YL_Data_Store_CPT extends WC_Product_Data_Store_CPT {
public function read( &$product ) { // this is required
$product->set_defaults();
$post_object = get_post( $product->get_id() );
if ( ! $product->get_id() || ! $post_object || 'dish' !== $post_object->post_type ) {
throw new Exception( __( 'Invalid product.', 'woocommerce' ) );
}
$product->set_props(
array(
'name' => $post_object->post_title,
'slug' => $post_object->post_name,
'date_created' => 0 < $post_object->post_date_gmt ? wc_string_to_timestamp( $post_object->post_date_gmt ) : null,
'date_modified' => 0 < $post_object->post_modified_gmt ? wc_string_to_timestamp( $post_object->post_modified_gmt ) : null,
'status' => $post_object->post_status,
'description' => $post_object->post_content,
'short_description' => $post_object->post_excerpt,
'parent_id' => $post_object->post_parent,
'menu_order' => $post_object->menu_order,
'reviews_allowed' => 'open' === $post_object->comment_status,
)
);
$this->read_attributes( $product );
$this->read_downloads( $product );
$this->read_visibility( $product );
$this->read_product_data( $product );
$this->read_extra_data( $product );
$product->set_object_read( true );
}
// maybe overwrite other functions from WC_Product_Data_Store_CPT
}
class YL_WC_Order_Item_Product extends WC_Order_Item_Product {
public function set_product_id( $value ) {
if ( $value > 0 && 'dish' !== get_post_type( absint( $value ) ) ) {
$this->error( 'order_item_product_invalid_product_id', __( 'Invalid product ID', 'woocommerce' ) );
}
$this->set_prop( 'product_id', absint( $value ) );
}
}
function YL_woocommerce_data_stores( $stores ) {
// the search is made for product-$post_type so note the required 'product-' in key name
$stores['product-dish'] = 'YL_Data_Store_CPT';
return $stores;
}
add_filter( 'woocommerce_data_stores', 'YL_woocommerce_data_stores' , 11, 1 );
function YL_woo_product_class( $class_name , $product_type , $product_id ) {
if ($product_type == 'dish')
$class_name = 'YL_Dish_Product';
return $class_name;
}
add_filter('woocommerce_product_class','YL_woo_product_class',25,3 );
function my_woocommerce_product_get_price( $price, $product ) {
if ($product->get_type() == 'dish' ) {
$price = 10; // or get price how ever you see fit
}
return $price;
}
add_filter('woocommerce_get_price','my_woocommerce_product_get_price',20,2);
add_filter('woocommerce_product_get_price', 'my_woocommerce_product_get_price', 10, 2 );
// required function for allowing posty_type to be added; maybe not the best but it works
function YL_woo_product_type($false,$product_id) {
if ($false === false) { // don't know why, but this is how woo does it
global $post;
// maybe redo it someday?!
if (is_object($post) && !empty($post)) { // post is set
if ($post->post_type == 'dish' && $post->ID == $product_id)
return 'dish';
else {
$product = get_post( $product_id );
if (is_object($product) && !is_wp_error($product)) { // post not set but it's a dish
if ($product->post_type == 'dish')
return 'dish';
} // end if
}
} else if(wp_doing_ajax()) { // has post set (usefull when adding using ajax)
$product_post = get_post( $product_id );
if ($product_post->post_type == 'dish')
return 'dish';
} else {
$product = get_post( $product_id );
if (is_object($product) && !is_wp_error($product)) { // post not set but it's a dish
if ($product->post_type == 'dish')
return 'dish';
} // end if
} // end if // end if
} // end if
return false;
}
add_filter('woocommerce_product_type_query','YL_woo_product_type',12,2 );
function YL_woocommerce_checkout_create_order_line_item_object($item, $cart_item_key, $values, $order) {
$product = $values['data'];
if ($product->get_type() == 'dish') {
return new YL_WC_Order_Item_Product();
} // end if
return $item ;
}
add_filter( 'woocommerce_checkout_create_order_line_item_object', 'YL_woocommerce_checkout_create_order_line_item_object', 20, 4 );
function cod_woocommerce_checkout_create_order_line_item($item,$cart_item_key,$values,$order) {
if ($values['data']->get_type() == 'dish') {
$item->update_meta_data( '_dish', 'yes' ); // add a way to recognize custom post type in ordered items
return;
} // end if
}
add_action( 'woocommerce_checkout_create_order_line_item', 'cod_woocommerce_checkout_create_order_line_item', 20, 4 );
function YL_woocommerce_get_order_item_classname($classname, $item_type, $id) {
global $wpdb;
$is_IA = $wpdb->get_var("SELECT meta_value FROM {$wpdb->prefix}woocommerce_order_itemmeta WHERE order_item_id = {$id} AND meta_key = '_dish'");
if ('yes' === $is_IA) { // load the new class if the item is our custom post
$classname = 'YL_WC_Order_Item_Product';
} // end if
return $classname;
}
add_filter( 'woocommerce_get_order_item_classname', 'YL_woocommerce_get_order_item_classname', 20, 3 );
The above code does add a CPT to your cart (GREAT!!) but the price is always set to 10,00
So the code below doesn't give the right price :(
add_filter('woocommerce_get_price', 'yl_get_dish_price', 20,2);
function yl_get_dish_price($price,$post) {
if ($post->post->post_type === 'dish') {
$price = get_field('price', $post->ID);
}
return $price;
}
Any idea?
Since WooCommerce 3, the hook woocommerce_get_price is obsolete and deprecated… It's replaced by the following composite hook:
add_filter( 'woocommerce_product_get_price', 'yl_get_dish_price', 20, 2 );
add_filter( 'woocommerce_product_get_regular_price', 'yl_get_dish_price', 20, 2 );
function yl_get_dish_price( $price, $product ) {
if ( $product->is_type('dish') ) {
$price = get_field( 'price', $product->get_id() );
}
return $price;
}
It could and should better work.

Displaying data in WooCommerce admin orders list custom columns

I'm adding the following custom columns and they both appear:
function add_order_new_column_header( $columns ) {
$new_columns = array();
foreach ( $columns as $column_name => $column_info ) {
$new_columns[ $column_name ] = $column_info;
if ( 'order_total' === $column_name ) {
$new_columns['order_shipping'] = __( 'Tarnemeetod', 'my-textdomain' );
$new_columns['order_payment'] = __( 'Maksemeetod', 'my-textdomain' );
}
}
return $new_columns;
Then I'm adding the following to generate data:
add_filter( 'manage_edit-shop_order_columns', 'add_order_new_column_header', 20);
add_action( 'manage_shop_order_posts_custom_column', 'add_wc_order_admin_list_column_content' );
function add_wc_order_admin_list_column_content( $column ) {
global $post;
if ( 'order_shipping' === $column ) {
$order = wc_get_order( $post->ID );
echo $order->get_shipping_method();
if ( 'order_payment' === $column ) {
$order = wc_get_order( $post->ID );
echo $order->get_payment_method_title();
}
}
}
order_shipping is showing correct shipping method, but order_payment is empty.
I'm not using multiple languages on the site. WooCommerce 4.1.1, PHP 7.3.19
view from WooCommerce orders admin panel:
I've tried both get_payment_method and get_payment_method_list. I have orders with BACS mostly and a few Cash on Pickup which is disabled for now. What am I missing here?
There are some mistakes and missing things in your code, try the following instead:
add_filter( 'manage_edit-shop_order_columns', 'add_custom_columns_to_admin_orders', 20);
function add_custom_columns_to_admin_orders( $columns ) {
$new_columns = array();
foreach ( $columns as $column_name => $column_info ) {
$new_columns[ $column_name ] = $column_info;
if ( 'order_total' === $column_name ) {
$new_columns['order_shipping'] = __( 'Tarnemeetod', 'my-textdomain' );
$new_columns['order_payment'] = __( 'Maksemeetod', 'my-textdomain' );
}
}
return $new_columns;
}
add_action( 'manage_shop_order_posts_custom_column', 'custom_columns_content_in_admin_orders' );
function custom_columns_content_in_admin_orders( $column ) {
global $post, $the_order;
if ( 'order_shipping' === $column ) {
echo $the_order->get_shipping_method();
}
if ( 'order_payment' === $column ) {
echo $the_order->get_payment_method_title();
}
}
Code goes in functions.php file of your active child theme (or active theme). Tested and works.

Woocommerce Is_Purchasable cause ajax error

I'm using the code below to modify the WooCommerce Is_Purchasable option so that, item Y is purchasable if item X is added to the cart.
But it gives ajax error when trying to add item Y to the cart.
Here's the code:
function aelia_get_cart_contents() {
$cart_contents = array();
/**
* Load the cart object. This defaults to the persistant cart if null.
*/
$cart = WC()->session->get( 'cart', null );
if ( is_null( $cart ) && ( $saved_cart = get_user_meta( get_current_user_id(), '_woocommerce_persistent_cart', true ) ) ) {
$cart = $saved_cart['cart'];
} elseif ( is_null( $cart ) ) {
$cart = array();
}
if ( is_array( $cart ) ) {
foreach ( $cart as $key => $values ) {
$_product = wc_get_product( $values['variation_id'] ? $values['variation_id'] : $values['product_id'] );
if ( ! empty( $_product ) && $_product->exists() && $values['quantity'] > 0 ) {
if ( $_product->is_purchasable() ) {
// Put session data into array. Run through filter so other plugins can load their own session data
$session_data = array_merge( $values, array( 'data' => $_product ) );
$cart_contents[ $key ] = apply_filters( 'woocommerce_get_cart_item_from_session', $session_data, $values, $key );
}
}
}
}
return $cart_contents;
}
// Step 1 - Keep track of cart contents
add_action('wp_loaded', function() {
// If there is no session, then we don't have a cart and we should not take
// any action
if(!is_object(WC()->session)) {
return;
}
// Product Y
global $y_cart_items;
$y_cart_items = 2986;
//Product X
global $x_cart_items;
$x_cart_items = array(
'297'
);
// Step 2
add_filter('woocommerce_is_purchasable', function($is_purchasable, $product) {
global $y_cart_items;
global $x_cart_items;
if( $product->id == $y_cart_items ) {
// make it false
$is_purchasable = false;
// get the cart items object
foreach ( aelia_get_cart_contents() as $key => $item ) {
// do your condition
if( in_array( $item['product_id'], $x_cart_items ) ) {
// Eligible product found on the cart
$is_purchasable = true;
break;
}
}
}
return $is_purchasable;
}, 10, 2);
}, 10);
// Step 3 - Explain customers why they can't add some products to the cart
add_filter('woocommerce_get_price_html', function($price_html, $product) {
if(!$product->is_purchasable() && is_product()) {
$price_html .= '<p>' . __('Add Product X to be able to purchase Product Y.', 'woocommerce') . '</p>';
}
return $price_html;
}, 10, 2);
How do I fix this? Thank you.

Woocommerce CSV export not showing values

I've noticed a strange behaviour of one of my functions. Just take a look. Using first function I'm displaying some custom fields data in the backend:
add_filter( 'manage_edit-shop_order_columns', 'MY_COLUMNS_FUNCTION', 10 );
function MY_COLUMNS_FUNCTION( $columns ) {
$new_columns = ( is_array( $columns ) ) ? $columns : array();
unset( $new_columns['order_actions'] );
//edit this for you column(s)
//all of your columns will be added before the actions column
$new_columns['product_name'] = 'Product';
$new_columns['authors_income'] = 'Author';
$new_columns['order_actions'] = $columns['order_actions'];
return $new_columns;
}
add_action( 'manage_shop_order_posts_custom_column', 'MY_COLUMNS_VALUES_FUNCTION', 2 );
function MY_COLUMNS_VALUES_FUNCTION( $column ) {
global $post;
$order = new WC_Order( $post->ID );
$items = $order->get_items();
//start editing, I was saving my fields for the orders as custom post meta
//if you did the same, follow this code
if ( $column == 'authors_income' ) {
foreach ( $items as $item ) {
echo $item['PLN-dla-autora'];
echo ' zł';
}
}
if ( $column == 'product_name' ) {
foreach ( $items as $item ) {
echo $item['name'];
}
}
}
add_filter( "manage_edit-shop_order_sortable_columns", 'MY_COLUMNS_SORT_FUNCTION' );
function MY_COLUMNS_SORT_FUNCTION( $columns ) {
$custom = array(
//start editing
'authors_income' => 'PLN-dla-autora',
'product_name' => 'name'
//stop editing
);
return wp_parse_args( $custom, $columns );
Everything works there like a charm. Then I have second function, which somehow does the same, yet it will print the data into the CSV file. Unfortunatelly, this code doesnt work (it display only column header, no data included). I dont see any major differences between those functions so why is that?
function wc_csv_export_modify_column_headers( $column_headers ) {
$new_headers = array(
'author_income' => 'PLN dla autora',
// add other column headers here in the format column_key => Column Name
);
return array_merge( $column_headers, $new_headers );
}
add_filter( 'wc_customer_order_csv_export_order_headers', 'wc_csv_export_modify_column_headers' );
// set the data for each for custom columns
function wc_csv_export_modify_row_data( $order_data, $order ) {
$custom_data = array(
'author_income' => get_post_meta( $order->id, 'PLN dla autora', true ),
// add other row data here in the format column_key => data
);
return array_merge( $order_data, $custom_data );
}
add_filter( 'wc_customer_order_csv_export_order_row', 'wc_csv_export_modify_row_data', 10, 2 );

Categories