Return API

Return API для управления процессами возврата с 8 методами для работы с возвратными отгрузками и штрихкодами.

Обзор

Return API предоставляет инструменты для управления возвратными отгрузками, генерации штрихкодов и получения аналитики по возвратам.

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

  • 📋 Управление возвратными отгрузками
  • 🔍 Генерация и управление штрихкодами
  • 📊 Получение информации о возвратах FBS
  • 📄 Экспорт штрихкодов в PDF и PNG форматах
  • ⚙️ Проверка доступности функций возврата
  • 📈 Аналитика возвратов по периодам

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

Управление штрихкодами

getGiveoutBarcode() - Получить значение штрихкода

const barcode = await returnApi.getGiveoutBarcode();

resetGiveoutBarcode() - Сгенерировать новый штрихкод

const newBarcode = await returnApi.resetGiveoutBarcode();

getGiveoutPDF() - Получить штрихкод в PDF формате

const pdfBarcode = await returnApi.getGiveoutPDF();

getGiveoutPNG() - Получить штрихкод в PNG формате

const pngBarcode = await returnApi.getGiveoutPNG();

Управление возвратными отгрузками

getGiveoutInfo(request) - Информация о возвратной отгрузке

const giveoutInfo = await returnApi.getGiveoutInfo({
  giveout_id: 12345
});

getGiveoutList(request?) - Список возвратных отгрузок

const giveouts = await returnApi.getGiveoutList({
  limit: 100
});

isGiveoutEnabled() - Проверить доступность функции

const isEnabled = await returnApi.isGiveoutEnabled();

Аналитика возвратов

getReturnsCompanyFbsInfo(request?) - Информация о возвратах FBS

const fbsInfo = await returnApi.getReturnsCompanyFbsInfo({
  date_from: '2024-01-01T00:00:00Z',
  date_to: '2024-01-31T23:59:59Z'
});

TypeScript интерфейсы

// Основные запросы
interface GiveoutInfoRequest {
  giveout_id: number;
}

interface GiveoutListRequest {
  limit?: number;
  last_id?: number;
}

interface ReturnsCompanyFbsInfoRequest {
  date_from?: string;
  date_to?: string;
}

// Ответы
interface GiveoutGetBarcodeResponse {
  barcode: string;
  expiry_date: string;
}

interface GiveoutBarcodeResetResponse {
  barcode: string;
  png_data: string;
  expiry_date: string;
}

interface GiveoutGetPDFResponse {
  pdf_data: string;
  file_name: string;
}

interface GiveoutGetPNGResponse {
  png_data: string;
  file_name: string;
}

interface GiveoutInfoResponse {
  giveout_id: number;
  status: "PENDING" | "IN_PROCESS" | "COMPLETED" | "CANCELLED";
  created_at: string;
  updated_at: string;
  total_amount: string;
  currency_code: string;
  items: Array<{
    sku: number;
    offer_id: string;
    name: string;
    quantity: number;
    price: string;
    return_reason: string;
    return_reason_name: string;
    condition: "NEW" | "DAMAGED" | "USED";
    refund_amount: string;
    commission_amount: string;
  }>;
  delivery_info: {
    delivery_id: string;
    tracking_number: string;
    delivery_service: string;
    pickup_address: string;
    expected_pickup_date: string;
  };
}

interface GiveoutIsEnabledResponse {
  is_enabled: boolean;
  message?: string;
  restrictions?: Array<{
    type: string;
    description: string;
  }>;
}

interface GiveoutListResponse {
  giveouts: Array<{
    giveout_id: number;
    status: "PENDING" | "IN_PROCESS" | "COMPLETED" | "CANCELLED";
    created_at: string;
    updated_at: string;
    items_count: number;
    total_amount: string;
    currency_code: string;
    delivery_service: string;
    tracking_number?: string;
  }>;
  total: number;
  has_next: boolean;
  last_id?: number;
}

