Задать вопрос

Приложения

Примечание

Приложения работают в тестовом режиме

Для сторонних сервисов, которым недостаточно API, мы предоставляем возможность создавать приложения (аналогичные приложениям в социальных сетях).

Приложения могут подписываться на потоки событий и добавлять в наш интерфейс собственные виджеты. Потоки дают возможность не делать регулярные запросы на проверку появления новых сущностей и изменения старых, а получать их на указаный url. А виджеты позволяют отображать стороннюю информацию, прямо в интерфейсе Мегаплана, и инициировать действия для сторонних сервисов.

Добавление и управление приложениями происходит на странице /settings/application.

Аутентификация в API

Каждому приложению выделяется токен. Узнать его можно на странице редактирования приложения. UUID приложения и токен можно использовать для аутентификации в API. В этом случае UUID используется в роли AccessId, а токен в роли SecretKey.

Потоки событий

Для приложения можно указать список потоков и url, куда они должны отправляться. События приходят на указанный url, в виде HTTPS POST-запросов, с данными в теле запроса. Самоподписанные сертификаты не поддерживаются.

В данный момент, в наличие следующие потоки:

  • клиенты (contractors)
  • сделки (deals)
  • события (events)
  • задачи (tasks)
  • проекты (projects)

Примерная структура запроса для события обновления сделки:

{
    "data": {
        "id": "43",
        "name": "№1",
        "price": {
            "value": 188,
            "currency": "RUB",
            "contentType": "Money"
        },
        "manager": {
            "id": "1000001",
            "age": null,
            "uid": 1000075,
            "name": "User Name",
            "avatar": null,
            "birthday": null,
            "lastName": "Name",
            "position": "Директор",
            "firstName": "User",
            "middleName": "",
            "contactInfo": [
                {
                    "type": "email",
                    "value": "dev-null@megoplan.ru",
                    "comment": "",
                    "contentType": "ContactInfo"
                },
                {
                    "type": "mobile",
                    "number": "0000000",
                    "comment": "",
                    "areaCode": "910",
                    "contentType": "Phone",
                    "countryCode": 7
                }
            ],
            "contentType": "Employee"
        },
        "invoices": [],
        "positions": [
            {
                "id": "85",
                "name": "То",
                "count": 1,
                "contentType": "DealPosition"
            },
            {
                "id": "94",
                "name": "Это",
                "count": 2,
                "contentType": "DealPosition"
            }
        ],
        "contractor": {
            "id": "1000251",
            "type": {
                "id": "1",
                "name": "Клиент",
                "type": "client",
                "contentType": "ContractorType"
            },
            "avatar": null,
            "gender": "male",
            "status": null,
            "company": null,
            "birthday": null,
            "lastName": "Какой-то",
            "position": "",
            "firstName": "клиент",
            "canSeeFull": true,
            "isFavorite": false,
            "middleName": "",
            "contactInfo": [],
            "contentType": "ContractorHuman",
            "description": "",
            "responsibles": [
                {
                    "id": "1000001",
                    "age": null,
                    "uid": 1000075,
                    "name": "User Name",
                    "avatar": null,
                    "birthday": null,
                    "lastName": "Name",
                    "position": "Директор",
                    "firstName": "User",
                    "middleName": "",
                    "contactInfo": [
                        {
                            "type": "email",
                            "value": "dev-null@megoplan.ru",
                            "comment": "",
                            "contentType": "ContactInfo"
                        },
                        {
                            "type": "mobile",
                            "number": "0000000",
                            "comment": "",
                            "areaCode": "910",
                            "contentType": "Phone",
                            "countryCode": 7
                        }
                    ],
                    "contentType": "Employee"
                }
            ],
            "possibleActions": [
                "act_edit",
                "message",
                "task",
                "add_item",
                "add_deal"
            ],
            "preferTransport": "",
            "unreadNewsCount": 0,
            "availableActions": [],
            "dateLastReadNews": null
        },
        "isFavorite": false,
        "contentType": "Deal",
        "description": "Нам бы того и этого",
        "timeCreated": {
            "value": "2016-10-24T11:01:03+00:00",
            "contentType": "DateTime"
        },
        "currentState": {
            "id": "94",
            "name": "Сделка",
            "result": "active",
            "contentType": "DealState"
        },
        "positionsCount": 2,
        "shortDescription": "Сделка №1 с Какой-то клиент"
    },
    "uuid": "44b95294-1fcf-4f9f-bd57-a05ab3a68615",
    "event": "on_after_update",
    "model": "Deal",
    "accountId": "mydomain.megaplan.ru",
    "fullEventName": "BumsTradeM_Deal.on_after_update",
    "integrationUuid": "babf410e-53da-4b52-9be8-efa685bef725"
}

