Документация разработчика приложений для Магазина приложений МойСклад
Быстрый старт
SaaS-сервис управления торговлей МойСклад дает возможность сторонним разработчикам размещать приложения в магазине приложений МоегоСклада, а пользователям МоегоСклада — устанавливать и пользоваться этими приложениями.
Приложение — программный продукт, который расширяет и дополняет функциональность МоегоСклада. Приложения размещаются в витрине приложений.
Витрина приложений — это отдельный экран МоегоСклада, на котором размещаются опубликованные приложения. Витрина доступна всем пользователям МоегоСклада, размещение приложений — только разработчику, установка приложений — только администратору.
Разработчик управляет своими приложениями через аккаунт разработчика. Аккаунт разработчика — привязанный к конкретному разработчику аккаунт МоегоСклада. На таком аккаунте в витрине приложений отображаются приложения разработчика в статусах Черновик (Draft) и Готово, отправлено на модерацию (Ready). Это позволяет выполнять проверку и отладку приложений до их публикации для всех пользователей МоегоСклада. К одному разработчику может быть привязано несколько аккаунтов.
Конечный пользователь сервиса МойСклад также работает с МоимСкладом в рамках своего аккаунта. Обычно аккаунт соответствует одной компании или бизнесу. В аккаунте может быть несколько пользователей. Один из этих пользователей имеет права администратора аккаунта.
Партнер МоегоСклада — это компания, получающая бесплатный доступ к приложениям с целью рекламы, распространения и их продажи новым и существующим пользователям МоегоСклада. Установки приложений на аккаунт партнера имеют признак notForResale
в атрибутах подписки (см. например Получение статуса приложения на аккаунте).
Как создать и опубликовать приложение
Для магазина приложений МоегоСклада можно создать приложение на любом языке программирования с использованием любых фреймворков. Допустимые типы приложений: серверные приложения, телефония, приложения лояльности.
Каждый тип приложений подключается к МоемуСкладу с помощью своего API: серверные приложения используют JSON API 1.2, приложения телефонии — Phone API 1.0, приложения лояльности — Loyalty API.
Процесс создания приложений телефонии и лояльности описан в Сценарии работы с Phone API 1.0 и Сценарии работы с LoyaltyAPI. Пошаговый процесс создания серверных приложений рассмотрим ниже.
Определите, к какому типу относится приложение.
Создайте бизнес-логику приложения на сервере.
Создайте черновик приложения в личном кабинете разработчика, используя один из примеров дескриптора. После создания черновика вы получите appId, appUid, secret key, необходимые для работы с Vendor API.
Реализуйте эндпоинты согласно спецификации Vendor API 1.0, чтобы обрабатывать события приложения. При установке и возобновлении приложения МойСклад передает токен для доступа к JSON API 1.2.
Если пользователь должен будет настраивать приложение при его установке на аккаунт, то в ответе на запрос установки приложения верните SettingsRequired и реализуйте iframe-часть приложения. По возможности используйте UI Kit МоегоСклада. При необходимости предусмотрите ролевую модель для ограничения доступа к функциональности приложения.
Если в приложении нужны дополнительные поля для сущностей и/или документов, создайте их через JSON API 1.2 в рамках активации приложения. Если при этом процесс создания занимает более одной минуты, используйте асинхронную активацию с возвратом статуса Activating.
Если приложение нужно встроить в интерфейс МоегоСклада, создайте виджет приложения. Для этого нужно реализовать iframe-часть виджета и обновить дескриптор, добавив тег
widgets
. Подробнее читайте в разделе Виджеты.Разместите приложение в магазине приложений.
Порядок размещения приложения в магазине приложений
После готовности приложения на стороне разработчика в общем случае разработчику нужно выполнить следующие шаги по размещению приложения в магазине приложений:
- Создать Черновик приложения в личном кабинете разработчика.
- После создания Черновика на странице редактирования приложения будет доступен Секретный ключ (Secret Key).
- Создать аккаунт разработчика, привязав аккаунт МоегоСклада к аккаунту в личном кабинете разработчика.
- Протестировать и отладить приложение на аккаунте разработчика.
- Отправить приложение на модерацию через личный кабинет разработчика.
Далее сотрудник МоегоСклада проверяет приложение. Если приложение проходит проверку, оно публикуется на витрине приложений в течение 2 рабочих дней. Если приложение не проходит проверку, оно возращается разработчику для устранения замечаний.
Демо-приложение
Для демонстрации взаимодействия приложений с магазином приложений МоегоСклада было создано демо-приложение на PHP.
В демо-приложении реализованы следующие функции:
- Активация с получением токена доступа к JSON API 1.2 и деактивация по Vendor API.
- Использование основной iframe-части для настройки приложения администратором аккаунта с обновлением статуса в магазине приложений.
- Получение контекста пользователя для iframe — отображение информации по пользователю, проверка прав администратора.
- Получение информации из МоегоСклада по JSON API 1.2 с доступом по токену.
Более подробная информация и исходный код приложения доступны по ссылке.
Типы приложений для магазина приложений
Тип приложения определяет, в первую очередь, технический способ интеграции приложения с МоимСкладом.
В магазин приложений МоегоСклада можно добавить:
- Серверные приложения,
- Телефония (Phone API),
- Приложения лояльности (Loyalty API).
Поддержка функций в зависимости от типа приложения:
Функциональность | Серверные приложения | Телефония | Приложения лояльности |
---|---|---|---|
Iframe и доступ к контексту пользователя | + | - | - |
Активация и деактивация по Vendor API | + | - | - |
Доступ по токену к JSON API 1.2 | + | - | - |
Отладка на аккаунтах разработчика | + | + | + |
Phone API | - | + | - |
Loyalty API | - | - | + |
Виджеты | + | - | - |
Количество приложений на аккаунте | Не ограничено | 1 | 1 |
Серверные приложения
Серверные приложения — это приложения, основная функциональность которых основана на обмене данными с МоимСкладом по JSON API. Для этого при активации приложения по Vendor API на сервер разработчика передается токен доступа к JSON API 1.2. Доступ по токену поддерживается только в JSON API 1.2.
Для серверных приложений поддерживается активация и деактивация по Vendor API.
Для серверных приложений доступно получение контекста текущего пользователя МоегоСклада (через Vendor API) — то есть можно узнать, какой именно пользователь какого аккаунта открывает приложение в UI МоегоСклада.
Также серверное приложение может иметь iframe-часть — страницу, которую можно использовать как пользовательский интерфейс, в том числе для настроек приложения со стороны пользователя МоегоСклада или администратора аккаунта. Iframe-часть приложения загружается по указанному разработчиком URL на отдельной вкладке внутри раздела Приложения МоегоСклада. Поддерживается возможность расширенного iframe. Подробнее смотрите Дескриптор.
Только для серверных приложений доступны виджеты.
Только серверные приложения могут быть платными.
В общем случае жесткие требования к внешнему виду и визуальному дизайну iframe-части приложений отсутствуют. Приветствуется визуальное соответствие дизайну МоегоСклада. Для этого разработан UI Kit:
UI Kit доступен по ссылке.
Одновременно на аккаунте может быть подключено несколько серверных приложений.
Доступна отладка на аккаунтах разработчика.
Телефония
Эти приложения представляют собой интеграцию с внешними сервисами телефонии по Phone API: https://online.moysklad.ru/api/phone/1.0/doc/index.html
Приложения телефонии не могут получать доступ по токену к JSON API, не имеют возможности активации и деактивации по Vendor API. В перспективе эти возможности могут появиться.
Одновременно на аккаунте может быть подключено только одно приложение телефонии.
Доступна отладка на аккаунтах разработчика.
Приложения лояльности
API для интеграции МоегоСклада с системами лояльности и внешними сервисами управления скидками: https://dev.moysklad.ru/doc/api/loyalty/1.0
Одновременно на аккаунте может быть подключено одно приложение лояльности.
Для отладки можно использовать собственный черновик приложения совместно с приложением Касса МоегоСклада.
Условия размещения приложений
Требования к приложениям
Ниже перечислены требования, которым должно соответствовать приложение для успешного прохождения модерации:
- Приложение должно быть полезным для пользователей МоегоСклада. Например: форма сбора лидов модерацию не пройдет.
- Приложение должно получать доступ к данным пользователей через JSON API 1.2 с использованием токена, выдаваемого приложению при установке на аккаунт.
- Запрашивать у пользователя логин-пароль для доступа с их использованием к JSON API не допускается.
- Использовать для доступа к данным пользователя следует именно JSON API 1.2. JSON API 1.1 использовать нельзя, так как только в JSON API 1.2 есть возможность доступа по токену.
- Если приложение требует настройки пользователем, настройка должна выполняться на стороне МоегоСклада, в iframe-части.
- Приложение не пройдет модерацию, если оно представляет собой только связку с внешним сервисом, а сама настройка интеграции с МоимСкладом выполняется во внешнем сервисе.
- Если в окне настройки приложения, в iframe-части, появляется вертикальный скролл, необходимо настроить автоматическое масштабирование по высоте и добавить тег expand в дескрипторе.
- Для разработки UI желательно использовать UI Kit МоегоСклада.
После публикации необходимо обеспечить бесперебойную работу приложения. Если приложение начинает давать сбои в работе, в том числе при установке или удалении, МойСклад может снять приложение с публикации до устранения разработчиком причины ошибок.
Требования к иконкам приложений
При разработке иконки, которая будет отображаться на витрине приложений, нужно ориентироваться на следующие требования:
- Отчетливая крупная форма отчетливого цвета. Иконка будет отображаться на белом фоне, поэтому может потеряться, если в ней будут мелкие детали на белом фоне. Рекомендуем использовать один крупный знак — он легко воспринимается.
- Не делайте иконку без фона, если только она не заполняет выделенное пространство плотно, иначе она будет «исчезать» на фоне других иконок.
- Не ставьте скриншоты и фотографии и элементы интерфейса. Фотографии и экраны приложений могут включать много ненужных и плохо различимых деталей интерфейса. Эти детали могут отвлечь или запутать пользователя.
- По возможности не используйте слова. Используйте знак вашего логотипа, если у вас есть отдельный. Если используете какую-то букву из вашего логотипа как знак, можно использовать ее. Если нет, используйте полное название, учитывая остальные рекомендации.
- Не ставьте логотип МоегоСклада или его имитации. Указать логотип МоегоСклада, значит сообщить пользователю, что МоейСклад участвовал в разработке приложения, что будет неправдой.
- Рекомендуемый размер иконки: не менее 300x300px. Можно использовать все это пространство и не оставлять внутренних белых рамок.
- Рекомендуемое расширение: .svg, но также можно использовать .png или .jpeg.
Стоимость приложения
По умолчанию все публикуемые приложения платные и оплачиваются клиентом через биллинг МоегоСклада. Клиент может оплатить приложение любым удобным ему способом: картой, по счету от физического лица или юридического лица, через СБП, в рублях, казахстанских тенге или узбекских сумах. Если пользователь не продлил или не оплатил подписку, доступ к приложению приостанавливается, а при поступлении оплаты – возобновляется. Подробнее см. Процесс деактивации приложения на аккаунте.
Приложение не может быть бесплатным, если для его полноценной работы требуется оплата вне магазина приложений. Есть только два исключения: смс-рассылки и телефония, которые используют свой транзакционный биллинг.
Если вы хотите разместить бесплатное приложение, которое будет выполнять все свои функции без дополнительной оплаты, вам нужно заранее связаться с нами по почте apps@moysklad.ru и согласовать такую публикацию.
В некоторых случаях вы можете предоставлять бесплатный доступ к функциональности приложения в рамках тарифной сетки, когда один из предлагаемых тарифов будет бесплатным, а остальные платными. В бесплатном тарифе, например, может быть ограничено количество поддерживаемых операций или доступны только базовые функции.
Требования к разработчикам
Для размещения любых приложений у вас должно быть зарегистрировано ИП или юрлицо в РФ или странах СНГ, банковский расчетный счет в российских рублях для получения вознаграждения. Размещая приложение разработчик принимает условия договора-оферты для разработчиков.
Расчет вознаграждения и комиссия
Все приложения продаются по модели подписки, начальная стоимость указывается в рублях за 1 месяц.
Комиссия МоегоСклада – 25% от стоимости подписки, которую мы получили от пользователя. Если клиент купил подписку на 1 месяц на приложение стоимостью 1000 рублей/месяц, то разработчик получит за него 750 рублей за этот период.
Приложение можно купить на 1 месяц и на 12 месяцев. При покупке сразу на 12 месяцев для клиента действует скидка 15%, вознаграждение разработчику при этом рассчитывается от итоговой суммы, которую заплатил клиент: (Цена приложения х 12 месяцев – 15%) – 25%. Скидки при годовой оплате стимулируют пользователей к переходу на платный тариф и снижают % отказов от продления подписки.
Приложения также могут покупать наши партнеры, которые внедряют и настраивают МойСклад клиентам, им доступна скидка 30% на все приложения. При покупке приложения через партнера вознаграждение рассчитывается от итоговой суммы, которую получил МойСклад: (Цена приложения – 30%) – 25%.
Рекомендуемые цены
Вы можете указать любую стоимость, кратную 100 рублям. Мы рекомендуем придерживаться рыночных отношений и не указывать цены, существенно ниже аналогов, представленных в вашей категории приложений.
Основные категории и цены, на которые вы можете ориентироваться при формировании своих тарифных планов:
- CMS и конструкторы сайтов – от 600 рублей/месяц.
- подключение службы доставки – от 1000 рублей/месяц.
- импорт/экспорт товаров/заказов для маркетплейса – от 2500 рублей/месяц.
- комплексное приложение для работы с маркетплейсами – от 5000 рублей/месяц.
- интеграция с банками – от 1000 рублей/месяц.
Мы оставляем за собой право отказать вам в публикации приложения, если его стоимость будет провоцировать демпинг и негативно влиять на конкуренцию с другими разработчиками.
Изменение стоимости приложения
Доступны следующие варианты:
- Назначение платной подписки для приложения до его публикации.
- Изменение стоимости платной подписки или добавление нового платного тарифа(-ов).
Если приложение не опубликовано, перейдите в ЛКВ в карточку приложения, чтобы указать или изменить стоимость подписки. Чтобы изменить стоимость опубликованного приложения, напишите на apps@moyskad.ru, для добавления нового тарифа к существующему – создайте новую версию, укажите в ней нужные данные и отправьте на модерацию.
Пробный период платных приложений
Для приложения с платной подпиской можно установить пробный период, в течение которого пользователь сможет бесплатно установить и оценить его возможности.
Условия пробного периода:
- Пробный период указывается в карточке приложения во вкладке «Тарифы» – нужно поставить галочку «Триал» у нужного платного тарифа и указать количество дней.
- Можно выбрать от 1 до 14 дней. После окончания пробного периода приложение становится доступно только по подписке и продолжит работу при наличии оплаты.
- Срок действия пробного периода начинается с момента первой установки приложения. В течение всего периода (1-14 дней) пользователь может удалять и устанавливать ваше приложение любое количество раз.
- Пробный период можно добавить, изменить или убрать у уже опубликованного приложения через создание новой версии. При этом уже установленные экземпляры приложения продолжат свою работу с тем пробным периодом, который был доступен пользователям на момент установки приложения.
- Если у вашего приложения несколько платных тарифов, указать пробный период можно только у одного из них. Обычно это самый старший тариф с наибольшим количеством функций.
- Приложение с пробным периодом может установить пользователь с любым тарифом МоегоСклада независимо от баланса его счета. Однако пользователям с Бесплатным тарифом недоступны некоторые возможности приложений, например вебхуки.
Рекомендуем всегда устанавливать пробный период, так как это значительно повышает конверсию в установки и оплату, по сравнению с приложениями без пробного периода.
Продление пробного периода
По запросу пользователя вы можете повторно продлить пользователю пробный период на еще один срок от 1 до 14 дней. Для этого в ЛКВ нужно выбрать приложение и нажать кнопку «Дополнительный триал», указать название аккаунта пользователя и срок пробного периода.
- Продление можно сделать только 1 раз.
- При продлении пробного периода, дополнительный пробный период не суммируется с текущим, а накладывается на него. То есть если вы установили пробный период на четырнадцать дней, а на десятый день продлили его еще на семь дней, оставшийся пробный период — семь дней, а не одиннадцать.
- Продление пробного периода недоступно, если пользователь перешел на платную подписку.
Порядок получения выплат
В начале каждого месяца в личном кабинете разработчика формируется отчет за прошедший месяц. В отчете указаны все проданные подписки для всех приложений, их количество и итоговая сумма к выплате.
Чтобы получить выплату:
- Заполните реквизиты.
- Проверьте и согласуйте отчет в кабинете разработчика. Для этого перейдите в раздел Отчеты → По выплатам, выберите период и нажмите на кнопку Согласовать в соответствующей строке.
- Выплата поступит на указанные реквизиты разработчика в течение пяти рабочих дней с даты согласования отчета.
По вопросам, связанным с выплатами, обращайтесь: apps@moysklad.ru.
Руководство разработчика
Доступ по токену к JSON API
Токен для доступа к JSON API 1.2 передается разработчику при активации приложения через Vendor API в момент установки или возобновления приложения. Время жизни токена не ограничено.
Токен аннулируется при удалении и приостановке приложения. На момент деактивации приложения через Vendor API токен уже аннулирован.
Токен при доступе к JSON API 1.2 следует передавать как Bearer-токен, а именно в виде заголовка HTTP-запроса:
Authorization: Bearer <access_token>
Пример:
Authorization: Bearer 6ab89be1ae6ff147755625ee8da948e42612233b
Диаграмма последовательности предоставления доступа при подключении приложения
Диаграмма последовательности отзыва доступа при отключении приложения
Работа с вебхуками
Если приложение может создать один или несколько вебхуков на аккаунте пользователя, необходимо учитывать:
- Вебхуки доступны только на пробном и платных тарифах.
- Если пользователь инициирует удаление приложения, перед тем как удалить приложение, МойСклад удаляет все вебхуки, созданные этим приложением.
- Перед приостановкой приложения вебхуки будут отключены, а после возобновления — включены. Для платных приложений.
Подробную информацию о работе с вебхуками можно получить по ссылке.
Особенности доступа к некоторым функциям JSON API 1.2
На данный момент для приложений существуют ограничения в работе с JSON API 1.2:
- Приложение не может получать Информацию о действующей подписке компании.
- Приложение не может создавать Бонусные операции.
- Приложение может использовать Шаблоны документов, но получаемый шаблон или шаблон на основании документа, имеет меньший набор предзаполненных полей чем шаблон полученный по авторизации пользователя.
Подробнее о том, как работать с API МоегоСклада, смотрите в видео.
Главный iframe
Главный iframe приложения — это основное окно для настройки и работы с серверным приложением, которое открывается при нажатии
на кнопку Перейти в приложение на витрине либо при переходе по прямой ссылке вида
https://online.moysklad.ru/app/#embed-apps?id={appId}
.
За настройку и работу этого элемента отвечает блок iframe в дескрипторе. В случае отсутствия этого блока считается, что у приложения отсутствует iframe-часть.
При реализации главного iframe приложения следует учитывать следующее:
- По возможности используйте UI Kit МоегоСклада для визуального оформления содержимого.
- Если в iframe будет какая-либо пользовательская функциональность кроме настроек, предусмотрите доступ к этой функциональности для пользователей с разными правами доступа, не только для администратора.
- Если содержимое iframe не умещается в минимальную допустимую высоту окна (768px), то необходимо добавить js скрипт для автоматического масштабирования высоты окна в зависимости от контента на свою страницу. Пример:
<!doctype html>
<html>
<head>
...
</head>
<body>
...
<script type="text/javascript"
src="https://apps-api.moysklad.ru/js/ns/appstore/app/v1/moysklad-iframe-expand-3.js"></script>
</body>
</html>
Виджеты
Виджет — это плагин, имеющий визуальную часть. Визуальная часть виджета — прямоугольный блок, который встраивается в интерфейс МоегоСклада в определенном месте. Содержимое блока определяется приложением.
Можно создать виджеты двух типов:
c фиксированной шириной — 416px для всего виджета с рамкой, 384px — для рабочей области (iframe, определяемый приложением).
c фиксированной высотой — высота задается в дескрипторе.
Виджеты отображаются в двух режимах:
- развернутая форма (полная) — виджет отображается с рабочей iframe-областью;
- свернутая форма — рабочая область виджета скрыта.
Если у пользователя установлены несколько приложений с виджетами, встроенными на одну страницу МоегоСклада, отображаются все виджеты.
Порядок отображения соответствует расположению родительских приложений на витрине. В редакторах, поддерживающих функцию Drag-and-drop, пользователь может сам поменять порядок отображения виджетов.
Размещение виджетов
Виджеты можно добавить на страницы МоегоСклада, которые есть в списке.
Виджеты на страницах создания, например в точке встраивания document.customerorder.create
, имеют ограниченную функциональность
по сравнению с виджетами на страницах редактирования, например document.customerorder.edit
.
Это выражается в меньшем количестве поддерживаемых протоколов и в реализации самих протоколов. Например, в сообщении Change часть полей, которые заполняются после первого сохранения документа (id
, created
, meta
и другие) будет заполнено значением null
.
Поддержка сервисных протоколов виджетами на страницах создания пока не реализована.
Каждая точка встраивания имеет свой список поддерживаемых дополнительных протоколов.
Чтобы встроить виджет на страницу МоегоСклада, которой нет в списке, свяжитесь с нами в Telegram или по электронной почте.
Жизненный цикл виджета
Виджет начинает отображаться на странице только после перехода приложения в статус Activated.
Если приложение предполагает настройку и статус SettingsRequired, виджет отображается после настройки приложения.
При первом открытии страницы с виджетом в рамках одной вкладки браузера cистема МоегоСклада загружает виджет по HTTP GET-запросом по URL, указанному в теге sourceUrl. При этом система генерирует и передает GET-параметром contextKey
ключ, по которому виджет получает текущий
контекст пользователя через Vendor API.
Прочие данные передаются c помощью postMessage при первом и последующих открытиях виджета. Так, при каждом открытии виджета система МоегоСклада отображает ранее загруженный iframe виджета без повторной загрузки с сервера разработчика, и через postMessage передает в iframe этого виджета сообщение Open
.
Без дополнительных настроек виджет отображает содержимое сразу, как только получает сообщение Open
. Даже если еще не успел обновить отображаемую информацию.
Примеры JS-кода доступны в демо-приложении на GitHub. Примеры демонстрируют:
- как получить сообщение
Open
с идентификатором документаobjectId
, в котором открыт виджет; - как залогировать в консоль входящие и исходящие сообщения, то есть сообщения от виджета и к виджету.
Если виджет поддерживает протокол open-feedback, система не отображает содержимое виджета сразу, а ждет ответного сообщения от виджета о готовности. До этого момента внутри виджета отображается заглушка. Когда виджет готов, он отправляет сообщение OpenFeedback
. После этого система полностью открывает виджет пользователю.
Если виджет поддерживает протокол change-handler, при редактировании документа пользователем на странице с виджетом он оповещается об изменениях, получая сообщение Change
,
содержащее несохраненное состояние документа.
Если виджет поддерживает протокол update-provider, при редактировании документа пользователем на странице с виджетом
он может изменять несохраненное состояние документа, отправляя сообщение UpdateRequest
со списком полей, которые необходимо изменить.
При сохранении страницы с виджетом, если виджет, который находится на экране редактирования сущности, поддерживает протокол save-handler, он оповещается о факте сохранения объекта пользователем, получая сообщение Save
.
Виджет, поддерживающий протокол dirty-state, может сообщить хост-окну, что в виджете есть несохраненные изменения. Для этого виджет отправляет хост-окну сообщение SetDirty
. Виджет может отправить хост-окну сообщение ClearDirty
, после чего диалог подтвержения закрытия окна не будет появляться, при условии, что отсутствуют несохраненные изменения в самом UI МоегоСклада или в других виджетах.
Внутренний dirty-флаг для виджета в хост-окне сбрасывается при открытии. То есть при отправке сообщения Open
хост-окно считает, что в виджете нет несохраненных изменений.
Поддержку виджетом протоколов open-feedback, save-handler, dirty-state, change-handler необходимо указать в дескрипторе приложения.
Как работают виджеты
Описание конфигурации виджетов приложения в дескрипторе
Виджеты доступны для серверных приложений с дескриптором версии v2.
Пример дескриптора приложения с виджетом в карточке контрагента расположен в правой части экрана.
Дескриптор приложения с виджетом в карточке контрагента
<ServerApplication xmlns="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2
https://apps-api.moysklad.ru/xml/ns/appstore/app/v2/application-v2.xsd">
<iframe>...</iframe>
<vendorApi>...</vendorApi>
<access>...</access>
<widgets>
<entity.counterparty.edit>
<sourceUrl>https://b2b.moysklad.ru/widget/counter-party</sourceUrl>
<height>
<fixed>28px</fixed>
</height>
<supports>
<open-feedback/>
<save-handler/>
</supports>
<uses>
<good-folder-selector/>
</uses>
</entity.counterparty.edit>
</widgets>
</ServerApplication>
Подробнее структура дескриптора для приложения с виджетом описана в разделе Дескриптор приложений.
После того, как пользователь установил и настроил приложение с дескриптором из примера, виджет отображается в карточке контрагента.
Загрузка виджета на странице
Виджет на странице загружается в iframe по URL, указанному в теге
<sourceUrl>...</sourceUrl>
виджета в дескрипторе приложения.
Параметры содержимого виджетов: - ширина 400px — для виджетов всех приложений, - высота — статически указывается разработчиком в дескрипторе приложения. В примере высота — 61px.
Как и основной iframe-части приложения, виджету GET-параметром передается contextKey
, по которому через Vendor API можно получить информацию о текущем
пользователе.
Пока происходит загрузка виджета, отображается ненавязчивый лоадер.
Кэширование виджетов
Система виджетов в МоемСкладе реализована так, чтобы, по-возможности, загружать виджет один раз. При первом открытии страницы с виджетом в рамках одной вкладки браузера происходит загрузка. Далее iframe c загруженным в него виджетом кэшируется и переиспользуется во всех последующих открытиях страницы с виджетом в рамках одной вкладки браузера.
Если по техническим причинам кэширование не произошло, хост-окно может: - создать несколько iframe-экземпляров для одной точки расширения в рамках одной вкладки браузера, при этом чем эти экземпляры могут существовать одновременно; - не кэшировать iframe виджета после загрузки.
Открытие виджета
Когда пользователь открывает страницу с виджетом, хост-окно отображает iframe виджета, только что загруженный или ранее закэшированный, и передает в этот iframe сообщение Open
через postMessage.
Пример сообщения Open
cмотрите в правой части экрана.
Сообщение Open для виджета на экране создания в Заказе покупателя
{
"name": "Open",
"messageId": 12345,
"extensionPoint": "document.customerorder.create",
"objectId": null,
"displayMode": "expanded"
}
Сообщение Open для виджета на экране редактирования в Заказе покупателя
{
"name": "Open",
"messageId": 12345,
"extensionPoint": "document.customerorder.edit",
"objectId": "8e9512f3-111b-11ea-0a80-02a2000a3c9c",
"displayMode": "expanded"
}
Здесь:
extensionPoint
— текущая точка расширения;objectId
— идентификатор текущего документа или сущности. Для виджета, отображаемого на экране создания, значение —null
;displayMode
— режим отображения виджета. Сейчас может принимать только одно значениеexpanded
.
Виджет при получении сообщения Open
может, например, обратиться на сервер за данными для указанного объекта objectId
и отобразить их пользователю.
Примечание: в сообщении Open передается идентификатор текущей открытой сущности в карточке, который отображается в URL браузера в параметре id
. Несмотря на то, что для сущностей Товар, Услуга, Комплект и Модификация этот идентификатор отличается от используемого в remap API, запрос по нему по-прежнему будет работать. При этом сервер будет использовать редирект .
Пример запроса для Товара https://online.moysklad.ru/app/#good/edit?id=9e73d736-a0de-11e9-9109-f8fc00095c7f
приведен в правой части экрана. Для упрощения часть вывода пропущена.
Ответ на запрос получения Товара
curl -X GET --location "https://api.moysklad.ru/api/remap/1.2/entity/product/9e73d736-a0de-11e9-9109-f8fc00095c7f" -H "Content-Type: application/json" -H "Authorization: Bearer ..." -v
> GET /api/remap/1.2/entity/product/9e73d736-a0de-11e9-9109-f8fc00095c7f HTTP/1.1
> Host: online.moysklad.ru
> User-Agent: curl/7.68.0
> Accept: */*
> Content-Type: application/json
> Authorization: Bearer ...
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 308 Permanent Redirect
< Server: nginx/1.18.0
< Date: Fri, 28 Jan 2022 11:13:00 GMT
< Content-Length: 0
< Connection: keep-alive
< Cache-Control: max-age=0, no-cache
< X-Lognex-Reset: 0
< X-Lognex-Retry-After: 0
< Location: https://api.moysklad.ru/api/remap/1.2/entity/product/9e73e41d-a0de-11e9-9109-f8fc00095c81
< X-Lognex-Retry-TimeInterval: 3000
< X-RateLimit-Remaining: 44
< X-RateLimit-Limit: 45
< Strict-Transport-Security: max-age=15552000
<
* Connection #1 to host online.moysklad.ru left intact
* Issue another request to this URL: 'https://api.moysklad.ru/api/remap/1.2/entity/product/9e73e41d-a0de-11e9-9109-f8fc00095c81'
* Found bundle for host online.moysklad.ru: 0x55cb04fa3970 [serially]
* Can not multiplex, even if we wanted to!
* Re-using existing connection! (#1) with host online.moysklad.ru
* Connected to online.moysklad.ru (88.212.252.4) port 443 (#1)
> GET /api/remap/1.2/entity/product/9e73e41d-a0de-11e9-9109-f8fc00095c81 HTTP/1.1
> Host: online.moysklad.ru
> User-Agent: curl/7.68.0
> Accept: */*
> Content-Type: application/json
> Authorization: Bearer ...
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Server: nginx/1.18.0
< Date: Fri, 28 Jan 2022 11:13:00 GMT
< Content-Type: application/json;charset=utf-8
< Content-Length: 6535
< Connection: keep-alive
< Vary: Accept-Encoding
< Cache-Control: no-cache
< X-Lognex-Reset: 0
< X-Lognex-Retry-After: 0
< X-Lognex-Retry-TimeInterval: 3000
< X-RateLimit-Remaining: 43
< X-RateLimit-Limit: 45
< Strict-Transport-Security: max-age=15552000
<
{
"meta" : {
"href" : "https://api.moysklad.ru/api/remap/1.2/entity/product/9e73e41d-a0de-11e9-9109-f8fc00095c81",
"metadataHref" : "https://api.moysklad.ru/api/remap/1.2/entity/product/metadata",
"type" : "product",
"mediaType" : "application/json",
"uuidHref" : "https://online.moysklad.ru/app/#good/edit?id=9e73d736-a0de-11e9-9109-f8fc00095c7f"
},
"id" : "9e73e41d-a0de-11e9-9109-f8fc00095c81",
...
}
Протокол обратной связи при открытии виджета
По умолчанию при открытии закэшированного виджета его содержимое отображается сразу.
Если виджет при открытии делает обращение к серверу, может быть видна небольшая задержка. В это время будет отображается прежнее состояние и содержание виджета, например, данные для прошлого контрагента.
Протокол обратной связи позволяет виджету явно сообщить хост-окну в какой именно момент отобразить содержимое виджета. До этого содержимое виджета будет закрыто ненавязчивым лоадером:
Тег дополнительных протоколов supports с протоколом open-feedback
<supports>
<open-feedback/>
</supports>
Для переключения хост-окна на использование протокола обратной связи при открытии виджета в дескрипторе для виджета надо явно указать поддержку дополнительного протокола open-feedback. Пример тега дополнительных протоколов supports с указанным в нем протоколом open-feedback смотрите в правой части экрана.
Виджет передает сообщение OpenFeedback
хост-окну в качестве сигнала готовности содержимого виджета для отображения пользователю. Пример сообщения OpenFeedback
смотрите в правой части экрана.
Cообщение OpenFeedback
{
"name": "OpenFeedback",
"correlationId": 12345
}
Здесь correlationId
содержит значение messageId
ранее полученного сообщения Open
.
Хост-окно, получив сообщение OpenFeedback
, отображает содержимое виджета пользователю и убирает лоадер.
Сохранение пользователем редактируемого объекта
Хост-окно может оповещать виджет о факте сохранения редактируемого объекта. Для этого в дескрипторе для виджета нужно объявить поддержку опционального протокола save-handler.
Тег дополнительных протоколов supports с протоколом save-handler
<supports>
<save-handler/>
</supports>
Хост-окно отправляет виджету сообщение Save
через postMessage при сохранении редактируемого объекта после сохранения объекта в базе данных. То есть на момент получения виджетом сообщения Save сохраненное состояние объекта уже доступно через JSON API.
Сохранение редактируемого объекта инициируется пользователем:
при явном нажатии на кнопку Сохранить, в том числе при сохранении объекта без фактического внесения изменений;
при покидании объекта и его явном сохранении через диалог подтверждения сохранения изменений, в том числе при листании кнопками-стрелочками на соседние объекты;
при автоматическом сохранении изменений закрываемого объекта, например, при создании связанного документа Отгрузки из Заказа покупателя.
Пример сообщения Save
cмотрите в правой части экрана.
Сообщение Save
{
"name": "Save",
"messageId": 32109,
"extensionPoint": "entity.counterparty.edit",
"objectId": "8e9512f3-111b-11ea-0a80-02a2000a3c9c"
}
Здесь:
extensionPoint
— текущая точка расширения;objectId
— идентификатор текущего документа или сущности, аналогичен идентификатору в сообщенииOpen
.
Признак несохраненного состояния виджета
Тег дополнительных протоколов supports с протоколом dirty-state
<supports>
<dirty-state/>
</supports>
Хост-окно поддерживает подтверждение закрытия окна пользователем, если он изменил данные в форме виджета, но не сохранил их. Для этого в дескрипторе для виджета нужно объявить поддержку опционального протокола dirty-state.
Сообщение SetDirty
{
"name": "SetDirty",
"messageId": 12,
"openMessageId": 7
}
После того, как пользователь внес изменения в виджет, он отправляет хост-окну сообщение SetDirty
. Пример сообщения SetDirty смотрите в правой части экрана.
Здесь openMessageId содержит значение messageId ранее полученного сообщения Open
.
Система учитывает, что в виджете есть несохраненные изменения. Далее, если пользователь нажимает кнопку Закрыть или другим способом пытается уйти с формы редактирования, система отображает диалог подтверждения сохранения изменений:
Сообщение ClearDirty
{
"name": "ClearDirty",
"messageId": 13
}
Если виджет после отправки SetDirty
отправляет хост-окну сообщение ClearDirty
, система не учитывает данный виджет при отображении диалога подтверждения сохранения изменений. То есть, если отсутствуют прочие несохраненные изменения самого объекта или в других виджетах, система не запрашивает диалог подтверждения сохранения изменений при закрытии редактируемого объекта.
Получение состояния редактируемого объекта
Хост-окно может оповещать виджет об изменениях несохраненного состояния редактируемого объекта. Для этого в дескрипторе для виджета нужно объявить поддержку опционального протокола change-handler.
Тег дополнительных протоколов supports с протоколом change-handler
<supports>
<change-handler/>
</supports>
Хост-окно отправляет виджету сообщение Change
через postMessage, содержащее несохраненное состояние документа при редактировании документа пользователем.
Отправка сообщения Change
инициируется при следующих действиях пользователя:
- при изменении полей документа, в том числе дополнительных полей, путем редактирования/выбора значения в селекторе;
- при добавлении/удалении/редактировании позиций документа.
Отправка сообщения Change
не происходит в следующих случаях:
- при открытии экрана редактирования документа;
- при изменении состояния документа в результате сохранения пользователем;
- при изменении полей, которые не поддерживаются;
- если при редактировании значение редактируемого поля не изменилось, то есть при отсутствии реальных изменений.
Узнать, для каких точек поддерживается протокол change-handler, можно тут.
Пример сообщения Change
cмотрите в правой части экрана.
Здесь changeHints
представляет собой массив с подсказками о том, что именно было изменено в редактируемом объекте:
_fields
— стандартные простые и ссылочные поля объекта (название, даты, контрагент и т. п.);positions
— позиции документа;attributes
— значения дополнительных полей объекта.
Поле objectState
— измененное состояние объекта, которое представляет собой JavaScript-объект, соответствующий по структуре ответу JSON API 1.2 на получение того же объекта (документа) с позициями.
Несмотря на то, что структура objectState
в целом соответствует JSON API 1.2, имеются расхождения:
- Поля, обязательные в JSON API 1.2, могут быть не заданы в несохраненном состоянии документа. В качестве значение таких полей в
objectState
передаетсяnull
. - Числовые поля, которые могут иметь разные типы (целочисленные и с плавающей точкой) в JSON API 1.2, в
objectState
имеют один и тот же тип Number. Это связано с тем, чтоobjectState
передается не как JSON, а как JavaScript Object. - В
objectState
передается документ со всеми позициями, что в целом соответствует запросу в JSON API cexpand=positions
. При этом в метаданных позиций документа всегдаoffset=0
, аlimit
зависит отsize
:limit=1000
, еслиsize <= 1000
иlimit=size
еслиsize > 1000
. - В objectState учитывается URL сервиса — МойСклад или Моя Торговля.
- В дополнительных полях типа Файл в
value
содержится имя файла с расширением, в отличие от JSON API 1.2. - На страницах создания (точка расширения
*.create
) часть полей, которые заполняются после первого сохранения документа, могут быть не заполнены — иметь значениеnull
:id
,accountId
,created
,meta
,href
,uuidHref
для документа;externalCode
для документа, кроме Заказа покупателя, где внешний код может быть заполнен пользователем;id
,accountId
,meta
,href
,uuidHref
для позиций документа.
- на страницах создания некоторые поля могут иметь другое значение:
updated
— заполняется временем открытия страницы документа.
Актуальные сведения о поддержке конкретных полей документов в протоколе change-handler смотрите в документации JSON API 1.2:
- Заказ покупателя
- Оприходование
- Отгрузка
- Приемка
- Перемещение
- Списание
- Счет покупателю
- Счет поставщика
- Возврат покупателя
- Розничная продажа
Сообщение Change
{
"name": "Change",
"extensionPoint": "document.customerorder.edit",
"messageId": 7,
"changeHints": [
"positions",
"_fields"
],
"objectState": {
"meta": {
"href": "https://api.moysklad.ru/api/remap/1.2/entity/customerorder/c4c6e6ea-b3f5-11eb-0a80-35ed000000b8",
"metadataHref": "https://api.moysklad.ru/api/remap/1.2/entity/customerorder/metadata",
"type": "customerorder",
"mediaType": "application/json",
"uuidHref": "https://online.moysklad.ru/app/#customerorder/edit?id=c4c6e6ea-b3f5-11eb-0a80-35ed000000b8"
},
"id": "c4c6e6ea-b3f5-11eb-0a80-35ed000000b8",
"accountId": "5fc956ad-b3f2-11eb-0a80-1b8a00000000",
"created": "2021-05-13 17:16:11.465",
"payedSum": 0,
"shippedSum": 0,
"invoicedSum": 0,
"name": "00001",
"applicable": true,
"moment": "2021-05-13 17:15:00.000",
"store": {
"meta": {
"href": "https://api.moysklad.ru/api/remap/1.2/entity/store/605491e4-b3f2-11eb-0a80-35ed00000074",
"metadataHref": "https://api.moysklad.ru/api/remap/1.2/entity/store/metadata",
"type": "store",
"mediaType": "application/json",
"uuidHref": "https://online.moysklad.ru/app/#warehouse/edit?id=605491e4-b3f2-11eb-0a80-35ed00000074"
}
},
"rate": {
"currency": {
"meta": {
"href": "https://api.moysklad.ru/api/remap/1.2/entity/currency/6055a619-b3f2-11eb-0a80-35ed00000079",
"metadataHref": "https://api.moysklad.ru/api/remap/1.2/entity/currency/metadata",
"type": "currency",
"mediaType": "application/json",
"uuidHref": "https://online.moysklad.ru/app/#currency/edit?id=6055a619-b3f2-11eb-0a80-35ed00000079"
}
}
},
"organization": {
"meta": {
"href": "https://api.moysklad.ru/api/remap/1.2/entity/organization/6051401c-b3f2-11eb-0a80-35ed00000072",
"metadataHref": "https://api.moysklad.ru/api/remap/1.2/entity/organization/metadata",
"type": "organization",
"mediaType": "application/json",
"uuidHref": "https://online.moysklad.ru/app/#mycompany/edit?id=6051401c-b3f2-11eb-0a80-35ed00000072"
}
},
"agent": {
"meta": {
"href": "https://api.moysklad.ru/api/remap/1.2/entity/counterparty/60550738-b3f2-11eb-0a80-35ed00000077",
"metadataHref": "https://api.moysklad.ru/api/remap/1.2/entity/counterparty/metadata",
"type": "counterparty",
"mediaType": "application/json",
"uuidHref": "https://online.moysklad.ru/app/#company/edit?id=60550738-b3f2-11eb-0a80-35ed00000077"
}
},
"state": {
"meta": {
"href": "https://api.moysklad.ru/api/remap/1.2/entity/customerorder/metadata/states/60850d6a-b3f2-11eb-0a80-35ed00000097",
"type": "state",
"metadataHref": "https://api.moysklad.ru/api/remap/1.2/entity/customerorder/metadata",
"mediaType": "application/json"
}
},
"externalCode": "JAGi0Yg0i0OYvylp7SzDi3",
"vatEnabled": true,
"vatIncluded": true,
"vatSum": 0,
"sum": 21000,
"updated": "2021-05-13 17:16:11.434",
"reservedSum": 20000,
"attributes": [
{
"meta": {
"href": "https://api.moysklad.ru/api/remap/1.2/entity/customerorder/metadata/attributes/14fb3ad9-b3f6-11eb-0a80-35ed000000cb",
"type": "attributemetadata",
"mediaType": "application/json"
},
"id": "14fb3ad9-b3f6-11eb-0a80-35ed000000cb",
"name": "Строка",
"type": "string",
"value": "123АААББвQ"
},
{
"meta": {
"href": "https://api.moysklad.ru/api/remap/1.2/entity/customerorder/metadata/attributes/14fbcb79-b3f6-11eb-0a80-35ed000000cc",
"type": "attributemetadata",
"mediaType": "application/json"
},
"id": "14fbcb79-b3f6-11eb-0a80-35ed000000cc",
"name": "Ссылка",
"type": "link",
"value": null
},
{
"meta": {
"href": "https://api.moysklad.ru/api/remap/1.2/entity/customerorder/metadata/attributes/14fbd363-b3f6-11eb-0a80-35ed000000cd",
"type": "attributemetadata",
"mediaType": "application/json"
},
"id": "14fbd363-b3f6-11eb-0a80-35ed000000cd",
"name": "Компания",
"type": "counterparty",
"value": {
"meta": {
"href": "https://api.moysklad.ru/api/remap/1.2/entity/counterparty/6054e7f9-b3f2-11eb-0a80-35ed00000075",
"metadataHref": "https://api.moysklad.ru/api/remap/1.2/entity/counterparty/metadata",
"type": "counterparty",
"mediaType": "application/json",
"uuidHref": "https://online.moysklad.ru/app/#company/edit?id=6054e7f9-b3f2-11eb-0a80-35ed00000075"
},
"name": "ООО \"Поставщик\""
}
}
],
"positions": {
"meta": {
"href": "https://api.moysklad.ru/api/remap/1.2/entity/customerorder/c4c6e6ea-b3f5-11eb-0a80-35ed000000b8/positions",
"type": "customerorderposition",
"mediaType": "application/json",
"size": 2,
"limit": 1000,
"offset": 0
},
"rows": [
{
"meta": {
"href": null,
"type": "customerorderposition",
"mediaType": "application/json"
},
"id": null,
"accountId": "5fc956ad-b3f2-11eb-0a80-1b8a00000000",
"price": 10000,
"quantity": 2,
"reserve": 2,
"shipped": 0,
"assortment": {
"meta": {
"href": "https://api.moysklad.ru/api/remap/1.2/entity/product/788a1cc7-b3f6-11eb-0a80-35ed000000e2",
"metadataHref": "https://api.moysklad.ru/api/remap/1.2/entity/product/metadata",
"type": "product",
"mediaType": "application/json",
"uuidHref": "https://online.moysklad.ru/app/#good/edit?id=78896bd4-b3f6-11eb-0a80-35ed000000e0"
}
},
"vat": 0,
"discount": 0
},
{
"meta": {
"href": null,
"type": "customerorderposition",
"mediaType": "application/json"
},
"id": null,
"accountId": "5fc956ad-b3f2-11eb-0a80-1b8a00000000",
"price": 1000,
"quantity": 1,
"shipped": 0,
"assortment": {
"meta": {
"href": "https://api.moysklad.ru/api/remap/1.2/entity/service/9d0c9a63-b3f6-11eb-0a80-35ed000000eb",
"metadataHref": "https://api.moysklad.ru/api/remap/1.2/entity/service/metadata",
"type": "service",
"mediaType": "application/json",
"uuidHref": "https://online.moysklad.ru/app/#good/edit?id=9d0c74c3-b3f6-11eb-0a80-35ed000000e9"
}
},
"vat": 0,
"discount": 0
}
]
}
}
}
Валидация состояния редактируемого объекта
Виджет может проверять состояние редактируемого объекта и запрещать хост-окну сохранять объект, если он невалиден. Для этого в дескрипторе для виджета нужно объявить поддержку опционального протокола validation-feedback, который является параметром тега change-handler
.
Тег дополнительных протоколов supports с протоколом change-handler
<supports>
<change-handler>
<validation-feedback/>
</change-handler>
</supports>
Протокол работает в паре с change-handler
, то есть виджет, поддерживающий протокол validation-feedback
, должен отправить сообщение ValidationFeedback
о валидности документа в ответ на сообщение Change
.
Если виджет в сообщении ValidationFeedback
укажет, что документ невалиден, то при попытке сохранить документ пользователь увидит сообщение об ошибке, которое включает в себя наименование виджета.
Если виджет по какой-то причине не отправит ValidationFeedback
или отправит некорректное сообщение, пользователь не сможет сохранить документ.
Подробнее о том, для каких точек поддерживается протокол validation-feedback, смотрите здесь.
Пример сообщений ValidationFeedback
cмотрите в правой части экрана.
Здесь:
messageId
— целочисленный идентификатор сообщения, уникальный в рамках текущего взаимодействия виджет — хост-окно. Назначается виджетом;correlationId
— идентификатор соответствующего сообщенияChange
;valid
— признак валидности документа;message
— сообщение об ошибке. Требуется для случая когдаvalid=false
. Максимум 100 символов.
Сообщение ValidationFeedback — документ валиден (может быть сохранен)
{
"name": "ValidationFeedback",
"messageId": 11,
"correlationId": 10,
"valid": true
}
Сообщение ValidationFeedback — документ невалиден (не должен быть сохранен)
{
"name": "ValidationFeedback",
"messageId": 12,
"correlationId": 11,
"valid": false,
"message": "Пример ошибки от разработчика"
}
Изменение состояния редактируемого объекта
Тег дополнительных протоколов supports с протоколом update-provider
<supports>
<update-provider/>
</supports>
Виджет может изменять поля текущего редактируемого объекта посредством передачи сообщения UpdateRequest
хост-окну.
Для этого в дескрипторе для виджета нужно объявить поддержку опционального протокола update-provider.
Изменения в этом протоколе, в отличие от JSON API, происходят без сохранения состояния объекта в базе данных МоегоСклада, так же, как если бы они были сделаны самим пользователем. Виджет должен отправлять сообщения UpdateRequest
преимущественно в качестве реакции на действия пользователя, чтобы предупредить его об изменениях в редактируемом документе.
Сообщение UpdateRequest
{
"name": "UpdateRequest",
"messageId": 10,
"updateState": {
"name": "1",
"deliveryPlannedMoment": "2021-08-21T12:15:50.333Z",
"applicable": true,
"description": null
}
}
Сценарий работы:
- Виджет отправляет хост-окну сообщение
UpdateRequest
, содержащее набор полей документа и/или позиции документа, которые нужно изменить. - Хост-окно валидирует содержимое сообщения и отправляет обратно ответ
UpdateRequest
. - Если сообщение
UpdateRequest
невалидно, хост-окно отправляет в ответ сообщениеInvalidMessageError
, содержащее описание ошибочных полей.
Примеры сообщений UpdateRequest
смотрите в правой части экрана.
Здесь:
messageId
— целочисленный идентификатор сообщения, уникальный в рамках текущего взаимодействия виджет — хост-окно. Назначается виджетом;updateState
— список полей, которые необходимо изменить. Соответствует телу запроса для обновления соответствующего документа в JSON API, смотрите, например, Заказ покупателя.
Пример сообщения UpdateResponse
смотрите в правой части экрана.
Сообщение UpdateResponse
{
"name": "UpdateResponse",
"correlationId": 10
}
Здесь:
correlationId
— идентификатор соответствующего сообщенияUpdateRequest
.
Сообщение UpdateRequest для изменения дополнительных полей
{
"name":"UpdateRequest",
"messageId":10,
"updateState":{
"description": "красивое",
"attributes":[
{
"meta":{
"href":"https://api.moysklad.ru/api/remap/1.2/entity/customerorder/metadata/attributes/f6d39a2b-146f-11ec-0a80-072a002cf678",
"type":"attributemetadata"
},
"name":"Доставлено в срок",
"type":"boolean",
"value":true
},
{
"id":"f6d39d12-146f-11ec-0a80-072a002cf679",
"name":"Срок доставки, дней",
"type":"long",
"value":10
},
{
"id":"f6d39d12-146f-11ec-0a80-072a002cf678",
"value":45.78
}
]
}
}
Сообщение UpdateRequest для добавления позиций
{
"name":"UpdateRequest",
"messageId":10,
"updateState":{
"vatIncluded": true,
"positions": [
{
"quantity": 10,
"price": 100,
"discount": 0,
"vat": 0,
"assortment": {
"meta": {
"href": "https://api.moysklad.ru/api/remap/1.2/entity/product/8b382799-f7d2-11e5-8a84-bae5000003a5",
"type": "product"
}
},
"reserve": 10
},
{
"quantity": 1,
"price": 200,
"assortment": {
"meta": {
"href": "https://api.moysklad.ru/api/remap/1.2/entity/service/be903062-f504-11e5-8a84-bae50000019a",
"type": "service"
}
},
"pack": null
},
{
"quantity": 30,
"price": 300,
"discount": 0,
"vat": 18,
"assortment": {
"meta": {
"href": "https://api.moysklad.ru/api/remap/1.2/entity/bundle/c02e3a5c-007e-11e6-9464-e4de00000006",
"type": "bundle"
}
},
"pack": {
"id": "1bf22e62-8b47-11e8-56c0-000800000006"
},
"reserve": 30
}
]
}
}
Сообщение UpdateRequest для изменения существующих позиций
{
"name":"UpdateRequest",
"messageId":10,
"updateState":{
"vatIncluded": true,
"positions": [
{
"id": "be903062-f504-11e5-8a84-bae50000019a",
"price": 100,
"discount": -10
},
{
"id": "be903062-f504-11e5-8a84-bae500000123",
"quantity": 30,
"price": 300,
"discount": 0,
"vat": 18,
"assortment": {
"meta": {
"href": "https://api.moysklad.ru/api/remap/1.2/entity/bundle/c02e3a5c-007e-11e6-9464-e4de00000006",
"type": "bundle"
}
},
"reserve": 30
}
]
}
}
Сообщение UpdateRequest для добавления одной новой и сохранения трех существующих позиций
{
"name":"UpdateRequest",
"messageId":10,
"updateState":{
"vatIncluded": true,
"positions": [
{
"id": "be903062-f504-11e5-8a84-bae50000019a"
},
{
"id": "0fb51a51-e01d-48da-9035-4b21f5e69055"
},
{
"id": "ef34072d-5fd3-4ac4-b4b9-87458ca61da2"
},
{
"quantity": 1,
"price": 300,
"assortment": {
"meta": {
"href": "https://api.moysklad.ru/api/remap/1.2/entity/service/c02e3a5c-007e-11e6-9464-e4de00000006",
"type": "service"
}
}
}
]
}
}
Работа с полями из updateState
:
- Список может содержать одно или несколько полей для изменения.
- При изменении значения поля на то же самое, поле в интерфейсе не обновляется и документ не считается измененным. То есть пользователь может закрыть экран редактирования документа без диалога с вопросом «Данные были изменены. Сохранить изменения?». К позициям это не относится: если позиции в запросе есть, список всегда обновляется и документ считается измененным.
- Содержимое поля можно сбросить, указав в качестве его значения
null
. - Если пришло несколько сообщений подряд, все они обрабатываются последовательно.
- Поля типа Дата-время необходимо передавать с включением информации о часовом поясе, чтобы избежать неопределенности в интерпретации.
- Значения полей типа Дата-время всегда округляются до минут, секунды отбрасываются.
- Для значений ссылочных полей обязательными являются
meta.href
иmeta.type
, остальные поля внутриmeta
игнорируются. - Для одновременного изменения согласованных полей, таких как Организация (Контрагент), Счет, Договор, необходимо, чтобы их значения были совместимы. Счет должен принадлежать указанной организации, договор должен относиться к этой организации и контрагенту. Иначе возникнет ошибка валидации и значения полей в интерфейсе не изменятся.
Работа с дополнительными полями (attributes):
- Для идентификации дополнительного поля необходимо указать
meta.href
либоid
. Если указаны оба поля, значение берется изmeta.href
. - Для передачи значения поля служит поле
value
. - Остальные поля не являются обязательными.
- Пока не поддерживаются дополнительные поля типа Файл.
Работа с позициями (positions):
- При указании позиций в сообщении
UpdateRequest
существующие позиции в документе полностью заменяются позициями из сообщения. - Для редактирования позиции необходимо использовать
id
существующей позиции, например, получив их в сообщенииChange
или через JSON API. - При необходимости добавить новые позиции с сохранением существующих можно указать
id
существующих позиций и новые позиции. В результате останутся существующие позиции и добавятся новые. - До сохранения документа в базе данных у новых позиций отсутствует
id
. - Позиции добавляются в порядке, который указан в сообщении.
Подробнее о том, для каких точек поддерживается протокол update-provider, смотрите в статье.
Сервисы хост-окна
В виджетах доступны сервисы хост-окна:
Селектор группы товаров
Дескриптор приложения с виджетом, использующим селектор группы товаров
<ServerApplication xmlns="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2
https://apps-api.moysklad.ru/xml/ns/appstore/app/v2/application-v2.xsd">
<iframe>
<sourceUrl>https://example.com/iframe.html</sourceUrl>
<expand>true</expand>
</iframe>
<vendorApi>
<endpointBase>https://example.com/dummy-app</endpointBase>
</vendorApi>
<access>
<resource>https://api.moysklad.ru/api/remap/1.2</resource>
<scope>admin</scope>
</access>
<widgets>
<entity.counterparty.edit>
<sourceUrl>https://example.com/widget.php</sourceUrl>
<height>
<fixed>150px</fixed>
</height>
<uses>
<good-folder-selector/>
</uses>
</entity.counterparty.edit>
</widgets>
</ServerApplication>
Дескриптор приложения, iframe-часть и модальное окно которого используют селектор группы товаров
<ServerApplication xmlns="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2
https://apps-api.moysklad.ru/xml/ns/appstore/app/v2/application-v2.xsd">
<iframe>
<sourceUrl>https://example.com/iframe.html</sourceUrl>
<expand>true</expand>
<uses>
<good-folder-selector/>
</uses>
</iframe>
<vendorApi>
<endpointBase>https://example.com/dummy-app</endpointBase>
</vendorApi>
<access>
<resource>https://api.moysklad.ru/api/remap/1.2</resource>
<scope>admin</scope>
</access>
<popups>
<popup>
<name>coolPopup</name>
<sourceUrl>https://vendorurl.coolpopup.ru</sourceUrl>
<uses>
<good-folder-selector/>
</uses>
</popup>
</popups>
</ServerApplication>
Позволяет виджетам, основной iframe-части и модальным окнам приложений переиспользовать существующий в МоемСкладе селектор группы
товаров с получением ими результата выбора пользователя. Чтобы виджет, iframe-часть или модальное окно начали поддерживать селектор в дескрипторе, необходимо добавить в блоки widgets
, iframe
или popup
блок:
<uses>
<good-folder-selector/>
</uses>
Рассмотрим пример с виджетом. Когда виджет отправляет хост-окну сообщение SelectGoodFolderRequest
через Window.postMessage,
хост-окно запрашивает у пользователя выбор группы товаров, используя встроенный в МойСклад селектор:
Cообщение SelectGoodFolderRequest
{
"name": "SelectGoodFolderRequest",
"messageId": 12345
}
Здесь:
- messageId
— целочисленный идентификатор сообщения, уникальный в рамках текущего взаимодействия виджет — хост-окно. Назначается виджетом.
После совершения пользователем выбора группы товаров или отказа от него хост-окно передает виджету результат действий пользователя в сообщении SelectGoodFolderResponse
.
Cообщение SelectGoodFolderResponse (Пользователь выбрал группу товаров, имеющую идентификатор 8e9512f3-111b-11ea-0a80-02a2000a3c9c)
{
"name": "SelectGoodFolderResponse",
"correlationId": 12345,
"selected": true,
"goodFolderId": "8e9512f3-111b-11ea-0a80-02a2000a3c9c"
}
Здесь:
correlationId
— идентификатор соответствующего сообщенияSelectGoodFolderRequest
;selected
— признак наличия выбора;goodFolderId
— идентификатор выбранной группы товаров.
Cообщение SelectGoodFolderResponse (Пользователь отменил выбор)
{
"name": "SelectGoodFolderResponse",
"correlationId": 12345,
"selected": false
}
Стандартные диалоги
Дескриптор с виджетом, использующим стандартные диалоги
<ServerApplication xmlns="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2
https://apps-api.moysklad.ru/xml/ns/appstore/app/v2/application-v2.xsd">
<iframe>
<sourceUrl>https://example.com/iframe.html</sourceUrl>
<expand>true</expand>
</iframe>
<vendorApi>
<endpointBase>https://example.com/dummy-app</endpointBase>
</vendorApi>
<access>
<resource>https://api.moysklad.ru/api/remap/1.2</resource>
<scope>admin</scope>
</access>
<widgets>
<entity.counterparty.edit>
<sourceUrl>https://example.com/widget.php</sourceUrl>
<height>
<fixed>150px</fixed>
</height>
<uses>
<standard-dialogs/>
</uses>
</entity.counterparty.edit>
</widgets>
</ServerApplication>
Дескриптор с виджетом, использующим стандартные диалоги, позволяет виджетам приложений использовать существующие в МоемСкладе стандартные диалоги.
Чтобы виджет начал поддерживать протокол, в дескрипторе необходимо добавить блок:
<uses>
<standard-dialogs/>
</uses>
Когда виджет хочет показать пользователю стандартный диалог, он отправляет хост-окну сообщение ShowDialogRequest
. В сообщении указывается текст сообщения и кнопки, которые необходимо отобразить пользователю. Наример:
Cообщение ShowDialogRequest
{
"name": "ShowDialogRequest",
"messageId": 12345,
"dialogText": "Учетная запись будет удалена. Вы хотите продолжить?",
"buttons": [
{"name": "Yes", "caption": "Да, удалить"},
{"name": "No", "caption": "Нет"}
] 3
}
Параметры сообщения ShowDialogRequest
:
messageId
— целочисленный идентификатор сообщения, уникальный в рамках текущего взаимодействия виджет — хост-окно. Назначается виджетом;dialogText
— текст сообщения, который нужно отобразить пользователю МоегоСклада. Максимальный размер — 4096 символов. HTML-теги не допускаются (будут экранированы);buttons
— список кнопок в диалоге, элементами которого являются объекты с двумя обязательными полями:name
— имя кнопки, будет возвращено в сообщенииShowDialogResponse
,caption
— текст, отображаемый на кнопке. Максимальный размер поляcaption
— 100 символов. HTML-теги в нем не допускаются (будут экранированы).
После того, как пользователь нажимает кнопку в диалоге или принудительно закрывает хост-окно (нажимает на «крестик»), результат действий пользователя отображается в сообщении ShowDialogResponse
.
Cообщение ShowDialogResponse (Пользователь нажимает кнопку Нет)
{
"name": "ShowDialogResponse",
"correlationId": 12345,
"buttonName": "No",
"dialogResolution": "normal"
}
Параметры ответа ShowDialogResponse
:
correlationId
— идентификатор соответствующего сообщенияShowDialogResponse
;dialogResolution
— признак выбора:normal
— была нажата одна из кнопок,closedByUser
— диалог был завершен принудительно;buttonName
— имя выбранной кнопки.
Cообщение ShowDialogResponse (Пользователь закрыл диалог, нажав на «крестик»)
{
"name": "ShowDialogResponse",
"correlationId": 12345,
"dialogResolution": "closedByUser"
}
Примечание: В версии Google Chrome 92.0 и выше использование браузерных диалоговых окон через вызовы Window.alert(), Window.confirm() из iframe запрещено. Поэтому рекомендуется использовать сервис стандартных диалогов МоегоСклада.
Протокол навигации
Дескриптор с виджетом, использующим протокол навигации
<ServerApplication xmlns="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2
https://apps-api.moysklad.ru/xml/ns/appstore/app/v2/application-v2.xsd">
<iframe>
<sourceUrl>https://example.com/iframe.html</sourceUrl>
<expand>true</expand>
</iframe>
<vendorApi>
<endpointBase>https://example.com/dummy-app</endpointBase>
</vendorApi>
<access>
<resource>https://api.moysklad.ru/api/remap/1.2</resource>
<scope>admin</scope>
</access>
<widgets>
<entity.counterparty.edit>
<sourceUrl>https://example.com/widget.php</sourceUrl>
<height>
<fixed>150px</fixed>
</height>
<uses>
<navigation-service/>
</uses>
</entity.counterparty.edit>
</widgets>
</ServerApplication>
Дескриптор приложения, у которого iframe-часть и модальное окно используют протокол навигации
<ServerApplication xmlns="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2
https://apps-api.moysklad.ru/xml/ns/appstore/app/v2/application-v2.xsd">
<iframe>
<sourceUrl>https://example.com/iframe.html</sourceUrl>
<expand>true</expand>
<uses>
<navigation-service/>
</uses>
</iframe>
<vendorApi>
<endpointBase>https://example.com/dummy-app</endpointBase>
</vendorApi>
<access>
<resource>https://api.moysklad.ru/api/remap/1.2</resource>
<scope>admin</scope>
</access>
<popups>
<popup>
<name>coolPopup</name>
<sourceUrl>https://vendorurl.coolpopup.ru</sourceUrl>
<uses>
<navigation-service/>
</uses>
</popup>
</popups>
</ServerApplication>
Позволяет виджетам, iframe-части и модальным окнам приложений осуществлять переход на другую страницу МоегоСклада и открывать МойСклад в новой вкладке. Чтобы виджет, iframe или модальное окно начали поддерживать протокол навигации в дескрипторе необходимо добавить в блоки widgets
, iframe
или popup
блок:
<uses>
<navigation-service/>
</uses>
Примеры смотрите в правой части экрана.
Рассмотрим пример с виджетом. Когда виджет отправляет хост-окну сообщение NavigateRequest
(через Window.postMessage), хост-окно переходит на другую страницу МоегоСклада или открывает в новой вкладке браузера нужную страницу МоегоСклада.
Cообщение NavigateRequest
{
"name": "NavigateRequest",
"messageId": 12345,
"path": "#good/edit?id=e8a46787-0ff4-11ec-0a80-1eb200000740",
"target": "blank"
}
Параметры сообщения NavigateRequest
:
messageId
— целочисленный идентификатор сообщения, уникальный в рамках текущего взаимодействия виджет — хост-окно. Назначается виджетом.path
— путь до страницы, на которую виджет хочет осуществить переход. Например, чтобы осуществить переход пользователя на страницу реестра заказов покупателя https://online.moysklad.ru/app/#customerorder, нужно передать#customerorder
.target
— вид навигации. Может принимать одно из двух значений:self
— переход в текущей вкладке,blank
— открытие в новой вкладке.
Если валидация сообщения пройдет успешно, перед переходом пользователя будет отправлен NavigateResponse
обратно в виджет.
Cообщение NavigateResponse
{
"name": "NavigateResponse",
"correlationId": 12345
}
Параметры ответа NavigateResponse
:
correlationId
— идентификатор соответствующего сообщенияNavigateRequest
.
При навигации из модального окна в текущей вкладке (target
имеет значение self
) произойдет переход, и модальное окно будет отображаться поверх страницы. Если необходимо, чтобы после перехода окно закрывалось, используйте сообщение ClosePopup
. Подробнее смотрите в разделе Кастомные модальные окна.
Кастомные модальные окна
Кастомные модальные окна позволяют расширить функциональность iframe-части и виджетов с фиксированной высотой и шириной.
Характеристики кастомного модального окна:
- Открывается на весь экран аналогично прочим модальным окнам в интефейсе МоегоСклада. Например, вызываемое через иконку «карандаш» окно редактирования сущностей в полях.
- Является модальным, то есть открывается поверх текущей страницы МоегоСклада, и требует действия от пользователя внутри этого окна — взаимодействия с веб-страницей и/или закрытие окна.
- Содержимое окна определяет разработчик. Передавать данные можно от виджета и iframe-части в модальное окно и в обратном направлении.
- Может получать текущий контекст пользователя, так же как виджеты и основная iframe-часть приложения.
- В качестве заголовка окна всегда используется название приложения.
- Имеет кнопку принудительного закрытия для пользователя — «крестик» справа вверху.
- Изменяет свои размеры при изменении размеров окна браузера.
Рассмотрим работу кастомных модальных окон на примере виджетов. В случае iframe-части все работает аналогично.
Чтобы виджет использовал кастомные модальные окна, добавьте в дескриптор приложения блок:
<popups>
...
</popups>
Пример дескриптора с блоком popups
смотрите в правой части экрана.
Дескриптор с виджетом и iframe-частью, использующие кастомные модальные окна
<ServerApplication xmlns="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2
https://apps-api.moysklad.ru/xml/ns/appstore/app/v2/application-v2.xsd">
<iframe>
<sourceUrl>https://example.com/iframe.html</sourceUrl>
<expand>true</expand>
</iframe>
<vendorApi>
<endpointBase>https://example.com/dummy-app</endpointBase>
</vendorApi>
<access>
<resource>https://api.moysklad.ru/api/remap/1.2</resource>
<scope>admin</scope>
</access>
<widgets>
<entity.counterparty.edit>
<sourceUrl>https://example.com/widget.php</sourceUrl>
<height>
<fixed>150px</fixed>
</height>
<uses>
<good-folder-selector/>
</uses>
</entity.counterparty.edit>
</widgets>
<popups>
<popup>
<name>somePopup</name>
<sourceUrl>https://example.com/popup.php</sourceUrl>
</popup>
<popup>
<name>somePopup2</name>
<sourceUrl>https://example.com/popup-2.php</sourceUrl>
</popup>
</popups>
</ServerApplication>
Сообщение ShowPopupRequest
{
"name": "ShowPopupRequest",
"messageId": 12,
"popupName": "somePopup",
"popupParameters": "hello"
}
Сообщение OpenPopup
{
"name": "OpenPopup",
"messageId": 36,
"popupName": "somePopup",
"popupParameters": "hello"
}
Сообщение ClosePopup
{
"name": "ClosePopup",
"messageId": 17,
"popupResponse": "world"
}
Сообщение ShowPopupResponse
{
"name": "ShowPopupResponse",
"correlationId": 12,
"popupName": "somePopup",
"popupResolution": "normal",
"popupResponse": "world"
}
Виджет может отобразить одно из кастомных модальных окон, отправив сообщение ShowPopupRequest
с именем выбранного окна хост-окну. Пример такого сообщения смотрите в правой части экрана.
Здесь:
messageId
— идентификатор сообщения;popupName
— имя открываемого окна;popupParameters
— опциональные параметры, передаваемые окну виджетом. Может иметь любой тип, в том числеnull
.
МойСклад проверяет сообщение ShowPopupRequest
. Если сообщение валидно, отображается модальное окно: загружается страница окна по адресу sourceUrl
в iframe, в GET-параметре передается contextKey
. Процесс аналогичен загрузке виджета. Значение sourceUrl
загружается из соответствующего элемента списка модальных окон <popups>
в дескрипторе. Поиск производится по popupName
, переданному в сообщении.
После загрузки модального окна хост-окно отправляет ему сообщение OpenPopup
. Набор полей тот же, что и в ShowPopupRequest
.
При этом messageId
в данном сообщении свой, а не тот, что был передан в сообщении ShowPopupRequest
.
Для закрытия модального окна, оно отправляет сообщение ClosePopup
хост-окну. Пример такого сообщения смотрите в правой части экрана.
Здесь:
messageId
— идентификатор сообщения;popupResponse
— опциональный ответ, возвращаемый виджету. Может иметь любой тип, в том числеnull
.
МойСклад, в свою очередь, отправляет сообщение ShowPopupResponse
виджету, открывшему окно. Пример такого сообщения смотрите в правой части экрана.
Здесь:
correlationId
— идентификатор соответствующего сообщения ShowPopupRequest;popupName
— имя открывавшегося модального окна;popupResolution
— вариант, по которому произошло закрытие модального окна:normal
— нормальное закрытие окна поClosePopup
,closedByUser
— закрытие окна пользователем путем нажатия на «крестик»;popupResponse
— опциональный ответ, возвращаемый виджету.
Страницы кастомных модальных окон кэшируются аналогично кэшированию виджетов. При повторном открытии окна по сообщению ShowPopupRequest
переиспользуется ранее загруженный iframe.
Ниже приводятся примеры работы с кастомными модальными окнами.
Пример работы без возврата параметров из модального окна
Пример взаимодействия без передачи дополнительных параметров
// виджет -> хост-окно
{
"name": "ShowPopupRequest",
"messageId": 12,
"popupName": "somePopup"
}
// хост-окно -> модальное окно
{
"name": "OpenPopup",
"messageId": 35,
"popupName": "somePopup"
}
// хост-окно -> виджет
{
"name": "ShowPopupResponse",
"correlationId": 12,
"popupName": "somePopup",
"popupResolution": "closedByUser"
}
Пример взаимодействия с передачей параметров в виде строки
// виджет -> хост-окно
{
"name": "ShowPopupRequest",
"messageId": 17,
"popupName": "somePopup",
"popupParameters": "hello"
}
// хост-окно -> модальное окно
{
"name": "OpenPopup",
"messageId": 36,
"popupName": "somePopup",
"popupParameters": "hello"
}
// хост-окно -> виджет
{
"name": "ShowPopupResponse",
"correlationId": 17,
"popupName": "somePopup",
"popupResolution": "closedByUser"
}
- Виджет отправляет хост-окну сообщение
ShowPopupRequest
. В сообщении указывается имя модального окна и опциональные параметры. - Хост-окно отображает модальное окно, загружая страницу окна по адресу
sourceUrl
в iframe с передачейcontextKey
в GET-параметре. - Хост-окно отправляет в iframe модального окна сообщение
OpenPopup
, передавая в нем опциональные параметры от виджета. - Пользователь взаимодействует с веб-содержимым модального окна, после чего закрывает его через системную кнопку («крестик»), находящуюся в верхнем правом углу окна.
- Система скрывает модальное окно и отправляет виджету сообщение
ShowPopupResponse
с указанием того, что окно было закрыто пользователем через системную кнопку ("popupResolution": "closedByUser"
).
Пример модального окна с наличием только системной кнопки закрытия:
Закрытие модального окна с использованием сообщения ClosePopup
...
// модальное окно -> хост-окно
{
"name": "ClosePopup",
"messageId": 37,
}
// хост-окно -> виджет
{
"name": "ShowPopupResponse",
"correlationId": 14,
"popupName": "somePopup",
"popupResolution": "normal"
}
Разработчик может отобразить на странице и собственную кнопку закрытия окна. При нажатии на нее будет отправляться сообщение ClosePopup
, а виджет получит сообщение ShowPopupResponse
с "popupResolution": "normal"
.
Пример работы с возвратом параметров из модального окна
Пример ответа с передачей данных о нажатой кнопке
// виджет -> хост-окно
{
"name": "ShowPopupRequest",
"messageId": 29,
"popupName": "somePopup"
}
// хост-окно -> модальное окно
{
"name": "OpenPopup",
"messageId": 36,
"popupName": "somePopup"
}
// пользователь нажимает на кнопку «Сохранить»
// модальное окно -> хост-окно
{
"name": "ClosePopup",
"messageId": 44,
"popupResponse": "save"
}
// хост-окно -> виджет
{
"name": "ShowPopupResponse",
"correlationId": 29,
"popupName": "somePopup",
"popupResolution": "normal",
"popupResponse": "save"
}
Если модальному окну требуется вернуть информацию обратно в виджет, окно должно передать ее в поле popupResponse
сообщения ClosePopup
.
- Виджет отправляет хост-окну сообщение
ShowPopupRequest
, указывая в нем имя модального окна и опциональные параметры. - Хост-окно отображает модальное окно, загружая его страницу по адресу
sourceUrl
в iframe с передачейcontextKey
в GET-параметре. - Хост-окно отправляет в iframe модального окна сообщение
OpenPopup
с опциональными параметрами. - Пользователь взаимодействует с веб-содержимым модального окна, после чего нажимает кнопку закрытия или сохранения, находящуюся внутри страницы модального окна.
- Модальное окно отправляет хост-окну сообщение
ClosePopup
. В нем передаются параметры, которые зависят от действий пользователя, например тип нажатой кнопки. - Система скрывает модальное окно и отправляет виджету сообщение
ShowPopupResponse
с указанием параметров, переданных модальным окном.
Пользователь может закрыть модальное окно принудительно. При этом параметры в виджет не будут переданы.
Пример модального окна с кнопками «Сохранить» и «Отмена»:
Отображение содержимого, которое не вмещается в окно целиком
Пример плавающей верстки содержимого
<!doctype html>
<html lang="ru">
<head>
<meta charset="utf-8">
<title>Popup example</title>
<style>
body {
overflow: hidden;
}
.main-container {
display: flex;
flex-direction: column;
height: 100vh;
}
.content-container {
overflow: auto;
flex-grow: 1;
}
.buttons-container {
padding-top: 15px;
min-height: 55px;
}
</style>
<link rel="stylesheet" href="css/uikit.css">
</head>
<body>
<div class="main-container">
<div class="content-container">
<!--Разместите содержимое здесь -->
</div>
<div class="buttons-container">
<button class="button button--success">Сохранить</button>
<button class="button">Отмена</button>
</div>
</div>
</body>
</html>
Если во модальном окне нужно отобразить содержимое, которое может не поместиться на экране пользователя, используйте плавающую верстку. Так вы можете создать полосы прокрутки для содержимого, и кнопки закрытия окна всегда будут отображаться в нижней части окна.
Пример модального окна с полосами прокрутки:
Пример такой верстки с использованием UI Kit представлен справа.
Способы передачи параметров
Пример взаимодействия с передачей параметров в виде строки
// виджет -> хост-окно
{
"name": "ShowPopupRequest",
"messageId": 12,
"popupName": "somePopup",
"popupParameters": "hello"
}
// хост-окно -> модальное окно
{
"name": "OpenPopup",
"messageId": 35,
"popupName": "somePopup",
"popupParameters": "hello"
}
Пример взаимодействия с передачей параметров в виде объекта
// виджет -> хост-окно
{
"name": "ShowPopupRequest",
"messageId": 12,
"popupName": "somePopup",
"popupParameters": {
"aaa": 1,
"bbb": "qwerty"
}
}
// хост-окно -> модальное окно
{
"name": "OpenPopup",
"messageId": 35,
"popupName": "somePopup",
"popupParameters": {
"aaa": 1,
"bbb": "qwerty"
}
}
Пример взаимодействия с передачей параметров в виде массива
// виджет -> хост-окно
{
"name": "ShowPopupRequest",
"messageId": 12,
"popupName": "somePopup",
"popupParameters": [123, "foobar"]
}
// хост-окно -> модальное окно
{
"name": "OpenPopup",
"messageId": 35,
"popupName": "somePopup",
"popupParameters": [123, "foobar"]
}
Существует несколько способов передачи параметров между виджетами и модальными окнами:
- передача в виде примитивного значения;
- передача в виде объекта;
- передача в виде массива, в том числе массива объектов;
- передача в виде значения
null
.
Справа приведены примеры передачи параметров из виджета в модальное окно через сообщение ShowPopupRequest
.
Аналогичные способы передачи можно использовать для возврата ответа в сообщении ClosePopup
.
Ошибки
При получении сообщения от виджета хост-окно производит валидацию сообщения. Если проверка не пройдена, хост-окно возвращает в ответ сообщение InvalidMessageError
со списком ошибок.
Пример сообщения InvalidMessageError
{
"name": "InvalidMessageError",
"correlationId": null,
"invalidMessage": {
"name": "SelectGoodFolderRequest"
},
"errors": [
{
"code": 1001,
"error": "Отсутствует обязательный параметр messageId"
}
]
}
Пример такого сообщения смотрите в правой части экрана.
Здесь:
correlationId
— идентификатор сообщения, которое вызвало ошибку;invalidMessage
— исходное сообщение, которое вызвало ошибку;errors
— список ошибок, каждое из которых содержит поля:code
— код ошибки иerror
— описание ошибки.
Перечень возможных ошибок представлен в таблице.
Код ошибки | Сообщение | Описание | Пример |
---|---|---|---|
1000 | Недопустимое состояние виджета %state% для сообщения %message.name%, допустимые состояния: %message.expectedStates% | Не допускается отправка данного сообщения из текущего состояния виджета | Недопустимое состояние виджета Opened для сообщения OpenFeedback, допустимые состояния: Opening |
1001 | Отсутствует обязательный параметр %parameter.name% | В сообщении отсутствует обязательный параметр | Отсутствует обязательный параметр messageId |
1002 | Некорректное значение параметра %parameter.name%: %пояснение% | Параметр сообщения имеет некорректное значение | Некорректное значение параметра popupName: popup with name = 'somePopup1' not found |
1003 | Параллельный запрос %message.name% | Не допускается отправка виджетом повторного запроса до момента получения ответа на предыдущий такой же запрос. Например: виджет уже отправил SelectGoodFolderRequest и пользователь еще не завершил выбор |
Параллельный запрос SelectGoodFolderRequest |
1004 | Виджет не поддерживает протокол для обработки сообщения %message.name% | Не допускается обработка сообщения в виджете, который не поддерживает протокол данного сообщения | Виджет не поддерживает протокол для обработки сообщения ShowDialogRequest |
Дескриптор приложения
Дескриптор приложения — XML-структура, которая описывает технические параметры встраивания/интеграции приложения разработчика в МойСклад.
Содержимое дескриптора должно соответствовать версии XSD-схемы. Актуальной версией считается v2.
История версий XSD-схемы дескриптора
Версия | Описание | Разрешенное содержимое дескриптора | Поддерживаемые типы приложений |
---|---|---|---|
1.0.0 | Серверные и простые iFrame-приложения | vendorApi, access, iframe | iFrame, Серверные |
1.1.0 | Расширение iFrame (тег expand) | vendorApi, access, iframe(c expand) | iFrame, Серверные |
v2 | Виджеты в документах и сущностях. Кастомные модальные окна. Гибкие права приложений. Дополнительные и сервисные протоколы. | vendorApi, access(с permissions), iframe(c expand), widgets, popups | Серверные |
Основные отличия дескриптора v2 от дескрипторов версий 1.x.x:
- Изменение корневого тега — теперь каждый тип приложений представлен своим корневым тегом. В версии v2 есть только один тип приложений — ServerApplication (iframe-приложения объявлены deprecated и с отменой поддержки дескрипторов версий 1.x.x станут недоступны).
- Добавлен необязательный блок widgets для указания конфигурации виджетов приложения.
- Добавлен необязательный блок popups для указания конфигурации модального окна приложения.
Дескрипторы версий 1.x.x некоторое время будут продолжать поддерживаться.
Содержимое дескриптора приложения
<ServerApplication xmlns="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2
https://apps-api.moysklad.ru/xml/ns/appstore/app/v2/application-v2.xsd">
<iframe>
<sourceUrl>https://example.com/iframe.html</sourceUrl>
<expand>true</expand>
</iframe>
<vendorApi>
<endpointBase>https://example.com/dummy-app</endpointBase>
</vendorApi>
<access>
<resource>https://api.moysklad.ru/api/remap/1.2</resource>
<scope>custom</scope>
<permissions>
<viewDashboard/>
<customerOrder>
<view/>
<create/>
<update/>
</customerOrder>
</permissions>
</access>
<widgets>
<entity.counterparty.edit>
<sourceUrl>https://example.com/widget.php</sourceUrl>
<height>
<fixed>150px</fixed>
</height>
<uses>
<good-folder-selector/>
<standard-dialogs/>
<navigation-service/>
</uses>
</entity.counterparty.edit>
</widgets>
<popups>
<popup>
<name>somePopup</name>
<sourceUrl>https://example.com/popup.php</sourceUrl>
</popup>
<popup>
<name>somePopup2</name>
<sourceUrl>https://example.com/popup-2.php</sourceUrl>
</popup>
</popups>
</ServerApplication>
В актуальной версии дескриптора приложения четыре блока: iframe, vendorApi, access, widgets, popups. Порядок расположения этих блоков относительно друг друга в дескрипторе может быть произвольным.
Блок | Назначение | Доступно для типов приложений | Требует наличия других блоков |
---|---|---|---|
iframe | Описывает iframe-часть приложения | Серверные приложения | Не требует |
vendorApi | Описывает взаимодействие по Vendor API | Серверные приложения | Не требует |
access | Описывает требуемый доступ приложения к ресурсам пользовательского аккаунта | Серверные приложения | vendorApi |
widgets | Описывает виджеты | Серверные приложения | Не требует |
popups | Описывает кастомные модальные окна | Серверные приложения | Не требует |
Блок iframe
В теге iframe/sourceUrl указывается URL, по которому будет загружаться содержимое главного iframe внутри UI МоегоСклада. В URL допускается использование только протокола HTTPS.
В случае отсутствия блока iframe в дескрипторе считается, что у приложения отсутствует iframe-часть.
В теге iframe/expand указывается boolean
значение, которое отвечает, будет ли iframe расширяться в основном
приложении
МоегоСклада. Под расширением подразумевается автоматическое изменение высоты iframe-элемента в зависимости от контента.
Данный элемент является опциональным со значением false
по умолчанию, но требуется его установка для случаев, когда
содержимое не умещается в окне браузера.
Если значение установлено (expand=true
), то на странице, указанной в sourceUrl, должен работать скрипт,
оповещающий страницу МоегоСклада об изменении высоты его контента. Для этого необходимо реализовать отправку сообщения
EventMessage
при любом изменении высоты контента. При этом:
- сообщение необходимо послать родительскому окну (
parent
); - данные сообщения должны содержать свойство
height
— высоту страницы, которая сейчас отображается, в пикселях.
Примечание: для того чтобы не реализовывать это поведение самостоятельно, можно подключить следующий js скрипт на свою страницу.
Тег uses — опциональный. Предназначен для сервисных протоколов, используемых iframe. На данный момент в нем можно указать следующие протоколы:
- good-folder-selector позволяет iframe-частям переиспользовать существующий в МоемСкладе селектор группы товаров с получением результата выбора пользователя. Параметры у протокола отсутствуют. Подробнее о протоколе читайте в разделе Селектор группы товаров.
- navigation-service позволяет iframe-частям приложений осуществлять переход на другую страницу МоегоСклада и открывать МойСклад в новой вкладке. Параметры у протокола отсутствуют. Подробнее о протоколе читайте в разделе Протокол навигации.
Блок vendorApi
В теге vendorApi/endpointBase указывается базовый URL эндпоинта на стороне разработчика, к которому будет обращаться МойСклад. В URL допускается использование только протокола HTTPS.
Для получения полного адреса конкретного эндпоинта Vendor API на стороне разработчика к базовому URL добавляется суффикс
/api/moysklad/vendor/1.0
и путь эндпоинта. Шаблон формирования полного URL ресурса в общем случае такой:
{endpointBase}/api/moysklad/vendor/1.0/{endpointPath}/…
Для эндпоинта активации/деактивации приложений на аккаунте шаблон следующий (endpointPath = apps):
{endpointBase}/api/moysklad/vendor/1.0/apps/{appId}/{accountId}
Например, если:
https://example.com/dummy-app - appId = 5f3c5489-6a17-48b7-9fe5-b2000eb807fe
- accountId = f088b0a7-9490-4a57-b804-393163e7680f
- endpointPath = apps
то полный URL ресурса на стороне разработчика, к которому будет выполнять запросы МойСклад при активации и деактивации приложения на аккаунте, будет следующим:
https://example.com/dummy-app/api/moysklad/vendor/1.0/apps/5f3c5489-6a17-48b7-9fe5-b2000eb807fe/f088b0a7-9490-4a57-b804-393163e7680f
В случае отсутствия блока vendorApi в дескрипторе не выполняется активация и деактивация приложения на серверах разработчика.
Блок access
Требуется для серверных приложений, которые хотят получить доступ по JSON API к ресурсам аккаунта. В случае отсутствия этого блока в дескрипторе приложения при установке на аккаунт приложению не выдаются никакие доступы к ресурсам. Наличие блока access требует наличия блока vendorApi для передачи токена к ресурсам аккаунта при активации приложения по Vendor API.
В теге access/resource указывается ресурс, к которому приложению нужен доступ.
На текущий момент для ресурса возможно только одно значение: https://api.moysklad.ru/api/remap/1.2
В теге access/scope указывается требуемый уровень доступа.
Для него на текущий момент доступно два значения: admin
и custom
.
- Если указан уровень
admin
, приложение будет работать с правами администратора аккаунта. - Если указан уровень
custom
, приложение получит доступ только к отчетам, документам и сущностям, перечисленным в теге permissions.
В теге access/permissions указываются требуемые пермиссии.
Данный тег обязателен для уровня доступа со значением custom
.
Пример заполнения блока access с указанием прав Администратора:
<access>
<resource>https://api.moysklad.ru/api/remap/1.2</resource>
<scope>admin</scope>
</access>
Пример заполнения блока access с явным перечислением пермиссий:
<access>
<resource>https://api.moysklad.ru/api/remap/1.2</resource>
<scope>custom</scope>
<permissions>
<viewDashboard/>
<viewAudit/>
<viewProductCostAndProfit/>
<customerOrder>
<view/>
<create/>
<update/>
<delete/>
<approve/>
<print/>
</customerOrder>
<company>
<view/>
<create/>
</company>
</permissions>
</access>
Перечисленные в теге permissions права доступа могут включать в себя:
- Пользовательские — права доступа, в которых достаточно указать только название. Позволяют получить доступ к отчетам в МоемСкладе. Могут принимать следующие значения: viewDashboard, viewAudit, viewSaleProfit, viewTurnover, viewCompanyCRM, viewProfitAndLoss, viewMoneyDashboard, viewProductCostAndProfit
- Сущностей — права доступа, в которых помимо названия необходимо указывать так же уровни доступа к соответствующим сущностям и документам: view, create, update и другие.
Есть три типа значений для пермиссий сущностей:
OPERATION (view, create, update, delete, print, approve) — purchaseOrder, invoiceIn, supply, purchaseReturn, factureIn, customerOrder, invoiceOut, demand, commissionReportIn, commissionReportOut, salesReturn, factureOut, enter, loss, internalOrder, move, priceList, paymentIn, paymentOut, cashIn, cashOut, retailDemand, retailSalesReturn, retailDrawerCashIn, retailDrawerCashOut, bonusTransaction, prepayment, prepaymentReturn, processing, processingOrderю.
DICTIONARY (view, create, update, delete, print) — good, inventory, company, contract, retailShift.
BASE (view, create, update, delete, print) — retailStore, processingPlan, myCompany, employee, warehouse, currency, project, country, uom, customEntity.
Подробнее о пермиссиях в МоемСкладе смотрите в документации JSON API.
Примечания:
- Имеются два ограничения на сочетания пермиссий сущностей:
- уровень доступа
<view/>
необходим, если есть другие уровни; - уровень доступа
<update/>
необходим, если требуется уровень<delete/>
.
- уровень доступа
- При установке приложения ему будет автоматически предоставлено право на просмотр справочника
Валют (
<currency><view/></currency>
). - В настоящий момент нет отдельной пермиссии для работы с веб-хуками. Приложение, которое хочет получить доступ к ним, должно работать с правами администратора.
- В настоящий момент не поддерживается пермиссия для работы с Задачами (
script
). Приложение, которое хочет получить доступ к ним, должно работать с правами администратора. - В настоящий момент не поддерживаются пермиссии для работы с сущностями Маркировки:
crptCancellation
,crptPackageCreation
,crptPackageItemRemoval
,crptPackageDisaggregation
,GTINList
,trackingCodeList
.
Блок widgets
Сейчас доступны следующие точки расширения:
- entity.counterparty.edit — карточка Контрагента
- entity.product.edit — карточка Товара
- entity.variant.edit — карточка Модификации
- entity.service.edit — карточка Услуги
- entity.bundle.edit — карточка Комплекта
- entity.productfolder.edit — карточка Группы товаров
- document.customerorder.create — новый документ Заказ покупателя (до первого сохранения)
- document.customerorder.edit — документ Заказ покупателя
- document.demand.create — новый документ Отгрузка (до первого сохранения)
- document.demand.edit — документ Отгрузка
- document.invoiceout.create — новый документ Счет покупателю (до первого сохранения)
- document.invoiceout.edit — документ Счет покупателю
- document.invoicein.create — новый документ Счет поcтавщика (до первого сохранения)
- document.invoicein.edit — документ Счет поcтавщика
- document.processingorder.edit — документ Заказ на производство
- document.purchaseorder.edit — документ Заказ поставщику
- document.supply.create — новый документ Приемка (до первого сохранения)
- document.supply.edit — документ Приемка
- document.paymentin.edit — документ Входящий платеж
- document.paymentout.edit — документ Исходящий платеж
- document.cashin.edit — документ Приходный ордер
- document.cashout.edit — документ Расходный ордер
- document.move.create — новый документ Перемещение (до первого сохранения)
- document.move.edit — документ Перемещение
- document.loss.create — новый документ Списание (до первого сохранения)
- document.loss.edit — документ Списание
- document.enter.create — новый документ Оприходование (до первого сохранения)
- document.enter.edit — документ Оприходование
- document.internalorder.edit — документ Внутренний заказ
- document.inventory.edit — документ Инвентаризация
- document.purchasereturn.edit — документ Возврат поставщику
- document.salesreturn.create — новый документ Возврат покупателя
- document.salesreturn.edit — документ Возврат покупателя
- document.retaildemand.create — новый документ Розничная продажа
- document.retaildemand.edit — документ Розничная продажа
- document.retailsalesreturn.edit — документ Розничный возврат
- document.retaildrawercashin.edit — документ Внесение денег
- document.retaildrawercashout.edit — документ Выплата денег
Блок widgets с точками расширения в контрагенте и заказе покупателя
<widgets>
<entity.counterparty.edit>
<sourceUrl>https://example.com/widget-counterparty.php</sourceUrl>
<height>
<fixed>200px</fixed>
</height>
<supports>
<open-feedback/>
</supports>
</entity.counterparty.edit>
<document.customerorder.create>
<sourceUrl>https://example.com/widget-customerorder-create.php</sourceUrl>
<height>
<fixed>50px</fixed>
</height>
</document.customerorder.create>
<document.customerorder.edit>
<sourceUrl>https://example.com/widget-customerorder.php</sourceUrl>
<height>
<fixed>50px</fixed>
</height>
<uses>
<good-folder-selector/>
<standard-dialogs/>
<navigation-service/>
</uses>
</document.customerorder.edit>
</widgets>
Сначала необходимо определить в блоке widgets точку расширения — указать страницу, где будет расположен виджет.
В одном дескрипторе может быть указано несколько точек расширения, то есть одно приложение сможет создать сразу несколько виджетов на разных страницах. В то же время для приложения действует правило: одна страница — один виджет. То есть, в дескрипторе может быть указано только по одной точке расширения каждого типа.
Тем не менее в итоге на одной странице может оказаться несколько виджетов (от разных приложений).
Список тегов для точек расширения:
Тег sourceUrl — обязательный. Содержит URL, по которому загружается код виджета в iframe. В URL допускается использование только протокола HTTPS.
Тег height — обязательный. В теге height/fixed задается фиксированная высота виджета в пикселях, в формате 150px.
Пример заполненного блока widgets можно увидеть справа.
Блок дополнительных протоколов (supports)
Блок supports — опциональный. Предназначен для дополнительных протоколов, поддерживаемых виджетом. На данный момент в нем можно указать протоколы:
- open-feedback — при открытии экрана обеспечивает скрытие содержимого виджета до явного уведомления от виджета о готовности. Параметры у протокола отсутствуют.
- save-handler — при сохранении сущности или объекта позволяет уведомить об этом виджет. Параметры у протокола отсутствуют.
- dirty-state — при наличии несохраненных изменений в виджете позволяет отобразить диалог подтверждения сохранения изменений. Параметры у протокола отсутствуют.
- change-handler — при изменении несохраненного состояния объекта позволяет уведомить об этом виджет, отправляя
текущее состояние объекта. Параметры:
- validation-feedback — виджет поддерживает протокол валидации. Хост-окно будет ожидать от виджета
сообщение
ValidationFeedback
в ответ на сообщениеChange
.
- validation-feedback — виджет поддерживает протокол валидации. Хост-окно будет ожидать от виджета
сообщение
- update-provider — позволяет менять текущее состояние объекта отправляя сообщение
UpdateRequest
из виджета. Параметры у протокола отсутствуют.
Доступность дополнительных протоколов в зависимости от точек встраивания
Точка встраивания | open-feedback | save-handler | dirty-state | change-handler | validation-feedback | update-provider |
---|---|---|---|---|---|---|
entity.counterparty.edit | ✅ | ✅ | ✅ | ⬜ | ⬜ | ⬜ |
entity.product.edit | ✅ | ✅ | ✅ | ⬜ | ⬜ | ⬜ |
entity.variant.edit | ✅ | ✅ | ✅ | ⬜ | ⬜ | ⬜ |
entity.service.edit | ✅ | ✅ | ✅ | ⬜ | ⬜ | ⬜ |
entity.bundle.edit | ✅ | ✅ | ✅ | ⬜ | ⬜ | ⬜ |
entity.productfolder.edit | ✅ | ✅ | ✅ | ⬜ | ⬜ | ⬜ |
document.customerorder.create | ⬜ | ⬜ | ⬜ | ✅ | ✅ | ⬜ |
document.customerorder.edit | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
document.demand.create | ⬜ | ⬜ | ⬜ | ✅ | ✅ | ⬜ |
document.demand.edit | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
document.invoiceout.create | ⬜ | ⬜ | ⬜ | ✅ | ✅ | ⬜ |
document.invoiceout.edit | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
document.invoicein.create | ⬜ | ⬜ | ⬜ | ✅ | ✅ | ⬜ |
document.invoicein.edit | ✅ | ✅ | ✅ | ✅ | ✅ | ⬜ |
document.processingorder.edit | ✅ | ✅ | ✅ | ⬜ | ⬜ | ⬜ |
document.purchaseorder.edit | ✅ | ✅ | ✅ | ⬜ | ⬜ | ⬜ |
document.supply.create | ⬜ | ⬜ | ⬜ | ✅ | ✅ | ⬜ |
document.supply.edit | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
document.paymentin.edit | ✅ | ✅ | ✅ | ⬜ | ⬜ | ⬜ |
document.paymentout.edit | ✅ | ✅ | ✅ | ⬜ | ⬜ | ⬜ |
document.cashin.edit | ✅ | ✅ | ✅ | ⬜ | ⬜ | ⬜ |
document.cashout.edit | ✅ | ✅ | ✅ | ⬜ | ⬜ | ⬜ |
document.move.create | ⬜ | ⬜ | ⬜ | ✅ | ✅ | ⬜ |
document.move.edit | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
document.loss.create | ⬜ | ⬜ | ⬜ | ✅ | ✅ | ⬜ |
document.loss.edit | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
document.enter.create | ⬜ | ⬜ | ⬜ | ✅ | ✅ | ⬜ |
document.enter.edit | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
document.internalorder.edit | ✅ | ✅ | ✅ | ⬜ | ⬜ | ⬜ |
document.inventory.edit | ✅ | ✅ | ✅ | ⬜ | ⬜ | ⬜ |
document.purchasereturn.edit | ✅ | ✅ | ✅ | ⬜ | ⬜ | ⬜ |
document.salesreturn.create | ⬜ | ⬜ | ⬜ | ✅ | ✅ | ⬜ |
document.salesreturn.edit | ✅ | ✅ | ✅ | ✅ | ✅ | ⬜ |
document.retaildemand.create | ⬜ | ⬜ | ⬜ | ✅ | ✅ | ⬜ |
document.retaildemand.edit | ✅ | ✅ | ✅ | ✅ | ✅ | ⬜ |
document.retailsalesreturn.edit | ✅ | ✅ | ✅ | ⬜ | ⬜ | ⬜ |
document.retaildrawercashin.edit | ✅ | ✅ | ✅ | ⬜ | ⬜ | ⬜ |
document.retaildrawercashout.edit | ✅ | ✅ | ✅ | ⬜ | ⬜ | ⬜ |
Подробнее о дополнительных протоколах читайте в разделе Как работают виджеты.
Блок сервисных протоколов (uses)
Блок uses — опциональный. Предназначен для сервисных протоколов, используемых виджетом. На данный момент в нем можно указать следующие протоколы:
- good-folder-selector позволяет виджетам приложений переиспользовать существующий в МоемСкладе селектор группы товаров. При этом виджет получает результат выбора пользователя. Параметры у протокола отсутствуют. Подробнее про протокол можно прочитать в разделе Селектор группы товаров.
- standard-dialogs позволяет виджетам приложений использовать стандартные диалоги, существующие в МоемСкладе. При этом виджет получает результат выбора пользователя (кнопка, нажатая пользователем). Параметры у протокола отсутствуют. Подробнее о протоколе читайте в разделе Стандартные диалоги.
- navigation-service позволяет виджетам приложений осуществлять переход на другую страницу МоегоСклада и открывать МойСклад в новой вкладке. Параметры у протокола отсутствуют. Подробнее о протоколе читайте в разделе Протокол навигации.
Блок popups
Блок popups с двумя модальными окнами, одно из которых использует протокол good-folder-selector
<popups>
<popup>
<name>somePopup1</name>
<sourceUrl>https://example.com/popup-1.php</sourceUrl>
</popup>
<popup>
<name>somePopup2</name>
<sourceUrl>https://example.com/popup-2.php</sourceUrl>
<uses>
<good-folder-selector/>
</uses>
</popup>
</popups>
Служит для задания списка кастомных модальных окон, которые могут использоваться приложением в виджетах (блок widgets) и iframe-части (блок iframe).
- Чтобы задать имя модального окна, используйте тег
name
(обязательный). - Чтобы задать адрес страницы, используйте тег
sourceUrl
(обязательный).
Тег uses — опциональный. Предназначен для сервисных протоколов, используемых модальным окном. Он указывается в блоке popup. На данный момент в этом теге можно указать следующие протоколы:
- good-folder-selector позволяет модальному окну переиспользовать селектор группы товаров, существующий в МоемСкладе. При этом селекторы получают результат выбора пользователя. Параметры у протокола отсутствуют. Подробнее о протоколе читайте в разделе Селектор группы товаров.
- navigation-service позволяет модальному окну приложений осуществлять переход на другую страницу МоегоСклада и открывать МойСклад в новой вкладке. Параметры у протокола отсутствуют. Подробнее о протоколе читайте в разделе Протокол навигации.
Подробнее о работе с кастомными модальными окнами читайте в разделе Кастомные модальные окна.
Примеры дескрипторов
Для серверных приложений (актуальная версия схемы дескриптора v2)
Дескриптор для серверных приложений
<ServerApplication xmlns="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2
https://apps-api.moysklad.ru/xml/ns/appstore/app/v2/application-v2.xsd">
<vendorApi>
<endpointBase>https://example.com/dummy-app</endpointBase>
</vendorApi>
<access>
<resource>https://api.moysklad.ru/api/remap/1.2</resource>
<scope>admin</scope>
</access>
</ServerApplication>
Дескриптор для серверных приложений с iframe-частью и расширением окна (expand)
<ServerApplication xmlns="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2
https://apps-api.moysklad.ru/xml/ns/appstore/app/v2/application-v2.xsd">
<iframe>
<sourceUrl>https://example.com/iframe.html</sourceUrl>
<expand>true</expand>
</iframe>
<vendorApi>
<endpointBase>https://example.com/dummy-app</endpointBase>
</vendorApi>
<access>
<resource>https://api.moysklad.ru/api/remap/1.2</resource>
<scope>admin</scope>
</access>
</ServerApplication>
Дескриптор для серверных приложений с виджетом в карточке контрагента
<ServerApplication xmlns="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2
https://apps-api.moysklad.ru/xml/ns/appstore/app/v2/application-v2.xsd">
<iframe>
<sourceUrl>https://example.com/iframe.html</sourceUrl>
</iframe>
<vendorApi>
<endpointBase>https://example.com/dummy-app</endpointBase>
</vendorApi>
<access>
<resource>https://api.moysklad.ru/api/remap/1.2</resource>
<scope>admin</scope>
</access>
<widgets>
<entity.counterparty.edit>
<sourceUrl>https://example.com/widget.php</sourceUrl>
<height>
<fixed>150px</fixed>
</height>
</entity.counterparty.edit>
</widgets>
</ServerApplication>
Дескриптор для серверных приложений с виджетом в карточке контрагента, Заказе покупателя и Отгрузке и протоколами openfeedback, save-handler, change-handler
<ServerApplication xmlns="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2
https://apps-api.moysklad.ru/xml/ns/appstore/app/v2/application-v2.xsd">
<iframe>
<sourceUrl>https://example.com/iframe.html</sourceUrl>
</iframe>
<vendorApi>
<endpointBase>https://example.com/dummy-app</endpointBase>
</vendorApi>
<access>
<resource>https://api.moysklad.ru/api/remap/1.2</resource>
<scope>admin</scope>
</access>
<widgets>
<entity.counterparty.edit>
<sourceUrl>https://example.com/widget.php</sourceUrl>
<height>
<fixed>150px</fixed>
</height>
<supports>
<open-feedback/>
</supports>
</entity.counterparty.edit>
<document.customerorder.edit>
<sourceUrl>https://example.com/widget-customerorder.php</sourceUrl>
<height>
<fixed>50px</fixed>
</height>
<supports>
<open-feedback/>
<save-handler/>
<change-handler/>
</supports>
</document.customerorder.edit>
<document.demand.edit>
<sourceUrl>https://example.com/widget-demand.php</sourceUrl>
<height>
<fixed>50px</fixed>
</height>
<supports>
<open-feedback/>
<change-handler/>
</supports>
</document.demand.edit>
</widgets>
</ServerApplication>
Дескриптор для серверных приложений с виджетом в Заказе покупателя и протоколом change-handler c validation-feedback
<ServerApplication xmlns="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2
https://apps-api.moysklad.ru/xml/ns/appstore/app/v2/application-v2.xsd">
<iframe>
<sourceUrl>https://example.com/iframe.html</sourceUrl>
</iframe>
<vendorApi>
<endpointBase>https://example.com/dummy-app</endpointBase>
</vendorApi>
<access>
<resource>https://api.moysklad.ru/api/remap/1.2</resource>
<scope>admin</scope>
</access>
<widgets>
<document.customerorder.edit>
<sourceUrl>https://example.com/widget-customerorder.php</sourceUrl>
<height>
<fixed>50px</fixed>
</height>
<supports>
<change-handler>
<validation-feedback/>
</change-handler>
</supports>
</document.customerorder.edit>
</widgets>
</ServerApplication>
Дескриптор для серверных приложений с виджетом в карточке контрагента, Заказе покупателя и Отгрузке и протоколами good-folder-selector и dirty-state
<ServerApplication xmlns="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2
https://apps-api.moysklad.ru/xml/ns/appstore/app/v2/application-v2.xsd">
<iframe>
<sourceUrl>https://example.com/iframe.html</sourceUrl>
</iframe>
<vendorApi>
<endpointBase>https://example.com/dummy-app</endpointBase>
</vendorApi>
<access>
<resource>https://api.moysklad.ru/api/remap/1.2</resource>
<scope>admin</scope>
</access>
<widgets>
<entity.counterparty.edit>
<sourceUrl>https://example.com/widget.php</sourceUrl>
<height>
<fixed>150px</fixed>
</height>
<supports>
<dirty-state/>
</supports>
<uses>
<good-folder-selector/>
</uses>
</entity.counterparty.edit>
<document.customerorder.edit>
<sourceUrl>https://example.com/widget-customerorder.php</sourceUrl>
<height>
<fixed>50px</fixed>
</height>
<supports>
<dirty-state/>
</supports>
<uses>
<good-folder-selector/>
</uses>
</document.customerorder.edit>
<document.demand.edit>
<sourceUrl>https://example.com/widget-demand.php</sourceUrl>
<height>
<fixed>50px</fixed>
</height>
<supports>
<dirty-state/>
</supports>
<uses>
<good-folder-selector/>
</uses>
</document.demand.edit>
</widgets>
</ServerApplication>
Дескриптор для серверных приложений с виджетом в Заказе покупателя и Счете покупателю
<ServerApplication xmlns="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2
https://apps-api.moysklad.ru/xml/ns/appstore/app/v2/application-v2.xsd">
<iframe>
<sourceUrl>https://example.com/iframe.html</sourceUrl>
</iframe>
<vendorApi>
<endpointBase>https://example.com/dummy-app</endpointBase>
</vendorApi>
<access>
<resource>https://api.moysklad.ru/api/remap/1.2</resource>
<scope>admin</scope>
</access>
<widgets>
<document.customerorder.edit>
<sourceUrl>https://example.com/widget-customerorder.php</sourceUrl>
<height>
<fixed>150px</fixed>
</height>
</document.customerorder.edit>
<document.invoiceout.edit>
<sourceUrl>https://example.com/widget-invoiceout.php</sourceUrl>
<height>
<fixed>110px</fixed>
</height>
</document.invoiceout.edit>
</widgets>
</ServerApplication>
Дескриптор для серверных приложений с виджетом в Заказе покупателя и двумя кастомными модальными окнама, одно из которых поддерживает протокол good-folder-selector
<ServerApplication xmlns="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2
https://apps-api.moysklad.ru/xml/ns/appstore/app/v2/application-v2.xsd">
<iframe>
<sourceUrl>https://example.com/iframe.html</sourceUrl>
</iframe>
<vendorApi>
<endpointBase>https://example.com/dummy-app</endpointBase>
</vendorApi>
<access>
<resource>https://api.moysklad.ru/api/remap/1.2</resource>
<scope>admin</scope>
</access>
<widgets>
<document.customerorder.edit>
<sourceUrl>https://example.com/widget-customerorder.php</sourceUrl>
<height>
<fixed>150px</fixed>
</height>
</document.customerorder.edit>
</widgets>
<popups>
<popup>
<name>viewPopup</name>
<sourceUrl>https://example.com/view-popup.php</sourceUrl>
</popup>
<popup>
<name>editPopup</name>
<sourceUrl>https://example.com/edit-popup.php</sourceUrl>
<uses>
<good-folder-selector/>
</uses>
</popup>
</popups>
</ServerApplication>
Дескриптор для серверных приложений с явным указанием прав доступа
<ServerApplication xmlns="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://apps-api.moysklad.ru/xml/ns/appstore/app/v2
https://apps-api.moysklad.ru/xml/ns/appstore/app/v2/application-v2.xsd">
<iframe>
<sourceUrl>https://example.com/iframe.html</sourceUrl>
</iframe>
<vendorApi>
<endpointBase>https://example.com/dummy-app</endpointBase>
</vendorApi>
<access>
<resource>https://api.moysklad.ru/api/remap/1.2</resource>
<scope>custom</scope>
<permissions>
<viewDashboard/>
<viewAudit/>
<purchaseOrder>
<view/>
<create/>
<update/>
<delete/>
<print/>
<approve/>
</purchaseOrder>
<good>
<view/>
<create/>
<print/>
</good>
</permissions>
</access>
</ServerApplication>
Для серверных приложений (устаревшие версии схемы дескриптора 1.x.x)
Минимальный дескриптор для серверных приложений (без возможности настройки параметров приложения пользователем МоегоСклада, так как у приложения отсутствует iframe-часть)
<application xmlns="https://apps-api.moysklad.ru/xml/ns/appstore/app/v1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://apps-api.moysklad.ru/xml/ns/appstore/app/v1
https://apps-api.moysklad.ru/xml/ns/appstore/app/v1/application-1.1.0.xsd">
<vendorApi>
<endpointBase>https://example.com/dummy-app</endpointBase>
</vendorApi>
<access>
<resource>https://online.moysklad.ru/api/remap/1.2</resource>
<scope>admin</scope>
</access>
</application>
Дескриптор для серверных приложений с iframe-частью
<application xmlns="https://apps-api.moysklad.ru/xml/ns/appstore/app/v1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://apps-api.moysklad.ru/xml/ns/appstore/app/v1
https://apps-api.moysklad.ru/xml/ns/appstore/app/v1/application-1.1.0.xsd">
<iframe>
<sourceUrl>https://example.com/iframe.html</sourceUrl>
</iframe>
<vendorApi>
<endpointBase>https://example.com/dummy-app</endpointBase>
</vendorApi>
<access>
<resource>https://online.moysklad.ru/api/remap/1.2</resource>
<scope>admin</scope>
</access>
</application>
Для телефонии
Для приложений телефонии дескриптор на текущий момент не требуется (не поддерживается).
Для приложений лояльности
Для интеграций с системами лояльности дескриптор на текущий момент не требуется (не поддерживается).
Vendor API 1.0
Системы разработчиков взаимодействуют с магазином приложений МоегоСклада через Vendor API.
Функции Vendor API:
- Активация и деактивация приложений на аккаунте.
- Получение контекста пользователя приложения.
Далее приводятся спецификации REST-эндпоинтов на стороне МоегоСклада и разработчика приложений.
Аутентификация взаимодействия по Vendor API
Все запросы от МоегоСклада к серверу разработчика и от сервера разработчика к МоемуСкладу должны быть подписаны с использованием JWT (JSON Web Token) описанным ниже образом.
Секретный ключ secretKey
Если приложение предполагает взаимодействие по Vendor API — оповещение по активации и деактивации, получение контекста пользователя для iframe, приложению необходим Секретный ключ.
Секретный ключ (secretKey) используется для построения или вычисления сигнатуры JWT.
Секретный ключ генерируется на стороне МоегоСклада и выдается разработчику:
- Разработчику необходимо завести Черновик приложения в личном кабинете разработчика. После этого открывается доступ к странице редактирования.
- На странице редактирования в числе полей, доступных для просмотра (read-only), доступен Секретный ключ.
- Пока приложение находится в статусе Черновик, разработчик может перегенерировать Секретный ключ самостоятельно.
- После передачи приложения на модерацию Секретный ключ доступен только для просмотра и копирования.
- Чтобы перегенерировать secretKey для приложения не в статусе Черновик, разработчику необходимо обратиться к сотрудникам МоегоСклада.
Исходящие запросы МойСклад → Разработчик
МойСклад подписывает свои запросы к REST-эндпоинтам разработчика, передавая JWT-токен в заголовке HTTP-запроса следующим образом:
Authorization: Bearer <token>
Описание полей JWT-payload:
Поле | Описание | Предполагаемое использование разработчиком |
---|---|---|
"iat" | Время генерации токена в секундах от 1970-01-01T00:00:00Z UTC | На усмотрение разработчика |
"exp" | Время жизни токена в секундах от 1970-01-01T00:00:00Z UTC — позволяет ограничить срок валидности токена. После указанного момента времени токен считается невалидным. | При получении JWT после времени, указанного в "exp", следует отклонять запрос с ошибкой аутентификации |
"jti" | Уникальный идентификатор токена — позволяет отслеживать одноразовость применения токена | При получении JWT с "jti", который был уже получен ранее, следует отклонять запрос с ошибкой аутентификации |
JWT-header всегда такой:
{
"alg": "HS256",
"typ": "JWT"
}
JWT-payload включает в себя следующие поля:
{
"iat": 1516239022,
"exp": 1516239322,
"jti": "6S3BQLsaSRNdEnhPCoW9lplY2LozRUOq"
}
Входящие запросы Разработчик → МойСклад
Разработчик при выполнении REST запросов к МоемуСкладу по Vendor API должен подписывать их одноразовым токеном JWT. Токен следует передавать в заголовке HTTP-запроса:
Authorization: Bearer <token>
Поле "alg" в JWT-заголовке всегда должно иметь значение HS256. Соответственно, JWT-сигнатура всегда должна генерироваться по алгоритму HMAC SHA-256 с использованием секретного ключа secretKey.
Описание полей JWT-payload:
Поле | Обязательное | Описание | Обработка на стороне МоегоСклада |
---|---|---|---|
"sub" | да | appUid приложения | Используется для идентификации и авторизации приложения |
"iat" | да | Время генерации токена в секундах от 1970-01-01T00:00:00Z UTC | Используется для ограничения допустимого времени жизни токена |
"exp" | нет | Время жизни токена в секундах от 1970-01-01T00:00:00Z UTC. После этого момента токен считается невалидным | Если данное поле присутствует, оно также используется для проверки валидности токена по времени |
"jti" | да | Уникальный идентификатор токена | Используется для проверки одноразовости использования токена |
В системе МоегоСклада есть следующие ограничения:
- JWT-токен не может быть использован повторно. Для этого система проверяет значение поля "jti" на уникальность среди предыдущих запросов.
- на максимальное время жизни для JWT-токена (порядка нескольких минут) — maxTokenLifetime. При проверке/валидации на
стороне МоегоСклада значение "exp" ограничивается максимальным временем жизни. Если (exp - iat) превышает
maxTokenLifetime или поле "exp" отсутствует, в качестве "exp" используется iat + maxTokenLifetime. То есть:
effectiveExp = exp ? min(exp, iat + maxTokenLifetime) : iat + maxTokenLifetime
JWT-header может быть с указанием поля "typ":
{
"alg": "HS256",
"typ": "JWT"
}
и без указания поля "typ":
{
"alg": "HS256"
}
JWT-payload должен содержать следующие поля:
(поле "exp" — опциональное, остальные — обязательные)
{
"sub": "dummyapp.example-vendor",
"iat": 1516239022,
"exp": 1516239322,
"jti": "6S3BQLsaSRNdEnhPCoW9lplY2LozRUOq"
}
Ошибки аутентификации и авторизации
Обработка ошибок аутентификации и авторизации на стороне МоегоСклада осуществляется аналогично тому, как это сделано
в JSON API 1.2. То есть при возникновении ошибок возвращается аналогичный
объект error
и заголовки HTTP-ответа X-Lognex-Auth и X-Lognex-Auth-Message.
Процесс активации приложения на аккаунте
Активация приложения — это процесс включения приложения для конкретного аккаунта в системе разработчика. Выполняется при подключении приложения пользователем или при возобновлении платного приложения. В рамках активации происходит оповещение сервера разработчика со стороны МоегоСклада.
В общем виде процесс активации приложения выглядит так:
- Пользователь МоегоСклада устанавливает приложение.
- МойСклад делает HTTP PUT запрос на сервер разработчика, в том числе передавая причину активации и токен доступа к JSON API, если в дескрипторе приложения такой доступ запрашивается. При этом доступ по переданному токену уже работает.
- Сервер разработчика в рамках допустимого тайм-аута отвечает на запрос одним из статусов в теле ответа:
- Activating — статус предназначен для асинхронной активации на стороне разработчика — если в рамках допустимого тайм-аута обработки HTTP PUT запроса не получается активировать приложение. Например, для активации приложения требуется несколько минут. Далее разработчик должен оповестить МойСклад через эндпоинт Изменение статуса приложения на аккаунте о завершении активации приложения статусом Activated или о том, что приложению требуется настройка статусом SettingsRequired.
- SettingsRequired — статусом, с помощью которого разработчик сообщает МоемуСкладу, что приложению требуется настройка пользователем через iframe-часть приложения. После того, как пользователь настроит приложение, разработчик должен оповестить МойСклад через эндпоинт Обратного вызова изменения статуса приложения на аккаунте об активации приложения, передав статус Activated.
- Activated — статус означает, что приложение сразу было полностью активировано и уже работает.
Диаграмма деятельности активации приложения:
Процесс деактивации приложения на аккаунте
Деактивация приложения — это процесс выключения приложения для конкретного аккаунта в системе разработчика. Выполняется при отключении приложения пользователем или при приостановке платного приложения. При деактивации происходит оповещение сервера разработчика со стороны МоегоСклада.
Процесс деактивации приложения гораздо проще процесса активации:
- Пользователь МоегоСклада нажимает на кнопку Удалить в карточке приложения.
- Для платных приложений возможна приостановка в случаях:
- У пользователя, на аккаунте которого есть установленные платные приложения, кончается подписка.
- Пользователь, на аккаунте которого есть установленные платные приложения, оплачивает подписку меньшей суммой, чем требуется для оплаты установленных платных приложений.
- Для платных приложений возможна приостановка в случаях:
- МойСклад аннулирует доступ по токену, если он был выдан, и выполняет HTTP DELETE запрос с причиной деактивации к серверу разработчика. На момент получения разработчиком оповещения по деактивации доступ по токену уже не работает.
Диаграмма деятельности деактивации приложения:
Процесс приостановки и возобновления работы приложения на аккаунте
Приостановка приложения — это автоматический процесс выключения платного приложения при отсутствии на счету пользователя МоегоСклада суммы, необходимой для оплаты подписки. После приостановки токен доступа приложения аннулируется. Первыми приостанавливаются последние установленные приложения на аккаунте.
Возобновление приложения — это процесс включения приостановленного платного приложения. Возобновление может быть выполнено:
- автоматически после пополнения баланса, если в приложении включено автопродление
- вручную пользователем МоегоСклада, если в приложении выключено автопродление
После возобновления приложению выдается новый токен доступа. Первыми возобновляются приложения, установленные ранее других приложений.
REST-эндпоинты на стороне разработчика приложений
Разработчику необходимо реализовать REST-эндпоинт, если ему требуется:
- активировать или деактивировать приложение у себя в системе при установке, приостановке, возобновлении или удалении приложения на аккаунте пользователя МоегоСклада;
- получать токен для доступа к JSON API 1.2.
При этом REST--эндпоинт должен иметь доступ по HTTPS, поддерживать HTTP-методы PUT, GET, DELETE и иметь вид:
https://<VENDOR-SERVER-ENDPOINT>/api/moysklad/vendor/1.0/apps/{appId}/{accountId}
где:
- VENDOR-SERVER-ENDPOINT — URL, указанный в дескрипторе приложения;
- appId
UUID
— идентификатор приложения в магазине приложений; - accountId
UUID
— идентификатор аккаунта в МоемСкладе.
Активация приложения на аккаунте
Запрос должен обрабатываться сервером идемпотентно. МойСклад может повторять/дублировать запросы в соответствии со своей внутренней логикой. Например, при работе механизма Retry.
HTTP-метод: PUT
Content-Type: application/json
В теле запроса передается:
- appUid
String
устанавливаемого приложения. Может быть полезно видеть не только UUID приложения, но и его appUid. Например, для разбора непонятных ситуаций и/или логирования. appUid состоит из алиасприложения.алиасвендора, например egais-integration.moysklad - accountName
String
— имя аккаунта, на который осуществляется подключение приложения. Полезно видеть не только UUID аккаунта, но и accountName. - cause
String
— причина активации. Возможные значения:- Install — установка приложения на аккаунт
- Resume — возобновление приложения на аккаунте
- TariffChanged — изменение тарифа подписки
- access
Array
— доступы к ресурсам, указанным в дескрипторе приложения. Сейчас из ресурсов для приложений доступно только JSON API 1.2. Если ваше приложение не требует доступа к API, то данный объект не придет. Не включается в тело для ("Cause": "TariffChanged"
). Атрибуты:- resource
String
— ресурс, к которому предоставлен доступ; - scope
Array
— какие права предоставлены на доступ данному ресурсу. На данный момент доступ предоставляется в двух вариантах — с правами администратора ("scope": ["admin"]
) и явно указанным набором прав ("scope": ["custom"]
); - permissions
Array
— к каким объектам приложение может получить доступ. Включается в тело только для ("scope": ["custom"]
). Соответствует ответу в запросе на получение списка прав Сотрудника; - access_token
String
— Bearer токен доступа к данному ресурсу.
- resource
- subscription — объект, описывающий параметры текущей подписки. Поля:
- tariffId
UUID
— идентификатор тарифа, на котором приобретена подписка; - trial
Boolean
— признак пробной подписки; - tariffName
String
— название тарифа (не обязательный); - expiryMoment
String
— дата окончания подписки в формате RFC 3339 (не обязательный); - notForResale
Boolean
— признак того, что аккаунт, установивший приложение, является аккаунтом партнера МоегоСклада.
- tariffId
В теле ответа ожидаем получить следующую JSON-структуру
(обратите внимание, в ответе обязательно требуется
HTTP-заголовок Content-Type: application/json
, смотрите примеры ниже):
- status
String
— статус активации приложения. Возможные значения: Activating, SettingsRequired, Activated.
Статус | Описание | Дальнейшие действия разработчика | Отображаемый пользователю статус приложения на витрине приложений |
---|---|---|---|
Activating | Приложение в процессе активации | Разработчик оповестит МойСклад по эндпоинту обратного вызова об изменении статуса приложения на SettingsRequired или Activated | Приложение подключается |
SettingsRequired | Требуется настройка приложения пользователем | Разработчик оповестит МойСклад об изменении статуса на Activated, когда пользователь выполнит настройку приложения через iframe-часть | Приложение требует настройки |
Activated | Приложение на аккаунте полностью активировано и начало свою работу | Действий разработчика не требуется | Приложение подключено |
HTTP status codes:
- 200 OK — система разработчика успешно обработала запрос на активацию приложения и вернула статус активации приложения на аккаунте в теле ответа.
- 551 Lifecycle Processing Failed (кастомный статус) — система разработчика не смогла выполнить активацию приложения для аккаунта. Это ошибка активации приложения, установка приложения на аккаунте завершается с ошибкой, приложение не становится установленным на аккаунт, переходит в специальное состояние ActivationFailed.
- прочие статусы обрабатываются как ошибка — запускается механизм Retry.
Пример активации приложения при установке на аккаунт:
Request:
PUT https://example.com/baseurl/api/moysklad/vendor/1.0/apps/5f3c5489-6a17-48b7-9fe5-b2000eb807fe/f088b0a7-9490-4a57-b804-393163e7680f
Body
{
"appUid": "example-app.example-vendor",
"accountName": "dummyaccount",
"cause": "Install",
"access": [
{
"resource": "https://api.moysklad.ru/api/remap/1.2",
"scope": ["admin"],
"access_token": "6ab89be1ae6ff147755625ee8da948e42612233b"
}
],
"subscription": {
"tariffId": "23ca69d4-2657-40c4-8ba1-6ce24ddeac2e",
"trial": true,
"tariffName": "Basic",
"expiryMoment": "2024-01-19T18:50:12+03:00",
"notForResale": false
}
}
Response:
Response 200
Content-Type: application/json
Body:
{
"status": "SettingsRequired"
}
Пример активации платного приложения при возобновлении работы приложения на аккаунте после поступления оплаты приложения:
Request:
PUT https://example.com/baseurl/api/moysklad/vendor/1.0/apps/5f3c5489-6a17-48b7-9fe5-b2000eb807fe/f088b0a7-9490-4a57-b804-393163e7680f
Body
{
"appUid": "example-app.example-vendor",
"accountName": "dummyaccount",
"cause": "Resume",
"access": [
{
"resource": "https://api.moysklad.ru/api/remap/1.2",
"scope": ["admin"],
"access_token": "6ab89be1ae6ff147755625ee8da948e42612233b"
}
],
"subscription": {
"tariffId": "23ca69d4-2657-40c4-8ba1-6ce24ddeac2e",
"trial": false,
"tariffName": "Basic",
"expiryMoment": "2024-01-19T18:50:12+03:00",
"notForResale": false
}
}
Response:
Response 200
Content-Type: application/json
Body:
{
"status": "Activated"
}
Пример активации приложения с блоком permissions (гибким набором прав):
Request:
PUT https://example.com/baseurl/api/moysklad/vendor/1.0/apps/5f3c5489-6a17-48b7-9fe5-b2000eb807fe/f088b0a7-9490-4a57-b804-393163e7680f
Body
{
"appUid": "example-app.example-vendor",
"accountName": "account-test",
"access": [
{
"resource": "https://api.moysklad.ru/api/remap/1.2",
"scope": ["custom"],
"permissions": {
"supply": {
"view": "ALL",
"update": "ALL"
},
"viewDashboard": true,
"viewAudit": true
},
"access_token": "test-token"
}
],
"cause": "Install"
}
Response:
Response 200
Content-Type: application/json
Body:
{
"status": "SettingsRequired"
}
Пример активации приложения при изменении тарифа на аккаунте:
Request:
PUT https://example.com/baseurl/api/moysklad/vendor/1.0/apps/5f3c5489-6a17-48b7-9fe5-b2000eb807fe/f088b0a7-9490-4a57-b804-393163e7680f
Body
{
"appUid": "example-app.example-vendor",
"accountName": "dummyaccount",
"cause": "TariffChanged",
"subscription": {
"tariffId": "23ca69d4-2657-40c4-8ba1-6ce24ddeac2e",
"trial": false,
"tariffName": "Basic",
"expiryMoment": "2024-01-19T18:50:12+03:00",
"notForResale": false
}
}
Response:
Response 200
Content-Type: application/json
Body:
{
"status": "Activated"
}
Деактивация приложения на аккаунте
HTTP-метод: DELETE
Тело запроса:
- cause
String
— причина деактивации. Возможные значения:- Uninstall — удаление приложения на аккаунте;
- Suspend (возможно только у платных приложений) — приостановка работы приложения на аккаунте.
Тело ответа: пустое
HTTP status codes:
- 200 OK — приложение успешно отключено (деактивировано) во внешней системе разработчика.
- 404 Not Found — приложение отключено или никогда не было подключено (никогда не активировалось) для данного аккаунта.
- 551 Lifecycle Processing Failed (кастомный статус) — внешняя система не смогла выполнить деактивацию приложения для аккаунта.
- прочие статусы обрабатываются как ошибка — запускается механизм Retry.
Пример деактивации приложения при удалении с аккаунта:
Request:
DELETE https://example.com/baseurl/api/moysklad/vendor/1.0/apps/5f3c5489-6a17-48b7-9fe5-b2000eb807fe/f088b0a7-9490-4a57-b804-393163e7680f
Body
{
"appUid": "example-app.example-vendor",
"accountName": "account-test",
"cause": "Uninstall"
}
Response:
Response 200
Content-Type: application/json
Пример деактивации платного приложения при приостановке приложения на аккаунте (при отсутствии оплаты приложения):
Request:
DELETE https://example.com/baseurl/api/moysklad/vendor/1.0/apps/5f3c5489-6a17-48b7-9fe5-b2000eb807fe/f088b0a7-9490-4a57-b804-393163e7680f
Body
{
"appUid": "example-app.example-vendor",
"accountName": "account-test",
"cause": "Suspend"
}
Response:
Response 200
Content-Type: application/json
Проверка статуса активации приложения в системе разработчика
HTTP-метод: GET
Content-Type: application/json
Тело запроса: пустое
Тело ответа:
- status
String
— статус активации приложения. Возможные значения: Activating, SettingsRequired, Activated.
HTTP status codes:
- 200 OK — приложение активировано или активируется во внешней системе. Статус активации — в теле ответа;
- 404 Not Found — приложение отключено или никогда не было подключено для данного аккаунта.
Пример
Request:
GET https://example.com/api/moysklad/vendor/1.0/apps/5f3c5489-6a17-48b7-9fe5-b2000eb807fe/f088b0a7-9490-4a57-b804-393163e7680f
Response:
Response 200
Content-Type: application/json
Response body
{
"status": "SettingsRequired"
}
REST-эндпоинты на стороне МоегоСклада
Rest-эндпоинты на стороне МоегоСклада позволяют разработчику ограниченно управлять состоянием установки приложения для конкретного аккаунта и получать информацию о пользователе, который работает с приложением в UI МоегоСклада.
Базовый URL REST-эндпоинтов со стороны МоегоСклада (далее — MARKETPLACE-ENDPOINT
):
https://apps-api.moysklad.ru/api/vendor/1.0
При запросах к API обязательно нужно указать в HTTP-заголовке запроса Accept-Encoding формат сжатия содержимого — gzip. Если указан другой формат, возвращается ошибка 415 Unsupported Media Type (Неподдерживаемый тип данных).
На текущий момент со стороны МоегоСклада есть следующие эндпоинты:
- Получение статуса приложения на аккаунте
- Изменение статуса приложения на аккаунте
- Получение контекста пользователя для приложений с iframe-частью, кастомными модальными окнами и виджетами
Получение статуса приложения на аккаунте
С помощью этого вызова разработчик может получить текущий статус установки приложения на аккаунте пользователя.
Resource: MARKETPLACE-ENDPOINT/apps/{appId}/{accountId}/status
Здесь:
- appId
UUID
— идентификатор приложения в МоемСкладе; - accountId
UUID
— идентификатор аккаунта в МоемСкладе.
HTTP-метод: GET
Тело запроса: отсутствует
Тело ответа:
В случае успешного ответа возвращается текущий статус приложения на аккаунте со следующими атрибутами:
Название | Тип | Описание | Обязательное при ответе |
---|---|---|---|
status | String | Текущий статус приложения на аккаунте | Да |
cause | String | Причина перехода в текущий статус | Нет |
subscription | Object | Параметры текущей подписки | Нет |
Атрибуты сущности subscription
Название | Тип | Описание | Обязательное при ответе |
---|---|---|---|
tariffId | UUID | Идентификатор тарифа, на котором приобретена подписка | Да |
trial | Boolean | Признак пробной подписки | Да |
tariffName | String | Название тарифа | Нет |
expiryMoment | String | Дата окончания подписки в формате RFC 3339 | Нет |
notForResale | Boolean | Признак того, что аккаунт, установивший приложение, является аккаунтом партнера МоегоСклада | Да |
Если приложение не подключено на данном аккаунте или указанный аккаунт отсутствует в МоемСкладе, возвращается ошибка 404 Not Found (код 2004).
Таблица возможных статусов приложения на аккаунте
status | cause | Значение |
---|---|---|
Activating | Install | Приложение в процессе установки на аккаунт |
ActivationFailed | Install | Произошла ошибка при установке приложения на аккаунт |
SettingsRequired | Приложение успешно установлено на акканут, для полноценной работы требуется настройка пользователя | |
Activated | Приложение активно на аккаунте (успешно установлено на аккаунт и, если нужно, настроено пользователем) | |
Deactivating | Uninstall | Приложение в процессе удаления с аккаунта |
DeactivationFailed | Uninstall | Произошла ошибка при удалении приложения с аккаунта |
Deactivating | Suspend | Приложение в процессе приостановки на аккаунте |
DeactivationFailed | Suspend | Произошла ошибка во время приостановки приложения на аккаунте |
Suspended | Приложение приостановлено на аккаунте | |
Activating | Resume | Приложение в процессе возобновления работы на аккаунте |
ActivationFailed | Resume | Произошла ошибка во время возобновления работы приложения на аккаунте |
HTTP status codes:
- 200 OK — все в порядке, в ответе отдается состояние приложения.
- 404 Not Found — приложение не подключено на данном аккаунте (код 2004).
Пример запроса на получение статуса приложения на аккаунте
curl "https://apps-api.moysklad.ru/api/vendor/1.0/apps/5f3c5489-6a17-48b7-9fe5-b2000eb807fe/f088b0a7-9490-4a57-b804-393163e7680f/status"
-H "Accept: application/json"
-H "Authorization: Bearer ..."
Response 200 (application/json). Успешный запрос.
{
"status": "Activated",
"cause": "Install",
"subscription": {
"tariffId": "23ca69d4-2657-40c4-8ba1-6ce24ddeac2e",
"trial": false,
"tariffName": "Basic",
"expiryMoment": "2024-01-19T18:50:12+03:00",
"notForResale": false
}
}
Изменение статуса приложения на аккаунте
С помощью PUT запроса разработчик может изменить статус устанавливающегося приложения пользователя. При активации приложения со стороны разработчика, разработчик может ответить одним из статусов Activated, Activating, SettingsRequired. Если разработчик перевел в статусы Activating и SettingsRequired, то МойСклад ожидает, что разработчик с помощью обратного вызова оповестит МойСклад о том, что активация на его стороне завершена.
Resource: MARKETPLACE-ENDPOINT/apps/{appId}/{accountId}/status
Здесь:
- appId
UUID
— идентификатор приложения в МоемСкладе; - accountId
UUID
— идентификатор аккаунта в МоемСкладе.
HTTP-метод: PUT
Тело запроса:
- status
String
— текущий актуальный статус приложения. Возможные значения: Activating, SettingsRequired, Activated.
При обработке данного запроса МойСклад проверяет возможность перехода приложения на аккаунте в требуемое состояние в соответствии с жизненным циклом приложения на аккаунте. При отсутствии перехода по жизненному циклу — ошибка. Если приложение на аккаунте уже находится в целевом состоянии, то ошибки нет.
Тело ответа: пустое (за исключением ошибок)
HTTP status codes:
- 200 OK — МойСклад перевел приложение на аккаунте в соответствующее переданному статусу состояние или приложение уже находилось в требуемом состоянии.
- 404 Not Found — приложение не подключено на данном аккаунте.
- 409 Conflict — в случае отсутствия соответствующего перехода по жизненному циклу приложения на аккаунте.
Пример запроса на изменение статуса приложения на аккаунте
curl -X PUT "https://apps-api.moysklad.ru/api/vendor/1.0/apps/5f3c5489-6a17-48b7-9fe5-b2000eb807fe/f088b0a7-9490-4a57-b804-393163e7680f/status"
-H "Content-Type: application/json"
-H "Accept: application/json"
-H "Authorization: Bearer ..."
-d '{
"status": "Activating"
}'
Response 200 (application/json). Успешный запрос.
Получение контекста пользователя для приложений с iframe-частью, кастомными модальными окнами и виджетами
Через этот эндпоинт можно получить информацию о пользователе, который использует приложение в UI МоегоСклада. Для получения контекста пользователя в URL, по которому загружается основной iframe, модальное окно или виджет, добавляется GET-параметр contextKey.
Пример того, что будет загружаться в iframe, при условии, что в дескрипторе приложения
sourceUrl
имеет значение https://yoursite.ru/moysklad
:
https://yoursite.ru/moysklad?contextKey=1c14e98cd272239c03bf3d9697f167699743292c
.
contextKey — это временный ключ, который может быть использован в течение 5 минут с момента загрузки iframe для получения контекста пользователя через данный эндпоинт. Если был использован contextKey, после окончания времени его жизни эндпоинт вернет ошибку 404 Not Found
.
Resource: MARKETPLACE-ENDPOINT/context/{contextKey}
Здесь:
- contextKey
String
— ключ, переданный ранее GET-параметром при загрузке iframe.
HTTP-метод: POST
Тело запроса: пустое
Тело ответа:
В случае успешного ответа возвращается такое же по структуре содержимое как в эндпоинте получения Контекста сотрудника JSON API:
https://api.moysklad.ru/api/remap/1.2/context/employee
.
В случае ошибок возвращается JSON-объект с ошибкой. Подробнее см. Обработка ошибок МоегоСклада.
HTTP status codes:
- 200 OK — все в порядке, в ответе отдается контекст пользователя;
- 403 Forbidden — приложение не авторизовано на доступ по данному contextKey;
- 404 Not Found — contextKey не найден или истекло время его жизни.
Пример запроса на получение контекста пользователя
curl -X POST "https://apps-api.moysklad.ru/api/vendor/1.0/context/6S3BQLsaSRNdEnhPCoW9lplY2LozRUOq6S3BQLsaSRNdEnh"
-H "Accept: application/json"
-H "Authorization: Bearer ..."
Response 200 (application/json). Успешный запрос.
{
"meta": {
"href": "https://api.moysklad.ru/api/remap/1.2/entity/employee/b0a02321-13e3-11e9-912f-f3d4002516e3?expand=cashier.retailStore",
"metadataHref": "https://api.moysklad.ru/api/remap/1.2/entity/employee/metadata",
"type": "employee",
"mediaType": "application/json",
"uuidHref": "https://online.moysklad.ru/app/#employee/edit?id=b0a02321-13e3-11e9-912f-f3d4002516e3"
},
"id": "b0a02321-13e3-11e9-912f-f3d4002516e3",
"accountId": "b0b309ee-13e3-11e9-9109-f8fc0001f188",
"owner": {
"meta": {
"href": "https://api.moysklad.ru/api/remap/1.2/entity/employee/b0a02321-13e3-11e9-912f-f3d4002516e3",
"metadataHref": "https://api.moysklad.ru/api/remap/1.2/entity/employee/metadata",
"type": "employee",
"mediaType": "application/json",
"uuidHref": "https://online.moysklad.ru/app/#employee/edit?id=b0a02321-13e3-11e9-912f-f3d4002516e3"
}
},
"shared": true,
"group": {
"meta": {
"href": "https://api.moysklad.ru/api/remap/1.2/entity/group/b0b3c289-13e3-11e9-9109-f8fc0001f189",
"metadataHref": "https://api.moysklad.ru/api/remap/1.2/entity/group/metadata",
"type": "group",
"mediaType": "application/json"
}
},
"updated": "2019-12-10 18:37:25.786",
"name": "Кожевников",
"externalCode": "Exh56G1wiRTPHpYBc-nx12",
"archived": false,
"created": "2019-01-09 10:53:45.202",
"uid": "admin@bkozhevnikov",
"email": "bkozhevnikov@moysklad.ru",
"lastName": "Кожевников",
"fullName": "Кожевников",
"shortFio": "Кожевников",
"cashiers": [
{
"meta": {
"href": "https://api.moysklad.ru/api/remap/1.2/entity/retailstore/b0b7cd8d-13e3-11e9-912f-f3d400251724/cashiers/b0b7d387-13e3-11e9-912f-f3d400251725",
"type": "cashier",
"mediaType": "application/json"
}
}
],
"permissions": {
"currency": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL"
},
"uom": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL"
},
"productfolder": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL",
"print": "ALL"
},
"product": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL",
"print": "ALL"
},
"bundle": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL",
"print": "ALL"
},
"service": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL",
"print": "ALL"
},
"consignment": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL",
"print": "ALL"
},
"variant": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL",
"print": "ALL"
},
"store": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL"
},
"counterparty": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL",
"print": "ALL"
},
"organization": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL"
},
"employee": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL"
},
"contract": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL",
"print": "ALL"
},
"project": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL"
},
"country": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL"
},
"customentity": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL"
},
"demand": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL",
"approve": "ALL",
"print": "ALL"
},
"customerorder": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL",
"approve": "ALL",
"print": "ALL"
},
"internalorder": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL",
"approve": "ALL",
"print": "ALL"
},
"invoiceout": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL",
"approve": "ALL",
"print": "ALL"
},
"invoicein": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL",
"approve": "ALL",
"print": "ALL"
},
"paymentin": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL",
"approve": "ALL",
"print": "ALL"
},
"paymentout": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL",
"approve": "ALL",
"print": "ALL"
},
"cashin": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL",
"approve": "ALL",
"print": "ALL"
},
"cashout": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL",
"approve": "ALL",
"print": "ALL"
},
"supply": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL",
"approve": "ALL",
"print": "ALL"
},
"salesreturn": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL",
"approve": "ALL",
"print": "ALL"
},
"purchasereturn": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL",
"approve": "ALL",
"print": "ALL"
},
"retailstore": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL"
},
"receipttemplate": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL"
},
"retailshift": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL",
"print": "ALL"
},
"retaildemand": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL",
"approve": "ALL",
"print": "ALL"
},
"retailsalesreturn": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL",
"approve": "ALL",
"print": "ALL"
},
"retaildrawercashin": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL",
"approve": "ALL",
"print": "ALL"
},
"retaildrawercashout": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL",
"approve": "ALL",
"print": "ALL"
},
"prepayment": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL",
"approve": "ALL",
"print": "ALL"
},
"prepaymentreturn": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL",
"approve": "ALL",
"print": "ALL"
},
"purchaseorder": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL",
"approve": "ALL",
"print": "ALL"
},
"move": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL",
"approve": "ALL",
"print": "ALL"
},
"enter": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL",
"approve": "ALL",
"print": "ALL"
},
"loss": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL",
"approve": "ALL",
"print": "ALL"
},
"facturein": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL",
"approve": "ALL",
"print": "ALL"
},
"factureout": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL",
"approve": "ALL",
"print": "ALL"
},
"commissionreportin": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL",
"approve": "ALL",
"print": "ALL"
},
"commissionreportout": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL",
"approve": "ALL",
"print": "ALL"
},
"pricelist": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL",
"approve": "ALL",
"print": "ALL"
},
"processingplanfolder": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL"
},
"processingplan": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL"
},
"processing": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL",
"approve": "ALL",
"print": "ALL"
},
"processingorder": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL",
"approve": "ALL",
"print": "ALL"
},
"assortment": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL",
"print": "ALL"
},
"inventory": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL",
"print": "ALL"
},
"bonustransaction": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL",
"approve": "ALL",
"print": "ALL"
},
"crptorder": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL",
"approve": "ALL",
"print": "ALL"
},
"webhook": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL"
},
"task": {
"view": "ALL",
"create": "ALL",
"update": "ALL",
"delete": "ALL",
"done": "ALL"
},
"dashboard": {
"view": "ALL"
},
"stock": {
"view": "ALL"
},
"customAttributes": {
"view": "ALL"
},
"pnl": {
"view": "ALL"
},
"company_crm": {
"view": "ALL"
},
"tariff_crm": {
"view": "ALL"
},
"audit_dashboard": {
"view": "ALL"
},
"admin": {
"view": "ALL"
},
"dashboardMoney": {
"view": "ALL"
}
}
}
Обработка ошибок на стороне МоегоСклада
При взаимодействии Разработчик → МойСклад обработка ошибок на стороне МоегоСклада выполняется аналогично тому, как это сделано в JSON API 1.2 . В тело ответа включается JSON-объект с описанием ошибки. При необходимости в ответе проставляются соответствующие HTTP-заголовки.
Механизм Retry
Механизм Retry повторяет попытки выполнить запрос к системе разработчика в соответствии с конфигурационным профилем, глобальным для магазина приложений. В общем случае профиль задается двумя параметрами:
- максимальное количество повторений,
- функция delay(i), возвращающая время задержки до очередного повтора.
Если израсходованы все количества повторений, Retry завершается с ошибкой. Дальнейшее поведение системы зависит от операции, которую пытались выполнить.
Личный кабинет разработчика
Личный кабинет разработчика
Личный кабинет разработчика — это сервис, который упрощает работу над приложениями для магазина приложений.
В личном кабинете можно:
- Создавать Черновики приложений;
- Редактировать Черновики приложений и просматривать информацию о приложениях в прочих статусах;
- Редактировать информацию о разработчике;
- Отправлять Черновики приложений на модерацию;
- Следить за статусом публикации своих приложений;
- Предоставлять и продлевать пробный период для платных приложений;
- Привязывать аккаунты в МоемСкладе к аккаунту в личном кабинете для отладки неопубликованных приложений;
- Просматривать отчеты по установкам и выручке;
- Просматривать, скачивать и согласовывать отчет;
- Просматривать отчет по причинам удаления приложений.
Получение доступа к личному кабинету разработчика
Для получения доступа в личный кабинет разработчика заполните анкету. Доступ и дальнейшие инструкции будут высланы на почту, указанную в анкете, в течение нескольких дней.
Жизненный цикл приложения
Статусы жизненного цикла приложения для магазина приложений:
- Черновик (Draft) — статус черновика, предназначен для разработки и тестирования. Приложение не отображается на витрине у пользователей МоегоСклада.
- Готово, отправлено на модерацию (Ready) — статус готовности. Приложение готово к публикации со стороны разработчика. Сотрудники МоегоСклада проверят приложение перед публикацией. Приложение не отображается на витрине у пользователей МоегоСклада.
- Опубликовано (Published) — приложение опубликовано на витрине приложений и доступно к установке пользователями МоегоСклада.
- Снято с публикации (Hidden) — публикация приложения приостановлена. Приложение исчезает с витрины, но у текущих пользователей приложения сохраняется возможность работать с ним.
- Версия опубликована (Merged) — версия приложения опубликована на витрине приложений. Данный статус является внутренним, и не отображается в списке приложений и на витрине.
Жизненный цикл приложения:
- Приложение создается в личном кабинете разработчика со статусом Draft. Разработчик проверяет и отлаживает работу приложения на своем аккаунте. Когда приложение готово, разработчик передает его на публикацию. Статус приложения меняется на Ready.
- Сотрудник МоегоСклада проверяет, затем публикует приложение на витрине. Статус приложения меняется на Published. Приложение становится доступно пользователю МоегоСклада для установки.
- Пользователь МоегоСклада устанавливает (подключает) приложение на свой аккаунт, настраивает и использует. Если приложение не нужно, пользователь может удалить (отключить) приложение на аккаунте.
- Разработчик может внести изменения в опубликованное приложение, создав для нее новую версию и внеся в нее изменения.
- Чтобы убрать приложение с витрины приложений, его нужно перевести в статус Hidden. Установленные до этого момента экземпляры приложения на аккаунтах пользователей продолжают работать и отображаться на UI МоегоСклада, пока пользователи их не удалят. Для аккаунтов, удаливших приложение, и аккаунтов, на которых оно не было установлено, приложение скрывается. Установка приложения становится невозможной. Так можно скрыть только бесплатные приложения.
Создание черновика приложения
- В личном кабинете разработчика нажмите на кнопку Создать приложение. Откроется форма создания черновика приложения.
- Укажите Псевдоним приложения. Псевдоним — уникальный идентификатор. Невозможно создать два приложения с одинаковыми псевдонимами.
- Создайте приложение подходящего типа: Серверное приложение, Телефония, Приложение лояльности. Тип приложения влияет на его платность и необходимость заполнения Дескриптора:
- Серверное приложение. Дескриптор приложения обязателен для заполнения. Правила заполнения дескриптора смотрите в разделе Дескриптор приложения. Создается с одним бесплатным тарифом. Можно изменить стоимость или добавить новые тарифы при редактировании.
- Телефония. Не может быть платным, не нужно заполнять Дескриптор. Телефония оплачивается пользователем отдельно через опцию CRM.
- Приложение лояльности. Не может быть платным, не нужно заполнять Дескриптор.
Редактирование черновика приложения
Если после создания приложения его настройки нужно изменить, выберите приложение в списке приложений и нажмите в меню Редактировать.
На вкладке Общая информация о приложении доступны те же поля, что и в форме создания черновика приложения. Исключение составляет только Псевдоним приложения - его изменять после создания нельзя.
На вкладке Учетные данные приложения отображаются параметры для идентификации приложения:
- Прямая ссылка на приложение. Публичная ссылка для прямого доступа к приложению на витрине приложений МС.
- Идентификатор приложения (appId). Используется как параметр запроса при взаимодействии по Vendor API.
- Глобальный идентификатор (appUid). Используется для формирования JWT.
- Секретный ключ. Используется для подписи JWT. Для просмотра нажмите на кнопку «глаз» у скрытого точками поля, для генерации нового ключа — кнопку Сгенерировать.
Для всех полей доступна кнопка Копировать, которая вставляет содержимое поля в буфер обмена.
По умолчанию Серверные приложения создаются с одним бесплатным тарифом. Для изменения стоимости приложения предназначен раздел Создание и редактирование тарифов на вкладке Тарифы.
Вкладка доступна только для Серверных приложений.
Для добавления тарифа нажмите кнопку Добавить новый тариф и заполните его поля:
- Название тарифа. Должно быть кратким и давать представление о сути тарифного плана. Примеры: Лайт, Мидл, Профи и т.д. Для приложений с одним тарифом не выводится на витрине.
- Платный. Признак платности. Для того чтобы сделать тариф бесплатным, уберите галочку. Бесплатным может быть только один тариф.
- Стоимость. Задает стоимость приложения в рублях в месяц. Для платного тарифа является обязательным полем.
- Триал. Признак наличия Пробного периода. Требует указания длительности в днях. Максимальная длительность — 14 дней. Триальным может быть только один тариф.
- Описание. Помогает пользователям сравнивать тарифы. Является обязательным в случае наличия 2-х и более тарифов у приложения.
Подробнее о выборе стоимости приложений в разделе Стоимость приложения.
Отправка на модерацию
Перед публикацией на витрине МоегоСклада новое приложение должно пройти модерацию.
Чтобы отправить приложение на модерацию, выберите нужное приложение в статусе Черновик из списка и нажмите пункт меню Отправить на модерацию.
Обязательно заполните поле Электронная почта для уведомлений в разделе Реквизиты / Информация о разработчике. Это позволяет получать оповещения и следить за процессом прохождения модерации.
После того как приложение отправлено на модерацию, его нельзя редактировать. Редактирование становится доступным снова, если модератор возвращает приложение на доработку.
Просмотр опубликованного приложения
Для просмотра информации о приложении выберите его в списке приложений и нажмите в меню пункт Посмотреть приложение.
В форме просмотра отображаются те же поля, что и в форме редактирования черновика приложения, но все они находятся в режиме только для чтения («read-only»).
Внесение изменений в опубликованное приложение
Функция доступна только для Серверных приложений.
Для изменения данных опубликованного приложения, выберите его в списке приложений и нажмите в меню пункт Создать версию для редактирования. После этого в списке приложения появится новая версия в виде черновика, в которую будут скопированы данные из опубликованного приложения. В списке приложений она будет отображаться в виде строки, вложенной в исходное (опубликованное) приложение. После создания версии приложения работать с ней можно как с обычным черновиком: изменять параметры, отлаживать на витрине и т.д.
Отличия от обычных черновиков следующие:
- У версии свои собственные идентификаторы приложения (appId и appUid), а также секретный ключ. Идентификаторы существующих тарифов у опубликованного приложения и версии совпадают.
- Нельзя удалять существующие тарифы (те, которые есть в опубликованном приложении).
- Нельзя менять стоимость и признак платности у существующих тарифов.
- Нельзя создавать более одной версии для одного опубликованного приложения.
После проверки внесенных изменений версия, так же как и черновик, отправляется на модерацию. После успешной публикации версия скрывается, а измененные поля переносятся в опубликованное приложение (в том числе увеличивается номер версии). При этом следующие поля остаются неизменными (не переносятся из черновика):
- Идентификатор приложения (appId).
- Псевдоним приложения.
- Секретный ключ (secretKey).
Ручная приостановка приложения
В личном кабинете разработчик может тестировать и отлаживать приложение при приостановке и возобновлении на аккаунте.
Чтобы протестировать приложение при приостановке:
- Привяжите аккаунт в МоемСкладе к аккаунту разработчика в личном кабинете разработчика (создайте аккаунт разработчика).
- Заведите платное приложение в личном кабинете разработчика.
- Установите приложение на аккаунте разработчика.
- Выберите приложение в списке и нажмите на меню Приостановить на аккаунте. Работа приложения будет приостановлена.
- Чтобы отладить приложение при возобновлении работы, нажмите на кнопку Активировать в карточке приложения на витрине МоегоСклада.
Приостановка возможна только для платных черновиков приложений, которые уже установлены или устанавливаются.
Продление пробного периода платного приложения
В личном кабинете разработчика можно продлить пробный период приложения. Продление на выбранном аккаунте пользователя доступно один раз и только для платных опубликованных приложений. Для этого:
- В Списке приложений выберите платное опубликованное приложение.
- Нажмите на пункт меню Дополнительный триал.
- В окне Предоставление пробного периода заполните поля Аккаунт и Количество дней.
- Для приложения с несколькими тарифами, выберите тариф, на который нужно предоставить пробный период.
- Если поля заполнены корректно, откроется окно Подтверждение пробного периода.
- Проверьте указанный пробный период и нажмите на кнопку Да, предоставить.
Редактирование информации о разработчике
Чтобы отредактировать информацию о разработчике приложения, в меню личного кабинета перейдите на вкладку Реквизиты / Информация о разработчике.
Функция доступна, пока на аккаунте отсутствуют приложения или есть только Черновики. В остальных случаях данные доступны только для просмотра, для изменения обратитесь к модератору МоегоСклада.
Также можно внести или отредактировать реквизиты для выплат денежных средств за платные приложения. Для этого в меню перейдите на вкладку Реквизиты / Реквизиты компании.
Привязка аккаунта к разработчику
Отладить приложение для магазина приложений можно на аккаунте разработчика. Чтобы сделать аккаунт в МоемСкладе аккаунтом разработчика, свяжите аккаунт с нужным разработчиком. Для этого в личном кабинете разработчика, в верхнем меню нажмите на кнопку Привязать.
Важно: Для привязки необходим доступ к электронной почте сотрудника, указанного в аккаунте МоегоСклада на странице Подписка. По умолчанию это администратор, зарегистрировавший аккаунт. Однако далее вместо администратора можно указать другого сотрудника.
Чтобы изменить уже существующую привязку, нажмите на имя текущей привязки. Предыдущая привязка будет удалена, установленные на отвязанном аккаунте приложения в статусах Черновик (Draft) и Готово, отправлено на модерацию (Ready) будут деинсталлированы.
Отладка приложений на аккаунтах разработчика
Отладить неопубликованное приложение можно в аккаунте разработчика. Неопубликованные приложения разработчика находятся на витрине приложений в блоке Приложения в разработке. Неопубликованные приложения видит только разработчик, для пользователей они не видны.
Неопубликованные приложения можно подключать и отключать так же, как и обычные опубликованные приложения.
Отчеты в личном кабинете разработчика
Сводный отчет по установкам приложений
В данном отчете можно посмотреть сколько активных установок есть у приложений, сколько из них пробных и оплачиваемых. Также можно посмотреть сколько приостановленных установок.
Для просмотра отчета по установкам необходимо перейти в раздел Отчеты в верхнем меню и выбрать вкладку Установки.
В отчет включаются:
- Все приложения независимо от платности и наличия установок
- В статусе Опубликовано и Снято с публикации.
Отчет по выручке платных приложений
Отчет предоставляет информацию о том, сколько приложение заработало денежных средств. Учитываются только реально оплачиваемые установки. Установки приложений на Пробном тарифе и с Пробным периодом не учитываются.
Для просмотра отчета по выручке необходимо в разделе Отчеты верхнего меню выбрать вкладку Выручка.
В отчет включаются:
- Приложения во всех статусах.
- Платные приложения, у которых есть хотя бы одна успешная установка.
- Бесплатные приложения, если ранее они были платными и имели успешные установки.
Отчет об использовании приложений
Отчет об использовании приложений является основанием для выплаты разработчику денежных средств за использование приложения пользователями МоегоСклада. Отчет содержит информацию о том, сколько денег заработало каждое приложение в отдельности и все приложения разработчика вместе за месяц.
Для просмотра и согласования отчета в разделе Отчеты верхнего меню выберите вкладку Выплаты.
Отчет формируется раз в месяц, доступен для скачивания в формате PDF. Процесс работы с отчетом:
- Чтобы отчет сформировался, заполните реквизиты.
- Отчет поступает в статусе Сформировано. На почту приходит оповещение.
- Просмотрите и согласуйте отчет (кнопка Согласовать). Статус отчета меняется на Согласовано.
- Вознаграждение разработчика выплачивается согласно банковским реквизитам. В отчете появляется отметка Выплачено.
Если по отчету есть вопросы или возражения, не согласовывайте его и обратитесь к сотруднику МоегоСклада.
Отчет по причинам удаления приложений
При удалении приложения на витрине МоегоСклада пользователю предлагается оставить отзыв о приложении:
Если пользователь указывает одну и более причин удаления и нажимает на кнопку Отправить отзыв, разработчик получает этот отзыв.
На email разработчика, указанный в личном кабинете в разделе:
Реквизиты -> Информация о разработчике -> Контакты разработчика -> Электронная почта для уведомлений
приходит сообщение с причинами удаления.
Список всех отзывов пользователей отображается в отчете Причины удаления приложений. Чтобы просмотреть его, в разделе Отчеты верхнего меню выберите вкладку Причины удаления.
Чтоба настроить отправку писем, отметьте флагом "Отправлять уведомления о причинах удаления приложений на email". Если функция отключена, отзывы продолжают собираться в данный отчет.
Ограничение: В отчете выводятся только 1000 последних отзывов за выбранный период.
Права доступа для приложений
Чтобы серверное приложение получило доступ к JSON API, в дескрипторе приложения нужно указать желаемый уровень доступа к JSON API. Можно запросить следующие уровни доступа:
admin — соответствует пользовательской роли
Системный администратор
. Приложение получает полный доступ ко всем отчетам, сущностям и документам в МоемСкладе. Уровень в перспективе будет устранен.custom — соответствует пользовательской роли
Индивидуальная роль
. Приложение получает доступ только к указанным в дескрипторе отчетам, сущностям и документам. Уровень рекомендуется для использования всеми приложениями кроме тех, которые работают с вебхуками и задачами.
Права приложения на аккаунте фиксируются на момент установки приложения. При последующем обновлении дескриптора приложения права приложения на аккаунте сохраняются.
При разработке приложений одновременно могут существовать и работать установки приложений с разными правами. Необходимо в том или ином виде поддерживать работу приложения с несколькими наборами прав.
Предоставление приложению новых дополнительных прав должно происходить через явное согласие пользователя. Этот механизм будет реализован в будущем. Сейчас пользователь может обновить права через переустановку. Нужно следить, чтобы при удалении приложения данные аккаунта сохранялись некоторое время.
Подробнее о ролях пользователей смотрите в документации JSON API.
Подробнее о правах доступа для приложений смотрите в разделе Информация о правах доступа.
Прямая ссылка на приложение
Разработчик может получить прямую ссылку на приложение.
Для этого в личном кабинете разработчика перейдите в режим просмотра или редактирования приложения и скопируйте прямую ссылку на приложение.
Также прямую ссылку можно получить в витрине приложений из адресной строки браузера, если открыть карточку приложения на витрине.
При переходе по короткой ссылке открывается витрина с карточкой приложения. Если пользователь авторизован, витрина с карточкой открывается сразу. Если нет, для просмотра необходимо авторизаваться.
Если приложение было удалено или недоступно по прямой ссылке, при переходе по ссылке открывается витрина и появляется сообщение о том, что приложение не найдено.
Если приложение снято с публикации, но уже установлено некоторыми пользователями, оно будет отображаться для этих пользователей по ссылке как на витрине. Для остальных пользователей приложение будет недоступно.
Список последних изменений
02-04-2024
Добавлено
- Возможность получения шаблонов документов по токену приложения.
07-03-2024
Добавлено
- В тело запроса при Активация приложения на аккаунте добавлен атрибут
subscription.notForResale
. - В тело ответа при Получение статуса приложения на аккаунте добавлен атрибут
subscription.notForResale
.
19-01-2024
Добавлено
- В тело запроса при Активация приложения на аккаунте добавлен атрибут
subscription.expiryMoment
. - В тело ответа при Получение статуса приложения на аккаунте добавлен атрибут
subscription.expiryMoment
.
27-09-2023
Добавлено
- В тело запроса при Активация приложения на аккаунте добавлен атрибут
subscription.tariffName
(название тарифа) и информация о новой причине активации ("cause": "TariffChanged"
). - В тело ответа при Получение статуса приложения на аккаунте добавлен атрибут
subscription.tariffName
(название тарифа).
19-09-2023
Изменено
- В дескрипторе приложений в качестве значения блока
access.resource
можно указывать https://api.moysklad.ru/api/remap/1.2.
06-09-2023
Добавлено
- В тело запроса при активации приложения на аккаунте добавлена информация о подписке (атрибут
subscription
).
30-08-2023
Изменено
- Базовый URL REST-эндпоинтов vendor API (
MARKETPLACE-ENDPOINT
) сменился на https://apps-api.moysklad.ru/api/vendor/1.0. - Для запросов к vendor API необходимо использовать сжатие (заголовок
Accept-Encoding
) - URL личного кабинета разработчика сменился на https://apps.moysklad.ru/cabinet.
23-11-2022
Изменено
- Версия 2 дескриптора приложений: Виджеты в Розничном возврате, Внесении и Выплате денег.
- Поддержка дополнительного поля типа справочник [Товар] в протоколе change-handler.
18-11-2022
Изменено
- Версия 2 дескриптора приложений: Протокол change-handler и validation-feedback для виджетов в Розничной продаже.
09-11-2022
Изменено
- Версия 2 дескриптора приложений: Протокол change-handler и validation-feedback для виджетов в Возврате покупателя.
12-10-2022
Изменено
- Версия 2 дескриптора приложений: Протокол update-provider для виджетов в Списании и Счете покупателю.
06-09-2022
Изменено
- Версия 2 дескриптора приложений: Протокол change-handler и validation-feedback для виджетов в Счете поставщика.
23-08-2022
Изменено
- Прекращена поддержка типа приложений iframe.
01-08-2022
Добавлено
- Поддержка накладных расходов (поле
overhead
) в протоколе update-provider в Отгрузке, Перемещении и Оприходовании.
26-07-2022
Добавлено
- Поддержка накладных расходов (поле
overhead
) в протоколе update-provider в Приемке.
22-07-2022
Изменено
- Версия 2 дескриптора приложений: Протокол update-provider для виджетов в Отгрузке, Перемещении и Оприходовании.
07-07-2022
Изменено
- Версия 2 дескриптора приложений: Протокол change-handler и validation-feedback для виджетов в Счете покупателю.
04-07-2022
Изменено
- Версия 2 дескриптора приложений: Протокол update-provider для виджетов в Приемке.
20-05-2022
Добавлено
- Поддержка полей
incomingDate
иincomingNumber
для протокола change-handler в Приемке.
20-04-2022
Изменено
- Требования к иконкам приложений.
04-03-2022
Изменено
- Версия 2 дескриптора приложений:
Поддержка протокола навигации (
<navigation-service/>
) в главном iframe приложения и модальных окнах.
27-01-2022
Изменено
- Версия 2 дескриптора приложений:
Гибкие права приложений — поддержка права видеть себестоимость, цену закупки и прибыль товаров
.
21-01-2022
Изменено
- Версия 2 дескриптора приложений: Виджеты в Товаре, Модификации, Услуге, Комплекте, Группе товаров.
21-12-2021
Изменено
- Версия 2 дескриптора приложений: Виджеты на странице создания в Перемещении, Списании и Оприходовании. Поддержка протокола валидации на страницах создания и редактирования Перемещения, Списания и Оприходования.
16-12-2021
Изменено
- Версия 2 дескриптора приложений: Виджеты на странице создания в Приемке и Отгрузке. Поддержка протокола валидации на страницах создания и редактирования Приемки и Отгрузки.
10-12-2021
Изменено
- Версия 2 дескриптора приложений: Протокол change-handler для виджетов в Перемещении, Списании и Оприходовании.
29-11-2021
Изменено
- Версия 2 дескриптора приложений: Протокол change-handler для виджетов в Приемке.
24-11-2021
Изменено
- Версия 2 дескриптора приложений: Виджеты на странице создания в Заказе покупателя. Поддержка протокола валидации при создании Заказа покупателя.
19-11-2021
Изменено
- Версия 2 дескриптора приложений: Поддержка протокола валидации при редактировании Заказа покупателя.
17-11-2021
Изменено
- Версия 2 дескриптора приложений:
Поддержка селектора групп товаров (
<good-folder-selector/>
) в главном iframe приложения и модальных окнах.
11-11-2021
Изменено
- Версия 2 дескриптора приложений: Протокол change-handler для виджетов в Отгрузке.
01-11-2021
Изменено
- Версия 2 дескриптора приложений: Протокол update-provider для виджетов в Заказе покупателя.
21-10-2021
Изменено
- Версия 2 дескриптора приложений: Протокол навигации в виджетах.
07-10-2021
Изменено
- Версия 2 дескриптора приложений: Виджеты в Возвратах покупателя и в Возвратах поставщику.
16-09-2021
Изменено
- Версия 2 дескриптора приложений: Виджеты в Перемещении, Списании, Оприходовании, Внутреннем заказе, Инвентаризации.
30-08-2021
Изменено
- Версия 2 дескриптора приложений: Гибкие права приложений.
10-08-2021
Изменено
- Версия 2 дескриптора приложений: Стандартные диалоги.
13-05-2021
Изменено
- Версия 2 дескриптора приложений: Протокол change-handler для виджетов в Заказе покупателя.
21-01-2021
Изменено
- Версия 2 дескриптора приложений: Виджеты в Розничной продаже, Входящем и Исходящем платеже, Приходном и Расходном ордере.
13-01-2021
Изменено
- Версия 2 дескриптора приложений: Кастомные модальные окна в виджетах.
04-12-2020
Изменено
- Версия 2 дескриптора приложений: Виджеты в Счете поставщика, Заказе поставщику, Заказе на производство, Приемке.
30-11-2020
Изменено
- Версия 2 дескриптора приложений: Виджеты с поддержкой протокола dirty-state.
13-11-2020
Изменено
- Версия 2 дескриптора приложений: Виджеты с поддержкой протокола save-handler.
10-11-2020
Изменено
- Версия 2 дескриптора приложений: Виджеты в новой карточке Контрагента.
09-11-2020
Изменено
- Версия 2 дескриптора приложений: Виджеты в Счете покупателю.
22-10-2020
Добавлено
- VendorApi 1.0: Новый эндпоинт на стороне МоегоСклада: Получение статуса приложения на аккаунте.
08-10-2020
Изменено
- Версия 2 дескриптора приложений: Виджеты с поддержкой селектора групп товаров.
22-09-2020
Изменено
- Версия 2 дескриптора приложений: Виджеты в Заказе покупателя и Отгрузке.
18-08-2020
Добавлено
- Версия 2 дескриптора приложений: Виджеты в карточке контрагента.