Как использовать сложные формулы с датами в запросах 1С: расчет срока оплаты и просрочки

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

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

Основные функции для работы с датами

Для решения задач, описанных в теме, нам понадобятся две ключевые функции языка запросов. Рассмотрим их подробнее, прежде чем переходить к сложным формулам.

  1. ДОБАВИТЬКДАТЕ(Дата, ТипПериода, Количество). Эта функция позволяет прибавить к исходной дате определенное количество временных отрезков (день, месяц, год, квартал и т.д.). Важно помнить, что третий параметр — Количество — должен иметь числовой тип данных.
  2. РАЗНОСТЬДАТ(ДатаНачала, ДатаКонца, ТипПериода). Функция вычисляет разницу между двумя датами. Результатом всегда является целое число. Обратите внимание на порядок аргументов: если ДатаКонца меньше ДатаНачала, результат будет отрицательным.

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

Проанализируем ситуацию, с которой столкнулся автор темы. Он попытался сначала рассчитать срок оплаты, присвоить ему псевдоним СрокОплаты, а затем использовать этот псевдоним в следующей строке запроса для вычисления просрочки:


ВЫБРАТЬ
    ДОБАВИТЬКДАТЕ(Реализация.Дата, ДЕНЬ, Реализация.Договор.ДопустимоеЧислоДнейЗадолженности) КАК СрокОплаты,
    РАЗНОСТЬДАТ(СрокОплаты, &НаДату, ДЕНЬ) КАК ДнейПросрочки
ИЗ
    Документ.РеализацияТоваровУслуг КАК Реализация

При выполнении такого кода система выдаст ошибку "Поле не найдено 'СрокОплаты'". Выясним причину: в языке запросов 1С (как и в стандартном SQL) псевдоним поля, определенный в секции ВЫБРАТЬ, недоступен для использования в расчетах внутри этого же списка выбора. Псевдонимы нужны для именования колонок в результирующей таблице, но расчетный механизм "не видит" их в процессе формирования строк.

Вариант 1: Использование вложенных функций

Самый прямой способ решить задачу — вкладывать одну функцию в другую. Рассмотрим, как составить формулу для расчета дней просрочки, исходя из выражения: &НаДату - (ДатаДокумента + ДопустимоеЧислоДней).

Посмотрим на пример правильного написания вложенной конструкции:


ВЫБРАТЬ
    РАЗНОСТЬДАТ(
        ДОБАВИТЬКДАТЕ(Реализация.Дата, ДЕНЬ, Реализация.Договор.ДопустимоеЧислоДнейЗадолженности),
        &НаДату,
        ДЕНЬ
    ) КАК ДнейПросрочки
ИЗ
    Документ.РеализацияТоваровУслуг КАК Реализация

В данном случае функция ДОБАВИТЬКДАТЕ вычисляет дату, до которой должна быть произведена оплата, и передает этот результат в качестве первого аргумента функции РАЗНОСТЬДАТ. Вторым аргументом выступает параметр запроса &НаДату.

Вариант 2: Использование вложенных запросов или временных таблиц

Если формула становится слишком громоздкой (например, если нужно использовать рассчитанный срок оплаты в нескольких местах или условиях ГДЕ), рекомендуется использовать вложенные запросы. Это сделает код более читаемым и позволит обращаться к рассчитанному полю по имени.

Разберем этот метод по шагам:


ВЫБРАТЬ
    ВЗ.СрокОплаты,
    РАЗНОСТЬДАТ(ВЗ.СрокОплаты, &НаДату, ДЕНЬ) КАК ДнейПросрочки
ИЗ
    (ВЫБРАТЬ
        ДОБАВИТЬКДАТЕ(Реализация.Дата, ДЕНЬ, Реализация.Договор.ДопустимоеЧислоДнейЗадолженности) КАК СрокОплаты
    ИЗ
        Документ.РеализацияТоваровУслуг КАК Реализация) КАК ВЗ

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

Важные нюансы и обработка пустых значений

При расчетах в реальных конфигурациях (УТ 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С и передать её в запрос как готовый параметр.

← На главную