ABAP

CDS View vs COST 테이블 — 배부 요율 차이 #shorts #SAP #ABAP

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

이 글은 SAP S/4HANA의 CDS View인 I_ActivityType을 중심으로 CO(Controlling) 모듈의 활동 유형 마스터 데이터를 어떻게 조회하고 실무 시나리오에 활용하는지 다룹니다. CSLA는 원가 센터가 제공하는 서비스 단위(기계 시간, 인건비 시간 등)와 배부 요율을 관리하는 객체이며, I_ActivityType은 이 마스터 데이터를 CDS 계층에서 표준 방식으로 노출하는 뷰입니다.

  • I_ActivityType 뷰의 필드 구조와 연관 뷰 파악
  • ABAP CDS에서 원가 센터 뷰와 조인해 배부 요율 조회
  • Fiori Elements/RAP 소비 시나리오에 맞는 서비스 정의
  • 성능·권한·로깅을 고려한 프로덕션 수준 코드 작성

사전에 알고 있으면 좋은 것들

이 글은 ABAP CDS View 문법(@AbapCatalog, @ObjectModel), CO 모듈의 기본 개념(원가 센터, 활동 유형, 내부 배부), 그리고 SAP S/4HANA의 Virtual Data Model(VDM) 계층(Basic/Composite/Consumption) 구분을 이해하고 있다는 전제 하에 진행합니다. RAP(ABAP RESTful Application Programming Model) 기본 흐름을 알면 3단계 예제 이해가 수월합니다.

환경 및 버전 준비물

실습에 사용한 환경은 다음과 같습니다.

  • SAP S/4HANA 2022 또는 2023 On-Premise (Cloud Private/Public Edition에서도 유사한 뷰 제공, 단 필드 노출 범위 차이 있음)
  • ABAP Development Tools (ADT) 3.36 이상, Eclipse 2023-09 기반
  • SAP GUI 8.00 (CSKB/CSKA/CSLA 테이블 확인용)
  • 테스트용 CO 조직: 원가 센터(KOSTL), 활동 유형(LSTAR), 컨트롤링 영역(KOKRS)이 마스터 데이터로 세팅되어 있어야 합니다
  • 권한: S_TCODE(SE16, KL03), K_CSKS, K_CSLA 조회 권한
테이블 관점에서 활동 유형 마스터는 CSLA(기본 정보)/CSLT(텍스트)에 저장되며, 배부 요율은 COST/COKS 관련 계획 테이블에 시간의존적으로 관리됩니다. I_ActivityType은 이 중 CSLA/CSLT를 조합해 노출한 Basic 뷰로 이해하면 됩니다.

핵심 개념 짚어보기

CO 모듈에서 "활동(Activity)"은 원가 센터가 다른 원가 객체에 제공하는 서비스 단위입니다. 예를 들어 생산 원가 센터 MECH-01이 "기계 시간(MHR)" 활동을 시간당 45,000원의 배부 요율로 제공하면, 제조 오더가 기계를 3시간 사용했을 때 135,000원이 원가 센터에서 오더로 배부됩니다. 이때 "MHR"이 활동 유형이며, "45,000원/시간"이 배부 요율(Activity Rate)입니다.

이 관계를 카페 원두 도매로 비유하면 이해가 쉽습니다. 원가 센터는 로스팅 공장, 활동 유형은 원두 종류(에스프레소용/드립용), 배부 요율은 kg당 단가, 소비하는 원가 객체는 각 카페 지점에 해당합니다. I_ActivityType은 이 중 "원두 종류 카탈로그"만 뽑아 보여주는 뷰라고 볼 수 있습니다.

계층 관점에서 I_ActivityType은 Basic Interface View(I_ 접두어)에 속하고, 상위에서 C_ActivityType과 같은 Consumption 뷰나 Fiori 앱이 이를 참조합니다. 주요 필드는 대략 다음과 같습니다.

  • ControllingArea: 컨트롤링 영역 (KOKRS)
  • ActivityType: 활동 유형 코드 (LSTAR)
  • ActivityTypeName: 활동 유형 명칭
  • ActivityUnit: 활동 단위 (예: HR, MIN, KG)
  • ActivityTypeCategory: 활동 유형 범주 (1=수동, 2=간접 결정 등)
  • ValidityStartDate / ValidityEndDate: 유효 기간

배부 요율 자체는 I_ActivityType에는 직접 포함되지 않는 것이 일반적이며, 계획 데이터를 노출하는 별도 CDS 뷰(예: I_ActivityTypePrice 계열)와 조인해서 얻습니다. 이 점이 초보자가 자주 혼동하는 부분입니다.

