SAP创建会计凭证BAPI:BAPI_ACC_DOCUMENT_CHECK

在 SAP FICO 模块开发中,会计凭证的自动化处理是高频需求 —— 比如资产折旧分摊、费用批量过账等场景,通过 ABAP 调用标准 BAPI 能高效规避手动操作的误差,同时确保数据符合财务规范。

一、报表核心用途与场景

这份报表(ZFB01_H)的核心目标是:基于资产维度进行费用分摊,并自动创建会计凭证;同时支持已创建凭证的冲销操作。典型应用场景包括:

  • 月度资产折旧费用按成本中心分摊;
  • 批量调整历史凭证(创建后需冲销修正);
  • 标准化财务过账流程(避免手动录入科目、金额的错误)。

报表核心依赖两个 SAP 标准 BAPI:

  1. BAPI_ACC_DOCUMENT_CHECK:凭证预检查(避免直接过账导致的错误);
  2. BAPI_ACC_DOCUMENT_POST:正式创建会计凭证

二、核心代码逻辑拆解

1. 基础变量定义:BAPI 参数映射

首先需定义 BAPI 所需的标准结构变量,这些结构直接对应会计凭证的 “抬头 - 行项目 - 扩展字段 - 返回消息” 层级:

*&---BAPI变量定义
DATA:
  " 凭证抬头(公司代码、凭证类型、过账日期等核心信息)
  ls_documentheader TYPE bapiache09, 
  " 总账行项目(科目、成本中心、资产号等行级信息)
  lt_accountgl      TYPE STANDARD TABLE OF bapiacgl09, 
  ls_accountgl      TYPE bapiacgl09,
  " 金额信息(行项目金额、货币类型)
  lt_currencyamount TYPE STANDARD TABLE OF bapiaccr09, 
  ls_currencyamount TYPE bapiaccr09,
  " 扩展字段(存储BAPI标准结构未涵盖的自定义字段,如记账代码)
  lt_extension2     TYPE STANDARD TABLE OF bapiparex,   
  ls_extension2     TYPE bapiparex,
  ls_exten          TYPE zsfi_acc_doc_post_ext,  " 自定义扩展结构
  " 返回消息(错误/成功提示)
  lt_return         TYPE STANDARD TABLE OF bapiret2,   
  ls_return         TYPE bapiret2,
  " 凭证号(BAPI返回的唯一标识)
  lv_key            TYPE bapiache09-obj_key,           
  lv_msg            TYPE bapi_msg.  " 错误消息拼接变量

关键说明:自定义扩展结构ZSFI_ACC_DOC_POST_EXT通常用于存储 BAPI 标准结构未包含的字段(如本例中的 “记账代码 BSCHL”),需确保该结构在 SE11 中已激活,且字段类型与 BAPI 要求一致。

具体内容可以参考:https://share.note.youdao.com/s/11ChRW0I

2. 步骤 1:会计凭证创建(核心流程)

凭证创建遵循 “参数配置 → 预检查 → 正式过账 → 事务提交” 的流程,确保数据合规性。

2.1 配置凭证抬头信息

抬头信息是整个凭证的核心标识,需明确公司代码、凭证类型、过账日期等关键参数:

*&---抬头数据配置
ls_documentheader-comp_code  = '1000'.    " 公司代码(示例:1000)
ls_documentheader-doc_type   = 'SA'.       " 凭证类型(SA=总账凭证,FICO常用)
ls_documentheader-username   = sy-uname.   " 过账用户(当前登录用户)
ls_documentheader-doc_date   = sy-datum.   " 凭证日期(当前日期)
ls_documentheader-pstng_date = sy-datum.   " 过账日期(当前日期,需在会计期间内)
ls_documentheader-header_txt = '折旧费用分摊'.  " 凭证抬头文本(便于财务追溯)

注意:凭证类型(DOC_TYPE)需在 TCODE OB41中配置,不同类型对应不同的过账规则(如 SA 用于总账、KV 用于供应商发票)。

2.2 配置凭证行项目(借方 + 贷方)

会计凭证需满足 “借贷平衡”,本例中借方和贷方均使用同一总账科目(9990010500),金额分别为10-10(负数表示贷方)。

