Как в запросе 1С убрать значение 737 653 при расчете просрочки по пустой дате

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

При разработке отчетов по взаиморасчетам или поставкам программисты часто сталкиваются с ситуацией, когда в колонке «Просрочка» вместо ожидаемого нуля или пустого значения выводится странное число 737 653. Разберем природу этого явления, выясним причину возникновения ошибки и рассмотрим несколько способов корректного решения этой задачи в языке запросов 1С.

Почему возникает число 737 653?

Прежде чем исправлять код, проанализируем ситуацию. Число 737 653 — это не случайная ошибка системы, а количество дней, прошедших с начала нашей эры (01.01.0001) до текущей даты (примерно август 2020 года, когда обсуждалась тема). В системе 1С пустая дата хранится именно как 01.01.0001 0:00:00. Когда мы используем функцию РАЗНОСТЬДАТ между пустой датой и заполненным параметром &ТекущаяДата, система честно вычисляет разницу в днях за два тысячелетия.

Способ 1: Использование конструкции ВЫБОР КОГДА

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

Рассмотрим пример реализации:


ВЫБОР
    КОГДА ДОБАВИТЬКДАТЕ(ДоговорыКонтрагентов.тн_СрокПоставки, ДЕНЬ, ДоговорыКонтрагентов.СрокОплаты) = ДАТАВРЕМЯ(1, 1, 1)
        ТОГДА 0
    ИНАЧЕ РАЗНОСТЬДАТ(ДОБАВИТЬКДАТЕ(ДоговорыКонтрагентов.тн_СрокПоставки, ДЕНЬ, ДоговорыКонтрагентов.СрокОплаты), &ТекДата, ДЕНЬ)
КОНЕЦ КАК Просрочка

В этом примере мы проверяем результат сложения даты поставки и срока оплаты. Если в базе данных эти поля не заполнены, функция ДОБАВИТЬКДАТЕ вернет пустую дату 01.01.0001. Условие КОГДА ... = ДАТАВРЕМЯ(1, 1, 1) перехватит это значение и вернет 0, не доводя дело до ошибочного расчета в 700 тысяч дней.

Способ 2: Комплексная проверка на NULL и пустую дату

В реальных задачах данные часто приходят из левых соединений, что порождает значения NULL. Если поле содержит NULL, любая арифметическая операция с ним также вернет NULL или приведет к ошибке. Проанализируем более надежный вариант кода, который учитывает оба фактора:


ВЫБОР
    КОГДА ДоговорыКонтрагентов.тн_СрокПоставки ЕСТЬ NULL 
        ИЛИ ДоговорыКонтрагентов.тн_СрокПоставки = ДАТАВРЕМЯ(1, 1, 1)
        ТОГДА 0
    ИНАЧЕ ВЫБОР 
        КОГДА ДОБАВИТЬКДАТЕ(ДоговорыКонтрагентов.тн_СрокПоставки, ДЕНЬ, ДоговорыКонтрагентов.СрокОплаты) < &ТекДата
            ТОГДА РАЗНОСТЬДАТ(ДОБАВИТЬКДАТЕ(ДоговорыContragentov.тн_СрокПоставки, ДЕНЬ, ДоговорыКонтрагентов.СрокОплаты), &ТекДата, ДЕНЬ)
        ИНАЧЕ 0
    КОНЕЦ
КОНЕЦ КАК Просрочка

Здесь мы используем вложенную конструкцию ВЫБОР. Внешний блок отсекает некорректные исходные данные (пустые ссылки или отсутствие записей), а внутренний — проверяет, наступила ли фактически просрочка по сравнению с текущей датой.

Проблема смешивания типов данных

Важный нюанс, который мы должны учитывать: типизация колонок запроса. В сообщении форума автор пытался вернуть пустую строку "" вместо числа в колонке «Просрочка». В языке запросов 1С это крайне не рекомендуется. Если в одной колонке система встретит и числа, и строки, это может привести к непредсказуемому поведению при выводе в список или при попытке суммирования итогов.

Рекомендация: Всегда возвращайте числовое значение (например, 0). Чтобы пользователь видел пустую ячейку вместо нуля, используйте возможности оформления в Системе компоновки данных (СКД):

  1. Перейдите на вкладку Наборы данных в СКД.
  2. Найдите ваше поле Просрочка.
  3. В колонке Оформление установите параметр Формат.
  4. В строке формата укажите ЧН=0. Это скажет системе: "Если значение равно 0, ничего не выводи".

Как вывести текст «Просрочено на ... дней»?

Иногда заказчик просит объединить число с текстом в одной колонке. Разберем, почему это сложно сделать в запросе. Язык запросов 1С имеет очень ограниченную поддержку конкатенации (сложения) строк. Напрямую сложить строку и результат функции РАЗНОСТЬДАТ не получится, так как типы данных несовместимы.

Посмотрим, как решить эту задачу правильно через СКД:

Используйте поле Выражение представления в настройках поля набора данных. Там доступен полноценный встроенный язык. Вы можете написать:

"Просрочено на " + Строка(Просрочка) + " дн."

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

Оптимизация вычислений во временных таблицах

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

Рассмотрим вариант с использованием временной таблицы:


ВЫБРАТЬ
    ДоговорыКонтрагентов.Ссылка КАК Договор,
    ДОБАВИТЬКДАТЕ(ДоговорыКонтрагентов.тн_СрокПоставки, ДЕНЬ, ДоговорыКонтрагентов.СрокОплаты) КАК ДатаПлановогоПлатежа
ПОМЕСТИТЬ ВТ_Сроки
ИЗ
    Справочник.ДоговорыКонтрагентов КАК ДоговорыКонтрагентов
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ_Сроки.Договор,
    ВЫБОР
        КОГДА ВТ_Сроки.ДатаПлановогоПлатежа > ДАТАВРЕМЯ(1, 1, 1) И ВТ_Сроки.ДатаПлановогоПлатежа < &ТекДата
            ТОГДА РАЗНОСТЬДАТ(ВТ_Сроки.ДатаПлановогоПлатежа, &ТекДата, ДЕНЬ)
        ИНАЧЕ 0
    КОНЕЦ КАК ПросрочкаДней
ИЗ
    ВТ_Сроки КАК ВТ_Сроки

Такой подход делает код гораздо более читаемым. Мы сначала один раз вычисляем дату, в которой суммированы все условия, и сохраняем её в ВТ_Сроки. Во втором запросе мы просто оперируем готовым значением. Это исключает дублирование логики и снижает риск возникновения ошибок при копировании формул.

Подведем итоги

Для корректного расчета просрочки и избавления от магического числа 737 653 нам необходимо:

← На главную