매입채무 송장 처리 데이터 템플릿
매입채무 송장 처리 데이터 템플릿
- 상세 분석에 권장되는 속성
- 효과적인 추적을 위한 주요 프로세스 활동
- 단계별 데이터 추출 안내
매입채무 송장 처리 속성
| 이름 | 설명 | ||
|---|---|---|---|
| 송장 Invoice | 송장 문서의 고유 식별자로, 매입채무 프로세스의 기본 케이스 ID로 사용됩니다. | ||
| 설명 송장은 접수부터 지급까지 관련 활동을 연결하는 중심 객체입니다. SAP S/4HANA에서는 일반적으로 회사 코드(BUKRS), 고유 문서 번호(BELNR), 회계연도(GJAHR)를 조합해 식별합니다. 송장별로 분석하면 송장 처리 전 과정을 확인할 수 있습니다. 전체 주기 시간 계산, 개별 송장의 병목 파악, 송장이 프로세스에서 거치는 다양한 경로 분석에 필요한 기준입니다. 중요한 이유 송장별 처리 과정을 고유하게 식별하므로 전체 수명 주기를 추적하고 케이스별로 프로세스 성과를 분석할 수 있습니다. 확보 방법 BKPF(회계 문서 헤더) 또는 RBKP(송장 접수 문서 헤더) 테이블의 BUKRS, BELNR, GJAHR 필드로 구성된 복합 키입니다. 예시 1000-1900000001-20231710-1900000002-20232000-5100000003-2024 | |||
| 이벤트 시간 EventTime | 활동이 발생한 정확한 날짜와 시간입니다. | ||
| 설명 이벤트 시간은 각 활동에 연결된 타임스탬프로, 송장과 관련된 이벤트의 시간 순서를 보여줍니다. 프로세스 흐름을 파악하고 시간 기반 분석을 수행하는 데 필요한 데이터입니다. 분석에서는 이벤트 시간을 기준으로 활동 순서를 정하고, 단계 간 주기 시간과 대기 시간을 계산하며, 기간별 프로세스 성과를 분석합니다. 기간별 비교에는 월별 분석 등이 포함됩니다. 모든 소요 시간 기반 KPI의 기준이 됩니다. 중요한 이유 이 타임스탬프는 이벤트를 시간순으로 정렬하고 주기 시간과 소요 시간 등 시간 기반 지표를 계산하는 데 필요하며, process mining의 기본 데이터입니다. 확보 방법 SAP 테이블의 여러 날짜 및 시간 필드에서 가져옵니다. 예를 들어 생성일(BKPF-CPUDT), 전기일(BKPF-BUDAT), 반제일(BSAK-AUGDT), 변경 로그 타임스탬프(CDHDR-UDATE/UTIME)가 있습니다. 예시 2023-10-01T09:00:00Z2023-10-05T14:30:15Z2023-10-15T11:21:05Z | |||
| 활동 ActivityName | 송장 처리 과정에서 발생한 특정 업무 이벤트 또는 단계의 이름입니다. | ||
| 설명 활동은 매입채무 프로세스의 개별 단계나 작업을 나타냅니다. 예를 들어 '송장 접수', '송장 전기', '지급 실행' 등이 있습니다. 이러한 활동이 프로세스 맵을 구성합니다. 활동 분석은 process mining의 기본입니다. 프로세스 흐름을 시각화하고, 일반적인 경로를 파악하며, 표준 프로세스에서 벗어난 사례를 찾고, 각 단계의 빈도와 소요 시간을 측정할 수 있습니다. 송장별 활동 순서가 해당 송장의 처리 경로를 이룹니다. 중요한 이유 프로세스 단계를 정의해 흐름을 시각화하고 분석하며, 병목과 재작업 과정을 파악할 수 있습니다. 확보 방법 문서 상태 변경(예: BKPF-BSTAT), 변경 문서(CDHDR/CDPOS 테이블), 워크플로 로그 등 여러 출처에서 가져옵니다. 일반적으로 별도의 추출 로직이 필요합니다. 예시 송장 접수송장 승인지급 실행송장 지급 차단 | |||
| 원천 시스템 SourceSystem | 데이터를 추출한 시스템입니다. | ||
| 설명 이 속성은 프로세스 데이터의 출처를 나타냅니다. 이 화면에서는 일반적으로 'SAP S/4HANA'가 값으로 사용됩니다. ERP나 통합 시스템을 여러 개 사용하는 환경에서는 데이터 계보를 추적하고 데이터를 구분하는 데 중요한 필드입니다. 올바른 데이터셋을 분석하도록 하며, 데이터 품질 문제를 원천 시스템까지 추적하는 데 도움이 됩니다. 중요한 이유 데이터 출처를 식별합니다. 데이터 거버넌스, 문제 해결, 여러 시스템을 사용하는 환경에서 중요합니다. 확보 방법 일반적으로 데이터 추출 과정에서 데이터셋의 출처를 표시하기 위해 고정값으로 추가합니다. 예시 SAP S/4HANASAP ECC 6.0S4H_PROD_100 | |||
| 최근 데이터 업데이트 LastDataUpdate | 이 레코드의 데이터를 원천 시스템에서 마지막으로 새로 고친 시점을 나타내는 타임스탬프입니다. | ||
| 설명 이 속성은 SAP S/4HANA에서 데이터를 가장 최근에 추출하거나 업데이트한 날짜와 시간을 제공합니다. 분석 대상 데이터의 최신성을 파악하는 데 필요한 메타데이터입니다. 사용자는 이 정보를 통해 프로세스 분석에 반영된 데이터가 얼마나 최신인지 확인할 수 있습니다. 데이터 지연에 대한 기대치를 관리하고, 데이터 새로 고침 일정을 수립하며, 데이터 무결성을 유지하는 데도 중요합니다. 중요한 이유 데이터의 최신성을 나타내므로 사용자가 프로세스 분석에 반영된 데이터의 업데이트 상태를 확인할 수 있습니다. 확보 방법 원천 시스템에서 데이터를 추출할 때 각 레코드에 생성해 기록합니다. 예시 2024-05-20T04:00:00Z2024-05-21T04:00:00Z | |||
| 공급업체명 VendorName | 송장을 제출한 공급업체의 이름입니다. | ||
| 설명 이 속성에는 공급업체의 공식 명칭이 들어 있습니다. 송장 문서에 저장된 공급업체 번호를 통해 연결됩니다. 공급업체 분석은 관계를 관리하고 특정 공급업체와 관련된 프로세스 문제를 찾는 데 중요합니다. '불일치가 있는 송장을 가장 많이 제출하는 공급업체는 어디인가?', '특정 전략 공급업체에 대금을 꾸준히 기한 내 지급하고 있는가?'와 같은 질문에 답할 수 있습니다. 송장 번호 및 금액과 함께 중복 지급 가능성을 찾는 데도 사용됩니다. 중요한 이유 공급업체별 프로세스 성과를 분석해 문제가 있는 공급업체를 찾고 전략적 공급업체와의 관계를 효과적으로 관리할 수 있습니다. 확보 방법 BKPF 또는 RBKP의 공급업체 번호(LIFNR)를 기준으로 공급업체 마스터 데이터 테이블 LFA1의 NAME1 필드에서 가져옵니다. 예시 Office Supplies Inc.Global Consulting GroupMachine Parts GmbH | |||
| 구매 주문서 번호 PurchaseOrderNumber | 해당하는 경우 송장과 연결된 구매 주문서의 고유 식별자입니다. | ||
| 설명 이 속성은 송장을 사전 승인된 구매 주문서(PO)와 연결합니다. 구매 주문서 번호가 있어야 구매 주문서, 송장, 입고 내역 간 3자 대사를 수행할 수 있습니다. 규정 준수와 효율성 분석에 중요한 속성입니다. 조달 정책 준수 수준을 측정하는 '구매 주문서 없는 송장 비율' KPI를 계산하는 데 사용됩니다. 또한 구매 주문서가 있는 송장의 대사 과정을 분석하는 '3자 대사 성과' 대시보드에도 필요합니다. 중요한 이유 3자 대사 효율을 분석하고 구매 주문서 없이 처리된 송장을 찾아 조달 정책 준수 여부를 측정하는 데 중요합니다. 확보 방법 송장 개별 항목 테이블에서 확인할 수 있습니다. 예를 들어 MM 송장은 RSEG-EBELN, FI 송장은 BSEG-EBELN에 저장됩니다. 예시 45000012344500005678 | |||
| 사용자 이름 UserName | 활동을 수행한 사람의 사용자 ID입니다. | ||
| 설명 이 속성은 송장 전기, 승인, 반제 등 특정 활동을 수행한 SAP 사용자 ID를 기록합니다. 프로세스 단계를 개별 사용자와 연결합니다. 사용자 이름별 분석은 업무량 분포와 우수 수행자를 파악하고 추가 교육이 필요한 사용자를 찾는 데 도움이 됩니다. 대시보드에서 승인 병목을 분석할 때도 중요합니다. 프로세스 지연을 일으키는 승인자를 구체적으로 확인할 수 있습니다. 중요한 이유 활동을 담당자별로 구분해 사용자 성과와 업무량을 분석하고 직무 분리 정책 준수 여부를 확인할 수 있습니다. 확보 방법 일반적으로 BKPF-USNAM(입력자)과 같은 헤더 테이블이나 CDHDR-USERNAME(변경자)과 같은 변경 문서 테이블에서 확인할 수 있습니다. 예시 ABROWNJSMITHAP_AUTOMATION | |||
| 송장 금액 InvoiceAmount | 원본 문서 통화로 표시한 송장 총액입니다. | ||
| 설명 공급업체가 제출한 송장 총액입니다. 공제나 할인을 적용하기 전의 상품 또는 서비스 비용, 세금 및 기타 비용이 포함됩니다. 송장 금액은 다양한 재무 분석에 사용되는 중요한 속성입니다. 고액 송장의 우선순위를 정하고, 프로세스 지연이 재무에 미치는 영향(예: 고액 송장의 연체료)을 파악하며, '고액 송장은 승인 경로가 다른가?'와 같이 프로세스를 구분해 분석할 수 있습니다. 중복 지급 가능성을 찾는 데도 필요합니다. 중요한 이유 프로세스의 재무적 맥락을 제공해 금액별 분석과 고액 송장의 우선순위 설정, 재무 영향의 정량화를 지원합니다. 확보 방법 MM 송장은 RBKP-RMWWR(송장 총액)과 같은 테이블에서 확인할 수 있습니다. FI 송장은 BSEG의 개별 항목(WRBTR 필드)을 합산해 계산할 수 있습니다. 예시 1500.00250.7512345.50 | |||
| 송장 만기일 InvoiceDueDate | 공급업체에 송장 대금을 지급해야 하는 날짜입니다. | ||
| 설명 송장 만기일은 연체료를 방지하고 공급업체와의 관계를 유지하기 위해 대금을 지급해야 하는 기한입니다. 송장의 기준일과 공급업체와 합의한 지급 조건을 바탕으로 계산합니다. '지급 규정 준수 및 경과 기간' 대시보드와 '기한 내 지급률' KPI에 필요한 날짜입니다. 만기일과 실제 지급일을 비교하면 대금이 기한 내, 조기 또는 지연 지급되었는지 확인할 수 있으며, 이는 재무와 공급업체 관계에 직접 영향을 줍니다. 중요한 이유 기한 내 지급 분석의 기준이 됩니다. 지급 성과와 공급업체 관계 및 연체료에 미치는 영향을 측정할 수 있습니다. 확보 방법 이 날짜는 계산되는 경우가 많습니다. 순 만기일은 BSEG-NETDT 필드에 저장됩니다. 지급 기준일(BSEG-ZFBDT)과 지급 조건(BSEG-ZTERM)으로도 계산할 수 있습니다. 예시 2023-10-312023-11-152024-01-10 | |||
| 회사 코드 CompanyCode | 송장을 처리하는 조직 단위입니다. | ||
| 설명 회사 코드는 외부 보고를 위해 완전하고 독립적인 회계 장부를 작성할 수 있는 최소 조직 단위입니다. 매입채무에서는 공급업체에 대금을 지급할 법인을 의미합니다. 회사 코드별로 분석하면 조직 내 여러 법인의 프로세스 성과를 비교할 수 있습니다. 이를 통해 표준 프로세스를 따르는 사업 부문과 효율이 낮거나 주기 시간이 길고 재작업률이 높은 부문을 파악할 수 있습니다. 중요한 이유 법인별 프로세스 성과를 비교해 지역이나 사업 부문별 문제와 모범 사례를 찾을 수 있습니다. 확보 방법 문서 헤더 테이블에서 확인합니다. FI 송장은 주로 BKPF-BUKRS, MM 송장은 RBKP-BUKRS에 저장됩니다. 예시 10001710US01DE01 | |||
| 공급업체 송장 번호 VendorInvoiceNumber | 공급업체가 송장 문서에 기재한 번호입니다. | ||
| 설명 공급업체의 회계 시스템에서 사용하는 참조 번호로, 종이 또는 전자 송장에 기재되어 있습니다. 송장 접수 시 직접 입력하거나 OCR로 추출합니다. 이 필드는 운영과 분석에 매우 중요하며, 특히 '중복 송장 지급 가능성' 대시보드에서 유용합니다. 중복 여부를 확인할 때는 공급업체명, 공급업체 송장 번호, 송장 금액이 같은 내부 송장 문서가 여러 건 있는지 검색하는 방법을 흔히 사용합니다. 송장을 식별하는 주요 외부 참조 번호입니다. 중요한 이유 중복 지급 가능성을 탐지하는 데 중요한 필드이며, 공급업체와 소통할 때 사용하는 주요 외부 참조 번호입니다. 확보 방법 문서 헤더의 'Reference' 필드에 저장되며, 일반적으로 BKPF-XBLNR에서 확인할 수 있습니다. 예시 INV-2023-9876733401120231015-001 | |||
| 반제일 ClearingDate | 지급이 이루어지고 미결 항목에서 송장이 반제된 날짜입니다. | ||
| 설명 반제일은 송장의 재무 결제가 완료된 날짜입니다. 'Payment Cleared' 활동이 발생한 날짜로, 대부분의 정상적인 송장 처리 과정에서 마지막 단계에 해당합니다. 이 날짜를 기준으로 실제 지급일을 계산해 송장 지급 기한과 비교합니다. 따라서 '기한 내 지급률' KPI를 계산하고 지급 성과를 분석하는 데 필수적입니다. 전체 송장 처리 시간의 종료 시점을 정하는 데도 사용됩니다. 중요한 이유 송장 결제가 최종 완료된 날짜로, 처리 시간 계산의 종료 시점이자 기한 내 지급 분석의 기준입니다. 확보 방법 공급업체의 경우 BSAK-AUGDT 등 반제 항목 테이블에서 확인할 수 있습니다. 예시 2023-10-282023-11-142024-01-09 | |||
| 송장 문서 유형 InvoiceDocumentType | SAP에서 송장 처리 방식을 결정하는 송장 문서 분류입니다. | ||
| 설명 문서 유형은 SAP에서 회계 문서를 분류하는 주요 설정 항목입니다. 예를 들어 'KR'은 일반적으로 공급업체 송장, 'RE'는 MM 송장, 'KG'는 공급업체 대변 메모에 사용됩니다. 문서 유형에 따라 번호 범위와 필수 입력 필드 등이 결정됩니다. 프로세스 분석에서 문서 유형별로 필터링하면 송장 종류에 따른 프로세스 흐름을 비교할 수 있습니다. 예를 들어 대변 메모의 승인 과정은 일반 송장과 다를 수 있습니다. '송장 승인 경로 변형' 대시보드에서 유용하게 활용할 수 있습니다. 중요한 이유 송장 유형별 처리 방식을 구분해 처리 경로와 주기 시간의 차이를 파악할 수 있습니다. 확보 방법 문서 헤더 테이블의 BKPF-BLART 필드에서 직접 가져옵니다. 예시 KRREKG | |||
| 송장 통화 InvoiceCurrency | 송장 금액에 적용되는 통화 코드입니다(예: USD, EUR). | ||
| 설명 이 속성은 송장 금액의 통화를 지정하며, 재무 데이터를 해석하는 데 필요한 정보를 제공합니다. 다국적 조직에서는 통화를 고려하지 않고 송장을 분석하면 결과가 왜곡될 수 있습니다. 이 필드를 사용하면 모든 금액을 하나의 보고 통화로 환산하거나 통화별로 분석을 나누어 지역별 재무 활동을 파악할 수 있습니다. 중요한 이유 송장 금액의 통화 정보를 제공해 정확한 재무 분석과 보고를 지원합니다. 특히 다국적 조직에서 유용합니다. 확보 방법 주로 BKPF-WAERS 또는 RBKP-WAERS 등 문서 헤더 테이블에서 확인할 수 있습니다. 예시 USDEURGBPJPY | |||
| 자동화 여부 IsAutomated | 사용자가 아닌 시스템이 활동을 자동으로 수행했는지 나타내는 플래그입니다. | ||
| 설명 이 불리언 속성은 사람이 시작한 활동과 시스템 작업, 워크플로 또는 봇이 수행한 활동을 구분합니다. 예를 들어 자동 지급 실행이나 시스템에서 생성한 송장 전기는 자동화된 활동으로 표시됩니다. 이 속성을 분석하면 매입채무 프로세스의 자동화 수준을 파악할 수 있습니다. 자동화 도입의 성과를 측정하고, 자동화 단계와 수동 단계의 효율을 비교하며, 추가 자동화 기회를 찾는 데 사용할 수 있습니다. 중요한 이유 프로세스의 자동화 수준을 측정해 자동화 효과를 분석하고 추가 개선 기회를 찾을 수 있습니다. 확보 방법 사용자 이름(예: 'SAP_SYSTEM' 또는 'BATCHUSER'와 같은 시스템 사용자 ID)이나 자동화 작업과 연결된 특정 트랜잭션 코드를 기준으로 판별합니다. 예시 truefalse | |||
| 지급 조건 PaymentTerms | 공급업체와 합의한 송장 지급 조건으로, 조기 지급 할인 기회가 포함되는 경우가 많습니다. | ||
| 설명 지급 조건은 지급 기한과 조기 지급 할인 적용 기준을 정합니다. 예를 들어 'Z001'은 '30일 이내 지급, 10일 이내 지급 시 2% 할인'을 의미할 수 있습니다. 이 속성은 '조기 지급 할인 적용률' 대시보드의 기반이 됩니다. 지급 조건을 분석하면 할인 대상인 송장을 모두 확인할 수 있습니다. 실제 적용된 할인과 비교하면 놓친 절감 기회를 파악하고 지급 프로세스의 효율성을 측정할 수 있습니다. 중요한 이유 조기 지급 할인 기회를 분석하고, 지급 프로세스의 재무 성과를 측정하며, 놓친 절감 기회를 파악하는 데 필수적입니다. 확보 방법 공급업체 개별 항목의 BSEG-ZTERM 테이블 또는 송장 헤더의 RBKP-ZTERM에서 확인할 수 있습니다. 예시 Z0010001NT30 | |||
| 지급 차단 사유 BlockingReason | 송장 지급이 차단된 이유를 나타내며, 불일치가 있음을 의미합니다. | ||
| 설명 송장이 3-way match 또는 다른 검증 단계에서 검사를 통과하지 못하면 지급이 차단됩니다. 지급 차단 사유에는 수량 불일치, 가격 차이, 입고 누락 등 문제가 구체적으로 표시됩니다. 이 속성은 '송장 불일치 재작업 분석' 대시보드에 중요합니다. 차단 사유별 발생 빈도를 분석하면 프로세스 비효율의 근본 원인을 찾을 수 있습니다. 예를 들어 '가격 차이'가 자주 발생한다면 구매 시스템의 마스터 데이터에 문제가 있을 수 있습니다. 중요한 이유 송장 불일치와 재작업의 근본 원인을 직접 파악할 수 있어, 원인에 맞춘 프로세스 개선이 가능합니다. 확보 방법 RSEG와 같은 송장 개별 항목 테이블에서 SPGR*로 시작하는 필드(예: SPGRP, SPGRQ, SPGRT)에 저장됩니다. RBKP_BLOCKED에서도 확인할 수 있습니다. 예시 가격 불일치수량 불일치입고 누락 | |||
| 지연 지급 여부 IsLatePayment | 송장이 지급 기한 이후에 지급되었는지 나타내는 참/거짓 값입니다. | ||
| 설명 계산된 이 속성은 송장 대금이 공식 지급 기한 이후에 지급되었는지를 참/거짓으로 나타냅니다. '반제일'과 '송장 지급 기한'을 비교해 계산합니다. 이 값을 사용하면 '지급 규정 준수 및 경과 기간' 대시보드와 '기한 내 지급률' KPI를 간편하게 분석할 수 있습니다. 지연 지급 건수를 집계하고, 기한 내 지급 비율을 계산하며, 지연 지급률이 높은 공급업체나 회사 코드를 쉽게 찾아낼 수 있습니다. 중요한 이유 지급 조건 준수 여부를 직접 측정하고, 기한 내 지급 KPI 계산을 간소화하며, 지급 성과가 낮은 영역을 파악하는 데 도움이 됩니다. 확보 방법 계산된 속성입니다. 계산식: IF ClearingDate > InvoiceDueDate THEN true ELSE false. 예시 truefalse | |||
| 할인 적용 여부 DiscountTaken | 조기 지급 할인이 실제로 적용되었는지 나타내는 참/거짓 값입니다. | ||
| 설명 이 속성은 송장 대금을 지급할 때 현금 할인이 실제로 적용되었는지를 나타냅니다. 매입채무(AP) 프로세스의 재무 효율성을 측정하는 데 중요한 요소입니다. 이 값은 '조기 지급 할인 적용률' KPI의 핵심입니다. 지급 조건에 따라 할인이 가능한 송장만 추린 뒤 이 값을 분석하면 절감한 금액과 놓친 할인 기회를 정확히 계산할 수 있습니다. 이를 통해 매입채무(AP) 처리 성과를 명확한 수치로 확인할 수 있습니다. 중요한 이유 적용 가능한 조기 지급 할인을 얼마나 잘 확보했는지 직접 측정하며, 이는 회사의 수익에 직접 영향을 줍니다. 확보 방법 지급 문서의 할인 금액 필드(BSEG-SKNTO)가 0보다 큰지 확인해 계산합니다. 예시 truefalse | |||
매입채무 송장 처리 활동
| 활동 | 설명 | ||
|---|---|---|---|
| 송장 승인 | 워크플로 시스템에서 송장에 필요한 승인이 모두 완료된 상태입니다. 일반적으로 송장을 전기하거나 지급 차단을 해제하기 전에 거치는 마지막 단계입니다. | ||
| 중요한 이유 이 주요 단계는 승인 주기의 종료를 나타냅니다. 승인 요청부터 승인 완료까지 걸린 시간은 효율성을 판단하는 중요한 지표입니다. 확보 방법 SAP Business Workflow 로그에서 완료 또는 최종 승인 단계로 기록합니다. 또는 승인 요청 후 지급 차단이 해제된 시점을 기준으로 추론할 수 있습니다. 기록 SAP 워크플로 로그에서 워크플로 완료 이벤트를 추출하거나 최종 'release' 이벤트를 찾습니다. 이벤트 유형 explicit | |||
| 송장 전기 | 송장을 총계정원장에 정식으로 기록해 금융 부채를 인식합니다. 임시 저장된 문서를 전기하거나 바로 전기할 수 있습니다. | ||
| 중요한 이유 중요한 재무 단계입니다. 회사의 지급 의무가 확정되며, 일반적으로 지급 일정을 수립하기 위한 선행 조건입니다. 확보 방법 문서 헤더(BKPF)의 전기일(BUDAT)로 이 이벤트를 확인합니다. 전기된 문서의 문서 상태(BKPF-BSTAT)는 공란입니다. 기록 임시 저장되지 않은 문서(BKPF-BSTAT이 공란)의 전기 타임스탬프(BKPF-BUDAT)를 사용합니다. 이벤트 유형 explicit | |||
| 송장 접수 | 이 활동은 SAP에서 송장 문서가 수동으로 생성되거나 OCR/VIM과 같은 자동화 인터페이스를 통해 생성된 시점을 나타냅니다. 일반적으로 회계 문서 헤더의 생성 날짜와 시간으로 이 이벤트를 기록합니다. | ||
| 중요한 이유 프로세스의 시작점인 이 활동은 송장 처리의 전체 주기 시간과 매입채무 프로세스의 처리량을 측정하는 데 필요합니다. 확보 방법 회계 문서 헤더 테이블(BKPF)의 문서 생성 날짜(CPUDT)와 시간(CPUTM)을 사용해 이 이벤트를 기록합니다. 기록 송장 문서의 생성 타임스탬프(BKPF-CPUDT, BKPF-CPUTM)를 사용합니다. 이벤트 유형 explicit | |||
| 송장 지급 차단 | 시스템 또는 사용자가 송장에 지급 차단을 설정해 지급할 수 없도록 한 상태입니다. 일반적으로 가격이나 수량 불일치, 승인 누락 등이 원인입니다. | ||
| 중요한 이유 문제와 재작업을 보여주는 주요 지표입니다. 차단 사유와 지속 시간을 분석하면 지급 지연과 프로세스 비효율의 근본 원인을 찾을 수 있습니다. 확보 방법 회계 문서의 공급업체 개별 항목(BSEG)에 있는 지급 차단 키 필드(ZLSPR)에 명시적으로 기록된 상태입니다. 기록 BSEG-ZLSPR 필드에 차단 사유가 입력되면 변경 문서에 기록됩니다. 이벤트 유형 explicit | |||
| 송장 취소 | 송장 문서를 역분개해 재무상 효력을 없앤 상태입니다. 잘못된 입력이나 공급업체 분쟁 등으로 발생하며, 프로세스가 종료되는 또 다른 경우입니다. | ||
| 중요한 이유 취소 내역을 추적하면 중복 제출이나 잘못된 송장 데이터 등 프로세스 실패 원인을 파악하고 상위 단계의 문제를 찾을 수 있습니다. 확보 방법 역분개 문서가 생성되면 명시적으로 기록됩니다. 원본 문서 헤더(BKPF)에는 역분개 문서 번호(STBLG)와 역분개 사유가 입력됩니다. 기록 원본 문서 헤더(BKPF-STBLG)에 연결된 역분개 문서의 전기일을 찾습니다. 이벤트 유형 explicit | |||
| 지급 반제 | 보조원장에서 지급 내역과 송장을 서로 대사해 송장 처리를 최종 마감하는 활동입니다. 프로세스가 완료되었음을 나타냅니다. | ||
| 중요한 이유 프로세스의 최종 단계로, 전체 주기 시간을 정확히 계산하는 데 필요합니다. 부채가 결제되었음을 확인합니다. 확보 방법 송장 문서의 공급업체 개별 항목(BSEG)에 있는 반제일(AUGDT) 필드가 입력되면 명시적으로 기록되는 이벤트입니다. 기록 송장 개별 항목의 반제일(BSEG-AUGDT)을 사용합니다. 이벤트 유형 explicit | |||
| 지급 실행 | 송장 대금이 지급된 상태입니다. 지급 실행이 완료되고 지급 문서가 생성되어 전기된 시점에 기록합니다. | ||
| 중요한 이유 현금 흐름 분석과 '기한 내 지급률' KPI 측정에 중요합니다. 지급일과 송장 만기일을 비교해 산출합니다. 확보 방법 송장을 반제하는 지급 문서의 전기일로 확인합니다. 지급 문서 번호는 송장 개별 항목(BSEG)의 반제 문서 필드(AUGBL)에 연결되어 있습니다. 기록 송장 개별 항목을 반제하는 지급 문서의 전기일(BUDAT)을 찾습니다. 이벤트 유형 explicit | |||
| 구매 주문서 대사 완료 | 송장이 해당 구매 주문서와 성공적으로 대사되었음을 나타냅니다. 구매 기반 송장의 3자 대사에서 중요한 단계입니다. | ||
| 중요한 이유 이 활동을 분석하면 대사 효율을 측정할 수 있으며, '3자 대사 성과' 및 '구매 주문서 없는 송장 비율' KPI를 산출하는 데 필요합니다. 확보 방법 BSEG 또는 ACDOCA 테이블의 송장 개별 항목에 유효한 구매 주문서 번호(EBELN)와 항목 번호(EBELP)가 있으면 이 이벤트를 추론합니다. 기록 송장 문서가 생성될 때 구매 주문서 참조(BSEG-EBELN)가 있는지 확인해 추론합니다. 이벤트 유형 inferred | |||
| 불일치 해결 | 이 활동은 지급 차단의 원인이 되었을 가능성이 있는 문제를 조사해 해결했음을 나타냅니다. 송장의 지급 차단이 해제된 시점에 기록합니다. | ||
| 중요한 이유 이 재작업 과정을 추적하는 것은 '송장 불일치 재작업 분석' 대시보드에 중요합니다. 오류 수정에 든 시간과 노력을 정량화할 수 있습니다. 확보 방법 지급 차단이 해제된 변경 문서를 확인해 추론합니다. 주요 데이터 출처는 BSEG-ZLSPR 필드의 변경 로그입니다. 기록 BSEG 테이블의 ZLSPR 필드 값이 입력된 상태에서 공란으로 변경된 변경 문서를 찾습니다. 이벤트 유형 inferred | |||
| 송장 만기일 경과 | 송장의 순 만기일이 지났지만 대금이 반제되지 않았음을 나타내는 계산 이벤트입니다. 지급이 늦었거나 연체된 상태를 의미합니다. | ||
| 중요한 이유 '지급 규정 준수 및 경과 기간' 대시보드에 필요한 활동입니다. 연체 송장을 미리 파악하고 관리하며, 지급 지연의 근본 원인을 분석하는 데 도움이 됩니다. 확보 방법 SAP에 명시적으로 기록되는 이벤트가 아닙니다. 시스템의 현재 날짜와 순 만기일을 비교해 계산합니다. 순 만기일은 BSEG-ZFBDT 또는 기준일과 지급 조건을 바탕으로 산출합니다. 기록 이벤트 타임스탬프가 송장의 순 만기일보다 늦을 때 발생하는 계산 이벤트입니다. 이벤트 유형 calculated | |||
| 송장 반려 | 승인자가 승인 워크플로 중에 송장을 반려한 상태입니다. 일반적으로 송장을 담당자에게 돌려보내 수정이나 확인을 요청합니다. | ||
| 중요한 이유 반려를 추적하면 승인 과정의 재작업을 파악하고 정책 미준수나 잘못된 송장 계정 지정 여부를 확인할 수 있습니다. 확보 방법 송장과 연결된 SAP Business Workflow 로그에 특정 결과 이벤트로 기록됩니다. 기록 SAP 워크플로 로그에서 'rejected' 상태 이벤트를 추출합니다. 이벤트 유형 explicit | |||
| 송장 임시 저장 | 시스템에 입력했지만 아직 총계정원장에 전기하지 않은 송장입니다. 미완성 문서를 저장해 두었다가 나중에 처리하거나 승인받기 위해 의도적으로 거치는 단계인 경우가 많습니다. | ||
| 중요한 이유 임시 저장된 송장을 추적하면 정식 전기 전에 발생하는 지연을 파악하고, 데이터 누락이나 초기 검증 문제를 확인할 수 있습니다. 확보 방법 회계 문서 헤더(BKPF)의 문서 상태 필드에서 상태를 확인합니다. BSTAT = 'V'이면 임시 저장 상태이며, 해당 상태가 설정된 시점에 이벤트가 발생합니다. 기록 BKPF 테이블의 변경 문서에서 BSTAT 필드가 'V'(Vor-erfasst/사전 입력)로 설정된 항목을 찾습니다. 이벤트 유형 inferred | |||
| 승인 워크플로로 송장 전달 | 업무 규칙에 따라 필요한 승인을 받도록 송장을 워크플로에 제출한 상태입니다. 승인 하위 프로세스가 시작되는 시점입니다. | ||
| 중요한 이유 이 활동은 '송장 평균 승인 시간' KPI를 측정하고 승인 병목을 분석하는 기준점입니다. 확보 방법 송장 객체(예: BUS2081)와 연결된 워크플로 인스턴스의 시작을 기록하는 SAP Business Workflow 로그(SWW* 테이블)에서 확인할 수 있습니다. 기록 송장 문서와 연결된 SAP 워크플로 로그(예: SWW_WIHEAD 테이블)에서 워크플로 시작 이벤트를 추출합니다. 이벤트 유형 explicit | |||
| 입고 대사 완료 | 송장의 수량과 금액이 해당 입고 문서와 성공적으로 대사되었음을 나타냅니다. 3자 대사에서 마지막으로 수행하는 검증입니다. | ||
| 중요한 이유 이 활동을 추적하면 3자 대사 과정의 비효율을 찾아내고 실제 입고 내역과 공급업체가 청구한 내용 간의 불일치를 확인할 수 있습니다. 확보 방법 송장 개별 항목에 자재 문서 참조(입고)가 있는지 확인해 추론합니다. 일반적으로 구매 주문서 항목 이력을 통해 연결됩니다. 기록 송장 개별 항목에 입고 문서 참조가 있는지 확인해 추론합니다. 예를 들어 MIRO 송장의 경우 RSEG에서 확인할 수 있습니다. 이벤트 유형 inferred | |||
| 지급 제안 생성 | 지급 실행(예: F110)의 일부로 송장이 지급 제안에 포함된 상태입니다. 지급 실행을 최종 처리하면 지급할 수 있도록 준비된 상태입니다. | ||
| 중요한 이유 미결 부채가 실제 지급을 준비하는 항목으로 전환되는 시점을 보여줍니다. 지급 운영의 효율을 분석하는 데 도움이 됩니다. 확보 방법 지급 실행 데이터 테이블, 특히 REGUP(지급 프로그램 처리 항목)과 REGUH(헤더)에 명시적으로 기록됩니다. 기록 REGUH에서 확인한 지급 실행에 송장이 REGUP 테이블에 나타나는 시점을 찾습니다. 이벤트 유형 explicit | |||
데이터 추출 안내
단계
- 사전 요건 및 액세스: CDS view가 있는 SAP S/4HANA 데이터베이스 스키마(일반적으로 SAPABAP1 또는 유사한 스키마)에 읽기 권한이 있는 사용자를 준비합니다. SAP HANA 데이터베이스에 연결할 수 있는 SAP HANA Studio, DBeaver 등의 SQL 클라이언트 도구도 필요합니다.
- 주요 CDS View 확인: 추출에 사용할 기본 CDS view는 I_JournalEntry, I_JournalEntryItem, I_SupplierInvoiceAPI01, I_ChangeDocument, I_WorkflowStatusDetails, I_PaymentProposalItem입니다. 각 view의 주요 필드를 확인합니다.
- 쿼리 범위 설정: SQL 클라이언트에서 SAP HANA 데이터베이스에 연결합니다. 전체 쿼리를 실행하기 전에 원천 시스템 식별자, 송장 날짜 범위(CreationDateTime), 해당 Company Code를 지정합니다.
- 기본 쿼리 준비: 쿼리 섹션에 제공된 전체 SQL 쿼리를 SQL 클라이언트에 복사합니다. 쿼리는 Common Table Expression(CTE)을 사용해 먼저 송장 기본 집합을 선택한 다음, 15개 활동의 데이터를 결합해 이벤트 로그를 구성합니다.
- 쿼리 매개변수 설정: 복사한 SQL 쿼리에서 자리표시자 변수를 찾습니다. '[YYYY-MM-DD]'를 분석 기간의 시작일과 종료일로 바꿉니다. '[Your Company Code 1]', '[Your Company Code 2]'는 분석할 SAP Company Code 목록으로 바꿉니다.
- 추출 쿼리 실행: 전체 SQL 쿼리를 실행합니다. 데이터 양과 선택한 날짜 범위에 따라 완료까지 몇 분에서 몇 시간이 걸릴 수 있습니다.
- 초기 결과 검토: 쿼리가 끝나면 결과의 처음 수백 행을 확인합니다. 데이터가 일관적인지, 모든 열에 예상한 값이 있는지, 서로 다른 ActivityName 값이 포함되어 있는지 살펴봅니다.
- 이벤트 로그 내보내기: SQL 클라이언트에서 전체 결과를 CSV 파일로 내보냅니다. 문자 인코딩 문제를 방지하려면 UTF-8로 저장합니다. 예를 들어 파일 이름을 sap_s4hana_ap_event_log.csv로 지정합니다.
- 업로드 준비: process mining 도구에 업로드하기 전에 CSV 파일의 열 이름이 필수 속성 이름과 정확히 일치하는지 확인합니다. 필수 속성에는 Invoice, ActivityName, EventTime, SourceSystem, LastDataUpdate, UserName 등이 포함됩니다.
- process mining 도구에 업로드: 생성한 CSV 파일을 process mining 플랫폼에 업로드하고 열을 해당 케이스 ID, 활동, 타임스탬프 필드에 매핑합니다.
구성
- 주요 CDS View: 추출에는 표준 S/4HANA CDS View를 조합해 사용합니다. 주요 view는 다음과 같습니다.
- I_JournalEntry 및 I_JournalEntryItem: 재무 문서의 헤더, 항목, 전기 세부 정보, 반제 정보를 가져옵니다.
- I_SupplierInvoiceAPI01: PO 참조와 지급 보류를 포함한 MM(물류) 송장 세부 정보를 가져옵니다.
- I_ChangeDocument: 지급 보류 설정 또는 해제와 같은 변경의 정확한 타임스탬프를 추적합니다.
- I_WorkflowStatusDetails: 송장 승인 워크플로와 관련된 이벤트를 추출합니다.
- I_PaymentProposalItem: 지급 실행 제안에 송장이 포함된 시점을 확인합니다.
- I_Supplier: VendorName과 같은 공급업체 마스터 정보를 데이터에 추가합니다.
- 날짜 범위 필터링: 데이터 양을 제한하려면 날짜 범위 필터를 반드시 적용해야 합니다. 제공된 쿼리는 Invoices_Base CTE에서 CreationDateTime을 기준으로 필터링합니다. 초기 분석에는 3~6개월 범위를 권장합니다. 처리 성능을 관리하기 쉽습니다.
- 필수 필터: 항상 CompanyCode로 필터링합니다. 모든 회사 코드를 한 번에 분석하면 속도가 크게 느려질 수 있고 업무상 의미가 없을 수도 있습니다. 또한 JournalEntryType을 필터링해 공급업체 관련 문서(예: 'KR', 'RE')만 선택합니다.
- 사전 요건: 쿼리를 실행하는 데이터베이스 사용자는 쿼리에 사용하는 모든 CDS view와 기본 HANA 스키마에 대한 SELECT 권한이 있어야 합니다. SAP GUI의 애플리케이션 수준 액세스만으로는 충분하지 않습니다.
- 성능 고려 사항: I_ChangeDocument를 직접 조회하면 리소스 사용량이 많을 수 있습니다. 제공된 쿼리는 먼저 송장을 필터링해 부하를 줄입니다. 데이터가 매우 많다면 사용량이 적은 시간대에 추출하거나 날짜 범위를 나누어 실행하세요.
샘플 쿼리 sql
-- Common Table Expression (CTE) to select the base set of AP Invoices
WITH Invoices_Base AS (
SELECT
I_JournalEntry.CompanyCode,
I_JournalEntry.AccountingDocument,
I_JournalEntry.FiscalYear,
CONCAT(I_JournalEntry.CompanyCode, CONCAT(I_JournalEntry.AccountingDocument, I_JournalEntry.FiscalYear)) AS InvoiceId,
I_JournalEntry.CreationDateTime,
I_JournalEntry.CreatedByUser,
I_JournalEntry.DocumentStatus,
I_JournalEntry.JournalEntryType,
I_JournalEntry.ReversalReferenceJournalEntry,
I_JournalEntry.IsReversed,
I_JournalEntry.ReversalDate,
IJE_ITEM.NetDueDate,
IJE_ITEM.Supplier,
SUP.SupplierName AS VendorName,
IJE_ITEM.AmountInCompanyCodeCurrency AS InvoiceAmount,
MM.PurchaseOrder AS PurchaseOrderNumber,
MM.PaymentBlockingReason
FROM I_JournalEntry
-- Join to get item details like due date and supplier
LEFT JOIN I_JournalEntryItem AS IJE_ITEM
ON I_JournalEntry.CompanyCode = IJE_ITEM.CompanyCode
AND I_JournalEntry.AccountingDocument = IJE_ITEM.AccountingDocument
AND I_JournalEntry.FiscalYear = IJE_ITEM.FiscalYear
AND IJE_ITEM.IsSupplier = 'X'
-- Join to get vendor name from master data
LEFT JOIN I_Supplier AS SUP
ON IJE_ITEM.Supplier = SUP.Supplier
-- Join to get MM Invoice specific data like PO Number and Payment Block
LEFT JOIN I_SupplierInvoiceAPI01 AS MM
ON I_JournalEntry.AccountingDocument = MM.AccountingDocument
AND I_JournalEntry.CompanyCode = MM.CompanyCode
AND I_JournalEntry.FiscalYear = MM.FiscalYear
WHERE
I_JournalEntry.JournalEntryType IN ('KR', 'RE') -- Standard Vendor Invoice Types
AND I_JournalEntry.CompanyCode IN ('[Your Company Code 1]', '[Your Company Code 2]')
AND I_JournalEntry.CreationDateTime BETWEEN '[YYYY-MM-DD]T00:00:00Z' AND '[YYYY-MM-DD]T23:59:59Z'
)
-- Event: 1. Invoice Received
SELECT
B.InvoiceId AS "Invoice",
'Invoice Received' AS "ActivityName",
B.CreationDateTime AS "EventTime",
'SAP_S4HANA' AS "SourceSystem",
CURRENT_UTCTIMESTAMP AS "LastDataUpdate",
B.CreatedByUser AS "UserName",
B.CompanyCode AS "CompanyCode",
B.VendorName AS "VendorName",
B.InvoiceAmount AS "InvoiceAmount",
B.PurchaseOrderNumber AS "PurchaseOrderNumber",
B.NetDueDate AS "InvoiceDueDate"
FROM Invoices_Base B
UNION ALL
-- Event: 2. Invoice Parked
SELECT
B.InvoiceId AS "Invoice",
'Invoice Parked' AS "ActivityName",
B.CreationDateTime AS "EventTime",
'SAP_S4HANA' AS "SourceSystem",
CURRENT_UTCTIMESTAMP AS "LastDataUpdate",
B.CreatedByUser AS "UserName",
B.CompanyCode AS "CompanyCode",
B.VendorName AS "VendorName",
B.InvoiceAmount AS "InvoiceAmount",
B.PurchaseOrderNumber AS "PurchaseOrderNumber",
B.NetDueDate AS "InvoiceDueDate"
FROM Invoices_Base B
WHERE B.DocumentStatus = 'V' -- 'V' stands for Parked
UNION ALL
-- Event: 3. Purchase Order Matched
SELECT
B.InvoiceId AS "Invoice",
'Purchase Order Matched' AS "ActivityName",
B.CreationDateTime AS "EventTime",
'SAP_S4HANA' AS "SourceSystem",
CURRENT_UTCTIMESTAMP AS "LastDataUpdate",
B.CreatedByUser AS "UserName",
B.CompanyCode AS "CompanyCode",
B.VendorName AS "VendorName",
B.InvoiceAmount AS "InvoiceAmount",
B.PurchaseOrderNumber AS "PurchaseOrderNumber",
B.NetDueDate AS "InvoiceDueDate"
FROM Invoices_Base B
WHERE B.PurchaseOrderNumber IS NOT NULL AND B.PurchaseOrderNumber <> ''
UNION ALL
-- Event: 4. Goods Receipt Matched
SELECT
B.InvoiceId AS "Invoice",
'Goods Receipt Matched' AS "ActivityName",
B.CreationDateTime AS "EventTime",
'SAP_S4HANA' AS "SourceSystem",
CURRENT_UTCTIMESTAMP AS "LastDataUpdate",
B.CreatedByUser AS "UserName",
B.CompanyCode AS "CompanyCode",
B.VendorName AS "VendorName",
B.InvoiceAmount AS "InvoiceAmount",
B.PurchaseOrderNumber AS "PurchaseOrderNumber",
B.NetDueDate AS "InvoiceDueDate"
FROM Invoices_Base B
INNER JOIN I_SupplierInvoiceItemAPI01 AS MM_ITEM
ON B.AccountingDocument = MM_ITEM.AccountingDocument
AND B.FiscalYear = MM_ITEM.FiscalYear
WHERE MM_ITEM.GoodsReceipt IS NOT NULL AND MM_ITEM.GoodsReceipt <> ''
UNION ALL
-- Event: 5. Invoice Blocked For Payment
SELECT
B.InvoiceId AS "Invoice",
'Invoice Blocked For Payment' AS "ActivityName",
B.CreationDateTime AS "EventTime", -- Approximates block time as creation time if blocked on entry
'SAP_S4HANA' AS "SourceSystem",
CURRENT_UTCTIMESTAMP AS "LastDataUpdate",
B.CreatedByUser AS "UserName",
B.CompanyCode AS "CompanyCode",
B.VendorName AS "VendorName",
B.InvoiceAmount AS "InvoiceAmount",
B.PurchaseOrderNumber AS "PurchaseOrderNumber",
B.NetDueDate AS "InvoiceDueDate"
FROM Invoices_Base B
WHERE B.PaymentBlockingReason IS NOT NULL AND B.PaymentBlockingReason <> ''
UNION ALL
-- Event: 6. Discrepancy Resolved (Payment Block Removed)
SELECT
B.InvoiceId AS "Invoice",
'Discrepancy Resolved' AS "ActivityName",
CD.ChangeTime AS "EventTime",
'SAP_S4HANA' AS "SourceSystem",
CURRENT_UTCTIMESTAMP AS "LastDataUpdate",
CD.UserName AS "UserName",
B.CompanyCode AS "CompanyCode",
B.VendorName AS "VendorName",
B.InvoiceAmount AS "InvoiceAmount",
B.PurchaseOrderNumber AS "PurchaseOrderNumber",
B.NetDueDate AS "InvoiceDueDate"
FROM Invoices_Base B
INNER JOIN I_ChangeDocument AS CD
ON CONCAT(B.CompanyCode, B.AccountingDocument, B.FiscalYear) = CD.ObjectValue
WHERE CD.ChangeDocumentObject = 'INVOICE'
AND CD.TableName = 'RBKP'
AND CD.FieldName = 'ZLSPR' -- Field for Payment Block
AND CD.NewFieldValue = '' -- Block was removed
UNION ALL
-- Event: 7, 8, 9. Workflow Events (Routed, Approved, Rejected)
SELECT
B.InvoiceId AS "Invoice",
CASE WF.WorkflowStatus
WHEN 'READY' THEN 'Invoice Routed For Approval'
WHEN 'APPROVED' THEN 'Invoice Approved'
WHEN 'REJECTED' THEN 'Invoice Rejected'
END AS "ActivityName",
WF.WorkflowStatusChangedDateTime AS "EventTime",
'SAP_S4HANA' AS "SourceSystem",
CURRENT_UTCTIMESTAMP AS "LastDataUpdate",
WF.WorkflowStatusChangedByUser AS "UserName",
B.CompanyCode AS "CompanyCode",
B.VendorName AS "VendorName",
B.InvoiceAmount AS "InvoiceAmount",
B.PurchaseOrderNumber AS "PurchaseOrderNumber",
B.NetDueDate AS "InvoiceDueDate"
FROM Invoices_Base B
INNER JOIN I_WorkflowStatusDetails AS WF
ON B.InvoiceId = WF.WorkflowScenarioInstance
WHERE WF.WorkflowStatus IN ('READY', 'APPROVED', 'REJECTED')
UNION ALL
-- Event: 10. Invoice Posted
SELECT
B.InvoiceId AS "Invoice",
'Invoice Posted' AS "ActivityName",
JE.PostingDate AS "EventTime",
'SAP_S4HANA' AS "SourceSystem",
CURRENT_UTCTIMESTAMP AS "LastDataUpdate",
B.CreatedByUser AS "UserName",
B.CompanyCode AS "CompanyCode",
B.VendorName AS "VendorName",
B.InvoiceAmount AS "InvoiceAmount",
B.PurchaseOrderNumber AS "PurchaseOrderNumber",
B.NetDueDate AS "InvoiceDueDate"
FROM Invoices_Base B
INNER JOIN I_JournalEntry AS JE
ON B.AccountingDocument = JE.AccountingDocument
AND B.CompanyCode = JE.CompanyCode
AND B.FiscalYear = JE.FiscalYear
WHERE B.DocumentStatus <> 'V' -- Any status other than Parked is considered Posted for AP
UNION ALL
-- Event: 11. Payment Proposal Created
SELECT
B.InvoiceId AS "Invoice",
'Payment Proposal Created' AS "ActivityName",
PPI.PaymentProposalRunDate AS "EventTime",
'SAP_S4HANA' AS "SourceSystem",
CURRENT_UTCTIMESTAMP AS "LastDataUpdate",
PPI.CreatedByUser AS "UserName",
B.CompanyCode AS "CompanyCode",
B.VendorName AS "VendorName",
B.InvoiceAmount AS "InvoiceAmount",
B.PurchaseOrderNumber AS "PurchaseOrderNumber",
B.NetDueDate AS "InvoiceDueDate"
FROM Invoices_Base B
INNER JOIN I_PaymentProposalItem AS PPI
ON B.CompanyCode = PPI.CompanyCode
AND B.AccountingDocument = PPI.AccountingDocument
AND B.FiscalYear = PPI.FiscalYear
UNION ALL
-- Event: 12. Payment Executed
-- This links the invoice to its clearing document, which is the payment document
SELECT DISTINCT
B.InvoiceId AS "Invoice",
'Payment Executed' AS "ActivityName",
CLEAR_JE.CreationDateTime AS "EventTime",
'SAP_S4HANA' AS "SourceSystem",
CURRENT_UTCTIMESTAMP AS "LastDataUpdate",
CLEAR_JE.CreatedByUser AS "UserName",
B.CompanyCode AS "CompanyCode",
B.VendorName AS "VendorName",
B.InvoiceAmount AS "InvoiceAmount",
B.PurchaseOrderNumber AS "PurchaseOrderNumber",
B.NetDueDate AS "InvoiceDueDate"
FROM Invoices_Base B
INNER JOIN I_JournalEntryItem AS IJE_ITEM
ON B.CompanyCode = IJE_ITEM.CompanyCode
AND B.AccountingDocument = IJE_ITEM.AccountingDocument
AND B.FiscalYear = IJE_ITEM.FiscalYear
INNER JOIN I_JournalEntry AS CLEAR_JE
ON IJE_ITEM.ClearingJournalEntry = CLEAR_JE.AccountingDocument
AND IJE_ITEM.CompanyCode = CLEAR_JE.CompanyCode
WHERE IJE_ITEM.ClearingJournalEntry IS NOT NULL AND IJE_ITEM.ClearingJournalEntry <> ''
AND CLEAR_JE.JournalEntryType = 'KZ' -- Vendor Payment Document Type
UNION ALL
-- Event: 13. Invoice Due Date Passed
SELECT
B.InvoiceId AS "Invoice",
'Invoice Due Date Passed' AS "ActivityName",
ADD_DAYS(B.NetDueDate, 1) AS "EventTime",
'SAP_S4HANA' AS "SourceSystem",
CURRENT_UTCTIMESTAMP AS "LastDataUpdate",
'SYSTEM' AS "UserName",
B.CompanyCode AS "CompanyCode",
B.VendorName AS "VendorName",
B.InvoiceAmount AS "InvoiceAmount",
B.PurchaseOrderNumber AS "PurchaseOrderNumber",
B.NetDueDate AS "InvoiceDueDate"
FROM Invoices_Base B
LEFT JOIN I_JournalEntryItem AS IJE_ITEM
ON B.CompanyCode = IJE_ITEM.CompanyCode
AND B.AccountingDocument = IJE_ITEM.AccountingDocument
AND B.FiscalYear = IJE_ITEM.FiscalYear
WHERE B.NetDueDate < CURRENT_DATE
AND IJE_ITEM.ClearingDate IS NULL -- Invoice is not yet cleared
UNION ALL
-- Event: 14. Payment Cleared
SELECT DISTINCT
B.InvoiceId AS "Invoice",
'Payment Cleared' AS "ActivityName",
IJE_ITEM.ClearingDate AS "EventTime",
'SAP_S4HANA' AS "SourceSystem",
CURRENT_UTCTIMESTAMP AS "LastDataUpdate",
IJE_ITEM.ChangedByUser AS "UserName", -- User who cleared it
B.CompanyCode AS "CompanyCode",
B.VendorName AS "VendorName",
B.InvoiceAmount AS "InvoiceAmount",
B.PurchaseOrderNumber AS "PurchaseOrderNumber",
B.NetDueDate AS "InvoiceDueDate"
FROM Invoices_Base B
INNER JOIN I_JournalEntryItem AS IJE_ITEM
ON B.CompanyCode = IJE_ITEM.CompanyCode
AND B.AccountingDocument = IJE_ITEM.AccountingDocument
AND B.FiscalYear = IJE_ITEM.FiscalYear
WHERE IJE_ITEM.ClearingDate IS NOT NULL
UNION ALL
-- Event: 15. Invoice Cancelled
SELECT
B.InvoiceId AS "Invoice",
'Invoice Cancelled' AS "ActivityName",
B.ReversalDate AS "EventTime",
'SAP_S4HANA' AS "SourceSystem",
CURRENT_UTCTIMESTAMP AS "LastDataUpdate",
B.CreatedByUser AS "UserName", -- User who created the original document
B.CompanyCode AS "CompanyCode",
B.VendorName AS "VendorName",
B.InvoiceAmount AS "InvoiceAmount",
B.PurchaseOrderNumber AS "PurchaseOrderNumber",
B.NetDueDate AS "InvoiceDueDate"
FROM Invoices_Base B
WHERE B.IsReversed = 'X'; 단계
- 관련 SAP S/4HANA 테이블이 있는 SAP HANA 스키마에 직접 읽기 권한이 있는지 확인합니다. BKPF, ACDOCA와 함께 미결 문서, 워크플로, 구매, 입고, 지급 제안, 반제, 역분개 데이터에 사용하도록 설정된 추가 테이블을 읽는 데 필요한 연결 문자열, 인증 정보, 스키마 이름, 권한을 확보합니다.
- 실행 전에 대상 시스템의 기술 데이터 모델을 확인합니다. 시스템에서 사용하는 실제 이름, 키 필드, 타임스탬프 필드, 역분개 관계, 지급 관계, 워크플로 데이터 저장 방식을 검증합니다. 쿼리의 대괄호로 표시된 각 자리표시자를 시스템에 해당하는 객체 또는 필드로 바꿉니다. 워크플로나 미결 문서 데이터가 하나의 공통 테이블에 저장된다고 가정하지 마세요.
- 추출 매개변수를 설정합니다. [Start date], [End date], [Company code filter], [Document type filter], [Schema name]을 지정합니다. 처음에는 3~6개월 범위로 실행하고 성능과 데이터 완전성을 검증한 뒤 범위를 늘립니다.
- SAP HANA Database Explorer 또는 승인된 SQL 실행 도구 등 허가된 SAP HANA SQL 클라이언트에서 SQL 쿼리를 실행합니다. 쿼리는 명시적으로 추출한 각 활동에 대해 한 행씩 출력하며, ProcessMind가 이벤트를 추론하도록 의존하지 않습니다.
- 결과를 검증합니다. Invoice가 케이스 식별자인지, ActivityName에 필수 활동 15개가 모두 포함되어 있는지, EventTime이 입력되어 있고 시간 순서가 타당한지, SourceSystem이 SAP S/4HANA를 나타내는지, LastDataUpdate에 추출 갱신 타임스탬프가 있는지 확인합니다. 데이터를 불러오기 전에 중복 이벤트, 역분개 관계, 문서 조인을 검토합니다.
- 시스템별 매핑이 필요하면 적용합니다. 예를 들어 설정된 미결 문서 원천을 Invoice Parked에, 워크플로 원천을 Invoice Routed For Approval, Invoice Approved, Invoice Rejected에, 지급 원천을 Payment Proposal Created와 Payment Executed에 매핑합니다. 감사 추적이 필요하면 원천 식별자를 추가 열에 보존합니다.
- 이벤트당 한 행이 포함된 구분 파일 또는 데이터베이스 결과 집합으로 내보냅니다. UTF-8 인코딩을 사용하고 타임스탬프의 시간대를 일관되게 유지합니다. Invoice, ActivityName, EventTime, SourceSystem, LastDataUpdate, UserName, CompanyCode, VendorName, InvoiceAmount, PurchaseOrderNumber, InvoiceDueDate 열 이름을 그대로 유지하고, 송장별로 활동을 집계하지 않습니다.
- 이벤트 로그를 ProcessMind에 업로드하고 Invoice를 케이스 식별자, ActivityName을 활동 열, EventTime을 이벤트 시간 열로 설정합니다. 선택 속성이 해당 필드에 매핑되었는지 확인합니다. ProcessMind는 이벤트 로그를 그대로 읽으므로 시각화할 모든 활동이 업로드 전에 행으로 포함되어 있어야 합니다.
구성
- 날짜 범위: 처음에는 3~6개월로 설정합니다. 추출하는 활동에 따라 전기일, 문서 생성일 또는 관련 원천 이벤트 날짜를 사용합니다. 쿼리 실행 시간과 원천 데이터 보존 기간을 확인한 뒤에만 범위를 늘립니다.
- 회사 필터: [Company code filter]를 설정해 필요한 회사 코드만 추출합니다. 제한이 필요하지 않다면 운영 환경에서 무제한으로 조회하지 말고, 통제된 방식으로 전체 회사를 선택합니다.
- 문서 필터: 대상 시스템에서 공급업체 송장, 대변 메모, 미결 문서, 지급 문서, 역분개에 사용하는 문서 유형을 확인한 뒤에만 [Document type filter]를 설정합니다.
- 스키마 및 객체 매핑: [Schema name]과 대괄호로 표시된 모든 원천 객체 또는 필드를 대상 SAP S/4HANA 시스템에서 확인한 값으로 바꿉니다. 직접 실행하는 HANA 쿼리는 시스템에서 활성화한 애플리케이션, 확장 기능, 워크플로 설계, 데이터 모델에 맞게 조정해야 합니다.
- 타임스탬프 처리: 모든 원천 타임스탬프를 하나의 시간대로 정규화합니다. 날짜만 있는 경우 문서화된 기본 시간 값을 사용하고, 해당 제약을 데이터 사전에 기록합니다.
- 이벤트 의미: 각 활동을 별도의 행으로 추출합니다. 여러 활동을 하나의 송장 레코드로 합치지 말고, ProcessMind가 매칭, 승인, 만기일 또는 반제 이벤트를 도출할 것으로 기대하지 않습니다.
- 만기일 이벤트: 만기일이 설정된 평가 시점보다 앞서고 해당 시점까지 반제 이벤트가 없는 송장에 대해서만 Invoice Due Date Passed를 생성합니다. 쿼리는 이 계산에 [Evaluation timestamp]를 사용합니다.
- 성능: 초기 기간과 회사 범위를 제한하고, 인덱스 또는 파티셔닝에 적합한 필드를 필터링하며, 불필요하게 많은 열을 조회하지 않습니다. 승인된 보고 시간대에 실행합니다. 운영 쿼리가 합의된 실행 시간을 초과하면 원천 데이터 일부를 스테이징하는 방안을 검토합니다.
- 갱신 전략: LastDataUpdate에는 추출 실행 시각을 입력합니다. 증분 적재에서는 관련 원천 변경 타임스탬프를 기준으로 워터마크를 유지하고, 늦은 변경과 역분개를 반영할 수 있도록 조회 기간을 겹치게 설정합니다.
- 사전 요건: 필요한 데이터베이스 권한, 승인된 운영 환경 액세스, 시스템의 Universal Journal 설정에 대한 이해, 설정된 미결 문서·구매·입고·워크플로·지급·반제·역분개 원천에 대한 액세스, 해당하는 SAP 라이선스 또는 거버넌스 승인.
샘플 쿼리 sql
WITH
params AS (
SELECT
TO_DATE('[Start date]') AS start_date,
TO_DATE('[End date]') AS end_date,
TO_TIMESTAMP('[Evaluation timestamp]') AS evaluation_ts,
TO_TIMESTAMP('[Extraction timestamp]') AS extraction_ts
FROM DUMMY
),
base_invoice AS (
SELECT
b.mandt,
b.bukrs,
b.belnr,
b.gjahr,
b.bldat,
b.budat,
b.cpudt,
b.cputm,
b.usnam,
b.blart,
b.xblnr,
b.stblg,
b.stjah,
a.lifnr,
a.wrbtr,
a.waers,
a.zfbdt,
a.zbd1t,
a.zbd2t,
a.zbd3t,
a.ebeln,
a.ebelp,
a.augbl,
a.augdt,
a.buzei,
v.name1 AS vendor_name,
CASE
WHEN a.zfbdt IS NOT NULL THEN ADD_DAYS(a.zfbdt, COALESCE(a.zbd1t, 0))
ELSE NULL
END AS invoice_due_date
FROM [Schema name].BKPF b
INNER JOIN [Schema name].ACDOCA a
ON a.mandt = b.mandt
AND a.rbukrs = b.bukrs
AND a.belnr = b.belnr
AND a.gjahr = b.gjahr
LEFT JOIN [Schema name].[Vendor master table] v
ON v.[Vendor key field] = a.lifnr
CROSS JOIN params p
WHERE b.bukrs IN ([Company code filter])
AND b.blart IN ([Document type filter])
AND b.cpudt BETWEEN p.start_date AND p.end_date
),
invoice_received AS (
SELECT DISTINCT
CAST(belnr AS NVARCHAR(40)) AS invoice,
'Invoice Received' AS activity_name,
TO_TIMESTAMP(TO_VARCHAR(cpudt, 'YYYY-MM-DD') || ' ' || COALESCE(TO_VARCHAR(cputm), '00:00:00')) AS event_time,
CAST(usnam AS NVARCHAR(80)) AS user_name,
bukrs AS company_code,
vendor_name,
wrbtr AS invoice_amount,
waers AS document_currency,
CAST(ebeln AS NVARCHAR(40)) AS purchase_order_number,
invoice_due_date
FROM base_invoice
),
invoice_parked AS (
SELECT
CAST([Parked invoice key field] AS NVARCHAR(40)) AS invoice,
'Invoice Parked' AS activity_name,
[Parked event timestamp field] AS event_time,
CAST([Parked user field] AS NVARCHAR(80)) AS user_name,
[Parked company code field] AS company_code,
[Parked vendor name field] AS vendor_name,
[Parked amount field] AS invoice_amount,
[Parked currency field] AS document_currency,
CAST([Parked purchase order field] AS NVARCHAR(40)) AS purchase_order_number,
[Parked due date field] AS invoice_due_date
FROM [Schema name].[Parked document source]
WHERE [Parked event date field] BETWEEN (SELECT start_date FROM params) AND (SELECT end_date FROM params)
AND [Parked company code field] IN ([Company code filter])
),
purchase_order_matched AS (
SELECT DISTINCT
CAST(i.belnr AS NVARCHAR(40)) AS invoice,
'Purchase Order Matched' AS activity_name,
COALESCE([Purchase order match timestamp field], TO_TIMESTAMP(TO_VARCHAR(i.cpudt, 'YYYY-MM-DD') || ' ' || COALESCE(TO_VARCHAR(i.cputm), '00:00:00'))) AS event_time,
CAST(COALESCE([Purchase order match user field], i.usnam) AS NVARCHAR(80)) AS user_name,
i.bukrs AS company_code,
i.vendor_name,
i.wrBtr AS invoice_amount,
i.waers AS document_currency,
CAST(i.ebeln AS NVARCHAR(40)) AS purchase_order_number,
i.invoice_due_date
FROM base_invoice i
INNER JOIN [Schema name].[Purchase order match source] m
ON m.[Invoice document key field] = i.belnr
AND m.[Invoice company code field] = i.bukrs
AND m.[Invoice fiscal year field] = i.gjahr
WHERE i.ebeln IS NOT NULL
),
goods_receipt_matched AS (
SELECT DISTINCT
CAST(i.belnr AS NVARCHAR(40)) AS invoice,
'Goods Receipt Matched' AS activity_name,
[Goods receipt match timestamp field] AS event_time,
CAST(COALESCE([Goods receipt match user field], i.usnam) AS NVARCHAR(80)) AS user_name,
i.bukrs AS company_code,
i.vendor_name,
i.wrbtr AS invoice_amount,
i.waers AS document_currency,
CAST(i.ebeln AS NVARCHAR(40)) AS purchase_order_number,
i.invoice_due_date
FROM base_invoice i
INNER JOIN [Schema name].[Goods receipt match source] g
ON g.[Invoice document key field] = i.belnr
AND g.[Invoice company code field] = i.bukrs
AND g.[Invoice fiscal year field] = i.gjahr
WHERE i.ebeln IS NOT NULL
),
invoice_blocked AS (
SELECT DISTINCT
CAST(i.belnr AS NVARCHAR(40)) AS invoice,
'Invoice Blocked For Payment' AS activity_name,
[Payment block timestamp field] AS event_time,
CAST(COALESCE([Payment block user field], i.usnam) AS NVARCHAR(80)) AS user_name,
i.bukrs AS company_code,
i.vendor_name,
i.wrbtr AS invoice_amount,
i.waers AS document_currency,
CAST(i.ebeln AS NVARCHAR(40)) AS purchase_order_number,
i.invoice_due_date
FROM base_invoice i
INNER JOIN [Schema name].[Payment block source] b
ON b.[Invoice document key field] = i.belnr
AND b.[Invoice company code field] = i.bukrs
AND b.[Invoice fiscal year field] = i.gjahr
WHERE [Payment block value field] IS NOT NULL
),
discrepancy_resolved AS (
SELECT DISTINCT
CAST(i.belnr AS NVARCHAR(40)) AS invoice,
'Discrepancy Resolved' AS activity_name,
[Payment block removal timestamp field] AS event_time,
CAST(COALESCE([Payment block removal user field], i.usnam) AS NVARCHAR(80)) AS user_name,
i.bukrs AS company_code,
i.vendor_name,
i.wrbtr AS invoice_amount,
i.waers AS document_currency,
CAST(i.ebeln AS NVARCHAR(40)) AS purchase_order_number,
i.invoice_due_date
FROM base_invoice i
INNER JOIN [Schema name].[Payment block history source] h
ON h.[Invoice document key field] = i.belnr
AND h.[Invoice company code field] = i.bukrs
AND h.[Invoice fiscal year field] = i.gjahr
WHERE [Previous payment block value field] IS NOT NULL
AND [New payment block value field] IS NULL
),
invoice_routed_for_approval AS (
SELECT DISTINCT
CAST(i.belnr AS NVARCHAR(40)) AS invoice,
'Invoice Routed For Approval' AS activity_name,
[Workflow routed timestamp field] AS event_time,
CAST([Workflow initiator field] AS NVARCHAR(80)) AS user_name,
i.bukrs AS company_code,
i.vendor_name,
i.wrbtr AS invoice_amount,
i.waers AS document_currency,
CAST(i.ebeln AS NVARCHAR(40)) AS purchase_order_number,
i.invoice_due_date
FROM base_invoice i
INNER JOIN [Schema name].[Workflow event source] w
ON w.[Invoice document key field] = i.belnr
AND w.[Invoice company code field] = i.bukrs
AND w.[Invoice fiscal year field] = i.gjahr
WHERE [Workflow event type field] = '[Workflow routed event value]'
),
invoice_approved AS (
SELECT DISTINCT
CAST(i.belnr AS NVARCHAR(40)) AS invoice,
'Invoice Approved' AS activity_name,
[Workflow approval timestamp field] AS event_time,
CAST([Workflow approver field] AS NVARCHAR(80)) AS user_name,
i.bukrs AS company_code,
i.vendor_name,
i.wrbtr AS invoice_amount,
i.waers AS document_currency,
CAST(i.ebeln AS NVARCHAR(40)) AS purchase_order_number,
i.invoice_due_date
FROM base_invoice i
INNER JOIN [Schema name].[Workflow event source] w
ON w.[Invoice document key field] = i.belnr
AND w.[Invoice company code field] = i.bukrs
AND w.[Invoice fiscal year field] = i.gjahr
WHERE [Workflow event type field] = '[Workflow approved event value]'
),
invoice_rejected AS (
SELECT DISTINCT
CAST(i.belnr AS NVARCHAR(40)) AS invoice,
'Invoice Rejected' AS activity_name,
[Workflow rejection timestamp field] AS event_time,
CAST([Workflow rejector field] AS NVARCHAR(80)) AS user_name,
i.bukrs AS company_code,
i.vendor_name,
i.wrbtr AS invoice_amount,
i.waers AS document_currency,
CAST(i.ebeln AS NVARCHAR(40)) AS purchase_order_number,
i.invoice_due_date
FROM base_invoice i
INNER JOIN [Schema name].[Workflow event source] w
ON w.[Invoice document key field] = i.belnr
AND w.[Invoice company code field] = i.bukrs
AND w.[Invoice fiscal year field] = i.gjahr
WHERE [Workflow event type field] = '[Workflow rejected event value]'
),
invoice_posted AS (
SELECT DISTINCT
CAST(belnr AS NVARCHAR(40)) AS invoice,
'Invoice Posted' AS activity_name,
TO_TIMESTAMP(TO_VARCHAR(budat, 'YYYY-MM-DD') || ' 00:00:00') AS event_time,
CAST(usnam AS NVARCHAR(80)) AS user_name,
bukrs AS company_code,
vendor_name,
wrbtr AS invoice_amount,
waers AS document_currency,
CAST(ebeln AS NVARCHAR(40)) AS purchase_order_number,
invoice_due_date
FROM base_invoice
WHERE stblg IS NULL
),
payment_proposal_created AS (
SELECT DISTINCT
CAST(i.belnr AS NVARCHAR(40)) AS invoice,
'Payment Proposal Created' AS activity_name,
[Payment proposal timestamp field] AS event_time,
CAST([Payment proposal user field] AS NVARCHAR(80)) AS user_name,
i.bukrs AS company_code,
i.vendor_name,
i.wrbtr AS invoice_amount,
i.waers AS document_currency,
CAST(i.ebeln AS NVARCHAR(40)) AS purchase_order_number,
i.invoice_due_date
FROM base_invoice i
INNER JOIN [Schema name].[Payment proposal source] p
ON p.[Invoice document key field] = i.belnr
AND p.[Invoice company code field] = i.bukrs
AND p.[Invoice fiscal year field] = i.gjahr
),
payment_executed AS (
SELECT DISTINCT
CAST(i.belnr AS NVARCHAR(40)) AS invoice,
'Payment Executed' AS activity_name,
[Payment execution timestamp field] AS event_time,
CAST([Payment execution user field] AS NVARCHAR(80)) AS user_name,
i.bukrs AS company_code,
i.vendor_name,
i.wrbtr AS invoice_amount,
i.waers AS document_currency,
CAST(i.ebeln AS NVARCHAR(40)) AS purchase_order_number,
i.invoice_due_date
FROM base_invoice i
INNER JOIN [Schema name].[Payment execution source] p
ON p.[Invoice document key field] = i.belnr
AND p.[Invoice company code field] = i.bukrs
AND p.[Invoice fiscal year field] = i.gjahr
),
invoice_due_date_passed AS (
SELECT DISTINCT
CAST(i.belnr AS NVARCHAR(40)) AS invoice,
'Invoice Due Date Passed' AS activity_name,
TO_TIMESTAMP(TO_VARCHAR(i.invoice_due_date, 'YYYY-MM-DD') || ' 23:59:59') AS event_time,
CAST(i.usnam AS NVARCHAR(80)) AS user_name,
i.bukrs AS company_code,
i.vendor_name,
i.wrbtr AS invoice_amount,
i.waers AS document_currency,
CAST(i.ebeln AS NVARCHAR(40)) AS purchase_order_number,
i.invoice_due_date
FROM base_invoice i
WHERE i.invoice_due_date IS NOT NULL
AND TO_TIMESTAMP(TO_VARCHAR(i.invoice_due_date, 'YYYY-MM-DD') || ' 23:59:59') < (SELECT evaluation_ts FROM params)
AND i.augbl IS NULL
),
payment_cleared AS (
SELECT DISTINCT
CAST(i.belnr AS NVARCHAR(40)) AS invoice,
'Payment Cleared' AS activity_name,
TO_TIMESTAMP(TO_VARCHAR(i.augdt, 'YYYY-MM-DD') || ' 00:00:00') AS event_time,
CAST(i.usnam AS NVARCHAR(80)) AS user_name,
i.bukrs AS company_code,
i.vendor_name,
i.wrbtr AS invoice_amount,
i.waers AS document_currency,
CAST(i.ebeln AS NVARCHAR(40)) AS purchase_order_number,
i.invoice_due_date
FROM base_invoice i
WHERE i.augbl IS NOT NULL
AND i.augdt IS NOT NULL
),
invoice_cancelled AS (
SELECT DISTINCT
CAST(belnr AS NVARCHAR(40)) AS invoice,
'Invoice Cancelled' AS activity_name,
TO_TIMESTAMP(TO_VARCHAR([Reversal posting date field], 'YYYY-MM-DD') || ' 00:00:00') AS event_time,
CAST(COALESCE([Reversal user field], usnam) AS NVARCHAR(80)) AS user_name,
bukrs AS company_code,
vendor_name,
wrbtr AS invoice_amount,
waers AS document_currency,
CAST(ebeln AS NVARCHAR(40)) AS purchase_order_number,
invoice_due_date
FROM base_invoice
WHERE stblg IS NOT NULL
),
all_events AS (
SELECT * FROM invoice_received
UNION ALL SELECT * FROM invoice_parked
UNION ALL SELECT * FROM purchase_order_matched
UNION ALL SELECT * FROM goods_receipt_matched
UNION ALL SELECT * FROM invoice_blocked
UNION ALL SELECT * FROM discrepancy_resolved
UNION ALL SELECT * FROM invoice_routed_for_approval
UNION ALL SELECT * FROM invoice_approved
UNION ALL SELECT * FROM invoice_rejected
UNION ALL SELECT * FROM invoice_posted
UNION ALL SELECT * FROM payment_proposal_created
UNION ALL SELECT * FROM payment_executed
UNION ALL SELECT * FROM invoice_due_date_passed
UNION ALL SELECT * FROM payment_cleared
UNION ALL SELECT * FROM invoice_cancelled
)
SELECT
invoice AS "Invoice",
activity_name AS "ActivityName",
event_time AS "EventTime",
'SAP S/4HANA' AS "SourceSystem",
(SELECT extraction_ts FROM params) AS "LastDataUpdate",
user_name AS "UserName",
company_code AS "CompanyCode",
vendor_name AS "VendorName",
invoice_amount AS "InvoiceAmount",
purchase_order_number AS "PurchaseOrderNumber",
invoice_due_date AS "InvoiceDueDate"
FROM all_events
WHERE invoice IS NOT NULL
AND event_time IS NOT NULL
ORDER BY "Invoice", "EventTime", "ActivityName"; 단계
- 요건 정의 및 설계: 코딩 전에 업무 분석가와 협의해 필수 활동 15개의 정확한 트리거 조건과 데이터 필드를 확인합니다. 범위에 포함할 관련 SAP 테이블, 문서 유형(예: 'KR', 'RE'), 회사 코드를 파악합니다.
- ABAP 프로그램 생성: 트랜잭션 코드 SE38을 사용해 ABAP Editor를 실행합니다. 예를 들어 Z_PM_AP_INVOICE_EXTRACT와 같은 새 실행 프로그램을 생성합니다. 설명이 포함된 제목을 지정하고 애플리케이션을 'Financial Accounting'으로 설정합니다.
- 선택 화면 정의: 프로그램에서 PARAMETERS와 SELECT-OPTIONS 키워드를 사용해 선택 화면을 정의합니다. 사용자가 송장 생성일 기준 추출 날짜 범위, 대상 Company Code(BUKRS), 관련 송장 Document Type(BLART)을 지정할 수 있도록 합니다. 애플리케이션 서버의 출력 파일 경로를 지정하는 매개변수도 추가합니다.
- 데이터 선언: 필수 및 권장 속성을 포함해 최종 이벤트 로그 형식(예: TY_EVENT_LOG)에 맞는 내부 테이블 구조를 정의합니다. BKPF, BSEG, RBKP, RSEG, CDHDR, CDPOS, REGUH 등 다양한 SAP 원천 테이블에서 선택한 데이터를 담을 내부 테이블도 선언합니다.
- 주요 데이터 선택: 사용자가 선택 화면에서 지정한 조건에 따라 RBKP(물류 송장)와 BKPF(재무 송장)에서 기본 송장 집합을 선택해 추출을 시작합니다. 후속 데이터 조회에 사용할 기본 송장 키를 내부 테이블에 저장합니다.
- 활동을 순차적으로 추출: 기본 집합의 각 송장에 대해 업무 활동별 타임스탬프와 세부 정보를 찾는 조회를 수행합니다. 예를 들어 지급 보류 변경은 CDHDR와 CDPOS에서, 지급 실행 데이터는 REGUH와 REGUP에서, 역분개 문서 세부 정보는 BKPF에서 조회합니다. 찾은 활동마다 최종 이벤트 로그 테이블에 새 레코드를 추가합니다.
- 계산 이벤트 로직: 테이블 필드에 직접 저장되지 않는 활동은 ABAP 로직으로 구현합니다. 'Invoice Due Date Passed' 이벤트에는 송장 만기일(BSEG-ZFBDT + 지급 조건)과 반제일(BSEG-AUGDT)을 사용합니다. 반제일이 만기일보다 늦으면 만기일을 타임스탬프로 설정한 새 이벤트 레코드를 생성합니다.
- 데이터 변환 및 보강: 활동 데이터를 수집하면서 필수 속성을 모두 채웁니다. LFA1에서 공급업체 이름을 조회하고, 날짜와 시간을 하나의 타임스탬프 문자열로 변환(CONCATENATE...INTO...)하며, SourceSystem 값을 설정합니다.
- 출력 파일 생성: 모든 송장과 해당 활동을 처리해 최종 내부 테이블에 모은 뒤 OPEN DATASET, LOOP AT ... TRANSFER, CLOSE DATASET 문을 사용해 선택 화면에서 지정한 애플리케이션 서버 경로에 파일을 씁니다.
- 다운로드 및 업로드 준비: 트랜잭션 코드 CG3Y를 사용해 생성된 파일을 애플리케이션 서버에서 로컬 컴퓨터로 다운로드합니다. 파일을 UTF-8 인코딩 CSV 형식으로 저장합니다. process mining 도구에 업로드하기 전에 열 이름이 필수 속성(Invoice, ActivityName, EventTime 등)과 일치하는지 확인합니다.
구성
- 날짜 범위: 송장 생성일(BKPF-CPUDT 또는 RBKP-CPUDT)에 P_CPUDT 선택 옵션을 설정합니다. 초기 분석에는 6~12개월 데이터를 권장합니다.
- 회사 코드(P_BUKRS): 특정 회사 코드를 필터링하는 데 필요한 SELECT-OPTIONS 매개변수입니다. 꼭 필요한 경우가 아니라면 모든 회사 코드를 한 번에 처리하지 않는 것이 좋습니다.
- 송장 문서 유형(P_BLART): 관련 송장 문서 유형을 필터링하는 SELECT-OPTIONS 매개변수입니다. 일반적인 유형으로는 'KR'(공급업체 송장), 'KG'(공급업체 대변 메모), 'RE'(물류 송장 검증)가 있습니다.
- 실행 모드: 데이터가 많으면 포그라운드 대화형 처리에서 시간 초과가 발생하지 않도록 프로그램을 백그라운드 작업(SM36/SM37)으로 실행합니다. 사용량이 적은 시간대에 예약합니다.
- 출력 파일 경로: SAP 애플리케이션 서버의 파일 경로와 이름(예: /tmp/ 디렉터리)을 지정하는 PARAMETER입니다. 다운로드 전에 이 위치에 파일이 기록됩니다.
- 사전 요건: 보고서를 실행하는 사용자에게 FI, CO, MM 테이블(BKPF, BSEG, RBKP, RSEG, LFA1), 변경 문서 테이블(CDHDR, CDPOS), 워크플로 테이블을 읽을 권한이 있어야 합니다. 애플리케이션 서버에 파일을 쓰려면 권한 객체 S_DATASET도 필요합니다.
샘플 쿼리 abap
*&---------------------------------------------------------------------*
*& Report Z_PM_AP_INVOICE_EXTRACT
*&---------------------------------------------------------------------*
*& This report extracts Accounts Payable invoice lifecycle events for
*& process mining analysis.
*&---------------------------------------------------------------------*
REPORT z_pm_ap_invoice_extract.
*&---------------------------------------------------------------------*
*& Data Structures
*&---------------------------------------------------------------------*
TYPES: BEGIN OF ty_event_log,
invoice TYPE belnr_d,
activityname TYPE string,
eventtime TYPE string,
sourcesystem TYPE logsys,
lastdataupdate TYPE string,
username TYPE uname,
companycode TYPE bukrs,
vendorname TYPE name1_gp,
invoiceamount TYPE wrbtr,
purchaseordernumber TYPE ebeln,
invoiceduedate TYPE d,
END OF ty_event_log.
DATA: gt_event_log TYPE TABLE OF ty_event_log.
DATA: gv_system_id TYPE logsys.
DATA: gv_last_update TYPE string.
*&---------------------------------------------------------------------*
*& Selection Screen
*&---------------------------------------------------------------------*
SELECT-OPTIONS: s_bukrs FOR bkpf-bukrs OBLIGATORY,
s_cpudt FOR bkpf-cpudt OBLIGATORY DEFAULT sy-datum,
s_blart FOR bkpf-blart.
PARAMETERS: p_fpath TYPE string OBLIGATORY DEFAULT '/tmp/ap_extract.csv'.
*&---------------------------------------------------------------------*
*& Main Processing Block
*&---------------------------------------------------------------------*
START-OF-SELECTION.
" Get System ID and Update Timestamp
CALL FUNCTION 'OWN_LOGICAL_SYSTEM_GET'
IMPORTING
own_logical_system = gv_system_id
EXCEPTIONS
own_logical_system_not_defined = 1
OTHERS = 2.
CONCATENATE sy-datum sy-uzeit INTO gv_last_update.
" Internal tables for SAP data
DATA: lt_bkpf TYPE TABLE OF bkpf,
lt_rbkp TYPE TABLE OF rbkp.
" Select base documents
SELECT * FROM bkpf INTO TABLE lt_bkpf
WHERE bukrs IN s_bukrs
AND cpudt IN s_cpudt
AND blart IN s_blart
AND ( blart = 'KR' OR blart = 'KG' ). " Example FI Invoice Types
SELECT * FROM rbkp INTO TABLE lt_rbkp
WHERE bukrs IN s_bukrs
AND cpudt IN s_cpudt
AND blart IN s_blart
AND blart = 'RE'. " Example MM Invoice Type
" --- Process each invoice document ---
LOOP AT lt_bkpf ASSIGNING FIELD-SYMBOL(<fs_bkpf>).
PERFORM process_invoice USING <fs_bkpf>.
ENDLOOP.
LOOP AT lt_rbkp ASSIGNING FIELD-SYMBOL(<fs_rbkp>).
PERFORM process_mm_invoice USING <fs_rbkp>.
ENDLOOP.
" Write output to file
PERFORM write_output_file.
*&---------------------------------------------------------------------*
*& Form PROCESS_INVOICE (Handles FI Invoices)
*&---------------------------------------------------------------------*
FORM process_invoice USING iv_bkpf TYPE bkpf.
DATA: ls_bseg TYPE bseg,
ls_lfa1 TYPE lfa1,
ld_due_date TYPE d.
DATA: ls_event TYPE ty_event_log.
" Get Vendor and other details from first line item
SELECT SINGLE * FROM bseg INTO ls_bseg
WHERE bukrs = iv_bkpf-bukrs
AND belnr = iv_bkpf-belnr
AND gjahr = iv_bkpf-gjahr
AND koart = 'K'.
IF sy-subrc = 0.
SELECT SINGLE name1 FROM lfa1 INTO ls_lfa1-name1 WHERE lifnr = ls_bseg-lifnr.
CALL FUNCTION 'DETERMINE_DUE_DATE'
EXPORTING
i_zfbdt = ls_bseg-zfbdt
i_zbd1t = ls_bseg-zbd1t
i_zbd2t = ls_bseg-zbd2t
i_zbd3t = ls_bseg-zbd3t
i_zbd1p = ls_bseg-zbd1p
i_zbd2p = ls_bseg-zbd2p
i_zterm = ls_bseg-zterm
IMPORTING
e_faedt = ld_due_date.
ENDIF.
" Helper function to populate common fields
MACRO set_common_fields.
ls_event-invoice = iv_bkpf-belnr.
ls_event-sourcesystem = gv_system_id.
ls_event-lastdataupdate = gv_last_update.
ls_event-companycode = iv_bkpf-bukrs.
ls_event-vendorname = ls_lfa1-name1.
ls_event-invoiceduedate = ld_due_date.
SELECT SINGLE wrbtr FROM bseg INTO ls_event-invoiceamount WHERE belnr = iv_bkpf-belnr AND gjahr = iv_bkpf-gjahr AND koart = 'K'.
ENDMACRO.
" 1. Invoice Received
CLEAR ls_event.
set_common_fields.
ls_event-activityname = 'Invoice Received'.
CONCATENATE iv_bkpf-cpudt iv_bkpf-cputm INTO ls_event-eventtime.
ls_event-username = iv_bkpf-usnam.
APPEND ls_event TO gt_event_log.
" 2. Invoice Parked (if document was created as parked)
IF iv_bkpf-bstat = 'V'.
CLEAR ls_event.
set_common_fields.
ls_event-activityname = 'Invoice Parked'.
CONCATENATE iv_bkpf-cpudt iv_bkpf-cputm INTO ls_event-eventtime.
ls_event-username = iv_bkpf-usnam.
APPEND ls_event TO gt_event_log.
ENDIF.
" 10. Invoice Posted (For non-parked, same as received. For parked, this needs CDHDR/CDPOS logic not shown for brevity)
IF iv_bkpf-bstat <> 'V'.
CLEAR ls_event.
set_common_fields.
ls_event-activityname = 'Invoice Posted'.
CONCATENATE iv_bkpf-budat iv_bkpf-cputm INTO ls_event-eventtime. " Using posting date
ls_event-username = iv_bkpf-usnam.
APPEND ls_event TO gt_event_log.
ENDIF.
" 5. & 7. Invoice Blocked / Discrepancy Resolved from Change Docs
DATA: lt_cdhdr TYPE TABLE OF cdhdr, lt_cdpos TYPE TABLE OF cdpos.
DATA(ld_objectkey) = |{ iv_bkpf-bukrs }{ iv_bkpf-belnr }{ iv_bkpf-gjahr }|.
SELECT * FROM cdhdr INTO TABLE lt_cdhdr WHERE objectclas = 'BELEG' AND objectid = ld_objectkey.
IF sy-subrc = 0.
SELECT * FROM cdpos INTO TABLE lt_cdpos FOR ALL ENTRIES IN lt_cdhdr
WHERE changenr = lt_cdhdr-changenr AND tabname = 'BSEG' AND fname = 'ZLSPR'.
LOOP AT lt_cdpos ASSIGNING FIELD-SYMBOL(<fs_cdpos>).
READ TABLE lt_cdhdr ASSIGNING FIELD-SYMBOL(<fs_cdhdr>) WITH KEY changenr = <fs_cdpos>-changenr.
IF sy-subrc = 0.
CLEAR ls_event.
set_common_fields.
IF <fs_cdpos>-value_new IS NOT INITIAL AND <fs_cdpos>-value_old IS INITIAL.
ls_event-activityname = 'Invoice Blocked For Payment'.
ELSEIF <fs_cdpos>-value_new IS INITIAL AND <fs_cdpos>-value_old IS NOT INITIAL.
ls_event-activityname = 'Discrepancy Resolved'.
ELSE.
CONTINUE.
ENDIF.
CONCATENATE <fs_cdhdr>-udate <fs_cdhdr>-utime INTO ls_event-eventtime.
ls_event-username = <fs_cdhdr>-username.
APPEND ls_event TO gt_event_log.
ENDIF.
ENDLOOP.
ENDIF.
" 6. 8. 9. Workflow Events (Routed, Approved, Rejected) - Simplified Example
" This requires knowledge of specific workflow templates. Placeholder logic:
" SELECT ... FROM SWW_WI2OBJ ... WHERE INSTID = [Invoice Object]
" SELECT ... FROM SWWWIHEAD ... to get status and times
" 11. & 12. & 14. Payment Proposal, Executed, Cleared
IF ls_bseg-augbl IS NOT INITIAL.
DATA: ls_regup TYPE regup.
SELECT SINGLE * FROM regup INTO ls_regup WHERE vblnr = ls_bseg-belnr.
IF sy-subrc = 0.
DATA(ld_rundate) = ls_regup-laufd.
CLEAR ls_event.
set_common_fields.
ls_event-activityname = 'Payment Proposal Created'.
CONCATENATE ld_rundate '000000' INTO ls_event-eventtime.
APPEND ls_event TO gt_event_log.
ENDIF.
CLEAR ls_event.
set_common_fields.
ls_event-activityname = 'Payment Executed'.
CONCATENATE ls_bseg-augdt '120000' INTO ls_event-eventtime. " Using clearing date as proxy
APPEND ls_event TO gt_event_log.
CLEAR ls_event.
set_common_fields.
ls_event-activityname = 'Payment Cleared'.
CONCATENATE ls_bseg-augdt '120001' INTO ls_event-eventtime.
APPEND ls_event TO gt_event_log.
ENDIF.
" 13. Invoice Due Date Passed (Calculated)
IF ls_bseg-augdt IS NOT INITIAL AND ld_due_date IS NOT INITIAL.
IF ls_bseg-augdt > ld_due_date.
CLEAR ls_event.
set_common_fields.
ls_event-activityname = 'Invoice Due Date Passed'.
CONCATENATE ld_due_date '235959' INTO ls_event-eventtime.
APPEND ls_event TO gt_event_log.
ENDIF.
ENDIF.
" 15. Invoice Cancelled
IF iv_bkpf-stblg IS NOT INITIAL.
DATA: ls_rev_bkpf TYPE bkpf.
SELECT SINGLE * FROM bkpf INTO ls_rev_bkpf WHERE belnr = iv_bkpf-stblg.
IF sy-subrc = 0.
CLEAR ls_event.
set_common_fields.
ls_event-activityname = 'Invoice Cancelled'.
CONCATENATE ls_rev_bkpf-cpudt ls_rev_bkpf-cputm INTO ls_event-eventtime.
ls_event-username = ls_rev_bkpf-usnam.
APPEND ls_event TO gt_event_log.
ENDIF.
ENDIF.
ENDFORM.
*&---------------------------------------------------------------------*
*& Form PROCESS_MM_INVOICE (Handles MM/Logistics Invoices)
*&---------------------------------------------------------------------*
FORM process_mm_invoice USING iv_rbkp TYPE rbkp.
" This form would be similar to PROCESS_INVOICE, but starts with RBKP.
" It needs to find the corresponding FI document in BKPF via AWKEY.
" The logic for PO/GR Matched would be included here.
" For demonstration, creating placeholder events for MM-specific activities.
DATA: ls_event TYPE ty_event_log.
ls_event-invoice = iv_rbkp-belnr.
ls_event-sourcesystem = gv_system_id.
ls_event-lastdataupdate = gv_last_update.
ls_event-companycode = iv_rbkp-bukrs.
" 1. Invoice Received (MM)
ls_event-activityname = 'Invoice Received'.
CONCATENATE iv_rbkp-cpudt iv_rbkp-cputm INTO ls_event-eventtime.
ls_event-username = iv_rbkp-usnam.
APPEND ls_event TO gt_event_log.
" 3. Purchase Order Matched (Implicit)
ls_event-activityname = 'Purchase Order Matched'.
CONCATENATE iv_rbkp-cpudt iv_rbkp-cputm INTO ls_event-eventtime.
ls_event-username = iv_rbkp-usnam.
APPEND ls_event TO gt_event_log.
" 4. Goods Receipt Matched (Implicit)
ls_event-activityname = 'Goods Receipt Matched'.
CONCATENATE iv_rbkp-cpudt iv_rbkp-cputm INTO ls_event-eventtime.
ls_event-username = iv_rbkp-usnam.
APPEND ls_event TO gt_event_log.
" NOTE: The rest of the events (Block, Pay, etc.) would be found by linking
" RBKP to BKPF and then reusing the logic from PROCESS_INVOICE.
" Link: BKPF-AWKEY = CONCATENATE( RBKP-BELNR, RBKP-GJAHR ).
ENDFORM.
*&---------------------------------------------------------------------*
*& Form WRITE_OUTPUT_FILE
*&---------------------------------------------------------------------*
FORM write_output_file.
DATA: lv_string TYPE string.
OPEN DATASET p_fpath FOR OUTPUT IN TEXT MODE ENCODING UTF-8.
IF sy-subrc <> 0.
MESSAGE 'Error opening file.' TYPE 'E'.
RETURN.
ENDIF.
" Write Header
lv_string = 'Invoice,ActivityName,EventTime,SourceSystem,LastDataUpdate,UserName,CompanyCode,VendorName,InvoiceAmount,PurchaseOrderNumber,InvoiceDueDate'.
TRANSFER lv_string TO p_fpath.
" Write Data
LOOP AT gt_event_log ASSIGNING FIELD-SYMBOL(<fs_event>).
" Create a comma-separated string, handling potential commas in data
CONCATENATE <fs_event>-invoice
<fs_event>-activityname
<fs_event>-eventtime
<fs_event>-sourcesystem
<fs_event>-lastdataupdate
<fs_event>-username
<fs_event>-companycode
<fs_event>-vendorname
<fs_event>-invoiceamount
<fs_event>-purchaseordernumber
<fs_event>-invoiceduedate
INTO lv_string SEPARATED BY ','.
TRANSFER lv_string TO p_fpath.
ENDLOOP.
CLOSE DATASET p_fpath.
WRITE: / 'Extraction complete. File written to:', p_fpath.
ENDFORM. 시작할 준비가 되셨나요?
이 템플릿으로 process mining을 시작하고 매입채무 운영의 효율을 높여 보세요. 지금 프로세스를 더 효율적이고 간결하게 개선하는 여정을 시작할 수 있습니다.
연체료를 줄이세요. 지금 매입채무 송장 처리를 개선하세요
처리 비용을 60% 줄이고 중복 지급을 간편하게 방지하세요.
신용카드 없이 몇 분 만에 시작할 수 있습니다.