【问题标题】:Apply woocommerce_coupon_get_discount_amount only to percent type coupons仅将 woocommerce_coupon_get_discount_amount 应用于百分比类型的优惠券
【发布时间】:2022-01-15 16:37:40
【问题描述】:

我创建了以下函数来覆盖产品价格并为 WooCommerce 提供最大折扣。但是,我似乎无法弄清楚为什么 woocommerce_coupon_get_discount_amount 过滤器适用于所有优惠券。

我试图让它发挥作用的方式是,如果优惠券是百分比优惠券,它将限制该优惠券的总折扣金额。如果优惠券是 fixed_cart 类型,并且填写了我添加的自定义字段 _price_per_product_amt,那么它将更改产品的价格。

如果我的购物车中只有一张优惠券,这两个功能都可以使用。如果我在购物车中有 fixed_cart_coupon,它将更改价格并将优惠券金额设置为 0 美元(优惠券的金额设置为 0 美元)。但是,如果我也添加百分比优惠券,该功能适用​​于百分比优惠券,但它也会向 fixed_cart_coupon 添加折扣金额。它应该只修改购物车中百分比优惠券的折扣金额。

// filter to change discount if over max coupon amount
function filter_woocommerce_coupon_get_discount_amount( $discount, $discounting_amount, $cart_item, $single, $instance ) {

    $cartCoupons = WC()->cart->get_applied_coupons();
    
    foreach ($cartCoupons as $key => $appliedCoupon) {
        $coupon = new WC_Coupon($appliedCoupon);
        $couponType = get_post_meta( $coupon->get_id(), 'discount_type', true );
        if ($couponType == 'percent') {
            $maxCouponAmount = get_post_meta( $coupon->get_id(), '_max_discount', true );
            $excludedProducts = explode(",", get_post_meta( $coupon->get_id(), 'exclude_product_ids', true ));
            $cartLines = count(WC()->cart->get_cart());
            $cartLineItems = WC()->cart->get_cart();

            foreach ($cartLineItems as $cartItem){
                $cartProductID[] = $cartItem['product_id'];

                if (!empty($excludedProducts)) {
                    $cartLinesWithoutExcluded = array_intersect($cartProductID,$excludedProducts);
                } else {
                    $cartLinesWithoutExcluded = $cartProductID;
                }
                $cartLinesWithoutExcluded = count($cartLinesWithoutExcluded);
                $totalCartItems = $cartLines - $cartLinesWithoutExcluded;
                $discount = $maxCouponAmount / $totalCartItems;
            }
        } else {
            $discount = 0.00;
        }

        return $discount;

    }

}

// apply the coupon whether it is max discount or a product price adjustment
function apply_max_amount_or_product_price_adjustment(){

    if ( is_admin() && ! defined( 'DOING_AJAX' ) )
    return;

    if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
    return;

    if ( !is_admin() && !wp_is_json_request() ) {
        global $wp, $woocommerce;
        $cartCoupons = WC()->cart->get_applied_coupons();
        foreach ($cartCoupons as $key => $appliedCoupon) {
            $coupon = new WC_Coupon($appliedCoupon);
            $maxCouponAmount = get_post_meta( $coupon->get_id(), '_max_discount', true );
            $excludedProducts = explode(",", get_post_meta( $coupon->get_id(), 'exclude_product_ids', true ));
            $couponType = get_post_meta( $coupon->get_id(), 'discount_type', true );
            // $fixedProductPrice = get_post_meta( $coupon->get_id(), '_adjust_price', true );
            $couponAmount = WC()->cart->get_coupon_discount_amount( $appliedCoupon );

            if (!empty($maxCouponAmount) && $couponType == 'percent' && ($couponAmount > $maxCouponAmount || $couponAmount == $maxCouponAmount)) {
                add_filter( 'woocommerce_coupon_get_discount_amount', 'filter_woocommerce_coupon_get_discount_amount', 10, 5 );
            }

            if ($couponType == 'fixed_cart'){
                $cart = WC()->cart->get_cart();
                $couponProducts = explode(',',get_post_meta( $coupon->get_id(), 'product_ids', true ));
                $fixedPricePerProduct = get_post_meta( $coupon->get_id(), '_price_per_product_amt', true );

                foreach( $cart as $cart_item ) {
                    if (in_array($cart_item['data']->get_parent_id(), $couponProducts)) {
                        $cart_item['data']->set_price( $fixedPricePerProduct );
                    }
                }
            }
        }
    }
}
add_action('woocommerce_before_calculate_totals', 'apply_max_amount_or_product_price_adjustment', 10, 1);

