Prices & Stocks API Documentation

Overview

Prices & Stocks API предоставляет полный контроль над ценами и остатками товаров на OZON. API включает 9 методов для управления ценообразованием, складскими остатками, уценёнными товарами и таймерами актуальности цен.

Key Features

  • 💰 Управление ценами - обновление цен, old_price, premium_price
  • 📦 Управление остатками - синхронизация складских остатков по всем складам
  • 🏷️ Уценённые товары - работа с уценённой продукцией и установка скидок
  • ⏱️ Таймеры актуальности - управление таймерами минимальных цен
  • 🏭 FBS/rFBS поддержка - работа со складами продавца
  • 📊 Детальная информация - получение подробных данных о ценах и остатках
  • 🔄 Массовые операции - обновление до 100 товаров за запрос

Rate Limits

  • Цены: до 10 обновлений в час на товар
  • Остатки: до 80 запросов в минуту с аккаунта
  • Массовые операции: до 100 товаров за запрос

Available Methods

Price Management Methods

updatePrices()

Обновляет цены одного или нескольких товаров с поддержкой обычной и премиум-цены.

const result = await pricesStocksApi.updatePrices({
  prices: [{
    offer_id: 'ITEM001',
    price: '1500',
    old_price: '2000',
    premium_price: '1400',
    currency_code: 'RUB'
  }, {
    product_id: 123456,
    price: '999',
    old_price: '0', // Сброс старой цены
    currency_code: 'RUB'
  }]
});

result.result?.forEach(item => {
  if (item.updated) {
    console.log(`✅ Цена товара ${item.offer_id || item.product_id} обновлена`);
  } else {
    console.log(`❌ Ошибки: ${item.errors?.join(', ')}`);
  }
});

getPrices()

Получает информацию о ценах товаров с фильтрацией и пагинацией.

const prices = await pricesStocksApi.getPrices({
  filter: {
    offer_id: ['ITEM001', 'ITEM002'],
    visibility: 'VISIBLE'
  },
  limit: 100,
  cursor: 'next_page_token'
});

prices.items?.forEach(item => {
  console.log(`Товар ${item.offer_id}: ${item.price} ${item.currency_code}`);
  if (item.old_price) {
    console.log(`  Скидка: ${((item.old_price - item.price) / item.old_price * 100).toFixed(1)}%`);
  }
  if (item.premium_price) {
    console.log(`  Premium цена: ${item.premium_price}`);
  }
});

Stock Management Methods

updateStocks()

Обновляет количество товаров на складах.

const result = await pricesStocksApi.updateStocks({
  stocks: [{
    offer_id: 'ITEM001',
    stock: 100,
    warehouse_id: 12345
  }, {
    product_id: 987654,
    stock: 50,
    warehouse_id: 67890
  }, {
    offer_id: 'ITEM002',
    stock: 0, // Убрать из продажи
    warehouse_id: 12345
  }]
});

result.result?.forEach(item => {
  if (item.updated) {
    console.log(`✅ Остатки товара ${item.offer_id || item.product_id} обновлены`);
  } else {
    console.log(`❌ Ошибки: ${item.errors?.join(', ')}`);
  }
});

getStocks()

Получает информацию о количестве товаров по схемам FBS и rFBS.

const stocks = await pricesStocksApi.getStocks({
  filter: {
    offer_id: ['ITEM001', 'ITEM002'],
    visibility: 'VISIBLE'
  },
  limit: 100
});

stocks.items?.forEach(item => {
  console.log(`📦 Товар ${item.offer_id} (SKU: ${item.sku})`);
  let totalStock = 0;
  let totalReserved = 0;
  
  item.stocks?.forEach(stock => {
    console.log(`  Склад ${stock.warehouse_id}: ${stock.present} в наличии, ${stock.reserved} зарезервировано`);
    totalStock += stock.present;
    totalReserved += stock.reserved;
  });
  
  console.log(`  Общий остаток: ${totalStock}, зарезервировано: ${totalReserved}`);
});

getStocksByWarehouseFbs()

Получает детальную информацию об остатках на складах FBS/rFBS.

const stockInfo = await pricesStocksApi.getStocksByWarehouseFbs({
  sku: ['123456789', '987654321']
});

