1. 개요 및 이 글에서 다루는 범위
S/4HANA의 CO-PA(Controlling - Profitability Analysis, 수익성 분석)는 매출과 원가를 다차원으로 분해하여 마진 구조를 파악하는 핵심 관리회계 기능입니다. 이 다차원 분석의 뼈대가 되는 것이 바로 I_ProfitabilitySegment CDS 뷰이며, 수익성 세그먼트(Profitability Segment) 번호에 매핑된 특성(Characteristic) 조합을 제공합니다. 이 글은 해당 뷰의 구조, 조인 전략, 실무 응용 방법을 심층적으로 다룹니다.
- I_ProfitabilitySegment의 필드 구조와 CO-PA 마스터 데이터 관계 이해
- 수익성 세그먼트 번호(PROFITABILITY_SEGMENT)를 통한 조인 패턴 습득
- Account-based CO-PA 환경에서 실전 리포팅 CDS 작성
- 성능 튜닝 및 권한(DCL) 처리 방법 습득
2. 사전에 갖춰야 할 배경
이 글은 S/4HANA CDS 뷰 문법(define view entity, association, annotation)에 익숙하고, CO-PA의 기본 개념(특성/값필드, Account-based vs Costing-based)을 이해하고 있는 독자를 대상으로 합니다. 또한 ACDOCA(Universal Journal) 테이블 구조와 CE4xxxx 세그먼트 테이블의 존재 이유를 알고 있어야 실무 활용도가 높아집니다. Eclipse ADT 사용 경험도 필요합니다.
3. 환경 및 버전, 준비물
본 예제는 다음 환경을 기준으로 작성되었습니다. 릴리스에 따라 뷰 필드 및 애노테이션이 일부 상이할 수 있으므로 SAP Notes로 최신 사항을 확인하는 것을 권장합니다.
- SAP S/4HANA 2022 FPS02 이상 (Cloud Private Edition / On-Premise)
- ABAP Platform 2022 이상, CDS View Entity 지원
- Account-based Profitability Analysis 활성화 (SPRO: Controlling → Profitability Analysis)
- Eclipse ADT (ABAP Development Tools) 2024-06 이상
- 필수 권한: S_DEVELOP (개발), S_RS_COMP (조회), CO-PA Operating Concern 접근권한
- 테스트 데이터: 최소 1개 이상의 판매 문서가 CO-PA로 전기되어 CE4xxxx 세그먼트가 생성된 상태
4. 핵심 개념: 수익성 세그먼트란 무엇인가
CO-PA에서 하나의 트랜잭션(예: 매출 인식)이 발생하면, 시스템은 해당 트랜잭션의 특성 조합(Customer + Product + Sales Organization + Distribution Channel + Region 등)을 하나의 고유 번호로 압축합니다. 이 번호가 바로 수익성 세그먼트 번호(PAOBJNR / PROFITABILITY_SEGMENT)이며, CE4xxxx(xxxx = Operating Concern) 테이블에 저장됩니다.
비유하자면, 수익성 세그먼트는 "복합 좌표를 압축한 우편번호"입니다. Customer=C001, Product=P100, Region=SEOUL이라는 세 개의 축을 매번 저장하는 대신, 하나의 8자리 숫자(예: 00012345)로 요약합니다. Universal Journal(ACDOCA)의 각 라인은 이 우편번호만 저장하고, 실제 세부 특성은 CE4에서 조회합니다.
I_ProfitabilitySegment는 이 CE4 테이블을 릴리스 독립적인 CDS 뷰로 감싼 Basic Interface View입니다. Operating Concern에 종속적인 물리 테이블명(CE4S001, CE4A100 등)을 직접 참조하지 않아도 되도록 추상화 계층을 제공하며, 다음과 같은 표준 특성을 노출합니다.
ProfitabilitySegment— 세그먼트 번호(PAOBJNR)ControllingArea— 관리 회계 영역Customer,Product,SalesOrganization,DistributionChannel,DivisionProfitCenter,BusinessArea,Country,Region- 고객 정의 특성(WWxxx 필드) — 프로젝트별로 확장
중요한 점은 이 뷰가 매출/원가 금액을 포함하지 않는다는 것입니다. 금액은 ACDOCA의 WSL, HSL, KSL 필드에서 조회하고, 특성은 이 뷰를 조인하여 조합하는 것이 Account-based CO-PA의 표준 패턴입니다.
5. 실전 코드: 단계별 구현
5-1. 기본: 세그먼트 특성 단독 조회
가장 먼저 I_ProfitabilitySegment를 단순 래핑하여 필요한 특성만 노출하는 Consumption View를 만듭니다. 재무팀에서 특정 관리회계 영역의 유효 세그먼트 목록을 원하는 시나리오입니다.
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'CO-PA 세그먼트 마스터 조회'
@Metadata.allowExtensions: true
define view entity ZC_CopaSegmentBasic
as select from I_ProfitabilitySegment as Seg
{
key Seg.ProfitabilitySegment as SegmentNumber,
Seg.ControllingArea as ControllingArea,
Seg.Customer as CustomerId,
Seg.Product as ProductId,
Seg.SalesOrganization as SalesOrg,
Seg.DistributionChannel as DistChannel,
Seg.Division as Division,
Seg.ProfitCenter as ProfitCenter,
Seg.Country as CountryCode,
Seg.Region as RegionCode
}
where Seg.ControllingArea = 'A000';
이 뷰를 Data Preview로 실행하면 CE4 테이블의 원본 세그먼트 레코드가 사람이 읽을 수 있는 이름으로 매핑되어 노출됩니다. ControllingArea 필터는 필수인데, 하나의 시스템에 여러 Operating Concern이 존재할 경우 성능 확보를 위한 것입니다.
5-2. 실무 시나리오: ACDOCA 조인 + 어소시에이션 + 로깅
실무에서는 세그먼트만으로 의미가 없습니다. ACDOCA와 조인하여 세그먼트별 매출/매출원가를 산출해야 합니다. 아래는 지역·제품 기준 마진 리포트의 Composite View입니다.
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'CO-PA 세그먼트별 마진 분석'
@VDM.viewType: #COMPOSITE
@Analytics.dataCategory: #CUBE
define view entity ZC_CopaMarginBySegment
as select from I_JournalEntryItem as Je
inner join I_ProfitabilitySegment as Seg
on Je.ProfitabilitySegment = Seg.ProfitabilitySegment
and Je.ControllingArea = Seg.ControllingArea
association [0..1] to I_Product as _Product
on $projection.ProductId = _Product.Product
association [0..1] to I_Customer as _Customer
on $projection.CustomerId = _Customer.Customer
{
key Je.CompanyCode as CompanyCode,
key Je.FiscalYear as FiscalYear,
key Je.FiscalPeriod as FiscalPeriod,
key Seg.ProfitabilitySegment as SegmentNumber,
Seg.Customer as CustomerId,
Seg.Product as ProductId,
Seg.SalesOrganization as SalesOrg,
Seg.Region as RegionCode,
Je.GLAccount as GLAccount,
@Semantics.amount.currencyCode: 'CompanyCodeCurrency'
@DefaultAggregation: #SUM
Je.AmountInCompanyCodeCurrency as AmountLC,
Je.CompanyCodeCurrency as CompanyCodeCurrency,
_Product,
_Customer
}
where Je.LedgerFiscalYear = '0L'
and Je.ProfitabilitySegment <> '0000000000';
여기서 눈여겨 볼 지점은 세 가지입니다. 첫째, ControllingArea도 조인 조건에 반드시 포함해야 합니다(세그먼트 번호는 Controlling Area 내에서만 유일). 둘째, Je.ProfitabilitySegment <> '0000000000' 필터로 CO-PA 미할당 라인을 제외합니다. 셋째, @Analytics.dataCategory: #CUBE로 팩트/차원 관계를 선언하여 Analytical Query로 확장 가능하게 합니다.
ABAP에서 이 뷰를 소비하며 예외 로깅을 넣는 예입니다.
CLASS zcl_copa_margin_reader DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
METHODS read_margin
IMPORTING iv_company_code TYPE bukrs
iv_fiscal_year TYPE gjahr
RETURNING VALUE(rt_data) TYPE STANDARD TABLE OF zc_copamarginbysegment.
ENDCLASS.
CLASS zcl_copa_margin_reader IMPLEMENTATION.
METHOD read_margin.
TRY.
SELECT company_code, fiscal_year, fiscal_period,
segment_number, product_id, region_code,
SUM( amount_l_c ) AS amount_l_c, company_code_currency
FROM zc_copamarginbysegment
WHERE company_code = @iv_company_code
AND fiscal_year = @iv_fiscal_year
GROUP BY company_code, fiscal_year, fiscal_period,
segment_number, product_id, region_code,
company_code_currency
INTO CORRESPONDING FIELDS OF TABLE @rt_data.
IF sy-subrc <> 0.
MESSAGE i001(zcopa) WITH iv_company_code iv_fiscal_year.
ENDIF.
CATCH cx_sy_open_sql_db INTO DATA(lx_sql).
cl_bali_log_writer=>write(
iv_object = 'ZCOPA'
iv_subobject = 'MARGIN_READ'
iv_message = lx_sql->get_text( ) ).
RAISE SHORTDUMP TYPE cx_sy_conversion_error EXPORTING previous = lx_sql.
ENDTRY.
ENDMETHOD.
ENDCLASS.
5-3. 프로덕션: 성능 최적화 + DCL 권한 + 단위 테스트
대량 데이터에서 세그먼트 조인은 병목의 주범입니다. 세그먼트 번호에 인덱스가 걸려있지만, ACDOCA와의 조인 순서 및 필터 순서가 잘못되면 풀 스캔이 발생합니다. 아래는 성능 힌트와 파라미터를 적용한 프로덕션 버전입니다.
@AccessControl.authorizationCheck: #CHECK
@ClientHandling.algorithm: #SESSION_VARIABLE
@Metadata.ignorePropagatedAnnotations: true
define view entity ZC_CopaSegmentAnalyticsProd
with parameters
p_controlling_area : ckmlha-kokrs,
p_from_period : poper,
p_to_period : poper,
p_fiscal_year : gjahr
as select from I_JournalEntryItem as Je
inner join I_ProfitabilitySegment as Seg
on Je.ProfitabilitySegment = Seg.ProfitabilitySegment
and Je.ControllingArea = Seg.ControllingArea
{
key Je.CompanyCode as CompanyCode,
key Je.AccountingDocument as AccountingDocument,
key Je.LedgerGLLineItem as LineItemId,
Seg.ProfitabilitySegment as SegmentNumber,
Seg.Customer as CustomerId,
Seg.Product as ProductId,
Seg.SalesOrganization as SalesOrg,
Seg.ProfitCenter as ProfitCenter,
@Semantics.amount.currencyCode: 'CompanyCodeCurrency'
Je.AmountInCompanyCodeCurrency as AmountLC,
Je.CompanyCodeCurrency as CompanyCodeCurrency
}
where Je.ControllingArea = :p_controlling_area
and Je.FiscalYear = :p_fiscal_year
and Je.FiscalPeriod between :p_from_period and :p_to_period
and Je.LedgerFiscalYear = '0L'
and Je.ProfitabilitySegment <> '0000000000';
파라미터를 통한 강제 필터 푸시다운이 핵심입니다. 그리고 DCL로 Profit Center 기준 권한을 걸어 조직별 데이터 격리를 구현합니다.
@MappingRole: true
define role ZC_CopaSegmentProdRole {
grant select on ZC_CopaSegmentAnalyticsProd
where ProfitCenter = aspect pfcg_auth (K_PCA, KOKRS, PRCTR, ACTVT = '03')
and CompanyCode = aspect pfcg_auth (F_BKPF_BUK, BUKRS, ACTVT = '03');
}
단위 테스트는 CDS Test Double Framework를 활용합니다.
CLASS ltcl_copa_margin DEFINITION FINAL FOR TESTING
DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
DATA: mo_env TYPE REF TO if_cds_test_environment.
METHODS: setup, single_segment FOR TESTING.
ENDCLASS.
CLASS ltcl_copa_margin IMPLEMENTATION.
METHOD setup.
mo_env = cl_cds_test_environment=>create( i_for_entity = 'ZC_COPAMARGINBYSEGMENT' ).
mo_env->clear_doubles( ).
ENDMETHOD.
METHOD single_segment.
DATA lt_seg TYPE STANDARD TABLE OF i_profitabilitysegment.
lt_seg = VALUE #( ( profitabilitysegment = '0000012345'
controllingarea = 'A000'
customer = 'C1001'
product = 'P200' ) ).
mo_env->insert_test_data( lt_seg ).
" 검증 로직 생략
ENDMETHOD.
ENDCLASS.
6. 흔한 실수와 트러블슈팅
실무에서 자주 마주치는 문제와 해결책입니다.
Q1. 세그먼트 번호가 '0000000000'인 라인이 대량으로 나옵니다.
A. 이는 CO-PA 대상이 아닌 GL 라인(예: 자산 계정, 재무 지급)입니다. WHERE 절에서 명시적으로 제외해야 합니다. 또한 Costing-based CO-PA만 활성화된 시스템에서는 ACDOCA에 CO-PA 특성이 채워지지 않으므로, CE1xxxx 테이블 기반 뷰(I_CostingBasedProfitabilityAnalysis 계열)를 사용해야 합니다.
Q2. Data Preview에서 CE4 테이블이 없다는 덤프가 발생합니다.
A. Operating Concern이 활성화되지 않았거나 클라이언트에 CO-PA가 세팅되지 않은 경우입니다. KEA0 트랜잭션에서 Operating Concern 상태를 확인하고, 필요 시 KEKE로 클라이언트에 할당하는 것을 권장합니다.
Q3. 조인 시 카티션 곱이 발생해 결과가 급증합니다.
A. 조인 조건에 ControllingArea를 빠뜨린 경우가 대부분입니다. 세그먼트 번호는 Controlling Area 내부에서만 유일하므로, 반드시 ProfitabilitySegment와 ControllingArea를 함께 조인 키로 사용해야 합니다.
Q4. 커스텀 특성(WW필드)이 뷰에 노출되지 않습니다.
A. I_ProfitabilitySegment는 표준 특성 위주로 노출됩니다. 프로젝트 커스텀 특성은 CDS Extension(extend view entity)으로 확장하거나, CE4xxxx 물리 테이블을 직접 참조하는 별도 뷰를 만드는 방식이 일반적으로 권장됩니다.
7. 확장 학습 방향
이 글에서 다룬 세그먼트 조회를 마스터했다면, 다음 주제로 시야를 넓히는 것을 권장합니다. Margin Analysis Cube(I_MarginAnalysisCube) 계열 뷰를 통해 사전 정의된 마진 리포트 구조를 학습하고, Predictive Accounting 및 Group Reporting과의 연계 시나리오를 검토해볼 수 있습니다. 또한 SAP Analytics Cloud Live Connection으로 이 CDS 뷰들을 노출하여 대시보드화하는 아키텍처도 실무 가치가 큽니다.
- I_MarginAnalysisCube와 예측 원가 연계
- Universal Parallel Accounting (UPA) 도입 시 세그먼트 처리 변화
- Analytical Query(@Analytics.query: true)로 KPI 계산 필드 추가
8. 함께 보면 좋은 자료
- SAP S/4HANA On-Premise 공식 문서 포털 (help.sap.com)
- SAP S/4HANA Cloud Profitability Analysis 가이드 (help.sap.com)
- ABAP CDS View Entity 개발 가이드 (help.sap.com)
- SAP API Business Hub - CDS View 카탈로그
- SAP Community - S/4HANA Finance
- SAP Note 2349278 - Account-Based Profitability Analysis Migration
- SAP Note 2382863 - Universal Journal 및 CO-PA 통합 아키텍처
댓글 0
아직 댓글이 없습니다.