Почему при прибавлении дней к дате по календарному графику в запросе 1С возникает NULL и как это исправить?

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

При разработке механизмов расчета плановых дат (например, времени реакции на заявку или срока исполнения задач) разработчики часто используют данные регистра сведений КалендарныеГрафики. Однако при добавлении определенного количества рабочих дней можно столкнуться с ситуацией, когда итоговое поле принимает значение NULL, хотя календарь заполнен на весь текущий год.

Рассмотрим подробно, почему возникает эта ошибка, проанализируем структуру запроса и разберем способы корректного расчета дат с учетом графиков работы.

Причина возникновения проблемы

Выясним причину такого поведения запроса. Типичная схема расчета даты по производственному календарю включает следующие шаги:

  1. Извлечение текущей даты документа и определение порядкового номера дня с начала года через поле КоличествоДнейВГрафикеСНачалаГода.
  2. Прибавление к найденному номеру дня требуемого времени реагирования (в днях).
  3. Левое соединение таблицы обратно с графиком по условию совпадения года и полученного суммарного номера дня.

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


ПО (ГОД(ВложенныйЗапрос.ДатаСоздания) = Вт_Календарь.Год)
 И (ВложенныйЗапрос.КоличествоДнейВГрафикеСНачалаГода + ВложенныйЗапрос.ВремяРеагирования = Вт_Календарь.КоличествоДнейВГрафикеСНачалаГода)

Если сумма КоличествоДнейВГрафикеСНачалаГода + ВремяРеагирования превышает максимальное число рабочих дней в текущем году (например, при переносе даты с конца декабря на январь следующего года), запрос пытается найти в графике текущего года день с порядковым номером 366 или выше. Так как таких записей в пределах одного года не существует, левое соединение возвращает NULL.

Кроме того, в новом календарном году значение поля КоличествоДнейВГрафикеСНачалаГода сбрасывается и снова начинается с 1. Прямое прибавление дней без учета сброса нумерации в новом году не позволит найти правильную дату даже при снятии ограничения по полю Год.

Способ 1. Проверка нахождение даты в рамках текущего года

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

Разберем пример запроса с определением признака ВРамкахГода:


ВЫБРАТЬ
    ВложенныйЗапрос.Запрос КАК Запрос,
    ВложенныйЗапрос.Услуга КАК Услуга,
    ВложенныйЗапрос.ПлановоеВремя КАК ПлановоеВремя,
    ВложенныйЗапрос.ДатаСоздания КАК ДатаСоздания,
    ВложенныйЗапрос.ВремяРеагирования КАК ВремяРеагирования,
    Вт_Календарь.КоличествоДнейВГрафикеСНачалаГода КАК КоличествоДнейВГрафикеСНачалаГода,
    МаксимальноеЧислоДнейГрВгоду.МаксКоличествоДнейВГодуИзГрафика КАК МаксКоличествоДнейВГодуИзГрафика,
    ВЫБОР
        КОГДА Вт_Календарь.КоличествоДнейВГрафикеСНачалаГода + ВложенныйЗапрос.ВремяРеагирования <= МаксимальноеЧислоДнейГрВгоду.МаксКоличествоДнейВГодуИзГрафика
            ТОГДА ИСТИНА
        ИНАЧЕ ЛОЖЬ
    КОНЕЦ КАК ВРамкахГода
