Как вывести дополнительные реквизиты номенклатуры во вторую таблицу формы при активизации строки

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

В процессе разработки интерфейсов на платформе «1С:Предприятие 8.3» часто возникает задача организации связанных списков по принципу «главный — подчиненный» (Master-Detail). Типичный пример такой задачи — отображение списка номенклатуры в одной таблице, а перечня её дополнительных реквизитов и их конкретных значений — во второй таблице на той же форме. При этом состав и значения реквизитов должны динамически обновляться при перемещении курсора по списку товаров.

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

Анализ типичных ошибок и заблуждений

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

Ошибка 1. Попытка работы с базой данных на клиенте. Часто начинающие разработчики пытаются обратиться к менеджерам объектов или планам видов характеристик непосредственно в клиентских процедурах:


&НаКлиенте
Процедура ВывестиДопРеквизиты()
    ТекущаяСтрока = Элементы.ТаблицаНоменклатуры.ТекущиеДанные;
    // Ошибка! ПВХ недоступен на клиенте
    ДополнительныеРеквизиты = ПланыВидовХарактеристик.Получить(ТекущаяСтрока.Номенклатура.Категория); 
КонецПроцедуры

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

Ошибка 2. Использование no-code связывания через категории. Предложение связать путь к данным подчиненной таблицы напрямую через свойства категории (например, указав путь вида Элементы.Таблица1.ТекущиеДанные.Номенклатура.Категория.ДополнительныеРеквизиты) содержит две серьезные проблемы:

  1. Различие в структуре данных. В табличной части категории номенклатуры хранятся только *настройки* (какие именно свойства должны быть у номенклатуры этой категории). Самих *значений* (например, конкретного цвета или размера конкретного товара) там нет. Значения свойств хранятся непосредственно в табличной части самой номенклатуры либо в специальном регистре сведений.
  2. Неявные вызовы сервера. Обращение к свойствам через несколько точек (номенклатура -> категория -> табличная часть) на клиенте заставляет платформу выполнять скрытые синхронные серверные вызовы. Если пользователь начнет быстро прокручивать список клавиатурой, интерфейс приложения зависнет.

---

Способ 1. Прямой серверный запрос (Классический вариант)

Рассмотрим универсальный способ, который будет стабильно работать в любой конфигурации 1С. Суть метода заключается в том, что при активизации строки номенклатуры мы передаем ссылку на выбранный товар на сервер, выполняем запрос к его табличной части дополнительных реквизитов и загружаем результат в реквизит формы (ТаблицуЗначений).

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

  1. Создадим на форме реквизит с типом ТаблицаЗначений (назовем его ТаблицаСвойств).
  2. Добавим в эту таблицу две колонки: Свойство (тип ПланВидовХарактеристикСсылка.ДополнительныеРеквизитыИСведения) и Значение (тип Характеристика.ДополнительныеРеквизитыИСведения).
  3. Вынесем эту таблицу на форму в качестве подчиненного элемента интерфейса.

Теперь напишем обработчики событий в модуле управляемой формы:


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

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

Этот способ работает максимально прозрачно и производительно за счет использования метода Загрузить(), который мгновенно заполняет таблицу на форме без построчного обхода коллекции.

---

Способ 2. Использование готовых механизмов БСП

Если ваша внешняя обработка или форма разрабатывается для типовой конфигурации (например, 1С:ERP, Управление торговлей 11, Комплексная автоматизация 2, УНФ), то изобретать велосипед с написанием прямых SQL-подобных запросов не обязательно. В таких системах развернута БСП (Библиотека стандартных подсистем), в которой есть готовый программный интерфейс для работы со свойствами.

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

Заменим серверный метод из предыдущего примера на интеграционный вызов функции БСП:


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

Применение метода УправлениеСвойствами.ПолучитьЗначенияСвойств() гарантирует, что ваша обработка будет вести себя стандартно по отношению к остальным объектам конфигурации.

---

Важная оптимизация: защита от зависаний интерфейса (Debounce-эффект)

Проанализируем сценарий работы пользователя: он открывает список номенклатуры и зажимает клавишу "Вниз" на клавиатуре, чтобы быстро пролистать справочник в поисках нужной позиции.

Событие ПриАктивизацииСтроки срабатывает на каждый шаг перемещения курсора. Если в обработчике этого события прописан прямой серверный вызов (как в Способе 1 и 2), программа пошлет десятки запросов к серверу за одну секунду. База данных не будет успевать отдавать ответы, интерфейс у пользователя начнет дергаться, а сервер приложений получит паразитную нагрузку.

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

  1. При активизации строки мы не бежим сразу на сервер.
  2. Мы взводим таймер (обработчик ожидания) на короткий промежуток времени — например, 0.3 секунды.
  3. Если пользователь в течение этих 0.3 секунд перешел на следующую строку, мы сбрасываем старый таймер и взводим новый.
  4. Серверный вызов произойдет только тогда, когда пользователь остановит фокус клавиатуры на конкретной строке дольше чем на 0.3 секунды.

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


&НаКлиенте
Процедура СписокНоменклатурыПриАктивизацииСтроки(Элемент)
    
    // Шаг 1. Отключаем ранее запущенный обработчик ожидания, если он был активен
    ОтключитьОбработчикОжидания("Подключаемый_ЗаполнитьДопРеквизиты");
    
    // Шаг 2. Подключаем обработчик ожидания заново с минимальным интервалом времени (0.3 сек)
    // Параметр "Однократно" устанавливаем в Истина, чтобы процедура не зацикливалась
    ПодключитьОбработчикОжидания("Подключаемый_ЗаполнитьДопРеквизиты", 0.3, Истина);
    
КонецПроцедуры

&НаКлиенте
Процедура Подключаемый_ЗаполнитьДопРеквизиты()
    
    // Шаг 3. Получаем текущие данные из таблицы формы
    ТекущиеДанные = Элементы.СписокНоменклатуры.ТекущиеДанные;
    
    Если ТекущиеДанные <> Неопределено Тогда
        // Только в этот момент происходит реальное обращение к серверу
        ЗаполнитьДопРеквизитыНаСервере(ТекущиеДанные.Номенклатура);
    Иначе
        Объект.ТаблицаСвойств.Очистить();
    КонецЕсли;
    
КонецПроцедуры

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

Выбирайте наиболее подходящий для вашей конфигурации метод (прямой запрос для кастомных структур или вызовы БСП для типовых решений) и обязательно применяйте оптимизацию через обработчик ожидания для обеспечения максимального комфорта работы пользователей.

← На главную