Недавно вышла шестая версия бесплатной открытой платформы lsFusion. В этой статье я постараюсь вкратце рассказать об основных новых возможностях этой версии и проиллюстрировать их примерами и gif-картинками для тех, кто предпочитает визуальное восприятие информации. Многие из этих возможностей можно попробовать онлайн на официальном сайте или в демо-версии одного из решений на базе lsFusion.
Шестая версия lsFusion делает значительный шаг вперед, окончательно превращая платформу из инструмента для разработки бизнес-приложений в мощную платформу для создания веб-приложений. Основной акцент сделан на улучшение Web UX, что критически важно для современных приложений. Среди ключевых нововведений — интеграция с Bootstrap, использование различных API браузеров и поддержка прогрессивных веб-приложений (PWA).
lsFusion прекрасно подходит для разработки корпоративных и учётных приложений в качестве альтернативы платформе 1С. При этом, в отличие от 1С, она полностью открыта и бесплатна, что особенно важно для компаний, стремящихся к снижению затрат и независимости от вендоров.
Оглавление
Возможность выполнения интерактивных действий в интеграционном API
Поддержка классических браузерных интеграций/API с пользователем (push, notification, share)
Возможность в событии изменения CHANGE свойств определять причину события
Поддержка кастомизированных агрегаций в операторах GROUP и PARTITION
Bootstrap дизайн
В новой версии lsFusion интегрирован Bootstrap, что открывает широкий спектр возможностей для улучшения интерфейсов приложений. Теперь все системные элементы, такие как формы, кнопки и другие интерфейсные компоненты, используют классы Bootstrap, где это возможно. Это существенно упрощает разработку, обеспечивая единообразие и современный внешний вид интерфейсов.
Помимо классической (по умолчанию) темы, подключены несколько предустановленных Bootstrap тем. Это позволяет пользователям выбирать и настраивать тему, соответствующую их предпочтениям и стилю бренда, без необходимости дополнительной работы по интеграции сторонних стилей.

Основным способом интеграции Bootstrap в lsFusion является использование атрибута class (подробно о нем мы поговорим далее). Этот атрибут позволяет разработчикам легко применять Bootstrap классы к любым элементам интерфейса, что дает возможность быстро и эффективно адаптировать дизайн под конкретные требования проекта, обеспечивая его гибкость и визуальную привлекательность.
myprop1 'Switch' = DATA BOOLEAN ();
myprop2 'Checkbox' = DATA BOOLEAN ();
myprop3 'Secondary checkbox' = DATA BOOLEAN ();
myprop4 'Floating caption' = DATA STRING ();
FORM test
PROPERTIES () myprop4, myprop1, myprop2, myprop3
;
// ПРИМЕР
DESIGN test {
// как переключатель
PROPERTY(myprop1()) {
class = 'form-switch';
}
// как кнопка
PROPERTY(myprop2()) {
valueClass = 'btn-check';
}
// как второстепенная кнопка
PROPERTY(myprop3()) {
captionClass = 'btn btn-outline-secondary';
valueClass = 'btn-check';
}
// как плавающий заголовок
PROPERTY(myprop4()) {
class = 'form-floating';
toolbar = FALSE; // we don't want toolbar in that case it breaks everything
placeholder = ''; // floating needs input with the placeholder
panelCaptionLast = TRUE; // caption should be after input
}
}
// КОНЕЦ ПРИМЕРА
run() {
SHOW test;
}

Поддержка иконок: Bootstrap Icons и FontAwesome
В новой версии lsFusion подключены две популярных библиотеки иконок: Bootstrap Icons и FontAwesome. Это значительно расширяет возможности визуального оформления интерфейса.
Для различных элементов, таких как действия или элементы навигатора, а также для подстрок внутри строковых литералов (при помощи тега интерполяции $M), теперь можно явно задавать иконки. Это улучшает внешний вид приложения, делая его более интуитивным и привлекательным для пользователей.
// ПРИМЕР
testAction 'Test' {
MESSAGE 'Something happened $M{stop}'; // неявное указание иконки в строке
}
isCar = DATA BOOLEAN (); // при нажатии меняется картинка
FORM test
OBJECTS i = INTEGER
FILTERS iterate(i, 1, 10)
PROPERTIES 'Column with image' = HTML(IF mod(i,3) = 0 THEN '$M{car} Car' ELSE '$M{share} Share') // вставляем картинку в данные (для этого преобразовываем к HTML типу) в зависимости от условия
PROPERTIES () isCar, testAction IMAGE // нужна иконка, но платформа сама должна определить исходя из заголовка/имени
;
DESIGN test {
image = 'fa fa-star'; // явное указание иконки
OBJECTS {
NEW myContainer {
caption = 'Container';
image = IF isCar() THEN 'car' ELSE 'question'; // неявное указание иконки - платформа должна сама найти наиболее подходящую
MOVE PROPERTY(testAction());
}
}
}
// КОНЕЦ ПРИМЕРА
run() {
SHOW test;
}

