Sapforum.Biz

Инструменты => ABAP - Инструментальные средства => Техники расширений системы SAP => Тема начата: Uukrul от Сентябрь 01, 2016, 12:24:39 pm

Название: 7 - BADI - Технология внедрения бизнес расширений / дополнений
Отправлено: Uukrul от Сентябрь 01, 2016, 12:24:39 pm
BADI – Технология внедрения бизнес расширений / дополнений в код стандартных транзакций, данная техника доступна в любых модулях системы, фактически эта технология вышла для замены техники Customerexits, используя объектно-ориентированный подход к реализации расширений системы. Технология BADI призвана решить основную проблему использования точки расширения несколькими пользователями с изоляцией реализаций. Если проанализировать код системы и посмотреть места добавления вызовов BADI, то можно заменить, что вызовы добавлены до или после вызова Customerexits, т.е. функции вида CALL CUSTOMER-FUNCTION. Следовательно, в большинстве случаев можно использовать Customerexits или соответствующий ему BADI. Кстати, в таком случае в классе такого BADI перечень доступных для реализации методов, совпадает по количеству с перечнем функций в рамках компонента расширения Customerexits-а.

На данном этапе существуют два варианта реализации технологии BADI, это так называемы старые и новые BADI расширения. Различие между ними, это способ реализации класса расширения. В старых BADI, как я понимаю, реализация была сделана следующим образом, в программе в точке расширения реализовывался вызов соответствующего метода класса. Пользователю, фактически предлагалось, переопределять метод класса и таким образом выстраивалась цепочка независимых реализаций. Такая методика позволяла разнести разные реализации в свои расширения, однако проблемы в одной из наследуемых реализации могли поломать работу всех пользовательских расширений. Поэтому, через некоторое время компания перешла на новый тип BADI, теперь при реализации вы пишете класс, как наследник заранее предопределенного класса, реализующего расширение. В точке вызова система проверяет наличие всех созданных и активных инстанций – наследников от базового класса расширения и вызывает соответствующие методы всех зарегистрированных классов наследников. Перечень методов, которые будут вызваться и точки вызова, заранее определены в родительском классе. Для механизма реализации новых BADI в язык системы были введены две новые служебные команды GET BADI и CALL BADI.

Примечание: По заявлениям разработчиков SAP, новая техника BADI работает быстрее предыдущего используемого механизма. Хотя это и логично, так как новый механизм использует новые команды языка, а не вызов класса который определяет наличие расширения. Однако среднестатистический пользователь вряд ли заметит какое либо ускорение при вызовах новой или старой реализации.

На первый взгляд, для пользователя особо ничего не изменяется при реализации старых или новых BADI. Однако, на самом деле отличия существенные. Новая техника расширений решила проблему хранения глобальных переменных в рамках класса реализации, что было довольно проблематично реализовать используя механизмы наследования методов. Классы реализации полностью стали независимыми и соответственно разработчики получили раздельные объекты, которые можно независимо и параллельно обрабатывать. Именно новая технология BADI, предоставляет полную изоляцию каждой инстанции, не понимание этих различий, приводит к не правильному использованию новых BADI. Например, при переходе от старого типа реализации к новому набор методов остался старый, а вот параметры методов существенно изменились, что привело к полной дезориентации части разработчиков, особенно индусских. Простой пример, в системе существует BADI: MB_MIGO_BADI – Поля пользователя на экране MIGO. В старой реализации метода, если правильно помню (к сожалению старой системы, у меня уже нет), CHECK_ITEM вам передавалась позиция документа, которую вы могли проверить на ошибки и вернуть результат проверки в параметр ET_BAPIRET2. В новой реализации в данный метод передается только значение переменной I_LINE_ID – Unique Identification of Document Line, т.е. номер позиции которую надо проверить, но данных самой позиции вам не передается. Это привело к тому, что на куче индусских форумов и части русскоязычных, скопированных из индусских, реализуется механизм сохранения вводимых позиций через IMPORT TO MEMORY в методе IF_EX_MB_MIGO_BADI~LINE_MODIFY, чтобы потом в методе проверки сделать EXPORT FROM MENORY и далее проверить значения. Я так понимаю, все реализующие этот механизм, считают себя профессионалами, а вот разработчиков компании SAP полными идиотами, которые даже нормально не понимают, что забыли передать в метод проверки сам объект проверки – позицию документа. Они, видите ли, передали какую-то непонятную переменную I_LINE_ID, которая содержит просто число, причем это число даже не является порядковым номером позиции документа. В общем, совсем не понятно как теперь работать и поэтому люди пишут какие-то кривые обходные методики для получения доступа к позициям документа внутри BADI, но при этом не просто пишут, они еще и рекомендуют их как единственно верное решение. Лично я, когда впервые столкнулся с новым BADI, и почитал предложенные механизмы, решил что это я, что-то не понимаю, но вряд ли разработчики SAP, могли так ошибиться. На самом деле, достаточно было просмотреть рекомендуемую реализацию шаблона BADI, чтобы понять, что действительно заблуждаюсь я, и действительно, в методе проверки CHECK_ITEM, при использовании нового механизма BADI, не нужно передавать строку документа.

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


