~/icsd.ir — bash
SYSTEM_ONLINE

🚚 فصل ۵: روش‌های ارسال پیشرفته

در این فصل یاد می‌گیرید چطور سیستم ارسال حرفه‌ای، چندمنطقه‌ای و سفارشی برای فروشگاه ایرانی بسازید — از تیپاکس و پست تا محاسبه پویای هزینه بر اساس وزن و موقعیت.

🌍 مفهوم Shipping Zones

ووکامرس از منطقه ارسال (Shipping Zone) استفاده می‌کند. هر منطقه می‌تواند چندین روش ارسال داشته باشد:

منطقه مناطق پوشش روش‌ها
تهران استان تهران پیک موتوری، پست پیشتاز
شهرستان سایر استان‌ها تیپاکس، پست
خارج کشور سایر کشورها EMS، DHL

تنظیم از: WooCommerce → Settings → Shipping → Shipping zones

🛠️ ساخت روش ارسال سفارشی

برای ساخت روش ارسال جدید، باید کلاس WC_Shipping_Method را extend کنید:

includes/class-wc-shipping-tipax.php
<?php
defined('ABSPATH') || exit;

class WC_Shipping_Tipax extends WC_Shipping_Method {
    
    public function __construct($instance_id = 0) {
        $this->id                 = 'tipax';
        $this->instance_id        = absint($instance_id);
        $this->method_title       = 'تیپاکس';
        $this->method_description = 'ارسال با تیپاکس به سراسر کشور';
        $this->supports = ['shipping-zones', 'instance-settings', 'instance-settings-modal'];
        
        $this->init();
    }
    
    public function init() {
        $this->init_form_fields();
        $this->init_settings();
        
        $this->title = $this->get_option('title');
        $this->cost  = $this->get_option('cost');
        $this->cost_per_kg = $this->get_option('cost_per_kg');
        $this->free_above  = $this->get_option('free_above');
        
        add_action('woocommerce_update_options_shipping_' . $this->id, 
                   [$this, 'process_admin_options']);
    }
    
    public function init_form_fields() {
        $this->instance_form_fields = [
            'title' => [
                'title'   => 'عنوان',
                'type'    => 'text',
                'default' => 'تیپاکس',
            ],
            'cost' => [
                'title'   => 'هزینه پایه (تومان)',
                'type'    => 'number',
                'default' => '50000',
            ],
            'cost_per_kg' => [
                'title'   => 'هزینه به ازای هر کیلوگرم',
                'type'    => 'number',
                'default' => '10000',
            ],
            'free_above' => [
                'title'       => 'ارسال رایگان از مبلغ',
                'type'        => 'number',
                'description' => 'صفر = غیرفعال',
                'default'     => '0',
            ],
        ];
    }
    
    public function calculate_shipping($package = []) {
        $cost = (float) $this->cost;
        $weight = 0;
        
        // محاسبه وزن کل
        foreach ($package['contents'] as $item) {
            $weight += floatval($item['data']->get_weight()) * $item['quantity'];
        }
        
        $cost += $weight * floatval($this->cost_per_kg);
        
        // ارسال رایگان
        $cart_total = $package['contents_cost'] ?? 0;
        if ($this->free_above > 0 && $cart_total >= $this->free_above) {
            $cost = 0;
        }
        
        $this->add_rate([
            'id'      => $this->get_rate_id(),
            'label'   => $this->title . ($cost == 0 ? ' (رایگان)' : ''),
            'cost'    => $cost,
            'package' => $package,
            'meta_data' => [
                'وزن کل' => $weight . ' کیلوگرم',
            ],
        ]);
    }
}

tipax-shipping.php
// ثبت روش ارسال
add_filter('woocommerce_shipping_methods', function($methods) {
    $methods['tipax'] = 'WC_Shipping_Tipax';
    return $methods;
});

add_action('woocommerce_shipping_init', function() {
    require_once __DIR__ . '/includes/class-wc-shipping-tipax.php';
});

⚖️ محاسبه بر اساس وزن و حجم

محاسبه پلکانی (Tier-based)

public function calculate_shipping($package = []) {
    $weight = $this->calculate_total_weight($package);
    $cost = $this->get_tier_cost($weight);
    
    $this->add_rate([
        'id'    => $this->get_rate_id(),
        'label' => $this->title,
        'cost'  => $cost,
    ]);
}

