SAP MM 모듈의 자재 입출고 처리는 화면에서는 MIGO·MB1A·MB1B·MB1C 같은 트랜잭션으로 나뉘어 있지만, ABAP 코드에서는 BAPI_GOODSMVT_CREATE 하나로 모두 처리할 수 있습니다. MES·WMS 같은 외부 시스템 연동, 입고 자동화 배치, 생산 실적 자동 반영 등 자재 이동이 발생하는 모든 인터페이스의 핵심 BAPI 입니다.
이 BAPI 는 GOODSMVT_HEADER + GOODSMVT_CODE + GOODSMVT_ITEM 3종 세트로 호출됩니다. GOODSMVT_CODE 가 "어떤 화면 흐름인가"(PO 입고·생산 입고·출고·이동·취소) 를 결정하고, GOODSMVT_ITEM 의 MOVE_TYPE(이동유형 BWART) 이 "어떤 분개를 일으킬 것인가" 를 결정합니다.
이 글에서는 GOODSMVT_CODE 분기·이동유형·헤더·아이템 필드 채우기·시리얼 / 배치 처리·TESTRUN 시뮬레이션·COMMIT·자주 발생하는 함정까지 정리합니다.
핵심 — GOODSMVT_CODE + MOVE_TYPE 두 축이 결정한다
| CODE | 화면 트랜잭션 | 동작 | 대표 BWART |
|---|---|---|---|
| 01 | MB01 / MIGO_GR |
PO 입고 | 101 (입고) / 102 (취소) |
| 02 | MB31 |
생산오더 입고 | 101 (생산 입고) |
| 03 | MB1A |
출고 | 201 (비용센터) / 261 (생산오더) |
| 04 | MB1B |
자재 / 저장위치 이동 | 309 (자재간) / 311 (저장위치) |
| 05 | MB1C |
기타 입고 (참조 없음) | 501 (외부) / 561 (초기재고) |
| 06 | MBST / MIGO_GR_REV |
자재문서 취소 | 취소 BAPI 별도 사용 권장 |
PO 입고를 자동화한다면 CODE = 01 + MOVE_TYPE = 101 조합이 표준입니다. CODE 만 정확히 넘기면 BAPI 가 화면 흐름에 맞는 검증 로직을 자동으로 적용해주므로, 잘못된 조합(예: CODE=03 + MOVE_TYPE=101) 으로 호출하면 "Goods movement type not valid for code" 에러가 발생합니다.
시그니처 — IMPORTING / EXPORTING / TABLES
| 📋 구분 | 파라미터 | 타입 | 설명 |
|---|---|---|---|
| IMPORTING | GOODSMVT_HEADER |
BAPI2017_GM_HEAD_01 |
자재문서 헤더 (필수) |
| IMPORTING | GOODSMVT_CODE |
BAPI2017_GM_CODE |
동작 분기 코드 (필수) |
| IMPORTING | TESTRUN |
BAPI2017_GM_GEN-TESTRUN |
시뮬레이션 모드 (기본 공백) |
| EXPORTING | GOODSMVT_HEADRET |
BAPI2017_GM_HEAD_RET |
처리 결과 헤더 |
| EXPORTING | MATERIALDOCUMENT |
MAT_DOC |
생성된 자재문서 번호 |
| EXPORTING | MATDOCUMENTYEAR |
DOC_YEAR |
자재문서 회계연도 |
| TABLES | GOODSMVT_ITEM |
BAPI2017_GM_ITEM_CREATE |
자재 / 수량 / 이동유형 (필수) |
| TABLES | GOODSMVT_SERIALNUMBER (Opt) |
BAPI2017_GM_SERIALNUMBER |
시리얼 번호 (해당 시) |
| TABLES | RETURN |
BAPIRET2 |
처리 메시지 |
| TABLES | EXTENSIONIN (Opt) |
BAPIPAREX |
CI Include 확장 필드 |
1단계 — GOODSMVT_HEADER 채우기
헤더는 자재문서 1건당 하나의 행. 전기일자·문서일자·헤더 텍스트 정도만 채우면 됩니다.
DATA ls_header TYPE bapi2017_gm_head_01.
ls_header-pstng_date = sy-datum. " 전기일자
ls_header-doc_date = sy-datum. " 문서일자
ls_header-ref_doc_no = 'GR-2026-001'. " 외부 참조번호 (선택)
ls_header-header_txt = '자동 입고 - MES 인터페이스'.
PSTNG_DATE(전기일자)가 회계 기간 마감과 직결되는 가장 중요한 필드입니다. 이전 기간 입고를 처리하려면 회계 기간을 다시 열거나(MMPV·OB52), 열려 있는 기간의 일자로 조정해야 합니다.
2단계 — GOODSMVT_CODE 결정
CODE 값 자체는 단순 단일 값이지만 이 한 글자가 전체 흐름을 결정합니다.
DATA ls_code TYPE bapi2017_gm_code.
ls_code-gm_code = '01'. " PO 입고
T-Code 별 매핑이 헷갈리면 SE16N 에서 T158G 테이블을 조회해서 회사 환경의 정확한 코드를 확인할 수 있습니다.
3단계 — GOODSMVT_ITEM 채우기 (PO 입고 표준 패턴)
아이템은 자재 라인별로 한 줄씩 추가합니다. PO 입고는 PO 번호 + PO 아이템 + 이동유형만 채우면 나머지는 BAPI 가 PO 마스터에서 끌어옵니다.
DATA: lt_item TYPE TABLE OF bapi2017_gm_item_create,
ls_item TYPE bapi2017_gm_item_create.
ls_item-material = 'TEST-MAT-001'. " 자재
ls_item-plant = '0001'. " 플랜트
ls_item-stge_loc = '0001'. " 저장위치
ls_item-move_type = '101'. " 이동유형 - PO 입고
ls_item-vendor = '0000100001'. " 거래처
ls_item-entry_qnt = '100'. " 입고 수량
ls_item-entry_uom = 'EA'. " 단위
ls_item-po_number = '4500000001'. " PO 번호
ls_item-po_item = '00010'. " PO 아이템
ls_item-mvt_ind = 'B'. " B = PO 참조 입고
ls_item-batch = 'BATCH-001'. " 배치 (선택)
APPEND ls_item TO lt_item.
핵심 포인트 — MVT_IND(Movement Indicator) 가 PO 참조 여부를 결정합니다. B = PO 참조 입고, F = 생산오더 입고, A = 보증 자재 입고. 공백으로 두면 참조 없는 입고로 처리되어 PO 의 GR 수량이 차감되지 않습니다.
4단계 — 시나리오별 ITEM 필드 차이
이동유형에 따라 채우는 필드가 달라집니다. 가장 자주 쓰는 4가지 패턴을 비교합니다.
| 시나리오 | CODE | BWART | 핵심 필드 |
|---|---|---|---|
| PO 입고 | 01 | 101 | PO_NUMBER · PO_ITEM · MVT_IND='B' |
| 생산오더 입고 | 02 | 101 | ORDERID · MVT_IND='F' |
| 비용센터 출고 | 03 | 201 | COSTCENTER · GR_RCPT |
| 저장위치 이동 | 04 | 311 | STGE_LOC + MOVE_STLOC |
저장위치 이동(311) 의 경우 출발 저장위치(STGE_LOC) + 도착 저장위치(MOVE_STLOC) 를 동시에 채워야 합니다. 하나만 채우면 BAPI 가 거부합니다.
5단계 — TESTRUN 시뮬레이션
TESTRUN = 'X' 로 호출하면 실제 자재문서를 만들지 않고 검증만 수행합니다. 사전 점검에 유용하지만 DB 락 / 시리얼 번호 같은 일부 검증을 건너뛰므로 100% 보장은 아닙니다.
CALL FUNCTION 'BAPI_GOODSMVT_CREATE'
EXPORTING
goodsmvt_header = ls_header
goodsmvt_code = ls_code
testrun = 'X' " 시뮬레이션
IMPORTING
goodsmvt_headret = ls_headret
materialdocument = lv_mblnr
matdocumentyear = lv_mjahr
TABLES
goodsmvt_item = lt_item
return = lt_return.
대량 인터페이스 배치에서는 사전 검증 패스 1회 + 실제 등록 패스 1회의 2-pass 패턴을 권장합니다. 검증 패스에서 걸러진 건만 실제 등록에서 제외하면 운영 데이터를 깨끗하게 유지할 수 있습니다.
6단계 — 호출 + COMMIT
전체 호출 흐름.
CALL FUNCTION 'BAPI_GOODSMVT_CREATE'
EXPORTING
goodsmvt_header = ls_header
goodsmvt_code = ls_code
IMPORTING
goodsmvt_headret = ls_headret
materialdocument = lv_mblnr
matdocumentyear = lv_mjahr
TABLES
goodsmvt_item = lt_item
return = lt_return.
READ TABLE lt_return INTO ls_return WITH KEY type = 'E'.
IF sy-subrc = 0.
CALL FUNCTION 'BAPI_TRANSACTION_ROLLBACK'.
ELSE.
CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'
EXPORTING wait = 'X'.
WRITE: / '자재문서:', lv_mblnr, '/', lv_mjahr.
ENDIF.
흔히 빠뜨리는 함정
GOODSMVT_CODE 와 BWART 불일치
가장 흔한 에러입니다. CODE = 01 (PO 입고) 인데 MOVE_TYPE = 311 (저장위치 이동) 을 넣으면 BAPI 가 거부합니다. CODE 가 화면 흐름이고 BWART 가 분개 룰이므로 두 값이 같은 비즈니스 동작을 가리켜야 합니다.
MVT_IND 누락
PO 입고에서 MVT_IND 를 공백으로 두면 자재문서는 만들어지지만 PO 의 GR 수량이 차감되지 않습니다. PO 가 영영 미입고 상태로 남고 후속 송장 처리에서 막힙니다. PO 참조 입고에서는 반드시 'B' 를 넣으시기 바랍니다.
단위 변환 차이
PO 단위와 자재 마스터 기본 단위가 다른 경우(예: PO 는 BOX, 마스터는 EA), ENTRY_UOM 에 들어가는 단위와 수량이 PO 의 단위 변환 규칙에 맞지 않으면 에러가 발생합니다. 외부 시스템에서 받은 수량은 호출 측에서 단위까지 같이 전달받아야 안전합니다.
회계 기간 마감
전기일자(PSTNG_DATE) 가 속한 기간이 마감되면 "Posting period not open" 에러가 발생합니다. MMRV 에서 열린 기간 확인, MMPV 로 기간 이월, OB52 로 회계 기간 제어가 필요합니다.
배치 관리 자재인데 BATCH 누락
배치 관리(MARA-XCHPF = 'X') 자재인데 BATCH 필드를 비워두면 "Batch not specified" 에러가 발생합니다. 신규 배치는 같이 생성하거나, 미리 BAPI_BATCH_CREATE 로 만든 배치를 넘기시기 바랍니다.
시리얼 관리 자재
시리얼 관리 자재(MARC-SERAIL) 는 입고 수량만큼 시리얼 번호를 GOODSMVT_SERIALNUMBER 테이블에 채워야 합니다. 100개 입고면 시리얼 100건. 모자라면 BAPI 가 거부합니다.
COMMIT 누락
가장 빈번한 실수. BAPI 자체 호출 성공이라도 BAPI_TRANSACTION_COMMIT 누락 시 DB 미반영. 비동기 인터페이스에서 직후 SELECT 시 데이터를 못 봅니다.
전체 코드 — 복사용 통합본
*&---------------------------------------------------------------------*
*& Report Z_XX_GOODS_RECEIPT
*&---------------------------------------------------------------------*
*& 입고 BAPI 호출 샘플 (PO 참조 표준 입고)
*&---------------------------------------------------------------------*
REPORT z_xx_goods_receipt.
DATA: ls_header TYPE bapi2017_gm_head_01,
ls_code TYPE bapi2017_gm_code,
ls_headret TYPE bapi2017_gm_head_ret,
lt_item TYPE TABLE OF bapi2017_gm_item_create,
ls_item TYPE bapi2017_gm_item_create,
lt_return TYPE TABLE OF bapiret2,
ls_return TYPE bapiret2,
lv_mblnr TYPE bapi2017_gm_head_ret-mat_doc,
lv_mjahr TYPE bapi2017_gm_head_ret-doc_year.
* ★ 1) 헤더 (자재문서 1건의 공통 정보)
ls_header-pstng_date = sy-datum.
ls_header-doc_date = sy-datum.
ls_header-ref_doc_no = 'GR-2026-001'.
ls_header-header_txt = '자동 입고 - 인터페이스'.
* ★ 2) CODE — 01 = PO 입고 (T158G)
ls_code-gm_code = '01'.
* ★ 3) 아이템 (PO 라인별)
ls_item-material = 'TEST-MAT-001'.
ls_item-plant = '0001'.
ls_item-stge_loc = '0001'.
ls_item-move_type = '101'. " PO 입고
ls_item-vendor = '0000100001'.
ls_item-entry_qnt = '100'.
ls_item-entry_uom = 'EA'.
ls_item-po_number = '4500000001'.
ls_item-po_item = '00010'.
ls_item-mvt_ind = 'B'. " B = PO 참조 입고
APPEND ls_item TO lt_item.
* 추가 라인 가능
ls_item-material = 'TEST-MAT-002'.
ls_item-plant = '0001'.
ls_item-stge_loc = '0001'.
ls_item-move_type = '101'.
ls_item-vendor = '0000100001'.
ls_item-entry_qnt = '50'.
ls_item-entry_uom = 'EA'.
ls_item-po_number = '4500000001'.
ls_item-po_item = '00020'.
ls_item-mvt_ind = 'B'.
APPEND ls_item TO lt_item.
* ★ 4) BAPI 호출
CALL FUNCTION 'BAPI_GOODSMVT_CREATE'
EXPORTING
goodsmvt_header = ls_header
goodsmvt_code = ls_code
* testrun = 'X' " 시뮬레이션 모드 활성화 시
IMPORTING
goodsmvt_headret = ls_headret
materialdocument = lv_mblnr
matdocumentyear = lv_mjahr
TABLES
goodsmvt_item = lt_item
return = lt_return.
* ★ 5) RETURN 검증
READ TABLE lt_return INTO ls_return
WITH KEY type = 'E'.
IF sy-subrc = 0.
CALL FUNCTION 'BAPI_TRANSACTION_ROLLBACK'.
WRITE: / '입고 실패'.
LOOP AT lt_return INTO ls_return.
WRITE: / ls_return-type, ls_return-id,
ls_return-number, ls_return-message.
ENDLOOP.
ELSE.
CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'
EXPORTING wait = 'X'.
WRITE: / '입고 성공',
/ '자재문서:', lv_mblnr,
/ '회계연도:', lv_mjahr.
ENDIF.
같이 보면 좋은 글은 "BAPI_GOODSMVT_CANCEL — 입고 취소 BAPI 호출 방법" 입니다. 입고 등록과 짝꿍 BAPI 이므로 자재문서 생성 → 취소 패턴을 한 세트로 익혀두면 좋습니다.
요약
| 단계 | 처리 내용 | 핵심 포인트 |
|---|---|---|
| 1 | 시나리오 결정 | CODE + BWART 조합 매칭 |
| 2 | HEADER 채우기 | PSTNG_DATE 회계 기간 확인 |
| 3 | ITEM 채우기 | PO 참조 시 MVT_IND='B' 필수 |
| 4 | 배치 / 시리얼 | 자재 마스터 플래그에 맞춰 채움 |
| 5 | TESTRUN | 대량 배치 사전 검증 권장 |
| 6 | 호출 + COMMIT | wait='X' 동기 처리 |
입고 BAPI 는 "GOODSMVT_CODE 가 화면 흐름을 결정하고, MOVE_TYPE 이 분개를 결정한다" 는 두 축만 정확히 이해하면 PO 입고·생산 입고·자재이동·기타 입고 모든 시나리오를 같은 BAPI 로 처리할 수 있습니다. MES·WMS 연동이나 자동 입고 배치를 만든다면 시뮬레이션(TESTRUN) → 실제 등록(COMMIT) 2-pass 패턴을 운영 표준으로 잡아두시면 좋습니다.
Disclaimer — 이 포스트는 실무 정리 노트를 바탕으로 AI 보조로 정리되었습니다.
BAPI 시그니처·구조체 필드(BAPI2017_GM_HEAD_01·BAPI2017_GM_ITEM_CREATE)·이동유형(BWART)·T158G 코드는 SAP MM 표준(패키지 MB · ECC 6.0 / S/4HANA on-premise) 기준이며, 사내 추가 이동유형·BAdI 구현·시리얼/배치 정책에 따라 일부 동작이 다를 수 있으니 운영 시스템 적용 전 개발·QA 환경에서 검증하시기 바랍니다.