Skip to content

Reports Module Guide

Полное руководство по использованию модуля отчётов (sdk.reports) для получения данных о складах, поставках, остатках и продажах.

Overview

Модуль Reports предоставляет доступ к операционным отчётам:

  • Остатки на складах (Warehouse Remains)
  • Платное хранение (Paid Storage)
  • Отчёт о приёмке (Acceptance Report)
  • Поставки (Incomes)
  • Остатки товаров (Stocks)
  • Продажи (Sales)
  • Заказы (Orders)

Quick Start

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

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

// Получить остатки на складах
const stocks = await sdk.reports.getStocks('2024-12-01');

// Получить продажи за период
const sales = await sdk.reports.getSales('2024-12-01', 0);

1. Остатки на складах (Warehouse Remains)

Асинхронный отчёт об остатках товаров на всех складах WB.

Создание задания

typescript
// Шаг 1: Создать задание на формирование отчёта
const task = await sdk.reports.warehouseRemains({
  groupByBrand: false,
  groupBySubject: false,
  groupBySa: false,
  groupByNm: true  // Группировка по nm_id
});

const taskId = task.data.taskId;
console.log(`Task created: ${taskId}`);

Проверка статуса и скачивание

typescript
async function getWarehouseRemains(sdk: WildberriesSDK) {
  // Создать задание
  const task = await sdk.reports.warehouseRemains({ groupByNm: true });
  const taskId = task.data.taskId;

  // Ожидать выполнения
  let status = 'processing';
  let attempts = 0;
  const maxAttempts = 60;

  while (status !== 'done' && attempts < maxAttempts) {
    await delay(5000); // 5 сек между проверками
    const statusResp = await sdk.reports.getWarehouseRemainsReportStatus(taskId);
    status = statusResp.data.status;

    if (status === 'canceled' || status === 'purged') {
      throw new Error(`Task failed: ${status}`);
    }
    attempts++;
  }

  if (status !== 'done') {
    throw new Error('Task timeout');
  }

  // Скачать отчёт
  const report = await sdk.reports.downloadWarehouseRemainsReport(taskId);
  return report;
}

const delay = (ms: number) => new Promise(r => setTimeout(r, ms));

Структура ответа

typescript
interface WarehouseRemainsItem {
  lastChangeDate: string;     // Дата последнего изменения
  warehouseName: string;      // Название склада
  supplierArticle: string;    // Артикул продавца
  nmId: number;               // Артикул WB
  barcode: string;            // Баркод
  category: string;           // Категория
  subject: string;            // Предмет
  brand: string;              // Бренд
  techSize: string;           // Размер
  Price: number;              // Цена
  Discount: number;           // Скидка
  isSupply: boolean;          // В поставке
  isRealization: boolean;     // В реализации
  SCCode: string;             // Код склада
  quantityWarehousesFull: number; // Общее количество на складах
  quantityNotInOrders: number;    // Не в заказах
  inWayToClient: number;      // В пути к клиенту
  inWayFromClient: number;    // В пути от клиента
  daysOnSite: number;         // Дней на сайте
}

Пример: Анализ остатков по складам

typescript
async function analyzeWarehouseStock(sdk: WildberriesSDK) {
  const report = await getWarehouseRemains(sdk);

  // Группировка по складам
  const byWarehouse = new Map<string, { count: number; value: number }>();

  for (const item of report) {
    const wh = item.warehouseName;
    const current = byWarehouse.get(wh) || { count: 0, value: 0 };

    current.count += item.quantityWarehousesFull || 0;
    current.value += (item.quantityWarehousesFull || 0) * (item.Price || 0);

    byWarehouse.set(wh, current);
  }

  console.log('=== Остатки по складам ===');
  for (const [warehouse, data] of byWarehouse) {
    console.log(`${warehouse}: ${data.count} шт, ${data.value.toFixed(2)} ₽`);
  }

  // Товары с низким остатком
  const lowStock = report.filter(item =>
    item.quantityNotInOrders > 0 &&
    item.quantityNotInOrders < 10
  );

  console.log(`\nТоваров с низким остатком (<10): ${lowStock.length}`);
}

2. Платное хранение (Paid Storage)

Детализированный отчёт о расходах на хранение товаров.

Асинхронный процесс

typescript
async function getPaidStorageReport(
  sdk: WildberriesSDK,
  dateFrom: string,
  dateTo: string
) {
  // Шаг 1: Создать задание (max 8 дней за запрос!)
  const task = await sdk.reports.paidStorage({ dateFrom, dateTo });
  const taskId = task.data.taskId;

  // Шаг 2: Проверять статус
  let status = 'processing';
  while (status !== 'done') {
    await delay(5000);
    const statusResp = await sdk.reports.getTasksStatu3(taskId);
    status = statusResp.data.status;

    if (status === 'canceled' || status === 'purged') {
      throw new Error(`Task failed: ${status}`);
    }
  }

  // Шаг 3: Скачать
  return await sdk.reports.getTasksDownload3(taskId);
}

