software, software.sap

“NOT EXISTS” Subqueries in ABAP CDS Views

ABAP CDS Views are great and all; however, it doesn’t support subqueries at this time. In this post, I would like to share a workaround which functions similar to “NOT EXISTS”.
The requirement was to exclude material document items (MSEG) which were reverse posted; and the reversal documents themselves.
If I was writing a regular ABAP SQL statement, I could easily achieve this with the help of a NOT EXISTS subquery. Check the code snippet below – I have excluded unrelated WHERE conditions for simplicity.
SELECT mblnr, mjahr, zeile 
  FROM mseg AS mseg1 
  WHERE 
    ( sjahr EQ '0000' OR sjahr IS NULL )
    AND
    ( NOT EXISTS (
      SELECT mandt FROM mseg AS mseg2 WHERE
        sjahr EQ mseg1.mjahr AND
        smbln EQ mseg1.mblnr AND
        smblp EQ mseg1.zeile
    ) )
  INTO TABLE @DATA(lt_mseg).
This logic doesn’t work in CDS views directly due to lack of subquery support. To get the same result using CDS views, I have split the query into two views. The first one returns a wide list where original and reversal MSEG records are listed side by side.
@AbapCatalog.sqlViewName: 'ZMMV_001'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Original and reversal docs side by side'
define view ZMMV_MAT_DOC_ITEM_REV_STATUS as
  select from
    mseg as mseg1
    left outer join mseg as mseg2 on
      mseg2.sjahr = mseg1.mjahr and
      mseg2.smbln = mseg1.mblnr and
      mseg2.smblp = mseg1.zeile
  {
    mseg1.mblnr as mblnr1,
    mseg1.mjahr as mjahr1,
    mseg1.zeile as zeile1,
    mseg1.sjahr as sjahr1,
    mseg1.smbln as smbln1,
    mseg1.smblp as smblp1,
    mseg2.mblnr as mblnr2,
    mseg2.mjahr as mjahr2,
    mseg2.zeile as zeile2,
    mseg2.sjahr as sjahr2,
    mseg2.smbln as smbln2,
    mseg2.smblp as smblp2
  }
The second view filters out the reversed & reversal documents from the first view, which leaves effective material documents only.
@AbapCatalog.sqlViewName: 'ZMMV_002'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Effective material document items'
define view ZMMV_EFFECTIVE_MAT_DOC_ITEM as
  select distinct from ZMMV_MAT_DOC_ITEM_REV_STATUS
  {
    key mblnr1 as mblnr,
    key mjahr1 as mjahr,
    key zeile1 as zeile
  }
  where
    sjahr1 = '0000' and
    (
      mjahr2 = '0000' or
      mjahr2 is null
    ) and
    (
      sjahr2 = '0000' or
      sjahr2 is null
    )
This approach can be implemented to replace NOT EXISTS in many other cases as well.
Standard
software, software.sap

A General Purpose ABAP Multiton Class

In this post, I will share a general purpose class covering the multiton design pattern. By implementing a simple interface, you can add multiton functionality to your existing classes.

Multiton is a performance oriented design pattern. It is based on the idea of caching and re-using objects corresponding to the same key. For each object key (such as a vendor number), a static object instance of a class (such as a vendor class) is kept in a central location. Whenever a client requests an object corresponding the key, the existing object is returned instead of creating a new one. This approach reduces the memory footprint due to the decreased number of objects, and avoids the performance cost to re-create objects having the same key.

A typical multiton class would have the following skeleton structure.

CLASS zcl_vendor DEFINITION
  PUBLIC FINAL CREATE PRIVATE.

  PUBLIC SECTION.

    DATA gv_lifnr TYPE lifnr READ-ONLY

    CLASS-METHODS get_instance
      IMPORTING !iv_lifnr TYPE lifnr
      RETURNUNG VALUE(ro_obj) TYPE REF TO zcl_vendor.

  PRIVATE SECTION.

   TYPES:
      BEGIN OF t_multiton,
        lifnr TYPE lifnr,
        obj TYPE REF TO zcl_vendor,
      END OF t_multiton,

      tt_multiton TYPE HASHED TABLE OF t_multiton
        WITH UNIQUE KEY primary_key COMPONENTS lifnr.

    CLASS-DATA gt_multiton TYPE tt_multiton.

    METHODS constructor
      IMPORTING
        !iv_lifnr TYPE lifnr.

  PROTECTED SECTION.

ENDCLASS.

 
CLASS zcl_vendor IMPLEMENTATION.

  METHOD constructor.
    “ Check if IV_LIFNR exists in LFA1 and raise error if not
    gv_lifnr = iv_lifnr.
  ENDMETHOD.

  METHOD get_instance.

    ASSIGN gt_multiton[ KEY primary_key
      COMPONENTS lifnr = iv_lifnr ]
      TO FIELD-SYMBOL(<ls_multiton>).

    IF sy-subrc NE 0.

      “ Check if IV_LIFNR exists in LFA1 and raise error if not
      DATA(ls_multiton) = VALUE t_multiton( lifnr = iv_lifnr ).
      ls_multiton-obj = NEW #( iv_lifnr ).
      INSERT ls_multiton 
        INTO TABLE gt_multiton
        ASSIGNING <ls_multiton>.

    ENDIF.

    ro_obj = <ls_multiton>-obj.

  ENDMETHOD.
 

ENDCLASS.

When ZCL_VENDOR=>GET_INSTANCE( ‘12345’ ) is called for the first time, a new instance of ZCL_VENOR is created and stored in GT_MULTITON; and that very instance is returned.

When ZCL_VENDOR=>GET_INSTANCE( ‘12345’ ) is called again, the existing instance of ZCL_VENDOR in GT_MULTITON is returned instead of a new instance. That saves memory and runtime.

