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