В различных бизнес-приложениях часто возникает задача по вводу документов. Обычно документ состоит из заголовка и некоторых строк, каждая из которых ссылается на некоторый объект (например, товар). Чаще всего, для ввода записей в документ используется обычная таблица, в которой пользователь может добавлять и удалять строки, а также изменять их содержимое.
Однако, в некоторых случаях, такая схема не всегда удобна для пользователей. В этой статье я расскажу, как можно строить удобный пользовательский интерфейс при помощи техники подбора товаров.
Задача
Как правило, при вводе документов для пользователя существует ограничение по набору объектов, которые могут быть в него добавлены. В этом случае, логично пользователю показывать список таких объектов с возможностью быстрого включения (и исключения) их в документ. В колонки, по каждому из объектов, также удобно показывать данные, необходимые для принятия решения о необходимости включения его в документ. И наконец вместе с объектом часто бывает потребность вводить его количество.
Рассмотрим три часто встречаемых в бизнес-приложениях типа документов:
- Заказ на закупку. В таких документах пользователю логично показывать список всех товаров, доступных для заказа от поставщика. В колонки удобно показывать текущий остаток, реализацию за определенный интервал, кол-во заказанного на закупку и продажу.
- Заказ на продажу. Здесь чаще всего показывается список товаров, которые есть на остатках выбранного склада и доступны для продажи выбранному клиенту. Также должны показываться текущие цены
- Изменение остатков. Это документ используется для корректировки текущих остатков в случае выявления каких-то расхождений с фактическим количеством. В подборе обычно показываются все товары, с возможностью вводить фактический остаток для любого из них. При этом в документ добавляется товар с количеством равным разнице между фактическим и текущим остатком.
Решение
Дальше я покажу как быстро и легко реализовать эту логику на базе открытой и бесплатной платформы lsFusion. В качестве примера создадим логику по вводу заказа на закупку.
Для начала добавим справочник товаров через стандартный CRUD интерфейс:
FORM product ‘Товар’OBJECTS p = Product PANEL PROPERTIES(p) name
|
Создадим понятие поставщик, и в форме редактирования дадим возможность выбирать товары, с которыми он работает:
in ‘Вкл’ = DATA BOOLEAN (Supplier, Product); // будет TRUE, если у поставщика разрешено закупать этот товар
|
Объявим логику заказов со строками:
supplier ‘Поставщик’ = DATA Supplier (Order);nameSupplier ‘Поставщик’ (Order o) = name(supplier(o));
|
Добавляем к строкам товары и количество:
quantity ‘Кол-во’ = DATA NUMERIC[14,3] (OrderDetail); |
Переходим непосредственно к построению нужной нам формы редактирования заказа. Для начала добавляем на нее сам заказ и его строки:
OBJECTS d = OrderDetailPROPERTIES(d) nameProduct, quantity, NEW, DELETE FILTERS order(d) = o
|
Тем самым мы получаем стандартный интерфейс по работе со строками заказа через добавление и удаление.
Дальше добавляем на эту форму нужный нам функционал подбора. Для этого сначала создаем на форме объект со списком всех товаров, в котором фильтруем только те, которые разрешены к заказу у выбранного поставщика:
EXTEND FORM order |
Настраиваем дизайн, чтобы строки заказа и подбор отображались как вкладки одного контейнера:
DESIGN order { |
Строим вспомогательные свойства для отображения и ввода пользователем количества в соответствующей колонке:
quantity 'Кол-во' (Order o, Product p) = |
Первое свойство считает для заказа и товара его количество в этом документе. Второй находит последнюю строку (пользователь может ввести несколько строк с одним товаром).
Дальше создаем действие, которое будет обрабатывать ввод пользователем значения в соответствующую колонку во вкладке подбора:
changeQuantity 'Изменить кол-во' (Order o, Product p) { |
И наконец добавляем колонку на форму, указывая действие, которое должно выполняться, когда пользователь будет пытаться менять ее значение:
EXTEND FORM order |
Осталось только нарисовать форму со списком заказов и добавить ее в навигатор:
NAVIGATOR {NEW orders; } |
Результирующая форма будет выглядеть приблизительно вот так:
Любые изменения сделанные на любой из вкладок будут автоматически отражаться на другой.
В реальных ERP-системах на форму добавляется значительно больше колонок и прочих элементов. Например, в одной из таких реализаций она выглядит следующим образом:
Посмотрим как этот функционал реализован в других бизнес-приложениях.
1С
В разных конфигурациях 1С логика подбора имеет определенные отличия, но принцип более менее одинаков. На форме редактирования документов есть кнопка Подобрать, которая вызывает диалог с выбором товара:
В этом диалоге подбирать товар можно следующим образом:
В этом механизме есть, как минимум, два неудобства.
Во-первых, для того, чтобы добавить количество товара в документ (а чаще всего пользователь уже знает количество, которое хочет добавить), нужно сначала дважды кликнуть на товар, добавив его в строки, а затем уже в строках менять количество. При этом в самом списке товаров пользователь не видит добавлен ли уже этот товар и с каким количеством. Кроме того, такая схема “сжирает” дополнительное место, так как приходится выводить две таблицы на один экран вместо одной.
Во-вторых, после того как были добавлены строки непосредственно в документ, если нажать повторно кнопку Подобрать, то подбор начнется “с чистого листа”. В нем не появится никаких строк в нижней таблице, и в момент повторного подбора не будет понятно, что уже есть в документе, а чего нет. Также это нарушает одно из важных правил при построении интерфейса: если пользователь ошибся, то ему нужно дать возможность вернутся назад и исправить ошибку. Здесь же получается, что он не сможет вернутся в подбор товара имея те же данные, что и до нажатия кнопки добавления в документ.
Впрочем, возможно в 1С можно реализовать и вышеописанную схему, но почему-то в типовых конфигурациях, которые доступны на сайте с демо-версиями используется другая.
Microsoft Dynamics 365
В качестве решения я взял Retail (только в нем я нашла похожий функционал из доступных на https://trials.dynamics.com). В нем вызвать подбор можно на форме Purchase orders двумя способами:
Первая кнопка реализована по схеме 1С (хотя там гораздо меньше колонок), поэтому на ней останавливаться не буду.
Вторая кнопка вызывает диалог со списком товаров, где количество уже можно вводить в колонки. Но сделано это очень своеобразно.
Во-первых, по умолчанию, там показываются только коды товаров (даже без наименований). Я конечно понимаю, что можно создать расширения и включить туда еще другие колонки, но видеть такое поведение в базовой версии немного странно.
Во-вторых, в Microsoft видимо так увлеклись дизайном, что почему-то забили на нормальную обработку скроллирования данных, обработку фокусов и событий. Выглядит это приблизительно вот так:
При обычной работе клавиатурой идет постоянное переключение текущих рядов при перечитывании данных. При этом начать вводить можно только дождавшись всех обработок событий, а иначе количество либо не вводится вообще, либо вводятся не в те ряды. Как с этим работают реальные пользователи для меня остается загадкой. Видимо конкретно этим функционалом мало кто пользуется. Ну или это ограничения демоверсий, а в эксплуатации все работает хорошо.
Заключение
Описанная схема ввод документов через отдельную вкладку подбор была хорошо оценена нашими пользователями.
На практике, эта концепция может усложняться различными способами. Например, вместо списка товара выводить список артикулов с возможностью ввод разных количеств для отдельных характеристик. Или в одном из документов в строках были склады, и требовалось в колонки задавать количества для каждого из этих складов.
Такую схему можно реализовать, например, в том же React, используя в качестве состояния текущие строки документа и отображая его двумя компонентами в двух разных вкладках. Но следует помнить, что записей в таблице с товарами может быть очень много, и придется реализовывать так называемый “infinite scroll”. Кроме того, желательно добавить пользователю возможность сортировать и фильтровать товары в этом списке (причем это надо делать на сервере, а не на клиенте, чтобы не тянуть туда лишние данные). Все это становится достаточно нетривиальной задачей для разработчика.
В платформе lsFusion этот функционал реализуется из коробки и требует лишь описанный выше код. Попробовать как это работает, а при желании и модифицировать код, можно онлайн на соответствующей странице. Вот исходный код целиком, который можно вставить на вкладку Платформа и затем нажав Play:
Исходный код
FORM product ‘Товар’OBJECTS p = Product PANEL PROPERTIES(p) name
|