Как автоматически подставлять вид номенклатуры в зависимости от папки (родителя) в 1С

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

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

Анализ типичной ошибки: почему запрос — это плохое решение

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


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

Почему этот подход не работает:

  1. Отсутствие отбора: Запрос выбирает все записи из справочника. Если в базе 10 000 товаров, цикл пройдет 10 000 раз.
  2. Перезапись значения: В цикле Объект.Вид (который является единственным реквизитом текущего создаваемого объекта) постоянно перезаписывается значением из каждой строки выборки. В итоге в реквизите останется значение из самой последней строки запроса, независимо от того, в какой папке вы находитесь.
  3. Неверный контекст: Запрос к базе данных не знает, в какой папке пользователь нажал кнопку «Создать», если вы не передали это как параметр.

Решение 1: Использование модуля формы (ПриСозданииНаСервере)

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

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


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

Разберем важные моменты этого кода:

Решение 2: Профессиональный подход через ОбработкуЗаполнения

Хотя решение в форме работает, оно имеет недостаток: если номенклатура будет создаваться программно (например, загрузкой из файла), код формы не сработает. Поэтому правила «хорошего тона» в 1С диктуют перенос логики заполнения в Модуль объекта.

Выясним, как работает процедура ОбработкаЗаполнения. Она вызывается автоматически при создании нового объекта. В параметр ДанныеЗаполнения система передает ссылку на родительскую папку, если создание происходит из списка с установленным фильтром или иерархией.


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

    // Проверяем наименование родителя
    Если РодительСсылка.Наименование = "Услуги" Тогда
        ЭтотОбъект.Вид = Перечисления.ВидыНоменклатуры.Услуга;
    ИначеЕсли РодительСсылка.Наименование = "Товары" Тогда
        ЭтотОбъект.Вид = Перечисления.ВидыНоменклатуры.Товар;
    КонецЕсли;
    
КонецПроцедуры

Важные рекомендации по работе с иерархией

Проанализируем риски использования сравнения по строке (Наименование = "Услуги"). Это крайне хрупкое решение. Если завтра пользователь переименует папку в «Услуги компании» или случайно поставит лишний пробел в конце, ваш код перестанет работать. Профессионалы рекомендуют следующие альтернативы:

  1. Использование предопределенных элементов: Если структура справочника жестко задана конфигурацией, сделайте папки «Товары» и «Услуги» предопределенными. Тогда сравнение будет выглядеть так: Если Родитель = Справочники.Номенклатура.ПапкаУслуги Тогда....
  2. Поиск по коду: Коды элементов меняют реже, чем наименования. Но это тоже не идеальный вариант.
  3. Иерархическая проверка: Если у вас есть подпапки (например, «Услуги -> Транспортные»), обычное равенство Родитель.Наименование не сработает для вложенных папок. В таких случаях в запросах или коде используют проверку ПринадлежитЭлементу(ГруппаУслуг) или аналогичные методы поиска в иерархии.

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

← На главную