Наконец-то нашлось время написать об ещё одном варианте SAP коннектора, который также активно используется в работе!
Итак, встречаем: SAP BAPI Connector!
SAP BAPI Connector работает с функциональными модулями в SAP.
Функциональный модуль (ФМ, он же FM) — код ABAP, оформленный в виде отдельной функции, которая может быть повторно использована в других разработках.
Имеет два вида параметров:
- Импортируемые— данные, передаваемые «на вход» ФМ из вызывающей программы;
- Экспортируемые— данные, возвращаемые из ФМ, результат работы;
Как раз с этими параметрами и работает коннектор, передает определенные значения на вход и получает запрашиваемый результат.
Импортируемые параметры могут быть как определенными значениями или переменными, так и таблицами. Результат обычно в виде таблицы.
Использование SAP BAPI Connector
Мы используем SAP BAPI коннектор в двух случаях:
- Получение данных (таблицы) из уже существующего ФМ в SAP. Полученную таблицу из SAP встраиваем в ассоциативную модель Клика или выгружали в файл формата qvd.
- Передача данных в SAP с помощью импортируемого параметра в виде таблицы. Таким образом, мы передаем в модуль данные, а уже эти данные обрабатываются на стороне SAP и записываются в конечную таблицу на стороне SAP, т.е. мы напрямую передаём данные в структуру таблиц SAP. В качестве экспортируемых данных мы получаем таблицу с неуспешно обработанными записями, а также причиной, по которой запись была отклонена (этот функционал реализуется специалистами SAP).
Так как на таблицы SAP у нас есть строго только права для чтения, этот функционал оказался очень удобной возможностью для взаимодействия QlikView и SAP.
Теперь по шагам расскажу о каждом из двух случаев.
Создание коннекта и получение данных из ФМ SAP
Для начала нужно создать строку коннекта.
Выбираем среди вариантов коннекта QvSAPBAPIConnector (при наличии установленного SAP коннектора)
Специалисты SAP с удовольствием сообщат необходимые параметры, остается нажать Test Connection:
При корректном заполнении получим:
Далее переходим в режим работы с функциональными модулями и нажимаем на кнопку BAPI:
В появившемся окне последовательно выполняем действия:
- Указываем имя функционального модуля.
- Нажимаем кнопку “Get parameters” (Получить параметры).
- Заполняем необходимые значения для полученных параметров.
- Выбираем экспортируемый параметр.
- Нажимаем кнопку “Add call to script”
- Генерится код скрипта
- Нажимаем на кнопку «ОК» и получаем скрипт из п.6 в скрипте QV.
В полученном скрипте вместо значений параметра можно указать переменные.
Как результат получаем таблицу ET_CALENDAR_TB и далее либо используем её при построении ассоциативной модели, либо выгружаем данные в файл формата qvd.
Передача данных в SAP с помощью BAPI Connector
Этот случай мне кажется более интересным и необычным, поэтому подробно его рассмотрим.
Примерный алгоритм работы для передачи данных в виде таблицы в качестве параметра при использовании SAP BAPI Connector таков:
- Определиться с данными
- Подготовить данные
- Согласовать формат.
- Загрузить данные для предварительной сверки.
- Подготовить переменные.
- Передать данные в SAP
- Обработать данные на стороне SAP
- Обработать информацию, переданную от SAP (в нашем случае, как говорил, – это таблица с неуспешно обработанными записями и их причинами)
Пример
Давайте разберём на примере передачи данных в SAP по приходам денежных средств от инкассаций.
- Определяемся с данными, которые нужно отправить.
У нас есть таблица Result, которую нам надо передать в SAP:
Result:
Дата_прихода_корр,
ПФМ,
Проинкассировано,
Банк_план
- Подготовка данных.
Это самый большой пункт!
Во-первых, нам нужно узнать у специалистов SAP структуру таблицы, в которую будут заливаться данных.
Структура целевой таблицы SAP в данном случае такова:
MANDT, — номер манданта
BUKRS, — наименование балансовой единицы
BUDAT, — дата
KUNNR, — номер магазина
WAERS, — валюта
WRBTR, — сумма в валюте
HBKID — банк зачисления
У нас есть не все столбцы, поэтому недостающие столбцы создаём самостоятельно, остальное преобразуем. В итоге получаем такой код:
T1:
LOAD
‘100’ as MANDT,
‘BSTP’ as BUKRS,
Year(Дата_прихода_корр)&Num(Month(Дата_прихода_корр),’00’)&Num(Day(Дата_прихода_корр), ’00’) as BUDAT,
ПФМ as KUNNR,
‘RUB’ as WAERS,
Floor(Sum(Проинкассировано)) as WRBTR,
ApplyMap(‘BankNameMap2’, Банк_план) as HBKID
Resident Result
Where not IsNull(ПФМ)
Group By Дата_прихода_корр, ПФМ, ApplyMap(‘BankNameMap2’, Банк_план);
Важные преобразования:
- Поля MANDT, BURKS, WAERS создаём самостоятельно.
- Поле Дата_прихода_корр преобразовываем в формат принимаемый SAP,’ YYYYMMDD’.
- Поле Банк_план корректируем при помощи таблицы сопоставления на значения, которые используются в SAP, как короткий код кредитора.
- Поле Проинкассировано округляем и суммируем, так как после применения mapping может получиться несколько строк по одному магазину/банку в один день, что противоречит логике выгрузки.
- При передаче чисел с дробной частью необходимо передавать значения с разделителем ‘.’ (точка), запятую в качестве разделителя числа функциональный модуль не принял.
Так как логика выгрузки была такой, чтобы мы перезаписывали обновленные значения на будущие даты, возникла необходимость добавить одну проверку, на случай, если в новой выгрузке нет данных по каким-либо магазинам. Данную проверку можно реализовать и на стороне SAP, мы же сделали в QlikView таким образом:
- Добавляем в LOAD ключевое поле:
Num(Дата_прихода_корр) & ‘|’& ApplyMap(‘BankNameMap2’, Банк_план) &’|’& ПФМ as ПФМ_БАНК_ДАТА
- Выгружаем из таблицы, в которую будет идти выгрузка, данные по магазинам, которых нет в текущей выгрузке, вместо суммы передаём «0» для перезаписи этих данных в дальнейшем:
Concatenate (T1)
LOAD
If(isNum(KUNNR), Num(KUNNR),KUNNR) as KUNNR,
HBKID,
100 as MANDT,
‘RUB’ as WAERS,
‘BSTP’ as BUKRS,
0 as WRBTR,
Year(BUDAT)&Num(Month(BUDAT),’00’)&Num(Day(BUDAT),’00’) as BUDAT
Where not Exists(ПФМ_БАНК_ДАТА, Num(BUDAT)&’|’& HBKID &’|’& If(isNum(KUNNR), Num(KUNNR),KUNNR));
SELECT
KUNNR,
HBKID,
WRBTR,
BUDAT
From Z_TABLE_IN_SAP
WHERE BUDAT >= ‘$(vTodayYear)$(vTodayMonth)$(vTodayDay)’;
- Удаляем ключевое поле DROP Field ПФМ_БАНК_ДАТА;
Подготовка данных завершена, но это еще не все.
Теперь нужно преобразовать их для дальнейшей передачи в функциональный модуль.
Данные передаются в функциональный метод следующим образом (часть скрипта для поля MANDT):
«table»:
[
{
«field»:»MANDT»,
«length»:3,
«type»:»CHAR»,
«values»:
[ «100» ]
},
Внутри фигурных скобок мы должны передать все значения. Чтобы сделать это за одну команду, мы нашли такое решение — использовать переменные.
Итак:
- для начала объединяем все значения в одно поле, делаем это с помощью функции Concat. Обязательным является использование сортировки, т.к. без неё данные внутри полученного поля будут располагаться в хаотичном порядке.
В качестве разделителя нужно использовать «,».
- Таблица Т2 будет содержать только одну запись, а значение в каждом поле будет содержать все значения поля из предыдущей таблицы.
На примере поля BUDAT:
T2: //Объединяем значения по полям.
LOAD
Concat(MANDT, ‘»,»‘) as MANDTx,
Concat(BUKRS, ‘»,»‘) as BUKRSx,
Concat(BUDAT, ‘»,»‘, KUNNR *100000000 + BUDAT + WRBTR) as BUDATx, //KUNNR*100000000 + BUDAT, это сортировка при использовании Concat, без неё весь скрипт бесполезен.
Concat(KUNNR, ‘»,»‘, , KUNNR *100000000 + BUDAT + WRBTR) as KUNNRx,
Concat(WAERS, ‘»,»‘) as WAERSx,
Concat(WRBTR, ‘»,»‘, , KUNNR *100000000 + BUDAT + WRBTR) as WRBTRx,
Concat(HBKID, ‘»,»‘, , KUNNR *100000000 + BUDAT + WRBTR) as HBKIDx
Resident T1;
- И, наконец, значения из полученных полей передаём в переменные, здесь как вариант подойдёт функция Peek .
LET vMandt = Peek(‘MANDTx’); //Присваиваем переменным значения полей для дальнейшей загрузки в SAP
LET vBukrs = Peek(‘BUKRSx’);
LET vBudat = Peek(‘BUDATx’);
LET vKunnr = Peek(‘KUNNRx’);
LET vWaers = Peek(‘WAERSx’);
LET vWrbtr = Peek(‘WRBTRx’);
LET vHbkid = Peek(‘HBKIDx’);
- Осталось передать данные в SAP.
Производим коннект, как в предыдущем примере.
В данном случае, в качестве входного параметра представлена таблица:
Для примера и лучшего понимания заполним несколько записей, нажав на строку с таблицей:
Нажимаем Add call to script
В полученном скрипте остается заменить значения в квадратных скобках на созданные ранее переменные!
Вместо:
«field»:»KUNNR»,
«length»:10,
«type»:»CHAR»,
«values»:
[ «555», «777» ]
Получаем:
«field»:»KUNNR»,
«length»:10,
«type»:»CHAR»,
«values»:
[ «$(vKunnr)» ]
Важно: поставить переменную в кавычках.
- При запуске скрипта QlikView данные передаются функциональному модулю, который и обрабатывает полученные значения.
- В качестве экспортного параметра приходит таблица, при наличии записей в ней можно понять, какие данные не передались в таблицу.
Заметки:
- При необходимости, изменив строку коннекта, можно передать данные на другой сервер SAP (к примеру, на тестовый) или же несколько раз выгружать модуль с разными параметрами.
- Сами данные записываются в таблицу довольно быстро, но иногда ответ до QlikView доходит довольно долго и программа переходит к следующему шагу с задержкой. На практике это было только однажды. В результате разделили выгрузку на несколько потоков.
- Если после скрипта коннектора на вкладке есть ещё операторы, то на них не действует подсветка синтаксиса. Эту проблему решает создание новой вкладки.
- Обязательно нужно убедиться, что значения в таблице на стороне SAP соответствуют значениям в начальной таблице, при наличии несовпадений нужно вернуться к условию сортировки функции Concat.
Возможности SAP BAPI Connector оказались очень востребованы, мы смогли перенести в отчёты QlikView некоторые полезные отчёты напрямую из функциональных модулей, а возможность передачи таблицы в качестве параметра позволила отказаться от схем с выкладыванием файлов на сетевые папки. Стабильность, скорость и гибкость передачи данных увеличились.
Надеюсь, что данная статья будет полезной.
Задавайте свои вопросы.
Благодарю за внимание!
Хотел бы добавить, что запускать функциональный модуль SAP можно также и из другого коннектора входящего в семейство SAP Connector: Qv SAP Report Connector.
Например, вот так (имя модуля ZBW_CHAIN_START, через него Кликом дается команда на выполнение определенных действий в SAP BW):
CUSTOM CONNECT TO «Provider=QvSAPReportConnector.dll;ASHOST=…»;
[ZBW_CHAIN_START]:
Load ;
Select SAPREPORT (REPORT (ZBW_CHAIN_START), VARIANT (), ROWS_PER_RECORD (1));