При разработке учетных механизмов в 1С часто возникает задача отслеживания сроков: срока действия визы, окончания договора, поверки оборудования или приближающегося дня рождения. Начинающие разработчики нередко пытаются сохранить количество оставшихся дней непосредственно в реквизит объекта справочника или документа при его записи. Однако на следующий день сохраненное число становится неактуальным, так как текущая дата изменилась, а объект в базе данных остался прежним.
Проанализируем, почему динамически изменяемые значения нельзя хранить в реквизитах базы данных, и подробно разберем правильные способы расчета и отображения таких показателей на управляемой форме элемента и в форме списка.
Если записать в реквизит справочника результат выражения разницы дат, значение останется статичным до тех пор, пока пользователь вручную не откроет и не перепишет этот элемент. Хранение таких данных в базе потребовало бы ежедневной регламентной перезаписи всех элементов справочника, что создает неоправданную нагрузку на базу данных и искажает историю изменений объектов.
В платформе 1С действует фундаментальное правило: динамически изменяемые значения, зависящие от текущей даты или времени, должны вычисляться "на лету" при открытии формы или при формировании динамического списка.
Рассмотрим типовую ошибку при попытке записать значение напрямую в элементы формы на клиенте. Если написать код вида Элементы.ОсталосьДней = ..., платформа выдаст исключение Поле объекта недоступно для записи, поскольку коллекция Элементы предназначена исключительно для управления визуальными элементами управления, а не для хранения данных.
Разберем по шагам правильный алгоритм вывода данных через реквизит формы:
ОсталосьДней с типом Число.Поле надписи либо Поле ввода с включенным свойством ТолькоПросмотр = Истина.Посмотрим на пример правильной реализации в модуле формы элемента:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
РассчитатьОстатокДней();
КонецПроцедуры
&НаСервере
Процедура ПриЧтенииНаСервере(ТекущийОбъект)
РассчитатьОстатокДней();
КонецПроцедуры
&НаСервере
Процедура РассчитатьОстатокДней()
Если ЗначениеЗаполнено(Объект.ДатаСледующегоВыезда) Тогда
// Используем ТекущаяДатаСеанса для учета часового пояса пользователя
ТекущаяДата = НачалоДня(ТекущаяДатаСеанса());
ДатаВыезда = НачалоДня(Объект.ДатаСледующегоВыезда);
ОсталосьДней = (ДатаВыезда - ТекущаяДата) / 86400;
Иначе
ОсталосьДней = 0;
КонецЕсли;
КонецПроцедуры
Обратите внимание на использование функции ТекущаяДатаСеанса() вместо ТекущаяДата(). Это гарантирует корректный расчет с учетом локального времени пользователя, если сервер 1С находится в другом часовом поясе.
Если не требуется создавать отдельный реквизит формы, можно воспользоваться элементом формы вида Декорация - Надпись. Разберем этот вариант:
ДекорацияОсталосьДней.Заголовок для этой декорации.Посмотрим на пример кода:
&НаКлиенте
Процедура ПриОткрытии(Отказ)
Если ЗначениеЗаполнено(Объект.ДатаСледующегоВыезда) Тогда
ДнейДоСобытия = (НачалоДня(Объект.ДатаСледующегоВыезда) - НачалоДня(ТекущаяДатаСеанса())) / 86400;
Элементы.ДекорацияОсталосьДней.Заголовок = "Осталось дней: " + Формат(ДнейДоСобытия, "ЧГ=0");
Иначе
Элементы.ДекорацияОсталосьДней.Заголовок = "Дата выезда не указана";
КонецЕсли;
КонецПроцедуры
Когда требуется видеть оставшееся количество дней сразу по всем сотрудникам в таблице справочника, расчет на форме элемента не подходит. Для этого используют возможности запроса в динамическом списке формы списка.
Разберем по шагам настройку динамического списка:
Список.ПроизвольныйЗапрос и перейдем к редактированию текста запроса.РАЗНОСТЬДАТ:
ВЫБРАТЬ
СправочникВизы.Ссылка КАК Ссылка,
СправочникВизы.ПометкаУдаления КАК ПометкаУдаления,
СправочникВизы.Наименование КАК Наименование,
СправочникВизы.ДатаСледующегоВыезда КАК ДатаСледующегоВыезда,
РАЗНОСТЬДАТ(&ТекущаяДата, СправочникВизы.ДатаСледующегоВыезда, ДЕНЬ) КАК ДнейДоВыезда
ИЗ
Справочник.Визы КАК СправочникВизы
Поскольку в языке запросов 1С отсутствует функция получения текущей системной даты, ее необходимо передавать в виде параметра из модуля формы списка.
В модуле формы списка напишем процедуру установки параметра:
&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
Список.Параметры.УстановитьЗначениеПараметра("ТекущаяДата", НачалоДня(ТекущаяДатаСеанса()));
КонецПроцедуры
После добавления поля ДнейДоВыезда в таблицу динамического списка пользователи смогут не только видеть актуальный остаток дней, но и выполнять по этой колонке сортировку, группировку и отбор стандартными средствами платформы.
Для визуального контроля приближающихся сроков настроим подсветку строк в динамическом списке с помощью механизма УсловноеОформление. Например, выделим красным цветом записи, где срок выезда уже наступил или наступает в течение ближайших трех дней.
Посмотрим на пример программной настройки условного оформления в процедуре ПриСозданииНаСервере:
&НаСервере
Процедура НастроитьОформлениеСроков()
ЭлементОформления = УсловноеОформление.Элементы.Добавить();
// Определяем оформление: красный цвет текста
ЭлементОформления.Оформление.УстановитьЗначениеПараметра("ЦветТекста", WebЦвета.Красный);
ЭлементОформления.Оформление.УстановитьЗначениеПараметра("Шрифт", Новый Шрифт(,, Истина)); // Полужирный
// Задаем условие: ДнейДоВыезда <= 3
ЭлементУсловия = ЭлементОформления.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементУсловия.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Список.ДнейДоВыезда");
ЭлементУсловия.ВидСравнения = ВидСравненияКомпоновкиДанных.МеньшеИлиРавно;
ЭлементУсловия.ПравоеЗначение = 3;
// Указываем оформляемые поля
ОформляемоеПоле = ЭлементОформления.Поля.Элементы.Добавить();
ОформляемоеПоле.Поле = Новый ПолеКомпоновкиДанных("ДнейДоВыезда");
КонецПроцедуры
Если рассчитанные дни требуются не только для визуального отображения пользователю, но и для автоматической отправки уведомлений на почту или формирования служебных заданий, создают Регламентное задание. Такое задание выполняется на сервере по расписанию (например, ежедневно в полночь), выбирает запросом объекты с критическим сроком и выполняет необходимые фоновые действия без участия пользователя.