1단계: 활동 유형 목록을 조회하는 기본 예제

가장 단순한 형태로 특정 컨트롤링 영역의 활동 유형 목록을 뽑아봅니다. 실무에서는 마스터 데이터 검증이나 F4 도움말 소스로 자주 사용됩니다.

@AbapCatalog.sqlViewName: 'ZVACTTYPBASIC'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Activity Type Basic List'
define view Z_ActivityTypeBasic
  as select from I_ActivityType
{
  key ControllingArea,
  key ActivityType,
      ActivityTypeName,
      ActivityUnit,
      ActivityTypeCategory,
      ValidityStartDate,
      ValidityEndDate
}
where
      ControllingArea    = 'A000'
  and ValidityEndDate   >= $session.system_date

실행 후 ADT의 Data Preview에서 유효한 활동 유형만 필터링되어 조회되는지 확인합니다. 이 뷰만으로도 "현재 유효한 활동 유형 목록"이라는 최소 기능은 충족됩니다.

2단계: 원가 센터·배부 요율과 조인하는 실무 시나리오

실무에서는 활동 유형 자체보다 "특정 원가 센터가 어떤 활동 유형을 어떤 요율로 제공하는가"가 훨씬 자주 필요합니다. 여기서는 원가 센터 뷰(I_CostCenter) 및 활동 요율 뷰와 조인하고, ABAP 클래스에서 예외 처리·로깅을 추가한 형태를 보여줍니다.

@AbapCatalog.sqlViewName: 'ZVCCACTRATE'
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Cost Center x Activity Type Rate'
define view Z_CostCenterActivityRate
  as select from    I_ActivityType          as at
    inner join      I_CostCenterActivityTypeCombination as cc
                     on  cc.ControllingArea = at.ControllingArea
                     and cc.ActivityType    = at.ActivityType
    left outer to one join I_ActivityTypePrice as pr
                     on  pr.ControllingArea = at.ControllingArea
                     and pr.ActivityType    = at.ActivityType
                     and pr.CostCenter      = cc.CostCenter
{
  key at.ControllingArea,
  key cc.CostCenter,
  key at.ActivityType,
      at.ActivityTypeName,
      at.ActivityUnit,
      pr.FiscalYear,
      pr.FiscalPeriod,
      pr.PlannedActivityPrice,
      pr.Currency
}
where
      at.ValidityEndDate >= $session.system_date

주의할 점은 조합 뷰 이름과 요율 뷰 이름은 릴리즈에 따라 다르게 제공된다는 점입니다. On-Premise에서는 I_ActivityTypePrice가 없을 수 있으므로, 없다면 COST 테이블 기반의 커스텀 Basic 뷰를 먼저 만든 뒤 조인하는 방식으로 우회합니다.

이 뷰를 ABAP에서 소비하는 클래스는 다음과 같이 작성합니다.

CLASS zcl_activity_rate_reader DEFINITION PUBLIC FINAL CREATE PUBLIC.
  PUBLIC SECTION.
    TYPES: BEGIN OF ty_rate,
             cost_center     TYPE kostl,
             activity_type   TYPE lstar,
             activity_name   TYPE text40,
             planned_price   TYPE p LENGTH 15 DECIMALS 2,
             currency        TYPE waers,
           END OF ty_rate,
           tt_rate TYPE STANDARD TABLE OF ty_rate WITH KEY cost_center activity_type.

    METHODS read_rates
      IMPORTING iv_controlling_area TYPE kokrs
                iv_cost_center      TYPE kostl OPTIONAL
      RETURNING VALUE(rt_rates)     TYPE tt_rate
      RAISING   cx_sy_open_sql_db.
ENDCLASS.

CLASS zcl_activity_rate_reader IMPLEMENTATION.
  METHOD read_rates.
    TRY.
        SELECT cost_center,
               activity_type,
               activitytypename AS activity_name,
               plannedactivityprice AS planned_price,
               currency
          FROM z_costcenteractivityrate
          WHERE controllingarea = @iv_controlling_area
            AND ( @iv_cost_center IS INITIAL OR cost_center = @iv_cost_center )
          INTO CORRESPONDING FIELDS OF TABLE @rt_rates.

        IF rt_rates IS INITIAL.
          cl_bali_log_db_writer=>log_message(
            iv_severity = 'W'
            iv_text     = |No activity rates found for { iv_controlling_area }| ).
        ENDIF.
      CATCH cx_sy_open_sql_db INTO DATA(lx_sql).
        cl_bali_log_db_writer=>log_message(
          iv_severity = 'E'
          iv_text     = lx_sql->get_text( ) ).
        RAISE EXCEPTION lx_sql.
    ENDTRY.
  ENDMETHOD.
