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