이 글이 답하는 질문
구매오더(ME21N)에서 자재와 공급업체를 입력했는데 단가 칸이 비어 있거나 0으로 뜨는 상황, MM 담당자라면 한 번쯤 겪습니다. 이 글은 "PO 단가는 대체 어디서 오는가"라는 질문에 데이터 레벨로 답합니다. 예제 기준은 SAP S/4HANA 2023 온프레미스이며, ECC 6.0에서도 동일한 구조가 적용됩니다.
- ☐ PO 단가가 자동으로 채워지는 4가지 경로(정보레코드·계약·소스리스트·수동)를 구분할 수 있다
- ☐ EINA → EINE → A017/A018 → KONP로 이어지는 저장 구조를 추적할 수 있다
- ☐ 단가가 안 뜨는 원인 6가지를 트랜잭션과 테이블로 즉시 점검할 수 있다
- ☐ 진단용 ABAP 리포트를 직접 만들어 운영에 쓸 수 있다
미리 깔고 가야 할 배경
MM 구매 프로세스의 기본 흐름(PR → PO → GR → IV)과 ME21N·ME13 정도의 트랜잭션 경험이 있다면 충분합니다. ABAP 코드는 7.40 이상 문법(인라인 선언, 신 Open SQL)을 쓰므로 SELECT 문을 읽을 수 있는 수준이면 됩니다. 조건 기법(Condition Technique)을 SD에서 접해봤다면 절반은 이미 아는 내용입니다.
테스트 환경
이 글의 모든 코드와 시나리오는 가상의 회사 한빛전자 기준으로 작성했습니다.
- 시스템: SAP S/4HANA 2023 FPS01 온프레미스 (ECC 6.0 EhP8에서도 동일 동작 확인 가능)
- 구매조직:
HB01/ 플랜트:P100(수원공장) - 자재:
HB-CHIP-1000(반도체 부품) / 공급업체:100234(세미콘상사) - 조건 스키마: 표준
RM0000, 총가격 조건유형PB00(접근순서 0002) - 필요 권한: SE38 실행, ME11~ME13, SE16N 조회 권한
핵심 개념 — PO 단가가 채워지는 네 갈래 길
정보레코드(Info Record)는 "특정 공급업체가 특정 자재를 얼마에, 며칠 만에 공급하는가"를 담는 단골 거래 명함이라고 생각하면 쉽습니다. 명함 앞면(일반 데이터)이 EINA, 뒷면의 구매조직별 거래조건이 EINE입니다. 그런데 정작 단가 자체는 EINE에 있지 않습니다. 가격은 조건 기법을 타고 별도 조건 테이블에 저장됩니다.
공급업체 100234 + 자재 HB-CHIP-1000
│
EINA (일반: 자재x업체 조합, INFNR 채번)
│
EINE (구매조직 HB01 / 플랜트 P100 / 정보카테고리 ESOKZ)
│
A017 (플랜트별 조건키) ──없으면──▶ A018 (구매조직별 조건키)
│ KNUMH(조건레코드 번호) + 유효기간 DATAB~DATBI
▼
KONP (금액 KBETR / 통화 KONWA / 가격단위 KPEIN)
ME21N에서 단가가 결정되는 경로는 일반적으로 다음 우선순위를 따릅니다.
| 순위 | 가격 소스 | 저장 위치 | 적용 조건 |
|---|---|---|---|
| 1 | 사용자 수동 입력 | 문서 조건 (PBXX) | 입력하면 항상 최우선 |
| 2 | 계약(Outline Agreement) 조건 | A016 → KONP | PO를 계약 참조로 생성했을 때 |
| 3 | 정보레코드 플랜트별 조건 | A017 → KONP | 유효기간 내 PB00 존재 시 |
| 4 | 정보레코드 구매조직별 조건 | A018 → KONP | A017이 없을 때 폴백 |
| 5 | 최종 PO 단가 | EINE-NETPR, 이력은 EIPA | 조건이 없고 커스터마이징이 허용할 때 |
소스리스트(ME01, 테이블 EORD)는 이 표에 없다는 점이 중요합니다. 소스리스트는 가격을 갖고 있지 않습니다. "이 자재는 이 업체(또는 이 계약)에서 사라"고 공급원을 지정하는 출입 명부일 뿐이며, 지정된 공급원의 정보레코드나 계약 조건이 결과적으로 단가를 결정합니다. 즉 소스리스트가 비어 있으면 공급업체 자동 제안이 끊기고, 그 결과 단가 결정까지 도미노처럼 실패하는 구조입니다.
직접 해보기 — 3단계 실습
1단계 (기본): 정보레코드 조건을 데이터로 직접 읽기
ME11로 업체 100234 × 자재 HB-CHIP-1000 정보레코드를 만들고 조건 화면에서 PB00 = 12,500 KRW / 1 EA를 등록했다고 합시다. 아래 리포트는 ME13이 내부적으로 밟는 경로(EINA → EINE → A017/A018 → KONP)를 그대로 재현합니다.
REPORT zmm_inforec_price.
" 한빛전자 예제: 정보레코드 PB00 조건 직접 조회
PARAMETERS: p_lifnr TYPE elifn OBLIGATORY DEFAULT '0000100234',
p_matnr TYPE matnr OBLIGATORY DEFAULT 'HB-CHIP-1000',
p_ekorg TYPE ekorg OBLIGATORY DEFAULT 'HB01',
p_werks TYPE werks_d DEFAULT 'P100',
p_datum TYPE datum DEFAULT sy-datum.
START-OF-SELECTION.
" 1) 일반 데이터(EINA): 업체 x 자재 조합 존재 여부
SELECT SINGLE infnr, loekz FROM eina
WHERE lifnr = @p_lifnr AND matnr = @p_matnr
INTO @DATA(ls_eina).
IF sy-subrc <> 0.
WRITE / '[X] EINA 없음: 정보레코드 자체가 없음 → ME11 생성 필요'. RETURN.
ELSEIF ls_eina-loekz IS NOT INITIAL.
WRITE / '[X] EINA 삭제 플래그 → ME15에서 확인'. RETURN.
ENDIF.
" 2) 구매조직 데이터(EINE): 플랜트별 → 구매조직 레벨 순서로 탐색
SELECT SINGLE loekz, netpr, waers, peinh FROM eine
WHERE infnr = @ls_eina-infnr AND ekorg = @p_ekorg
AND esokz = '0' AND werks = @p_werks
INTO @DATA(ls_eine).
IF sy-subrc <> 0.
SELECT SINGLE loekz, netpr, waers, peinh FROM eine
WHERE infnr = @ls_eina-infnr AND ekorg = @p_ekorg
AND esokz = '0' AND werks = @space
INTO @ls_eine.
IF sy-subrc <> 0.
WRITE / '[X] EINE 없음: 이 구매조직/플랜트용 뷰 미생성'. RETURN.
ENDIF.
ENDIF.
" 3) 조건키: A017(플랜트별) 우선, 없으면 A018(구매조직별)
SELECT SINGLE knumh, datab, datbi FROM a017
WHERE kappl = 'M' AND kschl = 'PB00'
AND lifnr = @p_lifnr AND matnr = @p_matnr
AND ekorg = @p_ekorg AND werks = @p_werks AND esokz = '0'
AND datab <= @p_datum AND datbi >= @p_datum
INTO @DATA(ls_key).
IF sy-subrc <> 0.
SELECT SINGLE knumh, datab, datbi FROM a018
WHERE kappl = 'M' AND kschl = 'PB00'
AND lifnr = @p_lifnr AND matnr = @p_matnr
AND ekorg = @p_ekorg AND esokz = '0'
AND datab <= @p_datum AND datbi >= @p_datum
INTO @ls_key.
ENDIF.
IF ls_key-knumh IS INITIAL.
WRITE: / '[!] 유효한 PB00 조건 없음. EINE-NETPR(최종단가)',
ls_eine-netpr CURRENCY ls_eine-waers, '만 참고 가능'. RETURN.
ENDIF.
" 4) 조건 명세(KONP): 실제 금액
SELECT kbetr, konwa, kpein, kmein, loevm_ko FROM konp
WHERE knumh = @ls_key-knumh INTO TABLE @DATA(lt_konp).
LOOP AT lt_konp INTO DATA(ls_konp) WHERE loevm_ko IS INITIAL.
WRITE: / '[O] 단가', ls_konp-kbetr CURRENCY ls_konp-konwa,
ls_konp-konwa, '/', ls_konp-kpein, ls_konp-kmein,
'(유효', ls_key-datab, '~', ls_key-datbi, ')'.
ENDLOOP.
2단계 (실무): 단가 결정 경로 전체를 진단하는 트레이스 리포트
실무에서는 "왜 안 뜨는지"를 우선순위 순서대로 한 번에 훑는 리포트가 유용합니다. 소스리스트 차단, 계약 만료, 조건 부재를 단계별 로그로 남깁니다.
REPORT zmm_price_path_trace.
" 우선순위대로 진단: ①소스리스트 ②계약 ③정보레코드 조건 ④가격이력
PARAMETERS: p_matnr TYPE matnr OBLIGATORY, p_lifnr TYPE elifn OBLIGATORY,
p_ekorg TYPE ekorg DEFAULT 'HB01',
p_werks TYPE werks_d DEFAULT 'P100',
p_datum TYPE datum DEFAULT sy-datum.
DATA lt_log TYPE TABLE OF string.
START-OF-SELECTION.
" STEP 1. 소스리스트(EORD): 공급원 지정/차단 여부
SELECT lifnr, ebeln, flifn, notkz, autet FROM eord
WHERE matnr = @p_matnr AND werks = @p_werks
AND vdatu <= @p_datum AND bdatu >= @p_datum
INTO TABLE @DATA(lt_eord).
IF lt_eord IS INITIAL.
APPEND |[1] 소스리스트 없음: 플랜트가 소스리스트 필수면 여기서 실패| TO lt_log.
ELSE.
LOOP AT lt_eord INTO DATA(ls_eord).
APPEND COND #( WHEN ls_eord-notkz IS NOT INITIAL
THEN |[1] 업체 { ls_eord-lifnr } 차단(NOTKZ) → 공급원 제외|
ELSE |[1] 공급원 { ls_eord-lifnr } 유효, 고정={ ls_eord-flifn }| )
TO lt_log.
ENDLOOP.
ENDIF.
" STEP 2. 유효한 계약(EKKO BSTYP='K')
SELECT k~ebeln, p~ebelp, p~netpr, k~waers FROM ekko AS k
INNER JOIN ekpo AS p ON p~ebeln = k~ebeln
WHERE k~bstyp = 'K' AND k~lifnr = @p_lifnr AND k~ekorg = @p_ekorg
AND p~matnr = @p_matnr AND p~loekz = @space
AND k~kdatb <= @p_datum AND k~kdate >= @p_datum
INTO TABLE @DATA(lt_ctr).
APPEND COND #( WHEN lt_ctr IS INITIAL
THEN |[2] 유효 계약 없음 → 정보레코드로 폴백|
ELSE |[2] 계약 { lt_ctr[ 1 ]-ebeln } 존재: 참조 생성 시 이 단가 적용| )
TO lt_log.
" STEP 3. 정보레코드 조건 유무 (A017 → A018)
SELECT SINGLE knumh FROM a017
WHERE kappl = 'M' AND kschl = 'PB00' AND lifnr = @p_lifnr
AND matnr = @p_matnr AND ekorg = @p_ekorg AND werks = @p_werks
AND esokz = '0' AND datab <= @p_datum AND datbi >= @p_datum
INTO @DATA(lv_knumh).
IF sy-subrc <> 0.
SELECT SINGLE knumh FROM a018
WHERE kappl = 'M' AND kschl = 'PB00' AND lifnr = @p_lifnr
AND matnr = @p_matnr AND ekorg = @p_ekorg AND esokz = '0'
AND datab <= @p_datum AND datbi >= @p_datum INTO @lv_knumh.
ENDIF.
APPEND COND #( WHEN lv_knumh IS INITIAL
THEN |[3] 유효한 PB00 조건 없음 → 유효기간/조직 불일치 의심|
ELSE |[3] 조건레코드 { lv_knumh } 발견 → KONP에서 단가 결정| ) TO lt_log.
" STEP 4. 최후 폴백: 가격 이력(EIPA)의 마지막 PO
SELECT SINGLE infnr FROM eina
WHERE lifnr = @p_lifnr AND matnr = @p_matnr INTO @DATA(lv_infnr).
IF sy-subrc = 0.
SELECT ebeln, ebelp FROM eipa WHERE infnr = @lv_infnr
ORDER BY ebeln DESCENDING INTO TABLE @DATA(lt_hist) UP TO 1 ROWS.
IF lt_hist IS NOT INITIAL.
APPEND |[4] 최종 PO { lt_hist[ 1 ]-ebeln } 이력 존재(참고용)| TO lt_log.
ENDIF.
ENDIF.
LOOP AT lt_log INTO DATA(lv_line). WRITE / lv_line. ENDLOOP.
3단계 (프로덕션): 권한·성능·테스트 보강
운영 배포 전에는 세 가지를 챙기는 것이 권장됩니다. 첫째, 구매조직 권한 체크를 넣어 타 조직 단가 노출을 막습니다. 둘째, A017·A018 조회는 전체 키를 WHERE에 지정했으므로 기본 키 인덱스를 그대로 타지만, 자재 범위 조회로 확장할 경우 FOR ALL ENTRIES 대신 조인으로 바꾸는 편이 HANA에서 유리합니다. 셋째, 날짜 경계(유효 시작일 = 오늘)를 ABAP Unit으로 고정해 회귀를 방지합니다.
" 권한 체크 예 (오브젝트명은 SU21에서 자사 릴리스 기준 확인 권장)
AUTHORITY-CHECK OBJECT 'M_EINF_EKO'
ID 'EKORG' FIELD p_ekorg
ID 'ACTVT' FIELD '03'.
IF sy-subrc <> 0.
MESSAGE '해당 구매조직 조회 권한이 없습니다' TYPE 'E'.
ENDIF.
삽질 노트 — 단가가 안 뜨는 원인과 처방
현장에서 자주 만나는 원인을 점검 순서대로 정리했습니다.
- 조건 유효기간 만료 — A017/A018의 DATBI가 과거. ME13 → 조건 → 유효기간 목록으로 확인하고 ME12에서 기간 연장.
- 구매조직·플랜트 불일치 — EINE가 HB01용인데 PO는 다른 구매조직, 또는 조건이 A017(플랜트 P100)로만 있는데 PO 플랜트가 P200인 경우. SE16N으로 A017 키를 직접 대조.
- 조건유형·스키마 미설정 — 커스텀 스키마에 PB00 계열이 빠졌거나 접근순서가 다른 경우. MEK1/MEK3과 스키마 결정(공급업체 스키마그룹) 확인.
- 소스리스트 미등록·차단 — 플랜트에 소스리스트 필수 플래그가 켜져 있는데 ME01 미등록이거나 NOTKZ 차단. 공급원 결정 실패 → 단가 결정도 실패.
- 정보 카테고리(ESOKZ) 불일치 — 표준(0) 정보레코드만 있는데 PO 품목범주가 하청(L)이면 하청용(3) 레코드를 별도로 찾으므로 단가가 비어 있음.
- 삭제 플래그 — EINA/EINE-LOEKZ 또는 KONP-LOEVM_KO. 화면에선 멀쩡해 보여 놓치기 쉽습니다.
Q1. ME13에는 조건이 보이는데 PO에는 안 뜹니다.
A. 가격결정 기준일 문제일 가능성이 큽니다. EINE의 가격결정일 카테고리가 납품일 기준이면, 납품일 시점에 유효한 조건이 없어서 탈락합니다. 조건 시작일이 미래인 경우도 동일 증상입니다.
Q2. 정보레코드는 있는데 단가가 0으로 뜹니다.
A. 조건 레코드가 없고 EINE-NETPR도 0인 상태입니다. 과거 PO에서 InfoUpdate 체크를 안 해 최종단가가 갱신되지 않은 전형적 케이스로, ME12에서 조건을 정식 등록하는 것이 권장됩니다.
Q3. 계약을 만들었는데 여전히 정보레코드 단가가 적용됩니다.
A. PO를 계약 참조 없이 생성하면 계약 조건(A016)은 타지 않습니다. 소스리스트에서 계약 번호를 공급원으로 고정하면 자동 참조가 일반적으로 유도됩니다.
핵심 한 줄
PO 단가는 화면이 아니라 A017/A018 → KONP의 유효한 조건 레코드에서 온다 — 안 뜨면 키(조직·플랜트·카테고리)와 유효기간부터 대조하라. 참고로 S/4HANA로 전환해도 이 마스터 조건 테이블 구조(Axxx·KONH·KONP)는 그대로 유지되며, 바뀐 것은 문서 조건(KONV → PRCD_ELEMENTS) 쪽입니다.
더 파볼 주제
단가 결정을 이해했다면 다음 주제로 확장해 보세요. 시간 의존 조건의 스케일(수량 할인) 설계, 계약 소진 추적(ME33K·릴리즈 통계), 쿼터 배분(Quota Arrangement)과 소스리스트의 상호작용, 그리고 S/4HANA의 Sourcing & Procurement 앱에서 정보레코드를 다루는 Fiori 기반 워크플로가 자연스러운 다음 코스입니다. 조건 기법 자체를 깊게 파려면 SD 프라이싱과 비교해 보는 것도 좋은 훈련이 됩니다.
참고 링크 모음
댓글 0
아직 댓글이 없습니다.