쌓고 쌓다

[ABAP] 엑셀 업로드와 BDC 프로그램 본문

SAP/ABAP

[ABAP] 엑셀 업로드와 BDC 프로그램

승민아 2025. 9. 15. 13:40
반응형

엑셀 파일을 선택하고 실행하면

 

엑셀 데이터를 읽어와 ABAP 데이터로 변환하고 ALV에 표시하며

 

이 데이터들을 가지고 BDC를 실행하여 데이터를 저장하는 과정을 만들어보자.

 

1. 파일 경로

파일 경로를 입력받기 위한 변수이다.

 

F4키를 눌러 파일 경로를 선택할 수 있도록 하자.

 

'ZCL_UTIL=>FILE_OPEN_DIALOG' 메소드를 사용해서

사용자가 파일을 선택할 수 있다.

 

선택한 파일의 경로는 위와 같은 문자열 형태로 PA_FILE 변수에 담긴다.

 

2. Excel 데이터 받기

위와 같은 엑셀 데이터를 업로드하여

 

 

 위처럼 ABAP 데이터로 변환해보자.

 

'ALSM_EXCEL_TO_INTERNAL_TABLE' 펑션을 사용하여

엑셀 파일 경로를 넘겨 데이터를 읽을 수 있다.

 

시작 COLUMN과 ROW는 엑셀에 표시된 열과 행을 보면 된다. 

 

I_BEGIN_COL : 데이터 시작 COLUMN으로 A열은 1열, B열은 2열, ...

I_BEGIN_ROW : 데이터 시작 ROW으로 1행, 2행, ...이다.

우린 첫 번째 1 ROW에는 컬럼명이 있으므로 2 ROW부터 데이터가 시작한다. 

I_END_COL : 데이터가 존재하는 마지막 COLUMN

I_END_ROW : 몇 개의 ROW까지 읽을 것 인가.

 

GT_ALSMEX의 변수 선언부이다.

 

'ALSM_EXCEL_TO_INTERNAL_TABLE' 펑션의 실행 결과로

받은 결과 테이블이다.

행과 열 위치 값과 그 위치에 어떤 값이 들어가 있는지 받은 상태이다.

 

값들은 C(50) 타입이며

우리는 이 데이터를 가지고 적절하게 가공하여 데이터를 사용하면 된다.

 

3. Excel 데이터 가공

ABAP 데이터로 바꿔 ALV에 뿌려주기 위해서 2개의 변수를 선언했다.

 

LS_PO_UPLOAD : 하나의 행을 위한 컬럼 데이터 8개를 모두 입력받으면 Internal Table에 APPEND할 변수

LS_ALSMEX : 엑셀에 금액과 수량 값으로 천 단위 구분자(,)가 포함되어 있을 수 있으므로 이것을 제거하는 과정에 필요

 

GT_ALSMEX 데이터를 루프문으로 가공하는 코드는 다음과 같다.

CASE문으로 COLUMN 위치에 맞춰 값들을 다뤘으며

'AT END OF' 구문을 통해 ROW 값이 바뀌기 전 마지막 행에서

Internal Table에 APPEND 하여 하나의 행을 완성한다.

 

엑셀 데이터 받고 가공하기 전체 코드

더보기

