ABAP

BUT000 vs BP 통합 뷰 — 조회 방법 차이 #shorts #SAP #ABAP

▶ YouTube에서 보기

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

SAP S/4HANA에서 가장 자주 호출되는 CDS 뷰 중 하나가 바로 I_BusinessPartner입니다. 전통적인 ERP 시대에는 고객(KNA1)과 공급업체(LFA1)가 분리되어 있었지만, S/4HANA는 BUT000을 중심으로 비즈니스 파트너(BP)를 단일 엔티티로 통합했습니다. 이 글은 그 통합 구조가 어떻게 CDS 뷰로 노출되는지, 그리고 실무에서 안전하게 조회·확장·재사용하는 방법을 단계별로 다룹니다.

  • BUT000과 KNA1/LFA1의 관계, 그리고 CVI(Customer-Vendor Integration)의 역할 이해
  • I_BusinessPartner 기본 필드와 연관 association 활용
  • 실무에서 자주 쓰는 조회 패턴(고객 전용, 공급업체 전용, 통합)
  • 성능·권한·확장(extension) 고려 사항

이 글을 읽기 전 알아두면 좋은 것

ABAP CDS 뷰의 기본 문법(define view entity, association, annotation)과 OpenSQL SELECT 구문에 익숙해야 합니다. 또한 SAP S/4HANA의 BP 트랜잭션(BP, BUP1/BUP3) 개념과 역할(Role: FLCU01, FLVN01 등)을 이해하고 있으면 본문 이해가 훨씬 빠릅니다. SE11/SE16N으로 BUT000, BUT020, BUT100 테이블을 조회해 본 경험이 있으면 좋습니다.

실습 환경과 사전 준비

이 글의 예제는 다음 환경을 기준으로 작성했습니다. 버전이 달라지면 필드 이름이나 association 명이 약간 변경될 수 있으니 시스템에서 직접 확인하길 권장합니다.

  • SAP S/4HANA 2022(또는 2023) on-premise / S/4HANA Cloud Public Edition 2402 이상
  • ABAP Development Tools(ADT) for Eclipse 2023-12 이상
  • HANA DB(2.0 SPS06+) 또는 Cloud 환경의 HANA Cloud
  • SAP_GWFND, SAP_BS_FND 컴포넌트가 활성화된 시스템
  • BP가 최소 10건 이상 등록되어 있고, 그중 일부에 Customer/Vendor 역할이 할당된 상태
  • 권한: S_RS_AUTH, B_BUPA_GRP(BP 권한 그룹), F_KNA1_BUK(고객) 등

온프레미스 환경이라면 SE80에서 패키지 API_BUSINESS_PARTNER의 존재 여부와 I_BusinessPartner 뷰가 활성 상태인지를 먼저 확인하는 것이 일반적입니다.

핵심 개념 — BUT000 중심의 BP 데이터 모델

S/4HANA의 BP 모델을 한 줄로 요약하면 "하나의 BP 번호(PARTNER)가 여러 역할(Role)을 가질 수 있고, 각 역할이 KNA1/LFA1 같은 전통 마스터로 매핑된다"입니다. 다음과 같은 계층으로 생각하면 이해가 쉽습니다.

BUT000 (Business Partner General Data)
  ├── BUT020 (BP - Address Assignment)
  ├── BUT021_FS (BP - Address Usage)
  ├── BUT100 (BP - Roles)
  ├── CVI_CUST_LINK → KNA1 (Customer)
  └── CVI_VEND_LINK → LFA1 (Vendor/Supplier)

여기서 핵심은 CVI(Customer-Vendor Integration)입니다. BP에 고객 역할(예: FLCU00, FLCU01)을 부여하면 CVI_CUST_LINK를 통해 KNA1 레코드가 자동 생성·동기화됩니다. 공급업체도 마찬가지로 CVI_VEND_LINKLFA1로 연결됩니다. 즉, 같은 회사가 우리 고객이자 협력업체일 수 있으며, S/4HANA에서는 동일한 BP 번호 아래에서 두 역할을 모두 보유합니다.

