...

Получение ежедневных курсов валют

Тема в разделе "Примеры решений и дополнительных модулей", создана пользователем kirillovykh, 27 май 2024.

  1. kirillovykh

    kirillovykh Участник

    Бизнес кейс
    Получение актуальных курсов валют

    Постановка
    Системный процесс на получение курсов валют

    Решение
    Данное решение является переработанным решением https://community.elma365.com/ru/threads/2241/.
    Основное отличие в том, что данные по курсам валют обрабатываются за один обход сопоставления строкового представления ответа с регулярным выражением с использованием именованных групп. Также разобран вопрос со временем запуска получения курсов.

    Веб-сервис для получения ежедневных данных ЦБР – https://cbr.ru/DailyInfoWebServ/DailyInfo.asmx.
    Метод для получения ежедневных курсов валют – https://cbr.ru/DailyInfoWebServ/DailyInfo.asmx?op=GetCursOnDateXML.
    Ответом являются курсы валют на заданный день в формате xml документа.

    Метод GetCursOnDate не используется, т.к. xml представление DataSet в данном решении является избыточным, но это не критично (т.к. в ответе добавляется «обёртка» таблицы).
    GET запрос на https://www.cbr.ru/scripts/XML_daily.asp не используется, т.к. было принято решение взаимодействовать с веб-сервисом DailyInfo.

    Используется версия метода с SOAP 1.2, что также не является критичным, но различия имеются (https://www.w3.org/2003/06/soap11-soap12.html). И судя по тэгам xml, в ответе на SOAP 1.2 приходит SOAP 1.1.

    Полученный ответ обрабатывается путём сопоставления строкового представления ответа с регулярным выражением (метод matchAll). В регулярном выражении используются Именованные группы для удобства обработки. На выходе – массив совпадений, где для каждого совпадения сформирована группа в виде объекта.
    Код:
    
    /**
    * Интерфейс для работы с именоваными группами в regex
    *
    * @remarks
    * Для поиска по содержимому тэга ValuteCursOnDate (Курс валюты) в XML Документе
    */
    interface ValuteCursOnDate {
        /**
         * Название валюты
         */
        name: string;
        /**
         * Номинал
         */
        nom: number;
        /**
         * Курс
         */
        curs: number;
        /**
         * ISO Цифровой код валюты
         */
        code: string;
        /**
         * ISO Символьный код валюты
         */
        chCode: string;
        /**
         * Курс за 1 единицу валюты
         */
        unitRate: number;
    }

    /**
    * Получить курсы валют на день
    */
    async function getDailyInfoXMLSoap12(): Promise<void> {
        // GetCursOnDateXML - Получение ежедневных курсов валют (как XMLDocument)
        const dailyinfo_response = await fetch(`https://www.cbr.ru/DailyInfoWebServ/DailyInfo.asmx`, {
            method: 'POST',
            headers: {
                'Content-Type': 'text/xml; charset=utf-8',
                'SOAPAction': 'http://web.cbr.ru/GetCursOnDateXML'
            },
            body: `<?xml version="1.0" encoding = "utf-8"?>
                    <soap12:Envelope xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd = "http://www.w3.org/2001/XMLSchema" xmlns:soap12 = "http://www.w3.org/2003/05/soap-envelope">
                        <soap12:Body>
                            <GetCursOnDateXML xmlns="http://web.cbr.ru/">
                                <On_date>${new Datetime().format()}</On_date>
                            </GetCursOnDateXML>
                        </soap12:Body>
                    </soap12:Envelope>`
        });

        if (!dailyinfo_response.ok) {
            throw new Error(`dailyinfo_response is not ok at ${getDailyInfoXMLSoap12.name}; Status: ${dailyinfo_response.status}, statusText: ${dailyinfo_response.statusText}.`);
        }

        const text: any = await dailyinfo_response.text();

        if (!text.matchAll) {
            throw new Error(`matchAll is not supported at ${getDailyInfoXMLSoap12.name}`);
        }

        // Match XMLDocument by regex with Named capturing groups
        // Поиск совпадений выполняется по тэгу ValuteCursOnDate и его содержимому. Для получения содержимого используются именованные группы
        const regex = /<ValuteCursOnDate>(?:.*?)<Vname>(?<name>.*?)<\/Vname>(?:.*?)<Vnom>(?<nom>.*?)<\/Vnom>(?:.*?)<Vcurs>(?<curs>.*?)<\/Vcurs>(?:.*?)<Vcode>(?<code>.*?)<\/Vcode>(?:.*?)<VchCode>(?<chCode>.*?)<\/VchCode>(?:.*?)<VunitRate>(?<unitRate>.*?)<\/VunitRate>(?:.*?)<\/ValuteCursOnDate>/gs;
        const match_all_iterator = text.matchAll(regex);

        const curs_on_date_table = Context.fields.curs_on_date_table.create();

        for (let exec_result of match_all_iterator) {
            if (!exec_result.groups) {
                throw new Error(`capturing groups is undefined at ${getDailyInfoXMLSoap12.name}`);
            }

            const groups: ValuteCursOnDate = exec_result.groups;

            const row = curs_on_date_table.insert();

            row.vchcode = groups.chCode;
            row.vcode = groups.code;
            row.vcurs = groups.curs;
            row.vname = groups.name;
            row.vnom = groups.nom;
            row.vunitrate = groups.unitRate;
        }

        // Update table
        Context.data.curs_on_date_table = curs_on_date_table;
    }

    В части сервисного процесса интересен только момент с определением времени запуска. По информации с сайта ЦБР:
    • официальные курсы валют устанавливаются ежедневно (за исключением нерабочих дней), вступают в силу на следующий календарный день после дня установления и действуют до вступления в силу следующего курса данной валюты. http://www.cbr.ru/Reception/TopicalMessage/Page/2656
    • точное время публикации официальных курсов валют в течение текущего дня не регламентировано Указанием Банка России от 03.10.2022 г. № 6290-У. Банк России, установив официальные курсы, публикует указанную информацию до 17:00 по мск. http://www.cbr.ru/Reception/TopicalMessage/Page/2661
    В тексте Указания для валют Доллар США, Евро и Китайский юань курс устанавливается на основе данных о средневзвешенном курсе, рассчитанном по сделкам, заключенным в период с 10:00 по 15:30 по мск. Официальные курсы других валют устанавливаются на основе соотношения данных о курсе доллара США по отношению к рублю и официальных курсов доллара США по отношению к соответствующим валютам, установленных и размещенных на официальных сайтах центральными (национальными) банками на 15:30 мск.

    По итогу, запуск процесса для достаточно гарантированного получения установленного курса валют на следующий день возможно выполнять после 17:00 мск текущего дня.

    Дополнительно возможно добавить проверку на дату установления курса по параметру OnDate в ответе, либо перед выполнением запроса на GetCursOnDateXML проверять последнюю дату публикации курса валют через GetLatestDateTime
    Последнее редактирование: 27 май 2024