interface ReturnsCompanyFbsInfoResponse {
  returns_info: Array<{
    date: string;
    count: number;
    amount: string;
    currency_code: string;
    return_reasons: Array<{
      reason: string;
      reason_name: string;
      count: number;
      amount: string;
    }>;
  }>;
  total_returns: number;
  total_amount: string;
  currency_code: string;
}

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

Настройка штрихкода для возвратов

// Проверка доступности функции возврата
const giveoutStatus = await returnApi.isGiveoutEnabled();

if (!giveoutStatus.is_enabled) {
  console.log("❌ Функция возвратных отгрузок недоступна");
  if (giveoutStatus.restrictions) {
    console.log("Ограничения:");
    giveoutStatus.restrictions.forEach(restriction => {
      console.log(`  - ${restriction.type}: ${restriction.description}`);
    });
  }
  return;
}

console.log("✅ Функция возвратных отгрузок доступна");

// Получение текущего штрихкода
let barcodeInfo = await returnApi.getGiveoutBarcode();
console.log(`Текущий штрихкод: ${barcodeInfo.barcode}`);
console.log(`Срок действия: ${barcodeInfo.expiry_date}`);

// Проверка срока действия штрихкода
const expiryDate = new Date(barcodeInfo.expiry_date);
const now = new Date();
const daysUntilExpiry = Math.ceil((expiryDate.getTime() - now.getTime()) / (1000 * 60 * 60 * 24));

if (daysUntilExpiry < 7) {
  console.log(`⚠️ Штрихкод истекает через ${daysUntilExpiry} дней - генерируем новый`);
  
  const newBarcodeInfo = await returnApi.resetGiveoutBarcode();
  console.log(`✅ Новый штрихкод: ${newBarcodeInfo.barcode}`);
  console.log(`Новый срок действия: ${newBarcodeInfo.expiry_date}`);
  
  // Сохранение PNG версии нового штрихкода
  const pngData = Buffer.from(newBarcodeInfo.png_data, 'base64');
  console.log(`📁 PNG штрихкод размером: ${pngData.length} байт`);
}

// Получение штрихкода в различных форматах
const pdfBarcode = await returnApi.getGiveoutPDF();
const pngBarcode = await returnApi.getGiveoutPNG();

console.log(`📄 PDF штрихкод: ${pdfBarcode.file_name}`);
console.log(`🖼️ PNG штрихкод: ${pngBarcode.file_name}`);

// Сохранение файлов (пример)
const fs = require('fs');
fs.writeFileSync('return-barcode.pdf', Buffer.from(pdfBarcode.pdf_data, 'base64'));
fs.writeFileSync('return-barcode.png', Buffer.from(pngBarcode.png_data, 'base64'));

console.log("✅ Штрихкоды сохранены в файлы");

Мониторинг возвратных отгрузок

// Получение всех активных возвратных отгрузок
let allGiveouts: any[] = [];
let lastId: number | undefined;

do {
  const giveouts = await returnApi.getGiveoutList({
    limit: 100,
    last_id: lastId
  });

  allGiveouts.push(...giveouts.giveouts);
  lastId = giveouts.has_next ? giveouts.last_id : undefined;

  console.log(`Загружено отгрузок: ${allGiveouts.length} из ${giveouts.total}`);

} while (lastId);

console.log(`\n=== Анализ ${allGiveouts.length} возвратных отгрузок ===`);

// Группировка по статусам
const statusGroups = allGiveouts.reduce((groups, giveout) => {
  const status = giveout.status;
  if (!groups[status]) {
    groups[status] = [];
  }
  groups[status].push(giveout);
  return groups;
}, {} as Record<string, any[]>);

Object.entries(statusGroups).forEach(([status, giveouts]) => {
  const totalAmount = giveouts.reduce((sum, g) => sum + parseFloat(g.total_amount), 0);
  const totalItems = giveouts.reduce((sum, g) => sum + g.items_count, 0);
  
  console.log(`\n${status}: ${giveouts.length} отгрузок`);
  console.log(`  Общая сумма: ${totalAmount.toFixed(2)} руб`);
  console.log(`  Общее количество товаров: ${totalItems}`);
});