Кроме того, в lsFusion реализована возможность автоматического подбора иконок по ключевым словам. Эти слова могут определяться из имен элементов, что обеспечивает быстрое и удобное оформление интерфейса с минимальными усилиями со стороны разработчиков. Таким образом, система сама подбирает наиболее подходящие иконки, что делает процесс разработки еще более эффективным.
Компоненты выбора
Вместо обычного текстового поля для отображения и изменения свойств на форме, в новой версии lsFusion появилась возможность использовать различные компоненты, такие как выпадающий список (dropdown), флажок (checkbox), группа флажков (checkbox group) и радиокнопки (radio groups). Эти компоненты, как и все остальные, оформлены в стиле Bootstrap.
OBJECTS i = MasterData.Item
PROPERTIES p1 = nameUom(i) SELECT, // определять автоматически на основе статистики
p2 = nameUom(i) SELECT 'dropdown' PANEL, // выпадающий список
p3 = nameUom(i) SELECT 'buttonGroup', // группа кнопок
p4 = nameUom(i) SELECT 'list' PANEL // список (радиокнопки)

Основное отличие этих компонентов заключается в том, что они могут отображать сразу все возможные значения свойств и позволяют выбирать новое значение одним действием, без необходимости начинать процесс редактирования и обращаться к серверу. Отметим также, что компоненты выбора могут использоваться даже в случае, если для свойства задан явный ON CHANGE. В этом случае при отображении компонента выбора будут учитываться, например, фильтры, заданные в диалоге, вызываемом в этом ON CHANGE:
OBJECTS it = MasterData.Item
PROPERTIES nameUom(it) SELECT 'buttonGroup' ON CHANGE {
DIALOG uoms OBJECTS i = uom(it) CHANGE LIST name(i) FILTERS mod(LONG(i), 3) = mod(LONG(it), 3); // показываем только единицы измерения, у которых ИД по модулю 3 совпадет с ИД по модулю 3 товара
}

Эти компоненты могут использоваться как для обычных свойств с одиночным значением, так и для свойств, значение которых представляет собой объединение множества значений для разных объектов с помощью операции конкатенации (GROUP CONCAT). В таких случаях новые компоненты позволяют выбирать несколько значений одновременно, что делает взаимодействие с интерфейсом более удобным и быстрым.
in = DATA BOOLEAN (InvoiceLine, Tax);
taxes 'Taxes' (InvoiceLine l) = GROUP CONCAT name(Tax t) IF in(l, t), ', ' ORDER t;
FORM test
OBJECTS i = InvoiceLine
PROPERTIES p1 = taxes(i) SELECT, // определять автоматически на основе статистики
p2 = taxes(i) SELECT 'dropdown' PANEL, // выпадающий список
p3 = taxes(i) SELECT 'buttonGroup', // группа кнопок
p4 = taxes(i) SELECT 'list' PANEL // список
;
run() {
SHOW test;
}

Использование этих компонентов может быть явно задано разработчиком или автоматически определено системой на основе статистики (количества возможных значений). При необходимости пользователи также могут настраивать их по своему усмотрению.

Явное задание тега и типа элемента input
В новой версии lsFusion появилась возможность задавать тег для HTML элемента, используемого при отображении свойства на форме. Если заданный тег - это input, то можно явно указать его тип, например, month, date, color, range и другие поддерживаемые браузером типы. Также поддерживается тип year, который не поддерживается нативно, но реализуется с помощью библиотеки yearpicker.
x = DATA LOCAL COLOR ();
y = DATA LOCAL DATE ();
yt = DATA LOCAL DATETIME ();
ytt = DATA LOCAL TIME ();
k = DATA LOCAL STRING ();
w = DATA LOCAL INTEGER ();
FORM test
PROPERTIES () x, y, yt, ytt, k, w, w2 = w
OBJECTS i = INTEGER
FILTERS iterate(i, 1, 10)
PROPERTIES () DRAW i x1=x, w3 = w
;
DESIGN test {
OBJECTS {
// используем нативные браузерные компоненты
PROPERTY (x1) {
tag = 'input';
}
PROPERTY (x()) {
tag = 'input';
}
PROPERTY (y()) {
tag = 'input';
valueWidth = 300;
inputType = 'date';
}
PROPERTY (yt()) {
tag = 'input';
valueWidth = 300;
inputType = 'datetime-local';
}
PROPERTY (ytt()) {
tag = 'input';
valueWidth = 300;
inputType = 'time';
}
PROPERTY (k()) {
tag = 'input';
inputType = 'month';
}
MOVE PROPERTY(w()) {
inputType = 'number';
}
PROPERTY(w3) {
tag = 'input';
inputType = 'range';
}
}
}
run() {
SHOW test;
}

