ABAP

BKPF vs I_AccountingDocument — FI 전표 CDS 조회 #shorts #SAP #ABAP

▶ YouTube에서 보기

개요 및 이 글에서 다루는 범위

SAP S/4HANA의 재무회계(FI) 모듈은 전통적으로 BKPF(전표 헤더)와 BSEG(전표 라인)이라는 두 클러스터 테이블에 데이터를 보관해 왔습니다. 그러나 S/4HANA로 전환되면서 SAP는 I_AccountingDocument를 비롯한 표준 CDS View 계층을 도입해 헤더와 라인을 일관된 의미모델로 노출하고 있습니다. 이 글에서는 I_AccountingDocument의 구조, 활성화 의도, 헤더-라인 조인 패턴, 그리고 실무 보고 시나리오에서의 활용법을 정리합니다.

  • I_AccountingDocument의 컬럼 구성과 키 항목 이해
  • BKPF/BSEG 직접 조회 대비 CDS View 사용의 이득 파악
  • 헤더 + 라인 아이템 통합 조회 패턴 작성
  • OData/ABAP SQL/CDS 소비 방법 비교
  • 대용량 전표 조회 시 성능 가이드라인 적용

사전에 알아두면 좋은 배경 지식

이 글은 ABAP CDS의 기본 문법(define view, association, annotation)을 알고 있다는 가정 하에 진행됩니다. 또한 SAP FI의 전표 구조, 즉 회사코드(BUKRS), 회계연도(GJAHR), 전표번호(BELNR)로 구성되는 헤더 키와 라인의 BUZEI(아이템번호) 개념을 알고 있어야 합니다. ADT(Eclipse용 ABAP Development Tools)에서 CDS 소스를 열고 Data Preview로 데이터를 확인할 수 있는 환경이면 충분합니다.

실습 환경과 권한 준비

이 글의 예제는 다음 환경을 기준으로 작성되었습니다.

  • SAP S/4HANA 2022 (FPS02) 또는 S/4HANA Cloud Private Edition 2023
  • ABAP Development Tools for Eclipse 3.36 이상
  • SAP HANA 2.0 SPS06 이상
  • 권한 객체: S_DEVELOP(개발), S_TABU_DISFC31(회계 데이터) 접근 허용
  • 샘플 데이터: 회사코드 1010(SAP 표준 데모), 기간 2024.01~2024.12 전표

Cloud Edition을 사용한다면 I_AccountingDocument는 Released CDS View(C1 릴리스)로 노출되어 있으므로 별도 Custom CDS에서 재사용이 가능합니다. On-Premise에서는 클라이언트별로 릴리스 상태를 ADT에서 확인하고, 일부 컬럼은 추가 어노테이션 확장이 필요할 수 있습니다. 권한 측면에서는 회계 데이터에 대한 F_BKPF_BUK(회사코드별 전표 조회) 권한이 부여되어야 데이터 미리보기에서 결과가 정상적으로 표시됩니다.

핵심 개념: 가상 데이터 모델(VDM)에서 I_AccountingDocument의 위치

SAP S/4HANA의 Virtual Data Model(VDM)은 세 단계 계층으로 구성됩니다. 가장 아래에 Basic View(I_*)가 위치하고, 그 위에 Composite View(C_*), 최종 소비층에 Consumption View(쿼리, OData 등)가 자리합니다. I_AccountingDocument는 이 중 Basic View 계층에 속하며, 회계 전표 헤더 정보를 의미적으로 정제해 노출합니다. 즉, BKPF를 직접 조회할 때 마주치는 짧은 필드명(BUKRS, GJAHR, BELNR, BLART 등)을 CompanyCode, FiscalYear, AccountingDocument, AccountingDocumentType처럼 셀프-디스크라이빙한 이름으로 바꿔주는 의미 계층입니다.

비유하자면 BKPF가 "원장 노트의 표지"라면, I_AccountingDocument는 "표지 위에 라벨을 붙이고 색인 카드를 끼워둔 정돈된 노트"입니다. 라벨 덕분에 어떤 필드가 무엇을 의미하는지 즉시 알 수 있고, 색인 카드(association) 덕분에 라인 아이템·회사코드 마스터·전표 유형 텍스트로 자연스럽게 따라갈 수 있습니다.

