您的采购到付款-采购订单数据模板
您的采购到付款-采购订单数据模板
- 详细分析的推荐属性
- 流程中需追踪的关键活动
- 分步数据提取指南
采购到付款—采购订单属性
| 名称 | 描述 | ||
|---|---|---|---|
| Event 时间 EventTime | 活动发生的时间戳。 | ||
| 描述 此属性记录流程中每个活动的确切日期和时间。它是流程挖掘中所有基于时间的分析的基础。\n\n“事件时间”用于按时间顺序排列活动以构建流程流向。此外,它还是计算所有基于持续时间的指标的基础,例如活动间的周期时间、等待时间和处理时间,这些对于绩效分析和瓶颈识别至关重要。 为何重要 此时间戳对于正确排列事件顺序以及计算所有绩效指标(包括周期时间、交货周期和等待时间)至关重要。 获取方式 与特定活动相关的时间戳字段,例如创建日期(EKKO-AEDAT 用于修改)或过账日期(MKPF-BUDAT 用于收货)。通常需要结合来自多个表的数据。 示例 2023-04-15T10:00:00Z2023-04-15T14:30:00Z2023-05-01T09:15:00Z | |||
| 活动 ActivityName | 采购订单流程中发生的业务事件或步骤的名称。 | ||
| 描述 此属性描述采购订单生命周期内的特定操作或状态变化,例如“创建采购订单”、“采购订单已审批”或“已过账收货”。这些活动的顺序构成了流程流转。\n\n分析活动的顺序和频率是流程挖掘的核心。它有助于发现实际流程,将其与设计模型进行对比,识别瓶颈(例如“收到发票”后的长时间等待),并量化返工(例如重复的“采购订单已修改”活动)。 为何重要 它定义了流程中的各个步骤,实现了端到端流程的可视化分析、变体分析以及瓶颈识别。 获取方式 通常通过结合多个表和字段(例如 EKKO/EKPO 中的状态字段或 CDHDR/CDPOS 中的变更凭证日志)得出,用以代表关键业务里程碑。 示例 采购订单已创建采购订单已批准收货过账完成发票已接收 | |||
| 采购订单 PurchaseOrderNumber | 采购订单 (PO) 的唯一标识符,作为追踪采购生命周期的主要 Case ID。 | ||
| 描述 采购订单编号是连接所有相关活动的核心标识符,从初始创建到最终收货和完结。它在流程挖掘分析中充当 Case ID。\n\n在分析中,按此编号对事件进行分组可以还原每个采购订单的完整路径。这对于计算周期时间、分析流程变体以及识别针对单个订单的瓶颈或偏差至关重要。 为何重要 它是将所有采购事件连接成单一端到端流程的核心纽带,能够对每个采购订单的生命周期进行详细分析。 获取方式 此属性可在 SAP S/4HANA 表 EKKO 的 EBELN 字段中找到。 示例 450001712345000171244500017125 | |||
| 最后数据更新 LastDataUpdate | 从源系统最后一次刷新或提取数据的时间戳。 | ||
| 描述 此属性表示正在分析的数据的新鲜度。它显示了从 SAP S/4HANA 最近一次提取数据的日期和时间。\n\n了解最后一次数据更新时间对于用户理解其分析的时效性至关重要。这有助于他们正确解读分析结果,明确他们看到的是实时信息还是特定时间点的快照,从而影响基于分析采取的任何行动的相关性。 为何重要 告知用户数据的及时性,确保他们理解分析结果的背景和相关性。 获取方式 这是在数据提取、转换和加载 (ETL) 过程中添加的元数据时间戳。 示例 2024-05-21T02:00:00Z2024-05-20T02:00:00Z2024-05-19T02:00:00Z | |||
| 源系统 SourceSystem | 标识数据的来源系统。 | ||
| 描述 此属性指定事件数据的来源系统,例如“SAP S/4HANA 生产环境”或“SAP ECC”。\n\n在多系统环境中,此字段对于数据血缘、故障排除以及确保正确解读来自不同来源的数据至关重要。它有助于理解数据的背景,并可用于针对特定系统环境过滤分析。 为何重要 提供关于数据来源的关键背景信息,这对于多系统环境下的数据治理、校验和分析至关重要。 获取方式 这通常是在数据提取、转换和加载 (ETL) 过程中添加的静态值,用于标记数据集的来源。 示例 S4H_PROD_100ECC_EU_200S4H_US_300 | |||
| 供应商ID VendorId | 提供商品或服务的供应商的唯一标识符。 | ||
| 描述 供应商 ID 是关键的主数据,它将采购订单与特定供应商关联起来。它贯穿于整个采购流程,用于沟通、交付和付款。\n\n在流程挖掘中,此属性支持按供应商进行细分绩效分析。这对于“供应商交货周期绩效”和“各供应商退货率”等仪表板至关重要,有助于识别最可靠的供应商以及可能导致延误或质量问题的供应商。 为何重要 支持以供应商为中心的分析,有助于评估绩效、识别表现优异和欠佳的供应商,并优化供应链。 获取方式 此属性位于 SAP S/4HANA 表 EKKO 的 LIFNR 字段。 示例 100023100045100088 | |||
| 客户要求的交货日期 RequestedDeliveryDate | 企业要求供应商交付货物或服务的日期。 | ||
| 描述 此属性指定采购订单中约定的目标交货日期。它作为衡量供应商交货绩效的基准。\n\n在流程挖掘中,将此日期与实际收货日期(“已过账收货”时间戳)进行对比,以计算“供应商准时交货率”KPI。分析与该日期的偏差有助于评估供应商可靠性并管理供应链风险。 为何重要 作为衡量供应商准时交货绩效的基准,这是供应链管理和运营规划的关键 KPI。 获取方式 这可以在计划行表 EKET 的 EINDT 字段中找到。 示例 2023-06-012023-06-152023-07-01 | |||
| 总净额 TotalNetAmount | 采购订单的总金额,不含税费和运费。 | ||
| 描述 此属性代表采购订单的货币净值。它是衡量采购交易规模的关键财务指标。\n\n该金额对于财务分析至关重要,例如按金额对采购订单进行分类(高价值 vs. 低价值),以观察其流程路径是否存在差异。它还可以用于设定分析的优先级,重点关注可能带有更高财务风险或对业务影响更大的高价值订单。 为何重要 支持基于财务维度的分析,有助于按价值对采购订单进行分类,并优先对高支出领域进行流程改进。 获取方式 此属性可在 SAP S/4HANA 表 EKKO 的 NETWR 字段中找到。 示例 1500.0025000.50125.75 | |||
| 用户 UserName | 执行特定活动的用户的标识符。 | ||
| 描述 此属性捕获负责创建、修改或审批凭证的 SAP 用户 ID。它为系统中采取的操作提供了可追溯性。\n\n按用户进行分析有助于识别培训需求、工作量分配和个人绩效。例如,可以用它来观察特定用户是否始终与较长的审批时间或频繁的审批后修改相关联,从而为资源管理和流程改进计划提供参考。 为何重要 提供责任归属并支持个人或团队层面的绩效分析,有助于识别培训需求或资源限制。 获取方式 此信息可在 EKKO 的 ERNAM(创建者)等字段中找到,或来自变更凭证表中的用户字段 (CDHDR-USERNAME)。 示例 CB9980000012JSMITHRROE | |||
| 采购申请 PurchaseRequisitionNumber | 发起该采购订单的采购申请 (PR) 标识符。 | ||
| 描述 此属性将采购订单链接回其源采购申请。如果直接创建采购订单,则并非所有采购订单都会有关联的申请。\n\n此链接对于分析从初始请求开始的完整端到端采购流程至关重要。它支持诸如“采购申请审批时间”之类的 KPI,并且是识别“违规支出”的基础,即在没有先行且获批申请的情况下创建的采购订单。 为何重要 将采购订单与初始申请联系起来,从而实现端到端流程分析,并识别不合规的越权采购。 获取方式 此属性位于 SAP S/4HANA 表 EKPO(采购订单行项目层级)的 BANFN 字段。 示例 1001005110010052 | |||
| 采购订单凭证类型 DocumentType | 区分不同类型采购订单的分类,例如标准采购订单、服务采购订单或库存转储订单。 | ||
| 描述 凭证类型是 SAP 中的关键配置元素,用于控制采购订单的流程流转、编号范围和字段。它允许企业针对不同场景定制采购流程。\n\n按凭证类型分析流程对于理解流程变体至关重要。例如,标准商品采购订单的流程可能与服务采购订单或库存转储订单截然不同。利用此属性可以过滤和比较这些不同的流程流向,从而发现具体的改进机会。 为何重要 它对采购订单进行分类,方便对比不同的采购流程,并协助分析流程流转和周期时间中的差异。 获取方式 此属性位于 SAP S/4HANA 表 EKKO 的 BSART 字段。 示例 NBFOUB | |||
| PO审批周期 PoApprovalCycleTime | 计算出的从采购订单创建到获得最终审批之间的时间跨度。 | ||
| 描述 该指标衡量“创建采购订单”活动与“采购订单已审批”活动之间流逝的时间。它针对每个采购订单 Case 进行计算。\n\n此属性提供了对内部审批效率的直接衡量。它是“采购订单审批周期”仪表板和 KPI 的主要指标,有助于识别审批流程中发生延误的环节,并精准定位加速审批的机会。 为何重要 直接衡量内部审批流程的效率,有助于识别并解决减缓采购速度的瓶颈。 获取方式 通过计算每个采购订单的“采购订单已批准”事件与“采购订单已创建”事件之间的时间差得出。 示例 P2D4H30MP0D2H15MP5D | |||
| 供应商准时交货 SupplierOnTimeDelivery | 一个计算出的标记,用于指示收货过账是否在请求的交货日期当天或之前完成。 | ||
| 描述 此布尔属性通过对比“已过账收货”活动的时间戳与“请求交货日期”得出。如果收货在请求日期当天或之前,则标记为“true”。\n\n此属性直接支持“供应商准时交货率”KPI。它允许用户轻松过滤准时或延迟交付,从而简化分析,这对于供应商绩效仪表板和供应商评分至关重要。 为何重要 直接衡量供应商的可靠性,为准时交货 KPI 奠定基础,并实现有效的供应商绩效管理。 获取方式 通过比较“收货已过账”活动的 timestamp 与“RequestedDeliveryDate”属性计算得出。 示例 truefalse | |||
| 公司代码 CompanyCode | 创建采购订单所属的法人实体或公司的标识符。 | ||
| 描述 公司代码代表组织内独立的会计单位。所有与采购订单相关的财务交易都会过账到特定的公司代码。\n\n这是一个基础的组织属性,支持跨不同法人实体过滤和比较采购流程。按公司代码进行分析可以揭示流程执行中的不一致性、不同的效率水平或整个组织中各异的合规率。 为何重要 支持按法律实体进行流程分析,便于比较业务不同部分的绩效和合规性。 获取方式 此属性位于 SAP S/4HANA 表 EKKO 的 BUKRS 字段。 示例 101017102000 | |||
| 工厂 Plant | 货物送达或服务交付的运营设施或地点(工厂)。 | ||
| 描述 在 SAP 中,工厂是生产、存储货物或提供服务的物理地点。它是物流和计划的关键要素。 按工厂细分流程分析可以揭示采购流程中地域性或地点特定的差异。例如,它可以显示某些工厂是否经历了更长的交货时间或具有更高的退货率,从而指向局部物流或质量控制问题。 为何重要 支持基于位置的分析,突出显示不同运营地点、工厂或仓库之间的流程绩效差异。 获取方式 此属性位于 SAP S/4HANA 表 EKPO 的 WERKS 字段。 示例 10101710DE01 | |||
| 是否为违规支出 IsMaverickSpend | 一个计算出的标记,用于指示采购订单是否在没有先前批准的采购申请的情况下创建。 | ||
| 描述 此布尔标记在数据处理期间生成。如果采购订单缺少关联的采购申请,或者采购订单的创建绕过了标准审批工作流,则将其设置为“true”。\n\n此属性直接支持“违规支出识别”仪表板及相关 KPI。它有助于量化非合规采购行为的程度,使企业能够针对特定部门或用户组加强采购政策和控制。 为何重要 直接识别不合规采购,有助于量化流程偏差并加强财务控制和采购政策。 获取方式 基于特定凭证类型中“PurchaseRequisitionNumber”的值缺失,或通过分析事件序列得出的计算字段。 示例 truefalse | |||
| 是否返工 IsRework | 一个计算出的标记,用于指示采购订单是否经历了返工,如批准后变更或退货。 | ||
| 描述 此布尔属性是通过分析每个采购订单的活动序列计算得出的。如果“采购订单已审批”事件之后发生了“采购订单已修改”事件,或者存在“退货”事件,则标记为“true”。\n\n此标记简化了“直通处理率”KPI 的计算。它支持轻松过滤和可视化所有需要人工干预或更正的采购订单,有助于量化返工的成本和频率。 为何重要 通过标记带有返工的 case 来帮助量化流程低效,这对于计算直通处理率和识别偏差的根本原因至关重要。 获取方式 基于活动序列的计算字段。逻辑会检查审批后是否发生了“采购订单已更改”事件,或者是否存在“已退货”事件。 示例 truefalse | |||
| 物料类别 ItemCategory | 对采购订单行项目进行分类,例如标准、寄售、外协加工或服务。 | ||
| 描述 项目类别决定了特定物料或服务的采购如何受控及处理。它会影响后续步骤,如收货和发票校验。\n\n该属性对于根据购买内容分析流程变体非常重要。例如,服务项目(需要服务确认单)的流程与标准库存项目显著不同。按项目类别进行分析有助于解释这些差异,并支持针对性的流程改进。 为何重要 通过区分不同类型的采购(如货物、服务或分包),解释流程差异。 获取方式 此属性位于 SAP S/4HANA 表 EKPO 的 PSTYP 字段。 示例 093 | |||
| 物料编号 MaterialNumber | 所采购的具体物料或商品的标识符。 | ||
| 描述 物料编号是分配给 SAP 中每个物料主记录的唯一代码。它用于与该物料相关的所有交易,包括采购、库存管理和销售。\n\n按物料编号或物料组进行分析可以实现基于商品的分析。它可以帮助识别某些类型物料的采购流程是否效率较低、提前期较长或更容易发生退货,为品类管理提供洞察。 为何重要 支持基于商品的分析,有助于识别与特定产品或材料相关的流程问题或供应商绩效问题。 获取方式 此属性位于 SAP S/4HANA 表 EKPO 的 MATNR 字段。 示例 RM100-100FG210SERV-CONSULT | |||
| 采购组 PurchasingGroup | 负责特定采购活动的具体采购员组。 | ||
| 描述 采购组是指负责特定采购活动、物料或供应商的买方或买方小组。他们是供应商的主要联系点。 与采购组织相比,该属性允许对工作量和绩效进行更细致的分析。它可以用来识别负载过重的团队,衡量不同买方小组的效率,并了解哪些小组更容易出现越权采购等流程偏差。 为何重要 提供采购组绩效的细粒度视图,支持在团队层面分析工作量、效率和流程合规性。 获取方式 此属性位于 SAP S/4HANA 表 EKKO 的 EKGRP 字段。 示例 001002N00 | |||
| 采购组织 PurchasingOrganization | 负责采购物料与服务并与供应商进行谈判的组织单元。 | ||
| 描述 采购组织是采购中的核心组织单位。它可以设置在集团、公司或工厂层面,负责所有的采购活动。\n\n按采购组织分析流程有助于评估不同采购团队或地区的效率和绩效。它可以突出显示不同组织单位在供应商谈判、流程合规性或审批延迟方面的差异。 为何重要 支持不同采购部门或地区之间的绩效比较,有助于识别最佳实践和待改进领域。 获取方式 此属性位于 SAP S/4HANA 表 EKKO 的 EKORG 字段。 示例 10101710US01 | |||
采购到付款—采购订单活动
| 活动 | 描述 | ||
|---|---|---|---|
| 发票已接收 | 代表供应商发票输入 SAP 系统并将其关联到相应采购订单。这是一项明确的财务过账,会生成会计凭证。 | ||
| 为何重要 这是连接采购流程与应付账款流程的关键里程碑。它支持分析从收货到发票处理之间的时间跨度。 获取方式 会计凭证在表 BKPF(抬头)中创建,其行项目位于 BSEG 或通用日记账 ACDOCA 中。该凭证通过表 RSEG 与采购订单链接。 捕获 来自会计凭证抬头表 BKPF 的凭证录入日期 (CPUDT)。 事件类型 explicit | |||
| 已创建请购单 | 此活动标志着对商品或服务的正式请求,从而启动采购流程。当用户保存新的采购申请凭证时(例如使用事务码 ME51N),该事件会被明确捕获。 | ||
| 为何重要 这是许多采购订单生命周期的主要起点。分析从该事件到采购订单创建的时间有助于识别寻源和内部处理中的延迟。 获取方式 记录在 EBAN(采购申请)表中。创建事件的时间戳可以在 EBAN 对象的变更历史表 CDHDR 和 CDPOS 中找到。 捕获 在 EBAN 表中创建凭证时记录的事件。 事件类型 explicit | |||
| 收货过账完成 | 代表从供应商处收到货物的物理过程及系统中相应的录入。这是一项明确的交易,会更新采购订单历史记录。 | ||
| 为何重要 这是一个重要的里程碑,它标志着供应商交货周期的结束和内部发票校验流程的开始。对于追踪准时交货率至关重要。 获取方式 在 MKPF(抬头)和 MSEG(项目)表中记录为物料凭证,并以特定的移动类型(如 101)关联到采购订单历史表 EKBE 中。 捕获 来自物料凭证抬头表 (MKPF) 的过账日期 (BUDAT),通过 EKBE 关联。 事件类型 explicit | |||
| 请购单已批准 | 代表经理或指定审批人对采购申请的正式审批。这通常根据申请凭证的状态变化来推断,表明其已准备好转换为采购订单。 | ||
| 为何重要 这是追踪审批周期和识别瓶颈的关键里程碑。此环节的延误会直接影响采购订单的创建速度及发送给供应商的时间。 获取方式 从 EBAN 表中的发布状态字段(例如 FRGZU - 发布指示符)推断得出。timestamp 派生自记录最终发布状态设定时间的变更凭证 (CDHDR/CDPOS)。 捕获 根据 EBAN 表中发布状态字段的变更日志 (CDHDR/CDPOS) 推断得出。 事件类型 inferred | |||
| 采购订单已创建 | 标志着正式采购订单文档的创建。订单可能参考或不参考采购申请创建。这是当采购订单文档首次在系统中保存时捕获的明确事件。 | ||
| 为何重要 此活动可作为流程的备选起点,特别是对于违规采购分析。它是追踪采购订单整体处理时间的基础事件。 获取方式 记录在采购订单抬头表 EKKO 中。凭证的创建日期 (AEDAT) 和时间直接存储在该表中。 捕获 EKKO 表中采购订单凭证的创建 timestamp (AEDAT)。 事件类型 explicit | |||
| 采购订单已完成 | 此活动表示采购订单行项目在物流层面被视为已关闭。当“交货已完成”和“最终发票”标记均已设置时,即可推断出此状态。 | ||
| 为何重要 这作为采购订单生命周期分析的终点。衡量到此事件的时间可以提供采购运营的端到端周期时间。 获取方式 从采购订单行项目表 EKPO 上的状态标志推断得出。当“交货完成”指示符 (ELIKZ) 和“最终发票”指示符 (EREKZ) 均设置为 true 时,发生该事件。 捕获 当 EKPO 字段 ELIKZ 和 EREKZ 均标记为完成时,从变更日志推断得出。 事件类型 inferred | |||
| 采购订单已批准 | 表示采购订单已获得所有必要的内部审批,并获准发布给供应商。该事件是根据采购订单发布策略中的状态变化推断出来的。 | ||
| 为何重要 这是衡量审批效率和审批后返工的关键里程碑。分析采购订单从创建到审批之间的时间可以揭示内部流程的延误。 获取方式 从 EKKO 表中的发布指示符 (FRGKE) 推断得出。timestamp 是通过查看变更历史 (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) 捕捉。timestamp 派生自变更凭证 (CDHDR/CDPOS)。 捕获 当设置删除标记 (LOEKZ) 时,来自变更凭证 (CDHDR/CDPOS) 的时间戳。 事件类型 explicit | |||
| 采购订单已发送给供应商 | 代表采购订单传达给供应商的时刻(例如通过 EDI、电子邮件或打印)。此事件通常通过系统的输出管理日志捕获。 | ||
| 为何重要 此活动是供应商交货周期的真实起点。对于准确衡量供应商从收到订单那一刻起的绩效至关重要。 获取方式 从输出控制表 NAST 中捕捉,该表记录了为采购凭证发送的消息。可以使用相关输出类型(如 EDI、电子邮件)的日期和时间。 捕获 NAST 表中采购订单首次成功输出消息的时间戳。 事件类型 inferred | |||
| 采购订单已变更 | 此活动表示在初始创建后对采购订单进行了修改,例如更改数量、价格或交货日期。这在系统变更日志中被明确捕获。 | ||
| 为何重要 追踪修改(尤其是审批后的修改)对于识别流程低效、返工和潜在合规问题至关重要。频繁的修改可能预示着最初的规格说明定义不当。 获取方式 记录在采购订单对象 (EINKBELEG) 的变更凭证表 CDHDR(抬头)和 CDPOS(项目)中。每次修改都会生成详细的日志条目。 捕获 为 EKKO 或 EKPO 表中关键字段的更改记录的事件,记录在 CDHDR/CDPOS 中。 事件类型 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 客户端中快速查看输出。检查是否存在不同的活动,确保 timestamp 已正确填充,并验证 case ID (PurchaseOrderNumber) 是否一致。
- 导出数据: 将 SQL 工具中的完整结果集导出为 CSV(逗号分隔值)文件。确保文件使用 UTF-8 编码以防止字符问题。
- 准备上传: 在上传到 ProcessMind 之前,打开 CSV 文件并验证列标题是否与数据要求中定义的属性完全匹配(PurchaseOrderNumber, ActivityName, EventTime 等)。如果您的导出工具更改了列名,请进行调整。
- 上传至 ProcessMind: 将最终确定的 CSV 文件上传到您的 ProcessMind 项目中。在导入过程中,将文件中的列映射到相应的 case ID、活动和 timestamp 字段。
配置
- 核心 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' 步骤
- 先决条件与访问: 确保您拥有的用户具有在 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 客户端中快速查看输出。检查是否存在不同的活动,确保 timestamp 已正确填充,并验证 case ID (PurchaseOrderNumber) 是否一致。
- 导出数据: 将 SQL 工具中的完整结果集导出为 CSV(逗号分隔值)文件。确保文件使用 UTF-8 编码以防止字符问题。
- 准备上传: 在上传到 ProcessMind 之前,打开 CSV 文件并验证列标题是否与数据要求中定义的属性完全匹配(PurchaseOrderNumber, ActivityName, EventTime 等)。如果您的导出工具更改了列名,请进行调整。
- 上传至 ProcessMind: 将最终确定的 CSV 文件上传到您的 ProcessMind 项目中。在导入过程中,将文件中的列映射到相应的 case ID、活动和 timestamp 字段。
配置
- 核心 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' 步骤
- 规格与设计: 定义事件日志文件的最终数据结构,包括所有必需和推荐的属性。记录用于获取 13 个必需活动数据的特定 SAP 表(例如 EKKO, EKPO, EKBE, CDHDR, CDPOS, BKPF)。
- 创建程序: 在 SAP GUI 中,使用事务代码 SE38 或 SE80 进入 ABAP 编辑器。创建一个新的可执行程序,例如 Z_PM_PO_EXTRACT。
- 定义选择屏幕: 为报告编写选择屏幕代码。这允许用户过滤他们想要提取的数据。包括采购订单创建日期范围 (P_AEDAT)、公司代码 (P_BUKRS) 和采购凭证类型 (P_BSART) 的参数。
- 数据声明: 定义程序所需的内表和数据结构。这包括一个与规格步骤中定义的结构相匹配的最终事件日志内表。
- 实现数据选择逻辑: 编写核心 ABAP 逻辑来选择每个活动的数据。这涉及针对相关 SAP 表的一系列 SELECT 语句,并在必要时进行关联。对于基于变更的事件,从变更日志表 CDHDR 和 CDPOS 中读取。
- 转换与映射数据: 对于检索到的每条记录,将 SAP 表字段映射到最终事件日志内表的对应列。根据正在处理的事件设置 ActivityName(例如“采购订单已创建”)。将日期和时间字段转换为 EventTime 的统一 timestamp 格式。
- 合并事件数据: 处理完所有 13 种活动类型后,确保所有数据都收集到一个统一的内表中。该表现在代表了所选采购订单的完整事件日志。
- 实现文件输出: 添加将最终内表写入文件的功能。推荐的方法是使用 cl_gui_frontend_services=>gui_download 方法,允许用户将文件作为 CSV 保存到本地机器上,或使用 OPEN DATASET 将其保存到 SAP 应用服务器以进行后台处理。
- 创建事务代码 (可选): 为了方便业务用户访问程序,使用事务代码 SE93 创建一个自定义事务代码(例如 ZPM_PO_EXTRACT)来执行您的 ABAP 程序。
- 计划后台作业: 对于大数据量或自动提取,使用事务代码 SM36 计划程序作为后台作业运行。输出文件将写入程序逻辑中指定的应用服务器路径。
配置
- 筛选标准: 程序应包含筛选参数以有效过滤数据。关键过滤器包括:
- 日期范围: 采购订单创建日期 (EKKO-AEDAT) 的必填日期范围。建议从 3-6 个月的时间段开始,以平衡数据量和报告性能。
- 公司代码 (BUKRS): 对于拥有多个法律实体的组织,缩小提取范围至关重要。
- 采购凭证类型 (BSART): 允许过滤特定的采购订单类型(如标准采购订单、框架订单或库存转储订单),以便进行针对性分析。
- 读取变更日志: 提取“采购订单已批准”或“采购订单已更改”等活动依赖于读取 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.