您的采购到付款:采购订单数据模板
您的采购到付款:采购订单数据模板
- 用于详细分析的建议属性
- 流程中需要跟踪的关键活动
- 分步数据提取指南
采购到付款:采购订单属性
| 名称 | 说明 | ||
|---|---|---|---|
| 事件时间 EventTime | 表示活动发生时间的时间戳。 | ||
| 说明 此属性记录流程中每项活动发生的准确日期和时间,是流程挖掘中所有时间分析的基础。 事件时间用于按时间顺序排列活动,构建流程路径。此外,它还是计算各类时长指标的依据,例如活动间周期时间、等待时间和处理时间。这些指标对于绩效分析和瓶颈识别至关重要。 重要性 此时间戳对于正确排列事件,以及计算周期时间、交付周期和等待时间等绩效指标至关重要。 获取途径 与特定活动相关的时间戳字段,例如创建日期(变更对应EKKO-AEDAT)或过账日期(收货对应MKPF-BUDAT)。通常需要整合多个表中的数据。 示例 2023-04-15T10:00:00Z2023-04-15T14:30:00Z2023-05-01T09:15:00Z | |||
| 活动 ActivityName | 采购订单流程中发生的业务事件或步骤的名称。 | ||
| 说明 此属性描述采购订单生命周期中的具体操作或状态变更,例如“创建采购订单”“批准采购订单”或“过账收货”。这些活动按顺序排列,构成流程路径。 分析活动的顺序和频率是流程挖掘的核心。它有助于发现实际流程、将其与设计模型进行比较、识别瓶颈(例如“收到发票”后等待时间过长),并量化返工情况(例如反复出现“变更采购订单”活动)。 重要性 此属性定义流程中的各个步骤,可用于直观呈现和分析端到端流程、分析流程变体并识别瓶颈。 获取途径 通常根据多个表和字段推导,例如EKKO/EKPO中的状态字段或CDHDR/CDPOS中的变更单据日志,以表示关键业务节点。 示例 创建采购订单批准采购订单过账收货收到发票 | |||
| 采购订单 PurchaseOrderNumber | 采购订单(PO)的唯一标识符,是跟踪采购生命周期的主要案例ID。 | ||
| 说明 采购订单编号是关联所有相关活动的核心标识符,涵盖从初始创建到最终收货和完成的全过程。它是流程挖掘分析中的案例标识符。 分析时,按此编号对事件分组,即可还原每张采购订单的完整历程。这对于计算周期时间、分析流程变体,以及发现单张订单特有的瓶颈或偏差至关重要。 重要性 这是将所有采购事件串联为单一端到端流程的关键字段,可详细分析每张采购订单的生命周期。 获取途径 此属性位于SAP S/4HANA的EKKO表中,字段为EBELN。 示例 450001712345000171244500017125 | |||
| 数据最近更新时间 LastDataUpdate | 数据最近一次从源系统刷新或提取的时间戳。 | ||
| 说明 此属性表示所分析数据的新鲜度,显示最近一次从SAP S/4HANA提取数据的日期和时间。 了解数据最近更新时间,有助于用户判断分析结果的时效性。用户可以据此确认查看的是实时信息,还是某个时间点的数据快照,从而判断基于分析采取的行动是否仍然适用。 重要性 告知用户数据的时效性,帮助他们理解分析结果的背景和适用性。 获取途径 这是在数据提取、转换和加载(ETL)过程中添加的元数据时间戳。 示例 2024-05-21T02:00:00Z2024-05-20T02:00:00Z2024-05-19T02:00:00Z | |||
| 源系统 SourceSystem | 标识数据提取自哪个源系统。 | ||
| 说明 此属性指定事件数据的来源系统,例如“SAP S/4HANA生产系统”或“SAP ECC”。 在包含多个系统的环境中,此字段对于数据溯源、故障排查以及确保正确解读不同来源的数据至关重要。它有助于了解数据背景,也可用于筛选特定系统环境中的分析数据。 重要性 提供数据来源的重要背景信息,对于多系统环境中的数据治理、验证和分析至关重要。 获取途径 通常在数据提取、转换和加载(ETL)过程中添加静态值,用于标记数据集的来源。 示例 S4H_PROD_100ECC_EU_200S4H_US_300 | |||
| 供应商ID VendorId | 提供商品或服务的供应商的唯一标识符。 | ||
| 说明 供应商ID是重要的主数据,可将采购订单关联到特定供应商,并用于采购流程中的沟通、交付和付款。 在流程挖掘中,此属性可用于按供应商细分绩效分析。它是“供应商交付周期绩效”和“按供应商统计的退货率”等仪表板的重要数据,有助于识别可靠的供应商,以及可能造成延误或质量问题的供应商。 重要性 支持以供应商为中心的分析,帮助评估绩效、识别表现较好或较差的供应商,并优化供应链。 获取途径 此属性位于SAP S/4HANA的EKKO表中,字段为LIFNR。 示例 100023100045100088 | |||
| 净额总计 TotalNetAmount | 采购订单的总金额,不含税费和运费。 | ||
| 说明 此属性表示采购订单的净金额,是反映采购交易规模的重要财务数据。 此金额对于财务分析至关重要。例如,按金额将采购订单分为高额和低额订单,比较其流程路径是否不同;也可以据此确定分析优先级,重点关注财务风险较高或对业务影响较大的高额订单。 重要性 支持基于金额的分析,帮助按金额细分采购订单,并优先改进高支出领域的流程。 获取途径 此属性位于SAP S/4HANA的EKKO表中,字段为NETWR。 示例 1500.0025000.50125.75 | |||
| 用户 UserName | 执行特定活动的用户标识符。 | ||
| 说明 此属性记录负责创建、变更或批准单据的SAP用户ID,可追溯系统中的操作。 按用户分析有助于发现培训需求、了解工作量分配和个人绩效。例如,您可以查看特定用户是否经常对应较长的审批时间或频繁的审批后变更,并据此优化资源管理和流程。 重要性 明确责任归属,并支持个人或团队层面的绩效分析,帮助发现培训机会或资源限制。 获取途径 此信息可在EKKO中的ERNAM(创建者)等字段中找到,也可从变更单据表的用户字段(CDHDR-USERNAME)获取。 示例 CB9980000012JSMITHRROE | |||
| 要求交付日期 RequestedDeliveryDate | 业务方要求供应商交付商品或服务的日期。 | ||
| 说明 此属性指定采购订单中约定的目标交付日期,是衡量供应商交付绩效的基准。 在流程挖掘中,可将此日期与实际收货日期(“过账收货”的时间戳)进行比较,计算“供应商按时交付率”关键绩效指标。分析与此日期的偏差,有助于评估供应商可靠性并管理供应链风险。 重要性 作为衡量供应商按时交付绩效的基准,是供应链管理和运营规划的重要关键绩效指标。 获取途径 此字段位于计划行表EKET中,字段名为EINDT。 示例 2023-06-012023-06-152023-07-01 | |||
| 采购申请 PurchaseRequisitionNumber | 发起采购订单的采购申请(PR)标识符。 | ||
| 说明 此属性将采购订单关联到其来源采购申请。直接创建的采购订单可能没有采购申请。 此关联对于从初始申请开始分析完整的端到端采购流程至关重要。它支持“采购申请审批时间”等关键绩效指标,也是识别“未经授权采购”的基础,即采购订单在没有事先批准的采购申请时创建。 重要性 将采购订单关联到初始申请,支持端到端流程分析,并识别不合规的未经授权采购。 获取途径 此属性位于SAP S/4HANA的EKPO表(采购订单项目级别)中,字段为BANFN。 示例 1001005110010052 | |||
| 采购订单单据类型 DocumentType | 用于区分不同采购订单类型的分类,例如标准采购订单、服务采购订单或库存调拨订单。 | ||
| 说明 单据类型是SAP中的关键配置项,可控制采购订单的流程路径、编号范围和字段。企业可据此针对不同场景调整采购流程。 按单据类型分析流程对于了解流程差异至关重要。例如,标准货物采购订单的流程可能与服务采购订单或库存调拨订单大不相同。此属性可用于筛选和比较不同流程路径,发现具体的改进机会。 重要性 对采购订单进行分类,便于比较不同采购流程,并帮助解释流程路径和周期时间的差异。 获取途径 此属性位于SAP S/4HANA的EKKO表中,字段为BSART。 示例 NBFOUB | |||
| 供应商准时交付 SupplierOnTimeDelivery | 计算得出的标记,用于指示收货是否在要求的交付日期当天或之前过账。 | ||
| 说明 此布尔属性通过比较“收货已过账”活动的时间戳与“要求交付日期”得出。如果收货在要求日期当天或之前完成,则标记为'true'。 此属性直接支持“供应商准时交付率”KPI。借助此属性,用户可以轻松筛选准时或延迟交付,便于在供应商绩效仪表板中分析表现并进行供应商评分。 重要性 直接衡量供应商的可靠性,为准时交付KPI提供依据,并支持有效管理供应商绩效。 获取途径 通过比较“收货已过账”活动的时间戳与'RequestedDeliveryDate'属性计算得出。 示例 truefalse | |||
| 公司代码 CompanyCode | 创建采购订单的法人实体或公司的标识。 | ||
| 说明 公司代码代表组织内独立的会计核算单位。与采购订单相关的所有财务交易都会记入特定公司代码。 这是重要的组织属性,可用于筛选和比较不同法人实体的采购流程。按公司代码分析,有助于发现流程执行差异、效率水平差异或组织内合规率差异。 重要性 可按法人实体细分流程分析,便于比较企业不同部门的绩效和合规情况。 获取途径 此属性位于SAP S/4HANA表EKKO的字段BUKRS中。 示例 101017102000 | |||
| 工厂 Plant | 接收货物或提供服务的运营设施或地点。 | ||
| 说明 在SAP中,工厂是生产或存储货物,或提供服务的实体地点,也是物流和计划的重要组成部分。 按工厂细分流程分析,可发现采购流程在不同地区或场所的差异。例如,您可以查看某些工厂是否交付周期更长或货物退货率更高,从而发现当地物流或质量控制问题。 重要性 支持按地点分析,突出不同运营场所、工厂或仓库之间的流程绩效差异。 获取途径 此属性位于SAP S/4HANA表EKPO的字段WERKS中。 示例 10101710DE01 | |||
| 是否为未经授权的采购 IsMaverickSpend | 计算得出的标记,用于指示采购订单是否在没有事先批准的采购申请的情况下创建。 | ||
| 说明 此布尔标记在数据处理过程中生成。如果采购订单没有关联的采购申请,或创建采购订单时绕过了标准审批工作流,则标记为'true'。 此属性直接支持“未经授权采购识别”仪表板及相关KPI。它有助于量化不合规采购行为的程度,便于企业针对特定部门或用户群体加强采购政策和控制。 重要性 直接识别不合规采购,帮助量化流程偏差,并落实财务控制和采购政策。 获取途径 计算字段:根据特定单据类型的'PurchaseRequisitionNumber'是否为空,或通过分析事件顺序得出。 示例 truefalse | |||
| 是否返工 IsRework | 计算得出的标记,用于指示采购订单是否经过返工,例如审批后变更或货物退回。 | ||
| 说明 此布尔属性通过分析每个采购订单的活动顺序计算得出。如果“采购订单已更改”事件发生在“采购订单已批准”事件之后,或存在“货物已退回”事件,则标记为'true'。 此标记简化了“直通处理率”KPI的计算。您可以轻松筛选并查看所有需要人工干预或更正的采购订单,从而量化返工的成本和频率。 重要性 通过标记发生返工的案例,帮助量化流程效率低下的问题。这对于计算直通处理率和识别偏差根因至关重要。 获取途径 根据活动顺序计算。逻辑会检查“采购订单已更改”事件是否发生在审批之后,或是否存在“货物已退回”事件。 示例 truefalse | |||
| 物料编号 MaterialNumber | 所采购物料或货物的标识。 | ||
| 说明 物料编号是SAP为每条物料主数据记录分配的唯一代码,用于该物料相关的所有交易,包括采购、库存管理和销售。 按物料编号或物料组分析,可按商品类别进行分析。这有助于识别特定类型物料的采购流程是否效率较低、周期较长或更容易发生退货,为品类管理提供参考。 重要性 支持按商品类别分析,帮助识别与特定产品或物料相关的流程问题或供应商绩效问题。 获取途径 此属性位于SAP S/4HANA表EKPO的字段MATNR中。 示例 RM100-100FG210SERV-CONSULT | |||
| 采购组 PurchasingGroup | 负责特定采购活动的买方或买方团队。 | ||
| 说明 采购组由负责特定采购活动、物料或供应商的买方或买方团队组成,是供应商的主要联系人。 与采购组织相比,此属性可更细致地分析工作量和绩效。您可以借此识别负担过重的团队、衡量不同买方团队的效率,并了解哪些团队更容易出现流程偏差,例如未经授权的采购。 重要性 细致呈现买方团队的绩效,支持在团队层面分析工作量、效率和流程遵循情况。 获取途径 此属性位于SAP S/4HANA表EKKO的字段EKGRP中。 示例 001002N00 | |||
| 采购组织 PurchasingOrganization | 负责采购物料和服务并与供应商谈判的组织单位。 | ||
| 说明 采购组织是采购业务中的重要组织单位,可按集团、公司或工厂层级设置,并负责所有采购活动。 按采购组织分析流程,有助于评估不同采购团队或地区的效率和绩效,也能揭示不同组织单位在供应商谈判、流程合规或审批延迟方面的差异。 重要性 可比较不同采购部门或地区的绩效,帮助发现最佳实践和待改进领域。 获取途径 此属性位于SAP S/4HANA表EKKO的字段EKORG中。 示例 10101710US01 | |||
| 项目类别 ItemCategory | 用于对采购订单行项目分类,例如标准、寄售、分包或服务。 | ||
| 说明 项目类别决定如何管理和处理特定物料或服务的采购,并影响后续步骤,例如收货和发票核验。 此属性有助于根据采购内容分析流程变体。例如,服务项目需要服务录入单,其流程与标准库存项目有明显差异。按项目类别分析有助于解释这些差异,并有针对性地改进流程。 重要性 通过区分不同类型的采购(如货物、服务或分包),说明流程中的差异。 获取途径 此属性位于SAP S/4HANA表EKPO的字段PSTYP中。 示例 093 | |||
采购到付款:采购订单活动
| 活动 | 说明 | ||
|---|---|---|---|
| 创建采购申请 | 此活动表示正式提出商品或服务需求,标志着采购流程启动。用户保存新的采购申请单据时(例如使用事务代码ME51N),系统会明确记录此事件。 | ||
| 重要性 这是许多采购订单生命周期的主要起点。分析从此事件到采购订单创建的时间,有助于发现寻源和内部处理中的延误。 获取途径 记录在EBAN表(采购申请)中。创建事件的时间戳可从EBAN对象的变更历史表CDHDR和CDPOS中获取。 采集 在EBAN表中创建单据时记录此事件。 事件类型 explicit | |||
| 创建采购订单 | 表示正式采购订单单据已创建,可以引用采购申请,也可以不引用。采购订单单据首次在系统中保存时,系统会明确记录此事件。 | ||
| 重要性 此活动可作为流程的另一个起点,尤其适用于分析未经授权的采购。它也是跟踪采购订单整体处理时间的基础事件。 获取途径 记录在采购订单抬头表EKKO中。单据的创建日期(AEDAT)和时间直接存储在此表中。 采集 采购订单单据在EKKO表中的创建时间戳(AEDAT)。 事件类型 explicit | |||
| 完成采购订单 | 此活动表示从物流角度看,采购订单项目已关闭。当“交货完成”和“最终发票”标识均已设置时,可推断此事件。 | ||
| 重要性 这是采购订单生命周期分析的终点。衡量到达此事件所需的时间,即可计算采购运营的端到端周期时间。 获取途径 根据采购订单项目表EKPO中的状态标识推断。当“交货完成”标识(ELIKZ)和“最终发票”标识(EREKZ)均设为true时,即发生此事件。 采集 当EKPO表中的ELIKZ和EREKZ字段均标记为完成时,可根据变更日志推断此事件。 事件类型 inferred | |||
| 批准采购申请 | 表示经理或指定审批人正式批准采购申请。通常可根据申请单据的状态变更推断此事件,表明申请已准备好转为采购订单。 | ||
| 重要性 这是跟踪审批周期时间和发现瓶颈的关键节点。此处的延误会直接影响采购订单的创建和发送速度。 获取途径 根据EBAN表中的发布状态字段推断,例如FRGZU(发布标识)。时间戳取自记录最终发布状态设置时间的变更单据(CDHDR/CDPOS)。 采集 根据EBAN表中发布状态字段的变更日志(CDHDR/CDPOS)推断。 事件类型 inferred | |||
| 批准采购订单 | 表示采购订单已获得所有必要的内部审批,获准发送给供应商。此事件可根据采购订单发布策略的状态变更推断。 | ||
| 重要性 这是衡量审批效率和审批后返工情况的关键节点。分析采购订单从创建到获批的时间,有助于发现内部流程延误。 获取途径 根据EKKO表中的发布标识(FRGKE)推断。通过查看变更历史(CDHDR/CDPOS),确定该字段更新为“已发布”状态的时间戳。 采集 根据EKKO表中发布标识字段(FRGKE)的变更日志推断。 事件类型 inferred | |||
| 收到发票 | 表示供应商发票录入SAP系统并关联到相应采购订单。此财务过账会创建会计凭证。 | ||
| 重要性 这是连接采购流程与应付账款流程的关键节点。通过此节点,可以分析收货与发票处理之间的时间。 获取途径 会计凭证抬头记录在BKPF表中,行项目记录在BSEG或统一日记账ACDOCA中。凭证通过RSEG表关联到采购订单。 采集 会计凭证抬头表BKPF中的凭证录入日期(CPUDT)。 事件类型 explicit | |||
| 过账收货 | 表示实际收到供应商交付的货物,并在系统中登记。此交易会更新采购订单历史记录。 | ||
| 重要性 这是一个重要节点,标志着供应商交付周期结束,并开始内部发票核验流程。跟踪按时交付率时,此节点必不可少。 获取途径 作为物料凭证记录在MKPF(抬头)和MSEG(项目)表中,并通过特定移动类型(例如101)关联到采购订单历史表EKBE。 采集 通过EKBE关联的物料凭证抬头表MKPF中的过账日期(BUDAT)。 事件类型 explicit | |||
| 删除采购订单 | 表示取消或逻辑删除采购订单项目或整张单据。用户在单据上设置删除标识时,系统会记录此事件。 | ||
| 重要性 此活动是流程的另一个终点,表示流程失败或取消。分析采购订单被删除的原因,有助于发现需求计划或需求定义中的问题。 获取途径 根据采购订单抬头表EKKO或项目表EKPO中的删除标识(LOEKZ)获取。时间戳取自变更单据(CDHDR/CDPOS)。 采集 变更单据(CDHDR/CDPOS)中设置删除标识(LOEKZ)时的时间戳。 事件类型 explicit | |||
| 变更采购订单 | 此活动表示采购订单创建后发生了修改,例如数量、价格或交付日期变更。系统会在变更日志中明确记录此事件。 | ||
| 重要性 跟踪变更,尤其是审批后的变更,对于发现流程低效、返工和潜在合规问题至关重要。频繁变更可能意味着初始规格不准确。 获取途径 记录在采购订单对象(EINKBELEG)的变更单据表CDHDR(抬头)和CDPOS(项目)中。每次变更都会生成详细的日志条目。 采集 EKKO或EKPO表中的关键字段发生变更时,会在CDHDR/CDPOS中记录此事件。 事件类型 explicit | |||
| 向供应商发送采购订单 | 表示采购订单发送给供应商的时间,例如通过EDI、电子邮件或打印方式发送。此事件通常记录在系统的输出管理日志中。 | ||
| 重要性 此活动标志着供应商交付周期的真正开始。要从供应商收到订单时起准确衡量其绩效,这一时间点至关重要。 获取途径 从输出控制表NAST中获取,该表记录发送给采购单据的消息。可使用相关输出类型(例如EDI、电子邮件)的日期和时间。 采集 NAST表中该采购订单首次成功输出消息的时间戳。 事件类型 inferred | |||
| 录入服务确认 | 此活动表示确认采购订单中指定的服务已完成。创建服务录入单时,系统会明确记录此事件。 | ||
| 重要性 对于服务类采购,此活动相当于收货。跟踪服务交付周期并确保及时向供应商付款时,此节点至关重要。 获取途径 创建服务录入单时记录,数据存储在ESSR(抬头)和ESLL(行项目)表中。创建日期即为时间戳。 采集 ESSR表中服务录入单据的创建日期。 事件类型 explicit | |||
| 支付发票 | 表示通过付款批次或手动付款最终结清供应商发票。此财务交易会创建清账凭证。 | ||
| 重要性 虽然此活动在技术上属于付款流程,但将其纳入分析有助于完整了解采购到付款周期,也是分析付款条款和付款绩效的关键。 获取途径 付款记录为BKPF/ACDOCA中的清账凭证。BSEG或ACDOCA表中发票行项目的清账日期(AUGDT)表示付款事件。 采集 发票凭证的清账日期(AUGDT),可在BSEG或ACDOCA中查找。 事件类型 explicit | |||
| 退回货物 | 表示此前收到的货物已退还给供应商,通常是由于质量问题、损坏或发错货。系统会将此事件明确记录为冲销收货移动。 | ||
| 重要性 此活动反映返工情况,也可能揭示供应商质量或订单准确性问题。某个供应商或物料的退货频率较高,说明可能存在问题。 获取途径 作为具有特定退货移动类型(例如122)的物料凭证记录。此事件记录在MKPF/MSEG中,并关联到EKBE历史表中的采购订单。 采集 EKBE中具有退货移动类型的物料凭证过账日期。 事件类型 explicit | |||
数据提取指南
步骤
- 准备工作与访问权限:确保您拥有具备相应授权的用户,可查询SAP S/4HANA系统中的核心数据服务(CDS)视图。您可以通过SAP HANA Studio、Eclipse的ABAP开发工具(ADT),或支持连接SAP HANA数据库的SQL第三方数据提取工具访问。
- 确认系统连接信息:获取SAP S/4HANA系统所需的连接参数,包括主机、实例编号和身份验证凭据。
- 连接数据库:使用您选择的SQL客户端,连接存放CDS视图的SAP S/4HANA数据库。
- 准备SQL查询:将本文档查询部分提供的完整SQL查询复制到SQL编辑器中。该查询用于提取所有必需的活动和属性。
- 设置筛选参数:找到查询中的占位值,将_start_date和_end_date替换为所需的分析日期范围(例如,'20230101'和'20231231')。修改poh.CompanyCode筛选条件,加入您要分析的公司代码。
- 执行查询:在S/4HANA数据库中运行修改后的SQL查询。所需时间取决于数据量和指定的日期范围。
- 检查初步结果:查询完成后,在SQL客户端中快速检查输出。确认包含不同活动、时间戳已正确填充,并核实案例ID(PurchaseOrderNumber)一致。
- 导出数据:使用SQL工具将完整结果集导出为CSV(逗号分隔值)文件。请使用UTF-8编码,避免字符显示异常。
- 准备上传:上传到ProcessMind之前,打开CSV文件,确认列标题与数据要求中定义的属性完全一致(PurchaseOrderNumber、ActivityName、EventTime等)。如果导出工具更改了列名,请进行调整。
- 上传到ProcessMind:将最终CSV文件上传到ProcessMind项目。导入时,将文件中的列映射到相应的案例ID、活动和时间戳字段。
配置
- 使用的主要CDS视图:提取逻辑依赖一组标准且语义丰富的CDS视图,主要包括:
- I_PurchaseOrderItemAPI01:采购订单项目核心数据。
- I_PurchaseRequisitionItemAPI01:采购申请详情。
- I_MaterialDocumentItem:收货和退货等货物移动记录。
- I_ServiceEntrySheetAPI01:服务确认事件。
- I_SupplierInvoiceAPI01:供应商发票信息。
- I_OperationalAcctgDocItem:关联发票与财务凭证,以跟踪付款。
- I_ChangeDocument:采购订单变更记录。
- 日期范围筛选:必须应用日期范围筛选,以控制性能和数据量。查询使用_start_date和_end_date占位符筛选采购订单创建日期(PurchaseOrderDate)。建议首次提取3至6个月的数据。
- 组织筛选:查询应始终按CompanyCode筛选,将提取范围限定在相关业务单元。您也可以在主PO_base公用表表达式中添加PurchaseOrderType或PurchasingOrganization筛选条件,进一步缩小范围。
- 准备工作:执行查询的用户必须拥有上述所有CDS视图的SELECT授权。通常,S/4HANA会通过特定业务或分析角色授予这些视图的访问权限。权限不足时,查询将失败。
查询示例 sql
WITH PO_base AS (
SELECT
poh.PurchaseOrder AS PurchaseOrderNumber,
poi.PurchaseOrderItem AS PurchaseOrderItem,
poh.CompanyCode,
poh.PurchaseOrderType AS DocumentType,
poh.Supplier AS VendorId,
poh.PurchaseOrderDate,
poi.PurchaseRequisition AS PurchaseRequisitionNumber,
poi.NetPriceAmount * poi.OrderQuantity AS TotalNetAmount, -- Note: This is item-level net amount
poh.CreationDate AS POCreationDate,
poh.CreationTime AS POCreationTime,
poh.LastChangeDateTime AS POLastChangeDateTime,
poi.IsDeleted,
poi.DeliveryIsCompleted,
poi.FinalInvoiceIsExpected,
poi.GoodsReceiptIsExpected,
poi.LastGoodsReceiptDate,
poi.LastInvoiceReceiptDate
FROM I_PurchaseOrderAPI01 poh
JOIN I_PurchaseOrderItemAPI01 poi
ON poh.PurchaseOrder = poi.PurchaseOrder
WHERE
poh.PurchaseOrderDate BETWEEN '_start_date' AND '_end_date' -- Placeholder: e.g., '20230101' and '20230630'
AND poh.CompanyCode IN ('[YourCompanyCode]') -- Placeholder: e.g., '1010'
)
-- 1. Purchase Requisition Created
SELECT
po.PurchaseOrderNumber,
'Purchase Requisition Created' AS ActivityName,
CAST(CONCAT(pr.CreationDate, 'T', pr.CreationTime) AS TIMESTAMP) AS EventTime,
'[Your S/4HANA System ID]' AS SourceSystem, -- Placeholder
CURRENT_UTCTIMESTAMP AS LastDataUpdate,
po.VendorId,
pr.CreatedByUser AS UserName,
po.TotalNetAmount,
po.PurchaseRequisitionNumber,
NULL AS RequestedDeliveryDate, -- Available in PR, add if needed
po.DocumentType
FROM I_PurchaseRequisitionItemAPI01 pr
JOIN PO_base po
ON pr.PurchaseRequisition = po.PurchaseRequisitionNumber AND pr.PurchaseRequisitionItem = po.PurchaseOrderItem
UNION ALL
-- 2. Purchase Requisition Approved
SELECT
po.PurchaseOrderNumber,
'Purchase Requisition Approved' AS ActivityName,
CAST(CONCAT(pr.PurReqnReleaseDate, 'T', '000000') AS TIMESTAMP) AS EventTime, -- Time is not available in this view
'[Your S/4HANA System ID]' AS SourceSystem,
CURRENT_UTCTIMESTAMP AS LastDataUpdate,
po.VendorId,
NULL AS UserName, -- Approver info requires complex joins
po.TotalNetAmount,
po.PurchaseRequisitionNumber,
NULL AS RequestedDeliveryDate,
po.DocumentType
FROM I_PurchaseRequisitionItemAPI01 pr
JOIN PO_base po
ON pr.PurchaseRequisition = po.PurchaseRequisitionNumber AND pr.PurchaseRequisitionItem = po.PurchaseOrderItem
WHERE
pr.PurReqnReleaseDate IS NOT NULL
UNION ALL
-- 3. Purchase Order Created
SELECT
po.PurchaseOrderNumber,
'Purchase Order Created' AS ActivityName,
CAST(CONCAT(po.POCreationDate, 'T', po.POCreationTime) AS TIMESTAMP) AS EventTime,
'[Your S/4HANA System ID]' AS SourceSystem,
CURRENT_UTCTIMESTAMP AS LastDataUpdate,
po.VendorId,
poh.CreatedByUser AS UserName,
po.TotalNetAmount,
po.PurchaseRequisitionNumber,
poi.RequestedDeliveryDate,
po.DocumentType
FROM PO_base po
JOIN I_PurchaseOrderAPI01 poh ON po.PurchaseOrderNumber = poh.PurchaseOrder
JOIN I_PurchaseOrderItemAPI01 poi ON po.PurchaseOrderNumber = poi.PurchaseOrder AND po.PurchaseOrderItem = poi.PurchaseOrderItem
UNION ALL
-- 4. Purchase Order Approved
SELECT DISTINCT
po.PurchaseOrderNumber,
'Purchase Order Approved' AS ActivityName,
CAST(poh.ReleaseDate AS TIMESTAMP) AS EventTime, -- Assuming ReleaseDate reflects final approval
'[Your S/4HANA System ID]' AS SourceSystem,
CURRENT_UTCTIMESTAMP AS LastDataUpdate,
po.VendorId,
NULL AS UserName, -- Approver info requires complex joins
po.TotalNetAmount,
po.PurchaseRequisitionNumber,
poi.RequestedDeliveryDate,
po.DocumentType
FROM PO_base po
JOIN I_PurchaseOrderAPI01 poh ON po.PurchaseOrderNumber = poh.PurchaseOrder
JOIN I_PurchaseOrderItemAPI01 poi ON po.PurchaseOrderNumber = poi.PurchaseOrder AND po.PurchaseOrderItem = poi.PurchaseOrderItem
WHERE poh.ReleaseDate IS NOT NULL
UNION ALL
-- 5. Purchase Order Sent to Vendor
SELECT DISTINCT
po.PurchaseOrderNumber,
'Purchase Order Sent to Vendor' AS ActivityName,
CAST(poh.ReleaseDate AS TIMESTAMP) AS EventTime, -- Using ReleaseDate as a proxy for sending time
'[Your S/4HANA System ID]' AS SourceSystem,
CURRENT_UTCTIMESTAMP AS LastDataUpdate,
po.VendorId,
NULL AS UserName,
po.TotalNetAmount,
po.PurchaseRequisitionNumber,
poi.RequestedDeliveryDate,
po.DocumentType
FROM PO_base po
JOIN I_PurchaseOrderAPI01 poh ON po.PurchaseOrderNumber = poh.PurchaseOrder
JOIN I_PurchaseOrderItemAPI01 poi ON po.PurchaseOrderNumber = poi.PurchaseOrder AND po.PurchaseOrderItem = poi.PurchaseOrderItem
WHERE poh.ReleaseDate IS NOT NULL
UNION ALL
-- 6. Purchase Order Changed
SELECT DISTINCT
ch.OBJECTID AS PurchaseOrderNumber,
'Purchase Order Changed' AS ActivityName,
CAST(CONCAT(ch.ChangeDocumentDate, 'T', ch.ChangeDocumentTime) AS TIMESTAMP) AS EventTime,
'[Your S/4HANA System ID]' AS SourceSystem,
CURRENT_UTCTIMESTAMP AS LastDataUpdate,
po.VendorId,
ch.UserName,
po.TotalNetAmount,
po.PurchaseRequisitionNumber,
NULL AS RequestedDeliveryDate,
po.DocumentType
FROM I_ChangeDocument ch
JOIN PO_base po ON ch.OBJECTID = po.PurchaseOrderNumber
WHERE
ch.ObjectClassName = 'EINKBELEG' -- Object Class for Purchase Documents
AND CAST(CONCAT(ch.ChangeDocumentDate, 'T', ch.ChangeDocumentTime) AS TIMESTAMP) > CAST(CONCAT(po.POCreationDate, 'T', po.POCreationTime) AS TIMESTAMP)
UNION ALL
-- 7. Goods Receipt Posted
SELECT
po.PurchaseOrderNumber,
'Goods Receipt Posted' AS ActivityName,
CAST(CONCAT(md.PostingDate, 'T', md.CreationTime) AS TIMESTAMP) AS EventTime,
'[Your S/4HANA System ID]' AS SourceSystem,
CURRENT_UTCTIMESTAMP AS LastDataUpdate,
po.VendorId,
md.CreatedByUser AS UserName,
po.TotalNetAmount,
po.PurchaseRequisitionNumber,
NULL AS RequestedDeliveryDate,
po.DocumentType
FROM I_MaterialDocumentItem md
JOIN PO_base po
ON md.PurchaseOrder = po.PurchaseOrderNumber AND md.PurchaseOrderItem = po.PurchaseOrderItem
WHERE
md.GoodsMovementType = '101'
UNION ALL
-- 8. Services Confirmation Entered
SELECT
po.PurchaseOrderNumber,
'Services Confirmation Entered' AS ActivityName,
CAST(se.PostingDate AS TIMESTAMP) AS EventTime,
'[Your S/4HANA System ID]' AS SourceSystem,
CURRENT_UTCTIMESTAMP AS LastDataUpdate,
po.VendorId,
se.CreatedByUser AS UserName,
po.TotalNetAmount,
po.PurchaseRequisitionNumber,
NULL AS RequestedDeliveryDate,
po.DocumentType
FROM I_ServiceEntrySheetAPI01 se
JOIN PO_base po
ON se.PurchaseOrder = po.PurchaseOrderNumber AND se.PurchaseOrderItem = po.PurchaseOrderItem
UNION ALL
-- 9. Goods Returned
SELECT
po.PurchaseOrderNumber,
'Goods Returned' AS ActivityName,
CAST(CONCAT(md.PostingDate, 'T', md.CreationTime) AS TIMESTAMP) AS EventTime,
'[Your S/4HANA System ID]' AS SourceSystem,
CURRENT_UTCTIMESTAMP AS LastDataUpdate,
po.VendorId,
md.CreatedByUser AS UserName,
po.TotalNetAmount,
po.PurchaseRequisitionNumber,
NULL AS RequestedDeliveryDate,
po.DocumentType
FROM I_MaterialDocumentItem md
JOIN PO_base po
ON md.PurchaseOrder = po.PurchaseOrderNumber AND md.PurchaseOrderItem = po.PurchaseOrderItem
WHERE
md.GoodsMovementType = '122'
UNION ALL
-- 10. Invoice Received
SELECT
po.PurchaseOrderNumber,
'Invoice Received' AS ActivityName,
CAST(inv.PostingDate AS TIMESTAMP) AS EventTime,
'[Your S/4HANA System ID]' AS SourceSystem,
CURRENT_UTCTIMESTAMP AS LastDataUpdate,
po.VendorId,
inv.CreatedByUser AS UserName,
po.TotalNetAmount,
po.PurchaseRequisitionNumber,
NULL AS RequestedDeliveryDate,
po.DocumentType
FROM I_SupplierInvoiceAPI01 inv
JOIN PO_base po
ON inv.PurchaseOrderReference = po.PurchaseOrderNumber
WHERE
inv.DebitCreditCode = 'H' -- 'H' for Credit (Supplier Invoice)
UNION ALL
-- 11. Invoice Paid
SELECT
po.PurchaseOrderNumber,
'Invoice Paid' AS ActivityName,
CAST(doc.ClearingDate AS TIMESTAMP) AS EventTime,
'[Your S/4HANA System ID]' AS SourceSystem,
CURRENT_UTCTIMESTAMP AS LastDataUpdate,
po.VendorId,
doc.CreatedByUser AS UserName,
po.TotalNetAmount,
po.PurchaseRequisitionNumber,
NULL AS RequestedDeliveryDate,
po.DocumentType
FROM I_SupplierInvoiceAPI01 inv
JOIN I_OperationalAcctgDocItem doc
ON inv.AccountingDocument = doc.AccountingDocument
JOIN PO_base po
ON inv.PurchaseOrderReference = po.PurchaseOrderNumber
WHERE
doc.IsCleared = 'X' AND doc.ClearingDate IS NOT NULL
UNION ALL
-- 12. Purchase Order Completed
SELECT
po.PurchaseOrderNumber,
'Purchase Order Completed' AS ActivityName,
CAST(GREATEST(po.LastGoodsReceiptDate, po.LastInvoiceReceiptDate) AS TIMESTAMP) AS EventTime,
'[Your S/4HANA System ID]' AS SourceSystem,
CURRENT_UTCTIMESTAMP AS LastDataUpdate,
po.VendorId,
'SYSTEM' AS UserName,
po.TotalNetAmount,
po.PurchaseRequisitionNumber,
NULL AS RequestedDeliveryDate,
po.DocumentType
FROM PO_base po
WHERE
po.DeliveryIsCompleted = 'X'
AND (po.FinalInvoiceIsExpected = 'X' OR po.GoodsReceiptIsExpected = '') -- Logic for completion
AND GREATEST(po.LastGoodsReceiptDate, po.LastInvoiceReceiptDate) IS NOT NULL
UNION ALL
-- 13. Purchase Order Deleted
SELECT
po.PurchaseOrderNumber,
'Purchase Order Deleted' AS ActivityName,
CAST(po.POLastChangeDateTime AS TIMESTAMP) AS EventTime,
'[Your S/4HANA System ID]' AS SourceSystem,
CURRENT_UTCTIMESTAMP AS LastDataUpdate,
po.VendorId,
NULL AS UserName, -- User who set the flag is in change docs
po.TotalNetAmount,
po.PurchaseRequisitionNumber,
NULL AS RequestedDeliveryDate,
po.DocumentType
FROM PO_base po
WHERE
po.IsDeleted = 'X' 步骤
- 确认可以直接通过SQL访问包含EKKO和EKPO的SAP HANA架构,并取得所需的读取权限。将架构和连接占位符替换为系统配置的值。
- 使用[Start timestamp]和[End timestamp]定义提取时间范围。首次加载建议选择三至六个月。只有在报告范围要求时,才应用公司代码和凭证类型筛选条件。
- 从EKKO和EKPO中识别采购订单,并保留采购订单编号、供应商、凭证类型、公司代码、创建日期、创建时间和项目级属性。汇总EKPO净值,计算采购订单级别的TotalNetAmount。
- 从EBAN提取采购申请创建和审批事件,并使用EKPO中的采购申请编号和项目引用,将采购申请与采购订单关联。由于采购申请审批标记和时间戳会因版本配置而异,请根据[Configure based on your system]中的说明,按当前SAP版本的发布策略配置审批状态和时间戳表达式。
- 提取采购订单创建、审批、变更、删除和完成事件。创建事件使用EKKO中的创建日期和时间。审批、变更和删除事件需要发布记录或变更历史数据源。如果所选架构未提供相关历史记录,请使用[Your table name]和[Your column name]配置相应的数据源表达式。
- 从适用的输出、物料凭证、服务录入、发票、会计和清账数据源中提取供应商沟通、收货、服务确认、退货、收票和发票付款事件。由于仅靠EKKO和EKPO无法提供这些数据,查询中为系统专属对象设置了明确的数据源占位符。
- 将所有来源事件统一为相同的事件日志结构。每行必须包含PurchaseOrderNumber、ActivityName、EventTime、SourceSystem、LastDataUpdate、VendorId、UserName、TotalNetAmount、PurchaseRequisitionNumber、RequestedDeliveryDate和DocumentType。如果数据源中存在同一活动的多个有效事件,请保留所有记录。
- 验证时间戳,仅删除完全重复的数据源行,不要推断没有数据源记录的活动。ProcessMind会按原样读取事件日志,因此流程可视化中显示的每项活动都必须有明确的数据行。
- 将结果导出为分隔文件或数据库结果集,包含一行标题且列名保持稳定。使用ProcessMind上传配置支持的时间戳格式,将PurchaseOrderNumber保留为文本,并通过已配置的ProcessMind数据连接上传完整事件日志。
配置
- 数据源对象:EKKO和EKPO是已确认的采购订单抬头和项目数据源。采购申请、发布状态、变更历史、输出、货物移动、服务录入单、发票、付款和清账所需的其他对象,必须根据SAP S/4HANA版本和当前数据模型进行配置。
- 案例标识符:使用PurchaseOrderNumber作为案例标识符。项目级事件应关联到采购订单编号;如有需要,请在其他特定数据源列中保留项目引用。
- 日期范围:建议从三至六个月开始。加载历史数据时,请分段处理,并核对重叠的时间边界,避免遗漏。
- 筛选条件:根据所需范围配置公司代码、凭证类型、VendorId、采购组织、采购组和事件日期筛选条件。公司代码和凭证类型必须使用目标系统中的有效值。
- 事件定义:如果有相关凭证字段或日志,创建和删除事件可以明确识别。审批和完成属于基于状态的事件,需要配置状态时间戳。不要仅根据当前状态创建事件行,除非有可靠的事件时间戳。
- 性能:尽早按事件日期和组织范围筛选;条件允许时,先汇总EKPO再与事件数据源关联;处理大量历史数据时,按时间段执行。确保数据库统计信息适用,并避免对项目、会计和变更历史数据源进行不受限的关联。
- 刷新时间戳:每次运行时,将LastDataUpdate设为本次提取的执行时间,并填入每一行。
- 准备工作:需要具备SAP HANA连接、所有已配置数据源对象的读取权限,以及采购、库存管理、服务采购、发票校验和应付账款相关数据的访问权限;还需要能够导入所选文件或结果集的ProcessMind连接。
- 系统专属配置:将查询中所有方括号括起的数据源占位符替换为目标SAP S/4HANA系统中经批准的表、视图、列或表达式。请勿在查询或提取配置中暴露凭据。
查询示例 sql
WITH
params AS (
SELECT
CAST('[Start timestamp]' AS TIMESTAMP) AS start_ts,
CAST('[End timestamp]' AS TIMESTAMP) AS end_ts,
CAST(CURRENT_TIMESTAMP AS TIMESTAMP) AS last_data_update,
CAST('[Source system]' AS NVARCHAR(100)) AS source_system
FROM DUMMY
),
po_base AS (
SELECT
h.MANDT,
h.EBELN AS PurchaseOrderNumber,
h.LIFNR AS VendorId,
h.BSART AS DocumentType,
h.BUKRS AS CompanyCode,
CAST(h.AEDAT AS DATE) AS POChangedDate,
CAST(h.AEDAT AS TIMESTAMP) AS POChangedTimestamp,
CAST(h.ERNAM AS NVARCHAR(100)) AS POCreatedBy,
CAST(h.BEDAT AS DATE) AS PODate,
CAST(h.EBELN AS NVARCHAR(20)) AS PurchaseOrderKey,
CAST(SUM(COALESCE(i.NETWR, 0)) AS DECIMAL(23, 2)) AS TotalNetAmount,
CAST(MIN(i.BEDNR) AS NVARCHAR(20)) AS PurchaseRequisitionNumber,
CAST(MIN(i.EINDT) AS DATE) AS RequestedDeliveryDate
FROM EKKO h
INNER JOIN EKPO i
ON i.MANDT = h.MANDT
AND i.EBELN = h.EBELN
WHERE h.AEDAT >= (SELECT start_ts FROM params)
AND h.AEDAT < (SELECT end_ts FROM params)
AND h.BUKRS IN ([Company Code filter])
AND h.BSART IN ([Document Type filter])
GROUP BY
h.MANDT,
h.EBELN,
h.LIFNR,
h.BSART,
h.BUKRS,
h.AEDAT,
h.ERNAM,
h.BEDAT
),
po_items AS (
SELECT
i.MANDT,
i.EBELN AS PurchaseOrderNumber,
i.EBELP,
i.BANFN AS PurchaseRequisitionNumber,
i.BEDNR,
i.EINDT AS RequestedDeliveryDate
FROM EKPO i
),
events AS (
SELECT
p.PurchaseOrderNumber,
'Purchase Requisition Created' AS ActivityName,
CAST(r.[Purchase requisition creation timestamp] AS TIMESTAMP) AS EventTime,
s.source_system AS SourceSystem,
s.last_data_update AS LastDataUpdate,
p.VendorId,
CAST(r.[Purchase requisition created by] AS NVARCHAR(100)) AS UserName,
p.TotalNetAmount,
CAST(r.[Purchase requisition number] AS NVARCHAR(20)) AS PurchaseRequisitionNumber,
p.RequestedDeliveryDate,
p.DocumentType
FROM po_base p
INNER JOIN [Your requisition source table or view] r
ON r.[Client] = p.MANDT
AND r.[Purchase requisition number] = p.PurchaseRequisitionNumber
CROSS JOIN params s
WHERE r.[Purchase requisition creation timestamp] >= s.start_ts
AND r.[Purchase requisition creation timestamp] < s.end_ts
UNION ALL
SELECT
p.PurchaseOrderNumber,
'Purchase Requisition Approved' AS ActivityName,
CAST(r.[Purchase requisition approval timestamp] AS TIMESTAMP) AS EventTime,
s.source_system,
s.last_data_update,
p.VendorId,
CAST(r.[Purchase requisition approver] AS NVARCHAR(100)),
p.TotalNetAmount,
CAST(r.[Purchase requisition number] AS NVARCHAR(20)),
p.RequestedDeliveryDate,
p.DocumentType
FROM po_base p
INNER JOIN [Your requisition approval history table or view] r
ON r.[Client] = p.MANDT
AND r.[Purchase requisition number] = p.PurchaseRequisitionNumber
CROSS JOIN params s
WHERE r.[Purchase requisition approval timestamp] >= s.start_ts
AND r.[Purchase requisition approval timestamp] < s.end_ts
AND r.[Approval status] = '[Approved status value]'
UNION ALL
SELECT
p.PurchaseOrderNumber,
'Purchase Order Created',
CAST(p.PODate AS TIMESTAMP),
s.source_system,
s.last_data_update,
p.VendorId,
p.POCreatedBy,
p.TotalNetAmount,
p.PurchaseRequisitionNumber,
p.RequestedDeliveryDate,
p.DocumentType
FROM po_base p
CROSS JOIN params s
WHERE p.PODate >= CAST(s.start_ts AS DATE)
AND p.PODate < CAST(s.end_ts AS DATE)
UNION ALL
SELECT
p.PurchaseOrderNumber,
'Purchase Order Approved',
CAST(a.[Purchase order approval timestamp] AS TIMESTAMP),
s.source_system,
s.last_data_update,
p.VendorId,
CAST(a.[Approver] AS NVARCHAR(100)),
p.TotalNetAmount,
p.PurchaseRequisitionNumber,
p.RequestedDeliveryDate,
p.DocumentType
FROM po_base p
INNER JOIN [Your purchase order release history table or view] a
ON a.[Client] = p.MANDT
AND a.[Purchase order number] = p.PurchaseOrderNumber
CROSS JOIN params s
WHERE a.[Purchase order approval timestamp] >= s.start_ts
AND a.[Purchase order approval timestamp] < s.end_ts
AND a.[Release status] = '[Approved release status value]'
UNION ALL
SELECT
p.PurchaseOrderNumber,
'Purchase Order Sent to Vendor',
CAST(o.[Output timestamp] AS TIMESTAMP),
s.source_system,
s.last_data_update,
p.VendorId,
CAST(o.[Output user] AS NVARCHAR(100)),
p.TotalNetAmount,
p.PurchaseRequisitionNumber,
p.RequestedDeliveryDate,
p.DocumentType
FROM po_base p
INNER JOIN [Your purchase order output source table or view] o
ON o.[Client] = p.MANDT
AND o.[Purchase order number] = p.PurchaseOrderNumber
CROSS JOIN params s
WHERE o.[Output timestamp] >= s.start_ts
AND o.[Output timestamp] < s.end_ts
AND o.[Output status] = '[Successfully processed output status]'
UNION ALL
SELECT
p.PurchaseOrderNumber,
'Purchase Order Changed',
CAST(c.[Change timestamp] AS TIMESTAMP),
s.source_system,
s.last_data_update,
p.VendorId,
CAST(c.[Changed by] AS NVARCHAR(100)),
p.TotalNetAmount,
p.PurchaseRequisitionNumber,
p.RequestedDeliveryDate,
p.DocumentType
FROM po_base p
INNER JOIN [Your purchase order change history table or view] c
ON c.[Client] = p.MANDT
AND c.[Purchase order number] = p.PurchaseOrderNumber
CROSS JOIN params s
WHERE c.[Change timestamp] >= s.start_ts
AND c.[Change timestamp] < s.end_ts
AND c.[Change indicator] = '[Changed indicator value]'
UNION ALL
SELECT
p.PurchaseOrderNumber,
'Goods Receipt Posted',
CAST(g.[Goods movement timestamp] AS TIMESTAMP),
s.source_system,
s.last_data_update,
p.VendorId,
CAST(g.[Posted by] AS NVARCHAR(100)),
p.TotalNetAmount,
p.PurchaseRequisitionNumber,
p.RequestedDeliveryDate,
p.DocumentType
FROM po_base p
INNER JOIN [Your goods movement source table or view] g
ON g.[Client] = p.MANDT
AND g.[Purchase order number] = p.PurchaseOrderNumber
CROSS JOIN params s
WHERE g.[Goods movement timestamp] >= s.start_ts
AND g.[Goods movement timestamp] < s.end_ts
AND g.[Movement type] IN ([Goods receipt movement types])
AND g.[Reversal indicator] IS NULL
UNION ALL
SELECT
p.PurchaseOrderNumber,
'Services Confirmation Entered',
CAST(v.[Service entry timestamp] AS TIMESTAMP),
s.source_system,
s.last_data_update,
p.VendorId,
CAST(v.[Entered by] AS NVARCHAR(100)),
p.TotalNetAmount,
p.PurchaseRequisitionNumber,
p.RequestedDeliveryDate,
p.DocumentType
FROM po_base p
INNER JOIN [Your service entry sheet source table or view] v
ON v.[Client] = p.MANDT
AND v.[Purchase order number] = p.PurchaseOrderNumber
CROSS JOIN params s
WHERE v.[Service entry timestamp] >= s.start_ts
AND v.[Service entry timestamp] < s.end_ts
AND v.[Service entry status] = '[Accepted service entry status]'
UNION ALL
SELECT
p.PurchaseOrderNumber,
'Goods Returned',
CAST(g.[Goods movement timestamp] AS TIMESTAMP),
s.source_system,
s.last_data_update,
p.VendorId,
CAST(g.[Posted by] AS NVARCHAR(100)),
p.TotalNetAmount,
p.PurchaseRequisitionNumber,
p.RequestedDeliveryDate,
p.DocumentType
FROM po_base p
INNER JOIN [Your goods movement source table or view] g
ON g.[Client] = p.MANDT
AND g.[Purchase order number] = p.PurchaseOrderNumber
CROSS JOIN params s
WHERE g.[Goods movement timestamp] >= s.start_ts
AND g.[Goods movement timestamp] < s.end_ts
AND g.[Movement type] IN ([Goods return movement types])
AND g.[Reversal indicator] IS NULL
UNION ALL
SELECT
p.PurchaseOrderNumber,
'Invoice Received',
CAST(i.[Invoice posting timestamp] AS TIMESTAMP),
s.source_system,
s.last_data_update,
p.VendorId,
CAST(i.[Posted by] AS NVARCHAR(100)),
p.TotalNetAmount,
p.PurchaseRequisitionNumber,
p.RequestedDeliveryDate,
p.DocumentType
FROM po_base p
INNER JOIN [Your supplier invoice source table or view] i
ON i.[Client] = p.MANDT
AND i.[Purchase order number] = p.PurchaseOrderNumber
CROSS JOIN params s
WHERE i.[Invoice posting timestamp] >= s.start_ts
AND i.[Invoice posting timestamp] < s.end_ts
AND i.[Invoice status] = '[Posted invoice status]'
UNION ALL
SELECT
p.PurchaseOrderNumber,
'Invoice Paid',
CAST(i.[Clearing timestamp] AS TIMESTAMP),
s.source_system,
s.last_data_update,
p.VendorId,
CAST(i.[Cleared by] AS NVARCHAR(100)),
p.TotalNetAmount,
p.PurchaseRequisitionNumber,
p.RequestedDeliveryDate,
p.DocumentType
FROM po_base p
INNER JOIN [Your supplier invoice clearing source table or view] i
ON i.[Client] = p.MANDT
AND i.[Purchase order number] = p.PurchaseOrderNumber
CROSS JOIN params s
WHERE i.[Clearing timestamp] >= s.start_ts
AND i.[Clearing timestamp] < s.end_ts
AND i.[Clearing status] = '[Cleared status value]'
UNION ALL
SELECT
p.PurchaseOrderNumber,
'Purchase Order Completed',
CAST(x.[Completion timestamp] AS TIMESTAMP),
s.source_system,
s.last_data_update,
p.VendorId,
CAST(x.[Completion user] AS NVARCHAR(100)),
p.TotalNetAmount,
p.PurchaseRequisitionNumber,
p.RequestedDeliveryDate,
p.DocumentType
FROM po_base p
INNER JOIN [Your purchase order item status source table or view] x
ON x.[Client] = p.MANDT
AND x.[Purchase order number] = p.PurchaseOrderNumber
CROSS JOIN params s
WHERE x.[Completion timestamp] >= s.start_ts
AND x.[Completion timestamp] < s.end_ts
AND x.[Delivery completed indicator] = '[Set indicator value]'
AND x.[Final invoice indicator] = '[Set indicator value]'
UNION ALL
SELECT
p.PurchaseOrderNumber,
'Purchase Order Deleted',
CAST(d.[Deletion timestamp] AS TIMESTAMP),
s.source_system,
s.last_data_update,
p.VendorId,
CAST(d.[Deleted by] AS NVARCHAR(100)),
p.TotalNetAmount,
p.PurchaseRequisitionNumber,
p.RequestedDeliveryDate,
p.DocumentType
FROM po_base p
INNER JOIN [Your purchase order deletion history table or view] d
ON d.[Client] = p.MANDT
AND d.[Purchase order number] = p.PurchaseOrderNumber
CROSS JOIN params s
WHERE d.[Deletion timestamp] >= s.start_ts
AND d.[Deletion timestamp] < s.end_ts
AND d.[Deletion indicator] = '[Set deletion indicator value]'
)
SELECT
PurchaseOrderNumber,
ActivityName,
EventTime,
SourceSystem,
LastDataUpdate,
VendorId,
UserName,
TotalNetAmount,
PurchaseRequisitionNumber,
RequestedDeliveryDate,
DocumentType
FROM events
WHERE PurchaseOrderNumber IS NOT NULL
AND ActivityName IS NOT NULL
AND EventTime IS NOT NULL
ORDER BY PurchaseOrderNumber, EventTime, ActivityName; 步骤
- 规格与设计:定义事件日志文件的最终数据结构,包含所有必需和建议的属性。记录13项必需活动各自使用的SAP表(例如EKKO、EKPO、EKBE、CDHDR、CDPOS、BKPF)作为数据源。
- 创建程序:在SAP GUI中使用事务代码SE38或SE80打开ABAP编辑器。创建新的可执行程序,例如Z_PM_PO_EXTRACT。
- 定义选择屏幕:为报表编写选择屏幕,供用户筛选要提取的数据。添加采购订单创建日期范围(P_AEDAT)、公司代码(P_BUKRS)和采购凭证类型(P_BSART)参数。
- 声明数据:定义程序所需的内部表和数据结构,包括一个与规格步骤中定义的结构一致的最终事件日志内部表。
- 实现数据选择逻辑:编写ABAP核心逻辑,为13项活动分别选择数据。根据需要,通过一系列SELECT语句查询相关SAP表并进行关联。对于基于变更的事件,请读取变更日志表CDHDR和CDPOS。
- 转换并映射数据:将检索到的每条记录中的SAP表字段映射到最终事件日志内部表的相应列。根据所处理的事件设置ActivityName(例如'Purchase Order Created'),并将日期和时间字段转换为统一的EventTime时间戳格式。
- 汇总事件数据:处理完13类活动后,确保所有数据都汇总到一个统一的内部表中。该表即为所选采购订单的完整事件日志。
- 实现文件输出:添加将最终内部表写入文件的功能。建议使用cl_gui_frontend_services=>gui_download方法,让用户将文件保存为本地CSV;也可以使用OPEN DATASET将文件保存到SAP应用服务器,以便后台处理。
- 创建事务代码(可选):为方便业务用户访问程序,可使用事务代码SE93创建自定义事务代码(例如ZPM_PO_EXTRACT),用于运行ABAP程序。
- 安排后台作业:对于大量数据或自动提取,可使用事务代码SM36将程序安排为后台作业。输出文件将写入程序逻辑中指定的应用服务器路径。
配置
- 选择条件:程序应包含筛选参数,以便有效筛选数据。主要筛选条件包括:
- 日期范围:必须指定采购订单创建日期(EKKO-AEDAT)的日期范围。建议从3至6个月开始,以控制数据量并确保报表性能。
- 公司代码(BUKRS):对于拥有多个法人实体的组织,此条件至关重要,可缩小提取范围。
- 采购凭证类型(BSART):可筛选特定采购订单类型,例如标准采购订单、框架订单或库存调拨订单,以便聚焦分析。
- 读取变更日志:提取“Purchase Order Approved”或“Purchase Order Changed”等活动时,需要读取SAP变更日志表(CDHDR、CDPOS),这可能占用较多资源。ABAP逻辑应优化查询,仅选择必要的对象类别(EINKBELEG、BANF)以及表和字段组合。
- 授权:运行此报表的用户或技术账户需要读取多个SAP模块及系统级表的权限,包括物料管理(MM)、财务会计(FI)等。相关表包括EKKO、EKPO、EBAN、EKBE、BKPF、BSAK、RBKP、NAST、CDHDR和CDPOS。
- 后台执行:如果提取的数据超过数月,或系统事务量较大,请始终在后台运行程序,以避免对话进程超时。
查询示例 abap
REPORT z_pm_po_extract.
" ====================================================================
" SELECTION SCREEN
" ====================================================================
SELECTION-SCREEN BEGIN OF BLOCK b1 WITH FRAME TITLE TEXT-001.
SELECT-OPTIONS: s_aedat FOR sy-datum OBLIGATORY.
SELECT-OPTIONS: s_bukrs FOR ekko-bukrs.
SELECT-OPTIONS: s_bsart FOR ekko-bsart.
PARAMETERS: p_sysid TYPE string DEFAULT '[Your SAP System ID]'.
SELECTION-SCREEN END OF BLOCK b1.
" ====================================================================
" DATA DECLARATIONS
" ====================================================================
TYPES: BEGIN OF ty_event_log,
purchaseordernumber TYPE ebeln,
activityname TYPE string,
eventtime TYPE timestamp,
sourcesystem TYPE string,
lastdataupdate TYPE timestamp,
vendorid TYPE lifnr,
username TYPE ernam,
totalnetamount TYPE netwr,
purchaserequisitionnumber TYPE banfn,
requesteddeliverydate TYPE eedat,
documenttype TYPE bsart,
END OF ty_event_log.
DATA: lt_event_log TYPE TABLE OF ty_event_log,
ls_event_log TYPE ty_event_log.
DATA: lt_ekko TYPE TABLE OF ekko,
lt_ekpo TYPE TABLE OF ekpo.
" ====================================================================
" START OF SELECTION
" ====================================================================
START-OF-SELECTION.
" Get current timestamp for LastDataUpdate
GET TIME STAMP FIELD ls_event_log-lastdataupdate.
ls_event_log-sourcesystem = p_sysid.
" --- Initial Data Selection: Purchase Orders in Scope ---
SELECT * FROM ekko INTO TABLE lt_ekko
WHERE aedat IN s_aedat
AND bukrs IN s_bukrs
AND bsart IN s_bsart.
IF lt_ekko IS INITIAL.
MESSAGE 'No Purchase Orders found for the given criteria.' TYPE 'S' DISPLAY LIKE 'E'.
RETURN.
ENDIF.
SELECT * FROM ekpo INTO TABLE lt_ekpo
FOR ALL ENTRIES IN lt_ekko
WHERE ebeln = lt_ekko-ebeln.
" --- 1. Purchase Requisition Created ---
SELECT ban.banfn, ban.erdat, ban.erzet, ban.ernam,
ekpo.ebeln, ekpo.netwr, ekpo.eindt, ekpo.bsart, ekpo.lifnr, ekko.bukrs
FROM eban AS ban
INNER JOIN ekpo AS ekpo ON ban.banfn = ekpo.banfn AND ban.bnfpo = ekpo.bnfpo
INNER JOIN ekko AS ekko ON ekpo.ebeln = ekko.ebeln
WHERE ekko.ebeln IN @( VALUE #( FOR ls_ekko IN lt_ekko ( ls_ekko-ebeln ) ) )
INTO TABLE @DATA(lt_pr_created).
LOOP AT lt_pr_created INTO DATA(ls_pr_created).
ls_event_log-purchaseordernumber = ls_pr_created-ebeln.
ls_event_log-activityname = 'Purchase Requisition Created'.
CONVERT DATE ls_pr_created-erdat TIME ls_pr_created-erzet INTO TIME STAMP ls_event_log-eventtime TIME ZONE sy-zonlo.
ls_event_log-vendorid = ls_pr_created-lifnr.
ls_event_log-username = ls_pr_created-ernam.
ls_event_log-totalnetamount = ls_pr_created-netwr.
ls_event_log-purchaserequisitionnumber = ls_pr_created-banfn.
ls_event_log-requesteddeliverydate = ls_pr_created-eindt.
ls_event_log-documenttype = ls_pr_created-bsart.
APPEND ls_event_log TO lt_event_log.
ENDLOOP.
" --- 2. Purchase Requisition Approved (via Change Docs on Release Indicator) ---
SELECT h.objectid, h.udate, h.utime, h.username
FROM cdhdr AS h
INNER JOIN cdpos AS p ON h.objectclas = p.objectclas AND h.objectid = p.objectid AND h.changenr = p.changenr
INNER JOIN ekpo AS ekpo ON h.objectid = ekpo.banfn
INNER JOIN ekko AS ekko ON ekpo.ebeln = ekko.ebeln
WHERE h.objectclas = 'BANF'
AND p.tabname = 'EBAN'
AND p.fname = 'FRGZU'
AND p.value_new = 'X' "Configure based on your system release indicator for 'Approved'
AND ekko.ebeln IN @( VALUE #( FOR ls_ekko IN lt_ekko ( ls_ekko-ebeln ) ) )
INTO TABLE @DATA(lt_pr_approved).
LOOP AT lt_pr_approved INTO DATA(ls_pr_approved).
SELECT SINGLE ebeln FROM ekpo INTO ls_event_log-purchaseordernumber WHERE banfn = ls_pr_approved-objectid.
ls_event_log-activityname = 'Purchase Requisition Approved'.
CONVERT DATE ls_pr_approved-udate TIME ls_pr_approved-utime INTO TIME STAMP ls_event_log-eventtime TIME ZONE sy-zonlo.
ls_event_log-username = ls_pr_approved-username.
" Other attributes can be populated with another SELECT if needed.
APPEND ls_event_log TO lt_event_log.
ENDLOOP.
" --- 3. Purchase Order Created ---
LOOP AT lt_ekko INTO DATA(ls_ekko_created).
ls_event_log-purchaseordernumber = ls_ekko_created-ebeln.
ls_event_log-activityname = 'Purchase Order Created'.
CONVERT DATE ls_ekko_created-aedat TIME ls_ekko_created-erzet INTO TIME STAMP ls_event_log-eventtime TIME ZONE sy-zonlo.
ls_event_log-vendorid = ls_ekko_created-lifnr.
ls_event_log-username = ls_ekko_created-ernam.
ls_event_log-totalnetamount = ls_ekko_created-rlwrt.
ls_event_log-purchaserequisitionnumber = ''. "Can be enriched later if needed
ls_event_log-requesteddeliverydate = ''. "Can be enriched from EKPO
ls_event_log-documenttype = ls_ekko_created-bsart.
APPEND ls_event_log TO lt_event_log.
ENDLOOP.
" --- 4. Purchase Order Approved (via Change Docs on Release Indicator) ---
SELECT h.objectid, h.udate, h.utime, h.username
FROM cdhdr AS h
INNER JOIN cdpos AS p ON h.objectclas = p.objectclas AND h.objectid = p.objectid AND h.changenr = p.changenr
WHERE h.objectclas = 'EINKBELEG'
AND p.tabname = 'EKKO'
AND p.fname = 'FRGKE'
AND p.value_new = 'R' "R for Released
AND h.objectid IN @( VALUE #( FOR ls_ekko IN lt_ekko ( ls_ekko-ebeln ) ) )
INTO TABLE @DATA(lt_po_approved).
LOOP AT lt_po_approved INTO DATA(ls_po_approved).
ls_event_log-purchaseordernumber = ls_po_approved-objectid.
ls_event_log-activityname = 'Purchase Order Approved'.
CONVERT DATE ls_po_approved-udate TIME ls_po_approved-utime INTO TIME STAMP ls_event_log-eventtime TIME ZONE sy-zonlo.
ls_event_log-username = ls_po_approved-username.
APPEND ls_event_log TO lt_event_log.
ENDLOOP.
" --- 5. Purchase Order Sent to Vendor ---
SELECT n.objky, n.vstat, n.datvr, n.uhrvr, e.ernam
FROM nast AS n
INNER JOIN ekko AS e ON n.objky = e.ebeln
WHERE n.kappl = 'EF' "Application for Purchasing
AND n.kschl = '[Your PO Output Type]' "e.g. NEU
AND n.vstat = '1' "Successfully processed
AND n.objky IN @( VALUE #( FOR ls_ekko IN lt_ekko ( ls_ekko-ebeln ) ) )
INTO TABLE @DATA(lt_po_sent).
LOOP AT lt_po_sent INTO DATA(ls_po_sent).
ls_event_log-purchaseordernumber = ls_po_sent-objky.
ls_event_log-activityname = 'Purchase Order Sent to Vendor'.
CONVERT DATE ls_po_sent-datvr TIME ls_po_sent-uhrvr INTO TIME STAMP ls_event_log-eventtime TIME ZONE sy-zonlo.
ls_event_log-username = ls_po_sent-ernam.
APPEND ls_event_log TO lt_event_log.
ENDLOOP.
" --- 6. Purchase Order Changed ---
SELECT objectid, udate, utime, username FROM cdhdr
WHERE objectclas = 'EINKBELEG'
AND tcode IN ('ME22', 'ME22N')
AND objectid IN @( VALUE #( FOR ls_ekko IN lt_ekko ( ls_ekko-ebeln ) ) )
INTO TABLE @DATA(lt_po_changed).
LOOP AT lt_po_changed INTO DATA(ls_po_changed).
ls_event_log-purchaseordernumber = ls_po_changed-objectid.
ls_event_log-activityname = 'Purchase Order Changed'.
CONVERT DATE ls_po_changed-udate TIME ls_po_changed-utime INTO TIME STAMP ls_event_log-eventtime TIME ZONE sy-zonlo.
ls_event_log-username = ls_po_changed-username.
APPEND ls_event_log TO lt_event_log.
ENDLOOP.
" --- 7. Goods Receipt Posted & 9. Goods Returned ---
SELECT e.ebeln, m.budat, m.cpudt, m.cputm, m.usnam, b.shkzg, b.bwart
FROM mkpf AS m
INNER JOIN mseg AS s ON m.mblnr = s.mblnr AND m.mjahr = s.mjahr
INNER JOIN t156 AS t ON s.bwart = t.bwart
INNER JOIN ekbe AS e ON s.ebeln = e.ebeln AND s.ebelp = e.ebelp AND s.mblnr = e.belnr AND s.mjahr = e.gjahr
WHERE e.ebeln IN @( VALUE #( FOR ls_ekko IN lt_ekko ( ls_ekko-ebeln ) ) )
AND e.bwart IN ('101', '102', '122', '123') "GR, GR Reversal, Return
INTO TABLE @DATA(lt_goods_mvmt).
LOOP AT lt_goods_mvmt INTO DATA(ls_goods_mvmt).
ls_event_log-purchaseordernumber = ls_goods_mvmt-ebeln.
IF ls_goods_mvmt-bwart = '101'.
ls_event_log-activityname = 'Goods Receipt Posted'.
ELSE.
ls_event_log-activityname = 'Goods Returned'.
ENDIF.
CONVERT DATE ls_goods_mvmt-cpudt TIME ls_goods_mvmt-cputm INTO TIME STAMP ls_event_log-eventtime TIME ZONE sy-zonlo.
ls_event_log-username = ls_goods_mvmt-usnam.
APPEND ls_event_log TO lt_event_log.
ENDLOOP.
" --- 8. Services Confirmation Entered ---
SELECT h.erdat, h.erzeit, h.ernam, l.ebeln
FROM essr AS h
INNER JOIN esll AS l ON h.lblni = l.lblni
WHERE l.ebeln IN @( VALUE #( FOR ls_ekko IN lt_ekko ( ls_ekko-ebeln ) ) )
INTO TABLE @DATA(lt_services).
LOOP AT lt_services INTO DATA(ls_services).
ls_event_log-purchaseordernumber = ls_services-ebeln.
ls_event_log-activityname = 'Services Confirmation Entered'.
CONVERT DATE ls_services-erdat TIME ls_services-erzeit INTO TIME STAMP ls_event_log-eventtime TIME ZONE sy-zonlo.
ls_event_log-username = ls_services-ernam.
APPEND ls_event_log TO lt_event_log.
ENDLOOP.
" --- 10. Invoice Received ---
SELECT r.ebeln, r.cpudt, r.cputm, r.usnam
FROM rbkp AS r
WHERE r.ebeln IN @( VALUE #( FOR ls_ekko IN lt_ekko ( ls_ekko-ebeln ) ) )
INTO TABLE @DATA(lt_invoice_rcvd).
LOOP AT lt_invoice_rcvd INTO DATA(ls_invoice_rcvd).
ls_event_log-purchaseordernumber = ls_invoice_rcvd-ebeln.
ls_event_log-activityname = 'Invoice Received'.
CONVERT DATE ls_invoice_rcvd-cpudt TIME ls_invoice_rcvd-cputm INTO TIME STAMP ls_event_log-eventtime TIME ZONE sy-zonlo.
ls_event_log-username = ls_invoice_rcvd-usnam.
APPEND ls_event_log TO lt_event_log.
ENDLOOP.
" --- 11. Invoice Paid ---
SELECT b.ebeln, s.augdt, s.augbl, b.usnam
FROM rbkp AS b
INNER JOIN bseg AS e ON b.belnr = e.belnr AND b.gjahr = e.gjahr
INNER JOIN bsak AS s ON e.bukrs = s.bukrs AND e.belnr = s.belnr AND e.gjahr = s.gjahr AND e.buzei = s.buzei
WHERE b.ebeln IN @( VALUE #( FOR ls_ekko IN lt_ekko ( ls_ekko-ebeln ) ) )
AND s.augdt IS NOT NULL
INTO TABLE @DATA(lt_invoice_paid).
LOOP AT lt_invoice_paid INTO DATA(ls_invoice_paid).
ls_event_log-purchaseordernumber = ls_invoice_paid-ebeln.
ls_event_log-activityname = 'Invoice Paid'.
CONVERT DATE ls_invoice_paid-augdt INTO TIME STAMP ls_event_log-eventtime TIME ZONE sy-zonlo.
ls_event_log-username = ls_invoice_paid-usnam.
APPEND ls_event_log TO lt_event_log.
ENDLOOP.
" --- 12. Purchase Order Completed & 13. Purchase Order Deleted (via Change Docs) ---
SELECT h.objectid, h.udate, h.utime, h.username, p.fname
FROM cdhdr AS h
INNER JOIN cdpos AS p ON h.changenr = p.changenr
INNER JOIN ekpo AS ekpo ON h.objectid = |{ ekpo.ebeln }{ ekpo.ebelp }|
WHERE h.objectclas = 'EINKBELEG'
AND p.tabname = 'EKPO'
AND p.fname IN ('ELIKZ', 'EREKZ', 'LOEKZ')
AND p.value_new = 'X'
AND ekpo.ebeln IN @( VALUE #( FOR ls_ekko IN lt_ekko ( ls_ekko-ebeln ) ) )
INTO TABLE @DATA(lt_po_status_change).
LOOP AT lt_po_status_change INTO DATA(ls_po_status_change).
ls_event_log-purchaseordernumber = substring( val = ls_po_status_change-objectid, off = 0, len = 10 ).
CASE ls_po_status_change-fname.
WHEN 'LOEKZ'.
ls_event_log-activityname = 'Purchase Order Deleted'.
WHEN 'ELIKZ' OR 'EREKZ'.
"This logic may need refinement to check if both are now set.
ls_event_log-activityname = 'Purchase Order Completed'.
ENDCASE.
CONVERT DATE ls_po_status_change-udate TIME ls_po_status_change-utime INTO TIME STAMP ls_event_log-eventtime TIME ZONE sy-zonlo.
ls_event_log-username = ls_po_status_change-username.
APPEND ls_event_log TO lt_event_log.
ENDLOOP.
" --- Final Output to CSV ---
CALL METHOD cl_gui_frontend_services=>gui_download
EXPORTING
filename = 'C:\temp\po_event_log.csv'
filetype = 'ASC'
CHANGING
data_tab = lt_event_log. 优化采购订单流程,立即缩短周期时间
消除低效环节,将采购到付款流程中采购订单的周期时间缩短30%。
无需信用卡,几分钟即可完成设置。