Как интерактивно выбрать группу справочника при программном создании элемента в 1С?

Программист 1С v8.3 (Управляемые формы) 1C:Бухгалтерия IT и автоматизация бизнеса
← На главную

В процессе разработки автоматизации в среде «1С:Предприятие 8.3» часто возникает задача программного создания элементов справочников (например, Номенклатуры). Обычно все реквизиты заполняются автоматически на сервере. Однако бизнес-требования заказчиков нередко содержат исключения: например, выбор папки (родительской группы), в которую должен быть помещен новый элемент, должен осуществляться пользователем вручную в процессе работы алгоритма. Это классическая задача на стыке клиентской интерактивности и серверной бизнес-логики.

Давайте подробно разберем, почему нельзя просто вызвать форму выбора в середине серверной процедуры создания элемента, как правильно распределить код между клиентом и сервером и какие механизмы платформы 1С помогут нам реализовать эту задачу элегантно и быстро.

Причина сложности: разграничение контекстов исполнения

Прежде чем перейти к написанию кода, проанализируем ситуацию с точки зрения архитектуры управляемого приложения 1С. Любые интерактивные действия (открытие окон, диалогов, предупреждений и выбор значений из списков) могут выполняться исключительно на клиенте. В то же время создание, заполнение и запись нового объекта базы данных (СправочникОбъект) должны происходить только на сервере.

Если мы попытаемся вызвать интерактивный метод в серверной процедуре СоздатьНоменклатуру(), система выдаст критическую ошибку, так как серверный поток не может прервать свою работу для ожидания ответа от пользователя. Следовательно, нам необходимо сначала получить от пользователя целевую группу на клиенте, а затем передать её в качестве параметра на сервер для создания элемента. Выясним, как это сделать правильно несколькими способами.

Способ 1. Использование механизма оповещений (ОписаниеОповещения)

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

Для реализации этого подхода разберем пошагово следующий алгоритм:

  1. На клиенте подготавливаем параметры для открытия формы выбора.
  2. Создаем объект ОписаниеОповещения, который указывает, какую процедуру нужно выполнить после того, как пользователь выберет группу или закроет форму.
  3. Открываем форму выбора справочника Номенклатура, ограничив выбор только группами.
  4. В процедуре обработки выбора получаем ссылку на группу и вызываем серверную процедуру создания элемента.

Посмотрим на пример практической реализации данного алгоритма:


&НаКлиенте
Процедура НачатьСозданиеНоменклатуры(Команда)

    // Настраиваем параметры открытия формы выбора
    ПараметрыВыбора = Новый Структура;
    ПараметрыВыбора.Вставить("РежимВыбора", Истина);
    
    // Ограничиваем выбор только группами справочника
    ПараметрыВыбора.Вставить("ВыборГруппИЭлементов", ПредопределенноеЗначение("ИспользованиеГруппИЭлементов.Группы"));
    
    // Разрешаем или запрещаем выбирать корень справочника (без папки)
    ПараметрыВыбора.Вставить("РазрешитьВыборКорня", Истина);
    
    // Задаем процедуру, которая примет выбранное значение
    ОповещениеВыбора = Новый ОписаниеОповещения("ВыборГруппыЗавершение", ЭтотОбъект);
    
    // Открываем стандартную форму выбора справочника Номенклатура
    ОткрытьФорму("Справочник.Номенклатура.ФормаВыбора", 
                 ПараметрыВыбора, 
                 ЭтотОбъект, 
                 ,,,, 
                 ОповещениеВыбора, 
                 РежимОткрытияОкнаФормы.БлокироватьОкноВладельца);

КонецПроцедуры

&НаКлиенте
Процедура ВыборГруппыЗавершение(РезультатВыбора, ДополнительныеПараметры) Экспорт

    // Проверяем, выбрал ли пользователь значение или просто закрыл форму
    Если РезультатВыбора = Неопределено Тогда
        ПоказатьПредупреждение(, "Создание номенклатуры отменено: не выбрана группа!");
        Возврат;
    КонецЕсли;
    
    // Если группа успешно выбрана, передаем её на сервер для создания элемента
    СоздатьНоменклатуруНаСервере(РезультатВыбора);
    
    ПоказатьОповещениеПользователя("Успешно",, "Новый элемент номенклатуры успешно создан!");

КонецПроцедуры

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


