Почему не совпадают конечные остатки в виртуальных таблицах Остатки и ОстаткиИОбороты регистра накопления?

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

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

Причина 1. Включение границы периода и передача типа Дата

Главная причина расхождения между таблицами заключается в специфике обработки типа Дата виртуальной таблицей Остатки. Если в параметр Период таблицы остатков передается обычная дата со временем 23:59:59, платформа рассчитывает остаток на начало этой секунды. Документы, проведенные ровно в 23:59:59, в выборку таблицы Остатки не попадают.

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

Для гарантированного включения всех документов последней секунды суток нельзя просто прибавлять одну секунду к дате (получая 00:00:00 следующих суток), так как в начале следующих суток уже могут быть зарегистрированы другие документы. Правильным решением является использование платформенного объекта Граница с видом ВидГраницы.Включая.

Посмотрим на пример правильной инициализации параметров в коде 1С:


КонецПериодаГраница = Новый Граница(КонецДня(ПараметрДатаКон), ВидГраницы.Включая);

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

Запрос.УстановитьПараметр("ПериодОстатков", КонецПериодаГраница);
Запрос.УстановитьПараметр("ПараметрНоменклатура", ВыбНоменклатура);
Запрос.УстановитьПараметр("ПараметрСклад", ВыбСклад);

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


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

Причина 2. Пустые значения в поле Регистратор

Вторая сложность, с которой сталкиваются разработчики в запросах к таблице ОстаткиИОбороты, — появление записей, где поле Регистратор содержит значение NULL (пустую ссылку). Проанализируем, почему это происходит.

При вызове виртуальной таблицы с периодичностью Регистратор и режимом выборки ДвиженияИГраницыПериода платформа принудительно рассчитывает и добавляет в результат две системные строки:

  1. Строку с начальным остатком на начало указанного периода;
  2. Строку с конечным остатком на конец периода.

Эти граничные строки отражают совокупное состояние остатка на границы интервала, а не конкретную хозяйственную операцию, поэтому поле Регистратор в них физически не заполнено. Если в запросе выполняется обращение через точку к реквизитам документа (например, Регистратор.Дата), неявное левое соединение транслирует значение NULL, а при сортировке по этому полю данные строки могут оказаться в самом верху или внизу таблицы.

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


ВЫБРАТЬ
	ТоварыНаСкладахОстаткиИОбороты.Период КАК Период,
	ТоварыНаСкладахОстаткиИОбороты.Регистратор КАК Регистратор,
	ТоварыНаСкладахОстаткиИОбороты.ВНаличииПриход КАК ВНаличииПриход,
	ТоварыНаСкладахОстаткиИОбороты.ВНаличииРасход КАК ВНаличииРасход,
	ТоварыНаСкладахОстаткиИОбороты.ВНаличииКонечныйОстаток КАК ВНаличииКонечныйОстаток
ИЗ
	РегистрНакопления.ТоварыНаСкладах.ОстаткиИОбороты(
			&ПараметрДатаСтарт,
			&ПараметрДатаКон,
			Регистратор,
			Движения,
			Номенклатура = &ПараметрНоменклатура
				И Склад = &ПараметрСклад) КАК ТоварыНаСкладахОстаткиИОбороты

Причина 3. Ошибки в условиях и оптимизация запросов к виртуальным таблицам

При построении запросов к виртуальным таблицам регистров накопления критически важно соблюдать стандарты разработки платформы 1С:

  1. Фильтрация в параметрах виртуальной таблицы: Условия отбора по измерениям (Номенклатура, Склад) всегда необходимо помещать в круглые скобки параметров самой таблицы, а не в секцию ГДЕ. Использование конструкции ГДЕ заставляет СУБД сначала рассчитать виртуальные итоги по всему регистру и лишь затем отсекать ненужное, что резко снижает производительность и может маскировать логические ошибки выборки.
  2. Обращение через точку к ссылке: Поля измерений ссылочного типа в регистрах уже содержат готовую ссылку. Конструкции вида Номенклатура.Ссылка являются избыточными; достаточно писать Номенклатура.

Диагностика физических данных и пересчет итогов

Если после выравнивания границ периодов с помощью объекта Граница расхождение все равно сохраняется, выполним следующие шаги диагностики:

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