Меню

Расширения системы (Enhancement Framework). Часть 1

|

Продолжение. Предыдущая статья из цикла статей Техники расширений стандартной системы SAP «BADI – Технология внедрения бизнес расширений / дополнений».

Enhancements spot/section – Техника расширения, позволяющая практически выполнить внедрение пользовательского кода в любом месте стандартной бизнес-транзакции.

Уважаемые коллеги!

Я продолжаю публикацию на портале SAPLand цикла статей «Техники расширений стандартной системы SAP».

Все статьи цикла приведены внизу публикации.

Предыдущая статья из цикла статей Техники расширений стандартной системы SAP  «BADI – Технология внедрения бизнес расширений / дополнений».

Примечание: Начиная с версии ECC 6.0 появился замечательный механизм расширения функциональности системы (Enhancement spot/section), однако, это и самый простой способ «поломать систему об колено», так как никакого контроля за тем, что и как вы реализуете в точке расширения, система не осуществляет.

Общая концепция.

Enhancements spot/section – Техника расширения, позволяющая практически выполнить внедрение пользовательского кода в любом месте стандартной бизнес-транзакции. Enhancement-ы в системе разделяются на «явные» и «не явные». Явные точки расширения аналогично технике Customer-exits используют специально введенный оператор, неявные же фактически присутствуют в начале и конце логически завершенных блоков кода или структур данных. Техника Enhancements spot/section позволяет внедрять пользовательский код в контекст выполнения стандартного кода системы. Вы получаете доступ ко всем переменным и можете фактически полностью переопределить логику работы системы, что является очень небезопасным, так как может привести к серьезным сбоям в работе стандартных транзакций, в случае некорректной реализации кода расширения. На данный момент это, с одной стороны- очень мощный механизм расширения функциональности, а с другой стороны- самый небезопасный из всех типов расширений. Общая рекомендация от компании SAP: если существует возможность использования любой другой тип расширения – используйте его, технику Enhancements – используйте только в том случае, если других возможностей влияния на выполняемый код нет.

Немного теории, сформулированной после прочтения статьи «Новая концепция расширений как метод совершенствования программ SAP без их модификации», автор Майкл Акер (Michael Acker), SAP Professional Journal 2008 год. Остановлюсь только на ключевых моментах. Тем, кого заинтересуют подробности, рекомендую прочтение оригинала статьи или посещения сайта http://help.sap.com.

Enhancement Spot, фигурально говоря, – новый механизм экзитов в системе, фактически, система предоставляет новую возможность расширения стандартной функциональности без получения ключей модификации на объекты системы. Так же гарантируется, что ни при каких обновлениях системы, сделанные вами расширения, не будут затерты, хотя при этом и не гарантируется, что они останутся работоспособными. На данный момент, концепция расширений поддерживает следующие расширения объектов системы:

  • Расширение классов — возможно добавление новых необязательных параметров в методы, добавление методов pre и post к существующему методу, которые вызываются перед вызовом самого метода, ну и доступно общее переопределение метода с использованием инструкции overwrite.
  • Расширение функций — возможность добавления новых необязательных параметров к функции. При этом, конечно же, нужно будет использовать функциональность «расширение кода», так как добавленные вами параметры нужно же кому-то и как-то обрабатывать.
  • Расширение исходного кода — как возможность вставки своей логики в определенные позиции исходного кода, так и замещение части кода своей логикой работы. Собственно говоря, этот механизм кажется мне наиболее часто используемым на практике, поэтому примеры в статье построены для демонстрации работы именно этого расширения.
  • Расширение web-экранов — позволяет добавлять новые элементы на экраны пользовательского интерфейса. С этим расширением я пока не сталкивался, но если  кому-то это  интересно и нужно, то идем на www.help.sap.com, так как имеются в виду web-экраны базирующиеся на парадигме контроллера ракурсов модели (Model View Controller, MVC).
  • Новые BADI — технология BADI внедрений была реализована, начиная с версии 4.6, однако, с появлением технологии расширений, принцип технической реализации BADI стал другой, т. е. с виду использование осталось фактически таким же, но разработчики SAP говорят, что новые BADI стали работать быстрее. Технология BADI стала работать, используя, так называемые, точки расширения основанные на технике Enhancement Spot.