For more information on multiton, you can refer to my book Design Patterns in ABAP Objects.

Now, what is the value-add of this post?

Instead of creating a specialized multiton implementation into every required class, I have created a general purpose multiton class which does all the hard work of caching objects. All you have to do is to implement an interface into your existing class to add multiton functionality.

Let’s assume that our vanilla class looks like this.

CLASS zcl_bc_multiton_demo DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.

    data:
      gv_id    type char15,
      gv_erdat type erdat,
      gv_ernam type ernam.

    methods:
      constructor importing iv_id type char15.

  PROTECTED SECTION.
  PRIVATE SECTION.
ENDCLASS.

CLASS zcl_bc_multiton_demo IMPLEMENTATION.

  method constructor.
    gv_id = iv_id.
    gv_erdat = sy-datum.
    gv_Ernam = sy-uname.
  endmethod.

ENDCLASS.

Pretty simple, huh? This is the class we presumably need multiton functionality on.

This is the interface we need to implement.

interface ZIF_BC_MULTITON
  public .

  class-methods:
      get_instance
        importing
          !iv_objectid type CDOBJECTV
        returning
          value(ro_obj) type ref to ZIF_BC_MULTITON
        raising
          CX_SY_CREATE_OBJECT_ERROR.

endinterface.

After implementing the interface, our vanilla class looks like this.

CLASS zcl_bc_multiton_demo DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.

    interfaces ZIF_BC_MULTITON.

    data:
      gv_id    type char15,
      gv_erdat type erdat,
      gv_ernam type ernam.

    methods:
      constructor importing iv_id type char15.

  PROTECTED SECTION.
  PRIVATE SECTION.
ENDCLASS.



CLASS zcl_bc_multiton_demo IMPLEMENTATION.

  method constructor.
    gv_id = iv_id.
    gv_erdat = sy-datum.
    gv_Ernam = sy-uname.
  endmethod.

  METHOD zif_bc_multiton~get_instance.
    ro_obj ?= new zcl_Bc_multiton_demo( conv #( iv_objectid ) ).
  ENDMETHOD.

ENDCLASS.

And here is the general purpose class that does the caching.

CLASS zcl_bc_multiton DEFINITION
  PUBLIC
  FINAL
  CREATE public .

  PUBLIC SECTION.

    class-METHODS:
      get_obj
        IMPORTING
          !iv_clsname   TYPE seoclsname
          !iv_objectid  TYPE cdobjectv
        RETURNING
          VALUE(ro_obj) TYPE REF TO zif_bc_multiton
        RAISING
          cx_sy_create_object_error.

  PROTECTED SECTION.
  PRIVATE SECTION.

    TYPES:
      BEGIN OF t_multiton,
        clsname  TYPE seoclsname,
        objectid TYPE cdobjectv,
        cx       TYPE REF TO cx_sy_create_object_error,
        obj      TYPE REF TO zif_bc_multiton,
      END OF t_multiton,

      tt_multiton
        TYPE HASHED TABLE OF t_multiton
        WITH UNIQUE KEY primary_key COMPONENTS clsname objectid.

    class-DATA:
      gt_multiton TYPE tt_multiton.

ENDCLASS.



CLASS zcl_bc_multiton IMPLEMENTATION.

  METHOD get_obj.

    ASSIGN gt_multiton[
        KEY primary_key COMPONENTS
        clsname  = iv_clsname
        objectid = iv_objectid
      ] TO FIELD-SYMBOL(<ls_mt>).

    IF sy-subrc NE 0.

      DATA(ls_mt) = VALUE t_multiton(
        clsname  = iv_clsname
        objectid = iv_objectid
      ).

      TRY.

          CALL METHOD (ls_mt-clsname)=>zif_bc_multiton~get_instance
            EXPORTING
              iv_objectid = ls_mt-objectid
            RECEIVING
              ro_obj      = ls_mt-obj.

        CATCH cx_sy_create_object_error INTO ls_mt-cx ##no_handler.
        CATCH cx_root INTO DATA(lo_diaper).

          ls_mt-cx = NEW #(
            textid    = cx_sy_create_object_error=>cx_sy_create_object_error
            classname = CONV #( ls_mt-clsname )
            previous  = lo_diaper
          ).

      ENDTRY.

      INSERT ls_mt
        INTO TABLE gt_multiton
        ASSIGNING <ls_mt>.

    ENDIF.

    IF <ls_mt>-cx IS NOT INITIAL.
      RAISE EXCEPTION <ls_mt>-cx.
    ENDIF.

    ro_obj = <ls_mt>-obj.

  ENDMETHOD.

ENDCLASS.

Now, if we need to create an instance of ZCL_BC_MULTITON_DEMO bypassing the multiton cache, all we need to do is to create the object regularly as demonstrated below.

DATA(lo_obj) = NEW zcl_bc_multiton_demo( 'DUMMY' ).

If we need to get advantage of multiton, here is what we need to do.

DATA(lo_obj) = CAST zcl_bc_multiton_demo(
  zcl_bc_multiton=>get_obj(
    iv_clsname = 'ZCL_BC_MULTITON_DEMO'
    iv_objectid = 'DUMMY'
  )
).

 

Pretty neat, eh? Having ZCL_BC_MULTITON_DEMO, we don’t need to deal with caching anywhere else. This class will do the multiton caching for you, and return the cached instance in case you re-call GET_OBJ with the same object id.

In case you need the original source codes of the samples mentioned here, you can visit my GitHub ABAP library.

Standard
software, software.sap

Eğlencelik SAP Entry’leri

