ABAP

CSKA 직접 조회 그만 — I_CostElement 원가 요소 뷰 #shorts #SAP #ABAP

▶ YouTube에서 보기

개요 및 핵심 체크포인트

SAP S/4HANA에서 컨트롤링(CO) 모듈을 다룰 때 가장 빈번하게 마주치는 마스터 데이터 중 하나가 바로 원가 요소(Cost Element)입니다. 전통적인 ECC 시절에는 CSKA/CSKB/CSKS 같은 별도 테이블로 관리되던 원가 요소가, S/4HANA에서는 GL 계정과 통합되면서 I_CostElement CDS 뷰를 통해 표준화된 인터페이스로 노출됩니다. 이 글에서는 CSKA 테이블의 본질부터 시작해서, I_CostElement 뷰의 내부 구조, Primary와 Secondary 원가 요소의 차이, 그리고 ABAP 코드에서 실전 조회 패턴까지 단계적으로 정리합니다.

  • CSKA가 어떤 데이터를 담는 테이블인지 컨트롤링 영역과의 관계로 이해
  • I_CostElement CDS 뷰의 주요 필드와 association 구조 파악
  • CostElementCategory(원가 요소 카테고리) 코드값별 실무 의미 분류
  • Primary vs Secondary 원가 요소를 GL 계정 관점에서 구분
  • SELECT/JOIN/WHERE 절에서 I_CostElement를 안전하게 활용하는 패턴 습득
  • 제조원가·인건비·감가상각 시나리오에서 자주 발생하는 실수 회피

이 글을 읽기 전에 갖춰두면 좋은 배경

SAP CO 모듈의 기본 개념(Controlling Area, Cost Center, Internal Order)을 알고 있다는 전제하에 진행합니다. ABAP CDS 문법(@AbapCatalog, association, define view)에 한 번이라도 노출된 경험이 있으면 코드를 읽는 속도가 빨라집니다. 또한 GL 계정 마스터(SKA1/SKB1)와 S/4HANA에서의 GL-CostElement 통합(Universal Journal, ACDOCA) 흐름을 어렴풋이라도 알고 있다면 Primary/Secondary 구분이 한층 명확하게 와닿습니다.

실행 환경 및 필요한 권한 사전 점검

이 글의 예제 코드는 SAP S/4HANA 2022 (FPS02) 이상 또는 S/4HANA Cloud Public Edition 2308 이후 릴리스를 기준으로 작성되었습니다. ECC 6.0 EHP8 환경에는 I_CostElement 뷰가 존재하지 않으니, 동일 데이터를 다뤄야 할 경우 CSKA/CSKB를 직접 SELECT 해야 합니다. 개발 도구로는 ABAP Development Tools(ADT) for Eclipse 2024-06 이상을 권장합니다. 트랜잭션 권한으로는 S_DEVELOP(개발 객체), S_RS_AUTH(분석 뷰 접근), 그리고 CO 데이터 조회를 위한 K_CSKS/K_KOKR(컨트롤링 영역) 권한 객체가 필요할 수 있습니다. CDS 뷰 자체는 일반적으로 SE16/SE16N 대신 ADT의 Data Preview(F8) 기능으로 확인합니다.

또한 CDS_TEST_DOUBLE_FRAMEWORK 사용을 위해 ABAP Unit 권한, 그리고 운영계 배포 시 Authorization Default for CDS Entity(S_DATASET의 행 단위 권한과는 별개) 설정을 함께 검토해야 합니다.

CSKA 테이블과 I_CostElement의 본질 이해

먼저 CSKA가 무엇인지부터 명확히 짚고 갑시다. CSKA는 Cost Elements (Data Dependent on Chart of Accounts)로, 차트 오브 어카운트(Chart of Accounts) 단위로 관리되는 원가 요소 마스터 데이터입니다. 키 필드는 KTOPL(차트 오브 어카운트)과 KSTAR(원가 요소 번호)입니다. 반면 CSKB는 컨트롤링 영역과 유효 기간별로 관리되는 데이터를 담는 테이블로, 키가 KOKRS(Controlling Area) + KSTAR + DATBI(유효 종료일)입니다.

