Quants API

Quants API для управления эконом-товарами с 2 методами для работы с квантовыми единицами товаров.

Обзор

Quants API предназначен для управления эконом-товарами (квантами) - специальными единицами товаров с фиксированными количествами и ценами.

Основные возможности:

  • 📋 Получение информации об эконом-товарах
  • 🔍 Фильтрация и поиск квантовых единиц
  • 📊 Управление видимостью и статусами
  • 💰 Контроль цен и количеств

Доступные методы

getInfo(request) - Информация об эконом-товаре

const quantInfo = await quantsApi.getInfo({
  quant_code: ['QUANT001', 'QUANT002']
});

getList(request?) - Список эконом-товаров

const quantList = await quantsApi.getList({
  visibility: 'VISIBLE',
  limit: 100
});

TypeScript интерфейсы

// Основные запросы
interface QuantInfoRequest {
  quant_code: string[];
}

interface QuantListRequest {
  visibility?: "VISIBLE" | "INVISIBLE" | "IN_SALE" | "NOT_IN_SALE" | "ARCHIVED" | "MODERATED" | "REJECTED";
  status?: "ACTIVE" | "INACTIVE" | "PENDING" | "BLOCKED";
  limit?: number;
  cursor?: string;
}

// Ответы
interface QuantInfoResponse {
  items: Array<{
    quant_code: string;
    name: string;
    description: string;
    quantity: number;
    price: string;
    currency_code: string;
    status: "ACTIVE" | "INACTIVE" | "PENDING" | "BLOCKED";
    visibility: "VISIBLE" | "INVISIBLE" | "IN_SALE" | "NOT_IN_SALE" | "ARCHIVED" | "MODERATED" | "REJECTED";
    created_at: string;
    updated_at: string;
    category_id: number;
    brand: string;
    seller_product_id?: string;
    ozon_product_id?: number;
    attributes: Array<{
      attribute_id: number;
      attribute_name: string;
      value: string;
    }>;
    images: Array<{
      url: string;
      index: number;
      is_main: boolean;
    }>;
    pricing: {
      base_price: string;
      discount_price?: string;
      discount_percentage?: number;
    };
    stock: {
      quantity: number;
      reserved: number;
      available: number;
    };
    metrics: {
      views: number;
      purchases: number;
      conversion_rate: number;
    };
  }>;
  total: number;
}

interface QuantListResponse {
  products: Array<{
    quant_code: string;
    name: string;
    quantity: number;
    price: string;
    currency_code: string;
    status: "ACTIVE" | "INACTIVE" | "PENDING" | "BLOCKED";
    visibility: "VISIBLE" | "INVISIBLE" | "IN_SALE" | "NOT_IN_SALE" | "ARCHIVED" | "MODERATED" | "REJECTED";
    created_at: string;
    updated_at: string;
    category_id: number;
    brand: string;
    images: Array<{
      url: string;
      is_main: boolean;
    }>;
    stock: {
      available: number;
      reserved: number;
    };
  }>;
  total: number;
  cursor?: string;
  has_next: boolean;
}

Примеры использования

Получение информации об эконом-товарах

// Детальная информация по кодам квантов
const quantInfo = await quantsApi.getInfo({
  quant_code: ['QUANT001', 'QUANT002', 'QUANT003']
});

quantInfo.items.forEach(item => {
  console.log(`\n=== ${item.name} (${item.quant_code}) ===`);
  console.log(`Количество: ${item.quantity} шт`);
  console.log(`Цена: ${item.price} ${item.currency_code}`);
  console.log(`Статус: ${item.status}`);
  console.log(`Видимость: ${item.visibility}`);
  
  if (item.pricing.discount_price) {
    console.log(`Скидка: ${item.pricing.discount_percentage}%`);
    console.log(`Цена со скидкой: ${item.pricing.discount_price} ${item.currency_code}`);
  }
  
  console.log(`Остаток: ${item.stock.available} из ${item.stock.quantity}`);
  console.log(`Конверсия: ${item.metrics.conversion_rate}% (${item.metrics.purchases} покупок из ${item.metrics.views} просмотров)`);
  
  // Атрибуты товара
  if (item.attributes.length > 0) {
    console.log("\nАтрибуты:");
    item.attributes.forEach(attr => {
      console.log(`  ${attr.attribute_name}: ${attr.value}`);
    });
  }
});

Получение списка эконом-товаров

// Получение всех видимых эконом-товаров
let allQuants: any[] = [];
let cursor: string | undefined;