Благодаря появлению техники Enhancement, в системе стала доступна техника бизнес-расширений функциональности, т.е. когда вы заходите в транзакцию SFW5, Рисунок 1: SFW5-01.png и активируете необходимую вам бизнес-функцию, знайте, что фактически в вашей системе происходит активация иногда сотен точек расширений, которые реализуют функциональность данной бизнес-фукнции.

Рис. 1: SFW5-01.png

Раширения классов

            Любой стандартный класс системы можно дополнить новыми интерфейсами, атрибутами, методами или событиями. При этом, например, методы, можно расширить дополнительными точками входа перед вызовом метода, после вызова или путем полного переопределения метода. Для расширения класса требуется зайти в транзакцию SE24 – Построитель классов, выбрать кнопку «Просмотр», так как если вы выберете «Изменить», то система попросить ключ модификации для объекта. Переходите к просмотру данных класса и по меню выбираете: «Класс» – «Расширить», Рисунок 2: CL-01.png.

            Если вы в первые, расширяете класс, то у вас будет запрошено имя расширения и, если в вы планируете расширить несколько объектов, для реализации одного бизнес-процесса, то желательно их объединить в группу/контейнер расширений,

Рис. 2: CL-01.png

Имя расширения, как и имя контейнера, создается согласно стандартным пользовательским соглашениям, т.е. Z или Y, Рисунок 3: CL-02.png.  Если для класса уже существует расширение, тогда вам будет выдан перечень существующих расширений, и вы сможете или выбрать изменение уже существующего или создать новое расширение, однако, если это расширение компании SAP, то конечно же, вы не сможете его использовать для включения своих изменений.

Рис. 3: CL-02.png

После подтверждения создания расширения вы получите возможность дополнять класс новыми атрибутами или методами, как видим на Рисунок 4: CL-03.png; в таблицах, открылись поля для ввода необходимой информации по расширению класса. Вы должны понимать, что если вы добавляете новый атрибут или метод в класс, то вы должны обеспечить его реализацию. Ограничения по именам никакого нет, но я рекомендую не выходить за стандартное соглашение по именам, так как, возможно, разработчик тоже захочет расширить свой класс, и при очередном обновлении вы столкнетесь с проблемами совпадения имен. Поэтому я бы называл, свои добавленные атрибуты или методы начиная с префикса «ZZ_». Так сказать. для надежности, Рисунок 5: CL-04.png. Как видим. после добавления нового метода система видит, что в данном случае, метод находится в расширении ZI_CL_SALV_TABLE.

Рис. 4: CL-03.png

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

Рис. 5: CL-04.png

Почему этого не сделал автор класса, я не знаю, наверное, предполагал, что в заголовок будет выводиться одна или две строки, поэтому вполне хватает динамического расчета высоты заголовка. Попробуем немного переопределить поведение класса используя свой метод DISPLAY, но с передачей параметра, определяющего высоту заголовка таблицы. Под высотой заголовка имеется в виду область, обозначенная на Рисунок 6: CL-05.png. Сейчас в ней выведено 4 строки, и по высоте это составляет порядка 87 пикселов (значение взято в ходе отладки из подпрограммы, которая определяет автоматически ширину заголовка).

Рис. 6: CL-05.png

В рамках своего метода, вы имеете полный доступ ко всем переменным класса, поэтому можете выполнять такие операции, как переопределение внутренних атрибутов класса или вызов защищенных методов. Например, по умолчанию в новом методе отображения класса я просто скопировал код из стандартного метода:

METHOD zz_display .
  CHECK me->r_controller IS BOUND.

  me->r_controller->display( ).

ENDMETHOD.

Как видим, я обращаюсь к переменной, которая является внутренней в классе, Рисунок 7: CL-06.png, и система не выдает никакой ошибки.

Рис. 7: CL-06.png

К сожалению, в данном случае выполнить красивую реализацию управления высотой заголовка не представилось возможным. Автор класса, как оказалось, использовал внутри стандартный вызов ФМ: REUSE_ALV_GRID_DISPLAY, причем при вызове, автор класса просто закомментировал параметры, отвечающие за управление высотой заголовка, так же, как и закомментировал событие, вызываемое перед отрисовкой заголовка, поэтому корректно исправить данный класс оказалось невозможно. Поэтому расширить класс мы можем, но вот повлиять на поведение – нет; точнее - можем, но количество добавленных модификаций, по моему мнению, будет слишком большим, и это, как раз, тот случай, когда проще использовать или прямой вызов функционального модуля REUSE_ALV_GRID_DISPLAY или использовать другой класс вывода ALV-таблицы.

