ABAP

ABAP 개발자 80%가 놓치는 I_ServiceOrder 구조 #shorts #SAP #ABAP

▶ YouTube에서 보기

1. I_ServiceOrder가 필요한 이유 — CS 도메인의 데이터 통합 과제

SAP S/4HANA Customer Service(CS) 모듈에서 서비스 오더(Service Order)는 고객 설비의 수리, 정기 점검, 현장 출동 작업 등을 처리하는 핵심 트랜잭션입니다. 기존 ECC 시절에는 AUFK, VIQMEL, RIPW1 같은 트랜잭션 테이블을 직접 조인해 데이터를 추출해야 했고, 헤더와 오퍼레이션(Operation), 컴포넌트(Component) 사이의 관계를 일일이 SELECT로 풀어내는 보고서가 흔했습니다. S/4HANA로 넘어오면서 CDS(Core Data Services) View 레이어가 도입되었고, 그중 I_ServiceOrder는 서비스 오더 헤더를 단일 진입점으로 노출하는 인터페이스 뷰(Interface View) 역할을 합니다.

이 글에서 다루는 범위는 다음과 같습니다.

  • I_ServiceOrder의 CDS 계층 내 위치와 명명 규칙 이해
  • 서비스 오더 헤더의 핵심 필드와 의미 분석
  • Sold-To Party, Ship-To Party 등 고객 마스터와의 Association 구조
  • ABAP CDS 소비(Consumption) 코드 작성 패턴 3단계
  • OData 노출 및 운영 환경에서 자주 쓰는 필터 전략

대상 독자는 ABAP 7.55 이상, S/4HANA 2022 또는 2023 환경에서 CS 모듈을 다루는 개발자, 그리고 Fiori/BTP 측에서 서비스 오더 데이터를 조회해야 하는 백엔드 엔지니어를 가정합니다.

2. 사전 점검 — 알고 있어야 할 배경

이 글을 효율적으로 따라오려면 ABAP CDS DDIC View 정의 문법(@AbapCatalog, define view, association)에 익숙해야 합니다. 또한 Virtual Data Model(VDM)의 3계층 — Basic, Composite, Consumption — 구분을 한 번이라도 학습했다면 좋습니다. CS 모듈의 트랜잭션(IW31, IW32)과 서비스 오더 타입(SM01, SM02 등) 개념, 그리고 비즈니스 파트너(BP) 마스터에 Sold-To/Ship-To 역할을 부여하는 방식을 알고 있으면 7번 섹션의 OData 연동까지 매끄럽게 이해할 수 있습니다.

3. 검증 환경과 준비 사항

다음 환경에서 동작을 확인한 결과를 기준으로 설명합니다. 마이너 릴리스에 따라 필드 가용성이 달라질 수 있으므로, 실제 시스템에서는 SE11 또는 ADT의 Data Preview로 한 번 더 확인하는 것을 권장합니다.

  • S/4HANA on-premise 2022 FPS02 (또는 2023 초기 릴리스)
  • ABAP Platform 2022, 일반적으로 NetWeaver 7.57 기반
  • ABAP Development Tools(ADT) for Eclipse 2023-09 이상
  • 권한 오브젝트: I_AUART(서비스 오더 타입), B_USERSTAT(상태), 그리고 BP 조회를 위한 B_BUPA_RLT
  • 샘플 데이터: CS 베스트프랙티스 스코프 아이템 BH1(Service Order Management)로 생성된 데모 오더

ADT에서 Ctrl+Shift+AI_ServiceOrder를 검색하면 뷰 정의 소스를 바로 열람할 수 있습니다. 시스템에 해당 뷰가 보이지 않는다면 CS 관련 비즈니스 평션(CS_SE_CI_1 계열)이 활성화되지 않았을 가능성이 큽니다.

4. CDS 계층 구조에서 I_ServiceOrder의 위치

SAP VDM은 알파벳 접두어로 계층을 구분합니다. I_는 인터페이스(reuse) 뷰, C_는 컨슈머(소비) 뷰, P_는 프라이빗(내부) 뷰를 의미합니다. 비유하자면 I_ 계층은 도서관의 "표준화된 목록 카드", C_ 계층은 "독자가 들고 가는 추천 코너", P_ 계층은 "사서만 들어가는 서고"에 해당합니다.

