Почему отчет на СКД не видит данные за 23:59:59 и как это исправить?

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

Каждый разработчик 1С рано или поздно сталкивается с ситуацией, когда отчет на СКД, казалось бы, работает корректно, но при формировании на конец дня "теряет" документы, проведенные в самую последнюю секунду — 23:59:59. Документы, проведенные в 23:59:58, попадают в отчет, а самые последние — нет. Эта проблема не является ошибкой платформы или СКД, а связана с фундаментальными принципами работы виртуальных таблиц регистров накопления. Давайте подробно разберемся в причинах и рассмотрим надежные способы решения.

Выясним причину: особенность работы виртуальных таблиц и "границы" периода

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

Используя управляемую консоль запросов и отчетов, рассмотрим две основные виртуальные таблицы, которые используются для получения остатков:

  1. РегистрНакопления.ИмяРегистра.Остатки(&Период, &Условие)
  2. РегистрНакопления.ИмяРегистра.ОстаткиИОбороты(&НачалоПериода, &КонецПериода, ...).

Проблема кроется в поведении первой таблицы — Остатки. По умолчанию эта таблица получает срез данных на НАЧАЛО указанной секунды. Это называется границей "Исключая". Когда вы запрашиваете остатки на 31.03.2023 23:59:59, система отдает вам состояние регистра на момент начала этой секунды, то есть до того, как были учтены движения документов, проведенных ровно в 23:59:59.

В отличие от нее, виртуальная таблица ОстаткиИОбороты при расчете поля КонечныйОстаток использует границу "Включая". Это значит, что в конечный остаток попадут все движения, совершенные в течение всего периода, включая последнюю секунду конечной даты.

Именно эта разница в поведении и приводит к "потере" данных в отчетах, которые для получения конечного остатка используют виртуальную таблицу Остатки.

Решение 1: Использование объекта "Граница" (рекомендуемый способ)

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

Объект Граница создается с двумя параметрами: сама дата и вид границы. Вид границы может быть:

Чтобы отчет работал корректно, нам нужно передать в параметр виртуальной таблицы объект Граница с видом Включая.

Как это сделать в отчете на СКД?

Обычно это делается программно, например, в обработчике события ПриКомпоновкеРезультата модуля отчета. Посмотрим на пример:


Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка)

    СтандартнаяОбработка = Ложь;

    // Получаем значение параметра с датой окончания из настроек отчета
    Настройка = КомпоновщикНастроек.ПолучитьНастройки();
    ДатаОкончания = Настройка.ПараметрыДанных.НайтиЗначениеПараметра(Новый ПараметрКомпоновкиДанных("КонецПериода")).Значение;

    // Если дата задана, создаем границу и устанавливаем ее в качестве значения параметра
    Если ЗначениеЗаполнено(ДатаОкончания) Тогда
        // Создаем границу, которая явно указывает, что нужно включить момент времени
        ГраницаКонца = Новый Граница(ДатаОкончания, ВидГраницы.Включая);
        
        // В параметрах данных СКД может быть другой параметр, например, КонецПериодаОстатков
        // Устанавливаем значение именно для того параметра, который используется в таблице Остатки
        КомпоновщикНастроек.Настройки.ПараметрыДанных.УстановитьЗначениеПараметра("КонецПериодаОстатков", ГраницаКонца);
    КонецЕсли;

    // Выполняем компоновку с измененными параметрами
    КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
    МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, КомпоновщикНастроек.ПолучитьНастройки(), ДанныеРасшифровки);

    ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
    ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, , ДанныеРасшифровки);

    ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
    ПроцессорВывода.УстановитьДокумент(ДокументРезультат);
    ПроцессорВывода.Вывести(ПроцессорКомпоновки);

КонецПроцедуры

Этот метод является предпочтительным, так как он делает ваш код явным и понятным: вы не "обманываете" систему, сдвигая время, а прямо говорите ей, как следует обрабатывать границу периода.

Решение 2: Сдвиг периода на одну секунду (распространенный прием)

Второй, очень популярный способ — это небольшой "трюк" со временем. Логика проста: если мы не можем получить остатки на конец секунды 23:59:59, то запросим остатки на начало следующей секунды — 00:00:00 следующего дня. Так как на начало этой секунды уже точно будут учтены все движения предыдущего дня, мы получим верный результат.

Это можно сделать прямо в тексте запроса СКД с помощью встроенных функций языка запросов.

Посмотрим на пример изначального запроса автора:


ВЫБРАТЬ
    ...
ИЗ
    РегистрНакопления.АР_ВзаиморасчетыПоАренде.Остатки({(&КонецПериодаОстатков)}, ) КАК АР_ВзаиморасчетыПоАрендеОстатки
...

Чтобы применить сдвиг, можно изменить текст запроса или выражение для параметра КонецПериодаОстатков на вкладке "Параметры" в СКД.

Выражение для параметра будет выглядеть так:

ДОБАВИТЬКДАТЕ(КОНЕЦПЕРИОДА(&КонецПериода, "ДЕНЬ"), "СЕКУНДА", 1)

Разберем его по частям:

Этот способ хорошо работает и часто применяется из-за своей простоты. Однако стоит помнить о возможном побочном эффекте при использовании этой же даты в таблице ОстаткиИОбороты: если вы передадите туда дату со сдвигом, в обороты могут попасть движения, совершенные в 00:00:00 следующего дня, что может быть нежелательно.

Важное замечание: Иногда, при использовании стандартного объекта СтандартныйПериод в СКД, применение сложных выражений для его свойств ДатаНачала и ДатаОкончания может работать не так, как ожидается. В таких случаях, как поступил автор темы, бывает проще отказаться от СтандартныйПериод и создать два отдельных параметра (например, НачалоПериода и КонецПериода), чтобы иметь полный контроль над их значениями и выражениями.

Заключение

Проблема с "потерей" данных за 23:59:59 — это не ошибка, а следствие четко определенных правил работы виртуальных таблиц в 1С. Понимание разницы между включающей и исключающей границами периода является ключом к написанию корректных и предсказуемых отчетов.

Для решения задачи:

  1. Рекомендуемый метод: Используйте объект Граница с видом ВидГраницы.Включая для явного управления поведением системы.
  2. Альтернативный метод: Добавляйте одну секунду к концу периода, чтобы сдвинуть точку среза данных на начало следующего дня.

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

← На главную