Расширение «перед» или «после» вызова стандартного метода.

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

Итак, расширяем класс путем добавления кода, выполняющегося перед вызовом стандартного метода класса. Для этого становимся в поле интересующего нас метода и по меню выбираем: «Класс» – «Расширить», а так как у нас уже есть одно расширение класса, то система попросит нас выбрать имя расширения, в рамках которого будем проводить модификацию, Рисунок 8: CL-07.png.

Рис. 8: CL-07.png

Далее устанавливаем курсор на имя метода DISPLAY и по меню выбираем: «Обработать» – «Операции расширения» – «Добавить предварит. метод», Рисунок 9: CL-08.png.

Рис. 9: CL-08.png

Если проблем с добавлением не будет, то в колонке «PreExit», будет добавлена кнопка перехода к коду метода, Рисунок 10: CL-09.png

Рис. 10: CL-09.png

Так как реализации мы еще не создавали, то в последней колонке «Внедрение расширений» пока пусто, но как только мы перейдем к введению кода, в колонке будет подставлено имя нашего внедренного расширения. При первом входе в ведение предварительного метода, система сгенерирует новый класс, реализующий данный метод. Код класса будет следующим:

CLASS LCL_ZI_CL_SALV_TABLE DEFINITION.
PUBLIC SECTION.
CLASS-DATA OBJ TYPE REF TO LCL_ZI_CL_SALV_TABLE.
DATA CORE_OBJECT TYPE REF TO CL_SALV_TABLE .
 INTERFACES IPR_ZI_CL_SALV_TABLE.
  METHODS:
   CONSTRUCTOR IMPORTING CORE_OBJECT
     TYPE REF TO CL_SALV_TABLE OPTIONAL.
ENDCLASS.
CLASS LCL_ZI_CL_SALV_TABLE IMPLEMENTATION.
METHOD CONSTRUCTOR.
  ME->CORE_OBJECT = CORE_OBJECT.
ENDMETHOD.

METHOD IPR_ZI_CL_SALV_TABLE~DISPLAY.
*"------------------------------------------------------------------------*
*" Declaration of PRE-method, do not insert any comments here please!
*"
*"methods DISPLAY .
*"------------------------------------------------------------------------*

*
* Реализация PRE-метода

*
ENDMETHOD.
ENDCLASS.

Как видим, создается класс, в котором объявляется внутренняя переменная CORE_OBJECT. Эта переменная получает ссылку на основную реализацию класса, к которой выполнится вызов.

Особенности реализации классов для Pre, Post или Overwrite методов.

Параметры данных методов всегда равны оригинальным параметрам замещаемых методов, которые вы переопределяете, соблюдая требования:

  • Pre-метод – Не должен иметь выходных параметров
  • Post-метод – Не должен иметь входных параметров, при этом выходные параметры (EXPORT) оригинального метода становятся изменяемыми параметрами (CHANGING) в Post-методе. Возвращаемые параметры (RETURNING) становятся так же изменяемыми параметрами (CHANGING).
  • Overwrite – Реализация параметров не изменяется.

Добавить или изменить набор передаваемых параметров нельзя, а то, что вы получаете в своем локальном классе ссылку на оригинальный объект, в этом как раз и кроется основанная проблема такого метода расширения класса. Она заключается в том, что вы имеете доступ только к задекларированным методам и атрибутам класса. Никакие атрибуты или методы, отмеченные как внутренние, вам в этой реализации расширения не доступны, поэтому область использования данного типа расширения значительно сужается. Особенно мне не очень понятна схема переопределения OverwriteExit. Так как обычно внутри метода вызывается код работающий с внутренними переменными, доступа к которым мы внутри своей реализации класса не имеем. Что мы там можем делать в этой ситуации, мне сложно сказать. Я склоняюсь к мнению, что эти расширения больше нужны самим разработчикам SAP; когда нужно полностью изменить реализацию метода, то они пишут новый метод, например DISPLAY_2. Далее, чтобы не поломать весь код, который использует старый вызов, плюс это может быть код партнеров, они просто реализуют замещение метода DISPLAY, внутри которого вызывают новый метод DISPLAY_2; таким образом, не ломая использование данного класса нигде по системе, и тем самым обеспечивают беспроблемное обновление программ.

Расширение функций.

