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