Woocommerce中基于自定义复选框动态计算的自定义费用

huangapple go评论61阅读模式
英文:

Dynamic calculated custom fees based on custom checkboxes in Woocommerce

问题

我明白你的请求,以下是翻译好的部分:

在WooCommerce使用Avada主题时,我有一个小问题:
我想要显示一个字段一次,即使有多个产品,然后一次计算价格。

以下是Avada主题的review-order.php部分文件:


do_action( 'woocommerce_review_order_before_cart_contents' );

    foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
        $_product     = apply_filters( 'woocommerce_cart_item_product', $cart_item['data'], $cart_item, $cart_item_key );
        $product_id   = apply_filters( 'woocommerce_cart_item_product_id', $cart_item['product_id'], $cart_item, $cart_item_key );
        $product_name = apply_filters( 'woocommerce_cart_item_name', $_product->get_name(), $cart_item, $cart_item_key );

        // 输出产品名称。
        echo '<h4>' . esc_html( $product_name ) . '</h4>';

        // 输出liftgate字段。
        woocommerce_form_field( 'liftgate_' . $cart_item_key, array(
            'type'  => 'checkbox',
            'class' => array( 'form-row-wide' ),
            'label' => 'Add Liftgate ($90)',
        ), '');

        // 输出inside delivery字段。
        woocommerce_form_field( 'inside_delivery_' . $cart_item_key, array(
            'type'  => 'checkbox',
            'class' => array( 'form-row-wide' ),
            'label' => 'Add Inside Delivery ($135)',
        ), '');
    }

    do_action( 'woocommerce_review_order_after_cart_contents' );

这是我主题的functions.php文件中相关的代码:

function calculate_additional_fees( $cart ) {
    $liftgate_fee = 90;
    $inside_delivery_fee = 135;

    $liftgate_total = 0;
    $inside_delivery_total = 0;

    // 遍历购物车项目
    foreach ( $cart->get_cart() as $cart_item_key => $cart_item ) {
        if ( isset( $_POST['liftgate_' . $cart_item_key] ) && $_POST['liftgate_' . $cart_item_key] == 1 ) {
            $liftgate_total += $liftgate_fee;
        }

        if ( isset( $_POST['inside_delivery_' . $cart_item_key] ) && $_POST['inside_delivery_' . $cart_item_key] == 1 ) {
            $inside_delivery_total += $inside_delivery_fee;
        }
    }

    // 添加liftgate费用
    if ( $liftgate_total > 0 ) {
        $cart->add_fee( 'Liftgate', $liftgate_total );
    }

    // 添加inside delivery费用
    if ( $inside_delivery_total > 0 ) {
        $cart->add_fee( 'Inside Delivery', $inside_delivery_total );
    }
}
add_action( 'woocommerce_cart_calculate_fees', 'calculate_additional_fees', 20, 1 );

如何修复这些问题?

英文:

I have a little problem on WooCommerce using Avada theme:
I want to display a field one time, even if there are multiple products, and calculate the price one time.

See the details are below...

Here is Avada theme review-order.php partial file:

&lt;?php

