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 乘性高斯噪声在深度学习网络中的应用
  • ¥15 运筹学排序问题中的在线排序
  • ¥15 关于docker部署flink集成hadoop的yarn,请教个问题 flink启动yarn-session.sh连不上hadoop,这个整了好几天一直不行,求帮忙看一下怎么解决
  • ¥30 求一段fortran代码用IVF编译运行的结果
  • ¥15 深度学习根据CNN网络模型,搭建BP模型并训练MNIST数据集
  • ¥15 C++ 头文件/宏冲突问题解决
  • ¥15 用comsol模拟大气湍流通过底部加热(温度不同)的腔体
  • ¥50 安卓adb backup备份子用户应用数据失败
  • ¥20 有人能用聚类分析帮我分析一下文本内容嘛
  • ¥30 python代码,帮调试,帮帮忙吧