В разработке на платформе 1С:Предприятие 8 одним из самых фундаментальных и в то же время вызывающих вопросы механизмов является работа с временными отметками. Когда мы строим отчеты или реализуем логику проведения документов, нам крайне важно понимать, как система определяет «точку во времени». Часто возникают ситуации, когда остатки «не идут» на одну копейку или документ не видит собственных движений — для решения этой задачи есть поиск отрицательных остатков в регистрах накопления. Чтобы избежать подобных ошибок, мы подробно разберем, что такое МоментВремени, как работает объект Граница и каким образом виртуальные таблицы запросов интерпретируют эти данные.
Для начала проанализируем, почему простой даты и времени (объекта типа Дата) недостаточно для однозначного определения последовательности событий в базе данных. В 1С в одну и ту же секунду может быть записано неограниченное количество документов. Если мы будем опираться только на время, система не сможет понять, какой из документов был «раньше», а какой «позже» — для этого есть исправление очередности расчетов при совпадении даты документов.
Здесь на сцену выходит МоментВремени. Это специальный объект платформы, который состоит из двух составляющих: даты (со временем) и ссылки на документ (регистратор). Рассмотрим подробнее, как платформа выстраивает документы в «линейку времени»:
ПриходнаяНакладная могут всегда стоять перед РасходнаяНакладная при одинаковом времени, просто в силу их внутреннего ID в дереве метаданных.Таким образом, МоментВремени — это уникальная «координата» документа на временной оси, которая позволяет системе четко сказать, какие записи регистра были сделаны до него, а какие после (поможет групповое перепроведение документов и восстановление последовательности).
Сам по себе МоментВремени просто указывает на точку. Но когда мы передаем эту точку в запрос (например, в параметры виртуальной таблицы), нам нужно уточнить: хотим ли мы видеть данные «по эту точку включительно» или «строго до этой точки»? Для этого используется объект Граница.
Рассмотрим два варианта использования ВидГраницы:
Посмотрим на пример создания такого объекта в коде:
// Получаем момент времени конкретного документа
МоментДокумента = Объект.Ссылка.МоментВремени();
// Создаем границу, которая включает движения документа
ГраницаВключая = Новый Граница(МоментДокумента, ВидГраницы.Включая);
// Создаем границу, которая НЕ включает движения документа
ГраницаИсключая = Новый Граница(МоментДокумента, ВидГраницы.Исключая);
Виртуальная таблица Остатки имеет свои правила «по умолчанию», которые часто сбивают с толку новичков. Для этой задачи есть выявление и контроль ошибок в учёте товаров. Выясним причину странного поведения, когда при передаче даты остатки получаются «не те», используя для отладки управляемую консоль запросов.
Если в параметр Период таблицы Остатки передать обычную Дата, система всегда трактует это как исключая. То есть, если вы передадите '2023-12-31 23:59:59', остатки будут рассчитаны на начало этой секунды. Все движения, сделанные в 59-ю секунду, учтены не будут.
Для получения корректных остатков на конец дня или на момент документа, а также для таких задач, как интерактивное формирование текста запроса для иерархии, проанализируем следующие подходы:
МоментВремени() (удобно через заполнение документа Корректировка регистров остатками). В запросах 1С при передаче МоментВремени в таблицу остатков, он по умолчанию воспринимается как «Исключая». Однако для явного указания и чистоты кода лучше использовать Новый Граница(Ссылка.МоментВремени(), ВидГраницы.Исключая).Новый Граница(Ссылка.МоментВремени(), ВидГраницы.Включая).КонецДня(Дата) не сработает так, как ожидается, потому что он не захватит движения последней секунды. Правильный способ: Новый Граница(КонецДня(Дата), ВидГраницы.Включая).В отличие от остатков, виртуальная таблица Обороты работает по принципу инклюзивности. Параметры НачалоПериода и КонецПериода по умолчанию включают указанную дату или момент времени.
Разберем ситуацию: если вы укажете в качестве конца периода МоментВремени документа (без использования Граница), то обороты этого документа попадут в выборку. Это логично для отчетов, но требует внимательности при совмещении данных из таблиц остатков и оборотов в одном запросе.
Многие задаются вопросом: как 1С превращает объект Граница в SQL-запрос? Понимание этого процесса снимает магию с «моментов времени». На уровне СУБД 1С не хранит тип «Момент времени». Платформа разворачивает условие по границе в составной фильтр по полям Period (Дата) и Recorder (Ссылка).
Например, условие Граница(МоментВремениДокумента, Включая) трансформируется в SQL примерно так:
WHERE (Period < ДатаДокумента)
OR (Period = ДатаДокумента AND Recorder <= СсылкаДокумента)
Именно сравнение ссылок (Recorder <= СсылкаДокумента) позволяет системе точно определить местоположение границы внутри одной секунды. При этом сравнение ссылок идет по их внутреннему двоичному представлению.
Рассмотрим несколько важных нюансов, которые помогут в промышленной разработке:
МоментВремени от нового, еще не записанного в базу документа. Поле Ссылка у него будет пустым, и результат работы Границы в запросе будет непредсказуемым (скорее всего, запрос вернет пустой результат или ошибку). Сначала Записать(), потом получение остатков.МоментВремени — операция достаточно быстрая, так как поле Период и Регистратор входят в индексы таблиц итогов и движений. Однако помните, что расчет на конкретный момент требует от системы «докрутки» оборотов от ближайшей точки хранения итогов (начала месяца).Если Момент1 > Момент2 Тогда.... Система сама корректно сравнит даты, а при их равенстве — приоритеты документов.Подводя итог, можно сказать: всегда используйте Граница(МоментВремени, ВидГраницы.Включая), когда вам нужно получить состояние системы после конкретного события, и МоментВремени (или Исключая), когда нужно состояние до него. Использование простых дат в параметрах периодов — верный путь к трудноуловимым ошибкам в расчетах.