2.2.1 借方行项目(记账代码 40)
*&---借方行项目(行号10)
lv_posnr = lv_posnr + 10.  " 行项目编号(通常按10递增,避免重复)
CLEAR:ls_accountgl.
ls_accountgl-itemno_acc  = lv_posnr.       " 行项目编号(与金额表对应)
ls_accountgl-gl_account  = '9990010500'.   " 总账科目(示例科目)
ls_accountgl-comp_code   = '1000'.         " 公司代码(与抬头一致)
ls_accountgl-item_text  = '测试1213234'.   " 行项目文本(便于明细查询)
APPEND ls_accountgl TO lt_accountgl.

*&---借方金额配置
CLEAR:ls_currencyamount.
ls_currencyamount-itemno_acc = lv_posnr.   " 行号与总账行项目一致
ls_currencyamount-amt_doccur = 10.         " 金额(正数=借方)
ls_currencyamount-currency = 'CNY'.        " 货币类型(CNY=人民币)
APPEND ls_currencyamount TO lt_currencyamount.

*&---扩展字段:记账代码(40=借方)
CLEAR:ls_extension2.
ls_exten-posnr    = lv_posnr.              " 行号关联
ls_exten-bschl   = '40'.                   " 记账代码(40=借方,50=贷方)
ls_extension2-structure  = 'ZSFI_ACC_DOC_POST_EXT'.  " 自定义扩展结构名
ls_extension2-valuepart1 = ls_exten.       " 扩展字段值(结构赋值)
APPEND ls_extension2 TO lt_extension2.
2.2.2 贷方行项目(记账代码 50)
*&---贷方行项目(行号20)
lv_posnr = lv_posnr + 10.  " 行号递增(20)
CLEAR:ls_accountgl.
ls_accountgl-itemno_acc  = lv_posnr.
ls_accountgl-gl_account  = '9990010500'.   " 与借方科目一致,确保借贷平衡
ls_accountgl-comp_code   = '1000'.
APPEND ls_accountgl TO lt_accountgl.

*&---贷方金额配置(负数表示贷方)
CLEAR:ls_currencyamount.
ls_currencyamount-itemno_acc = lv_posnr.
ls_currencyamount-amt_doccur = -10.        " 负数=贷方
ls_currencyamount-currency   = 'CNY'.
APPEND ls_currencyamount TO lt_currencyamount.

*&---扩展字段:记账代码(50=贷方)
CLEAR:ls_extension2.
ls_exten-posnr    = lv_posnr.
ls_exten-bschl   = '50'.                   " 记账代码50=贷方
ls_extension2-structure  = 'ZSFI_ACC_DOC_POST_EXT'.
ls_extension2-valuepart1 = ls_exten.
APPEND ls_extension2 TO lt_extension2.

关键规则:

 
  1. 行项目编号(ITEMNO_ACC)需唯一,且与金额表(CURRENCYAMOUNT)、扩展字段表(EXTENSION2)的行号严格对应;
  2. 金额需满足 “借方总和 = 贷方总和”,否则 BAPI 检查会报错;
  3. 记账代码(BSCHL)是财务过账的核心规则,需在 TCODE OB41中配置(如 40 对应总账借方、50 对应总账贷方)。
2.3 凭证预检查(避免直接过账错误)

调用BAPI_ACC_DOCUMENT_CHECK进行预检查,提前拦截错误(如科目不存在、金额不平衡、过账日期不在会计期间内等):

*&---凭证预检查
CLEAR:lt_return.
CALL FUNCTION 'BAPI_ACC_DOCUMENT_CHECK'
  EXPORTING
    documentheader = ls_documentheader     " 凭证抬头
  TABLES
    accountgl      = lt_accountgl          " 总账行项目
    currencyamount = lt_currencyamount     " 金额信息
    extension2     = lt_extension2         " 扩展字段
    return         = lt_return.            " 返回消息

*&---解析错误消息
CLEAR:lv_msg.
LOOP AT lt_return INTO ls_return WHERE type CA 'AEX'.  " A=终止,E=错误,X=出口错误
  CLEAR: lv_msg2.
  " 拼接完整错误消息(含变量替换)
  CALL FUNCTION 'MESSAGE_TEXT_BUILD'
    EXPORTING
      msgid               = ls_return-id
      msgnr               = ls_return-number
      msgv1               = ls_return-message_v1
      msgv2               = ls_return-message_v2
      msgv3               = ls_return-message_v3
      msgv4               = ls_return-message_v4
    IMPORTING
      message_text_output = lv_msg2.
  lv_msg = |{ lv_msg }/{ lv_msg2 }|.  " 合并多条错误消息
