WooCommerce:如何让经销商条件折扣在运费前生效
解决WooCommerce经销商折扣先于运费计算的问题
问题原因
你当前使用woocommerce_cart_calculate_fees钩子添加折扣,但这个钩子是在WooCommerce计算完运费、税费之后才触发的——这不仅会让折扣显示在运费条目之后,而且运费计算时完全不会考虑折扣后的金额。另外,add_fee方法添加的是额外费用项,不会改变购物车商品的原始总价,导致运费始终基于原价计算。
修改方案
改用woocommerce_before_calculate_totals钩子(该钩子在所有费用计算前触发),直接调整购物车商品的价格来实现折扣。这样运费计算时会基于折扣后的商品总价,同时折扣会在运费之前生效并显示。
修改后的完整代码
// 在所有费用计算前应用经销商折扣 add_action('woocommerce_before_calculate_totals', 'apply_dealer_discounts', 10, 1); // 应用经销商自定义配送规则 add_filter('woocommerce_package_rates', 'custom_shipping_rules_dealer', 20, 2); function apply_dealer_discounts($cart) { // 避免重复计算,防止钩子多次触发 if (did_action('woocommerce_before_calculate_totals') >= 2) { return; } // 获取当前用户 $current_user = wp_get_current_user(); // 检查用户角色是否为dealer if (!in_array('dealer', (array) $current_user->roles)) { return; } $core_seasonal_discount = 0.10; // 10%折扣 $keg_discount = 0.05; // 5%折扣 $total_discount_rate = 0; // 先判断购物车内是否有符合折扣条件的商品 $has_core_seasonal = false; $has_keg = false; foreach ($cart->get_cart() as $cart_item) { if (has_term(['core', 'seasonal'], 'product_cat', $cart_item['product_id'])) { $has_core_seasonal = true; } if (has_term('keg', 'product_cat', $cart_item['product_id'])) { $has_keg = true; } } // 计算累计折扣率 if ($has_core_seasonal) { $total_discount_rate += $core_seasonal_discount; } if ($has_keg) { $total_discount_rate += $keg_discount; } // 对所有商品应用折扣 if ($total_discount_rate > 0) { foreach ($cart->get_cart() as $cart_item) { $product = $cart_item['data']; $original_price = $product->get_price('edit'); $discounted_price = $original_price * (1 - $total_discount_rate); $product->set_price($discounted_price); } } } function custom_shipping_rules_dealer($rates, $package) { // 获取当前用户 $current_user = wp_get_current_user(); // 检查用户角色是否为dealer if (!in_array('dealer', (array) $current_user->roles)) { return $rates; } // 初始化统计变量 $total_core_seasonal = 0; $keg_count = 0; // 遍历购物车商品统计数量 foreach ($package['contents'] as $item) { if (has_term(['core', 'seasonal'], 'product_cat', $item['product_id'])) { $total_core_seasonal += $item['quantity']; } if (has_term('keg', 'product_cat', $item['product_id'])) { $keg_count += $item['quantity']; } } // 获取当前配送区域 $zone = WC_Shipping_Zones::get_zone_matching_package($package); $zone_name = $zone->get_zone_name(); // 调试日志(可根据需求删除) error_log('配送区域: ' . $zone_name); error_log('Keg商品数量: ' . $keg_count); error_log('核心/季节商品数量: ' . $total_core_seasonal); // 应用配送规则 foreach ($rates as $rate_key => $rate) { // 核心/季节商品配送规则 if ($total_core_seasonal > 0) { if ($total_core_seasonal >= 10) { $rates[$rate_key]->cost = 0; } else { $rates[$rate_key]->cost = 60 * $total_core_seasonal; } } // Keg商品配送规则 if ($keg_count > 0) { if ($zone_name === '曼谷大都会区') { $rates[$rate_key]->cost = 0; } else { $rates[$rate_key]->cost = 300 * $keg_count; } } } return $rates; }
关键修改点说明
- 钩子替换:将折扣应用逻辑移到
woocommerce_before_calculate_totals,确保在运费计算前执行。 - 直接修改商品价格:不再用
add_fee添加折扣项,而是直接调整商品销售价格,让购物车总价实时更新为折扣后金额。 - 防重复计算:添加
did_action判断,避免钩子重复触发导致折扣多次叠加。 - 逻辑优化:先统计符合折扣条件的商品类型,再计算累计折扣率统一应用,也可根据需求改为针对不同商品类型单独折扣。
内容的提问来源于stack exchange,提问作者SavPhill
相关产品推荐
相关产品推荐