【问题讨论】:

    标签: woocommerce hook-woocommerce coupon filter-woocommerce


    【解决方案1】:

    我似乎无法弄清楚为什么 woocommerce_coupon_get_discount_amount 过滤器适用于所有优惠券。

    这是因为当您从函数“apply_max_amount_or_product_price_adjustment”添加过滤器“filter_woocommerce_coupon_get_discount_amount”时,它会添加到所有优惠券中。此过滤器稍后会触发,因此您应用或不应用它的条件将不起作用(因为它已被添加)。您应该删除添加过滤器的部分并将您的过滤器设置在此功能之外。

    当您添加任何过滤器时,即使在条件内,由于它已添加,因此每次都会添加它,直到它被 remove_filter( ... ) 删除。在您的情况下,您无法在“apply_max_amount_or_product_price_adjustment”中删除_filter,因为它为时过早,并且会为所有优惠券事件“百分比”类型删除它。

    但是,如果我也添加百分比优惠券,该功能适用​​于百分比优惠券,但它也会向固定购物车优惠券添加折扣金额

    这是因为您循环过滤器“filter_woocommerce_coupon_get_discount_amount”中的所有优惠券,并始终从第一张优惠券返回价值。每次有 $coupon->get_discount_amount() 时都会触发此过滤器,因此它会在结帐时在您的购物车上应用的每张优惠券上触发。您应该删除循环并仅检查当前优惠券。为了更好地理解,您应该将参数“$instance”重命名为“$coupon”,即当前的 WC_Coupon 对象。

    您可以查看 WC_Coupon 类并在此处搜索过滤器“woocommerce_coupon_get_discount_amount”(按 ctrl + f 在页面中找到它):https://github.com/woocommerce/woocommerce/blob/trunk/plugins/woocommerce/includes/class-wc-coupon.php

    另外,在你的循环中要小心:

    foreach ($cartLineItems as $cartItem){
        $cartProductID[] = $cartItem['product_id'];
    
        if (!empty($excludedProducts)) {
            $cartLinesWithoutExcluded = array_intersect($cartProductID,$excludedProducts);
        } else {
            $cartLinesWithoutExcluded = $cartProductID;
        }
    
        $cartLinesWithoutExcluded = count($cartLinesWithoutExcluded);
        $totalCartItems = $cartLines - $cartLinesWithoutExcluded;
        $discount = $maxCouponAmount / $totalCartItems; 
    }
    // here $discount is the one calculated in last loop like all previous loops didn't exists. (same thing for $cartLinesWithoutExcluded and $totalCarItems)
    

    因为它总是只返回最后一个购物车商品的 $discount。

    试试这个代码(未测试):

    add_filter( 'woocommerce_coupon_get_discount_amount', 'filter_woocommerce_coupon_get_discount_amount', 10, 5 ); // add filter for all coupons
    // filter to change discount if over max coupon amount
    function filter_woocommerce_coupon_get_discount_amount( $discount, $discounting_amount, $cart_item, $single, $coupon ) { 
            // remove loop on applied coupons because you are managing coupon that is currently applied to the cart   
            if ( $coupon->is_type( 'percent' ) ) {
                $maxCouponAmount = get_post_meta( $coupon->get_id(), '_max_discount', true );
    
                // TRY TO REMOVE THIS FOR TESTING
                // OR TRY TO return $maxCouponAmount to check this value
                if ( empty( $maxCouponAmount ) ) { // do not change coupon discount if no max_discount
                    return $discount;
                }
                // ---
    
                $excludedProducts = explode(",", get_post_meta( $coupon->get_id(), 'exclude_product_ids', true ));
                $cartLines = count(WC()->cart->get_cart());
                $cartLineItems = WC()->cart->get_cart();
    
                // Changes in your loop
                $cartLinesWithoutExcluded = 0; // *** UPDATE HERE
    
                foreach ($cartLineItems as $cartItem){
                    $cartProductID[] = $cartItem['product_id'];
                    if (!empty($excludedProducts)) {
                        $cartLinesWithoutExcluded += count ( array_intersect($cartProductID,$excludedProducts) );
                    } else {
                        $cartLinesWithoutExcluded += count ( $cartProductID );
                    }
                }
                $totalCartItems = $cartLines - $cartLinesWithoutExcluded;
                $discount = $totalCartItems > 0 ? $maxCouponAmount / $totalCartItems : 0; // Never divide by 0 !
                // end of changes in your loop
            } elseif ( $coupon->is_type( 'fixed_cart' ) ) {
                $discount = 0.00;
            }
    
            return $discount;
    
    }
    
    // apply the coupon whether it is max discount or a product price adjustment
    function apply_max_amount_or_product_price_adjustment(){
    
        if ( is_admin() && ! defined( 'DOING_AJAX' ) )
        return;
    
        if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
        return;
    
        if ( !is_admin() && !wp_is_json_request() ) {
            global $wp, $woocommerce;
            $cartCoupons = WC()->cart->get_applied_coupons();
            foreach ($cartCoupons as $key => $appliedCoupon) {
                $coupon = new WC_Coupon($appliedCoupon);
                $maxCouponAmount = get_post_meta( $coupon->get_id(), '_max_discount', true );
                $excludedProducts = explode(",", get_post_meta( $coupon->get_id(), 'exclude_product_ids', true ));
                $couponType = get_post_meta( $coupon->get_id(), 'discount_type', true );
                // $fixedProductPrice = get_post_meta( $coupon->get_id(), '_adjust_price', true );
                $couponAmount = WC()->cart->get_coupon_discount_amount( $appliedCoupon );
    
                // removed adding filter here
    
                if ($couponType == 'fixed_cart'){
                    $cart = WC()->cart->get_cart();
                    $couponProducts = explode(',',get_post_meta( $coupon->get_id(), 'product_ids', true ));
                    $fixedPricePerProduct = get_post_meta( $coupon->get_id(), '_price_per_product_amt', true );
    
                    foreach( $cart as $cart_item ) {
                        if (in_array($cart_item['data']->get_parent_id(), $couponProducts)) {
                            $cart_item['data']->set_price( $fixedPricePerProduct );
                        }
                    }
                }
            }
        }
    }
    add_action('woocommerce_before_calculate_totals', 'apply_max_amount_or_product_price_adjustment', 10, 1);
    

    如有必要,请测试和编辑代码。对不起,如果我的解释看起来很混乱,我在半夜写。

    【讨论】:

    • 这些更改允许 fixed_cart 优惠券根据需要覆盖价格,但随后它也会为百分比优惠券设置 0 折扣。
    • 你可以试试'$cartLinesWithoutExcluded = 0;'在 filter_woocommerce_coupon_get_discount_amount 中?
    • 这也不会改变它。它仍然尝试对固定优惠券应用百分比。
    • 您在哪里为每张优惠券设置元“_max_discount”?它是在后台手动填写的元字段还是以编程方式计算的元字段?如果计算出来,你能编辑你的第一篇文章添加你的代码吗?
    • 我还更新了一些代码行
    最近更新 更多