Создание расширения для транзакции MIGO

Пример работы с BADI, предлагаю рассмотреть на основе MB_MIGO_BADI, раз уже начал этот раздел с ее упоминания. Так как система новая то будем использовать механизм реализации нового BADI. Кстати, если вы выберете использовать старый или так называемый классический BADI, то все равно на определенном этапе реализации система скажет вам, что уже существует новая реализация для данного BADI, поэтому будет выполнена конвертация данных для нового механизма. Поэтому отсюда следует, что в системе не может существовать одновременно поддержка реализации старого и нового BADI для одного и того же объекта, как например MB_MIGO_BADI.

Создание точки расширения выполняется в транзакции SE19 – BAdI-Builder – внедрения, рисунок 1: badi-01.png. Транзакция работает или в режиме создания или в режиме изменения расширения. Если честно не очень распространенный вариант первого экрана транзакции. Для создания точки расширения требуется ввести имя существующей в системе точки расширения, в данном случае это MB_MIGO_BADI. Выбираем режим создания расширения.

Появится диалоговый экран с запросом создаваемой точки расширения, который будет реализовывать наше расширение. Так как основное имя MB_MIGO_BADI, то имя создаваемой точки пусть будет ZZ_MB_MIGO_BADI, имя может быть любое подходящее под соглашения по наименованию пользовательских объектов, рисунок 2: badi-02.png.

Для группировки нескольких точек расширений, которые реализуют один бизнес-процесс, можно создать групповое имя, которое будет объединять создаваемые расширения, для упрощения управления всеми реализациями. Если это просто локальная реализация, то можно не создавать групповое имя. После подтверждения создания появляется запрос на ввод имени реализации, указания класса реализации и выбора определения BADI, рисунок 3: badi-03.png, это все организовано таким образом, так как точка расширения может включать в себя несколько различных классов, фактически составляющих реализацию точки расширения. В данном случае точка расширения совпадает по имени с классом реализации, при этом класс реализации для точки только один.

После заполнения всех полей подтверждаем ввод. Система предложит нам вариант создания реализации (фактически наследуемого класса). Так как в данном случае у нас есть пример реализации и мы создаем расширение данного типа в первый раз, то лучшим выходом будет создание реализации на основе копирования примера класса. В противном случае, если вы знаете, как создавать расширение, то выбираете создание пустого класса, так как при создании копирования примера класса, создаваемый новый класс будет реализовывать все методы класса шаблона, хотя вам вполне может и не требуется реализация всех методов, рисунок 4: bapi-04.png.

Таким образом, мы получаем реализующий расширение класс с перечнем все доступных методов, которые уже изначально содержат пример правильного кода, который позволит вам написать свою реализацию по аналогии с примером. Сохраняем созданный класс и теперь можно перейти к просмотру/редактированию созданного класса расширения, рисунок 5: badi-05.png. После сохранения расширения его нужно активировать. Если расширение активно, то результат его работы можно увидеть в транзакции MIGO.

