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