您的采购到付款:采购订单数据模板
您的采购到付款:采购订单数据模板
- 详细分析所需的推荐属性
- 流程中需要跟踪的关键活动
- 分步数据提取指南
采购到付款-采购订单属性
| 名称 | 说明 | ||
|---|---|---|---|
| 事件时间 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 | 业务要求供应商交付货物或服务的日期。 | ||
| 说明 此属性指定采购订单中约定的目标交付日期,是衡量供应商交付绩效的基准。 在流程挖掘中,该日期会与实际收货日期(“已过账收货”时间戳)进行比较,用于计算“供应商按时交付率”KPI。分析偏离该日期的情况,有助于评估供应商可靠性并管理供应链风险。 为什么重要 作为衡量供应商按时交付绩效的基准,是供应链管理和运营规划中的关键KPI。 获取位置 可在计划行表EKET的EINDT字段中找到。 示例 2023-06-012023-06-152023-07-01 | |||
| 采购申请 PurchaseRequisitionNumber | 发起采购订单的采购申请(PR)标识符。 | ||
| 说明 此属性将采购订单关联回其来源采购申请。直接创建的采购订单不一定有采购申请。 该关联对于分析从初始申请开始的完整端到端采购流程至关重要。它支持“采购申请审批时间”等KPI,也是识别“非计划支出”的基础,即采购订单在没有先行且已批准的采购申请的情况下创建。 为什么重要 将采购订单与初始申请关联起来,支持端到端流程分析,并识别不合规的非计划支出。 获取位置 此属性位于SAP S/4HANA的EKPO表(采购订单行项目级别)中,字段为BANFN。 示例 1001005110010052 | |||
| 采购订单单据类型 DocumentType | 用于区分不同采购订单类型的分类,例如标准采购订单、服务采购订单或库存转储订单。 | ||
| 说明 单据类型是SAP中的关键配置元素,用于控制采购订单的流程路径、编号范围和字段。企业可以据此针对不同场景调整采购流程。 按单据类型分析流程,对于理解流程变体至关重要。例如,标准货物采购订单的流程可能与服务采购订单或库存调拨订单大不相同。该属性支持筛选和比较这些不同的流程路径,以发现具体的改进机会。 为什么重要 它对采购订单进行分类,支持比较不同采购流程,并帮助解释流程路径和周期时间的差异。 获取位置 此属性位于SAP S/4HANA的EKKO表中,字段为BSART。 示例 NBFOUB | |||
| 供应商准时交付 SupplierOnTimeDelivery | 用于标识收货过账是否在要求交付日期当天或之前完成的计算字段。 | ||
| 说明 此布尔属性通过比较'Goods Receipt Posted'活动的时间戳与'Requested Delivery Date'得出。如果收货过账时间早于或等于要求日期,则标记为'true'。 该属性直接支持“供应商准时交付率”KPI。它简化了准时和延迟交付的筛选分析,是供应商绩效仪表板和供应商评分的重要依据。 为什么重要 直接衡量供应商可靠性,为准时交付KPI提供基础,并支持有效的供应商绩效管理。 获取位置 通过比较'Goods Receipt Posted'活动的时间戳与'RequestedDeliveryDate'属性计算得出。 示例 truefalse | |||
| 公司代码 CompanyCode | 创建采购订单的法人实体或公司的标识符。 | ||
| 说明 公司代码代表组织内独立的会计核算单位。与采购订单相关的所有财务交易都会记入特定的公司代码。 这是一个基础组织属性,可用于筛选和比较不同法人实体的采购流程。按公司代码分析,可以发现组织内的流程执行差异、效率差异或合规率差异。 为什么重要 支持按法人实体细分流程分析,便于比较企业不同部分的绩效和合规情况。 获取位置 此属性位于SAP S/4HANA表EKKO的字段BUKRS中。 示例 101017102000 | |||
| 工厂 Plant | 接收货物或提供服务的运营设施或地点。 | ||
| 说明 在SAP中,工厂是生产或存储货物,或提供服务的物理地点,是物流和计划管理的重要组成部分。 按工厂细分流程分析,可以发现采购流程在不同地区或地点的差异。例如,分析可以显示某些工厂是否交付时间更长或货物退货率更高,从而指向当地物流或质量控制问题。 为什么重要 支持基于地点的分析,突出不同运营地点、工厂或仓库之间的流程绩效差异。 获取位置 此属性位于SAP S/4HANA表EKPO的字段WERKS中。 示例 10101710DE01 | |||
| 是否为非计划采购 IsMaverickSpend | 用于标识采购订单是否在没有事先批准的采购申请的情况下创建的计算字段。 | ||
| 说明 此布尔标记在数据处理过程中生成。如果采购订单没有关联的采购申请,或创建采购订单时绕过了标准审批工作流,则标记为'true'。 该属性直接支持“非计划采购识别”仪表板及相关KPI。它有助于量化不合规采购行为的程度,使企业能够针对特定部门或用户群体强化采购政策和控制措施。 为什么重要 直接识别不合规采购,帮助量化流程偏差并落实财务控制和采购政策。 获取位置 根据特定单据类型中'PurchaseRequisitionNumber'没有值的情况计算,或通过分析事件序列得出。 示例 truefalse | |||
| 是否返工 IsRework | 用于标识采购订单是否发生返工,例如审批后变更或货物退回的计算字段。 | ||
| 说明 此布尔属性通过分析每个采购订单的活动序列计算得出。如果在'Purchase Order Approved'事件之后出现'Purchase Order Changed'事件,或存在'Goods Returned'事件,则标记为'true'。 该标记简化了“直通处理率”KPI的计算。您可以轻松筛选和可视化所有需要人工干预或修正的采购订单,从而量化返工的成本和发生频率。 为什么重要 通过标记发生返工的案例,帮助量化流程低效程度。这对于计算直通处理率和识别偏差根因至关重要。 获取位置 根据活动序列计算。如果审批后出现'Purchase Order Changed'事件,或存在'Goods Returned'事件,则标记为返工。 示例 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 | |||
| 已收到发票 | 表示供应商发票已录入SAP系统,并与相应采购订单关联。这是一项会创建会计凭证的明确财务过账。 | ||
| 为什么重要 这是连接采购流程与应付账款流程的关键里程碑,可用于分析收货与发票处理之间的时间。 获取位置 会计凭证创建于BKPF表(抬头),其行项目位于BSEG或统一日记账ACDOCA中。该凭证通过RSEG表与采购订单关联。 采集 会计凭证抬头表BKPF中的凭证录入日期(CPUDT)。 事件类型 explicit | |||
| 已过账收货 | 表示从供应商处实际收到货物,并在系统中完成相应录入。这是一项会更新采购订单历史的明确事务。 | ||
| 为什么重要 这是结束供应商交付周期并开始内部发票核验流程的重要里程碑,也是跟踪按时交付率的基础。 获取位置 作为物料凭证记录于MKPF(抬头)和MSEG(项目)表,并通过特定移动类型(例如101)关联至采购订单历史表EKBE。 采集 通过EKBE关联的物料凭证抬头表MKPF中的过账日期(BUDAT)。 事件类型 explicit | |||
| 采购申请已审批 | 表示经理或指定审批人已正式批准采购申请。通常可根据采购申请单据中的状态变更推断,表明该申请已准备好转换为采购订单。 | ||
| 为什么重要 这是跟踪审批周期时间和识别瓶颈的关键里程碑。此处的延迟会直接影响采购订单创建并发送给供应商的速度。 获取位置 根据EBAN表中的发布状态字段推断,例如FRGZU(发布指示符)。时间戳取自CDHDR/CDPOS变更单据,用于记录最终发布状态的设置时间。 采集 根据EBAN表中发布状态字段的变更日志(CDHDR/CDPOS)推断。 事件类型 inferred | |||
| 采购订单已完成 | 此活动表示从物流角度看,采购订单行项目已关闭。当“交付完成”和“最终发票”指示符均已设置时,可推断此事件发生。 | ||
| 为什么重要 这是采购订单生命周期分析的终点。衡量到达此事件所需的时间,即可获得采购运营的端到端周期时间。 获取位置 根据采购订单行项目表EKPO中的状态标记推断。当“交付完成”指示符(ELIKZ)和“最终发票”指示符(EREKZ)均为true时,事件发生。 采集 根据变更日志推断,条件是EKPO字段ELIKZ和EREKZ均标记为完成。 事件类型 inferred | |||
| 采购订单已审批 | 表示采购订单已获得所有必要的内部审批,并获准发送给供应商。通常根据采购订单发布策略中的状态变更推断此事件。 | ||
| 为什么重要 这是衡量审批效率和审批后返工的关键里程碑。分析采购订单创建与审批之间的时间,有助于发现内部流程延迟。 获取位置 根据EKKO表中的发布指示符FRGKE推断。通过查看CDHDR/CDPOS变更历史,确定该字段更新为“已发布”状态的时间戳。 采集 根据EKKO表中发布指示符字段FRGKE的变更日志推断。 事件类型 inferred | |||
| 发票已付款 | 标志着通过付款运行或手工付款完成供应商发票的最终结算。这是一项会创建清账凭证的明确财务交易。 | ||
| 为什么重要 虽然技术上属于付款流程,但纳入此活动可以完整呈现采购到付款周期。这对于分析付款条件和付款绩效至关重要。 获取位置 付款以清账凭证形式记录于BKPF/ACDOCA中。BSEG或ACDOCA表中发票行项目的清账日期(AUGDT)表示付款事件。 采集 发票凭证的清账日期(AUGDT),位于BSEG或ACDOCA中。 事件类型 explicit | |||
| 已录入服务确认 | 此活动标志着采购订单中指定的服务已完成确认。系统会通过创建服务条目表明确记录此活动。 | ||
| 为什么重要 对于服务类采购,这相当于收货。它对于跟踪服务交付周期并确保供应商及时收款至关重要。 获取位置 通过创建服务条目表记录,数据存储于ESSR(抬头)和ESLL(行项目)表中。创建日期作为时间戳。 采集 ESSR表中服务条目表单据的创建日期。 事件类型 explicit | |||
| 已退货 | 表示此前收到的货物已退回供应商,通常原因包括质量问题、损坏或发错货。系统会将其记录为明确的冲销收货移动。 | ||
| 为什么重要 此活动突出显示返工以及供应商质量或订单准确性方面的潜在问题。某个供应商或物料频繁退货,通常意味着存在问题。 获取位置 作为带有特定退货移动类型(例如122)的物料凭证记录。事件记录于MKPF/MSEG,并通过EKBE历史表关联至采购订单。 采集 EKBE中带有退货移动类型的物料凭证过账日期。 事件类型 explicit | |||
| 采购订单已删除 | 表示采购订单行项目或整个单据被取消或逻辑删除。当用户在单据上设置删除标记时,系统会捕获此事件。 | ||
| 为什么重要 此活动是流程的另一个终点,表示流程失败或被取消。分析采购订单被删除的原因,有助于发现需求计划或需求定义中的问题。 获取位置 从采购订单抬头表EKKO或行项目表EKPO中的删除指示符LOEKZ获取。时间戳取自CDHDR/CDPOS变更单据。 采集 删除标记LOEKZ设置时,来自变更单据CDHDR/CDPOS的时间戳。 事件类型 explicit | |||
| 采购订单已发送给供应商 | 表示采购订单传达给供应商的时刻,例如通过EDI、电子邮件或打印件发送。此事件通常通过系统的输出管理日志捕获。 | ||
| 为什么重要 这是供应商交付周期的真正起点。要准确衡量供应商从收到订单起的绩效,这一活动至关重要。 获取位置 从输出控制表NAST中获取。该表记录采购单据发送的消息,可使用相关输出类型(例如EDI、电子邮件)的日期和时间。 采集 NAST表中采购订单首次成功输出消息的时间戳。 事件类型 inferred | |||
| 采购订单已变更 | 此活动表示采购订单初次创建后发生了修改,例如数量、价格或交付日期变更。系统会在变更日志中明确记录此活动。 | ||
| 为什么重要 跟踪变更,尤其是审批后的变更,对于识别流程低效、返工和潜在合规问题至关重要。频繁变更可能表明初始规格不完善。 获取位置 记录于采购订单对象(EINKBELEG)的变更单据表CDHDR(抬头)和CDPOS(项目)中。每次变更都会生成详细的日志条目。 采集 记录EKKO或EKPO表关键字段变更的事件,数据来源为CDHDR/CDPOS。 事件类型 explicit | |||
提取指南
步骤
- 前提条件和访问权限:确保您拥有具备相应授权的用户,可查询SAP S/4HANA系统中的Core Data Services(CDS)视图。您可以通过SAP HANA Studio、适用于Eclipse的ABAP Development Tools(ADT),或支持通过SQL连接SAP HANA数据库的第三方数据提取工具访问。
- 确认系统连接信息:获取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:用于记录采购订单变更。
- 日期范围筛选:必须应用日期范围筛选,以控制性能和数据量。查询使用采购订单创建日期(PurchaseOrderDate)上的_start_date和_end_date占位符。建议初始提取3至6个月的数据。
- 组织范围筛选:查询应始终按CompanyCode筛选,将提取范围限制在相关业务单元内。还可以在PO_base公共表表达式中增加PurchaseOrderType或PurchasingOrganization筛选条件,进一步细化结果。
- 前提条件:执行查询的用户需要对上述所有CDS视图拥有SELECT授权。在S/4HANA中,通常通过特定业务角色或分析角色授予这些视图的访问权限。缺少适当权限时,查询将失败。
a 示例查询 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]定义提取时间窗口。首次加载建议使用3至6个月的时间范围。仅在报告范围有要求时,应用公司代码和凭证类型筛选条件。
- 从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作为案例标识。项目级事件应关联到采购订单号;如有需要,在其他特定来源列中保留项目引用。
- 日期范围:从3至6个月开始。加载历史数据时,分段处理较小时间窗口,并核对重叠边界,避免遗漏。
- 筛选条件:根据所需范围配置公司代码、凭证类型、VendorId、采购组织、采购组和事件日期筛选条件。公司代码和凭证类型筛选必须使用目标系统中的有效值。
- 事件语义:当相关凭证字段或日志可用时,创建和删除可以作为明确事件。审批和完成属于基于状态的事件,需要配置状态时间戳。不要仅根据当前状态创建没有可靠事件时间戳的行。
- 性能:尽早按事件日期和组织范围筛选;在可行时先汇总EKPO,再与事件来源连接;处理大量历史数据时使用时间窗口。确保数据库统计信息适当,避免在项目、会计和变更历史来源之间执行不受限制的连接。
- 刷新时间戳:对同一批次中的每一行,将LastDataUpdate设置为提取执行时间戳。
- 前提条件:所需前提包括SAP HANA连接、对所有已配置来源对象的读取授权、对相关采购、库存管理、服务采购、发票校验和应付账款数据的访问权限,以及能够导入所选文件或结果集的ProcessMind连接。
- 系统特定配置:将查询中的每个方括号来源占位符替换为目标SAP S/4HANA系统中已批准的表、视图、列或表达式。不要在查询或提取配置中暴露凭据。
a 示例查询 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项活动选择数据。根据需要,对相关SAP表执行一系列SELECT语句并进行连接。对于基于变更的事件,从变更日志表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。
- 后台执行:对于覆盖数月以上数据的提取,或在交易量较高的系统中运行时,应始终在后台执行程序,避免对话进程超时。
a 示例查询 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. 优化您的P2P采购订单:立即缩短周期时间
消除低效环节,将您的P2P采购订单周期时间缩短30%。
无需信用卡,几分钟即可完成设置。