Как исправить ошибку «Поле объекта не обнаружено (ДанныеПосещений)» и реализовать динамическую таблицу посещаемости?

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

При разработке прикладных решений на платформе 1С:Предприятие 8.3 начинающие разработчики часто сталкиваются с ситуацией, когда код, который кажется логически верным, вызывает ошибку выполнения. Одной из таких проблем является обращение к реквизитам объекта в контексте управляемой формы, особенно если эти реквизиты имеют специфический тип данных, такой как ХранилищеЗначения.

В этой статье мы подробно разберем, почему возникает ошибка «Поле объекта не обнаружено», проанализируем разницу между клиентским и серверным контекстом и изучим правильные подходы к реализации динамических таблиц (так называемых «шахматок») для учета посещаемости тренировок или занятий.

Причина возникновения ошибки: почему поле «не обнаружено»?

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

  1. Доступность типов данных на клиенте: Реквизиты с типом ХранилищеЗначения физически не передаются с сервера на клиент. Платформа 1С делает это намеренно для оптимизации трафика. Поскольку хранилище может содержать тяжелые данные (картинки, файлы, сложные структуры), передача их в данные формы привела бы к огромным задержкам. Поэтому на клиенте в объекте ДанныеФормыКоллекция этого поля просто не существует.
  2. Контекст ДанныеФормы vs Объект: На клиенте вы работаете не с самим объектом базы данных, а с его «проекцией» — данными формы. Если поле не включено в состав данных, доступных на клиенте, обращение к нему вызовет исключение.

Разберем по шагам: исправление контекста

Первым делом нам необходимо перенести логику обработки данных на сервер. Только на сервере платформа позволяет полноценно работать с объектом метаданных и всеми его полями, включая ХранилищеЗначения. Проанализируем, как должен измениться код.

Вместо прямой попытки записи в реквизит на клиенте, мы создаем серверную процедуру:


&НаСервере
Процедура ЗаполнитьДанныеПосещаемостиНаСервере()
    // Получаем таблицу дат из реквизита объекта
    ТаблицаДаты = Объект.ДатыТренеровок;
    
    Для Каждого СтрокаПосещаемость Из Объект.Посещаемость Цикл
        // Создаем новую структуру для хранения данных
        ДанныеДляХранения = Новый Структура;
        
        Для Каждого СтрокаДаты Из ТаблицаДаты Цикл
            ДатаТренировки = СтрокаДаты.Дата;
            // Устанавливаем значение по умолчанию (Ложь - не посетил)
            ДанныеДляХранения.Вставить(Строка(ДатаТренировки), Ложь); 
        КонецЦикла;
        
        // ВАЖНО: В ХранилищеЗначения нельзя записать структуру напрямую через "="
        // Нужно инициализировать новый объект типа ХранилищеЗначения
        СтрокаПосещаемость.ДанныеПосещений = Новый ХранилищеЗначения(ДанныеДляХранения);
    КонецЦикла;
КонецПроцедуры

Обратите внимание на использование функции Строка() при вставке даты в ключи структуры. В 1С ключом структуры может быть только строка, соответствующая правилам идентификатора, либо просто любая строка (в зависимости от версии платформы), но использование типа Дата напрямую может привести к трудночитаемым ключам.

Проблема визуализации: динамические колонки

Даже если мы правильно сохраним данные в ХранилищеЗначения, пользователь не увидит их на форме автоматически. Реквизит типа ХранилищеЗначения нельзя просто «вытащить» на форму как колонку. Для реализации интерфейса «Шахматка» нам нужно программно изменять состав реквизитов формы.

Рассмотрим правильный алгоритм создания динамической таблицы:

  1. Удаление старых колонок: При изменении состава дат нужно удалить предыдущие динамические реквизиты.
  2. Добавление новых реквизитов формы: Используется метод ИзменитьРеквизиты().
  3. Создание элементов формы: Создание колонок (типа ПолеФормы) и привязка их к созданным реквизитам.

Посмотрим на пример кода, который формирует колонки для каждой даты:


