ABAP

RESB vs CDS 뷰 — 생산 오더 컴포넌트 어떻게 다른가 #shorts #SAP #ABAP

개요와 이 글에서 얻어갈 것

S/4HANA 환경에서 생산 오더의 BOM 컴포넌트를 조회할 때, 여전히 많은 코드베이스가 RESB 테이블을 직접 SELECT하고 있습니다. RESB는 예약(Reservation)과 종속소요량을 함께 담는 레거시 테이블이라 필드 의미가 복잡하고, 생산 오더 컨텍스트에서만 필요한 필드를 뽑아 쓰기에 부적합한 경우가 많습니다. 이 글에서는 I_ProductionOrderComponent CDS 뷰를 사용해 같은 데이터를 더 안전하고 의미론적으로 명확하게 조회하는 방법을 다룹니다.

  • RESB 직접 조회의 구조적 한계 이해
  • I_ProductionOrderComponent 뷰 필드 매핑 파악
  • 기본 SELECT부터 Association 조인, AMDP 성능 최적화까지 3단계 실습
  • 기존 RESB 쿼리를 CDS로 전환하는 체크리스트 확보

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

ABAP CDS 뷰의 기본 문법(@AbapCatalog, define view, Association _Assoc)과 SELECT 문 사용법, 그리고 PP(Production Planning) 모듈의 생산 오더(AUFK/AFKO/AFPO) 구조에 대한 기초 지식이 있으면 좋습니다. RESB 테이블을 한 번이라도 SELECT해본 경험이 있다면 필드 매핑을 훨씬 빠르게 이해할 수 있습니다.

버전 및 준비 환경

이 글의 예제는 SAP S/4HANA 2022 On-Premise / SAP S/4HANA Cloud Private Edition 기준으로 작성했습니다. I_ProductionOrderComponent 뷰는 S/4HANA 1809 이상에서 제공되며, 릴리스에 따라 필드 세트가 조금씩 확장되어 왔습니다. 실습에는 다음을 준비합니다.

  • ADT(ABAP Development Tools, Eclipse 기반) 최신 버전
  • PP 모듈 마스터 데이터(자재, 작업장, BOM) 및 오픈 상태의 생산 오더 몇 건
  • 테스트용 개발 패키지와 트랜스포트 요청
  • SE16H 또는 DBACOCKPIT 접근 권한(성능 비교 확인용)

Cloud Public Edition의 경우 릴리스드 상태(C1)로 노출된 필드만 사용 가능하므로, ADT의 Repository Tree에서 릴리스 스코프를 먼저 확인하는 것을 권장합니다.

왜 RESB 직접 조회를 벗어나야 하는가

RESB는 이름 그대로 "Reservation / Dependent Requirements"입니다. 즉, 생산 오더 컴포넌트뿐 아니라 수동 예약, 네트워크 활동 예약, 계획 오더의 종속소요량이 한 테이블에 뒤섞여 저장됩니다. 이 때문에 실무에서 자주 발생하는 문제는 다음과 같습니다.

  1. 의미 필터의 부재: RSART(예약 유형), AUFNR(오더 번호), BDART(소요량 유형)을 조합해야만 "생산 오더 컴포넌트"라는 개념을 분리할 수 있습니다.
  2. 필드 이름의 모호성: BDMNG(소요량), ENMNG(출고량) 등이 UoM 없이 raw 필드로 노출되어 있어 실수하기 쉽습니다.
  3. 단위/통화 변환의 수작업: 컴포넌트 소요량을 오더 단위나 자재 기본 단위로 변환하려면 별도 로직이 필요합니다.
  4. 미래 호환성: SAP는 S/4HANA에서 재고/소요량 데이터 액세스를 CDS Virtual Data Model(VDM) 중심으로 재편했습니다.

I_ProductionOrderComponent 뷰의 핵심 구조를 도식으로 표현하면 다음과 같습니다.