&НаСервереБезКонтекста
Процедура СоздатьНоменклатуруНаСервере(ВыбранныйРодитель)

    // Создаем новый объект номенклатуры
    НоваяНоменклатура = Справочники.Номенклатура.СоздатьЭлемент();
    
    // Заполняем реквизиты значениями по умолчанию или переданными параметрами
    НоваяНоменклатура.Наименование = "Новый программный товар";
    НоваяНоменклатура.Родитель = ВыбранныйРодитель;
    
    // Заполняем обязательные реквизиты для БП 3.0 (например, ВидНоменклатуры)
    // НоваяНоменклатура.ВидНоменклатуры = ...
    
    // Записываем элемент в базу данных
    Попытка
        НоваяНоменклатура.Записать();
    Исключение
        ВызватьИсключение "Не удалось записать номенклатуру: " + ОписаниеОшибки();
    КонецПопытки;

КонецПроцедуры

Способ 2. Декларативное решение через реквизит формы (без написания сложного кода)

Проанализируем альтернативную ситуацию. Что делать, если мы запускаем наш алгоритм создания номенклатуры из какой-то конкретной обработки или формы документа? В таком случае нам вовсе не обязательно программно конструировать ОписаниеОповещения и открывать модальные окна выбора.

Мы можем использовать декларативные возможности платформы 1С. Разберем этот вариант по шагам:

  1. Добавим в реквизиты нашей обработки или формы новый реквизит с именем ЦелеваяГруппа и типом данных СправочникСсылка.Номенклатура.
  2. Вынесем этот реквизит на управляемую форму в виде поля ввода.
  3. В свойствах созданного поля на форме найдем категорию «Параметры выбора» или свойство «Выбор групп и элементов» (Choice folders and items) и установим значение в положение «Группы» (Folders).

При такой настройке платформа «1С:Предприятие 8» полностью берет на себя управление интерфейсом. Когда пользователь нажмет на кнопку выбора («три точки») в этом поле, система автоматически откроет форму выбора номенклатуры, отфильтрованную исключительно по группам. Кнопка «Выбрать» будет доступна только при активации папок.

Тогда наш программный код на клиенте и сервере значительно упрощается и сводится к следующему лаконичному виду:


&НаКлиенте
Процедура СоздатьЭлемент(Команда)

    // Проверяем заполненность поля формы перед выполнением серверной операции
    Если НЕ ЗначениеЗаполнено(Объект.ЦелеваяГруппа) Тогда
        ПоказатьПредупреждение(, "Пожалуйста, выберите целевую группу на форме перед созданием!");
        Возврат;
    КонецЕсли;

    СоздатьНоменклатуруНаСервере(Объект.ЦелеваяГруппа);
    
КонецПроцедуры

Дополнительные полезные приемы при интерактивном выборе

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

1. Автоматическое позиционирование на нужной папке (Текущая строка)

Если у вас в системе есть папка, в которую пользователи чаще всего складывают новые элементы, вы можете автоматически сфокусировать форму выбора на ней при открытии. Для этого используется параметр ТекущаяСтрока. Посмотрим, как дополнить параметры открытия формы:


// Предположим, у нас есть ссылка на часто используемую группу "Материалы"
ЧастаяГруппа = Справочники.Номенклатура.НайтиПоНаименованию("Материалы", Истина);

ПараметрыВыбора = Новый Структура;
ПараметрыВыбора.Вставить("РежимВыбора", Истина);
ПараметрыВыбора.Вставить("ВыборГруппИЭлементов", ИспользованиеГруппИЭлементов.Группы);
ПараметрыВыбора.Вставить("ТекущаяСтрока", ЧастаяГруппа); // Форма откроется сразу спозиционированной на этой папке

2. Обеспечение безопасности при работе в фоновых заданиях

Важно помнить, что интерактивный ввод невозможен в фоновых заданиях, регламентных операциях, а также при вызовах через Web- и HTTP-сервисы. Если ваш код создания номенклатуры универсален и может вызываться из разных мест системы, обязательно предусматривайте проверку доступности клиентского контекста или наличие переданного значения родителя по умолчанию:


Если НЕ ЗначениеЗаполнено(ВыбранныйРодитель) Тогда
    // Если родитель не передан (например, при интеграции через API),
    // подставляем предопределенную или дефолтную группу во избежание ошибок записи
    ВыбранныйРодитель = Справочники.Номенклатура.ПустаяСсылка(); 
КонецЕсли;

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

← На главную