개요 및 이 글에서 다루는 범위
S/4HANA의 품질 관리(QM) 모듈에서 검사 로트(Inspection Lot)에 연결된 특성(Characteristic) 단위의 결과 데이터를 표준화된 방식으로 노출하는 CDS View가 I_InspectionResult입니다. 이 글은 해당 뷰의 구조와 키 필드, 그리고 실무에서 검사 특성별 합격/불합격 판정, 측정값(Measured Value), 샘플 수량을 어떻게 조회·집계·확장하는지 단계적으로 다룹니다.
- I_InspectionResult의 데이터 모델과 주요 필드 의미 파악
- ABAP CDS 및 ABAP SQL을 활용한 결과 데이터 조회 패턴
- 합격률(Acceptance Rate), 평균 측정값 등 KPI 산출 방법
- OData 노출 및 권한·성능 측면 주의점
읽기 전에 필요한 배경
이 글은 ABAP CDS View(DEFINE VIEW ENTITY) 정의 문법, ABAP SQL의 SELECT ... FROM 기본 구문, 그리고 QM의 기본 용어(Inspection Lot, Operation, Characteristic, Sample)를 어느 정도 알고 있다는 전제 하에 작성되었습니다. SADL(Service Adaptation Description Language) 또는 RAP(Restful ABAP Programming Model) 경험이 있다면 OData 노출 부분이 더 수월합니다.
환경 및 준비물
- SAP S/4HANA 2021 FPS02 이상 또는 S/4HANA Cloud —
I_InspectionResult는 S/4HANA 1909 이상에서 제공되며 릴리스별로 필드 구성이 일부 다를 수 있습니다. - ABAP Development Tools(ADT) for Eclipse 3.30+
- QM 모듈이 활성화되어 있고, 검사 로트 테이블(QALS), 결과 테이블(QAMR/QASR) 등에 트랜잭션 데이터가 존재할 것
- 권한 객체
Q_TCODE,Q_ROUT_GRP, 그리고 PFCG에서 검사 로트 표시 권한(예: QA32, QE51N 관련) - OData 테스트용 Postman 또는 Fiori UI5 환경
I_InspectionResult의 동작 원리와 데이터 모델
QM에서 검사 로트가 생성되면 작업(Operation)별로 검사 특성이 배정되고, 검사원이 측정/판정 결과를 입력하면 결과 레코드가 누적됩니다. 전통적으로는 QALS(검사 로트 헤더), QAMV(특성 마스터), QAMR(특성 결과), QASR(샘플 결과) 테이블을 직접 조인해야 했습니다. I_InspectionResult는 이 복잡한 조인을 캡슐화하여 "한 줄 = 하나의 검사 특성 결과"라는 일관된 형태로 제공합니다.
쉽게 비유하자면, QALS/QAMR/QASR이 원재료 창고라면,
I_InspectionResult는 사용 가능한 형태로 가공된 반제품입니다. 개발자는 SELECT 한 번으로 "어느 로트의, 어느 작업의, 어느 특성이, 어떤 측정값으로, 어떤 판정을 받았는가"를 한 번에 얻습니다.
주요 키 및 일반 필드 구성은 다음과 같습니다.
InspectionLot— 검사 로트 번호(QALS-PRUEFLOS)InspPlanOperationInternalID— 검사 작업 내부 IDInspectionCharacteristic— 특성 번호InspectionCharacteristicAttribute— 정성/정량 구분 등 속성InspResultMeasuredValue— 측정값(수치형 결과)InspResultValuationResult— 합격(A) / 불합격(R) 판정 코드NmbrOfInspectionSamples,InspResultNmbrOfDefects— 샘플/불량 수InspectionUnit— 측정 단위
이 뷰는 일반적으로 @AccessControl.authorizationCheck: #CHECK가 적용되어, DCL(Data Control Language)을 통한 권한 분리가 자동으로 작동합니다. 즉 쿼리 결과는 호출한 사용자의 검사 로트 권한 범위로 자동 필터링됩니다.
실전 예제 1단계 — 검사 결과 기본 조회
특정 검사 로트의 모든 특성 결과를 가져오는 가장 단순한 패턴입니다. 시나리오: "방금 완료된 화학원료 입고 로트 4500001234의 검사 결과를 한눈에 보고 싶다".
REPORT zqm_inspection_result_basic.
DATA: lt_results TYPE TABLE OF i_inspectionresult,
ls_result TYPE i_inspectionresult.
PARAMETERS: p_lot TYPE qals-prueflos DEFAULT '040000001234'.
SELECT InspectionLot,
InspPlanOperationInternalID,
InspectionCharacteristic,
InspResultMeasuredValue,
InspectionUnit,
InspResultValuationResult,
NmbrOfInspectionSamples
FROM I_InspectionResult
WHERE InspectionLot = @p_lot
ORDER BY InspPlanOperationInternalID,
InspectionCharacteristic
INTO TABLE @lt_results.
LOOP AT lt_results INTO ls_result.
WRITE: / ls_result-InspectionLot,
ls_result-InspectionCharacteristic,
ls_result-InspResultMeasuredValue,
ls_result-InspectionUnit,
ls_result-InspResultValuationResult.
ENDLOOP.
p_lot에 12자리 검사 로트 번호를 채워야 합니다. QALS의 PRUEFLOS는 NUMC(12)이므로 앞자리 0을 포함해야 검색이 됩니다.
실전 예제 2단계 — KPI 집계, 에러 처리, 로깅
실무 요구: "특정 자재의 최근 30일간 합격률을 작업별로 보고 싶다". I_InspectionResult를 헤더 뷰(I_InspectionLot)와 조인하고, 합격/전체를 비율로 계산합니다.
CLASS zcl_qm_acceptance_kpi DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
TYPES: BEGIN OF ty_kpi,
material TYPE matnr,
operation_id TYPE i_inspectionresult-InspPlanOperationInternalID,
total_chars TYPE i,
accepted_chars TYPE i,
acceptance_rate TYPE p LENGTH 5 DECIMALS 2,
END OF ty_kpi.
METHODS calculate
IMPORTING iv_material TYPE matnr
iv_days TYPE i DEFAULT 30
RETURNING VALUE(rt_kpi) TYPE STANDARD TABLE OF ty_kpi WITH EMPTY KEY
RAISING cx_sy_open_sql_db.
ENDCLASS.
CLASS zcl_qm_acceptance_kpi IMPLEMENTATION.
METHOD calculate.
DATA(lv_from_date) = cl_abap_context_info=>get_system_date( ) - iv_days.
TRY.
SELECT lot~Material AS material,
res~InspPlanOperationInternalID AS operation_id,
COUNT(*) AS total_chars,
SUM( CASE res~InspResultValuationResult
WHEN 'A' THEN 1
ELSE 0
END ) AS accepted_chars
FROM I_InspectionResult AS res
INNER JOIN I_InspectionLot AS lot
ON res~InspectionLot = lot~InspectionLot
WHERE lot~Material = @iv_material
AND lot~InspectionLotCreationDate >= @lv_from_date
GROUP BY lot~Material, res~InspPlanOperationInternalID
INTO TABLE @DATA(lt_raw).
LOOP AT lt_raw INTO DATA(ls_raw).
APPEND VALUE #(
material = ls_raw-material
operation_id = ls_raw-operation_id
total_chars = ls_raw-total_chars
accepted_chars = ls_raw-accepted_chars
acceptance_rate = COND #(
WHEN ls_raw-total_chars = 0 THEN 0
ELSE ls_raw-accepted_chars * 100 / ls_raw-total_chars )
) TO rt_kpi.
ENDLOOP.
CATCH cx_sy_open_sql_db INTO DATA(lx_db).
cl_application_log_writer=>add_message(
iv_object = 'ZQM'
iv_subobject = 'KPI'
iv_text = lx_db->get_text( ) ).
RAISE EXCEPTION lx_db.
ENDTRY.
ENDMETHOD.
ENDCLASS.
세 가지 핵심: 첫째, COUNT와 SUM(CASE ...)으로 합격률 분자/분모를 한 번의 쿼리로 산출합니다. 둘째, COND 식으로 분모 0(ZERODIVIDE)을 회피합니다. 셋째, DB 예외는 Application Log에 기록 후 재던집니다(re-raise).
실전 예제 3단계 — RAP 서비스 노출과 성능·보안 고려
KPI를 Fiori Elements 분석 카드나 외부 BI에서 소비할 수 있도록 OData V4로 노출합니다. 집계용 CDS View Entity를 별도로 만들고 Service Definition에 노출하는 것이 핵심입니다.
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Inspection Acceptance KPI by Material'
@Analytics.dataCategory: #CUBE
define view entity ZC_InspAcceptanceKPI
as select from I_InspectionResult as res
association [1..1] to I_InspectionLot as _Lot
on $projection.InspectionLot = _Lot.InspectionLot
{
key res.InspectionLot,
key res.InspPlanOperationInternalID,
key res.InspectionCharacteristic,
_Lot.Material,
_Lot.Plant,
res.InspResultValuationResult,
res.InspResultMeasuredValue,
res.InspectionUnit,
@DefaultAggregation: #SUM
cast( 1 as abap.int4 ) as CharacteristicCount,
@DefaultAggregation: #SUM
case res.InspResultValuationResult
when 'A' then 1 else 0
end as AcceptedCount,
_Lot
}
@EndUserText.label: 'Inspection Acceptance Service'
define service ZUI_INSP_ACCEPTANCE {
expose ZC_InspAcceptanceKPI as InspAcceptanceKPI;
}
Service Binding에서 OData V4 — UI 시나리오로 등록하면 Fiori Analytical List Page에서 바로 차트화할 수 있습니다. 운영 단계 점검 항목은 다음과 같습니다.
- 필터 강제: 결과 테이블은 수백만 건이 흔합니다.
@UI.selectionField로 Material, Plant, 날짜 범위를 필수 필터로 지정해 풀스캔을 막습니다. - HANA 푸시다운: CASE/SUM을 CDS 레벨에 두면 HANA에서 집계 후 작은 행 수만 ABAP으로 전달됩니다.
- 권한: 베이스 뷰의
#CHECK설정이 상속되는지 확인해야 합니다. - 릴리스 컨트랙트: 버전 업그레이드 시 필드 변경 가능성을 모니터링하는 것을 권장합니다.
자주 발생하는 실수와 해결법
Q1. 결과가 0건으로 나옵니다.
검사 로트 번호를 12자리 NUMC로 보내지 않으면 매칭이 실패합니다. CONVERSION_EXIT_ALPHA_INPUT으로 변환하거나 도메인 변환 출력 형식을 그대로 사용하세요. 또한 사용 결정(Usage Decision)이 내려지지 않은 경우 결과가 임시 영역에 머무를 수 있습니다.
Q2. InspResultMeasuredValue가 항상 0으로 보입니다.
정성 특성(합/부 판정만 있는 특성)은 측정값이 없습니다. InspectionCharacteristicCategory로 정량/정성 여부를 먼저 필터링해야 의미 있는 통계가 나옵니다.
Q3. OData 호출 시 타임아웃이 발생합니다.
날짜 범위를 반드시 첫 번째 필터로 사용하고, $top/$skip 페이징을 적용하세요. 분석용이라면 집계 CDS를 별도로 만들어 원본 라인을 직접 노출하지 않는 편이 안전합니다.
Q4. 합격률 계산에서 분모가 0이 나옵니다.
특정 작업이 스킵되었거나 모든 특성이 정성-건너뜀 상태일 때 발생합니다. SQL CASE 분기와 ABAP COND로 분모 0을 명시적으로 처리해야 ZERODIVIDE 덤프를 막을 수 있습니다.
연관 주제와 이후 확장 방향
한 단계 더 나아가려면 다음을 권장합니다. 첫째, I_InspectionLot, I_InspectionOperation, I_InspectionCharacteristic와의 조인 패턴을 정리해 "검사 360 뷰"를 만들어 보세요. 둘째, RAP의 Managed/Unmanaged 시나리오로 검사 결과 입력 화면을 커스터마이즈할 때 분석 CDS와 트랜잭션 CDS를 어떻게 분리할지 설계해 보세요. 셋째, SAP Analytics Cloud 또는 Datasphere에서 라이브 커넥션으로 KPI를 시각화하면 품질 대시보드를 비교적 빠르게 완성할 수 있습니다.
댓글 0
아직 댓글이 없습니다.