Эти параметры могут быть заданы явно разработчиком или автоматически определяться платформой в зависимости от типа данных, событий, имени свойства и других факторов.
Расширенная поддержка атрибутов свойств
В новой версии lsFusion значительно расширен набор поддерживаемых атрибутов свойств. Рассмотрим подробнее каждый из поддерживаемых атрибутов и их функционал.
placeholder, pattern, regexp: Эти атрибуты используются для ввода данных и предоставляют функциональность, аналогичную браузеру и другим UI библиотекам и платформам. placeholder задает текст-заполнитель, который отображается внутри текстового поля до ввода данных. pattern и regexp позволяют задавать шаблоны для проверки правильности ввода, обеспечивая валидацию данных.
comment: Атрибут comment позволяет добавлять комментарии к свойствам, аналогично тому, как это реализовано в Bootstrap и других UI библиотеках и платформах.
tooltip: Поддержка всплывающих подсказок (tooltip) для заголовков и значений свойств. Всплывающие подсказки могут отображаться как при отображении свойств в виде поля, так и в таблице.
wrap, wrapWordBreak, collapse: Эти атрибуты позволяют явно задавать, как переносить текст, который не умещается по горизонтали. Это аналогично реализации с использованием CSS-свойств white-space и word-break. wrap и wrapWordBreak определяют, как текст будет переноситься, а collapse позволяет сворачивать длинные тексты.
panelCaption* и panelComment*: Набор атрибутов, таких как panelCaptionVertical, panelCaptionLast, panelCaptionAlignment, panelCommentVertical, panelCommentLast, panelCommentAlignment, которые задают вертикальное или горизонтальное (по умолчанию) расположение, последнее или первое (по умолчанию) расположение, и выравнивание (stretch, start, end, center) элементов панели. Эти настройки аналогичны свойствам flex-alignment в браузере.
class: Атрибут class позволяет задавать CSS-классы для элементов отображения, используя несколько классов через пробел, аналогично атрибуту class в браузере. Также можно задавать атрибуты элементов или значения стилей (style), указывая их в строке в формате <имя атрибута/стиля>=значение. Если указанное имя соответствует существующему стилю, оно задает стиль (например, width=100px), если не соответствует - задает атрибут.
Все эти атрибуты могут быть динамическими, то есть задаваться при помощи выражений.
digits = DATA LONG ();
licensePlate = DATA STRING ();
FORM test
PROPERTIES digits(), licensePlate();
;
DESIGN test {
PROPERTY(licensePlate()) {
pattern = '9\{5\}-9\{${digits()}\}'; //for strings use inputMask
regexp = '\\d\{2\}-\\d\{${digits()}\}'; //usual regexp syntax
placeholder = 'Введи номер с ${digits()} цифрами после запятой';
regexpMessage = 'Некорректный формат';
comment = 'Рег. номер - ${digits()} цифры после запятой';
panelCommentVertical = TRUE;
}
}
run() {
digits() <- 3;
SHOW test;
}

Всплывающие контейнеры
В новой версии lsFusion введена поддержка popup контейнеров. Эти контейнеры отображаются в виде кнопки, и их содержимое становится доступным при клике на эту кнопку. Атрибут popup со значением TRUE определяет, что контейнер является popup контейнером, аналогично тому, как tabbed контейнеры определяются с помощью атрибута tabbed. Если popup контейнер не активен или скрыт, данные внутри него не загружаются, что улучшает производительность.
digits = DATA LONG ();
licensePlate = DATA STRING ();
FORM test
PROPERTIES digits(), licensePlate(), '' = licensePlate() + ' ' + STRING(digits());
;
DESIGN test {
NEW X {
popup = TRUE;
image = 'hamburger';
MOVE PROPERTY(digits());
MOVE PROPERTY(licensePlate());
}
}
run() {
SHOW test;
}

Также, для групп объектов на форме, в дизайн по умолчанию добавлен новый предопределенный контейнер типа POPUP, аналогичный контейнеру TOOLBAR. Этот контейнер отображается в виде иконки с тремя точками (гамбургер) и используется в случаях, когда пространство ограничено, а нужно отобразить множество элементов, таких как действия или кнопки.
testAction () {
MESSAGE 'HI';
}
FORM test
PROPERTIES testAction();
;
DESIGN test {
POPUP {
MOVE PROPERTY(testAction());
}
}
run() {
SHOW test;
}

Для отображения popup контейнеров, как и всех других popup компонентов, в новой версии используется библиотека tippy.
Неактивные свойства/действия
В новой версии lsFusion добавлена поддержка отображения неактивных свойств и действий. Это реализуется нативно на уровне браузера (если такая поддержка имеется, а если нет — на уровне самой платформы), и с использованием соответствующих классов Bootstrap.
Поддержка неактивных свойств и действий может быть статичной, задаваемой с помощью опции DISABLE, аналогично READONLY. Также возможна динамическая неактивность, которая определяется в зависимости от значения выражения, с помощью опции DISABLEIF, аналогично READONLYIF.
quantity = DATA INTEGER ();
comment = DATA STRING ();
FORM test
PROPERTIES () quantity, comment DISABLEIF NOT quantity() > 5; // активировать, только если количество больше 5
;
run() {
SHOW test;
}

