Как правильно получить остатки на конец дня в 1С: использование объекта Граница

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

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

Почему стандартный метод КонецДня() может работать некорректно

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

Проанализируем пример кода, который часто встречается в правилах выгрузки или внешних обработках:


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

// Ошибка: использование КонецДня не захватит движения последней секунды
Запрос.УстановитьПараметр("ДатаОстатков", КонецДня(ДатаОстатков)); 

В данном случае, если банковская выписка или приходный кассовый ордер были записаны ровно в 23:59:59, их влияние на остаток не будет учтено. Это критично при переносе данных (например, из УТ 10.3 в УТ 11.5), так как итоговые сальдо в новой базе просто не сойдутся.

Решение проблемы: Использование объекта Граница

Для того чтобы включить движения последней секунды в выборку, необходимо использовать специальный объект — Граница. Этот объект позволяет явно указать платформе, нужно ли включать само граничное значение в интервал или нет. Рассмотрим правильный способ установки параметра запроса:


// Создаем объект Граница, включающий указанный момент времени
ГраницаОстатков = Новый Граница(КонецДня(ДатаОстатков), ВидГраницы.Включая);

Запрос.УстановитьПараметр("ДатаОстатков", ГраницаОстатков);

Использование ВидГраницы.Включая гарантирует, что все записи, сделанные в 23:59:59, будут обработаны виртуальной таблицей остатков. Это универсальный стандарт для работы с регистрами накопления.

Особенности работы с регистрами сведений (СрезПоследних)

Иногда возникает вопрос: нужно ли использовать Границу для получения среза последних в регистрах сведений? Рассмотрим ситуацию, когда в запросе одновременно присутствуют и остатки по регистрам накопления, и срез последних (например, для получения курса валют на дату остатков). Как было замечено в обсуждении, объект Граница корректно воспринимается методом СрезПоследних.

Проанализируем комбинированный запрос:


Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   Остатки.Организация,
    |   Остатки.СуммаОстаток * ЕСТЬNULL(КурсыВалют.Курс, 1) КАК СуммаРегл
    |ИЗ
    |   РегистрНакопления.ДенежныеСредства.Остатки(&ДатаГраница, ) КАК Остатки
    |ЛЕВОЕ СОЕДИНЕНИЕ 
    |   РегистрСведений.КурсыВалют.СрезПоследних(&ДатаГраница, ) КАК КурсыВалют
    |ПО Остатки.Валюта = КурсыВалют.Валюта";

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

Хотя для регистра сведений часто достаточно простой даты, использование единого параметра типа Граница является допустимым и рабочим вариантом, упрощающим структуру кода.

Работа с Границей через COM-соединение

Особую сложность представляет получение остатков через COM-соединение с другой базой 1С. Поскольку Граница — это объектный тип платформы, его нельзя просто передать как строку или число через границу приложений. Нам необходимо создать объект Граница именно в контексте той базы, к которой мы подключились.

Рассмотрим, как это реализовать программно:


// Предположим, COMСоединение уже установлено
ДатаКонцаДня = КонецДня(ВыбраннаяДата);

// Создаем объект Граница внутри внешней базы через COM
ВидВключая = COMСоединение.ВидГраницы.Включая;
ПараметрГраница = COMСоединение.NewObject("Boundary", ДатаКонцаДня, ВидВключая);

// Устанавливаем параметр в запросе, который выполняется через COM
COMЗапрос = COMСоединение.NewObject("Query");
COMЗапрос.Text = "ВЫБРАТЬ ... ИЗ РегистрНакопления.ТоварыНаСкладах.Остатки(&Период, )";
COMЗапрос.SetParameter("Период", ПараметрГраница);

Если по каким-то причинам создание объекта через NewObject в COM-соединении недоступно или вызывает ошибки, программисты иногда прибегают к «обходному пути»: передают в запрос дату, увеличенную на одну секунду (начало следующего дня):


// Альтернативный вариант (менее академичный, но эффективный)
ДатаДляЗапроса = КонецДня(НужнаяДата) + 1; 
Запрос.УстановитьПараметр("ДатаОстатков", ДатаДляЗапроса);

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

Подведем итоги

Для получения корректных остатков на конец периода в 1С мы рекомендуем придерживаться следующих правил:

  1. Всегда используйте объект Граница с параметром ВидГраницы.Включая для виртуальных таблиц остатков.
  2. Помните, что простая передача КонецДня() игнорирует документы, записанные в ту же секунду.
  3. При работе через COM-соединение создавайте объект Граница на стороне удаленного сервера через NewObject.
  4. Для регистров сведений использование Границы также допустимо и позволяет унифицировать параметры запроса.

Соблюдение этих правил позволит избежать трудноуловимых ошибок при сверке данных и обеспечит точность ваших отчетов и алгоритмов переноса данных.

← На главную