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

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

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

Почему ТекущиеДанные возвращает Неопределено при обращении к форме

Проанализируем распространенную ошибку: при попытке получить доступ к табличной части документа разработчик получает экземпляр формы и обращается к свойству ТекущиеДанные табличного поля формы.

Рассмотрим, почему такой код не работает:

  1. Метод ПолучитьФорму() лишь инициализирует экземпляр формы в оперативной памяти, но не выполняет ее интерактивное открытие на экране. В не отображаемом окне физически отсутствует пользовательский фокус ввода, активный курсор и выделенная строка, поэтому свойство ТекущиеДанные гарантированно возвращает Неопределено.
  2. Даже если форма документа уже открыта у пользователя на рабочем столе, повторный вызов ПолучитьФорму() без указания уникального ключа может создать отдельный независимый экземпляр формы, в котором выделение строки также отсутствует.
  3. Свойство ТекущиеДанные предназначено исключительно для интерактивной работы и возвращает только одну строку, на которой в текущий момент спозиционирован курсор пользователя, а не все данные табличной части.

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

Правильный подход: получение объекта из ссылки

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

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

ДокументОбъект = ВыборДокумента.ПолучитьОбъект();

После вызова этого метода разработчик получает полный доступ к коллекции строк табличной части (в нашем случае Товары) и может применить один из способов удаления строк.

Способ 1. Удаление строк с помощью метода НайтиСтроки

Когда требуется удалить строки по конкретному значению (например, по определенной номенклатуре), наиболее удобным и лаконичным решением является использование встроенного метода НайтиСтроки(). Метод Удалить() табличной части платформы 1С поддерживает передачу не только числового индекса, но и ссылки на саму строку коллекции.

Разберем реализацию этого способа по шагам:

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

Посмотрим на пример программного кода:


ДокументОбъект = ВыборДокумента.ПолучитьОбъект();

Отбор = Новый Структура("Номенклатура", ИскомаяНоменклатура);
НайденныеСтроки = ДокументОбъект.Товары.НайтиСтроки(Отбор);

Для Каждого СтрокаТЧ Из НайденныеСтроки Цикл
    ДокументОбъект.Товары.Удалить(СтрокаТЧ);
КонецЦикла;

ДокументОбъект.Записать(РежимЗаписиДокумента.Запись);

Способ 2. Удаление строк в цикле с конца коллекции (обратный цикл)

Если условие удаления строк является составным или требует сложного вычисления, разработчики часто используют перебор строк в цикле. Здесь кроется критическая ошибка: прямой цикл (например, Для Каждого или Для Индекс = 0 По Количество - 1) при удалении элементов приводит к сдвигу индексов оставшихся строк. В результате следующая за удаленной строка пропускается, а при обращении к последним индексам возникает ошибка выхода за границы массива.

Чтобы обойти эту проблему, применим обратный цикл — удаление элементов с конца коллекции к началу. При таком подходе удаление текущей строки не влияет на числовые индексы тех строк, которые стоят перед ней.

Рассмотрим пример реализации обратного цикла:


ДокументОбъект = ВыборДокумента.ПолучитьОбъект();

КоличествоСтрок = ДокументОбъект.Товары.Количество();

Для Индекс = -(КоличествоСтрок - 1) По 0 Цикл
    НомерСтроки = -Индекс;
    СтрокаТЧ = ДокументОбъект.Товары[НомерСтроки];
    
    Если СтрокаТЧ.Номенклатура = ИскомаяНоменклатура Тогда
        ДокументОбъект.Товары.Удалить(НомерСтроки);
    КонецЕсли;
КонецЦикла;

ДокументОбъект.Записать(РежимЗаписиДокумента.Запись);

Способ 3. Выгрузка в таблицу значений и обратная загрузка

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

Разберем последовательность действий:

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

Посмотрим на пример кода:


ДокументОбъект = ВыборДокумента.ПолучитьОбъект();

ТаблицаТоваров = ДокументОбъект.Товары.Выгрузить();

Отбор = Новый Структура("Номенклатура", ИскомаяНоменклатура);
УдаляемыеСтроки = ТаблицаТоваров.НайтиСтроки(Отбор);

Для Каждого СтрокаТЗ Из УдаляемыеСтроки Цикл
    ТаблицаТоваров.Строки.Удалить(СтрокаТЗ);
КонецЦикла;

ДокументОбъект.Товары.Загрузить(ТаблицаТоваров);
ДокументОбъект.Записать(РежимЗаписиДокумента.Запись);

Важные нюансы при программном изменении документов

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

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