SAP

VL01N 납품 vs PGI — 재고 언제 빠지나 #shorts #SAP #SD

▶ YouTube에서 보기

📖 이 글이 답하는 질문

VL01N으로 납품 문서를 만들고 나서 MB51을 열어봤는데 자재문서가 한 건도 없어서 당황한 적이 있다면, 이 글이 그 궁금증을 정리해 줍니다. 납품(Delivery) 문서를 만드는 것과 출고전기(PGI, Post Goods Issue)를 하는 것은 완전히 다른 사건이며, 재고와 회계가 실제로 움직이는 시점은 오직 출고전기 순간입니다. 이 글을 읽고 나면 아래 항목에 스스로 답할 수 있습니다.

  • 납품 문서 생성과 출고전기가 왜 별개의 단계로 나뉘어 있는가
  • 재고 수량 감소와 매출원가 전기가 일어나는 정확한 시점은 언제인가
  • 피킹과 확인(Confirmation) 단계에서는 무슨 일이 일어나는가
  • 미출고 납품이 쌓이면 어떤 문제가 생기고, ABAP으로 어떻게 추적하는가
  • VL09로 출고전기를 취소할 때 반드시 먼저 확인할 것은 무엇인가

📚 미리 알아두면 좋은 것

SD(판매·유통) 모듈의 기본 문서 개념 — 수주(Sales Order), 납품(Outbound Delivery), 청구(Billing) — 을 한 번이라도 만져봤다면 충분합니다. ABAP 예제를 따라 하려면 SELECT 문과 내부 테이블 정도의 기초 문법, 그리고 SE38/ADT에서 리포트를 실행해 본 경험이 있으면 좋습니다. MM 재고관리 쪽은 이동유형(Movement Type)이라는 단어만 들어봤어도 무리 없이 읽을 수 있습니다.

🔧 테스트 환경

이 글의 예제는 SAP S/4HANA 2023 (On-Premise, FPS01) 기준으로 작성했고, ECC 6.0 EHP8과 차이가 있는 지점은 본문에서 따로 짚습니다. 가장 큰 차이는 상태 테이블입니다. ECC에서는 납품 헤더 상태(WBSTK 등)가 VBUK에, 품목 상태가 VBUP에 있었지만, S/4HANA에서는 이 상태 필드들이 LIKP/LIPS 본 테이블로 통합되었습니다. 아래 코드는 S/4HANA 기준이므로 ECC에서 돌리려면 VBUK 조인으로 바꿔야 합니다.

예제 시나리오는 가상의 유통사 한빛트레이딩입니다. 회사코드 K100, 플랜트 P100(판교 물류센터), 출하지점 KP01, 판매 자재 FG-CAM-001(차량용 블랙박스), 고객 CU-9001(가온마트)을 사용합니다. 수주 4711번에서 납품 80001234번이 생성되는 흐름을 따라갑니다. 실습하려면 개발 서버에 표준 SD 프로세스가 열려 있고 VL01N/VL02N 실행 권한이 있어야 합니다.

💡 핵심 개념: 납품 문서는 송장 출력, 출고전기는 트럭 출발

택배에 비유하면 정확합니다. 납품 문서를 만드는 것은 송장을 출력해서 상자에 붙이는 일이고, 출고전기는 트럭이 상자를 싣고 실제로 출발하는 일입니다. 송장을 백 장 출력해도 창고의 물건은 한 개도 줄지 않습니다. 트럭이 떠나는 순간에만 재고가 줄고, 회계 장부에 "물건이 나갔다"는 기록이 남습니다.

표준 주문-출하 흐름을 단계별로 보면 이렇습니다.

단계트랜잭션생성되는 것재고 영향회계 영향
수주VA01VBAK/VBAP없음 (ATP 소요만 발생)없음
납품 생성VL01NLIKP/LIPS수량 감소 없음없음
피킹·확인VL02N, LT03전송오더(WM/EWM)창고 내 위치 이동만없음
출고전기(PGI)VL02N자재문서 + 회계문서재고 감소 (이동유형 601)매출원가 전기
청구VF01VBRK/VBRP + 회계문서없음매출·채권 인식