&НаСервере
Процедура СформироватьКолонкиНаСервере()
    
    МассивДобавляемыхРеквизитов = Новый Массив;
    МассивУдаляемыхРеквизитов = Новый Массив;
    
    // Сначала соберем список уже существующих динамических колонок для удаления
    // (Чтобы не плодить их при каждом нажатии кнопки)
    Для Каждого Реквизит Из ПолучитьРеквизиты("Объект.Посещаемость") Цикл
        Если Лев(Реквизит.Имя, 5) = "Дата_" Тогда
            МассивУдаляемыхРеквизитов.Добавить("Объект.Посещаемость." + Реквизит.Имя);
        КонецЕсли;
    КонецЦикла;
    
    // Добавляем новые реквизиты на основе таблицы дат
    Для Каждого СтрокаДаты Из Объект.ДатыТренеровок Цикл
        ИмяРеквизита = "Дата_" + Формат(СтрокаДаты.Дата, "ДФ=ddMMyyyy");
        ТипБулево = Новый ОписаниеТипов("Булево");
        НовыйРеквизит = Новый РеквизитФормы(ИмяРеквизита, ТипБулево, "Объект.Посещаемость", "Дата: " + Формат(СтрокаДаты.Дата, "ДЛФ=D"));
        МассивДобавляемыхРеквизитов.Добавить(НовыйРеквизит);
    КонецЦикла;
    
    // Применяем изменения структуры формы
    ИзменитьРеквизиты(МассивДобавляемыхРеквизитов, МассивУдаляемыхРеквизитов);
    
    // Теперь создаем элементы интерфейса (колонки в таблице)
    Для Каждого Реквизит Из МассивДобавляемыхРеквизитов Цикл
        НоваяКолонка = Элементы.Добавить(Реквизит.Имя, Тип("ПолеФормы"), Элементы.Посещаемость);
        НоваяКолонка.Вид = ВидПоляФормы.ПолеФлажка;
        НоваяКолонка.ПутьКДанным = "Объект.Посещаемость." + Реквизит.Имя;
    КонецЦикла;

КонецПроцедуры

Как связать данные из ХранилищаЗначения с колонками формы?

Проанализируем важный нюанс: колонки, которые мы создали программно, существуют только «в памяти» формы. Данные в них не попадут сами собой из ХранилищаЗначения. Выясним, как организовать обмен данными:

При чтении (открытии формы): Мы должны извлечь структуру из ХранилищаЗначения каждой строки табличной части и распределить значения по программно созданным реквизитам Дата_....

При записи документа: Нам нужно выполнить обратную операцию — собрать значения из колонок Дата_... обратно в структуру и упаковать ее в ХранилищеЗначения для сохранения в базу данных.

Пример кода для выгрузки данных перед записью:


&НаСервере
Процедура ПередЗаписьюНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
    
    Для Каждого СтрокаТЧ Из ТекущийОбъект.Посещаемость Цикл
        СтруктураПосещений = Новый Структура;
        
        Для Каждого СтрокаДаты Из ТекущийОбъект.ДатыТренеровок Цикл
            ИмяПоля = "Дата_" + Формат(СтрокаДаты.Дата, "ДФ=ddMMyyyy");
            // Читаем значение из динамического реквизита строки
            // В ТекущийОбъект эти реквизиты не попадут, к ним нужно обращаться через Данные Формы
            ЗначениеФлажка = ЭтаФорма.Объект.Посещаемость.НайтиПоИдентификатору(СтрокаТЧ.ПолучитьИдентификатор())[ИмяПоля];
            СтруктураПосещений.Вставить(ИмяПоля, ЗначениеФлажка);
        КонецЦикла;
        
        СтрокаТЧ.ДанныеПосещений = Новый ХранилищеЗначения(СтруктураПосещений);
    КонецЦикла;
    
КонецПроцедуры

Альтернативный (рекомендуемый) подход

Проанализируем, почему использование ХранилищаЗначения может быть не самым оптимальным решением. Если вам нужно будет построить отчет (например, «Список прогульщиков за месяц»), вы не сможете сделать это простым запросом, так как данные внутри хранилища недоступны для языка SQL. Платформе придется считывать каждый документ, распаковывать хранилище, что крайне медленно.

Более правильная архитектура:

  1. Создать отдельную скрытую табличную часть (или регистр сведений) со структурой: Студент | Дата | Посетил (Булево).
  2. На форме продолжать отображать «красивую шахматку», используя программные реквизиты, как описано выше.
  3. При сохранении — разворачивать данные из колонок формы в строки скрытой табличной части.

Этот метод сочетает в себе удобство пользовательского интерфейса и высокую производительность системы при формировании отчетов и анализе данных.

Резюме

Подведем итог нашего разбора. Ошибка Поле объекта не обнаружено в данном контексте — это сигнал о том, что вы пытаетесь работать с серверными данными на клиенте. Чтобы решить задачу, мы:

Используя предложенные методики, вы сможете создавать сложные интерактивные формы, которые будут работать стабильно и соответствовать стандартам разработки .

← На главную