private function get_tier_cost($weight) {
    $tiers = [
        ['max' => 1, 'cost' => 50000],   // تا ۱ کیلو
        ['max' => 5, 'cost' => 80000],   // تا ۵ کیلو
        ['max' => 10, 'cost' => 120000], // تا ۱۰ کیلو
        ['max' => 30, 'cost' => 200000], // تا ۳۰ کیلو
    ];
    
    foreach ($tiers as $tier) {
        if ($weight <= $tier['max']) return $tier['cost'];
    }
    
    // بالای ۳۰ کیلو
    return 200000 + (($weight - 30) * 10000);
}

محاسبه با ابعاد (Volumetric)

private function calculate_volumetric_weight($package) {
    $total = 0;
    foreach ($package['contents'] as $item) {
        $product = $item['data'];
        $length = $product->get_length() ?: 1;
        $width  = $product->get_width() ?: 1;
        $height = $product->get_height() ?: 1;
        
        // فرمول استاندارد: (طول × عرض × ارتفاع) / 5000 cm³
        $volumetric = ($length * $width * $height) / 5000;
        $actual = floatval($product->get_weight());
        
        // وزن قابل قبول = max(فیزیکی، حجمی)
        $total += max($volumetric, $actual) * $item['quantity'];
    }
    return $total;
}

🚛 یکپارچه‌سازی با API تیپاکس

تیپاکس API محاسبه هزینه دارد. برای دریافت قیمت زنده:

public function calculate_shipping($package = []) {
    $destination = $package['destination'];
    $weight = $this->calculate_total_weight($package);
    
    // فراخوانی API تیپاکس
    $response = wp_remote_post('https://api.tipaxco.com/v1/quotation', [
        'headers' => [
            'Authorization' => 'Bearer ' . $this->api_token,
            'Content-Type'  => 'application/json',
        ],
        'body' => wp_json_encode([
            'origin_city_id'      => $this->origin_city_id,
            'destination_city_id' => $this->city_to_id($destination['city']),
            'weight'              => $weight,
            'package_count'       => count($package['contents']),
        ]),
        'timeout' => 15,
    ]);
    
    if (is_wp_error($response)) {
        // در صورت خطا، fallback به محاسبه دستی
        $cost = $this->fallback_calculate($weight);
    } else {
        $body = json_decode(wp_remote_retrieve_body($response), true);
        $cost = $body['data']['price'] ?? $this->fallback_calculate($weight);
    }
    
    $this->add_rate([
        'id'    => $this->get_rate_id(),
        'label' => 'تیپاکس - ۲ تا ۳ روز کاری',
        'cost'  => $cost,
    ]);
}

// Cache برای جلوگیری از فراخوانی مکرر
private function get_cached_quote($key, $callback) {
    $cached = get_transient('tipax_quote_' . $key);
    if ($cached !== false) return $cached;
    
    $result = $callback();
    set_transient('tipax_quote_' . $key, $result, HOUR_IN_SECONDS);
    return $result;
}

💡 بهترین روش: برای جلوگیری از کندی صفحه سبد خرید، نتیجه API را با Transient کش کنید (یک ساعت کافی است).

📮 یکپارچه‌سازی با پست ایران

پست ایران سرویس پیشتاز و سفارشی دارد:

class WC_Shipping_IranPost extends WC_Shipping_Method {
    
    public function init_form_fields() {
        $this->instance_form_fields = [
            'service_type' => [
                'title' => 'نوع سرویس',
                'type'  => 'select',
                'options' => [
                    'pishtaz'  => 'پیشتاز (۲-۴ روز)',
                    'sefaresh' => 'سفارشی (۵-۷ روز)',
                    'EMS'      => 'EMS بین‌المللی',
                ],
            ],
            'cod_enabled' => [
                'title' => 'پرداخت در محل',
                'type'  => 'checkbox',
                'label' => 'فعال‌سازی COD',
            ],
        ];
    }
    
    public function calculate_shipping($package = []) {
        $service = $this->get_option('service_type');
        $weight = $this->calculate_total_weight($package);
        
        $costs = [
            'pishtaz'  => 35000 + ($weight * 5000),
            'sefaresh' => 25000 + ($weight * 3000),
            'EMS'      => 150000 + ($weight * 20000),
        ];
        
        $cost = $costs[$service] ?? 50000;
        
        // اضافه کردن کرایه پرداخت در محل
        if ('yes' === $this->get_option('cod_enabled')) {
            $cost += 10000;
        }
        
        $this->add_rate([
            'id'    => $this->get_rate_id(),
            'label' => 'پست ایران - ' . $this->get_service_label($service),
            'cost'  => $cost,
        ]);
    }
}