이 View는 단독으로도 헤더 조회 용도로 사용되지만, 실제 가치는 association을 통한 확장에 있습니다. 대표적인 association으로는 _JournalEntryItem(라인 아이템, 내부적으로 I_JournalEntryItem으로 연결), _CompanyCode, _FiscalYearVariant, _AccountingDocumentType 등이 있어, 헤더에서 라인으로 따라가는 패스 또는 마스터 데이터로 가는 패스를 한 줄의 path expression으로 표현할 수 있습니다. 결과적으로 BKPF-BSEG JOIN 시 자주 잊어버리는 MANDT 조건, 부분 키 누락으로 인한 카티전 폭증을 피할 수 있고, 어노테이션을 통해 통화·금액·일자 등 의미 정보가 일관되게 전달됩니다.

1단계 예제: 헤더 정보 단순 조회

먼저 회사코드 1010의 2024년도 전표 헤더를 100건만 조회하는 가장 단순한 형태부터 시작합니다. ABAP Open SQL에서 CDS View는 일반 테이블처럼 다룰 수 있습니다.

REPORT z_fi_acc_doc_header.

DATA: lt_doc_header TYPE TABLE OF i_accountingdocument.

SELECT companycode,
       fiscalyear,
       accountingdocument,
       accountingdocumenttype,
       documentdate,
       postingdate,
       creationdate,
       createdbyuser
  FROM i_accountingdocument
  WHERE companycode  = '1010'
    AND fiscalyear   = '2024'
  INTO TABLE @DATA(lt_header)
  UP TO 100 ROWS.

LOOP AT lt_header INTO DATA(ls_header).
  WRITE: / ls_header-accountingdocument,
           ls_header-postingdate,
           ls_header-accountingdocumenttype.
ENDLOOP.

여기서 주목할 점은 WHERE 절에 CompanyCodeFiscalYear를 함께 지정했다는 것입니다. 이 두 필드는 BKPF의 1차 인덱스에 포함되므로 필터 푸시다운이 HANA 레벨까지 도달하여 빠르게 결과가 반환됩니다. 반대로 PostingDate만으로 조건을 주면 풀 스캔이 발생할 가능성이 있으므로 1차 키 컬럼을 우선 좁히는 습관이 중요합니다.

2단계 예제: 헤더와 라인 아이템 통합 조회

실무에서는 헤더만 보는 경우보다 라인 아이템과 함께 보는 경우가 많습니다. 예를 들어 "회사코드 1010에서 2024년 3월 한 달간 발생한 모든 매출 전표의 G/L 계정별 금액과 텍스트"를 뽑아야 한다고 가정해 봅시다. 이를 위해 헤더용 I_AccountingDocument와 라인용 I_JournalEntryItem(또는 I_OperationalAcctgDocItem)을 조합하는 Custom CDS View를 작성합니다.

@AbapCatalog.sqlViewName: 'ZIVFIDOCFULL'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'FI Doc Header + Line Combined'
define view Z_I_FiDocFullView
  with parameters
    p_companycode    : bukrs,
    p_fiscalyear     : gjahr,
    p_postingdatefrom: budat,
    p_postingdateto  : budat
  as select from I_AccountingDocument as hdr
    inner join   I_JournalEntryItem   as itm
      on  hdr.CompanyCode        = itm.CompanyCode
      and hdr.FiscalYear         = itm.FiscalYear
      and hdr.AccountingDocument = itm.AccountingDocument
{
  key hdr.CompanyCode             as CompanyCode,
  key hdr.FiscalYear              as FiscalYear,
  key hdr.AccountingDocument      as AccountingDocument,
  key itm.AccountingDocumentItem  as DocumentItem,
      hdr.AccountingDocumentType  as DocType,
      hdr.PostingDate             as PostingDate,
      hdr.DocumentDate            as DocumentDate,
      itm.GLAccount               as GLAccount,
      itm.DebitCreditCode         as DrCrIndicator,
      @Semantics.amount.currencyCode: 'TransactionCurrency'
      itm.AmountInTransactionCurrency as TrxAmount,
      itm.TransactionCurrency     as TransactionCurrency,
      @Semantics.amount.currencyCode: 'CompanyCodeCurrency'
      itm.AmountInCompanyCodeCurrency as LocalAmount,
      itm.CompanyCodeCurrency     as CompanyCodeCurrency,
      itm.AccountingDocumentItemText as ItemText
}
where hdr.CompanyCode = :p_companycode
  and hdr.FiscalYear  = :p_fiscalyear
  and hdr.PostingDate between :p_postingdatefrom and :p_postingdateto

