dongyan4424 2017-07-21 17:18
浏览 73
已采纳

WooCommerce:结账验证失败后更新自定义字段

In my project I'm customizing some of the WooCommerce features.
My "shipping methods" are:
1. delivery
2. take away

I also added a custom field in the checkout page that is a <select> populated with the valid times for delivery (case "1") or for the take away (case "2").

It may happen that a user selects 2. take away in the cart page, then selects a time valid for "2", but then changes to 1. delivery and the selected time may not be valid anymore, neither the option list and the custom field label.

Of course i'm using the woocommerce_checkout_process hook to warn the user via wc_add_notice(), but even if the woocommerce_checkout_fields hook is triggered (that's where i create the select list), the <select> values are not updated.

I think there is an AJAX call that is related only to the shipping method and doesn't update the other checkout fields, although woocommerce_checkout_fields hook is triggered.

How to update the custom fields? Do i need some js/jquery/AJAX?

Or: can a custom field be related to a shipping method (and get updated via AJAX with it)? How?

EDIT

Custom field code:

add_filter( 'woocommerce_checkout_fields', 'fty_filter_checkout_fields' );
function my_filter_checkout_fields($fields) {
    $must_deliver   =   WC()->cart->shipping_total > 0.0;   // true=deliver, false=take away

    // some complex code to calculate time lists omitted, samples array instead:

    $delivery_time_list =   array(
        "deliver 10:00",
        "deliver 11:00",
        "deliver 12:00",
        "deliver 13:00"
    );

    $takeaway_time_list =   array(
        "takeaway 10:00",
        "takeaway 10:30",
        "takeaway 11:00",
        "takeaway 11:30",
        "takeaway 12:00",
        "takeaway 12:30",
        "takeaway 13:00",
        "takeaway 13:30"
    );

    // add the new conditional field
    if($must_deliver) {
        $fields['my_delivery_datetime'] = array(
            'my_delivery_time' => array(
                'type'      => 'select',
                'options'   => $delivery_time_list,
                'required'  => true,
                'label'     => __('Delivery time')
            )
        );
    } else {
        $fields['my_delivery_time'] = array(
            'my_delivery_time' => array(
                'type'      => 'select',
                'options'   => $takeaway_time_list,
                'required'  => true,
                'label'     => __('Take away time')
            )
        );
    }

    return $fields;
}

an idea of the validation code:

add_action('woocommerce_checkout_process', 'my_checkout_date_time_validation', 30, 1);
function my_checkout_date_time_validation($doh) {
    $time = filter_input(INPUT_POST, 'my_delivery_time');
    $shipping = filter_input(INPUT_POST, 'shipping_method', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY);

    if(strpos($time, "deliver")!==FALSE && strpos($shipping[0], "local_pickup")!==FALSE) {
        wc_add_notice('Please re-select take away time', 'error');
    } else if(strpos($time, "takeaway")!==FALSE && strpos($shipping[0], "distance_based_rate")!==FALSE) {
        wc_add_notice('Please re-select delivery time', 'error');
    }

}

here's about shipping methods;

add_action( 'woocommerce_flat_rate_shipping_add_rate', 'add_distance_based_delivery_rate', 10, 2 );
function add_distance_based_delivery_rate( $method, $rate ) {
    $new_rate          = $rate;
    $new_rate['id']    .= ':' . 'distance_based_rate';
    $new_rate['label'] = 'delivery'; // Rename to 'Rushed Shipping'.

    // incredibly complex code used to calculate delivery costs omitted
    $dist_cost  =   1000;

    $new_rate['cost']  += $dist_cost;
    $method->add_rate( $new_rate );
}

Thanx!

  • 写回答