ИЗ
    (ВЫБРАТЬ
        itilprofЗапросы.Ссылка КАК Запрос,
        &Услуга КАК Услуга,
        &ПлановоеВремяЗагрузкиСпециалиста КАК ПлановоеВремя,
        itilprofЗапросы.ДатаСоздания КАК ДатаСоздания,
        ВЫБОР
            КОГДА НЕ &Услуга ЕСТЬ NULL 
                ТОГДА ВЫБОР
                        КОГДА НЕ &ПлановоеВремяЗагрузкиСпециалиста = 0
                            ТОГДА &ПлановоеВремяЗагрузкиСпециалиста
                        ИНАЧЕ 1
                    КОНЕЦ
            ИНАЧЕ 1
        КОНЕЦ КАК ВремяРеагирования
    ИЗ
        Документ.itilprofЗапросы КАК itilprofЗапросы
    ГДЕ
        itilprofЗапросы.Ссылка = &Ссылка) КАК ВложенныйЗапрос
    ЛЕВОЕ СОЕДИНЕНИЕ Вт_Календарь КАК Вт_Календарь
    ПО (ГОД(ВложенныйЗапрос.ДатаСоздания) = Вт_Календарь.Год)
        И (НАЧАЛОПЕРИОДА(ВложенныйЗапрос.ДатаСоздания, ДЕНЬ) = Вт_Календарь.ДатаГрафика)
    ЛЕВОЕ СОЕДИНЕНИЕ (ВЫБРАТЬ
        МАКСИМУМ(Вт_Календарь.КоличествоДнейВГрафикеСНачалаГода) КАК МаксКоличествоДнейВГодуИзГрафика,
        Вт_Календарь.Год КАК Год
    ИЗ
        Вт_Календарь КАК Вт_Календарь
    СГРУППИРОВАТЬ ПО
        Вт_Календарь.Год) КАК МаксимальноеЧислоДнейГрВгоду
    ПО (ГОД(ВложенныйЗапрос.ДатаСоздания) = МаксимальноеЧислоДнейГрВгоду.Год)

Этот подход позволяет избежать некорректных данных и явно обработать ситуации, когда сдвиг даты выходит за пределы заполненного года.

Способ 2. Организация сквозной нумерации рабочих дней

Если необходимо корректно получать дату при переходе на следующий год исключительно средствами языка запросов 1С, рекомендуется использовать алгоритм со сквозной нумерацией:

  1. Сформировать временную таблицу со всеми рабочими днями за несколько лет (например, за текущий и следующий).
  2. Применить функцию АВТОНОМЕРЗАПИСИ() или соединение таблицы «самой с собой» по условию ДатаГрафика >= ДатаГрафика для формирования единого сквозного порядкового номера дня.
  3. Выполнять связывание исходного документа с таблицей графика по условию: СквознойНомерНачала + ВремяРеагирования = СквознойНомерЦелевойДаты.

Способ 3. Оптимизация восстановления времени дня

Обратите внимание на конструкцию прибавления времени дня. Вместо тройного вызова функции ДОБАВИТЬКДАТЕ для часов, минут и секунд:


ДОБАВИТЬКДАТЕ(ДОБАВИТЬКДАТЕ(ДОБАВИТЬКДАТЕ(ДатаГрафика, ЧАС, ЧАС(ДатаСоздания)), МИНУТА, МИНУТА(ДатаСоздания)), СЕКУНДА, СЕКУНДА(ДатаСоздания))

Эффективнее рассчитывать разницу в секундах от начала дня с помощью РАЗНОСТЬДАТ и прибавлять её за одну операцию:


ДОБАВИТЬКДАТЕ(Вт_Календарь.ДатаГрафика, СЕКУНДА, РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(ВложенныйЗапрос.ДатаСоздания, ДЕНЬ), ВложенныйЗапрос.ДатаСоздания, СЕКУНДА))

Этот метод делает код запроса более читаемым и снижает нагрузку на СУБД.

Способ 4. Использование программных методов БСП

При работе в конфигурациях на базе Библиотеки Стандартных Подсистем (БСП) перенос дат с учетом графиков, сменности, предпраздничных дней и переносов праздников лучше выполнять программно во встроенном языке.

Для этого в общем модуле КалендарныеГрафики предусмотрены готовые функции, которые автоматически учитывают переход через границы годов и праздничные дни. Запросом выбираются первичные данные, после чего в цикле вызывается программный расчет даты, что полностью исключает ошибки типа NULL.

← На главную