본문 바로가기
BAPI · BADI · RFC · Interface

[SAP ABAP] BAPI_GOODSMVT_CREATE — 입고·출고·자재이동 BAPI 호출 방법 (MIGO 자동화 · GOODSMVT_CODE · BWART)

by Song.sh 2026. 5. 21.

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_ITEMMOVE_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 환경에서 검증하시기 바랍니다.