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