본문 바로가기
ABAP

[ABAP] 구시스템 참조하여 데이터 가져오기

by 키노s 2025. 6. 28.

 

이번은 PO를 마이그레이션 하는데 엑셀을 만들어야 하는 불편함을 없애고자 

직접 구시스템에서 가져와서 생성하는 프로그램입니다.

 

가져오기

구시스템의 PO 테이블을 가져오는 기능입니다.

가져온 후 미리 생성된 테이블에 저장합니다.

 

생성하기
저장된 테이블을 바탕으로 ALV에 보여준 후 선택하여 생성하게 됩니다.

 

 

생성하는 로직은 엑셀을 참조하는 것과 별반 다른것이 없습니다.

 

그래서 타SAP에서 가져오는 것을 코딩하겠습니다.

먼저 어떤정보를 가져올지랑 초기값 세팅하는 부분입니다.

FORM get_data_from_ecc.
  "--------------------------------------------------------------------
  " 1. 필수 입력 값 검증
  "--------------------------------------------------------------------
  IF s_bukrs[] IS INITIAL.
    MESSAGE '회사코드를 입력해주세요.' TYPE 'E'.
    LEAVE LIST-PROCESSING.
  ENDIF.

  IF s_aedat[] IS INITIAL.
    MESSAGE '생성일을 입력해주세요.' TYPE 'E'.
    LEAVE LIST-PROCESSING.
  ENDIF.

  "데이터 일괄 처리(Chunk Processing)를 위한 카운트 변수 선언
  DATA: lv_total TYPE i, lv_cnt TYPE i, lv_mod TYPE i,
        lv_total0 TYPE i, lv_cnt0 TYPE i, lv_mod0 TYPE i,
        lv_total1 TYPE i, lv_cnt1 TYPE i, lv_mod1 TYPE i,
        lv_total2 TYPE i, lv_cnt2 TYPE i, lv_mod2 TYPE i,
        lv_total3 TYPE i, lv_cnt3 TYPE i, lv_mod3 TYPE i,
        lv_total4 TYPE i, lv_cnt4 TYPE i, lv_mod4 TYPE i,
        lv_total5 TYPE i, lv_cnt5 TYPE i, lv_mod5 TYPE i,
        lv_total6 TYPE i, lv_cnt6 TYPE i, lv_mod6 TYPE i.

  "--------------------------------------------------------------------
  " 2. 구매 오더 헤더(EKKO) 데이터 추출
  "--------------------------------------------------------------------
  IF p_only = 'X'. "선택: 이미 Z-Table에 있는 PO 목록만 대상으로 추출
    SELECT ebeln
      INTO CORRESPONDING FIELDS OF TABLE @gt_eccpo
      FROM zmigpo_ecc.

    SORT gt_eccpo.
    DELETE ADJACENT DUPLICATES FROM gt_eccpo.

    lv_total = lines( gt_eccpo ).
    LOOP AT gt_eccpo INTO gs_eccpo.
      lv_cnt = lv_cnt + 1.
      lv_total = lv_total - 1.
      lv_mod = lv_total MOD 5000.

      gt_ebeln = VALUE #( BASE gt_ebeln ( ebeln = gs_eccpo-ebeln ) ).

      "5000건씩 묶어서 RFC 호출
      IF lv_mod = 0 OR lv_total = 0.
        PERFORM get_material_data_from_ecc USING 'EKKO'.
        CLEAR gt_ebeln.
      ENDIF.
    ENDLOOP.

  ELSE. "선택화면의 조건(회사코드, 생성일)으로 대상 PO 추출
    PERFORM get_material_data_from_ecc USING 'EKKO'.
  ENDIF.

  "--------------------------------------------------------------------
  " 3. 구매 오더 품목/납품/계정 (EKPO/EKET/EKKN) 데이터 추출
  "--------------------------------------------------------------------
  lv_total = lines( gt_zmigpoekko ).
  LOOP AT gt_zmigpoekko INTO gs_zmigpoekko.
    lv_cnt = lv_cnt + 1.
    lv_total = lv_total - 1.
    lv_mod = lv_total MOD 5000.

    gt_ebeln = VALUE #( BASE gt_ebeln ( ebeln = gs_zmigpoekko-ebeln ) ).

    "5000건씩 묶어서 관련 테이블 데이터 추출
    IF lv_mod = 0 OR lv_total = 0.
      PERFORM get_material_data_from_ecc USING 'EKPO'.
      PERFORM get_material_data_from_ecc USING 'EKET'.
      PERFORM get_material_data_from_ecc USING 'EKKN'.
      CLEAR gt_ebeln.
    ENDIF.
  ENDLOOP.

  "--------------------------------------------------------------------
  " 4. 가격 조건(KONV) 데이터 추출
  "--------------------------------------------------------------------
  
  "--------------------------------------------------------------------
  " 5. 예약(RESB) 데이터 추출 (외주/하청 품목 대상)
  "--------------------------------------------------------------------
  
  "--------------------------------------------------------------------
  " 6. 주소(ADRC) 데이터 추출
  "--------------------------------------------------------------------
  
  "--------------------------------------------------------------------
  " 7. 구매 오더 이력(EKBE) 데이터 추출
  "--------------------------------------------------------------------
  
  "--------------------------------------------------------------------
  " 8. 서비스(ESLH/ESLL/ESKL) 데이터 추출 (서비스 품목 대상)
  "--------------------------------------------------------------------
  