do_action( &#39;woocommerce_review_order_before_cart_contents&#39; );

    foreach ( WC()-&gt;cart-&gt;get_cart() as $cart_item_key =&gt; $cart_item ) {
        $_product     = apply_filters( &#39;woocommerce_cart_item_product&#39;, $cart_item[&#39;data&#39;], $cart_item, $cart_item_key );
        $product_id   = apply_filters( &#39;woocommerce_cart_item_product_id&#39;, $cart_item[&#39;product_id&#39;], $cart_item, $cart_item_key );
        $product_name = apply_filters( &#39;woocommerce_cart_item_name&#39;, $_product-&gt;get_name(), $cart_item, $cart_item_key );

        // Output the product name.
        echo &#39;&lt;h4&gt;&#39; . esc_html( $product_name ) . &#39;&lt;/h4&gt;&#39;;

        // Output the liftgate field.
        woocommerce_form_field( &#39;liftgate_&#39; . $cart_item_key, array(
            &#39;type&#39;  =&gt; &#39;checkbox&#39;,
            &#39;class&#39; =&gt; array( &#39;form-row-wide&#39; ),
            &#39;label&#39; =&gt; &#39;Add Liftgate ($90)&#39;,
        ), &#39;&#39;);

        // Output the inside delivery field.
        woocommerce_form_field( &#39;inside_delivery_&#39; . $cart_item_key, array(
            &#39;type&#39;  =&gt; &#39;checkbox&#39;,
            &#39;class&#39; =&gt; array( &#39;form-row-wide&#39; ),
            &#39;label&#39; =&gt; &#39;Add Inside Delivery ($135)&#39;,
        ), &#39;&#39;);
    }

    do_action( &#39;woocommerce_review_order_after_cart_contents&#39; );

Here is my related code in y theme's functions.php file:

function calculate_additional_fees( $cart ) {
    $liftgate_fee = 90;
    $inside_delivery_fee = 135;

    $liftgate_total = 0;
    $inside_delivery_total = 0;

    // Loop through cart items
    foreach ( $cart-&gt;get_cart() as $cart_item_key =&gt; $cart_item ) {
        if ( isset( $_POST[&#39;liftgate_&#39; . $cart_item_key] ) &amp;&amp; $_POST[&#39;liftgate_&#39; . $cart_item_key] == 1 ) {
            $liftgate_total += $liftgate_fee;
        }

        if ( isset( $_POST[&#39;inside_delivery_&#39; . $cart_item_key] ) &amp;&amp; $_POST[&#39;inside_delivery_&#39; . $cart_item_key] == 1 ) {
            $inside_delivery_total += $inside_delivery_fee;
        }
    }

    // Add liftgate fee
    if ( $liftgate_total &gt; 0 ) {
        $cart-&gt;add_fee( &#39;Liftgate&#39;, $liftgate_total );
    }

    // Add inside delivery fee
    if ( $inside_delivery_total &gt; 0 ) {
        $cart-&gt;add_fee( &#39;Inside Delivery&#39;, $inside_delivery_total );
    }
}
add_action( &#39;woocommerce_cart_calculate_fees&#39;, &#39;calculate_additional_fees&#39;, 20, 1 );

How could I fix those issues.

答案1

得分: 0

为允许根据购物车项目计数而触发的两个唯一复选框计算的2个自定义费用,需要使用Ajax和会话变量提供更复杂的功能...

首先,您应该恢复原始的模板文件,从您的Avada主题自定义的review-order.php文件中删除这两个自定义字段。

基于[此类似的主题][1],以下是代码:

// 为包装选择添加自定义单选框字段
add_action( 'woocommerce_review_order_after_shipping', 'checkout_shipping_form_delivery_addition', 20 );
function checkout_shipping_form_delivery_addition(){
    $domain = 'woocommerce';

    echo '<tr class="delivery-checkbox"><th>' . __('Delivery options (per item)', $domain) . '</th><td>';

    // 从WooCommerce会话变量中获取交付选项数据
    $delivery_fees = WC()->session->get('delivery_options');
    
    $chosen_liftgate     = isset($delivery_fees['liftgate']) && $delivery_fees['liftgate'] ? true : false;
    $chosen_ins_delivery = isset($delivery_fees['ins_delivery']) && $delivery_fees['ins_delivery'] ? true : false;

    // 输出Liftgate字段
    woocommerce_form_field( 'liftgate', array(
        'type'  => 'checkbox',
        'class' => array( 'form-row-wide' ),
        'label' => __('Liftgate ($90)', $domain),
    ), $chosen_liftgate);

    // 输出Inside Delivery字段
    woocommerce_form_field( 'ins_delivery', array(
        'type'  => 'checkbox',
        'class' => array( 'form-row-wide' ),
        'label' => __('Inside Delivery ($135)', $domain),
    ), $chosen_ins_delivery);

    echo '</td></tr>';
}

// PHP:从非必填字段中移除“(可选)”
add_filter( 'woocommerce_form_field' , 'remove_checkout_optional_fields_label', 10, 4 );
function remove_checkout_optional_fields_label( $field, $key, $args, $value ) {
    // 仅在结帐页面上
    if( is_checkout() && ! is_wc_endpoint_url() && ( 'liftgate' === $key || 'inside_delivery' === $key )) {
        $optional = '&nbsp;<span class="optional">(' . esc_html__( 'optional', 'woocommerce' ) . ')</span>';
        $field = str_replace( $optional, '', $field );
    }
    return $field;
}

// jQuery - Ajax脚本
add_filter( 'wp_footer' , 'checkout_delivery_script' );
function checkout_delivery_script() {
    // 仅在结帐页面上
    if( ! ( is_checkout() && ! is_wc_endpoint_url() ) ) return;
    
    // WC()->session->__unset('delivery_options'); // 重置'delivery_options'会话变量

    $optional = '&nbsp;<span class="optional">(' . esc_html__( 'optional', 'woocommerce' ) . ')</span>';
    ?>
    <script>
    jQuery(function($){
        if (typeof wc_checkout_params === 'undefined')
            return false;
            
        // 在“更新”结帐表单事件上移除自定义复选框字段中的“(可选)”
        $(document.body).on('update_checkout', function(){
            $('#liftgate_field label > .optional').remove();
            $('#inside_delivery_field label > .optional').remove();
        });
        
        // Ajax 
        $('form.checkout').on('change', '.delivery-checkbox input', function(e){
            //e.preventDefault();
            var iSchecked = $(this).is(":checked") ? 1 : 0,
                fieldKey = e.target['name'];
            
            $.ajax({
                type: 'POST',
                url: wc_checkout_params.ajax_url,
                data: {
                    'action':     'delivery_options',
                    'is_checked': iSchecked,
                    'field_slug': fieldKey,
                },
                success: function (result) {
                    $('body').trigger('update_checkout');
                    console.log(result); // 仅用于测试 | 待移除
                },
                error: function(error){
                    console.log(error); // 仅用于测试 | 待移除
                }
            });
        });
    });
    </script>
    <?php
}

// 获取Ajax请求并保存到WC会话中
add_action( 'wp_ajax_delivery_options', 'wc_get_delivery_options_data' );
add_action( 'wp_ajax_nopriv_delivery_options', 'wc_get_delivery_options_data' );
function wc_get_delivery_options_data() {
    if ( isset($_POST['is_checked']) && isset($_POST['field_slug']) ){
        // 从WooCommerce会话变量中获取交付选项
        $delivery_options = WC()->session->get('delivery_options');
        
        // 如果为空,则初始化
        if ( empty($delivery_options) ) {
            $delivery_options = array( 'liftgate' => 0,  'ins_delivery' => 0 );
        }
        
        // 清理数据
        $field_slug = sanitize_key( $_POST['field_slug'] );
        $is_checked = $_POST['is_checked'] ? '1' : '0';
        
        // 将新数据设置到数据数组中
        $delivery_options[$field_slug] = $is_checked;
        
        // 更新交付选项WooCommerce会话变量
        WC()->session->set('delivery_options', $delivery_options );
        
        echo json_encode( $delivery_options ); // 返回值给jQuery
    }
    die();
}

// 动态计算交付选项费用
add_action( 'woocommerce_cart_calculate_fees', 'calculate_additional_fees', 20, 1 );
function calculate_additional_fees( $cart ) {
    // 仅在结帐页面上
    if( ! ( is_checkout() && ! is_wc_endpoint_url() ) ) return;
    
    // 项目计数
    $cart_items_count = count($cart->get_cart()); // 按购物车项目计数
    // $cart_items_count = $cart->get_cart_contents_count(); // 按总购物车项目数量计数
 
    // 按项目计算费用
    $liftgate_fee = 90;
    $inside_delivery_fee = 135;
    
    // 从WooCommerce会话变量中获取交付选项
    $delivery_options = WC()->session->get('delivery_options');

    // 添加Liftgate费用
    if ( isset($delivery_options['liftgate']) && $delivery_options['liftgate'] ) {
        $cart->add_fee( __('Liftgate'), $liftgate_fee * $cart_items_count );
    }

    // 添加Inside Delivery费用
    if ( isset($delivery_options['ins_delivery']) && $delivery_options['ins_delivery'] ) {
        $cart->add_fee( __('Inside Delivery

<details>
<summary>英文:</summary>

To allow 2 custom fees calculated on cart items count triggered by 2 unique checkboxes, requires something much more complex powered by Ajax and session variables... 

First, you should restore the original template file, removing those 2 custom fields from your Avada theme customized *review-order.php* file.

Based [on this similar thread][1] here is the code:

// Add a custom radio fields for packaging selection
add_action( 'woocommerce_review_order_after_shipping', 'checkout_shipping_form_delivery_addition', 20 );
function checkout_shipping_form_delivery_addition(){
$domain = 'wocommerce';

echo &#39;&lt;tr class=&quot;delivery-checkbox&quot;&gt;&lt;th&gt;&#39; . __(&#39;Delivery options (per item)&#39;, $domain) . &#39;&lt;/th&gt;&lt;td&gt;&#39;;
// Get delivery options data from WooCommerce Session variable
$delivery_fees = WC()-&gt;session-&gt;get(&#39;delivery_options&#39;);
$chosen_liftgate     = isset($delivery_fees[&#39;liftgate&#39;]) &amp;&amp; $delivery_fees[&#39;liftgate&#39;] ? true : false;
$chosen_ins_delivery = isset($delivery_fees[&#39;ins_delivery&#39;]) &amp;&amp; $delivery_fees[&#39;ins_delivery&#39;] ? true : false;
// Output the liftgate field.
woocommerce_form_field( &#39;liftgate&#39;, array(
&#39;type&#39;  =&gt; &#39;checkbox&#39;,
&#39;class&#39; =&gt; array( &#39;form-row-wide&#39; ),
&#39;label&#39; =&gt; __(&#39;Liftgate ($90)&#39;, $domain),
), $chosen_liftgate);
// Output the inside delivery field.
woocommerce_form_field( &#39;ins_delivery&#39;, array(
&#39;type&#39;  =&gt; &#39;checkbox&#39;,
&#39;class&#39; =&gt; array( &#39;form-row-wide&#39; ),
&#39;label&#39; =&gt; __(&#39;Inside Delivery ($135)&#39;, $domain),
), $chosen_ins_delivery);
echo &#39;&lt;/td&gt;&lt;/tr&gt;&#39;;

}

// PHP: Remove "(optional)" from our non required fields
add_filter( 'woocommerce_form_field' , 'remove_checkout_optional_fields_label', 10, 4 );
function remove_checkout_optional_fields_label( $field, $key, $args, $value ) {
// Only on checkout page
if( is_checkout() && ! is_wc_endpoint_url() && ( 'liftgate' === $key || 'inside_delivery' === $key )) {
$optional = '&nbsp;<span class="optional">(' . esc_html__( 'optional', 'woocommerce' ) . ')</span>';
$field = str_replace( $optional, '', $field );
}
return $field;
}

// jQuery - Ajax script
add_filter( 'wp_footer' , 'checkout_delivery_script' );
function checkout_delivery_script() {
// Only on checkout page
if( ! ( is_checkout() && ! is_wc_endpoint_url() ) ) return;

// WC()-&gt;session-&gt;__unset(&#39;delivery_options&#39;); // Reset &#39;delivery_options&#39; session variable
$optional = &#39;&amp;nbsp;&lt;span class=&quot;optional&quot;&gt;(&#39; . esc_html__( &#39;optional&#39;, &#39;woocommerce&#39; ) . &#39;)&lt;/span&gt;&#39;;
?&gt;
&lt;script&gt;
jQuery(function($){
if (typeof wc_checkout_params === &#39;undefined&#39;)
return false;
// On &quot;update&quot; checkout form event Remove &quot;(optional)&quot; from our custom checkbox fields
$(document.body).on(&#39;update_checkout&#39;, function(){
$(&#39;#liftgate_field label &gt; .optional&#39;).remove();
$(&#39;#inside_delivery_field label &gt; .optional&#39;).remove();
});
// Ajax 
$(&#39;form.checkout&#39;).on(&#39;change&#39;, &#39;.delivery-checkbox input&#39;, function(e){
//e.preventDefault();
var iSchecked = $(this).is(&quot;:checked&quot;) ? 1 : 0,
fieldKey = e.target[&#39;name&#39;];
$.ajax({
type: &#39;POST&#39;,
url: wc_checkout_params.ajax_url,
data: {
&#39;action&#39;:     &#39;delivery_options&#39;,
&#39;is_checked&#39;: iSchecked,
&#39;field_slug&#39;: fieldKey,
},
success: function (result) {
$(&#39;body&#39;).trigger(&#39;update_checkout&#39;);
console.log(result); // just for testing | TO BE REMOVED
},
error: function(error){
console.log(error); // just for testing | TO BE REMOVED
}
});
});
});
&lt;/script&gt;
&lt;?php

}

// Get Ajax request and saving to WC session
add_action( 'wp_ajax_delivery_options', 'wc_get_delivery_options_data' );
add_action( 'wp_ajax_nopriv_delivery_options', 'wc_get_delivery_options_data' );
function wc_get_delivery_options_data() {
if ( isset($_POST['is_checked']) && isset($_POST['field_slug']) ){
// Get delivery options from WooCommerce Session variable
$delivery_options = WC()->session->get('delivery_options');

    // Initializing if empty
if ( empty($delivery_options) ) {
$delivery_options = array( &#39;liftgate&#39; =&gt; 0,  &#39;ins_delivery&#39; =&gt; 0 );
}
// Sanitizing data
$field_slug = sanitize_key( $_POST[&#39;field_slug&#39;] );
$is_checked = $_POST[&#39;is_checked&#39;] ? &#39;1&#39; : &#39;0&#39;;
// Set new data to the data array
$delivery_options[$field_slug] = $is_checked;
// Updating delivery options WooCommerce Session variable
WC()-&gt;session-&gt;set(&#39;delivery_options&#39;, $delivery_options );
echo json_encode( $delivery_options ); // Return the value to jQuery
}
die();

}

// Calculate delivery option Fees dynamically
add_action( 'woocommerce_cart_calculate_fees', 'calculate_additional_fees', 20, 1 );
function calculate_additional_fees( $cart ) {
// Only on checkout page
if( ! ( is_checkout() && ! is_wc_endpoint_url() ) ) return;

// Items count
$cart_items_count = count($cart-&gt;get_cart()); // count by cart items
// $cart_items_count = $cart-&gt;get_cart_contents_count(); // count by total cart items quantity
// Fee costs by item
$liftgate_fee = 90;
$inside_delivery_fee = 135;
// Get delivery options from WooCommerce Session variable
$delivery_options = WC()-&gt;session-&gt;get(&#39;delivery_options&#39;);
// Add liftgate fee
if ( isset($delivery_options[&#39;liftgate&#39;]) &amp;&amp; $delivery_options[&#39;liftgate&#39;] ) {
$cart-&gt;add_fee( __(&#39;Liftgate&#39;), $liftgate_fee * $cart_items_count );
}
// Add inside delivery fee
if ( isset($delivery_options[&#39;ins_delivery&#39;]) &amp;&amp; $delivery_options[&#39;ins_delivery&#39;] ) {
$cart-&gt;add_fee( __(&#39;Inside Delivery&#39;), $inside_delivery_fee * $cart_items_count );
}

}

Code goes in functions.php file of your active child theme (or active theme). Tested and works.
[![enter image description here][2]][2]
[1]: https://stackoverflow.com/questions/51558286/dynamic-shipping-fee-based-on-custom-radio-buttons-in-woocommerce/51572051#51572051
[2]: https://i.stack.imgur.com/3qdKn.png
</details>

huangapple
  • 本文由 发表于 2023年6月9日 07:11:59
  • 转载请务必保留本文链接:https://go.coder-hub.com/76436244.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定