Аналогично способу расширения классов система позволяет добавлять собственные параметры к функциональным модулям системы. После добавления новых параметров в систему нужно обязательно добавить также и реализацию их заполнения значениями. Для этого нужно использовать определённую методику модификации исходного кода модулей, о чём будет рассказано ниже. Для расширения функциональных модулей переходите к просмотру необходимого модуля, транзакция SE37, Рисунок 11: FM-01.png, и по меню выбираете: «Функциональный модуль» – «Расширить интерфейс».

Рис. 11: FM-01.png

После выбора команды расширения вам будут доступны поля ввода собственных параметров в интерфейс функционального модуля. Соглашение по именам параметров рекомендую такое же, как и при расширении классов, т.е. начинаем наименование полей с префикса ZZ. Все добавляемые параметры буду иметь атрибут – «не обязательный», чтобы не нарушить вызовы данного модуля в уже существующих текстах программ.

Расширения исходного кода.

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

История одной проблемы: Лет пять назад попросили в одной системе поискать причину, почему для определенной группы материалов в продуктивной системе при прогоне ППМ не создавались плановые заявки на закупку. До меня там с этой проблемой бодались местные специалисты, потом неделю искали специалисты из SAP AG, собственно один из них и подключил меня. То, что нужно было срочно, стало понятно буквально через час после того, когда мне дали параметры

Если хотите прочитать статью полностью и оставить свои комментарии присоединяйтесь к sapland

У вас уже есть учетная запись?

Войти

Обсуждения Количество комментариев4

Комментарий от  

Юрий Слободчиков

  |  01 декабря 2016, 04:55

> так как последняя переменная в структуре содержит объявление, заканчивающееся запятой, а объявление расширения вклинивается в эту структуру, то мы получаем проблему, которую в принципе решить невозможно
 
На самом деле можно поставить DATA: или TYPE: перед добавлением нового поля и всё должно работать.
 
Например:
data:    begin of bvttab occurs 20,
           bvtyp(50) type c,
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""$"$\SE:(1) Структ. BVTTAB, Выход                                                                                                                             S
*$*$-Start: (1)---------------------------------------------------------------------------------$*$*
ENHANCEMENT 1  ZZZZZ.    "active version
DATA:           zz_bvtyp_ext(10) TYPE c.
ENDENHANCEMENT.
*$*$-End:   (1)---------------------------------------------------------------------------------$*$*
         end of bvttab.

Комментарий от  

Александр Неловкин

  |  01 декабря 2016, 10:46

Олег, по поводу: "...Так как обычно внутри метода вызывается код работающий с внутренними переменными, доступа к которым мы внутри своей реализации класса не имеем..." (это касаемо Pre, Post или Overwrite)
 
Мы имеем доступ ко всем атрибутам и методам класса (и приватным в том числе). Обращаться к ним можно CORE_OBJECT-><имя атрибута/метода>

Комментарий от  

Олег Точенюк

  |  01 декабря 2016, 13:18

> так как последняя переменная в структуре содержит объявление, заканчивающееся запятой, а объявление расширения вклинивается в эту структуру, то мы получаем проблему, которую в принципе решить невозможно
 
На самом деле можно поставить DATA: или TYPE: перед добавлением нового поля и всё должно работать.
 
Например:
data:    begin of bvttab occurs 20,
           bvtyp(50) type c,
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""$"$\SE:(1) Структ. BVTTAB, Выход                                                                                                                             S
*$*$-Start: (1)---------------------------------------------------------------------------------$*$*
ENHANCEMENT 1  ZZZZZ.    "active version
DATA:           zz_bvtyp_ext(10) TYPE c.
ENDENHANCEMENT.
*$*$-End:   (1)---------------------------------------------------------------------------------$*$*
         end of bvttab.

Хороший вариант, спасибо.

Комментарий от  

Олег Точенюк

  |  01 декабря 2016, 13:20

Олег, по поводу: "...Так как обычно внутри метода вызывается код работающий с внутренними переменными, доступа к которым мы внутри своей реализации класса не имеем..." (это касаемо Pre, Post или Overwrite)
 
Мы имеем доступ ко всем атрибутам и методам класса (и приватным в том числе). Обращаться к ним можно CORE_OBJECT-><имя атрибута/метода>

Не нравится мне такой доступ, это как по мне аналогично доступам через ASSIGN, можно, но потом проблем можно отгрести :-(