Skip to content
Advertisement

Time loop for WooCommerce checkout select option from date picker input field

Input field one – delivery date has a datepicker attached. This fills in the required date by client. This field determines the delivery time options that are populated in the select field next to it.

However the options are based on some rules

  1. Time slots are 30 mins intervals from 11am to 4pm.
  2. If the date chosen = today, delivery start time = current time + one hour.
  3. if date is future, delivery is from 11am to 4pm. 30 mins intervals

I need help making the array for the woocommerce options basing on the parameters above from the chosen date. Thanks.

Screenshot is descriptive.

Sample code: First I update the select field options on selecting the date in the using the date picker. Using the code below.

add_action( 'wp_footer', 'woocommerce_update_on_date_select', 21 );

function woocommerce_update_on_date_select() {
    if (is_checkout()) {
    ?>
    <script type="text/javascript">
        $(document).ready(function() {
          function updateCart() {
            var d = $("#billing_country").val(),
              e = $("#billing_state").val(),
              f = $("input#billing_postcode").val(),
              g = $("#billing_city").val(),
              h = $("input#billing_address_1").val(),
              i = $("input#billing_address_2").val(),
              j = d,
              k = e,
              l = f,
              m = g,
              n = h,
              o = i;
            $("#ship-to-different-address").find("input").is(":checked") && (j = $("#shipping_country").val(), k = $("#shipping_state").val(), l = $("input#shipping_postcode").val(), m = $("#shipping_city").val(), n = $("input#shipping_address_1").val(), o = $("input#shipping_address_2").val());
            var p = {
              security: wc_checkout_params.update_order_review_nonce,
              payment_method: $("#order_review").find('input[name="payment_method"]:checked').val(),
              country: d,
              state: e,
              postcode: f,
              city: g,
              address: h,
              address_2: i,
              s_country: j,
              s_state: k,
              s_postcode: l,
              s_city: m,
              s_address: n,
              s_address_2: o,
              post_data: $("form.checkout").serialize()
            };
            var c  = {update_shipping_method: !0};
            if (!1 !== c.update_shipping_method) {
              var q = {};
              $('select.shipping_method, input[name^="shipping_method"][type="radio"]:checked, input[name^="shipping_method"][type="hidden"]').each(function() {
                q[$(this).dat$("index")] = $(this).val()
              }), p.shipping_method = q
            }
            $.ajax({
              type: "POST",
              url: wc_checkout_params.wc_ajax_url.toString().replace("%%endpoint%%", "update_order_review"),
              data: p,
              success: function(c) {
                var d = $('.woocommerce-checkout input[name="payment_method"]:checked').attr("id");
                if ("true" === c.reload) return void window.location.reload();
                $(".woocommerce-NoticeGroup-updateOrderReview").remove();
                var e = $("#terms").prop("checked");
                if (c && c.fragments && $.each(c.fragments, function(b, c) {
                    $(b).replaceWith(c)
                  }), e && $("#terms").prop("checked", !0), "failure" === c.result) {
                  var f = $("form.checkout");
                  $(".woocommerce-error, .woocommerce-message").remove(), c.messages ? f.prepend('<div class="woocommerce-NoticeGroup-updateOrderReview">' + c.messages + "</div>") : f.prepend(c), f.find(".input-text, select, input:checkbox").blur(), $("html, body").animate({
                    scrollTop: $("form.checkout").offset().top - 100
                  }, 1e3)
                }
              }
            });
          }

          $('#billing_delivery_date').attr('readonly', true);
          $('#billing_serving').on('change', function(){
              updateCart();
          });

          $( "#billing_delivery_date" ).on('change', function() {
            updateCart();
          });
      });
    </script>
    <?php
    }
}

Then using the date chosen from date-picker, I try to make the array to send to the select field in the form.

