Bot Jualan dengan Multi-Warehouse

Cara buat bot jualan WhatsApp dengan multi-warehouse. Location-based fulfillment, stok terdistribusi, ongkir optimal. Panduan lengkap!

Bot Jualan dengan Multi-Warehouse
Bot Jualan dengan Multi-Warehouse

Multi-warehouse = Faster delivery, lower cost!

Bot dengan multi-warehouse memilih gudang terdekat untuk fulfillment - ongkir lebih murah, pengiriman lebih cepat.


Kenapa Multi-Warehouse?

📦 BENEFITS:

SINGLE WAREHOUSE:
❌ Ongkir mahal ke daerah jauh
❌ Delivery lama
❌ Single point of failure
❌ Stok terpusat

MULTI-WAREHOUSE:
✅ Ongkir optimal (gudang terdekat)
✅ Same-day/next-day possible
✅ Redundancy
✅ Stok terdistribusi

Warehouse Setup

📍 CONTOH SETUP:

WAREHOUSE JAKARTA (WH-JKT)
- Cover: Jabodetabek, Jawa Barat
- Capacity: 10,000 SKU
- Priority: 1 (main)

WAREHOUSE SURABAYA (WH-SBY)
- Cover: Jawa Timur, Bali, Kalimantan
- Capacity: 5,000 SKU
- Priority: 2

WAREHOUSE MEDAN (WH-MDN)
- Cover: Sumatera
- Capacity: 3,000 SKU
- Priority: 3

WAREHOUSE MAKASSAR (WH-MKS)
- Cover: Sulawesi, Papua, NTT/NTB
- Capacity: 2,000 SKU
- Priority: 4

Template Messages

Location Check:

📍 CEK LOKASI PENGIRIMAN

Hai! Untuk kasih estimasi ongkir
dan waktu kirim terbaik:

Kirim KOTA atau KODE POS kamu!

Contoh:
- Surabaya
- 60234
- Jl. Raya Darmo, Surabaya

Atau share lokasi WhatsApp! 📍

Warehouse Assignment:

📦 INFO PENGIRIMAN

Alamat: Surabaya, Jawa Timur

✅ KABAR BAIK!
Pesanan kamu akan dikirim dari:
📍 Gudang Surabaya

ESTIMASI:
━━━━━━━━━━━━━━━━━━━━
- Same Day: Rp 15.000 (order < 12:00)
- Next Day: Rp 12.000
- Regular (2-3 hari): Rp 9.000
━━━━━━━━━━━━━━━━━━━━

💡 Lebih cepat & murah karena
   gudang ada di kotamu!

Lanjut order?
Ketik ORDER [PRODUK]

Stock Check Multi-WH:

📦 CEK STOK

Produk: [NAMA PRODUK]

KETERSEDIAAN:
━━━━━━━━━━━━━━━━━━━━
📍 Jakarta: ✅ 50 pcs
📍 Surabaya: ✅ 25 pcs
📍 Medan: ✅ 15 pcs
📍 Makassar: ❌ Habis
━━━━━━━━━━━━━━━━━━━━

Lokasi kamu: Surabaya
Dikirim dari: 📍 Surabaya (terdekat)
Stok: ✅ Tersedia

Order sekarang?

Optimized Shipping:

🚚 OPSI PENGIRIMAN

Alamat: [ALAMAT CUSTOMER]
Gudang: 📍 Surabaya (22 km)

PILIH KURIR:
━━━━━━━━━━━━━━━━━━━━
1️⃣ Same Day (Grab/Gojek)
   💰 Rp 18.000
   ⏰ Hari ini (3-5 jam)

2️⃣ JNE YES
   💰 Rp 15.000
   ⏰ Besok

3️⃣ SiCepat BEST
   💰 Rp 12.000
   ⏰ 1-2 hari

4️⃣ JNE REG
   💰 Rp 9.000
   ⏰ 2-3 hari
━━━━━━━━━━━━━━━━━━━━

Ketik angka untuk pilih!

Split Shipment:

📦 INFO PENGIRIMAN

Order: #ORD-20260217-001

⚠️ Pesanan akan dikirim dari
   2 GUDANG berbeda:

PAKET 1 (dari Jakarta):
━━━━━━━━━━━━━━━━━━━━
- Produk A x 2
- Produk B x 1
🚚 JNE REG - Rp 15.000
📅 Estimasi: 2-3 hari

PAKET 2 (dari Surabaya):
━━━━━━━━━━━━━━━━━━━━
- Produk C x 1
🚚 JNE REG - Rp 9.000
📅 Estimasi: 1-2 hari

💰 Total ongkir: Rp 24.000

⚠️ Paket akan datang terpisah!
   Masing-masing punya no resi.

OK untuk lanjut?

Consolidated Option:

💡 OPSI GABUNG PAKET

Mau hemat ongkir?

Semua produk bisa dikirim dari
Jakarta (stok tersedia):

SPLIT (2 paket):
- Ongkir total: Rp 24.000
- Estimasi: Paket 1 (3hr), Paket 2 (2hr)

GABUNG (1 paket dari Jakarta):
- Ongkir: Rp 15.000
- Hemat: Rp 9.000! 💰
- Estimasi: 2-3 hari

Pilih mana?
1️⃣ Split (lebih cepat, 2 paket)
2️⃣ Gabung (lebih hemat, 1 paket)