Примечание

Приложение должно быть готово к тому, что ему придет больше 1 запроса, с одним и тем же событием. Для отсеивания дубликатов, в событии присутствует параметр uuid, уникальный для события.

Событие считается успешно доставленным, если запрос уложился в 3 секунды и http-статус ответа - 200 OK. Если попытка завершилась неудачей, то следующие попытки будут после паузы: pauseInSeconds = min(2 ^ attemptsCount, 4 * 60 * 60).

Событие установки приложения

Если приложение устанавливается из каталога, после установки оно получает событие Integration.on_install:

{
    "data": {
        "name": "Testing",
        "uuid": "babf410e-53da-4b52-9be8-efa685bef725",
        "token": "YmVmODExOGZlOTZhMzg4YTQ1ZjJmODljMmJkOGU1YmQxYTBkNjM0ODE4NTRlODVhNTAzNzdiN2JjZWI0ZjA5NQ",
        "pageUri": "",
        "version": 1,
        "widgets": [],
        "isEnabled": true,
        "streamUri": "https:\/\/requestb.in\/sznzpwsz",
        "identifier": "testing_foo",
        "contentType": "Integration",
        "preferencesUri": "",
        "subscribedStreams": []
    },
    "uuid": "bf36262d-d057-4dba-8049-c345e9d321c6",
    "event": "on_install",
    "model": "Integration",
    "accountId": "megaplan.localhost",
    "fullEventName": "bums\\integration\\Model\\Integration.on_install",
    "integrationUuid": "babf410e-53da-4b52-9be8-efa685bef725"
}

Виджеты

Виджеты дают приложению возможность добавить HTML и JS-код в определенное место интерфейса Мегаплана.

Внешний вид окна редактора виджетов:

../_images/widget_editor.png

Загрузка внешнего CSS-файла

a9n.css("https://cdn.jsdelivr.net/jquery.suggestions/16.5.2/css/suggestions.css").then(function() {
    console.log('CSS loaded');
});

Загрузка внешнего JavaScript-файла

a9n.js("https://cdn.jsdelivr.net/jquery.suggestions/16.5.2/js/jquery.suggestions.min.js").then(function() {
    console.log('JS loaded');
});

Получение информации о текущем пользователе

a9n.user().then(function(current_user) {
    console.log('Current user:', current_user);
});

Подписаться на события виджет добавлен на страницу/убран со страницы

Если виджет отображается в новом (single-page) интерфейсе то нужно учитывать, что блок с кодом вашего виджета - динамический. Это значит что он может быть добавлен уже после того как вся страница полностью загрузилась. Аналогично блок с кодом вашего виджета может быть убран со страницы, хотя сама страница при это не будет перезагружена. Чтобы ваш javascript код был в курсе того что происходт с блоком виджета нужно подписаться на события “componentDidMount” и “componentWillUnmount”.

a9n.subscribe(this,'componentDidMount', function(){
    // блок с html виджета отобразился
    // здесь можно начинать что-то с ним делать
})

