이 예제로 얻는 것
SAP S/4HANA 환경에서 구매 요청(Purchase Requisition) 데이터를 조회할 때, 전통적으로는 EBAN 테이블을 직접 SELECT 해왔습니다. 하지만 ABAP Cloud, RAP, Fiori Elements 시대에는 I_PurchaseRequisition CDS 뷰를 사용하는 방식이 권장됩니다. 이 글은 EBAN에 익숙한 개발자가 I_PurchaseRequisition으로 부드럽게 전환할 수 있도록 개념·비교·실전 코드·마이그레이션 체크까지 다룹니다.
- VDM(Virtual Data Model) 계층에서 I_ 뷰의 역할 이해
- EBAN 필드 → CDS 시맨틱 필드 매핑 습득
- Association을 활용한 조인 없는 관련 데이터 접근
- ZC_ 소비 뷰(Consumption View) 작성 실습
- ABAP Unit + cl_cds_test_environment로 CDS 뷰 테스트
미리 알아두면 좋은 것
OpenSQL / ABAP SQL 기본 문법, Core Data Services(CDS) 뷰의 기초 개념(@AbapCatalog.sqlViewName, define view), 구매(MM) 모듈의 구매 요청(PR) 개념(BANFN, BNFPO), Eclipse ADT(ABAP Development Tools) 사용 경험이 있으면 이해가 훨씬 빠릅니다. RAP나 Fiori Elements 경험은 필수는 아니지만 도움이 됩니다.
실습 환경과 준비물
- SAP S/4HANA 2022 이상 (온프레미스) 또는 S/4HANA Cloud Public Edition 최신 릴리즈
- ABAP Platform 2022 이상 (VDM 뷰
I_PurchaseRequisition이 안정적으로 제공되는 기준) - ABAP Development Tools (ADT) for Eclipse 최신 버전
- 권한:
S_DEVELOP(개발), MM 모듈 데이터 조회 권한(M_BANF_*) - 테스트 데이터: 구매 요청 몇 건이 등록된 클라이언트(MM01/ME51N 등)
온프레미스에서는 SE11에서 EBAN 테이블 정의를, ADT에서 I_PurchaseRequisition을 열어 두 정의를 나란히 비교하면 이해가 빠릅니다. ABAP Cloud(Steampunk, 개발자 확장) 환경이라면 EBAN 직접 접근이 원천 차단되므로 CDS 뷰가 유일한 통로입니다.
핵심 개념: VDM 계층과 I_PurchaseRequisition의 위치
SAP의 VDM(Virtual Data Model)은 CDS 뷰를 3계층으로 분류합니다. 마치 데이터 시장의 도소매 구조와 비슷합니다.
- Basic View (I_ 접두어): 원천 테이블에 가장 가까운 뷰. EBAN, EKKO 같은 물리 테이블을 시맨틱 필드명으로 감싸고, 표준 Association을 제공합니다.
I_PurchaseRequisition이 여기 속합니다. - Composite View (C_ 접두어 - Composite / P_ 등): 여러 Basic View를 조합해 비즈니스 의미가 있는 데이터셋을 만듭니다.
- Consumption View (C_ 접두어): UI(Fiori Elements), OData Service 등 최종 소비를 위한 뷰. UI 어노테이션이 붙습니다.
EBAN을 도매 창고, I_PurchaseRequisition을 표준 규격 상자로 재포장한 물류센터, C_PurchaseRequisitionTP류를 완제품 매장이라고 생각하면 됩니다. 개발자는 물류센터 단계인 I_ 뷰부터 접근하는 것이 원칙입니다.
왜 EBAN 직접 접근을 지양하는가
- ABAP Cloud 호환성: Cloud Development Model에서는 릴리즈되지 않은 테이블 직접 접근이 금지됩니다. EBAN은 릴리즈 컨트랙트가
Not Released이지만,I_PurchaseRequisition은 Cloud Development(C1) 컨트랙트로 릴리즈되어 있습니다. - 시맨틱 필드명:
BANFN대신PurchaseRequisition,MENGE대신RequestedQuantity처럼 코드만 봐도 의미가 파악됩니다. - Association:
_Material,_Plant같은 Association으로 JOIN을 직접 작성하지 않고 관련 엔티티에 접근합니다. - 업그레이드 안전성: SAP가 물리 테이블 구조를 조정해도 CDS 뷰가 호환 레이어를 제공합니다.
I_PurchaseRequisition의 주요 필드
| CDS 필드 | EBAN 필드 | 의미 |
|---|---|---|
| PurchaseRequisition | BANFN | 구매 요청 번호 |
| PurchaseRequisitionItem | BNFPO | 구매 요청 아이템 |
| Material | MATNR | 자재 번호 |
| Plant | WERKS | 플랜트 |
| RequestedQuantity | MENGE | 요청 수량 |
| PurchasingOrganization | EKORG | 구매 조직 |
| AccountAssignmentCategory | KNTTP | 계정 지정 카테고리 |
| PurReqReleaseStatus | FRGZU | 릴리즈 상태 |
| CreationDate | BADAT | 생성일 |
1단계: 기본 조회 예제
가장 단순한 형태로 특정 플랜트의 미완료 구매 요청을 조회합니다. EBAN SELECT와 비교해 보면 필드명이 얼마나 읽기 쉬워졌는지 실감할 수 있습니다.
REPORT z_pr_basic_read.
DATA: lt_pr TYPE STANDARD TABLE OF i_purchaserequisition.
SELECT PurchaseRequisition,
PurchaseRequisitionItem,
Material,
Plant,
RequestedQuantity,
BaseUnit,
CreationDate,
PurReqReleaseStatus
FROM i_purchaserequisition
WHERE Plant = '1010'
AND IsDeleted = @abap_false
AND PurchaseRequisitionIsClosed = @abap_false
ORDER BY CreationDate DESCENDING
INTO TABLE @lt_pr
UP TO 100 ROWS.
LOOP AT lt_pr ASSIGNING FIELD-SYMBOL(<fs_pr>).
WRITE: / <fs_pr>-PurchaseRequisition,
<fs_pr>-PurchaseRequisitionItem,
<fs_pr>-Material,
<fs_pr>-RequestedQuantity,
<fs_pr>-BaseUnit.
ENDLOOP.
동일 조회를 EBAN으로 작성한다면 WHERE WERKS = '1010' AND LOEKZ = '' AND ...처럼 되어 도메인 지식이 없으면 해석이 어렵습니다.
2단계: 실무 시나리오 - Association과 에러 처리
구매 요청 목록과 함께 자재의 설명, 플랜트 이름까지 한 번에 가져오는 시나리오입니다. Association 표기 _Material, _Plant를 활용하면 JOIN을 명시적으로 쓰지 않아도 됩니다.
CLASS zcl_pr_reader DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
TYPES:
BEGIN OF ty_pr_enriched,
pr_number TYPE i_purchaserequisition-purchaserequisition,
pr_item TYPE i_purchaserequisition-purchaserequisitionitem,
material TYPE i_purchaserequisition-material,
material_desc TYPE string,
plant TYPE i_purchaserequisition-plant,
plant_name TYPE string,
quantity TYPE i_purchaserequisition-requestedquantity,
base_unit TYPE i_purchaserequisition-baseunit,
END OF ty_pr_enriched,
tt_pr_enriched TYPE STANDARD TABLE OF ty_pr_enriched WITH EMPTY KEY.
METHODS read_open_requisitions
IMPORTING iv_plant TYPE i_purchaserequisition-plant
RETURNING VALUE(rt_result) TYPE tt_pr_enriched
RAISING cx_sy_open_sql_db.
ENDCLASS.
CLASS zcl_pr_reader IMPLEMENTATION.
METHOD read_open_requisitions.
TRY.
SELECT pr~PurchaseRequisition AS pr_number,
pr~PurchaseRequisitionItem AS pr_item,
pr~Material AS material,
mat~MaterialName AS material_desc,
pr~Plant AS plant,
plant~PlantName AS plant_name,
pr~RequestedQuantity AS quantity,
pr~BaseUnit AS base_unit
FROM i_purchaserequisition AS pr
LEFT OUTER JOIN i_product AS mat
ON mat~Product = pr~Material
AND mat~Language = @sy-langu
LEFT OUTER JOIN i_plant AS plant
ON plant~Plant = pr~Plant
WHERE pr~Plant = @iv_plant
AND pr~PurchaseRequisitionIsClosed = @abap_false
INTO TABLE @rt_result.
CATCH cx_sy_open_sql_db INTO DATA(lx_sql).
RAISE EXCEPTION lx_sql.
ENDTRY.
ENDMETHOD.
ENDCLASS.
3단계: 프로덕션 - ZC_ 소비 뷰와 단위 테스트
미승인(릴리즈 대기) 구매 요청만 필터링하는 사용자 정의 뷰를 만들고, cl_cds_test_environment로 단위 테스트를 작성합니다.
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: '미승인 구매 요청 소비 뷰'
@Metadata.allowExtensions: true
define view entity ZC_PurReqPendingRelease
as select from I_PurchaseRequisition as PR
association [0..1] to I_Product as _Material
on $projection.Material = _Material.Product
association [0..1] to I_Plant as _Plant
on $projection.Plant = _Plant.Plant
{
key PR.PurchaseRequisition,
key PR.PurchaseRequisitionItem,
PR.Material,
PR.Plant,
PR.RequestedQuantity,
PR.BaseUnit,
PR.PurchasingGroup,
PR.PurchasingOrganization,
PR.AccountAssignmentCategory,
PR.CreationDate,
PR.PurReqReleaseStatus,
_Material,
_Plant
}
where PR.PurReqReleaseStatus = ''
and PR.PurchaseRequisitionIsClosed = false
and PR.IsDeleted = false;
CLASS ltcl_pr_pending DEFINITION FINAL FOR TESTING
DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
CLASS-DATA env TYPE REF TO if_cds_test_environment.
CLASS-METHODS class_setup.
CLASS-METHODS class_teardown.
METHODS setup.
METHODS pending_only FOR TESTING RAISING cx_static_check.
ENDCLASS.
CLASS ltcl_pr_pending IMPLEMENTATION.
METHOD class_setup.
env = cl_cds_test_environment=>create(
i_for_entity = 'ZC_PURREQPENDINGRELEASE' ).
ENDMETHOD.
METHOD class_teardown.
env->destroy( ).
ENDMETHOD.
METHOD setup.
env->clear_doubles( ).
ENDMETHOD.
METHOD pending_only.
DATA lt_double TYPE STANDARD TABLE OF i_purchaserequisition.
lt_double = VALUE #(
( PurchaseRequisition = '1000001' PurchaseRequisitionItem = '10'
Plant = '1010' Material = 'MAT-A' RequestedQuantity = '5'
PurReqReleaseStatus = '' PurchaseRequisitionIsClosed = abap_false
IsDeleted = abap_false )
( PurchaseRequisition = '1000002' PurchaseRequisitionItem = '10'
Plant = '1010' Material = 'MAT-B' RequestedQuantity = '2'
PurReqReleaseStatus = 'X' PurchaseRequisitionIsClosed = abap_false
IsDeleted = abap_false )
).
env->insert_test_data( lt_double ).
SELECT * FROM ZC_PurReqPendingRelease INTO TABLE @DATA(lt_actual).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_actual ) ).
cl_abap_unit_assert=>assert_equals( exp = '1000001'
act = lt_actual[ 1 ]-PurchaseRequisition ).
ENDMETHOD.
ENDCLASS.
자주 겪는 실수와 해결
Q1. EBAN에는 있는데 I_PurchaseRequisition에는 필드가 안 보입니다.
CDS 뷰는 시맨틱 필드만 노출합니다. EBAN의 LOEKZ(삭제 마크)는 IsDeleted로, FRGZU는 PurReqReleaseStatus로 이름이 바뀝니다. ADT에서 뷰를 열어 Field 탭을 확인하거나 F2로 원소스 매핑을 조회하세요.
Q2. Association 경로가 헷갈립니다.
Association 이름은 뷰 정의에서 association ... as _Material로 선언된 별칭 그대로 씁니다. ABAP SQL에서는 pr~\_Material.MaterialName 형태의 경로 표현식으로 접근합니다.
Q3. 대량 조회 시 성능이 EBAN 직접 SELECT보다 느립니다.
I_ 뷰는 여러 Association·계산 필드를 포함하므로 필요한 필드만 명시적으로 SELECT 하는 것이 중요합니다. SELECT *는 피하고, WHERE 조건을 인덱스가 있는 필드(PurchaseRequisition, Plant 등)로 좁혀야 합니다.
Q4. ABAP Cloud에서 EBAN을 직접 SELECT 했더니 신택스 에러가 납니다.
EBAN은 Cloud Development 컨트랙트로 릴리즈되어 있지 않기 때문에 정상 동작입니다. I_PurchaseRequisition이나 API 릴리즈된 소비 뷰만 사용해야 합니다.
마이그레이션 체크리스트
EBAN 직접 조회를 I_PurchaseRequisition으로 교체할 때 놓치기 쉬운 항목을 정리합니다.
BANFN→PurchaseRequisition,BNFPO→PurchaseRequisitionItemMATNR→Material,WERKS→Plant,MENGE→RequestedQuantityLOEKZ = ''→IsDeleted = abap_falseFRGZU→PurReqReleaseStatus(릴리즈 상태 boolean화)- MARA 조인(자재명) →
_MaterialAssociation 경로 - ABAP Unit:
cl_cds_test_environment로 ZC_ 뷰 WHERE 조건 격리 검증 - 병렬 결과 비교: 기존 EBAN 쿼리 행 수 vs CDS 뷰 결과 행 수 일치 확인
댓글 0
아직 댓글이 없습니다.