Как в запросе 1С получить курс валюты на дату каждого документа (срез последних на каждую дату)?

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

При работе в конфигурации «Управление торговлей 10.3» (или любой другой конфигурации на платформе 1С:Предприятие) разработчики часто сталкиваются с задачей: вывести в одном запросе список документов (например, реализаций или возвратов) и актуальный курс валюты на момент совершения каждой хозяйственной операции. Задача усложняется тем, что курс вводится не каждый день, а периодически (например, раз в неделю или две), поэтому просто соединить таблицы по точному совпадению дат невозможно.

Стандартная виртуальная таблица СрезПоследних() периодического регистра сведений умеет получать срез только на одну конкретную дату, передаваемую в качестве параметра. Если нам нужно получить курс на множество разных дат из таблицы документов, этот механизм в лоб не сработает. В этой статье мы подробно разберем классический и наиболее производительный способ решения этой задачи — метод «Среза последних на каждую дату в запросе».

Постановка задачи и анализ проблемы

Представим ситуацию: нам необходимо построить запрос по табличной части документа ВозвратТоваровОтПокупателя или РеализацияТоваровУслуг. Требуется вывести не только количество и сумму продажи, но и курс доллара США (USD), который действовал на дату проведения каждого конкретного документа. При этом курс в регистр сведений КурсыВалют заносится вручную раз в две недели.

Если мы попытаемся сделать простое левое соединение таблицы документов с таблицей регистра по условию Документ.Дата = КурсыВалют.Период, то мы не получим данные для тех дней, когда курс не вводился. Нам нужно соединение, которое найдет ближайшую предыдущую (или совпадающую) дату записи курса для каждого документа.

Для реализации этого алгоритма мы применим технологию временных таблиц. Весь процесс мы разобьем на три логических этапа:

  1. Выборка исходных документов во временную таблицу.
  2. Поиск самой свежей даты курса для каждого документа (максимальный период регистра, который меньше или равен дате документа).
  3. Соединение исходных документов с найденными датами и получение физических значений курса и кратности из регистра сведений.

Шаг 1. Подготовка исходных данных во временной таблице

Сначала выберем все необходимые данные по документам реализации и поместим их во временную таблицу ВТ_Реализации. Это позволит нам зафиксировать выборку документов и оптимизировать дальнейшие соединения. Использование временных таблиц крайне важно для производительности СУБД.

Рассмотрим код первого пакета запроса:


ВЫБРАТЬ
	ЛОЖЬ КАК Признак,
	РеализацияТоваровУслугТовары.Ссылка.Дата КАК Дата,
	РеализацияТоваровУслугТовары.Ссылка КАК ДокументПартии,
	РеализацияТоваровУслугТовары.Номенклатура КАК Номенклатура,
	РеализацияТоваровУслугТовары.СерияНоменклатуры КАК СерияНоменклатуры,
	РеализацияТоваровУслугТовары.Цена КАК Цена,
	РеализацияТоваровУслугТовары.Количество КАК КоличествоОтгружено,
	РеализацияТоваровУслугТовары.Коэффициент КАК Коэффициент,
	РеализацияТоваровУслугТовары.СтавкаНДС КАК СтавкаНДС,
	РеализацияТоваровУслугТовары.Сумма КАК СуммаОтгружено,
	РеализацияТоваровУслугТовары.СуммаНДС КАК СуммаНДСОтгружено,
	РеализацияТоваровУслугТовары.Склад КАК Склад,
	РеализацияТоваровУслугТовары.ЗаказПокупателя КАК ЗаказПокупателя,
	РеализацияТоваровУслугТовары.ЕдиницаИзмерения КАК ЕдиницаИзмерения,
	РеализацияТоваровУслугТовары.ЗаказПокупателя.ДоговорКонтрагента КАК ДоговорКонтрагента,
	РеализацияТоваровУслугТовары.Качество КАК Качество
ПОМЕСТИТЬ ВТ_Реализации
ИЗ
	Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТоваровУслугТовары
ГДЕ
	РеализацияТоваровУслугТовары.Ссылка.Контрагент = &Контрагент
	И РеализацияТоваровУслугТовары.Ссылка.Проведен
	И РеализацияТоваровУслугТовары.Ссылка.Дата < &Дата
	И РеализацияТоваровУслугТовары.Номенклатура = &Номенклатура
ИНДЕКСИРОВАТЬ ПО
	ДокументПартии,
	Дата
;

Важный нюанс: Обратите внимание на инструкцию ИНДЕКСИРОВАТЬ ПО в конце пакета. Индексирование временной таблицы по полям, которые будут участвовать в связях (в данном случае ДокументПартии и Дата), существенно ускоряет выполнение запроса на больших базах данных.

Шаг 2. Определение дат актуальных курсов валют

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

Сгруппировав результат по документам, мы находим максимальную дату курса (функция МАКСИМУМ). Это и будет дата последней установленной котировки на момент проведения операции:


ВЫБРАТЬ
	ВТ_Реализации.ДокументПартии КАК ДокументПартии,
	МАКСИМУМ(КурсыВалют.Период) КАК ПериодКурса
ПОМЕСТИТЬ ВТ_МаксДатыКурсов
ИЗ
	ВТ_Реализации КАК ВТ_Реализации
		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют КАК КурсыВалют
			ПО КурсыВалют.Период <= ВТ_Реализации.Дата
				И КурсыВалют.Валюта = &ВалютаUSD
СГРУППИРОВАТЬ ПО
	ВТ_Реализации.ДокументПартии
ИНДЕКСИРОВАТЬ ПО
	ДокументПартии,
	ПериодКурса
;

Благодаря этому шагу мы отсекаем все промежуточные и неактуальные даты курсов, оставляя для каждого документа ровно одну правильную дату из регистра сведений.

Шаг 3. Получение значений курса и формирование итоговой таблицы

На заключительном этапе мы соединяем исходную таблицу документов ВТ_Реализации с таблицей найденных дат ВТ_МаксДатыКурсов, а затем повторно соединяем ее с регистром КурсыВалют. На этот раз соединение происходит по точному совпадению даты (полученной на шаге 2) и выбранной валюты. Это позволяет нам вытащить реальные значения курса и его кратности.

Посмотрим на итоговую часть запроса:


ВЫБРАТЬ
	ВТ_Реализации.Признак КАК Признак,
	ВТ_Реализации.Дата КАК Дата,
	ВТ_Реализации.ДокументПартии КАК ДокументПартии,
	ВТ_Реализации.Номенклатура КАК Номенклатура,
	ВТ_Реализации.СерияНоменклатуры КАК СерияНоменклатуры,
	ВТ_Реализации.Цена КАК Цена,
	ВТ_Реализации.КоличествоОтгружено КАК КоличествоОтгружено,
	ВТ_Реализации.Коэффициент КАК Коэффициент,
	ВТ_Реализации.СтавкаНДС КАК СтавкаНДС,
	ВТ_Реализации.СуммаОтгружено КАК СуммаОтгружено,
	ВТ_Реализации.СуммаНДСОтгружено КАК СуммаНДСОтгружено,
	ВТ_Реализации.Склад КАК Склад,
	ВТ_Реализации.ЗаказПокупателя КАК ЗаказПокупателя,
	ВТ_Реализации.ЕдиницаИзмерения КАК ЕдиницаИзмерения,
	ВТ_Реализации.ДоговорКонтрагента КАК ДоговорКонтрагента,
	ВТ_Реализации.Качество КАК Качество,
	ЕСТЬNULL(КурсыВалют.Курс, 1) КАК КурсUSD,
	ЕСТЬNULL(КурсыВалют.Кратность, 1) КАК КратностьUSD
ИЗ
	ВТ_Реализации КАК ВТ_Реализации
		ЛЕВОЕ СОЕДИНЕНИЕ ВТ_МаксДатыКурсов КАК ВТ_МаксДатыКурсов
			ПО ВТ_Реализации.ДокументПартии = ВТ_МаксДатыКурсов.ДокументПартии
		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют КАК КурсыВалют
			ПО ВТ_МаксДатыКурсов.ПериодКурса = КурсыВалют.Период
				И КурсыВалют.Валюта = &ВалютаUSD
УПОРЯДОЧИТЬ ПО
	Дата УБЫВ

Как правильно применить этот код во встроенном языке 1С?

Чтобы запустить этот механизм на форме документа или в обработке, нам нужно передать в параметры запроса ссылку на валюту USD. Давайте посмотрим, как это сделать в модуле формы:


Запрос = Новый Запрос;
// Указываем весь текст пакета запросов, написанный выше
Запрос.Текст = "..."; // Сюда вставляем объединенный текст из трех шагов

// Установка стандартных параметров автора
Запрос.УстановитьПараметр("Контрагент", владелецформы.Контрагент);
Запрос.УстановитьПараметр("Номенклатура", ТекСтрока.Номенклатура);
Запрос.УстановитьПараметр("Дата", владелецформы.Дата);

// Находим валюту USD по коду "840" и передаем в параметры
ВалютаUSD = Справочники.Валюты.НайтиПоКоду("840");
Если ВалютаUSD.Пустая() Тогда
	// Если по коду не нашли, попробуем по наименованию
	ВалютаUSD = Справочники.Валюты.НайтиПоНаименованию("USD");
КонецЕсли;
Запрос.УстановитьПараметр("ВалютаUSD", ВалютаUSD);

РезультатЗапроса = Запрос.Выполнить().Выгрузить();
ЭлементыФормы.ТаблицаРеализации.Значение = РезультатЗапроса;
ЭлементыФормы.ТаблицаРеализации.СоздатьКолонки();

Важные технические нюансы, о которых часто забывают разработчики

При работе с курсами валют в 1С есть подводные камни, которые могут привести к неверным финансовым расчетам или ошибкам в программе. Разберем их подробнее:

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

← На главную