аботать с точкой расширения можно или используя и дальше транзакцию SE19 или же можно работать уже с реализующим классом используя транзакцию SE24 / SE80.

   Сейчас транзакция MIGO не содержит закладки пользователя, рисунок 6: bapi-07.png, так как класс еще не активирован, но если выполнить активацию, то при просмотре документов будет доступна закладка с полями пользователя.

Так как, класс был создан на основе шаблона, то количество объектов подлежащих активации будет большим. Вы должны просто выделить все позиции и провести активацию всех объектов, рисунок 7: badi-06.png.

После активации данных в транзакции MIGO появятся закладки полей пользователя, рисунок 8: badi-08.png.

Если закладка не появилась или же при выполнении стандартной транзакции, которую вы расширили вы не попадаете в текст реализации расширения, то убедитесь что внедрение активировано и оно вызывается. Для этого в транзакции SE19 перейдите на закладку расширенные элементы внедрения и просмотрите статус расширения. Курсор должен стоять  на имени BADI-внедрения, а не на реализующем классе, рисунок 9: badi-09.png.
Название: 7 - BADI - Технология внедрения бизнес расширений / дополнений
Отправлено: Uukrul от Сентябрь 01, 2016, 01:15:50 pm
Как это все работает?

Система видит, что существует реализация расширения и создан наследник класса, поэтому в момент запуска транзакции она создает класс – отвечающий за реализацию. В вашем классе вам доступен метод INIT, который вызывается в конструкторе. Для каждого BADI-расширения возможны различные правила создания реализующего класса, именно по этому, общая рекомендация при создании BADI, в первый раз, выполнять создание путем копирования из образца, если конечно это возможно. В методе INIT система должна вернуть имя вашей реализации. В данном случае имя реализации предлагается задать константой в рамках вашего класса и далее выполнить присвоение этой константы в возвращаемый параметры метода, рисунок 10: badi-10.png.

И собственно реализация метода инициализации класса, рисунок 11: badi-11.png. В принципе это демонстрация правильного стиля программирования, когда имя класса реализации, по которому система будет в дальнейшем проводить идентификацию вашего класса, определять через внутреннюю константу в рамках класса.

Далее при каждом добавлении строки в документ движения материала в транзакции MIGO, система будет вызвать метод LINE_MODIFY – Add / Change a Line (GOITEM), т.е. при каждом добавлении или изменении строки документа вы будете получать уведомление. Как образом предлагается реализовать данный метод? Так как фактически вы создали класс, то все необходимы для работы поля из позиции документа, предлагается хранить во внутренней приватной таблице класса. Структура, описывающая таблицу создается в словаре данных и затем вы оформляете внутреннюю переменную в рамках класса, рисунок 12: badi-12.png.

В коде реализации, вы просто читаете необходимую строку позиции документа из этой таблицы по ключу I_LINE_ID, который обязательно должен быть включен в вашу внутреннюю структуру, так как это уникальный номер строки позиции документа. Если строка найдена, тогда вы модифицируете ее в своей таблице, так как пришли новые изменения, если же строки нет, тогда вы добавляете эту строку в свою внутреннюю таблицу. Таким образом, в рамках реализующего расширение класса вы всегда имеете таблицу строк документа. Именно поэтому в метод CHECK_ITEM – Check Item Data of Goods Movement система передает вам только уникальный номер строки, которую следует проверить, так как саму строку вы должны извлечь из внутренней таблицы строк своего класса реализации. Само собой в этой таблице нужно сохранить не только поля, которые вы добавили на собственный экран, но и поля которые вам будут нужны для проверки введенных данных, рисунок 13: badi-13.png, поэтому вам не нужно выполнять какие-то телодвижения по сохранению позиций документа через память системы. Вы уже имеете класс, который может хранить все необходимые данные, вплоть до копии всех полей позиции документа, если это вам необходимо.

