본문 바로가기
RAP

RAP 17 - MANAGED REPORT : ADDITIONAL SAVE

by SAPortal 2026. 9. 3.
반응형

 

 

 

목차

    Additional Save

    Managed 시나리오에서 프레임워크의 자동 저장과 별개로 개발자가 직접 저장로직을 추가하는 기능입니다. 구현위치는 Behavior Definition에 선언 후  Saver Class의 save_modified 메소드 내에 구현합니다.

     

    주요용도

    변경이력 로그 기록 - 누가 무엇을 언제 변경했는지 별도 테이블에 이력 남기기

    외부 시스템 연동 - 저장 시점에 API 호출이나 메시지 출력

    집계 테이블 갱신 - 요약 데이터를 별도 테이블로 관리

     

    우리는 이중에서 변경이력을 남기는 로직을 추가해 보겠습니다. Behavior Definition에서 Travel쪽에 with additional save를 추가합니다. 그리고 Method를 생성합니다.

     

     

    Handler Class가 아닌 Save Class에 Method가 생성된걸 볼 수 있습니다. Method명에 마우스를 클릭해 파라미터를 살펴보겠습니다. create, update, delete로 나눠져 있는걸 보니 데이터를 생성할때는 create로 수정할때는 update로 삭제할때는 delete로 값이 들어오는 것 같습니다. [derived type..]을 클릭해 보겠습니다.

     

     

    생성할때 입력한 Travel정보가 zever_m_travel_i 파라미터로 들어올 것 같습니다. 그럼 Booking정보는 어떻게 받아야 할까요?

    우리가 Behavior Definition에 with additional save를 Travel 쪽에만 추가를 해줘서 그렇습니다.

     

     

    Behavior Definition으로 이동해서 Booking쪽에도 with additional save를 추가해 줍니다. 그러고 나서 파라미터를 다시 확인해 보겠습니다.

     

     

    Travel과 Booking에 대한 파라미터가 둘다 보입니다.

     

     

    아니면 Travel과 Booking쪽에 추가했던 with additional save를 모두 삭제하고 상단에 추가해 주면 모든 Entity에 additional save가 적용이 됩니다. 이번 실습에서는 아래와 같이 가장 상단에 추가하고 진행하겠습니다.

     

     

     

    Log 저장을 위한 테이블 2개를 생성합니다.

    Key필드로 sysuuid_x16 타입의 log_id를 추가했고 나머지 필드는 기존 Travel과 Booking 테이블의 필드를 그대로 가져왔습니다.

     

    < ZEVER_TRAVEL_L >

    @EndUserText.label : 'Travel Log Table'
    @AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
    @AbapCatalog.tableCategory : #TRANSPARENT
    @AbapCatalog.deliveryClass : #A
    @AbapCatalog.dataMaintenance : #RESTRICTED
    define table zever_travel_l {
    
      key client    : abap.clnt not null;
      key log_id    : sysuuid_x16 not null;
      operation     : abap.char(20);
      travel_id     : /dmo/travel_id not null;
      agency_id     : /dmo/agency_id;
      customer_id   : /dmo/customer_id;
      begin_date    : /dmo/begin_date;
      end_date      : /dmo/end_date;
      @Semantics.amount.currencyCode : 'zever_travel_l.currency_code'
      booking_fee   : /dmo/booking_fee;
      @Semantics.amount.currencyCode : 'zever_travel_l.currency_code'
      total_price   : /dmo/total_price;
      currency_code : /dmo/currency_code;
      description   : /dmo/description;
      status        : /dmo/travel_status;
      createdby     : syuname;
      createdat     : timestampl;
    
    }

     

     

    < ZEVER_BOOKING_L >

    @EndUserText.label : 'Booking Log Table'
    @AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
    @AbapCatalog.tableCategory : #TRANSPARENT
    @AbapCatalog.deliveryClass : #A
    @AbapCatalog.dataMaintenance : #RESTRICTED
    define table zever_booking_l {
    
      key client    : abap.clnt not null;
      key log_id    : sysuuid_x16 not null;
      operation     : abap.char(20);
      travel_id     : /dmo/travel_id not null;
      booking_id    : /dmo/booking_id not null;
      booking_date  : /dmo/booking_date;
      customer_id   : /dmo/customer_id;
      carrier_id    : /dmo/carrier_id;
      connection_id : /dmo/connection_id;
      flight_date   : /dmo/flight_date;
      @Semantics.amount.currencyCode : 'zever_booking_l.currency_code'
      flight_price  : /dmo/flight_price;
      currency_code : /dmo/currency_code;
      createdby     : syuname;
      createdat     : timestampl;
    
    }

     

     

    Log 테이블을 생성하고 save_modified 메소드에 로직을 구현해 보겠습니다. 첫번째로 데이터를 생성했을때의 로직입니다.

    소스를 천천히 보면 그렇게 어렵지 않습니다.

     

    1. 저장을 위한 Internal Table을 선언합니다.

    2. Travel정보가 생성되면 저장시점에 create-zever_m_travel_i로 값이 들어옵니다. 그 값을 Log저장을 위한 내부테이블

        lt_travel_log에 값을 넣습니다. Operation필드에는 'CREATE'를 넣어주고 log_id필드에는

        cl_system_uuid=>create_uuid_x16_static( ) 메소드를 사용하여 값을 넣어줍니다.

    3. Booking정보가 생성되면 저장시점에 create-zever_m_booking_i로 값이 들어옵니다. 그 값을 Log저장을 위한 내부테이블

        lt_booking_log에 값을 넣습니다. Booking도 마찬가지로 Operation필드에는 'CREATE'를 넣어주고 log_id필드에는

        cl_system_uuid=>create_uuid_x16_static( ) 메소드를 사용하여 값을 넣어줍니다.

     

     

    다음은 데이터를 수정했을때의 로직입니다.

    4. Travel정보가 변경되면 저장시점에 update-zever_m_travel_i로 값이 들어옵니다. Operation 필드에 'UPDATE'라고

        입력하는것 외에 생성때와 로직은 동일합니다.

    5. Booking정보가 변경되면 저장시점에 update-zever_m_booking_i로 값이 들어옵니다. Booking도 마찬가지로 Operation

        필드에 'UPDATE'라고 입력하는것 외에 생성때와 로직은 동일합니다.

     

     

    마지막으로 데이터를 삭제했을때의 로직입니다. 삭제때는 우리가 받는 파라미터에 모든필드의 값이 넘어오지 않고 오직 Key필드만 넘어옵니다. 그래서 Log테이블에도 Entity의 Key필드만 저장되도록 했습니다.

    6. Travel정보가 삭제되면 저장시점에 delete-zever_m_travel_i로 값이 들어옵니다. Operation 필드에 'DELETE'라고

        입력하는것 외에 생성때와 로직은 동일합니다.

    7. Booking정보가 삭제되면 저장시점에 delete-zever_m_booking_i로 값이 들어옵니다. Booking도 마찬가지로 Operation

        필드에 'DELETE'라고 입력하는것 외에 생성때와 로직은 동일합니다.

    8. 생성 or 수정 or 삭제가 되어 내부테이블에 값이 존재하면 실제 Log테이블에 데이터를 저장합니다.

     

     

    이제 테스트를 해보겠습니다. App을 실행한 후 신규 Travel ID를 생성해 보겠습니다. Booking정보는 10번, 20번 두개를 추가하고 저장버튼을 클릭합니다. 

     

     

    ZEVER_TRAVEL_L 테이블입니다. 한건이 저장되었습니다.

     

     

    ZEVER_BOOKING_L 테이블입니다. 두건이 저장되었습니다.

     

     

    수정 및 삭제도 한번씩 해보면서 Log가 잘 저장되는지 테스트 해보시기 바랍니다.

     

    아래는 save_modified의 전체소스 입니다.

      METHOD save_modified.
    
        DATA : lt_travel_log   TYPE TABLE OF zever_travel_l,
               lt_booking_log  TYPE TABLE OF zever_booking_l.
    
    *- CREATE
        IF create-zever_m_travel_i IS NOT INITIAL.
          LOOP AT create-zever_m_travel_i INTO DATA(ls_create_travel).
            APPEND INITIAL LINE TO lt_travel_log ASSIGNING FIELD-SYMBOL(<fs_travel_log>).
            <fs_travel_log>-operation     = 'CREATE'.
            <fs_travel_log>-travel_id     = ls_create_travel-TravelId.
            <fs_travel_log>-agency_id     = ls_create_travel-AgencyId.
            <fs_travel_log>-customer_id   = ls_create_travel-CustomerId.
            <fs_travel_log>-begin_date    = ls_create_travel-BeginDate.
            <fs_travel_log>-end_date      = ls_create_travel-EndDate.
            <fs_travel_log>-booking_fee   = ls_create_travel-BookingFee.
            <fs_travel_log>-total_price   = ls_create_travel-TotalPrice.
            <fs_travel_log>-currency_code = ls_create_travel-CurrencyCode.
            <fs_travel_log>-description   = ls_create_travel-Description.
            <fs_travel_log>-status        = ls_create_travel-Status.
            <fs_travel_log>-createdby     = cl_abap_context_info=>get_user_technical_name( ).
            GET TIME STAMP FIELD <fs_travel_log>-createdat.
            TRY.
                <fs_travel_log>-log_id = cl_system_uuid=>create_uuid_x16_static( ).
              CATCH cx_uuid_error.
            ENDTRY.
          ENDLOOP.
        ENDIF.
    
        IF create-zever_m_booking_i IS NOT INITIAL.
          LOOP AT create-zever_m_booking_i INTO DATA(ls_create_booking).
            APPEND INITIAL LINE TO lt_booking_log ASSIGNING FIELD-SYMBOL(<fs_booking_log>).
            <fs_booking_log>-operation     = 'CREATE'.
            <fs_booking_log>-travel_id     = ls_create_booking-TravelId.
            <fs_booking_log>-booking_id    = ls_create_booking-BookingId.
            <fs_booking_log>-booking_date  = ls_create_booking-BookingDate.
            <fs_booking_log>-customer_id   = ls_create_booking-CustomerId.
            <fs_booking_log>-carrier_id    = ls_create_booking-CarrierId.
            <fs_booking_log>-connection_id = ls_create_booking-ConnectionId.
            <fs_booking_log>-flight_date   = ls_create_booking-FlightDate.
            <fs_booking_log>-flight_price  = ls_create_booking-FlightPrice.
            <fs_booking_log>-currency_code = ls_create_booking-CurrencyCode.
            <fs_booking_log>-createdby     = cl_abap_context_info=>get_user_technical_name( ).
            GET TIME STAMP FIELD <fs_booking_log>-createdat.
            TRY.
                <fs_booking_log>-log_id = cl_system_uuid=>create_uuid_x16_static( ).
              CATCH cx_uuid_error.
            ENDTRY.
          ENDLOOP.
        ENDIF.
    
    *- UPDATE
        IF update-zever_m_travel_i IS NOT INITIAL.
          LOOP AT update-zever_m_travel_i INTO DATA(ls_update_travel).
            APPEND INITIAL LINE TO lt_travel_log ASSIGNING <fs_travel_log>.
            <fs_travel_log>-operation     = 'UPDATE'.
            <fs_travel_log>-travel_id     = ls_update_travel-TravelId.
            <fs_travel_log>-agency_id     = ls_update_travel-AgencyId.
            <fs_travel_log>-customer_id   = ls_update_travel-CustomerId.
            <fs_travel_log>-begin_date    = ls_update_travel-BeginDate.
            <fs_travel_log>-end_date      = ls_update_travel-EndDate.
            <fs_travel_log>-booking_fee   = ls_update_travel-BookingFee.
            <fs_travel_log>-total_price   = ls_update_travel-TotalPrice.
            <fs_travel_log>-currency_code = ls_update_travel-CurrencyCode.
            <fs_travel_log>-description   = ls_update_travel-Description.
            <fs_travel_log>-status        = ls_update_travel-Status.
            <fs_travel_log>-createdby = cl_abap_context_info=>get_user_technical_name( ).
            GET TIME STAMP FIELD <fs_travel_log>-createdat.
            TRY.
                <fs_travel_log>-log_id = cl_system_uuid=>create_uuid_x16_static( ).
              CATCH cx_uuid_error.
            ENDTRY.
          ENDLOOP.
        ENDIF.
    
        IF update-zever_m_booking_i IS NOT INITIAL.
          LOOP AT update-zever_m_booking_i INTO DATA(ls_update_booking).
            APPEND INITIAL LINE TO lt_booking_log ASSIGNING <fs_booking_log>.
            <fs_booking_log>-operation     = 'UPDATE'.
            <fs_booking_log>-travel_id     = ls_update_booking-TravelId.
            <fs_booking_log>-booking_id    = ls_update_booking-BookingId.
            <fs_booking_log>-booking_date  = ls_update_booking-BookingDate.
            <fs_booking_log>-customer_id   = ls_update_booking-CustomerId.
            <fs_booking_log>-carrier_id    = ls_update_booking-CarrierId.
            <fs_booking_log>-connection_id = ls_update_booking-ConnectionId.
            <fs_booking_log>-flight_date   = ls_update_booking-FlightDate.
            <fs_booking_log>-flight_price  = ls_update_booking-FlightPrice.
            <fs_booking_log>-currency_code = ls_update_booking-CurrencyCode.
            <fs_booking_log>-createdby     = cl_abap_context_info=>get_user_technical_name( ).
            GET TIME STAMP FIELD <fs_booking_log>-createdat.
            TRY.
                <fs_booking_log>-log_id = cl_system_uuid=>create_uuid_x16_static( ).
              CATCH cx_uuid_error.
            ENDTRY.
          ENDLOOP.
        ENDIF.
    
    *- DELETE
        IF delete-zever_m_travel_i IS NOT INITIAL.
          LOOP AT delete-zever_m_travel_i INTO DATA(ls_delete_travel).
            APPEND INITIAL LINE TO lt_travel_log ASSIGNING <fs_travel_log>.
            <fs_travel_log>-operation = 'DELETE'.
            <fs_travel_log>-travel_id = ls_delete_travel-TravelId.
            <fs_travel_log>-createdby = cl_abap_context_info=>get_user_technical_name( ).
            GET TIME STAMP FIELD <fs_travel_log>-createdat.
            TRY.
                <fs_travel_log>-log_id = cl_system_uuid=>create_uuid_x16_static( ).
              CATCH cx_uuid_error.
            ENDTRY.
          ENDLOOP.
        ENDIF.
    
        IF delete-zever_m_booking_i IS NOT INITIAL.
          LOOP AT delete-zever_m_booking_i INTO DATA(ls_delete_booking).
            APPEND INITIAL LINE TO lt_booking_log ASSIGNING <fs_booking_log>.
            <fs_booking_log>-operation  = 'DELETE'.
            <fs_booking_log>-travel_id  = ls_delete_booking-TravelId.
            <fs_booking_log>-booking_id = ls_delete_booking-BookingId.
            <fs_booking_log>-createdby     = cl_abap_context_info=>get_user_technical_name( ).
            GET TIME STAMP FIELD <fs_booking_log>-createdat.
            TRY.
                <fs_booking_log>-log_id = cl_system_uuid=>create_uuid_x16_static( ).
              CATCH cx_uuid_error.
            ENDTRY.
          ENDLOOP.
        ENDIF.
    
        IF lt_travel_log IS NOT INITIAL.
          MODIFY zever_travel_l FROM TABLE @lt_travel_log.
        ENDIF.
    
        IF lt_booking_log IS NOT INITIAL.
          MODIFY zever_booking_l FROM TABLE @lt_booking_log.
        ENDIF.
      ENDMETHOD.
    반응형

    'RAP' 카테고리의 다른 글

    RAP 16 - MANAGED REPORT : DETERMINATION  (0) 2026.08.31
    RAP 15 - MANAGED REPORT : FEATURE CONTROL  (0) 2026.08.24
    RAP 14 - MANAGED REPORT : ACTION  (0) 2026.08.04
    RAP 13 - MANAGED REPORT : VALIDATION  (0) 2026.08.03
    RAP 12 - MANAGED REPORT : EML  (0) 2026.07.26