Дерево значений под отладкой: от разворота строк до выгрузки в таблицу
Содержание
Чтобы посмотреть дерево значений в отладчике 1С, поставьте точку останова (F9) на строке после заполнения дерева, дождитесь остановки, выделите имя переменной и нажмите Shift+F9 — откроется окно «Вычислить выражение». В нём разверните свойство Строки: каждая строка дерева содержит свои значения колонок и собственную коллекцию Строки с подчинёнными элементами. Для постоянного наблюдения перетащите переменную в табло (Ctrl+Alt+W).
Ручной разворот удобен, пока в дереве десяток строк и два-три уровня. Если строк сотни, а нужная запись лежит на пятом уровне, быстрее одной строкой кода превратить дерево в плоскую таблицу или в табличный документ с отступами. Все три приёма работают в конфигураторе платформы 8.3, отладка в 1С:EDT устроена похоже.
Из чего состоит дерево значений и как оно выглядит под отладкой
ДеревоЗначений — универсальная коллекция с иерархией. В отличие от таблицы значений, строки здесь вложены друг в друга. У объекта два главных свойства:
- 📋
Колонки— описание колонок: имя, тип значения, заголовок; - 🌳
Строки— коллекция строк верхнего уровня.
Каждая строка (СтрокаДереваЗначений) хранит значения колонок и ещё два свойства:
- 🔽
Строки— подчинённые строки этого узла; - 🔼
Родитель— ссылка на строку уровнем выше (у верхнего уровня —Неопределено).
Отсюда главная особенность просмотра. Отладчик показывает дерево не как иерархический список, а как вложенные свойства. Чтобы добраться до третьего уровня, нужно развернуть Строки → [0] → Строки → [2] → Строки и так далее. В окне это выглядит как матрёшка, и на больших данных легко потеряться.
| Что хотите увидеть | Где смотреть в отладчике |
|---|---|
| Сколько колонок и как они называются | Дерево.Колонки |
| Строки верхнего уровня | Дерево.Строки |
| Детей конкретного узла | Дерево.Строки[0].Строки |
| Значение колонки у строки | Дерево.Строки[0].Строки[1].Сумма |
| Уровень вложенности строки | ТекСтрока.Уровень() |
Индексы строк в выражениях считаются с нуля. Первая строка верхнего уровня — Дерево.Строки[0], а не [1].
Перед отладкой: где поставить точку останова
Дерево значений видно только там, где оно существует как переменная. Отсюда три правила.
- Останавливайтесь после заполнения. Точка на строке
Дерево = Запрос.Выполнить().Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкам)сработает до присваивания, и вы увидите старое значение. Ставьте точку на следующую строку. - Учитывайте контекст выполнения. Дерево, собранное в процедуре с директивой
&НаСервере, живёт только на сервере. Чтобы остановиться внутри, отладка на сервере должна быть включена. В файловой базе серверный код выполняется в процессе клиента, и отдельной настройки обычно не требуется. В клиент-серверной базе сервер 1С нужно запустить с поддержкой отладки. - Различайте дерево и реквизит формы. Если дерево лежит в реквизите формы, на клиенте отладчик покажет не
ДеревоЗначений, аДанныеФормыДерево.
Последний пункт чаще всего сбивает с толку. У ДанныеФормыДерево нет свойства Строки. Подчинённые элементы получают методом ПолучитьЭлементы(), а родителя — методом ПолучитьРодителя():
// На клиенте, реквизит формы типа ДеревоЗначений
Объект.Номенклатура.ПолучитьЭлементы()[0].ПолучитьЭлементы()[1].Количество
На сервере тот же реквизит можно превратить в обычное дерево через РеквизитФормыВЗначение("Номенклатура") и уже его разворачивать привычным способом.
Способ 1. Разворачиваем дерево в окне «Вычислить выражение» и в табло
Это базовый способ, ему не нужен дополнительный код.
Окно «Вычислить выражение»
- Дождитесь остановки на точке останова.
- Выделите в модуле имя переменной с деревом, например
ДеревоОстатков. - Нажмите
Shift+F9или выберите в менюОтладка → Вычислить выражение. - В таблице результата раскройте узел переменной, затем
Строки. - Раскройте нужную строку: в ней вы увидите значения всех колонок и вложенную коллекцию
Строки. - Повторяйте, пока не дойдёте до нужного уровня.
В поле «Выражение» можно сразу писать путь до нужного узла: ДеревоОстатков.Строки[2].Строки[0]. Это быстрее, чем кликать по плюсикам, особенно если адрес строки вы уже знаете.
У окна есть команда показа значения в отдельном окне. Для коллекций она открывает удобный просмотр с колонками. Если в вашей версии платформы дерево открывается в таком окне без иерархии, переходите к способу 2: плоская таблица даст тот же обзор, но со всеми уровнями сразу.
Табло
Табло (Отладка → Табло или Ctrl+Alt+W) хранит выражения между остановками. Добавьте в него переменную с деревом и пару точечных выражений:
ДеревоОстатков.Строки.Количество()— сколько узлов верхнего уровня;ТекСтрока.Уровень()— на какой глубине сейчас цикл;ТекСтрока.Родитель— кто родитель текущего узла.
При пошаговом проходе (F10 — шаг через, F11 — шаг в процедуру) табло обновляется само. Так удобно отслеживать, как дерево заполняется в цикле.
Разворот очень большого дерева в окне отладчика может подвесить конфигуратор на десятки секунд. Если в дереве тысячи строк, не раскрывайте корень целиком — сразу пишите выражение до нужного узла или используйте способ 2.
Как анализировать свойства дерева значений выражениями
Окно «Вычислить выражение» умеет не только показывать переменные, но и вызывать методы коллекций. Это быстрый способ ответить на вопрос «есть ли нужная строка и где она».
| Задача | Выражение |
|---|---|
| Число строк верхнего уровня | Дерево.Строки.Количество() |
| Найти первую строку по значению во всех уровнях | Дерево.Строки.Найти("Гвозди", "Номенклатура", Истина) |
| Найти все строки по условию во всех уровнях | Дерево.Строки.НайтиСтроки(Новый Структура("Склад", Склад), Истина) |
| Глубина найденной строки | Найденная.Уровень() |
| Цепочка родителей | Найденная.Родитель.Родитель |
| Типы колонок | Дерево.Колонки.Сумма.ТипЗначения |
Третий параметр метода Найти() и второй у НайтиСтроки() — ВключатьПодчиненные. Со значением Истина поиск идёт по всем уровням, без него — только по коллекции, у которой метод вызван.
НайтиСтроки() возвращает массив. Разверните его в окне результата, и вы увидите все совпадения, даже если они разбросаны по разным веткам. Потом у любой из них можно посмотреть Уровень() и подняться по Родитель, чтобы понять, в какую группу она попала.
💡 Частый вопрос под отладкой: «почему итог в группе не совпадает с суммой строк». Сравните значение колонки у группы с выражением Дерево.Строки[0].Строки.Итог("Сумма"). Метод Итог() считает только по переданной коллекции, без вложенных уровней, поэтому для проверки многоуровневой группы вызывайте его на каждом уровне.
Способ 2. Выгружаем дерево в плоскую таблицу значений
Когда нужно окинуть взглядом всё дерево целиком, удобнее плоская таблица: одна строка дерева — одна строка таблицы, плюс колонки с уровнем и «адресом» узла. Такую таблицу отладчик показывает компактнее, а адрес сразу подсказывает выражение для перехода к строке.
Добавьте функцию в общий отладочный модуль (серверный, с флагом «Вызов сервера» при необходимости) или в расширение, чтобы не трогать типовую конфигурацию :
// Превращает дерево значений в плоскую таблицу.
// ОтладкаУровень — глубина строки, ОтладкаПуть — адрес вида [0][2][1].
Функция ДеревоВТаблицу(Дерево) Экспорт
Результат = Новый ТаблицаЗначений;
Результат.Колонки.Добавить("ОтладкаУровень");
Результат.Колонки.Добавить("ОтладкаПуть");
Для Каждого Колонка Из Дерево.Колонки Цикл
Результат.Колонки.Добавить(Колонка.Имя, Колонка.ТипЗначения);
КонецЦикла;
ДобавитьВетку(Дерево.Строки, Результат, "");
Возврат Результат;
КонецФункции
Процедура ДобавитьВетку(Строки, Результат, ПутьРодителя)
Индекс = 0;
Для Каждого СтрокаДерева Из Строки Цикл
Путь = ПутьРодителя + "[" + Формат(Индекс, "ЧН=0; ЧГ=0") + "]";
НоваяСтрока = Результат.Добавить();
ЗаполнитьЗначенияСвойств(НоваяСтрока, СтрокаДерева);
НоваяСтрока.ОтладкаУровень = СтрокаДерева.Уровень();
НоваяСтрока.ОтладкаПуть = Путь;
ДобавитьВетку(СтрокаДерева.Строки, Результат, Путь);
Индекс = Индекс + 1;
КонецЦикла;
КонецПроцедуры
Как пользоваться:
- Остановитесь в точке, где дерево уже заполнено.
- В окне «Вычислить выражение» введите
ОтладкаСервер.ДеревоВТаблицу(ДеревоОстатков), гдеОтладкаСервер— имя вашего модуля. - Откройте результат в отдельном окне: перед вами вся иерархия, отсортированная по обходу дерева.
- Нашли подозрительную строку с путём
[1][0][3]— значит, в исходном дереве этоДеревоОстатков.Строки[1].Строки[0].Строки[3].
Префикс Отладка у служебных колонок нужен, чтобы имена не совпали с колонками дерева. Колонка Уровень в отчётных деревьях встречается часто, и без префикса Колонки.Добавить() выдал бы ошибку о дублирующемся имени.
Новый общий модуль меняет конфигурацию. Пока сеанс отладки запущен, изменения не применятся: обновите конфигурацию базы данных и перезапустите отлаживаемый сеанс. В рабочей базе такие модули не добавляйте — делайте это в копии или в расширении.
Способ 3. Отладочный табличный документ с отступами
Табличный документ удобен, когда дерево нужно показать коллеге, сравнить два прогона или сохранить для разбора. Уровень вложенности передаётся отступом в первой колонке, поэтому иерархия читается глазами.
// Выводит дерево в табличный документ: шапка + строки с отступом по уровню.
Функция ДеревоВТабличныйДокумент(Дерево) Экспорт
ТабДок = Новый ТабличныйДокумент;
НомерКолонки = 1;
Для Каждого Колонка Из Дерево.Колонки Цикл
ТабДок.Область(1, НомерКолонки).Текст = Колонка.Имя;
НомерКолонки = НомерКолонки + 1;
КонецЦикла;
НомерСтроки = 1;
ВывестиВетку(ТабДок, Дерево.Строки, Дерево.Колонки, НомерСтроки);
Возврат ТабДок;
КонецФункции
Процедура ВывестиВетку(ТабДок, Строки, Колонки, НомерСтроки)
Для Каждого СтрокаДерева Из Строки Цикл
НомерСтроки = НомерСтроки + 1;
НомерКолонки = 1;
Для Каждого Колонка Из Колонки Цикл
ТабДок.Область(НомерСтроки, НомерКолонки).Текст = Строка(СтрокаДерева[Колонка.Имя]);
НомерКолонки = НомерКолонки + 1;
КонецЦикла;
ТабДок.Область(НомерСтроки, 1).Отступ = СтрокаДерева.Уровень();
ВывестиВетку(ТабДок, СтрокаДерева.Строки, Колонки, НомерСтроки);
КонецЦикла;
КонецПроцедуры
НомерСтроки передаётся в рекурсию по ссылке, поэтому нумерация сквозная: после вывода ветки следующая строка верхнего уровня встаёт сразу под последним ребёнком.
Где посмотреть результат:
- 🖥️ на клиенте — вызовите функцию с сервера, верните документ и покажите его:
ТабДок.Показать("Дерево остатков"); - 💾 в файл — на сервере выполните
ТабДок.Записать("C:\Temp\derevo.mxl")и откройте файл в конфигураторе черезФайл → Открыть; - 🔍 в отладчике — вычислите выражение с вызовом функции и откройте результат в отдельном окне.
Запись в файл на сервере сохраняет его на диск компьютера, где работает сервер 1С, а не на ваш. Для файловой базы это один и тот же компьютер, для клиент-серверной — нет.
Сравнение способов просмотра
| Способ | Когда подходит | Ограничения |
|---|---|---|
| Окно «Вычислить выражение» и табло | Небольшое дерево, нужен один-два узла | Медленно на больших деревьях, много ручного разворота |
Выражения Найти() / НайтиСтроки() | Известно, что искать | Нужно знать имя колонки и значение |
| Плоская таблица значений | Обзор всего дерева, поиск аномалий | Нужен отладочный модуль в конфигурации или расширении |
| Табличный документ | Сохранить, показать, сравнить два прогона | Значения превращаются в строки, ссылки уже не раскрыть |
Ошибки, которые мешают увидеть дерево
Ещё две ловушки, которые не дают ошибок, но путают выводы:
- 🔁 Изменение дерева во время просмотра. Выражение вида
Дерево.Строки.Добавить()в окне вычисления реально добавит строку в отлаживаемом коде. Любой вызов метода с побочным эффектом меняет данные, и дальнейшее выполнение пойдёт уже с ними. - 🧮 Дерево из запроса без итогов.
Выгрузить(ОбходРезультатаЗапроса.ПоГруппировкам)строит иерархию только по полям из секцииИТОГИ ПО. Если её нет, дерево получится одноуровневым, и в отладчике вы не найдёте ожидаемых групп.
Частые вопросы
Можно ли в отладчике изменить значение в строке дерева?
Как скопировать дерево из отладчика в текст?
ЗначениеВСтрокуВнутр(Дерево) и скопируйте результат. Строка неудобна для чтения, но её можно восстановить обратно через ЗначениеИзСтрокиВнутр() в тестовой обработке.Почему в дереве из запроса пустые значения у групп?
Работает ли просмотр дерева значений в 1С:EDT?
Найти() и вызовом отладочных функций работают и там.