Возможность выполнения интерактивных действий в интеграционном API
В новой версии lsFusion расширены возможности интеграционного API. Теперь endpoint'ы exec и eval поддерживают выполнение действий с интерактивными взаимодействиями, если действие выполняется в вкладке браузера.
Если действие, выполненное в вкладке браузера, включает интерактивное взаимодействие (например, открытие формы), платформа ищет существующую вкладку (или приложение PWA) и выполняет действие в ней. Если найденная вкладка неактивна, отображается уведомление, при нажатии на которое происходит переход на эту вкладку. Если вкладка не находится, приложение открывается в текущей вкладке браузера, откуда выполняется действие или запрос.

Эта возможность особенно важна для реализации Share API (подробнее см. в следующих пунктах).
Поддержка классических браузерных интеграций/API с пользователем (push, notification, share)
В новой версии lsFusion добавлена поддержка классических браузерных интеграций и API для взаимодействия с пользователем, включая push, notification и share.
Share API
Share API состоит из двух частей:
Генерация ссылки: Выполняется с помощью оператора EXTERNAL (с опцией HTTP или LSF) с пустой строкой подключения. Вместо выполнения запроса, оператор сохраняет его в заданное свойство. Оператор EXTERNAL с опцией LSF позволяет сохранить и впоследствии выполнить любое действие на языке lsf с заданным именем или любой код на языке lsf. В этом действии могут быть интерактивные взаимодействия (как описано выше).
Возможность поделиться ссылкой через Share API: Осуществляется с помощью встроенных действий с именем share, которые используют браузерное Share API для создания и распространения ссылок. Это API позволяет пользователю делиться контентом с помощью стандартных диалогов шаринга, которые поддерживаются операционной системой или браузером, таких как отправка ссылки через мессенджеры, социальные сети, электронную почту и другие приложения.
По умолчанию для любой формы автоматически добавляется действие "поделиться формой", которое генерирует ссылку, показывающую текущую форму с текущими объектами, и выполняет действие по распространению этой ссылки с помощью браузерного Share API. Также действия для шаринга добавляются в дизайн формы по умолчанию.

Notification API
Возможность показывать нотификации с заданными параметрами, такими как текст, изображение и другими параметрами нотификаций в браузере. Также можно задавать действие, которое будет выполняться в приложении при нажатии на нотификацию. Дополнительно можно добавлять кнопки в нотификацию для выполнения других действий, передавая информацию о том, какая кнопка была нажата. Если клиент не активен или закрыт, открывается новый клиент, так как это делается при выполнении интерактивных действий в интеграционном API.
simpleNotify() {
NEWTHREAD {
INPUT s = STRING ACTIONS 'new.png' {
MESSAGE 'NEW';
},'delete.png' {
MESSAGE 'DELETE';
} DO {
}
} TO exportInteger;
notify('SOMETHING HAPPENED', exportInteger());
}
complexNotify() {
NEWTHREAD {
INPUT s = STRING DO {
IF s = 'a1' THEN
MESSAGE 'CLICKED NEW';
ELSE IF s = 'a2' THEN
MESSAGE 'CLICKED DELETE';
ELSE
MESSAGE 'CLICKED NOTIFICATION';
}
} TO exportInteger;
notify((JSON FROM title = 'SOMETHING HAPPENED', options = (JSON FROM icon = '$R{share.png}', image = '$R{tools.png}', actions = (JSON FROM title = (IF i = 1 THEN 'New' ELSE 'Delete'), action = 'a' + i, icon = (IF i = 1 THEN '$R{new.png}' ELSE '$R{delete.png}') WHERE iterate(i, 1, 2)))), exportInteger());
}
FORM test
PROPERTIES () simpleNotify, complexNotify
;
run() {
SHOW test;
}

Push API
Push API позволяет серверу инициировать действия в браузерных вкладках клиента. Вкладка определяется объектом соединения на сервере, создаваемым при её открытии. Push-уведомления могут немедленно выполнять действие или отображать уведомление, при нажатии на которое действие будет выполнено. Это аналогично классическому Push API в браузерах, где сервер может инициировать взаимодействие с клиентом даже при неактивном окне.
simplePushNotify 'Simple' () {
NEWTHREAD {
SHOW Invoicing.invoice DOCKED;
} TO exportInteger;
pushNotify(pushConnection(currentUser()), 'SOMETHING HAPPENED', exportInteger());
}
complexPushNotify 'Complex' () {
NEWTHREAD {
INPUT s = STRING DO {
IF s = 'a1' THEN
MESSAGE 'CLICKED NEW';
ELSE IF s = 'a2' THEN
MESSAGE 'CLICKED DELETE';
ELSE
MESSAGE 'CLICKED NOTIFICATION';
}
} TO exportInteger;
pushNotify(currentConnection(), (JSON FROM title = 'SOMETHING HAPPENED', options = (JSON FROM icon = '$R{lightMode.png}', image = '$R{logoHorizontal.png}', actions = (JSON FROM title = (IF i = 1 THEN 'New' ELSE 'Delete'), action = 'a' + i, icon = (IF i = 1 THEN '$R{new.png}' ELSE '$R{delete.png}') WHERE iterate(i, 1, 2)))), action(exportInteger()));
}
FORM test
PROPERTIES () simplePushNotify, complexPushNotify
;
run() {
SHOW test DOCKED;
}

