При разработке и доработке отчетов в платформе 1С:Предприятие 7.7 начинающие программисты часто сталкиваются с ошибками синтаксического контроля и времени выполнения: «Переменная не определена» и «Поле агрегатного объекта не обнаружено». Разберем по шагам, из-за чего возникают такие сообщения, как устроен контекст видимости переменных и как правильно выводить данные из прямых SQL-запросов и таблиц значений в печатную форму отчета.
В языке 1С 7.7 поддерживается неявное объявление локальных переменных: переменная создается в момент первого присваивания ей значения. Однако если переменная впервые передается в метод в качестве выходного параметра или используется в выражении до инициализации, платформа выдаст ошибку.
Рассмотрим классический пример ошибочного кода:
Функция ТестВыбораИзСписка()
Список = СоздатьОбъект("СписокЗначений");
Список.ДобавитьЗначение("Первое значение");
Список.ДобавитьЗначение("Второе значение");
Список.ДобавитьЗначение("Третье значение");
// Ошибка: переменная ВыбранноеЗначение еще не объявлена и не инициализирована
Если Список.ВыбратьЗначение(ВыбранноеЗначение, "Тест",,, 1) = 1 Тогда
Возврат ВыбранноеЗначение;
КонецЕсли;
Возврат 0;
КонецФункции
Метод ВыбратьЗначение() ожидает переменную в первом параметре для записи выбранного элемента. Так как переменная ВыбранноеЗначение ранее не встречалась в левой части оператора присваивания, компилятор не знает о ее существовании. Чтобы исправить такую ситуацию, достаточно предварительно объявить переменную или присвоить ей начальное значение:
Функция ТестВыбораИзСписка()
Перем ВыбранноеЗначение; // Явное объявление переменной
Список = СоздатьОбъект("СписокЗначений");
Список.ДобавитьЗначение("Первое значение");
Если Список.ВыбратьЗначение(ВыбранноеЗначение, "Тест",,, 1) = 1 Тогда
Возврат ВыбранноеЗначение;
КонецЕсли;
Возврат 0;
КонецФункции
Частая причина ошибки «Переменная не определена» при формировании отчетов — обращение к несуществующим переменным внутри ячеек макета (табличного документа).
Когда выполняется метод Таб.ВывестиСекцию("Секция"), платформа 1С 7.7 вычисляет выражения во всех ячейках выводимой секции. Если в свойствах ячейки указан тип «Выражение» и написано, например, Спр.ИстФин.ТекущийЭлемент(), система ищет переменную модуля с именем Спр. Если такой переменной в модуле отчета нет, возникает ошибка компиляции шаблона ячейки.
Проанализируем правила привязки данных макета к коду:
Перем) и доступны в момент вызова ВывестиСекцию().ТабОбъект), в ячейках секции строки должны использоваться поля текущей строки этой таблицы (ТабОбъект.с1, ТабОбъект.ИстФин и т. д.), а не переменные временных запросов.ТекущийЭлемент() вызывать не нужно — достаточно указать само поле или его реквизит (например, ТабОбъект.ИстФин.Наименование).Ошибка «Поле агрегатного объекта не обнаружено» означает попытку обратиться к свойству, реквизиту или колонке, которых нет у данного объекта. Выясним основные причины этой проблемы в отчетах 1С 7.7:
с1, с2, с3, с4, то попытка присвоить значение в ТабОбъект.ИстФин = ... вызовет исключение, так как колонка ИстФин не была добавлена методом НоваяКолонка().ТабОбъект.с1 вне цикла Пока ТабОбъект.ПолучитьСтроку() = 1 Цикл или без предварительного вызова ТабОбъект.ТекущаяСтрока(НомерСтроки) приводит к ошибке, так как текущая строка не выбрана.Объект.ИстФин). Для периодических реквизитов требуется использовать метод Получить() с указанием даты или устанавливать дату выборки через метод ИспользоватьДату()._1SCONST. Прямое обращение вида $Спр.ИстФин к периодическому полю внутри SQL-запроса не сработает корректно. Для таких полей следует применять мета-функцию $ПоследнееЗначение.Объект.ИстФин(Спр.ID, :Дата).Разберем по шагам, как правильно организовать получение данных и их вывод в макет отчета без ошибок видимости и лишней нагрузки на базу данных:
RS.ВыполнитьИнструкцию()) внутри вложенных циклов по справочникам приводит к критическому замедлению системы (проблема N+1). Следует выполнять один общий запрос с группировками ко всей базе данных.Посмотрим на пример корректной передачи данных из результата запроса в таблицу значений и макет:
// 1. Инициализация колонок таблицы значений
ТабОбъект = СоздатьОбъект("ТаблицаЗначений");
ТабОбъект.НоваяКолонка("Объект");
ТабОбъект.НоваяКолонка("ИстФин");
ТабОбъект.НоваяКолонка("СуммаНеПодпис", "Число");
ТабОбъект.НоваяКолонка("СуммаПодпис", "Число");
ТабОбъект.НоваяКолонка("ДолгЭкспертизы", "Число");
// 2. Обработка строк результата запроса
ТЗ = RS.ВыполнитьИнструкцию(ТекстЗапроса);
ТЗ.ВыбратьСтроки();
Пока ТЗ.ПолучитьСтроку() = 1 Цикл
ТабОбъект.НоваяСтрока();
ТабОбъект.Объект = ТЗ.Объект;
ТабОбъект.ИстФин = ТЗ.ИстФин;
Если Число(ТЗ.ВидАкта.Код) = 4 Тогда
ТабОбъект.ДолгЭкспертизы = ТЗ.ДолгПоОплате;
Иначе
Если ТЗ.ПризнакПодпис = Перечисление.Признак.Нет Тогда
ТабОбъект.СуммаНеПодпис = ТЗ.ДолгПоОплате;
ИначеЕсли ТЗ.ПризнакПодпис = Перечисление.Признак.Да Тогда
ТабОбъект.СуммаПодпис = ТЗ.ДолгПоОплате;
КонецЕсли;
КонецЕсли;
КонецЦикла;
// 3. Сворачивание и группировка данных
ТабОбъект.Свернуть("Объект,ИстФин", "СуммаНеПодпис,СуммаПодпис,ДолгЭкспертизы");
// 4. Обход и вывод в печатную форму
ТабОбъект.ВыбратьСтроки();
Пока ТабОбъект.ПолучитьСтроку() = 1 Цикл
// В ячейках секции "Объект" в макете указываем:
// ТабОбъект.Объект, ТабОбъект.ИстФин, ТабОбъект.СуммаНеПодпис и т.д.
Таб.ВывестиСекцию("Объект");
КонецЦикла;
Следуя этим правилам, мы гарантируем, что все переменные объявлены в нужной области видимости, реквизиты доступны для платформы, а отчет формируется быстро и без ошибок.