FORM CALL_SCREEN_100 .

  IF PA_EKORG IS INITIAL OR PA_EKGRP IS INITIAL OR PA_BUKRS IS INITIAL OR PA_FILE IS INITIAL.
    MESSAGE '필수값을 입력해주세요.' TYPE 'S' DISPLAY LIKE 'E'.
    RETURN.
  ENDIF.

  CALL FUNCTION 'ALSM_EXCEL_TO_INTERNAL_TABLE'
    EXPORTING
      FILENAME    PA_FILE
      I_BEGIN_COL " 데이터 시작 COLUMN
      I_BEGIN_ROW " 데이터 시작 ROW
      I_END_COL   " 데이터 끝 COLUMN
      I_END_ROW   1000 " 몇개의 ROW 읽을 것인가
    TABLES
      INTERN      GT_ALSMEX.

  DATA LS_PO_UPLOAD LIKE LINE OF GT_PO_UPLOAD" 하나의 ROW 읽기 완료했을때 APPEND용
         LS_ALSMEX    LIKE LINE OF GT_ALSMEX" 단가 및 수량 READ하고 변환용

  LOOP AT GT_ALSMEX ASSIGNING FIELD-SYMBOL(<FS_ALSMEX>).

    CASE <FS_ALSMEX>-COL.
      WHEN 1" 공급업체
        LS_PO_UPLOAD-LIFNR <FS_ALSMEX>-VALUE.
      WHEN 2" 자재코드
        LS_PO_UPLOAD-MATNR <FS_ALSMEX>-VALUE.
      WHEN 3" 수량
        REPLACE ALL OCCURRENCES OF ',' IN <FS_ALSMEX>-VALUE WITH SPACE" 수량의 천단위 표시 , 제거
        LS_PO_UPLOAD-MENGE <FS_ALSMEX>-VALUE.
      WHEN 4" 단위
        LS_PO_UPLOAD-MEINS <FS_ALSMEX>-VALUE.
      WHEN 5" 단가
        READ TABLE GT_ALSMEX INTO LS_ALSMEX WITH KEY ROW <FS_ALSMEX>-ROW COL 6.
        REPLACE ALL OCCURRENCES OF ',' IN LS_ALSMEX-VALUE WITH SPACE" 금액의 천단위표시 , 제거

        CALL FUNCTION 'BAPI_CURRENCY_CONV_TO_INTERNAL'
          EXPORTING
            AMOUNT_EXTERNAL      CONV BAPICURR-BAPICURR<FS_ALSMEX>-VALUE " C(50)을 CURR 금액 타입으로 변경
            CURRENCY             CONV TCURC-WAERSLS_ALSMEX-VALUE " C(50)을 WAERS 통화 타입으로 변경
            MAX_NUMBER_OF_DIGITS 11 " LENGTH
          IMPORTING
            AMOUNT_INTERNAL      LS_PO_UPLOAD-NETPR.
      WHEN 6" 통화
        LS_PO_UPLOAD-WAERS <FS_ALSMEX>-VALUE.
      WHEN 7" 플랜트
        LS_PO_UPLOAD-WERKS <FS_ALSMEX>-VALUE.
      WHEN 8" 저장위치
        LS_PO_UPLOAD-LGORT <FS_ALSMEX>-VALUE.
    ENDCASE.

    AT END OF ROW.
      APPEND LS_PO_UPLOAD TO GT_PO_UPLOAD.
      CLEAR LS_PO_UPLOAD.
    ENDAT.

  ENDLOOP.

  DATA LT_PO_UPLOAD_TEMP LIKE GT_PO_UPLOAD.
  LT_PO_UPLOAD_TEMP GT_PO_UPLOAD.

  SELECT A~LIFNRB~NAME1A~MATNRC~MAKTXA~MENGEA~MEINSA~NETPRA~NETPR * A~MENGE AS NETWRA~WAERSA~WERKSA~LGORT
    FROM @LT_PO_UPLOAD_TEMP AS A
    LEFT OUTER JOIN LFA1 AS ON LPADA~LIFNR10'0' B~LIFNR
    LEFT OUTER JOIN MAKT AS ON A~MATNR C~MATNR AND C~SPRAS @SY-LANGU
    ORDER BY A~LIFNR ASCENDING
    INTO CORRESPONDING FIELDS OF TABLE @GT_PO_UPLOAD.

  LOOP AT GT_PO_UPLOAD ASSIGNING FIELD-SYMBOL(<FS_PO_UPLOAD>).

    <FS_PO_UPLOAD>-LIGHT 2.

    IF <FS_PO_UPLOAD>-LIFNR IS INITIAL OR <FS_PO_UPLOAD>-NAME1 IS INITIAL.
      <FS_PO_UPLOAD>-LIGHT 1.
      <FS_PO_UPLOAD>-MESSAGE '올바른 공급업체를 입력해주세요.'.
    ENDIF.

    IF <FS_PO_UPLOAD>-MATNR IS INITIAL OR <FS_PO_UPLOAD>-MAKTX IS INITIAL.
      <FS_PO_UPLOAD>-LIGHT 1.
      <FS_PO_UPLOAD>-MESSAGE '올바른 자재를 입력해주세요.'.
    ENDIF.

    IF <FS_PO_UPLOAD>-MENGE <= OR <FS_PO_UPLOAD>-NETPR <= 0.
      <FS_PO_UPLOAD>-LIGHT 1.
      <FS_PO_UPLOAD>-MESSAGE '수량/단가에 0보다 큰 수를 입력해주세요.'.
    ENDIF.

  ENDLOOP.

  CALL SCREEN 100.
ENDFORM.

 

 

4. BDC 프로그램으로 가공한 Excel 데이터 업로드하기

이제 업로드 버튼을 눌렀을떄 Internal Tabel에 들어있는 값을 가지고

BDC 업로드를 돌려보자.

 