do {
  const response = await quantsApi.getList({
    visibility: 'VISIBLE',
    status: 'ACTIVE',
    limit: 100,
    cursor
  });

  allQuants.push(...response.products);
  cursor = response.cursor;

  console.log(`Загружено товаров: ${allQuants.length} из ${response.total}`);
  
  // Анализ по ходу загрузки
  const lowStockItems = response.products.filter(p => p.stock.available < 10);
  if (lowStockItems.length > 0) {
    console.log(`⚠️ Товаров с низким остатком в текущей партии: ${lowStockItems.length}`);
  }

} while (cursor && response.has_next);

console.log(`\nОбщая статистика по ${allQuants.length} эконом-товарам:`);
console.log(`Средняя цена: ${calculateAveragePrice(allQuants)} руб.`);
console.log(`Товаров с остатком < 10: ${allQuants.filter(p => p.stock.available < 10).length}`);

function calculateAveragePrice(products: any[]): string {
  const total = products.reduce((sum, p) => sum + parseFloat(p.price), 0);
  return (total / products.length).toFixed(2);
}

Мониторинг статуса эконом-товаров

// Анализ статусов и видимости товаров
const statusAnalysis = await quantsApi.getList({
  limit: 1000
});

// Группировка по статусам
const statusGroups = statusAnalysis.products.reduce((groups, product) => {
  const key = `${product.status}_${product.visibility}`;
  if (!groups[key]) {
    groups[key] = [];
  }
  groups[key].push(product);
  return groups;
}, {} as Record<string, any[]>);

console.log("\n=== Анализ статусов эконом-товаров ===");
Object.entries(statusGroups).forEach(([status, products]) => {
  console.log(`${status}: ${products.length} товаров`);
  
  // Показать проблемные товары
  if (status.includes('INACTIVE') || status.includes('BLOCKED')) {
    console.log("  Проблемные товары:");
    products.slice(0, 5).forEach(p => {
      console.log(`    - ${p.name} (${p.quant_code})`);
    });
    if (products.length > 5) {
      console.log(`    ... и еще ${products.length - 5}`);
    }
  }
});

Сложные сценарии

QuantAnalyticsSystem - Система аналитики эконом-товаров

class QuantAnalyticsSystem {
  constructor(private api: QuantsApi) {}

  async generatePerformanceReport(): Promise<QuantPerformanceReport> {
    // Получение всех активных квантов
    const activeQuants = await this.getAllActiveQuants();
    
    // Детальная информация для анализа
    const detailedInfo = await this.getDetailedQuantInfo(activeQuants);
    
    // Анализ производительности
    const performanceMetrics = this.calculatePerformanceMetrics(detailedInfo);
    
    // Рекомендации по оптимизации
    const recommendations = this.generateOptimizationRecommendations(detailedInfo);
    
    return {
      report_date: new Date().toISOString(),
      total_quants: activeQuants.length,
      performance_metrics: performanceMetrics,
      top_performers: this.identifyTopPerformers(detailedInfo),
      underperformers: this.identifyUnderperformers(detailedInfo),
      optimization_recommendations: recommendations,
      inventory_alerts: this.generateInventoryAlerts(detailedInfo)
    };
  }

  private async getAllActiveQuants(): Promise<QuantProduct[]> {
    const quants: QuantProduct[] = [];
    let cursor: string | undefined;

    do {
      const response = await this.api.getList({
        status: 'ACTIVE',
        visibility: 'IN_SALE',
        limit: 500,
        cursor
      });

      quants.push(...response.products);
      cursor = response.cursor;

    } while (cursor);

    return quants;
  }

  private async getDetailedQuantInfo(quants: QuantProduct[]): Promise<DetailedQuantInfo[]> {
    const batchSize = 50;
    const detailedInfo: DetailedQuantInfo[] = [];

    for (let i = 0; i < quants.length; i += batchSize) {
      const batch = quants.slice(i, i + batchSize);
      const quantCodes = batch.map(q => q.quant_code);

      const infoResponse = await this.api.getInfo({ quant_code: quantCodes });
      detailedInfo.push(...infoResponse.items.map(item => ({
        ...item,
        efficiency_score: this.calculateEfficiencyScore(item),
        profitability_score: this.calculateProfitabilityScore(item),
        market_position: this.analyzeMarketPosition(item)
      })));

      // Rate limiting
      await new Promise(resolve => setTimeout(resolve, 100));
    }

    return detailedInfo;
  }

