Passing custom data from cart items to Order meta in Woocommerce 3 - php

I have implemented a custom HTML Form and asking for some data which my customers will pass to place order successfully. Without these details my order has no importance.
For HTML form, I am referencing some custom PHP script which is below and which processes POST data from the Form and creates Cart with these data programmatically. Thanks #LoicTheAztec to help me achieve this.
The script.php file code:
<?php
require_once("../wp-load.php");
$customer_name = $_POST["customer_name"];
$customer_email = $_POST["customer_email"];
$customer_sex = $_POST["customer_sex"];
$customer_age = $_POST["customer_age"];
$product_id = $_POST["product_id"];
$custom_data = array(); // Initializing
if( isset($_POST['customer_name']) && ! empty($_POST['customer_name']) )
$custom_data['custom_data']['name'] = $_POST['customer_name'];
if( isset($_POST['customer_email']) && ! empty($_POST['customer_email']) )
$custom_data['custom_data']['email'] = $_POST['customer_email'];
if( isset($_POST['customer_sex']) && ! empty($_POST['customer_sex']) )
$custom_data['custom_data']['sex'] = $_POST['customer_sex'];
if( isset($_POST['customer_age']) && ! empty($_POST['customer_age']) )
$custom_data['custom_data']['age'] = $_POST['customer_age'];
global $woocommerce;
if (WC()->cart->add_to_cart( $product_id, '1', '0', array(), $custom_data )) {
var_dump($product_id);
} else {
var_dump($customer_name);
}
header("Location: ./checkout");
?>
As you see we have programmatically created a cart item using WC_Cart add_to_cart() method. So, all custom data is being saved to Cart as custom cart item data.
And now, we have also placed a code block into functions.php to print these data on Checkout page.
add_action( 'woocommerce_after_order_notes', 'my_custom_checkout_field' );
function my_custom_checkout_field( $checkout ) {
//var_dump($checkout);
global $woocommerce;
echo '<div id="my_custom_checkout_field"><h2>' . __('Child Info') . '</h2>';
foreach ( $woocommerce->cart->get_cart() as $cart_item ) {
if( isset($cart_item['custom_data']) ) {
$custom_data = $cart_item['custom_data'];
echo("<div>Name: <strong>" . $custom_data['name'] . "</strong></div>");
echo("<div>Email: <strong>" . $custom_data['email'] . "</strong></div>");
echo("<div>Gender: <strong>" . $custom_data['sex'] . "</strong></div>");
echo("<div>Age: <strong>" . $custom_data['age'] . "</strong></div>");
}
}
echo '</div>';
}
Now, I am trying to add these data printed on Checkout page to the Order page as well. As my order can't be completed without these data, user need to fill up these data and when he creates order, these data needs to be passed to the Order Summary page as well. And admin also needs to be able to see these data so he can process the order.
I hope this description clears everything and thanks again #LoicTheAztec to make me able to do this. Thank you very much.