stockInfo.result?.forEach(product => {
  console.log(`🏭 SKU: ${product.sku}`);
  product.stocks?.forEach(stock => {
    console.log(`  Склад "${stock.warehouse_name}" (ID: ${stock.warehouse_id})`);
    console.log(`    В наличии: ${stock.present}`);
    console.log(`    Зарезервировано: ${stock.reserved}`);
    console.log(`    Ожидается: ${stock.waiting || 0}`);
  });
});

Discounted Products Methods

getDiscountedProductInfo()

Получает информацию об уценённых товарах по их SKU.

const discountedInfo = await pricesStocksApi.getDiscountedProductInfo({
  discounted_skus: ['987654321', '123456789']
});

discountedInfo.items?.forEach(item => {
  console.log(`🏷️ Уценённый товар:`);
  console.log(`  Уценённый SKU: ${item.discounted_sku}`);
  console.log(`  Основной SKU: ${item.original_sku}`);
  console.log(`  Состояние: ${item.condition}`);
  console.log(`  Скидка: ${item.discount_percentage}%`);
  console.log(`  Причина уценки: ${item.reason || 'Не указана'}`);
});

updateDiscountedProductDiscount()

Устанавливает размер скидки на уценённые товары FBS.

const result = await pricesStocksApi.updateDiscountedProductDiscount({
  product_id: 123456,
  discount: 25 // 25% скидка
});

if (result.result) {
  console.log('✅ Скидка 25% успешно установлена');
} else {
  console.log('❌ Ошибка при установке скидки');
}

Action Timer Methods

getActionTimerStatus()

Получает статус таймеров актуальности минимальной цены.

const timerStatus = await pricesStocksApi.getActionTimerStatus({
  product_ids: ['123456', '789012']
});

timerStatus.statuses?.forEach(status => {
  console.log(`⏱️ Товар ${status.product_id}:`);
  console.log(`  Таймер: ${status.is_timer_enabled ? '🟢 включен' : '🔴 выключен'}`);
  if (status.next_update_time) {
    console.log(`  Следующее обновление: ${new Date(status.next_update_time).toLocaleString()}`);
  }
});

updateActionTimer()

Обновляет таймер актуальности минимальной цены для товаров.

await pricesStocksApi.updateActionTimer({
  product_ids: ['123456', '789012']
});

console.log('✅ Таймеры актуальности обновлены');

TypeScript Interfaces

Request Types

interface PricesStocksImportPricesRequest {
  prices: PriceUpdateItem[];
}

interface PriceUpdateItem {
  offer_id?: string;
  product_id?: number;
  price: string;
  old_price?: string;
  premium_price?: string;
  currency_code: 'RUB' | 'USD' | 'EUR';
}

interface PricesStocksUpdateStocksRequest {
  stocks: StockUpdateItem[];
}

interface StockUpdateItem {
  offer_id?: string;
  product_id?: number;
  stock: number;
  warehouse_id: number;
}

interface PricesStocksGetPricesRequest {
  filter?: PricesFilter;
  limit?: number;
  cursor?: string;
}

interface PricesFilter {
  offer_id?: string[];
  product_id?: number[];
  visibility?: 'VISIBLE' | 'INVISIBLE' | 'ALL';
}

interface PricesStocksGetStocksRequest {
  filter?: StocksFilter;
  limit?: number;
  cursor?: string;
}

interface StocksFilter {
  offer_id?: string[];
  product_id?: number[];
  visibility?: 'VISIBLE' | 'INVISIBLE' | 'ALL';
  warehouse_id?: number[];
}

interface PricesStocksGetDiscountedInfoRequest {
  discounted_skus: string[];
}

interface PricesStocksUpdateDiscountRequest {
  product_id: number;
  discount: number;
}

interface PricesStocksActionTimerStatusRequest {
  product_ids: string[];
}

interface PricesStocksActionTimerUpdateRequest {
  product_ids: string[];
}

interface PricesStocksGetStocksByWarehouseFbsRequest {
  sku: string[];
}

Response Types

interface PricesStocksImportPricesResponse {
  result?: PriceUpdateResult[];
}