Her seferinde aramak zorunda kalmamak için; Ekşi Sözlük’te yazdığım eğlencelik SAP Entry’lerimi buraya toplamak istedim.

Standard
software, software.sap

Decorator Design Pattern

Giriş

Bu makalede; sık kullanılan Design Pattern’lardan biri olan Decorator’ı tanıyacağız.

Bu Pattern, size SAP’nin BADI teknolojisi üzerinden tanıdık gelebilir. Şimdiye kadar BADI’lerin içine kod yazmış olabilirsiniz. Bu yazının sonunda, uygulamalarınızı BADI mantığında genişletebilir hale gelmenizi hedefliyoruz.

Ön Koşullar

Bu makalede, aşağıdaki konulara aşina olduğunuz varsayılmaktadır.

  • ABAP bilgisi
  • Object Oriented ABAP tecrübesi
  • Interface ve Class kavramları
  • Casting

Decorator Nedir?

Decorator Design Pattern; aynı veri üzerinde birden fazla değişiklik yapılması gerektiği durumlarda kullanılır. Bu değişikliklerin tamamını ana programa veya merkezi bir sınıfa kodlamak yerine; dağınık haldeki mikro sınıflara kodlama prensibine dayanır.

Bu sayede; yeni bir kod eklemek gerektiğinde, test edilmiş eski kodlara herhangi bir şekilde dokunmadan yeni bir sınıf yazmak yeterli olmaktadır. Veya mevcut bir işlevi çıkarmak gerektiğinde, o işlevi barındıran sınıfı etkisiz hale getirmek yeterlidir.

Örnek Uygulama: User Exit

Decorator mantığını anlamak ve avantajlarını görmek için, tipik uygulama alanlarından biri olan User Exit örneğini inceleyeceğiz.

Aşağıdaki fonksiyonun bir User Exit olduğunu varsayalım.

FUNCTION ornek_user_exit
  IMPORTING
    !iv_bukrs TYPE bukrs
    !iv_waers TYPE waers
  TABLES
    !ct_data STRUCTURE ztt_data.

INCLUDE zxornek.

ENDFUNCTION.

Bu User Exit içerisinde 3 ayrı iş yapılacağını düşünerek, konuyu irdelemeye başlayalım.

Klasik ABAP Uygulaması

Geleneksel ABAP anlayışında, ZXORNEK dosyası içerisinde bu 3 iş alt alta ve ayrı ayrı kodlanacaktır.

*&----------------
*& Include ZXORNEK
*&----------------

" Exit'te yapılacak 1. iş buraya kodlanır
" Exit'te yapılacak 2. iş buraya kodlanır
" Exit'te yapılacak 3. iş buraya kodlanır

Bu yaklaşımın pek çok sakıncası vardır; bunlardan bazılarını dile getirelim.

  • Bu Include içerisinde kodlar, ister istemez birbirini etkileyecektir. Örneğin üst satırlara yazılmış bir EXIT komutu, alt satırlarda çalışması gereken bir kodun hiç çalışmamasına yol açabilir.
  • ZXORNEK içerisine 4. bir ek yapıldığında, diğer 3 işe ait senaryoların da tekrar test edilmesi gerekir.
  • ZXORNEK üzerinde farklı ABAP’çılar paralel çalışamaz.
  • ZXORNEK altındaki farklı işler farklı Request’lere dahil edilip canlıya ayrı ayrı taşınamaz. Parçalı taşıma gerektiğinde, kodların yıldızlanıp taşınıp tekrar açılması gibi zahmetli ve riskli yaklaşımlar gerekir.
  • ZXORNEK’e kodlar eklendikçe, bu dosya zaman içerisinde uzun ve anlaşılması zor hale gelir, bakım maliyeti ve hata riski gittikçe artar.

Şimdi aynı uygulamayı Decorator Design Patern ile yapıp, bu dezavantajları ortadan nasıl kaldıracağımızı görelim.

Decorator Adım 1: Interface

Çözümümüzün ilk adımı, bu User Exit için bir Interface oluşturmaktır. SAPGUI üzerinde SE24 işlem koduna giderek veya Eclipse içerisinde New → ABAP Interface menüsünü kullanarak Interface tanımlayabiliriz.

INTERFACE zif_decorator PUBLIC.

METHODS decorate
  IMPORTING
    !iv_bukrs TYPE bukrs
    !iv_waers TYPE waers
  CHANGING
    !ct_data TYPE ztt_data.

DECORATE yordamındaki parametrelerin, User Exit parametreleri ile örtüştüğüne dikkat edin.

Basit olması açısından, Interface’imiz içerisinde; hata yönetimi gibi ek işlevleri devre dışı bıraktık.

Decorator Adım 2: Uygulama Sınıfları

Bu adımda, 3 farklı işi yapacak 3 bağımsız sınıf yazacağız. SAPGUI üzerinde SE24 işlem koduna giderek veya Eclipse içerisinde New → ABAP Class menüsünü kullanarak sınıf tanımlayabiliriz.

CLASS zcl_decorator01 DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC.

  PUBLIC SECTION.
    INTERFACES zif_decorator.
  PROTECTED SECTION.
  PRIVATE SECTION.

ENDCLASS.

CLASS zcl_decorator01 IMPLEMENTATION.

  METHOD zif_decorator~decorate.

    MODIFY ct_data
      FROM VALUE t_data( yabanci_kur = abap_true )
      TRANSPORTING yabanci_kur
      WHERE waers NE iv_waers.

  ENDMETHOD.

ENDCLASS.

Sınıf ZIF_DECORATOR Interface’ini uygulayıp, hayali tablomuzdaki YABANCI_KUR alanını doldurmuş olduk.