납품 문서(LIKP 헤더, LIPS 품목)가 생성되면 시스템 안에서는 "이 재고를 이 고객에게 보내기로 확정했다"는 논리적 예약이 잡힙니다. MD04를 열면 해당 수량이 납품 소요로 표시되고, 다른 수주가 그 재고를 가용량(ATP)으로 쓰지 못하게 막습니다. 하지만 MARD의 비제한 재고 수량 자체는 그대로입니다. 피킹을 하고 확인까지 마쳐도 마찬가지입니다. WM/EWM이 켜진 플랜트라면 피킹은 저장위치 안에서 상자가 선반에서 출고 대기 구역으로 옮겨지는 것일 뿐, 플랜트 재고 총량에는 손대지 않습니다.

출고전기 버튼을 누르는 순간, 네 가지가 동시에 일어납니다.

  1. 재고 감소 — 이동유형 601로 플랜트/저장위치 재고가 납품 수량만큼 줄어듭니다.
  2. 자재문서 생성 — MKPF/MSEG에 기록됩니다. S/4HANA에서는 실제 저장은 MATDOC 단일 테이블로 통합되었고 MKPF/MSEG는 호환성 뷰로 유지됩니다.
  3. 회계문서 생성 — 일반적으로 차변 매출원가(COGS) / 대변 재고자산으로 전기됩니다. 즉 매출원가는 청구 시점이 아니라 출고전기 시점에 잡힙니다. 청구(VF01)에서는 매출과 채권만 인식됩니다.
  4. 상태 변경과 문서흐름 기록 — 납품의 총 이동 상태(WBSTK)가 'C'로 바뀌고, VBFA 문서흐름에 후속 문서유형 'R'(자재문서)이 연결되며, 이 납품이 청구 대상 목록(VF04)에 올라갑니다.

핵심 한 줄 — 납품 문서는 약속이고, 출고전기는 이행입니다. 재고 수량과 회계 전표는 출고전기 순간에만 움직입니다.

💻 직접 해보기 — 3단계 ABAP 예제

1단계: 납품 한 건의 출고전기 상태 확인

가장 먼저 확인할 필드는 LIKP의 WBSTK(총 이동 상태)입니다. 'A'는 미처리, 'B'는 부분 처리, 'C'는 출고전기 완료입니다. 실제출고일(WADAT_IST)이 비어 있으면 아직 트럭이 떠나지 않은 것입니다.

REPORT zsd_pgi_check_basic.

"한빛트레이딩: 납품 한 건의 출고전기 여부를 확인하는 기본 예제
PARAMETERS p_vbeln TYPE vbeln_vl OBLIGATORY DEFAULT '0080001234'.

START-OF-SELECTION.

  "S/4HANA: 상태 필드(WBSTK)가 VBUK에서 LIKP로 통합됨
  SELECT SINGLE vbeln, lfart, wadat, wadat_ist, wbstk
    FROM likp
    WHERE vbeln = @p_vbeln
    INTO @DATA(ls_dlv).

  IF sy-subrc <> 0.
    WRITE / |납품 { p_vbeln ALPHA = OUT }을(를) 찾을 수 없습니다.|.
    RETURN.
  ENDIF.

  WRITE: / |납품번호   : { ls_dlv-vbeln ALPHA = OUT }|,
         / |계획출고일 : { ls_dlv-wadat DATE = USER }|,
         / |실제출고일 : { COND #( WHEN ls_dlv-wadat_ist IS INITIAL
                                    THEN `미출고`
                                    ELSE |{ ls_dlv-wadat_ist DATE = USER }| ) }|.

  CASE ls_dlv-wbstk.
    WHEN 'C'.
      WRITE / '출고전기 완료 - 재고와 회계가 이미 움직였습니다.'.
    WHEN 'A' OR 'B'.
      WRITE / '출고전기 미완료 - 납품 문서만 있고 재고는 그대로입니다.'.
    WHEN OTHERS.
      WRITE / |이동 상태 { ls_dlv-wbstk }: 출고 대상이 아닌 문서입니다.|.
  ENDCASE.

2단계: 실무 시나리오 — 지연 미출고 납품 모니터 리포트

실무에서 진짜 문제는 "납품은 만들어졌는데 며칠째 출고전기가 안 된" 문서들입니다. 이런 미출고 납품이 쌓이면 (1) 해당 재고가 ATP에서 계속 묶여 다른 주문이 재고 부족으로 막히고, (2) 매출원가와 매출 인식이 지연되어 월말 결산 수치가 틀어지며, (3) VF04 청구 대상에 올라오지 않아 대금 회수가 늦어집니다. 표준으로는 VL06O(출하 모니터)로 볼 수 있지만, 계획출고일 기준 지연 일수를 걸러내는 자체 리포트를 하나 두면 편합니다.