"--------------------------------------------------------------------
  " 9. WBS 및 공정(PRPS/AFVC) 데이터 추출 (프로젝트 관련)
  "--------------------------------------------------------------------
  
  "--------------------------------------------------------------------
  " 10. 추출한 모든 데이터를 Z-Table에 저장
  "--------------------------------------------------------------------
  PERFORM save_ecc_data.
ENDFORM.

 

 

FORM get_material_data_from_ecc USING pv_tabname.
  "--------------------------------------------------------------------
  " 1. RFC 호출을 위한 변수 선언
  "--------------------------------------------------------------------
  DATA: lt_data    TYPE TABLE OF zcom3000, "RFC로부터 받은 Raw 데이터
        lt_options LIKE TABLE OF /bods/rfc_db_opt, "동적 WHERE 조건절
        lt_fields  LIKE TABLE OF rfc_db_fld.  "추출할 필드 리스트

  DATA(lv_fdate) = s_aedat-low.
  DATA(lv_tdate) = s_aedat-high.
  IF lv_tdate IS INITIAL.
    lv_tdate = sy-datum.
  ENDIF.

  "--------------------------------------------------------------------
  " 2. 테이블 이름(pv_tabname)에 따라 동적으로 WHERE 조건 생성
  "--------------------------------------------------------------------
  CASE pv_tabname.
    WHEN 'EKKO'. "구매 오더 헤더(EKKO) 조건 생성
      IF p_only = 'X'. "Z-Table의 PO 목록만 대상일 경우
        lt_options = VALUE #( FOR wa IN gt_ebeln ( text = |OR EBELN = '{ wa-ebeln }' | ) ).
      ELSE. "선택 화면의 조건으로 대상 선정
        lt_options = VALUE #( ( text = |( AEDAT BETWEEN '{ lv_fdate }' AND '{ lv_tdate }' )| ) ).
        IF s_bukrs[] IS NOT INITIAL.
          lt_options = VALUE #( BASE lt_options ( text = |AND BUKRS IN @s_bukrs | ) ).
        ENDIF.
        IF s_ebeln[] IS NOT INITIAL.
          lt_options = VALUE #( BASE lt_options ( text = |AND EBELN IN @s_ebeln | ) ).
        ENDIF.
        IF s_lifnr[] IS NOT INITIAL.
          lt_options = VALUE #( BASE lt_options ( text = |AND LIFNR IN @s_lifnr | ) ).
        ENDIF.
      ENDIF.

    WHEN 'EKPO'. "구매 오더 품목(EKPO) 조건 생성
      IF gt_ebeln[] IS NOT INITIAL.
        lt_options = VALUE #( FOR wa IN gt_ebeln ( text = |OR EBELN = '{ wa-ebeln }' | ) ).
        IF s_werks[] IS NOT INITIAL.
          lt_options = VALUE #( BASE lt_options ( text = |AND WERKS IN @s_werks | ) ).
        ENDIF.
      ENDIF.

    WHEN 'EKET' OR 'EKKN' OR 'RESB' OR 'ESLH' OR 'EKBE'. "PO 번호가 Key인 하위 테이블 조건
      IF gt_ebeln[] IS NOT INITIAL.
        lt_options = VALUE #( FOR wa IN gt_ebeln ( text = |OR EBELN = '{ wa-ebeln }' | ) ).
        IF pv_tabname = 'EKBE'. "구매이력은 입고(E)만 대상
           lt_options = VALUE #( BASE lt_options ( text = |AND ( BEWTP = 'E' )| ) ).
        ENDIF.
      ENDIF.

    WHEN 'ESLL' OR 'ESKL'. "서비스 라인/계정 (Packno 기준)
      IF gt_packno[] IS NOT INITIAL.
        lt_options = VALUE #( FOR wa IN gt_packno ( text = |OR PACKNO = '{ wa-packno }'| ) ).
      ENDIF.

    WHEN 'PRPS'. "WBS 마스터 (PSPNR 기준)
      IF gt_ps_psp_pnr[] IS NOT INITIAL.
        lt_options = VALUE #( FOR wa IN gt_ps_psp_pnr ( text = |OR PSPNR = '{ wa-ps_psp_pnr }'| ) ).
      ENDIF.

    WHEN 'AFVC'. "공정 데이터 (AUFPL, APLZL 기준)
      IF gt_nplnr[] IS NOT INITIAL.
        lt_options = VALUE #( FOR wa IN gt_nplnr ( text = |OR ( AUFPL = '{ wa-aufpl }' AND APLZL = '{ wa-aplzl }' )| ) ).
      ENDIF.

    WHEN 'ADRC'. "주소 마스터 (Address Number 기준)
      IF gt_adrn2[] IS NOT INITIAL.
        DELETE ADJACENT DUPLICATES FROM gt_adrn2.
        lt_options = VALUE #( FOR wa IN gt_adrn2 ( text = |OR ADDRNUMBER = '{ wa-addrnumber }'| ) ).
      ENDIF.

    WHEN 'KONV'. "가격 조건 (KNUMV 기준)
      IF gt_konv[] IS NOT INITIAL.
        lt_options = VALUE #( FOR wa IN gt_konv ( text = |OR KNUMV = '{ wa-knumv }'| ) ).
      ENDIF.

    WHEN 'STXH' OR 'STXL'. "텍스트 테이블
      " ... (텍스트 테이블용 조건 생성 로직) ...

  ENDCASE.

  "WHERE 조건이 없는 경우 RFC 호출 방지
  IF lt_options IS INITIAL.
    RETURN.
  ENDIF.
  
  "첫번째 'OR'을 '(' 로 변경하여 올바른 괄호 구조의 WHERE 절 생성
  READ TABLE lt_options INDEX 1.
  IF sy-subrc = 0.
    REPLACE 'OR' IN lt_options[ 1 ]-text WITH '('.
    MODIFY lt_options INDEX 1.
    CONCATENATE lt_options[ lines( lt_options ) ]-text ')' INTO lt_options[ lines( lt_options ) ]-text.
  ENDIF.

  "--------------------------------------------------------------------
  " 3. 원격 시스템(ECC)의 테이블 데이터를 RFC로 호출
  "--------------------------------------------------------------------
  CALL FUNCTION 'ZCOMM_RFC_READ_TABLE_CALL'
    EXPORTING
      query_table = CONV tabname( pv_tabname )
      delimiter   = p_delim
      dest        = p_dest
    TABLES
      options     = lt_options
      fields      = lt_fields
      data        = lt_data.

  "--------------------------------------------------------------------
  " 4. 수신된 Raw 데이터를 파싱하여 Target 인터널 테이블에 저장
  "--------------------------------------------------------------------
  "저장할 Target 테이블과 Work Area의 이름을 동적으로 생성
  DATA(lv_wa_name)   = |GS_ZMIGPO{ pv_tabname }|.
  DATA(lv_itab_name) = |GT_ZMIGPO{ pv_tabname }|.

  "Field Symbol을 이용해 동적으로 Work Area와 테이블 할당
  FIELD-SYMBOLS: <fs_wa>   TYPE any,
                 <fs_itab> TYPE STANDARD TABLE,
                 <fs_comp> TYPE any.
  ASSIGN (lv_wa_name) TO <fs_wa>.
  ASSIGN (lv_itab_name) TO <fs_itab>.
  
  "수신된 데이터 한 줄(ls_data)을 LOOP
  LOOP AT lt_data INTO DATA(ls_data).
    DATA(lv_raw_string) = ls_data-wa.
    
    "해당 테이블의 필드 목록을 LOOP
    LOOP AT lt_fields INTO DATA(ls_fields).
      "문자열을 구분자(p_delim)로 잘라내어 필드 값을 분리
      SPLIT lv_raw_string AT p_delim INTO DATA(lv_value) lv_raw_string.
      
      "분리된 값을 Target Work Area의 해당 필드에 동적으로 할당
      ASSIGN COMPONENT ls_fields-fieldname OF STRUCTURE <fs_wa> TO <fs_comp>.
      IF sy-subrc = 0.
        <fs_comp> = lv_value.
      ENDIF.
    ENDLOOP.
    
    "한 줄이 완성된 Work Area를 Target 인터널 테이블에 추가
    APPEND <fs_wa> TO <fs_itab>.
  ENDLOOP.
