ABAP

EWM 태스크 30초 완성 #shorts #SAP #ABAP

▶ YouTube에서 보기

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

SAP Extended Warehouse Management(EWM)에서 창고 태스크(Warehouse Task)는 작업자의 실제 물리적 이동·작업을 지시하는 최소 실행 단위입니다. I_WarehouseTask는 이 태스크 데이터를 ABAP CDS 뷰 계층에서 표준화된 형태로 노출하는 인터페이스 뷰이며, RAP/Fiori/Analytics에서 EWM 태스크 정보를 조회할 때 가장 먼저 접근하게 되는 진입점입니다. 이 글에서는 다음을 단계별로 살펴봅니다.

  • I_WarehouseTask의 위치(VDM 계층)와 키 필드 구조 이해
  • 창고 번호·태스크 유형·이동 데이터 필드의 의미와 조인 전략
  • 기본 조회 → 실무 시나리오(피킹/입고) → 프로덕션 패턴까지 3단계 코드 작성
  • EWM 권한 객체와 클라이언트/시간 의존성에서의 흔한 함정 회피
이 글은 S/4HANA 2022 이상의 Embedded EWM 환경을 기준으로 작성되었으며, Decentralized EWM 1909 이상에서도 일부 필드를 제외하면 동일하게 적용됩니다.

먼저 알고 있어야 할 것들

이 글은 ABAP CDS의 기본 문법(define view, association, annotation)과 SELECT/OpenSQL 사용 경험을 가정합니다. 또한 EWM의 핵심 마스터/트랜잭션 객체(창고 번호, Storage Bin, Handling Unit, Warehouse Order, Warehouse Task)의 개념을 알고 있으면 필드 매핑이 훨씬 쉽습니다. RAP 또는 Service Binding 작성 경험이 있다면 3단계 프로덕션 예제 이해가 빨라집니다.

환경 및 준비 사항

본 예제는 다음 환경에서 검증을 권장합니다.

  • SAP S/4HANA 2022 FPS02 또는 2023(Embedded EWM 활성화)
  • ABAP Development Tools(ADT) for Eclipse 2024-03 이상
  • 권한 객체: /SCWM/MON, /SCWM/WHN(창고 번호 권한), S_RFC
  • 최소 1건 이상의 Warehouse Order/Task 트랜잭션 데이터(테스트 창고 번호 권장)
  • 분석 시나리오라면 SAP Analytics Cloud 또는 Fiori Elements List Report 프리뷰 사용

ADT에서 I_WarehouseTask를 Open Repository Object로 열어 Source/Annotation을 먼저 살펴본 뒤 실습을 진행하면 필드 의미를 빠르게 파악할 수 있습니다.

핵심 개념: 창고 태스크와 I_WarehouseTask의 위치

EWM의 실행 모델은 "Document → Order → Task" 3계층으로 단순화할 수 있습니다. Outbound Delivery Order가 들어오면 시스템은 Warehouse Request를 만들고, 이를 기반으로 Warehouse Order(작업 단위 묶음)와 Warehouse Task(실제 이동/적치/피킹 1건)를 생성합니다. 태스크 1건은 본질적으로 "어떤 자재(Product)를, 얼마나(Quantity), 어디서(Source Bin/HU) 어디로(Destination Bin/HU) 옮긴다"는 명령서이며, 데이터베이스 레벨에서는 /SCWM/ORDIM_C(확정), /SCWM/ORDIM_O(미확정), /SCWM/ORDIM_H(이력) 테이블에 분산 저장됩니다.

I_WarehouseTask는 이 분산된 테이블을 비즈니스적으로 의미 있는 단일 뷰로 묶어주는 Virtual Data Model(VDM)의 Interface View(I_ 접두어)입니다. 비유하자면 물류 센터의 "작업 지시서 캐비넷"을 표준 양식으로 정리해 외부에 노출한 창구입니다. 내부 구조가 어떻게 바뀌더라도 I_ 뷰의 시그니처는 안정적으로 유지되므로, 컨슈머는 테이블을 직접 참조하지 않고 이 뷰만 바라보면 됩니다.