Эти возможности делают интеграцию с пользователем более гибкой и удобной, расширяя функционал lsFusion в контексте современных веб-приложений.
Поддержка PWA
В новой версии lsFusion добавлена поддержка Progressive Web Apps (PWA). Это позволяет пользователям устанавливать приложение нативно в браузере со всеми возможностями PWA, такими как:
Push-уведомления, даже когда браузер не открыт
Ярлык приложения и возможность найти его в списке приложений операционной системы
Доступ к оборудованию устройства и другим функциям, которые обычно доступны нативным приложениям

Уровни MESSAGE
Уровни сообщений (MESSAGE) в новой версии lsFusion, расположенные по возрастанию важности, включают: LOG, INFO, WARN, DEFAULT, SUCCESS, ERROR.
В зависимости от уровня сообщения выполняются одно или несколько из следующих действий:
Запись в лог: Сообщение записывается в лог с фоном, соответствующим цвету Bootstrap для данного типа сообщения.
Нотификация: Сообщение отображается в виде нотификации. В некоторых уровнях нотификация показывается только если клиент неактивен.
Диалоговое окно: Сообщение отображается на экране в виде диалогового окна с фоном, соответствующим цвету Bootstrap для данного типа сообщения.
MESSAGE 'Some info' INFO;
MESSAGE 'Some warning' WARN;
MESSAGE 'Some log' LOG;

Следующие пункты скорее связаны не с Web UX, а Business Applications Web UX
Выплывающая панель (окно) навигатора, возможность делать верхнюю панель навигатора вертикальной или горизонтальной
В новой версии lsFusion добавлена возможность настройки верхней панели навигатора (root) и вспомогательной панели (toolbar):
Выплывающая верхняя панель навигатора: Верхняя панель может быть настроена так, чтобы "выплывать" при наведении курсора, с появлением и исчезанием текста.
Вертикальная или горизонтальная ориентация: Верхняя панель навигатора может быть настроена в вертикальном или горизонтальном положении в зависимости от предпочтений пользователя и требований интерфейса.
Выплывающая вспомогательная панель: Вспомогательная панель, элементы которой определяются в зависимости от выбранного элемента на верхней панели, может также быть выплывающей. Она появляется при выборе элемента на верхней панели и исчезает через некоторое время.
Пользовательская настройка: Пользователи могут сами определять расположение верхней панели и режим выплывания, что позволяет создать наиболее удобный и эффективный интерфейс для работы.

Эти функции предназначены для того, чтобы оставлять больше места для работы с формами после их открытия, делая интерфейс более адаптивным и удобным для пользователей.
Tiny/Mini размеры дизайна
В новой версии lsFusion добавлены дополнительные CSS-стили для уменьшения размеров элементов интерфейса, специально разработанные для бизнес-приложений, таких как ERP-системы. Обычный дизайн на основе Bootstrap может быть слишком громоздким и требовать много скроллирования, особенно когда в приложении присутствует большое количество элементов.
Для решения этой проблемы внедрены два дополнительных размера дизайна:
Mini: Уменьшенные размеры элементов, которые делают интерфейс более компактным.
Tiny: Еще более уменьшенные размеры элементов, обеспечивающие максимальную компактность.
Эти стили помогают уменьшить необходимость скроллирования и делают интерфейс более эргономичным для пользователей бизнес-приложений.
Направленные кнопки
В новой версии lsFusion добавлена поддержка направленных кнопок (arrow buttons). Эти кнопки особенно важны для бизнес-приложений, где часто требуется визуально отображать и управлять процессами. Направленные кнопки позволяют сделать интерфейс более интуитивным и привлекательным, улучшая визуальное представление процессов и упрощая работу с ними.
NEW statusGroup {
custom = '';
class = 'btn-group';
}
…
statusGroup {
MOVE PROPERTY(canceled(o)) {
valueClass = 'btn-check';
captionClass = 'btn btn-outline-primary btn-arrow-right';
}
}