// Анализ отгрузок, требующих внимания
const pendingGiveouts = statusGroups['PENDING'] || [];
const inProcessGiveouts = statusGroups['IN_PROCESS'] || [];

if (pendingGiveouts.length > 0) {
  console.log(`\n⏳ Ожидающие обработки: ${pendingGiveouts.length}`);
  
  // Показываем самые старые ожидающие отгрузки
  const oldestPending = pendingGiveouts
    .sort((a, b) => new Date(a.created_at).getTime() - new Date(b.created_at).getTime())
    .slice(0, 5);

  oldestPending.forEach(giveout => {
    const daysWaiting = Math.floor((Date.now() - new Date(giveout.created_at).getTime()) / (1000 * 60 * 60 * 24));
    console.log(`  - Отгрузка ${giveout.giveout_id}: ожидает ${daysWaiting} дней (${giveout.items_count} товаров)`);
  });
}

if (inProcessGiveouts.length > 0) {
  console.log(`\n🔄 В процессе: ${inProcessGiveouts.length}`);
}

Детальный анализ возвратной отгрузки

// Получение детальной информации о конкретной отгрузке
const giveoutId = 12345;
const giveoutInfo = await returnApi.getGiveoutInfo({ giveout_id: giveoutId });

console.log(`\n=== Детали отгрузки ${giveoutId} ===`);
console.log(`Статус: ${giveoutInfo.status}`);
console.log(`Создана: ${giveoutInfo.created_at}`);
console.log(`Обновлена: ${giveoutInfo.updated_at}`);
console.log(`Общая сумма: ${giveoutInfo.total_amount} ${giveoutInfo.currency_code}`);

// Информация о доставке
if (giveoutInfo.delivery_info) {
  console.log(`\nДоставка:`);
  console.log(`  Служба доставки: ${giveoutInfo.delivery_info.delivery_service}`);
  console.log(`  Трек-номер: ${giveoutInfo.delivery_info.tracking_number}`);
  console.log(`  Адрес забора: ${giveoutInfo.delivery_info.pickup_address}`);
  console.log(`  Ожидаемая дата забора: ${giveoutInfo.delivery_info.expected_pickup_date}`);
}

// Анализ товаров в отгрузке
console.log(`\nТовары (${giveoutInfo.items.length}):`);

const reasonCounts = new Map<string, number>();
const conditionCounts = new Map<string, number>();
let totalRefund = 0;
let totalCommission = 0;

giveoutInfo.items.forEach(item => {
  console.log(`\n  ${item.name} (${item.offer_id})`);
  console.log(`    SKU: ${item.sku}`);
  console.log(`    Количество: ${item.quantity}`);
  console.log(`    Цена: ${item.price} руб`);
  console.log(`    Причина возврата: ${item.return_reason_name}`);
  console.log(`    Состояние: ${item.condition}`);
  console.log(`    Возврат: ${item.refund_amount} руб`);
  console.log(`    Комиссия: ${item.commission_amount} руб`);

  // Статистика
  reasonCounts.set(item.return_reason_name, (reasonCounts.get(item.return_reason_name) || 0) + 1);
  conditionCounts.set(item.condition, (conditionCounts.get(item.condition) || 0) + 1);
  totalRefund += parseFloat(item.refund_amount);
  totalCommission += parseFloat(item.commission_amount);
});

console.log(`\n=== Статистика по отгрузке ===`);
console.log(`Общая сумма возврата: ${totalRefund.toFixed(2)} руб`);
console.log(`Общая комиссия: ${totalCommission.toFixed(2)} руб`);

console.log(`\nПричины возврата:`);
reasonCounts.forEach((count, reason) => {
  console.log(`  ${reason}: ${count} товаров`);
});

console.log(`\nСостояние товаров:`);
conditionCounts.forEach((count, condition) => {
  console.log(`  ${condition}: ${count} товаров`);
});

Аналитика возвратов FBS

// Получение аналитики возвратов за последние 3 месяца
const threeMonthsAgo = new Date();
threeMonthsAgo.setMonth(threeMonthsAgo.getMonth() - 3);

