При разработке внешних отчетов или обработок программисты часто сталкиваются с ситуацией, когда данные, полученные «простым» запросом к виртуальной таблице остатков, не совпадают с цифрами в стандартной Оборотно-сальдовой ведомости (ОСВ). Особенно остро эта проблема проявляется в конце отчетного периода, например, на 31 июля в 23:59:59. Проанализируем, почему это происходит и как добиться идеального соответствия данных в 1С:УПП 1.3 и других конфигурациях.
Разберем ситуацию: в ОСВ на конец дня числится 10 единиц товара, а запрос показывает 12. Выясняется, что в последнюю секунду дня (23:59:59) был оформлен расход на 2 единицы. Посмотрим на техническую сторону вопроса. В платформе «1С:Предприятие 8» дата — это не просто точка, а момент на оси времени. Если мы передаем в запрос обычную дату 31.07.2019 23:59:59, система по умолчанию интерпретирует ее как начало этой секунды. Следовательно, все документы, записанные в эту же самую секунду, не попадают в расчет остатков.
Стандартные отчеты, такие как ОСВ, используют специальные механизмы для включения движений «граничной» секунды. Чтобы наш запрос возвращал идентичный результат, нам необходимо использовать объект Граница.
Объект Граница позволяет явно указать системе, нужно ли включать движения, совершенные в указанный момент времени, в выборку. Рассмотрим, как правильно создать этот объект и передать его в запрос. Вместо передачи простой даты, мы создаем массив параметров и инициализируем новый объект.
Проанализируем правильный код инициализации границы:
ДатаКонцаПериода = КонецДня(ДатаОтчета);
// Создаем объект Граница, включающий саму секунду
ПараметрыГраницы = Новый Массив;
ПараметрыГраницы.Добавить(ДатаКонцаПериода);
ПараметрыГраницы.Добавить(ВидГраницы.Включая);
КонечнаяГраница = Новый(Тип("Граница"), ПараметрыГраницы);
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ХозрасчетныйОстатки.Счет КАК Счет,
| ХозрасчетныйОстатки.КоличествоОстаток КАК КоличествоОстаток
|ИЗ
| РегистрБухгалтерии.Хозрасчетный.Остатки(&Период, Счет = &Счет, , ) КАК ХозрасчетныйОстатки";
Запрос.УстановитьПараметр("Период", КонечнаяГраница);
Запрос.УстановитьПараметр("Счет", ПланыСчетов.Хозрасчетный.ТоварыНаСкладах);
Важный момент: Обратите внимание, что в тексте запроса мы используем параметр &Период внутри скобок виртуальной таблицы. Если оставить скобки пустыми, запрос возьмет текущие итоги на данный момент, что также приведет к неверным результатам за прошлые периоды.
Если ваша задача — получить остатки не просто на конец дня, а на конкретный документ (чтобы увидеть остатки строго после проведения расходной накладной), используйте МоментВремени. Этот объект уникален для каждой транзакции и гарантирует правильную последовательность записей, даже если в одну секунду введено 100 документов.
Рассмотрим пример кода:
// Получаем момент времени конкретного документа
ТекМоментВремени = СсылкаНаДокумент.МоментВремени();
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ХозрасчетныйОстатки.КоличествоОстаток
|ИЗ
| РегистрБухгалтерии.Хозрасчетный.Остатки(&Момент, Счет = &Счет, , ) КАК ХозрасчетныйОстатки";
Запрос.УстановитьПараметр("Момент", ТекМоментВремени);
Многие начинающие программисты совершают ошибку, накладывая фильтры по счету или субконто в секции ГДЕ. Выясним, почему это плохо. Когда условие стоит в ГДЕ, система сначала рассчитывает остатки по всем счетам и всей номенклатуре базы данных, а только потом отсеивает нужное. Это колоссальная нагрузка на сервер.
Рассмотрим правильный подход — использование параметров виртуальной таблицы. Это позволяет SQL-серверу сразу отобрать нужные данные, используя индексы таблиц итогов.
Структура параметров виртуальной таблицы Остатки выглядит так:
Пример оптимизированного запроса для 1С:УПП 1.3:
// Подготовим массив субконто для фильтрации
МассивСубконто = Новый Массив;
МассивСубконто.Добавить(ПланыВидовХарактеристик.ВидыСубконтоХозрасчетные.Номенклатура);
МассивСубконто.Добавить(ПланыВидовХарактеристик.ВидыСубконтоХозрасчетные.Склады);
Запрос.Текст =
"ВЫБРАТЬ
| ХозрасчетныйОстатки.Субконто1 КАК Номенклатура,
| ХозрасчетныйОстатки.КоличествоОстаток КАК Остаток
|ИЗ
| РегистрБухгалтерии.Хозрасчетный.Остатки(&Граница, Счет = &Счет, &ВидыСубконто, Субконто2 = &Склад) КАК ХозрасчетныйОстатки";
Запрос.УстановитьПараметр("ВидыСубконто", МассивСубконто);
Запрос.УстановитьПараметр("Склад", ВыбранныйСклад);
Если даже после применения Границы данные все равно «разлетаются», проанализируем следующие ситуации:
Подведем итог: для получения данных, идентичных Оборотно-сальдовой ведомости, всегда используйте объект Граница(КонецДня(Дата), ВидГраницы.Включая) и передавайте фильтры (Счет, Субконто, Организация) непосредственно в параметры виртуальной таблицы, а не в секцию ГДЕ. Это обеспечит и точность, и высокую скорость работы вашего отчета.