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