interface PriceUpdateResult {
  offer_id?: string;
  product_id?: number;
  updated: boolean;
  errors?: string[];
}

interface PricesStocksGetPricesResponse {
  items?: PriceInfo[];
  cursor?: string;
  has_next?: boolean;
}

interface PriceInfo {
  offer_id: string;
  product_id: number;
  sku: string;
  price: number;
  old_price?: number;
  premium_price?: number;
  currency_code: string;
  min_ozon_price?: number;
  max_price?: number;
  recommended_price?: number;
}

interface PricesStocksUpdateStocksResponse {
  result?: StockUpdateResult[];
}

interface StockUpdateResult {
  offer_id?: string;
  product_id?: number;
  updated: boolean;
  errors?: string[];
}

interface PricesStocksGetStocksResponse {
  items?: StockInfo[];
  cursor?: string;
  has_next?: boolean;
}

interface StockInfo {
  offer_id: string;
  product_id: number;
  sku: string;
  stocks: WarehouseStock[];
}

interface WarehouseStock {
  warehouse_id: number;
  present: number;
  reserved: number;
  waiting?: number;
}

interface PricesStocksGetDiscountedInfoResponse {
  items?: DiscountedProductInfo[];
}

interface DiscountedProductInfo {
  discounted_sku: string;
  original_sku: string;
  condition: string;
  discount_percentage: number;
  reason?: string;
  defects?: string[];
}

interface PricesStocksActionTimerStatusResponse {
  statuses?: ActionTimerStatus[];
}

interface ActionTimerStatus {
  product_id: string;
  is_timer_enabled: boolean;
  next_update_time?: string;
}

interface PricesStocksGetStocksByWarehouseFbsResponse {
  result?: FbsStockInfo[];
}

interface FbsStockInfo {
  sku: string;
  stocks: FbsWarehouseStock[];
}

interface FbsWarehouseStock {
  warehouse_id: number;
  warehouse_name: string;
  present: number;
  reserved: number;
  waiting?: number;
}

Usage Examples

Basic Price and Stock Management

import { OzonApi } from 'bmad-ozon-seller-api';

const ozonApi = new OzonApi({
  clientId: 'your-client-id',
  apiKey: 'your-api-key'
});

// Синхронизация цен и остатков
async function syncPricesAndStocks() {
  try {
    // Обновить цены с акционными предложениями
    const priceUpdates = await ozonApi.pricesStocks.updatePrices({
      prices: [
        {
          offer_id: 'SMARTPHONE_XYZ',
          price: '25000',
          old_price: '30000', // Показать скидку
          premium_price: '24000', // Цена для Premium покупателей
          currency_code: 'RUB'
        },
        {
          offer_id: 'LAPTOP_ABC',
          price: '75000',
          old_price: '0', // Сбросить старую цену
          currency_code: 'RUB'
        }
      ]
    });
    
    console.log('💰 Результаты обновления цен:');
    priceUpdates.result?.forEach(item => {
      if (item.updated) {
        console.log(`✅ ${item.offer_id}: цена обновлена`);
      } else {
        console.log(`❌ ${item.offer_id}: ${item.errors?.join(', ')}`);
      }
    });
    
    // Обновить остатки на разных складах
    const stockUpdates = await ozonApi.pricesStocks.updateStocks({
      stocks: [
        {
          offer_id: 'SMARTPHONE_XYZ',
          stock: 150,
          warehouse_id: 12345 // Основной склад
        },
        {
          offer_id: 'SMARTPHONE_XYZ',
          stock: 75,
          warehouse_id: 67890 // Дополнительный склад
        },
        {
          offer_id: 'LAPTOP_ABC',
          stock: 25,
          warehouse_id: 12345
        }
      ]
    });
    
    console.log('\n📦 Результаты обновления остатков:');
    stockUpdates.result?.forEach(item => {
      if (item.updated) {
        console.log(`✅ ${item.offer_id}: остатки обновлены`);
      } else {
        console.log(`❌ ${item.offer_id}: ${item.errors?.join(', ')}`);
      }
    });
    
    return { priceUpdates, stockUpdates };
  } catch (error) {
    console.error('❌ Ошибка синхронизации:', error);
    throw error;
  }
}