ENDLOOP.

*&---检查失败:回滚事务
IF lv_msg IS NOT INITIAL .
  CALL FUNCTION 'BAPI_TRANSACTION_ROLLBACK'.  " 事务回滚
  MESSAGE lv_msg TYPE 'E'.  " 提示错误
  EXIT.  " 终止后续流程
ENDIF.

为什么需要预检查?
直接调用BAPI_ACC_DOCUMENT_POST虽然也会返回错误,但预检查能在 “不生成凭证” 的前提下拦截问题,避免后续冲销的麻烦,尤其适合批量过账场景。

2.4 正式过账与事务提交

预检查通过后,调用BAPI_ACC_DOCUMENT_POST创建凭证,并通过BAPI_TRANSACTION_COMMIT提交事务(确保凭证持久化):

*&---正式创建凭证
CLEAR:lv_key.
CALL FUNCTION 'BAPI_ACC_DOCUMENT_POST'
  EXPORTING
    documentheader = ls_documentheader
  IMPORTING
    obj_key        = lv_key  " 返回的凭证号(格式:公司代码+会计年度+凭证编号)
  TABLES
    accountgl      = lt_accountgl
    currencyamount = lt_currencyamount
    extension2     = lt_extension2
    return         = lt_return.

*&---提交事务(wait='X'确保即时生效)
CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'
  EXPORTING
    wait = 'X'.  " 等待提交完成,避免后续操作读取不到凭证

MESSAGE |凭证创建成功!凭证号:{ lv_key }| TYPE 'S'.

凭证号(OBJ_KEY)解读:
返回的lv_key通常格式为 “公司代码 + 会计年度 + 凭证编号”(如000000001010002025),可通过BAPI_ACC_DOCUMENT_GETDETAIL查询凭证明细。

如只需凭证号:LV_BELNR = LV_KEY+0(10).

三、关键技术点与优化建议

通过这份代码,我们可以提炼出 SAP BAPI 开发的几个核心最佳实践:

1. 事务一致性:COMMIT 与 ROLLBACK 的正确使用

  • COMMIT:调用BAPI_TRANSACTION_COMMIT时必须加wait='X',确保 BAPI 操作即时生效,避免后续查询(如冲销时读 BKPF)不到数据;
  • ROLLBACK:只要 BAPI 返回错误(TYPE 包含 A/E/X),必须调用BAPI_TRANSACTION_ROLLBACK,避免事务锁定或脏数据。

2. 扩展字段处理:EXTENSION2 的正确配置

自定义字段(如本例的记账代码 BSCHL)需通过EXTENSION2传递,需满足两个条件:

  1. 自定义结构(如ZSFI_ACC_DOC_POST_EXT)的名称必须在 BAPI 的 “扩展结构列表” 中注册(可通过 TCODE BAPI_EXTENSION维护);
  2. EXTENSION2STRUCTURE字段需填写自定义结构名,VALUEPART1字段需直接赋值结构变量(而非单个字段)。

3. 错误消息处理:MESSAGE_TEXT_BUILD 的应用

BAPI 返回的消息(BAPIRET2)通常包含ID(消息类)、NUMBER(消息号)和MESSAGE_V1-V4(变量),直接输出MESSAGE字段可能缺失变量信息,需通过MESSAGE_TEXT_BUILD函数拼接完整消息,便于问题定位。

4. 批量处理优化(代码扩展建议)

原代码中注释了 “按资产号分组分摊” 的逻辑(GROUP BY),实际批量处理时需注意:

  • 按资产号 / 公司代码 / 会计期间分组,避免同一资产重复过账;
  • 批量过账时建议加COMMIT WORK AND WAIT(而非循环内调用 BAPI_COMMIT),减少数据库交互次数,提升性能;
  • 增加日志表(如ZTFIV034)记录每笔凭证的创建 / 冲销状态,便于追溯问题。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值