VDM 계층 구조에서 흐름은 다음과 같습니다.

  • Basic View: 원천 테이블 1:1 매핑(예: /SCWM/ORDIM_C 래퍼)
  • Composite/Interface View: I_WarehouseTask — 비즈니스 시맨틱 부여, association 정의
  • Consumption View: C_WarehouseTaskTP 류 — Fiori 앱·OData 노출용

주요 키·식별 필드는 다음과 같이 이해하면 됩니다.

  • EWMWarehouse: 창고 번호(이전 LGNUM)
  • WarehouseTask: 태스크 번호(10자리, TANUM에 해당)
  • WarehouseOrder: 상위 묶음 단위
  • WarehouseProcessType, ActivityArea, StorageProcess: 프로세스 분류
  • SourceStorageBin/DestinationStorageBin, SourceHandlingUnit/DestinationHandlingUnit: 이동 출발/도착
  • Product, ProductQuantity, ProductQuantityUnit: 자재와 수량
  • WarehouseTaskStatus(미확정/확정/취소), ConfirmationDateTime

실전 코드 1단계: 기본 조회 뷰 작성

가장 먼저 I_WarehouseTask를 단순 래핑하여 특정 창고의 오늘 생성된 피킹 태스크를 조회하는 커스텀 뷰를 만들어 봅니다. Z 네임스페이스로 인터페이스 뷰를 작성합니다.

@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Picking Task Overview'
@Metadata.ignorePropagatedAnnotations: true
define view entity ZI_PickingTaskOverview
  as select from I_WarehouseTask as Task
{
  key Task.EWMWarehouse                 as Warehouse,
  key Task.WarehouseTask                as TaskNumber,
      Task.WarehouseOrder               as OrderNumber,
      Task.WarehouseProcessType         as ProcessType,
      Task.Product                      as Material,
      Task.ProductQuantity              as PickQuantity,
      Task.ProductQuantityUnit          as PickUnit,
      Task.SourceStorageBin             as FromBin,
      Task.DestinationStorageBin        as ToBin,
      Task.WarehouseTaskStatus          as TaskStatus,
      Task.CreationDateTime             as CreatedAt,
      Task.ConfirmationDateTime         as ConfirmedAt
}
where
      Task.WarehouseProcessType like '20%'   -- Picking 프로세스 그룹
  and Task.CreationDateTime    >= $session.system_date

위 뷰는 I_WarehouseTask의 raw field를 비즈니스에 친숙한 alias로 노출합니다. $session.system_date 활용으로 하드코딩된 날짜를 피했고, like '20%' 패턴으로 피킹 프로세스 유형 그룹을 필터링했습니다. ADT의 Data Preview로 즉시 결과를 확인할 수 있습니다.

실전 코드 2단계: 입고-피킹 통합 시나리오와 에러 처리

실무에서는 단순 조회를 넘어 Inbound(GR) 태스크와 Outbound(Picking) 태스크를 함께 모니터링하면서, 미확정 태스크의 지연 시간을 계산해야 합니다. Association을 활용해 자재 마스터·창고 조직 정보를 결합하고, ABAP 클래스에서 호출 시 예외 처리를 포함시킵니다.

