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

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

При разработке документов в 1С:Предприятии 8.3 одной из самых частых задач является автоматическое заполнение дополнительных реквизитов строки табличной части (например, артикула, единицы измерения, ставки НДС или цены) сразу после того, как пользователь выбрал номенклатуру. На первый взгляд задача кажется простой, но при неправильном подходе она может привести к существенным задержкам в работе интерфейса, особенно в условиях медленного интернет-соединения или при большом количестве строк в документе. Разберем наиболее эффективные способы решения этой задачи, проанализируем их плюсы и минусы, а также выясним, почему некоторые привычные методы считаются «плохим тоном» в разработке.

Проблема обращения «через точку»

Прежде чем перейти к оптимальным решениям, проанализируем типичную ошибку начинающих разработчиков. Часто можно встретить код вида ТекущаяСтрока.Артикул = ТекущаяСтрока.Номенклатура.Артикул;. На первый взгляд это удобно, но с точки зрения производительности это крайне неэффективно.

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

Решение 1: Использование функций БСП (Библиотеки стандартных подсистем)

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

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


&НаКлиенте
Процедура ТоварыНоменклатураПриИзменении(Элемент)
    ТекущаяСтрока = Элементы.Товары.ТекущиеДанные;
    Если ТекущаяСтрока = Неопределено Тогда
        Возврат;
    КонецЕсли;

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

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

Почему это эффективно? Функция БСП внутри себя формирует прямой SQL-запрос, который выбирает исключительно перечисленные поля (Артикул, ЕдиницаИзмерения, СтавкаНДС). Это происходит мгновенно и не нагружает систему лишними данными. Использование &НаСервереБезКонтекста дополнительно ускоряет процесс, так как на сервер не передается состояние всей формы (контекст).

Решение 2: Массовое заполнение через запрос

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

В такой ситуации мы применим подход с использованием Запроса и временных таблиц. Посмотрим на алгоритм:

  1. Выгружаем колонку с номенклатурой из табличной части в массив.
  2. Передаем массив на сервер.
  3. Выполняем запрос к справочнику Номенклатура с фильтром по массиву (ГДЕ Ссылка В (&Массив)).
  4. Получаем результат и обходим его, заполняя строки документа.

Пример реализации для обработки всей таблицы:


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

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

Решение 3: Кэширование данных на клиенте

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

Создадим в модуле формы переменную КэшНоменклатуры. При выборе товара сначала заглянем в кэш, и только если его там нет — отправимся на сервер.


&НаКлиенте
Перем КэшНоменклатуры; // Используем переменную модуля

&НаКлиенте
Процедура ПриОткрытии(Отказ)
    КэшНоменклатуры = Новый Соответствие;
КонецПроцедуры

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

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

Дополнительные советы по оптимизации

Выясним еще несколько важных нюансов, которые помогут сделать код профессиональным:

Подводя итог, можно сказать: для одиночного ввода используйте &НаСервереБезКонтекста и функции БСП ЗначенияРеквизитовОбъекта. Для массовой обработки — один Запрос с передачей массива ссылок. Эти методы гарантируют высокую скорость работы интерфейса и минимальную нагрузку на серверную инфраструктуру 1С.

← На главную