Почему не выполняется цикл по результату НайтиСтроки в 1С и как правильно заполнить дерево значений

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке сложных отчетов или обработок в 1С программисты часто сталкиваются с ситуацией, когда программный код кажется логически верным, но при выполнении вложенные циклы просто игнорируются платформой. Рассмотрим типичную ситуацию: у нас есть несколько таблиц значений, полученных путем копирования и сворачивания исходного запроса, и мы пытаемся построить на их основе иерархическую структуру (например, дерево значений). Выясним, почему метод НайтиСтроки может возвращать пустой массив и как обеспечить корректное отображение данных на форме.

Разбор основной проблемы: почему массив пустой?

Проанализируем ситуацию, описанную в исходном коде. Автор пытается выполнить поиск в таблице Запрос2 по коду операции, полученному из таблицы Запрос3. На первый взгляд всё верно, однако цикл Для Каждого Стр2 Из Запрос4 Цикл не отрабатывает. Главная причина здесь кроется в механизме работы метода НайтиСтроки. В 1С этот метод выполняет строгое сравнение по типам данных и значениям.

Рассмотрим основные причины, по которым поиск может не дать результатов:

  1. Несоответствие типов данных: Если в колонке Код таблицы Запрос2 хранится число, а в НовСтр.КодОпер (или в структуре поиска) передается строка, 1С не найдет совпадений. Например, число 10 не равно строке "10".
  2. Лишние пробелы и невидимые символы: Часто данные из внешних источников или загруженные из текстовых файлов содержат невидимые пробелы в конце или в начале строки. Строка "001" не будет равна строке "001 ".
  3. Обрезание строк: Как было замечено в обсуждении, если длина колонки в приемнике меньше длины исходных данных, строка может «обрезаться», что сделает поиск невозможным.

Для решения подобных проблем рекомендуется всегда использовать функцию СокрЛП() при заполнении таблиц и подготовке структур поиска, а также явно приводить типы данных.

Программное заполнение дерева значений на форме

Когда мы работаем с иерархическими структурами, такими как дерево значений на форме (в коде автора это _ПредТипДог), важно правильно соблюдать вложенность элементов. Посмотрим на пример кода, который решает задачу заполнения данных по контрагентам, лицензиям и договорам:


// Предположим, Запрос2 и Запрос3 — это Таблицы Значений
Запрос4 = Запрос2.НайтиСтроки(Новый Структура("Код", НовСтр.КодОпер)); 

Для Каждого Стр2 Из Запрос4 Цикл 
    // Создаем строку верхнего уровня в дереве (Контрагент)
    Стр_СведКАгент = _ПредТипДог.ПолучитьЭлементы().Добавить();
    Стр_СведКАгент.УИД = НовСтр.УИД;
    Стр_СведКАгент.УИДЛок = Новый УникальныйИдентификатор();
    Стр_СведКАгент.НаимОрг = Стр2.Контрагент;
    Стр_СведКАгент.ИННЮЛ = Стр2.Контрагент.ИНН;
    Стр_СведКАгент.КПП = Стр2.Контрагент.КПП;
    
    // Ищем детальные записи в исходной таблице
    ПараметрыОтбора = Новый Структура();
    ПараметрыОтбора.Вставить("Контрагент", Стр2.Контрагент);
    ПараметрыОтбора.Вставить("Лицензия", Стр2.Лицензия);
    
    Запрос5 = Запрос.НайтиСтроки(ПараметрыОтбора);
    
    Для Каждого Стр3 Из Запрос5 Цикл 
        // Добавляем подчиненную строку (Детализация по суммам)
        Стр_ДокПОбНЛ = Стр_СведКАгент.ПолучитьЭлементы().Добавить();
        ЗаполнитьЗначенияСвойств(Стр_ДокПОбНЛ, Стр3);
        
        // Еще один уровень вложенности (Тип документа: Договор или Лицензия)
        Стр_ТипДока = Стр_ДокПОбНЛ.ПолучитьЭлементы().Добавить();
        
        // Логика выбора типа документа
        Если ЗначениеЗаполнено(Стр3.Лицензия) Тогда
            Стр_ТипДока.ТипДок = "Лицензия";
            Стр_ТипДока.НомДок = Стр3.НомерЛицензии;
            Стр_ТипДока.ДатаДок = Стр3.ДатаЛицензии;
        Иначе
            Стр_ТипДока.ТипДок = "Договор";
            Стр_ТипДока.НомДок = Стр3.НомерДоговора;
            Стр_ТипДока.ДатаДок = Стр3.ДатаДоговора;
        КонецЕсли;
    КонецЦикла;