이 복잡한 관계를 매번 JOIN으로 풀어 쓰는 것은 비효율적이기 때문에 SAP는 I_BusinessPartner라는 기본(basic) 인터페이스 뷰를 제공합니다. 비유하자면, BUT000이 원자재 창고라면 I_BusinessPartner는 "공장에서 1차 가공이 끝난 반제품"입니다. 여기에 association을 더 따라가면 주소(I_BusinessPartnerAddress), 역할(I_BusinessPartnerRole), 고객(I_Customer), 공급업체(I_Supplier)로 자연스럽게 연결됩니다.

I 계열(Interface) 뷰는 안정적인 API 계약을 의미하며, C 계열(Consumption)·P 계열(Private)과 구분됩니다. 일반적으로 커스텀 개발에서는 I 뷰를 직접 또는 association으로 호출하는 것이 권장됩니다.

실전 코드 — 1단계: 기본 조회

첫 번째 예제는 BP 번호와 이름, 카테고리(개인/법인)를 가져오는 가장 단순한 형태입니다. 시나리오는 "최근 등록된 법인 BP 50건을 화면에 표시"입니다.

REPORT zr_bp_list_basic.

DATA: lt_partner TYPE TABLE OF i_businesspartner,
      ls_partner TYPE i_businesspartner.

SELECT FROM i_businesspartner
  FIELDS businesspartner,
         businesspartnercategory,
         businesspartnerfullname,
         organizationbpname1,
         creationdate
  WHERE  businesspartnercategory = '2'   "2 = Organization
  ORDER BY creationdate DESCENDING
  INTO TABLE @lt_partner
  UP TO 50 ROWS.

LOOP AT lt_partner INTO ls_partner.
  WRITE: / ls_partner-businesspartner,
           ls_partner-businesspartnerfullname,
           ls_partner-creationdate.
ENDLOOP.

BusinessPartnerCategory'1'(Person), '2'(Organization), '3'(Group) 값을 가집니다. BusinessPartnerFullName은 카테고리에 따라 개인 이름 또는 조직명을 자동 조합해 제공하므로, 화면 표시용으로 매우 유용합니다.

실전 코드 — 2단계: 고객·공급업체 통합 조회와 예외 처리

실무에서는 "이 BP가 고객인가 공급업체인가, 아니면 둘 다인가"를 알아야 하는 경우가 많습니다. 예를 들어 글로벌 제조사 "ACME Industries"가 우리에게 부품을 팔기도 하고 완제품을 사가기도 한다고 가정합시다. 다음은 association을 활용해 한 번에 조회하는 패턴입니다.

@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'BP with Customer/Vendor Linkage'
define view entity ZI_BpDualRole
  as select from I_BusinessPartner as bp
  association [0..1] to I_Customer  as _Cust on  _Cust.Customer = bp.Customer
  association [0..1] to I_Supplier  as _Supp on  _Supp.Supplier = bp.Supplier
{
  key bp.BusinessPartner,
      bp.BusinessPartnerFullName,
      bp.BusinessPartnerCategory,
      bp.Customer,
      bp.Supplier,
      case when bp.Customer is not initial
            and bp.Supplier is not initial then 'BOTH'
           when bp.Customer is not initial then 'CUST'
           when bp.Supplier is not initial then 'VEND'
           else 'NONE'
      end as RoleFlag,
      _Cust.CustomerAccountGroup,
      _Supp.SupplierAccountGroup,
      _Cust,
      _Supp
}

이 뷰를 호출하는 ABAP 코드에서는 결과가 없을 때, 권한이 부족할 때 등을 명시적으로 처리해야 운영 환경에서 추적이 가능합니다.

DATA(lo_log) = cl_bali_log_db_writer=>create( ).

