Разработка и Конфигурация · 13 мин
Как удалить строки из табличной части по условию в 1С
Как в 1С удалить строки табличной части документа по условию: методы НайтиСтроки, Удалить, обратный цикл, примеры кода на встроенном языке и типичные ошибки.
Удаление строк табличной части по условию — типовая задача при доработке документов и обработок в 1С: очистить товары с нулевым количеством, убрать строки по выбранному складу, удалить позиции определённого вида номенклатуры. Задача решается несколькими способами, и выбор зависит от того, работаете ли вы с объектом документа, данными формы или запросом.
Главная ловушка — удаление строк внутри прямого цикла Для Каждого: после удаления элемента коллекция перестраивается, индексы смещаются, и часть строк пропускается либо возникает ошибка обращения по индексу. Ниже разобраны корректные приёмы: метод НайтиСтроки, обратный цикл по индексу, отбор через запрос и особенности управляемых форм.
Все примеры приведены на встроенном языке 1С и подходят для конфигураций на платформе 8.3 — как для обычных, так и для управляемых форм. Перед запуском любых массовых изменений на рабочей базе сделайте резервную копию или сначала проверьте код на копии базы.
Как устроена табличная часть и почему прямой цикл опасен
Табличная часть документа или справочника — это коллекция строк, доступная через свойство объекта, например ДокументОбъект.Товары. Каждая строка имеет индекс (начиная с нуля) и номер строки (начиная с единицы). При удалении строки все последующие строки сдвигаются на одну позицию вверх.
Если перебирать коллекцию циклом Для Каждого Строка Из ТабличнаяЧасть Цикл и внутри вызывать ТабличнаяЧасть.Удалить(Строка), то после первого удаления внутренний итератор продолжит со следующей позиции, но коллекция уже сдвинулась — одна строка будет пропущена. В цикле по индексу в прямом порядке эффект тот же: после удаления элемента с индексом 2 элемент с индексом 3 станет элементом с индексом 2, а цикл перейдёт к индексу 3 и пропустит его.
Способ 1. Удаление через НайтиСтроки с отбором
Самый надёжный и читаемый способ — сначала получить массив строк, подлежащих удалению, а затем удалить их. Метод НайтиСтроки принимает структуру отбора, где ключ — имя реквизита, а значение — искомое значение, и возвращает массив строк табличной части.
Отбор = Новый Структура;
Отбор.Вставить("Количество", 0);
НайденныеСтроки = Объект.Товары.НайтиСтроки(Отбор);
Для Каждого Строка Из НайденныеСтроки Цикл
Объект.Товары.Удалить(Строка);
КонецЦикла;
Здесь удаление идёт по массиву найденных строк, а не по самой коллекции, поэтому смещение индексов не мешает — каждая строка из массива удаляется по ссылке на объект строки. Такой код безопасен и легко читается.
В структуре отбора можно указать несколько условий одновременно — они объединяются по «И». Например, удалить строки с нулевым количеством по конкретному складу:
Отбор = Новый Структура;
Отбор.Вставить("Склад", ВыбранныйСклад);
Отбор.Вставить("Количество", 0);
Для Каждого Строка Из Объект.Товары.НайтиСтроки(Отбор) Цикл
Объект.Товары.Удалить(Строка);
КонецЦикла;
Способ 2. Обратный цикл по индексу
Когда условие сложнее простого равенства (например, сумма меньше порога или наименование содержит подстроку), применяйте цикл по индексу от конца коллекции к началу. При движении «снизу вверх» удаление текущей строки не влияет на индексы ещё не проверенных строк.
Всего = Объект.Товары.Количество();
Для Инд = 1 По Всего Цикл
Строка = Объект.Товары[Всего - Инд];
Если Строка.Сумма < 1000
Или СтрНайти(Строка.Комментарий, "отмена") > 0 Тогда
Объект.Товары.Удалить(Строка);
КонецЕсли;
КонецЦикла;
Обратите внимание на выражение Объект.Товары[Всего - Инд]: при Инд = 1 берётся последняя строка, при Инд = Всего — первая. Удаление строки сдвигает только те элементы, которые стоят после неё, а они уже проверены.
Этот способ универсален: условие может быть любым выражением встроенного языка, включая вызовы функций и обращения к реквизитам связанных объектов через точку.
- 1Определите условие удаления и сохраните количество строк в переменную до цикла
- 2Организуйте цикл от последней строки к первой через индекс
- 3Внутри цикла проверьте условие и вызовите Удалить для текущей строки
- 4После цикла проверьте итоговое количество строк и при необходимости запишите объект
Способ 3. Отбор строк во временную таблицу и загрузка обратно
Если нужно удалить много строк по сложному условию, иногда удобнее пойти от противного: выгрузить табличную часть в таблицу значений, оставить только нужные строки и загрузить результат обратно.
ТаблицаОстатка = Объект.Товары.Выгрузить();
Инд = ТаблицаОстатка.Количество() - 1;
Пока Инд >= 0 Цикл
Если ТаблицаОстатка[Инд].Отменено Тогда
ТаблицаОстатка.Удалить(Инд);
КонецЕсли;
Инд = Инд - 1;
КонецЦикла;
Объект.Товары.Загрузить(ТаблицаОстатка);
Метод Загрузить полностью замещает содержимое табличной части, поэтому предварительно очищать её не нужно. Способ удобен, когда над строками дополнительно выполняются вычисления, группировка или свёртка.
Учтите ограничение: при выгрузке и загрузке теряется привязка строк к «старым» объектам, поэтому если на строки табличной части где-то сохранены ссылки (например, в дополнительных таблицах формы по ключу строки), их придётся обновить.
Особенности работы в управляемой форме
На управляемой форме табличная часть обычно доступна как Объект.Товары (реквизит формы типа «ДанныеФормыКоллекция»). Методы НайтиСтроки, Удалить, Количество работают так же, но есть нюансы.
Во-первых, удаление строк на клиенте без контекстного серверного вызова возможно, поэтому простые операции очистки можно выполнять в клиентском обработчике кнопки — это быстрее и не требует передачи данных на сервер. Во-вторых, вместо Удалить у данных формы также доступен метод Удалить(Строка), а для очистки всей табличной части — Очистить().
Если обработка выполняется в серверной процедуре с директивой &НаСервере, а объект открыт в форме, работайте через РеквизитФормыВЗначение("Объект") и обратное преобразование ЗначениеВРеквизитФормы, либо манипулируйте коллекцией формы напрямую.
&НаКлиенте
Процедура УдалитьНулевые(Команда)
Отбор = Новый Структура("Количество", 0);
Найденные = Объект.Товары.НайтиСтроки(Отбор);
Для Каждого Строка Из Найденные Цикл
Объект.Товары.Удалить(Строка);
КонецЦикла;
Модифицированность = Истина;
КонецПроцедуры
После удаления строк на форме установите признак Модифицированность = Истина, если удаление выполняется программно в клиентской процедуре. Иначе пользователь может закрыть форму без вопроса о сохранении и потерять изменения.
Удаление строк внешней обработкой по нескольким документам
Частая прикладная задача — массово очистить табличные части в группе документов, например удалить отменённые позиции из всех реализаций за период. Здесь документы сначала выбираются запросом, затем каждый объект изменяется и записывается.
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Реализация.Ссылка КАК Ссылка
|ИЗ
| Документ.РеализацияТоваровУслуг КАК Реализация
|ГДЕ
| Реализация.Дата МЕЖДУ &Начало И &Конец
| И Реализация.Проведен";
Запрос.УстановитьПараметр("Начало", НачалоПериода);
Запрос.УстановитьПараметр("Конец", КонецПериода);
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
ДокОбъект = Выборка.Ссылка.ПолучитьОбъект();
Отбор = Новый Структура("Отменено", Истина);
Для Каждого Строка Из ДокОбъект.Товары.НайтиСтроки(Отбор) Цикл
ДокОбъект.Товары.Удалить(Строка);
КонецЦикла;
ДокОбъект.Записать(РежимЗаписиДокумента.Проведение);
КонецЦикла;
При большом количестве документов оборачивайте обработку в транзакцию порциями или фиксируйте обработанные ссылки, чтобы при сбое можно было продолжить с места остановки. Запись с проведением пересчитывает движения — оцените время выполнения на копии базы.
Сравнение способов и выбор подходящего
Каждый из рассмотренных приёмов имеет свою область применения. Ориентируйтесь на сложность условия и контекст выполнения.
| Способ | Когда применять | Ограничения |
|---|---|---|
| НайтиСтроки + Удалить | Условие — точное равенство одного или нескольких реквизитов | Не поддерживает сравнения «больше/меньше» и поиск подстроки |
| Обратный цикл по индексу | Любое условие, включая вычисляемые выражения | Код менее нагляден, легко ошибиться в границах цикла |
| Выгрузить/Загрузить | Сложная обработка: фильтрация, свёртка, группировка строк | Теряется привязка к исходным объектам строк, больше расход памяти |
| Запрос + ПолучитьОбъект | Массовая обработка многих документов | Затрагивает движения, требует транзакций и тестирования |
Как выбрать действие
Проверка результата и типичные ошибки
После выполнения кода убедитесь, что удалены именно те строки, которые планировались. Минимальная проверка — сравнить количество строк до и после, а также выборочно открыть документ и просмотреть табличную часть. Для массовых обработок полезно перед удалением выводить в журнал или сообщение номенклатуру и номер строки каждой удаляемой позиции.
Как проверить результат
Уменьшилось ровно на число строк, попадающих под условие
Удалено меньше или больше, чем ожидалось
Все строки, не подпадающие под условие, на месте и не изменены
Пропали строки, которые должны были остаться
Итоги пересчитаны и согласованы с оставшимися строками
Итоговые поля устарели или не совпадают с табличной частью
Проводки и записи регистров соответствуют новому составу строк
Движения отражают удалённые позиции
Отдельно отметьте для себя поведение зависимых механизмов: если в конфигурации есть подписки на события записи документа или обработчики заполнения, они сработают при записи объекта после удаления строк. Иногда это желательно (пересчёт итогов), иногда — источник неожиданных изменений.
Что важно запомнить
Удаляйте строки либо через массив, возвращаемый НайтиСтроки, либо в обратном цикле по индексу — оба способа защищают от смещения коллекции. Для массовой обработки документов используйте запрос и запись объектов, предварительно проверив код на копии базы. После удаления контролируйте количество строк, итоги документа и движения по регистрам.
Частые вопросы
Можно ли удалить сразу все строки табличной части?
Да, для полной очистки используйте метод Очистить(): Объект.Товары.Очистить(). Это быстрее и проще, чем перебор с удалением. Метод работает и для объекта, и для данных формы в управляемом приложении.
Почему НайтиСтроки возвращает пустой массив, хотя подходящие строки есть?
Чаще всего причина в несовпадении типов или значений: в отборе указано число, а в реквизите хранится строка; ссылка сравнивается с наименованием; у булева реквизита значение по умолчанию не задано. Проверьте тип и фактическое значение реквизита в отладчике.
Как удалить строки по условию «сумма больше заданной»?
Через структуру отбора НайтиСтроки такое условие задать нельзя — поддерживается только равенство. Используйте обратный цикл по индексу с проверкой Если Строка.Сумма > Порог Тогда ... Удалить внутри цикла.
Нужно ли перепроводить документ после удаления строк?
Если документ проведён и табличная часть влияет на движения, да — иначе регистры будут содержать данные по удалённым строкам. Запишите объект с режимом РежимЗаписиДокумента.Проведение. Для непроведённых документов достаточно обычной записи.
Можно ли отменить удаление строк, если ошибся?
До записи объекта — да: достаточно закрыть форму без сохранения или не вызывать Записать(). После записи восстановить строки штатно нельзя, поможет только резервная копия или повторный ввод данных. Поэтому массовые обработки всегда тестируют на копии базы.
Работает ли этот код на тонком клиенте и в веб-клиенте?
НайтиСтроки, Удалить и обратный цикл по данным формы работают на клиенте в обоих режимах. Обращение к объектам базы через ПолучитьОбъект и запросы доступны только на сервере — выносите такой код в серверные процедуры.