add_filter( 'woocommerce_checkout_fields' , 'brown_remove_billing_postcode_checkout' );

        function brown_remove_billing_postcode_checkout( $fields ) {
            $error = '';

            if (isset($_POST['post_data'])) {
                parse_str( $_POST['post_data'], $post_data );
            } else {
                $post_data = $_POST;
            }

            $billing_delivery_date = $post_data['billing_delivery_date'];

            if ( $billing_delivery_date = !NULL ) { 

                $date = new DateTime();
                $date->setTimezone(new DateTimeZone('Africa/Kampala'));
                $order_day = $date->format('Y-m-d H:i:s');

                $date = new DateTime(date('Y-m-d', strtotime(str_replace('/' , '-', $billing_delivery_date))));

                //If the date picked is greater than the current date
                if (strtotime($order_day) > strtotime($date) {
                    //Make an array of values from 11am to 4pm to pass to $fields['billing']['billing_serving'] options array

                } elseif ($order_day == $date) {
                    //If the Make an array of values from 11am to 4pm to pass to $fields['billing']['billing_serving'] options array
                    //Get the current time + one hour. Append hour intervals from next top of the hour until 4pm.
                }

            }

            // Add New Fields
            $fields['billing']['billing_serving'] = array(
                'label'     => __('Delivery Time', 'woocommerce'),
                'placeholder'   => _x('Time for your delivery', 'placeholder', 'woocommerce'),
                'required'  => true,
                'class'     => array('form-row-last'),
                'clear'     => false,
                'autocomplete' => false,
                'type'        => 'select',
                'options'     => array(
                           //Generated array goes here.
                           '11am'    => __('11:00am', 'woocommerce' ),
                           '1130am'  => __('11:30am', 'woocommerce' ),
                           '12noon'  => __('12:00pm', 'woocommerce' ),
                           '1230pm'  => __('12:30pm', 'woocommerce' ),
                           '1pm'     => __('1:00pm', 'woocommerce' ),
                           '130pm'   => __('1:30pm', 'woocommerce' ),
                           '2pm'     => __('2:00pm', 'woocommerce' ),
                           '230pm'   => __('2:30pm', 'woocommerce' ),
                           '3pm'     => __('3:00pm', 'woocommerce' ),
                           '330pm'   => __('3:30pm', 'woocommerce' ),
                           '4pm'     => __('4:00pm', 'woocommerce' )
                    )
             );

            return $fields;

        }

Advertisement

Answer

As the code for the Delivery date was missing, I have made it enabling jQuery-ui datepicker (and you should use your own CSS).

I haven’t use your jQuery code, because my approach is quiet different and I was a bit loosed in your code. My PHP code is also quiet different. The select field <options> is almost empty in php, because javascript build it dynamically.

Here php code is passing to Javascript/jQuery some data:

  • for the delivery starting date.
  • for the select option key/value pairs (available time slots + default time slots).

Here jQuery/Javascript is located in the same hooked function just after the custom checkout fields codeā€¦

The html for <option> tags in the “delivery time select field are built dynamically by jQuery depending on the Start date and the current time.

All your requirement are respected:

  • Time slots are 30 mins intervals from 11am to 4pm.
  • If selected date is today we add an offset of +1 hour to the time slots
  • If selected date is not today, we got the default time slots (11 available)

Now you should disable your related code to try this one:

 // Enable available jQuery datepicker script in WordPress
add_action( 'wp_enqueue_scripts', 'enabling_date_picker' );
function enabling_date_picker() {

    // Only on front-end and checkout page
    if( is_admin() || ! is_checkout() ) return;

    // Load available datepicker jQuery-ui plugin script
    wp_enqueue_script( 'jquery-ui-datepicker' );
}

// Add and display custom checkout fields + jQuery script
add_filter( 'woocommerce_checkout_fields' , 'brown_remove_billing_postcode_checkout' );
function brown_remove_billing_postcode_checkout( $fields ) {
    // Your Settings
    $start_hour = 11; // start time (in hours)
    $end_hour = 16; // end time (in hours)
    $offset = 1; // One hour before slot time (can be a float number like 1.5)
    date_default_timezone_set ('Africa/Kampala'); // The timezone

    // Initializing variables
    $hour = 3600; // 1 hour in seconds
    $day = $hour * 24; // 1 day in seconds
    $now = strtotime("now"); // Now time
    $real_now = $now + ($offset * $hour); // Now time + offset
    $today_date = date("Y-m-d"); // today date
    $tomorrow_date = date("Y-m-d", strtotime("+1 day")); // tomorow date
    $today_time = strtotime($today_date); // Today time at 00:00 in seconds
    $tomorrow_time = strtotime($tomorrow_date); // Tomorrow time at 00:00 in seconds
    $start_time = $today_time + ( $start_hour * $hour ); // Start datetime in seconds
    $end_time = $today_time + ( $end_hour * $hour ); // End datetime in seconds
    $today_slots = $default_slots = $option_days = array();

    // Add Delivery day field (with jquery-ui datepicker enabled)
    $fields['billing']['billing_delivery_day'] = array(
        'label'         => __('Delivery Day', 'woocommerce'),
        'placeholder'   => _x('Date for your delivery', 'placeholder', 'woocommerce'),
        'required'      => true,
        'id'            => 'datepicker', // Enable jQuery datepicker for this field
        'class'         => array('form-row-first'),
        'clear'         => false,
        'autocomplete'  => false,
        'type'          => 'text'
    );
   // Add Delivery hour slots
    $fields['billing']['billing_delivery_hour'] = array(
        'label'        => __('Delivery Time', 'woocommerce'),
        'required'     => true,
        'class'        => array('form-row-last'),
        'clear'        => false,
        'autocomplete' => false,
        'type'         => 'select',
        'options'      => array( '' => __('Select time for your delivery') ),
    );

    // Making the delivery hour slots <option> arrays for Javascript
    for($i = $start_time; $i <= $end_time; $i += 1800 ){ // 1800 seconds is half an hour
        $key     = date('H:i', $i);
        $value   = date('h:ia', $i);

        // Today available hour slots array
        if($real_now < $i)
            $today_slots[$key] = $value;

        // Default hour slots array
        $default_slots[$key] = $value;
    }

    // The correct start date and time (today or tomorow) for Javascript
    $date = $real_now < $end_time ? $today_date : $tomorrow_date;
    $dtime = $real_now < $end_time ? date("Y-m-dTH:i:s", $today_time) : date("Y-m-dTH:i:s", $tomorrow_time);

    ?>
    <script>
        jQuery(function($){
            var offsetDate = 15, // Number of days enabled in the datepicker (optional and disabled in the datepicker code)
                startingDate = new Date('<?php echo $dtime; ?>'), // Starting day (dynamic)
                endingDate = new Date('<?php echo $dtime; ?>'), // End date (calculated below)
                todaySlots = <?php echo json_encode($today_slots); ?>,
                defaultSlots = <?php echo json_encode($default_slots); ?>,
                sDay = 'input[name ="billing_delivery_day"]',
                sHour = 'select[name ="billing_delivery_hour"]',
                defaultOption = $(sHour+' > option').text(),
                todaySlotsLength = Object.keys(todaySlots).length;

            // ------ 1). Dates and Date picker ------ //

            // Set the default field start date
            $(sDay).val('<?php echo $date; ?>');
            $('#datepicker_field').addClass('woocommerce-validated');

            // Max date calculation (optional and diasabeld in the datepicker code)
            endingDate.setDate(startingDate.getDate()+offsetDate);

            // Jquery-ui datepicker
            $("#datepicker").datepicker({
                dateFormat: "yy-mm-dd",
                minDate: startingDate,
                // maxDate: endingDate, // optional, can be enabled.
                setDate: startingDate,
            });

            // ------ 2). HOUR slots select field (dynamic <option>) ------ //

            // Build the <option> html html in the select field dynamically
            function dynamic_select_options_buid( slotsType ){
                $.each( slotsType, function( index, value ){
                    $(sHour).append('<option value="'+index+'">'+value+'</option>');
                });
            }
            // Replace and Build the <option> html in the select field dynamically
            function dynamic_select_options_replace( slotsType ){
                $(sHour+' > option').remove();
                $(sHour).append('<option value="">'+defaultOption+'</option>');
                dynamic_select_options_buid( slotsType );
            }
            
            console.log(defaultOption);
            console.log(todaySlotsLength);
            if(todaySlotsLength != 0 && todaySlotsLength < 11 ){
                // Loaded at start
                dynamic_select_options_buid( todaySlots );

                // Live date selection event
                $(sDay).change( function(){
                    console.log('day changed: '+$(this).val());
                    if( $(this).val() != '<?php echo $date; ?>' )
                        dynamic_select_options_replace( defaultSlots );
                    else
                        dynamic_select_options_replace( todaySlots );
                })
            } else {
                dynamic_select_options_buid( defaultSlots );
            }
        });
    </script>
    <?
    return $fields;
}

Code goes in function.php file of your active child theme (or active theme).

Tested and works.

User contributions licensed under: CC BY-SA
4 People found this is helpful
Advertisement