  private calculatePerformanceMetrics(quantsInfo: DetailedQuantInfo[]): PerformanceMetrics {
    const totalViews = quantsInfo.reduce((sum, q) => sum + q.metrics.views, 0);
    const totalPurchases = quantsInfo.reduce((sum, q) => sum + q.metrics.purchases, 0);
    const totalRevenue = quantsInfo.reduce((sum, q) => sum + (q.metrics.purchases * parseFloat(q.price)), 0);

    return {
      overall_conversion_rate: totalPurchases / totalViews,
      average_conversion_rate: quantsInfo.reduce((sum, q) => sum + q.metrics.conversion_rate, 0) / quantsInfo.length,
      total_revenue: totalRevenue,
      average_revenue_per_quant: totalRevenue / quantsInfo.length,
      high_performers_count: quantsInfo.filter(q => q.efficiency_score > 0.8).length,
      low_performers_count: quantsInfo.filter(q => q.efficiency_score < 0.3).length,
      out_of_stock_count: quantsInfo.filter(q => q.stock.available === 0).length,
      low_stock_count: quantsInfo.filter(q => q.stock.available > 0 && q.stock.available < 10).length
    };
  }

  private generateOptimizationRecommendations(quantsInfo: DetailedQuantInfo[]): OptimizationRecommendation[] {
    const recommendations: OptimizationRecommendation[] = [];

    // Анализ цен
    const overpriced = quantsInfo.filter(q => q.efficiency_score < 0.3 && q.metrics.views > 100);
    if (overpriced.length > 0) {
      recommendations.push({
        type: "PRICE_OPTIMIZATION",
        priority: "HIGH",
        affected_quants: overpriced.length,
        description: `${overpriced.length} товаров имеют низкую эффективность при высоком трафике - рекомендуется снижение цен`,
        expected_impact: "Увеличение конверсии на 15-25%"
      });
    }

    // Анализ остатков
    const overstocked = quantsInfo.filter(q => 
      q.stock.available > 50 && q.metrics.purchases / q.stock.available < 0.1
    );
    if (overstocked.length > 0) {
      recommendations.push({
        type: "INVENTORY_OPTIMIZATION", 
        priority: "MEDIUM",
        affected_quants: overstocked.length,
        description: `${overstocked.length} товаров имеют избыточные остатки с низкой оборачиваемостью`,
        expected_impact: "Освобождение складских площадей, снижение затрат на хранение"
      });
    }

    // Анализ трендов
    const trendingProducts = quantsInfo.filter(q => 
      q.metrics.conversion_rate > 0.05 && q.stock.available < 20
    );
    if (trendingProducts.length > 0) {
      recommendations.push({
        type: "STOCK_REPLENISHMENT",
        priority: "HIGH", 
        affected_quants: trendingProducts.length,
        description: `${trendingProducts.length} высокоэффективных товаров нуждаются в пополнении остатков`,
        expected_impact: "Предотвращение потери продаж, увеличение выручки на 20-30%"
      });
    }

    return recommendations.sort((a, b) => {
      const priorityOrder = { "HIGH": 3, "MEDIUM": 2, "LOW": 1 };
      return priorityOrder[b.priority] - priorityOrder[a.priority];
    });
  }

  private generateInventoryAlerts(quantsInfo: DetailedQuantInfo[]): InventoryAlert[] {
    const alerts: InventoryAlert[] = [];

    quantsInfo.forEach(quant => {
      // Критически низкий остаток
      if (quant.stock.available === 0 && quant.metrics.conversion_rate > 0.02) {
        alerts.push({
          level: "CRITICAL",
          quant_code: quant.quant_code,
          message: "Товар закончился при высокой конверсии",
          suggested_action: "Срочное пополнение остатков"
        });
      }
      
      // Низкий остаток высокоэффективного товара
      else if (quant.stock.available < 5 && quant.efficiency_score > 0.7) {
        alerts.push({
          level: "WARNING",
          quant_code: quant.quant_code,
          message: "Низкий остаток эффективного товара",
          suggested_action: "Планирование пополнения остатков"
        });
      }
      
      // Избыточный остаток неэффективного товара
      else if (quant.stock.available > 100 && quant.efficiency_score < 0.2) {
        alerts.push({
          level: "INFO",
          quant_code: quant.quant_code,
          message: "Избыточный остаток низкоэффективного товара",
          suggested_action: "Рассмотрение промо-акций или снижения цены"
        });
      }
    });

    return alerts.sort((a, b) => {
      const levelOrder = { "CRITICAL": 3, "WARNING": 2, "INFO": 1 };
      return levelOrder[b.level] - levelOrder[a.level];
    });
  }

