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

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

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

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

Разбор архитектуры данных

Прежде чем переходить к коду, проанализируем структуру метаданных. У нас есть справочник Принтеры, который подчинен справочнику Картриджи. Это означает, что у каждого принтера есть реквизит Владелец, ссылающийся на конкретный картридж. Реквизит СрокЭксплуатации хранится в справочнике-владельце (картридже).

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

Вариант 1: Заполнение строки через событие ПриИзменении

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

Рассмотрим пошагово, как это реализовать в модуле формы документа:

  1. Создаем на форме событие ПринтерПриИзменении для соответствующего поля табличной части.
  2. В обработчике получаем текущую строку.
  3. Вызываем серверную функцию, которая вернет нам структуру со всеми необходимыми данными.

&НаКлиенте
Процедура ТЧПринтерПриИзменении(Элемент)
    
    ТекущаяСтрока = Элементы.ТЧ.ТекущиеДанные;
    
    Если ТекущаяСтрока = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    // Если принтер очистили, очищаем и зависимые поля
    Если ТекущаяСтрока.Принтер.Пустая() Тогда
        ТекущаяСтрока.Картридж = ПредопределенноеЗначение("Справочник.Картриджи.ПустаяСсылка");
        ТекущаяСтрока.СрокЭксплуатации = 0;
        Возврат;
    КонецЕсли;
    
    // Получаем данные с сервера
    ДанныеДляЗаполнения = ПолучитьДанныеПринтераНаСервере(ТекущаяСтрока.Принтер);
    
    // Заполняем реквизиты строки
    ЗаполнитьЗначенияСвойств(ТекущаяСтрока, ДанныеДляЗаполнения);
    
    // Рассчитываем специфические поля, которые зависят от текущего времени
    ТекущаяСтрока.Количество = 1;
    СекундВСутках = 24 * 60 * 60;
    ТекущаяСтрока.ДатаСледОбслуживания = ТекущаяДата() + (ТекущаяСтрока.СрокЭксплуатации * СекундВСутках);
    
КонецПроцедуры

Оптимизация: получение данных через запрос

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

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


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

Обратите внимание на использование функции ЗаполнитьЗначенияСвойств. Она автоматически сопоставляет имена полей из выборки запроса с ключами структуры. Это делает код чище и удобнее для поддержки.

Вариант 2: Массовое заполнение всей табличной части

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

Рассмотрим алгоритм массового заполнения:

  1. Выгружаем все ссылки на принтеры из табличной части в массив.
  2. Передаем массив на сервер.
  3. Одним запросом получаем данные для всех принтеров сразу.
  4. Возвращаем таблицу значений или массив структур и распределяем данные по строкам.

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

Нюансы работы с датами и периодами

В обсуждении возник важный вопрос: как правильно рассчитать ДатаСледОбслуживания. В 1С прибавление числа к дате означает прибавление секунд. Поэтому формула Дата + (КоличествоДней * 24 * 60 * 60) является классической.

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

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


ВЫБРАТЬ
    ТехОбслуживаниеСрезПоследних.ДатаСледОбслуживания КАК ДатаИзРегистра
ИЗ
    РегистрСведений.ТехОбслуживание.СрезПоследних(&Период, Принтер = &Принтер) КАК ТехОбслуживаниеСрезПоследних

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

Практические советы по оптимизации

Разберем типичную ошибку: попытку вернуть РезультатЗапроса или Выборку напрямую с сервера на клиент. Объекты типа "Запрос", "Выборка" и "РезультатЗапроса" не могут существовать на клиенте. Поэтому мы всегда выгружаем данные либо в Структуру (для одной строки), либо в Массив структур / Таблицу значений (для группы строк).

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

← На главную