Как получить остатки по всем договорам на момент каждого регистратора в 1С

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

При построении отчетов по взаиморасчетам периодически возникает задача: получить остатки по всем договорам контрагентов на момент времени каждого документа-регистратора за выбранный период. Часто требуется вывести не только те договоры, по которым конкретный документ сделал движения, но и полный срез актуальных остатков по всем остальным "пассивным" договорам на эту же дату.

Проанализируем, почему типовая виртуальная таблица не решает эту задачу "из коробки" и разберем три действенных способа реализации этой логики на платформе 1С.

Особенности работы виртуальных таблиц регистра накопления

Рассмотрим, почему обычный запрос к виртуальной таблице ОстаткиИОбороты с периодичностью Регистратор не дает нужного результата. Виртуальная таблица формирует строки только по тем комбинациям измерений (в нашем случае по полю ДоговорКонтрагента), по которым конкретный документ физически сделал движения.

Параметр виртуальной таблицы МетодДополнения со значением ДвиженияИГраницыПериода позволяет дополнить только начальную и конечную даты выбранного периода, но не создает строк по всем измерениям внутри промежуточных моментов времени. Поэтому для решения задачи требуется изменить подход к формированию запросов или связям данных.

Способ 1. Использование связей наборов данных в СКД

Разберем наиболее красивое и стандартное решение задачи средствами Системы компоновки данных (СКД). Этот метод основан на связывании двух наборов данных по моменту времени.

  1. Создаем Набор данных 1 (Запрос): Выбираем хронологический список документов-регистраторов (или их моментов времени) и список договоров контрагентов, по которым требуется получить аналитику.
    
    ВЫБРАТЬ РАЗЛИЧНЫЕ
    	Взаиморасчеты.Период КАК Период,
    	Взаиморасчеты.Регистратор КАК Регистратор,
    	Взаиморасчеты.ДоговорКонтрагента КАК ДоговорКонтрагента
    ИЗ
    	РегистрНакопления.ВзаиморасчетыСКонтрагентами КАК Взаиморасчеты
    ГДЕ
    	Взаиморасчеты.Период МЕЖДУ &НачалоПериода И &КонецПериода
    
  2. Создаем Набор данных 2 (Запрос): Обращаемся к виртуальной таблице Остатки регистра накопления ВзаиморасчетыСКонтрагентами.
    
    ВЫБРАТЬ
    	ВзаиморасчетыОстатки.ДоговорКонтрагента КАК ДоговорКонтрагента,
    	ВзаиморасчетыОстатки.СуммаВзаиморасчетовОстаток КАК Остаток
    ИЗ
    	РегистрНакопления.ВзаиморасчетыСКонтрагентами.Остатки(&ПериодОстатков, ) КАК ВзаиморасчетыОстатки
    
  3. Настраиваем связи наборов данных: Переходим на вкладку Связи наборов данных и добавляем связь от Набора 1 к Набору 2. В поле выражения источника указываем Регистратор (или Период), а в колонку Параметр вписываем ПериодОстатков.

При такой настройке платформа 1С при выводе каждой строки с регистратором автоматически передает его момент времени в параметр виртуальной таблицы остатков, рассчитывая срез по всем договорам на конкретный документ.

Способ 2. Получение среза через чистый язык запросов 1С

Если отчет строится без использования СКД, проанализируем, как сформировать полный срез остатков единым запросом. Для этого применяется создание матрицы комбинирования измерений и расчета нарастающего итога.

  1. Формируем временную таблицу уникальных регистраторов за период и временную таблицу всех аналитик (договоров).
  2. Делаем декартово соединение (или полное соединение без условий) для получения матрицы "Регистратор × Договор".
  3. Связываем полученную матрицу с начальными остатками на начало периода и всеми движениями регистра за выбранный период.
  4. Рассчитываем конечный остаток для каждой пары как начальный остаток плюс сумма приходов минус сумма расходов по регистраторам, позиция которых меньше или равна текущему документу.

Способ 3. Программируемый обход в коде модуля

Посмотрим на алгоритм, который выгодно использовать при выгрузке данных в таблицу значений или программной подготовке источника данных. Этот подход исключает тяжелые соединения в SQL-запросах.


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

// 2. Получаем все движения за период в хронологическом порядке
ЗапросДвижения = Новый Запрос;
ЗапросДвижения.Текст = 
	"ВЫБРАТЬ
	|	Взаиморасчеты.Период КАК Период,
	|	Взаиморасчеты.Регистратор КАК Регистратор,
	|	Взаиморасчеты.ДоговорКонтрагента КАК Договор,
	|	Взаиморасчеты.ВидДвижения КАК ВидДвижения,
	|	Взаиморасчеты.СуммаВзаиморасчетов КАК Сумма
	|ИЗ
	|	РегистрНакопления.ВзаиморасчетыСКонтрагентами КАК Взаиморасчеты
	|ГДЕ
	|	Взаиморасчеты.Период МЕЖДУ &НачалоПериода И &КонецПериода
	|УПОРЯДОЧИТЬ ПО
	|	Период АВТОУПОРЯДОЧЕНИЕ";
ЗапросДвижения.УстановитьПараметр("НачалоПериода", НачалоПериода);
ЗапросДвижения.УстановитьПараметр("КонецПериода", КонецПериода);
Выборка = ЗапросДвижения.Выполнить().Выбрать();

// 3. В цикле актуализируем остатки и фиксируем срез на каждый регистратор
Пока Выборка.Следующий() Цикл
	// Обновляем остаток конкретного договора в таблице остатков
	СтрокаОстатка = ТаблицаОстатков.Найти(Выборка.Договор, "Договор");
	Если СтрокаОстатка = Неопределено Тогда
		СтрокаОстатка = ТаблицаОстатков.Добавить();
		СтрокаОстатка.Договор = Выборка.Договор;
		СтрокаОстатка.Остаток = 0;
	КонецЕсли;
	
	Если Выборка.ВидДвижения = ВидДвиженияНакопления.Приход Тогда
		СтрокаОстатка.Остаток = СтрокаОстатка.Остаток + Выборка.Сумма;
	Иначе
		СтрокаОстатка.Остаток = СтрокаОстатка.Остаток - Выборка.Сумма;
	КонецЕсли;
	
	// На данном шаге ТаблицаОстатков содержит полный актуальный срез
	// остатков по ВСЕМ договорам на момент текущего Регистратора
КонецЦикла;

Оценки производительности и рекомендации

Выбирая метод реализации, важно учитывать объем данных в информационной базе:

Расчет среза остатков по всем измерениям на каждый момент времени SQL-запросом создает нагрузку пропорционально произведению количества документов на количество аналитик. При большом количестве документов и договоров запрос с нарастающим итогом может выполнять длительный расчет.

Для больших объемов данных рекомендуется использовать связи наборов данных в СКД или программный обход в коде 1С, так как они выполняют расчет линейно и не перегружают СУБД избыточными соединительными таблицами.

← На главную