const fbsReturnsInfo = await returnApi.getReturnsCompanyFbsInfo({
  date_from: threeMonthsAgo.toISOString(),
  date_to: new Date().toISOString()
});

console.log(`\n=== Аналитика возвратов FBS ===`);
console.log(`Общее количество возвратов: ${fbsReturnsInfo.total_returns}`);
console.log(`Общая сумма возвратов: ${fbsReturnsInfo.total_amount} ${fbsReturnsInfo.currency_code}`);

// Анализ по дням
console.log(`\nДетализация по дням:`);
fbsReturnsInfo.returns_info.forEach(dayInfo => {
  const date = new Date(dayInfo.date).toLocaleDateString('ru-RU');
  console.log(`\n${date}:`);
  console.log(`  Возвратов: ${dayInfo.count}`);
  console.log(`  Сумма: ${dayInfo.amount} ${dayInfo.currency_code}`);
  
  if (dayInfo.return_reasons.length > 0) {
    console.log(`  Причины возвратов:`);
    dayInfo.return_reasons
      .sort((a, b) => b.count - a.count)
      .forEach(reason => {
        console.log(`    ${reason.reason_name}: ${reason.count} (${reason.amount} руб)`);
      });
  }
});

// Статистика по причинам возвратов
const allReasons = new Map<string, { count: number; amount: number }>();

fbsReturnsInfo.returns_info.forEach(dayInfo => {
  dayInfo.return_reasons.forEach(reason => {
    const existing = allReasons.get(reason.reason_name) || { count: 0, amount: 0 };
    existing.count += reason.count;
    existing.amount += parseFloat(reason.amount);
    allReasons.set(reason.reason_name, existing);
  });
});

console.log(`\n=== Топ причин возвратов ===`);
const sortedReasons = Array.from(allReasons.entries())
  .sort((a, b) => b[1].count - a[1].count)
  .slice(0, 10);

sortedReasons.forEach(([reason, stats], index) => {
  const percentage = (stats.count / fbsReturnsInfo.total_returns * 100).toFixed(1);
  console.log(`${index + 1}. ${reason}:`);
  console.log(`   ${stats.count} возвратов (${percentage}%)`);
  console.log(`   ${stats.amount.toFixed(2)} руб`);
});

// Анализ трендов
const dailyStats = fbsReturnsInfo.returns_info.map(day => ({
  date: day.date,
  count: day.count,
  amount: parseFloat(day.amount)
})).sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime());

if (dailyStats.length > 7) {
  const lastWeekAvg = dailyStats.slice(-7).reduce((sum, day) => sum + day.count, 0) / 7;
  const previousWeekAvg = dailyStats.slice(-14, -7).reduce((sum, day) => sum + day.count, 0) / 7;
  const trendChange = ((lastWeekAvg - previousWeekAvg) / previousWeekAvg * 100);
  
  console.log(`\n=== Анализ трендов ===`);
  console.log(`Средние возвраты за последнюю неделю: ${lastWeekAvg.toFixed(1)} в день`);
  console.log(`Средние возвраты за предыдущую неделю: ${previousWeekAvg.toFixed(1)} в день`);
  
  if (trendChange > 10) {
    console.log(`📈 Рост возвратов: +${trendChange.toFixed(1)}% - требует внимания`);
  } else if (trendChange < -10) {
    console.log(`📉 Снижение возвратов: ${trendChange.toFixed(1)}%`);
  } else {
    console.log(`➡️ Стабильный уровень возвратов: ${trendChange.toFixed(1)}%`);
  }
}

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

ReturnWorkflowManager - Система управления возвратами

class ReturnWorkflowManager {
  constructor(private api: ReturnApi) {}