ENDFORM.

 

이제 실제로 구시스템과 연결하는 펑션입니다.

FUNCTION ZCOMM_RFC_READ_TABLE_CALL.
*"----------------------------------------------------------------------
*"*"Local Interface:
*"  IMPORTING
*"     REFERENCE(QUERY_TABLE) LIKE  DD02L-TABNAME
*"     REFERENCE(QUERY_TABLE_S4H) LIKE  DD02L-TABNAME OPTIONAL
*"     REFERENCE(DELIMITER) TYPE  SONV-FLAG DEFAULT '|'
*"     REFERENCE(NO_DATA) TYPE  SONV-FLAG DEFAULT SPACE
*"     REFERENCE(ROWSKIPS) TYPE  SOID-ACCNT DEFAULT 0
*"     REFERENCE(ROWCOUNT) TYPE  SOID-ACCNT DEFAULT 0
*"     REFERENCE(DEST) TYPE  RFCDEST DEFAULT 'R3PCLNT100'
*"     REFERENCE(MODE) TYPE  SONV-FLAG DEFAULT ''
*"  EXPORTING
*"     VALUE(E_DBCNT) TYPE  SY-DBCNT
*"  TABLES
*"      OPTIONS STRUCTURE  RFC_DB_OPT
*"      FIELDS STRUCTURE  RFC_DB_FLD
*"      DATA STRUCTURE  ZCOM3000
*"----------------------------------------------------------------------

  "--------------------------------------------------------------------
  " 1. 안전장치: Standard Table을 'M'(수정) 모드로 실행 시 확인 팝업
  "--------------------------------------------------------------------
  CASE QUERY_TABLE+0(1).
    WHEN 'Y' OR 'Z'. "Y 또는 Z로 시작하는 Custom Table은 통과
    WHEN OTHERS.    "Standard Table일 경우
      IF MODE = 'M'. "수정 모드일 때만
        PERFORM POPUP_CONFIRM
          USING '확인'
                'Standard table 업데이트를 요청하셨습니다. 정말 실행하시겠습니까?'
                DATA(LV_ANSWER).
        CHECK LV_ANSWER = '1'. "사용자가 '예'를 선택하지 않으면 중단
      ENDIF.
  ENDCASE.

  "--------------------------------------------------------------------
  " 2. 동적 데이터 객체 생성
  "--------------------------------------------------------------------
  "IMPORTING으로 받은 테이블 이름(QUERY_TABLE)으로 동적으로 인터널 테이블과 Work Area 생성
  "이를 통해 어떤 테이블 구조든 유연하게 처리 가능
  CREATE DATA GT_DATA TYPE TABLE OF (QUERY_TABLE).
  ASSIGN GT_DATA->* TO <FS_TABLE>.

  CREATE DATA GS_DATA TYPE (QUERY_TABLE).
  ASSIGN GS_DATA->* TO <WA_TABLE>.

  "--------------------------------------------------------------------
  " 3. 원격 RFC 호출하여 데이터 조회
  "--------------------------------------------------------------------
  "실제 데이터 조회는 원격지의 'ZCOMM_RFC_READ_TABLE' FM이 수행
  CALL FUNCTION 'ZCOMM_RFC_READ_TABLE'
    DESTINATION DEST
    EXPORTING
      QUERY_TABLE          = QUERY_TABLE
      DELIMITER            = DELIMITER
      NO_DATA              = NO_DATA
      ROWSKIPS             = ROWSKIPS
      ROWCOUNT             = ROWCOUNT
    IMPORTING
      UNICODE              = GV_UNICODE
    TABLES
      OPTIONS              = OPTIONS
      FIELDS               = FIELDS
      DATA                 = DATA
    EXCEPTIONS
      TABLE_NOT_AVAILABLE  = 1
      TABLE_WITHOUT_DATA   = 2
      OPTION_NOT_VALID     = 3
      FIELD_NOT_VALID      = 4
      NOT_AUTHORIZED       = 5
      DATA_BUFFER_EXCEEDED = 6
      OTHERS               = 7.

  "--------------------------------------------------------------------
  " 4. RFC 호출 결과에 따른 분기 처리
  "--------------------------------------------------------------------
  IF SY-SUBRC <> 0. "RFC 호출 실패 시 오류 처리
    CASE SY-SUBRC.
      WHEN 1. MESSAGE '잘못된 테이블 이름입니다.' TYPE 'S' DISPLAY LIKE 'E'.
      WHEN 2. MESSAGE '데이터가 없습니다.' TYPE 'S'.
      WHEN 3. MESSAGE '사용할 수 없는 QUERY입니다.' TYPE 'S' DISPLAY LIKE 'E'.
      WHEN 4. MESSAGE '잘못된 필드입니다.' TYPE 'S' DISPLAY LIKE 'E'.
      WHEN 5. MESSAGE '조회 권한이 없습니다.' TYPE 'S' DISPLAY LIKE 'E'.
      WHEN 6. MESSAGE '테이블 길이가 3000자를 초과합니다.' TYPE 'S' DISPLAY LIKE 'E'.
      WHEN OTHERS. MESSAGE '알 수 없는 오류입니다.' TYPE 'S' DISPLAY LIKE 'E'.
    ENDCASE.

  ELSE. "RFC 호출 성공 시 후속 처리
    I_DATA[]   = DATA[].
    I_FIELDS[] = FIELDS[].

    "MODE가 지정된 경우에만 데이터 변환 및 후속 작업 수행
    CHECK MODE IS NOT INITIAL.

    "수신된 Raw 데이터를 파싱하여 동적으로 생성된 인터널 테이블(<FS_TABLE>)에 채움
    PERFORM CONVERT_TABLE_DATA
      USING     DATA(LV_LINES)  "처리된 라인 수
                DELIMITER.      "구분자

    "수행 모드(MODE)에 따라 다른 작업 수행
    CASE MODE.
      WHEN 'M'.  "Modify Mode: 로컬 DB 테이블 업데이트
        IF LV_LINES > 0.
          IF QUERY_TABLE_S4H IS NOT INITIAL. "S4H용 테이블 이름이 지정된 경우
            MODIFY (QUERY_TABLE_S4H) FROM TABLE <FS_TABLE>.
          ELSE.
            MODIFY (QUERY_TABLE) FROM TABLE <FS_TABLE>.
          ENDIF.
          IF SY-SUBRC = 0.
            MESSAGE S001(00) WITH LV_LINES '건의 데이터를 업데이트하였습니다.'.
          ENDIF.
        END

 