КонецЦикла;

Обратите внимание на использование метода ПолучитьЭлементы(). В контексте дерева значений этот метод возвращает коллекцию подчиненных строк текущей строки. Если мы вызываем его у объекта дерева — получаем строки верхнего уровня, если у конкретной строки — получаем её «детей».

Почему данные могут не отображаться на форме?

Даже если программный код отработал без ошибок, пользователь может увидеть пустую таблицу на экране. Разберем, почему это происходит и как это исправить:

  1. Данные не связаны с реквизитом формы: Если _ПредТипДог — это локальная переменная (объект ДеревоЗначений), созданная в процедуре, её содержимое нужно явно поместить в реквизит формы. Для этого используется метод ЗначениеВРеквизитФормы(ДеревоОбъект, "РеквизитДеревоНаФорме").
  2. Строки свернуты: По умолчанию дерево на форме создается со свернутыми узлами. Если программно не вызвать метод Развернуть(), пользователю покажется, что данных нет.
  3. Отсутствие обновления: В управляемых формах иногда требуется принудительное обновление интерфейса, хотя при правильной работе с реквизитами формы это происходит автоматически.

Оптимизация: Использование Итогов в запросе

Рассмотрим ситуацию глубже. В исходном коде автор трижды копирует и сворачивает таблицы значений. Это ресурсоемкая операция, особенно на больших объемах данных. Разберем, как сделать это профессиональнее. Вместо ручной группировки и поиска строк с помощью НайтиСтроки, эффективнее использовать механизм ИТОГИ в языке запросов 1С.

Запрос с итогами позволяет сразу получить древовидную структуру. Пример запроса:


ТекстЗапроса = 
"ВЫБРАТЬ
|   Данные.Контрагент,
|   Данные.Лицензия,
|   Данные.Договор,
|   Данные.Код,
|   СУММА(Данные.Сумма) КАК Сумма
|ИЗ
|   &ТвояТаблица КАК Данные
|СГРУППИРОВАТЬ ПО
|   Данные.Контрагент,
|   Данные.Лицензия,
|   Данные.Договор,
|   Данные.Код
|ИТОГИ
|   СУММА(Сумма)
|ПО
|   Код,
|   Контрагент";

Такой запрос вернет результат, который можно выгрузить в дерево значений одной командой: ДеревоРезультат = Запрос.Выполнить().Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкам). Это исключит необходимость во вложенных циклах и значительно ускорит работу программы.

Безопасность и обработка исключений

Проанализируем использование конструкции Попытка — Исключение в коде. Пустой блок Исключение — это «мина замедленного действия». Если при обращении к свойствам объекта (например, Стр3.НомерЛицензии) возникнет ошибка (допустим, колонки не существует), программа просто промолчит, и данные не попадут на форму.

Рекомендация: всегда проверяйте наличие колонок или используйте проверку Если Стр3.Свойства().Найти("ИмяКолонки") <> Неопределено Тогда. Если вы всё же используете попытку, записывайте ошибку в журнал регистрации:


Попытка
    Стр_ДокПОбНЛ.НомДок = Стр3.НомерЛицензии;
Исключение
    ЗаписьЖурналаРегистрации("ЗаполнениеДерева", УровеньЖурналаРегистрации.Ошибка,,, ОписаниеОшибки());
КонецПопытки;

Резюме для исправления кода

Чтобы ваш цикл по массиву из НайтиСтроки гарантированно работал, выполните следующие шаги:

Соблюдение этих правил не только решит текущую проблему с "пустым массивом", но и сделает ваш код более производительным и легким в поддержке.

← На главную