const delay = (ms: number) => new Promise(r => setTimeout(r, ms));

Структура данных

typescript
interface PaidStorageItem {
  date: string;              // Дата расчёта
  warehouse: string;         // Склад
  warehouseCoef: number;     // Коэффициент склада
  nmId: number;              // Артикул WB
  barcode: string;           // Баркод
  subject: string;           // Предмет
  brand: string;             // Бренд
  vendorCode: string;        // Артикул продавца
  chrtId: number;            // Код размера
  size: string;              // Размер
  volume: number;            // Объём (л)
  calcType: string;          // Способ расчёта
  warehousePrice: number;    // Сумма хранения ₽
  barcodesCount: number;     // Количество единиц
  loyaltyDiscount: number;   // Скидка лояльности ₽
}

Пример: Топ товаров по хранению

typescript
async function getTopStorageProducts(
  sdk: WildberriesSDK,
  dateFrom: string,
  dateTo: string
) {
  const data = await getPaidStorageReport(sdk, dateFrom, dateTo);

  // Агрегация по товарам
  const byProduct = new Map<number, { subject: string; amount: number }>();

  for (const item of data) {
    const nmId = item.nmId;
    const current = byProduct.get(nmId) || { subject: item.subject, amount: 0 };
    current.amount += item.warehousePrice || 0;
    byProduct.set(nmId, current);
  }

  // Сортировка по убыванию
  const sorted = Array.from(byProduct.entries())
    .sort((a, b) => b[1].amount - a[1].amount)
    .slice(0, 20);

  console.log('=== Топ-20 по расходам на хранение ===');
  sorted.forEach(([nmId, data], i) => {
    console.log(`${i + 1}. nm_id=${nmId} | ${data.subject} | ${data.amount.toFixed(2)} ₽`);
  });

  // Итого
  const total = data.reduce((sum, item) => sum + (item.warehousePrice || 0), 0);
  console.log(`\nИТОГО за период: ${total.toFixed(2)} ₽`);
}

Ограничения

ПараметрЗначение
Max период8 дней
Rate limit создания1 req/min
Rate limit статуса1 req/5sec
Время обработки5-60 сек

3. Отчёт о приёмке (Acceptance Report)

Данные о приёмке товаров на склады WB.

typescript
async function getAcceptanceReport(
  sdk: WildberriesSDK,
  dateFrom: string,
  dateTo: string
) {
  // Создать задание
  const task = await sdk.reports.acceptanceReport({ dateFrom, dateTo });
  const taskId = task.data.taskId;

  // Ожидать выполнения
  let status = 'processing';
  while (status !== 'done') {
    await delay(5000);
    const statusResp = await sdk.reports.getAcceptanceReportStatus(taskId);
    status = statusResp.data.status;
  }

  // Скачать
  return await sdk.reports.downloadAcceptanceReport(taskId);
}

4. Поставки (Incomes)

Синхронный отчёт о поступлениях товаров.

typescript
// Получить поставки за период
const incomes = await sdk.reports.getIncomes('2024-12-01');

console.log(`Поставок: ${incomes.length}`);

for (const income of incomes) {
  console.log(`Поставка #${income.incomeId}:`);
  console.log(`  Дата: ${income.date}`);
  console.log(`  Склад: ${income.warehouseName}`);
  console.log(`  Товар: ${income.nmId} - ${income.supplierArticle}`);
  console.log(`  Количество: ${income.quantity}`);
}

Структура Incomes

typescript
interface IncomeItem {
  incomeId: number;          // ID поставки
  number: string;            // Номер УПД
  date: string;              // Дата поставки
  lastChangeDate: string;    // Дата обновления
  supplierArticle: string;   // Артикул продавца
  techSize: string;          // Размер
  barcode: string;           // Баркод
  quantity: number;          // Количество
  totalPrice: number;        // Сумма
  dateClose: string;         // Дата закрытия
  warehouseName: string;     // Склад
  nmId: number;              // Артикул WB
  status: string;            // Статус
}

5. Остатки товаров (Stocks)

Актуальные остатки на складах.

typescript
// Получить остатки на дату
const stocks = await sdk.reports.getStocks('2024-12-01');

// Анализ
const totalQuantity = stocks.reduce((sum, s) => sum + (s.quantity || 0), 0);
const totalValue = stocks.reduce((sum, s) =>
  sum + ((s.quantity || 0) * (s.Price || 0)), 0
);

console.log(`Всего товаров: ${stocks.length}`);
console.log(`Общее количество: ${totalQuantity}`);
console.log(`Общая стоимость: ${totalValue.toFixed(2)} ₽`);

Структура Stocks

typescript
interface StockItem {
  lastChangeDate: string;     // Дата изменения
  warehouseName: string;      // Склад
  supplierArticle: string;    // Артикул продавца
  nmId: number;               // Артикул WB
  barcode: string;            // Баркод
  quantity: number;           // Количество
  inWayToClient: number;      // В пути к клиенту
  inWayFromClient: number;    // В пути от клиента
  quantityFull: number;       // Полное количество
  category: string;           // Категория
  subject: string;            // Предмет
  brand: string;              // Бренд
  techSize: string;           // Размер
  Price: number;              // Цена
  Discount: number;           // Скидка
  isSupply: boolean;          // В поставке
  isRealization: boolean;     // В реализации
  SCCode: string;             // Код ШК
}

