이 글에서 다루는 것과 도달 지점
S/4HANA 환경에서 공급사 인보이스(Supplier Invoice, MIRO/MIR7로 등록된 문서)를 조회할 때 여전히 RBKP·RSEG 테이블을 직접 SELECT하는 코드가 많습니다. 이 접근은 R/3 시대의 유산이며, S/4HANA 이후에는 릴리스된 CDS View I_SupplierInvoice와 I_SupplierInvoiceItem을 활용하는 것이 권장됩니다. 이 글에서는 헤더/라인 테이블 직접 접근이 유발하는 문제, 공용 CDS 뷰의 필드 구조, 그리고 실무에서 자주 요구되는 미결 인보이스 조회·금액 집계·결제 분석 시나리오까지 단계별로 정리합니다.
- RBKP/RSEG의 구조적 한계와 CDS 대체 배경 이해
- I_SupplierInvoice 핵심 필드와 어소시에이션 파악
- 미결 인보이스(Payment/Clearing Status) 조회 실전 예제
- AP(매입채무) 보고서용 금액 집계 패턴 습득
- 성능·권한·감사 관점의 프로덕션 고려사항 체크
사전에 갖춰야 할 배경
ABAP OpenSQL(또는 ABAP SQL) 문법에 익숙하고, MM(구매)/FI(재무)에서 인보이스 검증(Logistics Invoice Verification, LIV) 프로세스를 어느 정도 이해하고 있어야 합니다. 또한 CDS View의 어노테이션(@ObjectModel, @Semantics), Association, 그리고 릴리스 상태(C1/C2 API 계약) 개념을 알고 있으면 이해가 빠릅니다. ADT(ABAP Development Tools) 기반 이클립스 환경 사용 경험도 필요합니다.
테스트 환경과 도구 준비
이 글의 예제는 다음 환경 기준으로 작성했습니다.
- SAP S/4HANA 2022 (온프레미스) 또는 SAP S/4HANA Cloud, private edition 2023 이상
- ABAP Platform 7.57 / ABAP SQL 지원
- ADT for Eclipse 2024-03 이상
- 테스트 데이터: MIRO 트랜잭션으로 등록된 공급사 인보이스 다수, FI 문서 전기 완료 상태 일부 포함
- 권한:
S_TABU_DIS또는 CDS 권한 오브젝트(S_RS_AUTH등)에 따른 조회 권한
S/4HANA 릴리스에 따라 필드명이 미세하게 다를 수 있으므로 시스템의 ADT에서 I_SupplierInvoice를 열어 실제 요소 이름을 확인하는 것을 권장합니다. 특히 클라우드 에디션에서는 계약 등급이 C1(Public API)인지 확인해야 나중에 릴리스 변경에 안전합니다.
왜 RBKP/RSEG 직접 SELECT를 피해야 하는가
RBKP는 인보이스 헤더, RSEG는 인보이스 라인 아이템을 담는 테이블입니다. R/3 시절부터 존재했고, ABAP 개발자에게는 익숙합니다. 하지만 다음과 같은 이유로 S/4HANA에서는 직접 SELECT를 지양해야 합니다.
비유하자면 RBKP/RSEG는 "부품 창고의 원자재"이고, I_SupplierInvoice는 "이미 조립되어 검수 마친 완제품"입니다. 원자재로 매번 조립하다 보면 조립 순서를 실수하거나 부속품을 빠뜨리기 쉽습니다.
첫째, 인보이스 상태 판별이 복잡합니다. 결제 완료 여부는 BKPF/BSEG의 청산 필드(AUGBL, AUGDT)와 조인해야 확정할 수 있는데, 이를 직접 구현하면 예외 케이스(부분 결제, 역분개)에서 오류가 잦습니다. 둘째, 통화 필드 변환(문서 통화, 로컬 통화, 그룹 통화)을 앱단에서 다시 처리해야 합니다. 셋째, S/4HANA는 MM/FI 통합 문서 흐름이 재설계되어 있어, RBKP만 봐서는 FI 반영 여부를 정확히 알 수 없습니다.
반면 I_SupplierInvoice는 SAP가 이러한 조인·변환·상태 계산을 이미 View 안에 캡슐화한 재사용 가능한 데이터 모델입니다. Association을 통해 공급사 마스터, 회사 코드, 라인 아이템, 세금 정보에 자연스럽게 연결됩니다. 또한 릴리스된 View이므로 Fiori 앱, RAP 서비스, CDP(Cloud Data Platform) 연동에서도 동일한 시맨틱을 공유할 수 있습니다.
주요 필드는 다음과 같습니다. 이름은 시맨틱 기반이라 가독성이 높습니다.
SupplierInvoice— 인보이스 문서 번호 (RBKP-BELNR)FiscalYear— 회계 연도 (RBKP-GJAHR)CompanyCode— 회사 코드 (BUKRS)Supplier— 공급사 번호 (LIFNR)DocumentDate/PostingDate— 문서일 / 전기일InvoiceGrossAmount— 문서 통화 총액DocumentCurrency— 문서 통화AccountingDocument— 연결된 FI 문서 번호PaymentTerms,DueCalculationBaseDate— 결제 조건 관련
단계별 실전 예제
1단계 · 기본 조회: 특정 공급사의 최근 인보이스
가장 단순한 시나리오부터 시작합니다. 특정 회사 코드에서 지정한 공급사의 최근 회계연도 인보이스를 조회합니다. RBKP 직접 SELECT와 비교해 보면 조인 없이도 공급사명·통화·전기일이 함께 나옵니다.
DATA lt_invoice_head TYPE TABLE OF i_supplierinvoice.
SELECT SupplierInvoice,
FiscalYear,
CompanyCode,
Supplier,
DocumentDate,
PostingDate,
InvoiceGrossAmount,
DocumentCurrency,
AccountingDocument
FROM i_supplierinvoice
WHERE CompanyCode = @iv_bukrs
AND Supplier = @iv_lifnr
AND FiscalYear = @iv_gjahr
ORDER BY PostingDate DESCENDING
INTO TABLE @lt_invoice_head
UP TO 100 ROWS.
IF sy-subrc = 0.
cl_demo_output=>display( lt_invoice_head ).
ENDIF.
동일한 결과를 RBKP로 직접 뽑으려면 LFA1(공급사명), T001(회사코드) 조인이 최소 2개 필요합니다. CDS 뷰는 Association _Supplier를 통해 필요할 때만 확장 조회할 수 있어 성능·가독성 모두 유리합니다.
2단계 · 실무 시나리오: 미결 인보이스 + 헤더/라인 결합
매입채무팀에서 "결제 대기 중인 인보이스와 각 라인의 PO 참조를 함께 보고 싶다"는 요구가 흔합니다. 이때 I_SupplierInvoiceItem과 결합하며, 상태 필드로 필터링합니다.
TYPES: BEGIN OF ty_open_invoice,
supplier_invoice TYPE i_supplierinvoice-supplierinvoice,
fiscal_year TYPE i_supplierinvoice-fiscalyear,
supplier TYPE i_supplierinvoice-supplier,
posting_date TYPE i_supplierinvoice-postingdate,
gross_amount TYPE i_supplierinvoice-invoicegrossamount,
currency TYPE i_supplierinvoice-documentcurrency,
purchase_order TYPE i_supplierinvoiceitem-purchaseorder,
po_item TYPE i_supplierinvoiceitem-purchaseorderitem,
item_amount TYPE i_supplierinvoiceitem-suppliernvoiceitmamount,
END OF ty_open_invoice.
DATA lt_open TYPE TABLE OF ty_open_invoice.
TRY.
SELECT h~supplierinvoice,
h~fiscalyear,
h~supplier,
h~postingdate,
h~invoicegrossamount,
h~documentcurrency,
i~purchaseorder,
i~purchaseorderitem,
i~suppliernvoiceitmamount
FROM i_supplierinvoice AS h
INNER JOIN i_supplierinvoiceitem AS i
ON h~supplierinvoice = i~supplierinvoice
AND h~fiscalyear = i~fiscalyear
WHERE h~companycode = @iv_bukrs
AND h~paymentstatus = 'P' " P = 미결(Pending) 예시 코드
AND h~documentdate BETWEEN @iv_from AND @iv_to
ORDER BY h~postingdate
INTO CORRESPONDING FIELDS OF TABLE @lt_open.
IF sy-subrc <> 0.
MESSAGE i208(00) WITH '조회 결과가 없습니다.'.
ENDIF.
CATCH cx_sy_open_sql_db INTO DATA(lx_sql).
" 로깅 프레임워크로 전달 (BAL, ALM 등)
cl_bali_free_text_setter=>create(
severity = if_bali_constants=>c_severity_error
text = lx_sql->get_text( ) ).
ENDTRY.
포인트는 세 가지입니다. 첫째, JOIN 키에 FiscalYear를 반드시 포함해야 합니다. 인보이스 번호는 연도별로 재시작되므로 누락 시 데이터 중복이 발생합니다. 둘째, 상태 코드 값(예: 'P')은 시스템 커스터마이징에 따라 다를 수 있으므로 상수화하거나 도메인 값을 조회해서 관리하는 것이 안전합니다. 셋째, DB 예외를 잡아 감사 로그로 남기는 것이 실무에서 표준입니다.
3단계 · 프로덕션: 공급사별 총 인보이스 금액 집계와 성능 튜닝
월별 AP 대시보드용 집계입니다. 대량 데이터를 다루므로 DB에서 GROUP BY를 처리하고, 결과만 앱단으로 가져옵니다. 세션 통화로 환산이 필요하면 CURRENCY_CONVERSION SQL 함수를 사용합니다.
TYPES: BEGIN OF ty_ap_summary,
supplier TYPE i_supplierinvoice-supplier,
fiscal_year TYPE i_supplierinvoice-fiscalyear,
invoice_count TYPE i,
gross_amount_lc TYPE p LENGTH 15 DECIMALS 2,
local_currency TYPE i_supplierinvoice-companycodecurrency,
END OF ty_ap_summary.
DATA lt_ap TYPE TABLE OF ty_ap_summary.
SELECT supplier,
fiscalyear,
COUNT( * ) AS invoice_count,
SUM( CURRENCY_CONVERSION(
amount => invoicegrossamount,
source_currency => documentcurrency,
target_currency => companycodecurrency,
exchange_rate_date => postingdate,
error_handling => 'SET_TO_NULL' ) ) AS gross_amount_lc,
companycodecurrency AS local_currency
FROM i_supplierinvoice
WHERE companycode = @iv_bukrs
AND fiscalyear = @iv_gjahr
AND clearingstatus <> 'C' " 청산 완료 제외
GROUP BY supplier, fiscalyear, companycodecurrency
ORDER BY gross_amount_lc DESCENDING
INTO CORRESPONDING FIELDS OF TABLE @lt_ap
UP TO 500 ROWS.
운영 관점 체크리스트입니다.
- 인덱스:
CompanyCode + FiscalYear + Supplier조합은 HANA에서 컬럼스토어로 처리되지만, 대량 조회 시 SQL Monitor로 실행 계획을 확인해 두면 좋습니다. - 단위 테스트:
CL_OSQL_TEST_ENVIRONMENT로 CDS 뷰를 더블링하여, 상태 코드 로직을 재현 가능한 테스트로 커버합니다. - 권한: 회사 코드/공급사에 대한 DCL(Data Control Language) 적용 여부를 확인해야 마스킹이 자동 반영됩니다.
- 로깅: BAL(Application Log) 또는 ALM(Application Lifecycle Management)에 실행 이력을 남겨 감사 추적을 확보합니다.
실수하기 쉬운 지점과 문제 해결
Q1. RBKP 대신 CDS를 썼는데 데이터가 더 적게 나옵니다. — CDS 뷰는 릴리스된 시맨틱을 기준으로 특정 문서 유형(예: 취소된 인보이스, Park 상태 문서)을 필터링해 두는 경우가 있습니다. 원인 파악은 ADT에서 CDS 뷰의 WHERE 절과 @ObjectModel.dataCategory를 확인하는 것부터 시작합니다. Park 문서까지 보려면 관련 확장 뷰(예: I_SupplierInvoiceParked 등 시스템별 확장) 여부를 검토하세요.
Q2. 상태 필드 값이 도메인 문서와 다르게 표기됩니다. — PaymentStatus, ClearingStatus는 도메인 고정값과 시맨틱 어노테이션이 붙어 있는 경우가 많습니다. 리터럴로 하드코딩하지 말고, DDIC 조회 또는 CDS Value Help를 통해 코드 목록을 얻어 상수 클래스로 관리하는 것이 안전합니다.
Q3. JOIN 후 라인 금액 합계가 헤더 총액과 안 맞습니다. — 헤더 JOIN 시 중복 확대를 확인하세요. 라인이 N건이면 헤더 필드가 N번 반복되므로, 헤더 금액을 SUM 하면 N배가 됩니다. 헤더 금액은 서브쿼리 또는 DISTINCT 후 집계하거나, 아예 라인 금액을 합산해서 검증용으로 쓰는 것이 일반적입니다.
Q4. 성능이 느립니다. — SELECT * 대신 필요한 필드만 명시하고, Association은 실제 참조하는 컬럼에만 접근하세요. Association 필드를 SELECT 리스트에 넣으면 그때만 조인이 발생하므로, 사용하지 않는 어소시에이션은 성능에 영향을 주지 않습니다.
확장해서 살펴볼 만한 주제
이 글의 내용을 익혔다면, 심화 주제로 I_JournalEntry(회계 저널) 뷰와 결합해 FI 반영까지 이어지는 전체 문서 흐름을 추적해 보세요. 또한 RAP(RESTful ABAP Programming) 모델로 인보이스 조회 서비스를 노출하거나, SAP Analytics Cloud에서 I_SupplierInvoice 기반 라이브 리포트를 구성하는 것도 자연스러운 확장입니다. CAP(Cloud Application Programming) 환경이라면 SAP BTP의 ABAP Environment에서 동일 뷰를 소비하는 서비스 개발도 가능합니다.
더 읽어볼 만한 자료
댓글 0
아직 댓글이 없습니다.