Save custom checkout delivery time field value in WooCommerce order details - php

On the checkout page, there are delivery methods, if a specific delivery method (only 1) is selected, then a drop-down list with the delivery time should appear. This field is implemented using the ACF plugin with Repeater and the field type is Text. The time selected by the user should be displayed in the order details in the WooCommerce admin panel.
Based on the theme code that I got, I wrote the next code:
In the woocommerce / checkout / form-shipping.php file the following code:
<div id="delivery_times" class="form-group d-none">
<label class="text-bold" for="select_time">Delivery time: </label>
<select id="select_time" class="form-control mb-1">
<option value="" selected>Select time</option>
<?php
$delivery_times = get_field('delivery_times', 'options');
$count = 0;
foreach ($delivery_times as $delivery_time):
echo '<option value="'.$delivery_time['range_time'].'" data-pickup="'.$count.'">'.$delivery_time['range_time'].'</option>';
$count++;
endforeach;
?>
</select>
</div>
There is a common theme-checkout.js file, in which I added the code:
$("#shipping_speed").change(function(){
var delivery_time = $(this).children("option:selected").data("type");
if(delivery_time == "tomorrow"){
$("#delivery_times").removeClass("d-none");
}else{
$("#delivery_times").addClass("d-none");
$("#select_time option[value='']").prop('selected',true);
}
});
And now I need to save the selected data and display it somehow in the order details for the administrator, but I don’t found the implementation of something similar in my theme. Please tell me how I can save this data from the ACF field to the order details?

Updated:
First on your html <select> the attibute name="select_time" is missing, so replace the line:
<select id="select_time" class="form-control mb-1">
by this line:
<select id="select_time" name="select_time" class="form-control mb-1">
Now you can use the following code snippet to save the chosen value as order meta data:
// Save the custom checkout field as the order meta
add_action( 'woocommerce_checkout_create_order', 'custom_checkout_field_update_order_meta', 10, 2 );
function custom_checkout_field_update_order_meta( $order, $data ) {
if ( isset($_POST['select_time']) ) {
$order->update_meta_data( 'delivery_time', esc_attr($_POST['select_time']) );
}
}
Then to display it in admin single order pages under shipping address:
add_action( 'woocommerce_admin_order_data_after_shipping_address', 'display_admin_order_select_time_custom_field' );
function display_admin_order_select_time_custom_field( $order ){
$delivery_time = $order->get_meta('delivery_time');
if( ! empty($delivery_time) ) {
echo '<p class="delivery_time"><strong>'. __("Delivery time", "woocommerce").'</strong>: '.$delivery_time.'</p>';
}
}
Then you can display this delivery time on customer orders and email notifications:
add_filter( 'woocommerce_get_order_item_totals', 'delivery_time_as_order_item_total_row', 10, 3 );
function delivery_time_as_order_item_total_row( $total_rows, $order, $tax_display ){
$delivery_time = $order->get_meta( 'delivery_time' ); // Get delivery time
$new_total_rows = array(); // Initializing
if( empty($delivery_time) ) {
return $total_rows; // Exit
}
// Loop through total rows
foreach( $total_rows as $key => $value ){
if( 'payment_method' == $key ) {
$new_total_rows['delivery_time'] = array(
'label' => __("Delivery time", "woocommerce"),
'value' => $delivery_time,
);
}
$new_total_rows[$key] = $total_rows[$key];
}
return $new_total_rows;
}
Code goes in functions.php file of the active child theme (or active theme). It should work.

Data probably it's saved on another php file, it's using the MVC model.
One thing I would say you is that it's a form, so it does have already other data processed, I would suggest you to find where it does process other data and add some properties there...
Makes sense?

Related

Change variation price when custom selection is made

