개요 및 이 글에서 다룰 범위
SAP S/4HANA로 전환된 환경에서 재무 전표(FI Document) 라인 아이템을 조회할 때, 과거처럼 BSEG 클러스터 테이블을 직접 SELECT 하는 방식은 더 이상 권장되지 않습니다. 대신 SAP는 I_JournalEntryItem이라는 표준 CDS View를 통해 ACDOCA(Universal Journal) 기반의 정규화된 라인 아이템을 노출합니다. 이 글에서는 BSEG와 I_JournalEntryItem의 구조적 차이를 짚고, ABAP에서 계정과목(GLAccount)과 금액(AmountInCompanyCodeCurrency)을 안정적으로 읽어오는 패턴을 단계별 예제로 다룹니다.
- BSEG와 ACDOCA, I_JournalEntryItem의 관계 이해
- CDS View 기반 라인 아이템 조회 코드 작성
- 회사코드 회계연도 전표번호 필터링 기법
- 금액 합계 계정과목별 집계 패턴 구현
- 권한 체크 및 성능 고려사항 검토
읽기 전에 갖춰두면 좋은 지식
이 글은 ABAP Open SQL 문법, CDS View의 기본 구조(@AbapCatalog.sqlViewName, association), 그리고 FI 전표 구조(헤더 BKPF, 라인 BSEG)에 대한 기초 이해를 전제로 합니다. 또한 S/4HANA의 Universal Journal(ACDOCA) 개념과 회계 전표가 어떻게 한 줄로 관리되는지 알고 있으면 본문 내용을 따라가기 수월합니다. Eclipse 기반 ADT에서 CDS View를 열어 본 경험이 있다면 association 점 표기법을 빠르게 이해할 수 있습니다.
실행 환경 및 준비물
본 예제는 다음 환경을 기준으로 검증되었습니다.
- SAP S/4HANA 2022 (FPS02) 이상 또는 S/4HANA Cloud Private Edition
- ABAP Development Tools(ADT) for Eclipse 2024-09 이상
- ABAP Language Version: Standard ABAP (Release 7.57+)
- 권한 객체: S_TABU_DIS(ACDOCA), F_BKPF_BUK(회사코드 권한)
- 테스트 데이터: FB50/FB60 등으로 생성된 G/L 전표 또는 AP/AR 전표
ECC 6.0 환경이라면 I_JournalEntryItem이 존재하지 않으므로 BSEG를 직접 사용해야 합니다. 본문 예제는 S/4HANA 전용입니다. CDS View 메타데이터는 트랜잭션 SE11 또는 ADT의 Data Definition 에디터에서 확인할 수 있으며, View Browser(F2)로 어떤 필드가 노출되는지 미리 점검하는 것을 권장합니다.
핵심 개념 — BSEG에서 ACDOCA, 그리고 I_JournalEntryItem까지
전통적 ECC에서 회계 라인 아이템은 BSEG라는 풀 테이블(pool table)에 저장되었습니다. BSEG는 클러스터 구조라 SELECT 시 인덱스 필드(MANDT, BUKRS, BELNR, GJAHR, BUZEI)만 효율적이고, 그 외 필드로 WHERE 조건을 걸면 풀스캔에 가까운 비용이 발생합니다. S/4HANA는 이를 ACDOCA(Universal Journal Line Item)라는 단일 정규 테이블로 통합했습니다. ACDOCA는 FI/CO/AA/ML 데이터를 한 줄에 담아 분개를 한 곳에서 관리합니다.
비유하자면 BSEG는 옛날 도서관의 카드 목록 박스이고, ACDOCA는 디지털 통합 카탈로그입니다. BSEG는 여전히 호환을 위해 보존되지만 실제 분석 보고에는 ACDOCA가 정답지 역할을 합니다.
I_JournalEntryItem은 ACDOCA 위에 얹힌 표준 CDS View로, "Interface View" 계층에 속합니다. 접두어 I_는 SAP가 공개한 기본(Interface) 뷰임을 의미하며, 이 위에 컨슈머 뷰(C_), 분석 뷰(A_) 등이 쌓이는 가상 데이터 모델(VDM) 구조의 토대가 됩니다. 주요 필드는 다음과 같이 의미 있는 이름으로 매핑됩니다.
CompanyCode← BUKRSFiscalYear← GJAHRAccountingDocument← BELNRAccountingDocumentItem← BUZEIGLAccount← HKONT (총계정원장 계정)AmountInCompanyCodeCurrency← DMBTR (회사코드 통화 금액)DebitCreditCode← SHKZG (S/H 차변 대변 표시)
또한 I_JournalEntryItem은 _JournalEntry(헤더 I_JournalEntry), _GLAccount, _CompanyCode 등 다양한 association을 노출해, 점 표기법(_JournalEntry.PostingDate)으로 조인 없이 헤더 정보를 끌어올 수 있습니다. 이 점이 BSEG 직접 조회 대비 가장 큰 생산성 차이입니다.
실전 예제 1단계 — 특정 전표의 라인 읽기
가장 단순한 시나리오부터 시작합니다. 회사코드 1010, 회계연도 2025, 전표번호 4900000123의 모든 라인을 읽어 계정과목과 금액을 출력하는 코드입니다.
REPORT zr_je_item_basic.
DATA: lt_lines TYPE TABLE OF i_journalentryitem,
ls_line TYPE i_journalentryitem.
SELECT companycode,
fiscalyear,
accountingdocument,
accountingdocumentitem,
glaccount,
amountincompanycodecurrency,
companycodecurrency,
debitcreditcode
FROM i_journalentryitem
WHERE companycode = '1010'
AND fiscalyear = '2025'
AND accountingdocument = '4900000123'
INTO TABLE @lt_lines.
LOOP AT lt_lines INTO ls_line.
WRITE: / ls_line-accountingdocumentitem,
ls_line-glaccount,
ls_line-debitcreditcode,
ls_line-amountincompanycodecurrency CURRENCY ls_line-companycodecurrency,
ls_line-companycodecurrency.
ENDLOOP.
주목할 점은 CURRENCY 추가 옵션으로 통화 단위에 맞춘 소수점 처리를 ABAP이 자동 수행한다는 부분입니다. ACDOCA의 금액 필드는 통화 기준 정규화가 되어 있어 BSEG처럼 JPY 등 무소수점 통화에서 100배 오차가 발생하는 함정을 피할 수 있습니다.
실전 예제 2단계 — 헤더 조인 및 계정과목별 집계, 예외 처리
실무에서는 단일 전표가 아닌, 특정 기간의 모든 라인을 계정과목별로 집계하는 요구가 잦습니다. 이 단계에서는 association으로 헤더의 PostingDate를 조건으로 사용하고, GROUP BY로 차변 대변 합계를 산출합니다. 또한 No data found와 권한 부족 상황을 분리해 로깅합니다.
CLASS zcl_je_aggregator DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
TYPES: BEGIN OF ty_agg,
glaccount TYPE i_journalentryitem-glaccount,
debit_total TYPE i_journalentryitem-amountincompanycodecurrency,
credit_total TYPE i_journalentryitem-amountincompanycodecurrency,
currency TYPE i_journalentryitem-companycodecurrency,
END OF ty_agg.
METHODS aggregate_by_account
IMPORTING iv_company_code TYPE bukrs
iv_date_from TYPE budat
iv_date_to TYPE budat
RETURNING VALUE(rt_agg) TYPE STANDARD TABLE OF ty_agg
RAISING cx_sy_open_sql_db.
ENDCLASS.
CLASS zcl_je_aggregator IMPLEMENTATION.
METHOD aggregate_by_account.
TRY.
SELECT je~glaccount,
SUM( CASE WHEN je~debitcreditcode = 'S'
THEN je~amountincompanycodecurrency
ELSE 0 END ) AS debit_total,
SUM( CASE WHEN je~debitcreditcode = 'H'
THEN je~amountincompanycodecurrency
ELSE 0 END ) AS credit_total,
je~companycodecurrency AS currency
FROM i_journalentryitem AS je
WHERE je~companycode = @iv_company_code
AND je~_journalentry-postingdate BETWEEN @iv_date_from AND @iv_date_to
GROUP BY je~glaccount, je~companycodecurrency
ORDER BY je~glaccount
INTO TABLE @rt_agg.
IF sy-subrc <> 0.
MESSAGE i208(00) WITH '해당 기간 라인 아이템 없음'.
ENDIF.
CATCH cx_sy_open_sql_db INTO DATA(lx_sql).
" 로깅: 권한 부족 또는 SQL 오류
cl_demo_output=>display( |SQL 오류: { lx_sql->get_text( ) }| ).
RAISE EXCEPTION lx_sql.
ENDTRY.
ENDMETHOD.
ENDCLASS.
association 점 표기법 _journalentry-postingdate를 통해 헤더 테이블을 명시적으로 조인하지 않고도 BUDAT 조건을 걸 수 있습니다. 내부적으로는 ACDOCA의 PostingDate 컬럼을 그대로 활용하므로 추가 조인 비용이 사실상 없습니다. 또한 SUM과 CASE 조합으로 차변/대변을 별도 합계로 분리해 시산표 스타일 결과를 만듭니다.
실전 예제 3단계 — 컨슈머 CDS View와 권한, 단위 테스트
마지막 단계에서는 ABAP에서 직접 집계 SQL을 작성하기보다 별도의 컨슈머 CDS View로 로직을 캡슐화하고, ABAP은 단순 조회만 수행하도록 책임을 분리합니다. 이 패턴은 재사용성과 분석 도구(예: Analytical List Page) 호환성을 높입니다.
@AbapCatalog.sqlViewName: 'ZJEACCTSUM'
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'GL Account Sum per Period'
define view ZC_JournalEntryAccountSum
with parameters
p_company_code : bukrs,
p_date_from : budat,
p_date_to : budat
as select from I_JournalEntryItem as Item
association [0..1] to I_GLAccount as _GLAccount
on $projection.GLAccount = _GLAccount.GLAccount
and $projection.ChartOfAccounts = _GLAccount.ChartOfAccounts
{
key Item.CompanyCode,
key Item.GLAccount,
Item._CompanyCode.ChartOfAccounts as ChartOfAccounts,
sum( case when Item.DebitCreditCode = 'S'
then Item.AmountInCompanyCodeCurrency
else 0 end ) as DebitTotal,
sum( case when Item.DebitCreditCode = 'H'
then Item.AmountInCompanyCodeCurrency
else 0 end ) as CreditTotal,
Item.CompanyCodeCurrency,
_GLAccount
}
where Item.CompanyCode = :p_company_code
and Item._JournalEntry.PostingDate between :p_date_from and :p_date_to
group by
Item.CompanyCode,
Item.GLAccount,
Item._CompanyCode.ChartOfAccounts,
Item.CompanyCodeCurrency
ABAP 측에서는 다음과 같이 파라미터 뷰를 호출합니다. 권한 체크는 CDS DCL(Data Control Language)에서 위임하고, 호출 코드는 비즈니스 로직에 집중합니다.
SELECT * FROM zc_journalentryaccountsum(
p_company_code = @iv_bukrs,
p_date_from = @iv_date_from,
p_date_to = @iv_date_to )
WHERE creditTotal > @iv_threshold
INTO TABLE @DATA(lt_summary)
UP TO 1000 ROWS.
마지막으로 단위 테스트는 CL_OSQL_TEST_ENVIRONMENT를 활용해 I_JournalEntryItem의 테스트 더블을 주입할 수 있습니다. 이로써 실제 ACDOCA에 의존하지 않고 격리된 테스트가 가능해집니다. 보안 측면에서는 DCL 룰 S_TABU_DIS 및 회사코드 권한이 적용되도록 @AccessControl.authorizationCheck: #CHECK를 반드시 유지하는 것이 일반적으로 권장됩니다.
흔히 마주치는 함정과 해결 단서
실무에서 자주 발생하는 이슈와 점검 포인트입니다.
- Q1. BSEG와 I_JournalEntryItem의 라인 수가 다른 이유는? ACDOCA는 BSEG-BSEG_ADD-FAGLFLEXA-COEP 등을 한 줄로 통합했기 때문에, 세금 라인이나 통계 라인의 표시가 다를 수 있습니다. 차이가 의심되면
I_OperationalAcctgDocItemCube나I_GLAccountLineItemRawData등 더 원천에 가까운 뷰로 교차 검증하세요. - Q2. 금액이 0으로 나오거나 부호가 반대로 보입니다. ACDOCA에는
AmountInCompanyCodeCurrency가 부호 포함 값으로 저장되는 컬럼과, 별도의DebitCreditCode로 부호를 표현하는 두 표현이 공존합니다. 합계 시 SHKZG와 부호를 동시에 적용하면 두 번 반전됩니다. 한 가지 기준만 선택하세요. - Q3. PostingDate로 조건을 걸었는데 성능이 느립니다. PostingDate는 ACDOCA의 직접 컬럼이므로 인덱스 활용이 가능하지만, 회사코드 조건이 빠지면 풀스캔이 됩니다. 항상
CompanyCode와 함께 사용하고, 범위가 1년 이상이면 회계연도 컬럼도 함께 지정하는 편이 안전합니다. - Q4. 권한 부족(sy-subrc = 4)이 자주 발생합니다. CDS DCL이 회사코드별로 권한을 거르기 때문에, SU53로 거절된 권한 객체를 확인하고 PFCG에서 회사코드 범위를 부여해야 합니다.
또한 BSEG에는 존재했으나 ACDOCA로 옮겨오며 의미가 달라진 필드(예: AUGBL 청산문서)가 있어, 단순 매핑만 믿지 말고 SAP Note 2160045, 2185026 같은 마이그레이션 노트를 참고하는 편이 안전합니다.
이어서 살펴볼 만한 주제
I_JournalEntryItem을 익혔다면 다음 주제로 확장해 보세요. 첫째, C_JournalEntryItemCube와 C_JournalEntryItemQuery를 활용한 분석 시나리오, 둘째, RAP(RESTful ABAP Programming Model)에서 라인 아이템을 BO로 노출해 Fiori 앱을 만드는 패턴, 셋째, CDS Table Function으로 AMDP를 결합해 복잡한 계산식을 HANA에서 처리하는 방법입니다. 더불어 결산 시나리오에서는 I_GLAccountLineItem, AR/AP 관점에서는 I_OpenItemByVendor I_OpenItemByCustomer가 짝을 이루므로 함께 살펴두면 도움이 됩니다.
참고 링크 모음
- SAP Help Portal — Universal Journal Entry(ACDOCA) 개요
- SAP Help Portal — Virtual Data Model(VDM) for Finance
- SAP Help Portal — ABAP CDS View Entities 가이드
- SAP API Business Hub — Journal Entry OData 서비스 목록
- SAP Community Blogs — ACDOCA와 CDS View 활용 사례 모음
- SAP Note 2160045 — Compatibility Views for FI Tables in S/4HANA
- SAP Note 2185026 — Finance Migration Considerations
댓글 0
아직 댓글이 없습니다.