REPORT zsd_open_delivery_monitor.

"판교 물류센터(P100): 계획출고일이 지났는데 PGI가 안 된 납품 추적
PARAMETERS: p_werks TYPE werks_d DEFAULT 'P100',
            p_days  TYPE i       DEFAULT 3.

START-OF-SELECTION.

  DATA(lv_limit) = sy-datum - p_days.

  SELECT k~vbeln, k~wadat, k~kunnr, k~vstel,
         p~posnr, p~matnr, p~lfimg, p~meins
    FROM likp AS k
    INNER JOIN lips AS p ON p~vbeln = k~vbeln
    WHERE k~wbstk <> 'C'                 "출고전기 미완료
      AND k~wadat <= @lv_limit           "계획출고일 p_days일 초과
      AND p~werks  = @p_werks
    ORDER BY k~wadat, k~vbeln, p~posnr
    INTO TABLE @DATA(lt_open).

  IF lt_open IS INITIAL.
    MESSAGE |{ p_days }일 이상 지연된 미출고 납품이 없습니다.| TYPE 'S'.
    RETURN.
  ENDIF.

  TRY.
      cl_salv_table=>factory(
        IMPORTING r_salv_table = DATA(lo_alv)
        CHANGING  t_table      = lt_open ).
      lo_alv->get_functions( )->set_all( abap_true ).
      lo_alv->get_columns( )->set_optimize( abap_true ).
      lo_alv->display( ).
    CATCH cx_salv_msg INTO DATA(lx_msg).
      "ALV 생성 실패도 삼켜버리지 말고 로그로 남긴다
      MESSAGE lx_msg->get_text( ) TYPE 'E'.
  ENDTRY.

ECC에서 쓰려면 likp~wbstk 대신 VBUK를 조인해 vbuk~wbstk로 읽어야 합니다.

3단계: 프로덕션 — 검증을 갖춘 출고전기 자동화

야간 배치로 출고전기를 자동화할 때는 세 가지 방어선이 필요합니다. 첫째 권한 검증(출하지점 단위), 둘째 VBFA 문서흐름으로 중복 전기 차단, 셋째 BAPI 결과에 따른 명시적 COMMIT/ROLLBACK입니다. 테스트 모드 플래그를 기본값으로 두는 것도 권장합니다.

REPORT zsd_pgi_post_secure.

PARAMETERS: p_vbeln TYPE vbeln_vl  OBLIGATORY,
            p_test  TYPE abap_bool AS CHECKBOX DEFAULT abap_true.

START-OF-SELECTION.

  "1) 존재 확인 + 출하지점 권한 검증
  SELECT SINGLE vstel FROM likp
    WHERE vbeln = @p_vbeln INTO @DATA(lv_vstel).
  IF sy-subrc <> 0.
    MESSAGE '납품 문서가 존재하지 않습니다.' TYPE 'E'.
  ENDIF.

  AUTHORITY-CHECK OBJECT 'V_LIKP_VST'
    ID 'VSTEL' FIELD lv_vstel
    ID 'ACTVT' FIELD '02'.
  IF sy-subrc <> 0.
    MESSAGE |출하지점 { lv_vstel } 변경 권한이 없습니다.| TYPE 'E'.
  ENDIF.

  "2) 중복 전기 차단: 문서흐름에 자재문서(R)가 이미 달려 있는가
  SELECT COUNT(*) FROM vbfa
    WHERE vbelv   = @p_vbeln
      AND vbtyp_n = 'R'.
  IF sy-dbcnt > 0.
    MESSAGE '이미 출고전기된 납품입니다. 중복 전기를 차단합니다.' TYPE 'E'.
  ENDIF.

  IF p_test = abap_true.
    MESSAGE '테스트 모드: 검증만 수행했고 전기하지 않았습니다.' TYPE 'S'.
    RETURN.
  ENDIF.

  "3) BAPI로 출고전기 실행
  DATA: ls_header  TYPE bapiobdlvhdrcon,
        ls_control TYPE bapiobdlvhdrctrlcon,
        lt_return  TYPE STANDARD TABLE OF bapiret2.

  ls_header-deliv_numb    = p_vbeln.
  ls_control-deliv_numb   = p_vbeln.
  ls_control-post_gi_flag = abap_true.

  CALL FUNCTION 'BAPI_OUTB_DELIVERY_CONFIRM_DEC'
    EXPORTING
      header_data    = ls_header
      header_control = ls_control
      delivery       = p_vbeln
    TABLES
      return         = lt_return.

  IF line_exists( lt_return[ type = 'E' ] ).
    CALL FUNCTION 'BAPI_TRANSACTION_ROLLBACK'.
    LOOP AT lt_return INTO DATA(ls_ret) WHERE type CA 'EA'.
      WRITE / |{ ls_ret-type } { ls_ret-message }|.
    ENDLOOP.
  ELSE.
    CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'
      EXPORTING wait = abap_true.
    MESSAGE |납품 { p_vbeln ALPHA = OUT } 출고전기 완료.| TYPE 'S'.
  ENDIF.