// Использование
const syncResult = await syncPricesAndStocks();

Comprehensive Inventory Analysis

// Получение полной информации о товарах
async function analyzeInventoryStatus(offerIds: string[]) {
  try {
    // Получить информацию о ценах
    const pricesInfo = await ozonApi.pricesStocks.getPrices({
      filter: {
        offer_id: offerIds,
        visibility: 'VISIBLE'
      },
      limit: 1000
    });
    
    // Получить информацию об остатках
    const stocksInfo = await ozonApi.pricesStocks.getStocks({
      filter: {
        offer_id: offerIds,
        visibility: 'VISIBLE'
      },
      limit: 1000
    });
    
    // Объединить данные для анализа
    const analysis = offerIds.map(offerId => {
      const priceData = pricesInfo.items?.find(item => item.offer_id === offerId);
      const stockData = stocksInfo.items?.find(item => item.offer_id === offerId);
      
      if (!priceData || !stockData) {
        return {
          offerId,
          status: 'not_found',
          issues: ['Товар не найден в каталоге']
        };
      }
      
      const totalStock = stockData.stocks?.reduce((sum, stock) => sum + stock.present, 0) || 0;
      const totalReserved = stockData.stocks?.reduce((sum, stock) => sum + stock.reserved, 0) || 0;
      const availableStock = totalStock - totalReserved;
      
      const issues: string[] = [];
      
      // Анализ остатков
      if (totalStock === 0) {
        issues.push('Нет остатков на складах');
      } else if (availableStock < 5) {
        issues.push('Критически низкий остаток (< 5)');
      } else if (availableStock < 20) {
        issues.push('Низкий остаток (< 20)');
      }
      
      // Анализ цен
      if (priceData.min_ozon_price && priceData.price < priceData.min_ozon_price) {
        issues.push(`Цена ниже минимальной (${priceData.min_ozon_price})`);
      }
      
      if (priceData.recommended_price && priceData.price > priceData.recommended_price * 1.2) {
        issues.push('Цена значительно выше рекомендуемой');
      }
      
      const discountPercentage = priceData.old_price 
        ? ((priceData.old_price - priceData.price) / priceData.old_price * 100)
        : 0;
      
      return {
        offerId,
        sku: priceData.sku,
        price: priceData.price,
        oldPrice: priceData.old_price,
        discountPercentage: Math.round(discountPercentage * 10) / 10,
        totalStock,
        availableStock,
        reservedStock: totalReserved,
        warehouseCount: stockData.stocks?.length || 0,
        issues,
        status: issues.length === 0 ? 'ok' : 'attention'
      };
    });
    
    // Вывести результаты анализа
    console.log('📊 АНАЛИЗ ТОВАРНОГО ПОРТФЕЛЯ');
    console.log('================================');
    
    const okProducts = analysis.filter(p => p.status === 'ok');
    const attentionProducts = analysis.filter(p => p.status === 'attention');
    const notFoundProducts = analysis.filter(p => p.status === 'not_found');
    
    console.log(`✅ Товары без проблем: ${okProducts.length}`);
    console.log(`⚠️  Требуют внимания: ${attentionProducts.length}`);
    console.log(`❌ Не найдены: ${notFoundProducts.length}`);
    
    if (attentionProducts.length > 0) {
      console.log('\n⚠️  ТОВАРЫ, ТРЕБУЮЩИЕ ВНИМАНИЯ:');
      attentionProducts.forEach(product => {
        console.log(`\n📦 ${product.offerId} (SKU: ${product.sku})`);
        console.log(`   Цена: ${product.price}${product.oldPrice ? ` (было ${product.oldPrice}₽, -${product.discountPercentage}%)` : ''}`);
        console.log(`   Остаток: ${product.availableStock} (всего ${product.totalStock}, зарезервировано ${product.reservedStock})`);
        console.log(`   Складов: ${product.warehouseCount}`);
        console.log(`   Проблемы: ${product.issues.join(', ')}`);
      });
    }
    
    return analysis;
  } catch (error) {
    console.error('❌ Ошибка анализа инвентаря:', error);
    throw error;
  }
}

