您的采购到付款发票处理数据模板
您的采购到付款发票处理数据模板
- 建议收集的属性
- 需要跟踪的关键活动
- SAP S/4HANA数据提取指南
采购到付款发票处理属性
| 名称 | 说明 | ||
|---|---|---|---|
| 发票编号 InvoiceNumber | 供应商发票凭证的唯一标识,也是该流程的主要案例标识。 | ||
| 说明 发票编号是SAP S/4HANA中分配给每张供应商发票的唯一标识。它将创建、暂存、审批和付款等相关活动关联起来,形成一个完整的流程实例。 在流程挖掘中,此属性是跟踪每张发票端到端处理过程的基础。它可以还原从收票到最终付款的完整流程,便于在单张发票层面分析周期时间、瓶颈和流程差异。 重要性 这是关联所有相关事件的关键标识,可完整追踪发票在系统中的生命周期。 获取途径 这是会计凭证编号,位于BKPF表的BELNR字段。 示例 190000000119000000451900000132 | |||
| 事件时间 EventTime | 活动发生的准确日期和时间。 | ||
| 说明 事件时间是记录特定活动准确发生时间的时间戳。计算流程不同步骤之间的持续时间、周期时间和等待时间,都需要此数据。 在流程挖掘分析中,准确的时间戳可用于衡量“发票平均周期时间”和“发票审批周期时间”等绩效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)。此关联是三方匹配流程的基础,该流程会核对发票、采购订单和收货记录。 按此属性分析,有助于了解有采购订单和无采购订单发票的处理问题。它对于调查匹配差异、了解流程中采购环节的效率至关重要。 重要性 它将发票关联到采购流程,对于分析匹配差异和采购订单合规情况至关重要。 获取途径 此信息通常位于凭证明细表BSEG的EBELN字段(采购凭证编号)。 示例 450000123445000056784500009012 | |||
| 付款条件 PaymentTerms | 定义与供应商约定的付款条件的代码,例如到期日和折扣期限。 | ||
| 说明 付款条件规定发票的付款规则,包括可享受的提前付款折扣。例如,“Z030”可能表示“30天内付款,不含折扣”。 此属性对于财务规划和优化营运资金至关重要。在流程挖掘中,它用于计算“付款到期日”并判断是否符合提前付款折扣条件,直接支持“提前付款折扣获取率”KPI。 重要性 它定义付款到期日和折扣规则,直接影响按期付款KPI和营运资金管理。 获取途径 位于BSEG表供应商行项目的ZTERM字段(付款条件码)。 示例 0001Z030NT60 | |||
| 冲销原因 ReversalReason | 说明发票凭证被冲销原因的代码。 | ||
| 说明 发票过账错误时,通常会进行冲销。冲销原因代码会说明采取此操作的原因,例如“过账日期错误”或“数据录入错误”。 分析冲销原因有助于发现发票过账流程中的错误规律。根据这些洞察,可以改进培训、加强系统控制或解决反复出现的问题,从而减少财务返工和管理开销。 重要性 它说明发票被取消的原因,直接揭示过账流程中的错误来源和返工情况。 获取途径 位于原始凭证抬头表BKPF的STGRD字段(冲销原因)。 示例 010205 | |||
| 发票日期 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 | |||
| 是否按期付款 IsPaidOnTime | 如果发票在付款到期日当天或之前付清,则此标记为true。 | ||
| 说明 此布尔属性通过比较实际付款日期(即“已执行付款”活动的时间戳)和“付款到期日”得出,为每张发票的付款状态提供明确的二元结果。 这是计算“按期付款率”KPI的核心依据。通过筛选和分析,可了解逾期付款的共同特征,例如相关供应商、公司代码或发票金额。 重要性 它直接衡量是否遵守付款条件,是供应商关系管理和财务运营的重要KPI。 获取途径 将“已执行付款”活动的EventTime与PaymentDueDate属性进行比较计算得出。(付款日期 <= PaymentDueDate)。 示例 truefalse | |||
| 是否自动执行 IsAutomated | 用于标记某项活动是否由自动化系统用户执行。 | ||
| 说明 如果与活动关联的用户是已知系统账户或批处理账户(如“WF-BATCH”或“SAP_SYSTEM”),此布尔属性的值为true。它有助于区分人工处理步骤和自动化处理步骤。 此属性对于计算“发票自动化率”KPI至关重要。通过分析流程中哪些环节已实现自动化,组织可以衡量自动化举措的成效,并发现进一步减少人工工作、提高效率的机会。 重要性 它区分人工活动和系统驱动的活动,是衡量自动化率和发现进一步自动化机会的基础。 获取途径 根据UserName属性得出。通过映射或规则将特定用户ID归类为“自动化”。 示例 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中创建了发票凭证。用户保存新发票凭证时即可记录此事件,凭证可能处于暂存或预记账状态。 | ||
| 重要性 此活动标志着发票处理周期的开始。分析此事件与其他事件之间的时间,对于衡量整体处理周期至关重要。 获取途径 此事件的时间取自凭证抬头表中的创建日期和时间(CPUDT、CPUTM),通常为BKPF;物流发票则通常为RBKP。FB60、MIRO或MIR7等事务代码(BKPF-TCODE)可用于判断创建方式。 采集 使用发票凭证的BKPF-CPUDT和BKPF-CPUTM作为创建时间戳。 事件类型 explicit | |||
| 发票已冲销 | 此活动表示冲销先前已过账的发票凭证。对于错误发票,这是一个终止事件,之后通常会重新正确录入发票。 | ||
| 重要性 冲销表明流程前期未能发现严重错误。跟踪冲销频率及根本原因,对于改进流程、减少财务差错至关重要。 获取途径 创建冲销凭证时即可识别冲销。原始凭证抬头(BKPF)中会记录冲销凭证编号(BKPF-STBLG),反之亦然。冲销凭证的过账日期就是事件时间。 采集 识别BKPF-STBLG字段有值的凭证,并使用冲销凭证的过账日期。 事件类型 explicit | |||
| 发票已批准 | 此活动表示指定审批人已批准发票。审批工作流成功结束或设置了放行标志时,即可记录此事件。 | ||
| 重要性 这是解除发票付款限制的关键节点。审批延误是常见瓶颈,跟踪此活动有助于找出审批缓慢的人员或流程步骤。 获取途径 可根据SAP工作流中的最终放行步骤,或跟踪发票及其采购凭证相关数据表中的放行状态字段变化来判断。 采集 根据工作流完成事件或凭证放行状态字段的变化进行判断。 事件类型 inferred | |||
| 发票已过账 | 这是重要的财务事件,表示暂存或已批准的发票正式过账至总账。此操作确认了对供应商的应付款项。 | ||
| 重要性 过账是一个重要节点,标志着数据录入和审批阶段结束,财务结算阶段开始。从发票创建到过账所需的时间,是衡量内部处理效率的重要指标。 获取途径 此事件通过凭证抬头中的过账日期(BKPF-BUDAT)识别。对于先暂存的凭证,状态变为已过账的时间就是该事件的时间戳。 采集 使用过账日期(BKPF-BUDAT)作为事件时间戳。 事件类型 explicit | |||
| 已执行付款 | 这是标准流程中的最后一项活动,表示付款已完成且发票已清账,款项已支付给供应商。 | ||
| 重要性 这标志着采购到付款发票生命周期的结束。计算端到端总周期时间,以及衡量是否按期付款,都离不开这一节点。 获取途径 此事件取自供应商行项目的清账凭证信息。清账日期(BSEG-AUGDT)和清账凭证(BSEG-AUGBL)表明付款已完成。 采集 使用已清账供应商行项目中的清账日期(BSEG-AUGDT)。 事件类型 explicit | |||
| 发票已拒绝 | 表示发票在审批过程中被拒绝。此事件会触发返工,需要更正发票并重新提交。 | ||
| 重要性 发票被拒绝是流程低效和数据质量问题的重要信号。分析拒绝频率和原因,有助于发现改进和培训机会。 获取途径 可根据SAP工作流中的特定状态更新(如“已拒绝”),或取消当前审批工作流并将发票退回处理人员的事件进行判断。 采集 根据表明发票被拒绝的工作流状态变化进行判断。 事件类型 inferred | |||
| 发票提交审批 | 此活动标志着发票正式审批工作流的启动。通常可根据发票状态变为“待审批”或系统生成工作流项目来判断。 | ||
| 重要性 这是衡量审批周期时间的起点。了解审批何时开始,对于发现审批工作流中的瓶颈至关重要。 获取途径 通常可根据与发票对象(如BUS2081)关联的SAP Business Workflow(SWW_WI2OBJ表)启动记录,或凭证抬头中自定义状态字段的变化来判断。 采集 根据与发票凭证相关的工作流项目创建记录进行判断。 事件类型 inferred | |||
| 已创建付款建议 | 在付款运行过程中,系统会选中发票并将其纳入付款建议。这是自动付款流程的第一步。 | ||
| 重要性 此活动表示付款意向。从这一步到最终执行付款之间的延迟,可能反映付款运行、审批或银行通信方面的问题。 获取途径 可在付款运行表中找到相关信息,具体来说是REGUP表,其中包含纳入付款建议的项目。对应REGUH表中的运行日期提供了时间戳。 采集 识别发票何时出现在付款建议运行生成的REGUP表中。 事件类型 explicit | |||
| 已逾期付款 | 这是一个计算得出的事件,表示发票付款在计算出的到期日之后执行。该事件通过比较两个日期字段得出。 | ||
| 重要性 此活动直接用于衡量按期付款KPI,并帮助识别经常逾期付款的供应商或业务单元。逾期付款可能损害供应商关系并导致罚款。 获取途径 通过比较清账日期(BSEG-AUGDT)和净到期日计算得出。到期日则根据基准日期(BSEG-ZFBDT)和付款条件(BSEG-ZTERM)计算。 采集 比较BSEG-AUGDT >(BSEG-ZFBDT + 付款期限天数)得出。 事件类型 calculated | |||
| 暂存发票 | 表示发票已录入系统,但尚未过账至总账。暂存可用于保存尚未填写完整的发票,或在过账前留待审核。 | ||
| 重要性 暂存表示流程有意暂停。跟踪暂存发票的持续时间和频率,有助于找出正式过账和审批周期开始前发生延误的原因。 获取途径 可通过用于暂存的事务代码(如MIR7、FV60)识别此类凭证,也可检查BKPF表中的特定状态字段或VBKPF等专用暂存凭证表。 采集 识别通过暂存事务创建的凭证,或检查凭证是否处于暂存状态。 事件类型 explicit | |||
| 更新发票数据 | 此活动表示发票凭证创建后发生了更改。审批被拒后返工或更正错误时,通常会出现此情况。 | ||
| 重要性 频繁更新可能意味着返工,也可能表明数据录入时存在质量问题。跟踪这些更改有助于量化纠错所需的工作,并找出常见错误。 获取途径 SAP会在更改凭证表CDHDR(抬头)和CDPOS(项目)中记录关键字段的变化。筛选与相关发票对象有关的更改记录,即可生成事件。 采集 从CDHDR和CDPOS表中提取与发票对象相关的更改事件。 事件类型 explicit | |||
| 解除付款冻结 | 表示问题已解决,先前设置的付款冻结已解除,发票因此重新具备付款资格。 | ||
| 重要性 从设置冻结到解除冻结的时间,就是流程异常的处理时间。缩短这段时间有助于提高效率并改善供应商关系。 获取途径 付款冻结码字段(BSEG-ZLSPR)被清空时,即可记录此事件。CDHDR和CDPOS表会记录此项更改,并提供解除冻结的时间戳。 采集 通过更改凭证(CDHDR/CDPOS)识别BSEG-ZLSPR字段被清空的时间。 事件类型 explicit | |||
| 设置付款冻结 | 此活动表示有意冻结发票付款。常见原因包括价格或数量差异,或贷项凭证尚未处理。 | ||
| 重要性 付款冻结是造成付款延迟和供应商争议的主要原因。分析冻结的频率、持续时间和原因,对于提高按时付款率至关重要。 获取途径 跟踪发票行项目中的付款冻结码字段(BSEG-ZLSPR)变化,即可记录此事件。CDHDR和CDPOS更改日志会提供设置冻结的时间戳和操作用户。 采集 通过更改凭证(CDHDR/CDPOS)识别BSEG-ZLSPR字段被填入的时间。 事件类型 explicit | |||
数据提取指南
步骤
- 前提条件与授权:确保执行提取的用户在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数据库。
- 设置提取参数:执行查询前,先定义关键参数。例如,指定提取日期范围,将
CreationDate设为介于'YYYY-MM-DD'和'YYYY-MM-DD'之间。还需确定要纳入的CompanyCode值,以限定数据提取范围。 - 自定义SQL查询:将提供的SQL查询复制到所选SQL客户端或数据提取工具中。仔细检查
'{StartDate}'、'{EndDate}'和('{CompanyCode1}', '{CompanyCode2}')等占位符,并替换为上一步确定的实际值。您可能还需要根据具体SAP配置调整工作流状态字段名称。 - 执行查询:在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,以及用于跟踪发票付款冻结、工作流状态等属性历史变更的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)筛选,以避免查询耗时过长。
查询示例 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 HANA租户或数据库架构,并获取只读数据库用户,且该用户有权访问所需SAP表以及获准使用的变更历史表或工作流表。请使用SAP HANA Database Explorer、SAP HANA Studio或获准的SQL客户端。不要使用生产环境的写入权限。
- 确认目标S/4HANA系统中的实际表名和列名。ACDOCA、BKPF和RBKP可作为标准起点,但工作流、暂存、付款建议、付款执行、变更历史和付款冻结历史可能使用特定版本或客户专属对象。请将查询中方括号内的每个占位符替换为已在系统目录中核实、且符合相关业务含义的对象。
- 使用[开始时间戳]和[结束时间戳]定义提取时段。提取足够宽的单据和事件时间范围,通常为三至六个月;如果付款到期日或逾期付款可能超出发票创建时段,请相应延长范围。
- 使用已配置的发票键识别发票案例。查询以InvoiceNumber作为案例标识,并在必要时在内部与公司代码和会计年度组合,避免编号冲突。请确认业务定义是否要求在ProcessMind案例标识中加入会计年度或公司代码。
- 从RBKP、BKPF和ACDOCA映射已过账的发票数据。使用RBKP获取发票抬头信息,使用BKPF获取会计凭证时间戳和用户信息,并在可用时使用ACDOCA获取供应商、金额、采购凭证、清账及付款相关会计信息。请勿假设每种过账场景都会填充所有字段。
- 从已核实的系统专属数据源映射暂存、审批、驳回、更新、付款冻结、冲销、付款建议和付款执行事件。请将占位数据源视图替换为获准使用的视图或表,这些视图或表应提供事件时间戳、发票引用、用户、状态以及新旧值。每项活动都必须作为明确的事件行输出,因为ProcessMind不会推断事件。
- 在非生产环境或只读会话中执行完整SQL查询。检查执行计划,并在适当情况下按公司代码、单据类型、会计年度和事件时间戳限制查询。避免对大型日记账表和历史表进行不受限制的联接。
- 按下文所述检查结果。确认所有必需列均存在,每个事件都已填充EventTime,每个案例都已填充InvoiceNumber;如果相应源数据存在,还应确认13种活动名称均有记录。
- 将结果导出为ProcessMind支持的分隔文件,建议使用UTF-8编码的CSV。每行对应一个事件,列包括InvoiceNumber、ActivityName、EventTime、UserName、CompanyCode、VendorNumber、AmountInCompanyCodeCurrency、PaymentDueDate、DocumentType、PaymentBlockReason和PurchasingDocument。统一时间戳时区,并保留标识符的前导零。
- 将事件日志上传到ProcessMind,并将InvoiceNumber设为案例标识、ActivityName设为活动列、EventTime设为时间戳列。如果ProcessMind配置支持额外的案例键,请采用与提取时相同的复合键策略。
配置
- 日期范围:初次提取时使用滚动的三至六个月时段。如果审批、付款、清账、冲销或逾期付款事件可能发生在发票创建时段之后,请纳入更多历史数据。
- 公司范围:按[公司代码筛选条件]筛选,并确认所选公司代码均获准用于数据提取。
- 单据范围:按[单据类型筛选条件]筛选,并纳入目标流程中的供应商发票、贷项通知单、暂存发票和冲销单据类型。
- 案例标识:按流程定义使用InvoiceNumber。如果发票编号并非全局唯一,请在源结果中保留公司代码和会计年度,或根据ProcessMind模型配置复合案例键。
- 事件来源:核实工作流状态变更、审批决定、暂存、变更历史、付款冻结、付款建议和付款执行的实际数据源。这些数据源因S/4HANA版本、启用范围、工作流设计和客户扩展而异。
- 时间戳规则:选择业务事件发生时间,而非数据提取时间。记录时区,并在上传前统一转换所有事件时间戳。
- 金额规则:使用公司代码货币金额,并确认所选数据源中的借贷符号是否一致。除非已验证发票案例的汇总规则,否则不要汇总日记账行。
- 付款规则:明确“付款已执行”指清账、付款凭证过账、银行执行还是其他业务里程碑。请使用符合约定流程定义的数据源。
- 逾期付款:仅当Payment Executed发生在PaymentDueDate之后时,才输出Late Payment Executed。查询会明确计算此事件,不依赖ProcessMind推断。
- 性能:按日期、公司代码、单据类型和相关会计年度限制源数据读取。仅选择必需列,检查查询计划;如果源历史数据量较大,可将获准的中间视图实体化。
- 前提条件:需要直接连接SAP HANA、拥有所选对象的读取权限和元数据查看权限,并能访问相关应付账款、总账、采购、工作流和付款数据。请确认已取得所需SAP许可证、数据库访问政策许可和数据保护审批。
- 安全:使用只读技术用户保护供应商和付款数据,并遵循组织的传输、审计、数据脱敏和凭据管理政策。
查询示例 sql
WITH
invoice_base AS (
SELECT
r.INV_DOC_NO AS InvoiceNumber,
r.COMPANY_CODE AS CompanyCode,
r.FISCAL_YEAR AS FiscalYear,
r.DOCUMENT_TYPE AS DocumentType,
r.VENDOR_NO AS VendorNumber,
r.GROSS_AMOUNT_CC AS AmountInCompanyCodeCurrency,
r.PAYMENT_DUE_DATE AS PaymentDueDate,
r.PURCHASING_DOCUMENT AS PurchasingDocument,
r.CREATED_AT AS InvoiceCreatedAt,
r.CREATED_BY AS InvoiceCreatedBy
FROM [Your RBKP invoice header source] r
WHERE r.CREATED_AT >= '[Start timestamp]'
AND r.CREATED_AT < '[End timestamp]'
AND r.COMPANY_CODE IN ([Company code filter])
AND r.DOCUMENT_TYPE IN ([Document type filter])
),
posted_accounting AS (
SELECT
b.INV_DOC_NO AS InvoiceNumber,
b.COMPANY_CODE AS CompanyCode,
b.FISCAL_YEAR AS FiscalYear,
b.ACCOUNTING_DOCUMENT AS AccountingDocument,
b.POSTING_DATE AS PostingDate,
b.CREATED_AT AS PostedAt,
b.CREATED_BY AS PostedBy,
b.REVERSAL_DOCUMENT AS ReversalDocument,
b.REVERSED_DOCUMENT AS ReversedDocument
FROM [Your BKPF accounting document source] b
WHERE b.CREATED_AT >= '[Start timestamp]'
AND b.CREATED_AT < '[End timestamp]'
AND b.COMPANY_CODE IN ([Company code filter])
),
journal_attributes AS (
SELECT
a.COMPANY_CODE AS CompanyCode,
a.FISCAL_YEAR AS FiscalYear,
a.ACCOUNTING_DOCUMENT AS AccountingDocument,
MAX(a.VENDOR_NO) AS VendorNumber,
SUM(a.AMOUNT_IN_COMPANY_CODE_CURRENCY) AS AmountInCompanyCodeCurrency,
MAX(a.PURCHASING_DOCUMENT) AS PurchasingDocument,
MAX(a.CLEARING_DATE) AS ClearingDate,
MAX(a.CLEARING_DOCUMENT) AS ClearingDocument
FROM [Your ACDOCA universal journal source] a
WHERE a.COMPANY_CODE IN ([Company code filter])
AND a.POSTING_DATE >= '[Start date]'
AND a.POSTING_DATE < '[End date]'
GROUP BY
a.COMPANY_CODE,
a.FISCAL_YEAR,
a.ACCOUNTING_DOCUMENT
),
source_events AS (
SELECT
e.INV_DOC_NO AS InvoiceNumber,
e.COMPANY_CODE AS CompanyCode,
e.FISCAL_YEAR AS FiscalYear,
e.EVENT_TIMESTAMP AS EventTime,
e.EVENT_USER AS UserName,
e.PAYMENT_BLOCK_REASON AS PaymentBlockReason,
e.PURCHASING_DOCUMENT AS PurchasingDocument,
e.VENDOR_NO AS VendorNumber,
e.AMOUNT_IN_COMPANY_CODE_CURRENCY AS AmountInCompanyCodeCurrency,
e.PAYMENT_DUE_DATE AS PaymentDueDate,
e.DOCUMENT_TYPE AS DocumentType,
e.EVENT_TYPE AS SourceEventType
FROM [Your verified invoice event and workflow source] e
WHERE e.EVENT_TIMESTAMP >= '[Start timestamp]'
AND e.EVENT_TIMESTAMP < '[End timestamp]'
AND e.COMPANY_CODE IN ([Company code filter])
),
base_events AS (
SELECT
i.InvoiceNumber,
'Invoice Document Created' AS ActivityName,
i.InvoiceCreatedAt AS EventTime,
i.InvoiceCreatedBy AS UserName,
i.CompanyCode,
COALESCE(i.VendorNumber, j.VendorNumber) AS VendorNumber,
COALESCE(i.AmountInCompanyCodeCurrency, j.AmountInCompanyCodeCurrency) AS AmountInCompanyCodeCurrency,
i.PaymentDueDate,
i.DocumentType,
CAST(NULL AS NVARCHAR(20)) AS PaymentBlockReason,
COALESCE(i.PurchasingDocument, j.PurchasingDocument) AS PurchasingDocument
FROM invoice_base i
LEFT JOIN posted_accounting p
ON p.InvoiceNumber = i.InvoiceNumber
AND p.CompanyCode = i.CompanyCode
AND p.FiscalYear = i.FiscalYear
LEFT JOIN journal_attributes j
ON j.CompanyCode = p.CompanyCode
AND j.FiscalYear = p.FiscalYear
AND j.AccountingDocument = p.AccountingDocument
WHERE i.InvoiceCreatedAt IS NOT NULL
UNION ALL
SELECT
s.InvoiceNumber,
'Invoice Parked' AS ActivityName,
s.EventTime,
s.UserName,
s.CompanyCode,
COALESCE(s.VendorNumber, i.VendorNumber) AS VendorNumber,
COALESCE(s.AmountInCompanyCodeCurrency, i.AmountInCompanyCodeCurrency) AS AmountInCompanyCodeCurrency,
COALESCE(s.PaymentDueDate, i.PaymentDueDate) AS PaymentDueDate,
COALESCE(s.DocumentType, i.DocumentType) AS DocumentType,
s.PaymentBlockReason,
COALESCE(s.PurchasingDocument, i.PurchasingDocument) AS PurchasingDocument
FROM source_events s
LEFT JOIN invoice_base i ON i.InvoiceNumber = s.InvoiceNumber AND i.CompanyCode = s.CompanyCode AND i.FiscalYear = s.FiscalYear
WHERE s.SourceEventType = 'PARKED'
UNION ALL
SELECT s.InvoiceNumber, 'Invoice Sent For Approval', s.EventTime, s.UserName, s.CompanyCode, COALESCE(s.VendorNumber, i.VendorNumber), COALESCE(s.AmountInCompanyCodeCurrency, i.AmountInCompanyCodeCurrency), COALESCE(s.PaymentDueDate, i.PaymentDueDate), COALESCE(s.DocumentType, i.DocumentType), s.PaymentBlockReason, COALESCE(s.PurchasingDocument, i.PurchasingDocument)
FROM source_events s LEFT JOIN invoice_base i ON i.InvoiceNumber = s.InvoiceNumber AND i.CompanyCode = s.CompanyCode AND i.FiscalYear = s.FiscalYear
WHERE s.SourceEventType = 'SENT_FOR_APPROVAL'
UNION ALL
SELECT s.InvoiceNumber, 'Invoice Approved', s.EventTime, s.UserName, s.CompanyCode, COALESCE(s.VendorNumber, i.VendorNumber), COALESCE(s.AmountInCompanyCodeCurrency, i.AmountInCompanyCodeCurrency), COALESCE(s.PaymentDueDate, i.PaymentDueDate), COALESCE(s.DocumentType, i.DocumentType), s.PaymentBlockReason, COALESCE(s.PurchasingDocument, i.PurchasingDocument)
FROM source_events s LEFT JOIN invoice_base i ON i.InvoiceNumber = s.InvoiceNumber AND i.CompanyCode = s.CompanyCode AND i.FiscalYear = s.FiscalYear
WHERE s.SourceEventType = 'APPROVED'
UNION ALL
SELECT s.InvoiceNumber, 'Invoice Rejected', s.EventTime, s.UserName, s.CompanyCode, COALESCE(s.VendorNumber, i.VendorNumber), COALESCE(s.AmountInCompanyCodeCurrency, i.AmountInCompanyCodeCurrency), COALESCE(s.PaymentDueDate, i.PaymentDueDate), COALESCE(s.DocumentType, i.DocumentType), s.PaymentBlockReason, COALESCE(s.PurchasingDocument, i.PurchasingDocument)
FROM source_events s LEFT JOIN invoice_base i ON i.InvoiceNumber = s.InvoiceNumber AND i.CompanyCode = s.CompanyCode AND i.FiscalYear = s.FiscalYear
WHERE s.SourceEventType = 'REJECTED'
UNION ALL
SELECT s.InvoiceNumber, 'Invoice Data Updated', s.EventTime, s.UserName, s.CompanyCode, COALESCE(s.VendorNumber, i.VendorNumber), COALESCE(s.AmountInCompanyCodeCurrency, i.AmountInCompanyCodeCurrency), COALESCE(s.PaymentDueDate, i.PaymentDueDate), COALESCE(s.DocumentType, i.DocumentType), s.PaymentBlockReason, COALESCE(s.PurchasingDocument, i.PurchasingDocument)
FROM source_events s LEFT JOIN invoice_base i ON i.InvoiceNumber = s.InvoiceNumber AND i.CompanyCode = s.CompanyCode AND i.FiscalYear = s.FiscalYear
WHERE s.SourceEventType = 'DATA_UPDATED'
UNION ALL
SELECT s.InvoiceNumber, 'Payment Block Set', s.EventTime, s.UserName, s.CompanyCode, COALESCE(s.VendorNumber, i.VendorNumber), COALESCE(s.AmountInCompanyCodeCurrency, i.AmountInCompanyCodeCurrency), COALESCE(s.PaymentDueDate, i.PaymentDueDate), COALESCE(s.DocumentType, i.DocumentType), s.PaymentBlockReason, COALESCE(s.PurchasingDocument, i.PurchasingDocument)
FROM source_events s LEFT JOIN invoice_base i ON i.InvoiceNumber = s.InvoiceNumber AND i.CompanyCode = s.CompanyCode AND i.FiscalYear = s.FiscalYear
WHERE s.SourceEventType = 'PAYMENT_BLOCK_SET'
UNION ALL
SELECT s.InvoiceNumber, 'Payment Block Removed', s.EventTime, s.UserName, s.CompanyCode, COALESCE(s.VendorNumber, i.VendorNumber), COALESCE(s.AmountInCompanyCodeCurrency, i.AmountInCompanyCodeCurrency), COALESCE(s.PaymentDueDate, i.PaymentDueDate), COALESCE(s.DocumentType, i.DocumentType), s.PaymentBlockReason, COALESCE(s.PurchasingDocument, i.PurchasingDocument)
FROM source_events s LEFT JOIN invoice_base i ON i.InvoiceNumber = s.InvoiceNumber AND i.CompanyCode = s.CompanyCode AND i.FiscalYear = s.FiscalYear
WHERE s.SourceEventType = 'PAYMENT_BLOCK_REMOVED'
UNION ALL
SELECT
i.InvoiceNumber,
'Invoice Posted',
p.PostedAt,
p.PostedBy,
i.CompanyCode,
COALESCE(i.VendorNumber, j.VendorNumber),
COALESCE(i.AmountInCompanyCodeCurrency, j.AmountInCompanyCodeCurrency),
i.PaymentDueDate,
i.DocumentType,
CAST(NULL AS NVARCHAR(20)),
COALESCE(i.PurchasingDocument, j.PurchasingDocument)
FROM invoice_base i
INNER JOIN posted_accounting p ON p.InvoiceNumber = i.InvoiceNumber AND p.CompanyCode = i.CompanyCode AND p.FiscalYear = i.FiscalYear
LEFT JOIN journal_attributes j ON j.CompanyCode = p.CompanyCode AND j.FiscalYear = p.FiscalYear AND j.AccountingDocument = p.AccountingDocument
WHERE p.PostedAt IS NOT NULL
UNION ALL
SELECT s.InvoiceNumber, 'Payment Proposal Created', s.EventTime, s.UserName, s.CompanyCode, COALESCE(s.VendorNumber, i.VendorNumber), COALESCE(s.AmountInCompanyCodeCurrency, i.AmountInCompanyCodeCurrency), COALESCE(s.PaymentDueDate, i.PaymentDueDate), COALESCE(s.DocumentType, i.DocumentType), s.PaymentBlockReason, COALESCE(s.PurchasingDocument, i.PurchasingDocument)
FROM source_events s LEFT JOIN invoice_base i ON i.InvoiceNumber = s.InvoiceNumber AND i.CompanyCode = s.CompanyCode AND i.FiscalYear = s.FiscalYear
WHERE s.SourceEventType = 'PAYMENT_PROPOSAL_CREATED'
UNION ALL
SELECT s.InvoiceNumber, 'Payment Executed', s.EventTime, s.UserName, s.CompanyCode, COALESCE(s.VendorNumber, i.VendorNumber), COALESCE(s.AmountInCompanyCodeCurrency, i.AmountInCompanyCodeCurrency), COALESCE(s.PaymentDueDate, i.PaymentDueDate), COALESCE(s.DocumentType, i.DocumentType), s.PaymentBlockReason, COALESCE(s.PurchasingDocument, i.PurchasingDocument)
FROM source_events s LEFT JOIN invoice_base i ON i.InvoiceNumber = s.InvoiceNumber AND i.CompanyCode = s.CompanyCode AND i.FiscalYear = s.FiscalYear
WHERE s.SourceEventType = 'PAYMENT_EXECUTED'
UNION ALL
SELECT s.InvoiceNumber, 'Late Payment Executed', s.EventTime, s.UserName, s.CompanyCode, COALESCE(s.VendorNumber, i.VendorNumber), COALESCE(s.AmountInCompanyCodeCurrency, i.AmountInCompanyCodeCurrency), COALESCE(s.PaymentDueDate, i.PaymentDueDate), COALESCE(s.DocumentType, i.DocumentType), s.PaymentBlockReason, COALESCE(s.PurchasingDocument, i.PurchasingDocument)
FROM source_events s LEFT JOIN invoice_base i ON i.InvoiceNumber = s.InvoiceNumber AND i.CompanyCode = s.CompanyCode AND i.FiscalYear = s.FiscalYear
WHERE s.SourceEventType = 'PAYMENT_EXECUTED'
AND s.EventTime > CAST(COALESCE(s.PaymentDueDate, i.PaymentDueDate) AS TIMESTAMP)
UNION ALL
SELECT
i.InvoiceNumber,
'Invoice Reversed',
p.ReversalEventAt,
p.ReversalUser,
i.CompanyCode,
COALESCE(i.VendorNumber, j.VendorNumber),
COALESCE(i.AmountInCompanyCodeCurrency, j.AmountInCompanyCodeCurrency),
i.PaymentDueDate,
i.DocumentType,
CAST(NULL AS NVARCHAR(20)),
COALESCE(i.PurchasingDocument, j.PurchasingDocument)
FROM invoice_base i
INNER JOIN [Your verified reversal event source] p ON p.INV_DOC_NO = i.InvoiceNumber AND p.COMPANY_CODE = i.CompanyCode AND p.FISCAL_YEAR = i.FiscalYear
LEFT JOIN journal_attributes j ON j.CompanyCode = p.COMPANY_CODE AND j.FiscalYear = p.FISCAL_YEAR AND j.AccountingDocument = p.ACCOUNTING_DOCUMENT
WHERE p.ReversalEventAt IS NOT NULL
)
SELECT
InvoiceNumber,
ActivityName,
EventTime,
UserName,
CompanyCode,
VendorNumber,
AmountInCompanyCodeCurrency,
PaymentDueDate,
DocumentType,
PaymentBlockReason,
PurchasingDocument
FROM base_events
WHERE InvoiceNumber IS NOT NULL
AND EventTime IS NOT NULL
ORDER BY InvoiceNumber, EventTime, ActivityName; 步骤
- 打开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):程序可选配特定供应商编号筛选条件,便于开展定向分析或测试。
- 输出文件配置:程序应提供参数,用于指定应用服务器上的输出文件路径和字段分隔符,例如逗号或分号。
- 前提条件:执行程序的用户或系统账户需要具备开发权限,能够通过
SE38创建并运行ABAP程序;还需要对FI、MM和Basis表拥有广泛的读取权限,包括BKPF、BSEG、RBKP、RSEG、CDHDR、CDPOS和工作流表。
查询示例 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. 立即优化SAP S/4HANA中的采购到付款发票处理
精准定位瓶颈,将发票周期时间缩短30%或以上。
无需信用卡,几分钟即可完成设置。