비유하자면 CSKA는 "원가 요소의 본적지(차트 오브 어카운트 레벨의 호적)"이고, CSKB는 "원가 요소의 거주지 변동 이력(컨트롤링 영역별 시간 슬라이스)"입니다. S/4HANA로 넘어오면서 가장 큰 변화는, Primary 원가 요소가 더 이상 별도 마스터로 신규 생성되지 않고 GL 계정 마스터(SKA1/SKB1)의 속성으로 흡수되었다는 점입니다. 즉 GL 계정을 만들면서 "이 계정의 종류(GL Account Type)"를 'Primary Costs or Revenue'로 지정하면 그 자체가 Primary 원가 요소가 됩니다. Secondary 원가 요소는 여전히 컨트롤링 내부 배부(Assessment, Distribution, Activity Allocation)용으로 'Secondary Costs' 타입의 GL 계정으로 생성됩니다.

I_CostElement는 이러한 통합 모델 위에서 CSKA/CSKB/SKA1/SKB1을 종합적으로 노출하는 Basic Interface View입니다. 명명 규칙 I_ 접두어는 SAP Virtual Data Model(VDM)에서 Interface View(기본 뷰 계층)를 의미하며, 그 위에 Composite View(C_) 또는 Consumption View(C_)가 쌓이는 구조입니다.

원가 요소 카테고리(CostElementCategory)는 원가 요소의 사용 목적을 분류합니다. 실무에서 자주 쓰이는 값은 다음과 같습니다.

  • 01 - Primary costs/cost-reducing revenues (재료비, 인건비 등 일반 비용)
  • 03 - Accrual/deferral via percentage method
  • 04 - Accrual/deferral via target=actual
  • 11 - Revenues (수익)
  • 12 - Sales deductions (매출 차감)
  • 21 - Internal settlement (내부 결산)
  • 22 - External settlement (외부 결산, 오더에서 자산/프로젝트로)
  • 31 - Order/project results analysis
  • 41 - Overhead rates (간접비율)
  • 42 - Assessment (배부)
  • 43 - Internal activity allocation (내부 활동 배부)

이 중 01과 11이 Primary 영역, 21·22·42·43이 대표적인 Secondary 영역에 해당합니다.

실전 코드 1단계 — 기본 SELECT 예제

가장 기본적인 형태로, 특정 컨트롤링 영역의 모든 원가 요소를 조회하는 예제부터 시작합니다. 시나리오는 "컨트롤링 영역 1000(글로벌)에서 현재 유효한 원가 요소 목록을 가져오기"입니다.

REPORT zr_costelem_basic_lookup.

DATA: lt_cost_elements TYPE TABLE OF I_CostElement,
      lv_target_area   TYPE kokrs VALUE '1000'.

SELECT CostElement,
       ControllingArea,
       CostElementCategory,
       ValidityStartDate,
       ValidityEndDate,
       CostElementDescription
  FROM I_CostElement
  WHERE ControllingArea  = @lv_target_area
    AND ValidityEndDate  >= @sy-datum
    AND ValidityStartDate <= @sy-datum
  INTO TABLE @lt_cost_elements
  UP TO 200 ROWS.

LOOP AT lt_cost_elements ASSIGNING FIELD-SYMBOL(<ls_ce>).
  WRITE: / <ls_ce>-CostElement,
           <ls_ce>-CostElementCategory,
           <ls_ce>-CostElementDescription.
ENDLOOP.

핵심은 두 가지입니다. 첫째, 유효성 기간(ValidityStartDate/ValidityEndDate)을 항상 함께 필터링해야 합니다. CSKB가 시간 슬라이스 테이블이기 때문에 같은 원가 요소가 여러 행으로 존재할 수 있고, 필터 없이 조회하면 만료된 데이터까지 섞입니다. 둘째, 컨트롤링 영역은 권한과 직결되므로 가능한 한 명시적으로 지정해야 합니다.

실전 코드 2단계 — 제조원가 분석 시나리오

이번에는 실무 시나리오로, "특정 회사 코드의 제조원가 관련 Primary 원가 요소만 추출하고, GL 계정 정보와 결합해 보고서로 출력"하는 예제를 만들어 봅니다. 에러 처리와 로깅을 포함합니다.

