You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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时,仅扣除1件作为"首件",剩余数量正常计入阶梯计算
  2. 优化运输类别匹配逻辑,找到对应类别后直接break,减少不必要的循环
  3. 确保总运费计算逻辑正确:始终包含区域费,加上剩余数量的阶梯运费

内容的提问来源于stack exchange,提问作者Shehab Zahran

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 12:44:56