I have a woocommerce variable product outside of loop and not on single product page, I want to change variation price based on the selection of a form. If an area is selected change variation price with the custom price added in the custom field created for that variation(area1 or area2). My code works for displaying the correct price after selection but when I add the variation to cart the default price of the variation is added to cart not the custom price.
<form class="areas-form" method="POST" action="">
Select Your Area
<select name="area" onchange="this.form.submit()">
<option value="" disabled selected>--select--</option>
<option value="area1">Area 1</option>
<option value="area2">Area 2</option>
</select>
</form>
add_filter( 'woocommerce_product_variation_get_price', 'varient_price', 99, 2 ); // this code is added in functions.php
function varient_price($price, $variation){
if ( $variation->product_type == 'variation' ) {
if(isset($_POST["area"])){
$area=$_POST["area"];
}
$price = get_post_meta( $variation->variation_id, $area.'_price',true);
return $price;
}
}
As per your question comment, rest display and other things you are managing.
Here, I have just attached the woocommerce hooks and calculation to get your area price to set in your selected variation.
Firstly, you need to get the dynamic price in your select option as per area wise.
Secondly, below code will help you to set your parameter request to be add into cart meta:
function add_area_data_cart_meta( $cart_item_data, $product_id ) {
if( isset( $_POST['area'] ) ) {
$cart_item_data[ "area" ] = $_POST['area'];
}
return $cart_item_data;
}
add_filter( 'woocommerce_add_cart_item_data', 'add_area_data_cart_meta', 99, 2 );
Third, before product get add to cart. The above code will fetch your area price value which we have added using above code and below code will set your area price as new price in the cart.
function calculate_area_price_as_variation_price( $cart_object ) {
if( !WC()->session->__isset( "reload_checkout" )) {
/* Gift wrap price */
$additionalPrice = 100;
foreach ( WC()->cart->get_cart() as $key => $value ) {
if( isset( $value["area"] ) ) {
if( method_exists( $value['data'], "set_price" ) ) {
/* Woocommerce 3.0 + */
$value['data']->set_price( $value["area"] );
} else {
/* Version before 3.0 */
$value['data']->price = ( $value["area"] );
}
}
}
}
}
add_action( 'woocommerce_before_calculate_totals', 'calculate_area_price_as_variation_price', 99 );
Please let me know if you find any issues.
Thanks.

Disallow customer to change country in WooCommerce checkout

I am looking for solution where I can find user's country in woocommerce checkout page. I am doing it via geolocation. However, the user can change that. I want to restrict it.
I am using a paid plugin to give coupons to users from a specific country. The country gets loaded perfectly and coupon is applied or rejected based on country. However, the user manually can change the country at the checkout page and avail the discount. My service is online thus there is no delivery of physical goods so entering wrong country wouldn't cause anything to the user.
I have tried 2-3 different coupon restriction plugins but all has the same problem. Does anyone has faced similar issue?
Updated
You can make checkout country dropdown fields disabled (read only) as follows:
add_filter( 'woocommerce_checkout_fields', 'checkout_country_fields_disabled' );
function checkout_country_fields_disabled( $fields ) {
$fields['billing']['billing_country']['custom_attributes']['disabled'] = 'disabled';
$fields['billing']['shipping_country']['custom_attributes']['disabled'] = 'disabled';
return $fields;
}
As disabled select fields doesn't get posted, you need additionally the following duplicated hidden fields with the correct values set in. It will avoid an error notice notifying that country required fields values are empty. So add this too:
// Mandatory for disable fields (hidden billing and shipping country fields with correct values)
add_filter( 'woocommerce_after_checkout_billing_form', 'checkout_country_hidden_fields_replacement' );
function checkout_country_hidden_fields_replacement( $fields ) {
$billing_country = WC()->customer->get_billing_country();
$shipping_country = WC()->customer->get_shipping_country();
?>
<input type="hidden" name="billing_country" value="<?php echo $billing_country; ?>">
<input type="hidden" name="shipping_country" value="<?php echo $shipping_country; ?>">
<?php
}
Code goes in functions.php file of the active child theme (or active theme). Tested and works.
Notes:
You will need to disable shipping calculator in cart page, if it's not done.
If you want to make country dropdown read only in Checkout and in My account Edit Address too, use the following instead:
add_filter( 'woocommerce_billing_fields', 'checkout_billing_country_field_disabled' );
function checkout_billing_country_field_disabled( $fields ) {
$fields['billing_country']['custom_attributes']['disabled'] = 'disabled';
return $fields;
}
add_filter( 'woocommerce_shipping_fields', 'checkout_shipping_country_field_disabled' );
function checkout_shipping_country_field_disabled( $fields ) {
$fields['shipping_country']['custom_attributes']['disabled'] = 'disabled';
return $fields;
}
// Mandatory for disable fields (hidden billing and shipping country fields with correct values)
add_filter( 'woocommerce_after_checkout_billing_form', 'checkout_country_hidden_fields_replacement' );
function checkout_country_hidden_fields_replacement( $fields ) {
$billing_country = WC()->customer->get_billing_country();
$shipping_country = WC()->customer->get_shipping_country();
?>
<input type="hidden" name="billing_country" value="<?php echo $billing_country; ?>">
<input type="hidden" name="shipping_country" value="<?php echo $shipping_country; ?>">
<?php
}

