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