2. Руководство разработчика › 2.3 Скрипты › Веб-модули

Веб-модули — это особый механизм интеграции серверной платформы Melbis с её Windows-клиентом Melbis Shop. Суть механизма: внутри программы встроен полноценный браузер на базе Chromium, который умеет открывать страницы сайта прямо внутри окон программы и общаться с ними в обе стороны — передавать контекст текущей работы менеджера на сервер и получать обратно результат. Разработчик создаёт обычный Melbis-модуль, а программа открывает его внутри своего окна.

Это позволяет расширять функциональность программы без изменения её кода: аналитика, отчёты, инструменты управления — всё это можно реализовать на PHP и подключить через настройки.


Встроенные веб-модули

Встроенные модули работают прямо внутри рабочих окон программы — окон товаров, заказов, покупателей, планировщика и переводов. Чтобы открыть встроенный веб-модуль в любом из этих окон, нажмите Ctrl+W — появится панель со встроенным браузером.

Ключевая особенность: когда менеджер перемещается по записям (выбирает товар, открывает заказ, переходит к покупателю), программа автоматически отправляет POST-запрос в модуль с контекстом текущего окна. Модуль всегда знает, с каким объектом сейчас работает менеджер, и может показать релевантную информацию — без каких-либо дополнительных действий с его стороны.

Этих модулей может быть несколько для каждого раздела — они переключаются вкладками. Для каждого модуля можно задать права доступа: какие группы пользователей его видят.

Примеры встроенных модулей для раздела «Товары / Цены»:

Примеры для раздела «Бизнес / Заказы» и «Бизнес / Покупатели»:

Настройка

Встроенные модули настраиваются в программе: «Проектирование → Модули и опции», закладка «Встроенные модули». Выберите место — товары, заказы, покупатели, планировщик или переводчик — и добавьте модуль со следующими параметрами:

Параметры, передаваемые программой

При каждом обращении к встроенному модулю программа отправляет POST-запрос с контекстом текущего окна. Поле уходит, только когда в окне выбрана его запись; если выделено несколько строк, их номера идут через ; в поле с окончанием _set.

Место Окно Поля
товары «Расположение», «Цены», «Обозреватель», «Выбор товаров», «Описания» melbis — пустое поле, с него начинается запрос окна; topic_id — раздел; store_id — товар; store_set — выделенные товары
товары «Описания», панель значений характеристик те же поля и ещё info_id — характеристика, info_value_id — значение, info_value_set — выделенные значения
заказы «Заказы» order_id — заказ; order_version_id — версия заказа; order_version_client_id — покупатель версии; order_version_user_id — менеджер версии; order_client_field_id — выбранная строка полей покупателя заказа; order_store_id — выбранная строка товаров заказа; order_option_id — выбранная строка опций заказа; order_set — выделенные заказы
заказы «Редактирование заказа» order_id, parent_version_id — родительская версия, order_version_id, order_version_client_id, order_version_user_id, order_client_field_id, client_field_id — поле покупателя, order_store_id, store_id — товар выбранной строки, store_option_id — опция товара, order_option_id — опция заказа, и order — заказ в правке целиком
покупатели «Покупатели» client_id — покупатель; client_field_id — выбранная строка полей покупателя; client_set — выделенные покупатели
планировщик «Планировщик» melbis; task_id — задача; note_id — запись задачи
переводчик «Переводы» melbis; trans_id — раздел переводов; origin_id — исходная строка; origin_set — выделенные строки; lang_id — выбранная строка перевода, номер строки trans_lang, а не языка

В «Описаниях» номера товаров и значений — номера основного каталога, а не строк рабочего раздела. Поле order окна «Редактирование заказа» — JSON с несохранёнными строками заказа: ключи new_orders_version, new_orders_client_field, new_orders_store, new_orders_store_option и new_orders_option, у каждого — список строк со всеми полями.

С флажком «Требуется идентификация пользователя» к любому набору добавляются login, stamp и sign: stamp — время по GMT, sign — подпись строки логин|время|WEB ключом, вычисленным из пароля. Сам пароль из программы не уходит. Так выглядит $mVars модуля в окне «Заказы»:

// $mVars в модуле:
[
    'get'  => ['mod' => 'melbis_web_sample'],
    'post' => [
        'order_id'                => '1',
        'order_version_id'        => '2',
        'order_version_client_id' => '1',
        'order_version_user_id'   => '1',
        'order_client_field_id'   => '3',
        'order_store_id'          => '2',
        'order_option_id'         => '3',
        'login'                   => 'admin',
        'stamp'                   => '2026-10-27 09:15:00',
        'sign'                    => 'RjaLSFiSrdVFBXJ/r+Ud+TtICKvNeF5s3s/hlQwXatv37woccBwsDvduIBM6fh0Pp2lJxA4PvA0zzwwCVYvODw==',
    ]
]

AI-помощник открывает модуль так же — инструментом shop_module: поля окна он передаёт сам, а login, stamp и sign пользователя сессии добавляет MCP-сервер.

Язык пользователя

Браузер программы работает на языке, выбранном в программе, и ставит его первым в заголовок Accept-Language каждого запроса. Модуль узнаёт его тем же методом, что и язык посетителя витрины (см. «Посетитель», раздел «Язык браузера»):

$lang = MELBIS()->AgentLanguage(['ru', 'uk', 'en'], 'en');

Внешний модуль, открытый в обычном браузере, получает так язык этого браузера, поэтому один и тот же код работает в обоих случаях. Когда модуль открывает AI-помощник, заголовка нет, и метод возвращает значение по умолчанию.


Библиотека melbis_inc_logic_auth

Для веб-модулей в стандартную поставку демо-магазина включена библиотека melbis_inc_logic_auth. Она берёт на себя весь рутинный код: авторизацию пользователя по подписи (login, stamp, sign), хранение состояния авторизации в PHP-сессии, проверку прав доступа к модулю и маршрутизацию POST-вызовов между функциями модуля.

Центральная функция — MELBIS_INC_LOGIC_AUTH\Router($mModule, $mVars) — выполняет следующее по порядку:

  1. Проверяет, не запрошен ли выход (logout в POST).
  2. Авторизует пользователя: по login, stamp и sign из POST — через SysUserSignCheck, — или по ранее сохранённой сессии.
  3. Проверяет право пользователя на доступ к данному модулю (по таблице прав в БД).
  4. Записывает через GlobalAssign('PAGE', [...]) глобальные переменные, доступные во всех шаблонах: статус авторизации (auth), user_id и mod — URL текущего модуля. Переменная {PAGE:MOD} используется в шаблонах как базовый URL для AJAX-запросов.
  5. Маршрутизирует вызов: если в POST передан параметр func — вызывает функцию модуля с этим именем (GetCataloge), передавая ей $userId и $mVars. Без func вызывается Page. Любая функция, кроме Page, работает только после входа и с правом на модуль — иначе ответ Access denied.

Благодаря этому вся главная функция модуля сводится к одной строке:

namespace MELBIS_WEB_SAMPLE;

use MELBIS_INC_LOGIC_AUTH as LOGIC_AUTH;

function Main($mVars)
{
    return LOGIC_AUTH\Router(MELBIS()->UnitName(), $mVars);
}

MELBIS()->UnitName() возвращает имя текущего модуля — оно передаётся в маршрутизатор для проверки прав и поиска функций модуля.

Имя функции приходит данными, из POST, поэтому маршрутизатор зовёт её через UnitFunc: тот собирает полное имя от выполняющегося модуля — MELBIS_WEB_SAMPLE\GetCataloge, а в модуле прежней формы, без пространства имён, MELBIS_WEB_SAMPLE_GetCataloge. Один и тот же маршрутизатор обслуживает обе формы.


Структура модуля: функция Page

Функция Page выводит страницу модуля: маршрутизатор зовёт её, когда func не передан. Это единственная функция, которую он зовёт и без входа, — она же показывает форму авторизации. Она получает $mUserId (или null, если вход не состоялся) и полный массив $mVars.

function Page($mUserId, $mVars)
{
    $tpl = MELBIS()->TplCreate();

    if ( $mUserId > 0 )
    {
        // Передать входные переменные в шаблон
        MELBIS()->TplAssign($tpl, 'VARS', var_export($mVars, true));

        // Передать данные заказа для механизма обратной передачи
        MELBIS()->TplAssign($tpl, 'ORDER', $mVars['post']['order'] ?? '{}');

        MELBIS()->TplParse($tpl, 'CONTENT', 'page');
    }
    else
    {
        // Пользователь не авторизован — показать форму входа
        MELBIS()->TplAssign($tpl, 'ORDER', '{}');
        MELBIS()->TplParse($tpl, 'CONTENT', 'auth');
    }

    MELBIS()->GlobalAppend('PAGE:TITLE', 'Sample Web module');

    return MELBIS()->TplFinal($tpl, 'main');
}