Customization on WooCommerce variable product with custom fields

We have created a site using WooCommerce and WordPress with predefined products.
However, Product A must have variations, to resolve this, we added custom fields such as Colors, Dimensions, etc...
Now once the end-users are navigating to the product, we are able to fetch the custom field values. We also modified the UI so that users can pick and choose from these custom fields. (We have a preview of the modifications done via JS/CSS), so for instance, if they choose a green color we use JS to add a layer of green so that the preview is real time.
Now, we have one challenge.
-> What is the best way to go about adding this product PLUS all modifications done to the cart?
So for instance Product A was modified (on the front-end) using data pulled from the custom fields to include Color: Green and Size: 100x100 instead of defaults values. How do we store this and pass the customized product to the cart?
Appreciate the help!. Glad to add more details if something is not clear.
(There are plugins out there that can provide this functionality; Things similar to WooCommerce Product Add-On, etc... However, we have to custom develop the feature.)
It requires some steps.
1). For your product page:
First you might need to add hidden fields to the add to cart form for each custom field like (example):
add_action( 'woocommerce_before_add_to_cart_button', 'add_hidden_empty_input_fields' );
function add_hidden_empty_input_fields() {
?>
<input type="hidden" name="hidden_color" id="hidden_color" value="">
<input type="hidden" name="hidden_dimensions" id="hidden_dimensions" value="">
<?php
}
Then you will have to make some changes to your Javascript code to set the custom fields chosen values in those hidden fields.
2). Pass the custom fields selected values as custom cart item data (example):
add_filter('woocommerce_add_cart_item_data', 'add_custom_field_data', 10, 3 );
function add_custom_field_data( $cart_item_data, $product_id, $variation_id ) {
if ( isset($_POST['hidden_color']) && ! empty($_POST['hidden_color']) ) {
$cart_item_data['color'] = esc_attr($_POST['hidden_color']);
}
if ( isset($_POST['hidden_dimensions']) && ! empty($_POST['hidden_dimensions']) ) {
$cart_item_data['dimensions'] = esc_attr($_POST['hidden_dimensions']);
}
return $cart_item_data;
}
And optionally display values on cart items (example):
add_filter( 'woocommerce_get_item_data', 'display_custom_cart_item_data', 10, 2 );
function display_custom_cart_item_data( $cart_data, $cart_item ) {
if ( isset($cart_item['color']) ) {
$cart_data[] = array( "name" => __("Color"), "value" => $cart_item['color'] );
}
if ( isset($cart_item['dimensions']) ) {
$cart_data[] = array( "name" => __("Dimensions"), "value" => $cart_item['dimensions'] );
}
return $cart_data;
}
3). Save the custom cart item data as custom order item meta data (example):
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['color']) ) {
$item->add_meta_data( __("Color"), $values['color'] );
}
if ( isset($values['dimensions']) ) {
$item->add_meta_data( __("Dimensions"), $values['dimensions'] );
}
}
Note: The data is going to be displayed on everywhere on orders and email notifications.
Code goes in functions.php file of the active child theme (or active theme). It should work.
The variations have a post_id, you would just use the variation post_id instead of the main product one.

Display custom fields values in WooCommerce order and email notification

