При разработке отчетов и обработок разработчики 1С часто сталкиваются с ситуацией, когда два аналогичных запроса к одному и тому же регистру накопления возвращают разное количество строк или разные суммы остатков. В частности, запрос к виртуальной таблице Остатки и запрос к виртуальной таблице ОстаткиИОбороты при передаче одинакового параметра даты (например, 30.11.2017 23:59:59) дают отличный друг от друга результат. Проанализируем причину такого поведения платформы 1С:Предприятие и разберем по шагам все способы решения этой проблемы.
Главная причина расхождения заключается в том, как платформа 1С обрабатывает параметр даты при формировании SQL-запросов к СУБД для различных виртуальных таблиц:
Дата (например, 23:59:59) расчет выполняется строго до указанной секунды. Движения регистра, сделанные ровно в эту секунду (в 23:59:59), не включаются в итоговый остаток. По умолчанию дата рассматривается как исключительная граница.КонецПериода) рассчитывается по умолчанию включительно. Движения, зарегистрированные ровно в 23:59:59, попадают в обороты и учитываются в поле КонечныйОстаток.Таким образом, если в системе присутствуют документы (например, расходные или приходные накладные), проведённые в последнюю секунду дня (23:59:59), таблица ОстаткиИОбороты учтет их движения, а таблица Остатки — пропустит. Из-за этого конечные данные в запросах не совпадут.
Если запрос выполняется в консоли запросов или нет возможности менять программный код передачи параметров, наиболее простым решением является сдвиг даты на 1 секунду непосредственно в самом тексте запроса. Для этого используется встроенная функция языка запросов ДОБАВИТЬКДАТЕ.
Рассмотрим пример корректировки запроса к таблице остатков:
ВЫБРАТЬ РАЗРЕШЕННЫЕ
ТоварыОрганизацийОстатки.АналитикаУчетаНоменклатуры.Номенклатура КАК Номенклатура,
ТоварыОрганизацийОстатки.КоличествоОстаток КАК Количество
ИЗ
РегистрНакопления.ТоварыОрганизаций.Остатки(
ДОБАВИТЬКДАТЕ(&Дата2, "СЕКУНДА", 1),
АналитикаУчетаНоменклатуры.Склад = &Склад
) КАК ТоварыОрганизацийОстатки
ГДЕ
ТоварыОрганизацийОстатки.КоличествоОстаток < 0
При добавлении одной секунды к дате 30.11.2017 23:59:59 граница запроса смещается на 01.12.2017 00:00:00. Поскольку расчет остатков выполняется не включая указанную секунду, в выборку попадают абсолютно все движения, сделанные до 00:00:00 включительно (в том числе движения за 23:59:59).
При формировании запроса из программного модуля 1С вместо прибавления секунды рекомендуется использовать специальный объект платформы — Граница. Он позволяет явно указать платформе, необходимо ли включать движения граничной секунды в расчет.
Разберем пример кода установки параметра в модуле:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ТоварыОстатки.Номенклатура КАК Номенклатура,
| ТоварыОстатки.КоличествоОстаток КАК Количество
|ИЗ
| РегистрНакопления.ТоварыВРознице.Остатки(&ДатаОстатков, ) КАК ТоварыОстатки";
// Указываем границу включительно
ГраницаКонец = Новый Граница(КонецДня(УказаннаяДата), ВидГраницы.Включая);
Запрос.УстановитьПараметр("ДатаОстатков", ГраницаКонец);
Результат = Запрос.Выполнить();
Обратите внимание: объект Граница нельзя интерактивно выбрать в стандартных полях ввода на форме или передать через стандартный диалог выбора даты. Он создается исключительно программным путем.
Если задача заключается в получении остатков на момент проведения конкретного документа, следует использовать объект МоментВремени или комбинацию Граница с моментом времени. Это гарантирует правильный учет позиционирования документа в логической последовательности:
// Получение остатка СТРОГО ДО проведения текущего документа:
РежимГраницы = Новый Граница(СсылкаНаДокумент.МоментВремени(), ВидГраницы.Исключая);
// Получение остатка ПОСЛЕ проведения текущего документа:
РежимГраницыВключая = Новый Граница(СсылкаНаДокумент.МоментВремени(), ВидГраницы.Включая);
При разработке отчетов на базе СКД разница в границах решается на уровне настройки параметров схемы компоновки данных. На вкладке «Параметры» для управляющего параметра даты или конца периода задается специальное выражение.
Рассмотрим выражение для параметра виртуальной таблицы остатков:
ДОБАВИТЬКДАТЕ(КОНЕЦПЕРИОДА(&КонецПериода, "ДЕНЬ"), "СЕКУНДА", 1)
Если в СКД используется системный параметр СтандартныйПериод, то при правильной связи внутренних параметров виртуальных таблиц с границами периода платформа автоматически формирует корректную границу с учетом типа виртуальной таблицы.
Помимо разницы в секундах, при сравнении результатов двух таблиц стоит учитывать следующие технические нюансы:
ВЫБРАТЬ РАЗРЕШЕННЫЕ, таблицы Остатки и ОстаткиИОбороты могут возвращать разное число строк. Таблица Остатки обращается к оптимизированным таблицам итогов регистра, а ОстаткиИОбороты при определенной структуре прав RLS может обращаться к таблицам документов-регистраторов, что приводит к дополнительным фильтрациям по правам.ОстаткиИОбороты только ради получения конечного остатка (чтобы не настраивать границу даты) является антипаттерном. Запрос к таблице остатков и оборотов заставляет СУБД рассчитывать обороты за весь указанный период, что существенно снижает скорость выполнения запроса по сравнению с прямым обращением к таблице Остатки.