서비스 오더 영역의 계층은 다음과 같이 정리됩니다.

  • R_ServiceOrderTP 또는 I_ServiceOrder — 인터페이스 레벨 헤더
  • I_ServiceOrderItem — 오퍼레이션/아이템 레벨
  • I_ServiceOrderPersonResp — 담당자/파트너 연계
  • C_ServiceOrderTP — Fiori 앱(예: Manage Service Orders)이 소비하는 트랜잭션 뷰

I_ServiceOrder는 AUFK(오더 마스터), VIQMEL(알림 헤더), VBAK(SD 헤더 일부 필드) 등을 결합해 단일한 평탄(flat) 구조로 노출합니다. 따라서 개발자는 더 이상 "어느 테이블에서 어떤 키로 조인해야 하는가"를 고민하지 않고, 비즈니스 도메인 관점에서 데이터에 접근할 수 있습니다.

핵심은 인터페이스 뷰이기 때문에 안정된 계약(stable contract)이 보장된다는 점입니다. SAP가 내부 테이블 구조를 변경하더라도 I_ 뷰의 필드명은 유지되므로, 커스텀 개발에서 이 뷰를 베이스로 잡으면 업그레이드 영향도가 크게 줄어듭니다.

5. 헤더 핵심 필드 해부

I_ServiceOrder의 주요 필드를 카테고리별로 정리하면 다음과 같습니다. 필드명은 시맨틱 명명(semantic naming) 규칙을 따르며, 기술 컬럼(AUFNR)이 아니라 비즈니스 의미가 드러나는 이름(ServiceOrder)을 사용합니다.

  • 키 영역: ServiceOrder(오더 번호), ServiceOrderType(오더 타입, 예: SM01), ServiceOrderCategory(카테고리 코드 30 = 서비스)
  • 일정: BasicStartDate, BasicEndDate, ScheduledStartDate, ScheduledEndDate
  • 조직 단위: MaintenancePlant, MaintenancePlanningPlant, SalesOrganization, DistributionChannel, Division
  • 고객 정보: Customer(Sold-To Party), ShipToParty, SalesOffice
  • 기술 오브젝트: TechnicalObject, TechnicalObjectType, Equipment, FunctionalLocation
  • 상태/통화: OrderIsReleased, OrderIsTechnicallyCompleted, TransactionCurrency

특히 ServiceOrderCategory는 PM(Plant Maintenance) 오더와 CS 오더를 구분하는 핵심 키입니다. PM 오더는 보통 카테고리 30 중에서도 내부 발주 형태이고, CS 오더는 외부 고객을 대상으로 하므로 Customer 필드가 채워져 있는지 여부로 추가 식별합니다.

6. 고객 정보 연계 — Sold-To와 Ship-To의 분리

CS 서비스 오더가 PM 오더와 가장 크게 다른 점은 외부 고객이 결제 및 수령 주체로 등장한다는 사실입니다. I_ServiceOrder는 다음과 같이 두 종류의 고객 ID를 노출합니다.

  • Customer — Sold-To Party. 청구 및 계약 주체
  • ShipToParty — 실제 현장에서 작업을 받는 고객(예: 본사가 청구를 받고 지점이 수리를 받는 경우)

두 필드 모두 비즈니스 파트너(BP) 마스터의 BU_PARTNER로 연결되며, I_ServiceOrder는 _Customer, _ShipToParty Association을 통해 I_Customer 또는 I_BusinessPartner 뷰로 즉시 탐색할 수 있게 해줍니다. 즉, 별도의 JOIN을 작성하지 않아도 _Customer.CustomerName처럼 점 표기법으로 접근이 가능합니다.

도식으로 표현하면 다음과 같습니다.

I_ServiceOrder
   |-- _Customer ---------> I_Customer (Sold-To)
   |-- _ShipToParty ------> I_Customer (Ship-To)
   |-- _TechnicalObject --> I_TechnicalObject
   |-- _MaintenancePlant -> I_Plant
   |-- _to_ServiceOrderItem (1:N) -> I_ServiceOrderItem

