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 用yalmip搭建模型,cplex求解,线性化处理的方法
  • ¥15 qt6.6.3 基于百度云的语音识别 不会改
  • ¥15 关于#目标检测#的问题:大概就是类似后台自动检测某下架商品的库存,在他监测到该商品上架并且可以购买的瞬间点击立即购买下单
  • ¥15 神经网络怎么把隐含层变量融合到损失函数中?
  • ¥30 自适应 LMS 算法实现 FIR 最佳维纳滤波器matlab方案
  • ¥15 lingo18勾选global solver求解使用的算法
  • ¥15 全部备份安卓app数据包括密码,可以复制到另一手机上运行
  • ¥20 测距传感器数据手册i2c
  • ¥15 RPA正常跑,cmd输入cookies跑不出来
  • ¥15 求帮我调试一下freefem代码