Начиная с ABAP 7.4 SP08 нам стал доступен специальный тип BAdI — AMDP BAdI, который позволяет заменить/расширить стандартную реализацию AMDP процедур реализованных SAP-ом или в Custom решениях. Основное предназначение AMDP BAdI — вызов процедур реализованных в реализации AMDP BAdI из других AMDP процедур в системе. Чтобы это стало возможным используется ключевое слово USING в определении AMDP процедуры:
1 2 3 4 5 6 7 |
METHOD some_amdp_method BY DATABASE PROCEDURE FOR HDB LANGUAGE SQLSCRIPT OPTIONS READ-ONLY USING amdp_badi_name=>amdp_method_name. CALL "AMDP_BADI_NAME=>AMDP_METHOD_NAME" (:some_parameters); ENDMETHOD. |
Особенности AMDP BAdI:
- Нет возможности использования BAdI фильтров
- Fallback класс — обязателен для реализации BAdI (данный класс определяет поведение по умолчанию, если не определена иная реализация)
- Каждый метод AMDP BAdI класса реализации должен быть AMDP процедурой
- Вызов таких BAdI может осуществляться обычным образом через GET BADI и CALL BADI
Далее реализуем свой AMDP BAdI, реализацию к нему по умолчанию через Fallback класс, а так же дополнительную Custom реализацию.
Создание своего AMDP BAdI
Для создания своего AMDP BAdI необходимо перейти в транзакцию SE20 и создать новую точку расширения — Enhancement Spot:
Native диалог в Eclipse ADT не доступен на моей системе, так или иначе перенаправит в SE20, но в самых последних версиях систем мы можем править точку расширения прямо в ADT
Пускай её имя будет — ZES_CUSTOM_AMDP_BADI_SAMPLE.
В следующем диалоге мы можем указать композитную точку расширения, если хотим объединить несколько точек в одну, но в примере этого делать не будем, оставляем пустым:
Далее необходимо указать запрос в рамках которого мы хотим сохранить нашу точку расширения, либо отметить её как локальный объект. После чего мы попадём на экран определения BAdI в рамках точки расширения, где можем нажать на создание нового BAdI:
В нашем демо сценарии мы создадим BAdI для поиска пользователей по заданным критериям фильтрации. Назовём BAdI следующим образом — ZBD_AMDP_USER_SEARCH:
Отметим сразу галочкой что мы создаём именно AMDP BAdI:
Следующим шагом определим имя интерфейса — ZIF_AMDP_USER_SEARCH:
После чего система сразу предложит перейти к определению методов интерфейса — согласимся.
Добавим в интерфейс метод: SEARCH_USERS:
В качестве параметра у нас будет строка с фильтром и возвращать мы будем таблицу со структурой таблицы USR02:
Обратите внимание, так как мы создаём интерфейс для вызова AMDP процедуры, передача параметров должна быть по значению.
После определения метода, сохраним интерфейс и выйдем к экрану ведения BAdI и зададим Fallback класс с именем — ZCL_BI_FB_AMDP_USER_SEARCH:
Далее система вновь предложит выбрать запрос и перейти в редактирование класса — соглашаемся, сохраняем его и активируем. Добавить реализацию AMDP метода мы не сможем в GUI транзакции, далее мы должны пользоваться Eclipse и ADT.
Первым делом откроем наш BAdI интерфейс и добавим к нему маркер AMDP:
1 2 3 4 5 6 7 8 9 10 11 12 |
INTERFACE zif_amdp_user_search PUBLIC . INTERFACES if_badi_interface . INTERFACES if_amdp_marker_hdb. METHODS search_users IMPORTING VALUE(iv_filter) TYPE string EXPORTING VALUE(et_users) TYPE tb_usr02 . ENDINTERFACE. |
Сохраним и активируем.
Далее открываем наш Fallback класс в Eclipse и пишем реализацию поиска по умолчанию:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
CLASS zcl_bi_fb_amdp_user_search DEFINITION PUBLIC FINAL CREATE PUBLIC . PUBLIC SECTION. INTERFACES if_badi_interface . INTERFACES zif_amdp_user_search . PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS zcl_bi_fb_amdp_user_search IMPLEMENTATION. METHOD zif_amdp_user_search~search_users BY DATABASE PROCEDURE FOR HDB LANGUAGE SQLSCRIPT OPTIONS READ-ONLY USING usr02. et_users = SELECT * FROM usr02 WHERE CONTAINS ((bname), :iv_filter) AND mandt = SESSION_CONTEXT( 'CLIENT' ); ENDMETHOD. ENDCLASS. |
Сохраняем, активируем класс и переходим обратно в SE20 где активируем точку расширения.
На данном этапе мы создали новое Custom AMDP BAdI и Fallback класс с реализацией по умолчанию. Логика реализации по умолчанию заключается в поиске всех пользователей чьё техническое имя содержит переданный фильтр.
Вызов AMDP BAdI
Вызов AMDP BAdI возможен двумя способами:
- Через стандартные GET BADI и CALL BADI
- Вызов из AMDP процедуры.
Для первого варианта напишем демо отчёт следующего вида:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
REPORT zcustom_badi_caller. PARAMETERS: p_search TYPE string. START-OF-SELECTION. DATA: go_badi TYPE REF TO zbd_amdp_user_search. TRY. GET BADI go_badi. CALL BADI go_badi->search_users EXPORTING iv_filter = p_search IMPORTING et_users = DATA(gt_users). DATA(go_alv) = NEW cl_gui_alv_grid( i_parent = cl_gui_container=>default_screen ). go_alv->set_table_for_first_display( EXPORTING i_structure_name = 'USR02' CHANGING it_outtab = gt_users ). WRITE space. CATCH cx_root. STOP. ENDTRY. |
В качестве примера строка поиска:
Результат — все пользователи с именем DEV* или S*:
Несмотря на возможность прямого вызова из ABAP кода, в основном AMDP BAdI необходимы для их вызова из AMDP процедур.
Обычно в таких процедурах написана некая стандартная логика и вызывается дополнительно Custom реализация через BAdI, например чтобы обогатить массив выбираемых данных.
Создадим подобный AMDP класс со стандартной логикой и следующим именем — zcl_amdp_user_search:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
CLASS zcl_amdp_user_search DEFINITION PUBLIC FINAL CREATE PUBLIC . PUBLIC SECTION. INTERFACES: zif_amdp_user_search. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS zcl_amdp_user_search IMPLEMENTATION. METHOD zif_amdp_user_search~search_users BY DATABASE PROCEDURE FOR HDB LANGUAGE SQLSCRIPT OPTIONS READ-ONLY USING usr02 zbd_amdp_user_search=>search_users. et_default_users = SELECT * FROM usr02 WHERE CONTAINS ((bname), :iv_filter) AND mandt = SESSION_CONTEXT( 'CLIENT' ); CALL "ZBD_AMDP_USER_SEARCH=>SEARCH_USERS" (:iv_filter, :et_users); et_users = SELECT * FROM :et_default_users UNION SELECT * FROM :et_users; ENDMETHOD. ENDCLASS. |
В качестве демо сценария наш AMDP класс использует свою стандартную логику поиска пользователей, дополнительно обогащая результат выборки через вызов AMDP BAdI и объединяет оба массива данных. По умолчанию логика выборки et_default_users ничем не отличается от реализованной в Fallback классе. Про оптимальность подобной дублирующей логики говорить тут не будем, все таки демо сценарий 🙂
Ну и создадим демо отчёт по вызову нового AMDP класса:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
REPORT zcustom_amdp_caller. PARAMETERS: p_search TYPE string. START-OF-SELECTION. DATA: go_amdp TYPE REF TO zcl_amdp_user_search. TRY. go_amdp = NEW #( ). go_amdp->zif_amdp_user_search~search_users( EXPORTING iv_filter = p_search IMPORTING et_users = DATA(gt_users) ). DATA(go_alv) = NEW cl_gui_alv_grid( i_parent = cl_gui_container=>default_screen ). go_alv->set_table_for_first_display( EXPORTING i_structure_name = 'USR02' CHANGING it_outtab = gt_users ). WRITE space. CATCH cx_root. STOP. ENDTRY. |
Результат его работы будет аналогичен предыдущему отчёту.
Создание Custom реализации AMDP BAdI
Для того чтобы наши демо отчёты стали возвращать какой-то иной массив данных, создадим новую реализацию BAdI.
В SE20 в разделе реализации создадим новую реализацию точки расширения с именем — ZEI_CUS_AMDP_USER_SEARCH:
Реализацией — ZBI_CUSTOM_USER_SEARCH и классом реализации — ZCL_BI_CUSTOM_USER_SEARCH:
При создании система уведомит что уже есть реализация Fallback класса, скопируем его реализацию.
Далее откроем класс в Eclipse и напишем обновлённую логику:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
CLASS zcl_bi_custom_user_search DEFINITION PUBLIC FINAL CREATE PUBLIC . PUBLIC SECTION. INTERFACES if_badi_interface . INTERFACES if_amdp_marker_hdb . INTERFACES zif_amdp_user_search . PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS zcl_bi_custom_user_search IMPLEMENTATION. METHOD zif_amdp_user_search~search_users BY DATABASE PROCEDURE FOR HDB LANGUAGE SQLSCRIPT OPTIONS READ-ONLY USING usr02 usr21 adrp. et_users = SELECT usr02.* FROM usr02 INNER JOIN usr21 ON usr21.bname = usr02.bname AND usr21.mandt = usr02.mandt INNER JOIN adrp ON adrp.persnumber = usr21.persnumber AND adrp.client = usr02.mandt AND adrp.date_from = '00010101' WHERE contains ((adrp.name_text), :iv_filter) AND usr02.mandt = session_context( 'CLIENT' ); ENDMETHOD. ENDCLASS. |
В части реализации мы сделали поиск не по техническому имени пользователя, а по его фамилии и имени из адресных данных.
Активируем класс, в SE20 активируем нашу BAdI реализацию, теперь она отображается в точке расширения как активная:
Запустим любой из наших отчётов с новой строкой поиска:
Результат:
По новой логике мы сформировали массив пользователей у которых:
- Техническое имя начинается на JANE* или DEV*
- Имя и фамилия содержат JANE* или DEV*
Пользователь BWDEVELOPER имеет следующее имя:
Таким образом реализовав Custom реализацию AMDP BAdI мы расширили логику определяемую по умолчанию.