Как правильно получить последнюю дату записи из регистра сведений и избежать ошибок при расчете интервалов

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

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

Разбор типичной проблемы: почему запрос выдает не те данные

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

Основная причина неверных данных в том, что при использовании виртуальной таблицы СрезПоследних без параметров, система выбирает последние записи вообще по всем комбинациям измерений, которые есть в регистре. Если мы не укажем конкретный СистемныйБлок, запрос вернет таблицу со всеми последними записями, и при обходе выборки в цикле мы рискуем получить значение из последней обработанной строки, которая может не относиться к нужному нам объекту.

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

Рассмотрим правильный подход к построению запроса. Одной из самых частых ошибок является наложение условий в секции ГДЕ вместо использования параметров виртуальной таблицы. Проанализируем, почему это плохо: когда условие стоит в ГДЕ, СУБД сначала получает все последние записи из регистра, и только потом фильтрует их. Если в регистре миллион записей, это приведет к колоссальным потерям производительности.

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


&НаСервере
Функция ПолучитьПоследнююДатуЗаписи(СистемныйБлок)
    
    Запрос = Новый Запрос;
    // Условия отбора передаем прямо в параметры виртуальной таблицы
    Запрос.Текст = 
        "ВЫБРАТЬ
        |	СпецификацияСрезПоследних.Период КАК ДатаПоследнейЗаписи
        |ИЗ
        |	РегистрСведений.Спецификация.СрезПоследних(&Период, СистемныйБлок = &СистемныйБлок) КАК СпецификацияСрезПоследних";
    
    Запрос.УстановитьПараметр("Период", ТекущаяДата());
    Запрос.УстановитьПараметр("СистемныйБлок", СистемныйБлок);
    
    РезультатЗапроса = Запрос.Выполнить();
    Выборка = РезультатЗапроса.Выбрать();
    
    Если Выборка.Следующий() Тогда
        Возврат Выборка.ДатаПоследнейЗаписи;
    Иначе
        // Если записей нет, возвращаем пустую дату
        Возврат '00010101';
    КонецЕсли;
    
КонецФункции

В данном примере мы используем поле Период. В периодических регистрах сведений Период — это и есть дата, на которую запись актуальна. Если ваш бизнес-процесс требует использования именно даты документа-регистратора, можно использовать Регистратор.Дата, но Период обычно работает быстрее и надежнее.

Устранение ошибки «Преобразование значения к типу Число»

Теперь разберем причину падения программы при расчете: СледующаяДата = ДатаИзмененияСостава + (7*24*3600);. Эта ошибка возникает, когда переменная ДатаИзмененияСостава содержит значение Неопределено или NULL.

Это происходит в двух случаях:

  1. Запрос не вернул ни одной строки (например, для этого системного блока еще нет ни одной записи в регистре). В этом случае функция возвращает Неопределено, если не предусмотрено иное значение по умолчанию.
  2. В запросе использовалось ЛЕВОЕ СОЕДИНЕНИЕ, и для левой таблицы не нашлось соответствия в правой, что породило значение NULL.

Чтобы этого избежать, проанализируем код на стороне клиента и добавим проверку:


&НаКлиенте
Процедура Изменить(Команда)
    
    // Предположим, СистемныйБлок — это реквизит формы
    ДатаПоследнейЗаписи = ПолучитьПоследнююДатуЗаписи(Объект.СистемныйБлок);
    
    // Проверяем, была ли вообще найдена дата
    Если ЗначениеЗаполнено(ДатаПоследнейЗаписи) Тогда
        
        КоличествоСекундВНеделе = 7 * 24 * 3600;
        СледующаяДатаРазрешения = ДатаПоследнейЗаписи + КоличествоСекундВНеделе;
        
        РазницаВСекундах = СледующаяДатаРазрешения - ТекущаяДата();
        
        Если РазницаВСекундах > 0 Тогда
            // Сообщаем пользователю, сколько осталось ждать
            ТекстПредупреждения = "Изменение запрещено! До возможности редактирования осталось: " + РазбитьСекунды(РазницаВСекундах);
            ПоказатьПредупреждение(, ТекстПредупреждения, 60, "Ограничение периода");
            Возврат;
        КонецЕсли;
        
    КонецЕсли;
    
    // Если дата пустая или неделя прошла — открываем форму
    ОткрытьФорму("РегистрСведений.Спецификация.ФормаЗаписи");
    
КонецПроцедуры

Нюансы работы с методом Следующий()

Посмотрим на работу с объектом ВыборкаИзРезультатаЗапроса. Когда нам нужно получить всего одно значение (например, дату самой последней записи), использование цикла Пока Выборка.Следующий() Цикл ... КонецЦикла является избыточным, хотя и рабочим вариантом. Профессиональнее использовать конструкцию Если Выборка.Следующий() Тогда. Это явно указывает другим разработчикам, что мы ожидаем и обрабатываем только одну строку данных.

Также важно помнить о том, что если вы используете ВЫБРАТЬ ПЕРВЫЕ 1 в тексте запроса, это обязательно должно сопровождаться инструкцией УПОРЯДОЧИТЬ ПО. В случае с виртуальной таблицей СрезПоследних система сама гарантирует получение актуальной записи на указанный период, поэтому дополнительные сортировки обычно не требуются, если отбор по измерениям уникален.

Рекомендации по улучшению кода

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

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

← На главную