이 Custom View는 다음과 같은 실무 포인트를 반영합니다.

  • PARAMETERS를 사용해 회사코드/회계연도/기간을 강제로 받게 만들었습니다. 이는 사용자가 무심코 전체 조회를 시도하는 것을 막아주는 1차 안전장치입니다.
  • JOIN 조건에 헤더 키 3개(CompanyCode, FiscalYear, AccountingDocument)를 모두 포함해 카티전 폭증을 방지합니다.
  • @Semantics.amount.currencyCode 어노테이션으로 거래 통화와 회사코드 통화를 분리해 노출해 BW 추출이나 Fiori에서 통화 변환 처리가 자연스럽게 이뤄지도록 합니다.

호출은 다음과 같이 진행합니다.

SELECT *
  FROM z_i_fidocfullview( p_companycode    = '1010',
                          p_fiscalyear     = '2024',
                          p_postingdatefrom = '20240301',
                          p_postingdateto   = '20240331' )
  INTO TABLE @DATA(lt_full)
  UP TO 1000 ROWS.

cl_demo_output=>display( lt_full ).

3단계 예제: 운영 환경을 고려한 집계 View와 OData 노출

마지막 단계는 운영 환경에서 자주 요청되는 G/L 계정별 월별 집계와 OData 서비스 노출입니다. 다음 예제는 위에서 만든 통합 View를 베이스로 합계만 노출하는 Consumption View입니다.