CLASS zcl_mfg_cost_analyzer DEFINITION
  PUBLIC FINAL CREATE PUBLIC.

  PUBLIC SECTION.
    TYPES: BEGIN OF ty_mfg_cost_row,
             cost_element     TYPE racct,
             gl_account_name  TYPE txt50_skat,
             category         TYPE kataz,
             account_group    TYPE ktoks,
             is_primary       TYPE abap_bool,
           END OF ty_mfg_cost_row,
           tt_mfg_cost_rows TYPE STANDARD TABLE OF ty_mfg_cost_row
                                  WITH EMPTY KEY.

    METHODS fetch_manufacturing_costs
      IMPORTING iv_controlling_area TYPE kokrs
                iv_company_code     TYPE bukrs
      RETURNING VALUE(rt_rows)      TYPE tt_mfg_cost_rows
      RAISING   cx_sy_open_sql_db.

  PRIVATE SECTION.
    METHODS log_warning
      IMPORTING iv_message TYPE string.
ENDCLASS.

CLASS zcl_mfg_cost_analyzer IMPLEMENTATION.
  METHOD fetch_manufacturing_costs.

    TRY.
        SELECT ce~CostElement                AS cost_element,
               skat~GLAccountName            AS gl_account_name,
               ce~CostElementCategory        AS category,
               ska~GLAccountGroup            AS account_group,
               CASE ce~CostElementCategory
                 WHEN '01' THEN @abap_true
                 WHEN '11' THEN @abap_true
                 ELSE @abap_false
               END                           AS is_primary
          FROM I_CostElement AS ce
          INNER JOIN I_GLAccountInChartOfAccounts AS ska
                  ON  ska~GLAccount         = ce~CostElement
          INNER JOIN I_GLAccountText AS skat
                  ON  skat~GLAccount        = ska~GLAccount
                  AND skat~ChartOfAccounts  = ska~ChartOfAccounts
                  AND skat~Language         = @sy-langu
          WHERE ce~ControllingArea       = @iv_controlling_area
            AND ce~CostElementCategory   IN ( '01', '11' )
            AND ce~ValidityEndDate       >= @sy-datum
            AND ce~ValidityStartDate     <= @sy-datum
            AND ska~GLAccountGroup       LIKE 'MFG%'
          INTO TABLE @rt_rows.

        IF rt_rows IS INITIAL.
          log_warning( |No mfg cost elements for area { iv_controlling_area }| ).
        ENDIF.

      CATCH cx_sy_open_sql_db INTO DATA(lx_db).
        log_warning( lx_db->get_text( ) ).
        RAISE EXCEPTION lx_db.
    ENDTRY.
  ENDMETHOD.

  METHOD log_warning.
    cl_bali_message_setter=>create(
      severity = if_bali_constants=>c_severity_warning
      text     = iv_message ).
  ENDMETHOD.
ENDCLASS.

여기서 주목할 점은 I_CostElementI_GLAccountInChartOfAccounts(SKA1 기반)와 INNER JOIN한다는 것입니다. S/4HANA에서 Primary 원가 요소는 GL 계정과 1:1로 매핑되므로 JOIN 키는 CostElement = GLAccount입니다. 또한 GLAccountGroup LIKE 'MFG%'으로 제조원가 계정 그룹만 필터링하는 패턴은 실무에서 매우 자주 쓰입니다.

실전 코드 3단계 — 프로덕션 수준의 인건비/감가상각 통합 조회

프로덕션 환경에서는 성능과 테스트 가능성이 중요합니다. 다음은 CDS 뷰를 직접 정의해 인건비(Personnel Cost)와 감가상각(Depreciation) 관련 원가 요소를 통합 조회하는 Composite View 예제입니다.

@AbapCatalog.sqlViewName: 'ZCV_OPEXCOSTELE'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'OPEX Cost Element Composite'
@VDM.viewType: #COMPOSITE

define view ZC_OpexCostElementOverview
  as select from I_CostElement as Ce
    association [0..1] to I_GLAccountInChartOfAccounts as _GLAcct
      on  $projection.CostElement      = _GLAcct.GLAccount
      and $projection.ChartOfAccounts  = _GLAcct.ChartOfAccounts
{
  key Ce.ControllingArea,
  key Ce.CostElement,
      Ce.ChartOfAccounts,
      Ce.CostElementCategory,
      Ce.ValidityStartDate,
      Ce.ValidityEndDate,

      case Ce.CostElementCategory
        when '01' then 'PRIMARY'
        when '11' then 'REVENUE'
        when '21' then 'SECONDARY_INTERNAL'
        when '22' then 'SECONDARY_EXTERNAL'
        when '42' then 'SECONDARY_ASSESSMENT'
        when '43' then 'SECONDARY_ACTIVITY'
        else           'OTHER'
      end                                    as CostNature,

      case
        when _GLAcct.GLAccountGroup like 'PERS%' then 'PERSONNEL'
        when _GLAcct.GLAccountGroup like 'DEPR%' then 'DEPRECIATION'
        when _GLAcct.GLAccountGroup like 'MFG%'  then 'MANUFACTURING'
        else                                          'OTHER'
      end                                    as OpexBucket,

      _GLAcct.GLAccountGroup                 as AccountGroup,
      _GLAcct
}
where Ce.ValidityEndDate   >= $session.system_date
  and Ce.ValidityStartDate <= $session.system_date

