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