1条回答 默认 最新

  • dsbruqxgt820011351 2017-07-22 15:32
    关注

    The code provided was mostly un-useful… I have make a lot of changes and optimizations. All my code is commented, it's tested on WooCommerce 3+ and perfectly works.

    You will have to add your "incredibly complex code used to calculate delivery costs omitted"…


    1) JAVASCRIPT FOR CONDITIONAL CHECKOUT FIELDS LIVE EVENTS
    The only way to get the hand on customer live events (browser side) is javascript/jQuery. So this is not easy because WooCommerce use already a lot of javascript/jQuery/Ajax on checkout page…

    I have included the javascript code into the hooked function, but you should save it in a separate file and register this script file with classic WordPress registering script function, like in this thread:

    Checkout fields: Hiding and showing existing fields

    2) USE EXISTING AVAILABLE SHIPPING METHODS (DYNAMIC PRICES CALCULATION):
    You don't need to create any shipping rate. You can use:

    • local_pickup available method for your "TAKE WAY"
    • flat_rate available method for your "Delivery" (with dynamic price calculations)

    For each of your shipping zones, enable, set and rename (label name) the 2 methods in Woocommerce > Settings > Shipping:

    enter image description here

    For the flat rate you can set any minimal amount (that will be overwritten by your calculations)…

    If you make changes you need to refresh shipping cached data: disable, save and enable, save those methods for the current shipping zone.

    3) SAVING THE SHIPPING TIME TO ORDER META DATA:
    I have add some code for that and it's save in 2 custom meta fields:

    • One for the chosen shipping
    • The other for the time

    4) DISPLAYING THE CHOSEN SHIPPING TYPE AND TIME IN A METABOX (IN ORDER EDIT PAGES):
    I have also add some code for that.

    enter image description here


    FINALY HERE IS THE CODE:

    add_action( 'woocommerce_after_order_notes', 'my_custom_checkout_field' );
    
    function my_custom_checkout_field( $checkout ) {
    
        // The 2 Options arrays in imput select
        $delivery_time_list[''] = $takeaway_time_list[''] = __('Select an hour');
        for($h = 10, $i = 0; $i < 8; $i++  ){
            if( $i % 2 == 0 ){
                $time = $h.':00';
                $delivery_time_list[$time] = 'deliver '.$time;
            } else {
                $time = $h.':30';
                $h++;
            }
            $takeaway_time_list[$time] = 'takeaway '.$time;
        }
    
        echo '<div id="delivery_checkout_fields"><h3>' . __('Shipping time options') . '</h3>';
    
        woocommerce_form_field( 'delivery_time', array(
            'type'      => 'select',
            'class'     => array('delivery-time form-row-wide'),
            'label'     => __('Delivery time'),
            'options'   => $delivery_time_list,
        ), $checkout->get_value( 'delivery_time' ) );
    
        woocommerce_form_field( 'takeaway_time', array(
            'type'      => 'select',
            'class'     => array('takeaway-time form-row-wide'),
            'label'     => __('Take away time'),
            'options'   => $takeaway_time_list,
        ), $checkout->get_value( 'takeaway_time' ) );
    
        echo '</div>';
    
        $required = esc_attr__( 'required', 'woocommerce' );
    
        ?>
        <script>
            jQuery(function($){
    
                var choosenShipMethod = $('input[name^="shipping_method"]:checked').val().split(':')[0], // Choosen shipping method slug
                    required = '<abbr class="required" title="<?php echo $required; ?>">*</abbr>'; // Required html
    
                // TESTING: displaying in console the choosen shipping
                console.log('Chosen shipping: '+choosenShipMethod);
    
                // Function that shows or hide imput select fields
                function showHide( actionToDo='show', selector='' ){
                    if( actionToDo == 'show' )
                        $(selector).show(function(){
                            $(this).addClass("validate-required");
                            $(this).removeClass("woocommerce-validated");
                            $(this).removeClass("woocommerce-invalid woocommerce-invalid-required-field");
                            $(selector+' label').append(required);
                            //console.log('Selector (show): '+selector);
                        });
                    else
                        $(selector).hide(function(){
                            $(this).removeClass("validate-required");
                            $(this).removeClass("woocommerce-validated");
                            $(this).removeClass("woocommerce-invalid woocommerce-invalid-required-field");
                            $(selector+' label > .required').remove();
                            //console.log('Selector (hide): '+selector);
                        });
                }
    
                // Initialising at start (Based on the choosen shipping method)
                if( choosenShipMethod == 'flat_rate' ) // Choosen "Delivery" (Hidding "Take away")
                {
                    showHide('show','#delivery_time_field' );
                    showHide('hide','#takeaway_time_field' );
                }
                else if( choosenShipMethod == 'local_pickup' ) // Choosen "Take away" (Hidding "Delivery")
                {
                    showHide('show','#takeaway_time_field' );
                    showHide('hide','#delivery_time_field' );
                }
                else // No shipping choosen yet (Hidding BOTH shipping dropdown hour selectors
                {
                    showHide('hide','#delivery_time_field' );
                    showHide('hide','#takeaway_time_field' );
                    $('#delivery_checkout_fields').hide();
                }
    
                // When shipping method is changed (Live event)
                $( 'form.checkout' ).on( 'change', 'input[name^="shipping_method"]', function() {
                    var changedShipMethod = $('input[name^="shipping_method"]:checked').val().split(':')[0];
                    if( changedShipMethod == 'flat_rate' )
                    {
                        // Choose "Delivery" | Show "Delivery" and Hide "Take away"
                        $('#delivery_checkout_fields').show();
                        showHide('show','#delivery_time_field' );
                        showHide('hide','#takeaway_time_field' );
                    }
                    else if( changedShipMethod == 'local_pickup' )
                    {
                        // Choose "Take away" | Show "Take away" and Hide "Delivery"
                        $('#delivery_checkout_fields').show();
                        showHide('show','#takeaway_time_field' );
                        showHide('hide','#delivery_time_field' );
                    }
                    console.log("Chosen shipping: "+changedShipMethod);
                });
    
                // When an hour is selected (LIVE event)
                $('#delivery_checkout_fields select').change( function(){
                    if( $(this).val() != '')
                        $(this).parent().removeClass("validate-required");
                    else
                        $(this).parent().addClass("validate-required");
    
                    console.log("Selector value: "+$(this).val());
                });
                // "select.shipping_method, input[name^="shipping_method"], #ship-to-different-address input, .update_totals_on_change select, .update_totals_on_change input[type="radio"], .update_totals_on_change input[type="checkbox"]"
                //"function (){t.reset_update_checkout_timer(),t.dirtyInput=!1,e(document.body).trigger("update_checkout")}"
            });
        </script>
        <?php
    
    }
    
    
    // Process the checkout (Checking if required fields are not empty)
    add_action('woocommerce_checkout_process', 'ba_custom_checkout_field_process');
    function ba_custom_checkout_field_process() {
    
        $delivery_time = $takeaway_time = 0;
        if ( $_POST['delivery_time'] ) $delivery_time = 1;
        if ( $_POST['takeaway_time'] ) $takeaway_time = 1;
    
        // Only one message is possible for both
        if ( ( $delivery_time + $takeaway_time ) == 0 ){
            wc_add_notice( __('Please select a <strong>shipping time</strong>.' ), 'error');
        }
    }
    
    
    ## CALCULATING THE DELIVERY FEE (BASED ON COUNTING THE DIFFERENT DATES For all items) ##
    add_filter( 'woocommerce_package_rates', 'custom_shipping_flat_rate_cost_calculation', 10, 2 );
    function custom_shipping_flat_rate_cost_calculation( $rates, $package )
    {
        ## --- CALCULATIONS Based on CART DATA (if needed) --- ##
    
        foreach(WC()->cart->get_cart() as $cart_item ):
            // HERE your incredibly complex code used to calculate delivery costs
        endforeach;
    
    
        ## --- CHANGING DYNAMICALLY THE METHODS COSTS --- ##
    
        foreach($rates as $rate_key => $rate_values):
    
            $method_id = $rate_values->method_id;
            $rate_id = $rate_values->id;
    
            // "DELIVERY" - "local_pickup" method (if needed)
            if ( 'flat_rate' === $method_id ){
    
                // HERE your incredibly complex code used to calculate delivery costs
    
                // Change the price cost
                $price_excl_tax = $rates[$rate_id]->cost + 2.5;
                $rates[$rate_id]->cost =  number_format($price_excl_tax, 2);
    
                $tax_calculation = $rates[$rate_id]->taxes[0] * 0.1;
                $rates[$rate_id]->taxes[0] = number_format($tax_calculation, 2);
            }
            // "TAKE WAY" - "local_pickup" method (if needed)
            elseif ( 'local_pickup' === $method_id )
            {
                // do something if needed
            }
    
        endforeach;
    
        return $rates;
    }
    
    
    // Save the "shipping time" in order meta data
    add_action( 'woocommerce_checkout_update_order_meta', 'save_shipping_time_in_order_meta',  100, 1 );
    function save_shipping_time_in_order_meta( $order_id ) {
    
        // Take away time
        $takeaway_time = $_POST['takeaway_time'];
        if ( ! empty( $takeaway_time ) ){
            add_post_meta( $order_id, '_shipping_time', $takeaway_time );
            add_post_meta( $order_id, '_shipping_type', __('Take away', 'woocommerce' ) );
        }
        // Delivery time
        $delivery_time = $_POST['delivery_time'];
        if ( ! empty( $delivery_time ) ){
            add_post_meta( $order_id, '_shipping_time', $delivery_time );
            add_post_meta( $order_id, '_shipping_type', __('Delivery', 'woocommerce' ) );
        }
    
    }
    
    // Adding shipping time metabox (on right side) to Order edit pages
    add_action( 'add_meta_boxes', 'add_order_shipping_time_meta_boxe' );
    function add_order_shipping_time_meta_boxe(){
    
        add_meta_box(
            'woocommerce-order-shipping-time-values', __( 'Shipping type and time', 'woocommerce' ),
            'order_shipping_time_values', 'shop_order', 'side', 'default'
        );
    }
    
    // Adding content to shipping time metabox to Order edit pages
    function order_shipping_time_values(){
        global $post;
    
        $type = get_post_meta($post->ID, '_shipping_type', true);
        $time = get_post_meta($post->ID, '_shipping_time', true);
    
        echo "<p><strong>Type:</strong> $type | <strong>time:</strong> $time</p>";
    }
    

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

    This code is tested on WooCommerce 3+ and works.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 matlab在安装时报错 无法找到入口 无法定位程序输入点
  • ¥15 收益高的广告联盟有哪些
  • ¥15 Android Studio webview 的使用问题, 播放器横屏全屏
  • ¥15 删掉jdk后重新下载,Java web所需要的eclipse无法使用
  • ¥15 uniapp正式环境中通过webapi将本地数据推送到设备出现的跨域问题
  • ¥15 xui建立节点,显示错误
  • ¥15 关于#单片机#的问题:开始、复位、十进制的功能可以实现,但是切换八进制的功能无法实现(按下按键也没有效果),把初始状态调成八进制,也是八进制可以实现但是切换到十进制不行(相关搜索:汇编语言|计数器)
  • ¥15 VINS-Mono或Fusion中feature_manager中estimated_depth是特征的深度还是逆深度?
  • ¥15 谷歌浏览器如何备份抖音网页数据
  • ¥15 分别有什么商家下面需要非常多的骑手为它工作?