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

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

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

Разбор типичной ошибки: почему данные не записываются

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

В чем заключается ошибка? Метод Выгрузить() создает в оперативной памяти полную копию данных табличной части в виде объекта ТаблицаЗначений. Эта копия никак не связана с исходным объектом документа «живой» связью. Изменяя строки в таблице значений, мы меняем данные только в этой временной таблице, а табличная часть самого документа остается прежней. Поэтому при вызове метода Записать() платформа сохраняет старое состояние документа.

Рассмотрим, как выглядит ошибочный подход:


// ОШИБОЧНЫЙ ВАРИАНТ
ТекОбъект = СсылкаНаДокумент.ПолучитьОбъект();
ТЗ = ТекОбъект.Товары.Выгрузить(); // Создается независимая копия данных

Для Каждого СтрокаТЗ Из ТЗ Цикл
    СтрокаТЗ.Количество = 10; // Меняем данные в копии
КонецЦикла;

ТекОбъект.Записать(); // В документе ничего не изменилось!

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

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

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

Проанализируем исправленный код:


Процедура ОбновитьДанныеВЗаказе(СсылкаНаЗаказ, НоменклатураПоиска, НовоеКолВып)
    
    // Проверяем, заполнена ли ссылка, чтобы избежать ошибки
    Если Не ЗначениеЗаполнено(СсылкаНаЗаказ) Тогда
        Возврат;
    КонецЕсли;

    // Получаем объект документа для редактирования
    ОбъектЗаказа = СсылкаНаЗаказ.ПолучитьОбъект();
    ИзмененияВнесены = Ложь;

    // Работаем напрямую с табличной частью документа
    Для каждого СтрокаТовары Из ОбъектЗаказа.Товары Цикл
        
        // Сравниваем номенклатуру
        Если СтрокаТовары.Номенклатура = НоменклатураПоиска Тогда
            
            // Меняем статус и количество прямо в объекте
            СтрокаТовары.Состояние = Перечисления.СтатусыЗаказа.ВПроизводстве;
            СтрокаТовары.КолВып = НовоеКолВып;
            
            ИзмененияВнесены = Истина;
            
        КонецЕсли;
        
    КонецЦикла;

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

КонецПроцедуры

Оптимизация поиска в табличной части

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

Использование метода НайтиСтроки(). Вместо того чтобы перебирать все строки и проверять условие через Если, мы можем передать структуру поиска. Это работает значительно быстрее, так как поиск оптимизирован на уровне платформы.


Отбор = Новый Структура;
Отбор.Вставить("Номенклатура", ТекНоменклатура);

НайденныеСтроки = ОбъектЗаказа.Товары.НайтиСтроки(Отбор);

Для каждого Строка Из НайденныеСтроки Цикл
    Строка.КолВып = ТекКолВып;
    Строка.Состояние = Перечисления.СтатусыЗаказа.Выполнено;
КонецЦикла;

Этот метод вернет массив строк, соответствующих условию. Если номенклатура в документе уникальна, в массиве будет всего один элемент. Если она встречается несколько раз — мы обновим все вхождения.

Важные нюансы записи и проведения

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

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

Поэтому рекомендуется придерживаться следующей логики:

Производительность и вынос метода «Записать»

Рассмотрим еще одну критическую ошибку производительности. Никогда не вызывайте метод Записать() внутри цикла по строкам. Каждая запись документа — это тяжелая транзакция в базе данных, которая блокирует таблицы и создает нагрузку на сервер.

Правило: Сначала соберите все необходимые изменения в объекте, находясь в памяти, и только после завершения всех циклов и проверок вызовите метод Записать() один раз.

Дополнительные рекомендации по безопасности данных

При программном изменении объектов следует учитывать еще несколько аспектов, которые помогут сделать ваш код профессиональным и отказоустойчивым:

  1. Проверка заполнения ссылок. Всегда проверяйте ЗначениеЗаполнено(Ссылка) перед вызовом ПолучитьОбъект(). Попытка получить объект от пустой ссылки вызовет фатальную ошибку исполнения.
  2. Блокировка данных. В высоконагруженных системах, пока ваш код меняет документ, другой пользователь может попытаться отредактировать его вручную. Чтобы избежать конфликтов, стоит изучить механизм БлокировкаДанных.
  3. Обновление открытых форм. Если вы программно изменили документ «Заказ», а в этот момент у пользователя на экране открыта форма этого заказа, он не увидит изменений, пока не переоткроет документ. Чтобы оповестить интерфейс об изменениях, используйте метод ОповеститьОбИзменении(Ссылка) после успешной записи.

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

← На главную