Примечание: Обратите внимание, что при реализации метода LINE_MODIFY, вы должны учесть так же и ситуацию, когда документ уже сохранен в базу данных системы, поэтому если дополнительные значения полей находятся в собственных таблицах, то при режиме просмотра, нужно прочитать эти данные из БД для их вывода на экран. Это выполняется в стандартном примере системы. Режим работы транзакции вы можете узнать в методе MODE_SET и уже по стандартной технологии в данном методе нужно сохранить полученный режим во внутреннюю объявленную переменную вашего класса.

При реализации Customerexit, экраны которые должны были реализовывать собственно вывод и управление пользовательскими полями, объявлялись внутри компонента расширения и мы сразу видели номера экранов, которые необходимо было создавать в дизайнере экранов. В данном случае класс реализации не может содержать экраны, поэтому для создания собственных экранов необходимо выполнить их создание в отдельном программном блоке. В данном случае рекомендуется создать отдельную группу функций, которая будет работать с экранами, а в методе вызова экрана PBO_DETAIL – PBO of Detail Screen, мы должны вернуть имя программы и номер экрана который нужно вывести. В данном случае имя главной программы для группы функций MIGO_BADI_EXAMPLE, будет SAPLMIGO_BADI_EXAMPLE, рисунок 14: badi-14.png. Так же в данном методе производиться передача значений для объявленных пользовательских полей во внутрь группы функций реализующих экраны, а внутри класса реализации расширения, мы запоминаем уникальный номер строки, значения для которой мы выводим на закладку.

Примечание: В данном примере, интересная методика реализации экранов для разных режимов работы транзакции: Создание/Просмотр/Изменения. Вместо того, чтобы передавать в группу функций код режима, который мы могли бы хранить во внутренней переменной класса, предлагается запомнить упрощенную схему разрешено или нет изменение значений, переменная G_NO_INPUT. И далее в зависимости от ее значения будет выводиться экран 0001 – разрешено редактировать значений или 0002 – Запрещено редактировать значений. Хотя это же самое можно реализовать и внутренней логиков обработки полей экрана.

Сохранение значений необходимо выполнить в методе POST_DOCUMENT – Post Goods Movement. Входящие параметры метода содержат позиции сохраняемого документа и его заголовок, а далее вы должны побеспокоиться о сохранении пользовательских значений в собственных таблицах. Ограничением будет являться то, что для реализации сохранения необходимо создать модуль обновления, который и будет фактически выполнять процесс записи в БД. Вызывать операцию подтверждения сохранения COMMIT WORK, категорически запрещено, как и операцию отмены изменений.

Примечание: Реализация метода сохранения, нужна только в том случае, если вы выполняете сохранение полей в собственные таблицы данных. Если же вы сделали расширение стандартной таблицы, например, расширили таблицу MKPF или MSEG, тогда система сама выполнит сохранение данных. Все что вы должны в этом случае сделать, это заполнить поля структуры CS_GOITEM в методе LINE_MODIFY. Перед этим, конечно же, нужно расширить эту структуру своим дополнением.

На этом создание внедрения можно было бы завершить, если бы не одна тонкость, поля пользователя будут сохраняться только в случае, если вы будете использовать транзакцию MIGO, однако если в собственных программах вы используете для формирования документа движения материала функцию: BAPI_GOODSMVT_CREATE – Провести движение материала, то с удивлением можете обнаружить, что переданные вами в расширении EXTENSIONIN[] пользовательские поля не будут сохранены. Все дело в том, что это как раз тот случай, когда нужно реализовывать обработку двух точек расширения, одну при работе с документом материала в диалоге, а другую MB_GOODSMOVEMENT, которая включает в себя ряд вспомогательных классов, одни из которых MB_BAPI_GOODSMVT_CREATE – Расширение BAPI_GOODSMVT_CREATE, для формирования документа через BAPI функции.  Кстати, если эти точки объединить одним групповым именем, то можно будет при внесении изменений определить, что обработать нужно как минимум и второе внедрение, кстати это пример точки расширения с реализацией нескольких классов внутри одной точки, рисунок 15: bapi-15.png.
Название: 7 - BADI - Технология внедрения бизнес расширений / дополнений
Отправлено: Uukrul от Сентябрь 01, 2016, 01:25:43 pm
Техника поиска BADI

