개요 및 핵심 포인트
이 글은 SAP S/4HANA의 표준 CDS View인 I_SupplierInvoice를 RBKP 테이블 구조와 FI(Financial Accounting) 연계 관점에서 다룹니다. MM-IV(Invoice Verification) 프로세스에서 생성된 공급업체 인보이스 헤더 데이터를 어떻게 CDS 계층에서 추상화하고, BKPF/BSEG와 같은 회계 전표 테이블과 어떻게 조인되는지를 실전 예제와 함께 정리합니다.
- RBKP 테이블 구조와 I_SupplierInvoice 필드 매핑 이해
- FI 전표(BKPF/BSEG)와의 연계 키 파악
- 공급업체 마스터(LFA1) 조회 패턴 습득
- 인보이스 승인 흐름(Blocked, Posted, Reversed) 상태 분기
- 프로덕션 환경에서의 권한·성능·로깅 처리 방안
알아두면 좋은 배경
이 글은 ABAP CDS View 기본 문법(define view, association), MM-IV 모듈의 인보이스 처리 흐름, FI 전표의 BKPF/BSEG 구조에 대한 기초 이해를 전제로 합니다. 또한 ADT(ABAP Development Tools)에서 CDS View를 활성화하고 데이터 미리보기를 실행할 수 있는 환경이 권장됩니다.
환경 및 버전 정보
아래 예제는 SAP S/4HANA 2022 / 2023 On-Premise 에디션과 S/4HANA Cloud Public Edition 환경 모두에서 일반적으로 동작하는 형태로 작성되었습니다. 다만 일부 필드는 릴리스에 따라 차이가 있을 수 있으므로 실제 시스템에서 SE11 또는 ADT의 Dictionary 뷰어로 확인이 필요합니다.
- SAP S/4HANA: 2022 FPS01 이상 권장
- ABAP Platform: 7.57 이상 (CDS Annotation 호환)
- 개발 도구: ADT for Eclipse 2023-06 이상
- 권한:
S_TABU_DIS(RBKP),S_RFC, FI 전표 조회용F_BKPF_BUK - 관련 테이블: RBKP(헤더), RSEG(아이템), BKPF/BSEG(FI 전표), LFA1(공급업체 마스터)
핵심 개념
RBKP는 MM-IV(Logistics Invoice Verification) 프로세스에서 공급업체 인보이스의 헤더 정보를 저장하는 클러스터드 테이블입니다. 인보이스 번호(BELNR)와 회계연도(GJAHR)가 기본 키이며, 전표 일자, 공급업체 코드, 통화, 총액, 결제 조건, 차단 사유 등을 담고 있습니다. RBKP의 데이터는 MIRO 트랜잭션 또는 BAPI(BAPI_INCOMINGINVOICE_CREATE)를 통해 생성되며, 동시에 FI 전표(BKPF/BSEG)가 자동 포스팅되어 회계 연계가 이루어집니다.
I_SupplierInvoice는 이 RBKP를 기반 데이터 소스로 삼아 CDS View 계층에서 표준화한 인터페이스 뷰(Interface View, "I_" 접두어)입니다. 비유하자면 RBKP가 "원자재 창고"라면 I_SupplierInvoice는 "검수·라벨링이 끝난 출고 진열대"에 해당합니다. 즉, 필드명은 의미 기반(SupplierInvoice, FiscalYear, InvoicingParty)으로 변경되고, 외화/현지통화 금액, 결제 조건, 차단 사유 등이 어노테이션 기반 메타데이터와 함께 노출됩니다.
FI 연계 흐름을 단순화하면 다음과 같습니다.
MIRO 포스팅 → RBKP/RSEG 생성 → BKPF/BSEG 자동 생성 → AWKEY 필드를 통해 연결
BKPF의 AWTYP는 "RMRP"(Invoice Verification) 값으로 채워지고, AWKEY는 "BELNR + GJAHR" 결합 키로 RBKP를 역참조합니다. I_SupplierInvoice는 이 관계를 association으로 추상화하여, 별도의 JOIN 없이도 회계 전표 헤더(_AccountingDocument)를 탐색할 수 있는 경로를 제공합니다. 또한 _Supplier association을 통해 LFA1 기반 공급업체 마스터로 손쉽게 이동할 수 있고, 차단 사유 코드(PaymentBlockingReason)를 통해 승인 워크플로우 상태를 판단합니다.
실전 예제 1단계 — 기본 조회
먼저 가장 기본적인 형태로, 특정 회계연도의 공급업체별 인보이스 총액을 집계하는 예제입니다.
REPORT z_supplier_invoice_basic.
DATA: lt_invoices TYPE TABLE OF i_supplierinvoice,
ls_invoice TYPE i_supplierinvoice.
SELECT supplierinvoice,
fiscalyear,
invoicingparty,
documentdate,
invoicegrossamount,
documentcurrency,
paymentblockingreason
FROM i_supplierinvoice
WHERE fiscalyear = '2025'
AND companycode = '1010'
INTO TABLE @lt_invoices
UP TO 100 ROWS.
LOOP AT lt_invoices INTO ls_invoice.
WRITE: / ls_invoice-supplierinvoice,
ls_invoice-invoicingparty,
ls_invoice-invoicegrossamount,
ls_invoice-documentcurrency.
ENDLOOP.
여기서 주의할 점은 SupplierInvoice 필드가 RBKP의 BELNR에 매핑된다는 것이며, 필드명은 의미 기반으로 변경되어 ABAP 개발자가 직관적으로 이해할 수 있도록 설계되어 있습니다.
실전 예제 2단계 — FI 전표 연계 및 차단 사유 분기
실무에서는 단순 조회를 넘어 회계 전표 연결, 차단 사유 분기, 에러 로깅이 필요합니다. 아래 CDS View는 I_SupplierInvoice를 확장하여 FI 전표 헤더(BKPF)와 공급업체 마스터(LFA1)를 association으로 결합한 사례입니다.
@AbapCatalog.sqlViewName: 'ZVENDINVFI'
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Vendor Invoice with FI link'
define view Z_VendorInvoiceWithFI
as select from I_SupplierInvoice as Inv
association [0..1] to I_AccountingDocument as _FiDoc
on _FiDoc.CompanyCode = Inv.CompanyCode
and _FiDoc.AccountingDocument = Inv.AccountingDocument
and _FiDoc.FiscalYear = Inv.FiscalYear
association [0..1] to I_Supplier as _Vendor
on _Vendor.Supplier = Inv.InvoicingParty
{
key Inv.SupplierInvoice,
key Inv.FiscalYear,
Inv.CompanyCode,
Inv.InvoicingParty,
_Vendor.SupplierName as VendorName,
Inv.DocumentDate,
Inv.PostingDate,
Inv.InvoiceGrossAmount,
Inv.DocumentCurrency,
Inv.PaymentBlockingReason,
case Inv.PaymentBlockingReason
when '' then 'RELEASED'
when 'A' then 'BLOCKED_PRICE'
when 'R' then 'BLOCKED_QTY'
else 'BLOCKED_OTHER'
end as ApprovalStatus,
_FiDoc.AccountingDocument as FiDocumentNumber,
_FiDoc.DocumentStatus as FiDocStatus,
_FiDoc,
_Vendor
}
위 View를 ABAP에서 호출하면서 차단 사유에 따라 분기 로깅을 수행하는 패턴은 다음과 같습니다.
METHOD process_vendor_invoices.
TRY.
SELECT supplierinvoice, fiscalyear, vendorname,
invoicegrossamount, approvalstatus, fidocumentnumber
FROM z_vendorinvoicewithfi
WHERE companycode = @iv_bukrs
AND postingdate IN @it_period
INTO TABLE @DATA(lt_result).
LOOP AT lt_result ASSIGNING FIELD-SYMBOL(<ls>).
CASE <ls>-approvalstatus.
WHEN 'BLOCKED_PRICE'.
log_event( iv_level = 'W'
iv_text = |Price block: { <ls>-supplierinvoice }| ).
WHEN 'BLOCKED_QTY'.
log_event( iv_level = 'W'
iv_text = |Qty block: { <ls>-supplierinvoice }| ).
WHEN 'RELEASED'.
append <ls> to rt_released.
ENDCASE.
ENDLOOP.
CATCH cx_sy_open_sql_db INTO DATA(lx_db).
log_event( iv_level = 'E' iv_text = lx_db->get_text( ) ).
RAISE EXCEPTION TYPE zcx_invoice_processing
EXPORTING previous = lx_db.
ENDTRY.
ENDMETHOD.
실전 예제 3단계 — 프로덕션 고려사항
대량 인보이스 처리 환경에서는 성능, 권한, 단위 테스트를 함께 설계해야 합니다. 아래는 패키지 크기 기반 배치, DCL 권한 적용, 테스트 더블 주입을 함께 적용한 형태입니다.
@MappingRole: true
define role Z_VendorInvoice_Role {
grant select on Z_VendorInvoiceWithFI
where CompanyCode = aspect pfcg_auth( F_BKPF_BUK, BUKRS, ACTVT = '03' );
}
CLASS zcl_invoice_batch DEFINITION
PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
METHODS constructor
IMPORTING io_reader TYPE REF TO zif_invoice_reader OPTIONAL.
PRIVATE SECTION.
DATA mo_reader TYPE REF TO zif_invoice_reader.
CONSTANTS c_package_size TYPE i VALUE 5000.
ENDCLASS.
CLASS zcl_invoice_batch IMPLEMENTATION.
METHOD constructor.
mo_reader = COND #( WHEN io_reader IS BOUND
THEN io_reader
ELSE NEW zcl_invoice_reader( ) ).
ENDMETHOD.
METHOD if_oo_adt_classrun~main.
DATA(lv_offset) = 0.
DO.
DATA(lt_chunk) = mo_reader->read_chunk(
iv_offset = lv_offset
iv_size = c_package_size ).
IF lt_chunk IS INITIAL.
EXIT.
ENDIF.
lv_offset = lv_offset + c_package_size.
ENDDO.
ENDMETHOD.
ENDCLASS.
핵심 포인트는 세 가지입니다. 첫째, DCL(define role)을 통해 CDS 레벨에서 회사코드 권한이 자동 적용되므로 ABAP 측에서 별도 AUTHORITY-CHECK를 누락해도 안전망이 형성됩니다. 둘째, 인터페이스(zif_invoice_reader) 주입으로 단위 테스트 시 RBKP를 직접 액세스하지 않고 더블로 대체할 수 있습니다. 셋째, 패키지 크기 기반 chunk 처리로 메모리 사용량을 일정 수준으로 유지합니다.
자주 마주치는 실수와 해결
실무 적용 시 가장 빈번한 함정과 FAQ를 정리합니다.
- Q1. I_SupplierInvoice 결과가 BKPF와 건수가 맞지 않습니다. RBKP에는 Parked(미등록), Reversed(취소) 상태가 모두 포함됩니다.
InvoiceReceiptIsReversed,DocumentHeaderStatus필드로 필터링해야 BKPF의 활성 전표 건수와 일치합니다. - Q2. AWKEY 조인이 동작하지 않습니다. BKPF의
AWKEY는 일반적으로 "BELNR(10) + GJAHR(4)" 결합 문자열입니다. CDS에서 직접 문자열 결합 조인은 성능 저하를 유발하므로, I_AccountingDocument의OriginalReferenceDocument또는 표준 association(_AccountingDocument)을 사용하는 것이 일반적으로 권장됩니다. - Q3. 권한 오류 없이 빈 결과만 반환됩니다.
@AccessControl.authorizationCheck: #CHECK어노테이션과 DCL이 모두 활성화되어 있고, 사용자에게 해당 회사코드(BUKRS) 권한이 없으면 데이터가 자동 필터링됩니다. SU53 대신 ST01의 Authorization Trace로 확인하는 편이 디버깅에 효과적입니다. - Q4. 외화 금액이 이상하게 표시됩니다.
InvoiceGrossAmount는 통화 참조 필드(DocumentCurrency)와 함께 어노테이션(@Semantics.amount.currencyCode)으로 묶여 있습니다. ABAP RTTI나 UI에서 통화 코드를 함께 전달하지 않으면 소수점 처리 오류가 발생합니다.
이어서 살펴볼 주제
이 글에서 다룬 I_SupplierInvoice 패턴을 익혔다면, 다음 주제로 확장하는 것을 권장합니다. 첫째, I_SupplierInvoiceItem(RSEG 기반)으로 라인 아이템 분석 및 GR/IR 매칭 로직 구현. 둘째, I_JournalEntry CDS View와 결합하여 G/L 계정 단위 잔액 검증. 셋째, RAP(RESTful ABAP Programming Model) 기반 Behavior Definition을 적용해 인보이스 승인/차단 해제 액션을 OData 서비스로 노출. 넷째, Embedded Analytics에서 C_SupplierInvoiceQuery 같은 Consumption View로 KPI 대시보드 구성.
댓글 0
아직 댓글이 없습니다.