Шаблон auth.htm содержит вызов модуля melbis_web_auth — он выводит форму логина. После отправки формы маршрутизатор LOGIC_AUTH\Router снова обрабатывает POST, авторизует пользователя и на этот раз Page разбирает основной шаблон page.htm.


AJAX-запросы из шаблона

Для интерактивных действий внутри модуля JavaScript передаёт параметр func в POST. Маршрутизатор найдёт и вызовет соответствующую функцию — при условии что пользователь авторизован и имеет доступ к модулю.

В шаблоне переменная {PAGE:MOD} содержит готовый URL текущего модуля (/?mod=melbis_web_sample) — его удобно использовать как точку для всех AJAX-запросов:

$('#melbis_table_cataloge').bootstrapTable({
    url: '{PAGE:MOD}',
    method: 'post',
    queryParams: function(params) {
        params.func = 'GetCataloge';  // маршрутизатор вызовет GetCataloge этого модуля
        return params;
    },
    pagination: true,
    sidePagination: 'server',
    pageSize: 20,
    sortName: 'absindex',
    sortOrder: 'asc'
});

На стороне PHP функция получает управление только если пользователь авторизован, выполняет запрос и возвращает JSON:

function GetCataloge($mUserId, $mVars)
{
    $limit  = (int) $mVars['post']['limit'];
    $offset = (int) $mVars['post']['offset'];
    $sort   = preg_replace('/[^a-z_]/', '', $mVars['post']['sort']);
    $sort  .= ($mVars['post']['order'] == 'asc') ? ' ASC' : ' DESC';

    $command = "SELECT t.id, t.name, t.tlevel, COUNT(ts.id) AS amount
                  FROM {DBNICK}_topic t
             LEFT JOIN {DBNICK}_topic_store ts ON t.id = ts.topic_id
              GROUP BY t.id
              ORDER BY $sort";

    $data = MELBIS()->SqlSelectLimit(__LINE__, $command, $offset, $limit);

    return json_encode($data);
}

Такой же принцип применяется для любых других функций модуля — поиска, фильтрации, сохранения данных в БД, отправки уведомлений и т.п.


Обратная передача данных в программу

Наиболее уникальная возможность встроенных веб-модулей — передача данных из браузера обратно в Windows-программу без дополнительных HTTP-запросов.

Страница отправляет программе сообщение методом window.chrome.webview.postMessage: объект с именем события в поле event и данными в поле data.

window.chrome.webview.postMessage({ event: 'MELBIS_ORDER_UPDATE', data: update });

Программа понимает два события:

Событие Где работает Что делает программа
MELBIS_ORDER_UPDATE окно редактирования заказа применяет data к полям редактируемого заказа
MELBIS_PRINT любой браузер программы открывает для страницы диалог печати Windows

Объект window.chrome.webview есть только в браузере программы. Модуль, который открывают и в обычном браузере, проверяет его перед отправкой (пример — в разделе «Печать» ниже).

MELBIS_ORDER_UPDATE работает, пока заказ ещё не сохранён. В остальных разделах (товары, покупатели) веб-модуль при необходимости пишет изменения напрямую в базу данных через обычные SQL-методы парсера.

Практический пример: модуль получает от программы все данные открытого заказа в JSON-формате, отображает их в интерактивных таблицах, и позволяет менеджеру редактировать отдельные поля прямо в браузере.

// PHP: получить данные заказа, переданные программой, и отдать в шаблон
MELBIS()->TplAssign($tpl, 'ORDER', $mVars['post']['order'] ?? '{}');
// Шаблон: распарсить JSON и построить таблицы
var melbis_order = {ORDER};

function melbis_init_back() {
    for (var table in melbis_order) {
        var data    = melbis_order[table];
        var columns = [];
        for (var c in data[0]) {
            columns.push({field: c, title: c});
        }
        $('.melbis_table_order[data-table="' + table + '"]').bootstrapTable({
            columns: columns,
            data:    data
        });
    }
}

