При разработке механизмов контроля в 1С программисты часто сталкиваются с задачей ограничения редактирования документа по прошествии определенного времени. Например, запретить изменение цен или комментариев, если с момента создания документа прошло более 10 дней. На первый взгляд задача кажется простой, но она таит в себе несколько подводных камней: от особенностей арифметики дат до логических ошибок, которые делают проверку бесполезной.
В этой статье мы подробно разберем, как правильно реализовать проверку даты, избегая типичных ошибок начинающих разработчиков.
Рассмотрим ситуацию: разработчик хочет прибавить к дате документа 10 дней. Он пишет код Лимит = ДатаДокумента + 10. В системе 1С тип Дата хранит значение с точностью до секунды. Любое числовое прибавление к дате воспринимается системой как прибавление секунд.
Таким образом, прибавив 10, мы получаем дату, которая наступит всего через 10 секунд. Чтобы корректно прибавить 10 дней, необходимо перевести их в секунды: 10 дней * 24 часа * 60 минут * 60 секунд = 864 000 секунд.
Разберем правильный пример расчета лимита:
КоличествоДней = 10;
СекундВСутках = 86400; // 24 * 3600
ЛимитДнейИзменения = ЭтотОбъект.Дата + (КоличествоДней * СекундВСутках);
Проанализируем фрагмент кода из обсуждения: Если ЭтотОбъект.Дата >= (ЭтотОбъект.Дата + 10 дней) Тогда .... Это условие никогда не будет выполнено (не станет истинным). Если мы берем текущую дату документа и сравниваем её с той же датой, увеличенной на 10 дней, результат всегда будет "Ложь", так как исходное число не может быть больше самого себя, увеличенного на положительное значение.
Чтобы проверка имела смысл, дату документа нужно сравнивать с текущим моментом времени (когда пользователь нажал кнопку "Записать").
Для корректной проверки срока давности документа мы должны сопоставить дату в документе с текущим временем на сервере. Рассмотрим, как это сделать в процедуре ПередЗаписью модуля объекта:
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
// Если документ новый, проверку можно пропустить
Если ЭтоНовый() Тогда
Возврат;
КонецЕсли;
// Исключаем срабатывание при обмене данными
Если ОбменДанными.Загрузка Тогда
Возврат;
КонецЕсли;
// 1. Получаем текущую дату сервера (сеанса)
ТекущийМомент = ТекущаяДатаСеанса();
// 2. Рассчитываем границу допустимого редактирования (дата документа + 10 дней)
// Используем КонецДня, чтобы пользователь мог редактировать весь 10-й день
ДатаОграничения = КонецДня(ЭтотОбъект.Дата + (10 * 86400));
// 3. Сравниваем: если сейчас времени больше, чем дата ограничения - запрещаем
Если ТекущийМомент > ДатаОграничения Тогда
Сообщить("Срок редактирования документа (10 дней) истек!");
Отказ = Истина; // Отменяем запись
КонецЕсли;
КонецПроцедуры
Важное примечание по безопасности: Всегда используйте ТекущаяДатаСеанса() вместо ТекущаяДата(). Функция ТекущаяДата() может вернуть время с локального компьютера пользователя, и хитрый пользователь сможет обойти вашу проверку, просто переведя часы в Windows. ТекущаяДатаСеанса() берет время с сервера 1С, которому можно доверять.
Если в вашей учетной политике 10 дней считаются по календарным дням, а не по секундам, проанализируем ситуацию с временем. Документ может быть создан в 8:00 утра или в 23:00 вечера. Чтобы избежать споров с пользователями, рекомендуется приводить обе даты к началу или концу дня с помощью функций НачалоДня() и КонецДня().
Посмотрим на пример использования НачалоДня для сравнения только календарных дат:
Если НачалоДня(ТекущаяДатаСеанса()) > НачалоДня(ЭтотОбъект.Дата + 10 * 86400) Тогда
// Условие сработает, только когда наступит 11-й календарный день
КонецЕсли;
Если пользователь в форме старого документа изменит его дату на текущую, а затем нажмет "Записать", ваша проверка ЭтотОбъект.Дата может сработать некорректно, так как ЭтотОбъект содержит уже измененные пользователем данные.
Проанализируем, как получить "честную" дату документа из базы данных. Для этого нужно обратиться к ссылке объекта:
// Ссылка хранит данные, которые записаны в БД на данный момент
СтараяДатаДокумента = ЭтотОбъект.Ссылка.Дата;
Если ТекущаяДатаСеанса() > (СтараяДатаДокумента + 10 * 86400) Тогда
// Пользователь пытается обмануть систему, меняя дату в форме
Сообщить("Нельзя редактировать документ, созданный более 10 дней назад!");
Отказ = Истина;
КонецЕсли;
В некоторых обсуждениях встречается метод сравнения через преобразование в строку: Формат(Дата, "ДЛФ=Д"). Мы не рекомендуем этот способ для использования в циклах или при частой записи, так как строковые операции и обратное преобразование в дату создают лишнюю нагрузку на процессор. Использование встроенных функций НачалоДня() и КонецДня() гораздо эффективнее и считается правильным стилем программирования в 1С.
Если вы работаете в современной конфигурации (например, ERP, КА 2 или Бухгалтерия 3.0), рассмотрите возможность использования механизма "Даты запрета изменения данных". Это стандартный функционал БСП (Библиотеки Стандартных Подсистем), который позволяет настраивать подобные ограничения без написания кода, в пользовательском режиме, с привязкой к ролям и конкретным пользователям.
Подведем итог:
ТекущаяДатаСеанса()).ЭтотОбъект.Ссылка.Дата.НачалоДня(), чтобы избежать проблем с точностью до секунд.