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