이 Composite View는 두 가지 보강을 합니다. 첫째, CostElementCategory 코드값을 사람이 읽을 수 있는 CostNature로 변환해 다운스트림 보고서가 코드값을 외울 필요가 없게 합니다. 둘째, GL 계정 그룹(GLAccountGroup)을 기반으로 운영비용(OPEX) 버킷을 분류해 인건비/감가상각/제조원가를 구분합니다. $session.system_date를 활용해 유효성 필터를 뷰 레벨에 내장한 것도 핵심 포인트입니다.

ABAP Unit 테스트는 CDS_TEST_ENVIRONMENT를 활용해 I_CostElement에 테스트 더블을 주입하면 됩니다. 보안 측면에서는 @AccessControl.authorizationCheck: #CHECK를 명시해 DCL(Data Control Language)에서 컨트롤링 영역 권한을 자동 적용하도록 했습니다.

흔히 발생하는 실수와 트러블슈팅 FAQ

Q1. SE16에서 CSKA를 조회했는데 데이터가 없습니다. S/4HANA에서는 Primary 원가 요소가 GL 계정 마스터로 흡수되었기 때문에, CSKA에는 Secondary 원가 요소와 마이그레이션된 일부 레거시 데이터만 남아 있는 경우가 많습니다. 통합 뷰가 필요하면 I_CostElement를 사용하거나, GL 계정 측에서 I_GLAccountInChartOfAccountsGLAccountType = 'P'(Primary) 또는 'S'(Secondary) 필터로 접근하세요.

Q2. 같은 원가 요소가 여러 행으로 나옵니다. CSKB가 시간 분할(time-dependent) 테이블이라 유효 기간이 다른 여러 슬라이스가 존재합니다. ValidityStartDate <= sy-datum AND ValidityEndDate >= sy-datum 조건을 빠뜨리면 중복으로 보입니다. 또한 컨트롤링 영역이 여러 개인 글로벌 환경에서는 ControllingArea 필터도 함께 걸어야 합니다.

Q3. JOIN 했더니 성능이 급격히 떨어집니다. I_CostElement는 내부적으로 여러 테이블을 결합한 뷰이므로, 추가 JOIN을 무분별하게 걸면 옵티마이저가 최적 플랜을 잡지 못합니다. 권장 패턴은 (1) WHERE 절에 키 필드(ControllingArea, CostElement, ValidityEndDate)를 최대한 명시, (2) 필요 컬럼만 SELECT, (3) LIKE 'MFG%' 같은 와일드카드는 가능하면 = 비교로 대체, (4) 대량 처리에는 PACKAGE SIZE 커서를 사용하는 것입니다.

그 외에도 자주 보이는 함정으로는, ECC 마인드로 KSTAR/KOKRS 필드명을 그대로 쓰려 하는 경우(CDS에서는 CostElement, ControllingArea), CostElementCategory를 INT로 비교하는 경우(실제 타입은 CHAR(2)), 그리고 DCL 권한 미설정으로 운영계에서 공백 결과가 나오는 경우 등이 있습니다.

한 단계 더 깊이 들어가고 싶다면

I_CostElement를 충분히 이해했다면, 자연스럽게 이어지는 주제는 (1) I_GLAccountLineItem / I_JournalEntryItem을 통한 실제 전표 라인 분석, (2) Universal Journal(ACDOCA) 기반의 통합 보고 모델, (3) I_CostCenter·I_InternalOrder와의 결합을 통한 코스트 오브젝트 분석, (4) Analytical Query(@Analytics.query: true)를 활용한 SAP Analytics Cloud 연계입니다. RAP(Restful ABAP Programming Model) 관점에서는 I_CostElement 위에 BDEF를 얹어 Fiori Elements 앱으로 노출하는 패턴도 학습할 만합니다.

더 파볼 만한 자료 모음

댓글 0

아직 댓글이 없습니다.