@AbapCatalog.sqlViewName: 'ZCVFIMONTHSUM'
@AccessControl.authorizationCheck: #CHECK
@OData.publish: true
@EndUserText.label: 'FI Monthly G/L Aggregation'
@VDM.viewType: #CONSUMPTION
@Analytics: { dataCategory: #CUBE }
define view Z_C_FiMonthlyGLSummary
  with parameters
    p_companycode: bukrs,
    p_fiscalyear : gjahr
  as select from Z_I_FiDocFullView(
                   p_companycode     : :p_companycode,
                   p_fiscalyear      : :p_fiscalyear,
                   p_postingdatefrom : '19000101',
                   p_postingdateto   : '99991231' )
{
  key CompanyCode,
  key FiscalYear,
  key GLAccount,
  key cast( substring( cast( PostingDate as abap.char( 10 ) ), 6, 2 ) as abap.numc(2) ) as FiscalPeriod,
      @DefaultAggregation: #SUM
      @Semantics.amount.currencyCode: 'CompanyCodeCurrency'
      LocalAmount,
      CompanyCodeCurrency
}
group by CompanyCode, FiscalYear, GLAccount,
         cast( substring( cast( PostingDate as abap.char( 10 ) ), 6, 2 ) as abap.numc(2) ),
         CompanyCodeCurrency

이 View는 @OData.publish: true 어노테이션을 통해 자동으로 OData v2 서비스 후보로 등록됩니다. /IWFND/MAINT_SERVICE 트랜잭션에서 ZC_FIMONTHLYGLSUMMARY_CDS 서비스를 활성화하면 외부 시스템이나 SAPUI5 앱에서 다음과 같이 호출할 수 있습니다.

// SAPUI5 컨트롤러 일부
const oModel = this.getOwnerComponent().getModel("fiSummary");
oModel.read("/Z_C_FiMonthlyGLSummary(p_companycode='1010',p_fiscalyear='2024')/Set", {
    urlParameters: {
        "$filter": "GLAccount eq '0000400000'",
        "$orderby": "FiscalPeriod asc",
        "$top": "12"
    },
    success: (oData) => {
        this.getView().getModel("local").setProperty("/monthly", oData.results);
    },
    error: (oError) => {
        sap.m.MessageBox.error("월별 집계 조회 실패: " + oError.message);
    }
});

운영 단계에서 권장되는 추가 점검 항목은 다음과 같습니다.

  • Custom View 위에 ABAP Unit Test를 만들어 PARAMETERS에 잘못된 회사코드를 넘겼을 때의 동작과 빈 결과 처리, 거래 통화/회사코드 통화 환산 결과 등을 검증합니다.
  • HANA Plan Visualizer(PlanViz) 또는 SQL Trace(ST05)로 실제 실행 계획을 확인해 푸시다운 여부, 인덱스 사용 여부를 점검합니다.
  • DCL(Data Control Language)을 별도로 정의해 회사코드별 권한을 강제 적용합니다. @AccessControl.authorizationCheck: #CHECK만으로는 표준 권한 검사가 활성화되지만, 세분화된 정책은 DCL이 더 안전합니다.

현장에서 자주 만나는 함정과 해결책

Q1. I_AccountingDocument를 조회했는데 일부 전표가 보이지 않습니다.
A. 가장 흔한 원인은 F_BKPF_BUK 권한 객체에서 해당 회사코드가 빠져 있는 경우입니다. @AccessControl.authorizationCheck: #CHECK가 적용된 View는 권한이 없는 행을 자동으로 필터링하기 때문에, 풀 데이터를 보려면 SU53으로 권한 누락을 점검하거나 임시로 #NOT_REQUIRED로 바꿔 비교합니다. 다만 운영에서는 #CHECK 유지가 일반적으로 권장됩니다.

Q2. 헤더-라인 JOIN 결과가 너무 느립니다.
A. 세 가지를 확인하세요. 첫째, JOIN 조건에 CompanyCode·FiscalYear·AccountingDocument 세 키가 모두 들어 있는지. 둘째, PostingDateDocumentDate 같은 비키 필드를 SELECT 전에 충분히 좁혔는지. 셋째, Custom View에 PARAMETERS를 두어 사용자가 좁은 범위만 조회하도록 강제했는지. 일반적으로 PARAMETERS는 단순 WHERE보다 푸시다운에 유리합니다.

Q3. BSEG를 직접 JOIN하던 기존 리포트를 마이그레이션할 때 무엇을 주의해야 하나요?
A. BSEG의 일부 컬럼은 I_JournalEntryItem이나 I_OperationalAcctgDocItem 등 여러 View에 분산되어 있을 수 있습니다. ADT의 Where-Used 또는 SAP Note "Released CDS Views for FI"를 참고해 정확한 대응 View를 찾고, 통화·일자 필드는 의미 어노테이션이 부여된 새 컬럼명을 따르는 것이 좋습니다. 또한 클러스터 테이블 시절에는 불가능했던 필드 단위 인덱스가 HANA 컬럼 스토어에서 가능해졌기 때문에, 기존의 우회 인덱스 테이블(BSIS, BSAS 등)을 굳이 JOIN하지 않아도 됩니다.

이어서 살펴보면 좋은 주제들

이 글에서 다룬 I_AccountingDocument 패턴을 이해했다면, 다음 주제로 확장해 보길 권장합니다. 첫째, Universal Journal(ACDOCA) 기반의 I_JournalEntryItemI_GLAccountLineItemRawData를 비교하면 S/4HANA의 단일 원장 사상이 더 명확해집니다. 둘째, Analytical Query(CDS Query)를 작성해 SAP Analytics Cloud(SAC) 라이브 연결로 노출하면 OData 없이도 실시간 분석이 가능합니다. 셋째, Embedded Analytics의 KPI Tile, Smart Business Service와 연계해 Fiori Launchpad에서 미결 항목·연체 채권 등의 지표를 노출하는 방향도 자연스러운 확장입니다.

참고할 만한 자료

댓글 0

아직 댓글이 없습니다.