Based on "Choosing a date and time after choosing the WooCommerce delivery method" answer code, that displays custom Pickup fields and delivery dates, the following code displays the delivery data of those fields on the order edit pages.
Here is my code:
// View fields in Edit Order Page
add_action( 'woocommerce_admin_order_data_after_billing_address', 'my_custom_fields_order_meta', 10, 1 );
function my_custom_fields_order_meta($order){
$delivery_option = $order->get_meta('_delivery_option');
if( $delivery_option == 'date' ) {
$delivery_datetime = $order->get_meta('_delivery_datetime');
echo '<p><strong>'.__('Delivery').':</strong> ' . get_post_meta( $order->id, '_delivery_option', true ) . '</p>';
echo '<p><strong>'.__('Delivery Date').':</strong> ' . get_post_meta( $order->id, '_delivery_datetime', true ) . '</p>';
}
}
Unfortunately, only the delivery date that the customer chooses is displayed correctly, and the options of the radio button "As Soon As Possible" are not shown.
Apparently, I'm doing something wrong.
I would like also to display these fields values on the Thank You page and in the email.
Any help is appreciated.
To display the custom fields values in backend order edit pages (if they are saved in database for the order), use the following:
// View fields in Edit Order Page
add_action( 'woocommerce_admin_order_data_after_billing_address', 'display_custom_fields_value_admin_order', 10, 1 );
function display_custom_fields_value_admin_order( $order ){
// Display the delivery option
if( $delivery_option = $order->get_meta('_delivery_option') )
echo '<p><strong>'.__('Delivery type').':</strong> ' . $delivery_option . '</p>';
// Display the delivery date
if( $delivery_datetime = $order->get_meta('_delivery_datetime') )
echo '<p><strong>'.__('Delivery Date').':</strong> ' . $delivery_datetime . '</p>';
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
The best shorter clean way to display the custom field values everywhere on frontend order pages and on email notifications is to display them in order totals table, just like used payment methods:
// Display the chosen delivery information
add_filter( 'woocommerce_get_order_item_totals', 'chosen_delivery_item_order_totals', 10, 3 );
function chosen_delivery_item_order_totals( $total_rows, $order, $tax_display ) {;
$new_total_rows = [];
// Loop through Order total lines
foreach($total_rows as $key => $total ){
// Get the chosen delivery values
$delivery_option = $order->get_meta('_delivery_option');
$delivery_datetime = $order->get_meta('_delivery_datetime');
// Display delivery information before payment method
if( ! empty($delivery_option) && 'payment_method' === $key ){
$label = empty($delivery_datetime) ? __('Delivery') : __('Delivery Date');
$value = empty($delivery_datetime) ? __('AZAP', $domain) : $delivery_datetime;
// Display 'Delivery method' line
$new_total_rows['chosen_delivery'] = array( 'label' => $label,'value' => $value );
}
$new_total_rows[$key] = $total;
}
return $new_total_rows;
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
Related thread: Choosing a date and time after choosing the WooCommerce delivery method

Show Woocommerce custom checkout field value in admin order making them editable

I am using "Show hide custom WooCommerce checkout field based on selected payment method" answer to one of my questions, to show / hide a custom checkout billing field, and it works fine.
Question: Is it possible to show my Custom field in WooCommerce orders in the admin panel?
To display "billing_options" custom checkout billing field value in admin order pages on the billing information column, use the following:
add_action( 'woocommerce_admin_order_data_after_billing_address', 'display_billing_options_value_in_admin_order', 10, 1 );
function display_billing_options_value_in_admin_order($order){
if( $value = get_post_meta( $order->get_id(), '_billing_options', true ) )
echo '<p><strong>'.__('Invoice Number', 'woocommerce').':</strong> ' . $value . '</p>';
}
To make this custom checkout billing field appear as editable in backend use the following:
add_filter( 'woocommerce_admin_billing_fields', 'custom_admin_billing_fields', 10, 1 );
function custom_admin_billing_fields( $fields ) {
$fields['options'] = array(
'label' => __('Invoice Number', 'woocommerce'),
'show' => true,
);
return $fields;
}
Code goes in function.php file of your active child theme (or active theme). tested and works.
add_action( 'woocommerce_admin_order_data_after_order_details', 'mycustom_order_meta_general' );
function mycustom_order_meta_general( $order ){ ?>
<br class="clear" />
<h4>Gift Order Edit</h4>
<?php
/*
* get all the meta data values we need
*/
$_mycustomfield = get_post_meta( $order->get_id(), '_mycustomfield', true );
?>
<div class="address">
<p><strong>My Custom Field</strong></p>
<?php
if( $_mycustomfield ) :
?>
<p><strong>MY custom:</strong> <?php echo $_mycustomfield ?></p>
<?php
endif;
?>
</div>
<?php } ?>

Categories