  async processReturnWorkflow(): Promise<ReturnWorkflowResult> {
    console.log("🔄 Запуск процесса управления возвратами...");

    // 1. Проверка доступности функций
    const systemCheck = await this.checkSystemAvailability();
    
    // 2. Обновление штрихкодов при необходимости
    const barcodeManagement = await this.manageBarcodes();
    
    // 3. Обработка активных отгрузок
    const giveoutProcessing = await this.processActiveGiveouts();
    
    // 4. Анализ возвратов и генерация отчетов
    const analytics = await this.generateReturnAnalytics();
    
    // 5. Генерация рекомендаций
    const recommendations = this.generateRecommendations(analytics);

    return {
      timestamp: new Date().toISOString(),
      system_status: systemCheck,
      barcode_management: barcodeManagement,
      giveout_processing: giveoutProcessing,
      analytics,
      recommendations
    };
  }

  private async checkSystemAvailability(): Promise<SystemStatus> {
    const enabledStatus = await this.api.isGiveoutEnabled();
    
    return {
      is_available: enabledStatus.is_enabled,
      message: enabledStatus.message,
      restrictions: enabledStatus.restrictions || [],
      health_score: enabledStatus.is_enabled ? 100 : 0
    };
  }

  private async manageBarcodes(): Promise<BarcodeManagement> {
    // Получение текущего штрихкода
    const currentBarcode = await this.api.getGiveoutBarcode();
    const expiryDate = new Date(currentBarcode.expiry_date);
    const daysUntilExpiry = Math.ceil((expiryDate.getTime() - Date.now()) / (1000 * 60 * 60 * 24));
    
    let barcodeUpdate: BarcodeUpdate | null = null;

    // Проверка необходимости обновления штрихкода
    if (daysUntilExpiry < 7) {
      console.log(`⚠️ Штрихкод истекает через ${daysUntilExpiry} дней - обновление...`);
      
      const newBarcodeInfo = await this.api.resetGiveoutBarcode();
      
      // Сохранение новых штрихкодов
      const [pdfBarcode, pngBarcode] = await Promise.all([
        this.api.getGiveoutPDF(),
        this.api.getGiveoutPNG()
      ]);

      barcodeUpdate = {
        old_barcode: currentBarcode.barcode,
        new_barcode: newBarcodeInfo.barcode,
        new_expiry: newBarcodeInfo.expiry_date,
        pdf_generated: !!pdfBarcode.pdf_data,
        png_generated: !!pngBarcode.png_data,
        updated_at: new Date().toISOString()
      };

      console.log(`✅ Штрихкод обновлен: ${newBarcodeInfo.barcode}`);
    }

    return {
      current_barcode: currentBarcode.barcode,
      expiry_date: currentBarcode.expiry_date,
      days_until_expiry: daysUntilExpiry,
      requires_update: daysUntilExpiry < 7,
      update_performed: !!barcodeUpdate,
      update_details: barcodeUpdate
    };
  }

  private async processActiveGiveouts(): Promise<GiveoutProcessingResult> {
    // Получение всех активных отгрузок
    const giveouts = await this.getAllGiveouts();
    
    // Группировка по статусам
    const statusGroups = this.groupGiveoutsByStatus(giveouts);
    
    // Обработка проблемных отгрузок
    const problemGiveouts = await this.identifyProblemGiveouts(giveouts);
    
    // Анализ производительности
    const performanceMetrics = this.calculateGiveoutPerformance(giveouts);

    return {
      total_giveouts: giveouts.length,
      status_breakdown: statusGroups,
      problem_giveouts: problemGiveouts,
      performance_metrics: performanceMetrics,
      recommendations: this.generateGiveoutRecommendations(statusGroups, problemGiveouts)
    };
  }

  private async generateReturnAnalytics(): Promise<ReturnAnalytics> {
    // Получение данных за последние 30 дней
    const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);
    const fbsInfo = await this.api.getReturnsCompanyFbsInfo({
      date_from: thirtyDaysAgo.toISOString(),
      date_to: new Date().toISOString()
    });

    // Анализ трендов
    const trendAnalysis = this.analyzeTrends(fbsInfo.returns_info);
    
    // Анализ причин возвратов
    const reasonAnalysis = this.analyzeReturnReasons(fbsInfo.returns_info);
    
    // Расчет ключевых метрик
    const keyMetrics = this.calculateReturnMetrics(fbsInfo);

