Всем привет!
Прошлую статью по интеграции с Wildberries я писал в конце 2020 и с тех пор многое поменялось. Работа с API стала чуть проще, однако нюансы взаимодействия с Вайлдберриз никуда не делись. В этой статье я покажу как взаимодействовать с АПИ из 1С и представленную мной обработку можно взять за пример и доработать.
Если вы предприниматель и продавец на Wildberries: в конце статьи есть возможность скачать обработку (платформа 8.3.14 , 8.3.8). В ней есть лишь несколько базовых функций (получение остатков, получение и обновление цен, создание карточки товара и получение списка карточек уже созданных). Для доработки остальных функций можно отдать обработку вашему программисту или написать мне в телеграм, поискав меня по имени.
Если вы программист: Здравствуйте, коллега! В статье расскажу некоторые нюансы схемы данных у WB и дам примеры кода. У Вайлдберриз теперь есть для тестирования запросов Swagger, и по сути главное для интеграции правильно сформировать JSON на стороне 1С. Но будьте аккуратны, тестовой среды больше нет и все запросы сразу попадают в продакшн и в ЛК вашего Заказчика. Не забудьте поставить плюсик статье!
1. Получение токена АПИ и supplierId для запросов.
Токен для АПИ получить легко. В разделе "настройки" в ЛК есть вкладка "доступ к новому API" где этот токен и получается. Токен нужно передавать в каждом запросе в HTTP заголовке Authorization
Как получить supplierId в личном кабинете Wildberries
Проще всего для этого в ЛК открыть список ваших юрлиц в правом верхнем углу (там где в меню Профиль и Настройки), кликнуть правой кнопкой мыши и нажать "просмотреть код" в Chrome.
Там сразу в HTML коде видна метка "FOR" где прописан ваш идентификатор supplierID.
Еще можно написать запрос в поддержку, с просьбой отправить вам supplierID, но они долго отвечают.
Этот самый идентификатор нужен для многих запросов к АПИ, хотя некоторые запросы и без него работают - видимо, есть связь между Токеном доступа к АПИ и supplierID.
2. Получение списка существующих карточек
Чаще всего бывает так, что карточки товара уже созданы вручную или загружены из Ексель, и их надо получить на стороне 1С. Делаем для этого запрос "/card/list" к АПИ. Важно убрать все из параметра filter и поставить limit побольше. Впрочем, я в обработке сделал пример где limit 100 и получение идет в цикле, увеличивая offset. Вот код в функции СписокКарточекВБ:
&НаКлиенте
Функция СобратьЗапросКарточек(Лимит,ОФсет)
СтруктураЗ = Новый Структура;
СтруктураЗ.Вставить("id",1);
СтруктураЗ.Вставить("jsonrpc","2.0");
СтрПарам = Новый Структура;//Params
СтрФильтра = Новый Структура;
СтрФильтра.Вставить("filter",Новый Массив);
СтрФильтра.Вставить("find",Новый Массив);
СтрОрдер = Новый Структура;
СтрОрдер.Вставить("column","");
СтрОрдер.Вставить("order","");
СтрФильтра.Вставить("order",СтрОрдер);
СтрПарам.Вставить("filter",СтрФильтра);
СтрЗапроса = Новый Структура;
СтрЗапроса.Вставить("limit",Лимит);//max 100
СтрЗапроса.Вставить("offset",ОФсет);
СтрПарам.Вставить("query",СтрЗапроса);
СтрПарам.Вставить("isArchive", ЛОЖЬ);
СтрПарам.Вставить("supplierID",Объект.supplierID);
СтрПарам.Вставить("withError", ЛОЖЬ);
СтруктураЗ.Вставить("params",СтрПарам);
Возврат СтруктураЗ;
КонецФункции
&НаКлиенте
Процедура СписокКарточекВБ(Команда)
СтруктураЗ = СобратьЗапросКарточек(100,0);
//json
СериализованнаяСтрока = СтруктураВJson(СтруктураЗ);
ЖсонОтправка = СериализованнаяСтрока;
Адрес = "/card/list";
СоответствиеОтветАПИ = ВыполнитьМетодАПИ(СериализованнаяСтрока,Адрес,"POST");
Если НЕ СоответствиеОтветАПИ=Неопределено Тогда
Рез = СоответствиеОтветАПИ.получить("result");
Курсор = Рез.Получить("cursor");
ВсегоНоменклатурыУВБ = Курсор.Получить("total");
ВыданоСейчас = Курсор.Получить("limit");
Смещение = Курсор.Получить("offset");
КартыМассив = Рез.Получить("cards");
сообщить("найдено артикулов="+ВыданоСейчас+ " шаг=0");
//СОПОСТАВИТЬ с 1С
СопоставитьНом1С(КартыМассив);
Лимит = 100;
счШага = 1;
Пока ВсегоНоменклатурыУВБ > ВыданоСейчас + Смещение Цикл
Смещение = Смещение+100;
счШага = счШага + 1;
//получим остальные страницы
Если ВсегоНоменклатурыУВБ - Смещение < 100 Тогда
СтруктураЗ = СобратьЗапросКарточек(ВсегоНоменклатурыУВБ - Смещение,Смещение);
Иначе
СтруктураЗ = СобратьЗапросКарточек(100,Смещение);
КонецЕсли;
СоответствиеОтветАПИ = ВыполнитьМетодАПИ(СериализованнаяСтрока,Адрес,"POST");
Если НЕ СоответствиеОтветАПИ=Неопределено Тогда
Рез = СоответствиеОтветАПИ.получить("result");
Курсор = Рез.Получить("cursor");
ВыданоСейчас = Курсор.Получить("limit");
Смещение = Курсор.Получить("offset");
КартыМассив = Рез.Получить("cards");
//СОПОСТАВИТЬ с 1С
СопоставитьНом1С(КартыМассив);
Иначе
Сообщить("Неверный код ответа card/list");
КонецЕсли;
сообщить("найдено артикулов="+ВыданоСейчас+ " шаг="+счШага);
КонецЦикла;
Иначе
Сообщить("Неверный код ответа card/list");
//ПоказатьПредупреждение(, "Ошибка соединения с Wildberries");
Возврат;
КонецЕсли;
КонецПроцедуры
На вкладке "служебное" всегда можно посмотреть отправленный и полученный текст JSON
3. Сопоставление данных Вайлдберриз и 1С. Нюансы схемы данных.
У Wildberries есть следующие сущности: Карточка (Card) и ее идентификатор imtId и supplierVendorCode (Артикул Поставщика), Номенклатура (массив nomenclatures в карточке) и ее идентификатор nmId и vendorCode (Артикул Цвета).
Еще у Карточки есть дополнения (addin) где указывается Бренд и ТНВЭД.
А у Номенклатуры есть variations где указывается Размер (идентификатор chrtId) и его Штрихкоды (barcodes). Внутри variations есть еще addin где указывается цена товара (целое число без копеек).
Для примера рассмотрим поиск по штрихкоду, а затем если не нашли, по артикулу номенклатуры (vendorCode) в базе 1С. Вот код:
&НаСервере
Процедура СопоставитьНом1С(КартыМассив)
счч=0;
Для Каждого КартаСоотв Из КартыМассив Цикл
//ид карты, в карте может быть несколько номенклатур
мИдВБ = КартаСоотв.Получить("imtId");//не используется далее
Номенклатуры = КартаСоотв.Получить("nomenclatures");//массив, в массиве соответствия
Для Каждого СоотвНом ИЗ Номенклатуры Цикл
//нужно для обмена остатками
nmId = СоотвНом.Получить("nmId");//ид конкретной номенклатуры
Артикул1С = СоотвНом.Получить("vendorCode");//артикул конкретной номенклатуры
//размеры
//у номенклатуры может быть несколько variations с разными ид chrtId
Вариации = СоотвНом.Получить("variations");
стрРазмеров="";//запишем через точку с запятой размеры
НайденоПОШК = Ложь;
Для Каждого СоотвВар Из Вариации Цикл
Баркод = СоотвВар.Получить("barcode");
Если Баркод=Неопределено Тогда
массивШК = СоотвВар.Получить("barcodes");
Для Каждого шк из массивШК Цикл
Баркод = шк;
КонецЦикла;
КонецЕсли;
chrtId = СоотвВар.Получить("chrtId");//число
ЭддИны = СоотвВар.Получить("addin");
//Размер = Эддины.Получить(1).Получить("params").Получить(0).Получить("value");
//chrtid = СтрЗаменить(Строка(chrtId)," ","") + "_" + СокрЛП(Размер);
стрРазмеров = стрРазмеров + Строка(chrtId) + ";" ;
//поищем номенклатуру по ШК
Если НЕ ПустаяСтрока(Баркод) Тогда
Номенклатура1С = ПоискПоШК(Баркод);
КонецЕсли;
КонецЦикла;
Если НайденоПОШК = Ложь Тогда
//поищем номенклатуру
Номенклатура1С = Справочники.Номенклатура.НайтиПоРеквизиту("Артикул",Артикул1С);
КонецЕсли;
Если НЕ Номенклатура1С.Пустая() Тогда
//запишем доп свойства
//ЗаписатьДопСвойство(Номенклатура1С,"wb_chrtid",стрРазмеров);
ЗаписатьДопСвойство(Номенклатура1С,"wb_nmid",nmId);
счн=счн+1;
Иначе
Сообщить("артикул не найден <"+Артикул1С+">");
КонецЕсли;
КонецЦикла;
счч = счч+1;
КонецЦикла;
Сообщить("Обработано Карт "+счч);
КонецПроцедуры
Функция ПоискПОШК(Баркод)
//{{КОНСТРУКТОР_ЗАПРОСА_С_ОБРАБОТКОЙ_РЕЗУЛЬТАТА
// Данный фрагмент построен конструктором.
// При повторном использовании конструктора, внесенные вручную изменения будут утеряны!!!
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ШтрихкодыНоменклатуры.Штрихкод КАК Штрихкод,
| ШтрихкодыНоменклатуры.Номенклатура КАК Номенклатура,
| ШтрихкодыНоменклатуры.Характеристика КАК Характеристика,
| ШтрихкодыНоменклатуры.Упаковка КАК Упаковка
|ИЗ
| РегистрСведений.ШтрихкодыНоменклатуры КАК ШтрихкодыНоменклатуры
|ГДЕ
| ШтрихкодыНоменклатуры.Штрихкод = &Штрихкод";
Запрос.УстановитьПараметр("Штрихкод", Баркод);
РезультатЗапроса = Запрос.Выполнить();
ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать();
Пока ВыборкаДетальныеЗаписи.Следующий() Цикл
Возврат ВыборкаДетальныеЗаписи.Номенклатура;
КонецЦикла;
//}}КОНСТРУКТОР_ЗАПРОСА_С_ОБРАБОТКОЙ_РЕЗУЛЬТАТА
Возврат Справочники.Номенклатура.ПустаяСсылка();
КонецФункции
Запишем идентификаторы nmId в регистр Дополнительные сведения, чтобы связать с номенклатурой 1С. Вот код:
&НаСервере
Процедура ЗаписатьДопСвойство(НоменклатураСсылка,ИмяСвойства,ЗначениеСвойства)
ПВХСвойство = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.НайтиПоНаименованию(ИмяСвойства,Истина);
Если ПВХСвойство.Пустая() Тогда
ПВХ = ПланыВидовХарактеристик.ДополнительныеРеквизитыИСведения.СоздатьЭлемент();
ПВХ.Наименование = ИмяСвойства;
ПВХ.Заголовок = ИмяСвойства;
ПВХ.ТипЗначения = Типзнч(ЗначениеСвойства);
ПВХ.Записать();
ПВХСвойство = ПВХ.Ссылка;
КонецЕсли;
Набор = РегистрыСведений.ДополнительныеСведения.СоздатьМенеджерЗаписи();
Набор.Период = ТекущаяДата();
Набор.Объект = НоменклатураСсылка;
Набор.Свойство = ПВХСвойство;
Набор.Значение = ЗначениеСвойства;
Набор.Записать(Истина);
КонецПроцедуры
Для более глубокой доработки механизма поиска Номенклатуры в 1С можно скачать обработку и дорабатывать как вам угодно.
4. Получение цен
Делаем GET Запрос на ресурс АПИ "/public/api/v1/info?quantity=0". В запросе нам возвращаются идентификаторы номенклатуры nmId, поэтому важно на шаге 3 понять как эти nmId сопоставлены с вашей базой 1С. Я в своей обработке предположил, что сначала получаются Карточки, и поэтому делаю поиск по табличке которая там есть. Цены беру в регистре ЦеныНоменклатуры. Поля discount и promoCode я не использую в расчете, просто храню и отображаю их в таблице в обработке.
5. Изменение цен
Для примера я взял УТ 11 на платформе 8.3.18, где цены лежат в регистре ЦеныНоменклатуры. Для своей конфигурации можно модифицировать запрос цен нужным образом. Отправляется POST запрос на ресурс "/public/api/v1/prices". Из таблички на форме отправляю те строки, где есть цена 1С и стоит галочка "Обновить цену". Чтобы не отправлять HTTP запрос по каждой строке с ценами, собираю в JSON запрос массив номенклатур.
Пример кода:
&НаСервере
Процедура ЗадатьЦеныИз1СНаСервере()
//задаем цены в ВБ из 1С - Таблички на форме
Адрес = "/public/api/v1/prices";
МассивЗапроса = Новый Массив;
Для Каждого СтрТ из Объект.Товары Цикл
Если СтрТ.ОбновитьЦену Тогда
СтруктураЦены = Новый Структура;
СтруктураЦены.Вставить("nmId",СтрТ.nmID);//число!
СтруктураЦены.Вставить("price",СтрТ.Цена1С);
КонецЕсли;
КонецЦикла;
Жсон = СтруктураВJson(МассивЗапроса);
ОтветАПИ = ВыполнитьМетодАПИ(Жсон,Адрес,"POST");
КонецПроцедуры
6. Получение и обновление остатков
Для получения остатков - делаем GET Запрос на ресурс АПИ "/api/v2/stocks?skip=0&take=1000". Кстати, по примеру видно что программисты в Вайлдберриз фанаты Властелина колец. Их пример:
Для обновления же остатков нужен POST запрос на ресурс "/api/v2/stocks" и там еще надо знать warehouseId. Где же его взять? Только из предыдущего запроса по получению остатков, либо сделать GET запрос к АПИ "/api/v2/warehouses".
7. Получение списка заказов
Делаем GET Запрос на ресурс АПИ "/api/v2/orders?date_start=2022-01-01T13:00:00Z&status=2&take=10&skip=0". Тут главное форматировать дату по RFC3339 в формате "yyyy-MM-ddTHH:mm:ssZ". Пример, который должен получиться из ответа АПИ:
Для обработки 1С я предполагаю, что структура заказа такая же, как в примере у Вайлдберриз. Нюанс обработки заказов в том, что Вайлдберриз выдают очередные левые идентификаторы в описании OrdersAPIResponse вместо привычного уже идентификатора номенклатуры nmID выдают идентификатор размера chrtId, ну и штрихкод тоже есть.
Как реализовать поиск в 1С заказов по данным штрихкода и chrtId оставляю на ваше усмотрение. Лично я решил в процессе получения карточек (вкладка 2 Карточки ВБ) записывать для Номенклатуры все ее ид размеров chrtId в регистр ДопСведений. И, таким образом, есть сопоставление chrtId и Номенклатуры 1С. Можно еще искать по Штрихкоду в регистре штрихкодов, но у ВБ могут быть свои штрихкоды, отличные от 1С.
Скриншоты интерфейса обработки:
По просьбам в комментариях добавил скриншоты интерфейса обработки.
8. Остальные функции
Думаю, что вышеприведенных примеров достаточно, чтобы дальше доработать остальное самостоятельно. Если есть вопросы, можно написать мне в телеграм, поискав меня по имени. Не забудьте поставить плюсик статье!
Также в конце статьи приведу список ошибок АПИ, с которыми я сталкивался в процессе тестирования, для того чтобы их могли найти в поисковике и прочитать эту статью. Обычно эти ошибки были следствием некорректного JSON, хотя иногда я был уверен что и баги в АПИ тоже есть.
Ошибки:
list of cards: (BadValue) $and must be an array
Следующие комбинации характеристик \"Артикул поставщика\" и \"Бренд\" уже используются
validation error, errorValidation
failed get card by imt id: unexpected end of JSON input cause: map[Внутренняя ошибка:None] card.cardByImtID
Характеристика: tech_size. Ошибка: Артикул с таким размером уже создан! Удалите позицию из спецификации и загрузите на нее баркод в разделе Загрузки - Загрузки баркодов
update card cause: map[err:get origin card: Internal Server Error]
Следующие комбинации характеристик \"Артикул поставщика\", \"Артикул цвета\" и \"Бренд\" уже используются
batch create error cause: map[err:Нет карточек для добавления. Карточки товара с штрихкодом уже созданы
Такая комбинация бренда и артикула поставщика уже используется
Характеристика: sa_nm. Ошибка: Артикул принадлежит другому поставщику
Ошибка: Артикул удален
Категория из выбранной карточки товара запрещена к реализации, возможность работать с такой карточка закрыта
Удаление номенклатуры невозможно, ввиду наличия поставок по ней
Благодарю за прочтение!
Готовое решение
Интеграция 1С с Маркетплейсами
Обеспечьте бесперебойную работу 1С с Ozon, Wildberries, Яндекс.Маркет и другими популярными маркетплейсами, оптимизируя бизнес-процессы, снижая риски и обеспечивая точность данных