이 구조 덕분에 "이 서비스 오더의 청구 고객명과 출고지 고객명을 동시에 보여달라"는 요구사항을 단 한 줄의 CDS 쿼리로 처리할 수 있습니다.

7. 실전 예제 — 단계별 ABAP 코드

가상의 시나리오로 "산업용 냉동기 유지보수 서비스 회사"의 백오피스 리포트를 만든다고 가정합니다. 3단계로 점진적으로 확장합니다.

1단계: 기본 조회 — 특정 기간의 미완료 오더 목록

REPORT zr_cs_chiller_open_orders.

DATA: lt_open_orders TYPE TABLE OF i_serviceorder.

SELECT serviceorder,
       serviceordertype,
       basicstartdate,
       basicenddate,
       customer,
       shiptoparty,
       orderisreleased,
       orderistechnicallycompleted
  FROM i_serviceorder
  WHERE serviceordercategory = '30'
    AND basicstartdate       BETWEEN @lv_from AND @lv_to
    AND orderistechnicallycompleted = ' '
  INTO TABLE @lt_open_orders.

cl_demo_output=>display( lt_open_orders ).

여기까지가 가장 단순한 형태입니다. CDS 뷰를 직접 SELECT 하는 방식은 ABAP 7.40 SP08 이상에서 지원되며, 내부적으로 HANA 푸시다운이 이루어져 클라이언트 메모리 부하가 적습니다.

2단계: 실무 시나리오 — 고객 정보 및 기술 객체와 결합, 예외 처리 포함

CLASS zcl_chiller_service_query DEFINITION PUBLIC FINAL CREATE PUBLIC.
  PUBLIC SECTION.
    TYPES: BEGIN OF ty_enriched_order,
             service_order   TYPE i_serviceorder-serviceorder,
             sold_to_name    TYPE bu_nameor1,
             ship_to_city    TYPE ad_city1,
             equipment_text  TYPE eqktx,
             planned_start   TYPE dats,
           END OF ty_enriched_order.

    METHODS fetch_enriched
      IMPORTING iv_plant TYPE werks_d
      RETURNING VALUE(rt_result) TYPE STANDARD TABLE OF ty_enriched_order
      RAISING   cx_sy_open_sql_db.
ENDCLASS.

CLASS zcl_chiller_service_query IMPLEMENTATION.
  METHOD fetch_enriched.
    TRY.
        SELECT so~serviceorder,
               so~\_Customer-OrganizationBPName1     AS sold_to_name,
               so~\_ShipToParty-CityName             AS ship_to_city,
               so~\_TechnicalObject-EquipmentName    AS equipment_text,
               so~scheduledstartdate                 AS planned_start
          FROM i_serviceorder AS so
          WHERE so~maintenanceplant = @iv_plant
            AND so~orderisreleased   = 'X'
          ORDER BY so~scheduledstartdate DESCENDING
          INTO TABLE @rt_result.

      CATCH cx_sy_open_sql_db INTO DATA(lx_db).
        " 로깅 후 상위로 재던지기
        cl_bali_log_db=>get_instance( )->add_message(
            iv_msg = lx_show->get_text( ) ).
        RAISE EXCEPTION lx_db.
    ENDTRY.
  ENDMETHOD.
ENDCLASS.

주목할 점은 so~\_Customer-OrganizationBPName1처럼 경로 표현식(path expression)을 사용했다는 점입니다. 백슬래시는 Association을 "왼쪽 외부 조인"으로 해석하라는 신호이며, 이 한 줄이 ECC 시절의 다단 JOIN 수십 줄을 대체합니다.

3단계: 프로덕션 — 페이징, 권한 체크, 단위 테스트