// 논리 구조 (개념 도식)
I_ProductionOrderComponent
  ├─ Header Keys: ManufacturingOrder, ManufacturingOrderItem, Reservation, ReservationItem
  ├─ Material Info: Material, Plant, StorageLocation, Batch
  ├─ Quantity Info: RequiredQuantity, WithdrawnQuantity, BaseUnit
  ├─ Status Info: IsFinallyIssued, DebitCreditCode, GoodsMovementType
  └─ Associations
       ├─ _ProductionOrder     → I_ProductionOrder
       ├─ _Material            → I_Product
       ├─ _Plant               → I_Plant
       └─ _StorageLocation     → I_StorageLocation

1단계 — 기본 SELECT로 감 잡기

" [Before] RESB 직접 조회
SELECT rsnum, rspos, aufnr, matnr, werks, lgort,
       bdmng, enmng, meins, xloek, kzear
  FROM resb
  INTO TABLE @DATA(lt_resb_legacy)
  WHERE werks = '1710'
    AND aufnr IN @rt_order
    AND bdart = 'SB'
    AND rsart = ''
    AND xloek = ''.
" [After] I_ProductionOrderComponent CDS 뷰 사용
SELECT ManufacturingOrder, ManufacturingOrderItem,
       Reservation, ReservationItem,
       Material, Plant, StorageLocation,
       RequiredQuantity, WithdrawnQuantity,
       BaseUnit, IsFinallyIssued
  FROM I_ProductionOrderComponent
  INTO TABLE @DATA(lt_components)
  WHERE Plant           = '1710'
    AND ManufacturingOrder IN @rt_order
    AND IsFinallyIssued  = ''.

2단계 — 실무 시나리오: 오더 헤더와 자재 마스터 조인

CLASS zcl_prod_comp_reader DEFINITION PUBLIC FINAL CREATE PUBLIC.
  PUBLIC SECTION.
    TYPES: BEGIN OF ty_open_component,
             manufacturing_order  TYPE aufnr,
             order_type           TYPE aufart,
             material             TYPE matnr,
             plant                TYPE werks_d,
             storage_location     TYPE lgort_d,
             required_qty         TYPE resb-bdmng,
             withdrawn_qty        TYPE resb-enmng,
             open_qty             TYPE resb-bdmng,
             base_unit            TYPE meins,
           END OF ty_open_component,
           tt_open_component TYPE STANDARD TABLE OF ty_open_component
                             WITH DEFAULT KEY.

    METHODS read_open_components
      IMPORTING iv_plant     TYPE werks_d
                iv_date_from TYPE dats
                iv_date_to   TYPE dats
      RETURNING VALUE(rt_result) TYPE tt_open_component.
ENDCLASS.

CLASS zcl_prod_comp_reader IMPLEMENTATION.
  METHOD read_open_components.
    SELECT comp~ManufacturingOrder      AS manufacturing_order,
           comp~_ProductionOrder~ProductionOrderType AS order_type,
           comp~Material                AS material,
           comp~Plant                   AS plant,
           comp~StorageLocation         AS storage_location,
           comp~RequiredQuantity        AS required_qty,
           comp~WithdrawnQuantity       AS withdrawn_qty,
           ( comp~RequiredQuantity - comp~WithdrawnQuantity ) AS open_qty,
           comp~BaseUnit                AS base_unit
      FROM I_ProductionOrderComponent AS comp
     WHERE comp~Plant = @iv_plant
       AND comp~_ProductionOrder~ProductionOrderActualReleaseDate
                                        BETWEEN @iv_date_from AND @iv_date_to
       AND comp~IsFinallyIssued         = ''
       AND comp~RequiredQuantity        > comp~WithdrawnQuantity
      INTO TABLE @rt_result.
  ENDMETHOD.
ENDCLASS.

3단계 — 프로덕션 급: AMDP + 파라미터 CDS 뷰로 성능 최적화