WebSocket
В новой версии lsFusion добавлен модуль WebSocket.lsf, поддерживающий прием и отправку сообщений по протоколу WebSocket. Этот модуль позволяет:
Создавать и удалять сокеты: Возможность создания и удаления объектов для установления WebSocket-соединений.
Обрабатывать события: Поддержка событий для обработки открытия и закрытия соединений, а также получения текстовых и бинарных сообщений.
Отправлять сообщения: Возможность отправки как текстовых, так и бинарных сообщений.
Управление через интерфейс: Форма для управления соединениями и сообщениями через графический интерфейс. В ней представлены объекты сокетов, поля для ввода сообщений и кнопки для их отправки.
Следующие возможности больше относятся к логике приложения и процессу разработки, а не Web UX.
Поддержка "табличных значений" в операторе FORMULA
В новой версии lsFusion добавлена поддержка "табличных значений" в операторе FORMULA. Если в формуле есть недостающие параметры, то считается, что выражение возвращает таблицу. Недостающие параметры считаются ключами возвращаемой таблицы, а значение формулы — значением какого-либо поля этой таблицы.
Этот функционал позволяет:
Подключать сторонние таблицы в базе данных.
Использовать табличные функции.
Выполнять операции unnesting, то есть преобразовывать значения строк, JSON и массивов в ряды/таблицы.
x (LONG key0) = FORMULA STRING 'masterdata_name_country' '_auto_masterdata_country';
sampleJson = JSON('\{ "x": \{ "y": [ \{ "f":5 \}, \{ "f":11 \}] \} \}');
sampleJson2 = JSON('\{ "x": 5, "y": \{ "f":5 \} \}');
field (JSON json, STRING field) = FORMULA JSON 'jsonb_extract_path($json, $field)';
field (JSON json, STRING field1, STRING field2) = FORMULA JSON 'jsonb_extract_path($json, $field1, $field2)';
array (JSON json, INTEGER row) = FORMULA JSON value 'jsonb_array_elements($json)';
map (JSON json, STRING key) = FORMULA JSON value 'jsonb_each($json)';
arrayElement(JSON json, INTEGER i) = FORMULA STRING '(($1)->(($2)-1))'; // one-based
calcSum(JSON json) = GROUP SUM INTEGER(field(array(field(json, 'x', 'y'), INTEGER r),'f'));
array(STRING string, STRING delimeter, INTEGER row) = FORMULA STRING value 'unnest(string_to_array($string, $delimeter))';
FORM test
OBJECTS k = LONG
PROPERTIES (k) x
PROPERTIES PANEL 'calcSum' = calcSum(sampleJson())
FILTERS x(k)
OBJECTS k2 = STRING
PROPERTIES VALUE(k2), 'V' = map(sampleJson2(), k2)
FILTERS map(sampleJson2(), k2)
;
run() {
MESSAGE JSON FROM name = array((GROUP CONCAT name(MasterData.Country r), ',' ORDER r), ',', INTEGER r);
MESSAGE arrayElement(field(sampleJson(), 'x', 'y'), 1);
SHOW test;
}

Оператор VALUE
В новой версии lsFusion добавлен оператор VALUE для работы с группами объектов. По аналогии с операторами FILTER, ORDER, VIEW, оператор VALUE работает с группами объектов, но с одним объектом из группы, а не со всеми сразу. Он возвращает текущее значение объекта на форме.
Этот оператор позволяет обращаться к текущим значениям объекта на форме за пределами самой формы, например, в локальных событиях предметной области.
FORM test
OBJECTS x = INTEGER
PROPERTIES VALUE(x)
FILTERS iterate(x, 1, 5)
;
test() {
MESSAGE 'OBJECT : ' + (VALUE test.x);
}
EXTEND FORM test
PROPERTIES test()
;
run() {
SHOW test;
}

Дополнительные события на форме
В новой версии lsFusion добавлены дополнительные события, которые позволяют более гибко управлять поведением формы и взаимодействием с пользователем.
Для свойств:
CHANGE BEFORE: Возникает до изменения свойства, не заменяя событие CHANGE. В этом событии можно отменить выполнение последующих событий изменения, заполнив beforeCanceled значением TRUE.
CHANGE AFTER: Возникает после изменения свойства, не заменяя событие CHANGE.
Для контейнеров:
ON COLLAPSE: Возникает при сворачивании контейнера.
ON EXPAND: Возникает при разворачивании контейнера
OBJECTS i = MasterData.Item
PROPERTIES p3 = nameUom(i) SELECT 'buttonGroup' ON CHANGE AFTER { MESSAGE 'Changed to: ' + nameUom(i); }

Возможность явно задавать имена в базе данных
В новой версии lsFusion добавлена возможность явно задавать имена в базе данных для следующих элементов lsFusion:
Индексы
Таблицы
Материализованные свойства
TABLE myTable 'tableX' (Sales.Order);
myField = DATA STRING (Sales.Order) MATERIALIZED 'fieldX' INDEXED 'indexX';
INDEX 'indexY' myField(Sales.Order o), o;
Эта функция позволяет разработчикам лучше контролировать структуру базы данных и упрощает интеграцию с другими системами, обеспечивая более понятные и управляемые схемы данных.
Возможность в событии изменения CHANGE свойств определять причину события
В новой версии lsFusion добавлена возможность определять причину изменения свойств при событии CHANGE. Причина события записывается в свойство eventSource, которое может принимать четыре значения:
EDIT: Когда событие вызывается пользователем (например, щелчком мыши и т.д.).
BINDING: Когда событие вызывается ассоциацией комбинацией клавиш или действий мыши (CHANGEKEY, CHANGEMOUSE).
PASTE: Когда событие вызывается операцией вставки.
CUSTOM: Когда событие вызывается программно.
x = DATA STRING (INTEGER);
changeX(i) {
INPUT x = x(i) DO
x(i) <- (IF eventSource() = 'PASTE' THEN 'PASTED ' ELSE '') + x;
}
FORM test
OBJECTS i = INTEGER
PROPERTIES(i) x ON CHANGE changeX(i), px = x PANEL ON CHANGE changeX(i)
FILTERS iterate(i, 1, 10)
;
DESIGN test {
PROPERTY(px) {
tag = '';
}
}
run() {
SHOW test;
}

