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

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

엑셀 데이터를 읽어와 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 = 1 " 데이터 시작 COLUMN
I_BEGIN_ROW = 2 " 데이터 시작 ROW
I_END_COL = 8 " 데이터 끝 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-WAERS( LS_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~LIFNR, B~NAME1, A~MATNR, C~MAKTX, A~MENGE, A~MEINS, A~NETPR, A~NETPR * A~MENGE AS NETWR, A~WAERS, A~WERKS, A~LGORT
FROM @LT_PO_UPLOAD_TEMP AS A
LEFT OUTER JOIN LFA1 AS B ON LPAD( A~LIFNR, 10, '0' ) = B~LIFNR
LEFT OUTER JOIN MAKT AS C 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 <= 0 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 C LENGTH 255, " 금액 천단위 제거된 값
LV_MENGE_FVAL TYPE C 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-GAPS, LV_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.
'SAP > ABAP' 카테고리의 다른 글
| [ABAP] Selection Texts의 DDIC Reference (6) | 2025.10.10 |
|---|---|
| [ABAP] 동적 ALV Field Catalog (0) | 2025.09.16 |
| [ABAP] LEFT OUTER JOIN할때 IS INITIAL 사용시 주의사항 (1) | 2025.09.14 |
| [ABAP] SmartForms Loop 사용법과 집계 구하는법 (4) | 2025.09.03 |
| [ABAP] ALV Cell Edit과 BAPI_PO_CHANGE (13) | 2025.08.28 |