Created
February 26, 2026 23:37
-
-
Save daniil-burdygin/533047d43f0f720fe930a9f6269ed74c to your computer and use it in GitHub Desktop.
WB API → Google Sheets: загрузка отчёта о продажах по реализации. Подробнее — в статье на dev.wildberries.ru
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // --------------------------------------------------------------------------- | |
| // WB API → Google Sheets — ознакомительная интеграция | |
| // Эндпоинт: GET /api/v5/supplier/reportDetailByPeriod | |
| // Хост: https://statistics-api.wildberries.ru | |
| // --------------------------------------------------------------------------- | |
| /** Добавляет меню «WB API» при открытии таблицы */ | |
| function onOpen() { | |
| SpreadsheetApp.getUi() | |
| .createMenu('WB API') | |
| .addItem('Ввести токен', 'setApiToken') | |
| .addItem('Загрузить отчёт', 'loadReport') | |
| .addToUi(); | |
| } | |
| // ---- Управление токеном ---------------------------------------------------- | |
| /** Запрашивает токен через диалог и сохраняет в PropertiesService */ | |
| function setApiToken() { | |
| const ui = SpreadsheetApp.getUi(); | |
| const result = ui.prompt( | |
| 'WB API — Токен', | |
| 'Вставьте ваш API-токен (категория «Статистика»):', | |
| ui.ButtonSet.OK_CANCEL | |
| ); | |
| if (result.getSelectedButton() !== ui.Button.OK) return; | |
| const token = result.getResponseText().trim(); | |
| if (!token) { | |
| ui.alert('Токен не может быть пустым.'); | |
| return; | |
| } | |
| PropertiesService.getUserProperties().setProperty('WB_API_TOKEN', token); | |
| ui.alert('Токен сохранён.'); | |
| } | |
| /** Возвращает сохранённый токен или null */ | |
| function getApiToken() { | |
| return PropertiesService.getUserProperties().getProperty('WB_API_TOKEN'); | |
| } | |
| // ---- Запрос к API ---------------------------------------------------------- | |
| /** | |
| * Загружает «Отчёт о продажах по реализации» за указанный период. | |
| * Обрабатывает пагинацию через параметр rrdid. | |
| */ | |
| function fetchReportData(token, dateFrom, dateTo) { | |
| const baseUrl = | |
| 'https://statistics-api.wildberries.ru/api/v5/supplier/reportDetailByPeriod'; | |
| const allRows = []; | |
| let rrdid = 0; | |
| while (true) { | |
| const url = | |
| baseUrl + | |
| '?dateFrom=' + encodeURIComponent(dateFrom) + | |
| '&dateTo=' + encodeURIComponent(dateTo) + | |
| '&limit=100000' + | |
| '&rrdid=' + rrdid; | |
| const options = { | |
| method: 'get', | |
| headers: { Authorization: token }, | |
| muteHttpExceptions: true, | |
| }; | |
| const response = UrlFetchApp.fetch(url, options); | |
| const code = response.getResponseCode(); | |
| if (code === 204) break; | |
| if (code === 401) { | |
| throw new Error('Ошибка 401: неверный или просроченный токен.'); | |
| } | |
| if (code === 429) { | |
| throw new Error( | |
| 'Ошибка 429: превышен лимит запросов. Подождите минуту и попробуйте снова.' | |
| ); | |
| } | |
| if (code !== 200) { | |
| throw new Error('Ошибка API: HTTP ' + code); | |
| } | |
| const data = JSON.parse(response.getContentText()); | |
| if (!Array.isArray(data) || data.length === 0) break; | |
| allRows.push(...data); | |
| const lastRow = data[data.length - 1]; | |
| rrdid = lastRow['rrd_id'] ?? 0; | |
| } | |
| return allRows; | |
| } | |
| // ---- Запись в таблицу ------------------------------------------------------ | |
| /** Записывает массив объектов на лист «Продажи» */ | |
| function writeToSheet(rows) { | |
| const ss = SpreadsheetApp.getActiveSpreadsheet(); | |
| let sheet = ss.getSheetByName('Продажи'); | |
| if (sheet) { | |
| sheet.clear(); | |
| } else { | |
| sheet = ss.insertSheet('Продажи'); | |
| } | |
| if (rows.length === 0) return; | |
| const headers = Object.keys(rows[0]); | |
| sheet.appendRow(headers); | |
| const data = rows.map((row) => headers.map((h) => row[h] ?? '')); | |
| if (data.length > 0) { | |
| sheet | |
| .getRange(2, 1, data.length, headers.length) | |
| .setValues(data); | |
| } | |
| } | |
| // ---- Главная функция ------------------------------------------------------- | |
| /** Точка входа: загружает отчёт и записывает на лист */ | |
| function loadReport() { | |
| const ui = SpreadsheetApp.getUi(); | |
| const token = getApiToken(); | |
| if (!token) { | |
| ui.alert('Сначала введите токен через меню «WB API → Ввести токен».'); | |
| return; | |
| } | |
| const now = new Date(); | |
| const weekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000); | |
| const dateFrom = formatDate(weekAgo); | |
| const dateTo = formatDate(now); | |
| try { | |
| ui.alert('Загрузка данных за период ' + dateFrom + ' — ' + dateTo + '…'); | |
| const rows = fetchReportData(token, dateFrom, dateTo); | |
| if (rows.length === 0) { | |
| ui.alert( | |
| 'Нет данных за выбранный период. Проверьте даты или попробуйте другой диапазон.' | |
| ); | |
| return; | |
| } | |
| writeToSheet(rows); | |
| ui.alert( | |
| 'Готово! Загружено строк: ' + | |
| rows.length + | |
| '. Данные на листе «Продажи».' | |
| ); | |
| } catch (e) { | |
| const msg = e instanceof Error ? e.message : String(e); | |
| ui.alert('Ошибка: ' + msg); | |
| } | |
| } | |
| // ---- Утилиты --------------------------------------------------------------- | |
| /** Форматирует Date в строку YYYY-MM-DD */ | |
| function formatDate(d) { | |
| const yyyy = d.getFullYear(); | |
| const mm = String(d.getMonth() + 1).padStart(2, '0'); | |
| const dd = String(d.getDate()).padStart(2, '0'); | |
| return yyyy + '-' + mm + '-' + dd; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment