Cách tạo Conditional Shipping Woocommerce thủ công
Chào quý khách, hiện Nam Digital có cơ hội thực hiện một dự Ecommerce sử dụng Wordpress Block Themes + Ecommerce + Phần mềm chuyển đổi tiền tệ CURCY, sau quá trình triển khai dự án hoàn tất, Nam gặp một đề bài mới về việc thực hiện tạo Shipping theo đều kiện, đây là những gợi ý trích nguyên văn từ khách hàng (Tôi sẽ để các tên chuyên mục là A, B cho dễ hình dung)
"If [(Total >=700 AND CATEGORY = A) AND Location is Vietnam] = Freeship If (Total >=1500 AND CATEGORY có cả A & B) Location is Vietnam] = Freeship, còn lại là Flat Rate (theo trình quản lý của Woocommerce)
Nam cũng thấy khá choáng ngợp với đề bài, và cũng tham khảo 1 số Plugins hỗ trợ xử lý khá tốt việc này, nhưng khách hàng đang muốn sử dụng giải pháp Hard Code, và việc đẩy lên Chatgpt để tìm lời giải đáp là vô cùng hợp lý.
Giải pháp: Viết 1 Plugins riêng cho phần này
Nam sẽ tạo 1 Plugins đảm nhiệm chức năng này, ta sẽ tạo 1 folder ví dụ là namdigital-custom-conditional-shipping, tạo 1 File php chủ của dự án, ta sẽ thêm 1 số thông tin
<?php
/**
* Plugin Name: Custom Conditional Shipping
* Description: Freeship or flat rate based on subtotal, product category, location and current currency (supports CURCY).
* Version: 1.2
* Requires at least: 6.0
* Requires PHP: 7.4
* Requires Plugins: woocommerce
* Author: Nam Digital
*/
Cần tạo điều kiện bắt buộc là phải có Woocommerce chạy, nếu có thì mới cho chạy Plugins
if (!defined('ABSPATH'))
exit;
add_action('plugins_loaded', 'nam_check_woocommerce_dependency');
function nam_check_woocommerce_dependency()
{
if (!class_exists('WooCommerce')) {
add_action('admin_notices', function () {
echo '<div class="notice notice-error"><p><strong>Custom Conditional Shipping</strong> requires WooCommerce to be installed and active.</p></div>';
});
return;
}
add_filter('woocommerce_package_rates', 'nam_custom_shipping_logic', 20, 2);
}
Ở đây hàm nam_custom_shipping_logic sẽ là hàm chính thực hiện phần Logic
Thiết lập hàm nam_custom_shipping_logic
Dưới đây là hàm đầy đủ, nhưng cơ bản nó sẽ thực hiện xác định khoảng giá được khuyến mại min là $700 là max là $1500 để được Freeship, sau đó sẽ xác định với điều kiện nào thì được khuyến mại $700, điều kiện nào là $1500
function nam_custom_shipping_logic($rates, $package)
{
$enable_freeship = false;
$threshold_type = '';
$currencyThreshold = [
'a' => floatval(get_theme_mod('nam_a_threshold', 700)), // USD
'other' => floatval(get_theme_mod('nam_other_threshold', 1500)), // USD
];
// Lấy thông tin từ CURCY
$rate = 1;
$currentCurrency = 'USD';
if (class_exists('WOOMULTI_CURRENCY_Data')) {
$multiCurrencySettings = WOOMULTI_CURRENCY_Data::get_ins();
$wmcCurrencies = $multiCurrencySettings->get_list_currencies();
$currentCurrency = $multiCurrencySettings->get_current_currency();
if (isset($wmcCurrencies[$currentCurrency]['rate'])) {
$rate = floatval($wmcCurrencies[$currentCurrency]['rate']);
}
// Fallback nếu VND nhưng rate vẫn là 1 (trường hợp lỗi)
if (strtoupper($currentCurrency) === 'VND' && $rate == 1) {
$rate = 25000; // tuỳ chỉnh theo giá bạn dùng
}
}
// Subtotal đã quy đổi (hiển thị cho user)
$subtotal = WC()->cart->subtotal;
// Subtotal USD
$subtotal_usd = (strtoupper($currentCurrency) === 'USD') ? $subtotal : ($subtotal / $rate);
// Kiểm tra quốc gia giao hàng
$destination_country = WC()->customer->get_shipping_country();
// Xác định danh mục
$hasA = false;
$hasB = false;
$hasOther = false;
$a_slugs = get_theme_mod('nam_a_slugs', 'a');
$b_slugs = get_theme_mod('nam_b_slugs', 'b');
foreach (WC()->cart->get_cart() as $cart_item) {
$product = $cart_item['data'];
$product_id = $product->get_id();
// Nếu là variation → dùng parent
if ($product->is_type('variation')) {
$product_id = $product->get_parent_id();
}
$terms = get_the_terms($product_id, 'product_cat');
if ($terms && !is_wp_error($terms)) {
$isKnown = false;
foreach ($terms as $term) {
$slug = strtolower($term->slug); // Dùng slug thay vì name
if ($slug === $a_slugs) {
$hasA = true;
$isKnown = true;
} elseif ($slug === $b_slugs) {
$hasB = true;
$isKnown = true;
}
}
if (!$isKnown) {
$hasOther = true;
}
}
}
$onlyA = $hasA && !$hasB && !$hasOther;
// Áp dụng điều kiện freeship
if ($destination_country === 'VN') {
if ($onlyA && $subtotal_usd >= $currencyThreshold['a']) {
$enable_freeship = true;
$threshold_type = 'a';
} elseif (($hasOther || ($hasA && ($hasB || $hasOther))) && $subtotal_usd >= $currencyThreshold['other']) {
$enable_freeship = true;
$threshold_type = 'other';
}
}
$subtotal_base = $subtotal_usd * $rate;
if (current_user_can('manage_options') && is_checkout()) {
echo '<pre style="background: #f1f1f1; padding: 10px; border: 1px solid #ccc;">';
echo "[Shipping Debug]\n";
echo '<pre>';
print_r($terms);
echo '</pre>';
echo "Currency: $currentCurrency\n";
echo "Rate: $rate\n";
echo "A: $a_slugs\n";
echo "B: $b_slugs\n";
echo "Subtotal: $subtotal\n";
echo "Subtotal (base): $subtotal_base\n";
echo "Subtotal (converted USD): $subtotal_usd\n";
echo "A only: " . ($onlyA ? 'YES' : 'NO') . "\n";
echo "Has A: " . ($hasA ? 'YES' : 'NO') . "\n";
echo "Has B: " . ($hasB ? 'YES' : 'NO') . "\n";
echo "Has Other: " . ($hasOther ? 'YES' : 'NO') . "\n";
echo "Threshold Used: $threshold_type\n";
echo "Enable FreeShip: " . ($enable_freeship ? 'YES' : 'NO') . "\n";
echo '</pre>';
}
// Ẩn logic free ship nếu cần
if ($enable_freeship) {
foreach ($rates as $rate_id => $rate) {
if ($rate->method_id !== 'free_shipping') {
unset($rates[$rate_id]);
}
}
} else {
foreach ($rates as $rate_id => $rate) {
if ($rate->method_id === 'free_shipping') {
unset($rates[$rate_id]);
}
}
}
return $rates;
}
Với logic kể trên, ta sẽ được Freeship khi:
- Có giỏ hàng chứa sản phẩm thuộc chuyên mục A có giá > $700
- Có giỏ hàng thuộc cả chuyên mục A lẫn B có giá > $1500
- Khu vực là Việt Nam (Ship nội địa)
Bước tiếp theo, xây dựng Themes Customizer để đưa các giá trị có thể chỉnh sửa được
// Xử lý logic cho chỉnh sửa loại shipping
function nam_register_shipping_settings($wp_customize)
{
// Section
$wp_customize->add_section('nam_shipping_section', [
'title' => __('Shipping Settings', 'your-textdomain'),
'priority' => 160,
]);
// RTW Threshold
$wp_customize->add_setting('nam_rtw_threshold', [
'default' => 700,
'transport' => 'refresh',
'type' => 'theme_mod',
]);
$wp_customize->add_control('nam_rtw_threshold', [
'label' => __('RTW Threshold (USD)', 'your-textdomain'),
'section' => 'nam_shipping_section',
'type' => 'number',
'input_attrs' => ['step' => 1, 'min' => 0],
]);
// Other Threshold
$wp_customize->add_setting('nam_other_threshold', [
'default' => 1500,
'transport' => 'refresh',
'type' => 'theme_mod',
]);
$wp_customize->add_control('nam_other_threshold', [
'label' => __('Other Threshold (USD)', 'your-textdomain'),
'section' => 'nam_shipping_section',
'type' => 'number',
'input_attrs' => ['step' => 1, 'min' => 0],
]);
// RTW Slugs (comma-separated)
$wp_customize->add_setting('nam_rtw_slugs', [
'default' => 'ready-to-wear',
'transport' => 'refresh',
'type' => 'theme_mod',
]);
$wp_customize->add_control('nam_rtw_slugs', [
'label' => __('RTW Category Slugs (comma-separated)', 'your-textdomain'),
'section' => 'nam_shipping_section',
'type' => 'text',
]);
// Accessories Slugs
$wp_customize->add_setting('nam_accessory_slugs', [
'default' => 'accessories',
'transport' => 'refresh',
'type' => 'theme_mod',
]);
$wp_customize->add_control('nam_accessory_slugs', [
'label' => __('Accessory Category Slugs (comma-separated)', 'your-textdomain'),
'section' => 'nam_shipping_section',
'type' => 'text',
]);
}
add_action('customize_register', 'nam_register_shipping_settings');
Tôi đã in 4 custom field vào themes đó là khoảng giá được khuyến mại của category A (mặc định $700), khoảng giá được khuyến mại khi mua cả A lẫn B ($1500) cũng như cho phép thay đổi chuyên mục A, B