При программном заполнении табличных частей в 1С:Предприятие 8 часто возникает задача визуального разделения групп данных. Например, при переносе состава спецификаций в документ производства разработчики часто хотят вставить пустую строку после каждого блока материалов, относящихся к разным изделиям. Казалось бы, задача тривиальная — вызвать метод Добавить(), однако на практике это часто приводит к логическим ловушкам: строки начинают бесконтрольно дублироваться, а циклы — работать некорректно. Рассмотрим подробнее, как решить эту задачу правильно, и разберем типичные ошибки, которые допускают даже опытные программисты.
Проанализируем ситуацию, с которой столкнулся автор темы. При попытке добавить пустую строку в конце цикла обхода выборки, он заметил, что количество пустых строк увеличивается в геометрической прогрессии. Выясним причину этого поведения. Основная ошибка заключалась в неправильной вложенности циклов и использовании коллекции, которая сама же и наполняется в процессе обхода.
Если мы помещаем команду Объект.Продукция.Добавить() внутрь цикла, который обходит уже добавленные строки, мы получаем «эффект снежного кома». Посмотрим на упрощенный пример ошибочной логики:
// ОШИБОЧНЫЙ ПРИМЕР
Пока Выборка.СледующийПоЗначениюПоля("Спецификация") Цикл
// Добавляем строку в одну ТЧ
НоваяСтрокаТовары = Объект.Товары.Добавить();
// Пытаемся обойти ТЧ, которую только что пополнили
Для Каждого ТекущаяСтрока Из Объект.Товары Цикл
// Здесь логика добавления данных в другую ТЧ (Продукция)
// И в конце этого вложенного цикла добавляется пустая строка
ПустаяСтрока = Объект.Продукция.Добавить();
КонецЦикла;
КонецЦикла;
В этой схеме при обработке второй спецификации цикл Для Каждого выполнится дважды, при третьей — трижды, и так далее. Именно это приводит к тому, что пустые строки начинают «задваиваться и затраиваться».
Самый надежный способ добавить строку-разделитель — использовать логическую переменную (флаг), которая будет отслеживать, были ли добавлены какие-либо значимые данные в текущем блоке. Это предотвратит появление лишних разделителей, если выборка по конкретной спецификации оказалась пустой.
Разберем этот алгоритм по шагам:
ЕстьДанныеДляДобавления значением Ложь перед началом обхода вложенной выборки.Истина.Истина, добавляем одну пустую строку.Рассмотрим реализацию этого метода в коде:
Для Каждого ТекущаяСтрокаТовара Из СписокТоваров Цикл
ЕстьСпецификация = Ложь; // Сбрасываем флаг для каждой группы
Пока ВыборкаДетали.Следующий() Цикл
Если ВыборкаДетали.СпособПолучения = Перечисления.СпособыПолучения.Произвести Тогда
ЕстьСпецификация = Истина;
НоваяСтрока = Объект.Продукция.Добавить();
НоваяСтрока.Номенклатура = ВыборкаДетали.Номенклатура;
// Заполнение остальных реквизитов...
КонецЕсли;
КонецЦикла;
// Если данные были добавлены, вставляем разделитель
Если ЕстьСпецификация Тогда
Разделитель = Объект.Продукция.Добавить();
КонецЕсли;
КонецЦикла;
Иногда по условиям дизайна интерфейса пустая строка должна идти перед группой данных, а не после неё. В этом случае логика немного меняется: мы сначала добавляем строку, а если выясняется, что данных для этой группы нет — удаляем её.
Проанализируем ситуацию: мы заранее не знаем, зайдет ли код в условие внутри цикла Пока Выборка.Следующий(). Поэтому мы создаем «кандидата» на пустую строку, сохраняем ссылку на него и работаем дальше.
Для Каждого ТекущаяСтрока Из Объект.Товары Цикл
// Добавляем потенциальную пустую строку в начало блока
ПустаяСтрокаПродукции = Объект.Продукция.Добавить();
ЕстьСпецификация = Ложь;
Пока Выборка.Следующий() Цикл
// Проверка условий получения материалов
Если Выборка.СпособПолученияМатериала = Перечисления.СпособыПолученияМатериалов.ПроизвестиПоСпецификации Тогда
ЕстьСпецификация = Истина;
СтрокаПродукция = Объект.Продукция.Добавить();
СтрокаПродукция.НоменклатураВЭтап = Выборка.Номенклатура1;
// Заполнение данных...
КонецЕсли;
КонецЦикла;
// Если в итоге спецификация не была обработана, удаляем заранее созданную пустую строку
Если Не ЕстьСпецификация Тогда
Объект.Продукция.Удалить(ПустаяСтрокаПродукции);
КонецЕсли;
КонецЦикла;
Важный нюанс: Этот метод менее производителен на очень больших таблицах, так как метод Удалить() заставляет платформу пересчитывать индексы строк. Однако для стандартных документов он вполне применим и удобен.
При работе с запросами в 1С крайне важно правильно использовать методы Следующий() и СледующийПоЗначениюПоля(). В сообщении 10 темы форума была показана структура, где внутри цикла по значению поля находился еще один цикл Для Каждого по табличной части. Это классическая ошибка «вложенных коллекций».
Выясним причину, почему это плохо. Если вы используете СледующийПоЗначениюПоля("Спецификация"), платформа ожидает, что внутри вы будете использовать обычный Следующий() для обхода записей только этой спецификации. Если же вы начинаете обходить всю табличную часть документа Для Каждого, вы теряете связь с текущей записью выборки и начинаете дублировать действия.
Правильная структура обхода иерархической выборки должна выглядеть так:
// 1. Внешний цикл фиксирует начало новой группы (например, Спецификации)
Пока Выборка.СледующийПоЗначениюПоля("Спецификация") Цикл
// Добавляем запись в заголовочную таблицу (Товары)
НоваяЗаписьТовары = Объект.Товары.Добавить();
ЗаполнитьЗначенияСвойств(НоваяЗаписьТовары, Выборка);
// 2. Внутренний цикл обходит только детальные записи этой спецификации
Пока Выборка.Следующий() Цикл
// Добавляем данные в подчиненную таблицу (Продукция)
НоваяЗаписьПродукция = Объект.Продукция.Добавить();
ЗаполнитьЗначенияСвойств(НоваяЗаписьПродукция, Выборка);
КонецЦикла;
// 3. После выхода из внутреннего цикла добавляем одну пустую строку
Объект.Продукция.Добавить();
КонецЦикла;
Рассмотрим ситуацию с точки зрения профессиональной разработки. Использование «пустых строк» в базе данных — это не самый чистый метод (так называемый «антипаттерн»). Пустые строки могут мешать при проверке заполнения документа, они «разрывают» данные при сортировке и могут вызвать ошибки при проведении.
Вместо физического добавления пустых строк в табличную часть, мы рекомендуем использовать Условное оформление управляемой формы:
ЭтоНачалоГруппы (тип Булево).Истина для первой строки каждой новой группы.ЭтоНачалоГруппы равно Истина, установить оформление (например, Жирная верхняя граница или Цвет фона).Этот способ позволяет визуально разделить данные, не засоряя таблицу пустыми строками, которые пользователь может случайно удалить или которые могут помешать расчетам.
Как упоминалось в сообщении 8, иногда нет возможности включить отладку (например, на рабочем сервере предприятия). В этом случае проанализируем поведение кода с помощью «логической отладки»:
Сообщить() или ОбщегоНазначенияКлиентСервер.СообщитьПользователю(). Выводите количество строк в таблице на каждом этапе: Сообщить("Текущий индекс: " + Объект.Продукция.Количество());.ЗаписьЖурналаРегистрации() позволит вам увидеть последовательность выполнения циклов и значения переменных даже без активного отладчика.ЛЕВОЕ СОЕДИНЕНИЕ, убедитесь, что оно не приводит к «размножению» строк из-за дублей в присоединяемой таблице (в данном случае в материалах спецификации).Подводя итог, отметим: добавление пустой строки в цикле требует строгого контроля контекста. Используйте флаги для проверки наличия данных и избегайте вложенных циклов по коллекциям, которые вы сами же и модифицируете. Это обеспечит стабильную работу кода и чистоту данных в ваших документах.