Interface’i uygulamamız, bu sınıfın ZIF_DECORATOR içerisinde geçen Method’ları barındırdığını sisteme garanti etmemiz anlamına geliyor. Sistemde ZIF_DECORATOR’u kullanacak şekilde yazılmış herhangi bir program, ZCL_DECORATOR01 sınıfının detaylarını bilmeye ihtiyaç duymadan bu sınıftan faydalanabilir. Zira; içerisinde DECORATE diye bir Method oluğu biliniyor.

Nasıl faydalanacağını birazdan göreceğiz.

Yine basit olması açısından, sınıf içerisindeki kodu basit tuttuk. Gerçek dünyada; bu sınıf içerisinde Type’lar, Private Method’lar, hata yönetimi, vb görmeyi beklerdik.

Aynı mantığı takip ederek, User Exit’te yapmamız gereken ikinci işi barındıran ikinci bir sınıf açıyoruz. Normalde bu sınıfların yapacağı işler daha karmaşık olacaktır, ancak örneği basit tutmak adına işleri de basit tutuyoruz.

CLASS zcl_decorator02 DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC.

  PUBLIC SECTION.
    INTERFACES zif_decorator.
  PROTECTED SECTION.
  PRIVATE SECTION.

ENDCLASS.

CLASS zcl_decorator02 IMPLEMENTATION.

  METHOD zif_decorator~decorate.

    CHECK zcl_fi_toolkit=>is_company_special(
        iv_bukrs
      ) eq abap_true.

    MESSAGE e300(zfi) WITH iv_bukrs.

  ENDMETHOD.

ENDCLASS.

Dikkat ederseniz; bu sınıfta CHECK komutunu çekinmeden kullanabiliyoruz. CHECK, olsa olsa ZCL_DECORATOR02’den çıkmamız anlamına gelir. Diğer uygulama sınıflarını etkilemeyecektir.

Şimdi son sınıfımızı da kodlayalım.

CLASS zcl_decorator03 DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC.

  PUBLIC SECTION.
    INTERFACES zif_decorator.
  PROTECTED SECTION.
  PRIVATE SECTION.

ENDCLASS.

CLASS zcl_decorator03 IMPLEMENTATION.

  METHOD zif_decorator~decorate.

    DELETE ct_data WHERE xflag EQ abap_false.
    CHECK ct_data IS INITIAL.
    MESSAGE e301(zfi).

  ENDMETHOD.

ENDCLASS.

Bu şekilde, basitleştirilmiş örnek sınıflarımızı tamamlamış oluyoruz. Elimizde bir Interface ve üç Class var.

Decorator Adım 3: User Exit Uygulaması

Bu son adımda; dağınık Lego parçaları gibi hazırladığımız bileşenleri User Exit içerisinde bir araya getireceğiz.

*&-----------------
 *& Include ZXORNEK
 *&----------------

DATA:
  lo_decorator TYPE REF TO zif_decorator,
  lo_object TYPE REF TO object.

" ZIF_DECORATOR Interface'ini uygulamış sınıfları tespit et

SELECT clsname
  FROM seometarel
  WHERE refclsname EQ 'ZIF_DECORATOR'
  INTO TABLE @data(lt_class).

" Her bir sınıfa ait nesneyi dinamik yaratıp çalıştır

LOOP AT lt_class ASSIGNING FIELD-SYMBOL(<ls_class>).

  CREATE OBJECT lo_object TYPE (<ls_class>-clsname).
  lo_decorator ?= lo_object.

  lo_decorator->decorate(
    EXPORTING
      iv_bukrs = iv_bukrs
      iv_waers = iv_waers
    CHANGING
      ct_data = ct_data[]
  ).

ENDLOOP.

Gördüğünüz gibi; Exit’in yaptığı iş, ZIF_DECORATOR Interface’ini uygulayan sınıfları tespit edip çağırmaktan ibaret. Exit içerisinde fonksiyonel herhangi bir kod yok.

Exit çalıştığında;

  1. SEOMETAREL tablosundan ZCL_DECORATOR01 & 02 & 03 sınıflarına erişilecektir.
  2. Loop’un ilk adımında, ZCL_DECORATOR01 içindeki DECORATE Method’u çalışacaktır.
  3. Loop’un ikinci adımında, ZCL_DECORATOR02 içindeki DECORATE Method’u çalışacaktır.
  4. Loop’un üçüncü adımında, ZCL_DECORATOR03 içindeki DECORATE Method’u çalışacaktır.

Inteface’i uygulayan sınıfları bulan kodu basit tutarak doğrudan SEOMETAREL tablosuna baktık. Bu basit kod, Abstract Class uygulandığı durumda sınıfları gözden kaçırabilir veya sınıfların sıralı çağırılması gereken durumda yeterli olmayabilir. Bu konudaki alternatif yaklaşımlar, örnek kodlarla birlikte Design Patterns in ABAP Objects adlı kitabımda var.

Değerlendirme

Bu yaklaşım, klasik ABAP yaklaşımının dezavantajlarını nasıl ortadan kaldırıyor, birer birer inceleyelim.

  • Kodlar birbirini etkilememektedir. Örneğin; ZCL_DECORATOR02 içerisindeki CHECK komutu, diğer sınıflarda yer alan kodların çalışmasını etkilememiştir.
  • ZCL_DECORATOR03’te bir değişiklik yaptığımızda, 01 ve 02’yi tekrar test etmemiz gerekmiyor.
  • 3 farklı ABAP’çı, 3 farklı sınıf üzerinde paralel çalışabilir.
  • ZCL_DECORATOR01 canlıya giderken, ZCL_DECORATOR02 geliştirme sisteminde bekleyebilir.
  • Bu Exit’e ek yapmak için tek yapmamız gereken şey ZCL_DECORATOR04 diye yeni bir sınıf açmaktır. Kodlar basit ve modüler parçacıklar halinde olduğundan, değişiklik yönetimi kolaylaşmaktadır.