전기 후 MB51에서 자재 FG-CAM-001, 이동유형 601로 조회하면 방금 생성된 자재문서가 보이고, 그 자재문서에서 회계문서로 이동하면 매출원가/재고자산 전표를 확인할 수 있습니다.

⚠️ 삽질 노트 — 자주 겪는 문제들

Q1. 납품을 만들었는데 MB51에 아무것도 안 나옵니다.

정상입니다. 자재문서는 출고전기 시점에만 생성됩니다. 납품 문서 생성은 LIKP/LIPS에만 기록을 남기며 재고관리(MM-IM) 쪽에는 아무 흔적도 남기지 않습니다. VL02N에서 출고전기를 완료한 뒤 다시 조회하세요.

Q2. PGI 버튼을 눌렀더니 "피킹 수량이 납품 수량과 다릅니다" 오류가 납니다.

피킹 관련 저장위치에서는 피킹 수량과 확인이 완료되어야 출고전기가 가능합니다. WM/EWM이 활성화된 플랜트라면 전송오더(LT03) 생성과 확인까지 끝나야 합니다. LIPS의 피킹 수량(PIKMG)과 납품 수량(LFIMG)이 일치하는지, 품목 피킹 상태가 완료인지 확인하세요.

Q3. 재고는 분명히 있는데 출고전기에서 재고 부족 오류가 납니다.

다른 미출고 납품이 같은 재고를 이미 물고 있을 가능성이 큽니다. 납품 문서는 재고 수량을 줄이지 않지만 가용량은 점유합니다. MD04나 CO09에서 해당 자재의 납품 소요를 확인하고, 위 2단계 리포트로 오래 방치된 납품을 찾아 정리(출고전기 또는 삭제)하세요.

Q4. VL09로 출고전기를 취소하려는데 주의할 점이 있나요?

세 가지를 먼저 확인해야 합니다. 첫째, 해당 납품에 청구 문서가 이미 생성되어 있으면 일반적으로 VF11로 청구를 먼저 취소해야 합니다. 둘째, 취소는 역이동유형 602로 새 자재문서를 만들어 재고를 되돌리는 방식이므로 전기일자가 속한 회계/자재 기간이 열려 있어야 합니다(월이 넘어갔다면 MMPV 마감 상태 확인). 셋째, 취소 후 납품 상태가 다시 'A/B'로 돌아가므로 피킹 상태와 후속 프로세스를 다시 점검해야 합니다. 취소 문서 역시 VBFA 문서흐름에 남으니 흐름이 꼬였는지 항상 문서흐름 화면으로 검증하는 습관을 권장합니다.

🚀 더 파볼 주제

출고전기까지 이해했다면 다음 갈래로 확장할 수 있습니다. (1) 청구와 수익 인식 — 매출원가는 PGI, 매출은 청구라는 시점 차이가 결산에 미치는 영향, (2) POD(Proof of Delivery) — 고객 인수 확인 후에 청구하는 시나리오에서 시점이 어떻게 달라지는지, (3) EWM 통합 환경의 출고 프로세스 — 창고 태스크와 PGI의 연결, (4) S/4HANA의 aATP(advanced ATP)와 백오더 처리, (5) MATDOC 기반의 재고 조회 CDS 뷰(I_MaterialDocumentItem 등) 활용. 특히 미출고 납품 모니터를 CDS 뷰 + Fiori 리스트 리포트로 옮겨보는 것은 좋은 다음 연습입니다.

📚 참고 링크 모음

댓글 0

아직 댓글이 없습니다.