Почему виртуальные таблицы остатков и остатков-оборотов в 1С выдают разный результат и как это исправить?

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

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

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

Главная причина расхождения заключается в том, как платформа 1С обрабатывает параметр даты при формировании SQL-запросов к СУБД для различных виртуальных таблиц:

  1. Виртуальная таблица «Остатки»: при передаче параметра типа Дата (например, 23:59:59) расчет выполняется строго до указанной секунды. Движения регистра, сделанные ровно в эту секунду (в 23:59:59), не включаются в итоговый остаток. По умолчанию дата рассматривается как исключительная граница.
  2. Виртуальная таблица «ОстаткиИОбороты»: параметр правого периода (КонецПериода) рассчитывается по умолчанию включительно. Движения, зарегистрированные ровно в 23:59:59, попадают в обороты и учитываются в поле КонечныйОстаток.

Таким образом, если в системе присутствуют документы (например, расходные или приходные накладные), проведённые в последнюю секунду дня (23:59:59), таблица ОстаткиИОбороты учтет их движения, а таблица Остатки — пропустит. Из-за этого конечные данные в запросах не совпадут.

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

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

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


ВЫБРАТЬ РАЗРЕШЕННЫЕ
    ТоварыОрганизацийОстатки.АналитикаУчетаНоменклатуры.Номенклатура КАК Номенклатура,
    ТоварыОрганизацийОстатки.КоличествоОстаток КАК Количество
ИЗ
    РегистрНакопления.ТоварыОрганизаций.Остатки(
        ДОБАВИТЬКДАТЕ(&Дата2, "СЕКУНДА", 1),
        АналитикаУчетаНоменклатуры.Склад = &Склад
    ) КАК ТоварыОрганизацийОстатки
ГДЕ
    ТоварыОрганизацийОстатки.КоличествоОстаток < 0

При добавлении одной секунды к дате 30.11.2017 23:59:59 граница запроса смещается на 01.12.2017 00:00:00. Поскольку расчет остатков выполняется не включая указанную секунду, в выборку попадают абсолютно все движения, сделанные до 00:00:00 включительно (в том числе движения за 23:59:59).

Способ 2. Использование объекта «Граница» в программном коде

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

Разберем пример кода установки параметра в модуле:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ТоварыОстатки.Номенклатура КАК Номенклатура,
    |	ТоварыОстатки.КоличествоОстаток КАК Количество
    |ИЗ
    |	РегистрНакопления.ТоварыВРознице.Остатки(&ДатаОстатков, ) КАК ТоварыОстатки";

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

Результат = Запрос.Выполнить();

Обратите внимание: объект Граница нельзя интерактивно выбрать в стандартных полях ввода на форме или передать через стандартный диалог выбора даты. Он создается исключительно программным путем.

Способ 3. Использование Момента Времени при контроле остатков

Если задача заключается в получении остатков на момент проведения конкретного документа, следует использовать объект МоментВремени или комбинацию Граница с моментом времени. Это гарантирует правильный учет позиционирования документа в логической последовательности:


// Получение остатка СТРОГО ДО проведения текущего документа:
РежимГраницы = Новый Граница(СсылкаНаДокумент.МоментВремени(), ВидГраницы.Исключая);

// Получение остатка ПОСЛЕ проведения текущего документа:
РежимГраницыВключая = Новый Граница(СсылкаНаДокумент.МоментВремени(), ВидГраницы.Включая);

Способ 4. Корректная настройка параметров в СКД (Системе компоновки данных)

При разработке отчетов на базе СКД разница в границах решается на уровне настройки параметров схемы компоновки данных. На вкладке «Параметры» для управляющего параметра даты или конца периода задается специальное выражение.

Рассмотрим выражение для параметра виртуальной таблицы остатков:

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

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

Дополнительные факторы: RLS и производительность

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

  1. Работа с правами доступа (RLS): если в запросе используется ключевое слово ВЫБРАТЬ РАЗРЕШЕННЫЕ, таблицы Остатки и ОстаткиИОбороты могут возвращать разное число строк. Таблица Остатки обращается к оптимизированным таблицам итогов регистра, а ОстаткиИОбороты при определенной структуре прав RLS может обращаться к таблицам документов-регистраторов, что приводит к дополнительным фильтрациям по правам.
  2. Производительность: использовать таблицу ОстаткиИОбороты только ради получения конечного остатка (чтобы не настраивать границу даты) является антипаттерном. Запрос к таблице остатков и оборотов заставляет СУБД рассчитывать обороты за весь указанный период, что существенно снижает скорость выполнения запроса по сравнению с прямым обращением к таблице Остатки.
← На главную