Yardımcı Diğer Pattern’lar

Sınıflar arasında veri paylaşma ihtiyacı ortaya çıkarsa, Decorator’a yardımcı Property Container Design Pattern uygulanabilir. Bu Pattern; her bir User Exit için ayrı bir Interface açma ihtiyacını ortadan kaldırıp, tüm User Exit’leri (SE19 benzeri) tek bir altyapıdan yönetmeyi de mümkün kılabilir.

Decorator sınıfları arasında, birbirine benzer iş yapan sınıfların işlevleri Template Method veya Servant uygulanarak ortaklaştırılabilir.

Sıralı çağırmak gibi basit ihtiyaçların ötesinde sınıfların koordine edilmesi gerekiyorsa, Mediator kullanılabilir.

Bahsetilen bu Pattern’ların tamamı, örnek uygulamalarla birlikte Design Patterns in ABAP Objects adlı kitabımda var.

Sonuç

Bu makalede, Decorator Design Pattern’i örnek User Exit uygulaması üzerinden incelemiş olduk. Design Pattern kullanmanın avantajlarını da kısmen gördük.

İncelediğimiz bu örnek Pattern’in geliştirmelerinizde faydalı olmasını ve diğer Pattern’leri öğrenmek konusunda sizi teşvik etmesini umuyorum.

Standard
software, software.sap

Technical SAP Cutover Steps

Here are some of the significant cutover steps for SAP go live.

System integrity:

  • Ensure that packages in dev / QA / live are identical
  • Ensure that E-Mails can be sent from the live system

Transport paths:

  • Ensure that “Transport of Copy” requests can be carried between dev – live systems
  • Ensure that requests in QA STMS can be forwarded to the live system
  • Ensure that workbench / customizing requests can be sent between dev – QA – live

Transport execution:

  • Transport external requests to the live system
  • Transport requests of all finished developments to the live system by selecting original requests & a transport of copies request containing everything
  • Ensure that all requests imported to QA are imported to the live system as well. TPALOG table + Excel VLOOKUP will help.

Soft config:

  • Definition of SM59 connections
  • Definition of SOAMANAGER connections
  • Definition of Z* number ranges in SNRO
  • Customizing of technical Z* tables
  • Ensure SCAL definitions & future maintenance
  • Define jobs

Workflow configuration:

  • Setting up WF-BATCH
  • Workflow configuration in SWU3
  • Ensure that Z* tasks are classified as general task (PFTC_DIS)
  • Execute SWU_OBUF and define a job if needed
  • If you use deadline monitoring, define  job for RSWWDHEX
Standard
software, software.sap

ABAP: UNION DISTINCT vs ALL

As many of you already know, ABAP supports the UNION command now. UNION is an SQL command to combine the results of two separate queries into one dataset.

Here is a sample dataset from the imaginary database table Z1:

ID
TEXT
001
One
002
Two

And here is a sample dataset from another imaginary database table Z2:

CODE
NAME
002
Two
016
Sixteen
019
Nineteen

Before the UNION command existed, we had to run two distinct queries to combine this data into a single internal table.

SELECT
    id AS key,
    text AS value
  FROM z1
  INTO TABLE @DATA(lt_itab).

SELECT
    code AS key,
    name AS value
  FROM z2
  APPENDING CORRESPONDING FIELDS OF @lt_itab.

Now, UNION allows us to merge those queries.

SELECT
      id AS key,
      text AS value
    FROM z1
  UNION
      code AS key,
      name AS value
    FROM z2
  INTO TABLE @DATA(lt_itab).

Here is the catch: If we execute the UNION query above, we get the following resultset:

KEY
VALUE
001
One
002
Two
016
Sixteen
019
Nineteen

Did you notice that record “002” appears only once? In spite of its existence in both of Z1 & Z2, the resultset included a singular “002” entry.

That’s the catch with UNION queries. They can be executed with two logical approaches to handle duplicate records.

UNION DISTINCT is the default mode, and it will eliminate duplicate records from the second query. That’s similar to the logic of SELECT DISTINCT or FOR ALL ENTRIES. That’s why “002” from the second table was missing in the resultset.

UNION ALL needs to be specified explicitly, and it tolerates duplicates from the second query. So, let’s modify our sample query accordingly.

SELECT
      id AS key,
      text AS value
    FROM z1
  UNION ALL
      code AS key,
      name AS value
    FROM z2
  INTO TABLE @DATA(lt_itab).

This time, our recordset will include duplicate records from Z2.

KEY
VALUE
001
One
002
Two
002
Two
016
Sixteen
019
Nineteen

Each approach has its time and place. If I am looking for a unique list of values, such as the combined list of tax numbers of clients & vendors, I would go for UNION DISTINCT. However; if I am querying transactions, such as BSIS / BSAS, I would go for UNION ALL because I probably wouldn’t want to miss any line items.

Standard
software, software.sap

Klasik ABAP, Object Oriented ABAP ve Design Patterns

1464-ppt

Bu yazıda; bana çok sık sorulan bazı soruları cevaplamak adına, SAP projelerinde klasik ABAP yerine Object Oriented yaklaşım kullanmanın faydalarından bahsedeceğim. Bunun yanı sıra, Object Oriented geliştirme yapmak isteyen bir programcının Design Pattern’lardan haberdar olmasının getirdiği avantajları ele alacağım.

Özet

