Работа с датами в языке запросов 1С — одна из базовых и одновременно коварных тем для разработчика. Часто возникает задача не просто вывести дату документа, но и произвести с ней математические вычисления: прибавить срок кредита, вычислить количество дней до оплаты или определить просрочку. В этой статье мы разберем, как правильно комбинировать встроенные функции запроса, почему нельзя использовать псевдонимы полей в расчетах и как избежать типичных ошибок при работе со временем.
Для решения задач, описанных в теме, нам понадобятся две ключевые функции языка запросов. Рассмотрим их подробнее, прежде чем переходить к сложным формулам.
Количество — должен иметь числовой тип данных.ДатаКонца меньше ДатаНачала, результат будет отрицательным.Проанализируем ситуацию, с которой столкнулся автор темы. Он попытался сначала рассчитать срок оплаты, присвоить ему псевдоним СрокОплаты, а затем использовать этот псевдоним в следующей строке запроса для вычисления просрочки:
ВЫБРАТЬ
ДОБАВИТЬКДАТЕ(Реализация.Дата, ДЕНЬ, Реализация.Договор.ДопустимоеЧислоДнейЗадолженности) КАК СрокОплаты,
РАЗНОСТЬДАТ(СрокОплаты, &НаДату, ДЕНЬ) КАК ДнейПросрочки
ИЗ
Документ.РеализацияТоваровУслуг КАК Реализация
При выполнении такого кода система выдаст ошибку "Поле не найдено 'СрокОплаты'". Выясним причину: в языке запросов 1С (как и в стандартном SQL) псевдоним поля, определенный в секции ВЫБРАТЬ, недоступен для использования в расчетах внутри этого же списка выбора. Псевдонимы нужны для именования колонок в результирующей таблице, но расчетный механизм "не видит" их в процессе формирования строк.
Самый прямой способ решить задачу — вкладывать одну функцию в другую. Рассмотрим, как составить формулу для расчета дней просрочки, исходя из выражения: &НаДату - (ДатаДокумента + ДопустимоеЧислоДней).
Посмотрим на пример правильного написания вложенной конструкции:
ВЫБРАТЬ
РАЗНОСТЬДАТ(
ДОБАВИТЬКДАТЕ(Реализация.Дата, ДЕНЬ, Реализация.Договор.ДопустимоеЧислоДнейЗадолженности),
&НаДату,
ДЕНЬ
) КАК ДнейПросрочки
ИЗ
Документ.РеализацияТоваровУслуг КАК Реализация
В данном случае функция ДОБАВИТЬКДАТЕ вычисляет дату, до которой должна быть произведена оплата, и передает этот результат в качестве первого аргумента функции РАЗНОСТЬДАТ. Вторым аргументом выступает параметр запроса &НаДату.
Если формула становится слишком громоздкой (например, если нужно использовать рассчитанный срок оплаты в нескольких местах или условиях ГДЕ), рекомендуется использовать вложенные запросы. Это сделает код более читаемым и позволит обращаться к рассчитанному полю по имени.
Разберем этот метод по шагам:
ВЫБРАТЬ
ВЗ.СрокОплаты,
РАЗНОСТЬДАТ(ВЗ.СрокОплаты, &НаДату, ДЕНЬ) КАК ДнейПросрочки
ИЗ
(ВЫБРАТЬ
ДОБАВИТЬКДАТЕ(Реализация.Дата, ДЕНЬ, Реализация.Договор.ДопустимоеЧислоДнейЗадолженности) КАК СрокОплаты
ИЗ
Документ.РеализацияТоваровУслуг КАК Реализация) КАК ВЗ
Во внутреннем запросе мы вычислили СрокОплаты. Во внешнем запросе мы уже можем обращаться к этому полю так же, как к любому другому реквизиту базы данных.
При расчетах в реальных конфигурациях (УТ 11, ERP, КА) часто возникают ситуации, когда в договоре не указано допустимое число дней задолженности. В этом случае значение поля будет NULL, и вся формула вернет NULL.
Рекомендуется всегда использовать функцию ЕСТЬNULL для числовых параметров, участвующих в расчетах дат. Проанализируем безопасный вариант кода:
ДОБАВИТЬКДАТЕ(
Реализация.Дата,
ДЕНЬ,
ЕСТЬNULL(Реализация.Договор.ДопустимоеЧислоДнейЗадолженности, 0)
)
Это гарантирует, что даже если в настройках договора пусто, запрос прибавит 0 дней к дате реализации, а не "сломает" весь расчет.
Функция РАЗНОСТЬДАТ считает количество переходов через полночь (00:00:00). Если дата реализации — 01.01.2023 14:00:00, а параметр &НаДату — 02.01.2023 09:00:00, разница будет составлять 1 день, хотя фактически прошло меньше 24 часов.
Чтобы расчет был более строгим и не зависел от времени документа, полезно использовать функцию НАЧАЛОПЕРИОДА. Рассмотрим пример:
РАЗНОСТЬДАТ(
НАЧАЛОПЕРИОДА(ДОБАВИТЬКДАТЕ(Реализация.Дата, ДЕНЬ, Срок), ДЕНЬ),
НАЧАЛОПЕРИОДА(&НаДату, ДЕНЬ),
ДЕНЬ
)
Это приведет все значения к 00:00:00 и позволит сравнивать "чистые" календарные дни просрочки.
Несмотря на мощь языка запросов, у него есть ограничения. Если ваша задача требует учета рабочих дней по производственному календарю, стандартных функций ДОБАВИТЬКДАТЕ будет недостаточно. В таких случаях придется выполнять левое соединение с регистром сведений "Производственный календарь" или "Графики работы", что значительно усложняет структуру запроса.
Также помните об оптимизации: если вы используете сложные вычисления с датами в секции ГДЕ, СУБД не сможет использовать индексы по полям дат, что приведет к замедлению отчета на больших объемах данных. В таких случаях лучше заранее рассчитать "граничную дату" в коде на языке 1С и передать её в запрос как готовый параметр.