a9n.subscribe(this,'componentWillUnmount', function(){
    // мы собираемся убрать блок с html виджета
    // здесь нужно освободить ресурсы и удалить html виджета вне блока (если есть)
})

Получение sdk для работы с телефонией

mpCallJsSDK представляет собой объект с набором функций и событий для работы с подсистемой телефонии в Мегаплане.

Ниже приведен базовый пример по работе с CallSDK

Документация по Http запросам для телефонии находится здесь

a9n.callSDK().then(function(mpCallJsSdk){
    console.log('CallSDK ready to work');
    // получим внутренний номер, или идентификатор, который указан для сотрудника в настройках телефонии
    var userPhone = mpCallJsSdk.getUserInternalNumberConfig();
    // конфиг добавляемый через http api для каждого пользователя
    var userConfig = mpCallJsSdk.getUserConfig();
    // конфиг серверный, создается через http API и для всех пользователей одинаковый
    var serverConfig = mpCallJsSdk.getServerConfig();

    mpCallJsSdk.on('connect', function () {
        // Событие необходимости подключения к серверу телефонии (одно на все вкладки мегаплана)
        // При закрытии вкладки это событие произойдет в другой одной вкладке
        // Тут реализовываем подключение(websocket), если это необходимо

        // когда подключение установлено, нужно сказать об этом Мегаплану:
        mpCallJsSdk.connected();
    });

    mpCallJsSdk.on('disconnect', function () {
        // Событие отключения от телефонии, например при отключении пользователя в настройках

        // После отключения или при разрыве соедеинения, нужно сказать об этом Мегаплану:
        mpCallJsSdk.disconnected();
    });

    // Обработка события нажатия на кнопку звонка в интерфейсе Мегапалана
    mpCallJsSdk.on('call', function(callInfo){
        //Совершаем звонок и далее вызываем функции обработки звонка, описаны ниже
    });

    // Обработка события смены статуса "Не беспокоить" в виджете телефонии
    mpCallJsSdk.on('changeDndMode', function(isModeOn){
        // если значение isModeOn === true, режим "Не беспокоить" включен
    });

    // При событиях звонка используется структура
    var callInfo = {
        fromPhone: '100', // с какого номера звонок
        toPhone: '101', // на какой номер звонок
        type: 'in', // in | out
        providerId: '', // Id системы телефонии для звонка, если есть
        recordLinks: [''] // Массив ссылок на запись разговора
    };

    // Входящий или исходящий звонок, определяется по callInfo.type
    mpCallJsSdk.calling(callInfo);
    //Разговор
    mpCallJsSdk.talking(callInfo);
    //Завершенный звонок
    mpCallJsSdk.finished(callInfo);

    //После подписки на все события, проставляем возможности текущего приложения в CallSDK, после чего происходит инициализация событий
    mpCallJsSdk.setCapabilities({
      'HOLD': false, // Hold call
      'WEBTALK': false, //Разговор из браузера
      'ANSWER': false, // Ответ на звонок
      'END_TALK': false, // Завершение вызова
      'TRANSFER': false, // Перевод звонка
      'AWAY_MODE': false, // Режим недоступен
      'CONFERENCE': false, // Конференция ( пока не реализовано )
      'MUTE': false, // При наличии WEBTALK отключение микрофона
      'CLEAR_STATE': true, // Сброс состояния (когда нет возможности завершить вызов, но нужно предоставить возможность скрыть виджет телефонии и сбросить его состояние
      'VIDEO_CALL': false // Видеозвонок ( пока не реализовано )
    });
});

Получение информации о виджете

Метод возвражает объект с параметром instance, который является DOM-элементом, в котором находится текст виджета.

console.log(a9n.widget());

Пример виджета

В качестве примера виджета, можно рассмотреть отображение подсказок из сервиса DaData , использующее их jQuery-плагин.