METHOD fetch_paged_orders.
  " 입력: iv_offset, iv_page_size, iv_status_filter
  " 인증된 사용자 권한 체크
  AUTHORITY-CHECK OBJECT 'I_AUART'
           ID 'AUART' FIELD iv_order_type
           ID 'ACTVT' FIELD '03'.
  IF sy-subrc <> 0.
    RAISE EXCEPTION TYPE zcx_cs_not_authorized.
  ENDIF.

  SELECT FROM i_serviceorder
    FIELDS serviceorder,
           customer,
           \_Customer-OrganizationBPName1 AS customer_name,
           basicstartdate,
           transactioncurrency
    WHERE servicedordertype     = @iv_order_type
      AND orderistechnicallycompleted = @iv_status_filter
    ORDER BY serviceorder
    OFFSET @iv_offset
    UP TO @iv_page_size ROWS
    INTO TABLE @DATA(lt_page).

  rt_result = lt_page.
ENDMETHOD.

테스트는 ABAP Unit + CDS Test Double Framework(cl_cds_test_environment)를 활용해 I_ServiceOrder를 인메모리 더블로 치환하면 됩니다. 운영 데이터에 의존하지 않고 회귀 검증이 가능해집니다.

8. OData 노출과 자주 쓰는 필터 패턴

I_ServiceOrder는 그 자체로 OData 서비스가 아니지만, 그 위에 C_ServiceOrderTP가 만들어지고 다시 @OData.publish: true 또는 RAP의 서비스 정의(Service Definition + Service Binding)를 통해 외부에 노출됩니다. Fiori 표준 앱 Manage Service Orders는 이 컨슈머 뷰를 기반으로 동작합니다.

실무에서 자주 쓰는 OData 쿼리 패턴은 다음과 같습니다.

GET /sap/opu/odata4/sap/api_service_order/srvd_a2x/sap/serviceorder/0001/ServiceOrder
   ?$filter=ServiceOrderCategory eq '30'
          and OrderIsTechnicallyCompleted eq false
          and Customer eq '0010001234'
   &$expand=_Customer,_TechnicalObject
   &$top=50

운영에서 자주 만나는 케이스별 권장 필터는 다음과 같습니다.

  • 일별 디스패치 보드: BasicStartDate eq today + MaintenancePlant eq 'XXXX'
  • SLA 위반 추적: ScheduledEndDate lt now and OrderIsTechnicallyCompleted eq false
  • 주요 고객 모니터링: Customer in ('VIP1','VIP2') and CreationDate ge 2026-01-01
  • 장비별 이력: Equipment eq '10000045' and ServiceOrderCategory eq '30'

흔한 함정과 트러블슈팅

마지막으로 현장에서 반복적으로 발견되는 세 가지 함정을 정리합니다.

  1. "필드가 비어 있는데 데이터는 분명 있다" — 클라이언트(MANDT) 필터가 누락된 커스텀 뷰를 만들면 일반적으로 발생합니다. I_ServiceOrder는 클라이언트 의존적이므로, 이를 베이스로 한 커스텀 뷰는 @ClientHandling.algorithm: #SESSION_VARIABLE가 자동 적용되는지 확인하세요.
  2. "PM 오더가 같이 조회된다"ServiceOrderType만 필터링하면 부족합니다. ServiceOrderCategory = '30' + Customer IS NOT INITIAL 조건을 함께 걸어야 CS 오더만 정확히 추출됩니다.
  3. "Association 경로가 NULL을 반환한다" — Sold-To만 입력하고 Ship-To는 비워두는 마스터 데이터가 의외로 많습니다. 경로 표현식의 결과가 NULL일 수 있다는 점을 전제하고, COALESCE 또는 ABAP 측에서 INITIAL 체크를 반드시 추가하세요.

이어서 학습하면 좋은 주제

I_ServiceOrder의 구조를 파악했다면, 다음 단계로 RAP(RESTful ABAP Programming Model) 기반의 R_ServiceOrderTP 비즈니스 오브젝트를 분석하고, 거기서 노출되는 액션(Release, TechnicallyComplete)을 호출하는 사용자 정의 Fiori 앱을 만들어보는 것을 권장합니다. 또한 BTP 측에서는 SAP Build Apps 또는 CAP을 통해 동일한 데이터를 외부 포털로 노출하는 시나리오로 확장할 수 있습니다. 마지막으로 Embedded Analytics 관점에서 C_ServiceOrderQuery 분석 뷰를 사용한 KPI 대시보드도 흥미로운 후속 주제입니다.

관련 자료 및 추천 링크

댓글 0

아직 댓글이 없습니다.