您的Record to Report-Journal Entry数据模板
您的Record to Report-Journal Entry数据模板
- 全面分析所需的推荐属性
- 需要跟踪的关键日记账分录活动
- SAP ECC数据提取实用指南
记录到报告-日记账分录属性
| 名称 | 说明 | ||
|---|---|---|---|
| 事件时间 EventTime | 表示Journal Entry中特定活动或事件发生时间的时间戳。 | ||
| 说明 事件时间提供Journal Entry流程中每项活动的准确日期和时间。这些数据对于计算周期时间、处理时长和步骤间延迟等所有时间指标至关重要。时间戳来源取决于活动类型,可以是凭证创建日期和时间(CPUDT/CPUTM),也可以是日志中的更改时间戳(CDHDR-UDATE/UTIME)。 在分析中,事件时间用于按时间顺序排列事件,是构建流程图的基础。它对于计算所有时间相关KPI不可或缺,例如Journal Entry平均周期时间、平均审批时间以及审批到过账的时间。 为什么重要 该时间戳是所有时间相关分析的基础,可用于计算周期时间、处理时长和瓶颈。 获取位置 根据活动类型从不同字段获取,主要来自BKPF中的创建时间戳(CPUDT、CPUTM),或CDHDR中的更改凭证时间戳(UDATE、UTIME)。 示例 2023-10-26T09:00:00Z2023-10-26T14:30:15Z2023-10-27T11:05:00Z | |||
| 日记账分录ID JournalEntryId | 财务会计凭证的唯一标识符,由公司代码、凭证编号和会计年度组成。 | ||
| 说明 Journal Entry ID是跟踪Journal Entry生命周期的主要案例标识符。它是一个复合键,通常由公司代码(BUKRS)、凭证编号(BELNR)和会计年度(GJAHR)拼接而成,以确保其在整个SAP系统中的唯一性。 在流程分析中,此ID用于关联创建、暂存、提交、审批、驳回和过账等所有相关活动。通过追踪该标识符,我们可以还原每个Journal Entry的端到端旅程,衡量周期时间,并识别特定分录的流程偏差或瓶颈。 为什么重要 这是跟踪Journal Entry从创建到最终过账的关键,可支持端到端流程分析和变体比较。 获取位置 这是一个派生属性,通常由BKPF表中的公司代码(BUKRS)、凭证编号(BELNR)和会计年度(GJAHR)字段拼接而成。 示例 1000-1000000123-20232000-1900000456-20231000-1800000789-2024 | |||
| 活动名称 ActivityName | Journal Entry流程中特定时点发生的业务活动或事件名称。 | ||
| 说明 活动名称描述Journal Entry生命周期中的具体步骤,例如“Journal Entry Created”“Journal Entry Approved”或“Journal Entry Posted”。该属性通常由SAP中的多个来源派生,包括事务代码(TCODE)、更改凭证日志(CDHDR和CDPOS表)以及凭证状态字段。 活动分析是流程挖掘的核心。它可以可视化流程图,计算步骤之间的转换时间,并识别返工循环,例如“Journal Entry Rejected”之后出现“Journal Entry Corrected”。这些数据是周期时间、返工率和流程变体相关仪表板的基础。 为什么重要 它定义流程图中的步骤,使Journal Entry工作流能够被可视化、分析和优化。 获取位置 来源包括BKPF中的事务代码TCODE、凭证状态、SWW_WI2OBJ等表中的工作流日志,以及CDHDR和CDPOS中的更改凭证。 示例 Journal Entry已创建Journal Entry已审批Journal Entry已驳回Journal Entry已过账 | |||
| 最后数据更新时间 LastDataUpdate | 表示数据最近一次从源系统提取或刷新的时间戳。 | ||
| 说明 此属性记录最近一次从SAP ECC提取数据的日期和时间。它是一个元数据字段,对于了解所分析数据的新鲜度和时效性至关重要。 在任何流程挖掘仪表板或分析中,了解最后更新时间对于建立用户对数据的信任并支持明智决策都十分关键。它有助于回答“这些信息更新到什么时候?”这一问题。 为什么重要 告知用户数据的新鲜度,帮助其了解分析时间范围并信任分析结果。 获取位置 这是由数据提取工具或ETL流程在数据刷新时生成并存储的元数据字段。 示例 2024-05-20T04:00:00Z2024-05-21T04:00:00Z2024-05-22T04:00:00Z | |||
| 源系统 SourceSystem | 提取流程数据的系统。 | ||
| 说明 此属性用于标识数据来源,本例中为特定的SAP ECC实例。它通常是在数据提取过程中添加的静态值。 在包含多个ERP或数据源的环境中,该属性虽然简单却十分重要。它可以确保数据血缘清晰,并支持按来源系统筛选或细分分析。 为什么重要 提供清晰的数据血缘,对于多源系统环境中的数据质量跟踪至关重要。 获取位置 通常是在数据转换过程中添加的静态值,用于标识特定的SAP ECC实例,例如“ECC_PROD_100”。 示例 SAP ECC EHP8ECC_FIN_PRODSAP_ERP_60 | |||
| 事务代码 TransactionCode | 用于创建或处理Journal Entry的SAP事务代码。 | ||
| 说明 事务代码(T-Code)是SAP中特定功能或程序的唯一标识符。对于Journal Entry,它表示分录的创建方式,例如手工创建(FB01、F-02)、暂存创建(FV50)或通过自动化接口创建。 该属性对于“手工活动优化”仪表板非常有价值。通过分析T-Code,我们可以区分手工和自动化活动,识别最耗时的手工流程,并定位自动化机会,以减少手工工作量、提升效率。 为什么重要 帮助区分手工与自动化流程,识别自动化和流程标准化机会。 获取位置 位于凭证抬头表BKPF的TCODE字段。 示例 FB01F-02FV50FBD1 | |||
| 公司代码 CompanyCode | 代表独立法人并据此编制财务报表的组织单元。 | ||
| 说明 公司代码是SAP Financials中的基础组织单元,代表一个独立法人,也是Journal Entry凭证抬头中的关键字段。 该属性对于按法人细分流程分析至关重要。它支持比较企业不同部分的流程绩效、合规率和KPI结果。例如,可以帮助识别审批延迟或高冲销率是否集中在特定公司代码。 为什么重要 支持在组织内不同法人或业务单元之间筛选和比较流程绩效。 获取位置 位于凭证抬头表BKPF的BUKRS字段。 示例 10002000US01DE01 | |||
| 凭证类型 DocumentType | 用于分类会计凭证,并控制其处理和存储方式。 | ||
| 说明 凭证类型用于区分不同类型的业务交易,例如总账分录(SA)、供应商发票(KR)或资产过账(AA)。它在系统配置阶段定义,并分配给每个Journal Entry。 这是分析中的关键属性,可按交易性质细分流程。“按类型统计Journal Entry吞吐量”仪表板和“按Journal Entry类型统计平均周期时间”KPI都直接依赖此字段。它有助于发现某些类型的分录是否更容易出现延迟、返工或冲销。 为什么重要 支持按交易类型细分分析,帮助识别流程问题是否集中在特定类型的Journal Entry中。 获取位置 位于凭证抬头表BKPF的BLART字段。 示例 SAKRREAA | |||
| 是否已冲销 IsReversed | 表示Journal Entry是否已冲销的布尔标记。 | ||
| 说明 此标记用于识别随后被另一张会计凭证冲销的Journal Entry。在SAP中,已冲销凭证会与冲销凭证关联,从而形成清晰的审计轨迹。 该属性是“Journal Entry冲销分析”仪表板和“Journal Entry冲销率”KPI的基础。它支持筛选已冲销分录并调查数据录入错误或会计处理不当等根因,以降低冲销发生频率。 为什么重要 通过标记后续被撤销的分录,直接支持冲销分析,帮助识别错误根因并提升数据完整性。 获取位置 根据BKPF表中的冲销凭证编号字段STBLG派生。如果STBLG不为空,则该标记为true。 示例 truefalse | |||
| 用户 User | 创建或更改Journal Entry的SAP用户ID。 | ||
| 说明 此属性记录负责执行特定活动的SAP用户名,例如创建、暂存或过账凭证。数据直接来自凭证抬头表或更改日志表。 分析用户属性是了解团队和个人绩效的关键。它支持用户生产力仪表板,跟踪每位用户的活动量和处理时间,还有助于识别返工循环、冲销或合规偏差的相关人员,从而开展针对性培训或流程改进。 为什么重要 识别每项活动的负责人,支持用户绩效、工作量分配和返工模式分析。 获取位置 通常来自BKPF表(创建者字段USNAM)或CDHDR表(更改者字段USERNAME)。 示例 ABROWNCJONESDSMITH | |||
| 过账日期 PostingDate | 交易记入总账并影响财务期间的日期。 | ||
| 说明 过账日期决定Journal Entry计入的会计期间。从财务和合规角度看,这是一个关键日期字段,必须与会计期间结账安排和监管要求保持一致。 在流程挖掘中,该日期用于监控合规性。“合规遵循监控”仪表板和“合规符合率”KPI利用此属性检查分录是否在正确期间过账,也可用于分析Journal Entry数量随时间变化的趋势。 为什么重要 对于财务报告和合规分析至关重要,可确保分录在正确的会计期间过账。 获取位置 位于凭证抬头表BKPF的BUDAT字段。 示例 2023-10-312023-11-302024-01-15 | |||
| 冲销原因 ReversalReason | 表示Journal Entry被冲销原因的代码。 | ||
| 说明 当凭证被冲销时,SAP允许用户指定原因代码。该代码以结构化方式说明冲销原因,例如过账日期错误或数据录入错误。 此属性是“Journal Entry Reversal Analysis”仪表板的重要输入。通过分析最常见的冲销原因,组织可以发现流程中的系统性问题或培训缺口,并采取有针对性的措施,避免未来发生错误,降低冲销率。 为什么重要 直接了解冲销发生的原因,从而开展有针对性的根因分析,减少未来错误。 获取位置 位于凭证抬头表BKPF的STGRD字段。 示例 010205 | |||
| 凭证总金额 TotalDocumentAmount | 以凭证货币计量的Journal Entry总金额。 | ||
| 说明 此属性表示Journal Entry的财务总金额,通常通过汇总该凭证所有借方或贷方行项目的绝对值计算得出。 按财务金额分析流程可以发现重要模式。例如,高金额分录可能遵循不同且更严格的审批路径。通过此属性筛选或细分分析,可以判断周期时间、驳回率或审批延迟是否与分录金额相关。 为什么重要 支持财务影响分析,例如分析处理时间或驳回率与Journal Entry金额之间的关系。 获取位置 这是一个计算字段,通过汇总BSEG表中指定Journal Entry所有行项目的金额字段(WRBTR或DMBTR)得出。 示例 1500.0025000.75125.50 | |||
| 审批时间 ApprovalTime | 从日记账分录提交审批到获批或被拒绝所经过的时间。 | ||
| 说明 此指标衡量审批子流程的持续时间,而审批子流程通常是整体周期时间的重要组成部分。计算方式为“Journal Entry Submitted”活动与对应的“Journal Entry Approved”或“Journal Entry Rejected”活动之间的时间差。 审批时间是“Journal Entry Approval Performance”仪表板和“Average Journal Entry Approval Time”KPI的核心指标。分析该时长有助于识别审批工作流中的瓶颈、衡量审批人的绩效,并为调整审批阈值等流程变更提供依据。 为什么重要 量化审批阶段的持续时间,帮助定位并解决审核和审批工作流中的延迟。 获取位置 用“Journal Entry Approved”或“Journal Entry Rejected”事件的时间戳减去“Journal Entry Submitted”事件的时间戳。 示例 P1DT2HPT4H15MP3D | |||
| 成本中心 CostCenter | 控制范围内的组织单元,表示发生成本的位置。 | ||
| 说明 成本中心是Controlling(CO)模块中的关键主数据元素,通常分配在日记账分录行项目层级。它用于跟踪特定部门、职能或地点产生的成本。 纳入成本中心后,可以更细致地分析日记账分录流程。您可以据此判断某些部门是否产生更多返工、周期时间更长,或手工录入量更高,从而从部门层面了解流程效率。 为什么重要 支持按部门或职能领域分析流程绩效,帮助定位局部效率低下的问题。 获取位置 位于凭证行项目表BSEG的KOSTL字段。 示例 4100CC_FINANCE_US10010101 | |||
| 是否暂存 IsParked | 布尔标记,表示日记账分录是否在过账前以暂存凭证形式保存。 | ||
| 说明 暂存凭证允许用户保存未完成的日记账分录,且不会影响财务余额。之后,其他用户可以在过账前完成或审核该分录。此标记用于识别经过暂存步骤的分录。 分析此属性有助于了解暂存功能的使用情况。它可以揭示暂存是否被用作非正式审核步骤,并可能造成延迟。同时,它支持端到端周期时间分析,区分直接过账的分录与先暂存后过账的分录。 为什么重要 识别使用暂存功能的分录。暂存可能造成延迟,也可能表明流程中存在非正式审核。 获取位置 根据表BKPF中的凭证状态字段(BSTAT)推导。值为“V”表示暂存凭证。 示例 truefalse | |||
| 是否返工 IsRework | 布尔标记,表示日记账分录是否经历过返工循环,例如被拒绝后重新更正。 | ||
| 说明 此标记用于识别偏离“正常路径”并需要采取纠正措施的案例。对于某个日记账分录,如果观察到“Journal Entry Rejected”后紧接“Journal Entry Corrected”等活动序列,通常会将该标记设为true。 此属性对于计算“Journal Entry Rework Rate”KPI以及分析“Rework and Rejection Rate”仪表板至关重要。它有助于量化流程低效程度,并为调查返工根因提供依据,例如需求不明确或文档不足。 为什么重要 标记需要更正的分录,从而量化返工并分析其根因,提升一次通过率。 获取位置 这是一个计算属性,通过分析案例的活动序列推导得出。如果出现拒绝或更正活动,则识别为返工循环。 示例 truefalse | |||
| 货币键 CurrencyKey | Journal Entry中记录金额所使用的货币代码。 | ||
| 说明 此属性指定Journal Entry使用的货币,例如USD、EUR或JPY,为凭证中的财务金额提供背景信息。 虽然它不一定是主要分析维度,但对于正确解读金额至关重要。在全球化组织中,还可以据此细分分析,比较外币和本币分录的流程是否存在差异。 为什么重要 为所有金额提供必要背景,确保财务分析和解读准确。 获取位置 位于凭证抬头表BKPF的WAERS字段。 示例 USDEURGBPJPY | |||
记录到报告-日记账分录活动
| 活动 | 说明 | ||
|---|---|---|---|
| Journal Entry冲销已处理 | 此活动标志着此前已过账Journal Entry的冲销。冲销会生成新的会计凭证,用于抵销原始分录。 | ||
| 为什么重要 这是衡量数据质量和流程准确性的关键事件。冲销率较高通常表明初始数据录入或审批阶段存在系统性问题,每次冲销都意味着返工。 获取位置 在原始凭证抬头BKPF表中识别此事件。凭证被冲销后,SAP会填充冲销凭证编号BKPF-STBLG和冲销原因BKPF-STGRD。事件时间戳为新冲销凭证的过账日期。 采集 识别原始凭证中BKPF-STBLG被填充的时刻;时间戳为冲销凭证的过账日期。 事件类型 explicit | |||
| Journal Entry已审批 | 此活动标志着工作流中Journal Entry的最终审批完成,凭证具备过账条件。该事件从工作流日志中最终“release”或“approve”步骤完成的记录捕获。 | ||
| 为什么重要 这是审批流程结束的关键里程碑。到达此活动所需的时长是审批效率的重要KPI,而从此事件到过账的时间则用于衡量审批后延迟。 获取位置 根据SAP Business Workflow日志中最终审批步骤的完成时间戳推断。这是凭证过账或准备过账前的最后一个审批操作。 采集 在工作流日志中识别最终“release”或“approve”步骤的完成记录。 事件类型 inferred | |||
| Journal Entry已提交 | 此活动表示暂存的Journal Entry已由创建者完成,现在可以进入审核和审批环节。通常通过启动与暂存凭证关联的SAP Business Workflow任务来捕获。 | ||
| 为什么重要 这标志着工作从创建者交接给审批人,并开始计算审批周期时间KPI。它是衡量审批工作流效率的关键里程碑。 获取位置 根据与财务凭证对象关联的审批工作流实例开始时间推断。需要分析SWW_WI2OBJ等工作流日志表,查找针对特定公司代码、凭证编号和会计年度启动的工作流。 采集 识别暂存凭证对象的工作流开始事件。 事件类型 inferred | |||
| Journal Entry已暂存 | 此活动表示Journal Entry最初以临时状态创建,尚未正式过账至总账。在SAP中,用户通过暂存事务保存凭证并将凭证状态设为“parked”时,系统会明确记录该活动。 | ||
| 为什么重要 对于涉及审核和审批的流程,这是一个关键的开始事件。分析暂存到过账之间的时间,有助于识别过账前和审批阶段的延迟。 获取位置 此事件从凭证抬头表BKPF中识别。当凭证创建时BKPF-BSTAT='V',即可视为已暂存。事件时间为创建日期和时间,即BKPF-CPUDT和BKPF-CPUTM。 采集 在BKPF中识别BKPF-BSTAT为“V”的凭证创建事件。 事件类型 explicit | |||
| Journal Entry已过账 | 这是Journal Entry正式记入总账并影响财务报表的核心活动。当凭证状态设为“posted”并分配过账日期时,系统会明确记录该事件。 | ||
| 为什么重要 这是最重要的里程碑,表示Journal Entry已成功处理。端到端周期时间通常计算至此,也是财务结账分析的关键事件。 获取位置 当BKPF表中的凭证具有过账日期BKPF-BUDAT时,即可识别该事件。对于暂存凭证,这对应于BKPF-BSTAT从“V”变为空白的时刻。过账时间戳为录入日期BKPF-CPUDT。 采集 识别BKPF-BSTAT从“V”变为空白的时刻;对于直接过账,则识别凭证创建事件。 事件类型 explicit | |||
| Journal Entry已创建 | 表示Journal Entry已直接过账创建,之前没有暂存步骤。当用户在SAP中通过直接过账事务创建凭证时,系统会记录该活动。 | ||
| 为什么重要 对于不需要审批工作流的简单Journal Entry流程,此活动可作为另一种起点。它有助于区分简单的直接过账与更复杂的暂存分录。 获取位置 此事件对应BKPF表中的凭证创建,且凭证状态BKPF-BSTAT为空,即已过账。事件时间为创建日期BKPF-CPUDT。对于此类凭证,“Created”和“Posted”事件会同时发生。 采集 在BKPF中识别BKPF-BSTAT为空的凭证创建事件。 事件类型 explicit | |||
| Journal Entry已更正 | 此活动表示原创建者在暂存的Journal Entry被退回要求更改后,对其进行了修改。通过检测“Changes Requested”事件后的凭证更改来推断。 | ||
| 为什么重要 跟踪更正有助于量化返工投入。变更请求到完成更正之间的时间,可以反映解决已提交分录问题所需的延迟。 获取位置 通过分析暂存凭证的更改凭证日志表CDHDR和CDPOS推断。如果工作流驳回事件后记录了更改,则表示已完成更正。时间戳来自CDHDR表。 采集 识别驳回事件后CDHDR/CDPOS中的更改日志记录。 事件类型 inferred | |||
| Journal Entry已驳回 | 此活动表示Journal Entry被最终驳回,之后不会再过账。它通常是审批工作流中的终止状态,最终可能导致暂存凭证被删除。 | ||
| 为什么重要 跟踪驳回对于质量管理至关重要。分析驳回原因和频率,有助于提升Journal Entry一次正确率。 获取位置 这是从SAP Business Workflow日志中捕获的结果,表示终止流程的最终“reject”用户决策。暂存凭证随后可能被删除。 采集 在凭证工作流日志中识别终止性的“reject”状态。 事件类型 inferred | |||
| Journal Entry行项目已清账 | 此活动表示对未清项管理的总账账户行项目进行核对,例如银行清算账户。当某个行项目与另一个行项目匹配并完成结清时,该活动发生。 | ||
| 为什么重要 对于银行对账等流程,项目清账时间是关键KPI。此活动有助于分析对账和月末结账流程的效率。 获取位置 此事件从行项目表BSEG中捕获。行项目清账后,清账日期BSEG-AUGDT和清账凭证BSEG-AUGBL字段会被填充。事件时间为清账日期。 采集 识别行项目清账日期BSEG-AUGDT被填充的时刻。 事件类型 explicit | |||
| Journal Entry要求更改 | 表示审批人在审核Journal Entry后,将其退回创建者进行更正的工作流节点。该事件从工作流日志中表示“rejection”或“send back”的用户决策记录捕获。 | ||
| 为什么重要 此活动对于识别返工循环至关重要。返工循环是效率低下和流程偏差的主要来源,高频发生通常表明分录质量存在问题或要求不明确。 获取位置 根据SAP Business Workflow日志中特定用户决策步骤的时间戳推断,该步骤对应“reject”或“send for correction”操作。 采集 在工作流日志中识别“rejection”或“rework”决策时间戳。 事件类型 inferred | |||
| 已识别手工录入 | 此活动用于识别Journal Entry是通过在线手工事务创建,还是通过自动接口或批处理创建。它不是用户操作,而是根据系统数据计算得出的分录属性。 | ||
| 为什么重要 区分手工分录与自动分录,是开展针对性流程改进的关键。手工流程通常是标准化和自动化项目的重点。 获取位置 通过分析凭证抬头表BKPF中的字段计算得出。事务代码BKPF-TCODE为“FB01”“FB50”或“FV50”等时,通常表示手工录入;其他T-code或特定批输入名称BKPF-AWKEY则可能表示自动化处理。 采集 根据BKPF-TCODE或凭证抬头中的其他源系统指标推导。 事件类型 calculated | |||
| 已识别跨公司过账 | 此计算活动用于标记影响多个公司代码的Journal Entry。通过分析单个财务凭证的行项目确定。 | ||
| 为什么重要 跨公司交易的处理和审批要求通常更复杂。识别此类交易后,可以单独分析其周期时间和流程路径,发现特有瓶颈。 获取位置 通过检查指定凭证编号BELNR在行项目表BSEG中的记录计算得出。如果行项目包含多个不同的公司代码BSEG-BUKRS,则该分录属于跨公司过账。 采集 检查单个BKPF-BELNR是否存在多个不同的BSEG-BUKRS值。 事件类型 calculated | |||
| 已附加文档 | 此活动表示用户将发票或电子表格等支持性文档附加到Journal Entry。该事件通常不会作为标准会计事件明确记录,而是通过检查与会计凭证对象关联的附件创建记录来推断。 | ||
| 为什么重要 跟踪此活动有助于验证是否遵循要求提供文档的政策。附件添加延迟可能是审批周期过长的根因。 获取位置 由于缺少带时间戳的明确事件,该活动难以可靠捕获。可以通过分析Generic Object Services(GOS)附件表(如SOOD),并将附件创建时间戳与Journal Entry对象键关联来推断。 采集 根据GOS表中关联对象的创建时间戳推断,例如SOOD。 事件类型 inferred | |||
| 暂存Journal Entry已删除 | 表示从未过账的暂存Journal Entry被删除。该情况可能发生在凭证被驳回后,或分录创建错误时。 | ||
| 为什么重要 此活动标志着流程未成功结束。分析暂存凭证被删除的原因,可以发现重复分录或流程理解有误等问题。 获取位置 当BKPF表中暂存凭证的状态发生更改时,系统会捕获此事件。状态字段BKPF-BSTAT会更新为“Z”(暂存凭证已删除)。更改时间戳可在凭证更改日志CDHDR中找到。 采集 识别BKPF-BSTAT更新为“Z”的时刻。 事件类型 explicit | |||
提取指南
步骤
- 创建ABAP程序:在SAP系统中,进入事务代码SE38(ABAP编辑器)。输入新程序名称,例如Z_PM_JE_EXTRACTION,然后点击“创建”。填写合适的标题,并将程序类型设置为“可执行程序”。
- 定义选择屏幕:在程序源代码中定义选择屏幕,允许用户指定凭证创建日期范围、公司代码和凭证类型,以限制提取数据量。
- 声明数据结构:定义用于存放最终事件日志数据的内表结构。该结构必须包含所有必需字段:JournalEntryId、ActivityName、EventTime、SourceSystem、LastDataUpdate,以及User、CompanyCode和PostingDate等推荐属性。
- 实现数据选择逻辑:编写核心ABAP SQL查询,提取14项必需活动的数据。数据来源包括BKPF(抬头)和BSEG(行项目)等主表、变更日志表CDHDR和CDPOS、SWWLOGHIST等工作流表,以及BSAS和BSAK等清账表。
- 提取暂存和已过账凭证:对于“Journal Entry Parked”事件,从BKPF中选择凭证状态(BSTAT)为“V”的记录。对于“Journal Entry Created”和“Journal Entry Posted”事件,从BKPF中选择状态为空的记录,表示正常的已过账凭证。
- 提取变更和删除事件:查询变更凭证表CDHDR和CDPOS,筛选对象类别“BELEG”,并按凭证键过滤,查找对应“Journal Entry Corrected”或“Parked Journal Entry Deleted”活动的变更。
- 提取工作流事件:要捕获“Journal Entry Submitted”“Approved”“Rejected”和“Changes Requested”等活动,请查询工作流表。使用SWW_WI2OBJ将会计凭证关联到工作流实例,然后读取SWWLOGHIST中的具体用户决策或状态变更。
- 识别计算活动:对于“Manual Entry Identified”,检查事务代码(BKPF-TCODE)是否属于已知手工录入事务代码列表。对于“Cross-Company Posting Identified”,分析某凭证的BSEG行项目,判断是否涉及多个公司代码。
- 整合并转换数据:选择各项活动的数据后,将其转换为最终事件日志结构。拼接Company Code、Document Number和Fiscal Year,生成JournalEntryId。将SAP日期和时间转换为单一的EventTime时间戳,并将各查询结果追加到最终内表。
- 实现文件导出:使用OPEN DATASET、LOOP AT、TRANSFER和CLOSE DATASET等ABAP文件处理语句,将整合后的内表写入SAP应用服务器目录中的CSV或平面文件,可通过事务AL11查看。
- 设置后台作业:进入事务SM36(定义后台作业)。创建新作业,定义执行ABAP程序的步骤,并设置计划,例如在业务低峰期每晚或每周运行,以自动执行提取。
- 获取并格式化文件:使用事务CG3Y,或联系系统管理员,将生成的文件从应用服务器下载到本地计算机。确保文件编码和格式适合上传到您的流程挖掘工具。
配置
- 日期范围:必须定义日期范围以控制性能。使用凭证创建日期(BKPF-CPUDT)作为主要筛选条件。首次分析建议选择3至6个月的数据。测试时,可选择已知包含数据的几天作为范围。
- 公司代码筛选:始终按公司代码(BKPF-BUKRS)筛选。一次提取所有公司代码的数据可能会大量占用系统资源。建议先从一个或少量相关公司代码开始。
- 凭证类型筛选:使用凭证类型筛选条件(BKPF-BLART)将范围缩小到特定日记账分录类型,例如不需要分析所有凭证类型时,可选择“SA”表示总账凭证。
- 工作流任务ID:工作流事件的提取逻辑取决于系统中用于审批、拒绝和提交的具体任务ID。这些ID必须根据贵公司的工作流定义配置到程序源代码中。
- 性能注意事项:程序会连接多个大型表,尤其是CDPOS和工作流历史表。在业务高峰期运行可能影响系统性能。请始终将其设置为后台作业,在低峰期运行。如果性能问题反复出现,可考虑创建辅助数据库索引。
- 前提条件:此方法要求用户拥有ABAP开发授权(用于SE38),以及创建和管理后台作业的权限(用于SM36)。用户或作业还需要对所有相关财务、工作流和系统表(BKPF、BSEG、CDHDR、CDPOS、SWWLOGHIST等)拥有读取权限。
a 示例查询 abap
REPORT Z_PM_JE_EXTRACTION.
*&---------------------------------------------------------------------*
*& Data Structures for Final Event Log
*&---------------------------------------------------------------------*
TYPES: BEGIN OF ty_event_log,
journalentryid TYPE string,
activityname TYPE string,
eventtime TYPE timestamp,
sourcesystem TYPE string,
lastdataupdate TYPE timestamp,
username TYPE uname,
companycode TYPE bukrs,
documenttype TYPE blart,
postingdate TYPE budat,
transactioncode TYPE tcode,
isreversed TYPE abap_bool,
END OF ty_event_log.
DATA: lt_final_log TYPE STANDARD TABLE OF ty_event_log.
DATA: ls_event TYPE ty_event_log.
*&---------------------------------------------------------------------*
*& Selection Screen Parameters
*&---------------------------------------------------------------------*
SELECT-OPTIONS: s_bukrs FOR bkpf-bukrs OBLIGATORY,
s_blart FOR bkpf-blart,
s_cpudt FOR bkpf-cpudt OBLIGATORY.
PARAMETERS: p_sysid TYPE sy-sysid DEFAULT sy-sysid.
*&---------------------------------------------------------------------*
*& Main Logic
*&---------------------------------------------------------------------*
START-OF-SELECTION.
DATA(lv_last_update) = cl_abap_context_info=>get_system_timestamp( ).
" 1. Journal Entry Parked
SELECT CONCAT( a~bukrs, a~belnr, a~gjahr ) AS journalentryid,
'Journal Entry Parked' AS activityname,
a~cpudt, a~cputm,
a~usnam AS username,
a~bukrs AS companycode,
a~blart AS documenttype,
a~bldat AS postingdate,
a~tcode AS transactioncode
FROM bkpf AS a
WHERE a~bukrs IN s_bukrs
AND a~blart IN s_blart
AND a~cpudt IN s_cpudt
AND a~bstat = 'V' " Parked Document
INTO TABLE @DATA(lt_parked).
IF sy-subrc = 0.
LOOP AT lt_parked ASSIGNING FIELD-SYMBOL(<fs_parked>).
ls_event-journalentryid = <fs_parked>-journalentryid.
ls_event-activityname = <fs_parked>-activityname.
CONVERT DATE <fs_parked>-cpudt TIME <fs_parked>-cputm INTO TIME STAMP ls_event-eventtime TIME ZONE sy-zonlo.
ls_event-sourcesystem = p_sysid.
ls_event-lastdataupdate = lv_last_update.
ls_event-username = <fs_parked>-username.
ls_event-companycode = <fs_parked>-companycode.
ls_event-documenttype = <fs_parked>-documenttype.
ls_event-postingdate = <fs_parked>-postingdate.
ls_event-transactioncode = <fs_parked>-transactioncode.
APPEND ls_event TO lt_final_log.
ENDLOOP.
ENDIF.
" 2. Journal Entry Created (directly posted, not parked first)
" 9. Journal Entry Posted
" These two events happen at the same time for a direct posting.
SELECT CONCAT( bukrs, belnr, gjahr ) AS journalentryid,
cpudt, cputm, usnam, bukrs, blart, budat, tcode, stblg
FROM bkpf
WHERE bukrs IN s_bukrs
AND blart IN s_blart
AND cpudt IN s_cpudt
AND bstat = '' " Normal, posted document
INTO TABLE @DATA(lt_posted).
IF sy-subrc = 0.
LOOP AT lt_posted ASSIGNING FIELD-SYMBOL(<fs_posted>).
" Activity: Journal Entry Created
ls_event-journalentryid = <fs_posted>-journalentryid.
ls_event-activityname = 'Journal Entry Created'.
CONVERT DATE <fs_posted>-cpudt TIME <fs_posted>-cputm INTO TIME STAMP ls_event-eventtime TIME ZONE sy-zonlo.
ls_event-sourcesystem = p_sysid.
ls_event-lastdataupdate = lv_last_update.
ls_event-username = <fs_posted>-usnam.
ls_event-companycode = <fs_posted>-bukrs.
ls_event-documenttype = <fs_posted>-blart.
ls_event-postingdate = <fs_posted>-budat.
ls_event-transactioncode = <fs_posted>-tcode.
ls_event-isreversed = COND #( WHEN <fs_posted>-stblg IS NOT INITIAL THEN abap_true ELSE abap_false ).
APPEND ls_event TO lt_final_log.
" Activity: Journal Entry Posted
ls_event-activityname = 'Journal Entry Posted'.
APPEND ls_event TO lt_final_log.
ENDLOOP.
ENDIF.
" 3. Documentation Attached (via GOS)
SELECT a~instid_a, c~cr_timestamp
FROM srgbtbrel AS a
INNER JOIN sood AS b ON a~instid_b = b~objid
INNER JOIN socf AS c ON b~filid = c~filid
WHERE a~typeid_a = 'BKPF'
AND a~bukrs IN s_bukrs
INTO TABLE @DATA(lt_attachments).
IF sy-subrc = 0.
LOOP AT lt_attachments ASSIGNING FIELD-SYMBOL(<fs_attach>).
ls_event-journalentryid = |{ <fs_attach>-instid_a(4) }{ <fs_attach>-instid_a+4(10) }{ <fs_attach>-instid_a+14(4) }|.
ls_event-activityname = 'Documentation Attached'.
ls_event-eventtime = <fs_attach>-cr_timestamp.
" Other attributes may need to be looked up from BKPF if needed.
APPEND ls_event TO lt_final_log.
ENDLOOP.
ENDIF.
" 4, 5, 6, 7, 8: Workflow events (Submitted, Changes Requested, Corrected, Approved, Rejected)
" This is a simplified example. Real logic depends on specific workflow templates.
SELECT a~instid, b~wi_cd, b~wi_ct, b~wi_aagent, b~wi_text
FROM sww_wi2obj AS a
INNER JOIN swwloghist AS b ON a~wi_id = b~wi_id
WHERE a~typeid = 'BKPF'
AND a~catid = 'BO'
AND a~bukrs IN s_bukrs
AND b~wi_cd BETWEEN s_cpudt-low AND s_cpudt-high
INTO TABLE @DATA(lt_workflow).
IF sy-subrc = 0.
LOOP AT lt_workflow ASSIGNING FIELD-SYMBOL(<fs_wf>).
ls_event-journalentryid = |{ <fs_wf>-instid(4) }{ <fs_wf>-instid+4(10) }{ <fs_wf>-instid+14(4) }|.
ls_event-activityname = CASE <fs_wf>-wi_text. " Simplified logic based on work item text
WHEN '[Placeholder for Submit Text]' THEN 'Journal Entry Submitted'
WHEN '[Placeholder for Approve Text]' THEN 'Journal Entry Approved'
WHEN '[Placeholder for Reject Text]' THEN 'Journal Entry Rejected'
WHEN '[Placeholder for Rework Text]' THEN 'Journal Entry Changes Requested'
ELSE ''
ENDCASE.
IF ls_event-activityname IS NOT INITIAL.
CONVERT DATE <fs_wf>-wi_cd TIME <fs_wf>-wi_ct INTO TIME STAMP ls_event-eventtime TIME ZONE sy-zonlo.
ls_event-username = <fs_wf>-wi_aagent.
APPEND ls_event TO lt_final_log.
ENDIF.
ENDLOOP.
ENDIF.
" 10. Manual Entry Identified & 11. Cross-Company Posting Identified
SELECT bukrs, belnr, gjahr, tcode FROM bkpf
WHERE bukrs IN s_bukrs AND blart IN s_blart AND cpudt IN s_cpudt
INTO TABLE @DATA(lt_calc_base).
LOOP AT lt_calc_base ASSIGNING FIELD-SYMBOL(<fs_calc>).
ls_event-journalentryid = |{ <fs_calc>-bukrs }{ <fs_calc>-belnr }{ <fs_calc>-gjahr }|.
" Check for manual entry T-Codes
IF <fs_calc>-tcode = 'FB01' OR <fs_calc>-tcode = 'F-02' OR <fs_calc>-tcode = 'FB50'.
ls_event-activityname = 'Manual Entry Identified'.
APPEND ls_event TO lt_final_log.
ENDIF.
" Check for cross-company posting
SELECT SINGLE bukrs FROM bseg WHERE belnr = <fs_calc>-belnr AND gjahr = <fs_calc>-gjahr AND bukrs <> <fs_calc>-bukrs INTO @DATA(lv_cross_bukrs).
IF sy-subrc = 0.
ls_event-activityname = 'Cross-Company Posting Identified'.
APPEND ls_event TO lt_final_log.
ENDIF.
ENDLOOP.
" 12. Journal Entry Line Item Cleared
SELECT a~bukrs, a~belnr, a~gjahr, a~augdt, a~augbl
FROM bsas AS a " G/L Cleared Items
WHERE a~bukrs IN s_bukrs
AND a~budat IN s_cpudt
INTO TABLE @DATA(lt_cleared_gl).
IF sy-subrc = 0.
LOOP AT lt_cleared_gl ASSIGNING FIELD-SYMBOL(<fs_clr>).
ls_event-journalentryid = |{ <fs_clr>-bukrs }{ <fs_clr>-belnr }{ <fs_clr>-gjahr }|.
ls_event-activityname = 'Journal Entry Line Item Cleared'.
CONVERT DATE <fs_clr>-augdt INTO TIME STAMP ls_event-eventtime TIME ZONE sy-zonlo.
" User is often not directly available for clearing events
APPEND ls_event TO lt_final_log.
ENDLOOP.
ENDIF.
" 13. Parked Journal Entry Deleted & 6. Journal Entry Corrected
SELECT objectid, changenr, username, udate, utime FROM cdhdr
WHERE objectclas = 'BELEG'
AND udate IN s_cpudt
INTO TABLE @DATA(lt_cdhdr).
LOOP AT lt_cdhdr ASSIGNING FIELD-SYMBOL(<fs_cdhdr>).
SELECT SINGLE tcode FROM cdpos WHERE changenr = <fs_cdhdr>-changenr AND fname = 'BSTAT' AND value_new = 'Z' INTO @DATA(lv_deleted_tcode).
ls_event-journalentryid = |{ <fs_cdhdr>-objectid(4) }{ <fs_cdhdr>-objectid+4(10) }{ <fs_cdhdr>-objectid+14(4) }|.
CONVERT DATE <fs_cdhdr>-udate TIME <fs_cdhdr>-utime INTO TIME STAMP ls_event-eventtime TIME ZONE sy-zonlo.
ls_event-username = <fs_cdhdr>-username.
IF sy-subrc = 0.
ls_event-activityname = 'Parked Journal Entry Deleted'.
APPEND ls_event TO lt_final_log.
ELSE.
ls_event-activityname = 'Journal Entry Corrected'.
APPEND ls_event TO lt_final_log.
ENDIF.
ENDLOOP.
" 14. Journal Entry Reversal Processed
SELECT CONCAT( a~bukrs, a~belnr, a~gjahr ) AS journalentryid,
a~cpudt, a~cputm, a~usnam
FROM bkpf AS a
WHERE a~bukrs IN s_bukrs
AND a~blart IN s_blart
AND a~cpudt IN s_cpudt
AND a~stblg IS NOT NULL " Document is a reversal
INTO TABLE @DATA(lt_reversals).
IF sy-subrc = 0.
LOOP AT lt_reversals ASSIGNING FIELD-SYMBOL(<fs_rev>).
ls_event-journalentryid = <fs_rev>-journalentryid.
ls_event-activityname = 'Journal Entry Reversal Processed'.
CONVERT DATE <fs_rev>-cpudt TIME <fs_rev>-cputm INTO TIME STAMP ls_event-eventtime TIME ZONE sy-zonlo.
ls_event-username = <fs_rev>-usnam.
APPEND ls_event TO lt_final_log.
ENDLOOP.
ENDIF.
" Final step: Output to file
DATA(lv_filename) = |/tmp/je_extraction_{ sy-datum }_{ sy-uzeit }.csv|.
OPEN DATASET lv_filename FOR OUTPUT IN TEXT MODE ENCODING UTF-8.
IF sy-subrc = 0.
" Write header
DATA(lv_header) = 'JournalEntryId,ActivityName,EventTime,SourceSystem,LastDataUpdate,User,CompanyCode,DocumentType,PostingDate,TransactionCode,IsReversed'.
TRANSFER lv_header TO lv_filename.
LOOP AT lt_final_log INTO ls_event.
DATA(lv_line) = |"{ ls_event-journalentryid }","|
|{ ls_event-activityname }","|
|{ ls_event-eventtime }","|
|{ ls_event-sourcesystem }","|
|{ ls_event-lastdataupdate }","|
|{ ls_event-username }","|
|{ ls_event-companycode }","|
|{ ls_event-documenttype }","|
|{ ls_event-postingdate }","|
|{ ls_event-transactioncode }","|
|{ ls_event-isreversed }"|.
TRANSFER lv_line TO lv_filename.
ENDLOOP.
CLOSE DATASET lv_filename.
ENDIF. 步骤
- 建立数据库连接:获取SAP ECC数据库的只读凭据。使用标准SQL客户端,例如DBeaver、SAP HANA Studio或SQL Server Management Studio,连接到数据库。
- 准备SQL查询:将本文档“query”部分提供的完整SQL查询复制到SQL客户端中。
- 设置提取参数:执行前,必须配置查询中的占位符。将“[START_DATE]”和“[END_DATE]”替换为所需日期范围,格式为“YYYYMMDD”。将“[COMPANY_CODE_1]”和“[COMPANY_CODE_2]”替换为您希望分析的具体SAP公司代码。
- 定义源系统:在主
SELECT语句中,将占位符“[Your SAP System ID]”替换为实际SAP系统ID(SID),以正确识别数据来源。 - 执行查询:针对SAP数据库运行配置好的SQL查询。执行时间取决于日期范围和数据库表的规模。
- 检查初始结果:查询完成后,快速浏览返回的行,确认数据已按预期填充。检查是否包含多种活动,以及
JournalEntryId和EventTime等关键字段是否为空。 - 处理时间戳:查询会将日期和时间字段拼接为
YYYYMMDDHHMMSS字符串。请确保后处理程序或目标系统能够解析此格式;如果数据库支持,也可以调整SQLCONCAT函数,将其转换为YYYY-MM-DDTHH:MI:SS等ISO 8601格式。 - 导出数据:通过SQL客户端将完整结果集导出为CSV文件。请使用UTF-8编码,避免特殊字符出现问题。
- 准备上传:上传到流程挖掘工具前,确认列标题符合所需数据架构。
JournalEntryId、ActivityName和EventTime是关键字段。添加LastDataUpdate列,并填入执行提取时的时间戳。 - 最终验证:执行“validationSteps”部分列出的步骤,确保提取的数据完整、准确后再开始分析。
配置
- 数据库授权:数据库用户需要对以下SAP表拥有读取权限:BKPF、BSEG、CDHDR、CDPOS、T001和V_USERNAME。对于工作流相关活动,还需要访问SWW_WI2OBJ和SWWLOGHIST。此级别的访问权限通常仅授予专业技术团队。
- 日期范围筛选:必须按具体日期范围筛选数据,以确保查询性能。提供的查询使用开始日期和结束日期占位符,并将其应用于凭证创建日期(
BKPF.CPUDT)。首次分析建议选择3至6个月的数据。 - 实体筛选:为控制数据量并聚焦分析,请始终按公司代码(
BKPF.BUKRS)筛选。您还可以按凭证类型(BKPF.BLART)筛选,仅纳入相关日记账分录类型,例如“SA”总账凭证;如果发票或付款等运营凭证不在分析范围内,也应将其排除。 - 性能注意事项:直接查询BSEG和CDPOS等核心表可能占用大量资源。强烈建议在业务低峰期执行提取,避免影响终端用户的系统性能。单次执行应避免提取超过一年的数据。
- 工作流任务ID:查询包含“[WF_TASK_ID_SUBMIT]”和“[WF_TASK_ID_APPROVE]”等占位符。必须将其替换为系统中特定日记账分录工作流配置所使用的实际任务ID。您可以咨询SAP Workflow专家,或通过事务PFTC分析技术工作流定义来确定这些ID。
a 示例查询 sql
WITH DOC_HEADERS AS (
SELECT
BUKRS,
BELNR,
GJAHR,
BLART,
BLDAT,
BUDAT,
CPUDT,
CPUTM,
USNAM,
TCODE,
BSTAT,
STBLG,
XRECH
FROM BKPF
WHERE CPUDT BETWEEN '[START_DATE]' AND '[END_DATE]'
AND BUKRS IN ('[COMPANY_CODE_1]', '[COMPANY_CODE_2]')
)
-- Event 1: Journal Entry Created (Directly Posted)
SELECT
CONCAT(H.BUKRS, H.BELNR, H.GJAHR) AS "JournalEntryId",
'Journal Entry Created' AS "ActivityName",
TO_TIMESTAMP(CONCAT(H.CPUDT, H.CPUTM), 'YYYYMMDDHH24MISS') AS "EventTime",
U.NAME_TEXT AS "User",
H.BUKRS AS "CompanyCode",
H.BLART AS "DocumentType",
H.BUDAT AS "PostingDate",
H.TCODE AS "TransactionCode",
CASE WHEN H.STBLG IS NOT NULL AND H.STBLG <> '' THEN TRUE ELSE FALSE END AS "IsReversed"
FROM DOC_HEADERS H
LEFT JOIN V_USERNAME U ON H.USNAM = U.BNAME
WHERE H.BSTAT = '' OR H.BSTAT = 'U'
UNION ALL
-- Event 2: Journal Entry Parked
SELECT
CONCAT(H.BUKRS, H.BELNR, H.GJAHR) AS "JournalEntryId",
'Journal Entry Parked' AS "ActivityName",
TO_TIMESTAMP(CONCAT(H.CPUDT, H.CPUTM), 'YYYYMMDDHH24MISS') AS "EventTime",
U.NAME_TEXT AS "User",
H.BUKRS AS "CompanyCode",
H.BLART AS "DocumentType",
H.BUDAT AS "PostingDate",
H.TCODE AS "TransactionCode",
FALSE AS "IsReversed"
FROM DOC_HEADERS H
LEFT JOIN V_USERNAME U ON H.USNAM = U.BNAME
WHERE H.BSTAT = 'V'
UNION ALL
-- Event 3: Journal Entry Posted (from Parked state)
SELECT
CONCAT(H.BUKRS, H.BELNR, H.GJAHR) AS "JournalEntryId",
'Journal Entry Posted' AS "ActivityName",
TO_TIMESTAMP(CONCAT(C.UDATE, C.UTIME), 'YYYYMMDDHH24MISS') AS "EventTime",
U.NAME_TEXT AS "User",
H.BUKRS AS "CompanyCode",
H.BLART AS "DocumentType",
H.BUDAT AS "PostingDate",
C.TCODE AS "TransactionCode",
CASE WHEN H.STBLG IS NOT NULL AND H.STBLG <> '' THEN TRUE ELSE FALSE END AS "IsReversed"
FROM DOC_HEADERS H
JOIN CDHDR C ON C.OBJECTCLAS = 'BELEG' AND C.OBJECTID = CONCAT(H.BUKRS, H.BELNR, H.GJAHR)
JOIN CDPOS P ON C.CHANGENR = P.CHANGENR AND P.OBJECTCLAS = 'BELEG' AND P.OBJECTID = C.OBJECTID
LEFT JOIN V_USERNAME U ON C.USERNAME = U.BNAME
WHERE H.BSTAT <> 'V'
AND P.TABNAME = 'BKPF'
AND P.FNAME = 'BSTAT'
AND P.VALUE_OLD = 'V'
AND P.VALUE_NEW <> 'V'
UNION ALL
-- Event 4: Parked Journal Entry Deleted
SELECT
CONCAT(H.BUKRS, H.BELNR, H.GJAHR) AS "JournalEntryId",
'Parked Journal Entry Deleted' AS "ActivityName",
TO_TIMESTAMP(CONCAT(C.UDATE, C.UTIME), 'YYYYMMDDHH24MISS') AS "EventTime",
U.NAME_TEXT AS "User",
H.BUKRS AS "CompanyCode",
H.BLART AS "DocumentType",
H.BUDAT AS "PostingDate",
C.TCODE AS "TransactionCode",
FALSE AS "IsReversed"
FROM DOC_HEADERS H
JOIN CDHDR C ON C.OBJECTCLAS = 'BELEG' AND C.OBJECTID = CONCAT(H.BUKRS, H.BELNR, H.GJAHR) AND C.TCODE = 'FBV0'
JOIN CDPOS P ON C.CHANGENR = P.CHANGENR AND P.OBJECTCLAS = 'BELEG' AND P.OBJECTID = C.OBJECTID
LEFT JOIN V_USERNAME U ON C.USERNAME = U.BNAME
WHERE P.TABNAME = 'BKPF'
AND P.FNAME = 'BSTAT'
AND P.VALUE_OLD = 'V'
AND P.VALUE_NEW = 'Z'
UNION ALL
-- Event 5: Journal Entry Reversal Processed
SELECT
CONCAT(H.BUKRS, H.STBLG, H.GJAHR) AS "JournalEntryId", -- Linking to the original document
'Journal Entry Reversal Processed' AS "ActivityName",
TO_TIMESTAMP(CONCAT(H.CPUDT, H.CPUTM), 'YYYYMMDDHH24MISS') AS "EventTime",
U.NAME_TEXT AS "User",
H.BUKRS AS "CompanyCode",
H.BLART AS "DocumentType",
H.BUDAT AS "PostingDate",
H.TCODE AS "TransactionCode",
TRUE AS "IsReversed"
FROM DOC_HEADERS H
LEFT JOIN V_USERNAME U ON H.USNAM = U.BNAME
WHERE H.STBLG IS NOT NULL AND H.STBLG <> ''
UNION ALL
-- Event 6: Journal Entry Line Item Cleared
SELECT
CONCAT(B.BUKRS, B.BELNR, B.GJAHR) AS "JournalEntryId",
'Journal Entry Line Item Cleared' AS "ActivityName",
TO_TIMESTAMP(B.AUGDT, 'YYYYMMDD') AS "EventTime", -- Clearing date used as event time
U.NAME_TEXT AS "User",
B.BUKRS AS "CompanyCode",
H.BLART AS "DocumentType",
H.BUDAT AS "PostingDate",
NULL AS "TransactionCode", -- Clearing transaction is in the clearing document header, complex to retrieve here
CASE WHEN H.STBLG IS NOT NULL AND H.STBLG <> '' THEN TRUE ELSE FALSE END AS "IsReversed"
FROM BSEG B
JOIN DOC_HEADERS H ON B.BUKRS = H.BUKRS AND B.BELNR = H.BELNR AND B.GJAHR = H.GJAHR
LEFT JOIN V_USERNAME U ON H.USNAM = U.BNAME
WHERE B.AUGBL IS NOT NULL AND B.AUGBL <> '' AND B.AUGDT <> '00000000'
UNION ALL
-- Event 7: Journal Entry Corrected (changes to a parked document)
SELECT
CONCAT(H.BUKRS, H.BELNR, H.GJAHR) AS "JournalEntryId",
'Journal Entry Corrected' AS "ActivityName",
TO_TIMESTAMP(CONCAT(C.UDATE, C.UTIME), 'YYYYMMDDHH24MISS') AS "EventTime",
U.NAME_TEXT AS "User",
H.BUKRS AS "CompanyCode",
H.BLART AS "DocumentType",
H.BUDAT AS "PostingDate",
C.TCODE AS "TransactionCode",
FALSE AS "IsReversed"
FROM DOC_HEADERS H
JOIN CDHDR C ON C.OBJECTCLAS = 'BELEG' AND C.OBJECTID = CONCAT(H.BUKRS, H.BELNR, H.GJAHR)
LEFT JOIN V_USERNAME U ON C.USERNAME = U.BNAME
WHERE H.BSTAT = 'V' AND C.TCODE IN ('FBV2', 'FBV4') -- FBV2 is change parked doc, FBV4 is change parked doc header
UNION ALL
-- Event 8: Documentation Attached (inferred from GOS attachment creation, requires configuration)
SELECT
CONCAT(H.BUKRS, H.BELNR, H.GJAHR) AS "JournalEntryId",
'Documentation Attached' AS "ActivityName",
TO_TIMESTAMP(CONCAT(REL.RECDATE, '000000'), 'YYYYMMDDHH24MISS') AS "EventTime",
U.NAME_TEXT AS "User",
H.BUKRS AS "CompanyCode",
H.BLART AS "DocumentType",
H.BUDAT AS "PostingDate",
H.TCODE AS "TransactionCode",
FALSE AS "IsReversed"
FROM DOC_HEADERS H
JOIN SRGBTBREL REL ON REL.INSTID_A = CONCAT('BUS2081', H.BUKRS, H.BELNR, H.GJAHR) -- BUS2081 is object type for BKPF
LEFT JOIN V_USERNAME U ON REL.RECUNAM = U.BNAME
WHERE REL.TYPEID_A = 'BUS2081' AND REL.RELTYPE = 'ATTA'
UNION ALL
-- Events 9-13 from Workflow (Submitted, Changes Requested, Approved, Rejected) requires specific workflow config
-- This is a generic template. The WI_RH_TASK must be adapted to your system.
SELECT
CONCAT(H.BUKRS, H.BELNR, H.GJAHR) AS "JournalEntryId",
CASE
WHEN LOG.WI_RH_TASK = '[WF_TASK_ID_SUBMIT]' THEN 'Journal Entry Submitted'
WHEN LOG.WI_RH_TASK = '[WF_TASK_ID_APPROVE]' AND LOG.METHOD = 'DECISION' AND LOG.EVT_ID = 'COMPLETED' THEN 'Journal Entry Approved'
WHEN LOG.WI_RH_TASK = '[WF_TASK_ID_REJECT]' AND LOG.METHOD = 'DECISION' AND LOG.EVT_ID = 'COMPLETED' THEN 'Journal Entry Rejected'
WHEN LOG.WI_RH_TASK = '[WF_TASK_ID_CHANGES_REQ]' AND LOG.METHOD = 'DECISION' AND LOG.EVT_ID = 'COMPLETED' THEN 'Journal Entry Changes Requested'
ELSE NULL
END AS "ActivityName",
TO_TIMESTAMP(CONCAT(LOG.EVT_DATE, LOG.EVT_TIME), 'YYYYMMDDHH24MISS') AS "EventTime",
U.NAME_TEXT AS "User",
H.BUKRS AS "CompanyCode",
H.BLART AS "DocumentType",
H.BUDAT AS "PostingDate",
NULL AS "TransactionCode",
FALSE AS "IsReversed"
FROM DOC_HEADERS H
JOIN SWW_WI2OBJ WIOBJ ON WIOBJ.INSTID = CONCAT(H.BUKRS, H.BELNR, H.GJAHR) AND WIOBJ.TYPEID = 'BKPF'
JOIN SWWLOGHIST LOG ON WIOBJ.WI_ID = LOG.WI_ID
LEFT JOIN V_USERNAME U ON LOG.EXEC_USER = U.BNAME
WHERE LOG.WI_RH_TASK IN ('[WF_TASK_ID_SUBMIT]', '[WF_TASK_ID_APPROVE]', '[WF_TASK_ID_REJECT]', '[WF_TASK_ID_CHANGES_REQ]')
UNION ALL
-- Event 14: Manual Entry Identified
SELECT
CONCAT(H.BUKRS, H.BELNR, H.GJAHR) AS "JournalEntryId",
'Manual Entry Identified' AS "ActivityName",
TO_TIMESTAMP(CONCAT(H.CPUDT, H.CPUTM), 'YYYYMMDDHH24MISS') AS "EventTime",
U.NAME_TEXT AS "User",
H.BUKRS AS "CompanyCode",
H.BLART AS "DocumentType",
H.BUDAT AS "PostingDate",
H.TCODE AS "TransactionCode",
CASE WHEN H.STBLG IS NOT NULL AND H.STBLG <> '' THEN TRUE ELSE FALSE END AS "IsReversed"
FROM DOC_HEADERS H
LEFT JOIN V_USERNAME U ON H.USNAM = U.BNAME
WHERE H.TCODE IN ('FB01', 'F-02', 'FB50', 'F-04', 'F-22', 'F-43', 'FB60', 'FB70', 'FV50', 'FV60', 'FV70')
UNION ALL
-- Event 15: Cross-Company Posting Identified
SELECT
CONCAT(H.BUKRS, H.BELNR, H.GJAHR) AS "JournalEntryId",
'Cross-Company Posting Identified' AS "ActivityName",
TO_TIMESTAMP(CONCAT(H.CPUDT, H.CPUTM), 'YYYYMMDDHH24MISS') AS "EventTime",
U.NAME_TEXT AS "User",
H.BUKRS AS "CompanyCode",
H.BLART AS "DocumentType",
H.BUDAT AS "PostingDate",
H.TCODE AS "TransactionCode",
CASE WHEN H.STBLG IS NOT NULL AND H.STBLG <> '' THEN TRUE ELSE FALSE END AS "IsReversed"
FROM DOC_HEADERS H
LEFT JOIN V_USERNAME U ON H.USNAM = U.BNAME
WHERE H.XRECH = 'X' 步骤
- 建立SAP连接:在您的第三方ETL工具中,为SAP ECC系统配置新的源连接。通常需要填写应用服务器详细信息、客户端、系统编号,以及拥有必要RFC授权的专用SAP用户。
- 定义数据源:在提取项目中,将所需的SAP表添加为数据源。主要包括BKPF(会计凭证抬头)、BSEG(会计凭证明细)、VBSEGK(暂存凭证抬头)、CDHDR(更改凭证抬头)、CDPOS(更改凭证明细)、SWW_WI2OBJ(工作流与对象链接)、SWWLOGHIST(工作流日志)和SRGBTBREL(GOS附件关系)。
- 提取基础事件(已创建和已暂存):创建第一个数据流,用于提取初始事件。对于“Journal Entry Parked”,使用VBSEGK作为数据源。对于“Journal Entry Created”,使用BKPF,并筛选出非冲销且未初始暂存的凭证。可通过与VBSEGK执行反连接来完成筛选。
- 提取工作流事件:使用对象键(公司代码+凭证编号+会计年度)将BKPF与SWW_WI2OBJ连接,以获取工作流实例ID。再将结果与SWWLOGHIST连接,根据日志中记录的工作流任务结果和用户决策,提取“Submitted”“Approved”“Rejected”和“Changes Requested”等事件。
- 提取更改和删除事件:使用CDHDR和CDPOS识别更改。对于“Journal Entry Corrected”,筛选针对暂存凭证的更改(对象类为“FIPP”)。对于“Parked Journal Entry Deleted”,在暂存凭证的更改日志中查找删除标记。
- 提取附件事件:要捕获“Documentation Attached”,请在对象类型为“BKPF”且关系为“[Your attachment relationship type]”的条件下,将BKPF与SRGBTBREL连接。链接的创建日期作为事件时间。
- 提取清账和冲销事件:对于“Journal Entry Line Item Cleared”,查询AUGBL字段已填充的BSEG记录。事件时间为清账凭证的过账日期(AUGDT)。对于“Journal Entry Reversal Processed”,查询BKPF中STBLG字段有值的冲销凭证。
- 派生计算事件:为计算事件创建独立的逻辑模块。对于“Manual Entry Identified”,根据手工交易代码列表(例如FB01、FB50、F-02)筛选BKPF。对于“Cross-Company Posting Identified”,按凭证ID对BSEG分组,并识别包含多个不同公司代码的凭证。
- 合并所有事件流:在ETL工具中使用UNION转换,将各个事件流(Created、Parked、Approved等)的输出合并到单一事件日志表中。确保所有事件流的列名和数据类型一致。
- 映射到最终架构:将合并后的数据映射到所需的事件日志结构,创建
JournalEntryId、ActivityName、EventTime、User以及其他必需和建议的属性。添加SourceSystem等静态列,并使用ETL作业的执行时间填充LastDataUpdate。 - 配置增量加载:对于持续提取,配置增量加载策略。使用最近的创建日期或更改日期(例如BKPF.CPUDT、CDHDR.UDATE)作为水位标记,仅提取上次运行后新增或更新的记录。
- 导出到ProcessMind:安排提取作业,并将最终输出步骤配置为CSV或Parquet文件,将事件日志保存到ProcessMind可访问的位置,以便上传。
配置
- 前提条件:拥有许可证的第三方ETL工具(例如Theobald Xtract Universal、Informatica、Talend),并配备专用SAP连接器。还需要具备RFC访问权限,并拥有读取财务表(例如表组F_00、F_WF对应的S_TABU_DIS权限)、工作流数据和更改日志的SAP用户账号。
- 连接参数:您需要SAP应用服务器IP地址或主机名、系统编号和客户端ID。SAP用户名和密码应使用安全的凭据管理方式。
- 关键筛选条件:始终在源端按公司代码(BKPF.BUKRS)和会计年度(BKPF.GJAHR)筛选,以限制数据量。强烈建议按凭证创建日期(BKPF.CPUDT)筛选,以定义明确的提取期间,例如最近6个月。
- 日期范围选择:初始加载请选择具有代表性的3至6个月期间。后续增量加载可使用
CPUDT等时间戳字段设置水位标记,仅获取新增记录。 - 性能注意事项:BSEG、CDPOS和工作流表上的连接可能非常缓慢。请确保ETL工具尽可能将筛选条件下推到SAP源端。如果工具支持,尤其是在加载大量历史数据时,请分批或分包提取数据。
- 工作流定制:识别“Approved”或“Rejected”等工作流活动的逻辑高度依赖具体的工作流模板。您需要从系统中确认正确的工作流任务ID和用户决策键,并将其用于筛选。
a 示例查询 sql
/*
This is a logical representation of the extraction configuration in a third-party ETL tool.
It is not executable SQL but defines the sources, joins, and transformations for each activity.
Placeholders like [Your SAP Source], [Date Filter], and [Company Code Filter] must be configured in the tool.
*/
-- Extraction block for 'Journal Entry Parked'
SELECT
CONCAT(v.BUKRS, v.VBELN, v.GJAHR) AS JournalEntryId,
'Journal Entry Parked' AS ActivityName,
CAST(CONCAT(v.CPUDT, v.CPUTM) AS TIMESTAMP) AS EventTime,
'SAP ECC' AS SourceSystem,
NOW() AS LastDataUpdate,
v.USNAM AS User,
v.BUKRS AS CompanyCode,
v.BLART AS DocumentType,
v.BUDAT AS PostingDate,
v.TCODE AS TransactionCode,
FALSE AS IsReversed
FROM [Your SAP Source].VBSEGK v
WHERE [Date Filter on v.CPUDT] AND [Company Code Filter on v.BUKRS]
UNION ALL
-- Extraction block for 'Journal Entry Created'
SELECT
CONCAT(h.BUKRS, h.BELNR, h.GJAHR) AS JournalEntryId,
'Journal Entry Created' AS ActivityName,
CAST(CONCAT(h.CPUDT, h.CPUTM) AS TIMESTAMP) AS EventTime,
'SAP ECC' AS SourceSystem,
NOW() AS LastDataUpdate,
h.USNAM AS User,
h.BUKRS AS CompanyCode,
h.BLART AS DocumentType,
h.BUDAT AS PostingDate,
h.TCODE AS TransactionCode,
FALSE AS IsReversed
FROM [Your SAP Source].BKPF h
LEFT JOIN [Your SAP Source].VBSEGK v ON h.AWKEY = CONCAT(v.BUKRS, v.VBELN, v.GJAHR)
WHERE h.BSTAT = '' AND v.VBELN IS NULL AND h.STBLG IS NULL
AND [Date Filter on h.CPUDT] AND [Company Code Filter on h.BUKRS]
UNION ALL
-- Extraction block for 'Journal Entry Posted' (from parked)
SELECT
CONCAT(h.BUKRS, h.BELNR, h.GJAHR) AS JournalEntryId,
'Journal Entry Posted' AS ActivityName,
CAST(CONCAT(h.CPUDT, h.CPUTM) AS TIMESTAMP) AS EventTime, -- Or a more precise posting time from change logs if available
'SAP ECC' AS SourceSystem,
NOW() AS LastDataUpdate,
h.USNAM AS User,
h.BUKRS AS CompanyCode,
h.BLART AS DocumentType,
h.BUDAT AS PostingDate,
h.TCODE AS TransactionCode,
FALSE AS IsReversed
FROM [Your SAP Source].BKPF h
JOIN [Your SAP Source].VBSEGK v ON h.AWKEY = CONCAT(v.BUKRS, v.VBELN, v.GJAHR)
WHERE [Date Filter on h.CPUDT] AND [Company Code Filter on h.BUKRS]
UNION ALL
-- Extraction block for 'Journal Entry Submitted', 'Approved', 'Rejected', 'Changes Requested'
SELECT
CONCAT(SUBSTRING(o.INSTID, 3, 4), SUBSTRING(o.INSTID, 7, 10), SUBSTRING(o.INSTID, 17, 4)) AS JournalEntryId,
CASE
WHEN wl.WI_TEXT LIKE '%Submit%' THEN 'Journal Entry Submitted'
WHEN wl.WI_TEXT LIKE '%Approve%' THEN 'Journal Entry Approved'
WHEN wl.WI_TEXT LIKE '%Reject%' THEN 'Journal Entry Rejected'
WHEN wl.WI_TEXT LIKE '%Request Changes%' THEN 'Journal Entry Changes Requested'
END AS ActivityName,
CAST(CONCAT(wl.WI_CD, wl.WI_CT) AS TIMESTAMP) AS EventTime,
'SAP ECC' AS SourceSystem,
NOW() AS LastDataUpdate,
wl.EXEC_USER AS User,
SUBSTRING(o.INSTID, 3, 4) AS CompanyCode,
NULL AS DocumentType,
NULL AS PostingDate,
NULL AS TransactionCode,
FALSE AS IsReversed
FROM [Your SAP Source].SWW_WI2OBJ o
JOIN [Your SAP Source].SWWLOGHIST wl ON o.WI_ID = wl.WI_ID
WHERE o.TYPEID = 'BKPF' AND o.CATID = 'BO'
AND wl.WI_TEXT IN ('[Your Submit Task Name]', '[Your Approve Task Name]', '[Your Reject Task Name]', '[Your Changes Request Task Name]')
AND [Date Filter on wl.WI_CD]
UNION ALL
-- Extraction block for 'Journal Entry Corrected'
SELECT
CONCAT(cd.OBJECTID_LONG_CHAR(3,4), cd.OBJECTID_LONG_CHAR(7,10), cd.OBJECTID_LONG_CHAR(17,4)) AS JournalEntryId,
'Journal Entry Corrected' AS ActivityName,
CAST(CONCAT(cd.UDATE, cd.UTIME) AS TIMESTAMP) AS EventTime,
'SAP ECC' AS SourceSystem,
NOW() AS LastDataUpdate,
cd.USERNAME AS User,
cd.OBJECTID_LONG_CHAR(3,4) AS CompanyCode,
NULL AS DocumentType,
NULL AS PostingDate,
cd.TCODE AS TransactionCode,
FALSE AS IsReversed
FROM [Your SAP Source].CDHDR cd
WHERE cd.OBJECTCLAS = 'FIPP' AND cd.CHANGE_IND = 'U'
AND [Date Filter on cd.UDATE]
UNION ALL
-- Extraction block for 'Parked Journal Entry Deleted'
SELECT
CONCAT(cd.OBJECTID_LONG_CHAR(3,4), cd.OBJECTID_LONG_CHAR(7,10), cd.OBJECTID_LONG_CHAR(17,4)) AS JournalEntryId,
'Parked Journal Entry Deleted' AS ActivityName,
CAST(CONCAT(cd.UDATE, cd.UTIME) AS TIMESTAMP) AS EventTime,
'SAP ECC' AS SourceSystem,
NOW() AS LastDataUpdate,
cd.USERNAME AS User,
cd.OBJECTID_LONG_CHAR(3,4) AS CompanyCode,
NULL AS DocumentType,
NULL AS PostingDate,
cd.TCODE AS TransactionCode,
FALSE AS IsReversed
FROM [Your SAP Source].CDHDR cd
WHERE cd.OBJECTCLAS = 'FIPP' AND cd.CHANGE_IND = 'D'
AND [Date Filter on cd.UDATE]
UNION ALL
-- Extraction block for 'Documentation Attached'
SELECT
CONCAT(SUBSTRING(r.INSTID_A, 3, 4), SUBSTRING(r.INSTID_A, 7, 10), SUBSTRING(r.INSTID_A, 17, 4)) AS JournalEntryId,
'Documentation Attached' AS ActivityName,
-- Note: A precise timestamp is often unavailable. Using document creation time as a proxy.
CAST(CONCAT(h.CPUDT, h.CPUTM) AS TIMESTAMP) AS EventTime,
'SAP ECC' AS SourceSystem,
NOW() AS LastDataUpdate,
h.USNAM AS User,
h.BUKRS AS CompanyCode,
h.BLART AS DocumentType,
h.BUDAT AS PostingDate,
h.TCODE AS TransactionCode,
FALSE AS IsReversed
FROM [Your SAP Source].SRGBTBREL r
JOIN [Your SAP Source].BKPF h ON h.BUKRS = SUBSTRING(r.INSTID_A, 3, 4) AND h.BELNR = SUBSTRING(r.INSTID_A, 7, 10) AND h.GJAHR = SUBSTRING(r.INSTID_A, 17, 4)
WHERE r.TYPEID_A = 'BKPF' AND r.RELTYPE = '[Configure based on your system]'
AND [Date Filter on h.CPUDT] AND [Company Code Filter on h.BUKRS]
UNION ALL
-- Extraction block for 'Journal Entry Reversal Processed'
SELECT
CONCAT(h.BUKRS, h.BELNR, h.GJAHR) AS JournalEntryId,
'Journal Entry Reversal Processed' AS ActivityName,
CAST(CONCAT(h.CPUDT, h.CPUTM) AS TIMESTAMP) AS EventTime,
'SAP ECC' AS SourceSystem,
NOW() AS LastDataUpdate,
h.USNAM AS User,
h.BUKRS AS CompanyCode,
h.BLART AS DocumentType,
h.BUDAT AS PostingDate,
h.TCODE AS TransactionCode,
TRUE AS IsReversed
FROM [Your SAP Source].BKPF h
WHERE h.STBLG IS NOT NULL AND h.STBLG <> ''
AND [Date Filter on h.CPUDT] AND [Company Code Filter on h.BUKRS]
UNION ALL
-- Extraction block for 'Is Reversed' flag on original document
SELECT
CONCAT(h_orig.BUKRS, h_orig.BELNR, h_orig.GJAHR) AS JournalEntryId,
'Is Reversed' AS ActivityName, -- This is an attribute update, modeled as an event
CAST(CONCAT(h_rev.CPUDT, h_rev.CPUTM) AS TIMESTAMP) AS EventTime,
'SAP ECC' AS SourceSystem,
NOW() AS LastDataUpdate,
h_rev.USNAM AS User,
h_orig.BUKRS AS CompanyCode,
h_orig.BLART AS DocumentType,
h_orig.BUDAT AS PostingDate,
h_orig.TCODE AS TransactionCode,
TRUE AS IsReversed
FROM [Your SAP Source].BKPF h_rev
JOIN [Your SAP Source].BKPF h_orig ON h_rev.STBLG = h_orig.BELNR AND h_rev.BUKRS = h_orig.BUKRS AND h_rev.GJAHR_S = h_orig.GJAHR
WHERE h_rev.STBLG IS NOT NULL AND h_rev.STBLG <> ''
AND [Date Filter on h_rev.CPUDT] AND [Company Code Filter on h_rev.BUKRS]
UNION ALL
-- Extraction block for 'Journal Entry Line Item Cleared'
SELECT
CONCAT(i.BUKRS, i.BELNR, i.GJAHR) AS JournalEntryId,
'Journal Entry Line Item Cleared' AS ActivityName,
CAST(i.AUGDT AS TIMESTAMP) AS EventTime,
'SAP ECC' AS SourceSystem,
NOW() AS LastDataUpdate,
NULL AS User, -- User who performed clearing is on the clearing document header
i.BUKRS AS CompanyCode,
NULL AS DocumentType,
NULL AS PostingDate,
NULL AS TransactionCode,
FALSE AS IsReversed
FROM [Your SAP Source].BSEG i
WHERE i.AUGBL IS NOT NULL AND i.AUGBL <> ''
AND [Date Filter on i.AUGDT] AND [Company Code Filter on i.BUKRS]
UNION ALL
-- Extraction block for 'Manual Entry Identified'
SELECT
CONCAT(h.BUKRS, h.BELNR, h.GJAHR) AS JournalEntryId,
'Manual Entry Identified' AS ActivityName,
CAST(CONCAT(h.CPUDT, h.CPUTM) AS TIMESTAMP) AS EventTime, -- Same time as creation
'SAP ECC' AS SourceSystem,
NOW() AS LastDataUpdate,
h.USNAM AS User,
h.BUKRS AS CompanyCode,
h.BLART AS DocumentType,
h.BUDAT AS PostingDate,
h.TCODE AS TransactionCode,
FALSE AS IsReversed
FROM [Your SAP Source].BKPF h
WHERE h.TCODE IN ('FB01', 'F-02', 'FB50', 'FV50', '[Add other manual T-Codes]')
AND [Date Filter on h.CPUDT] AND [Company Code Filter on h.BUKRS]
UNION ALL
-- Extraction block for 'Cross-Company Posting Identified'
SELECT
JournalEntryId,
'Cross-Company Posting Identified' AS ActivityName,
EventTime, -- Same time as creation
'SAP ECC' AS SourceSystem,
NOW() AS LastDataUpdate,
User,
CompanyCode,
DocumentType,
PostingDate,
TransactionCode,
IsReversed
FROM (
SELECT
CONCAT(h.BUKRS, h.BELNR, h.GJAHR) AS JournalEntryId,
CAST(CONCAT(h.CPUDT, h.CPUTM) AS TIMESTAMP) AS EventTime,
h.USNAM AS User,
h.BUKRS AS CompanyCode,
h.BLART AS DocumentType,
h.BUDAT AS PostingDate,
h.TCODE AS TransactionCode,
FALSE AS IsReversed,
(SELECT COUNT(DISTINCT i.BUKRS) FROM [Your SAP Source].BSEG i WHERE i.BELNR = h.BELNR AND i.BUKRS = h.BUKRS AND i.GJAHR = h.GJAHR) as CompanyCodeCount
FROM [Your SAP Source].BKPF h
WHERE [Date Filter on h.CPUDT] AND [Company Code Filter on h.BUKRS]
) AS CrossCompanyCheck
WHERE CompanyCodeCount > 1 立即优化您的Record to Report-Journal Entry流程
将日记账分录周期时间缩短30%,确保报告准确无误。
无需信用卡,立即开始改进。