Im trying to do the following:
I added a text field on the checkout where the customer has to enter a code, if the code is what I want, I apply a discount to the products depending on the category.
The problem I have until now is that I can only apply a fixed fee, I want to apply a percentage fee depending on the individual product price (depending on each product).
Also, right now the text field has not validation on the front end for the customer, how could I add that? I would like for the customer to know if he put the right code or not before checking out.
Thank you!
function custom_donation_form() { ?>
<input type="number" name="card_code" class="input-text" style="display: inline-block !important; width: auto !important;" placeholder="<?php echo esc_attr( 'Card Code:', 'woocommerce' ); ?>" value="" />
<?php
}
add_action( 'woocommerce_review_order_before_submit', 'custom_donation_form', 99 );
function donation_add_fee( $cart ) {
if( isset( $_REQUEST["card_code"] ) && !empty( $_REQUEST["card_code"] ) ) {
if( is_numeric( $_REQUEST["card_code"] ) && $_REQUEST["card_code"] == 1000 ) {
// You need to enter your fees here, in `category slug` => `fee amount` format
$fees = array(
'botas' => 10);
foreach ( $cart->get_cart() as $cart_item_key => $values ) {
$product_categories = get_the_terms( $values['product_id'], 'product_cat' );
if ( $product_categories && ! is_wp_error( $product_categories ) ) {
$product_categories = wp_list_pluck( $product_categories, 'slug' );
foreach ( $product_categories as $product_category ) {
if ( ! empty( $fees[ $product_category ] ) ) {
$name = 'Category fee: ' . get_the_title( $values['product_id'] );
$amount = $fees[ $product_category ];
$taxable = true;
$tax_class = '';
$cart->add_fee( $name, $amount, $taxable, $tax_class );
}
}
}
}
}
}
}
add_action( 'woocommerce_cart_calculate_fees', 'donation_add_fee', 1 );
The following code works and adds the purchase note to the cart items.
add_filter( 'woocommerce_get_item_data', 'wc_test', 10, 2 );
function wc_test ( $other_data, $cart_item ){
$post_data = get_post( $cart_item['product_id'] );
$other_data[] = array( 'name' => $post_data->_purchase_note );
return $other_data;
}
However, I always get ":" as result, for products that don't have a note.
I also need to add a specific attribute aswell under the note.
Any advice?
The colon is automatically added to the key. Because you only use name, this will be added at the end.
You can use get_purchase_note()
So you get:
// Display on cart & checkout pages
function filter_woocommerce_get_item_data( $item_data, $cart_item ) {
// Get an instance of the WC_Product object
$product = $cart_item['data'];
// Get purchase_note
$purchase_note = $product->get_purchase_note();
// Get the product attribute value (adjust if desired)
$attribute = $product->get_attribute( 'pa_color' );
// When empty
if ( empty ( $attribute ) ) {
$attribute = '';
}
// NOT empty
if ( ! empty( $purchase_note ) ) {
$item_data[] = array(
'key' => __( 'Note', 'woocommerce' ),
'value' => $purchase_note . $attribute,
'display' => $purchase_note . $attribute,
);
}
return $item_data;
}
add_filter( 'woocommerce_get_item_data', 'filter_woocommerce_get_item_data', 10, 2 );
I would like to delete an existing WooComerce item and add x new WooComerce items from it. It depends on how high the $ item-> quantity is.
My code looks like this:
add_action( 'woocommerce_thankyou', 'my_change_status_function', 10, 1 );
function my_change_status_function( $order_id ) {
$order = new WC_Order( $order_id );
foreach ( $order->get_items() as $item_id => $item ) {
if ( $item->get_quantity() > 1 ) {
wc_delete_order_item( $item_id );
for ( $i = 1; $i <= $item->get_quantity(); $i++ ) {
$new_item_ids = woocommerce_add_order_item(
$order_id,
array(
'order_item_name' => $item->get_name() . '_' . $i,
'order_item_type' => 'line_item',
)
);
if ( $new_item_ids ) {
foreach ( $metavalues as $key => $value ) {
wc_add_order_item_meta( $new_item_ids, $key, $value );
}
}
}
}
}
}
The point where I'm a little bit desperate is here:
if ( $new_item_ids ) {
foreach ( $metavalues as $key => $value ) { <-- I dont know where i get the $metavalues
wc_add_order_item_meta( $new_item_ids, $key, $value );
}
}
I've already tried this post from Stackoverflow:
WooCommerce Add item to Order
Could someone help me?
Thank you in advance
Since WooCommerce 3 your code is outdated and can be simplified using some WC_Abstract_Order methods. Also you should better use woocommerce_checkout_order_created hook instead, that is triggered after order is created:
add_action( 'woocommerce_checkout_order_created', 'separate_order_items_by_quantity' );
function separate_order_items_by_quantity( $order ) {
$items_processed = false; // Initializing
// Loop through order items
foreach ( $order->get_items() as $item_id => $item ) {
$quantity = $item->get_quantity(); // Get quantity
$product = $item->get_product(); // Get the WC_Product Object
// When item has quatity more than 1, separete items by quantity
if ( $item->get_quantity() > 1 ) {
$order->remove_item( $item_id ); // Remove item
$items_processed = true; // Flag it
// Loop through quantity
for ( $i = 1; $i <= $quantity; $i++ ) {
// Add the same productb (separated) n times the quantity
$new_item_id = $order->add_product( $product, 1, array(
'_custom_index' => $i, // (required) to have separated products
'name' => $product->get_name() . ' ' . $i, // (optional) change product name appending the index
'Some meta key' => 'some meta value', // (optional) Here you can set a custom meta key and its value (custom order item meta data) … To hide it from customer the metakey should start with an underscore.
) );
}
}
}
if( $items_processed ) {
$order->apply_changes(); // Merge changes with data and clear
$order->save(); // Save data
}
}
Code goes in functions.php file of the active child theme (or active theme). Tested and works.
based on Save product custom-field as custom order item metadata for WooCommerce admin manual orders
I have these codes that show the weight and dimensions of the product for orders placed from the frontend, but I can't get them to work for orders that are placed manually from the backend. When I add the products I do not see the weight and dimensions and they are not shown on the page to pay the order, in order-received and email.
That is my code:
Show and Save the weight of the product everywhere.
// Display the cart item weight in cart and checkout pages
add_filter( 'woocommerce_get_item_data', 'display_custom_item_data', 10, 2 );
function display_custom_item_data( $cart_item_data, $cart_item ) {
if ( $cart_item['data']->get_weight() > 0 ){
$cart_item_data[] = array(
'name' => __( 'Weight', 'woocommerce' ),
'value' => $cart_item['data']->get_weight() . ' ' . get_option('woocommerce_weight_unit')
);
}
return $cart_item_data;
}
// Save and Display the order item weight (everywhere)
add_action( 'woocommerce_checkout_create_order_line_item', 'display_order_item_data', 20, 4 );
function display_order_item_data( $item, $cart_item_key, $values, $order ) {
if ( $values['data']->get_weight() > 0 ){
$item->update_meta_data( __( 'Weight', 'woocommerce' ), $values['data']->get_weight() . ' ' . get_option('woocommerce_weight_unit') );
}
}
Show and Save product dimensions everywhere.
// Display the cart item weight in dimensions and checkout pages
add_filter( 'woocommerce_get_item_data', 'display_custom_item_data1', 10, 2 );
function display_custom_item_data1( $cart_item_data, $cart_item ) {
if ( $cart_item['data']->has_dimensions() > 0 ){
$cart_item_data[] = array(
'name' => __( 'Dimensions', 'woocommerce' ),
'value' => $cart_item['data']->get_dimensions() . ' ' . get_option('woocommerce_dimensions_unit')
);
}
return $cart_item_data;
}
// Save and Display the order item dimensions (everywhere)
add_action( 'woocommerce_checkout_create_order_line_item', 'display_order_item_data1', 20, 4 );
function display_order_item_data1( $item, $cart_item_key, $values, $order ) {
if ( $values['data']->has_dimensions() > 0 ){
$item->update_meta_data( __( 'Dimensions', 'woocommerce' ), $values['data']->get_dimensions() . ' ' . get_option('woocommerce_dimensions_unit') );
}
}
add_filter( 'woocommerce_format_dimensions', 'custom_formated_product_dimentions', 10, 2 );
function custom_formated_product_dimentions( $dimension_string, $dimensions ){
$text_domain = 'woocommerce';
$dimension_unit = get_option('woocommerce_dimensions_unit');
if ( empty( $dimension_string ) )
return __( 'N/A', $text_domain );
// Set here your new array of dimensions based on existing keys/values
$new_dimentions = array(
__('Largo:', $text_domain) => $dimensions['length'],
__('Ancho:', $text_domain) => $dimensions['width'],
__('Alto:', $text_domain) => $dimensions['height'],
);
$dimensions = array_filter( array_map( 'wc_format_localized_decimal', $new_dimentions ) );
foreach( $dimensions as $key => $dimention ){
$dimensions[$key] = ucfirst($key) . ' ' . $dimention. ' ' . get_option( 'woocommerce_dimension_unit' );
}
return implode( ' × ', $dimensions);
}
To display product weight and dimensions as custom order item meta data on manual backend orders, you will use woocommerce_before_save_order_item dedicated action hook as follow:
add_action( 'woocommerce_before_save_order_item', 'action_before_save_order_item_callback' );
function action_before_save_order_item_callback( $item ) {
// 1. WEIGHT
$weight = $item->get_meta('Weight');
// If custom meta data is not saved as order item
if ( empty($weight) ) {
// Get the WC_Product Object
$product = $item->get_product();
// Get custom meta data from the product
$weight = $product->get_weight();
$weight_unit = get_option('woocommerce_weight_unit');
// Save it as custom order item (if defined for the product)
if ( $weight ) {
$item->update_meta_data( 'Weight', $weight . ' ' . $weight_unit );
}
}
// 2. DIMENSIONS
$dimensions = $item->get_meta('Dimensions');
// If custom meta data is not saved as order item
if ( empty($dimensions) ) {
// Get the WC_Product Object
$product = $item->get_product();
// Get custom meta data from the product
$dimensions = $product->get_dimensions();
$dim_unit = get_option('woocommerce_dimensions_unit');
// Save it as custom order item (if defined for the product)
if ( $dimensions ) {
$item->update_meta_data( 'Dimensions', $dimensions . ' ' . $dim_unit );
}
}
}
Code goes in functions.php file of your active child theme (or active theme). Tested and works.
I've added two customer date inputs to the single product page. I need them to be required and validated before adding to the cart, and would also like the dates to be shown on the cart/checkout page and in the order emails.
I found the snippets needed here, however it was only for one custom field so I adjusted to make it for two: https://www.kathyisawesome.com/add-a-custom-field-to-woocommerce-product/
The input fields show up fine, but once you hit the Add to Cart button it doesn't carry throughout the order.
Here is the code used in my functions.php file:
/*
* Display inputs on single product page
*/
function amp_custom_option_1(){
$value = isset( $_POST['_est_delivery'] ) ? sanitize_text_field( $_POST['_est_delivery'] ) : '';
printf( '<div id="dates"><div class="delivery"><label>%s</label><input name="_est_delivery" value="%s" type="date" required /></div>', __( 'Estimated Delivery Date:', 'amp-plugin-textdomain-1' ), esc_attr( $value ) );
}
add_action( 'woocommerce_before_add_to_cart_form', 'amp_custom_option_1', 9 );
function amp_custom_option_2(){
$value = isset( $_POST['_est_pickup'] ) ? sanitize_text_field( $_POST['_est_pickup'] ) : '';
printf( '<div class="pickup"><label>%s</label><input name="_est_pickup" value="%s" type="date" required /></div></div>', __( 'Estimated Pickup Date:', 'amp-plugin-textdomain-2' ), esc_attr( $value ) );
}
add_action( 'woocommerce_before_add_to_cart_form', 'amp_custom_option_2', 9 );
/*
* Validate when adding to cart
*/
function amp_add_to_cart_validation_1($passed, $product_id, $qty){
if( isset( $_POST['_est_delivery'] ) && sanitize_text_field( $_POST['_est_delivery'] ) == '' ){
$product = wc_get_product( $product_id );
wc_add_notice( sprintf( __( '%s cannot be added to the cart until you enter a delivery date.', 'amp-plugin-textdomain-1' ), $product->get_title() ), 'error' );
return false;
}
return $passed;
}
add_filter( 'woocommerce_add_to_cart_validation', 'amp_add_to_cart_validation_1', 10, 3 );
function amp_add_to_cart_validation_2($passed, $product_id, $qty){
if( isset( $_POST['_est_pickup'] ) && sanitize_text_field( $_POST['_est_pickup'] ) == '' ){
$product = wc_get_product( $product_id );
wc_add_notice( sprintf( __( '%s cannot be added to the cart until you enter a pickup date.', 'amp-plugin-textdomain-2' ), $product->get_title() ), 'error' );
return false;
}
return $passed;
}
add_filter( 'woocommerce_add_to_cart_validation', 'amp_add_to_cart_validation_2', 10, 3 );
/*
* Add custom data to the cart item
*/
function amp_add_cart_item_data_1( $cart_item, $product_id ){
if( isset( $_POST['_est_delivery'] ) ) {
$cart_item['est_delivery'] = sanitize_text_field( $_POST['_est_delivery'] );
}
return $cart_item;
}
add_filter( 'woocommerce_add_cart_item_data', 'amp_add_cart_item_data_1', 10, 2 );
function amp_add_cart_item_data_2( $cart_item, $product_id ){
if( isset( $_POST['_est_pickup'] ) ) {
$cart_item['est_pickup'] = sanitize_text_field( $_POST['_est_pickup'] );
}
return $cart_item;
}
add_filter( 'woocommerce_add_cart_item_data', 'amp_add_cart_item_data_2', 10, 2 );
/*
* Load cart data from session
*/
function amp_get_cart_item_from_session_1( $cart_item, $values ) {
if ( isset( $values['est_delivery'] ) ){
$cart_item['est_delivery'] = $values['est_delivery'];
}
return $cart_item;
}
add_filter( 'woocommerce_get_cart_item_from_session', 'amp_get_cart_item_from_session_1', 20, 2 );
function amp_get_cart_item_from_session_2( $cart_item, $values ) {
if ( isset( $values['est_pickup'] ) ){
$cart_item['est_pickup'] = $values['est_pickup'];
}
return $cart_item;
}
add_filter( 'woocommerce_get_cart_item_from_session', 'amp_get_cart_item_from_session_2', 20, 2 );
/*
* Add meta to order item
*/
function amp_add_order_item_meta_1( $item_id, $values ) {
if ( ! empty( $values['est_delivery'] ) ) {
woocommerce_add_order_item_meta( $item_id, 'est_delivery', $values['est_delivery'] );
}
}
add_action( 'woocommerce_add_order_item_meta', 'amp_add_order_item_meta_1', 10, 2 );
function amp_add_order_item_meta_2( $item_id, $values ) {
if ( ! empty( $values['est_pickup'] ) ) {
woocommerce_add_order_item_meta( $item_id, 'est_pickup', $values['est_pickup'] );
}
}
add_action( 'woocommerce_add_order_item_meta', 'amp_add_order_item_meta_2', 10, 2 );
/*
* Get item data to display in cart
*/
function amp_get_item_data_1( $other_data, $cart_item ) {
if ( isset( $cart_item['est_delivery'] ) ){
$other_data[] = array(
'name' => __( 'Estimated Delivery Date:', 'amp-plugin-textdomain-1' ),
'value' => sanitize_text_field( $cart_item['est_delivery'] )
);
}
return $other_data;
}
add_filter( 'woocommerce_get_item_data', 'amp_get_item_data_1', 10, 2 );
function amp_get_item_data_2( $other_data, $cart_item ) {
if ( isset( $cart_item['est_pickup'] ) ){
$other_data[] = array(
'name' => __( 'Estimated Pickup Date', 'amp-plugin-textdomain-2' ),
'value' => sanitize_text_field( $cart_item['est_pickup'] )
);
}
return $other_data;
}
add_filter( 'woocommerce_get_item_data', 'amp_get_item_data_2', 10, 2 );
/*
* Show custom field in order overview
*/
function amp_order_item_product_1( $cart_item, $order_item ){
if( isset( $order_item['est_delivery'] ) ){
$cart_item_meta['est_delivery'] = $order_item['est_delivery'];
}
return $cart_item;
}
add_filter( 'woocommerce_order_item_product', 'amp_order_item_product_1', 10, 2 );
function amp_order_item_product_2( $cart_item, $order_item ){
if( isset( $order_item['est_pickup'] ) ){
$cart_item_meta['est_pickup'] = $order_item['est_pickup'];
}
return $cart_item;
}
add_filter( 'woocommerce_order_item_product', 'amp_order_item_product_2', 10, 2 );
/*
* Add the field to order emails
*/
function amp_email_order_meta_fields_1( $fields ) {
$fields['est_delivery'] = __( 'Estimated Delivery Date:', 'amp-plugin-textdomain-1' );
return $fields;
}
add_filter('woocommerce_email_order_meta_fields', 'amp_email_order_meta_fields_1');
function amp_email_order_meta_fields_2( $fields ) {
$fields['est_delivery'] = __( 'Estimate Pickup Date:', 'amp-plugin-textdomain-2' );
return $fields;
}
add_filter('woocommerce_email_order_meta_fields', 'amp_email_order_meta_fields_2');
I'm not sure what is wrong with my code? Any help is appreciated.
There was some errors and mistakes. I have changed and removed some hooks, remove unnecessary code, merged functions, revisited all your code. As Your 2 dates fields are on single product pages, they will be related to cart items and order items (so order items meta data).
I have set your 2 date fields slugs and labels in the first function, inside an array. Then I call that function everywhere else and I use a foreach loop to process each field. This avoid repetitions, optimize and compact the code.
The code (commented):
// Utility function that contain the 2 field keys and labels pairs used on all other functions
function get_date_label_keys(){
$text_domain = 'woocommerce';
return array( 'est_delivery' => __( 'Estimated Delivery Date', $text_domain ),
'est_pickup' => __( 'Estimated Pickup Date', $text_domain ) );
}
// Display custom fields on single product page (hook replaced)
add_action( 'woocommerce_before_add_to_cart_button', 'amp_display_custom_fields', 20 );
function amp_display_custom_fields(){
echo '<div id="dates">';
// Loop through each custom field
foreach( get_date_label_keys() as $key => $label ){
$class = str_replace('est_', '', $key); // The class
$value = isset($_POST[$key]) ? sanitize_text_field($_POST[$key]) : ''; // Display the value
printf( '<div class="%s"><label>%s:</label> <input type="date" name="%s" value="%s" required /></div>', $class, $label, $key, $value );
}
echo '</div><br clear="all">';
}
// Add to cart fields validation (in case of need)
add_filter( 'woocommerce_add_to_cart_validation', 'amp_add_to_cart_validation', 20, 3 );
function amp_add_to_cart_validation( $passed, $product_id, $qty ){
// Loop through each custom field
foreach( get_date_label_keys() as $key => $label ){
if( isset( $_POST[$key] ) && empty( $_POST[$key] ) ){
wc_add_notice( sprintf( __( '%s cannot be added to the cart until you enter a delivery date.', $domain ), get_the_title() ), 'error' );
$passed = false;
}
}
return $passed;
}
// Add to cart items the custom data
add_filter( 'woocommerce_add_cart_item_data', 'amp_add_cart_item_data', 20, 2 );
function amp_add_cart_item_data( $cart_item, $product_id ){
// Loop through each custom field
foreach( get_date_label_keys() as $key => $label ){
if( isset( $_POST[$key] ) )
$cart_item['dates'][$key] = sanitize_text_field( $_POST[$key] );
}
return $cart_item;
}
// Display the dates in cart items on cart and checkout pages
add_filter( 'woocommerce_get_item_data', 'amp_get_item_data', 20, 2 );
function amp_get_item_data( $item_data, $cart_item = null ) {
// Loop through each custom field
foreach( get_date_label_keys() as $key => $label ){
if ( isset( $cart_item['dates'][$key] ) )
$item_data[] = array(
'name' => $label,
'value' => sanitize_text_field( $cart_item['dates'][$key] )
);
}
return $item_data;
}
// Add order item meta data and Display the data in order items (hook replaced)
add_action( 'woocommerce_checkout_create_order_line_item', 'amp_add_order_item_meta', 20, 4 );
function amp_add_order_item_meta( $item, $cart_item_key, $values, $order ) {
foreach( get_date_label_keys() as $key => $label ){
// Loop through each custom field
if ( ! empty( $values['dates'][$key] ) )
$item->update_meta_data( $label, $values['dates'][$key] );
}
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
On cart page (and checkout too):
Order received and order view pages (in admin order edit pages and email notifications too):