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

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

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

Почему не работает стандартный запрос и конструкция ИТОГИ ПЕРИОДАМИ

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

  1. Ограничение виртуальной таблицы: виртуальная таблица СрезПоследних принимает в качестве параметра периода только константу или параметр запроса. В нее нельзя передать поле из другой таблицы или выборки, чтобы выполнить срез динамически для каждой строки.
  2. Ошибочная логика группировки: если использовать вложенный запрос с агрегатной функцией МАКСИМУМ(Период) без связи по условию ПериодРегистра <= ДатаСреза, запрос вернет одну глобальную максимальную дату за всю историю регистра по каждой валюте. В итоге во всех строках отчета отобразится только самый последний курс.
  3. Особенность периодических итогов: предложение ИТОГИ ПО ... ПЕРИОДАМИ строит регулярную временную сетку интервалов, но не заполняет автоматически значения ресурсов из регистра сведений для периодов, в которых не было физических движений.

Вариант 1. Классический срез на каждую дату через временные таблицы в запросе

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

Шаг 1. Формирование таблицы дат и валют. Поместим исходные данные (список валют и требуемых дат) во временную таблицу. В реальном отчете источником могут служить документы или календарь периодов.

Шаг 2. Поиск максимальной даты записи. Соединим таблицу дат с физической таблицей регистра сведений левым соединением по условию совпадения валюты и неравенству дат Регистр.Период <= ТаблицаДат.Период. Сгруппируем результат по валюте и дате среза, получив максимальную дату фактической записи в регистре.

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

Посмотрим на пример текста запроса:


ВЫБРАТЬ
    ТаблицаДат.Валюта КАК Валюта,
    ТаблицаДат.Период КАК Период
ПОМЕСТИТЬ ВТ_Даты
ИЗ
    &ТаблицаДат КАК ТаблицаДат
;

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

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ВТ_МаксимальныеДаты.Валюта КАК Валюта,
    ВТ_МаксимальныеДаты.ПериодСреза КАК Период,
    ЕСТЬNULL(КурсыВалют.Курс, 1) КАК Курс,
    ЕСТЬNULL(КурсыВалют.Кратность, 1) КАК Кратность
ИЗ
    ВТ_МаксимальныеДаты КАК ВТ_МаксимальныеДаты
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КурсыВалют КАК КурсыВалют
        ПО ВТ_МаксимальныеДаты.Валюта = КурсыВалют.Валюта
            И ВТ_МаксимальныеДаты.ПериодКурса = КурсыВалют.Период

Вариант 2. Использование Системы компоновки данных (СКД)

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

  1. Создадим первый набор данных (запрос) со списком дат и валют.
  2. Создадим второй набор данных (запрос) к виртуальной таблице:
    
    ВЫБРАТЬ
        КурсыВалютСрезПоследних.Валюта КАК Валюта,
        КурсыВалютСрезПоследних.Курс КАК Курс,
        КурсыВалютСрезПоследних.Кратность КАК Кратность
    ИЗ
        РегистрСведений.КурсыВалют.СрезПоследних(&Период, Валюта = &Валюта) КАК КурсыВалютСрезПоследних
    
  3. Перейдем на вкладку Связи наборов данных и настроим передачу параметров:

    В качестве источника связи укажем основной набор, в качестве приемника — набор со срезом. В колонке Условие связи свяжем поле Валюта, а в колонке Параметры сопоставим параметру Период значение поля Период из основного набора данных.

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

Вариант 3. Получение курсов программно во встроенном языке

Если требуется получить срез курса на дату в цикле обработки данных или в процедуре заполнения документа, рассмотрим использование метода менеджера регистра сведений ПолучитьПоследнее():


Отбор = Новый Структура("Валюта", ТекущаяВалюта);
ЗначенияКурса = РегистрыСведений.КурсыВалют.ПолучитьПоследнее(ДатаСреза, Отбор);

ТекущийКурс = ?(ЗначенияКурса.Курс = 0, 1, ЗначенияКурса.Курс);
ТекущаяКратность = ?(ЗначенияКурса.Кратность = 0, 1, ЗначенияКурса.Кратность);

Важные практические нюансы

  1. Учет кратности: курс большинства валют устанавливается с учетом кратности (например, за 10, 100 или 1000 единиц). При любых расчетах всегда выбирайте ресурс Кратность и рассчитывайте реальный курс как Курс / Кратность.
  2. Обработка отсутствующих значений: если дата документа или период отчета предшествует самой первой записи курса в базе, левое соединение вернет значение NULL. Обязательно оборачивайте числовые ресурсы функцией ЕСТЬNULL(), подставляя значение по умолчанию (обычно 1), чтобы предотвратить ошибки деления на ноль.
← На главную