@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'WHT Monitor with Delay'
define view entity ZI_WhseTaskMonitor
  as select from I_WarehouseTask as Task
  association [0..1] to I_Product   as _Product   on  _Product.Product   = Task.Product
  association [0..1] to I_EWMWarehouse as _Wh    on  _Wh.EWMWarehouse  = Task.EWMWarehouse
{
  key Task.EWMWarehouse                                    as Warehouse,
  key Task.WarehouseTask                                   as TaskNumber,
      Task.WarehouseProcessType                            as ProcessType,
      case
        when Task.WarehouseProcessType like '10%' then 'GR'
        when Task.WarehouseProcessType like '20%' then 'PICK'
        when Task.WarehouseProcessType like '40%' then 'PUTAWAY'
        else 'OTHER'
      end                                                  as TaskCategory,
      Task.Product                                         as Material,
      Task.ProductQuantity                                 as Quantity,
      Task.ProductQuantityUnit                             as Unit,
      Task.WarehouseTaskStatus                             as Status,
      Task.CreationDateTime                                as CreatedAt,
      Task.ConfirmationDateTime                            as ConfirmedAt,
      cast( tstmp_seconds_between(
              Task.CreationDateTime,
              tstmp_current_utctimestamp(),
              'FAIL' ) as abap.dec(15,0) )                 as OpenSeconds,
      _Product.ProductType                                 as MaterialType,
      _Wh.WarehouseDescription                             as WarehouseName,
      _Product,
      _Wh
}
where Task.WarehouseTaskStatus = ' '   -- 미확정만

ABAP 컨슈머 측에서는 try-catch와 로깅을 포함합니다.

METHOD get_open_tasks.
  DATA(lo_log) = cl_bali_log_factory=>create_log( ).

  TRY.
      SELECT FROM zi_whsetaskmonitor
        FIELDS warehouse, tasknumber, taskcategory, material,
               quantity, unit, openseconds, warehousename
        WHERE  warehouse    = @iv_warehouse
          AND  taskcategory IN ( 'PICK', 'PUTAWAY' )
          AND  openseconds  > @iv_threshold_sec
        ORDER BY openseconds DESCENDING
        INTO TABLE @rt_tasks.

      IF sy-subrc <> 0.
        lo_log->add_message(
          cl_bali_message_helper=>create_message(
            severity = if_bali_constants=>c_severity_info
            text     = |No open tasks for { iv_warehouse }| ) ).
      ENDIF.

    CATCH cx_sy_open_sql_db INTO DATA(lx_sql).
      lo_log->add_exception( lx_sql ).
      RAISE EXCEPTION TYPE zcx_wht_read
        EXPORTING previous = lx_sql.
  ENDTRY.

  cl_bali_log_db=>get_instance( )->save_log( log = lo_log ).
ENDMETHOD.

핵심은 ① CDS에서 비즈니스 카테고리화(case)와 시간 계산(tstmp_seconds_between)을 미리 수행한다는 점, ② 컨슈머는 단순 필터만 담당해 가독성과 재사용성을 높였다는 점입니다.

실전 코드 3단계: RAP Projection과 권한·성능 고려

프로덕션에서는 Fiori Elements 또는 SAC에서 곧바로 소비할 수 있도록 Projection View와 Service Binding을 구성합니다. 동시에 EWM 권한 객체 /SCWM/LGN 기반 DCL(Data Control Language)을 적용해 창고별 데이터 격리를 보장합니다.

@AccessControl.authorizationCheck: #CHECK
@Search.searchable: true
@OData.publish: false
@UI.headerInfo: { typeName: 'Warehouse Task',
                  typeNamePlural: 'Warehouse Tasks' }
define root view entity ZC_WarehouseTaskTP
  provider contract transactional_query
  as projection on ZI_WhseTaskMonitor
{
  @UI.lineItem:    [{ position: 10, label: 'Warehouse' }]
  @UI.selectionField: [{ position: 10 }]
  key Warehouse,

  @UI.lineItem:    [{ position: 20 }]
  @UI.identification: [{ position: 20 }]
  key TaskNumber,

  @UI.lineItem: [{ position: 30 }]
  TaskCategory,

  @UI.lineItem: [{ position: 40 }]
  @Semantics.quantity.unitOfMeasure: 'Unit'
  Quantity,
  Unit,

  @UI.lineItem: [{ position: 50, criticality: 'Criticality' }]
  OpenSeconds,

  case
    when OpenSeconds > 3600 then 1
    when OpenSeconds > 1800 then 2
    else 3
  end                                          as Criticality,

  Material,
  MaterialType,
  WarehouseName
}
@EndUserText.label: 'DCL for Warehouse Task'
@MappingRole: true
define role ZC_WAREHOUSE_TASK_ROLE {
  grant select on ZC_WarehouseTaskTP
    where ( Warehouse ) = aspect pfcg_auth( /SCWM/LGN, LGNUM, ACTVT = '03' );
}