    return {
      period: "30 days",
      total_returns: fbsInfo.total_returns,
      total_amount: parseFloat(fbsInfo.total_amount),
      currency: fbsInfo.currency_code,
      trend_analysis: trendAnalysis,
      reason_analysis: reasonAnalysis,
      key_metrics: keyMetrics
    };
  }

  private generateRecommendations(analytics: ReturnAnalytics): Recommendation[] {
    const recommendations: Recommendation[] = [];

    // Анализ уровня возвратов
    if (analytics.key_metrics.return_rate > 0.15) {
      recommendations.push({
        type: 'HIGH_RETURN_RATE',
        priority: 'HIGH',
        title: 'Высокий уровень возвратов',
        description: `Уровень возвратов составляет ${(analytics.key_metrics.return_rate * 100).toFixed(1)}%`,
        actions: [
          'Улучшить качество описаний товаров',
          'Проанализировать основные причины возвратов',
          'Рассмотреть улучшение упаковки'
        ],
        expected_impact: 'Снижение возвратов на 20-30%'
      });
    }

    // Анализ трендов
    if (analytics.trend_analysis.weekly_growth > 0.2) {
      recommendations.push({
        type: 'GROWING_RETURNS',
        priority: 'MEDIUM',
        title: 'Растущий тренд возвратов',
        description: `Возвраты выросли на ${(analytics.trend_analysis.weekly_growth * 100).toFixed(1)}% за неделю`,
        actions: [
          'Провести углубленный анализ причин роста',
          'Проверить изменения в ассортименте',
          'Оценить качество новых поставщиков'
        ],
        expected_impact: 'Стабилизация уровня возвратов'
      });
    }

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

interface ReturnWorkflowResult {
  timestamp: string;
  system_status: SystemStatus;
  barcode_management: BarcodeManagement;
  giveout_processing: GiveoutProcessingResult;
  analytics: ReturnAnalytics;
  recommendations: Recommendation[];
}

interface BarcodeManagement {
  current_barcode: string;
  expiry_date: string;
  days_until_expiry: number;
  requires_update: boolean;
  update_performed: boolean;
  update_details: BarcodeUpdate | null;
}

interface ReturnAnalytics {
  period: string;
  total_returns: number;
  total_amount: number;
  currency: string;
  trend_analysis: TrendAnalysis;
  reason_analysis: ReasonAnalysis;
  key_metrics: ReturnMetrics;
}

SmartBarcodeManager - Умный менеджер штрихкодов

class SmartBarcodeManager {
  private readonly warningThresholdDays = 7;
  private readonly criticalThresholdDays = 3;

  constructor(private api: ReturnApi) {}

  async maintainBarcodeHealth(): Promise<BarcodeHealthReport> {
    // Проверка текущего состояния штрихкода
    const currentStatus = await this.checkBarcodeStatus();
    
    // Автоматическое обновление при необходимости
    const updateResult = await this.performAutomaticUpdate(currentStatus);
    
    // Генерация и сохранение файлов штрихкодов
    const fileGeneration = await this.generateBarcodeFiles();
    
    // Планирование следующего обслуживания
    const maintenanceSchedule = this.planNextMaintenance(updateResult?.new_expiry || currentStatus.expiry_date);

    return {
      current_status: currentStatus,
      update_result: updateResult,
      file_generation: fileGeneration,
      maintenance_schedule: maintenanceSchedule,
      health_score: this.calculateHealthScore(currentStatus, updateResult)
    };
  }

  private async checkBarcodeStatus(): Promise<BarcodeStatus> {
    const barcodeInfo = await this.api.getGiveoutBarcode();
    const expiryDate = new Date(barcodeInfo.expiry_date);
    const now = new Date();
    const daysUntilExpiry = Math.ceil((expiryDate.getTime() - now.getTime()) / (1000 * 60 * 60 * 24));
    
    let status: 'HEALTHY' | 'WARNING' | 'CRITICAL' | 'EXPIRED';
    
    if (daysUntilExpiry < 0) {
      status = 'EXPIRED';
    } else if (daysUntilExpiry <= this.criticalThresholdDays) {
      status = 'CRITICAL';
    } else if (daysUntilExpiry <= this.warningThresholdDays) {
      status = 'WARNING';
    } else {
      status = 'HEALTHY';
    }

    return {
      barcode: barcodeInfo.barcode,
      expiry_date: barcodeInfo.expiry_date,
      days_until_expiry: daysUntilExpiry,
      status,
      requires_immediate_action: status === 'EXPIRED' || status === 'CRITICAL'
    };
  }

  private async performAutomaticUpdate(status: BarcodeStatus): Promise<BarcodeUpdateResult | null> {
    if (!status.requires_immediate_action && status.days_until_expiry > this.warningThresholdDays) {
      return null;
    }

    console.log(`🔄 Автоматическое обновление штрихкода (статус: ${status.status})`);
    
    try {
      const updateResult = await this.api.resetGiveoutBarcode();
      
      console.log(`✅ Штрихкод успешно обновлен: ${updateResult.barcode}`);
      
      return {
        success: true,
        old_barcode: status.barcode,
        new_barcode: updateResult.barcode,
        new_expiry: updateResult.expiry_date,
        updated_at: new Date().toISOString(),
        png_data: updateResult.png_data
      };
      
    } catch (error) {
      console.error(`❌ Ошибка обновления штрихкода:`, error);
      
      return {
        success: false,
        old_barcode: status.barcode,
        error_message: error.message,
        updated_at: new Date().toISOString()
      };
    }
  }

  private async generateBarcodeFiles(): Promise<FileGenerationResult> {
    const results: FileGenerationResult = {
      pdf_generated: false,
      png_generated: false,
      files: []
    };

    try {
      // Генерация PDF версии
      const pdfBarcode = await this.api.getGiveoutPDF();
      if (pdfBarcode.pdf_data) {
        results.pdf_generated = true;
        results.files.push({
          type: 'PDF',
          name: pdfBarcode.file_name,
          size: Buffer.from(pdfBarcode.pdf_data, 'base64').length,
          data: pdfBarcode.pdf_data
        });
        console.log(`📄 PDF штрихкод сгенерирован: ${pdfBarcode.file_name}`);
      }

      // Генерация PNG версии
      const pngBarcode = await this.api.getGiveoutPNG();
      if (pngBarcode.png_data) {
        results.png_generated = true;
        results.files.push({
          type: 'PNG',
          name: pngBarcode.file_name,
          size: Buffer.from(pngBarcode.png_data, 'base64').length,
          data: pngBarcode.png_data
        });
        console.log(`🖼️ PNG штрихкод сгенерирован: ${pngBarcode.file_name}`);
      }

      results.success = results.pdf_generated && results.png_generated;

    } catch (error) {
      console.error(`❌ Ошибка генерации файлов штрихкода:`, error);
      results.success = false;
      results.error_message = error.message;
    }

    return results;
  }

  private planNextMaintenance(expiryDate: string): MaintenanceSchedule {
    const expiry = new Date(expiryDate);
    const warningDate = new Date(expiry.getTime() - this.warningThresholdDays * 24 * 60 * 60 * 1000);
    const criticalDate = new Date(expiry.getTime() - this.criticalThresholdDays * 24 * 60 * 60 * 1000);

    return {
      next_check_date: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(), // ежедневная проверка
      warning_date: warningDate.toISOString(),
      critical_date: criticalDate.toISOString(),
      expiry_date: expiryDate,
      automated_update_enabled: true
    };
  }

  private calculateHealthScore(status: BarcodeStatus, updateResult: BarcodeUpdateResult | null): number {
    let score = 100;

    // Снижение за близость к истечению
    if (status.days_until_expiry < this.criticalThresholdDays) {
      score -= 40;
    } else if (status.days_until_expiry < this.warningThresholdDays) {
      score -= 20;
    }

    // Бонус за успешное обновление
    if (updateResult?.success) {
      score = Math.min(100, score + 20);
    }

    // Штраф за неудачное обновление
    if (updateResult && !updateResult.success) {
      score -= 30;
    }

    return Math.max(0, score);
  }
}

interface BarcodeHealthReport {
  current_status: BarcodeStatus;
  update_result: BarcodeUpdateResult | null;
  file_generation: FileGenerationResult;
  maintenance_schedule: MaintenanceSchedule;
  health_score: number;
}

interface BarcodeStatus {
  barcode: string;
  expiry_date: string;
  days_until_expiry: number;
  status: 'HEALTHY' | 'WARNING' | 'CRITICAL' | 'EXPIRED';
  requires_immediate_action: boolean;
}

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

try {
  const giveoutInfo = await returnApi.getGiveoutInfo({
    giveout_id: 12345
  });
  
  console.log("Информация о возвратной отгрузке получена");
} catch (error) {
  if (error.response?.status === 404) {
    console.error("Возвратная отгрузка не найдена");
  } else if (error.response?.status === 403) {
    console.error("Недостаточно прав для доступа к возвратам");
  } else if (error.response?.status === 400) {
    console.error("Некорректные параметры запроса:", error.response.data);
  } else {
    console.error("Неожиданная ошибка:", error.message);
  }
}

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

Автоматизация управления штрихкодами

// Планировщик обслуживания штрихкодов
class BarcodeMaintenanceScheduler {
  private maintenanceJob: NodeJS.Timeout | null = null;

  startAutomaticMaintenance(): void {
    // Ежедневная проверка в 9:00
    this.maintenanceJob = setInterval(async () => {
      const now = new Date();
      if (now.getHours() === 9 && now.getMinutes() === 0) {
        await this.performDailyMaintenance();
      }
    }, 60 * 1000); // проверяем каждую минуту
  }

  private async performDailyMaintenance(): Promise<void> {
    console.log("🕘 Запуск ежедневного обслуживания штрихкодов...");
    
    const manager = new SmartBarcodeManager(returnApi);
    const healthReport = await manager.maintainBarcodeHealth();
    
    if (healthReport.health_score < 80) {
      console.log(`⚠️ Низкий показатель здоровья штрихкода: ${healthReport.health_score}`);
      // Отправить уведомление администратору
    }
  }

  stopAutomaticMaintenance(): void {
    if (this.maintenanceJob) {
      clearInterval(this.maintenanceJob);
      this.maintenanceJob = null;
      console.log("⏹️ Автоматическое обслуживание остановлено");
    }
  }
}

Мониторинг производительности возвратов

// Система мониторинга KPI возвратов
class ReturnKPIMonitor {
  async generateKPIDashboard(): Promise<ReturnKPIDashboard> {
    const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);
    const fbsInfo = await returnApi.getReturnsCompanyFbsInfo({
      date_from: thirtyDaysAgo.toISOString(),
      date_to: new Date().toISOString()
    });

    return {
      return_rate: this.calculateReturnRate(fbsInfo),
      avg_return_value: this.calculateAverageReturnValue(fbsInfo),
      processing_time: await this.calculateProcessingTime(),
      top_return_reasons: this.getTopReturnReasons(fbsInfo),
      monthly_trend: this.calculateMonthlyTrend(fbsInfo),
      quality_score: this.calculateQualityScore(fbsInfo)
    };
  }

  private calculateReturnRate(fbsInfo: any): number {
    // Примерный расчет на основе средних показателей
    return fbsInfo.total_returns / 1000; // предполагаемые продажи
  }
}

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

  • FBS Compatibility: Штрихкоды работают только для схемы FBS
  • Barcode Expiry: Штрихкоды имеют ограниченный срок действия
  • File Formats: Поддерживаются форматы PDF и PNG для штрихкодов
  • Rate Limiting: API поддерживает до 100 запросов в минуту
  • Data Retention: Информация о возвратах хранится до 1 года
  • Real-time Updates: Статусы возвратных отгрузок обновляются в реальном времени
  • Integration Support: API интегрируется с системами логистики и учета
  • Security: Штрихкоды содержат зашифрованную информацию для безопасности