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