BDC는 구매 오더 생성 T Code인 ME21N를 기준으로 하겠다.

 

BDC 녹화는

T CODE : SHDB

를 통해서 하자.

 

녹화된 레코딩을 보고 BDC 프로그램을 작성하면 된다.

 

BDC 실행을 위한 변수이다.

BDC 프로그램의 실행 결과를 담은 메시지, BDC 실행 순서들을 담을 코드를 담는 변수이다.

 

루프문을 돌며 BDC 프로그램의 동작들을 넣어주면 되는데

 

AT NEW 부분은

구매오더 헤더 데이터의 공급업체, 구매조직, 구매그룹, 회사코드, 처럼

처음 한번 세팅해야 하는 부분이고

 

 

AT END부분은

품목 데이터를 다 넣고

최종적인 저장을 하는 BDC 코드를 넣고

BDC를 실행하는 부분이다.

 

 

그리고 AT NET와 AT END 사이의 부분은

품목 데이터처럼 반복적으로 값들을 넣어줘야 하는 부분이다.

 

 

PERFORM BDC_DYNPRO

레코딩 기록이다.

새로운 화면이 시작될 때마다 저렇게 프로그램명과 화면, 시작 값 'X'가 들어간 형태가 있다.

 

이 동작을 위한 코드들을 'BDC_DYNPRO' 펑션으로 따로 뺐다.

이 동작을 위해서 다음과 같이 서브루틴을 호출해 주면 된다.

 

 

 

PERFORM BDC_FIELD

레코딩 기록이다.

필드에 값을 넣을때마다, 또는 SAVE 버튼 또는 탭 버튼과 같은 OK_CODE가 동작할 때 코드들을 위해

필드이름과 필드 값을 넣어주면 된다.

 

BDC_OKCODE의 필드는 OK_CODE로 이해하면 된다.

 

필드에 값을 넣는 동작, OK_CODE 동작을 위해 다음과 같은 서브루틴을 호출하면 된다.

 

 

BDC 프로그램 동작 실행 코드

더보기

FORM PO_BDC_UPLOAD .