TRY.
    SELECT FROM zi_bpdualrole
      FIELDS businesspartner, businesspartnerfullname,
             roleflag, customer, supplier
      WHERE  roleflag = 'BOTH'
      INTO TABLE @DATA(lt_dual).

    IF lt_dual IS INITIAL.
      MESSAGE 'No BP with both Customer and Vendor role' TYPE 'I'.
      RETURN.
    ENDIF.

  CATCH cx_sy_open_sql_db INTO DATA(lx_sql).
    " 권한 부족 또는 뷰 비활성 상태일 때 발생 가능
    cl_demo_output=>display( |SQL error: { lx_sql->get_text( ) }| ).
ENDTRY.

여기서 두 가지를 주의해야 합니다. 첫째, Customer·Supplier 필드는 BP에 해당 역할이 부여되고 CVI 동기화가 완료된 경우에만 채워집니다. 역할만 부여하고 저장하지 않은 상태(트랜잭션 BP에서 hold 중)에는 비어 있을 수 있습니다. 둘째, I_Customer·I_Supplier association은 회사 코드(CompanyCode)·구매 조직(PurchasingOrganization) 레벨이 아닌 일반 데이터(KNA1/LFA1) 레벨입니다. 회사 코드별 신용한도나 결제 조건이 필요하다면 I_CustomerCompany, I_SupplierCompany로 한 단계 더 내려가야 합니다.

실전 코드 — 3단계: 프로덕션 품질 — 성능, 권한, 단위 테스트

실 운영에서는 BP 데이터가 수십만 건에 달하므로, 무리한 association 전개나 와일드카드 검색은 HANA에서도 비용이 큽니다. 다음은 페이지네이션, DCL(권한), ABAP Unit 테스트를 함께 적용한 예제입니다.

// DCL 권한 정의 예시
@MappingRole: true
define role ZI_BpDualRole_Role {
  grant select on ZI_BpDualRole
    where (BusinessPartner) = aspect pfcg_auth( B_BUPA_GRP, ACTVT = '03' );
}
CLASS zcl_bp_query DEFINITION PUBLIC FINAL CREATE PUBLIC.
  PUBLIC SECTION.
    TYPES: BEGIN OF ty_bp_row,
             partner  TYPE bu_partner,
             fullname TYPE bu_namepartf,
             role     TYPE c LENGTH 4,
           END OF ty_bp_row,
           tt_bp_row TYPE STANDARD TABLE OF ty_bp_row WITH EMPTY KEY.

    METHODS get_dual_role_partners
      IMPORTING iv_page_size TYPE i DEFAULT 100
                iv_offset    TYPE i DEFAULT 0
      RETURNING VALUE(rt_result) TYPE tt_bp_row
      RAISING   cx_sy_open_sql_db.
ENDCLASS.

CLASS zcl_bp_query IMPLEMENTATION.
  METHOD get_dual_role_partners.
    SELECT FROM zi_bpdualrole
      FIELDS businesspartner    AS partner,
             businesspartnerfullname AS fullname,
             roleflag           AS role
      WHERE  roleflag IN ( 'BOTH', 'CUST', 'VEND' )
      ORDER BY businesspartner
      INTO TABLE @rt_result
      OFFSET @iv_offset
      UP TO @iv_page_size ROWS.
  ENDMETHOD.
ENDCLASS.

단위 테스트는 CDS Test Double Framework를 사용해 DB 의존성을 제거합니다. 이 패턴은 회귀 테스트를 가볍게 돌릴 수 있어 권장됩니다.

CLASS ltc_bp_query DEFINITION FINAL FOR TESTING
  DURATION SHORT RISK LEVEL HARMLESS.
  PRIVATE SECTION.
    DATA mo_cut TYPE REF TO zcl_bp_query.
    DATA mo_env TYPE REF TO if_cds_test_environment.
    METHODS class_setup.
    METHODS get_dual_role FOR TESTING.
ENDCLASS.