🎯 شرایط ارسال هوشمند

غیرفعال کردن ارسال خاص بر اساس محصول

// مثلاً محصولات بزرگ نمی‌توانند با پیک موتوری ارسال شوند
add_filter('woocommerce_package_rates', function($rates, $package) {
    $has_large_item = false;
    
    foreach ($package['contents'] as $item) {
        if ($item['data']->get_weight() > 30) {
            $has_large_item = true;
            break;
        }
    }
    
    if ($has_large_item) {
        unset($rates['flat_rate:1']); // حذف پیک موتوری
    }
    
    return $rates;
}, 10, 2);

ارسال رایگان شرطی

add_filter('woocommerce_package_rates', function($rates, $package) {
    $cart_total = WC()->cart->get_subtotal();
    
    // اگر بالای ۵۰۰ هزار است، فقط ارسال رایگان نمایش بده
    if ($cart_total >= 500000 && isset($rates['free_shipping:1'])) {
        return ['free_shipping:1' => $rates['free_shipping:1']];
    }
    
    return $rates;
}, 10, 2);

ارسال در روزهای خاص

add_filter('woocommerce_package_rates', function($rates, $package) {
    $day = date('w'); // 5 = جمعه
    
    // در جمعه‌ها پیک موتوری غیرفعال
    if ($day == 5) {
        foreach ($rates as $rate_id => $rate) {
            if (strpos($rate_id, 'motorcycle') !== false) {
                unset($rates[$rate_id]);
            }
        }
    }
    
    return $rates;
}, 10, 2);

📦 ردیابی مرسوله

اضافه کردن قابلیت کد رهگیری به سفارش‌ها:

// متاباکس کد رهگیری در سفارش
add_action('add_meta_boxes', function() {
    add_meta_box(
        'shipping_tracking',
        'کد رهگیری مرسوله',
        'render_tracking_metabox',
        ['shop_order', 'woocommerce_page_wc-orders'], // HPOS support
        'side'
    );
});

function render_tracking_metabox($post_or_order) {
    $order = ($post_or_order instanceof WP_Post) 
        ? wc_get_order($post_or_order->ID) 
        : $post_or_order;
    
    $tracking = $order->get_meta('_tracking_code');
    $carrier = $order->get_meta('_tracking_carrier');
    
    wp_nonce_field('save_tracking', 'tracking_nonce');
    ?>
    

get_meta('_tracking_code'); $order->update_meta_data('_tracking_code', $code); $order->update_meta_data('_tracking_carrier', $carrier); $order->save(); // اگر کد جدید است، SMS بزن if ($code && $code !== $old_code) { send_tracking_sms($order, $code, $carrier); } }); function send_tracking_sms($order, $code, $carrier) { $phone = $order->get_billing_phone(); $name = $order->get_billing_first_name(); $carriers_url = [ 'tipax' => 'https://tipaxco.com/track/' . $code, 'post' => 'https://tracking.post.ir/?id=' . $code, ]; $message = sprintf( "%s عزیز، سفارش %s شما ارسال شد. کد رهگیری: %s پیگیری: %s", $name, $order->get_order_number(), $code, $carriers_url[$carrier] ?? '' ); // ارسال با API SMS (مثل کاوه‌نگار) wp_remote_post('https://api.kavenegar.com/v1/.../sms/send.json', [ 'body' => ['receptor' => $phone, 'message' => $message], ]); $order->add_order_note('SMS کد رهگیری به مشتری ارسال شد'); }

✅ نمایش به مشتری: کد رهگیری در ایمیل سفارش و صفحه «سفارش‌های من» نمایش داده می‌شود.

📝 خلاصه فصل

  • ساختار Shipping Zones را یاد گرفتید
  • ساخت روش ارسال سفارشی با Class
  • محاسبه پلکانی، وزنی و حجمی
  • یکپارچه‌سازی با API تیپاکس و پست
  • شرایط ارسال هوشمند با Filters
  • سیستم ردیابی مرسوله با SMS

نمایش سایت

رنگ سایت
حالت نمایش
اندازهٔ متن
خوانایی

این تنظیمات فقط روی مرورگر شما ذخیره می‌شود.