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 часов