이제 구시스템의 펑션입니다.

FUNCTION ZCOMM_RFC_READ_TABLE.
*"----------------------------------------------------------------------
*"*"Local Interface:
*"  IMPORTING
*"     VALUE(QUERY_TABLE) LIKE  DD02L-TABNAME
*"     VALUE(DELIMITER) LIKE  SONV-FLAG DEFAULT SPACE
*"     VALUE(NO_DATA) LIKE  SONV-FLAG DEFAULT SPACE
*"     VALUE(ROWSKIPS) LIKE  SOID-ACCNT DEFAULT 0
*"     VALUE(ROWCOUNT) LIKE  SOID-ACCNT DEFAULT 0
*"  EXPORTING
*"     VALUE(UNICODE) TYPE  CRMUCSYS
*"  TABLES
*"      OPTIONS STRUCTURE  RFC_DB_OPT
*"      FIELDS STRUCTURE  RFC_DB_FLD
*"      DATA STRUCTURE  ZCOM3000
*"  EXCEPTIONS
*"      TABLE_NOT_AVAILABLE
*"      TABLE_WITHOUT_DATA
*"      OPTION_NOT_VALID
*"      FIELD_NOT_VALID
*"      NOT_AUTHORIZED
*"      DATA_BUFFER_EXCEEDED
*"----------------------------------------------------------------------

  "--------------------------------------------------------------------
  " 1. 시스템 환경 및 조회 권한 확인
  "--------------------------------------------------------------------
  "현재 시스템이 유니코드 시스템인지 확인하여 EXPORTING 파라미터에 설정
  CASE SYST-SYSID.
    WHEN 'R3T' OR 'R3P' OR 'R3Q'. "Non-Unicode 시스템 목록
      UNICODE = ''.
    WHEN OTHERS.
      UNICODE = 'X'.
  ENDCASE.

  "사용자가 해당 테이블을 조회할 권한(S_TABU_DIS)이 있는지 확인
  CALL FUNCTION 'VIEW_AUTHORITY_CHECK'
    EXPORTING
      VIEW_ACTION          = 'S' "조회(Select)
      VIEW_NAME            = QUERY_TABLE
    EXCEPTIONS
      NO_AUTHORITY         = 2
      OTHERS               = 1.
  IF SY-SUBRC = 2.
    RAISE NOT_AUTHORIZED. "권한 없음 예외 발생
  ELSEIF SY-SUBRC = 1.
    RAISE TABLE_NOT_AVAILABLE. "테이블 없음 예외 발생
  ENDIF.

  "--------------------------------------------------------------------
  " 2. 조회 대상 테이블의 구조 정보 읽기
  "--------------------------------------------------------------------
  DATA: TABLE_STRUCTURE TYPE STANDARD TABLE OF DFIES,
        TABLE_TYPE      TYPE DD02V-TABCLASS.

  "ABAP Dictionary에서 테이블의 필드 정보를 가져옴
  CALL FUNCTION 'DDIF_FIELDINFO_GET'
    EXPORTING
      TABNAME   = QUERY_TABLE
    IMPORTING
      DDOBJTYPE = TABLE_TYPE
    TABLES
      DFIES_TAB = TABLE_STRUCTURE
    EXCEPTIONS
      OTHERS    = 1.
  IF SY-SUBRC <> 0.
    RAISE TABLE_NOT_AVAILABLE. "구조 정보 읽기 실패 시 예외 발생
  ENDIF.

  "--------------------------------------------------------------------

 

