Skip to content

Stock Management ​

Managing inventory across Wildberries warehouses.

Description ​

This use case demonstrates how to manage stock levels across multiple warehouses, including viewing current stock, updating quantities, and synchronizing with external inventory systems.

Get Seller Warehouses ​

typescript
import { WildberriesSDK } from 'daytona-wildberries-typescript-sdk';

const sdk = new WildberriesSDK({
  apiKey: process.env.WB_API_KEY!
});

// Get list of seller's warehouses
async function getWarehouses() {
  const warehouses = await sdk.products.warehouses();

  console.log('Your warehouses:');
  warehouses.forEach(wh => {
    console.log(`  ID: ${wh.id}, Name: ${wh.name}`);
  });

  return warehouses;
}

const warehouses = await getWarehouses();

Get Current Stock Levels ​

typescript
// Get stock from statistics API
async function getCurrentStock() {
  const today = new Date().toISOString().split('T')[0];
  const stocks = await sdk.reports.getStocks(today);

  console.log(`Total SKUs in stock: ${stocks.length}`);

  // Group by warehouse
  const byWarehouse = new Map<number, typeof stocks>();
  stocks.forEach(item => {
    const whId = item.warehouseName ? 1 : 0; // Simplified
    const current = byWarehouse.get(whId) ?? [];
    current.push(item);
    byWarehouse.set(whId, current);
  });

  return stocks;
}

const stocks = await getCurrentStock();

Create Stock Records ​

typescript
// Add new stock records to a warehouse
async function createStockRecords(
  warehouseId: number,
  skus: string[]
) {
  const result = await sdk.products.getStocks(warehouseId, { skus });

  console.log('Created stock records:');
  result.stocks?.forEach(item => {
    console.log(`  ${item.sku}: ${item.amount} units`);
  });

  return result.stocks;
}

// Example: Add new SKUs to warehouse
await createStockRecords(12345, ['SKU001', 'SKU002', 'SKU003']);

Update Stock Quantities ​

typescript
interface StockUpdate {
  sku: string;
  amount: number;
}

async function updateStockQuantities(
  warehouseId: number,
  updates: StockUpdate[]
) {
  await sdk.products.updateStock(warehouseId, {
    stocks: updates.map(u => ({
      sku: u.sku,
      amount: u.amount
    }))
  });

  console.log(`Updated ${updates.length} stock records`);
}

// Example: Update stock levels
await updateStockQuantities(12345, [
  { sku: 'SKU001', amount: 100 },
  { sku: 'SKU002', amount: 50 },
  { sku: 'SKU003', amount: 0 } // Out of stock
]);

Delete Stock Records ​

typescript
// Remove stock records from a warehouse
async function deleteStockRecords(
  warehouseId: number,
  skus: string[]
) {
  await sdk.products.deleteStock(warehouseId, { skus });
  console.log(`Deleted ${skus.length} stock records`);
}

// Example: Remove discontinued items
await deleteStockRecords(12345, ['OLD_SKU_001', 'OLD_SKU_002']);

Stock Level Helper Methods ​

typescript
// Use the helper method for updating stock
async function updateStockLevels(
  warehouseId: number,
  updates: Array<{ sku: string; quantity: number }>
) {
  await sdk.products.updateStock(warehouseId, {
    stocks: updates.map(u => ({ sku: u.sku, amount: u.quantity }))
  });

  console.log(`Updated ${updates.length} items`);
}

// Use the helper method for deleting stock
async function removeStockRecords(
  warehouseId: number,
  skus: string[]
) {
  await sdk.products.deleteStock(warehouseId, { skus });
  console.log(`Removed ${skus.length} items`);
}

Sync Stock from External System ​

typescript
interface ExternalInventory {
  sku: string;
  quantity: number;
  warehouseCode: string;
}

async function syncInventoryFromExternal(
  externalInventory: ExternalInventory[],
  warehouseMapping: Map<string, number> // External code -> WB warehouse ID
) {
  // Group by warehouse
  const byWarehouse = new Map<number, StockUpdate[]>();

  for (const item of externalInventory) {
    const wbWarehouseId = warehouseMapping.get(item.warehouseCode);
    if (!wbWarehouseId) continue;

    const current = byWarehouse.get(wbWarehouseId) ?? [];
    current.push({ sku: item.sku, amount: item.quantity });
    byWarehouse.set(wbWarehouseId, current);
  }

  // Update each warehouse
  for (const [warehouseId, updates] of byWarehouse) {
    await sdk.products.updateStock(warehouseId, { stocks: updates });
    console.log(`Synced ${updates.length} items to warehouse ${warehouseId}`);
  }
}

// Example usage
const mapping = new Map([
  ['MAIN', 12345],
  ['SECONDARY', 67890]
]);

await syncInventoryFromExternal([
  { sku: 'SKU001', quantity: 100, warehouseCode: 'MAIN' },
  { sku: 'SKU002', quantity: 50, warehouseCode: 'SECONDARY' }
], mapping);

Low Stock Alert System ​

