您的采购到付款(Purchase to Pay)-发票处理数据模板
您的采购到付款(Purchase to Pay)-发票处理数据模板
- 建议收集的属性
- 需要追踪的关键活动
- SAP S/4HANA 数据提取指南
采购到付款 - 发票处理属性
| 名称 | 描述 | ||
|---|---|---|---|
| 发票编号 InvoiceNumber | 供应商发票凭证的唯一标识符,作为流程的主要个案标识符。 | ||
| 描述 “发票编号”是 SAP S/4HANA 中分配给每个供应商发票的唯一标识符。它将创建、暂存、审批和付款等所有相关活动链接为一个内聚的流程实例。 在流程挖掘中,此属性是追踪每张发票端到端旅程的基础。它允许重建整个流程流(从收到发票到最终付款),从而在单张发票层面分析周期时间、瓶颈和流程变体。 为何重要 它是连接所有相关事件的关键标识,能够对发票在系统中的生命周期进行完整追踪。 获取方式 这是会计凭证编号,位于 BKPF 表的 BELNR 字段。 示例 190000000119000000451900000132 | |||
| Event 时间 EventTime | 活动发生的精确日期和时间。 | ||
| 描述 事件时间 (Event Time) 是记录特定活动发生的精确时间戳。这些数据对于计算流程中不同步骤之间的持续时间、周期时间和等待时间至关重要。 在流程挖掘分析中,准确的时间戳用于衡量绩效 KPI,如“平均发票周期时间”和“发票审批周期时间”。通过分析活动之间经过的时间,组织可以锁定发票延迟的瓶颈,并识别加速流程的机会。 为何重要 此时间戳是所有基于时间的分析的基础,包括性能监控、瓶颈识别和 SLA 追踪。 获取方式 通常源自更改凭证表 CDHDR(抬头)和 CDPOS(行项目),使用 UDATE 和 UTIME 字段。对于某些事件,它可能来自 BKPF(CPUDT、CPUTM)等表中的创建或输入日期。 示例 2023-04-15T10:30:00Z2023-04-18T14:05:21Z2023-05-02T09:00:00Z | |||
| 活动名称 ActivityName | 在特定时间点针对发票发生的业务活动或事件的名称。 | ||
| 描述 “活动名称”描述了发票处理生命周期中的特定步骤或状态更改。示例包括“发票凭证已创建”、“发票已发送审批”、“付款冻结已设置”和“付款已执行”。 此属性对于构建流程图至关重要,它以可视化方式展示了活动流。分析这些活动之间的顺序、频率和持续时间有助于识别瓶颈、返工循环和不合规的流程变体。它是任何流程挖掘分析的核心。 为何重要 它定义了流程中的步骤,支持流程图的可视化以及流程流和变体的分析。 获取方式 源自 SAP 事务代码 (SY-TCODE)、变更单据对象状态 (CDHDR/CDPOS) 以及指示状态变更的特定字段值的组合。 示例 发票已暂存发票已审批已执行付款 | |||
| 付款冻结原因 PaymentBlockReason | 指示发票被冻结付款原因的代码。 | ||
| 描述 当发票被冻结付款时,此属性提供了冻结的具体原因,如“数量差异”或“价格不匹配”。这些原因在 SAP 中进行配置,以标准化异常处理。 此属性对于“付款冻结发生次数及持续时间”仪表板至关重要。分析不同冻结原因的频率有助于识别付款延迟的根源(例如特定供应商、物料或内部流程的问题),从而采取有针对性的纠正措施。 为何重要 提供付款冻结的具体根源,实现有针对性的分析,从而减少延迟并提高“一次性成功率”。 获取方式 位于表 BSEG 的供应商行项目中,字段为 ZLSPR(付款冻结键)。 示例 RIA | |||
| 付款到期日 PaymentDueDate | 发票必须支付的最后期限,逾期将产生违约风险。 | ||
| 描述 “付款截止日期”是根据发票日期和约定的付款条件计算出的应向供应商付款的日期。它是流程中的关键截止限期。 此属性对于“按时付款率”这一 KPI 和“供应商付款绩效”仪表板至关重要。通过比较实际付款日期与截止日期,企业可以衡量其履行付款义务的能力,这直接影响供应商关系和财务信誉。 为何重要 它是衡量及时付款绩效的主要基准,对于维护良好的供应商关系和避免逾期费用至关重要。 获取方式 此日期通常直接位于 BSEG 表的供应商行项目中,即 ZFBDT 字段(到期日计算的基准日期)。净到期日是根据此基准日期和付款条件计算得出的。 示例 2023-05-302023-06-152023-07-01 | |||
| 供应商编号 VendorNumber | 提交发票的供应商的唯一标识符。 | ||
| 描述 “供应商编号”标识与发票关联的供应商或债权人。它将发票交易与供应商主数据链接起来。 此属性对于以供应商为中心的分析至关重要,例如评估“供应商付款绩效”或识别经常提交问题发票并导致异常或付款冻结的供应商。它有助于管理供应商关系并评估供应商可靠性。 为何重要 支持按供应商分析流程绩效,有助于识别模式、管理关系并评估与供应商相关的问题。 获取方式 通常位于会计凭证行项目表 BSEG 中的 LIFNR 字段。 示例 100345700012V9832 | |||
| 公司代码 CompanyCode | 代表独立法人公司的组织单位,用于创建财务报表。 | ||
| 描述 “公司代码”是 SAP 财务中的基本组织单位。每张发票都分配给特定的公司代码,这决定了负责该交易的法律实体。 在流程挖掘中,按公司代码进行过滤或对比对于分析不同业务部门、法律实体或国家的流程性能至关重要。它有助于识别效率、合规性和自动化水平方面的地域差异,从而支持有针对性的改进计划。 为何重要 它支持对组织内不同法律实体或地理位置的发票处理绩效进行细分和比较。 获取方式 这是凭证抬头表 BKPF 中的标准字段,字段名为 BUKRS。 示例 1000US01DE01 | |||
| 凭证类型 DocumentType | 对不同类型的会计单据进行分类的代码,例如供应商发票或贷记凭证。 | ||
| 描述 “凭证类型”在 SAP 中用于区分不同的业务交易。例如,“KR”通常代表标准供应商发票,而“KG”可能是供应商贷方凭证。 按凭证类型进行分析可以对流程进行细分,以了解如何处理不同类型的交易。例如,贷方凭证的处理流程可能与标准发票大不相同。这种细分提供了更准确且相关的流程洞察。 为何重要 它有助于区分各种财务交易,例如标准发票和贷记凭证,这些交易通常遵循不同的流程路径。 获取方式 位于单据抬头表 BKPF 的字段 BLART 中。 示例 KRREKG | |||
| 发票金额 AmountInCompanyCodeCurrency | 以公司代码本地货币计的发票总额。 | ||
| 描述 此属性代表发票的总金额。它是了解发票处理业务财务影响和规模的关键指标。 分析发票金额有助于优先处理高价值发票,识别支出趋势,并将流程问题与财务价值联系起来。例如,它可以用于调查高价值发票是否更容易被冻结或审批时间更长。 为何重要 为流程提供财务背景,以便根据金额进行分析,例如识别高价值发票的处理方式是否有所不同。 获取方式 此值通常由 BSEG 表中相关行项目的 WRBTR 字段(本地货币金额)求和得出。 示例 1500.75125000.00850.20 | |||
| 用户名称 UserName | 执行该活动的个人或系统的 SAP 用户 ID。 | ||
| 描述 此属性标识执行特定交易或创建凭证的用户。它可以是个人的用户 ID,也可以是自动化批处理作业的系统 ID。 按用户分析有助于了解工作量分布、识别培训需求并发现异常的用户行为。例如,它可以突出显示哪些用户经常处理异常,或者哪些发票是自动处理的(例如用户“BATCHUSER”),这对于计算“发票自动化率” KPI 至关重要。 为何重要 它将流程活动归因于特定用户或系统账户,从而能够进行工作量分析、绩效比较和自动化检测。 获取方式 源自 BKPF-USNAM(制单人)或 CDHDR-USERNAME(修改人)等字段。 示例 SMITHJMUELLERTWF-BATCH | |||
| 采购订单 PurchasingDocument | 发票关联的采购订单编号。 | ||
| 描述 “采购凭证”编号将供应商发票与原始采购订单 (PO) 关联起来。这种关联是“三路匹配”流程的基础,即根据 PO 和收货确认发票。 按此属性进行分析有助于了解与有 PO 支持的发票与无 PO 发票相关的问题。它是调查匹配差异以及了解流程中采购环节效率的关键。 为何重要 将发票链接到采购流程,这对于分析匹配差异和采购订单合规性至关重要。 获取方式 此信息通常位于凭证行项目表 BSEG 中的 EBELN 字段(采购凭证编号)。 示例 450000123445000056784500009012 | |||
| 付款条款 PaymentTerms | 定义与供应商商定的付款条件的代码,例如到期日和折扣期。 | ||
| 描述 付款条件定义了发票付款的规则,包括任何提前付款折扣。例如,“Z030”可能表示“30 天内全额支付”。 此属性对财务规划和营运资金优化至关重要。在流程挖掘中,它用于计算“付款截止日期”并确定提前付款折扣的资格,直接支持“提前付款折扣获取率”这一 KPI。 为何重要 定义付款到期日和折扣的规则,直接影响及时付款 KPI 和营运资本管理。 获取方式 位于表 BSEG 的供应商行项目中,字段为 ZTERM(付款条件代码)。 示例 0001Z030NT60 | |||
| 冲销原因 ReversalReason | 指示发票单据被冲销原因的代码。 | ||
| 描述 如果发票过账错误,通常会被冲销。冲销原因代码解释了采取此操作的原因,例如“过账日期错误”或“数据录入错误”。 分析冲销原因有助于识别发票过账流程中的错误模式。此洞察可用于改进培训、增强系统控制,或解决导致财务返工和管理开销的经常性问题。 为何重要 解释发票被冲销的原因,直接洞察过账流程中的错误来源和返工情况。 获取方式 位于表 BKPF 中原始单据的抬头,字段为 STGRD(冲销原因)。 示例 010205 | |||
| 发票处理时长 InvoiceProcessingTime | 一张发票从第一个活动到最后一个活动所经过的总时间。 | ||
| 描述 此指标计算处理单张发票的总时长,通常从发票创建或收到发票到最终付款。它提供了个案层面的端到端周期时间摘要。 此计算属性是“平均发票周期时间” KPI 和“端到端发票周期时间”仪表板的基础。它允许快速识别耗时最长的个案,并分析导致处理时间延长的因素。 为何重要 衡量每张发票流程的端到端效率,突出显示时长异常、需要调查的案例。 获取方式 通过计算每个唯一 InvoiceNumber 的最后一个事件与第一个事件的时间戳之差来计算。 示例 10天4小时25 天 1 小时5天8小时 | |||
| 发票日期 InvoiceDate | 供应商开具发票单据的日期。 | ||
| 描述 “发票日期”(也称为凭证日期)是供应商在发票上注明的日期。它被用作根据约定的付款条件计算付款到期日的起点。 在分析中,此日期是财务计算的基础,例如确定发票账龄和提前付款折扣的资格。它是“提前付款折扣获取率”这一 KPI 的关键输入。 为何重要 作为计算付款条件和截止日期的基准,对于管理营运资金和获取折扣至关重要。 获取方式 位于单据抬头表 BKPF 的字段 BLDAT(单据日期)中。 示例 2023-04-122023-05-152023-06-20 | |||
| 审批周期计数 ApprovalCycleCount | 发票被发送审批的次数统计。 | ||
| 描述 此指标计算单张发票发生“发票已发送审批”活动的次数。计数大于 1 表示发票至少被拒绝或退回过一次,需要新的审批周期。 此属性直接支持“一次性审批通过率” KPI。通过分析审批循环次数较高的发票,企业可以识别审批失败的原因(如信息不全或编码错误),并采取措施改进流程。 为何重要 量化审批子流程中的返工,有助于衡量一次通过率并识别审批拒绝的原因。 获取方式 通过计算每个唯一 InvoiceNumber 出现“发票已送审”活动的次数来计算。 示例 123 | |||
| 提取时间戳 ExtractionTimestamp | 从源系统提取数据的日期和时间。 | ||
| 描述 此属性记录数据提取事件的时间戳,反映了在流程挖掘工具中分析的数据的新鲜度。 在分析中,这用于了解所生成洞察的时效性。对于运营监控仪表板而言,确保决策基于最新信息并有效管理数据刷新周期至关重要。 为何重要 指示数据的新鲜度,确保分析和报告基于当前可用的最新信息。 获取方式 这不是 SAP 字段。它是由数据提取工具或 ETL 流程在抓取数据时生成并添加的。 示例 2023-10-27T02:00:00Z2023-10-28T02:00:00Z2023-10-29T02:00:00Z | |||
| 是否已自动化 IsAutomated | 指示活动是否由自动化系统用户执行的标记。 | ||
| 描述 如果与活动关联的用户是已知的系统或批处理账户(如“WF-BATCH”或“SAP_SYSTEM”),则此布尔属性为 true。它有助于区分人工和自动流程步骤。 此属性对于计算“发票自动化率” KPI 至关重要。通过分析流程的哪些部分已实现自动化,企业可以衡量其自动化计划的成功程度,并识别进一步减少人工投入和提高效率的机会。 为何重要 区分人工活动和系统驱动的活动,这是衡量自动化率和识别进一步自动化机会的基础。 获取方式 源自 UserName 属性。通过创建映射或规则将特定用户 ID 分类为“自动”。 示例 truefalse | |||
| 是否按时付款 IsPaidOnTime | 如果发票在付款到期日或之前支付,则该标记为 true。 | ||
| 描述 此布尔属性是比较实际付款日期(“付款已执行”活动的时间戳)与“付款截止日期”的结果。它为每张发票的付款状态提供了一个明确的二元结果。 这是“按时付款率”这一 KPI 的核心计算基础。它允许轻松进行过滤和分析,以了解逾期付款的特征,例如与延迟相关的常见供应商、公司代码或发票金额。 为何重要 直接衡量对付款条件的遵守情况,这是供应商关系管理和财务运营的关键 KPI。 获取方式 通过将“付款已执行”活动的 EventTime 与 PaymentDueDate 属性进行比较来计算(付款日期 <= 付款到期日)。 示例 truefalse | |||
| 是否返工 IsRework | 指示发票是否经历了返工活动(如审批被拒绝或移除付款冻结)的标记。 | ||
| 描述 此属性标记了经历过一次或多次返工循环的发票。返工通过特定的活动序列识别,例如在“发票已拒绝”之后的“发票已批准”,或在“付款冻结已设置”之后的“付款冻结已移除”。 此属性简化了“发票返工率”这一 KPI 的计算。它允许分析人员轻松隔离并调查有返工的个案,以了解低效和重复人工投入的根本原因。 为何重要 识别需要重复工作的低效流程,有助于量化浪费并查明流程例外情况的根本原因。 获取方式 根据事件日志中的活动顺序计算。例如,如果在发票的追踪中出现了“发票被拒绝”,则此标记将被设置为 true。 示例 truefalse | |||
| 清算凭证号 ClearingDocumentNumber | 结清发票的凭证编号,通常代表付款凭证。 | ||
| 描述 “清账凭证编号”将未清发票项与其清账交易(通常是付款凭证)相关联,以确认发票已支付。 此属性是发票与其付款之间的明确链接。它用于识别“付款已执行”活动及其对应的时间戳,这对于计算端到端周期时间和按时付款率至关重要。 为何重要 确认发票已支付并将其链接到特定的付款交易,这对于周期时间和付款绩效分析至关重要。 获取方式 位于单据段表 BSEG 的字段 AUGBL(清算凭证编号)中。 示例 150000000115000000231500000088 | |||
| 源系统ID SourceSystemId | 提取数据所在的源 SAP S/4HANA 系统的标识符。 | ||
| 描述 此属性指定来源系统,例如“S4H_PROD”或“ERP_EU”。这在拥有多个 ERP 实例或新旧系统并存的环境中尤为重要。 在分析方面,它允许比较不同系统或地区的流程绩效。它确保了数据溯源,并且在将来自多个源的数据合并到中央流程挖掘平台时,对于数据治理和故障排除至关重要。 为何重要 它提供了关于数据来源的上下文,这对于数据治理以及比较不同系统或公司地点的流程至关重要。 获取方式 此值通常在数据提取期间派生自 SAP 系统 ID (sy-sysid),或者在 ETL 管道中配置为静态值。 示例 S4PS4H_PROD_100ECC_EU | |||
采购到付款 - 发票处理活动
| 活动 | 描述 | ||
|---|---|---|---|
| 发票凭证已创建 | 这是第一个事件,标志着在 SAP 中创建发票凭证。当用户保存新的发票凭证时即可捕获,凭证可能处于暂存或预过账状态。 | ||
| 为何重要 此活动标志着发票处理生命周期的开始。分析从此事件到其他事件的时间对于衡量整体处理提前期至关重要。 获取方式 此事件是从凭证抬头表(通常是 BKPF,物流发票为 RBKP)中的创建日期和时间 (CPUDT, CPUTM) 捕获的。事务代码 (BKPF-TCODE) 如 FB60、MIRO 或 MIR7 表明了创建方式。 捕获 使用发票凭证的 BKPF-CPUDT 和 BKPF-CPUTM 提供的创建时间戳。 事件类型 explicit | |||
| 发票已冲销 | 代表冲销先前已过账发票单据的活动。这是错误发票的终结事件,通常随后会重新输入正确的信息。 | ||
| 为何重要 冲销表示流程前期未捕获的严重错误。追踪其频率和根本原因是流程改进和减少财务误差的关键。 获取方式 创建冲销单据时即识别为冲销。原始单据抬头 (BKPF) 将包含冲销单据编号 (BKPF-STBLG),反之亦然。冲销单据的过账日期即为事件时间。 捕获 识别在 BKPF-STBLG 字段中有值的单据,并使用冲销单据的过账日期。 事件类型 explicit | |||
| 发票已审批 | 此活动表示发票已获得指定权限部门的批准。当审批工作流成功结束或设置了释放标识时,即可捕获此活动。 | ||
| 为何重要 这是一个关键里程碑,它解除了发票的付款限制。审批延迟是一个常见的瓶颈,追踪此活动有助于精准发现进度缓慢的审批人或流程步骤。 获取方式 这可以从 SAP 工作流中的最终释放步骤中推断出来,或者通过追踪与发票或其采购凭证相关的表中释放状态字段的更改来推断。 捕获 根据工作流完成事件或单据释放状态字段的变更来推断。 事件类型 inferred | |||
| 发票已过账 | 这是一个关键的财务事件,即暂存或已批准的发票被正式过账到总账。此操作确认了对供应商的负债。 | ||
| 为何重要 过账是一个重要的里程碑,它将数据录入和审批与财务结算阶段区分开来。从发票创建到过账的时间是衡量内部处理效率的关键指标。 获取方式 此事件通过凭证抬头上的过账日期 (BKPF-BUDAT) 识别。对于先暂存的凭证,向过账状态的转变提供了事件时间戳。 捕获 使用过账日期 (BKPF-BUDAT) 作为事件时间戳。 事件类型 explicit | |||
| 已执行付款 | 这是标准流程中的最后一个活动,即完成付款并清账。这表示资金已拨付给供应商。 | ||
| 为何重要 这标志着 P2P 发票生命周期的结束。这对于计算端到端总周期时间以及衡量相对于到期日的按时付款绩效至关重要。 获取方式 此事件是从供应商行项目上的清账凭证信息中捕获的。清账日期 (BSEG-AUGDT) 和清账凭证 (BSEG-AUGBL) 表明已完成付款。 捕获 使用已清供应商行项目中的清账日期 (BSEG-AUGDT)。 事件类型 explicit | |||
| 付款冻结已解除 | 代表问题的解决,即移除之前设置的付款冻结。这使发票重新符合付款条件。 | ||
| 为何重要 冻结设置与移除之间的时间代表了流程异常的解决时间。缩短这一时长是提高效率和改善供应商关系的关键。 获取方式 当付款冻结键字段 (BSEG-ZLSPR) 被清除时,即可捕获此事件。此更改记录在 CDHDR 和 CDPOS 表中,提供了移除的时间戳。 捕获 识别何时通过变更单据 (CDHDR/CDPOS) 清除 BSEG-ZLSPR 字段。 事件类型 explicit | |||
| 付款建议已创建 | 发票被选中并包含在作为付款运行一部分的付款建议中。这是自动化付款流程的第一步。 | ||
| 为何重要 此活动表示付款意向。此步骤与最终付款执行之间的延迟可能揭示付款运行流程、审批或银行通信中的问题。 获取方式 这可以在付款运行表中找到,特别是 REGUP 表,它包含了付款建议中涉及的项目。对应 REGUH 表中的运行日期提供了时间戳。 捕获 识别发票何时出现在付款建议运行的 REGUP 表中。 事件类型 explicit | |||
| 发票已拒绝 | 代表在审批过程中拒绝发票。此事件会触发返工,需要修正并重新提交。 | ||
| 为何重要 发票拒绝是流程低效和数据质量问题的关键指标。分析拒绝频率和原因有助于发现改进和培训的机会。 获取方式 这可以从 SAP 工作流中的特定状态更新(如“已拒绝”状态)中推断,或者从取消当前审批工作流并将其退回给处理者的事件中推断。 捕获 根据指示拒绝的工作流状态变更来推断。 事件类型 inferred | |||
| 发票已暂存 | 代表已录入系统但尚未过账到总账的发票。暂存用于保存不完整的发票,或在过账前进行后续审查。 | ||
| 为何重要 “暂存” (Parking) 表示流程中有意暂停。追踪暂存发票的持续时间和频率,有助于在正式过账和审批周期开始前识别导致延迟的原因。 获取方式 这可以通过通过暂存交易(如 MIR7、FV60)创建的凭证来识别,或者通过检查 BKPF 表中的特定状态字段或专用的暂存凭证表(如 VBKPF)来识别。 捕获 识别通过暂存交易创建的单据,或检查是否存在暂存单据状态。 事件类型 explicit | |||
| 发票已送审 | 此活动标志着发票正式审批工作流的启动。通常在发票状态变为“待审批”或生成工作流项目时推断。 | ||
| 为何重要 这是衡量审批周期时间的起点。了解审批何时开始对于识别审批工作流本身的瓶颈至关重要。 获取方式 这通常从链接到发票对象(如 BUS2081)的 SAP 业务工作流 (SWW_WI2OBJ 表) 的启动中推断,或者从凭证抬头上自定义状态字段的更改中推断。 捕获 根据与发票单据相关的工作流项的创建来推断。 事件类型 inferred | |||
| 发票数据已更新 | 此活动反映了发票凭证在初步创建后进行的修改。这在拒绝后的返工周期或纠错过程中很常见。 | ||
| 为何重要 频繁的更新预示着录入点存在返工和潜在的数据质量问题。追踪这些变更由于有助于量化纠错所花费的精力,并识别常见错误。 获取方式 关键字段的变更会记录在 SAP 的变更单据表 CDHDR(抬头)和 CDPOS(项目)中。可以通过筛选相关发票对象的变更来生成事件。 捕获 从发票对象的 CDHDR 和 CDPOS 表中提取变更事件。 事件类型 explicit | |||
| 已设置付款冻结 | 特意对发票设置冻结以防止其被支付的活动。这通常是由于价格或数量差异,或者存在待处理的贷记凭证。 | ||
| 为何重要 付款冻结是导致逾期付款和供应商纠纷的主要原因。分析冻结的频率、持续时间和原因对于提高按时付款率至关重要。 获取方式 通过追踪发票行项目中的付款冻结键字段 (BSEG-ZLSPR) 的更改来捕获此事件。CDHDR 和 CDPOS 中的更改日志提供了设置冻结的时间戳和用户。 捕获 识别何时通过变更单据 (CDHDR/CDPOS) 填充 BSEG-ZLSPR 字段。 事件类型 explicit | |||
| 逾期付款已执行 | 这是一个计算得出的事件,当发票付款在计算的到期日之后执行时发生。它是通过比较两个日期字段得出的。 | ||
| 为何重要 此活动直接支持按时付款 KPI,并有助于识别经常逾期付款的供应商或业务部门,逾期付款会损害供应商关系并导致罚款。 获取方式 这是通过比较清账日期 (BSEG-AUGDT) 与净到期日计算得出的。到期日本身是根据基准日期 (BSEG-ZFBDT) 和付款条件 (BSEG-ZTERM) 计算的。 捕获 通过比较 BSEG-AUGDT > (BSEG-ZFBDT + 付款条件天数) 来推导。 事件类型 calculated | |||
提取指南
步骤
- 前提条件与权限:确保执行提取的用户在 SAP S/4HANA 中拥有访问所需核心数据服务 (CDS) 视图的必要权限。关键视图包括
I_InvoiceDocument、I_OperationalAcctgDocItem、I_ChangeDocument、I_ChangeDocumentItem和I_PaymentProposalItem。用户还需要拥有通过所选接口(如 OData 服务或直接 SQL 连接)执行查询的权限。 - 确定连接方法:确定您将如何连接到 SAP S/4HANA 系统以执行 SQL 查询。常用方法包括使用 SAP Data Services、SAP Data Intelligence、带有 SAP 连接器的第三方 ETL 工具,或者在组织安全策略允许的情况下,直接建立与 SAP HANA 数据库的 SQL 连接。
- 定义提取参数:在执行查询之前,定义关键参数。指定提取的日期范围,例如
CreationDate介于'YYYY-MM-DD'和'YYYY-MM-DD'之间。同时,确定您想要包含的特定CompanyCode值,以限制数据提取的范围。 - 自定义 SQL 查询:将提供的 SQL 查询复制到您选择的 SQL 客户端或数据提取工具中。仔细检查占位符,例如
'{StartDate}'、'{EndDate}'和('{CompanyCode1}', '{CompanyCode2}')。将这些占位符替换为您在上一步中定义的实际值。根据您的特定 SAP 配置,您可能还需要调整 workflow 状态的字段名称。 - 执行查询:对 SAP S/4HANA 数据库或通过相应的服务层运行完整的 SQL 查询。该查询设计非常全面,根据数据量和选定的日期范围,运行可能需要较长时间。监控执行过程,留意任何潜在的错误或超时。
- 检查初步结果:查询完成后,对输出进行快速检查。确认
InvoiceNumber、ActivityName和EventTime列已填充数据。验证您是否在ActivityName列中看到了多种不同的活动,而不仅仅是“发票单据已创建”。 - 处理数据转换:查询的结构旨在生成干净的事件日志格式。但请务必确保
EventTime列采用一致的时间戳格式,例如YYYY-MM-DDTHH:MM:SS。提供的查询在必要时会将日期和时间字段合并为一个时间戳。 - 导出数据:将最终结果集从您的工具导出为 CSV(逗号分隔值)文件。此格式与包括 ProcessMind 在内的流程挖掘工具普遍兼容。
- 准备上传:在上传之前,确认 CSV 文件使用 UTF-8 编码以防止字符问题。确保文件中的列标题与要求的属性完全匹配:
InvoiceNumber、ActivityName、EventTime、UserName、CompanyCode等。 - 上传至 ProcessMind:将准备好的 CSV 文件上传到您的流程挖掘项目中。将文件中的列映射到工具数据模型配置中对应的案例 ID、活动名称和时间戳字段。
配置
- 使用的 CDS 视图:主要数据源为标准 SAP CDS 视图。核心视图包括用于抬头数据的
I_InvoiceDocument、用于财务过账和清账详情的I_OperationalAcctgDocItem,以及用于追踪发票属性(如付款冻结和 workflow 状态)历史变更的I_ChangeDocument和I_ChangeDocumentItem。 - 日期范围筛选:为了保证系统性能,必须按特定日期范围筛选数据。提供的查询在
I_InvoiceDocument视图中为CreationDate设置了占位符。建议初始提取 3 到 6 个月的数据。 - 公司代码筛选:为确保提取的数据具有相关性且易于管理,请务必筛选一个或多个
CompanyCode。查询中包含占位符WHERE inv.CompanyCode IN ('{CompanyCode1}', '{CompanyCode2}')供此使用。 - 凭证类型筛选:您可以根据
InvoiceDocumentType进一步细化提取范围。例如,您可能希望包含标准供应商发票(RE),但排除贷记凭证。这可以添加到初始 CTE 的WHERE子句中。 - 前提条件:运行查询的用户需要拥有指定公司代码内财务和采购单据的相应显示权限。通过 SQL 客户端访问底层 HANA 数据库并非标准操作,需要特殊权限。
- 性能注意事项:从变更单据表(
I_ChangeDocument、I_ChangeDocumentItem)提取数据可能会消耗大量性能。务必对日期、公司代码和对象类(INCOMINGINVOICE)应用严格筛选,以防止执行时间过长。
a 查询示例 sql
WITH InvoiceBase AS (
SELECT
inv.InvoiceDocument,
inv.FiscalYear,
inv.CompanyCode,
inv.Supplier AS VendorNumber,
inv.DocumentType,
inv.GrossInvoiceAmountInCoCoCrcy AS AmountInCompanyCodeCurrency,
inv.NetDueDate AS PaymentDueDate,
inv.PurchasingDocument,
inv.CreationDateTime,
inv.CreatedByUser,
accdoc.AccountingDocument,
accdoc.ClearingDate,
accdoc.ClearingJournalEntry,
accdoc.PaymentBlockReason,
accdoc.IsReversed
FROM I_InvoiceDocument AS inv
LEFT JOIN I_OperationalAcctgDocItem AS accdoc
ON inv.AccountingDocument = accdoc.AccountingDocument
AND inv.FiscalYear = accdoc.FiscalYear
AND inv.CompanyCode = accdoc.CompanyCode
WHERE
inv.CreationDate BETWEEN '{StartDate}' AND '{EndDate}'
AND inv.CompanyCode IN ('{CompanyCode1}', '{CompanyCode2}')
)
-- 1. Invoice Document Created
SELECT
InvoiceDocument AS "InvoiceNumber",
'Invoice Document Created' AS "ActivityName",
CreationDateTime AS "EventTime",
CreatedByUser AS "UserName",
CompanyCode AS "CompanyCode",
VendorNumber AS "VendorNumber",
AmountInCompanyCodeCurrency AS "AmountInCompanyCodeCurrency",
PaymentDueDate AS "PaymentDueDate",
DocumentType AS "DocumentType",
CAST(NULL AS VARCHAR(1)) AS "PaymentBlockReason",
PurchasingDocument AS "PurchasingDocument"
FROM InvoiceBase
UNION ALL
-- 2. Invoice Parked
SELECT
i.InvoiceDocument AS "InvoiceNumber",
'Invoice Parked' AS "ActivityName",
i.CreationDateTime AS "EventTime",
i.CreatedByUser AS "UserName",
i.CompanyCode AS "CompanyCode",
i.Supplier AS "VendorNumber",
i.GrossInvoiceAmountInCoCoCrcy AS "AmountInCompanyCodeCurrency",
i.NetDueDate AS "PaymentDueDate",
i.DocumentType AS "DocumentType",
CAST(NULL AS VARCHAR(1)) AS "PaymentBlockReason",
i.PurchasingDocument AS "PurchasingDocument"
FROM I_InvoiceDocument AS i
WHERE
i.InvoiceDocumentIsParked = 'X'
AND i.CreationDate BETWEEN '{StartDate}' AND '{EndDate}'
AND i.CompanyCode IN ('{CompanyCode1}', '{CompanyCode2}')
UNION ALL
-- 3, 4, 5. Workflow activities (Sent for Approval, Approved, Rejected) from Change Docs
SELECT
cdpos.ObjectValue AS "InvoiceNumber",
CASE
WHEN cdpos.ValueNew = '[StatusSentForApproval]' THEN 'Invoice Sent For Approval'
WHEN cdpos.ValueNew = '[StatusApproved]' THEN 'Invoice Approved'
WHEN cdpos.ValueNew = '[StatusRejected]' THEN 'Invoice Rejected'
END AS "ActivityName",
CAST(cdhdr.ChangeDate AS TIMESTAMP) + CAST(cdhdr.ChangeTime AS TIME) AS "EventTime",
cdhdr.UserName AS "UserName",
inv.CompanyCode AS "CompanyCode",
inv.VendorNumber AS "VendorNumber",
inv.AmountInCompanyCodeCurrency AS "AmountInCompanyCodeCurrency",
inv.PaymentDueDate AS "PaymentDueDate",
inv.DocumentType AS "DocumentType",
CAST(NULL AS VARCHAR(1)) AS "PaymentBlockReason",
inv.PurchasingDocument AS "PurchasingDocument"
FROM I_ChangeDocument AS cdhdr
JOIN I_ChangeDocumentItem AS cdpos ON cdhdr.ChangeDocument = cdpos.ChangeDocument
JOIN InvoiceBase AS inv ON cdpos.ObjectValue = inv.InvoiceDocument
WHERE
cdhdr.ObjectClassName = 'INCOMINGINVOICE'
AND cdpos.FieldName = '[WorkflowStatusFieldName]'
AND cdpos.ValueNew IN ('[StatusSentForApproval]', '[StatusApproved]', '[StatusRejected]')
UNION ALL
-- 6. Invoice Data Updated
SELECT
cdpos.ObjectValue AS "InvoiceNumber",
'Invoice Data Updated' AS "ActivityName",
CAST(cdhdr.ChangeDate AS TIMESTAMP) + CAST(cdhdr.ChangeTime AS TIME) AS "EventTime",
cdhdr.UserName AS "UserName",
inv.CompanyCode AS "CompanyCode",
inv.VendorNumber AS "VendorNumber",
inv.AmountInCompanyCodeCurrency AS "AmountInCompanyCodeCurrency",
inv.PaymentDueDate AS "PaymentDueDate",
inv.DocumentType AS "DocumentType",
CAST(NULL AS VARCHAR(1)) AS "PaymentBlockReason",
inv.PurchasingDocument AS "PurchasingDocument"
FROM I_ChangeDocument AS cdhdr
JOIN I_ChangeDocumentItem AS cdpos ON cdhdr.ChangeDocument = cdpos.ChangeDocument
JOIN InvoiceBase AS inv ON cdpos.ObjectValue = inv.InvoiceDocument
WHERE
cdhdr.ObjectClassName = 'INCOMINGINVOICE'
AND cdpos.FieldName IN ('GrossInvoiceAmount', 'DocumentDate', 'PaymentTerms')
AND cdhdr.ChangeDate BETWEEN '{StartDate}' AND '{EndDate}'
UNION ALL
-- 7 & 8. Payment Block Set/Removed
SELECT
inv.InvoiceDocument AS "InvoiceNumber",
CASE
WHEN cdpos.ValueNew <> '' AND cdpos.ValueOld = '' THEN 'Payment Block Set'
WHEN cdpos.ValueNew = '' AND cdpos.ValueOld <> '' THEN 'Payment Block Removed'
END AS "ActivityName",
CAST(cdhdr.ChangeDate AS TIMESTAMP) + CAST(cdhdr.ChangeTime AS TIME) AS "EventTime",
cdhdr.UserName AS "UserName",
inv.CompanyCode AS "CompanyCode",
inv.VendorNumber AS "VendorNumber",
inv.AmountInCompanyCodeCurrency AS "AmountInCompanyCodeCurrency",
inv.PaymentDueDate AS "PaymentDueDate",
inv.DocumentType AS "DocumentType",
cdpos.ValueNew AS "PaymentBlockReason",
inv.PurchasingDocument AS "PurchasingDocument"
FROM I_ChangeDocument AS cdhdr
JOIN I_ChangeDocumentItem AS cdpos ON cdhdr.ChangeDocument = cdpos.ChangeDocument
JOIN InvoiceBase AS inv ON cdpos.ObjectValue = inv.AccountingDocument
WHERE
cdhdr.ObjectClassName = 'BELEG'
AND cdpos.TableName = 'BSEG'
AND cdpos.FieldName = 'ZLSPR'
AND ( (cdpos.ValueNew <> '' AND cdpos.ValueOld = '') OR (cdpos.ValueNew = '' AND cdpos.ValueOld <> '') )
UNION ALL
-- 9. Invoice Posted
SELECT
inv.InvoiceDocument AS "InvoiceNumber",
'Invoice Posted' AS "ActivityName",
CAST(accdoc.PostingDate AS TIMESTAMP) AS "EventTime",
accdoc.CreatedByUser AS "UserName",
inv.CompanyCode AS "CompanyCode",
inv.VendorNumber AS "VendorNumber",
inv.AmountInCompanyCodeCurrency AS "AmountInCompanyCodeCurrency",
inv.PaymentDueDate AS "PaymentDueDate",
inv.DocumentType AS "DocumentType",
accdoc.PaymentBlockReason AS "PaymentBlockReason",
inv.PurchasingDocument AS "PurchasingDocument"
FROM InvoiceBase AS inv
JOIN I_OperationalAcctgDocItem AS accdoc ON inv.AccountingDocument = accdoc.AccountingDocument
WHERE inv.AccountingDocument IS NOT NULL AND inv.IsReversed = FALSE
UNION ALL
-- 10. Payment Proposal Created
SELECT
item.InvoiceReference AS "InvoiceNumber",
'Payment Proposal Created' AS "ActivityName",
CAST(prun.PaymentRunDate AS TIMESTAMP) AS "EventTime",
prun.CreatedByUser AS "UserName",
item.CompanyCode AS "CompanyCode",
item.Supplier AS "VendorNumber",
item.AmountInTransactionCurrency AS "AmountInCompanyCodeCurrency",
item.NetDueDate AS "PaymentDueDate",
item.AccountingDocumentType AS "DocumentType",
item.PaymentBlockReason AS "PaymentBlockReason",
item.PurchasingDocument AS "PurchasingDocument"
FROM I_PaymentProposalItem as item
JOIN I_PaymentRun as prun ON item.PaymentRunName = prun.PaymentRunName
JOIN InvoiceBase AS inv ON item.InvoiceReference = inv.InvoiceDocument
UNION ALL
-- 11 & 12. Payment Executed / Late Payment Executed
SELECT
InvoiceDocument AS "InvoiceNumber",
CASE
WHEN ClearingDate > PaymentDueDate THEN 'Late Payment Executed'
ELSE 'Payment Executed'
END AS "ActivityName",
CAST(ClearingDate AS TIMESTAMP) AS "EventTime",
CAST(NULL AS VARCHAR(12)) AS "UserName", -- User for clearing is not always straightforward
CompanyCode AS "CompanyCode",
VendorNumber AS "VendorNumber",
AmountInCompanyCodeCurrency AS "AmountInCompanyCodeCurrency",
PaymentDueDate AS "PaymentDueDate",
DocumentType AS "DocumentType",
'' AS "PaymentBlockReason",
PurchasingDocument AS "PurchasingDocument"
FROM InvoiceBase
WHERE ClearingDate IS NOT NULL AND IsReversed = FALSE
UNION ALL
-- 13. Invoice Reversed
SELECT
rev.OriginalInvoiceDocument AS "InvoiceNumber",
'Invoice Reversed' AS "ActivityName",
rev.CreationDateTime AS "EventTime",
rev.CreatedByUser AS "UserName",
rev.CompanyCode AS "CompanyCode",
rev.Supplier AS "VendorNumber",
rev.GrossInvoiceAmountInCoCoCrcy AS "AmountInCompanyCodeCurrency",
CAST(NULL AS DATE) AS "PaymentDueDate",
rev.DocumentType AS "DocumentType",
CAST(NULL AS VARCHAR(1)) AS "PaymentBlockReason",
rev.PurchasingDocument AS "PurchasingDocument"
FROM I_InvoiceDocument AS rev
WHERE rev.OriginalInvoiceDocument IN (SELECT InvoiceDocument FROM InvoiceBase) AND rev.IsReversal = 'X' 步骤
- 前提条件与权限:确保执行提取的用户在 SAP S/4HANA 中拥有访问所需核心数据服务 (CDS) 视图的必要权限。关键视图包括
I_InvoiceDocument、I_OperationalAcctgDocItem、I_ChangeDocument、I_ChangeDocumentItem和I_PaymentProposalItem。用户还需要拥有通过所选接口(如 OData 服务或直接 SQL 连接)执行查询的权限。 - 确定连接方法:确定您将如何连接到 SAP S/4HANA 系统以执行 SQL 查询。常用方法包括使用 SAP Data Services、SAP Data Intelligence、带有 SAP 连接器的第三方 ETL 工具,或者在组织安全策略允许的情况下,直接建立与 SAP HANA 数据库的 SQL 连接。
- 定义提取参数:在执行查询之前,定义关键参数。指定提取的日期范围,例如
CreationDate介于'YYYY-MM-DD'和'YYYY-MM-DD'之间。同时,确定您想要包含的特定CompanyCode值,以限制数据提取的范围。 - 自定义 SQL 查询:将提供的 SQL 查询复制到您选择的 SQL 客户端或数据提取工具中。仔细检查占位符,例如
'{StartDate}'、'{EndDate}'和('{CompanyCode1}', '{CompanyCode2}')。将这些占位符替换为您在上一步中定义的实际值。根据您的特定 SAP 配置,您可能还需要调整 workflow 状态的字段名称。 - 执行查询:对 SAP S/4HANA 数据库或通过相应的服务层运行完整的 SQL 查询。该查询设计非常全面,根据数据量和选定的日期范围,运行可能需要较长时间。监控执行过程,留意任何潜在的错误或超时。
- 检查初步结果:查询完成后,对输出进行快速检查。确认
InvoiceNumber、ActivityName和EventTime列已填充数据。验证您是否在ActivityName列中看到了多种不同的活动,而不仅仅是“发票单据已创建”。 - 处理数据转换:查询的结构旨在生成干净的事件日志格式。但请务必确保
EventTime列采用一致的时间戳格式,例如YYYY-MM-DDTHH:MM:SS。提供的查询在必要时会将日期和时间字段合并为一个时间戳。 - 导出数据:将最终结果集从您的工具导出为 CSV(逗号分隔值)文件。此格式与包括 ProcessMind 在内的流程挖掘工具普遍兼容。
- 准备上传:在上传之前,确认 CSV 文件使用 UTF-8 编码以防止字符问题。确保文件中的列标题与要求的属性完全匹配:
InvoiceNumber、ActivityName、EventTime、UserName、CompanyCode等。 - 上传至 ProcessMind:将准备好的 CSV 文件上传到您的流程挖掘项目中。将文件中的列映射到工具数据模型配置中对应的案例 ID、活动名称和时间戳字段。
配置
- 使用的 CDS 视图:主要数据源为标准 SAP CDS 视图。核心视图包括用于抬头数据的
I_InvoiceDocument、用于财务过账和清账详情的I_OperationalAcctgDocItem,以及用于追踪发票属性(如付款冻结和 workflow 状态)历史变更的I_ChangeDocument和I_ChangeDocumentItem。 - 日期范围筛选:为了保证系统性能,必须按特定日期范围筛选数据。提供的查询在
I_InvoiceDocument视图中为CreationDate设置了占位符。建议初始提取 3 到 6 个月的数据。 - 公司代码筛选:为确保提取的数据具有相关性且易于管理,请务必筛选一个或多个
CompanyCode。查询中包含占位符WHERE inv.CompanyCode IN ('{CompanyCode1}', '{CompanyCode2}')供此使用。 - 凭证类型筛选:您可以根据
InvoiceDocumentType进一步细化提取范围。例如,您可能希望包含标准供应商发票(RE),但排除贷记凭证。这可以添加到初始 CTE 的WHERE子句中。 - 前提条件:运行查询的用户需要拥有指定公司代码内财务和采购单据的相应显示权限。通过 SQL 客户端访问底层 HANA 数据库并非标准操作,需要特殊权限。
- 性能注意事项:从变更单据表(
I_ChangeDocument、I_ChangeDocumentItem)提取数据可能会消耗大量性能。务必对日期、公司代码和对象类(INCOMINGINVOICE)应用严格筛选,以防止执行时间过长。
a 查询示例 sql
WITH InvoiceBase AS (
SELECT
inv.InvoiceDocument,
inv.FiscalYear,
inv.CompanyCode,
inv.Supplier AS VendorNumber,
inv.DocumentType,
inv.GrossInvoiceAmountInCoCoCrcy AS AmountInCompanyCodeCurrency,
inv.NetDueDate AS PaymentDueDate,
inv.PurchasingDocument,
inv.CreationDateTime,
inv.CreatedByUser,
accdoc.AccountingDocument,
accdoc.ClearingDate,
accdoc.ClearingJournalEntry,
accdoc.PaymentBlockReason,
accdoc.IsReversed
FROM I_InvoiceDocument AS inv
LEFT JOIN I_OperationalAcctgDocItem AS accdoc
ON inv.AccountingDocument = accdoc.AccountingDocument
AND inv.FiscalYear = accdoc.FiscalYear
AND inv.CompanyCode = accdoc.CompanyCode
WHERE
inv.CreationDate BETWEEN '{StartDate}' AND '{EndDate}'
AND inv.CompanyCode IN ('{CompanyCode1}', '{CompanyCode2}')
)
-- 1. Invoice Document Created
SELECT
InvoiceDocument AS "InvoiceNumber",
'Invoice Document Created' AS "ActivityName",
CreationDateTime AS "EventTime",
CreatedByUser AS "UserName",
CompanyCode AS "CompanyCode",
VendorNumber AS "VendorNumber",
AmountInCompanyCodeCurrency AS "AmountInCompanyCodeCurrency",
PaymentDueDate AS "PaymentDueDate",
DocumentType AS "DocumentType",
CAST(NULL AS VARCHAR(1)) AS "PaymentBlockReason",
PurchasingDocument AS "PurchasingDocument"
FROM InvoiceBase
UNION ALL
-- 2. Invoice Parked
SELECT
i.InvoiceDocument AS "InvoiceNumber",
'Invoice Parked' AS "ActivityName",
i.CreationDateTime AS "EventTime",
i.CreatedByUser AS "UserName",
i.CompanyCode AS "CompanyCode",
i.Supplier AS "VendorNumber",
i.GrossInvoiceAmountInCoCoCrcy AS "AmountInCompanyCodeCurrency",
i.NetDueDate AS "PaymentDueDate",
i.DocumentType AS "DocumentType",
CAST(NULL AS VARCHAR(1)) AS "PaymentBlockReason",
i.PurchasingDocument AS "PurchasingDocument"
FROM I_InvoiceDocument AS i
WHERE
i.InvoiceDocumentIsParked = 'X'
AND i.CreationDate BETWEEN '{StartDate}' AND '{EndDate}'
AND i.CompanyCode IN ('{CompanyCode1}', '{CompanyCode2}')
UNION ALL
-- 3, 4, 5. Workflow activities (Sent for Approval, Approved, Rejected) from Change Docs
SELECT
cdpos.ObjectValue AS "InvoiceNumber",
CASE
WHEN cdpos.ValueNew = '[StatusSentForApproval]' THEN 'Invoice Sent For Approval'
WHEN cdpos.ValueNew = '[StatusApproved]' THEN 'Invoice Approved'
WHEN cdpos.ValueNew = '[StatusRejected]' THEN 'Invoice Rejected'
END AS "ActivityName",
CAST(cdhdr.ChangeDate AS TIMESTAMP) + CAST(cdhdr.ChangeTime AS TIME) AS "EventTime",
cdhdr.UserName AS "UserName",
inv.CompanyCode AS "CompanyCode",
inv.VendorNumber AS "VendorNumber",
inv.AmountInCompanyCodeCurrency AS "AmountInCompanyCodeCurrency",
inv.PaymentDueDate AS "PaymentDueDate",
inv.DocumentType AS "DocumentType",
CAST(NULL AS VARCHAR(1)) AS "PaymentBlockReason",
inv.PurchasingDocument AS "PurchasingDocument"
FROM I_ChangeDocument AS cdhdr
JOIN I_ChangeDocumentItem AS cdpos ON cdhdr.ChangeDocument = cdpos.ChangeDocument
JOIN InvoiceBase AS inv ON cdpos.ObjectValue = inv.InvoiceDocument
WHERE
cdhdr.ObjectClassName = 'INCOMINGINVOICE'
AND cdpos.FieldName = '[WorkflowStatusFieldName]'
AND cdpos.ValueNew IN ('[StatusSentForApproval]', '[StatusApproved]', '[StatusRejected]')
UNION ALL
-- 6. Invoice Data Updated
SELECT
cdpos.ObjectValue AS "InvoiceNumber",
'Invoice Data Updated' AS "ActivityName",
CAST(cdhdr.ChangeDate AS TIMESTAMP) + CAST(cdhdr.ChangeTime AS TIME) AS "EventTime",
cdhdr.UserName AS "UserName",
inv.CompanyCode AS "CompanyCode",
inv.VendorNumber AS "VendorNumber",
inv.AmountInCompanyCodeCurrency AS "AmountInCompanyCodeCurrency",
inv.PaymentDueDate AS "PaymentDueDate",
inv.DocumentType AS "DocumentType",
CAST(NULL AS VARCHAR(1)) AS "PaymentBlockReason",
inv.PurchasingDocument AS "PurchasingDocument"
FROM I_ChangeDocument AS cdhdr
JOIN I_ChangeDocumentItem AS cdpos ON cdhdr.ChangeDocument = cdpos.ChangeDocument
JOIN InvoiceBase AS inv ON cdpos.ObjectValue = inv.InvoiceDocument
WHERE
cdhdr.ObjectClassName = 'INCOMINGINVOICE'
AND cdpos.FieldName IN ('GrossInvoiceAmount', 'DocumentDate', 'PaymentTerms')
AND cdhdr.ChangeDate BETWEEN '{StartDate}' AND '{EndDate}'
UNION ALL
-- 7 & 8. Payment Block Set/Removed
SELECT
inv.InvoiceDocument AS "InvoiceNumber",
CASE
WHEN cdpos.ValueNew <> '' AND cdpos.ValueOld = '' THEN 'Payment Block Set'
WHEN cdpos.ValueNew = '' AND cdpos.ValueOld <> '' THEN 'Payment Block Removed'
END AS "ActivityName",
CAST(cdhdr.ChangeDate AS TIMESTAMP) + CAST(cdhdr.ChangeTime AS TIME) AS "EventTime",
cdhdr.UserName AS "UserName",
inv.CompanyCode AS "CompanyCode",
inv.VendorNumber AS "VendorNumber",
inv.AmountInCompanyCodeCurrency AS "AmountInCompanyCodeCurrency",
inv.PaymentDueDate AS "PaymentDueDate",
inv.DocumentType AS "DocumentType",
cdpos.ValueNew AS "PaymentBlockReason",
inv.PurchasingDocument AS "PurchasingDocument"
FROM I_ChangeDocument AS cdhdr
JOIN I_ChangeDocumentItem AS cdpos ON cdhdr.ChangeDocument = cdpos.ChangeDocument
JOIN InvoiceBase AS inv ON cdpos.ObjectValue = inv.AccountingDocument
WHERE
cdhdr.ObjectClassName = 'BELEG'
AND cdpos.TableName = 'BSEG'
AND cdpos.FieldName = 'ZLSPR'
AND ( (cdpos.ValueNew <> '' AND cdpos.ValueOld = '') OR (cdpos.ValueNew = '' AND cdpos.ValueOld <> '') )
UNION ALL
-- 9. Invoice Posted
SELECT
inv.InvoiceDocument AS "InvoiceNumber",
'Invoice Posted' AS "ActivityName",
CAST(accdoc.PostingDate AS TIMESTAMP) AS "EventTime",
accdoc.CreatedByUser AS "UserName",
inv.CompanyCode AS "CompanyCode",
inv.VendorNumber AS "VendorNumber",
inv.AmountInCompanyCodeCurrency AS "AmountInCompanyCodeCurrency",
inv.PaymentDueDate AS "PaymentDueDate",
inv.DocumentType AS "DocumentType",
accdoc.PaymentBlockReason AS "PaymentBlockReason",
inv.PurchasingDocument AS "PurchasingDocument"
FROM InvoiceBase AS inv
JOIN I_OperationalAcctgDocItem AS accdoc ON inv.AccountingDocument = accdoc.AccountingDocument
WHERE inv.AccountingDocument IS NOT NULL AND inv.IsReversed = FALSE
UNION ALL
-- 10. Payment Proposal Created
SELECT
item.InvoiceReference AS "InvoiceNumber",
'Payment Proposal Created' AS "ActivityName",
CAST(prun.PaymentRunDate AS TIMESTAMP) AS "EventTime",
prun.CreatedByUser AS "UserName",
item.CompanyCode AS "CompanyCode",
item.Supplier AS "VendorNumber",
item.AmountInTransactionCurrency AS "AmountInCompanyCodeCurrency",
item.NetDueDate AS "PaymentDueDate",
item.AccountingDocumentType AS "DocumentType",
item.PaymentBlockReason AS "PaymentBlockReason",
item.PurchasingDocument AS "PurchasingDocument"
FROM I_PaymentProposalItem as item
JOIN I_PaymentRun as prun ON item.PaymentRunName = prun.PaymentRunName
JOIN InvoiceBase AS inv ON item.InvoiceReference = inv.InvoiceDocument
UNION ALL
-- 11 & 12. Payment Executed / Late Payment Executed
SELECT
InvoiceDocument AS "InvoiceNumber",
CASE
WHEN ClearingDate > PaymentDueDate THEN 'Late Payment Executed'
ELSE 'Payment Executed'
END AS "ActivityName",
CAST(ClearingDate AS TIMESTAMP) AS "EventTime",
CAST(NULL AS VARCHAR(12)) AS "UserName", -- User for clearing is not always straightforward
CompanyCode AS "CompanyCode",
VendorNumber AS "VendorNumber",
AmountInCompanyCodeCurrency AS "AmountInCompanyCodeCurrency",
PaymentDueDate AS "PaymentDueDate",
DocumentType AS "DocumentType",
'' AS "PaymentBlockReason",
PurchasingDocument AS "PurchasingDocument"
FROM InvoiceBase
WHERE ClearingDate IS NOT NULL AND IsReversed = FALSE
UNION ALL
-- 13. Invoice Reversed
SELECT
rev.OriginalInvoiceDocument AS "InvoiceNumber",
'Invoice Reversed' AS "ActivityName",
rev.CreationDateTime AS "EventTime",
rev.CreatedByUser AS "UserName",
rev.CompanyCode AS "CompanyCode",
rev.Supplier AS "VendorNumber",
rev.GrossInvoiceAmountInCoCoCrcy AS "AmountInCompanyCodeCurrency",
CAST(NULL AS DATE) AS "PaymentDueDate",
rev.DocumentType AS "DocumentType",
CAST(NULL AS VARCHAR(1)) AS "PaymentBlockReason",
rev.PurchasingDocument AS "PurchasingDocument"
FROM I_InvoiceDocument AS rev
WHERE rev.OriginalInvoiceDocument IN (SELECT InvoiceDocument FROM InvoiceBase) AND rev.IsReversal = 'X' 步骤
- 访问 ABAP 编辑器:登录您的 SAP S/4HANA 系统。打开事务代码
SE38(ABAP 编辑器)。 - 创建程序:在程序字段中为新程序命名,例如
Z_PM_INVOICE_EXTRACT,然后点击“创建”按钮。提供标题,将类型设置为“可执行程序”,并将其保存在适当的包中。 - 定义程序结构和选择屏幕:在编辑器中定义最终事件日志输出的数据结构。然后,创建一个选择屏幕,允许用户输入参数,如发票输入日期范围、公司代码和凭证类型。这可以使程序具备可重用性和灵活性。
- 实现数据选择逻辑:编写核心 ABAP SQL 语句以从各个 SAP 表中选择数据。程序将按顺序查询 13 项所需活动中的每一项。
- 提取抬头和行项目数据:对于“发票单据已创建”和“发票已过账”等基础事件,从
RBKP(后勤发票抬头)和BKPF(会计单据抬头)等主表中选择数据。 - 提取变更单据数据:对于“设置付款冻结”和“移除付款冻结”等活动,查询变更单据表
CDHDR(变更单据抬头)和CDPOS(变更单据项)。您需要识别特定字段的变更,例如表BSEG中的ZLSPR。 - 提取付款数据:要捕获付款相关活动,请查询
REGUP表(来自付款程序的已处理项目)以获取付款建议,查询BSAK表(已清算供应商项目)以获取已执行的付款。通过比较清算日期(AUGDT)与净到期日(ZFBDT)来区分“逾期付款已执行”。 - 提取工作流数据:对于审批活动,查询 SAP Business Workflow 表(如
SWW_WI2OBJ),将工作流项链接到发票对象。这部分高度依赖于您的特定工作流配置,可能需要进行大量适配。 - 统一数据为事件日志格式:对于所选的每个活动,将数据格式化为通用的内部表结构。该表中的每一行代表一个单独的事件,必须包含案例标识符(
InvoiceNumber)、ActivityName和EventTime以及其他推荐属性。 - 生成输出文件:使用 ABAP 语句
OPEN DATASET、TRANSFER和CLOSE DATASET将最终内部表的内容写入 SAP 应用服务器上的平面文件。建议使用逗号分隔值 (CSV) 格式。 - 计划并执行:在前台执行程序进行测试(使用
F8)。对于生产运行,使用事务代码SM36将其计划为后台作业,在非高峰时段运行以避免影响系统性能。 - 检索并上传:使用事务代码
AL11导航到保存文件的应用服务器目录。将文件下载到本地系统。在上传到流程挖掘工具之前,请确保文件采用 UTF-8 编码且格式正确。
配置
- 日期范围:根据发票输入日期(
RBKP-CPUDT)或过账日期(BKPF-BUDAT)定义特定的数据提取范围。初次分析建议选择 3 到 6 个月的时间段,以确保数据量适中。 - 公司代码 (BUKRS):必须按一个或多个公司代码进行筛选。在大型组织中提取所有公司代码的数据会导致运行时间极长且文件过大。
- 凭证类型 (BLART):筛选相关的凭证类型以隔离供应商发票。常见类型包括“RE”(发票 - 毛额)和“KR”(供应商发票)。这有助于从分析中排除无关单据。
- 供应商账号 (LIFNR):程序可以包含针对特定供应商编号的可选筛选器,这对于针对性分析或测试非常有用。
- 输出文件配置:程序应设置相应参数,以定义应用服务器上的输出文件路径以及字段分隔符(例如逗号或分号)。
- 前提条件:执行此程序的查询用户或系统账号需要具备创建和运行 ABAP 程序(通过
SE38)的开发人员权限,以及对 FI、MM 和 Basis 表(包括BKPF、BSEG、RBKP、RSEG、CDHDR、CDPOS及 workflow 相关表)的广泛读取权限。
a 查询示例 abap
REPORT Z_PM_INVOICE_EXTRACT.
* --- Internal table structure for the final event log
TYPES: BEGIN OF ty_s_event_log,
invoicenumber TYPE char25,
activityname TYPE char50,
eventtime TYPE char19, "YYYY-MM-DD HH:MM:SS
username TYPE sy-uname,
companycode TYPE bukrs,
vendornumber TYPE lifnr,
amountincompanycodecurrency TYPE wrbtr,
paymentduedate TYPE char10, "YYYY-MM-DD
documenttype TYPE blart,
paymentblockreason TYPE char1,
purchasingdocument TYPE ebeln,
END OF ty_s_event_log.
DATA: lt_event_log TYPE STANDARD TABLE OF ty_s_event_log.
DATA: ls_event_log TYPE ty_s_event_log.
* --- Selection Screen for user inputs
PARAMETERS: p_path TYPE string DEFAULT '/usr/sap/tmp/invoice_events.csv'.
SELECT-OPTIONS: s_erdat FOR sy-datum OBLIGATORY, " Entry Date
s_bukrs FOR bkpf-bukrs OBLIGATORY, " Company Code
s_blart FOR bkpf-blart. " Document Type
START-OF-SELECTION.
* --- 1. Invoice Document Created (from Logistics Invoice Verification)
SELECT CONCAT( rbkp~belnr, rbkp~gjahr ) AS invoicenumber,
'Invoice Document Created' AS activityname,
CONCAT( rbkp~cpudt, rbkp~cputm ) AS eventtime,
rbkp~usnam AS username,
rbkp~bukrs AS companycode,
rbkp~lifnr AS vendornumber,
rbkp~rmwwr AS amountincompanycodecurrency,
'' AS paymentduedate,
rbkp~blart AS documenttype,
rbkp~zuonr AS paymentblockreason,
'' AS purchasingdocument
FROM rbkp
INTO TABLE @DATA(lt_created)
WHERE rbkp~cpudt IN @s_erdat
AND rbkp~bukrs IN @s_bukrs
AND rbkp~blart IN @s_blart.
LOOP AT lt_created INTO DATA(ls_created).
ls_event_log-invoicenumber = ls_created-invoicenumber.
ls_event_log-activityname = ls_created-activityname.
ls_event_log-eventtime = |{ ls_created-eventtime(8) } { ls_created-eventtime+8(2) }:{ ls_created-eventtime+10(2) }:{ ls_created-eventtime+12(2) }|.
ls_event_log-username = ls_created-username.
ls_event_log-companycode = ls_created-companycode.
ls_event_log-vendornumber = ls_created-vendornumber.
ls_event_log-amountincompanycodecurrency = ls_created-amountincompanycodecurrency.
ls_event_log-paymentduedate = ''.
ls_event_log-documenttype = ls_created-documenttype.
ls_event_log-paymentblockreason = ''.
ls_event_log-purchasingdocument = ls_created-purchasingdocument.
APPEND ls_event_log TO lt_event_log.
ENDLOOP.
* --- 2. Invoice Parked (assuming status 'A' or 'B' in RBKP)
SELECT CONCAT( belnr, gjahr ) AS invoicenumber,
'Invoice Parked' AS activityname,
CONCAT( cpudt, cputm ) AS eventtime,
usnam AS username,
bukrs AS companycode,
lifnr AS vendornumber,
rmwwr AS amountincompanycodecurrency,
'' AS paymentduedate,
blart AS documenttype,
'' AS paymentblockreason,
'' AS purchasingdocument
FROM rbkp
INTO TABLE @DATA(lt_parked)
WHERE rbstat IN ('A', 'B')
AND cpudt IN @s_erdat
AND bukrs IN @s_bukrs
AND blart IN @s_blart.
LOOP AT lt_parked INTO DATA(ls_parked).
ls_event_log-invoicenumber = ls_parked-invoicenumber.
ls_event_log-activityname = ls_parked-activityname.
ls_event_log-eventtime = |{ ls_parked-eventtime(8) } { ls_parked-eventtime+8(2) }:{ ls_parked-eventtime+10(2) }:{ ls_parked-eventtime+12(2) }|.
ls_event_log-username = ls_parked-username.
ls_event_log-companycode = ls_parked-companycode.
ls_event_log-vendornumber = ls_parked-vendornumber.
ls_event_log-amountincompanycodecurrency = ls_parked-amountincompanycodecurrency.
ls_event_log-paymentduedate = ''.
ls_event_log-documenttype = ls_parked-documenttype.
ls_event_log-paymentblockreason = ''.
ls_event_log-purchasingdocument = ''.
APPEND ls_event_log TO lt_event_log.
ENDLOOP.
* --- 3, 4, 5. Sent For Approval, Approved, Rejected (Placeholder logic, needs adaptation)
* --- This logic is a generic template for SAP Business Workflow.
* --- Your implementation will vary. You must identify the correct workflow tasks.
SELECT obj.instid, wi.wi_cd, wi.wi_ct, wi.wi_stat, wi.wi_aagent
FROM sww_wi2obj AS obj
JOIN swwlog AS wi ON obj~instid = wi~wi_id
INTO TABLE @DATA(lt_workflow)
WHERE obj~typeid = 'BUS2081' " Business Object for Incoming Invoice
AND obj~catid = 'BO'
AND wi~wi_cd IN s_erdat.
LOOP AT lt_workflow INTO DATA(ls_workflow).
* --- This is a placeholder, adapt task IDs and logic
CASE ls_workflow-wi_stat.
WHEN 'STARTED'.
ls_event_log-activityname = 'Invoice Sent For Approval'.
WHEN 'COMPLETED'.
ls_event_log-activityname = 'Invoice Approved'.
WHEN 'CANCELLED'.
ls_event_log-activityname = 'Invoice Rejected'.
WHEN OTHERS.
CONTINUE.
ENDCASE.
* --- Code to get invoice details based on ls_workflow-instid needed here
* --- ... appending to lt_event_log ...
ENDLOOP.
* --- 6, 7, 8. Payment Block Set/Removed, Data Updated (from Change Docs)
SELECT h~objectid, h~username, h~udate, h~utime, p~fname, p~value_new, p~value_old
FROM cdhdr AS h
JOIN cdpos AS p ON h~objectclas = p~objectclas AND h~objectid = p~objectid AND h~changenr = p~changenr
INTO TABLE @DATA(lt_changes)
WHERE h~objectclas = 'BELEGV'
AND h~udate IN s_erdat.
LOOP AT lt_changes INTO DATA(ls_change).
ls_event_log-invoicenumber = |{ ls_change-objectid+10(10) }{ ls_change-objectid(4) }|.
ls_event_log-username = ls_change-username.
ls_event_log-eventtime = |{ ls_change-udate } { ls_change-utime(2) }:{ ls_change-utime+2(2) }:{ ls_change-utime+4(2) }|.
IF ls_change-fname = 'ZLSPR'. " Payment Block
IF ls_change-value_old IS INITIAL AND ls_change-value_new IS NOT INITIAL.
ls_event_log-activityname = 'Payment Block Set'.
ls_event_log-paymentblockreason = ls_change-value_new.
ELSEIF ls_change-value_old IS NOT INITIAL AND ls_change-value_new IS INITIAL.
ls_event_log-activityname = 'Payment Block Removed'.
ls_event_log-paymentblockreason = ''.
ELSE.
CONTINUE.
ENDIF.
ELSE.
ls_event_log-activityname = 'Invoice Data Updated'.
ENDIF.
* --- Need to select other attributes based on invoice number
* --- ... appending to lt_event_log ...
ENDLOOP.
* --- 9. Invoice Posted
SELECT CONCAT( bkpf~belnr, bkpf~gjahr ) AS invoicenumber,
'Invoice Posted' AS activityname,
CONCAT( bkpf~cpudt, bkpf~cputm ) AS eventtime,
bkpf~usnam AS username,
bkpf~bukrs AS companycode,
bseg~lifnr AS vendornumber,
bseg~wrbtr AS amountincompanycodecurrency,
bseg~zfBDT AS paymentduedate,
bkpf~blart AS documenttype,
bseg~zlspr AS paymentblockreason,
bseg~ebeln AS purchasingdocument
FROM bkpf
JOIN bseg ON bkpf~bukrs = bseg~bukrs AND bkpf~belnr = bseg~belnr AND bkpf~gjahr = bseg~gjahr
INTO TABLE @DATA(lt_posted)
WHERE bkpf~cpudt IN @s_erdat
AND bkpf~bukrs IN @s_bukrs
AND bkpf~blart IN @s_blart
AND bseg~koart = 'K'. " Vendor line
LOOP AT lt_posted INTO DATA(ls_posted).
ls_event_log-invoicenumber = ls_posted-invoicenumber.
ls_event_log-activityname = ls_posted-activityname.
ls_event_log-eventtime = |{ ls_posted-eventtime(8) } { ls_posted-eventtime+8(2) }:{ ls_posted-eventtime+10(2) }:{ ls_posted-eventtime+12(2) }|.
ls_event_log-username = ls_posted-username.
ls_event_log-companycode = ls_posted-companycode.
ls_event_log-vendornumber = ls_posted-vendornumber.
ls_event_log-amountincompanycodecurrency = ls_posted-amountincompanycodecurrency.
ls_event_log-paymentduedate = ls_posted-paymentduedate.
ls_event_log-documenttype = ls_posted-documenttype.
ls_event_log-paymentblockreason = ls_posted-paymentblockreason.
ls_event_log-purchasingdocument = ls_posted-purchasingdocument.
APPEND ls_event_log TO lt_event_log.
ENDLOOP.
* --- 10. Payment Proposal Created
SELECT CONCAT( regup~belnr, regup~gjahr ) AS invoicenumber,
'Payment Proposal Created' AS activityname,
CONCAT( reguh~erfdt, reguh~erfzt ) AS eventtime,
reguh~erfbu AS username,
regup~bukrs AS companycode,
regup~lifnr AS vendornumber,
regup~wrbtr AS amountincompanycodecurrency,
'' AS paymentduedate,
regup~blart AS documenttype,
'' AS paymentblockreason,
'' AS purchasingdocument
FROM regup
JOIN reguh ON regup~laufd = reguh~laufd AND regup~laufi = reguh~laufi
INTO TABLE @DATA(lt_proposal)
WHERE reguh~erfdt IN @s_erdat
AND regup~bukrs IN @s_bukrs.
LOOP AT lt_proposal INTO DATA(ls_proposal).
ls_event_log-invoicenumber = ls_proposal-invoicenumber.
ls_event_log-activityname = ls_proposal-activityname.
ls_event_log-eventtime = |{ ls_proposal-eventtime(8) } { ls_proposal-eventtime+8(2) }:{ ls_proposal-eventtime+10(2) }:{ ls_proposal-eventtime+12(2) }|.
ls_event_log-username = ls_proposal-username.
ls_event_log-companycode = ls_proposal-companycode.
ls_event_log-vendornumber = ls_proposal-vendornumber.
ls_event_log-amountincompanycodecurrency = ls_proposal-amountincompanycodecurrency.
APPEND ls_event_log TO lt_event_log.
ENDLOOP.
* --- 11, 12. Payment Executed / Late Payment Executed
SELECT CONCAT( belnr, gjahr ) AS invoicenumber,
augdt,
zfBDT
FROM bsak
INTO TABLE @DATA(lt_cleared)
WHERE augdt IN @s_erdat
AND bukrs IN @s_bukrs.
LOOP AT lt_cleared INTO DATA(ls_cleared).
IF ls_cleared-augdt > ls_cleared-zfbdt.
ls_event_log-activityname = 'Late Payment Executed'.
ELSE.
ls_event_log-activityname = 'Payment Executed'.
ENDIF.
ls_event_log-invoicenumber = ls_cleared-invoicenumber.
ls_event_log-eventtime = |{ ls_cleared-augdt } 00:00:00|.
* --- Need to select other attributes based on invoice number
* --- ... appending to lt_event_log ...
ENDLOOP.
* --- 13. Invoice Reversed
SELECT CONCAT( stblg, stjah ) AS invoicenumber,
'Invoice Reversed' AS activityname,
CONCAT( cpudt, cputm ) AS eventtime,
usnam AS username,
bukrs AS companycode,
'' AS vendornumber,
'' AS amountincompanycodecurrency,
'' AS paymentduedate,
blart AS documenttype,
'' AS paymentblockreason,
'' AS purchasingdocument
FROM bkpf
INTO TABLE @DATA(lt_reversed)
WHERE stblg IS NOT NULL
AND cpudt IN @s_erdat
AND bukrs IN @s_bukrs.
LOOP AT lt_reversed INTO DATA(ls_reversed).
ls_event_log-invoicenumber = ls_reversed-invoicenumber.
ls_event_log-activityname = ls_reversed-activityname.
ls_event_log-eventtime = |{ ls_reversed-eventtime(8) } { ls_reversed-eventtime+8(2) }:{ ls_reversed-eventtime+10(2) }:{ ls_reversed-eventtime+12(2) }|.
ls_event_log-username = ls_reversed-username.
ls_event_log-companycode = ls_reversed-companycode.
APPEND ls_event_log TO lt_event_log.
ENDLOOP.
* --- Write internal table to CSV file
OPEN DATASET p_path FOR OUTPUT IN TEXT MODE ENCODING UTF-8.
IF sy-subrc <> 0.
MESSAGE 'Error opening file.' TYPE 'E'.
ENDIF.
DATA: lv_line TYPE string.
FIELD-SYMBOLS: <fs_any> TYPE any.
* --- Header row
lv_line = 'InvoiceNumber,ActivityName,EventTime,UserName,CompanyCode,VendorNumber,AmountInCompanyCodeCurrency,PaymentDueDate,DocumentType,PaymentBlockReason,PurchasingDocument'.
TRANSFER lv_line TO p_path.
LOOP AT lt_event_log INTO ls_event_log.
CLEAR lv_line.
DO.
ASSIGN COMPONENT sy-index OF STRUCTURE ls_event_log TO <fs_any>.
IF sy-subrc <> 0.
EXIT.
ENDIF.
IF sy-index = 1.
lv_line = <fs_any>.
ELSE.
CONCATENATE lv_line <fs_any> INTO lv_line SEPARATED BY ','.
ENDIF.
ENDDO.
TRANSFER lv_line TO p_path.
ENDLOOP.
CLOSE DATASET p_path.
WRITE: / 'Extraction complete. File saved to:', p_path.