// Использование
const inventoryAnalysis = await analyzeInventoryStatus([
  'SMARTPHONE_XYZ',
  'LAPTOP_ABC',
  'HEADPHONES_DEF'
]);

Complex Scenarios

Automated Price & Stock Management System

Система автоматического управления ценами и остатками:

class AutomatedPriceStockManager {
  constructor(private ozonApi: OzonApi) {}
  
  /**
   * Автоматическое управление остатками с предупреждениями
   */
  async manageStockLevels(config: StockManagementConfig) {
    try {
      // Получить текущие остатки
      const currentStocks = await this.ozonApi.pricesStocks.getStocks({
        filter: {
          offer_id: config.monitoredProducts,
          visibility: 'VISIBLE'
        },
        limit: 1000
      });
      
      const actions: StockAction[] = [];
      
      currentStocks.items?.forEach(product => {
        const totalStock = product.stocks?.reduce((sum, stock) => sum + stock.present, 0) || 0;
        const totalReserved = product.stocks?.reduce((sum, stock) => sum + stock.reserved, 0) || 0;
        const availableStock = totalStock - totalReserved;
        
        const productConfig = config.productSettings[product.offer_id];
        if (!productConfig) return;
        
        // Проверка критически низкого остатка
        if (availableStock <= productConfig.criticalThreshold) {
          actions.push({
            type: 'critical_low_stock',
            offerId: product.offer_id,
            currentStock: availableStock,
            threshold: productConfig.criticalThreshold,
            action: 'emergency_restock'
          });
        }
        // Проверка низкого остатка
        else if (availableStock <= productConfig.lowThreshold) {
          actions.push({
            type: 'low_stock',
            offerId: product.offer_id,
            currentStock: availableStock,
            threshold: productConfig.lowThreshold,
            action: 'restock_soon'
          });
        }
        // Проверка избыточного остатка
        else if (availableStock >= productConfig.excessThreshold) {
          actions.push({
            type: 'excess_stock',
            offerId: product.offer_id,
            currentStock: availableStock,
            threshold: productConfig.excessThreshold,
            action: 'consider_promotion'
          });
        }
      });
      
      // Выполнить автоматические действия
      for (const action of actions) {
        await this.executeStockAction(action, config);
      }
      
      return actions;
    } catch (error) {
      console.error('❌ Ошибка управления остатками:', error);
      throw error;
    }
  }
  
  /**
   * Динамическое ценообразование на основе остатков и конкуренции
   */
  async dynamicPricing(pricingConfig: DynamicPricingConfig) {
    try {
      const priceUpdates: PriceUpdateItem[] = [];
      
      for (const [offerId, config] of Object.entries(pricingConfig.products)) {
        // Получить текущую информацию о цене и остатках
        const [priceInfo, stockInfo] = await Promise.all([
          this.ozonApi.pricesStocks.getPrices({
            filter: { offer_id: [offerId] },
            limit: 1
          }),
          this.ozonApi.pricesStocks.getStocks({
            filter: { offer_id: [offerId] },
            limit: 1
          })
        ]);
        
        const currentPrice = priceInfo.items?.[0];
        const currentStock = stockInfo.items?.[0];
        
        if (!currentPrice || !currentStock) continue;
        
        const totalStock = currentStock.stocks?.reduce((sum, stock) => sum + stock.present, 0) || 0;
        const availableStock = totalStock - (currentStock.stocks?.reduce((sum, stock) => sum + stock.reserved, 0) || 0);
        
        let newPrice = currentPrice.price;
        let reasoning = '';
        
        // Алгоритм динамического ценообразования
        if (availableStock < config.lowStockThreshold) {
          // Повысить цену при низком остатке
          newPrice = Math.min(
            currentPrice.price * (1 + config.lowStockPriceIncrease),
            config.maxPrice
          );
          reasoning = 'Низкий остаток - повышение цены';
        } else if (availableStock > config.highStockThreshold) {
          // Снизить цену при избыточном остатке
          newPrice = Math.max(
            currentPrice.price * (1 - config.highStockPriceDecrease),
            config.minPrice
          );
          reasoning = 'Избыточный остаток - снижение цены';
        }
        
        // Проверить изменение цены
        const priceChangePercent = Math.abs((newPrice - currentPrice.price) / currentPrice.price);
        if (priceChangePercent >= config.minPriceChangePercent) {
          priceUpdates.push({
            offer_id: offerId,
            price: newPrice.toString(),
            currency_code: 'RUB'
          });
          
          console.log(`💰 ${offerId}: ${currentPrice.price}₽ → ${newPrice}₽ (${reasoning})`);
        }
      }
      
      // Выполнить обновление цен
      if (priceUpdates.length > 0) {
        const result = await this.ozonApi.pricesStocks.updatePrices({
          prices: priceUpdates
        });
        
        console.log(`✅ Обновлено цен: ${result.result?.filter(r => r.updated).length}/${priceUpdates.length}`);
        return result;
      }
      
      console.log('ℹ️ Нет цен для обновления');
      return null;
    } catch (error) {
      console.error('❌ Ошибка динамического ценообразования:', error);
      throw error;
    }
  }
  