typescript
interface LowStockItem {
  nmId: number;
  sku: string;
  name: string;
  currentStock: number;
  threshold: number;
  daysOfStock: number;
}

async function checkLowStock(
  threshold = 10,
  salesDays = 30
): Promise<LowStockItem[]> {
  // 1. Get current stock
  const today = new Date().toISOString().split('T')[0];
  const stocks = await sdk.reports.getStocks(today);

  // 2. Get sales data for velocity calculation
  const salesFrom = new Date(Date.now() - salesDays * 24 * 60 * 60 * 1000)
    .toISOString().split('T')[0];
  const sales = await sdk.reports.getSales(salesFrom, 0);

  // 3. Calculate sales velocity per SKU
  const salesBySku = new Map<number, number>();
  sales.forEach(sale => {
    if (sale.nmId) {
      salesBySku.set(sale.nmId, (salesBySku.get(sale.nmId) ?? 0) + 1);
    }
  });

  // 4. Get product names
  const products = await sdk.products.getCardsList({ locale: 'ru' });
  const productNames = new Map(products.map(p => [p.nmID, p.title]));

  // 5. Find low stock items
  const lowStockItems: LowStockItem[] = [];

  for (const stock of stocks) {
    if (stock.quantity <= threshold) {
      const salesCount = salesBySku.get(stock.nmId) ?? 0;
      const dailySales = salesCount / salesDays;
      const daysOfStock = dailySales > 0
        ? Math.floor(stock.quantity / dailySales)
        : Infinity;

      lowStockItems.push({
        nmId: stock.nmId,
        sku: stock.barcode ?? '',
        name: productNames.get(stock.nmId) ?? 'Unknown',
        currentStock: stock.quantity,
        threshold,
        daysOfStock: daysOfStock === Infinity ? 999 : daysOfStock
      });
    }
  }

  // Sort by days of stock (most urgent first)
  lowStockItems.sort((a, b) => a.daysOfStock - b.daysOfStock);

  console.log(`Found ${lowStockItems.length} low stock items:`);
  lowStockItems.slice(0, 10).forEach(item => {
    console.log(`  ${item.name}: ${item.currentStock} units (~${item.daysOfStock} days)`);
  });

  return lowStockItems;
}

Stock Movement Report ​

typescript
interface StockMovement {
  date: string;
  nmId: number;
  sku: string;
  type: 'sale' | 'return' | 'inbound' | 'adjustment';
  quantity: number;
}

async function generateStockMovementReport(days = 30): Promise<StockMovement[]> {
  const fromDate = new Date(Date.now() - days * 24 * 60 * 60 * 1000)
    .toISOString().split('T')[0];

  // Get sales (outbound)
  const sales = await sdk.reports.getSales(fromDate, 0);

  // Get incomes (inbound)
  const incomes = await sdk.reports.getIncomes(fromDate);

  const movements: StockMovement[] = [];

  // Add sales
  sales.forEach(sale => {
    if (sale.nmId) {
      movements.push({
        date: sale.date ?? '',
        nmId: sale.nmId,
        sku: sale.barcode ?? '',
        type: sale.quantity && sale.quantity < 0 ? 'return' : 'sale',
        quantity: Math.abs(sale.quantity ?? 1)
      });
    }
  });

  // Add incomes
  incomes.forEach(income => {
    movements.push({
      date: income.date ?? '',
      nmId: income.nmId ?? 0,
      sku: income.barcode ?? '',
      type: 'inbound',
      quantity: income.quantity ?? 0
    });
  });

  // Sort by date
  movements.sort((a, b) => b.date.localeCompare(a.date));

  return movements;
}

Batch Stock Update with Validation ​

typescript
async function batchUpdateStockWithValidation(
  warehouseId: number,
  updates: StockUpdate[]
) {
  // Validate SKUs exist
  const products = await sdk.products.getCardsList({ locale: 'ru' });
  const validSkus = new Set<string>();

  products.forEach(p => {
    p.sizes?.forEach(size => {
      size.skus?.forEach(sku => validSkus.add(sku));
    });
  });

  // Filter to valid updates only
  const validUpdates = updates.filter(u => {
    if (!validSkus.has(u.sku)) {
      console.warn(`SKU not found: ${u.sku}`);
      return false;
    }
    if (u.amount < 0) {
      console.warn(`Invalid quantity for ${u.sku}: ${u.amount}`);
      return false;
    }
    return true;
  });

  if (validUpdates.length === 0) {
    console.log('No valid updates to apply');
    return;
  }

  // Apply updates in batches
  const batchSize = 100;
  for (let i = 0; i < validUpdates.length; i += batchSize) {
    const batch = validUpdates.slice(i, i + batchSize);
    await sdk.products.updateStock(warehouseId, { stocks: batch });
    console.log(`Updated batch ${Math.floor(i / batchSize) + 1}`);
  }

  console.log(`Successfully updated ${validUpdates.length} stock records`);
}

Back to Examples | Previous: Pricing Updates | Next: Shipping Management

Made with ❤️ for the Wildberries developer community