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

Программист 1С v8.3 (Управляемые формы)
← На главную

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

Разберем типичные ошибки начинающих разработчиков, проанализируем причины неправильной работы кода и выработаем оптимальное решение с точки зрения архитектуры платформы 1С:Предприятие 8.3.

Анализ типичных ошибок в запросах

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

Ошибка №1: Отсутствие условия в запросе. Посмотрим на текст запроса из первого сообщения. Разработчик выбирает данные из всего справочника Номенклатура без каких-либо фильтров. В результате база данных возвращает список ВСЕХ элементов. Если в справочнике 10 000 позиций, запрос вернет 10 000 строк. Это создает колоссальную нагрузку на сервер.

Ошибка №2: Неправильный обход выборки. Даже если запрос возвращает данные, к ним нужно обратиться через метод Следующий(). Без вызова этого метода курсор не встает на первую запись. Кроме того, в попытках исправить код, автор использовал цикл Пока ВыборкаТовары.Следующий(), внутри которого перезаписывал значение Объект.Вид. Это привело к тому, что в поле подставлялось значение из последней строки выборки запроса, игнорируя реальную принадлежность создаваемого элемента к конкретной группе.

Решение проблемы через запрос с параметрами

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


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    
    // Проверяем, что это новый объект и у него выбран родитель
    Если Объект.Ссылка.Пустая() И Не Объект.Родитель.Пустая() Тогда
        
        Запрос = Новый Запрос;
        Запрос.Текст = 
            "ВЫБРАТЬ
            |   Номенклатура.Наименование КАК Наименование
            |ИЗ
            |   Справочник.Номенклатура КАК Номенклатура
            |ГДЕ
            |   Номенклатура.Ссылка = &СсылкаНаРодителя";
        
        Запрос.УстановитьПараметр("СсылкаНаРодителя", Объект.Родитель);
        
        РезультатЗапроса = Запрос.Выполнить();
        Выборка = РезультатЗапроса.Выбрать();
        
        Если Выборка.Следующий() Тогда
            Если Выборка.Наименование = "Товары" Тогда
                Объект.ВидНоменклатуры = ПредопределенноеЗначение("Справочник.ВидыНоменклатуры.Товар");
            ИначеЕсли Выборка.Наименование = "Услуги" Тогда
                Объект.ВидНоменклатуры = ПредопределенноеЗначение("Справочник.ВидыНоменклатуры.Услуга");
            КонецЕсли;
        КонецЕсли;
        
    КонецЕсли;
    
КонецПроцедуры

В данном примере мы используем параметр &СсылкаНаРодителя, чтобы получить данные только об одной конкретной группе. Это эффективно и безопасно для производительности системы.

Оптимальный подход: Использование события «ОбработкаЗаполнения»

Профессиональная разработка в 1С подразумевает вынос логики заполнения из модулей форм в модули объектов. Рассмотрим подробнее, почему процедура ОбработкаЗаполнения в модуле объекта справочника Номенклатура подходит для нашей задачи лучше всего.

  1. Универсальность: Код сработает при создании элемента из любого места системы (из формы списка, программно, из обработки), а не только в конкретной управляемой форме.
  2. Производительность: В параметр ДанныеЗаполнения при создании из папки автоматически передается ссылка на родителя. Нам не нужно делать лишний запрос к БД, если мы просто хотим проверить ссылку.

Проанализируем пример реализации в модуле объекта:


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

Работа с глубокой иерархией (В ИЕРАРХИИ)

Рассмотрим ситуацию, когда структура справочника сложная: например, папка «Товары» содержит в себе подпапки «Мебель», «Ткани» и так далее. Если создать товар в папке «Мебель», условие Родитель.Наименование = "Товары" не сработает, так как родителем будет «Мебель», а не «Товары».

Для решения этой проблемы мы можем использовать метод ПринадлежитЭлементу() или запрос с оператором В ИЕРАРХИИ. Посмотрим, как проверить принадлежность к ветке «Товары» через запрос:


// Предположим, у нас есть ссылка на корневую папку "Товары" (ГруппаТовары)
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   Номенклатура.Ссылка КАК Ссылка
    |ИЗ
    |   Справочник.Номенклатура КАК Номенклатура
    |ГДЕ
    |   Номенклатура.Ссылка = &ТекущийРодитель
    |   И Номенклатура.Ссылка В ИЕРАРХИИ (&ГруппаТовары)";

Запрос.УстановитьПараметр("ТекущийРодитель", Объект.Родитель);
Запрос.УстановитьПараметр("ГруппаТовары", СсылкаНаГруппуТовары);

Если Не Запрос.Выполнить().Пустой() Тогда
    // Текущая папка находится внутри ветки "Товары"
    Объект.ВидНоменклатуры = ...
КонецЕсли;

Рекомендации по улучшению кода

Чтобы сделать ваше решение более надежным (отказоустойчивым), придерживайтесь следующих правил:

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

← На главную