您的应付账款发票处理数据模板
您的应付账款发票处理数据模板
- 全面分析所需的推荐属性
- 有效跟踪流程所需的关键活动
- 分步数据提取指南
应付账款发票处理属性
| 名称 | 说明 | ||
|---|---|---|---|
| 事件时间 EventTime | 活动发生的准确日期和时间。 | ||
| 说明 事件时间是与每项活动关联的时间戳,用于表示发票事件的时间顺序。这些数据对于理解流程和执行任何基于时间的分析都至关重要。 在分析中,事件时间用于正确排列活动、计算不同步骤之间的周期时间、识别等待时间,并分析不同时间段的流程绩效,例如逐月比较。它是所有基于持续时间的KPI的基础。 为什么重要 此时间戳对于按时间顺序排列事件以及计算周期时间和持续时间等所有时间指标至关重要,而这些指标是流程挖掘的基础。 获取位置 来源于SAP各表中的不同日期和时间字段,例如创建日期(BKPF-CPUDT)、过账日期(BKPF-BUDAT)、结算日期(BSAK-AUGDT)或更改日志时间戳(CDHDR-UDATE/UTIME)。 示例 2023-10-01T09:00:00Z2023-10-05T14:30:15Z2023-10-15T11:21:05Z | |||
| 发票 Invoice | 发票凭证的唯一标识符,作为应付账款流程的主要案例ID。 | ||
| 说明 发票是连接从接收至付款所有相关活动的核心对象。在SAP S/4HANA中,它通常由公司代码(BUKRS)、唯一凭证编号(BELNR)和会计年度(GJAHR)组成复合键。 按发票分析可以完整查看发票生命周期。这是计算总周期时间、识别单张发票的瓶颈以及了解发票在流程中可能采取的不同路径的基础。 为什么重要 它唯一标识每张发票的处理过程,从而可以追踪完整生命周期,并逐案分析流程绩效。 获取位置 这是根据BKPF(会计凭证抬头)或RBKP(凭证抬头:发票接收)表中的BUKRS、BELNR和GJAHR字段生成的复合键。 示例 1000-1900000001-20231710-1900000002-20232000-5100000003-2024 | |||
| 活动 ActivityName | 发票处理生命周期中发生的特定业务事件或步骤的名称。 | ||
| 说明 活动表示应付账款流程中的一个独立阶段或操作,例如“已收到发票”“发票已过账”或“付款已执行”。这些活动是流程图的基本组成部分。 活动分析是流程挖掘的核心。它有助于可视化流程、识别常见路径、发现与标准流程的偏差,并衡量每个步骤的频率和持续时间。对于特定发票而言,这些活动的顺序构成其流程历程。 为什么重要 它定义流程步骤,用于可视化和分析流程、识别瓶颈以及发现返工循环。 获取位置 来源包括凭证状态变更(例如BKPF-BSTAT)、更改凭证(CDHDR/CDPOS表)或工作流日志,通常需要自定义提取逻辑。 示例 收到发票发票已审批已执行付款发票付款冻结 | |||
| 上次数据更新 LastDataUpdate | 表示该记录数据最近一次从源系统刷新时间的时间戳。 | ||
| 说明 此属性提供最近一次从SAP S/4HANA提取或更新数据的日期和时间。它是重要的元数据字段,有助于了解当前分析数据的新鲜度。 用户需要通过此信息了解流程分析的时效性。它有助于管理对数据延迟的预期,也对安排数据刷新和维护数据完整性至关重要。 为什么重要 反映数据的新鲜度,确保用户了解流程分析数据的最新程度。 获取位置 该值在从源系统提取数据时生成,并写入每条记录。 示例 2024-05-20T04:00:00Z2024-05-21T04:00:00Z | |||
| 源系统 SourceSystem | 提取数据的系统。 | ||
| 说明 此属性标识流程数据的来源。在此视图中,该值通常为“SAP S/4HANA”。 在包含多个ERP或集成系统的环境中,此字段对于数据血缘和数据隔离至关重要。它确保分析使用正确的数据集,并通过追溯来源帮助诊断数据质量问题。 为什么重要 标识数据来源,对于数据治理、故障排查和多系统环境至关重要。 获取位置 通常是在数据提取过程中添加的静态值,用于标记数据集来源。 示例 SAP S/4HANASAP ECC 6.0S4H_PROD_100 | |||
| 供应商名称 VendorName | 提交发票的供应商名称。 | ||
| 说明 此属性包含供应商的正式名称,通过发票凭证中的供应商编号关联。 供应商分析对于管理供应商关系和识别特定供应商的流程问题至关重要。它可以帮助回答“哪些供应商提交的发票差异最多?”或“我们是否始终按时向某些战略供应商付款?”等问题。该字段与发票编号和金额一样,也是识别潜在重复付款的关键。 为什么重要 支持按供应商分析流程绩效,帮助识别问题供应商并有效管理战略供应商关系。 获取位置 通过BKPF或RBKP中的供应商编号(LIFNR)关联,从供应商主数据表LFA1的NAME1字段获取。 示例 Office Supplies Inc.Global Consulting GroupMachine Parts GmbH | |||
| 公司代码 CompanyCode | 处理发票的组织单位。 | ||
| 说明 公司代码是能够编制完整、独立账户并进行外部报告的最小组织单位。在应付账款场景中,它代表欠供应商款项的法人实体。 按公司代码分析可以比较组织内不同法人实体的流程绩效,帮助识别哪些业务遵循标准流程,以及哪些业务效率较低、周期时间较长或返工率较高。 为什么重要 支持比较不同法人实体的流程绩效,帮助识别区域或业务单元特有的问题和最佳实践。 获取位置 位于凭证抬头表中,主要包括FI发票的BKPF-BUKRS和MM发票的RBKP-BUKRS。 示例 10001710US01DE01 | |||
| 发票到期日 InvoiceDueDate | 供应商应收到发票付款的日期。 | ||
| 说明 发票到期日是向供应商付款的截止日期。按时付款可以避免滞纳金并维护良好的供应商关系。该日期根据发票基准日期和与供应商约定的付款条件计算。 此日期对于“Payment Compliance & Aging”仪表板和“On-Time Payment Rate”KPI至关重要。将到期日与实际付款日期比较,可以分析付款是否按时、提前或延迟完成,并了解其直接的财务和关系影响。 为什么重要 这是按时付款分析的主要依据,可衡量付款绩效及其对供应商关系和滞纳金的影响。 获取位置 该日期通常通过计算得出。净到期日在BSEG-NETDT字段中,也可以根据付款基准日期BSEG-ZFBDT和付款条件BSEG-ZTERM推导。 示例 2023-10-312023-11-152024-01-10 | |||
| 发票金额 InvoiceAmount | 按原始凭证币种计算的发票总含税金额。 | ||
| 说明 这是供应商提交的发票总金额,包括商品或服务成本、税费及其他费用,未扣除任何减项或折扣。 发票金额是多种分析中使用的关键财务属性。它有助于优先处理高价值发票、了解流程延误的财务影响,例如大额发票产生的滞纳金,并对流程进行分段,例如“高价值发票是否遵循不同的审批路径?”。它对于识别潜在重复付款也至关重要。 为什么重要 为流程分析提供财务背景,支持基于金额的分析、高价值发票优先级排序以及财务影响量化。 获取位置 MM发票通常位于RBKP-RMWWR(发票总金额)等字段中;FI发票则根据BSEG行项目的WRBTR字段计算。 示例 1500.00250.7512345.50 | |||
| 用户名 UserName | 执行活动人员的用户ID。 | ||
| 说明 此属性记录负责执行特定活动的SAP用户ID,例如过账、审批或结清发票,并将流程步骤关联到具体用户。 按用户名分析对于了解工作量分配、识别高绩效人员以及定位可能需要额外培训的用户至关重要。在仪表板中分析审批瓶颈时,该属性也很关键,因为它有助于识别导致流程延误的具体审批人。 为什么重要 将活动归属到具体人员,从而支持用户绩效、工作量以及职责分离政策合规性的分析。 获取位置 通常位于BKPF-USNAM(录入人)等抬头表字段中,或位于更改凭证表CDHDR-USERNAME(更改人)中。 示例 ABROWNJSMITHAP_AUTOMATION | |||
| 采购订单编号 PurchaseOrderNumber | 与发票关联的采购订单唯一标识符,如适用。 | ||
| 说明 此属性将发票关联到预先批准的采购订单(PO)。PO编号的存在是三方匹配流程(PO、发票、收货记录)的基础。 这是合规和效率分析的重要属性,用于计算“PO-Less Invoice Percentage”KPI,衡量采购政策的遵循情况,也是“3-Way Matching Performance”仪表板的基础,可用于分析有PO支持的发票匹配流程。 为什么重要 通过识别无PO处理的发票,对于分析三方匹配效率和衡量采购政策合规性至关重要。 获取位置 位于发票行项目表中,例如MM发票的RSEG-EBELN或FI发票的BSEG-EBELN。 示例 45000012344500005678 | |||
| 付款条件 PaymentTerms | 与供应商约定的发票付款条件,通常包括折扣机会。 | ||
| 说明 付款条件规定付款到期日和提前付款折扣的规则。例如,Z001可能表示“30天内付款,10天内付款可享受2%折扣”。 此属性是“Early Payment Discount Capture Rate”仪表板的基础。通过分析付款条件,可以识别所有符合折扣条件的发票。将其与实际获得的折扣进行比较,可以发现错失的节省机会并衡量付款流程效率。 为什么重要 对于分析提前付款折扣机会、衡量付款流程的财务绩效以及识别错失的节省至关重要。 获取位置 位于供应商行项目表BSEG-ZTERM中,或位于发票抬头RBKP-ZTERM中。 示例 Z0010001NT30 | |||
| 供应商发票编号 VendorInvoiceNumber | 供应商在其单据上提供的发票编号。 | ||
| 说明 这是供应商自有会计系统中的参考编号,印在纸质或电子发票上。收到发票时,该编号由人工录入或通过OCR识别。 该字段对运营管理和分析都非常重要,尤其适用于“潜在重复发票付款”仪表板。检测重复发票的一种常见方法,是查找供应商名称、供应商发票编号和发票金额均相同的多个内部发票单据。它是发票最主要的外部参考编号。 为什么重要 这是识别潜在重复付款的关键字段,也是与供应商沟通时最主要的外部参考编号。 获取位置 存储在单据抬头的“参考”字段中,通常对应BKPF-XBLNR。 示例 INV-2023-9876733401120231015-001 | |||
| 冻结原因 BlockingReason | 发票被付款冻结的原因,表示存在差异。 | ||
| 说明 当发票在三方匹配或其他校验步骤中未通过验证时,会被付款冻结。冻结原因说明问题性质,例如数量差异、价格偏差或缺少收货记录。 此属性对于“Invoice Discrepancy Rework Analysis”仪表板至关重要。分析不同冻结原因的频率,有助于识别流程低效的根本原因。例如,如果“价格偏差”经常出现,可能说明采购系统中的主数据存在问题。 为什么重要 直接揭示发票差异和返工的根本原因,支持有针对性的流程改进。 获取位置 存储在RSEG等发票行项目表中,字段名以SPGR*开头,例如SPGRP、SPGRQ、SPGRT。也可在RBKP_BLOCKED中找到。 示例 价格差异数量差异缺少收货凭证 | |||
| 发票凭证类型 InvoiceDocumentType | 发票凭证的分类,用于控制其在SAP中的处理方式。 | ||
| 说明 凭证类型是SAP中的关键配置要素,用于对会计凭证进行分类。例如,“KR”通常用于供应商发票,“RE”用于MM发票,“KG”用于供应商贷项通知单。该类型决定编号范围以及必填字段等内容。 在流程分析中,按凭证类型筛选可以比较不同发票类型的流程。例如,贷项通知单的审批流程可能不同于标准发票。这对“发票审批路由变体”仪表板很有帮助。 为什么重要 支持根据不同发票类型的处理方式对流程进行分段,揭示处理路径和周期时间的差异。 获取位置 直接来自凭证抬头表中的BKPF-BLART字段。 示例 KRREKG | |||
| 发票币种 InvoiceCurrency | 发票金额所使用的货币代码,例如USD、EUR。 | ||
| 说明 该属性说明发票金额采用的币种,为所有财务数值提供必要背景。 对于跨国组织,如果不考虑币种,发票分析可能产生误导。借助该字段,您可以将所有金额换算为统一的报告币种,或按币种拆分分析,以了解各地区的财务活动。 为什么重要 为发票金额提供必要背景,支持准确的财务分析和报告,尤其适用于跨国场景。 获取位置 可在单据抬头表中找到,主要对应BKPF-WAERS或RBKP-WAERS。 示例 USDEURGBPJPY | |||
| 已享受折扣 DiscountTaken | 表示是否成功应用提前付款折扣的布尔标记。 | ||
| 说明 该属性表示发票付款时是否实际享受了现金折扣,是衡量应付账款流程财务效率的重要指标。 该标记是“提前付款折扣获取率”KPI的核心。先根据付款条款筛选出具备折扣条件的发票,再分析该标记,企业即可准确计算节省的金额以及错失的节省机会,从而清晰、量化地评估应付账款绩效。 为什么重要 直接衡量可用提前付款折扣的获取成效,并直接影响公司的利润。 获取位置 通过检查付款单据中的折扣金额字段BSEG-SKNTO是否大于零得出。 示例 truefalse | |||
| 是否自动执行 IsAutomated | 用于标识活动是否由系统自动执行,而非由人工用户执行的标志。 | ||
| 说明 此布尔属性用于区分人工发起的活动与系统作业、工作流或机器人执行的活动。例如,自动付款运行或系统生成的发票过账会标记为自动执行。 分析此属性有助于了解应付账款流程的自动化程度,可用于衡量自动化计划的成效、比较自动步骤与人工步骤的效率,并识别进一步的自动化机会。 为什么重要 帮助衡量流程自动化程度,支持分析自动化效果并识别进一步改进机会。 获取位置 根据用户名(例如SAP_SYSTEM或BATCHUSER等系统用户ID)或与自动化作业关联的特定事务代码推导。 示例 truefalse | |||
| 是否逾期付款 IsLatePayment | 表示发票是否在到期日之后付款的布尔标记。 | ||
| 说明 该计算属性是一个简单的真/假标记,用于表示发票是否晚于正式到期日付款。它通过比较“清账日期”和“发票到期日”得出。 该标记简化了“付款合规与账龄”仪表板及“按时付款率”KPI的分析。您可以据此轻松筛选和汇总数据,统计逾期付款数量、计算按时付款比例,并识别逾期付款率较高的供应商或公司代码。 为什么重要 直接衡量付款条款的合规情况,简化按时付款KPI的计算,并帮助识别付款绩效较差的环节。 获取位置 计算属性。逻辑为:IF ClearingDate > InvoiceDueDate THEN true ELSE false。 示例 truefalse | |||
| 清账日期 ClearingDate | 付款完成且发票从未清项目中清账的日期。 | ||
| 说明 清账日期表示发票已完成财务结算,即“付款清账”活动发生的日期,通常也是发票流程成功完成的最后一步。 该日期用于计算实际付款日期,并与发票到期日进行比较。因此,它是计算“按时付款率”KPI及开展付款绩效分析的基础,也标志着端到端发票周期时间计算的终点。 为什么重要 标志着发票最终结算完成,是计算周期时间和分析按时付款情况的依据。 获取位置 可在已清账项目表中找到,例如供应商对应的BSAK-AUGDT。 示例 2023-10-282023-11-142024-01-09 | |||
应付账款发票处理活动
| 活动 | 说明 | ||
|---|---|---|---|
| 付款已结清 | 此活动标志着发票最终关闭,付款与发票在明细账中完成核销,表示流程结束。 | ||
| 为什么重要 作为流程的明确终点,此活动对于准确计算端到端周期时间至关重要,并确认负债已结清。 获取位置 当发票凭证供应商行项目(表BSEG)的结算日期字段AUGDT被填入时,记录此明确事件。 采集 使用发票行项目中的结算日期BSEG-AUGDT。 事件类型 explicit | |||
| 发票付款冻结 | 系统自动或人工对发票设置了冻结,导致发票无法付款。通常原因包括价格或数量差异,以及缺少审批。 | ||
| 为什么重要 这是问题和返工的重要指标。分析冻结原因及持续时间,有助于识别付款延误和流程低效的根本原因。 获取位置 这是会计凭证表BSEG中供应商行项目付款冻结码字段(ZLSPR)记录的明确状态。 采集 当BSEG-ZLSPR字段填入冻结原因时,通过更改凭证记录。 事件类型 explicit | |||
| 发票已取消 | 发票凭证已冲销,其财务影响被有效抵消。这是流程的另一种终态,通常由错误录入或供应商争议导致。 | ||
| 为什么重要 跟踪取消情况有助于识别流程失败原因,例如重复提交或发票数据错误,并发现上游问题。 获取位置 创建冲销凭证时会明确记录此事件。原始凭证抬头BKPF中将填入冲销凭证编号STBLG和冲销原因。 采集 识别冲销凭证的过账日期,该凭证通过原始凭证抬头BKPF-STBLG关联。 事件类型 explicit | |||
| 发票已审批 | 发票已在工作流系统中获得所有必要审批。这通常是发票过账或解除付款冻结前的最后一步。 | ||
| 为什么重要 这一关键里程碑标志着审批周期结束。从提交审批到完成审批之间的时间是衡量效率的重要指标。 获取位置 从SAP Business Workflow日志中的完成或最终释放步骤获取。也可以根据提交审批后付款冻结被移除进行推断。 采集 从SAP工作流日志中提取工作流完成事件,或识别最终的“release”事件。 事件类型 explicit | |||
| 发票已过账 | 发票已正式记入总账,形成财务负债。暂存凭证转为已过账凭证,或直接完成过账。 | ||
| 为什么重要 这是重要的财务里程碑,确认公司承担付款义务,通常也是安排付款的前提。 获取位置 此事件通过凭证抬头BKPF中的过账日期BUDAT识别。已过账凭证的凭证状态BKPF-BSTAT为空白。 采集 对未暂存的凭证使用过账时间戳BKPF-BUDAT(BKPF-BSTAT为空白)。 事件类型 explicit | |||
| 已执行付款 | 发票已完成付款。当付款运行完成并创建、过账付款凭证时记录此事件。 | ||
| 为什么重要 此活动对于现金流分析至关重要,也可通过将该日期与发票到期日比较,衡量“On-Time Payment Rate”KPI。 获取位置 从结清发票的付款凭证过账日期获取。付款凭证编号通过发票行项目BSEG中的结算凭证字段AUGBL关联。 采集 识别结清发票行项目的付款凭证过账日期BUDAT。 事件类型 explicit | |||
| 收到发票 | 此活动表示发票凭证已在SAP中创建,可通过人工方式或OCR/VIM等自动化接口创建。该事件通常从会计凭证抬头的创建日期和时间中获取。 | ||
| 为什么重要 作为流程起点,此活动对于计算发票端到端周期时间和衡量整个应付账款流程的吞吐量至关重要。 获取位置 此事件从会计凭证抬头表BKPF中获取,使用凭证创建日期CPUDT和时间CPUTM。 采集 对发票凭证使用创建时间戳(BKPF-CPUDT、BKPF-CPUTM)。 事件类型 explicit | |||
| 发票已提交审批 | 发票已根据业务规则提交至工作流,等待必要审批。这标志着审批子流程开始。 | ||
| 为什么重要 此活动是衡量“Average Invoice Approval Time”KPI和分析审批瓶颈的起点。 获取位置 可从SAP Business Workflow日志(SWW*表)中获取,这些日志记录与发票对象(例如BUS2081)关联的工作流实例启动。 采集 从与发票凭证关联的SAP工作流日志中提取工作流启动事件,例如表SWW_WIHEAD。 事件类型 explicit | |||
| 发票已超过到期日 | 表示发票净到期日已过,但尚未完成付款结清的计算事件,说明付款已逾期。 | ||
| 为什么重要 此活动对于“Payment Compliance & Aging”仪表板至关重要,有助于主动识别和管理逾期发票,并分析延迟付款的根本原因。 获取位置 SAP中没有明确记录此事件。该事件通过将系统当前日期与净到期日比较计算得出,净到期日根据BSEG-ZFBDT或基准日期及付款条件计算。 采集 当事件时间戳晚于发票净到期日时触发的计算事件。 事件类型 calculated | |||
| 发票暂存 | 表示发票已录入系统,但尚未过账至总账。这通常是有意保留未完成凭证,以便后续处理或审批。 | ||
| 为什么重要 跟踪暂存发票有助于识别正式过账前的延误,也能发现数据不完整或初始校验方面的问题。 获取位置 此状态根据会计凭证抬头中的凭证状态字段推断(BKPF-BSTAT='V'表示暂存)。事件在状态设置时发生。 采集 识别表BKPF中的更改凭证,其中BSTAT字段设置为'V'(Vor-erfasst/预录入)。 事件类型 inferred | |||
| 发票被拒绝 | 审批人在审批工作流中拒绝了发票。此操作通常会将发票退回处理人员,以便更正或补充说明。 | ||
| 为什么重要 跟踪拒绝情况可以发现审批流程中的返工循环,也可能表明政策不合规或发票编码错误。 获取位置 此事件作为发票相关SAP Business Workflow日志中的特定结果事件记录。 采集 从SAP工作流日志中提取“rejected”状态事件。 事件类型 explicit | |||
| 差异已解决 | 此活动表示此前发现的、可能导致付款冻结的问题已完成调查并得到解决。当发票上的付款冻结被移除时记录此事件。 | ||
| 为什么重要 跟踪这一返工循环对于“Invoice Discrepancy Rework Analysis”仪表板至关重要,有助于量化纠错所耗费的时间和精力。 获取位置 根据显示付款冻结被移除的更改凭证进行推断。BSEG-ZLSPR字段的更改日志是主要数据来源。 采集 识别表BSEG中的更改凭证,其中ZLSPR字段由某个值变为空白。 事件类型 inferred | |||
| 已创建付款建议 | 发票已作为付款运行的一部分纳入付款建议,例如F110。当前已安排付款,等待付款运行最终执行。 | ||
| 为什么重要 此活动显示负债从未结状态转为正在准备付款的项目,有助于分析付款运营效率。 获取位置 此事件明确记录在付款运行数据表中,具体包括REGUP(付款程序处理项目)和REGUH(抬头)。 采集 识别发票何时出现在表REGUP中,并关联REGUH中确定的付款运行。 事件类型 explicit | |||
| 收货匹配 | 此活动表示发票数量和金额已成功匹配对应的收货凭证。这是三方匹配场景中的最终校验。 | ||
| 为什么重要 跟踪此活动有助于定位三方匹配流程中的低效环节,并识别实际收货与供应商开票内容之间的差异。 获取位置 根据发票行项目中是否存在物料凭证引用(收货)进行推断,该引用通常通过采购订单项目历史关联。 采集 根据发票行项目中是否存在收货凭证引用进行推断,例如MIRO发票中的RSEG。 事件类型 inferred | |||
| 采购订单匹配 | 此活动表示发票已成功匹配对应的采购订单。这是基于采购的发票执行三方匹配时的关键步骤。 | ||
| 为什么重要 分析此活动有助于衡量匹配流程的效率,也是“3-Way Matching Performance”和“PO-Less Invoice Percentage”KPI的基础。 获取位置 当表BSEG或ACDOCA中的发票行项目包含有效的采购订单号(EBELN)和项目号(EBELP)时,即可推断发生此事件。 采集 根据发票凭证创建时是否存在采购订单引用(BSEG-EBELN)进行推断。 事件类型 inferred | |||
提取指南
步骤
- 前提条件与访问权限:确保您拥有可读取SAP S/4HANA数据库架构的用户权限,通常为SAPABAP1或存放CDS视图的类似架构。您还需要可连接SAP HANA数据库的SQL客户端工具,例如SAP HANA Studio、DBeaver或其他类似数据库查询工具。
- 识别核心CDS视图:本次提取主要使用I_JournalEntry、I_JournalEntryItem、I_SupplierInvoiceAPI01、I_ChangeDocument、I_WorkflowStatusDetails和I_PaymentProposalItem。请熟悉这些视图中的关键字段。
- 定义查询范围:打开SQL客户端并连接SAP HANA数据库。在运行完整查询前,先定义提取范围,包括设置正确的源系统标识、发票日期范围(CreationDateTime)以及相关公司代码。
- 准备主查询:将查询部分提供的完整SQL查询复制到SQL客户端中。该查询使用公用表表达式(CTE),先筛选发票基础数据集,再合并15项不同活动的数据,构建事件日志。
- 设置查询参数:在复制的SQL查询中找到占位变量。将“[YYYY-MM-DD]”替换为分析期间的开始和结束日期,将“[Your Company Code 1]”和“[Your Company Code 2]”替换为您希望分析的SAP公司代码列表。
- 执行提取查询:运行完整SQL查询。根据数据量和所选日期范围,执行过程可能需要几分钟到数小时。
- 检查初步结果:查询完成后,检查输出结果的前几百行。确认数据一致性,确保所有列均按预期填充,并验证是否存在不同的ActivityName值。
- 导出事件日志:通过SQL客户端将完整结果集导出为CSV文件。请确保文件采用UTF-8编码,以避免字符显示问题。文件名可使用具有描述性的名称,例如sap_s4hana_ap_event_log.csv。
- 准备上传:上传到流程挖掘工具前,请确认CSV文件中的列标题与所需属性名称完全一致,例如Invoice、ActivityName、EventTime、SourceSystem、LastDataUpdate、UserName等。
- 上传到流程挖掘工具:将生成的CSV文件上传到您的流程挖掘平台,并将各列映射到对应的案例ID、活动和时间戳字段。
配置
- 关键CDS视图:提取依赖多个标准S/4HANA CDS视图的组合。主要视图包括:
- I_JournalEntry和I_JournalEntryItem:用于提取核心财务凭证抬头、项目、过账明细和清账信息。
- I_SupplierInvoiceAPI01:用于提取MM(物流)发票专属明细,包括采购订单引用和付款冻结。
- I_ChangeDocument:用于跟踪变更的精确时间戳,例如设置或移除付款冻结。
- I_WorkflowStatusDetails:用于提取与发票审批工作流相关的事件。
- I_PaymentProposalItem:用于识别发票何时被纳入付款运行建议。
- I_Supplier:用于补充供应商主数据,例如VendorName。
- 日期范围筛选:必须应用日期范围筛选,以限制数据量。提供的查询在Invoices_BaseCTE中按CreationDateTime进行筛选。初次分析建议使用3至6个月的范围,以确保性能可控。
- 必需筛选条件:始终按CompanyCode筛选。一次分析所有公司代码可能非常缓慢,也可能不符合业务需求。同时按JournalEntryType筛选,仅选择与供应商相关的凭证,例如“KR”和“RE”。
- 前提条件:执行查询的数据库用户必须拥有查询中所用全部CDS视图及底层HANA架构的SELECT授权。仅具备SAPGUI中的应用层访问权限并不足够。
- 性能注意事项:直接查询I_ChangeDocument可能消耗大量资源。提供的查询会先筛选发票,以降低资源消耗。对于超大数据集,可考虑在业务低峰期执行提取,或按较小日期范围分批运行。
a 示例查询 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]。初始使用三至六个月的日期范围,验证性能和完整性后再延长。
- 在已批准的 SAP HANA SQL 客户端中执行 SQL 查询,例如 SAP HANA Database Explorer 或其他授权的 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个月开始。根据所提取的活动,使用过账日期、凭证创建日期或相关来源事件日期。只有在验证查询运行时间和来源数据保留情况后,才扩大范围。
- 公司筛选:配置[公司代码筛选],将提取范围限制为所需公司代码。如无需限制,请使用受控的全公司选择,而不是在生产环境中执行不受限制的查询。
- 凭证筛选:确认目标系统中供应商发票、贷项凭证、停放凭证、付款凭证和冲销所使用的凭证类型后,再配置[凭证类型筛选]。
- 架构和对象映射:将[架构名称]以及每个方括号中的来源对象或字段替换为已在目标SAP S/4HANA系统中验证的值。直接HANA查询必须根据系统已激活的应用、扩展、工作流设计和数据模型进行调整。
- 时间戳处理:将所有来源时间戳统一为同一时区。如果来源仅提供日期,请使用有文档记录的默认时间,并在数据字典中记录这一限制。
- 事件语义:将每个活动提取为单独一行。不要将多个活动合并为一条发票记录,也不要期待ProcessMind推导匹配、审批、到期日或清账事件。
- 到期日事件:仅当发票到期日早于配置的评估时间戳,且截至该时间戳不存在清账事件时,才生成Invoice Due Date Passed。查询使用[评估时间戳]进行计算。
- 性能:限制初始时间段和公司范围,筛选与索引或分区相关的字段,避免不必要的宽表投影,并在获批准的报表窗口内执行。如果生产查询超过约定运行时间,请考虑先暂存来源数据子集。
- 刷新策略:将LastDataUpdate设置为提取执行时间戳。对于增量加载,请根据相关来源变更时间戳保留水位线,并加入回溯时间段,以捕获延迟更新和冲销。
- 前提条件:具备所需数据库授权和获批准的生产访问权限,了解系统的Universal Journal配置,能够访问已配置的停放凭证、采购、收货、工作流、付款、清账和冲销来源,并取得适用的SAP许可或治理审批。
a 示例查询 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编辑器。创建新的可执行程序,例如Z_PM_AP_INVOICE_EXTRACT。填写描述性标题,并将应用设置为“Financial Accounting”。
- 定义选择屏幕:在程序中使用PARAMETERS和SELECT-OPTIONS关键字定义选择屏幕,允许用户指定提取日期范围(发票创建日期)、目标公司代码(BUKRS)和相关发票凭证类型(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格式保存。上传至流程挖掘工具前,确认列标题与必需属性匹配(Invoice、ActivityName、EventTime等)。
配置
- 日期范围:定义P_CPUDT选择选项,用于筛选发票创建日期(BKPF-CPUDT或RBKP-CPUDT)。初次分析建议使用6至12个月的数据。
- 公司代码(P_BUKRS):用于筛选特定公司代码的必需SELECT-OPTIONS参数。除非确有必要,否则不建议一次处理所有公司代码。
- 发票凭证类型(P_BLART):用于筛选相关发票凭证类型的SELECT-OPTIONS参数。常见类型包括“KR”(供应商发票)、“KG”(供应商贷项凭证)和“RE”(物流发票校验)。
- 执行模式:对于大数据量,应将程序作为后台作业(SM36/SM37)执行,避免前台对话进程超时。请安排在业务低峰期运行。
- 输出文件路径:用于指定SAP应用服务器上的文件路径和文件名的PARAMETER(例如/tmp/目录)。文件将在下载前写入此处。
- 前提条件:执行报表的用户需要具备读取FI、CO和MM表(BKPF、BSEG、RBKP、RSEG、LFA1)、变更凭证表(CDHDR、CDPOS)及工作流表的权限。此外,还需要授权对象S_DATASET,以便向应用服务器写入文件。
a 示例查询 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. 告别滞纳金:立即优化应付账款发票处理
轻松将处理成本降低60%,消除重复付款。
无需信用卡,几分钟即可开始。