// Клик по ячейке — диалог редактирования
$('.melbis_table_order').on('click-cell.bs.table', function(event, field, value, row) {
    var table = event.target.dataset.table;
    bootbox.prompt({
        title: 'Edit value of "' + field + '"',
        value: value,
        callback: function(result) {
            if (result != null) {
                var update = {};
                update[table] = [{id: row.id, [field]: result}];
                // Передать изменение в программу
                window.chrome.webview.postMessage({ event: 'MELBIS_ORDER_UPDATE', data: update });
            }
        }
    });
});

Программа получает событие MELBIS_ORDER_UPDATE и применяет изменения к полям заказа в своём интерфейсе.

Печать

Событие MELBIS_PRINT открывает для страницы классический диалог печати Windows: выбор принтера, числа копий и страниц. Стандартный window.print() показывает вместо него предпросмотр браузера внутри самой панели модуля.

Кнопка печати, которая работает и в программе, и в обычном браузере:

function melbis_print() {
    if (window.chrome && window.chrome.webview) {
        window.chrome.webview.postMessage({ event: 'MELBIS_PRINT' });
    } else {
        window.print();
    }
}

Внешние веб-модули

Внешние модули отличаются от встроенных принципиально одним: они не привязаны к конкретному окну программы и не получают контекст текущей записи. Вместо этого при открытии из программы передаются только параметры авторизации пользователя (login, stamp, sign).

Доступ к внешним модулям открывается через раздел «Бизнес / Веб-модули» — здесь расположен браузер с каталогом всех доступных модулей в левой панели. Кроме того, любой из таких модулей можно открыть в обычном браузере на любом устройстве — планшете, смартфоне, рабочем месте без программы.

Это делает внешние модули универсальным инструментом для дашбордов, аналитических отчётов и административных панелей — всего, что не требует привязки к конкретному товару или заказу. На скриншоте выше — пример реального внешнего модуля с аналитикой продаж, финансовым учётом, складским анализом и инструментами управления, встроенными прямо в программу.

Настройка внешних модулей — «Проектирование → Модули и опции», закладка «Внешние веб-модули»: задаётся название, URL и символьный ключ каждого модуля, а также права доступа по группам пользователей.


Авторизация во внешних модулях

Поскольку внешние модули открываются в том числе из обычного браузера, авторизация здесь работает в двух режимах автоматически:

При открытии из программы — Melbis Shop передаёт login, stamp и sign в POST. LOGIC_AUTH\Router авторизует пользователя без участия формы и сразу показывает содержимое.

При открытии в браузере напрямую — POST не содержит учётных данных, маршрутизатор передаёт $mUserId = null, и функция Page подставляет шаблон формы авторизации. После успешного входа данные сохраняются в PHP-сессии, и при последующих запросах форма больше не показывается.

Форма входа подписывает сама: функция melbis_crypto_sign из statics/melbis/main.js вычисляет в браузере ключ из пароля и отправляет те же login, stamp и sign — пароль из браузера не уходит. Подпись делает встроенный в браузер WebCrypto, поэтому форма работает только по https и в браузерах с Ed25519: Chrome 137, Firefox 129, Safari 17 и новее.

Один и тот же код модуля работает в обоих случаях — разветвление происходит полностью автоматически внутри библиотеки melbis_inc_logic_auth. Разработчику достаточно корректно обработать случай $mUserId == null в функции Page.


Итоговая схема

Windows Melbis Shop
    │
    │  POST: store_id / order_id / login / stamp / sign
    ▼
index.php → Run('melbis_web_sample')
    │
    ▼
MELBIS_WEB_SAMPLE\Main($mVars)
    │
    ▼
MELBIS_INC_LOGIC_AUTH\Router(...)
    ├─ авторизация + проверка прав
    ├─ func отсутствует      → MELBIS_WEB_SAMPLE\Page($userId, $mVars)         → HTML
    ├─ func = 'GetCataloge'  → MELBIS_WEB_SAMPLE\GetCataloge($userId, $mVars)  → JSON
    └─ func = 'GetGoods'     → MELBIS_WEB_SAMPLE\GetGoods($userId, $mVars)     → JSON

HTML-страница в браузере программы
    │  (только для окна редактирования заказа)
    ▼
postMessage({ event: 'MELBIS_ORDER_UPDATE', data: {"version":[...]} })
    │
    ▼
Windows Melbis Shop — применяет изменения к заказу