CLASS ltc_bp_query IMPLEMENTATION.
  METHOD class_setup.
    mo_env = cl_cds_test_environment=>create(
               i_for_entity = 'ZI_BPDUALROLE' ).
  ENDMETHOD.

  METHOD get_dual_role.
    DATA(lt_doubles) = VALUE zi_bpdualrole_tt(
      ( businesspartner = '0000010001'
        businesspartnerfullname = 'ACME Industries'
        roleflag = 'BOTH' ) ).
    mo_env->insert_test_data( lt_doubles ).

    mo_cut = NEW #( ).
    DATA(lt_result) = mo_cut->get_dual_role_partners( ).
    cl_abap_unit_assert=>assert_equals(
      exp = 1 act = lines( lt_result ) ).
  ENDMETHOD.
ENDCLASS.

성능 관점에서는 SELECT * 대신 필요한 필드만 명시하고, BusinessPartner 키 또는 SearchTerm1·CreationDate 인덱스를 활용하는 것이 일반적입니다. 와일드카드 검색은 SearchTerm1처럼 인덱스 친화적 필드에서만 권장됩니다.

흔한 실수와 트러블슈팅 FAQ

Q1. BP를 만들었는데 Customer 필드가 비어 있습니다.
A. BP에 고객 역할(FLCU00 일반, FLCU01 회사코드)이 부여되지 않았거나 CVI 동기화가 실패했을 가능성이 높습니다. 트랜잭션 MDS_LOAD_COCKPIT 또는 CVI_MAPPING_ERROR에서 동기화 로그를 확인하길 권장합니다.

Q2. I_BusinessPartnerA_BusinessPartner(OData)의 차이가 뭔가요?
A. I_ 접두어는 ABAP 내부에서 재사용하는 인터페이스 뷰이고, A_는 외부 OData 서비스(API_BUSINESS_PARTNER)로 노출되는 소비용 엔티티입니다. 내부 ABAP 로직은 I 뷰를, 외부 시스템 연동은 OData를 사용하는 것이 일반적입니다.

Q3. 고객과 공급업체를 INNER JOIN으로 묶으면 결과가 이상합니다.
A. BP는 두 역할을 모두 갖지 않을 수 있으므로 LEFT OUTER JOIN(또는 [0..1] association)을 써야 합니다. INNER JOIN은 "양쪽 역할이 모두 있는 BP"만 추리고 싶을 때만 의도적으로 사용합니다.

Q4. 권한 오류가 발생합니다.
A. B_BUPA_GRP(BP 권한 그룹), B_BUPA_RLT(BP 역할), 그리고 고객/공급업체 회사 코드 권한(F_KNA1_BUK, F_LFA1_BEK)을 모두 확인해야 합니다. DCL이 적용된 CDS 뷰는 PFCG 역할의 권한 객체를 그대로 따라갑니다.

Q5. 주소나 통신 정보는 어디에 있나요?
A. I_BusinessPartner 자체에는 표준 주소만 일부 포함됩니다. 다중 주소는 I_BusinessPartnerAddress, 통신은 I_AddressEmailAddress·I_AddressPhoneNumber를 association으로 따라가야 합니다.

이어서 살펴보면 좋은 주제

이 글에서 다룬 기본 패턴을 익혔다면 다음 주제로 확장해 보길 권장합니다. 첫째, I_BusinessPartner를 RAP(Restful ABAP Programming)의 데이터 소스로 활용해 BP 검색 Fiori 앱을 만드는 시나리오. 둘째, CDS View Entity Extension으로 회사별 커스텀 필드를 추가하는 방법(EXTEND VIEW ENTITY). 셋째, BP 변경 이벤트를 캡처하는 BUPA_INTERACT_BUPA BAdI 및 ABAP Daemon을 통한 실시간 동기화. 넷째, S/4HANA Cloud의 API_BUSINESS_PARTNER OData 서비스와의 매핑 차이.

더 깊이 파고들 수 있는 자료

댓글 0

아직 댓글이 없습니다.