<style>
    #js-contractor-company-name {
        width: 400px;
    }

    .dadata_container {
        width: 390px;
        font-size: 12px;
        background: #f0f0f0;
        padding: 5px;
    }

    .dadata_container dt,
    .dadata_container dd {
        padding-bottom: 5px;
    }

    .dadata_container dt {
        font-weight: bold;
    }
</style>
<script type="text/javascript">
    a9n.css("https://cdn.jsdelivr.net/jquery.suggestions/16.5.2/css/suggestions.css")
    a9n.js("https://cdn.jsdelivr.net/jquery.suggestions/16.5.2/js/jquery.suggestions.min.js").then(function () {
        var $container = $('<dl class="dadata_container"></dl>').insertAfter("#js-contractor-company-name")
        $("#js-contractor-company-name").suggestions({
            serviceUrl: "https://suggestions.dadata.ru/suggestions/api/4_1/rs",
            token: "<Ваш токен DaData>",
            type: "PARTY",
            count: 5,
            onSelect: function (suggestion) {
                var data = suggestion.data;
                $container.html('').append($('<dt><strong>' + data.opf.short + ' ' + data.name.full + '</strong></dt>'), $('<dt>Адрес</dt>'), $('<dd>' + data.address.value + '</dd>'), $('<dt>' + data.management.post + '</dt>'), $('<dd>' + data.management.name + '</dd>'), $('<dt>КПП</dt>'), $('<dd>' + data.kpp + '</dd>'), $('<dt>ИНН</dt>'), $('<dd>' + data.inn + '</dd>'), $('<dt>ОГРН</dt>'), $('<dd>' + data.ogrn + '</dd>'), $('<dt>Статус</dt>'), $('<dd>' + data.state.status + '</dd>'));
            }
        });
    });
</script>

Страница приложения

Если приложению необходима собственная страница в Мегаплане, доступная каждому пользователю, то при создании приложения, можно указать внешний URL. Тогда по адресу /application/page/<uuid приложения>, пользователю будет открываться интерфейс Мегаплана, со встроенным iframe.

Как это работает:

  1. Мы указываем нашему приложению URL собственной страницы.
  2. Пользователь переходит на страницу приложения (например, по ссылке, добавленной в меню) /application/page/<uuid приложения>.
  3. Пользователю показывается страница Мегаплана. В теле страницы находится iframe с адресом <URL страницы приложеняи>&accountId=<хост аккаунта>&applicationUuid=<uuid приложения>&sessionId=<id сессии>&userSign=<подпись пользователя>.
  4. Чтобы ваше приложение могло идентифицировать пользователя Мегаплана, оно должно сделать api-запрос по адресу /BumsSettingsApiV01/Application/checkUserSign.json?uuid=<uuid-приложения>&userSign=<подпись>. В ответ вы получите либо 403, если подпись не верна или старше 60 секунд, либо данные о пользователе.
  5. Чтобы не делать этот запрос каждый раз, в iframe передается параметр sessionId, который уникален для текущей сессии пользователя.

Пример ответа:

{
    "status": {
        "code": "ok",
        "message": null
    },
    "data": {
        "contentType": "Employee",
        "id": "1000001",
        "name": "User Name",
        "firstName": "User",
        "lastName": "Name",
        "position": "Директор",
        "uid": 1000075,
        "birthday": {
            "contentType": "DateTime",
            "value": "2016-09-06T21:00:00+00:00"
        },
        "age": null,
        "contactInfo": [
            {
                "contentType": "ContactInfo",
                "type": "email",
                "value": "dev-null@megoplan.ru"
            },
            {
                "contentType": "Phone",
                "areaCode": "910",
                "number": "0000000",
                "countryCode": 7,
                "type": "mobile",
                "comment": ""
            }
        ]
        ...
    }
}

Примеры стилей

Чтобы ваши виджеты и страницы были похожи на “родной” интерфейс Мегаплана, вы можете использовать нашу подборку стилей.