Update 2 - Two steps
1) Saving data:
We will save this custom customer data as "Hidden" order "item" meta data and then as order meta data too as this is related to subscriptions with a unique order item:
// Utility function: array of custom customer key/label pairs
function custom_data_keys_labels(){
return array(
'name' => __('Customer name'), 'email' => __('Customer email'),
'sex' => __('Customer gender'), 'age' => __('Customer age'),
);
}
// Add/save custom field value as custom HIDDEN order item meta data
add_action( 'woocommerce_checkout_create_order_line_item', 'custom_field_update_order_item_meta', 20, 4 );
function custom_field_update_order_item_meta( $item, $cart_item_key, $values, $order ) {
if ( ! isset( $values['custom_data'] ) )
return;
$custom_data = $values['custom_data'];
$meta_data = array();
$labels_keys = custom_data_keys_labels();
foreach( $labels_keys as $key => $label ){
if ( isset( $custom_data[$key] ) )
$meta_data[$key] = $custom_data[$key];
}
if ( sizeof( $meta_data ) > 0 )
$item->update_meta_data( __('_customer_data'), $meta_data );
return $cart_item_data;
}
// Add/save custom fields values as custom order meta data
add_action( 'woocommerce_checkout_create_order', 'my_custom_checkout_field_update_order_meta', 20, 2 );
function my_custom_checkout_field_update_order_meta( $order, $data ) {
$order_items = $order->get_items(); // Order itesm
$item = reset($order_items); // Keep only the first order item
$item_data = $item->get_meta( '_customer_data' ); // Get custom customer data
if( is_array($item_data) && sizeof($item_data) > 0 ){
foreach( $item_data as $key => $value ) {
if ( isset( $item_data[$key] ) )
$order->update_meta_data( '_customer_' . $key, $value );
}
// Mark as data saved
$order->update_meta_data( '_customer_data_set', true );
}
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
2) Displaying saved custom data:
The code below also use our utility function custom_data_keys_labels()…
// Order pages (frontend and admin) display
add_filter( 'woocommerce_order_details_after_order_table' , 'display_admin_order_meta_cutom_data', 20, 1 ); // Front
add_action( 'woocommerce_admin_order_data_after_billing_address', 'display_admin_order_meta_cutom_data', 20, 1 ); // Admin
function display_admin_order_meta_cutom_data( $order ){
$labels_keys = custom_data_keys_labels();
if( $order->get_meta( '_customer_data_set' ) ){
if( is_admin() ){ // Admin
echo '<p>';
foreach( $labels_keys as $key => $label ){
if ( $order->get_meta( '_customer_' . $key ) )
echo '<strong>' . $label . ':</strong> ' . $order->get_meta( '_customer_' . $key ) . '<br>';
}
echo '</p>';
}
else { // Front end: order view and Order received (thankyou)
echo '<table class="woocommerce-table"><tbody>';
foreach( $labels_keys as $key => $label ){
if ( $order->get_meta( '_customer_' . $key ) )
echo '<tr><th>' . $label . ':</th><td>' . $order->get_meta( '_customer_' . $key ) . '</td></tr>';
}
echo '</tbody></table>';
}
}
}
// Email notifications display
add_filter( 'woocommerce_email_order_meta_fields' , 'display_email_cutom_data', 20, 3 );
function display_email_cutom_data ( $fields, $sent_to_admin, $order ) {
$labels_keys = custom_data_keys_labels();
if( $order->get_meta( '_customer_data_set' ) ){
foreach( $labels_keys as $key => $label ){
$fields['customer_' . $key] = array(
'label' => $label,
'value' => $order->get_meta( '_customer_' . $key ),
);
}
}
return $fields;
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.

Related

Add custom uploaded image file to WooCommerce product table and invoice PDF

Hello community,
I want to show an image uploaded via custom-field on the product table and finally on the PDF invoice …
I use this solution: Add image file upload field to WooCommerce single products (LoicTheAztec's answer)
to make the image-upload work and now I want to extent this solution to show the image (img-name or img-url) also on the product table (emails) and finally on the PDF invoice…
How can I do this? Can someone help me?
Here ist the code from my functions.php:
// -----------------------------------------
// Image-Upload
// -----------------------------------------
// Display additional product fields (+ jQuery code)
add_action( 'woocommerce_before_variations_form', 'display_additional_product_fields', 9 );
function display_additional_product_fields(){
global $product;
$id = $product->get_id();
if(($id == 41745 ) || ($id == 41769 )):
?>
<p class="_customvariations form-row validate-required" id="image" >
<label for="file_field"><span style="color:#000000; font-weight:600;"><?php echo __("Bild hochladen") . ''; ?></span>
<input type='file' name='image' accept='image/*'>
</label>
</p>
<?php
endif;
}
// Add custom fields data as the cart item custom data
add_filter( 'woocommerce_add_cart_item_data', 'add_custom_fields_data_as_custom_cart_item_data', 10, 2 );
function add_custom_fields_data_as_custom_cart_item_data( $cart_item, $product_id ){
if( isset($_FILES['image']) && ! empty($_FILES['image']) ) {
$upload = wp_upload_bits( $_FILES['image']['name'], null, file_get_contents( $_FILES['image']['tmp_name'] ) );
$filetype = wp_check_filetype( basename( $upload['file'] ), null );
$upload_dir = wp_upload_dir();
$upl_base_url = is_ssl() ? str_replace('http://', 'https://', $upload_dir['baseurl']) : $upload_dir['baseurl'];
$base_name = basename( $upload['file'] );
$cart_item['file_upload'] = array(
'guid' => $upl_base_url .'/'. _wp_relative_upload_path( $upload['file'] ), // Url
'file_type' => $filetype['type'], // File type
'file_name' => $base_name, // File name
'title' => ucfirst( preg_replace('/\.[^.]+$/', '', $base_name ) ), // Title
);
$cart_item['unique_key'] = md5( microtime().rand() ); // Avoid merging items
}
return $cart_item;
}
// Display custom cart item data in cart (optional)
add_filter( 'woocommerce_get_item_data', 'display_custom_item_data', 10, 2 );
function display_custom_item_data( $cart_item_data, $cart_item ) {
if ( isset( $cart_item['file_upload']['title'] ) ){
$cart_item_data[] = array(
'name' => __( 'Individuelles Bild', 'woocommerce' ),
'value' => str_pad($cart_item['file_upload']['title'], 16, ' ', STR_PAD_LEFT) . '…',
);
}
return $cart_item_data;
}
// Save Image data as order item meta data
add_action( 'woocommerce_checkout_create_order_line_item', 'custom_field_update_order_item_meta', 20, 4 );
function custom_field_update_order_item_meta( $item, $cart_item_key, $values, $order ) {
if ( isset( $values['file_upload'] ) ){
$item->update_meta_data( '_img_file', $values['file_upload'] );
}
}
// Admin orders: Display a linked button + the link of the image file
add_action( 'woocommerce_after_order_itemmeta', 'backend_image_link_after_order_itemmeta', 10, 3 );
function backend_image_link_after_order_itemmeta( $item_id, $item, $product ) {
// Only in backend for order line items (avoiding errors)
if( is_admin() && $item->is_type('line_item') && $file_data = $item->get_meta( '_img_file' ) ){
echo '<p>'.__("Individuelles Bild öffnen") . '</p>'; // Optional
echo '<p><code>'.$file_data['guid'].'</code></p>'; // Optional
}
}
// Admin new order email: Display a linked button + the link of the image file
add_action( 'woocommerce_email_after_order_table', 'wc_email_new_order_custom_meta_data', 10, 4);
function wc_email_new_order_custom_meta_data( $order, $sent_to_admin, $plain_text, $email ){
// On "new order" email notifications
// if ( 'new_order' === $email->id ) {
foreach ($order->get_items() as $item ) {
if ( $file_data = $item->get_meta( '_img_file' ) ) {
echo '<p>
'.__("Individuelles Bild herunterladen") . '<br>
<pre><code style="font-size:12px; background-color:#eee; padding:5px;">'.$file_data['guid'].'</code></pre>
</p><br>';
}
}
//}
}
Thank you very much!
Edit:
Thanks Vincenzo Di Gaetano! Great answer, but I miss one little thing:
I would like to show the image-filename on the email attached PDF-invoice (I use woocommerce germanized plugin for german market, but I think this is not relevant because of order item meta).
I have a working custom textfield (code below) which shows the custom text everywhere, also on the email attached PDF-invoice. But I can't get this work for my image file-upload …
// -----------------------------------------
// Custom textfield
// -----------------------------------------
// 1. Show custom input field above Add to Cart
add_action( 'woocommerce_before_variations_form', 'wasentogo_product_add_on', 9 );
function wasentogo_product_add_on() {
global $product;
$id = $product->get_id();
if(($id == 41745 ) || ($id == 41775 )):
$value = isset( $_POST['custom_text_add_on'] ) ? sanitize_text_field( $_POST['_custom_text_add_on'] ) : '';
echo '<p class="_customvariations"><label><span style="color:#000000; font-weight:600;">Text eingeben</span><input name="custom_text_add_on" value="' . $value . '" maxlength="25"><abbr class="required" title="required"></abbr> max. 25 Zeichen</label></p>';
endif;
}
// -----------------------------------------
// 2. Throw error if custom input field empty
add_filter( 'woocommerce_add_to_cart_validation', 'wasentogo_product_add_on_validation', 10, 3 );
function wasentogo_product_add_on_validation( $passed, $product_id, $qty ){
if( isset( $_POST['custom_text_add_on'] ) && sanitize_text_field( $_POST['custom_text_add_on'] ) == '' ) {
wc_add_notice( 'Sie müssen erst einen individuellen Text eingeben!', 'error' );
$passed = false;
}
return $passed;
}
// -----------------------------------------
// 3. Save custom input field value into cart item data
add_filter( 'woocommerce_add_cart_item_data', 'wasentogo_product_add_on_cart_item_data', 10, 2 );
function wasentogo_product_add_on_cart_item_data( $cart_item, $product_id ){
if( isset( $_POST['custom_text_add_on'] ) ) {
$cart_item['custom_text_add_on'] = sanitize_text_field( $_POST['custom_text_add_on'] );
}
return $cart_item;
}
// -----------------------------------------
// 4. Display custom input field value # Cart
add_filter( 'woocommerce_get_item_data', 'wasentogo_product_add_on_display_cart', 10, 2 );
function wasentogo_product_add_on_display_cart( $data, $cart_item ) {
if ( isset( $cart_item['custom_text_add_on'] ) ){
$data[] = array(
'name' => 'Individueller Text',
'value' => sanitize_text_field( $cart_item['custom_text_add_on'] )
);
}
return $data;
}
// -----------------------------------------
// 5. Save custom input field value into order item meta
add_action( 'woocommerce_add_order_item_meta', 'wasentogo_product_add_on_order_item_meta', 10, 2 );
function wasentogo_product_add_on_order_item_meta( $item_id, $values ) {
if ( ! empty( $values['custom_text_add_on'] ) ) {
wc_add_order_item_meta( $item_id, 'Individueller Text', $values['custom_text_add_on'], true );
}
}
// -----------------------------------------
// 6. Display custom input field value into order table
add_filter( 'woocommerce_order_item_product', 'wasentogo_product_add_on_display_order', 10, 2 );
function wasentogo_product_add_on_display_order( $cart_item, $order_item ){
if( isset( $order_item['custom_text_add_on'] ) ){
$cart_item['custom_text_add_on'] = $order_item['custom_text_add_on'];
}
return $cart_item;
}
// -----------------------------------------
// 7. Display custom input field value into order emails
add_filter( 'woocommerce_email_order_meta_fields', 'wasentogo_product_add_on_display_emails' );
function wasentogo_product_add_on_display_emails( $fields ) {
$fields['custom_text_add_on'] = 'Individueller Text';
return $fields;
}
Can someone help me with this part too?
Maybe this screenshot helps to understand what I need. I want to show the img-filename listed on my invoice-PDF
I think I need these two parts from the custom textfield to build something simular for the img-field …
// 5. Save custom input field value into order item meta
add_action( 'woocommerce_add_order_item_meta', 'wasentogo_product_add_on_order_item_meta', 10, 2 );
function wasentogo_product_add_on_order_item_meta( $item_id, $values ) {
if ( ! empty( $values['custom_text_add_on'] ) ) {
wc_add_order_item_meta( $item_id, 'Individueller Text', $values['custom_text_add_on'], true );
}
}
// -----------------------------------------
// 6. Display custom input field value into order table
add_filter( 'woocommerce_order_item_product', 'wasentogo_product_add_on_display_order', 10, 2 );
function wasentogo_product_add_on_display_order( $cart_item, $order_item ){
if( isset( $order_item['custom_text_add_on'] ) ){
$cart_item['custom_text_add_on'] = $order_item['custom_text_add_on'];
}
return $cart_item;
}
// -----------------------------------------
Thank you very much again!
Instead of the woocommerce_email_after_order_table hook you can use woocommerce_order_item_meta_start. You can find it inside the template: /woocommerce/emails/email-order-items.php
The uploaded image link will be added to all email templates, including the customer-invoice.php template.
If $file_data = $item->get_meta( '_img_file' ) exists you can access the following information:
$file_data['file_name'] (including the image file extension)
$file_data['title'] (without the image file extension)
Since the woocommerce_order_item_meta_start hook doesn't have access to the email object (to check the template id) I thought about adding the image title to all email templates. Through the woocommerce_email_styles hook you can show or hide it based on the email template.
// shows the uploaded image of the order item
add_action( 'woocommerce_order_item_meta_start', 'show_uploaded_image_in_email_templates', 10, 4 );
function show_uploaded_image_in_email_templates( $item_id, $item, $order, $plain_text ) {
if ( $file_data = $item->get_meta( '_img_file' ) ) {
echo '<p>
' . __( "Individuelles Bild herunterladen" ) . '
<pre><code style="font-size:12px; background-color:#eee; padding:5px;">' . $file_data['guid'] . '</code></pre>
<span class="custom_image_filename">' . $file_data['title'] . '</span>
</p>';
}
}
// show or hide the image title based on the email template
add_filter( 'woocommerce_email_styles', 'add_woocommerce_email_styles', 10, 2 );
function add_woocommerce_email_styles( $css, $email ) {
if ( $email->id == 'customer_invoice' ) {
$css .= '.custom_image_filename { display: inline-block; }';
} else {
$css .= '.custom_image_filename { display: none; }';
}
return $css;
}
The invoice email will be:
All other emails:
The code has been tested and works. Add it to your active theme's functions.php.

Display all available shipping methods and costs on WooCommerce Order pages

Previous / Related Question: Display ALL available shipping methods for each specific order on admin edit order pages in Woocommerce
Currently in my WooCommerce based site, I am wanting to display the available shipping methods and prices on the order edit page.
It does not display the data as I want. For example, the output of my code so far results in:
Method 1
Method 2
Method 3
Price 1
Price 2
Price 3
When alternatively, I would like for it to display like this:
Method 1 - $Price 1
Method 2 - $Price 2
Method 3 - $Price 3
I understand why it is displaying this way, but I was curious how I could iterate the loops at the same time and format them, rather than one after the other.
This is my code so far:
add_action( 'woocommerce_admin_order_data_after_shipping_address', 'action_woocommerce_admin_order_data_after_shipping_address', 10, 1 );
function action_woocommerce_admin_order_data_after_shipping_address( $order ){
// Get meta
$rate_labels = $order->get_meta( '_available_shipping_methods' );
$rate_costs = $order->get_meta( '_available_shipping_method_cost' );
$methods = array ( $rate_labels, $rate_costs );
// True
if ( $rate_labels ) {
// Loop
echo '<p><strong>Shipping Methods: </strong>';
foreach( $rate_labels as $rate_label ) {
// Output
echo '<p>' . $rate_label . '</p>';
}
foreach( $rate_costs as $rate_cost ) {
// Output
echo '<p> $' . $rate_cost . '</p>';
}
}
}
The following slight different code will display the label and the cost of all available shipping methods (in one array | one foreach loop):
add_action( 'woocommerce_checkout_create_order', 'action_wc_checkout_create_order' );
function action_wc_checkout_create_order( $order ) {
$shipping_data = array(); // Initializing
// Get shipping packages keys from cart
$packages_keys = (array) array_keys(WC()->cart->get_shipping_packages());
// Loop through shipping packages keys (when cart is split into many shipping packages)
foreach( $packages_keys as $key ){
// Get available shipping rates from WC_Session
$shipping_rates = WC()->session->get('shipping_for_package_'.$key)['rates'];
// Loop through shipping rates
foreach( $shipping_rates as $rate_key => $rate ){
// Set all related shipping rate data in the array
$shipping_data[] = array(
'id' => $rate_key,
'method_id' => $rate->method_id,
'instance_id' => (int) $rate->instance_id,
'label' => $rate->label,
'cost' => (float) $rate->cost,
'taxes' => (array) $rate->taxes,
'package_key' => (int) $key,
);
}
}
// Save shipping data as order custom field
if( ! empty($shipping_data) ) {
$order->update_meta_data( '_shipping_data', $shipping_data );
}
}
add_action( 'woocommerce_admin_order_data_after_shipping_address', 'available_shipping_rates_after_shipping_address' );
function available_shipping_rates_after_shipping_address( $order ) {
// Get shipping rates custom meta data
$shipping_data = $order->get_meta( '_shipping_data' );
if ( ! empty($shipping_data) ) {
echo '<p><strong>Shipping Methods: </strong><br>';
// Loop through shipping rates data
foreach( $shipping_data as $rate ) {
// Calculate cost with taxes
$rate_cost = $rate['cost'] + array_sum($rate['taxes']);
// Output
echo $rate['label'] . ( $rate_cost > 0 ? ': '. wc_price($rate_cost) : '' ) . '<br>';
}
echo '</p>';
}
}
Code goes in functions.php file of your active child theme (or active theme). Tested and works.
In case anyone happens to have the same question as I did, here is how I did it:
function action_woocommerce_admin_order_data_after_shipping_address( $order ) {
// Get meta
$rate_labels = $order->get_meta( '_available_shipping_methods' );
$rate_costs = $order->get_meta( '_available_shipping_method_cost' );
$methods = array ( $rate_labels, $rate_costs );
// True
if ( $rate_labels ) {
// Loop
echo '<p><strong>Shipping Methods: </strong>';
foreach(array_combine($rate_labels, $rate_costs) as $rate_label => $rate_cost) {
echo '<p>' . $rate_label . ' - $' . $rate_cost . '</p>';
}
}
}

get HSN Code in Pdf invoice in woocommerce

I tried searching online and contacting the plugin author, and he said that it can be retrieved using the WordPress get post meta.
I'm using a plugin called woo-gst to add a product attribute called 'prod_hsn_id' which add a filed called HSN code at product edit page, I'm also using a pdf invoice plugin called woocommerce pdf invoice to generate pdf invoice. Now I want to display the HSN code on the invoice.
<?php foreach ( $this->order->get_items( 'line_item' ) as $item_id => $item ) {
$product = $this->order->get_product_from_item( $item ); ?>
<tr class="product-row">
<td>
<?php echo esc_html( $item['name'] );
global $wpdb;
$hidden_order_itemmeta = apply_filters( 'woocommerce_hidden_order_itemmeta', array(
'_qty',
'_tax_class',
'_product_id',
'_variation_id',
'_line_subtotal',
'_line_subtotal_tax',
'_line_total',
'_line_tax',
'_wc_cog_item_cost',
'_wc_cog_item_total_cost',
'_reduced_stock',
) );
$hidden_order_itemmeta = apply_filters( 'bewpi_hidden_order_itemmeta', $hidden_order_itemmeta );
foreach ( $this->order->has_meta( $item_id ) as $meta ) {
// Skip hidden core fields.
if ( in_array( $meta['meta_key'], $hidden_order_itemmeta, true ) ) {
continue;
}
// Skip serialised meta.
if ( is_serialized( $meta['meta_value'] ) ) {
continue;
}
// Get attribute data.
if ( taxonomy_exists( wc_sanitize_taxonomy_name( $meta['meta_key'] ) ) ) {
$term = get_term_by( 'slug', $meta['meta_value'], wc_sanitize_taxonomy_name( $meta['meta_key'] ) );
$meta['meta_key'] = wc_attribute_label( wc_sanitize_taxonomy_name( $meta['meta_key'] ) );
$meta['meta_value'] = isset( $term->name ) ? $term->name : $meta['meta_value'];
} else {
$meta['meta_key'] = apply_filters( 'woocommerce_attribute_label', wc_attribute_label( $meta['meta_key'], $product ), $meta['meta_key'] );
}
echo '<div class="item-attribute"><span style="font-weight: bold;">' . wp_kses_post( rawurldecode( $meta['meta_key'] ) ) . ': </span>' . wp_kses_post( rawurldecode( $meta['meta_value'] ) ) . '</div>';
}
$field_name = 'hsn_prod_id';
// then loop through items in order and print each custom field
foreach ( $this->order->get_items() as $item_id => $item ) {
if ( $product = $this->order->get_product_from_item( $item ) ) {
$location = $product->get_meta( $field_name );
if ( !empty($hsn_prod_id) ) {
echo '<div class="product-location">HSN Code: '.$hsn_prod_id.'</div>';
}
}
}
?>
</td>```
above is the code In the Invoice Template file I'm trying to display the HSN Code.
I was working on the same today, did not find a solution, and finally here is what I did and it worked well.
We need to pull the data from postmeta table, and to get that you need to have the post id for the product.
If you do $product_id = $product->get_id() this will return the id (they call it Variation ID). The post id and product id are not same.
To get the post id for the product you need to subtract -1 form the $product_id.
This is the line you can use to get the HSN code :
$hsn_prod_id = get_post_meta( $product->get_id()-1, 'hsn_prod_id', true );
UPDATE:
The logic for subtract -1 depends on the Wordpress,WooCommerce version may be. I have tried the same in a fresh setup where it didn't work. In there, I just used this $product_id = $product->get_id() value and it worked.
You can figure it by looking at the id for the product. The id it shows in the UI and the ID you see in the URL when you edit an order. If both are not the same you need to see the sequence difference and take a call.

Save custom values from Woocommerce checkout as custom order meta data

In Woocommerce, I have a checkout page (review order) with custom data.
I need to find a hook to register some custom data ($totaleiva_1 and $totalefinitocarrello) in the order and then I have to send them in email new order.
I'm not able to make it for instance. Any advice or help please?
Edit - That is my code:
$totaleiva_1 = 0;
$items = $woocommerce->cart->get_cart();
foreach($items as $item ) {
$totaleiva_1 += $totalForSebeneArray[$item ['data']->get_id()];
}
$totaleiva_1 = number_format($totaleiva_1, 2, '.', '');
$totalefinitocarrello = $totaleiva_1 + $total; echo "€";
echo $totalefinitocarrello;
You will need to add and display 2 hidden fields with the 2 desired values (to be able to get and save them as custom order meta data on submission).
Also your code is a bit outdated and can be simplified.
Here is your revisited code:
$totaleiva_1 = 0;
// Loop through cart items
foreach (WC()->cart->get_cart() as $cart_item ) {
$totale_iva_1 += $totalForSebeneArray[$cart_item['data']->get_id()];
}
$totale_finito_carrello = wc_price( $totale_iva_1 + $total );
echo $totale_finito_carrello;
// Display 2 hidden input fields
echo '<input type="hidden" id="totaleiva1" name="totaleiva1" value="'.$totale_iva_1.'">
<input type="hidden" id="tfcarrello" name="tfcarrello" value="'.$totale_finito_carrello.'">';
Then you will save that data as custom order meta data using:
add_action('woocommerce_checkout_create_order', 'save_order_custom_meta_data', 10, 2 );
function save_order_custom_meta_data( $order, $data ) {
if( isset($_POST['totaleiva1']) && ! empty($_POST['totaleiva1']) ) {
$order->update_meta_data( '_totale_iva_1', esc_attr( $_POST['totaleiva1'] ) );
}
if( isset($_POST['tfcarrello']) && ! empty($_POST['tfcarrello']) ) {
$order->update_meta_data( '_totale_fin_carrello', esc_attr( $_POST['tfcarrello'] ) );
}
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
Now to get that data and use it anywhere:
1) from the order ID variable, use:
$totaleiva_1 = get_post_meta( $order_id, '_totale_iva_1', true );
$totalefinitocarrello = get_post_meta( $order_id, '_totale_fin_carrello', true );
2 from the WC_Order Object:
$totaleiva_1 = $order->get_meta('_totale_iva_1');
$totalefinitocarrello = $order->get_meta('_totale_fin_carrello');
To display those in Woocommerce new order notification, ou will use:
add_action( 'woocommerce_email_order_details', 'email_order_details_action_callback', 5, 4 );
function email_order_details_action_callback( $order, $sent_to_admin, $plain_text, $email ){
if( $email->id === 'new_order' ) {
if ( $tiva1 = $order->get_meta('_totale_iva_1') ) {
echo '<p>'. __("Totale IVA") . ': ' . $tiva1 . '</p>';
}
if ( $tfcarr = $order->get_meta('_totale_fin_carrello') ) {
echo '<p>'. __("Totale finito carrello") . ': ' . $tfcarr . '</p>';
}
}
}
Display the fields in Admin order pages:
add_action( 'woocommerce_admin_order_data_after_billing_address', 'admin_order_after_billing_address_callback', 10, 1 );
function admin_order_after_billing_address_callback( $order ){
if ( $tiva1 = $order->get_meta('_totale_iva_1') ) {
echo '<p>'. __("Totale IVA") . ': ' . $tiva1 . '</p>';
}
if ( $tfcarr = $order->get_meta('_totale_fin_carrello') ) {
echo '<p>'. __("Totale finito carrello") . ': ' . $tfcarr . '</p>';
}
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
Now I think that all your IVA custom calculations can be handled by Woocommerce using available settings possibilities, as you are making things Much more complicated everywhere.

Auto add order item custom meta data to order note in WooCommerce

In WooCommerce, with this code, i'm using product custom fields to add some custom metadata to order items when making a purchase. I also need to auto add some of this custom metadata to the customer Order Notes (basically so it gets sent to my shipping fulfillment service).
Below is the code i'm currently using:
add_action('woocommerce_checkout_create_order', 'before_checkout_create_order', 20, 2);
function before_checkout_create_order( $order, $data ) {
$items_in_order=array();
foreach( $order->get_items() as $item_id => $item ) {
$product = $order->get_product_from_item( $item );
$text = $product->get_sku();
$bags_count = array(
'neutral_bags' => 0,
'bright_bags' => 0,
'pastel_bags' => 0,
'reg_tendons' => 0,
'gid_tendons' => 0
);
$meta_key1 = __('Neutral Bags', 'woocommerce');
$meta_key2 = __('Bright Bags', 'woocommerce');
$meta_key3 = __('Pastel Bags', 'woocommerce');
$meta_key4 = __('Reg Tendons', 'woocommerce');
$meta_key5 = __('GID Tendons', 'woocommerce');
$bags_count['neutral_bags'] += wc_get_order_item_meta( $item_id, $meta_key1, true );
$bags_count['bright_bags'] += wc_get_order_item_meta( $item_id, $meta_key2, true );
$bags_count['pastel_bags'] += wc_get_order_item_meta( $item_id, $meta_key3, true );
$bags_count['reg_tendons'] += wc_get_order_item_meta( $item_id, $meta_key4, true );
$bags_count['gid_tendons'] += wc_get_order_item_meta( $item_id, $meta_key5, true );
$text .= ' | ' . $bags_count['neutral_bags'];
$text .= ' | ' . $bags_count['bright_bags'];
$text .= ' | ' . $bags_count['pastel_bags'];
$text .= ' | ' . $bags_count['reg_tendons'];
$text .= ' | ' . $bags_count['gid_tendons'];
array_push( $items_in_order , $text );
}
$text = implode('<br/>', $items_in_order );
// get customer note
$note = $order->get_customer_note();
// Merging existing customer note with bags count (if it exist)
$note = empty( $note ) ? $text : $note . '<br/>' . $text . ' V1';
// Set the new customer note before saving
$order->set_customer_note( $note );
}
As per the below screenshot, you'll see the data is saved to the product, but doesn't get pulled into the order notes section correctly.
Any ideas why this might be?
As this is related to your other question and my answer there, so the meta keys are a bit different:
neutral_bag_count meta key is replaced by: Neutral Bags,
bright_bag_count meta key is replaced by: Bright Bags.
Update: I have revisited completely all your existing code, avoiding repetitions, making it more effective and compact.
I have set your custom fields keys and labels in a function that I call in all other hooked functions and I use foreach loops everywhere now. If you want to change a label, you will do it just once in this utility function.
The new code:
// Utility function (keys + translatable labels)
function bags_and_sheets_labels_keys(){
$domain = 'woocommerce';
return array(
'neutral_bag' => __('Neutral Bags', $domain),
'bright_bag' => __('Bright Bags', $domain),
'pastel_bag' => __('Pastel Bags', $domain),
'reg_tendon' => __('Regular Tendon Sheets', $domain),
'gid_tendon' => __('GID Tendon Sheets', $domain),
);
}
// Add custom fields to single product pages
add_action( 'woocommerce_before_add_to_cart_button', 'add_custom_fields_single_product', 20 );
function add_custom_fields_single_product(){
global $product;
$bags = bags_and_sheets_labels_keys();
?>
<div class="product-custom-fields">
<?php foreach($bags as $key => $value ): ?>
<input type="text" placeholder="<?php echo $value; ?>" name="<?php echo $key; ?>">
<?php endforeach; ?>
</div>
<div class="clear"></div>
<?php
}
// Save custom fields to cart object
add_filter( 'woocommerce_add_cart_item_data', 'save_custom_fields_cart_item_data', 10, 2 );
function save_custom_fields_cart_item_data( $cart_item_data, $product_id ){
$bags = bags_and_sheets_labels_keys();
foreach($bags as $key => $value ):
if(isset($_POST[$key]))
$cart_item_data['custom_data'][$key] = sanitize_text_field($_POST[$key]);
endforeach;
$cart_item_data['custom_data']['unique_key'] = md5( microtime().rand() );
WC()->session->set( 'custom_data', $cart_item_data['custom_data'] );
return $cart_item_data;
}
// Display custom fields in cart items (cart and checkout pages)
add_filter( 'woocommerce_get_item_data', 'display_custom_fields_cart_item_data', 10, 2 );
function display_custom_fields_cart_item_data($item_data, $cart_item){
if( ! array_key_exists( 'custom_data', $cart_item ) )
return $item_data;
$bags = bags_and_sheets_labels_keys();
foreach($bags as $key => $value ):
if( array_key_exists( $key, $cart_item['custom_data'] ) )
$item_data[] = array(
'key' => $value,
'value' => $cart_item['custom_data'][$key]
);
endforeach;
return $item_data;
}
// Save and display custom fields in order item meta
add_action( 'woocommerce_add_order_item_meta', 'add_custom_fields_order_item_meta', 20, 3 );
function add_custom_fields_order_item_meta( $item_id, $cart_item, $cart_item_key ) {
$bags = bags_and_sheets_labels_keys();
foreach($bags as $key => $meta_key_label ):
if( array_key_exists($key, $cart_item['custom_data']) )
wc_update_order_item_meta( $item_id, $meta_key_label, $cart_item['custom_data'][$key] );
endforeach;
}
// Add products SKUs and cutom fields as formated data in customer note
add_action('woocommerce_checkout_update_order_meta', 'after_checkout_create_order', 20, 2);
function after_checkout_create_order( $order_id, $data ) {
$skus = $counts = $output = array();
$bags = bags_and_sheets_labels_keys();
// Get the WC_Order Object (instance)
$order = wc_get_order($order_id);
// Loop through order items
foreach( $order->get_items() as $item_id => $item ) {
$product = $item->get_product();
$skus[] = $product->get_sku(); // Set SKUs in an array
// Add metakeys to the array (if they exist)
foreach( $bags as $meta_key_label ){
$item_meta = wc_get_order_item_meta( $item_id, $meta_key_label, true );
if( ! empty($item_meta) ){
$counts[$meta_key_label] += $item_meta;
$count = true;
}
}
}
// Add Products SKUs
$text = __('Products SKUs', 'woocommerce') . ': ' . implode(' - ', $skus ). ' | ';
// Get each custom item meta data count (if they exist)
if( isset($count)){
foreach( $counts as $key_label => $value ){
$output[] = $key_label . __(' count: ', 'woocommerce') . $value;
}
// Format all data as a text string
$text .= implode(' - ', $output );
}
// Get customer note
$note = $order->get_customer_note();
// Merging existing customer note with bags count (if it exist)
$note = empty( $note ) ? $text : $note . '<br/>' . $text;
// Set the new customer note before saving
$order->set_customer_note( $note );
$order->save();
}
Code goes in function.php file of the active child theme (or active theme).
Tested and works as expected.

Categories