  /**
   * Управление уценёнными товарами
   */
  async manageDiscountedProducts() {
    try {
      // Здесь бы был код для получения списка уценённых товаров
      // Но поскольку API не предоставляет метод для получения списка,
      // предположим, что у нас есть список SKU уценённых товаров
      const discountedSkus = await this.getDiscountedSkusFromDatabase();
      
      if (discountedSkus.length === 0) {
        console.log('ℹ️ Нет уценённых товаров для управления');
        return [];
      }
      
      // Получить информацию о уценённых товарах
      const discountedInfo = await this.ozonApi.pricesStocks.getDiscountedProductInfo({
        discounted_skus: discountedSkus
      });
      
      const actions: DiscountAction[] = [];
      
      discountedInfo.items?.forEach(item => {
        // Логика управления скидками
        let newDiscount = item.discount_percentage;
        let reason = '';
        
        // Увеличить скидку для товаров с серьёзными дефектами
        if (item.condition === 'DAMAGED' && item.discount_percentage < 30) {
          newDiscount = 30;
          reason = 'Увеличение скидки для повреждённого товара';
        } else if (item.condition === 'OPENED' && item.discount_percentage < 15) {
          newDiscount = 15;
          reason = 'Установка скидки для вскрытой упаковки';
        }
        
        if (newDiscount !== item.discount_percentage) {
          actions.push({
            discountedSku: item.discounted_sku,
            originalSku: item.original_sku,
            currentDiscount: item.discount_percentage,
            newDiscount,
            reason
          });
        }
      });
      
      // Выполнить обновления скидок
      for (const action of actions) {
        try {
          // Получить product_id по SKU (потребуется дополнительный запрос)
          const productId = await this.getProductIdBySku(action.discountedSku);
          
          if (productId) {
            await this.ozonApi.pricesStocks.updateDiscountedProductDiscount({
              product_id: productId,
              discount: action.newDiscount
            });
            
            console.log(`🏷️ ${action.discountedSku}: скидка ${action.currentDiscount}% → ${action.newDiscount}% (${action.reason})`);
          }
        } catch (error) {
          console.error(`❌ Ошибка обновления скидки для ${action.discountedSku}:`, error);
        }
      }
      
      return actions;
    } catch (error) {
      console.error('❌ Ошибка управления уценёнными товарами:', error);
      throw error;
    }
  }
  
  /**
   * Мониторинг и управление таймерами цен
   */
  async manageActionTimers(productIds: string[]) {
    try {
      // Проверить статус текущих таймеров
      const timerStatus = await this.ozonApi.pricesStocks.getActionTimerStatus({
        product_ids: productIds
      });
      
      const expiredTimers: string[] = [];
      const activeTimers: string[] = [];
      
      timerStatus.statuses?.forEach(status => {
        if (status.is_timer_enabled) {
          if (status.next_update_time && new Date(status.next_update_time) < new Date()) {
            expiredTimers.push(status.product_id);
          } else {
            activeTimers.push(status.product_id);
          }
        }
      });
      
      console.log(`⏱️ Таймеры - активных: ${activeTimers.length}, истекших: ${expiredTimers.length}`);
      
      // Обновить истекшие таймеры
      if (expiredTimers.length > 0) {
        await this.ozonApi.pricesStocks.updateActionTimer({
          product_ids: expiredTimers
        });
        
        console.log(`✅ Обновлено таймеров: ${expiredTimers.length}`);
      }
      
      return {
        activeTimers,
        expiredTimers,
        updated: expiredTimers.length
      };
    } catch (error) {
      console.error('❌ Ошибка управления таймерами:', error);
      throw error;
    }
  }
  
