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