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

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

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

Почему нельзя использовать форму для массового заполнения

Рассмотрим ситуацию, когда разработчик пытается получить форму документа через ПолучитьФорму(), программно заполнить её табличную часть и вызвать метод Форма.Записать(). Проанализируем, почему этот путь является тупиковым:

Вывод: Для групповой обработки документов всегда используйте объектную модель (ПолучитьОбъект()), работая напрямую с данными в базе.

Разбор типичной ошибки: «Остается только последняя строка»

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


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

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

Правильный алгоритм перезаполнения через объект

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

Шаг 1. Оптимизация запроса

Если мы выбираем данные напрямую из табличной части, запрос вернет столько строк, сколько их в документах. Чтобы не вызывать ПолучитьОбъект() десять раз для документа, в котором десять строк, применим группировку или обход по группировкам.


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   Перемещение.Ссылка КАК Ссылка,
    |   Перемещение.Номенклатура КАК Номенклатура,
    |   Перемещение.Количество КАК Количество
    |ИЗ
    |   Документ.ПеремещениеТМЦ.Товары КАК Перемещение
    |ГДЕ
    |   Перемещение.Ссылка.Дата МЕЖДУ &ДатаНач И &ДатаКон
    |ИТОГИ ПО
    |   Ссылка"; // Группируем по документу

Запрос.УстановитьПараметр("ДатаНач", ДатаНач);
Запрос.УстановитьПараметр("ДатаКон", ДатаКон);
Результат = Запрос.Выполнить();

Шаг 2. Обход результата и запись объекта

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


ВыборкаДокументов = Результат.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам);

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

Дополнительные методы ускорения обработки

Когда документов тысячи, стандартная запись может занять значительное время. Проанализируем инструменты, которые предоставляет платформа для ускорения этого процесса.

1. Использование ОбменДанными.Загрузка

Перед вызовом метода Записать() установите свойство ДокОбъект.ОбменДанными.Загрузка = Истина;. Это отключит выполнение тяжелых проверок в модуле объекта и подписки на события (например, контроль остатков, проверку прав доступа к регистрам). Это безопасно, если вы уверены в корректности записываемых данных.

2. Использование транзакций

Оберните цикл обработки в транзакцию. Это сократит количество обращений к диску для фиксации изменений СУБД. Рекомендуется фиксировать транзакцию порциями (например, по 200 документов), чтобы избежать переполнения лога транзакций.


НачатьТранзакцию();
// ... цикл обработки документов ...
ЗафиксироватьТранзакцию();

3. Метод Загрузить()

Если структура таблицы значений (ТЗ) полностью совпадает с колонками табличной части, можно использовать метод ДокОбъект.Товары.Загрузить(ВашаТаблицаЗначений);. Это работает на уровне платформы значительно быстрее, чем перебор строк в цикле Для Каждого.

Резюме

Проанализировав ситуацию, мы выяснили, что для успешного перезаполнения ТЧ необходимо:

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

Соблюдение этих правил гарантирует стабильную работу системы и целостность данных в информационной базе.

← На главную