  private calculateEfficiencyScore(quant: any): number {
    const conversionWeight = 0.4;
    const viewsWeight = 0.3;
    const stockTurnoverWeight = 0.3;

    const conversionScore = Math.min(quant.metrics.conversion_rate * 20, 1); // нормализация до 1
    const viewsScore = Math.min(quant.metrics.views / 1000, 1); // нормализация до 1
    const turnoverScore = quant.stock.quantity > 0 ? 
      Math.min(quant.metrics.purchases / quant.stock.quantity, 1) : 0;

    return (conversionScore * conversionWeight + 
            viewsScore * viewsWeight + 
            turnoverScore * stockTurnoverWeight);
  }
}

interface QuantProduct {
  quant_code: string;
  name: string;
  quantity: number;
  price: string;
  status: string;
  visibility: string;
}

interface DetailedQuantInfo {
  quant_code: string;
  name: string;
  quantity: number;
  price: string;
  stock: {
    quantity: number;
    available: number;
    reserved: number;
  };
  metrics: {
    views: number;
    purchases: number;
    conversion_rate: number;
  };
  efficiency_score: number;
  profitability_score: number;
  market_position: string;
}

interface QuantPerformanceReport {
  report_date: string;
  total_quants: number;
  performance_metrics: PerformanceMetrics;
  top_performers: DetailedQuantInfo[];
  underperformers: DetailedQuantInfo[];
  optimization_recommendations: OptimizationRecommendation[];
  inventory_alerts: InventoryAlert[];
}

SmartQuantManager - Умный менеджер квантов

class SmartQuantManager {
  constructor(private api: QuantsApi) {}

  async optimizeQuantPortfolio(): Promise<OptimizationResult> {
    // Получение текущего состояния портфеля
    const currentPortfolio = await this.analyzeCurrentPortfolio();
    
    // Идентификация возможностей оптимизации
    const opportunities = this.identifyOptimizationOpportunities(currentPortfolio);
    
    // Создание плана оптимизации
    const optimizationPlan = this.createOptimizationPlan(opportunities);
    
    return {
      current_state: currentPortfolio,
      optimization_opportunities: opportunities,
      recommended_actions: optimizationPlan.actions,
      expected_improvements: optimizationPlan.expectedImprovements,
      implementation_timeline: optimizationPlan.timeline
    };
  }

  private async analyzeCurrentPortfolio(): Promise<PortfolioAnalysis> {
    const allQuants = await this.getAllQuantsWithDetails();
    
    // Категоризация товаров по производительности
    const categories = this.categorizeByPerformance(allQuants);
    
    // Анализ распределения по ценовым сегментам
    const priceSegments = this.analyzePriceSegments(allQuants);
    
    // Анализ оборачиваемости
    const turnoverAnalysis = this.analyzeTurnover(allQuants);

    return {
      total_quants: allQuants.length,
      performance_categories: categories,
      price_segments: priceSegments,
      turnover_analysis: turnoverAnalysis,
      health_score: this.calculatePortfolioHealthScore(allQuants)
    };
  }

  private categorizeByPerformance(quants: DetailedQuantInfo[]): PerformanceCategories {
    const stars = quants.filter(q => q.efficiency_score > 0.8 && q.metrics.conversion_rate > 0.03);
    const cashCows = quants.filter(q => q.efficiency_score > 0.6 && q.metrics.purchases > 50);
    const questionMarks = quants.filter(q => q.efficiency_score > 0.4 && q.metrics.views > 500 && q.metrics.purchases < 20);
    const dogs = quants.filter(q => q.efficiency_score < 0.3 && q.metrics.conversion_rate < 0.01);

    return {
      stars: { count: stars.length, items: stars },
      cash_cows: { count: cashCows.length, items: cashCows },
      question_marks: { count: questionMarks.length, items: questionMarks },
      dogs: { count: dogs.length, items: dogs }
    };
  }

  private identifyOptimizationOpportunities(portfolio: PortfolioAnalysis): OptimizationOpportunity[] {
    const opportunities: OptimizationOpportunity[] = [];

    // Возможности для звезд (высокоэффективные товары)
    portfolio.performance_categories.stars.items.forEach(star => {
      if (star.stock.available < 20) {
        opportunities.push({
          type: "INCREASE_INVENTORY",
          quant_code: star.quant_code,
          priority: "HIGH",
          rationale: "Высокоэффективный товар с низким остатком",
          expected_revenue_increase: star.metrics.purchases * parseFloat(star.price) * 0.3
        });
      }
    });

    // Возможности для проблемных товаров
    portfolio.performance_categories.dogs.items.forEach(dog => {
      if (dog.stock.available > 50) {
        opportunities.push({
          type: "REDUCE_PRICE_OR_LIQUIDATE",
          quant_code: dog.quant_code,
          priority: "MEDIUM",
          rationale: "Неэффективный товар с избыточным остатком",
          expected_cost_savings: dog.stock.available * parseFloat(dog.price) * 0.1 // экономия на хранении
        });
      }
    });

    // Возможности для "знаков вопроса"
    portfolio.performance_categories.question_marks.items.forEach(questionMark => {
      opportunities.push({
        type: "TEST_PRICE_REDUCTION",
        quant_code: questionMark.quant_code,
        priority: "MEDIUM",
        rationale: "Высокий трафик, низкая конверсия - возможно, проблема с ценой",
        expected_conversion_increase: 0.02 // увеличение конверсии на 2%
      });
    });

    return opportunities.sort((a, b) => {
      const priorityOrder = { "HIGH": 3, "MEDIUM": 2, "LOW": 1 };
      return priorityOrder[b.priority] - priorityOrder[a.priority];
    });
  }
}