  private async executeStockAction(action: StockAction, config: StockManagementConfig) {
    switch (action.action) {
      case 'emergency_restock':
        console.log(`🚨 КРИТИЧЕСКИ НИЗКИЙ ОСТАТОК: ${action.offerId} (${action.currentStock} шт.)`);
        // Здесь можно добавить автоматическое создание заявки на пополнение
        break;
        
      case 'restock_soon':
        console.log(`⚠️ Низкий остаток: ${action.offerId} (${action.currentStock} шт.) - планируйте пополнение`);
        break;
        
      case 'consider_promotion':
        console.log(`📈 Избыточный остаток: ${action.offerId} (${action.currentStock} шт.) - рассмотрите акцию`);
        break;
    }
  }
  
  private async getDiscountedSkusFromDatabase(): Promise<string[]> {
    // Заглушка - в реальном приложении это был бы запрос к базе данных
    return [];
  }
  
  private async getProductIdBySku(sku: string): Promise<number | null> {
    // Заглушка - в реальном приложении нужно было бы получить product_id
    // через другие методы API или из базы данных
    return null;
  }
}

// Интерфейсы для системы управления
interface StockManagementConfig {
  monitoredProducts: string[];
  productSettings: Record<string, {
    criticalThreshold: number;
    lowThreshold: number;
    excessThreshold: number;
  }>;
}

interface StockAction {
  type: 'critical_low_stock' | 'low_stock' | 'excess_stock';
  offerId: string;
  currentStock: number;
  threshold: number;
  action: 'emergency_restock' | 'restock_soon' | 'consider_promotion';
}

interface DynamicPricingConfig {
  products: Record<string, {
    minPrice: number;
    maxPrice: number;
    lowStockThreshold: number;
    highStockThreshold: number;
    lowStockPriceIncrease: number;
    highStockPriceDecrease: number;
    minPriceChangePercent: number;
  }>;
}

interface DiscountAction {
  discountedSku: string;
  originalSku: string;
  currentDiscount: number;
  newDiscount: number;
  reason: string;
}

// Использование автоматизированной системы
const priceStockManager = new AutomatedPriceStockManager(ozonApi);

// Настройка мониторинга остатков
const stockConfig: StockManagementConfig = {
  monitoredProducts: ['SMARTPHONE_XYZ', 'LAPTOP_ABC', 'HEADPHONES_DEF'],
  productSettings: {
    'SMARTPHONE_XYZ': {
      criticalThreshold: 5,
      lowThreshold: 20,
      excessThreshold: 200
    },
    'LAPTOP_ABC': {
      criticalThreshold: 2,
      lowThreshold: 10,
      excessThreshold: 50
    },
    'HEADPHONES_DEF': {
      criticalThreshold: 10,
      lowThreshold: 50,
      excessThreshold: 500
    }
  }
};

// Запуск мониторинга остатков
const stockActions = await priceStockManager.manageStockLevels(stockConfig);

// Динамическое ценообразование
const pricingConfig: DynamicPricingConfig = {
  products: {
    'SMARTPHONE_XYZ': {
      minPrice: 20000,
      maxPrice: 35000,
      lowStockThreshold: 10,
      highStockThreshold: 100,
      lowStockPriceIncrease: 0.05,
      highStockPriceDecrease: 0.03,
      minPriceChangePercent: 0.02
    }
  }
};

const pricingResult = await priceStockManager.dynamicPricing(pricingConfig);

Error Handling