성능 측면 권장 사항은 다음과 같습니다.

  • 대량 태스크가 누적되는 창고는 CreationDateTime 기준 파티션 키 활용을 고려하고, 조회 시 항상 시간 범위 필터를 강제합니다.
  • Association은 필요한 시점에만 실제 select에 포함되도록 path 사용을 자제하고, 분석 뷰는 별도 Aggregation View(@Analytics.dataCategory: #CUBE)로 분리합니다.
  • 오래된 확정 태스크는 I_WarehouseTaskHistory 류의 이력 뷰로 분기 조회해 hot 영역의 부하를 줄입니다.

자주 마주치는 실수와 해결법

Q1. I_WarehouseTask에서 데이터가 한 건도 안 보입니다. 가장 흔한 원인은 권한 객체 /SCWM/LGN 미부여입니다. EWM 데이터는 단순 클라이언트 격리뿐 아니라 창고 번호별 권한이 강제되므로 SU53으로 권한 트레이스를 먼저 확인하세요. 또한 Embedded EWM의 경우 비즈니스 시스템 그룹(BSG) 설정이 잘못되면 같은 시스템에서도 뷰가 비어 보일 수 있습니다.

Q2. 확정 후 사라진 태스크는 어디서 보나요? 미확정/확정/취소는 각각 다른 물리 테이블에 저장됩니다. I_WarehouseTask는 주로 활성(미확정+확정 일부) 데이터를 노출하며, 이력은 별도의 Historical CDS 뷰 또는 /SCWM/ORDIM_H 기반 커스텀 뷰로 접근해야 합니다. 컨슈머가 "방금 확정한 태스크가 사라졌다"고 보고하면 status 필터를 점검하세요.

Q3. 시간 필드가 UTC라서 한국 시간과 다르게 보입니다. CreationDateTime·ConfirmationDateTime은 일반적으로 UTC TimeStamp입니다. UI 표시용으로는 @Semantics.systemDateTime: true를 활용하거나, 컨슈머 측에서 CONVERT TIME STAMP로 사용자 타임존에 맞춰 변환하는 것을 권장합니다. 보고서에서 "하루치 데이터"를 다룰 때 UTC 경계와 로컬 경계 차이로 누락이 생기지 않도록 주의하세요.

그 외 자주 발생하는 함정으로는, WarehouseProcessType 코드 체계가 고객별 커스터마이징되어 있다는 점(표준값 가정 금지), HU 관리 창고와 비-HU 창고에서 SourceHandlingUnit이 공백일 수 있다는 점, association을 무분별하게 select에 노출하면 HANA 옵티마이저가 불필요한 조인을 평탄화하지 못해 성능이 저하될 수 있다는 점을 들 수 있습니다.

이후에 학습하면 좋은 주제

I_WarehouseTask를 이해했다면 이어서 다음 주제로 확장하기를 권장합니다. 첫째, I_WarehouseOrder·I_WarehouseRequest·I_InboundDelivery와 association을 연결해 End-to-End 물류 흐름을 단일 뷰로 묶는 분석 모델을 설계해 보세요. 둘째, RAP의 Behavior Definition을 활용해 태스크 확정·취소 액션을 OData 서비스로 노출하는 Managed/Unmanaged 시나리오를 실습할 수 있습니다. 셋째, SAP Analytics Cloud Live Connection으로 OpenSeconds 기반 KPI 대시보드를 구성하면 운영 모니터링에 즉시 적용할 수 있습니다. 마지막으로, Event-Driven 아키텍처가 필요하다면 ABAP Channels나 Application Log + Event Mesh 연계를 검토하세요.

관련 문서 및 추가 학습 링크

댓글 0

아직 댓글이 없습니다.