ENDCLASS.

로깅 부분은 실제 프로젝트에서는 if_bali_log 인터페이스를 통한 Application Log(SLG1) 기록으로 대체하는 편이 운영 관점에서 안전합니다.

3단계: RAP 서비스로 노출하는 프로덕션 예제

운영 환경에서는 위 뷰를 RAP 기반 OData 서비스로 노출하고, DCL(Data Control Language)로 권한을 걸며, 성능을 위해 인덱싱/버퍼링을 고려합니다.

@AccessControl.authorizationCheck: #CHECK
@Metadata.allowExtensions: true
@EndUserText.label: 'Activity Rate Consumption'
@Search.searchable: true
define root view entity Z_C_ActivityRate
  provider contract transactional_query
  as projection on Z_CostCenterActivityRate
{
  key ControllingArea,
  key CostCenter,
  key ActivityType,
      @Search.defaultSearchElement: true
      ActivityTypeName,
      ActivityUnit,
      FiscalYear,
      FiscalPeriod,
      @Semantics.amount.currencyCode: 'Currency'
      PlannedActivityPrice,
      @Semantics.currencyCode: true
      Currency
}
@EndUserText.label: 'DCL for Activity Rate'
@MappingRole: true
define role Z_ActivityRate_Role {
  grant select on Z_C_ActivityRate
    where (ControllingArea) = aspect pfcg_auth( K_TKA, KOKRS, ACTVT = '03' )
      and (CostCenter)      = aspect pfcg_auth( K_CSKS, KOSTL, ACTVT = '03' );
}

서비스 정의와 바인딩까지 마치면 Fiori Elements List Report로 즉시 소비 가능합니다. 추가로 다음을 권장합니다.

  • 단위 테스트는 CL_CDS_TEST_ENVIRONMENT를 이용해 I_ActivityType과 관련 뷰를 더블(double)로 대체한 뒤, 특정 요율 시나리오에서 프로젝션 뷰가 기대한 값을 반환하는지 검증합니다.
  • 성능 관점에서는 대량 조회 시 ControllingArea + CostCenter를 반드시 필터에 포함시키고, 필요하면 @ObjectModel.usageType.dataClass: #MASTER 힌트로 마스터 성격을 명시합니다.
  • 보안 관점에서는 DCL 없이 배포하지 않도록 CI 파이프라인에서 @AccessControl.authorizationCheck: #NOT_REQUIRED인 뷰를 자동 검출해 리뷰 게이트를 겁니다.

흔한 실수와 트러블슈팅

Q1. I_ActivityType에서 배부 요율(Price) 필드가 안 보입니다.
A. I_ActivityType은 마스터 성격의 Basic 뷰라 요율은 포함하지 않는 것이 일반적입니다. 요율은 I_ActivityTypePrice 계열, 없다면 COST 테이블 기반 커스텀 뷰와 조인해야 합니다.

Q2. 유효 기간 필터를 걸었는데 만료된 활동 유형이 계속 조회됩니다.
A. CSLA는 시간의존적(time-dependent) 마스터라 동일 ActivityType에 여러 유효 구간 레코드가 존재합니다. ValidityEndDate >= $session.system_date and ValidityStartDate <= $session.system_date 조건을 함께 걸어야 정확합니다.

Q3. 권한 오류가 뜨는데 SU53에서는 활동 유형 관련 오브젝트가 안 잡힙니다.
A. CO 권한은 원가 센터 오브젝트(K_CSKS) 및 컨트롤링 영역(K_TKA)에 의존하는 경우가 많습니다. DCL에서 K_CSKS/K_TKA를 매핑했는지 먼저 확인하고, 다음으로 ST01 권한 트레이스를 켜서 실제 체크 오브젝트를 잡아냅니다.

그 외 자주 마주치는 이슈로는, 컨트롤링 영역 통화와 원가 센터 통화가 달라 요율 조회 결과가 오해되는 경우가 있습니다. @Semantics.amount.currencyCode 어노테이션과 함께 통화 필드를 반드시 노출해 UI에서 혼동을 방지하는 것이 좋습니다.

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

  • I_CostCenter, I_CostCenterText와의 조인을 통한 원가 센터 그룹별 요율 대시보드
  • Analytical CDS View(@Analytics.dataCategory: #CUBE)로 전환해 SAC(SAP Analytics Cloud) 라이브 연결로 노출
  • RAP Behavior Definition을 통해 요율을 커스텀 액션으로 시뮬레이션
  • Embedded Steampunk / SAP BTP ABAP Environment에서 I_ActivityType 릴리즈 상태 확인

댓글 0

아직 댓글이 없습니다.