구매오더의 처리 이력 — 즉 어떤 PO 라인에 언제 입고(GR · 101)가 들어왔고, 언제 입고취소(102)가 됐고, 송장(IR)이 어떻게 처리됐는지 — 는 모두 SAP 표준 테이블 EKBE(History per Purchasing Document) 에 쌓입니다. ME23N 의 “Purchase Order History” 탭에서 보이는 그 정보입니다.
이 EKBE 데이터를 코드에서 다루는 방법은 크게 세 가지입니다 — 표준 FM ME_READ_HISTORY 로 데이터만 읽기, 표준 FM MM_HISTORY_POPUP_SHOW 로 ME23N 과 동일한 팝업 띄우기, EKBE 를 직접 SELECT 해서 가공하기. 각각 적합한 상황이 다르고, 헷갈리면 “표준 FM 이 있는데 EKBE 를 직접 SELECT 한” 식으로 비효율적인 코드가 되기 쉽습니다.
이 글에서는 세 방법의 차이를 정리하고, 표준 프로그램 RM06EHBE 활용, 그리고 실무에서 자주 나오는 응용 — “이 PO 라인이 입고된 채로 남아있는지, 다 취소됐는지” 를 판단하는 패턴까지 함께 정리합니다.
핵심 — 구매오더 이력 처리 3가지 방법
| 방법 | 용도 | 언제 사용 |
|---|---|---|
ME_READ_HISTORY |
PO 이력을 내부 테이블로 받기 (데이터만) | 로직에서 입고 수량·금액 합산, 취소 여부 판단 등 후속 가공 필요할 때 |
MM_HISTORY_POPUP_SHOW |
ME23N 과 같은 이력 팝업 띄우기 (화면) | 사용자에게 표준 이력 화면 그대로 보여주고 싶을 때 (Z 리포트의 핫스팟에서 호출) |
RM06EHBE |
PO 이력 표준 리포트 | 화면으로 한꺼번에 여러 PO 이력을 보고 싶을 때 (SE38 직접 실행) |
EKBE 직접 SELECT |
테이블 직접 조회 | 표준 FM 으로는 어려운 복잡한 조건 (PO 여러 건 한꺼번에 · 특정 BWART 만) |
기본 원칙 — 단건 PO 라인의 이력은 ME_READ_HISTORY 로 받는 게 가장 안전합니다. SAP 가 내부에서 어떤 필드를 어떻게 채우는지(버퍼링 포함) 표준 동작을 보장하기 때문에 직접 SELECT 보다 호환성 좋습니다. 여러 PO 라인을 한 번에 다뤄야 하면 EKBE 직접 SELECT 로 가는 게 성능상 유리합니다.
1단계 — EKBE 테이블 구조 이해
먼저 데이터가 어디에 어떻게 쌓이는지부터 잡고 가는 게 좋습니다. EKBE 의 핵심 필드 — KEY + 이력 구분자:
| 필드 | PK | 의미 |
|---|---|---|
EBELN |
✔ | 구매오더 번호 |
EBELP |
✔ | PO 라인 번호 |
ZEKKN |
✔ | 계정할당 일련번호 |
VGABE |
✔ | 트랜잭션 카테고리 (1=GR, 2=IR, 3=Down payment, …) |
GJAHR / BELNR / BUZEI |
✔ | 회계연도 / MM 문서 번호 / 라인 — MSEG 와 조인 KEY |
BWART |
— | 이동유형 (101=입고, 102=입고취소, 122=품질반품, …) |
MENGE / DMBTR |
— | 이력 수량 / 금액 (회사통화) |
SHKZG |
— | 차/대변 지시자 (S=차변, H=대변 — 취소는 H) |
SMBLN / SJAHR / SMBLP |
— | 취소 대상 문서/연도/라인 (MSEG 의 취소 짝꿍 찾기) |
핵심 — EKBE 한 줄이 하나의 GR/IR 이벤트. 입고 한 번 들어왔다 취소되면 EKBE 에 BWART 101 한 줄 + 102 한 줄, 두 줄이 쌓입니다.
2단계 — ME_READ_HISTORY 로 이력 데이터 읽기
가장 표준적인 PO 라인 이력 조회 방법입니다. EXPORTING 으로 EBELN·EBELP·WEBRE 를 넘기고, TABLES XEKBE · XEKBES 로 결과를 받습니다.
DATA: lt_ekbe TYPE TABLE OF ekbe,
lt_ekbes TYPE TABLE OF ekbes.
CALL FUNCTION 'ME_READ_HISTORY'
EXPORTING
ebeln = '4500000001' " PO 번호
ebelp = '00010' " 라인 번호
webre = abap_true " 입고(GR) 관련 이력만 (WE 플래그)
TABLES
xekbe = lt_ekbe " 이력 라인 단위
xekbes = lt_ekbes. " 이력 합계 (BWART 그룹별)
핵심 파라미터:
WEBRE— “Goods Receipt 만 가져올지” 플래그.abap_true면 입고/입고취소 라인만 필터링됨.XEKBE— 한 라인 한 줄. 입고 → 취소 → 재입고 가 있으면 3줄.XEKBES— BWART/VGABE 단위 합계가 한 줄로 요약된 형태. “이 PO 라인에 GR 누적 얼마 들어왔나” 한 줄로 보고 싶을 때.
WEBRE 외에도 IR 만 필터하는 RERE, 미결제 분만 필터하는 XBLNR_FLAG 등의 옵션이 있어 시그니처를 SE37 에서 직접 한 번 확인해두면 도움됩니다.
3단계 — MM_HISTORY_POPUP_SHOW 로 ME23N 이력 팝업 띄우기
데이터가 아니라 사용자에게 표준 이력 화면 그대로 보여주고 싶을 때 쓰는 함수입니다. Z 리포트의 PO 번호 핫스팟에 연결해두는 패턴이 가장 흔합니다.
CALL FUNCTION 'MM_HISTORY_POPUP_SHOW'
EXPORTING
pi_ebeln = '4500000001' " PO 번호
pi_ebelp = '00010'. " 라인 번호
호출하면 ME23N 의 “Purchase Order History” 탭과 완전히 동일한 팝업 다이얼로그가 열립니다. 사용자가 보는 데이터가 표준 화면과 100% 일치해서, ALV 에 직접 EKBE 를 그려주는 것보다 “표준과 다르다” 는 컴플레인이 없습니다.
ALV 에서 PO 번호 컬럼에 핫스팟을 걸어 다음과 같이 연결하는 게 일반적:
" ALV USER_COMMAND 이벤트 핸들러 안
CASE rs_selfield-fieldname.
WHEN 'EBELN'.
CALL FUNCTION 'MM_HISTORY_POPUP_SHOW'
EXPORTING
pi_ebeln = rs_selfield-value
pi_ebelp = <fs_alv>-ebelp.
ENDCASE.
4단계 — RM06EHBE 표준 프로그램 활용
이력을 “리포트 형태로 한꺼번에” 보고 싶을 때는 표준 리포트 RM06EHBE 를 SE38 에서 실행하면 됩니다.
[1] T-Code: SE38
[2] Program name: RM06EHBE 입력 → 실행
[3] Selection Screen 에서 조건 입력
☑ EBELN — PO 번호 (Range)
☑ EBELP — 라인 번호 (선택)
☑ BWART — 이동유형 필터 (선택)
[4] 실행 → ALV 로 EKBE 이력 출력
여러 PO 의 이력을 한꺼번에 비교 분석하거나, 특정 기간의 입고 이력을 일괄 추출할 때 가장 빠른 방법. 사용자에게 “Z 리포트 만들지 말고 이거 쓰세요” 라고 안내하는 첫 후보입니다.
응용 — 입고/입고취소 여부 판단 패턴
실무에서 ME_READ_HISTORY 를 가장 많이 쓰는 시나리오는 “이 PO 라인이 현재 입고된 상태인가, 다 취소돼서 빈 상태인가” 를 판단하는 로직입니다. 핵심 아이디어 — EKBE 에 BWART 101 과 102 가 짝수 개로 쌓여 있으면 다 취소된 것, 홀수 개면 입고된 것이 남아있는 것.
방법 1 — ME_READ_HISTORY + MSEG 조인
DATA : lt_ekbe TYPE TABLE OF ekbe,
lt_ekbes TYPE TABLE OF ekbes.
CALL FUNCTION 'ME_READ_HISTORY'
EXPORTING
ebeln = '4500000001'
ebelp = '00010'
webre = abap_true
TABLES
xekbe = lt_ekbe
xekbes = lt_ekbes.
IF lt_ekbe IS NOT INITIAL.
" MSEG 에서 짝꿍 취소 전표 찾기
SELECT *
INTO TABLE @DATA(lt_102)
FROM mseg
FOR ALL ENTRIES IN @lt_ekbe
WHERE smbln = @lt_ekbe-belnr
AND sjahr = @lt_ekbe-gjahr
AND smblp = @lt_ekbe-buzei
AND bwart IN ( '101', '102' ).
DATA(lv_lines) = lines( lt_102 ).
DATA(lv_mod) = lv_lines MOD 2.
IF lv_mod = 0.
WRITE : / '입고된 거 전부 취소됨'.
ELSE.
WRITE : / '입고된 게 남아있음'.
ENDIF.
ENDIF.
판단 로직 핵심 — MSEG 에서 SMBLN(취소 대상 문서) 로 묶이는 101/102 라인들의 개수를 셉니다. 한 번 입고 → 한 번 취소면 짝수(=2건), 입고 → 취소 → 재입고면 홀수(=3건). 짝수면 “현재 남은 입고 없음”, 홀수면 “현재 입고 남아있음”.
방법 2 — MSEG 의 SGTXT/CHARG 로 직접 카운트
ME_READ_HISTORY 없이 더 간단히 가는 패턴. PO 라인이 아니라 “특정 적요 + 배치” 단위 입고 여부만 보고 싶을 때.
SELECT COUNT( * )
INTO lv_count
FROM mseg
WHERE sgtxt = ls_data-sgtxt
AND charg = ls_data-charg
AND bwart IN ( '101', '102' ).
lv_count = lv_count MOD 2.
IF lv_count = 0 AND ls_data-bwart = 'C'. " 취소(C) 요청인데
" 이미 다 취소됨 → 더 취소할 게 없음
APPEND VALUE #( type = 'E'
message = '취소할 수 있는 재고가 없습니다' ) TO pt_msg.
EXIT.
ENDIF.
방법 3 — MSEG 만으로 “취소된 전표 빼고 살아있는 것” 만 추리기
EKBE 안 거치고 MSEG 만으로 “취소되지 않은 입고 전표” 만 남기는 패턴. 한 PO 의 살아남은 입고 라인 전체를 알고 싶을 때.
SELECT mblnr, mjahr, ebeln, ebelp,
smbln, sjahr, bwart
INTO TABLE @DATA(lt_mseg)
FROM mseg
WHERE ebeln = @i_ebeln.
IF lt_mseg IS NOT INITIAL.
" 취소된 전표 제거
LOOP AT lt_mseg INTO DATA(ls_mseg) WHERE smbln IS NOT INITIAL.
" 1) 취소 전표 자체 (102)
DELETE lt_mseg
WHERE smbln = ls_mseg-smbln
AND sjahr = ls_mseg-sjahr
AND bwart = '102'.
" 2) 취소된 입고 전표 (101)
DELETE lt_mseg
WHERE mblnr = ls_mseg-smbln
AND mjahr = ls_mseg-mjahr
AND bwart = '101'.
CLEAR ls_mseg.
ENDLOOP.
" lt_mseg 에는 “취소되지 않은 입고” 만 남아있음
ENDIF.
세 가지 다 같은 목적이지만, 방법 1(ME_READ_HISTORY + MSEG) 이 표준에 가장 가깝고 PO 라인 단위 판단이 정확합니다. 방법 2/3 은 코드는 짧지만 PO 외부 시나리오(반품·재입고 변형) 가 들어오면 깨질 수 있어요.
자주 빠뜨리는 함정
WEBRE 플래그 누락
WEBRE = abap_true 를 안 주면 GR 외에 IR · 다운페이먼트까지 다 섞여 들어옵니다. 입고 여부만 보고 싶다면 반드시 WEBRE 를 켜야 BWART 101/102 라인만 깔끔하게 받습니다.
EBELP 자릿수
EBELP 는 5자리 NUMC. '10' 을 넘기면 안 되고 '00010' 으로 패딩해서 넘겨야 합니다. CONV 또는 변수 타입을 ebelp 로 선언해두면 자동 패딩.
MM_HISTORY_POPUP_SHOW 의 권한
표준 팝업이라 사용자 권한 체크가 들어갑니다. ME23N 을 못 쓰는 사용자에게 호출하면 “권한 없음” 메시지가 떠서 팝업이 안 열립니다. Z 리포트 권한 설계 시 ME23N 권한 객체(M_BEST_BSA 등) 도 같이 부여 필요.
EKBE 의 SHKZG 무시
EKBE 의 MENGE·DMBTR 은 절대값입니다. 부호는 SHKZG 에 있음 — S(차변)/H(대변). 합산할 때 SHKZG 안 보고 그냥 더하면 “입고 5개 + 취소 5개 = 10개” 같은 잘못된 합계가 나옵니다. 보통은 SHKZG='H' 면 부호 뒤집어서 합산.
FOR ALL ENTRIES 빈 테이블 체크
SELECT ... FOR ALL ENTRIES IN @lt_ekbe 인데 lt_ekbe 가 비어있으면 WHERE 조건이 사라지고 MSEG 전체를 긁어옵니다. 운영기에 사고나는 흔한 시나리오. IF lt_ekbe IS NOT INITIAL. 가드 필수.
전체 코드 — 복사용 통합본
위 패턴을 한 화면에 정리한 Z 리포트 형태:
REPORT zrxx_po_history_check.
PARAMETERS: p_ebeln TYPE ebeln OBLIGATORY,
p_ebelp TYPE ebelp OBLIGATORY.
DATA: lt_ekbe TYPE TABLE OF ekbe,
lt_ekbes TYPE TABLE OF ekbes.
* ─────────────────────────────────────────────
* 1) PO 라인 이력 읽기 (입고 관련만)
* ─────────────────────────────────────────────
CALL FUNCTION 'ME_READ_HISTORY'
EXPORTING
ebeln = p_ebeln
ebelp = p_ebelp
webre = abap_true
TABLES
xekbe = lt_ekbe
xekbes = lt_ekbes.
IF lt_ekbe IS INITIAL.
WRITE: / '입고 이력 없음'.
RETURN.
ENDIF.
* ─────────────────────────────────────────────
* 2) MSEG 에서 짝꿍 취소 전표 찾기
* ※ FOR ALL ENTRIES 전 lt_ekbe 비어있지 않은지 가드 (위에서 RETURN)
* ─────────────────────────────────────────────
SELECT *
INTO TABLE @DATA(lt_102)
FROM mseg
FOR ALL ENTRIES IN @lt_ekbe
WHERE smbln = @lt_ekbe-belnr
AND sjahr = @lt_ekbe-gjahr
AND smblp = @lt_ekbe-buzei
AND bwart IN ( '101', '102' ).
* ─────────────────────────────────────────────
* 3) 짝수면 다 취소, 홀수면 입고 남아있음
* ─────────────────────────────────────────────
DATA(lv_lines) = lines( lt_102 ).
DATA(lv_mod) = lv_lines MOD 2.
IF lv_mod = 0.
WRITE: / p_ebeln, '/', p_ebelp, ' → 입고된 거 전부 취소됨'.
ELSE.
WRITE: / p_ebeln, '/', p_ebelp, ' → 입고된 게 남아있음'.
* ─────────────────────────────────────────────
* 4) 표준 이력 팝업 띄우기 (옵션)
* ─────────────────────────────────────────────
CALL FUNCTION 'MM_HISTORY_POPUP_SHOW'
EXPORTING
pi_ebeln = p_ebeln
pi_ebelp = p_ebelp.
ENDIF.
요약
| 목적 | 사용 | 결과 |
|---|---|---|
| 데이터 읽기 | ME_READ_HISTORY + WEBRE |
XEKBE(라인 단위) · XEKBES(합계) |
| 화면 띄우기 | MM_HISTORY_POPUP_SHOW |
ME23N 과 동일한 표준 이력 팝업 |
| 표준 리포트 | RM06EHBE (SE38) |
여러 PO 이력 한꺼번에 ALV |
| 취소 여부 판단 | MSEG.SMBLN 으로 101/102 짝꿍 카운트 | 짝수 = 다 취소, 홀수 = 입고 남아있음 |
PO 이력 처리의 핵심은 “ME_READ_HISTORY 로 EKBE 받기 → MSEG 의 SMBLN/SMBLP 로 취소 짝꿍 매칭 → BWART 101/102 카운트의 짝/홀수 판단” 이 한 줄로 요약됩니다. 직접 EKBE/MSEG 를 SELECT 하기 전에 항상 “표준 FM 으로 같은 일을 할 수 있나” 를 먼저 검토해보는 게 안전합니다.
Disclaimer — 이 포스트는 실무 정리 노트를 바탕으로 AI 보조로 정리되었습니다.
ME_READ_HISTORY · MM_HISTORY_POPUP_SHOW 의 파라미터 구성은 NetWeaver 버전(ECC 6.0 / S/4HANA on-premise · Cloud) 과 적용된 SAP Note 에 따라 일부 차이가 있을 수 있으니, 실제 적용 시에는 SE37 에서 시그니처를 한 번 확인하고 사용하시기 바랍니다.