Почему периодические реквизиты справочника возвращают пустое значение в 1С 7.7 и как это исправить?

Программист 1С v7.7 IT и автоматизация бизнеса
← На главную

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

Разберем детально, почему так происходит, на примере практической задачи: нам необходимо обойти список объектов на обслуживании, проверить дату замены ЭКЛЗ (которая является периодическим реквизитом) и сопоставить её с выбранным месяцем года. Попутно мы выясним причины типичных ошибок позиционирования в таблицах значений и научимся их избегать.

Периодические реквизиты: почему они возвращают пустое значение?

Главная причина, по которой реквизит ДатаЗаменыЭКЛЗ возвращал пустое значение при обращении к нему напрямую через точку (СпрОбъекты.ДатаЗаменыЭКЛЗ), заключается в том, что данный реквизит в метаданных определен как периодический. В 1С 7.7 периодические реквизиты хранят историю изменений, привязанную к датам, и платформа не может выдать конкретное значение без указания даты среза.

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

Конфликт методов ИспользоватьДату() и Получить()

Проанализируем две стратегии получения периодических данных, которые часто конфликтуют между собой в кодовой базе:

  1. Использование метода ИспользоватьДату() для объекта справочника. Если перед выборкой элементов мы устанавливаем дату выборки для всего справочника, то платформа автоматически переводит все его периодические реквизиты в режим простых значений. В этом случае метод Получить() вызывать нельзя, иначе возникнет ошибка Значение не представляет агрегатный объект.
  2. Использование метода Получить() для конкретного реквизита. Если дата для справочника в целом не зафиксирована, реквизит остается периодическим агрегатным объектом. Для получения значения мы обязаны использовать метод Реквизит.Получить(НужнаяДата).

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


// Получение периодического значения на определенную дату
ДатаЭКЛЗ = СпрОбъекты.ДатаЗаменыЭКЛЗ.Получить(РабочаяДата());

А теперь рассмотрим правильный код, если ранее для справочника был применен метод ИспользоватьДату():


СпрОбъекты = СоздатьОбъект("Справочник.БП_ОбъектыНаОбслуживании");
СпрОбъекты.ИспользоватьВладельца(ТекущийЭлемент);
// Устанавливаем дату для автоматического извлечения периодических реквизитов
СпрОбъекты.ИспользоватьДату(РабочаяДата()); 
СпрОбъекты.ВыбратьЭлементы();
Пока СпрОбъекты.ПолучитьЭлемент() = 1 Цикл
    // Теперь реквизит возвращает простое значение (дату), метод Получить() НЕ нужен!
    ДатаЭКЛЗ = СпрОбъекты.ДатаЗаменыЭКЛЗ; 
КонецЦикла;

Производительность при работе с периодикой (Проблема N+1 запросов)

Важно помнить о производительности, особенно при работе с DBF или SQL-версией 1С 7.7. Когда мы в цикле обхода справочника пишем СпрОбъекты.Сотрудник.Получить(ТекущаяДата), платформа выполняет отдельный SQL-запрос к базе данных для каждой строки справочника. На больших объемах данных это приводит к критическому замедлению работы интерфейса.

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


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

Как правильно обходить Таблицу Значений в 1С 7.7

Еще одна частая ошибка, из-за которой реквизиты могут казаться пустыми или неверно обработанными, кроется в нарушении логики обхода строк Таблицы Значений (ТЗ). В 1С 7.7 крайне важен строгий порядок позиционирования курсора ТЗ.

Рассмотрим ошибочный фрагмент кода:


ТабОбъекты.ВыбратьСтроки();
// Ошибка: Попытка обратиться к реквизиту строки ДО вызова ПолучитьСтроку()
Если ДатаМесяц(ТабОбъекты.Объект.ДатаЗаменыЭКЛЗ) = НомерМесяца Тогда
    Пока ТабОбъекты.ПолучитьСтроку() = 1 Цикл
        // Код обработки
    КонецЦикла;
КонецЕсли;

В приведенном выше примере условие Если проверяется до того, как мы позиционировались на конкретной строке таблицы значений. Метод ВыбратьСтроки() лишь сбрасывает указатель в начало ТЗ, но не выбирает первую строку. В результате обращение к ТабОбъекты.Объект возвращает пустое значение, и условие никогда не выполняется корректно.

Перепишем алгоритм правильно:


ТабОбъекты.ВыбратьСтроки();
Пока ТабОбъекты.ПолучитьСтроку() = 1 Цикл
    // Теперь мы гарантированно позиционированы на текущей строке ТЗ
    Если ПустоеЗначение(ТабОбъекты.Объект) = 0 Тогда
        ДатаРеквизита = ТабОбъекты.Объект.ДатаЗаменыЭКЛЗ.Получить(РабочаяДата());
        Если ДатаМесяц(ДатаРеквизита) = НомерМесяца Тогда
            // Выполняем действия с объектом
        КонецЕсли;
    КонецЕсли;
КонецЦикла;

Решение проблемы с отсутствующими колонками и пометками

Ошибка Поле агрегатного объекта не обнаружено (Пометка) возникает, когда мы пытаемся обратиться к колонке таблицы значений, которой физически не существует в структуре этой ТЗ, либо когда мы перепутали объект ТЗ со справочником.

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


ТабОбъекты = СоздатьОбъект("ТаблицаЗначений");
ТабОбъекты.НоваяКолонка("Объект", "Справочник.БП_ОбъектыНаОбслуживании");
// Создаем числовую колонку для хранения признака пометки (0 - снята, 1 - установлена)
ТабОбъекты.НоваяКолонка("Пометка", "Число", 1); 

Чтобы предоставить пользователю возможность интерактивно отмечать строки в ТЗ на форме, можно настроить процедуру обработки клика по ячейке:


Процедура ПриНажатииНаЯчейкуТаблицы(Элемент, Колонка)
    Если Колонка = "Пометка" Тогда
        ТабОбъекты.Пометка = 1 - ТабОбъекты.Пометка; // Переключаем 0 в 1 и наоборот
    КонецЕсли;
КонецПроцедуры

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

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

  1. Всегда проверяйте в Конфигураторе, является ли реквизит справочника периодическим.
  2. При работе с периодическими реквизитами в циклах четко разделяйте использование методов ИспользоватьДату() (на уровне справочника) и Получить() (на уровне конкретного периодического реквизита).
  3. Никогда не обращайтесь к реквизитам строк ТЗ до вызова метода ПолучитьСтроку() внутри цикла.
  4. Своевременно инициализируйте все необходимые колонки в динамически создаваемых таблицах значений методом НоваяКолонка() с указанием типов данных для предотвращения ошибок типизации.
← На главную