Поддержка кастомизированных агрегаций в операторах GROUP и PARTITION
В новой версии lsFusion добавлена поддержка кастомизированных агрегаций в операторах GROUP и PARTITION. В качестве таких расширенных агрегаций можно использовать как встроенные в СУБД агрегированные функции (например mode или rank), так и пользовательские агрегированные функции (созданные при помощи операторов CREATE AGGREGATE). Например:
value = DATA INTEGER (INTEGER);
median() = GROUP CUSTOM 'mode' WITHIN ORDER value(INTEGER i);
percentile() = GROUP CUSTOM 'percentile_cont' 0.5 WITHIN ORDER value(INTEGER i);
lag2(INTEGER i) = PARTITION CUSTOM NULL 'lag' value(i), 2 ORDER i;
rank(INTEGER i) = PARTITION CUSTOM INTEGER 'rank' ORDER DESC value(i), i;
run() {
value(1) <- 5;
value(2) <- 16;
value(3) <- 15;
value(4) <- 10;
value(5) <- 10;
value(6) <- 4;
MESSAGE 'Median: ' + median() + ', Percentile: ' + percentile() + ', Two rows before 6: ' + lag2(6) + ', Rank of 2: ' + rank(2);
}

Поддержка действий в блоке LIST оператора INPUT
В предыдущих версиях для получения списка значений в блоке LIST можно было использовать только свойства. В новой версии добавлена возможность использовать в этом блоке и действия. Это реализовано аналогично действиям в блоке ACTIONS: в "параметре ввода" будет содержаться текущая введенная строка:
testInput = DATA STRING (INTEGER);
FORM test
OBJECTS i = INTEGER
FILTERS iterate(i, 1, 10)
PROPERTIES a = testInput(i) ON CHANGE {
INPUT s = testInput(i) CHANGE LIST {
EXTERNAL HTTP 'http://127.0.0.1:8888/eval/action?script=$1&p=$2' PARAMS 'EXPORT TOP 10 FROM value = name(MasterData.Item i) WHERE name(i) LIKE \'%\'+ $1 + \'%\'', s TO exportString;
inputList(INTEGER r) <- arrayText(JSON(exportString()), r);
};
}
PROPERTIES b = testInput(i) ON CHANGE {
INPUT s = testInput(i) CHANGE LIST {
inputList(INTEGER r) <- 'Option ${s} ' + r WHERE iterate(r, 1, i);
displayInputList(INTEGER r) <- 'Option ${s} <b>' + r + '</b>' WHERE iterate(r, 1, i);
};
}
;
run() {
SHOW test;
}

Возможность управлять порядками и фильтрами
В новой версии lsFusion добавлена возможность считывать текущие порядки и фильтры, а также устанавливать заданные. Например, следующий код изменяет направление текущих пользовательские порядков на противоположное.
FORM testForm
OBJECTS i = MasterData.Item
PROPERTIES (i) name, idBarCode, nameCategory
PROPERTIES 'Reverse orders' = {
ORDERS testForm.i; // reading orders
IMPORT UserEvents.orders FROM orders();
desc(INTEGER ip) <- NOT desc(ip) WHERE property(ip);
EXPORT UserEvents.orders TO orders;
ORDER testForm.i; // setting orders
}
;
run() {
SHOW testForm;
}

Эта функциональность охватывает как пользовательские фильтры, так и группы фильтров (а точнее текущие фильтры в них):
FORM testForm
OBJECTS i = MasterData.Item
PROPERTIES (i) name, idBarCode, nameCategory
FILTERGROUP fg
FILTER 'All containing АВ' name(i) LIKE '%АВ%'
FILTER 'All containing КА' name(i) LIKE '%КА%'
PROPERTIES 'Change filter group' = {
FILTERGROUPS testForm.fg; // reading filter groups
filterGroups() <- IF filterGroups() >= 2 THEN NULL ELSE filterGroups() (+) 1;
FILTERGROUP testForm.fg;
}
;
run() {
SHOW testForm;
}

Кроме того, теперь можно считывать и устанавливать значения фильтров для пользовательских фильтров заданных свойств:
FORM testForm
OBJECTS i = MasterData.Item
PROPERTIES (i) name FILTER, idBarCode, nameCategory
PROPERTIES 'Add A to the name filter value' = {
FILTERS PROPERTY testForm.name(i);
filtersProperty() <- CONCAT '', filtersProperty(), 'А';
FILTER PROPERTY testForm.name(i);
}
PROPERTIES 'Create barcode filter' = {
filtersProperty() <- '';
FILTER PROPERTY testForm.idBarCode(i);
}
;
run() {
SHOW testForm;
}

