개요와 이 글에서 다루는 범위
SAP S/4HANA 환경에서 회계 전표 헤더 정보를 조회할 때 과거에는 BKPF 테이블을 직접 SELECT 하는 방식이 일반적이었습니다. 그러나 가상 데이터 모델(VDM)이 도입된 이후로는 I_JournalEntry CDS View가 표준 접근 경로로 자리 잡았습니다. 이 글은 BKPF를 직접 다루던 ABAP 개발자가 VDM 기반 개발로 전환할 때 필요한 핵심 개념과 실전 코드를 단계별로 정리합니다.
- I_JournalEntry의 구조와 BKPF 대비 장점 이해
- 주요 필드(CompanyCode, AccountingDocument, FiscalYear 등) 의미 정리
- ABAP에서 OPEN SQL 및 ABAP CDS 소비 예제 3단계
- I_JournalEntryItem과 JOIN하는 실무 시나리오
- 인덱스 최적화와 권한 객체 사용 시 주의점
사전 이해가 필요한 항목
이 글은 BKPF/BSEG 테이블 구조, ABAP CDS View의 기본 문법(@AbapCatalog, @AccessControl), 그리고 OPEN SQL의 SELECT ... FROM cds_view 구문에 대한 최소한의 이해를 전제로 합니다. FI 모듈의 전표(Document) 개념 — 전표 번호, 회계연도, 회사코드의 3중 키 — 도 알고 있어야 합니다.
실행 환경과 준비물
실습은 SAP S/4HANA 2022 또는 2023 온프레미스, 그리고 Public Cloud 2402 이상 릴리스를 기준으로 합니다. I_JournalEntry는 Released CDS View로 분류되어 있어 Cloud/On-Premise 모두에서 ABAP 코드, RAP(BO 확장), Analytics, OData 서비스에서 사용 가능합니다. 개발 도구는 ABAP Development Tools (ADT) for Eclipse 3.34 이상을 권장합니다.
- 시스템: S/4HANA 2022 FPS01 이상 또는 Cloud 2402+
- 패키지: 테스트용 로컬 패키지 또는 $TMP
- 권한: F_BKPF_BUK (회사코드별 BKPF 조회), S_DEVELOP
- 샘플 데이터: 회사코드 1010, 회계연도 2025의 전표가 최소 한 건 이상
핵심 개념과 동작 원리
BKPF는 SAP ERP 시절부터 존재하는 회계 전표 헤더 테이블입니다. 약 100여 개의 필드를 가지며, 회사코드(BUKRS) + 전표번호(BELNR) + 회계연도(GJAHR)가 클러스터드 인덱스를 구성합니다. 문제는 이 테이블이 매우 광범위한 컨텍스트에서 사용된다는 점입니다. 통화 변환, 텍스트, 권한 필터링, 역기표(Reversal) 연결 같은 후처리를 매번 ABAP 코드에서 반복적으로 작성해야 했습니다.
I_JournalEntry는 BKPF를 베이스로 하면서 이러한 후처리를 CDS 레이어에서 표준화한 Interface View입니다. 일종의 "정제된 BKPF"라고 비유할 수 있습니다. 원본 BKPF가 정유소에 도착한 원유라면, I_JournalEntry는 자동차 주유소에 공급 가능한 휘발유입니다.
VDM 계층에서 I_ 접두사는 Interface View를 의미합니다. 재사용 가능한 의미론적 필드 네이밍(예: CompanyCode, AccountingDocument)을 제공하며, 상위 C_ Consumption View나 OData 서비스로 노출될 때 일관된 명칭이 유지됩니다.
주요 필드 매핑을 정리하면 다음과 같습니다.
| I_JournalEntry 필드 | BKPF 원본 필드 | 의미 |
|---|---|---|
| CompanyCode | BUKRS | 회사코드 |
| AccountingDocument | BELNR | 전표번호 |
| FiscalYear | GJAHR | 회계연도 |
| AccountingDocumentType | BLART | 전표 유형 (SA, KR 등) |
| DocumentDate | BLDAT | 전표일자 |
| PostingDate | BUDAT | 전기일 |
| FiscalPeriod | MONAT | 회계기간 |
| DocumentReferenceID | XBLNR | 참조 문서 ID |
| TransactionCurrency | WAERS | 전표 통화 |
| ReverseDocument | STBLG | 역기표 전표번호 |
이 매핑 덕분에 ABAP 코드에서 bkpf-bukrs 대신 CompanyCode처럼 의미가 명확한 명칭을 사용할 수 있습니다. 또한 @AccessControl.authorizationCheck: #CHECK 설정으로 DCL(Data Control Language) 권한이 자동 적용되어, 별도의 AUTHORITY-CHECK 코드 없이도 회사코드 단위 권한 필터가 동작합니다.
1단계 — 기본 조회 예제
가장 단순한 형태로 특정 회사코드의 2025년 전표 헤더 50건을 읽어옵니다. BKPF 직접 SELECT와 동일한 시나리오를 I_JournalEntry로 변환한 코드입니다.
REPORT zr_je_basic_read.
DATA: lt_journal TYPE TABLE OF i_journalentry,
lv_count TYPE i.
SELECT companycode,
accountingdocument,
fiscalyear,
accountingdocumenttype,
postingdate,
documentreferenceid,
transactioncurrency
FROM i_journalentry
WHERE companycode = '1010'
AND fiscalyear = '2025'
ORDER BY postingdate DESCENDING
INTO TABLE @lt_journal
UP TO 50 ROWS.
lv_count = lines( lt_journal ).
WRITE: / |조회된 전표 헤더: { lv_count } 건|.
LOOP AT lt_journal ASSIGNING FIELD-SYMBOL(<fs_je>).
WRITE: / <fs_je>-accountingdocument,
<fs_je>-postingdate,
<fs_je>-accountingdocumenttype,
<fs_je>-transactioncurrency.
ENDLOOP.
WHERE 절에 CompanyCode와 FiscalYear를 모두 명시한 것은 BKPF의 기본 인덱스 구조를 그대로 활용하기 위함입니다. AccountingDocument만으로 조회하면 풀스캔이 발생하므로 반드시 회사코드+회계연도 조합을 함께 지정합니다.
2단계 — 헤더와 라인 아이템 통합 조회, 에러 처리 포함
실무에서는 헤더만 보는 일이 드뭅니다. 라인 아이템(I_JournalEntryItem)과 조인해 전표 전체를 한 번에 가져오는 경우가 훨씬 많습니다. 다음 예제는 특정 기간에 전기된 매입 전표(BLART = 'KR')의 헤더와 라인을 함께 읽고, 통화/금액 합계를 검증하는 시나리오입니다.
REPORT zr_je_join_read.
TYPES: BEGIN OF ty_doc_summary,
companycode TYPE i_journalentry-companycode,
accountingdocument TYPE i_journalentry-accountingdocument,
fiscalyear TYPE i_journalentry-fiscalyear,
postingdate TYPE i_journalentry-postingdate,
transactioncurrency TYPE i_journalentry-transactioncurrency,
total_debit TYPE p LENGTH 15 DECIMALS 2,
total_credit TYPE p LENGTH 15 DECIMALS 2,
END OF ty_doc_summary.
DATA: lt_summary TYPE TABLE OF ty_doc_summary.
TRY.
SELECT h~companycode,
h~accountingdocument,
h~fiscalyear,
h~postingdate,
h~transactioncurrency,
SUM( CASE WHEN i~debitcreditcode = 'S'
THEN i~amountintransactioncurrency
ELSE 0 END ) AS total_debit,
SUM( CASE WHEN i~debitcreditcode = 'H'
THEN i~amountintransactioncurrency
ELSE 0 END ) AS total_credit
FROM i_journalentry AS h
INNER JOIN i_journalentryitem AS i
ON h~companycode = i~companycode
AND h~accountingdocument = i~accountingdocument
AND h~fiscalyear = i~fiscalyear
WHERE h~companycode = '1010'
AND h~fiscalyear = '2025'
AND h~accountingdocumenttype = 'KR'
AND h~postingdate BETWEEN '20250101' AND '20250331'
GROUP BY h~companycode,
h~accountingdocument,
h~fiscalyear,
h~postingdate,
h~transactioncurrency
INTO TABLE @lt_summary.
IF sy-subrc <> 0.
MESSAGE |조회 결과가 없습니다.| TYPE 'I'.
RETURN.
ENDIF.
LOOP AT lt_summary ASSIGNING FIELD-SYMBOL(<fs>).
IF <fs>-total_debit <> <fs>-total_credit.
WRITE: / |[경고] 차대변 불일치 - 전표 { <fs>-accountingdocument }|,
|차변 { <fs>-total_debit } / 대변 { <fs>-total_credit }|.
ENDIF.
ENDLOOP.
CATCH cx_sy_open_sql_db INTO DATA(lx_sql).
MESSAGE lx_sql->get_text( ) TYPE 'E'.
ENDTRY.
이 코드는 GROUP BY와 CASE WHEN을 사용해 단일 SELECT로 차대변 합계까지 계산합니다. ABAP에서 LOOP를 돌면서 합산하는 방식보다 DB 푸시다운으로 처리되므로 HANA에서 훨씬 효율적입니다. cx_sy_open_sql_db 예외 처리는 DB 연결 장애나 SQL 변환 실패 시 안정적인 종료를 보장합니다.
3단계 — 프로덕션 수준의 캡슐화와 권한, 단위 테스트
실 프로덕션 코드에서는 SELECT를 그대로 노출하지 않고 클래스로 감싸는 것이 일반적입니다. 또한 ABAP Unit 테스트와 권한 명시까지 포함해야 합니다.
CLASS zcl_journal_reader DEFINITION
PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
TYPES: BEGIN OF ty_je_header,
companycode TYPE i_journalentry-companycode,
accountingdocument TYPE i_journalentry-accountingdocument,
fiscalyear TYPE i_journalentry-fiscalyear,
postingdate TYPE i_journalentry-postingdate,
documenttype TYPE i_journalentry-accountingdocumenttype,
currency TYPE i_journalentry-transactioncurrency,
END OF ty_je_header.
TYPES tt_je_header TYPE STANDARD TABLE OF ty_je_header
WITH NON-UNIQUE KEY accountingdocument.
METHODS read_by_period
IMPORTING iv_company_code TYPE bukrs
iv_fiscal_year TYPE gjahr
iv_date_from TYPE budat
iv_date_to TYPE budat
RETURNING VALUE(rt_header) TYPE tt_je_header
RAISING cx_sy_open_sql_db.
ENDCLASS.
CLASS zcl_journal_reader IMPLEMENTATION.
METHOD read_by_period.
AUTHORITY-CHECK OBJECT 'F_BKPF_BUK'
ID 'BUKRS' FIELD iv_company_code
ID 'ACTVT' FIELD '03'.
IF sy-subrc <> 0.
RAISE EXCEPTION TYPE cx_sy_open_sql_db.
ENDIF.
SELECT companycode,
accountingdocument,
fiscalyear,
postingdate,
accountingdocumenttype AS documenttype,
transactioncurrency AS currency
FROM i_journalentry
WHERE companycode = @iv_company_code
AND fiscalyear = @iv_fiscal_year
AND postingdate BETWEEN @iv_date_from AND @iv_date_to
INTO TABLE @rt_header.
ENDMETHOD.
ENDCLASS.
이어서 ABAP Unit 테스트 골격은 다음과 같이 작성합니다. 실제 DB가 아닌 CDS Test Double Framework를 사용하면 테스트 데이터 격리가 가능합니다.
CLASS ltc_reader DEFINITION FOR TESTING
DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
DATA mo_cut TYPE REF TO zcl_journal_reader.
METHODS:
setup,
should_return_empty_for_future FOR TESTING.
ENDCLASS.
CLASS ltc_reader IMPLEMENTATION.
METHOD setup.
mo_cut = NEW #( ).
ENDMETHOD.
METHOD should_return_empty_for_future.
DATA(lt_result) = mo_cut->read_by_period(
iv_company_code = '1010'
iv_fiscal_year = '2099'
iv_date_from = '20990101'
iv_date_to = '20991231' ).
cl_abap_unit_assert=>assert_initial( lt_result ).
ENDMETHOD.
ENDCLASS.
자주 발생하는 실수와 해결법
Q1. WHERE 절에 AccountingDocument만 넣었더니 성능이 매우 느립니다.
BKPF의 기본 인덱스는 CompanyCode + AccountingDocument + FiscalYear 조합입니다. 전표번호만으로는 인덱스를 활용할 수 없습니다. 반드시 회사코드와 회계연도를 함께 지정하거나, 필요 시 보조 인덱스를 검토하세요.
Q2. I_JournalEntry로 조회했더니 데이터가 일부 누락됩니다.
DCL이 적용되어 권한이 없는 회사코드의 데이터가 자동으로 필터링됐을 가능성이 큽니다. F_BKPF_BUK, F_BKPF_BLA, F_BKPF_BES 권한 객체를 점검하세요. 디버깅 시에는 SU53을 활용하면 부족한 권한을 빠르게 확인할 수 있습니다.
Q3. BSEG를 조인하려면 어떻게 하나요?
BSEG는 클러스터 테이블이라 직접 조인이 불가능했지만, 라인 아이템 데이터는 I_JournalEntryItem(또는 ACDOCA 기반)을 통해 접근합니다. ACDOCA는 통합 저널 테이블로 BSEG의 회계 데이터를 모두 포함하며, I_JournalEntryItem이 이를 노출합니다.
Q4. 통화 변환을 매번 직접 해야 할까요?
아닙니다. I_JournalEntryItem에는 AmountInCompanyCodeCurrency, AmountInGlobalCurrency 등 미리 변환된 금액 필드가 제공됩니다. CURR_CONV 함수를 직접 호출하기 전에 표준 필드 존재 여부를 먼저 확인하세요.
심화 학습으로 이어지는 주제
I_JournalEntry를 익혔다면 다음 단계는 Consumption View 계층(C_JournalEntry 계열), RAP 기반 Behavior Definition으로 전표 생성/변경 BO 다루기, 그리고 Analytics CDS View(Cube/Query)로 회계 KPI를 노출하는 흐름입니다. ACDOCA 기반의 Universal Journal 구조를 학습하면 관리회계(CO), 재무회계(FI), 자산회계(AA)가 통합되는 원리를 깊이 이해할 수 있습니다.
- I_OperationalAcctgDocItemCube — 분석용 큐브 뷰
- RAP Managed BO로 전표 입력 시나리오 구현
- OData V4 서비스로 회계 전표 노출
댓글 0
아직 댓글이 없습니다.