При работе с документами в 1С часто возникает необходимость ограничить выбор элементов справочника в табличной части — для этого подойдёт расширение для настройки обязательности и условий контроля реквизитов. Например, в одной табличной части нам нужны только услуги, а в другой – только товары, при этом все они хранятся в одном иерархическом справочнике "Номенклатура". Давайте рассмотрим, как мы можем реализовать такой отбор, используя различные подходы, опираясь на лучшие практики и рекомендации.
Нашей задачей является создание двух табличных частей в документе: "Товары" и "Услуги". При добавлении строки в табличную часть "Услуги" необходимо, чтобы в списке выбора отображались только элементы, входящие в группу "Услуги" справочника "Номенклатура". И наоборот, при добавлении строки в табличную часть "Товары", в списке выбора должны быть доступны все элементы, кроме тех, что относятся к группе "Услуги". По сути, нам нужен отбор по родителю или по другому классифицирующему признаку — задача решается через инструмент централизованного управления логикой заполнения и контроля реквизитов.
Этот подход является наиболее очевидным, когда номенклатура уже классифицирована по иерархии групп — для удобства работы с такими списками есть подбор номенклатуры с дополнительными фильтрами и остатками. Мы будем использовать поле Родитель справочника Номенклатура для формирования отбора.
Прежде чем установить отбор, нам потребуется ссылка на группу "Услуги". Есть несколько способов получить эту ссылку:
ГруппаУслуги = Справочники.Номенклатура.НайтиПоНаименованию("Услуги", Истина); // Истина - поиск по группам
// Или
// ГруппаУслуги = Справочники.Номенклатура.НайтиПоКоду("000000001");
ГруппаНоменклатурыУслуги с типом СправочникСсылка.Номенклатура. Пользователь сам выбирает нужную группу в режиме Предприятия, и ссылка на неё хранится в константе. Это повышает гибкость, так как при изменении группы не нужно менять код, достаточно обновить константу.
ГруппаУслуги = Константы.ГруппаНоменклатурыУслуги.Получить();
Для установки отбора при выборе элемента в табличной части документа мы будем обрабатывать событие ОбработкаВыбора поля ввода, которое привязано к колонке с номенклатурой — это удобнее делать через инструмент для быстрого подбора по частям наименования и статусам. Это событие вызывается перед открытием стандартной формы выбора.
Предположим, у нас есть документ, и в его модуле формы мы хотим настроить отбор для колонки "Номенклатура" в табличных частях "Товары" и "Услуги".
Сначала найдем нужную группу. В данном примере будем использовать константу ГруппаНоменклатурыУслуги.
&НаКлиенте
Процедура УслугиНоменклатураОбработкаВыбора(Элемент, ВыбранноеЗначение, СтандартнаяОбработка)
// Отменяем стандартную обработку, чтобы вручную открыть форму выбора
СтандартнаяОбработка = Ложь;
// Получаем ссылку на группу "Услуги" из константы
// Предполагаем, что константа Константы.ГруппаНоменклатурыУслуги уже создана и заполнена
ГруппаУслуги = Константы.ГруппаНоменклатурыУслуги.Получить();
// Проверяем, что группа найдена
Если НЕ ГруппаУслуги.Пустая() Тогда
// Создаем объект Отбор для формы выбора
Отбор = Новый ОтборКомпоновкиДанных;
// Создаем элемент отбора по полю "Родитель"
ЭлементОтбора = Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Родитель");
ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
ЭлементОтбора.ПравоеЗначение = ГруппаУслуги;
ЭлементОтбора.Использование = Истина;
// Открываем форму выбора справочника "Номенклатура" с заданным отбором
ФормаВыбора = Справочники.Номенклатура.ПолучитьФормуВыбора(,,Элемент);
ФормаВыбора.Отбор = Отбор;
ФормаВыбора.Открыть();
Иначе
ПоказатьПредупреждение(,"Не заполнена константа 'Группа номенклатуры Услуги'!");
КонецЕсли;
КонецПроцедуры
Для табличной части "Товары" нам нужно отобрать все, кроме элементов из группы "Услуги". Здесь мы используем вид сравнения НеРавно.
&НаКлиенте
Процедура ТоварыНоменклатураОбработкаВыбора(Элемент, ВыбранноеЗначение, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
ГруппаУслуги = Константы.ГруппаНоменклатурыУслуги.Получить();
Если НЕ ГруппаУслуги.Пустая() Тогда
Отбор = Новый ОтборКомпоновкиДанных;
ЭлементОтбора = Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Родитель");
ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.НеРавно; // Отбираем все, кроме этой группы
ЭлементОтбора.ПравоеЗначение = ГруппаУслуги;
ЭлементОтбора.Использование = Истина;
ФормаВыбора = Справочники.Номенклатура.ПолучитьФормуВыбора(,,Элемент);
ФормаВыбора.Отбор = Отбор;
ФормаВыбора.Открыть();
Иначе
ПоказатьПредупреждение(,"Не заполнена константа 'Группа номенклатуры Услуги'!");
КонецЕсли;
КонецПроцедуры
Важный момент: Отбор по родителю с видом сравнения "НеРавно" может не полностью удовлетворять условию "все, кроме тех, которые находятся в группе 'Услуги'". Если иерархия многоуровневая, и "Услуги" имеют вложенные подгруппы, отбор по Родитель НеРавно ГруппаУслуги исключит только непосредственных потомков этой группы, но не её подгруппы или элементы, находящиеся глубоко в иерархии "Услуги". Для более точного исключения всей ветки иерархии, возможно, потребуется более сложный запрос или использование дополнительного реквизита, как мы рассмотрим далее.
Этот подход считается более надежным и гибким, часто используемым в типовых конфигурациях 1С. Вместо того чтобы полагаться исключительно на иерархию (которая может меняться, и элементы могут быть ошибочно помещены не в ту папку), мы вводим явный классификационный признак.
Мы можем добавить в справочник Номенклатура новый реквизит ВидНоменклатуры. Тип этого реквизита может быть:
ВидыНоменклатуры со значениями, например, "Товар", "Услуга", "Материал" и т.д. Это просто и эффективно для небольшого количества фиксированных видов.ВидыНоменклатуры, в котором будут храниться элементы "Товар", "Услуга" и т.д. Можно сделать его иерархическим или использовать предопределенные элементы. Этот вариант более гибкий, если список видов часто меняется или требует дополнительных свойств.Давайте рассмотрим примеры для обоих вариантов.
Предположим, у нас есть перечисление ВидыНоменклатуры со значением Услуга. В справочнике Номенклатура добавлен реквизит ВидНоменклатуры с типом ПеречислениеСсылка.ВидыНоменклатуры.
&НаКлиенте
Процедура УслугиНоменклатураОбработкаВыбора(Элемент, ВыбранноеЗначение, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
Отбор = Новый ОтборКомпоновкиДанных;
ЭлементОтбора = Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("ВидНоменклатуры"); // Имя нашего реквизита
ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
ЭлементОтбора.ПравоеЗначение = Перечисления.ВидыНоменклатуры.Услуга; // Значение перечисления
ЭлементОтбора.Использование = Истина;
ФормаВыбора = Справочники.Номенклатура.ПолучитьФормуВыбора(,,Элемент);
ФормаВыбора.Отбор = Отбор;
ФормаВыбора.Открыть();
КонецПроцедуры
Предположим, у нас есть справочник ВидыНоменклатуры с предопределенным элементом "Услуга". В справочнике Номенклатура добавлен реквизит ВидНоменклатуры с типом СправочникСсылка.ВидыНоменклатуры.
&НаКлиенте
Процедура ТоварыНоменклатураОбработкаВыбора(Элемент, ВыбранноеЗначение, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
// Получаем ссылку на предопределенный элемент справочника
ВидУслуга = Справочники.ВидыНоменклатуры.Услуга;
Отбор = Новый ОтборКомпоновкиДанных;
ЭлементОтбора = Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("ВидНоменклатуры");
ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.НеРавно; // Отбираем все, кроме услуг
ЭлементОтбора.ПравоеЗначение = ВидУслуга;
ЭлементОтбора.Использование = Истина;
ФормаВыбора = Справочники.Номенклатура.ПолучитьФормуВыбора(,,Элемент);
ФормаВыбора.Отбор = Отбор;
ФормаВыбора.Открыть();
КонецПроцедуры
Что делать, если нам нужно отобрать номенклатуру, которая относится к нескольким видам (например, "Товар" и "Материал"), но исключить "Услугу"? Или, как в сообщении 9 и 10, отобрать по двум видам перечисления? Для этого используем СписокЗначений или ФиксированныйМассив.
Предположим, нам нужно в ТЧ "Товары" видеть только "Товары" и "Материалы".
&НаКлиенте
Процедура ТоварыНоменклатураОбработкаВыбора(Элемент, ВыбранноеЗначение, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
// Создаем список значений для отбора
СписокРазрешенныхВидов = Новый СписокЗначений;
СписокРазрешенныхВидов.Добавить(Перечисления.ВидыНоменклатуры.Товар);
СписокРазрешенныхВидов.Добавить(Перечисления.ВидыНоменклатуры.Материал);
// Для справочника "ВидыНоменклатуры" это будет:
// СписокРазрешенныхВидов.Добавить(Справочники.ВидыНоменклатуры.Товар);
// СписокРазрешенныхВидов.Добавить(Справочники.ВидыНоменклатуры.Материал);
Отбор = Новый ОтборКомпоновкиДанных;
ЭлементОтбора = Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбора.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("ВидНоменклатуры");
ЭлементОтбора.ВидСравнения = ВидСравненияКомпоновкиДанных.ВСписке; // Указываем, что значение должно быть в списке
ЭлементОтбора.ПравоеЗначение = СписокРазрешенныхВидов; // Передаем список
ЭлементОтбора.Использование = Истина;
ФормаВыбора = Справочники.Номенклатура.ПолучитьФормуВыбора(,,Элемент);
ФормаВыбора.Отбор = Отбор;
ФормаВыбора.Открыть();
КонецПроцедуры
Автор сообщения 12 упоминал, что решил проблему через "фиксированный массив". Это как раз относится к подобной ситуации, когда нужно передать набор значений для отбора. ФиксированныйМассив — это оптимизированный для производительности вариант массива, который часто используется в программных отборах.
Для закрепления понимания, давайте еще раз выделим ключевые моменты, которые мы использовали для установки отбора в форме выбора:
ОбработкаВыбора: Это клиентское событие элемента формы (например, поля ввода в колонке табличной части), которое вызывается непосредственно перед открытием стандартной формы выбора. Это идеальное место для вмешательства в процесс выбора.СтандартнаяОбработка = Ложь: Внутри события ОбработкаВыбора мы обязательно устанавливаем параметр СтандартнаяОбработка в значение Ложь. Это предотвращает автоматическое открытие формы выбора без наших параметров.ОтборКомпоновкиДанных: Мы создаем новый объект ОтборКомпоновкиДанных. Он позволяет программно сформировать структуру отбора, аналогичную той, что используется в отчетах или динамических списках.Отбор.Элементы мы добавляем объекты ЭлементОтбораКомпоновкиДанных. Каждый такой элемент описывает одно условие отбора:
ЛевоеЗначение: Указывает на поле справочника, по которому будет производиться отбор (например, Новый ПолеКомпоновкиДанных("Родитель") или Новый ПолеКомпоновкиДанных("ВидНоменклатуры")).ВидСравнения: Определяет тип сравнения (например, ВидСравненияКомпоновкиДанных.Равно, .НеРавно, .ВСписке).ПравоеЗначение: Указывает значение или список значений, с которым сравнивается поле.Использование = Истина: Активирует элемент отбора.Отбор, мы получаем стандартную форму выбора справочника (например, Справочники.Номенклатура.ПолучитьФормуВыбора(,,Элемент)), передаем ей наш объект Отбор и открываем форму методом ФормаВыбора.Открыть().Хотя отбор по родителю кажется прямым и простым решением, мы выяснили, что он имеет свои ограничения, особенно при сложных иерархиях или необходимости исключить целую ветку. Смена группы элемента также может нарушить логику отбора — в таких случаях поможет автоматизация контроля ввода данных и настройки форм выбора.
Мы настоятельно рекомендуем использовать подход с дополнительным реквизитом ВидНоменклатуры (будь то Перечисление или Справочник). Этот метод обеспечивает большую гибкость, надежность и устойчивость к изменениям в иерархии справочника. Он соответствует принципам построения типовых конфигураций 1С, как было справедливо отмечено в сообщении 8.
Начните с добавления реквизита ВидНоменклатуры в ваш справочник Номенклатура, заполните его для существующих элементов и затем реализуйте отбор в событиях ОбработкаВыбора, используя ОтборКомпоновкиДанных. При необходимости отбора по нескольким значениям, используйте СписокЗначений или ФиксированныйМассив с ВидСравненияКомпоновкиДанных.ВСписке.
Таким образом, мы не только решили поставленную задачу, но и применили более масштабируемый и надежный подход, который пригодится вам в дальнейшей работе с платформой 1С.