Klasik ABAP ile yapılan geliştirmeler, yeterince esnek ve yeniden kullanılabilir olmamaktadır. Programda bir değişiklik istendiğinde veya programın bir parçasını bir başka noktada kullanmak gerektiğinde; canlı kullanılan kodlara müdahale edileceğinden geliştirme & test süresi uzayabilir ve ortaya hesapta olmayan yeni hatalar çıkabilir.

Object Oriented yaklaşım; geliştirmeleri yekpare yapılar olmaktan çıkarıp, Lego mantığında çok parçalı yapılara çevirmektedir. Bu küçük parçalar; güçlü, esnek, geliştirilebilir, yeniden kullanılabilir ve ikame edilebilir özellikte olmaktadır. Her bir parça; bir kez test edildikten sonra farklı geliştirmelerde tekrar tekrar kullanılabilmektedir. Bu yaklaşımla; geliştirme & test süreleri ve hata oranları azaltılabilir; dolayısıyla geliştirme maliyetleri düşürülebilir.

Design Pattern’lar, kendini zaman içerisinde ispatlamış hazır Object Oriented şemalardır. Bu şemaları tanıyan bir yazılmıcı; bir yazılım ihtiyacıyla karşılaştığında Amerika’yı baştan keşfetmek yerine bu hazır yapıları kullanmayı tercih edebilir. Bu yaklaşımla; Object Oriented kullanmanın avantajları katlanacaktır.

SAP’nin kendisinin de yeni geliştirmelerde Object Oriented yaklaşımı tercih ettiğini hatırlatarak; tüm yazılımcı ve yöneticilere klasik ABAP’tan Object Oriented ABAP’a evrilmeyi ve Design Pattern kullanmayı tavsiye ediyorum.

Klasik ABAP

Klasik ABAP ile yapılan bir geliştirme; büyük ihtimalle SE38 işlem kodunda oluşturulmuş bir program ve birkaç Include’dan ibaret olacaktır. Programlar arası modülarizasyon ise, tipik olarak SE37 fonksiyonları ile sağlanır. SAP’nin eski geliştirmeleri de bu prensiple oluşturulmuştur. Bu haldeki programlar, yekpare plastik oyuncaklara benzetilebilir.

Klasik ABAP ile yazılan bir program, yazılma amacına uygun bir şekilde çalışıp doğru sonuç getiriyor olabilir. Ancak; bu programın esnekliği düşük olacaktır. Bunun getirdiği bazı tipik dezavantajları inceleyelim.

  • Programa yeni özellikler eklemek istediğimizde; muhtemelen test edilmiş ve canlıya alınmış kısımlarına müdahale edip tekrar test etmek zorunda kalacağız.
  • Program içerisindeki bir algoritmayı bir başka programda daha kullanmak gerekirse, yine test edilmiş kodlara müdahale etmeden bunu yapmak zor olacaktır. Include dosyaları, fonksiyonlar ve PERFORM IN komutları; bu konuda yeterli esnekliği sağlamayabilir.
  • Programın algoritmasını entegrasyon senaryolarına dahil etmek veya Fiori / mobil gibi alternatif bir arayüzden kullanıcılara açmak söz konusu olduğunda, klasik ABAP ile oluşturulmuş yapılar yine ek geliştirme + test eforu ve hata potansiyeli getirecektir.
  • Bir program üzerinde aynı anda tek bir ABAP’çı çalışabilmektedir. Bunun yanı sıra; programın özelliklerinden sadece bir kısmını canlıya almak gerektiğinde, ciddi zorluklar gündeme gelebilir.

Bu ve buna benzer faktörler, yazılımcıları Object Oriented ABAP kullanmaya sevk etmektedir.

Object Oriented ABAP

Klasik ABAP ile yapılan geliştirmeyi yekpare bir oyuncağa benzetirsek; Object Oriented ABAP ile yapılan bir geliştirme Lego parçalarıyla oluşturulmuş bir oyuncağa benzeyecektir. Aynı parçayı (sınıfı) bugün belli bir programda belli bir amaçla kullanırken, yarın bir başka programda başka bir amaçla kullanabiliriz.

Elle tutulur bir örnek olması için; stokları listeleyen bir rapor yazdığımızı varsayalım. Klasik ABAP yaklaşımında; SE38’de yeni bir Z’li program oluşturur, bu programda hem veri okuma işini, hem de ALV ile ekranda gösterme işini yaparız. Object Oriented yaklaşımda ise; önce SE24’te stokları döndüren bir sınıf hazırlarız. Akabinde; SE38’deki program, bu sınıfı çağırır ve gelen sonuçları ALV ile ekrana döker. Bu sınıfı; uzun vadede, stok bilgisine ihtiyaç duyduğumuz tüm SAP programlarında, entegrasyonlarda, Fiori uygulamalarında, vs kullanabiliriz.

Object Oriented dünyaya adım attığımızda; karşımıza Class, Abstract Class, Interface, Public, Protected, Private, Static, Instance gibi kavramlar çıkacaktır. Bu kavramları anlayıp geliştirme yapmaya başladığımızda, Object Oriented ABAP’ın avantajlarından faydalanırız. SAP’nin kendisinin de yeni özellikleri büyük ölçüde Object Oriented yapılarda sunması, önemli bir göstergedir.

Teknik avantajlar, literatürde beş temel prensiple çerçevelenmektedir.

Abstraction: Bu prensip çerçevesinde; ana programın Interface’i tanıması yeterlidir, arkasındaki sınıfın tam olarak ne yaptığını bilmesine gerek yoktur. Bunu, televizyonun HDMI girişi gibi düşünebiliriz. HDMI arayüzü, veri akışını bir anlamda soyut hale getirir. Televizyon, HDMI kablosunun diğer ucunda bir DVD oynatıcı, oyun konsolu, dizüstü bilgisayar, vs olduğundan habersizdir. HDMI arayüzüne uyduğu sürece, her cihazla çalışabilir. Abstraction prensibi, bu yaklaşımı programlarımıza da uygulamamıza olanak tanır. Örneğin; 5 farklı BAPI çağıracak bir ABAP programı, ortak bir Interface üzerinden 5 farklı BAPI sınıfıyla çalışabilir. Arkasındaki sınıfın ne yaptığına bakmaksızın, sadece Interface ile muhatap olur.

