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

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

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

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

Проблема разделения контекста: Клиент и Сервер

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

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

Решение 1: Использование временных таблиц в запросе

Один из самых надежных и производительных способов — передать текущие данные табличной части на сервер и обработать их одним запросом. Проанализируем этот алгоритм по шагам:

  1. Подготовка данных на клиенте: В обработчике ПередЗаписью (или в вызванной из него процедуре) мы передаем коллекцию Объект.Товары на сервер.
  2. Создание временной таблицы: На сервере мы выгружаем коллекцию в таблицу значений.
  3. Выполнение запроса: Помещаем таблицу значений во временную таблицу запроса. Это позволяет нам соединить текущие (еще не записанные) данные формы с данными из справочников или регистров базы данных.
  4. Обновление данных: Результат запроса мы используем для заполнения недостающих реквизитов (например, Характеристик) в объекте или данных формы.

Пример реализации на сервере:


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

Запрос.УстановитьПараметр("ТаблицаТоваров", ТаблицаТоваров);
Результат = Запрос.Выполнить().Выгрузить();

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

Решение 2: Перенос логики в серверные модули

Для того чтобы поля пересчитались корректно (как если бы пользователь сам изменил характеристику), не нужно пытаться вызвать обработчик ТоварыХарактеристикаПриИзменении. В современных типовых конфигурациях (ERP 2.5, УТ 11.5) вся бизнес-логика вынесена в специализированные общие серверные модули.

Проанализируем ситуацию: процедура ТоварыХарактеристикаПриИзменении на клиенте обычно сама вызывает серверную процедуру для расчета цен. Нам достаточно найти этот серверный аналог. Часто это процедуры в модулях ОбработкаТабличныхЧастейСервер, НоменклатураСервер или специализированные процедуры в модуле менеджера документа.

Вместо эмуляции действий пользователя на форме, выполним следующие действия:

  1. Найдем в модуле формы, какой серверный метод вызывается при изменении характеристики.
  2. Вызовем этот же метод напрямую в нашем серверном коде после программной установки значения.
  3. Если используется типовое решение, посмотрим на использование методов типа РассчитатьЦеныИСуммы().

Решение 3: Метод ДанныеФормыВЗначение

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


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

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

Оптимизация производительности и "грязные чтения"

Важно помнить о нюансах работы с базой данных внутри транзакции записи. Рассмотрим несколько критических моментов:

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

← На главную