WooCommerce运费调整:首件收Shipping Zone运费,其余按Quantity Steps计费
WooCommerce运费计算规则调整问题:仅首件收区域运费,后续件按数量阶梯计算
我现有一段PHP代码,可在WooCommerce中基于**数量阶梯(Quantity Steps)和运输区域(Shipping Zone)**计算运费,总运费为两者之和。现在需要调整规则:
- 仅对购物车中的第一件商品收取运输区域成本
- 从第二件商品开始,仅应用基于数量阶梯的运费规则
初始代码
/* WooCommerce - increase Flat rate shipping based on X quantity steps */ add_filter( 'woocommerce_package_rates', 'add_cost_based_on_zone_and_quantity', 10, 2 ); function add_cost_based_on_zone_and_quantity( $rates, $package ) { $shipping_class_rates = array( 'very-light' => 20, // Shipping class slug => Quantity threshold for a step 'light' => 10, 'pre-medium' => 5, 'medium' => 3, 'post-medium' => 2, 'heavy' => 1, 'very-heavy' => 1/5, 'huge' => 1/10, 'very-huge' => 1, ); // Define extra costs by shipping zone ID $zone_costs = array( 'zone_1' => 80, // Adjust these with actual zone IDs and costs 'zone_2' => 100, 'zone_3' => 120, 'zone_4' => 140, ); // Initialize an array to keep track of item counts for each shipping class $item_counts = array(); foreach ($shipping_class_rates as $shipping_class => $qty_step) { $item_counts[$shipping_class] = 0; } // Count items per shipping class foreach ($package['contents'] as $cart_item) { $class_id = $cart_item['data']->get_shipping_class_id(); foreach ($shipping_class_rates as $shipping_class => $qty_step) { $term = get_term_by('slug', $shipping_class, 'product_shipping_class'); if ($class_id == $term->term_id) { $item_counts[$shipping_class] += $cart_item['quantity']; } } } // Determine the shipping zone and retrieve additional costs $shipping_zone = WC_Shipping_Zones::get_zone_matching_package($package); $zone_id = $shipping_zone->get_id(); foreach ($rates as $rate_key => $rate) { if ('flat_rate' === $rate->method_id) { $extra_cost = isset($zone_costs['zone_' . $zone_id]) ? $zone_costs['zone_' . $zone_id] : 0; $total_additional_cost = 0; // Calculate the additional cost for each shipping class foreach ($shipping_class_rates as $shipping_class => $qty_step) { $rate_operand = ceil($item_counts[$shipping_class] / $qty_step); $total_additional_cost += ($rate->cost * $rate_operand); } // Adjust the total cost $rates[$rate_key]->cost = $total_additional_cost + $extra_cost; } } return $rates; }
尝试的无效解决方案
我修改了代码,但结果不符合预期——系统仅收取运输区域成本,完全忽略了数量阶梯的运费计算,修改后的代码如下:
/* WooCommerce - increase Flat rate shipping based on X quantity steps */ add_filter('woocommerce_package_rates', 'add_cost_based_on_zone_and_quantity', 10, 2); function add_cost_based_on_zone_and_quantity($rates, $package) { $shipping_class_rates = array( 'very-light' => 20, // Shipping class slug => Quantity threshold for a step 'light' => 10, 'pre-medium' => 5, 'medium' => 3, 'post-medium' => 2, 'heavy' => 1, 'very-heavy' => 0.2, // 1/5 as a fraction 'huge' => 0.1, // 1/10 as a fraction 'very-huge' => 1, ); // Define extra costs by shipping zone ID $zone_costs = array( 'zone_1' => 80, // Adjust these with actual zone IDs and costs 'zone_2' => 100, 'zone_3' => 120, 'zone_4' => 140, ); // Initialize an array to keep track of item counts for each shipping class $item_counts = array_fill_keys(array_keys($shipping_class_rates), 0); // Calculate total item count in the cart $total_items = 0; foreach ($package['contents'] as $cart_item) { $total_items += $cart_item['quantity']; } // Determine the shipping zone and retrieve additional costs $shipping_zone = WC_Shipping_Zones::get_zone_matching_package($package); $zone_id = $shipping_zone->get_id(); $extra_cost = isset($zone_costs['zone_' . $zone_id]) ? $zone_costs['zone_' . $zone_id] : 0; if ($total_items <= 1) { // If there is only one item, apply the zone cost only foreach ($rates as $rate_key => $rate) { if ('flat_rate' === $rate->method_id) { $rates[$rate_key]->cost = $extra_cost; } } } else { // Count items per shipping class, excluding the first item $first_item_excluded = false; foreach ($package['contents'] as $cart_item) { if (!$first_item_excluded) { // Skip the first item $first_item_excluded = true; continue; } $class_id = $cart_item['data']->get_shipping_class_id(); foreach ($shipping_class_rates as $shipping_class => $qty_step) { $term = get_term_by('slug', $shipping_class, 'product_shipping_class'); if ($term && $class_id == $term->term_id) { $item_counts[$shipping_class] += $cart_item['quantity']; } } } foreach ($rates as $rate_key => $rate) { if ('flat_rate' === $rate->method_id) { $total_additional_cost = 0; // Calculate the additional cost for each shipping class foreach ($shipping_class_rates as $shipping_class => $qty_step) { $rate_operand = ceil($item_counts[$shipping_class] / $qty_step); $total_additional_cost += ($rate->cost * $rate_operand); } // Adjust the total cost $rates[$rate_key]->cost = $total_additional_cost + $extra_cost; } } } return $rates; }
修复后的代码
问题出在跳过第一件商品的逻辑:当第一件商品购买数量大于1时,直接跳过整个cart_item会漏掉本该计入阶梯计算的剩余数量。以下是修复后的代码:
/* WooCommerce - 仅首件收区域运费,后续件按数量阶梯计算运费 */ add_filter( 'woocommerce_package_rates', 'add_cost_based_on_zone_and_quantity', 10, 2 ); function add_cost_based_on_zone_and_quantity( $rates, $package ) { $shipping_class_rates = array( 'very-light' => 20, // 运输类别别名 => 阶梯数量阈值 'light' => 10, 'pre-medium' => 5, 'medium' => 3, 'post-medium' => 2, 'heavy' => 1, 'very-heavy' => 0.2, // 1/5 转为小数 'huge' => 0.1, // 1/10 转为小数 'very-huge' => 1, ); // 按运输区域ID定义区域运费 $zone_costs = array( 'zone_1' => 80, // 替换为实际区域ID和对应成本 'zone_2' => 100, 'zone_3' => 120, 'zone_4' => 140, ); // 初始化各运输类别的数量统计数组 $item_counts = array_fill_keys(array_keys($shipping_class_rates), 0); $first_item_processed = false; // 统计各运输类别的商品数量(扣除首件) foreach ($package['contents'] as $cart_item) { $class_id = $cart_item['data']->get_shipping_class_id(); $current_qty = $cart_item['quantity']; if (!$first_item_processed) { // 处理首件:仅保留区域费,剩余数量计入阶梯计算 if ($current_qty > 1) { // 首件留1个,剩下的数量加到对应运输类别 $remaining_qty = $current_qty - 1; foreach ($shipping_class_rates as $shipping_class => $qty_step) { $term = get_term_by('slug', $shipping_class, 'product_shipping_class'); if ($term && $class_id == $term->term_id) { $item_counts[$shipping_class] += $remaining_qty; break; } } } $first_item_processed = true; } else { // 非首件,全部数量计入阶梯计算 foreach ($shipping_class_rates as $shipping_class => $qty_step) { $term = get_term_by('slug', $shipping_class, 'product_shipping_class'); if ($term && $class_id == $term->term_id) { $item_counts[$shipping_class] += $current_qty; break; } } } } // 获取当前运输区域及对应成本 $shipping_zone = WC_Shipping_Zones::get_zone_matching_package($package); $zone_id = $shipping_zone->get_id(); $extra_cost = isset($zone_costs['zone_' . $zone_id]) ? $zone_costs['zone_' . $zone_id] : 0; // 调整平邮运费 foreach ($rates as $rate_key => $rate) { if ('flat_rate' === $rate->method_id) { $total_step_cost = 0; // 计算各运输类别的阶梯运费 foreach ($shipping_class_rates as $shipping_class => $qty_step) { if ($item_counts[$shipping_class] > 0) { $rate_operand = ceil($item_counts[$shipping_class] / $qty_step); $total_step_cost += ($rate->cost * $rate_operand); } } // 总运费 = 区域费 + 剩余数量的阶梯运费 $rates[$rate_key]->cost = $extra_cost + $total_step_cost; } } return $rates; }
关键修复点
- 修正首件商品的处理逻辑:当首件商品数量大于1时,仅扣除1件作为"首件",剩余数量正常计入阶梯计算
- 优化运输类别匹配逻辑,找到对应类别后直接
break,减少不必要的循环 - 确保总运费计算逻辑正确:始终包含区域费,加上剩余数量的阶梯运费
内容的提问来源于stack exchange,提问作者Shehab Zahran
相关产品推荐
相关产品推荐