Composition: Bir sınıf; aynen değişken ve yordam barındırdığı gibi, diğer sınıfları da barındırabilir. Bu prensip sayesinde; mikro servis mantığında sınıflar geliştirip, mevcut bir sınıfa yeni özellik eklemek gerektiğinde ilgili sınıfı dahil etmek gibi esnek yaklaşımlar uygulanabilmektedir. Örneğin; malzeme stoğu döndüren bir sınıf varsa, bu sınıfı hem kullanılabilirlik kontrolü BADI’sine, hem de stok simülasyonu yapan sınıfa dahil edebiliriz.

Inheritence: Bir sınıftan bir başka sınıf türetip, yordamlarını alt sınıfta kısmen değiştirmek mümkündür. Üst sınıftaki değişiklikler, alt sınıfa otomatik yansıyacaktır.

Encapsulation: Bir sınıfın değişken, yordam gibi bileşenlerini Public / Protected / Private diye işaretleyerek; dış dünyada ne kadarının kullanılabileceğini belirleyebiliriz. Bunu aynen; arabanın direksiyonunu sürücüye açarken motorunu saklamaya benzetebiliriz. Bu şekilde; sınıfımıza erişen bir program, işlevlere sadece izin verilen şekilde erişecek ve böylece hata riski azalacaktır.

Polymorphism: Aynı Interface’e sahip sınıflar, birbirinin yerine geçebilir. Örneğin; bir entegrasyon programının arkasında FTP’den veya SOAP servisinden veri okuyabilen birer sınıf olduğunu varsayalım. Ek olarak REST servisinden veri okuma ihtiyacı çıkarsa; aynı Interface’e sahip olan yeni bir sınıf açmamız yeterli olacaktır. Ana programın kodunda herhangi bir değişiklik yapmak gerekmez; zira ana program, söz konusu Interface’ten türemiş tüm sınıfları ister istemez destekleyecektir.

Bu prensiplerin sonucunda ortaya çıkan fonksiyonel avantajlardan bazılarını ifade etmek gerekirse;

  • Test edilmiş ve canlıya aktarılmış kod birimleri, farklı yerlerde tekrar tekrar kullanılabilir
  • Algoritmalar; farklı programlar ve entegre olunan farklı platformlar tarafından yeniden kullanılabilir
  • Programlar; HDMI veya USB mantığıyla, tak-çıkar bileşenlerden oluşan yapılar şeklinde tasarlanabilir
  • Programlar, mevcut kodlara dokunmadan genişletilebilir
  • Programcılar, birbirini kilitlemeden paralel çalışabilir
  • Program bileşenleri, birbirinden bağımsız test edilebilir; hatta birim testler otomatize edilebilir
  • Sınıflarda yapılan merkezi iyileştirmeler, sınıfı kullanan tüm programları olumlu etkiler
  • Geliştirmelerin canlıya kısmi olarak alınması kolaylaşır

Bu fonksiyonel avantajlar; proje yönetimi şu anlama gelmektedir:

  • Geliştirme süresi azalır
  • Test süreleri azalır
  • Hata oranı azalır

Üst yönetim açısından; geliştirme & test süresinin ve hataların azalması, doğrudan doğruya maliyetlerin azalması anlamına gelmektedir.

Ancak; bu avantajları yaşamak için, programların arkasına Class tanımlıyor olmak yeterli değildir. Normalde SE37’de fonksiyon formunda yazacağınız kodu SE24’e geçip Method olarak yazmak, Object Oriented kılığında klasik ABAP geliştirmesi yapmak anlamına gelmektedir. Bu avantajların ortaya çıkması için, Object Oriented bileşenlerin doğru kurgulanması gerekmektedir. Bu da, bizi Design Pattern kavramına götürüyor.

Design Patterns

Object Oriented ABAP ile geliştirme yapmayı Lego’ya benzetirsek; Design Pattern’lar hazır Lego şemaları gibi düşünülebilir. Yeni bir Lego oyuncağı aldığınızda; içinden çıkan şema, parçalarla oluşturabileceğiniz çalışır bir oyuncağı tarif edecektir. Aynı şekilde; Design Pattern’lar, Class, Abstract Class, Interface gibi “parçaları” kullanarak oluşturabileceğiniz yapıları tarif edecektir.

Elle tutulur bir örnek olması için; satın alma taleplerine onay verecek kişileri belirleme ihtiyacıyla karşı karşıya olduğumuzu varsayalım. Elimizde 10 tane kural olsun. Bir sınıf tanımlayıp, bu sınıf içerisine bu 10 kuralı IF / CASE zincirleri şeklinde tanımlamak mümkündür. Ancak; gelecekte 11. kural geldiğinde, test edilip canlıya atılmış bu sınıfa müdahale etmeden bunu uygulayamayız. 11. kuralı canlıya atmadan önce, diğer 10 kuralı da en baştan tekrar test etmek gerekir.

