Документация для вендоров
Терминология
МойСклад - SaaS-сервис управления торговлей, доступен по ссылке.
Магазин приложений - это набор программных компонентов в рамках сервиса МойСклад, позволяющий сторонним вендорам размещать свои приложения на витрине приложений, а пользователям МоегоСклада устанавливать (подключать) эти приложения и пользоваться ими.
Витрина приложений - отдельный экран МоегоСклада, на котором размещаются опубликованные приложения. Витрина доступна всем пользователям МоегоСклада, установка приложений - только администратору.
Вендор - сторонний по отношению к МоемуСкладу разработчик приложений для магазина приложений.
Приложение - разработанный вендором программный продукт, расширяющий и дополняющий функционал МоегоСклада. Приложение размещается на витрине магазина приложений и доступно для установки на аккаунт администратором аккаунта.
Аккаунт - пользовательский аккаунт МоегоСклада. Обычно аккаунт соответствует одной компании/бизнесу, в аккаунте несколько пользователей.
Аккаунт разработчика - привязанный к конкретному вендору пользовательский аккаунт МоегоСклада. На таком аккаунте на витрине приложений отображаются приложения вендора в статусах Черновик (Draft) и Готово, отправлено на модерацию (Ready), что позволяет выполнять проверку и отладку приложений до их публикации для всех пользователей МоегоСклада. Технически к одному вендору может быть привязано несколько аккаунтов.
Приложение на аккаунте - приложение, установленное (подключенное) на аккаунт. Альтернативный взаимозаменяемый термин - экземпляр приложения.
Пользователь МоегоСклада - конечный пользователь сервиса МойСклад, работающий с МоимСкладом в рамках определенного аккаунта.
Администратор аккаунта - пользователь МоегоСклада с правами администратора (в рамках определенного аккаунта).
Активация приложения - процесс включения приложения для конкретного аккаунта в системе вендора, выполняемый при подключении приложения пользователем (или при возобновлении платного приложения). В рамках активации происходит оповещение сервера вендора со стороны МоегоСклада.
Деактивация приложения - процесс выключения приложения для конкретного аккаунта в системе вендора, выполняемый при отключении приложения пользователем (или при приостановке платного приложения). В рамках деактивации происходит оповещение сервера вендора со стороны МоегоСклада.
Приостановка приложения - автоматический процесс выключения платного приложения при уменьшении количества "Опций платных приложений" или неоплате подписки пользователем МоегоСклада. После приостановки токен доступа приложения аннулируется. Первыми приостанавливаются последние установленные приложения на аккаунте.
Возобновление приложения - процесс включения приостановленного платного приложения при появлении на аккаунте свободных "Опций платных приложений". Возобновление может быть выполнено как автоматически (после оплаты пользователем МоегоСклада подписки или дополнительных "Опций платных приложений"), так и вручную пользователем МоегоСклада (активация экземпляра приложения по кнопке после освобождения "Опции платных приложений" путем удаления экземпляра другого платного приложения). После возобновления приложению выдается новый токен доступа. Первыми возобновляются приложения, установленные ранее прочих.
Быстрый старт
Как создать и опубликовать приложение
Для магазина приложений МоегоСклада можно создать приложение на любом языке программирования, с использованием любых фреймворков. Допустимые типы приложений: серверные приложения, телефония, приложения лояльности.
Каждый тип приложений подключается к МоемуСкладу с помощью своего 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.
Если пользователь должен будет настраивать приложение при его установке на аккаунт, реализуйте iframe-часть приложения. Обновите дескриптор в черновике, добавив тег
iframe
. По возможности используйте UI Kit МоегоСклада.Если в iframe-части серверного приложения будет какая-либо пользовательская функциональность кроме настроек, предусмотрите доступ к этой функциональности для пользователей с разными правами доступа (не только правами администратора).
Если в приложении нужны дополнительные поля для сущностей и/или документов, создайте их через JSON API 1.2 в рамках активации приложения. Если при этом процесс создания занимает много времени (более одной минуты) — используйте асинхронную активацию с возвратом статуса Activating.
Если приложение нужно встроить в интерфейс МоегоСклада, создайте виджет приложения. Для этого нужно реализовать iframe-часть виджета и обновить дескриптор, добавив тег
widgets
. Подробнее читайте в разделе Виджеты.Разместите приложение в магазине приложений.
Демо-приложение
Для демонстрации взаимодействия приложений с магазином приложений МоегоСклада было создано демо-приложение на 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 (см. скриншот), который доступен по ссылке https://github.com/moysklad/html-marketplace-1.0-uikit.
Одновременно на аккаунте может быть подключено несколько серверных приложений.
Доступна отладка на аккаунтах разработчика.
Телефония
Эти приложения представляют собой интеграцию с внешними сервисами телефонии по 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
Одновременно на аккаунте может быть подключено только одно такое приложение.
Для отладки можно использовать приложение Loyalty API (находится первым в блоке Скидки) либо собственный черновик приложения совместно с приложением Касса МоегоСклада (кроме версии для iOS).
Требования к приложениям
Требования к приложениям
Ниже перечислены требования, которым должно соответствовать приложение для успешного прохождения модерации:
- приложение должно быть полезным для пользователей МоегоСклада
- форма сбора лидов модерацию не пройдет
- приложение должно получать доступ к данным пользователей через JSON API 1.2 с использованием токена, выдаваемого приложению при установке на аккаунт
- запрашивать у пользователя логин-пароль для доступа с их использованием к JSON API НЕ допускается
- использовать для доступа к данным пользователя следует именно JSON API 1.2 (но не JSON API 1.1), так как только в JSON API 1.2 есть возможность доступа по токену
- если приложение требует настройки пользователем, то настройка должна выполняться на стороне МоегоСклада (в iframe-части)
- не пройдет модерацию приложение, если оно представляет собой только связку с внешним сервисом, а сама настройка интеграции с МоимСкладом выполняется в этом внешнем сервисе
- если в окне настройки приложения (в iframe-части) появляется вертикальный скролл, то необходимо настроить автоматическое масштабирование по высоте и добавить тег expand в дескрипторе.
- для разработки UI желательно использовать наш UI Kit (требование не жесткое)
После публикации необходимо обеспечить бесперебойную работу приложения. Если приложение начинает давать сбои в работе (в том числе при установке или удалении), МойСклад может снять приложение с публикации до устранения вендором причины ошибок.
Требования к иконкам приложений
При разработке иконки, которая будет отображаться на витрине приложений, нужно ориентироваться на следующие требования:
- Отчетливая крупная форма отчетливого цвета. Иконка будет на белом фоне и может теряться если в ней будут мелкие детали на белом фоне. К тому же один крупный знак будет просто и понятно восприниматься.
- Не делайте иконку без фона, если только она не заполняет выделенное пространство плотно, иначе она будет «исчезать» на фоне других иконок.
- Не ставьте скриншоты и фотографии и элементы интерфейса. Фотографии и экраны приложений могут включать много ненужных и плохо различичмых деталей. Детали интерфейса могут быть обманчивыми и путающими человека.
- По возможности не используйте слова. Используйте знак вашего логотипа, если у вас есть отдельный. Если используете какую-то букву из вашего логотипа как знак, то можно использовать его. Если вы так не делаете, то можно поставить полное название, учитывая все остальные пункты.
- Не ставьте логотип МоегоСклада или его имитации. Указать наш логотип значит сказать, что мы как-то участвовали в разработке приложения, что будет неправдой.
- Иконка - квадрат со стороной не менее 300x300. Рекомендуем использовать иконку размером 300х300. Можно использовать все это пространство и не оставлять внутренних белых рамок.
- Рекомендуемое расширение: .svg, но также можно использовать .png или .jpeg.
Условия размещения и оплаты приложений
Количество приложений, которые пользователь может установить на свой аккаунт, зависит от тарифа, актуальные условия тарифов размещены по ссылкам:
Условия размещения приложений
- Функциональность приложения доступна пользователю по подписке в полной мере. Исключение: если приложение использует услуги сторонних служб, например, СМС-рассылки или телефония, оплата этих услуг не входит в стоимость подписки и взимается вендором отдельно.
- Оплата приложения обычно взимается через биллинг МоегоСклада. В исключительных случаях возможно получения оплаты вендором. Конкретный механизм и его реализация зависит от вендора и согласуется индивидуально. Исключение: если приложение имеет свой биллинг или интеграции со сторонними службами (СМС-рассылки, телефония), использующими свой транзакционный биллинг, возможно получение оплаты вендором. Конкретный механизм и его реализация зависит от вендора и согласуется индивидуально.
- Приложения и интеграции, использующие биллинг МоегоСклада, доступны только по платной подписке. Оплата взимается в рамках подписки на МойСклад. Подробнее см. Условия оплаты
- Разместить приложение с платной подпиской могут только индивидуальный предприниматель (ИП) или общество с ограниченной ответственностью (ООО), которые являются налоговыми резидентами Российской Федерации.
- Размещая приложение с платной подпиской, разработчик/вендор принимает условия договора-оферты для разработчиков.
Условия оплаты приложений
- Для приложений с платной подпиской можно установить ежемесячную оплату, кратную 500 рублям. Например, 500, 1000 или 2500 рублей.
- Оплата за подписку на приложение взимается с пользователей в рамках стоимости их подписки на МойСклад в категории «Опции платных приложений».
- На оплату подписки приложения распространяются условия скидок, которые действуют на подписку МоегоСклада. Пользователь получает скидку при единовременной оплате подписки на три, шесть или двенадцать месяцев.
- Вендор получает 75% от стоимости подписки своего приложения без учета скидок и комиссий. Например, если пользователь оплатил подписку на приложение стоимостью 500 рублей на три месяца, вендор получает 500 * 0,75 = 375 рублей в месяц.
- Если пользователь не продлил или не оплатил подписку, его доступ к приложению приостанавливается, а при поступлении оплаты возобновляется. Подробнее см. Процесс деактивации приложения на аккаунте.
- В будущем возможно увеличение или уменьшение доли вендора.
Пробный период платных приложений
Для приложения с платной подпиской можно установить пробный период, чтобы пользователь мог некоторое время поработать с приложением бесплатно и оценить его.
Условия пробного периода:
- Пробный период можно установить до и после публикации приложения. После публикации пробный период выдается только новым пользователям — тем, кто еще ни разу не устанавливал приложение.
- Пробный период можно изменить или убрать у уже опубликованного приложения. При этом уже установленные экземпляры приложения продолжат свою работу с тем пробным периодом, который был доступен пользователям на момент установки приложения.
- Пробный период длится от одного до четырнадцати дней. После его завершения приложение становится доступно только по подписке и продолжает работу при наличии оплаты. Оплата производится с помощью «Опций платных приложений». При отсутствии у пользователя «Опций платных приложений» в количестве, необходимом для оплаты, доступ к приложению приостанавливается пока пользователь не внесет оплату.
- Приложение с пробным периодом может установить пользователь с любым тарифом независимо от количества «Опций платных приложений» на аккаунте. Исключение: пользователям с Бесплатным тарифом недоступны некоторые возможности приложений, например вебхуки.
Чтобы установить пробный период платного приложения до его опубликования, перейдите в личный кабинет вендора и укажите количество дней в форме создания приложения.
Чтобы добавить, изменить или удалить пробный период приложения после публикации, обратитесь к модератору МоегоСклада по адресу apps@moysklad.ru.
Продление пробного периода
Пробный период можно продлить только для опубликованного приложения, доступного по платной подписке. Один раз для одного аккаунта МоегоСклада.
Пробный период можно предоставить отдельным пользователям (аккаунтам), если приложение доступно по платной подписке и не имеет пробного периода.
Пробный период запускается по нажатию на кнопку подтверждения. Он длится строго указанное количество дней, но не более четырнадцати. Предоставленный для продления пробный период не суммируется с количеством дней текущего пробного периода и может на него накладываться. То есть если вы установили пробный период на четырнадцать дней, а на десятый день продлили его еще на семь дней, оставшийся пробный период составит семь дней, а не одиннадцать.
Чтобы продлить пробный период приложения, перейдите в личный кабинет вендора, выберите приложение в Списке приложений и нажмите на кнопку Продлить пробный период.
Пробный период нельзя предоставить, если:
- Приложение не опубликовано.
- Приложение доступно для всех пользователей без подписки и иных ограничений.
- На аккаунте для приложения действует активный пробный период и новый срок сокращает его: уменьшать количество дней нельзя.
- На аккаунте для приложения действует активная оплаченная подписка.
- На аккаунте для приложения уже продлевали пробный период.
Изменение стоимости приложения
Доступны следующие изменения стоимости приложения:
- Назначение платной подписки для приложения.
- Снятие ограничений (подписки) для доступа к приложению.
- Изменение стоимости платной подписки для приложения.
Если приложение не опубликовано, перейдите в личный кабинет вендора, чтобы назначить, изменить или отменить стоимость подписки. Если приложение опубликовано, обратитесь к модератору МоегоСклада по адресу apps@moysklad.ru.
Если для опубликованного приложения устанавливается платная подписка, обязательно нужно назначить пробный период сроком от семи до четырнадцати дней. Так пользователи, установившие приложение без подписки, смогут еще некоторое время поработать с ним бесплатно и принять решение о покупке подписки.
Порядок получения выплат
В начале каждого месяца в личном кабинете вендора формируется отчет за прошедший месяц. В отчете указаны все проданные подписки для всех приложений, их количество и итоговая сумма к выплате.
Чтобы получить выплату:
- Заполните реквизиты.
- Проверьте и согласуйте отчет в кабинете вендора. Для этого перейдите в раздел Отчеты → Выплаты, выберите период и нажмите на кнопку Согласовать в соответствующей строке.
- Выплата поступит на указанные реквизиты вендора в течение пяти рабочих дней с даты согласования отчета.
По вопросам, связанным с выплатами, обращайтесь по адресу 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 МоегоСклада, смотрите в видео.
Виджеты
Виджет - это плагин с визуальной частью, представляющей собой прямоугольный блок в конкретном месте в UI МоегоСклада. Содержимое блока определяется приложением.
FAQ по виджетам
Какими могут быть виджеты?
Сейчас можно создать только виджеты с:
- фиксированной шириной: 416px - для всего виджета (с рамкой), 384px - для рабочей области (iframe, определяемый приложением).
- фиксированной высотой: задается в дескрипторе.
Поддерживается два режима отображения виджетов:
- развернутая форма (полная) - виджет отображается с рабочей iframe-областью.
- свернутая форма - рабочая область скрыта.
Куда можно встраивать виджеты?
Список страниц для встраивания виджетов можно увидеть тут.
В каком порядке отображаются виджеты?
Если у пользователя установлены сразу несколько приложений с виджетами, встроенными на одну страницу МС, то отобразятся все виджеты в порядке, соответствующем расположению родительских приложений на витрине. В редакторах, где поддерживается drag-and-drop, пользователь может сам поменять порядок отображения виджетов.
В чем отличия у точек встраивания edit и create?
Виджеты на страницах создания (например в точке встраивания document.customerorder.create
) имеют ограниченную функциональность
по сравнению с виджетами на страницах редактирования (document.customerorder.edit
).
Это выражается как в меньшем количестве поддерживаемых протоколов, так и в реализации самих протоколов.
Например, в сообщении Change часть полей, которые заполняются после первого сохранения документа (id
, created
, meta
и т.д.)
будет заполнено значением null
.
Поддержка сервисных протоколов виджетами на страницах создания пока не реализована. Список поддерживаемых дополнительных протоколов в зависимости от точки встраивания можно увидеть тут.
Хочу встроить виджет в точку МоегоСклада, которой нет в списке - что делать?
Если вы хотите встроить виджет в какую-то точку МоегоСклада, но её пока нет в списке доступных точек расширения - обращайтесь с предложением в Telegram или по электронной почте, мы всегда открыты для таких запросов.
Жизненный цикл виджета
Виджет начинает отображаться на странице только после перехода приложения в статус Activated. Если приложение предполагает настройку и статус SettingsRequired - виджет отобразится после настройки приложения.
При первом открытии страницы с виджетом (в рамках одной вкладки браузера) cистема МоегоСклада
загружает виджет по HTTP GET-запросом по URL'у, указанному в теге sourceUrl. При этом система
генерирует (и передает GET-параметром contextKey
) ключ, по которому виджет получает текущий
контекст пользователя через Vendor API.
Прочие данные передаются при помощи 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://online.moysklad.ru/xml/ns/appstore/app/v2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://online.moysklad.ru/xml/ns/appstore/app/v2
https://online.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://online.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://online.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://online.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://online.moysklad.ru/api/remap/1.2/entity/product/9e73e41d-a0de-11e9-9109-f8fc00095c81",
"metadataHref" : "https://online.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://online.moysklad.ru/api/remap/1.2/entity/customerorder/c4c6e6ea-b3f5-11eb-0a80-35ed000000b8",
"metadataHref": "https://online.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://online.moysklad.ru/api/remap/1.2/entity/store/605491e4-b3f2-11eb-0a80-35ed00000074",
"metadataHref": "https://online.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://online.moysklad.ru/api/remap/1.2/entity/currency/6055a619-b3f2-11eb-0a80-35ed00000079",
"metadataHref": "https://online.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://online.moysklad.ru/api/remap/1.2/entity/organization/6051401c-b3f2-11eb-0a80-35ed00000072",
"metadataHref": "https://online.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://online.moysklad.ru/api/remap/1.2/entity/counterparty/60550738-b3f2-11eb-0a80-35ed00000077",
"metadataHref": "https://online.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://online.moysklad.ru/api/remap/1.2/entity/customerorder/metadata/states/60850d6a-b3f2-11eb-0a80-35ed00000097",
"type": "state",
"metadataHref": "https://online.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://online.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://online.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://online.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://online.moysklad.ru/api/remap/1.2/entity/counterparty/6054e7f9-b3f2-11eb-0a80-35ed00000075",
"metadataHref": "https://online.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://online.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://online.moysklad.ru/api/remap/1.2/entity/product/788a1cc7-b3f6-11eb-0a80-35ed000000e2",
"metadataHref": "https://online.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://online.moysklad.ru/api/remap/1.2/entity/service/9d0c9a63-b3f6-11eb-0a80-35ed000000eb",
"metadataHref": "https://online.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
cм. в правой части экрана.
Здесь
messageId
- целочисленный идентификатор сообщения, уникальный в рамках текущего взаимодействия виджет - хост-окно. Назначается виджетом;updateState
- список полей, которые необходимо изменить. Соответствует телу запроса для обновления соответствующего документа в JSON API (см. например Заказ покупателя)
Пример сообщения UpdateResponse
cм. в правой части экрана.
Сообщение UpdateResponse
{
"name": "UpdateResponse",
"correlationId": 10
}
Здесь
correlationId
- идентификатор соответствующего сообщенияUpdateRequest
.
Сообщение UpdateRequest для изменения дополнительных полей
{
"name":"UpdateRequest",
"messageId":10,
"updateState":{
"description": "красивое",
"attributes":[
{
"meta":{
"href":"https://online.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://online.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://online.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://online.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://online.moysklad.ru/api/remap/1.2/entity/bundle/c02e3a5c-007e-11e6-9464-e4de00000006",
"type": "bundle"
}
},
"reserve": 30
}
]
}
}
Сообщение UpdateRequest для добавления одной новой и сохранения 3-х существующих позиций
{
"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://online.moysklad.ru/api/remap/1.2/entity/service/c02e3a5c-007e-11e6-9464-e4de00000006",
"type": "service"
}
}
}
]
}
}
Работа с полями из updateState
:
- Список может содержать одно или несколько полей для изменения.
- При изменении значения поля на то же самое, поле на UI не обновляется и документ не считается измененным - т.е. пользователь может закрыть экран редактирования документа без диалога с вопросом “Данные были изменены. Сохранить изменения?”. К позициям это не относится: если позиции в запросе есть - список всегда обновляется и документ считается измененным.
- Содержимое поля можно сбросить указав в качестве его значения
null
. - Если пришло несколько сообщений подряд, все они обрабатываются последовательно.
- Поля типа Дата-время необходимо передавать с включением информации о часовом поясе, чтобы избежать неопределенности в интерпретации.
- Значения полей типа Дата-время всегда округляются до минут (секунды отбрасываются), по аналогии с UI.
- Для значений ссылочных полей обязательными являются
meta.href
иmeta.type
, остальные поля внутриmeta
игнорируются. - Для одновременного изменения согласованных полей, таких как Организация (Контрагент), Счет и Договор необходимо чтобы их значения были совместимы: счет должен принадлежать указанной организации, договор должен относиться к этой организации и контрагенту. В противном случае - возникнет ошибка валидации и значения полей на UI не изменятся.
Работа с дополнительными полями (attributes):
- Для идентификации дополнительного поля необходимо указать
meta.href
либоid
(если указаны оба поля, значение берется изmeta.href
). - Для передачи значения поля служит поле
value
. - Остальные поля не являются обязательными.
- Пока не поддерживаются доп. поля типа Файл.
Работа с позициями (positions):
- При указании позиций в сообщении
UpdateRequest
существующие позиции в документе полностью заменяются позициями из сообщения. - Для редактирования позиции необходимо использовать
id
существующей позиции (например, получив их в сообщенииChange
или через JSON API). - При необходимости добавить новые позиции с сохранением существующих можно указать
id
существующих позиций и новые позиции - в результате останутся существующие позиции и добавятся новые. - До сохранения документа в БД у новых позиций отсутствует
id
. - Позиции добавляются в порядке, который указан в сообщении.
Узнать, для каких точек поддерживается протокол update-provider, можно тут.
Сервисы хост-окна
В настоящий момент сервисы хост-окна представлены следующими:
Селектор группы товаров
Дескриптор приложения с виджетом, использующим селектор группы товаров
<ServerApplication xmlns="https://online.moysklad.ru/xml/ns/appstore/app/v2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://online.moysklad.ru/xml/ns/appstore/app/v2
https://online.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://online.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-часть и popup используют селектор группы товаров
<ServerApplication xmlns="https://online.moysklad.ru/xml/ns/appstore/app/v2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://online.moysklad.ru/xml/ns/appstore/app/v2
https://online.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://online.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-часть или попап-окно начали поддерживать селектор в дескрипторе, необходимо добавить блок:
<uses>
<good-folder-selector/>
</uses>
соответственно в блоки widgets
, iframe
или popup
. Примеры справа.
В частности далее рассмотрим кейс с виджетом.
Когда виджет отправляет хост-окну сообщение 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://online.moysklad.ru/xml/ns/appstore/app/v2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://online.moysklad.ru/xml/ns/appstore/app/v2
https://online.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://online.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": "Нет"}
]
}
Параметры сообщения 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://online.moysklad.ru/xml/ns/appstore/app/v2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://online.moysklad.ru/xml/ns/appstore/app/v2
https://online.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://online.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-часть и popup используют протокол навигации
<ServerApplication xmlns="https://online.moysklad.ru/xml/ns/appstore/app/v2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://online.moysklad.ru/xml/ns/appstore/app/v2
https://online.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://online.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 или попап начали поддерживать протокол навигации в дескрипторе необходимо добавить блок:
<uses>
<navigation-service/>
</uses>
соответственно в блоки widgets
, iframe
или popup
. Примеры справа.
В частности далее рассмотрим кейс с виджетом. Когда виджет отправляет хост-окну сообщение 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-частью, использующие кастомные попапы
<ServerApplication xmlns="https://online.moysklad.ru/xml/ns/appstore/app/v2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://online.moysklad.ru/xml/ns/appstore/app/v2
https://online.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://online.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"
}
Использование модальных окон позволяет виджетам (которые имеют фиксированную высоту и ширину) и iframe-части расширить свою функциональность путем отображения всплывающего (попап) окна, аналогичного существующим в МоемСкладе, с возможностью передачи данных как от виджета и iframe в попап-окно, так и в обратном направлении.
Попап-окна отображаются развернутыми на весь экран, что позволяет показать в них больший или дополнительный объем информации. При этом МС отрисовывает только заголовок окна с кнопкой закрытия в верхнем правом углу (в качестве заголовка используется название приложения). Все остальное должно быть отображено страницей вендора (страница отображаемая внутри iframe попапа).
Далее рассмотрим модальные окна на примере виджетов, для iframe-части аналогично. Для использования виджетом
попап-окон необходимо добавить блок
<popups>
...
</popups>
в дескрипторе приложения. Пример дескриптора с блоком popups
можно увидеть справа.
Виджет может отобразить одно из попап-окон, отправив сообщение ShowPopupRequest
с именем выбранного попапа хост-окну.
Пример такого сообщения можно увидеть справа.
Здесь:
messageId
- идентификатор сообщения;popupName
- имя открываемого попапа;popupParameters
- опциональные параметры, передаваемые попапу виджетом (может иметь любой тип, в том числеnull
).
МойСклад проверяет сообщение ShowPopupRequest
и, если сообщение валидно, то отображает попап-окно,
загружая страницу попапа по адресу sourceUrl
в iframe с передачей contextKey
в GET-параметре (аналогично загрузке виджета).
Значение 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
.
Подытожим сведения о попап-окнах
- Попап-окно открывается на весь экран аналогично прочим модальным окнам в UI МоегоСклада (например, вызываемые “через карандаш” окна редактирования сущностей в полях).
- Попап-окно является модальным, т.е. открывается поверх текущей страницы МС и требует действия от пользователя внутри этого окна (взаимодействие с веб-страницей и/или закрытие попапа).
- Содержимое попапа определяется вендором (веб-содержимое загружается в iframe окна аналогично загрузке iframe виджета или основной iframe-части приложения).
- Попап, так же как и виджеты и основной iframe приложения, может получать текущий контекст пользователя.
- В качестве заголовка попап-окна используется название приложения.
- У попапов всегда есть кнопка для принудительного закрытия попапа пользователем (крестик справа вверху).
- Попап изменяет свои размеры при изменении размеров окна браузера.
Ошибки
При получении сообщения от виджета хост-окно производит валидацию сообщения и, в случае если проверка не пройдена,
возвращает в ответ сообщение 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://online.moysklad.ru/xml/ns/appstore/app/v2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://online.moysklad.ru/xml/ns/appstore/app/v2
https://online.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://online.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
по умолчанию,
но требуется его установка для случаев, когда содержимое не умещается в окне браузера по высоте.
Тег uses - опциональный. Предназначен для сервисных протоколов, используемых iframe'ом. На данный момент в нем можно указать следующие протоколы:
- good-folder-selector позволяет iframe'ам переиспользовать существующий в МоемСкладе селектор группы товаров с получением ими результата выбора пользователя. Подробнее про протокол можно прочитать в разделе Селектор группы товаров. Параметры у протокола отсутствуют.
- navigation-service позволяет iframe'ам приложений осуществлять переход на другую страницу МоегоСклада и открывать МойСклад в новой вкладке. Подробнее про протокол можно прочитать в разделе Протокол навигации. Параметры у протокола отсутствуют.
Чтобы расширение iframe-части работало верно, на странице, которая указана в sourceUrl, должен работать скрипт, оповещающий страницу МоегоСклада об изменении высоты его контента, то есть:
- Реализовать посылку сообщения “EventMessage” при любом изменении высоты контента, причем
- сообщение послать parent окну
- данные сообщения должны содержать свойство “height” - высоту содержимого, страницы, которая сейчас отображается (в px)
Для удобства можно добавить следующий js скрипт на свою страницу. Пример:
<!doctype html>
<html>
<head>
...
</head>
<body>
...
<script type="text/javascript" src="https://online.moysklad.ru/js/ns/appstore/app/v1/moysklad-iframe-expand-3.js"></script>
</body>
</html>
Блок 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://online.moysklad.ru/api/remap/1.2
В теге access/scope указывается требуемый уровень доступа.
Для него на текущий момент доступно два значения: admin
и custom
.
Если указан уровень admin
, то приложение будет работать с правами администратора аккаунта.
Если указан уровень custom
, то приложение получит доступ только к отчетам, документам и сущностям,
перечисленным в теге permissions.
В теге access/permissions указываются требуемые пермиссии.
Данный тег обязателен для уровня доступа со значением custom
.
Пример заполнения блока access с указанием прав Администратора:
<access>
<resource>https://online.moysklad.ru/api/remap/1.2</resource>
<scope>admin</scope>
</access>
Пример заполнения блока access с явным перечислением пермиссий:
<access>
<resource>https://online.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 и т.д.
Есть три типа значений для пермиссий сущностей, далее будут указаны тип (уровни доступа) и названия:
1. 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
2. DICTIONARY (view, create, update, delete, print) — good, inventory, company, contract, retailShift
3. BASE (view, create, update, delete, print) — retailStore, processingPlan, myCompany, employee, warehouse, currency, project, country, uom, customEntity
Подробнее о пермиссиях в МоемСкладе см. в документации JSON API.
Примечания:
- Имеются два ограничения на сочетания пермиссий сущностей:
- уровень доступа
<view/>
необходим, если есть другие уровни; - уровень доступа
<update/>
необходим, если требуется уровень<delete/>
.
- уровень доступа
- При установке приложения ему будет автоматически предоставлено право на просмотр справочника Валют (
<currency><view/></currency>
). - В настоящий момент нет отдельной пермиссии для работы с web-хуками. Приложение, которое хочет получить доступ к ним, должно работать с правами администратора.
- В настоящий момент не поддерживается пермиссия для работы с Задачами (
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://online.moysklad.ru/xml/ns/appstore/app/v2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://online.moysklad.ru/xml/ns/appstore/app/v2
https://online.moysklad.ru/xml/ns/appstore/app/v2/application-v2.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>
</ServerApplication>
Дескриптор для серверных приложений с iframe-частью и расширением окна (expand)
<ServerApplication xmlns="https://online.moysklad.ru/xml/ns/appstore/app/v2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://online.moysklad.ru/xml/ns/appstore/app/v2
https://online.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://online.moysklad.ru/api/remap/1.2</resource>
<scope>admin</scope>
</access>
</ServerApplication>
Дескриптор для серверных приложений с виджетом в карточке контрагента
<ServerApplication xmlns="https://online.moysklad.ru/xml/ns/appstore/app/v2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://online.moysklad.ru/xml/ns/appstore/app/v2
https://online.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://online.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://online.moysklad.ru/xml/ns/appstore/app/v2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://online.moysklad.ru/xml/ns/appstore/app/v2
https://online.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://online.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://online.moysklad.ru/xml/ns/appstore/app/v2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://online.moysklad.ru/xml/ns/appstore/app/v2
https://online.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://online.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://online.moysklad.ru/xml/ns/appstore/app/v2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://online.moysklad.ru/xml/ns/appstore/app/v2
https://online.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://online.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://online.moysklad.ru/xml/ns/appstore/app/v2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://online.moysklad.ru/xml/ns/appstore/app/v2
https://online.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://online.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://online.moysklad.ru/xml/ns/appstore/app/v2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://online.moysklad.ru/xml/ns/appstore/app/v2
https://online.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://online.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://online.moysklad.ru/xml/ns/appstore/app/v2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://online.moysklad.ru/xml/ns/appstore/app/v2
https://online.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://online.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://online.moysklad.ru/xml/ns/appstore/app/v1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://online.moysklad.ru/xml/ns/appstore/app/v1
https://online.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://online.moysklad.ru/xml/ns/appstore/app/v1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://online.moysklad.ru/xml/ns/appstore/app/v1
https://online.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-токена (порядка нескольких минут) - назовем его 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:
https://dev.moysklad.ru/doc/api/remap/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-эндпоинты на стороне вендора приложений
Если вендору требуется
- активировать/деактивировать приложение у себя в системе при установке (возобновлении) и удалении (приостановке) приложения на аккаунте пользователя МоегоСклада
- получать токен для доступа к JSON API 1.2
то в этом случае вендору со своей стороны требуется реализовать REST-endpoint с доступом по 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
UUID
устанавливаемого приложения (может быть полезно видеть не только UUID приложения, но и его appUid - например, для разбора непонятных ситуаций и/или логирования) - accountName
String
- имя аккаунта, на который осуществляется подключение приложения (тоже полезно видеть не только UUID аккаунта, но и accountName) - cause
String
- основание (причина) активации, возможные значения:- Install - установка приложения на аккаунт
- Resume (возможно только у платных приложений) - возобновление работы приложения на аккаунте после приостановки
- access
Array
- доступы ресурсам, указанным в дескрипторе приложения. Если ваше приложение не требует доступа к API, то данный объект не придёт.- resource
String
- ресурс, к которому предоставлен доступ. Сейчас из ресурсов для приложений доступно только JSON API 1.2 (https://online.moysklad.ru/api/remap/1.2
) - scope
Array
- какие права предоставлены на доступ данному ресурсу. На данный момент доступ предоставляется в двух вариантах - с правами администратора ("scope": ["admin"]
) и явно указанным набором прав ("scope": ["custom"]
). - permissions - к каким объектам приложение может получить доступ. Включается в тело только для
"scope": ["custom"]
. Соответствует ответу в запросе на получение списка прав Сотрудника - access_token
String
- Bearer токен доступа к данному ресурсу.
- resource
В теле ответа ожидаем получить следующую 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://online.moysklad.ru/api/remap/1.2",
"scope": ["admin"],
"access_token": "6ab89be1ae6ff147755625ee8da948e42612233b"
}
]
}
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://online.moysklad.ru/api/remap/1.2",
"scope": ["admin"],
"access_token": "6ab89be1ae6ff147755625ee8da948e42612233b"
}
]
}
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": "app.test",
"accountName": "account-test",
"access": [
{
"resource": "https://online.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"
}
Деактивация приложения на аккаунте
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
{
"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
{
"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://online.moysklad.ru/api/vendor/1.0
На текущий момент со стороны МоегоСклада есть следующие эндпоинты:
- Получение или изменение статуса приложения на аккаунте
- Получение статуса приложения на аккаунте
- Обратный вызов изменения статуса приложения на аккаунте
- Получение контекста пользователя для iframe приложений
Получение или изменение статуса приложения на аккаунте
С помощью этого эндпоинта вендор может получить или изменить статус приложения пользователя.
Resource: MARKETPLACE-ENDPOINT/apps/{appId}/{accountId}/status
Здесь
- appId
UUID
- идентификатор приложения в МоемСкладе - accountId
UUID
- идентификатор аккаунта в МоемСкладе
Получение статуса приложения на аккаунте
HTTP-метод: GET
Тело запроса: отсутствует
Тело ответа:
В случае успешного ответа возвращается текущий статус приложения на аккаунте с опциональным указанием cause. В случае, если приложение не подключено на данном аккаунте (в том числе, если указанный аккаунт отсутствует в МоемСкладе) возвращается ошибка 404 Not Found (код 2004).
Атрибуты сущности
Название | Тип | Описание | Обязательное при ответе |
---|---|---|---|
status | String | Текущий статус приложения на аккаунте | да |
cause | String | Причина перехода в текущий статус | нет |
Таблица возможных статусов приложения на аккаунте
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://online.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": "Activating",
"cause": "Install"
}
Обратный вызов изменения статуса приложения на аккаунте
С помощью PUT запроса вендор может изменить статус устанавливающегося приложения пользователя. При активации приложения со стороны вендора, вендор может ответить одним из статусов Activated, Activating, SettingsRequired. Если вендор перевел в статусы Activating и SettingsRequired, то МойСклад ожидает, что вендор с помощью обратного вызова оповестит МойСклад о том, что активация на его стороне завершена.
HTTP-метод: PUT
Тело запроса:
- status
String
- текущий актуальный статус приложения. Возможные значения: Activating, SettingsRequired, Activated.
МойСклад при обработке данного запроса проверяет возможность перехода приложения на аккаунте в требуемое состояние в соответствии с жизненным циклом приложения на аккаунте. При отсутствии перехода по жизненному циклу - ошибка. Если приложение на аккаунте уже находится в целевом состоянии - то ошибки нет.
Тело ответа: пустое (за исключением ошибок)
HTTP status codes:
- 200 OK - МойСклад перевел приложение на аккаунте в соответствующее переданному статусу состояние (или приложение уже находилось в требуемом состоянии)
- 404 Not Found - приложение не подключено на данном аккаунте
- 409 Conflict - в случае отсутствия соответствующего перехода по жизненному циклу приложения на аккаунте
Пример запроса на изменение статуса приложения на аккаунте
curl -X PUT "https://online.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://online.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://online.moysklad.ru/api/vendor/1.0/context/6S3BQLsaSRNdEnhPCoW9lplY2LozRUOq6S3BQLsaSRNdEnh"
-H "Accept: application/json"
-H "Authorization: Bearer ..."
Response 200 (application/json). Успешный запрос.
{
"meta": {
"href": "https://online.moysklad.ru/api/remap/1.2/entity/employee/b0a02321-13e3-11e9-912f-f3d4002516e3?expand=cashier.retailStore",
"metadataHref": "https://online.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://online.moysklad.ru/api/remap/1.2/entity/employee/b0a02321-13e3-11e9-912f-f3d4002516e3",
"metadataHref": "https://online.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://online.moysklad.ru/api/remap/1.2/entity/group/b0b3c289-13e3-11e9-9109-f8fc0001f189",
"metadataHref": "https://online.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://online.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 https://dev.moysklad.ru/doc/api/remap/1.2/ - в тело ответа включается JSON объект с описанием ошибки и также проставляются (если нужно) соответствующие HTTP-заголовки в ответе.
Механизм Retry
Механизм Retry повторяет попытки выполнить запрос к системе вендора в соответствии с некоторым конфигурационным профилем, глобальным для магазина приложений, задаваемым в общем случае двумя параметрами:
- максимальным количеством повторений
- функцией delay(i), возвращающей время задержки до очередного повтора
Если израсходованы все количества повторений, то Retry завершается с ошибкой. Что система делает далее - зависит от контекста (какую операцию мы пытались выполнить).
Личный кабинет вендора
Для упрощения и автоматизации работы над приложениями магазина приложений был создан Личный кабинет вендора (ЛКВ). Это сервис, который позволяет вендорам самостоятельно создавать приложения в статусе Draft (Черновик), отлаживать и тестировать их на витрине приложений, вносить необходимые изменения (в том числе самостоятельно получать и изменять SecretKey) и, по окончании разработки, отправлять на модерацию в МойСклад.
Что можно делать в личном кабинете:
- Создавать Черновики приложений;
- Редактировать Черновики приложений и просматривать информацию о приложениях в прочих статусах;
- Редактировать информацию о разработчике;
- Отправлять Черновики приложений на модерацию;
- Следить за статусами публикации своих приложений;
- Предоставлять и продлевать пробный период для платных приложений;
- Привязывать аккаунты в МоемСкладе к аккаунту в Личном кабинете для отладки неопубликованных приложений;
- Просматривать отчеты по установкам и выручке;
- Просматривать, скачивать и согласовывать отчет об использовании приложений;
- Просматривать отчет по причинам удаления приложений.
Получение доступа к личному кабинету Вендора
Для получения доступа в Личный кабинет заполните, пожалуйста, анкету. Мы пришлем вам доступ и дальнейшие инструкции на указанный в анкете email в течение пары дней.
Жизненный цикл приложения
На текущий момент для приложения магазина приложений предусмотрены следующие статусы жизненного цикла:
- Черновик (Draft) - статус черновика, предназначен для разработки и тестирования, приложение не отображается на витрине у пользователей МоегоСклада.
- Готово, отправлено на модерацию (Ready) - статус готовности, означает, что приложение со стороны вендора готово к публикации (далее приложение будет промодерировано сотрудниками МоегоСклада и опубликовано), приложение все еще не отображается на витрине у пользователей МоегоСклада.
- Опубликовано (Published) - приложение опубликовано на витрине приложений и доступно к установке пользователями МоегоСклада.
- Снято с публикации (Hidden) - публикация приложения приостановлена. Приложение в статусе Hidden исчезает с витрины, но при этом у его текущих пользователей сохраняется возможность с ним работать.
Жизненный цикл приложения выглядит следующим образом:
- Приложение создается в Личном кабинете вендора со статусом Draft. Вендор проверяет и отлаживает работу приложения на аккаунте разработчика. По готовности приложения вендор передает его на публикацию, статус приложения меняется на Ready.
- Сотрудники МоегоСклада проверяют приложение и, в случае отсутствия замечаний, публикуют приложение на витрине, статус приложения меняется на Published. Приложение становится доступно для установки пользователями МоегоСклада.
- Пользователи МоегоСклада подключают/устанавливают приложение на свой аккаунт, выполняют его настройку (если таковая опция имеется в приложении), пользуются приложением и, если оно становится ненужным, отключают/удаляют приложение с аккаунта.
- Если по каким-то причинам приложение требуется убрать с витрины приложений, то оно переводится в статус Hidden. Установленные до этого момента экземпляры приложений на аккаунтах продолжают работать и отображаться на UI МоегоСклада до момента удаления приложения самим пользователем. Для прочих аккаунтов, а также для аккаунтов, удаливших приложение, оно скрывается и установка становится невозможна. Подобный вариант скрытия сейчас реализован только для бесплатных приложений.
Создание черновика приложения
Форма создания черновика приложения открывается по кнопке Создать приложение.
В первую очередь предлагается заполнить Псевдоним приложения. Псевдоним - уникальный идентификатор, поэтому невозможно создать два приложения с одинаковыми псевдонимами.
В личном кабинете Вендора можно завести следующие типы приложений:
- Серверное приложение
- Телефонию
- Приложение лояльности
Создание Серверных приложений:
При создании Серверного приложения можно указать платным или бесплатным будет ваше приложение: за это отвечает чекбокс Платное.
Для платных приложений нужно определить их стоимость, кратную 500 рублям. Максимальная стоимость приложения, которую можно задать через ЛКВ, 10000 рублей, минимальная, соответственно, 500 рублей. Если необходимо указать большую стоимость, вендор может обратиться к сотруднику МоегоСклада.
Для платных приложений открывается возможность добавить Пробный период. После установки отметки в чекбоксе Платное станет доступен селектор, в котором можно выбрать длительность Пробного периода (до 14 дней) или его отсутствие.
При создании Серверного приложения дескриптор обязателен для заполнения. Ознакомиться с правилами его заполнения можно в разделе Дескриптор приложения.
Создание Телефонии:
При создании приложения с типом Телефония нельзя проставить признак Платности, выбрать Пробный период и не нужно заполнять Дескриптор. Телефония оплачивается пользователем отдельно - через опцию CRM.
Создание Приложения лояльности:
При создании Приложения лояльности нельзя проставить признак Платности, выбрать Пробный период и не нужно заполнять Дескриптор.
Редактирование черновика приложения
После того, как приложение было создано, может потребоваться что-то в нем изменить. Для этого необходимо выбрать его в списке приложений и нажать кнопку Редактировать приложение.
Что следует знать:
- Редактировать можно только приложения в статусе Черновик.
- После передачи на модерацию можно будет просматривать данные приложения (нажав на кнопку Редактировать приложение), но внести изменения не получится. Возможность редактирования снова появится, если приложение будет возвращено на доработку модератором.
- После публикации приложения изменения могут быть внесены только модератором МоегоСклада по запросу
- В форме редактирования доступны те же поля, что и в форме создания. К ним применимо все перечисленное в разделе "Создание черновика приложения"
- Доступны просмотр и перегенерация Секретного ключа.
- Для просмотра следует нажать на кнопку-глаз у скрытого точками поля, для генерации нового ключа - кнопку Сгенерировать.
- кнопка Сгенерировать, как и прочие изменения приложения, доступна только для Черновиков
- Также в форме редактирования отображаются read-only поля с техническими параметрами и статусом публикации приложения
Отправка на модерацию
Перед публикацией на витрине МоегоСклада новое приложение должно сначала пройти модерацию.
Для отправки на модерацию следует выбрать нужное приложение в статусе Черновик из списка и нажать кнопку Отправить на модерацию.
Важно: для получения оперативных оповещений о ходе модерации необходимо заполнить поле Электронная почта для уведомлений в разделе Реквизиты / Информация о разработчике.
Ручная приостановка приложения
Возможности Личного кабинета позволяют разработчику тестировать и, при необходимости, отлаживать приложения при приостановке и возобновлении на аккаунте.
Для тестирования приостановки необходимо:
- Привязать аккаунт в МоемСкладе к аккаунту Вендора в ЛКВ (создать аккаунт разработчика).
- Завести платное приложение в ЛКВ.
- Установить приложение на аккаунте разработчика.
- Выбрать приложение в списке и нажать кнопку Приостановить на аккаунте.
- Готово! Приложение приостановлено.
- Для отладки возобновления можно воспользоваться кнопкой Активировать в карточке приложения на витрине МоегоСклада.
Приостановка возможна только для приложений, которые уже установлены или устанавливаются.
Продление пробного периода платного приложения
В Личном кабинете доступна возможность единоразово продлить пробный период на выбранном аккаунте для платного опубликованного приложения. Для этого необходимо:
- В Списке приложений выбрать платное опубликованное приложение.
- Нажать на кнопку Предоставить пробный период.
- В окне Предоставление пробного периода заполнить поля Аккаунт и Количество дней.
- При отсутствии ошибок откроется окно Подтверждение пробного периода.
- Нужно проверить предоставляемый пробный период и нажать на кнопку Да, предоставить.
Редактирование информации о разработчике
Для редактирования информации о вендоре (разработчике) приложения потребуется перейти на вкладку Реквизиты / Информация о разработчике в меню Личного кабинета.
Функция доступна, пока на аккаунте отсутствуют приложения или есть только Черновики. В остальных случаях данные доступны только для просмотра, а для изменения следует обратиться к модератору МоегоСклада.
Также есть возможность внести или отредактировать реквизиты для выплат денежных средств за платные приложения. Для этого потребуется перейти на вкладку Реквизиты / Реквизиты компании в меню.
Привязка аккаунта к вендору
Отладка приложений для магазина приложений производится на аккаунтах разработчика. Для того, чтобы аккаунт в МоемСкладе стал аккаунтом разработчика, его нужно привязать к вендору, и это тоже можно сделать в Личном кабинете вендора.
Инициировать привязку можно по кнопке Привязать, расположенной в верхнем меню Личного кабинета.
Важно: Для привязки необходимо иметь доступ к email ответственного сотрудника, назначенного в аккаунте МоегоСклада на странице Подписка. По умолчанию им является администратор, зарегистрировавший аккаунт, однако впоследствии этого пользователя можно изменить.
Изменить уже существующую привязку можно путем нажатия на имя текущей привязки. Предыдущая привязка будет удалена, а установленные на отвязанном аккаунте приложения в статусах Черновик (Draft) и Готово, отправлено на модерацию (Ready) деинсталлированы.
Сводный отчет по установкам приложений
В данном отчете можно посмотреть сколько активных установок есть у приложений, сколько из них пробных и оплачиваемых. Также можно посмотреть сколько приостановленных установок.
Для просмотра отчета по установкам необходимо перейти в раздел Отчеты в верхнем меню и выбрать вкладку Установки.
В отчет включаются:
- Все приложения независимо от платности и наличия установок
- В статусе Опубликовано и Снято с публикации.
Отчет по выручке платных приложений
Данный отчет предоставляет информацию по тому сколько приложение заработало денежных средств. Учитываются только реально оплачиваемые установки. Установки на Пробном тарифе и с Пробным периодом приложений не учитываются.
Для просмотра отчета по выручке необходимо в разделе Отчеты верхнего меню выбрать вкладку Выручка.
В отчет включаются:
- Приложения во всех статусах
- Платные приложения, у которых есть хотя бы 1 успешная установка
- Бесплатные приложения, если ранее они были платными с успешными установками.
Отчет об использовании приложений
Отчет об использовании приложений является основанием для выплаты вендору денежных средств за использование приложения пользователями МоегоСклада.
Для просмотра и согласования отчета необходимо в разделе Отчеты верхнего меню выбрать вкладку Выплаты.
Отчет формируется раз в месяц, доступен для скачивания в виде PDF-файла. Содержит информацию сколько денег заработало каждое приложение в отдельности и все приложения вендора вместе за месяц.
Что важно знать:
- Для формирования отчета должны быть заполнены реквизиты.
- Вендору поступает отчет в статусе Сформировано (на почту приходит оповещение).
- Вендор ознакамливается с отчетом и согласовывает его – кнопка Согласовать.
- Статус отчета меняется на Согласовано.
- Выплачивается вознаграждение согласно банковским реквизитам вендора и проставляется отметка Выплачено.
Если вендор имеет возражения по отчету, то он его не согласовывает и обращается к сотруднику МоегоСклада.
Отчет по причинам удаления приложений
При удалении приложения на витрине МоегоСклада пользователю предлагается оставить отзыв о приложении:
В случае если пользователь выберет один или несколько пунктов и нажмет на кнопку Отправить отзыв на Email для уведомлений вендора, указанный на вкладке Вендор, придет письмо с указанием причины удаления.
Список всех отзывов, оставленных пользователями, отображается в отчете Причины удаления приложений. Для его просмотра необходимо в разделе Отчеты верхнего меню выбрать вкладку Причины удаления.
Настроить отправку писем можно с помощью флага "Отправлять уведомления о причинах удаления приложений на email". После отключения уведомления на почту, отзывы продолжат собираться в данный отчет.
На текущий момент есть ограничение: в отчете выводятся только 1000 последних отзывов за выбранный период.
Отладка приложений на аккаунтах разработчика
Отладка неопубликованных приложений выполняется вендором на специальном аккаунте(ах), привязанных к вендору.
На таких аккаунтах неопубликованные приложения вендора появляются в специальном блоке на витрине приложений "ПРИЛОЖЕНИЯ В РАЗРАБОТКЕ". Для остальных пользователей приложения-черновики не будут видны.
Неопубликованные приложения можно подключать и отключать так же, как и обычные опубликованные приложения.
Привязка аккаунтов к вендору осуществляется через Личный кабинет вендора.
Общая схема процесса размещения приложения в магазине приложений
После готовности приложения на стороне вендора в общем случае вендору нужно выполнить следующие шаги по размещению приложения в магазине приложений:
- Создать Черновик приложения в Личном кабинете вендора.
- После создания Черновика на странице редактирования приложения будет доступен Секретный ключ (Secret Key).
- Создать аккаунт разработчика, привязав аккаунт МоегоСклада к аккаунту в Личном кабинете вендора.
- Протестировать и отладить приложение на аккаунте разработчика.
- Отправить приложение на модерацию через Личный кабинет вендора.
Далее сотрудник МоегоСклада проверяет приложение и, при отсутствии замечаний, публикует его на витрине приложений в течение 2 рабочих дней.
Права доступа для приложений
Серверные приложения, которые хотят получить доступ к JSON API, в дескрипторе должны указать желаемый уровень доступа к нему.
На текущий момент приложение может запросить следующие уровни доступа:
admin - соответствует пользовательской роли
Системный администратор
: приложение получит полный доступ ко всем отчетам, сущностям и документам в МоемСкладе. Этот уровень в перспективе будет устранен.custom - соответствует пользовательской роли
Индивидуальная роль
: приложение получит доступ только к указанным в дескрипторе отчетам, сущностям и документам. Этот уровень рекомендуется для использования приложениями, за исключением тех, которые работают с web-хуками и Задачами.
На текущий момент права приложения на аккаунте фиксируются на момент установки приложения и при последующем обновлении дескриптора приложения права приложения на данном аккаунте останутся прежними.
Следует учитывать это при разработке приложений - то есть что одновременно могут существовать и работать установки приложений с разными правами и, надо в том или ином поддерживать работу приложения с несколькими наборами прав. Предоставление приложению новых дополнительных прав должно происходить через явное подтверждение пользователем и это будет реализовано в будущем. Пока что пользователь может обновить права через переустановку: нужно следить, чтобы при удалении приложения данные аккаунта сохранялись какое-то время.
Подробнее о ролях пользователей см. в документации JSON API.
Более подробную информацию о правах доступа для приложений можно найти в разделе Информация о правах доступа.
Прямая ссылка на приложение
Вендоры могут получить прямую ссылку приложения. Для этого нужно в Личном кабинете вендора перейти в режим просмотра или редактирования нужного приложения и найти поле Прямая ссылка на приложение. Прямую ссылку можно также получить с витрины приложений из адресной строки браузера, если открыть карточку приложения на витрине.
При переходе по короткой ссылке открывается витрина с карточкой приложения. Если пользователь авторизован - то витрина с карточкой открывается сразу, если пользователь не авторизован - то через форму логина/авторизацию.
Если приложение не существует или не доступно на аккаунте пользователя, использующего прямую ссылку, по какой-либо из причин (то есть, если приложение не отображается на витрине, например, приложение в статусе Draft), то, при переходе этим пользователем по ссылке, открывается витрина и появляется сообщение о том, что приложение не найдено. Если приложение снято с публикации, но уже установлено некоторыми пользователями, то у них оно будет отображаться по ссылке, так же, как и на витрине, а у остальных, соответственно, нет.
Так как витрина приложений доступна только администраторам аккаунтов, то карточка приложения будет доступна только им.
Список последних изменений
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 приложения и popup'ах
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 приложения и popup'ах
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
Изменено
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 дескриптора приложений: Виджеты в карточке контрагента.