interface OptimizationOpportunity {
  type: "INCREASE_INVENTORY" | "REDUCE_PRICE_OR_LIQUIDATE" | "TEST_PRICE_REDUCTION" | "IMPROVE_VISIBILITY";
  quant_code: string;
  priority: "HIGH" | "MEDIUM" | "LOW";
  rationale: string;
  expected_revenue_increase?: number;
  expected_cost_savings?: number;
  expected_conversion_increase?: number;
}

interface PortfolioAnalysis {
  total_quants: number;
  performance_categories: PerformanceCategories;
  price_segments: PriceSegmentAnalysis;
  turnover_analysis: TurnoverAnalysis;
  health_score: number;
}

Обработка ошибок

try {
  const quantInfo = await quantsApi.getInfo({
    quant_code: ['INVALID_CODE']
  });
} catch (error) {
  if (error.response?.status === 404) {
    console.error("Эконом-товары с указанными кодами не найдены");
  } else if (error.response?.status === 400) {
    console.error("Некорректные параметры запроса:", error.response.data);
  } else {
    console.error("Неожиданная ошибка:", error.message);
  }
}

Лучшие практики

Эффективная работа с пагинацией

async function getAllQuantsEfficiently(): Promise<QuantProduct[]> {
  const allQuants: QuantProduct[] = [];
  let cursor: string | undefined;
  let batchCount = 0;

  do {
    const response = await quantsApi.getList({
      limit: 500, // максимальный размер батча
      cursor
    });

    allQuants.push(...response.products);
    cursor = response.cursor;
    batchCount++;

    // Логирование прогресса каждые 10 батчей
    if (batchCount % 10 === 0) {
      console.log(`Загружено ${allQuants.length} товаров в ${batchCount} батчах`);
    }

    // Rate limiting для больших объемов данных
    if (batchCount > 5) {
      await new Promise(resolve => setTimeout(resolve, 50));
    }

  } while (cursor);

  return allQuants;
}

Мониторинг критических метрик

interface QuantHealthCheck {
  total_active: number;
  out_of_stock: number;
  low_conversion: number;
  high_performers: number;
  health_score: number;
}

async function performHealthCheck(): Promise<QuantHealthCheck> {
  const activeQuants = await quantsApi.getList({
    status: 'ACTIVE',
    visibility: 'IN_SALE',
    limit: 1000
  });

  const outOfStock = activeQuants.products.filter(q => q.stock.available === 0).length;
  const codes = activeQuants.products.map(q => q.quant_code);
  
  const detailedInfo = await quantsApi.getInfo({ quant_code: codes.slice(0, 100) }); // пример для первых 100
  const lowConversion = detailedInfo.items.filter(q => q.metrics.conversion_rate < 0.01).length;
  const highPerformers = detailedInfo.items.filter(q => q.metrics.conversion_rate > 0.05).length;

  const healthScore = Math.max(0, Math.min(100, 
    100 - (outOfStock * 2) - (lowConversion * 1) + (highPerformers * 0.5)
  ));

  return {
    total_active: activeQuants.products.length,
    out_of_stock: outOfStock,
    low_conversion: lowConversion,
    high_performers: highPerformers,
    health_score: Math.round(healthScore)
  };
}

Интеграционные заметки

  • Rate Limiting: API поддерживает до 100 запросов в минуту
  • Batch Processing: Рекомендуется запрашивать информацию батчами до 50 кодов
  • Cursor Pagination: Используется курсорная пагинация для эффективной навигации
  • Status Management: Статусы товаров обновляются в реальном времени
  • Stock Synchronization: Остатки синхронизируются с основной системой учета
  • Performance Metrics: Метрики обновляются с задержкой до 24 часов