6. Продажи (Sales)

Отчёт о продажах.

typescript
// Получить продажи
// flag: 0 - без возвратов, 1 - с возвратами
const sales = await sdk.reports.getSales('2024-12-01', 1);

let totalRevenue = 0;
let salesCount = 0;
let returnsCount = 0;

for (const sale of sales) {
  if (sale.quantity > 0) {
    salesCount++;
    totalRevenue += sale.forPay || sale.finishedPrice || 0;
  } else {
    returnsCount++;
  }
}

console.log(`Продаж: ${salesCount}`);
console.log(`Возвратов: ${returnsCount}`);
console.log(`Выручка: ${totalRevenue.toFixed(2)} ₽`);

Структура Sales

typescript
interface SaleItem {
  date: string;               // Дата продажи
  lastChangeDate: string;     // Дата обновления
  warehouseName: string;      // Склад
  countryName: string;        // Страна
  oblastOkrugName: string;    // Округ
  regionName: string;         // Регион
  supplierArticle: string;    // Артикул продавца
  nmId: number;               // Артикул WB
  barcode: string;            // Баркод
  category: string;           // Категория
  subject: string;            // Предмет
  brand: string;              // Бренд
  techSize: string;           // Размер
  incomeID: number;           // ID поставки
  isSupply: boolean;          // В поставке
  isRealization: boolean;     // В реализации
  totalPrice: number;         // Цена без скидки
  discountPercent: number;    // Скидка %
  spp: number;                // Скидка постоянного покупателя
  paymentSaleAmount: number;  // Оплачено
  forPay: number;             // К перечислению
  finishedPrice: number;      // Цена со скидкой
  priceWithDisc: number;      // Цена со скидкой WB
  saleID: string;             // ID продажи
  orderType: string;          // Тип заказа
  sticker: string;            // Стикер
  gNumber: string;            // Номер заказа
  srid: string;               // ID позиции заказа
  quantity: number;           // Количество (- для возврата)
}

7. Заказы (Orders)

Отчёт о заказах покупателей.

typescript
// Получить заказы
// flag: 0 - без отмен, 1 - с отменами
const orders = await sdk.reports.getOrders('2024-12-01', 0);

console.log(`Всего заказов: ${orders.length}`);

// Группировка по статусам
const byStatus = new Map<string, number>();
for (const order of orders) {
  const status = order.orderType || 'unknown';
  byStatus.set(status, (byStatus.get(status) || 0) + 1);
}

console.log('По статусам:');
for (const [status, count] of byStatus) {
  console.log(`  ${status}: ${count}`);
}

Сводная таблица методов

МетодТипPeriod LimitRate Limit
warehouseRemains()Async-1/min
paidStorage()Async8 дней1/min
acceptanceReport()Async-1/min
getIncomes()Sync365 дней-
getStocks()Sync--
getSales()Sync365 дней-
getOrders()Sync365 дней-

Best Practices

1. Обработка асинхронных отчётов

typescript
async function waitForReport<T>(
  sdk: WildberriesSDK,
  taskId: string,
  statusFn: (id: string) => Promise<{ data: { status: string } }>,
  downloadFn: (id: string) => Promise<T>,
  maxWaitMs: number = 300000 // 5 минут
): Promise<T> {
  const startTime = Date.now();

  while (Date.now() - startTime < maxWaitMs) {
    await delay(5000);
    const { data } = await statusFn(taskId);

    if (data.status === 'done') {
      return await downloadFn(taskId);
    }

    if (data.status === 'canceled' || data.status === 'purged') {
      throw new Error(`Report failed: ${data.status}`);
    }
  }

  throw new Error('Report timeout');
}

2. Разбиение длинных периодов

typescript
function* dateChunks(
  dateFrom: string,
  dateTo: string,
  chunkDays: number
): Generator<{ from: string; to: string }> {
  const start = new Date(dateFrom);
  const end = new Date(dateTo);

  let current = new Date(start);

  while (current <= end) {
    const chunkEnd = new Date(current);
    chunkEnd.setDate(chunkEnd.getDate() + chunkDays - 1);

    yield {
      from: current.toISOString().split('T')[0],
      to: (chunkEnd > end ? end : chunkEnd).toISOString().split('T')[0]
    };

    current.setDate(current.getDate() + chunkDays);
  }
}

// Использование для Paid Storage (max 8 дней)
for (const chunk of dateChunks('2024-12-01', '2024-12-31', 7)) {
  const data = await getPaidStorageReport(sdk, chunk.from, chunk.to);
  // ...обработка
  await delay(61000); // Rate limit
}

Связанные материалы


← Back to Guides

Made with ❤️ for the Wildberries developer community