Поддержка raw строковых литералов
В новой версии реализована поддержка строковых литералов, в которых специальные символы и последовательности не интерпретируются как управляющие символы, локализация или интерполяция. Вместо этого они обрабатываются буквально. Такой тип строк удобен для написания путей к файлам, регулярных выражений или любого текста, содержащего множество специальных символов.
Для этого типа строк выбран следующий синтаксис. В большинстве случаев "сырой" литерал будет отличаться от обычного строкового литерала только префиксом r
или R
. Например:
r'D:\lsfusion\platform'
вместо'D:\\lsfusion\\platform'
r'\d{5}(-\d{4})?'
вместо'\\d\{5\}(-\\d\{4\})?'
R'{DETAILS}'
вместо'\{DETAILS\}'
Если внутри литерала необходимо использовать одинарную кавычку, синтаксис будет немного сложнее. После префикса r
или R
должен следовать специальный символ, и литерал должен завершаться тем же символом. Например:
r$'{McDonald's}'$
вместо'\{McDonald\'s\}'
R%' {'$50 Prize'} '%
вместо' \{\'$50 Prize\'\} '
Специальный символ следует выбирать так, чтобы внутри строки не встречалась последовательность одинарной кавычки, за которой следует этот символ.
Поддержка опций TOP / OFFSET
В новой версии большинство операторов теперь поддерживают опции TOP и OFFSET, которые позволяют считывать не все записи, а лишь их ограниченное количество. Это обеспечивает эффективную реализацию следующих сценариев:
Расширенная аналитика/ранжирование (операторы GROUP, PARTITION)
smth = DATA INTEGER (Item); first = DATA INTEGER (Category); firstSmth(Category ct) = GROUP SUM smth(Item i) IF level(category(i), ct) ORDER DESC smth(i) TOP first(ct); EXTEND FORM items PROPERTIES (c) first, firstSmth PROPERTIES (i) smth ; firstSmth(Item i, Category ct) = PARTITION LAST smth(i) IF level(category(i), ct) ORDER DESC smth(i) BY ct TOP first(ct) OFFSET 3; EXTEND FORM items PROPERTIES firstSmth(i, c) OBJECTS ti = Item PROPERTIES name(ti), firstSmth(ti, c), smth(ti) FILTERS level(category(ti), c), firstSmth(ti, c) ;
Пакетное чтение (оператор FOR):
FOR iterate(INTEGER j, 0, [GROUP SUM 1 IF Item i IS Item]() / 50) DO NEWSESSION NEWSQL NEWTHREAD { sleep(15000); APPLY FOR Item i IS Item ORDER i TOP 50 OFFSET j * 50 DO smth(i) <- smth(i) + 1; }
Пагинация (операторы EXPORT, JSON):
getItems(STRING nameFilter, INTEGER page, INTEGER pageSize) { exportJSON() <- JSON FROM name(Item i) WHERE name(i) LIKE '%'+nameFilter+'%' ORDER name(i) TOP pageSize OFFSET (page - 1) * pageSize; } // or getItems(STRING nameFilter, INTEGER page, INTEGER pageSize) { EXPORT FROM name(Item i) WHERE name(i) LIKE '%'+nameFilter+'%' ORDER name(i) TOP pageSize OFFSET (page - 1) * pageSize; }
Предпросмотр (оператор PRINT):
PRINT Authentication.customUsers TOP 10;
Заключение
Шестая версия платформы lsFusion существенно расширяет возможности разработки и дизайна современных веб-приложений, особенно в области пользовательского интерфейса (Web UX). Важнейшими обновлениями стали глубокая интеграция Bootstrap, что позволяет быстро и эффективно применять современные и адаптивные дизайны, и добавление поддержки популярных библиотек иконок (Bootstrap Icons, FontAwesome), упрощающих визуализацию интерфейсов.
Также значительное внимание уделено интерактивности и удобству пользователя. В частности, реализованы разнообразные компоненты выбора (dropdown, checkbox group, radio groups), которые упрощают работу с данными, и расширенная поддержка атрибутов для кастомизации форм (placeholder, pattern, tooltip и другие).
Отдельного внимания заслуживает поддержка прогрессивных веб-приложений (PWA) и браузерных API (push, notifications, share), что открывает дополнительные возможности для эффективной коммуникации и взаимодействия с пользователями.
Платформа lsFusion разрабатывается уже 12 лет. За это время было реализовано большое количество приложений на ее основе : от самых простых приложений на несколько форм до сложных ERP-систем с тысячами пользователей и процессов, а также террабайтными базами данных. Разработка платформы финансируется за счет компаний, создающих собственные коммерческие решения на ее основе.
lsFusion подходит для создания приложений как отдельным программистам, так и большим командам за счет высокой декларативности, поддержки ООП и возможности использования Git. Лицензия LGPL v3 платформы позволяет как разрабатывать внутренние приложения для компании, так и коммерческие продукты на ее основе.