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