이제 가져온 데이터를 저장해야겠죠~

소스를 보시면 알수 있듯이.. 구조를 동일하게 맞춰서 미리 생성해 놓습니다.

FORM save_ecc_data.
  "--------------------------------------------------------------------
  " 각 인터널 테이블의 데이터를 해당하는 Z-Table에 저장 (UPDATE or INSERT)
  " 주의: 현재 로직은 각 테이블 저장 후 바로 COMMIT WORK를 수행합니다.
  "       -> 이상적으로는 모든 MODIFY가 성공한 후 마지막에 한 번만 COMMIT 하는 것이
  "          데이터 정합성 측면에서 더 안전합니다.
  "--------------------------------------------------------------------

  "구매 오더 헤더(EKKO) 데이터 저장
  IF gt_zmigpoekko IS NOT INITIAL.
    MODIFY zmigpoekko FROM TABLE gt_zmigpoekko.
    IF sy-subrc = 0. COMMIT WORK. ENDIF.
  ENDIF.

  "구매 오더 품목(EKPO) 데이터 저장
  IF gt_zmigpoekpo IS NOT INITIAL.
    MODIFY zmigpoekpo FROM TABLE gt_zmigpoekpo.
    IF sy-subrc = 0. COMMIT WORK. ENDIF.
  ENDIF.


... 정의된 테이블 등등 .....

  "모든 작업 완료 후 성공 메시지 표시
  MESSAGE s010(zmm1).
ENDFORM.

 

 

엄청 단순하죠~ 

개발이 다 그렇습니다. ^^;

 

도움이 되셨으면 좋겠습니다.