Halbuki; Chain of Responsibility adlı Design Pattern, bize tam olarak bu problemi çözen hazır bir şema önermektedir. Bu şemadan; 10 kuralı 10 ayrı sınıf olarak tanımlayıp, uç uca ekliyoruz. Uygulama çalıştığında; her bir kural sınıfı satın alma belgesini değerlendirecek, onaycıları belirleyebiliyorsa belirleyecek, belirleyemiyorsa belgeyi bir sonraki sınıfa paslayacaktır. Bu yapıda; 11. kural gündeme geldiğinde, o kurala ait sınıfı yazıp zincire eklemek yeterlidir. Test edilmiş ve canlıda çalışan kodlara dokunmamıza gerek kalmayacaktır.

Birkaç örnek Design Pattern ifade etmek gerekirse; MVC, Observer, Data Access Object, Multiton, Decorator, Strategy dile getirilebilir. Bu kavramı ortaya atan ilk kitap, 30 civarı Pattern içermekteydi. Günümüzde ise; Flux gibi daha modern Pattern’lar da türetilmektedir.

Design Pattern’lar, genel anlamda aşağıdaki prensiplere uygun Object Oriented yapılar içermektedir:

  • Single Repository: Her bir sınıf, sadece tek bir sorumluluğa sahip olmalıdır.
  • Open-Closed: Sınıflar; genişletmeye açık, ama modifikasyona kapalı olmalıdır.
  • Liskov Substitution: Programın kodu değiştirilmeden, programın kullandığı sınıflar değiştirilebilmelidir.
  • Interface Segregation: Interface’leri opsiyonel Method’lar ile şişirmek yerine, birkaç Interface oluşturmak yeğlenmelidir.
  • Dependency Inversion: Programlar; doğrudan doğruya sınıflarla değil, Interface’lerle çalışacak şekilde tasarlanmalıdır.

Design Pattern’lar; bizi Anti-Pattern olarak bilinen hatalı yaklaşımlardan da uzak tutar. Tipik Anti-Pattern’lardan bazıları şunlardır:

  • Blob: Büyük bir sınıfın, işlerin tamamını yapmaya çalışmasıdır. İşlevler, mimari bir yaklaşımla alt sınıflara kırılmalıdır.
  • Copy-Paste Programming: Parametrik yordam tanımlamak yerine, kodların kopyala & yapıştır ile çoğaltılmasıdır.
  • Functional Decomposition: Object Oriented dünyada klasik ABAP alışkanlıklarını kullanmaktır. Bu durum, Design Pattern bilgisi olmadan Object Oriented kod yazan ABAP’çıların bir kısmında görülmektedir.
  • Jumble: Çok katmanlı yapılarda, alt – üst katmanların izole edilmeden birbirine erişmesidir. Özellikle Event’ler konusunda tecrübesiz programcılar, bu hataya düşmektedir.
  • Object Orgy: Sınıfın tüm bileşenlerinin Public olarak tanımlanmasıdır. Bu durum, sınıfın yapısını iyi tanımayan kişilerin kafasının karışmasına ve hatalı işlem yapmasına yol açar.
  • Spaghetti Code: Alt yordamlara kırılmadan devam eden uzun kod bloklarına bu isim verilir. Bu şekilde kodlanmış yordamların anlaşılması da, bakımının yapılması da çok zordur.

Yazılım literatüründeki Design Pattern’lar, uzun yıllardır sayısız projede test edilmiş ve zamana karşı kendini ispatlamış şemalardır. Pattern’lerin arkasındaki mantığı kavradıkça, münferit Pattern’leri birleştirerek kullanmak veya kendi Pattern’lerimizi geliştirmek de mümkündür.

Programcı ile yazılım mimarı arasındaki en önemli farklardan biri de budur. Mimar; bir yazılım ihtiyacına baktığında, o ihtiyaca uygun yapıyı sezip mevcut Pattern’lerden biri / birkaçını uygulamaya veya kendi yapısını oluşturmaya isabetli bir şekilde karar verebilecektir. Bu esnada; Anti-Pattern tuzaklarından da kaçınacaktır. Programcı, mimarın kurduğu yapı içerisindeki kodlamayı yapacaktır. Kariyerinde mimari yönde ilerlemek isteyen programcılar için, Design Pattern bilgisi elzemdir.

Design Pattern kullanmak; Object Oriented ABAP kullanmanın avantajlarını katlayacaktır. Daha sağlam, esnek ve en önemlisi genişletilebilir uygulamalar geliştirmeyi mümkün kılmaktadır. Amerika’yı tekrar keşfetmek yerine, zamanın sınavından geçmiş ve kendini ispatlamış doğru yapılar kurgulanır. Bunun sonucunda; geliştirme süreleri, hata oranları ve proje maliyetleri daha da azaltılabilir.

Sonuç

Klasik ABAP yerine Object Oriented ABAP kullanmak; geliştirme sürelerini kısaltacak, hata oranını düşürecek ve maliyetleri azaltacaktır. Design Pattern kullanmak ise, bu avantajları katlayacaktır.

Bu yaklaşımların birbiri üzerine inşa edildiği söylenebilir. ABAP’a ilk kez adım atan biri, yazılım dilini klasik ABAP yaklaşımıyla öğrenecektir. Klasik ABAP’tan bir sonraki adım, Object Oriented ABAP ile geliştirme yapmak olacaktır. Bu konuda tecrübe sahibi olup, mimari anlamda daha esnek ve kuvvetli yapılar ortaya çıkarmak isteyenler ise, bir sonraki aşamada Design Pattern’lara adım atabilir.

SAP’nin kendisinin de yeni geliştirmelerde Object Oriented yaklaşımı tercih ettiğini hatırlatarak; tüm yazılımcı ve yöneticilere klasik ABAP’tan Object Oriented ABAP’a evrilmeyi ve Design Pattern kullanmayı tavsiye ediyorum.

Kendini geliştirmek isteyen ve kaynak arayışında olanlar için, birer SAP Press kitabı önerelim.

 

Standard