Implementation

Warehouse Selection:

javascript

const warehouses = [
    {
        id: 'WH-JKT',
        name: 'Jakarta',
        location: { lat: -6.2088, lng: 106.8456 },
        coverage: ['DKI Jakarta', 'Jawa Barat', 'Banten'],
        priority: 1
    },
    {
        id: 'WH-SBY',
        name: 'Surabaya',
        location: { lat: -7.2575, lng: 112.7521 },
        coverage: ['Jawa Timur', 'Bali', 'Kalimantan'],
        priority: 2
    },
    // ... more warehouses
];

async function selectWarehouse(customerLocation, productId) {
    // Get warehouses with stock
    const warehousesWithStock = await getWarehousesWithStock(productId);
    
    if (warehousesWithStock.length === 0) {
        return { success: false, error: 'out_of_stock_all_warehouses' };
    }
    
    // Calculate distance to each
    const distances = warehousesWithStock.map(wh => ({
        ...wh,
        distance: calculateDistance(customerLocation, wh.location)
    }));
    
    // Sort by distance
    distances.sort((a, b) => a.distance - b.distance);
    
    return {
        success: true,
        warehouse: distances[0],
        alternatives: distances.slice(1)
    };
}

function calculateDistance(loc1, loc2) {
    // Haversine formula for distance
    const R = 6371; // Earth's radius in km
    const dLat = toRad(loc2.lat - loc1.lat);
    const dLng = toRad(loc2.lng - loc1.lng);
    
    const a = Math.sin(dLat/2) * Math.sin(dLat/2) +
              Math.cos(toRad(loc1.lat)) * Math.cos(toRad(loc2.lat)) *
              Math.sin(dLng/2) * Math.sin(dLng/2);
    
    const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
    
    return R * c;
}

Multi-WH Stock Check:

javascript

async function checkStockAllWarehouses(productId) {
    const stocks = await db.inventory.find({ productId });
    
    return stocks.map(s => ({
        warehouseId: s.warehouseId,
        warehouseName: getWarehouseName(s.warehouseId),
        quantity: s.quantity,
        available: s.quantity > 0
    }));
}

async function allocateStock(order, preferredWarehouse) {
    const allocation = [];
    let remainingItems = [...order.items];
    
    // Try to fulfill from preferred warehouse first
    const preferredStock = await db.inventory.find({
        warehouseId: preferredWarehouse,
        productId: { $in: remainingItems.map(i => i.productId) }
    });
    
    for (const item of remainingItems) {
        const stock = preferredStock.find(s => s.productId === item.productId);
        
        if (stock && stock.quantity >= item.quantity) {
            allocation.push({
                ...item,
                warehouseId: preferredWarehouse,
                fulfilled: true
            });
            remainingItems = remainingItems.filter(i => i.productId !== item.productId);
        }
    }
    
    // Fulfill remaining from other warehouses
    if (remainingItems.length > 0) {
        for (const item of remainingItems) {
            const altWarehouse = await findAlternativeWarehouse(item, order.customerLocation);
            
            if (altWarehouse) {
                allocation.push({
                    ...item,
                    warehouseId: altWarehouse.id,
                    fulfilled: true
                });
            } else {
                allocation.push({
                    ...item,
                    fulfilled: false,
                    error: 'out_of_stock'
                });
            }
        }
    }
    
    return {
        allocation,
        isSplit: new Set(allocation.map(a => a.warehouseId)).size > 1,
        warehouses: [...new Set(allocation.map(a => a.warehouseId))]
    };
}

Shipping Cost Calculation:

javascript

async function calculateShipping(allocation, destination) {
    const shipments = groupByWarehouse(allocation);
    
    const shippingOptions = [];
    
    for (const [warehouseId, items] of Object.entries(shipments)) {
        const warehouse = await getWarehouse(warehouseId);
        const totalWeight = items.reduce((sum, i) => sum + i.weight * i.quantity, 0);
        
        const rates = await getShippingRates(
            warehouse.location,
            destination,
            totalWeight
        );
        
        shippingOptions.push({
            warehouseId,
            warehouseName: warehouse.name,
            items,
            rates
        });
    }
    
    return {
        isSplit: shippingOptions.length > 1,
        shipments: shippingOptions,
        totalCost: shippingOptions.reduce((sum, s) => 
            sum + Math.min(...s.rates.map(r => r.cost)), 0
        )
    };
}

Best Practices

DO ✅

- Auto-select nearest warehouse
- Show customer the benefit
- Handle split shipment clearly
- Offer consolidation option
- Sync inventory real-time
- Fallback warehouse strategy

DON'T ❌

- Always ship from main WH
- Hide warehouse info
- Confusing split shipping
- Force expensive shipping
- Out of sync inventory
- No backup plan

FAQ

Kapan pakai multi-warehouse?

Saat punya customer di berbagai region dan shipping cost jadi masalah.

Split shipment bagus tidak?

Tergantung. Faster but higher cost. Offer choice to customer.

Bagaimana sync inventory?

Real-time sync critical. Use central inventory system.


Kesimpulan

Multi-warehouse = Better logistics!

Single WarehouseMulti-Warehouse
High shipping costOptimized cost
Slow to far areasFast everywhere
Single point riskRedundancy
Limited reachNational coverage

Setup Multi-Warehouse →


Artikel Terkait