// Комплексная обработка ошибок для операций с ценами и остатками
async function safePriceStockOperations() {
  try {
    // Обновление цен с валидацией
    const priceResult = await ozonApi.pricesStocks.updatePrices({
      prices: [{
        offer_id: 'ITEM001',
        price: '1000',
        currency_code: 'RUB'
      }]
    });
    
    // Проверка результатов обновления цен
    priceResult.result?.forEach(item => {
      if (!item.updated && item.errors) {
        item.errors.forEach(error => {
          console.error(`💰 Ошибка цены ${item.offer_id}: ${error}`);
        });
      }
    });
    
    // Обновление остатков с обработкой ошибок
    const stockResult = await ozonApi.pricesStocks.updateStocks({
      stocks: [{
        offer_id: 'ITEM001',
        stock: 100,
        warehouse_id: 12345
      }]
    });
    
    // Проверка результатов обновления остатков
    stockResult.result?.forEach(item => {
      if (!item.updated && item.errors) {
        item.errors.forEach(error => {
          console.error(`📦 Ошибка остатков ${item.offer_id}: ${error}`);
        });
      }
    });
    
    return { priceResult, stockResult };
  } catch (error) {
    if (error.code === 'PRICE_UPDATE_LIMIT_EXCEEDED') {
      console.error('❌ Превышен лимит обновления цен (10 раз в час)');
    } else if (error.code === 'STOCK_UPDATE_RATE_LIMIT') {
      console.error('❌ Превышен лимит запросов остатков (80 в минуту)');
    } else if (error.code === 'INVALID_WAREHOUSE_ID') {
      console.error('❌ Неверный идентификатор склада');
    } else if (error.code === 'PRODUCT_NOT_FOUND') {
      console.error('❌ Товар не найден в каталоге');
    } else if (error.code === 'INVALID_PRICE_RANGE') {
      console.error('❌ Цена вне допустимого диапазона');
    } else if (error.code === 'DISCOUNTED_PRODUCT_ONLY_FBS') {
      console.error('❌ Уценённые товары доступны только для FBS');
    } else {
      console.error('❌ Неожиданная ошибка:', error);
    }
    
    throw error;
  }
}

Best Practices

1. Управление ценами

const pricingBestPractices = {
  // Соблюдайте лимиты обновления
  priceUpdateLimit: 10, // обновлений в час на товар
  
  // Используйте валидацию цен
  validatePrices: (price: number, minPrice?: number, maxPrice?: number) => {
    if (minPrice && price < minPrice) return false;
    if (maxPrice && price > maxPrice) return false;
    return price > 0;
  },
  
  // Правильно работайте с old_price
  clearOldPrice: (item: PriceUpdateItem) => ({
    ...item,
    old_price: '0' // Для сброса старой цены
  }),
  
  // Мониторьте рекомендуемые цены
  checkRecommendedPrice: true
};

2. Управление остатками

const stockBestPractices = {
  // Соблюдайте rate limits
  stockUpdateRateLimit: 80, // запросов в минуту
  maxItemsPerRequest: 100,
  
  // Синхронизируйте по всем складам
  syncAllWarehouses: true,
  
  // Учитывайте зарезервированные остатки
  calculateAvailable: (present: number, reserved: number) => present - reserved,
  
  // Устанавливайте критические пороги
  criticalStockThreshold: 5,
  lowStockThreshold: 20
};

3. Автоматизация

const automationBestPractices = {
  // Регулярная синхронизация
  syncInterval: 300000, // 5 минут
  
  // Пакетная обработка
  batchSize: 50,
  
  // Логирование изменений
  logAllChanges: true,
  
  // Откат в случае ошибок
  enableRollback: true,
  
  // Уведомления о критических событиях
  notifyOnCriticalStock: true,
  notifyOnPriceErrors: true
};

Integration Notes

  • Rate Limits: Цены - 10 обновлений/час/товар, остатки - 80 запросов/минуту
  • Batch Operations: Максимум 100 товаров за запрос
  • Currency: Поддерживается RUB, USD, EUR
  • Precision: Цены с точностью до копеек
  • Warehouses: Поддержка множественных складов для FBS/rFBS
  • Validation: Автоматическая проверка min/max цен
  • Timers: Таймеры актуальности для минимальных цен
  • Discounted Products: Работа только со схемой FBS

Prices & Stocks API обеспечивает полный контроль над ценообразованием и управлением остатками, позволяя создавать эффективные системы автоматизации и мониторинга товарного портфеля.