@AbapCatalog.sqlViewName: 'ZCV_PROD_OPEN'
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Open Production Order Components'
define view entity ZC_ProductionOpenComponent
  with parameters
    p_plant     : werks_d,
    p_date_from : dats,
    p_date_to   : dats
  as select from I_ProductionOrderComponent as comp
{
  key comp.ManufacturingOrder,
  key comp.ManufacturingOrderItem,
  key comp.Reservation,
  key comp.ReservationItem,
      comp.Material,
      comp.Plant,
      comp.StorageLocation,
      comp.RequiredQuantity,
      comp.WithdrawnQuantity,
      cast( comp.RequiredQuantity - comp.WithdrawnQuantity
            as abap.quan( 13, 3 ) ) as OpenQuantity,
      comp.BaseUnit
}
where comp.Plant = :p_plant
  and comp.IsFinallyIssued = ''
  and comp.RequiredQuantity > comp.WithdrawnQuantity;
CLASS zcl_prod_open_amdp DEFINITION PUBLIC FINAL CREATE PUBLIC.
  PUBLIC SECTION.
    INTERFACES if_amdp_marker_hdb.
    TYPES: BEGIN OF ty_row,
             manufacturing_order TYPE aufnr,
             material            TYPE matnr,
             open_quantity       TYPE p LENGTH 13 DECIMALS 3,
             base_unit           TYPE meins,
           END OF ty_row,
           tt_rows TYPE STANDARD TABLE OF ty_row WITH DEFAULT KEY.

    CLASS-METHODS get_open_components
      IMPORTING VALUE(iv_plant)     TYPE werks_d
                VALUE(iv_date_from) TYPE dats
                VALUE(iv_date_to)   TYPE dats
      EXPORTING VALUE(et_rows)      TYPE tt_rows.
ENDCLASS.

CLASS zcl_prod_open_amdp IMPLEMENTATION.
  METHOD get_open_components BY DATABASE PROCEDURE
                             FOR HDB LANGUAGE SQLSCRIPT
                             OPTIONS READ-ONLY
                             USING ZC_ProductionOpenComponent.
    et_rows =
      SELECT ManufacturingOrder AS manufacturing_order,
             Material           AS material,
             OpenQuantity       AS open_quantity,
             BaseUnit           AS base_unit
        FROM "ZC_ProductionOpenComponent"( P_PLANT     => :iv_plant,
                                           P_DATE_FROM => :iv_date_from,
                                           P_DATE_TO   => :iv_date_to )
       ORDER BY manufacturing_order, material;
  ENDMETHOD.
ENDCLASS.

자주 마주치는 함정과 해결책

Q1. I_ProductionOrderComponent에 필드가 안 보입니다. 릴리스 스코프 문제일 가능성이 높습니다. ADT의 뷰 헤더에서 @API.state#RELEASED인지 확인하세요.

Q2. 수동 예약과 계획 오더 종속소요량도 함께 조회하고 싶습니다. 계획 오더는 I_PlannedOrderComponent, 수동 예약은 I_Reservation/I_ReservationItem 계열 뷰를 사용하세요.

Q3. 소요량과 출고량 단위가 다르게 나옵니다. EntryUnit/BaseUnit을 함께 조회하고, 필요 시 UNIT_CONVERSION SQL 함수를 CDS 표현식으로 사용하세요.

Q4. RESB 대비 성능이 오히려 느립니다. Association을 WHERE에 남발하면 옵티마이저가 조인 순서를 잘못 잡을 수 있습니다. 파라미터 뷰의 WHERE로 필터를 끌어올려 push-down을 유도하세요.

I_ProductionOrderComponent 너머로 — 자연스러운 확장

I_ProductionOrderComponent를 익혔다면 자연스럽게 확장할 주제가 여러 갈래로 열립니다. I_ProductionOrder, I_ProductionOrderOperation 뷰와 연계해 오더-오퍼레이션-컴포넌트 3단 리포트를 만들어 보세요. RAP 기반의 Custom Business Object에서 이 뷰를 Value Help로 활용하면 Fiori 앱에서 컴포넌트 선택 UX를 손쉽게 구현할 수 있습니다. Embedded Analytics의 Cube/Query 뷰로 승격시켜 소요량 KPI 대시보드로 확장하는 것도 현실적인 다음 단계입니다.

댓글 0

아직 댓글이 없습니다.