Для просмотра существующих BADI, в системе существует транзакция просмотра SE18, которая позволят просмотреть параметры и методы существующих классов, реализующих BADI. Данная транзакция разделяет BADI, на два вида, собственно новые BADI, называемые в данный момент «Точка расширения» и стандартные старые BADI.  Если вы попытаетесь найти BADI, который в данный момент уже мигрировал в новую реализацию, например приведенный выше MB_MIGO_BADI, то при просмотре такого  BADI, будет видно, что нужно использовать новую технику точек расширения, рисунок 16: badi-16.png т.е. значит, что идем в просмотр точки расширения с таким же именем.

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

Если же реализация осталась старой, т.е. это классический BADI, то при просмотре мы увидим классы, включенные в BADI, рисунок 17: badi-17.png.

Однако, искать BADI, таким образом, занятие не очень благодарное, да и результат может быть довольно не предсказуемым, т.е. скорее всего, будет найдено не то, особенно в том случае если имя точки расширения и описание, скажем так, слегка далеки от ее предназначения. Поэтому есть более простой метод поиска всех BADI, которые вызываются в ходе выполнения любой транзакции. Как мы помним, фактически BADI, это класс который создается в момент работы транзакции, таким образом, в системе существует программа, которая для любого выполняемого кода, если находит точку вызова BADI, должна проверить существование реализации данного BADI и если, такая реализация существует, то необходимо создать инстанцию реализующую класс.

В системе действительно есть реализация класса, который вызывается всегда, когда система пытается проверить существование реализации BADI. Это класс:  CL_EXITHANDLER и его метод GET_INSTANCE. В транзакции SE24 переходим к просмотру кода реализации и на строке:

call method cl_exithandler=>get_class_name_by_interface

Устанавливаем точку останова, рисунок 18: badi-18.png.

Теперь идем в интересующую нас транзакцию и в те моменты, когда будет проверяться реализация/существование BADI, система будет попадать в данный класс и метод. В переменной exit_name, система будет возвращать имя BADI. Соответственно, выполнив транзакцию, вы получите все BADI, который вызываются при ее работе, рисунок 19: badi-19.png.

Другой метод поиска BADI так же связан с отладчиком и появлением новым команд в языке, а именно команды GET BADI и CALL BADI. Поэтому можно поставить точку останова на вызов команды CALL BADI и получить в отладчике перечень реализуемых в транзакции методов расширений, рисунок 20: badi-20.png или же поставить точку останова на команду GET BADI для получения списка расширений.


Применимость – Некоторые особенности использования BADI.

Компания SAP ограничивает возможности использования BADI, почему и зачем это делается? Иногда если честно лично мне не очень ясно. Например, есть такая BAPI-функция BAPI_INCOMINGINVOICE_CREATE1 – создание счета логистики, в данной функции есть структура для передачи полей, определенных пользователе. Стандартно данный механизм использует технику передачи параметров через структуры:

   В данной BAPI-функции такие структуры так же определены. Далее стандартно в системе реализуется BADI-расширение, которое должно позволить корректно передать пользовательские поля в таблицы документа. Однако, не все так просто. Для данной BAPI, класс, реализующий возможности такой передачи, по странной причине объявлен внутренним. Таким образом, использование данной BADI, невозможно.

В данный момент определены следующие параметры:

Общий вывод из всего сказанного, очень неплохо, если в BADI стоит или не стоит флажок «Многоразовое», и совсем плохо, если стоят какие-либо следующие флажки. Это значит, что для вас как для клиента компании SAP, данное BADI закрыто для использования.