* 값을 입력할 필드들의 이름
  DATA LV_MATNR_FNAM TYPE STRING,
         LV_MENGE_FNAM TYPE STRING,
         LV_MEINS_FNAM TYPE STRING,
         LV_NETPR_FNAM TYPE STRING,
         LV_WERKS_FNAM TYPE STRING,
         LV_LGORT_FNAM TYPE STRING.

  DATA LV_ICON TYPE CHAR1.

  DATA LS_BDCMSGCOLL LIKE LINE OF GT_BDCMSGCOLL,
         LV_MSGTXT     TYPE STRING.

  DATA LS_OPT TYPE CTU_PARAMS" BDC 실행 옵션
  LS_OPT-DISMODE 'N'" A : 모든 화면, E : 에러발생시만, N : Not Display
  LS_OPT-UPDMODE ''" S : 동기, A : 비동기
  LS_OPT-DEFSIZE ''" 기본 윈도우 사이즈 설정, X : 예

  DATA LV_INDEX      TYPE N LENGTH 2" 몇번째 품목 데이터인지
         LV_NETPR_FVAL TYPE LENGTH 255" 금액 천단위 제거된 값
         LV_MENGE_FVAL TYPE LENGTH 255" 수량 천단위 제거된 값

  LOOP AT GT_PO_UPLOAD ASSIGNING FIELD-SYMBOL(<FS_PO_UPLOAD>).

    AT NEW LIFNR.
      LV_INDEX 1.
      PERFORM BDC_DYNPRO USING 'SAPLMEGUI' '0014'.
      PERFORM BDC_FIELD USING 'BDC_OKCODE' '=MEV4000BUTTON'.
      PERFORM BDC_FIELD USING 'MEPO_TOPLINE-SUPERFIELD' <FS_PO_UPLOAD>-LIFNR.


      PERFORM BDC_DYNPRO USING 'SAPLMEGUI' '0014'.
      PERFORM BDC_FIELD USING 'BDC_OKCODE' '=TABHDT1'" 조직 데이터 탭 누르기
      PERFORM BDC_FIELD USING 'MEPO1222-EKORG' PA_EKORG.
      PERFORM BDC_FIELD USING 'MEPO1222-EKGRP' PA_EKGRP.
      PERFORM BDC_FIELD USING 'MEPO1222-BUKRS' PA_BUKRS.

      PERFORM BDC_DYNPRO USING 'SAPLMEGUI' '0014'.
      PERFORM BDC_FIELD USING 'BDC_OKCODE' '=MEV4001BUTTON'. " 품목 탭 펼치는 버튼
      PERFORM BDC_FIELD USING 'MEPO1226-ZTERM' 'CV01'.
      PERFORM BDC_FIELD USING 'MEPO1226-WAERS' 'KRW'.
      PERFORM BDC_FIELD USING 'MEPO1226-WKURS' '1.00000'.

      PERFORM BDC_DYNPRO USING 'SAPLMEGUI' '0014'.
      PERFORM BDC_FIELD USING 'BDC_OKCODE' '=MESAVE'" 저장
    ENDAT.

    LV_MATNR_FNAM |MEPO1211-EMATN({ LV_INDEX })|.
    LV_MENGE_FNAM |MEPO1211-MENGE({ LV_INDEX })|.
    LV_MEINS_FNAM |MEPO1211-MEINS({ LV_INDEX })|.
    LV_NETPR_FNAM |MEPO1211-NETPR({ LV_INDEX })|.
    LV_WERKS_FNAM |MEPO1211-NAME1({ LV_INDEX })|.
    LV_LGORT_FNAM |MEPO1211-LGOBE({ LV_INDEX })|.

    WRITE <FS_PO_UPLOAD>-NETPR CURRENCY <FS_PO_UPLOAD>-WAERS TO LV_NETPR_FVAL" 통화 연결
    WRITE <FS_PO_UPLOAD>-MENGE UNIT <FS_PO_UPLOAD>-MEINS TO LV_MENGE_FVAL" 수량 단위 연결
    CONDENSE LV_NETPR_FVAL NO-GAPSLV_MENGE_FVAL NO-GAPS" C 타입에 수량, 통화 넣었을때 빈자리는 공백이 들어가기 때문에 제거

    PERFORM BDC_FIELD USING LV_MATNR_FNAM <FS_PO_UPLOAD>-MATNR.
    PERFORM BDC_FIELD USING LV_MENGE_FNAM LV_MENGE_FVAL.
    PERFORM BDC_FIELD USING LV_MEINS_FNAM <FS_PO_UPLOAD>-MEINS.
    PERFORM BDC_FIELD USING LV_NETPR_FNAM LV_NETPR_FVAL.
    PERFORM BDC_FIELD USING LV_WERKS_FNAM <FS_PO_UPLOAD>-WERKS.
    PERFORM BDC_FIELD USING LV_LGORT_FNAM <FS_PO_UPLOAD>-LGORT.
    LV_INDEX LV_INDEX + 1.

    AT END OF LIFNR" 마지막 공급업체 ROW일때 이 공급업체의 품목 데이터를 모두 넣었을테니 BDC 실행

      CALL TRANSACTION 'ME21N'
              USING GT_BDCDATA
              OPTIONS FROM LS_OPT
              MESSAGES INTO GT_BDCMSGCOLL.

      READ TABLE GT_BDCMSGCOLL INTO LS_BDCMSGCOLL WITH KEY MSGTYP 'S' MSGID '06' MSGNR '017'" PO 생성 완료 메시지 찾기
      IF SY-SUBRC 0.
        LV_ICON 3" 초록색 신호등
      ELSE.
        LV_ICON 1" 빨간색 신호등
      ENDIF.

      CALL FUNCTION 'MESSAGE_TEXT_BUILD'
        EXPORTING
          MSGID               LS_BDCMSGCOLL-MSGID
          MSGNR               LS_BDCMSGCOLL-MSGNR
          MSGV1               LS_BDCMSGCOLL-MSGV1
          MSGV2               LS_BDCMSGCOLL-MSGV2
          MSGV3               LS_BDCMSGCOLL-MSGV3
          MSGV4               LS_BDCMSGCOLL-MSGV4
        IMPORTING
          MESSAGE_TEXT_OUTPUT LV_MSGTXT.

*     처리한 공급업체 코드를 가지고 메시지, 신호등 값 할당
      LOOP AT GT_PO_UPLOAD ASSIGNING FIELD-SYMBOL(<FS_ALV_DATA>WHERE LIFNR <FS_PO_UPLOAD>-LIFNR.
        <FS_ALV_DATA>-EBELN LS_BDCMSGCOLL-MSGV2.
        <FS_ALV_DATA>-LIGHT LV_ICON.
        <FS_ALV_DATA>-MESSAGE LV_MSGTXT.
      ENDLOOP.

      CLEAR GT_BDCDATA.
      CLEAR GT_BDCMSGCOLL.
    ENDAT.

  ENDLOOP.

ENDFORM.

반응형