При построении отчетов и выгрузок на платформе 1С:Предприятие 7.7 разработчики нередко сталкиваются с ситуацией, когда движения за последний указанный день не попадают в итоговую выборку. Из-за этого возникает предположение, что дата в запросе воспринимается системой как начало дня (00:00:00), и возникает вопрос: как правильно задать конечную границу периода, чтобы захватить день полностью?
Разберем детально особенности работы с периодами во встроенном языке запросов 1С 7.7, выясним истинные причины пропажи движений и рассмотрим надежные варианты решения этой задачи.
В отличие от платформы 1С:Предприятие 8, где тип Дата всегда содержит и дату, и время с точностью до секунды, в платформе 1С 7.7 переменная типа Дата хранит исключительно календарный день (число, месяц, год). Внутреннее представление времени в объекте даты отсутствует.
Посмотрим на стандартную секцию периода во встроенном запросе к регистрам:
|Период с НачДата по КонДата;
Если в качестве параметров НачДата и КонДата передаются обычные значения типа Дата, платформа 1С 7.7 автоматически интерпретирует границы следующим образом:
Важное предостережение: прибавление к конечной дате единицы (КонДата = КонДата + 1) является грубейшей ошибкой. В 1С 7.7 такое выражение сместит границу на следующий календарный день, в результате чего запрос захватит лишние сутки целиком.
Таким образом, для получения данных за период с включением последнего дня достаточно передать конечную дату как есть:
КонДата = ТекущаяДата();
НачДата = КонДата - 45;
Запрос = СоздатьОбъект("Запрос");
ТекстЗапроса =
"//{{ЗАПРОС(Сформировать)
|Период с НачДата по КонДата;
|Без итогов;
|Номенклатура = Регистр.ОстаткиТМЦ.Номенклатура;
|Склад = Регистр.ОстаткиТМЦ.Склад;
|Количество = Регистр.ОстаткиТМЦ.Количество;
|Функция КоличествоНачОст = НачОст(Количество);
|Функция КоличествоПриход = Приход(Количество);
|Функция КоличествоРасход = Расход(Количество);
|Функция КоличествоКонОст = КонОст(Количество);
|Группировка Номенклатура без групп;
|Группировка День все;
|Условие(Номенклатура в СписокТМЦ);
|Условие(Склад = ОсновнойСклад);
|"//}}ЗАПРОС
;
Проанализируем ситуацию, когда документы за последний день были фактически проведены (например, утром в 11:00), но в отчете за этот день движения отсутствуют (Приход = 0, Расход = 0), а начальный остаток равен конечному (НачОст = КонОст). Выделим основные технические причины такого поведения.
1. Ошибочный расчет границы через точку актуальности (ТА)
Нередко в коде можно встретить конструкцию ограничения даты:
КонДата = Мин(ПолучитьДатуТА(), ТекущаяДата());
Уверенность в том, что ПолучитьДатуТА() всегда равна ТекущаяДата(), часто оказывается ложной. Если пользователи проводят документы неоперативно (задним числом) либо при старте дня точка актуальности не была смещена на текущую дату, функция ПолучитьДатуТА() вернет дату последнего проведенного оперативно документа (например, вчерашний день). В итоге КонДата устанавливается на вчера, а все движения за сегодня автоматически отсекаются из выборки запроса.
2. Смещение точки актуальности назад (документы находятся за ТА)
В архитектуре 1С 7.7 регистры хранят рассчитанные остатки строго на позицию точки актуальности. Если в процессе работы, группового перепроведения документов или через меню «Управление итогами» ТА была смещена назад в прошлое, документы, расположенные по временной оси позже ТА, перестают отражаться в итогах регистров. Встроенный запрос к регистрам не может получить данные о движениях, находящихся «в будущем» относительно текущей ТА базы.
3. Влияние модификатора «все» в группировке по дням
Конструкция Группировка День все; принудительно выводит строку для каждой календарной даты указанного диапазона, даже если в базе за этот день не было зарегистрировано ни одного движения. Если движения по указанным фильтрам не найдены или отрезаны границей ТА, система выводит пустую строку с нулевыми оборотами, создавая иллюзию присутствия дня в расчете.
4. Несоответствие условий фильтрации
В запросе используются строгие условия Условие(Номенклатура в СписокТМЦ); и Условие(Склад = ОсновнойСклад);. Если проведенный документ сформировал движения по другому складу или номенклатура не вошла в переданный список значений на момент выполнения выгрузки, обороты за день закономерно будут равны нулю.
Разберем практические шаги для предотвращения подобных ошибок при разработке отчетов и процедур обмена данными.
Шаг 1. Прямое задание дат периода
Если необходимо получить данные по текущий день включительно, передаем системную дату напрямую, без использования ПолучитьДатуТА():
КонДата = ТекущаяДата();
НачДата = КонДата - 45;
Шаг 2. Проверка актуальности итогов перед выполнением запроса
Для исключения ситуаций, когда точка актуальности отстает от реального времени, в программный код полезно добавить проверку актуальности итогов и позиции ТА:
Если ИтогиАктуальны() = 0 Тогда
Предупреждение("Итоги регистров не актуальны! Выполните пересчет итогов.");
КонецЕсли;
Если СравнитьТА(ТекущаяДата()) > 0 Тогда
Сообщить("Внимание: Точка актуальности отстает от текущей календарной даты!", "!");
КонецЕсли;
Шаг 3. Использование временного расчета регистров как альтернативы
Если отчет критичен к позиции документов или требуется рассчитать остатки строго на определенный момент времени без использования «черного» запроса, можно применить прямой расчет через объект Регистр:
Рег = СоздатьОбъект("Регистр.ОстаткиТМЦ");
Рег.ВременныйРасчет(1);
РассчитатьРегистрыПо(КонДата);
// Получение остатка на конец дня КонДата
Остаток = Рег.СводныйОстаток(ВыбНоменклатура, ВыбСклад, "Количество");
Метод РассчитатьРегистрыПо(КонДата) при передаче значения типа Дата рассчитывает остатки на конец указанного дня со всеми проведенными движениями, что обеспечивает стабильный и предсказуемый результат выборки данных.