Цены в МойСклад — это не одно поле, а структура: у товара может быть несколько типов цен (розница, опт, закупка), каждая со своей валютой и значением в копейках. Через JSON API 1.2 цены читаются из массива salePrices и обновляются с обязательной привязкой к типу цены из настроек компании.
Как устроена модель цен в МойСклад
У номенклатуры (товар, модификация, услуга, комплект) цены разделены на несколько сущностей:
salePrices— массив цен продажи; в каждом элементе значение, валюта и ссылка на тип цены;buyPrice— закупочная цена (одно значение с валютой);minPrice— минимальная цена продажи.
Все денежные значения передаются в копейках целым числом: цена 199,90 ₽ — это 19990. Это ключевой момент: передача рублей вместо копеек — самая частая ошибка при обновлении прайса.
https://api.moysklad.ru/api/remap/1.2. Товары читаются через /entity/product, модификации — через /entity/variant.Типы цен и где их взять
Тип цены (priceType) — это справочник уровня аккаунта: «Цена продажи», «Оптовая», «Розничная» и любые пользовательские. Прежде чем обновлять salePrices, нужно получить метаданные типов цен:
GET /api/remap/1.2/context/companysettings/pricetype
Ответ — массив типов с их id, name и meta. Именно meta типа цены вы подставляете в каждый элемент salePrices при записи. Получить один тип по id можно так:
GET /api/remap/1.2/context/companysettings/pricetype/{id}
Чтение цен товара
Запрос товара возвращает цены прямо в теле:
GET /api/remap/1.2/entity/product/{id}
Фрагмент ответа:
"salePrices": [{ "value": 19990, "currency": {"meta": {...}}, "priceType": {"meta": {...}, "name": "Цена продажи"} }], "buyPrice": { "value": 12000, "currency": {...} }, "minPrice": { "value": 15000, "currency": {...} }
Чтобы не тянуть лишние поля при выгрузке прайса, ограничьте выборку и используйте пагинацию:
GET /entity/product?limit=1000&offset=0— постранично;- для актуальных цен закупки и остатков удобнее отчёты, но сами цены продажи лежат в карточке товара.
Обновление цены: PUT одного товара
Цену меняют методом PUT /entity/product/{id}, передавая массив salePrices. Важно: сервис ожидает полный массив цен — если прислать только одну, остальные типы могут быть затёрты. Пример тела запроса:
{ "salePrices": [ { "value": 21990, "priceType": { "meta": { "href": "https://api.moysklad.ru/api/remap/1.2/context/companysettings/pricetype/{ptId}", "type": "pricetype", "mediaType": "application/json" } } } ] }
Если валюту не указать, применяется валюта аккаунта по умолчанию. Для смены закупочной цены передайте объект buyPrice с value в копейках.
- Получите справочник типов цен один раз и закешируйте.
- Сформируйте полный массив
salePricesпо всем нужным типам. - Отправьте PUT с этим массивом, чтобы не потерять другие цены.
Массовое обновление прайса
Для прайс-листа на сотни и тысячи позиций одиночные PUT неэффективны. Используйте bulk-обновление через POST /entity/product с массивом объектов в теле. Каждый объект должен содержать meta обновляемого товара и новый salePrices:
| Подход | Когда применять |
|---|---|
| PUT по одному | Точечная правка нескольких позиций |
| POST массивом (bulk) | Регулярная выгрузка прайса, до 1000 объектов за запрос |
| Импорт по externalCode | Синхронизация с внешним каталогом по внешнему ключу |
Один bulk-запрос обрабатывает до 1000 объектов. Прайс на 50 000 позиций разбивайте на пачки и держите паузу, чтобы не упереться в лимит запросов. Если объект содержит meta — товар обновляется, если нет — создаётся.
externalCode или code, а не по id — так внешняя система не зависит от внутренних идентификаторов МойСклад.Валюта, копейки и типичные ошибки
Несколько правил, которые экономят часы отладки:
- Копейки, а не рубли.
valueвсегда целое в минимальных единицах валюты. - Полный массив salePrices. Частичная передача может обнулить неуказанные типы цен.
- Валюта через meta. Для мультивалютного прайса указывайте
currency.metaиз/entity/currency. - minPrice ниже salePrices. Сервис не запрещает несогласованность, но бизнес-логика продаж это учитывает.
Проверить, что цена записалась корректно, проще всего повторным GET товара и сравнением value с ожидаемым значением в копейках.
Отдельно стоит помнить про модификации: у variant собственные цены, которые могут отличаться от родительского товара. Если модификация не имеет своей цены, применяется цена товара-родителя. При выгрузке прайса обрабатывайте обе сущности — /entity/product и /entity/variant — иначе часть номенклатуры уедет в каталог без корректных цен. Округление тоже на вашей стороне: сервис хранит ровно то целое число копеек, что вы передали, поэтому пересчёт из валюты поставщика и наценку выполняйте до записи.
Итог
Цены в JSON API 1.2 — это массив salePrices с обязательной привязкой к типу цены из /context/companysettings/pricetype, плюс buyPrice и minPrice. Значения — в копейках, справочник типов цен нельзя хардкодить, а для прайса на много позиций используется bulk через POST /entity/product. Соблюдение этих правил избавляет от затёртых цен и ошибок в разряд копеек.
Когда прайс из МойСклад нужно синхронизировать с каталогом на 1С-Битрикс — с учётом типов цен, оптовых уровней и валют — мы разрабатываем такой обмен и универсальные импортёры под конкретную структуру данных. Настроим корректный маппинг цен без потери значений и с логированием.
Частые вопросы
В каких единицах передаётся цена в API?
В копейках целым числом. Цена 199,90 рубля передаётся как 19990. Передача рублей вместо копеек — самая частая ошибка.
Откуда взять id типа цены для обновления?
Из справочника GET /context/companysettings/pricetype. В теле PUT подставляется meta нужного типа. Хардкодить id нельзя — они различаются между аккаунтами.
Почему после обновления пропали другие цены товара?
Потому что в salePrices был передан неполный массив. Сервис ожидает полный набор цен, поэтому формируйте массив сразу по всем нужным типам.
Как обновить закупочную цену?
Передайте объект buyPrice с полем value в копейках методом PUT /entity/product/{id}. Это отдельное поле, не входящее в salePrices.
Сколько товаров можно обновить одним bulk-запросом?
До 1000 объектов за один POST /entity/product. Большой прайс разбивайте на пачки и выдерживайте паузы, чтобы не превысить лимит запросов.
Как сопоставлять товары при обмене прайсом?
Удобнее по externalCode или code, а не по внутреннему id МойСклад. Так внешняя система не зависит от внутренних идентификаторов сервиса.
Что будет, если не указать валюту в цене?
Применяется валюта аккаунта по умолчанию. Для мультивалютного прайса явно указывайте currency.meta из справочника /entity/currency.
Как убедиться, что цена записалась правильно?
Сделайте повторный GET товара и сравните value в salePrices с ожидаемым значением в копейках. Это самый надёжный способ проверки.
Поможем с настройкой и поддержкой Для разработчиков
Поможем с настройкой, доработкой и поддержкой Для разработчиков.