清账¶
清账的自开发主要使用以下标准函数(实际也是BDC)来处理:
-
POSTING_INTERFACE_START
-
POSTING_INTERFACE_CLEARING
-
POSTING_INTERFACE_END
分析说明¶
清账主要是两个问题:
-
是否存在差异金额
-
差异金额的处理
如果不存在差异金额,则只需要处理未清项即可。如果存在差异金额,则需要根据业务顾问的操作流程,调整差异金额处理与未清项处理的先后顺序。
正常来说,财务顾问都是先挑选未清项,再由系统自动生成差异行。上面的标准BDC函数却是反过来,这会导致一些问题,比如清账字段AUGBL为空,无法关联未清项。
系统自动生成的差异行,还会按配置自动带出额外信息,如果业务顾问对这些字段有要求,也会让人头秃。
改得吐血,改出下面的工具类,SAP不提供标准函数来处理真的是太难了。
清账工具¶
该工具将调用标准函数的一些通用操作封装起来,使用的时候,只需传入未清项即可。
ZCL_FI_CLEARING
class ZCL_FI_CLEARING definition
public
create private .
public section.
types:
ty_amount type p length 16 decimals 2.
TYPES:
ty_exchange_rate TYPE p LENGTH 16 DECIMALS 6.
types:
BEGIN OF ty_clearing_line,
bukrs TYPE bseg-bukrs,
belnr TYPE bseg-belnr,
gjahr TYPE bseg-gjahr,
buzei TYPE bseg-buzei,
END OF ty_clearing_line .
types:
tt_clearing_line TYPE STANDARD TABLE OF ty_clearing_line WITH EMPTY KEY .
types:
BEGIN OF ty_result,
mtype TYPE bapi_mtype,
msg TYPE bapi_msg,
belnr TYPE bseg-belnr,
END OF ty_result .
class-methods CLASS_CONSTRUCTOR .
class-methods POST_CUSTOMER
importing
!I_BUKRS type BUKRS
!I_KUNNR type KUNNR
!I_AGUMS type AGUMS default 'A'
!I_BUDAT type BUDAT default SY-DATUM
!I_BKTXT type BKTXT optional
!IT_CLEARING_LINE type TT_CLEARING_LINE
!I_PROC type CHAR01 default 'M'
!IT_FTPOST type FAGL_T_FTPOST optional
returning
value(RESULT) type TY_RESULT .
class-methods POST_VENDOR
importing
!I_BUKRS type BUKRS
!I_LIFNR type LIFNR optional
!I_AGUMS type AGUMS default 'A'
!I_BUDAT type BUDAT default SY-DATUM
!I_BKTXT type BKTXT optional
!IT_CLEARING_LINE type TT_CLEARING_LINE
!I_PROC type CHAR01 default 'M'
!IT_FTPOST type FAGL_T_FTPOST optional
returning
value(RESULT) type TY_RESULT .
class-methods POST_LEDGER
importing
!I_BUKRS type BUKRS
!I_HKONT type HKONT
!I_AGUMS type AGUMS optional
!I_BUDAT type BUDAT default SY-DATUM
!I_BKTXT type BKTXT optional
!IT_CLEARING_LINE type TT_CLEARING_LINE
!I_PROC type CHAR01 default 'M'
!IT_FTPOST type FAGL_T_FTPOST optional
returning
value(RESULT) type TY_RESULT .
class-methods BEFORE_FCODE_F11
changing
!FT type BDCDATA_TAB .
class-methods AFTER_XFTPOST_LOOP
changing
!FT type BDCDATA_TAB .
protected section.
PRIVATE SECTION.
CLASS-DATA ms_t041a TYPE t041a .
CLASS-DATA:
mt_tbsl TYPE STANDARD TABLE OF tbsl .
CLASS-DATA:
BEGIN OF ms_extra,
diff_amount_proc TYPE CHAR01,
program TYPE bdcdata-program,
dynpro TYPE bdcdata-dynpro,
profit_loss TYPE xfeld, " 损益标识
FTPOST TYPE STANDARD TABLE OF ftpost WITH EMPTY KEY,
END OF ms_extra.
data m_WAERS type WAERS .
data m_WAERS_LOC type WAERS .
data m_WAERS_GRP type WAERS .
DATA m_bukrs TYPE bukrs .
DATA m_koart TYPE koart .
DATA m_agums TYPE agums .
DATA m_hkont TYPE hkont .
DATA m_budat TYPE budat .
DATA m_bktxt TYPE bktxt .
DATA:
mt_bseg TYPE STANDARD TABLE OF bseg .
DATA ms_result TYPE ty_result .
CLASS-METHODS determine_posting_key
IMPORTING
!i_koart TYPE koart
!i_umsks TYPE umsks
!i_amount_diff TYPE p
RETURNING
VALUE(r_bschl) TYPE bschl .
METHODS get_bsid
IMPORTING
!it_clearing_line TYPE tt_clearing_line .
METHODS get_bsik
IMPORTING
!it_clearing_line TYPE tt_clearing_line .
METHODS get_bseg
IMPORTING
!it_clearing_line TYPE tt_clearing_line .
methods GET_WAERS .
methods GET_WRBTR_DIFF
returning
value(R_WRBTR_DIFF) type ty_amount .
methods GET_PROFIT_LOSS
returning
value(R_PROFIT_LOSS) type XFELD .
methods GET_EXCHANGERATE
importing
!I_RATE_TYPE type BAPI1093_1-RATE_TYPE default 'M'
!I_FROM type WAERS
!I_TO type WAERS
!I_DATE type DATUM default SY-DATUM
returning
value(R_EXCHANGERATE) type TY_EXCHANGE_RATE .
METHODS posting_before .
METHODS posting_interface .
ENDCLASS.
CLASS ZCL_FI_CLEARING IMPLEMENTATION.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Static Public Method ZCL_FI_CLEARING=>AFTER_XFTPOST_LOOP
* +-------------------------------------------------------------------------------------------------+
* | [<-->] FT TYPE BDCDATA_TAB
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD after_xftpost_loop.
" 默认处理,先处理差异行,(再到该增强),再处理未清项
CHECK ms_extra-diff_amount_proc = 'M'
OR ms_extra-diff_amount_proc = ''.
DATA ls_bdcdata TYPE bdcdata.
DEFINE _bdc_data.
CLEAR ls_bdcdata.
ls_bdcdata-fnam = &1.
ls_bdcdata-fval = &2.
INSERT ls_bdcdata INTO TABLE ft[].
END-OF-DEFINITION.
" 按项目需求,填写需要的信息
LOOP AT ms_extra-ftpost INTO DATA(ls_ftpost).
_bdc_data ls_ftpost-fnam ls_ftpost-fval.
ENDLOOP.
ENDMETHOD.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Static Public Method ZCL_FI_CLEARING=>BEFORE_FCODE_F11
* +-------------------------------------------------------------------------------------------------+
* | [<-->] FT TYPE BDCDATA_TAB
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD before_fcode_f11.
" 先处理未清项,再处理差异行
" 差异行也走这段逻辑
CHECK ms_extra-diff_amount_proc = 'A'
OR ms_extra-profit_loss = abap_true.
DATA ls_bdcdata TYPE bdcdata.
DEFINE _bdc_begin.
CLEAR ls_bdcdata.
ls_bdcdata-program = &1.
ls_bdcdata-dynpro = &2.
ls_bdcdata-dynbegin = 'X'.
INSERT ls_bdcdata INTO TABLE ft[].
END-OF-DEFINITION.
DEFINE _bdc_data.
CLEAR ls_bdcdata.
ls_bdcdata-fnam = &1.
ls_bdcdata-fval = &2.
INSERT ls_bdcdata INTO TABLE ft[].
END-OF-DEFINITION.
" _bdc_begin 'SAPMF05A' '0733'. " 该增强位置是在保存前,原本的BDC代码只缺少命令
_bdc_data 'BDC_OKCODE' 'PA'. " 处理未清项
" _bdc_begin 'SAPMF05A' '0733'. " 该增强位置是在保存前,原本的BDC代码只缺少命令
_bdc_data 'SAPMF05A' 'PA'. " 处理未清项
" MAIN - 标准
" PART - 部分支付
" REST - 剩余项目
" WITH - 预扣税
_bdc_begin 'SAPDF05X' '3100'.
_bdc_data 'BDC_OKCODE' 'REST'. " 剩余过账
_bdc_begin 'SAPDF05X' '3100'.
_bdc_data 'BDC_OKCODE' 'PI'. " PI - 双击行
_bdc_begin 'SAPDF05X' '3100'.
_bdc_data 'BDC_OKCODE' 'BS'. " BS - 模拟
IF ms_extra-diff_amount_proc = 'A'.
IF ( ms_extra-program IS INITIAL OR ms_extra-dynpro IS INITIAL ) " 没有下一步的屏幕
OR ms_extra-ftpost IS INITIAL. " 没有特殊处理
" 没有需要补充的,直接保存
_bdc_begin 'SAPMF05A' '0301'. " 补充一个屏幕
" _bdc_data 'BDC_OKCODE' '/11'. " 后续的标准代码最后会手工添加一行命令保存
ELSE.
" 正常情况,由于行项目必填,会跳到主界面报错
* _bdc_begin 'SAPMF05A' '0700'.
* _bdc_data 'BDC_OKCODE' 'PI'. " 选择凭证行
*
* _bdc_begin 'SAPMF05A' '0610'.
* _bdc_data '*BSEG-BUZEI' '001'. " 默认选第一行
* _bdc_data 'BDC_OKCODE' 'ENTR'. " 确认
_bdc_begin 'SAPMF05A' '0700'.
_bdc_data 'BDC_OKCODE' 'NK'. " NK - 补充,跳转到需要补充数据的界面
" 按项目需求,填写需要手工处理的信息
_bdc_begin ms_extra-program ms_extra-dynpro.
LOOP AT ms_extra-ftpost INTO DATA(ls_ftpost).
_bdc_data ls_ftpost-fnam ls_ftpost-fval.
ENDLOOP.
ENDIF.
ENDIF.
" 对于外币清账,如果汇率有差异,还需要考虑损益行
IF ms_extra-profit_loss = abap_true.
_bdc_begin 'SAPMF05A' '0700'.
_bdc_data 'BDC_OKCODE' 'NK'. " 跳转到未完成行,在这里就是损益行
" 输入行文本并回到预览界面
_bdc_begin 'SAPMF05A' '0300'.
_bdc_data 'BDC_OKCODE' 'AB'.
_bdc_data 'BSEG-SGTXT' 'Clearing Profit & Loss'(004).
" 弹窗确认
" 我这项目的损益行输入后还会弹个CODING BLOCK输入框,自行调整
_bdc_begin 'SAPLKACB' '0002'.
_bdc_data 'BDC_OKCODE' 'ENTE'. " 确认
" 设置一个界面
_bdc_begin 'SAPMF05A' '0700'.
ENDIF.
ENDMETHOD.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Static Public Method ZCL_FI_CLEARING=>CLASS_CONSTRUCTOR
* +-------------------------------------------------------------------------------------------------+
* +--------------------------------------------------------------------------------------</SIGNATURE>
method CLASS_CONSTRUCTOR.
" AUGLV对应FB05要处理的业务
" UMBUCHNG, Transfer posting with clearing, 转账并清账
SELECT SINGLE * FROM t041a WHERE auglv = 'UMBUCHNG' INTO @ms_t041a.
SELECT * FROM tbsl INTO TABLE @mt_tbsl.
" 集团
SELECT SINGLE * FROM t000 WHERE mandt = @sy-mandt INTO @ms_t000.
IF sy-subrc <> 0.
CLEAR ms_t000.
ENDIF.
endmethod.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Static Private Method ZCL_FI_CLEARING=>DETERMINE_POSTING_KEY
* +-------------------------------------------------------------------------------------------------+
* | [--->] I_KOART TYPE KOART
* | [--->] I_UMSKS TYPE UMSKS
* | [--->] I_AMOUNT_DIFF TYPE P
* | [<-()] R_BSCHL TYPE BSCHL
* +--------------------------------------------------------------------------------------</SIGNATURE>
method DETERMINE_POSTING_KEY.
" 根据科目类型,特殊总账标识,差异金额,查找对应记账码
CASE i_koart.
WHEN 'D'.
IF i_amount_diff < 0.
IF i_umsks = space.
r_bschl = ms_t041a-rpdha.
ELSE.
r_bschl = ms_t041a-bsdhs.
ENDIF.
ELSE.
IF i_umsks = space.
r_bschl = ms_t041a-rpdso.
ELSE.
r_bschl = ms_t041a-bsdss.
ENDIF.
ENDIF.
WHEN 'K'.
IF i_amount_diff < 0.
IF i_umsks = space.
r_bschl = ms_t041a-rpkha.
ELSE.
r_bschl = ms_t041a-bskhs.
ENDIF.
ELSE.
IF i_umsks = space.
r_bschl = ms_t041a-rpkso.
ELSE.
r_bschl = ms_t041a-bskss.
ENDIF.
ENDIF.
WHEN 'S'.
IF i_amount_diff < 0.
r_bschl = ms_t041a-bssha.
ELSE.
r_bschl = ms_t041a-bssso.
ENDIF.
ENDCASE.
endmethod.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Private Method ZCL_FI_CLEARING->GET_BSEG
* +-------------------------------------------------------------------------------------------------+
* | [--->] IT_CLEARING_LINE TYPE TT_CLEARING_LINE
* +--------------------------------------------------------------------------------------</SIGNATURE>
method GET_BSEG.
CHECK it_clearing_line IS NOT INITIAL.
" 检查并获取可以清账的凭证数据
SELECT
bseg~bukrs,
bseg~belnr,
bseg~gjahr,
bseg~buzei,
bseg~umsks, " 特殊总账事务
bseg~umskz, " 特殊总账标识
bseg~zfbdt, " 付款清算日期
bseg~shkzg,
bseg~dmbtr,
bseg~wrbtr,
bseg~h_budat, " 过账日期,
bseg~h_waers,
bseg~zuonr,
bseg~sgtxt
FROM bseg
JOIN bkpf ON bseg~bukrs = bkpf~bukrs
AND bseg~belnr = bkpf~belnr
AND bseg~gjahr = bkpf~gjahr
FOR ALL ENTRIES IN @it_clearing_line[]
WHERE bseg~bukrs = @it_clearing_line-bukrs
AND bseg~belnr = @it_clearing_line-belnr
AND bseg~gjahr = @it_clearing_line-gjahr
AND bseg~buzei = @it_clearing_line-buzei
AND bseg~hkont = @m_hkont
INTO CORRESPONDING FIELDS OF TABLE @mt_bseg.
endmethod.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Private Method ZCL_FI_CLEARING->GET_BSID
* +-------------------------------------------------------------------------------------------------+
* | [--->] IT_CLEARING_LINE TYPE TT_CLEARING_LINE
* +--------------------------------------------------------------------------------------</SIGNATURE>
method GET_BSID.
CHECK it_clearing_line IS NOT INITIAL.
" 检查并获取可以清账的凭证数据
SELECT
bukrs,
belnr,
gjahr,
buzei,
projk, " 客户WBS必填
umsks, " 特殊总账事务
umskz, " 特殊总账标识
zfbdt, " 付款清算日期
shkzg,
dmbtr,
wrbtr,
budat AS h_budat, " 过账日期
waers AS h_waers,
zuonr,
sgtxt
FROM bsid
FOR ALL ENTRIES IN @it_clearing_line[]
WHERE bukrs = @it_clearing_line-bukrs
AND belnr = @it_clearing_line-belnr
AND gjahr = @it_clearing_line-gjahr
AND buzei = @it_clearing_line-buzei
AND kunnr = @m_hkont
INTO CORRESPONDING FIELDS OF TABLE @mt_bseg.
endmethod.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Private Method ZCL_FI_CLEARING->GET_BSIK
* +-------------------------------------------------------------------------------------------------+
* | [--->] IT_CLEARING_LINE TYPE TT_CLEARING_LINE
* +--------------------------------------------------------------------------------------</SIGNATURE>
method GET_BSIK.
CHECK it_clearing_line IS NOT INITIAL.
" 检查并获取可以清账的凭证数据
SELECT
bukrs,
belnr,
gjahr,
buzei,
umsks, " 特殊总账事务
umskz, " 特殊总账标识
zfbdt, " 付款清算日期
shkzg,
dmbtr,
wrbtr,
budat AS h_budat, " 过账日期
waers AS h_waers,
zuonr,
sgtxt
FROM bsik
FOR ALL ENTRIES IN @it_clearing_line[]
WHERE bukrs = @it_clearing_line-bukrs
AND belnr = @it_clearing_line-belnr
AND gjahr = @it_clearing_line-gjahr
AND buzei = @it_clearing_line-buzei
AND lifnr = @m_hkont
INTO CORRESPONDING FIELDS OF TABLE @mt_bseg.
endmethod.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Private Method ZCL_FI_CLEARING->POSTING_BEFORE
* +-------------------------------------------------------------------------------------------------+
* +--------------------------------------------------------------------------------------</SIGNATURE>
method POSTING_BEFORE.
IF m_bukrs IS INITIAL.
ms_result-mtype = 'E'.
ms_result-msg = '请输入公司代码'.
RETURN.
ENDIF.
IF m_hkont IS INITIAL.
ms_result-mtype = 'E'.
ms_result-msg = '请输入科目'.
RETURN.
ENDIF.
IF m_koart = 'D'
OR m_koart = 'K'
OR m_koart = 'S'.
ELSE.
ms_result-mtype = 'E'.
ms_result-msg = '目前仅支持客户清账、供应商清账、总账清账'.
RETURN.
ENDIF.
IF mt_bseg IS INITIAL.
ms_result-mtype = 'E'.
ms_result-msg = '没有未清凭证数据'.
RETURN.
ENDIF.
endmethod.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Private Method ZCL_FI_CLEARING->POSTING_INTERFACE
* +-------------------------------------------------------------------------------------------------+
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD posting_interface.
DATA:
lt_ftclear TYPE STANDARD TABLE OF ftclear,
lt_ftpost TYPE STANDARD TABLE OF ftpost,
lt_blntab TYPE STANDARD TABLE OF blntab,
lt_fttax TYPE STANDARD TABLE OF fttax.
" 清账项目选择条件
" 选择字段填BELNR,并传值是凭证+行项目,就能取到对应凭证行
" 因此无需其他额外的筛选条件
" 如果不能确定,或者就需要某一类的数据,可以自己酌情调整
DATA ls_ftclear TYPE ftclear.
DEFINE _ftclear.
CLEAR ls_ftclear.
ls_ftclear-agbuk = m_bukrs. " 公司
ls_ftclear-agkon = m_hkont. " 科目
ls_ftclear-agkoa = m_koart. " 账户类型
ls_ftclear-xnops = abap_true. " 标准未清项
ls_ftclear-agums = m_agums. " 特别总账标识
ls_ftclear-selfd = &2.
ls_ftclear-selvon = &3.
INSERT ls_ftclear INTO TABLE &1.
END-OF-DEFINITION.
" 手工清账项目,对于存在差额的项目,需要手工填写清账金额
" 就是BDC录屏,如果有缺什么字段,可以到前台F1获取屏幕字段名
DATA ls_ftpost TYPE ftpost.
DEFINE _ftpost.
IF &4 IS NOT INITIAL OR &4 <> ''.
CLEAR ls_ftpost.
ls_ftpost-stype = &2. " K抬头,P行项目
ls_ftpost-count = &3. " 抬头默认1,行项目流水
ls_ftpost-fnam = &4. " 屏幕字段
ls_ftpost-fval = &5. " 字段值
INSERT ls_ftpost INTO TABLE &1.
ENDIF.
END-OF-DEFINITION.
" 数据校验
CLEAR ms_result.
posting_before( ).
CHECK ms_result-mtype <> 'E'.
" 读取凭证的三种货币
get_waers( ).
" 汇总金额,获取清账差异金额
DATA(l_wrbtr_diff) = get_wrbtr_diff( ).
" 未清项目
LOOP AT mt_bseg REFERENCE INTO DATA(lr_bseg).
DATA(l_selfd) = 'BELNR'.
DATA(l_selval) = |{ lr_bseg->belnr }{ lr_bseg->gjahr }{ lr_bseg->buzei }|.
_ftclear lt_ftclear l_selfd l_selval.
ENDLOOP.
" 获取其他辅助参数
DATA l_umsks TYPE umsks. " 特殊总账事务
DATA l_umskz TYPE umskz. " 特殊总账标识
DATA lr_clearing_ref TYPE REF TO bseg. " 任取一行未清项作为差异金额的参考
LOOP AT mt_bseg REFERENCE INTO lr_bseg.
" 只要有一行是特殊总账事务,清账就以特殊总账方式进行
IF l_umsks IS INITIAL.
l_umsks = lr_bseg->umsks.
l_umskz = lr_bseg->umskz.
ENDIF.
" 找出金额绝对值最大的行作为参考行
" (我这项目要求如此,可自行调整)
IF lr_clearing_ref IS NOT BOUND.
lr_clearing_ref = lr_bseg.
ELSEIF abs( lr_bseg->dmbtr ) > abs( lr_clearing_ref->dmbtr ).
lr_clearing_ref = lr_bseg.
ENDIF.
ENDLOOP.
DATA(l_budat) = |{ m_budat DATE = USER }|. " BDC录屏,日期要填写用户格式
SELECT SINGLE waers FROM t001 WHERE bukrs = @m_bukrs INTO @DATA(l_waers). " 公司本币
" K-抬头
_ftpost lt_ftpost 'K' 1 'BKPF-BUKRS' m_bukrs.
_ftpost lt_ftpost 'K' 1 'BKPF-BLART' 'AB'. " 清账只有这一种凭证类型
_ftpost lt_ftpost 'K' 1 'BKPF-BLDAT' l_budat.
_ftpost lt_ftpost 'K' 1 'BKPF-BUDAT' l_budat.
_ftpost lt_ftpost 'K' 1 'BKPF-MONAT' m_budat+4(2).
_ftpost lt_ftpost 'K' 1 'BKPF-WAERS' l_waers.
IF m_bktxt IS NOT INITIAL.
_ftpost lt_ftpost 'K' 1 'BKPF-BKTXT' m_bktxt.
ENDIF.
" 判断是否存在差异金额
IF l_amount_diff = 0.
CLEAR ms_extra. " 没有差异金额,无需任何增强处理
ELSE. " 存在差异金额
" 根据科目、总账事务、余额,查找记账码
DATA l_bschl TYPE bschl.
l_bschl = determine_posting_key(
i_koart = m_koart
i_umsks = l_umsks
i_amount_diff = l_amount_diff
).
DATA l_sgtxt TYPE bseg-sgtxt.
l_sgtxt = |清账{ lr_clearing_ref->belnr }|. " 行文本,记录参考的凭证
" 对差异金额的处理方案选择
CASE ms_extra-diff_amount_proc.
WHEN 'A'. " 自动清账,先处理未清项,再处理差异金额
" 移除手工清账部分
DELETE lt_ftpost WHERE stype = 'P'.
" 项目特殊处理
IF l_umskz IS NOT INITIAL " 特殊总账
OR l_bschl+1(1) = '9'. " 冲销
DELETE ms_extra-ftpost WHERE fnam <> 'BSEG-SGTXT'. " 仅文本可输入
ENDIF.
" 复制LFIPIF00中的子例程dynpro_ermitteln
DATA l_winfk TYPE t019w-winfk.
CLEAR l_winfk.
CASE m_koart.
WHEN 'D'.
l_winfk = 'ZKOD'.
WHEN 'K'.
l_winfk = 'ZKOK'.
WHEN 'S'.
l_winfk = 'ZKOS'.
WHEN 'A'.
l_winfk = 'ZKOA'.
ENDCASE.
" 查找差异行的屏幕(从POSTING_INTERFACE_CLEARING中找到的代码)
CALL FUNCTION 'NEXT_DYNPRO_SEARCH'
EXPORTING
i_bschl = l_bschl
i_bukrs = m_bukrs
i_tcode = 'FB05'
i_umskz = l_umskz
i_winfk = l_winfk
IMPORTING
e_dynnra = ms_extra-dynpro
e_mpool = ms_extra-program
EXCEPTIONS
OTHERS = 99.
IF sy-subrc <> 0.
CLEAR ms_extra.
ENDIF.
WHEN OTHERS. " 手工清账,先处理差异金额
DATA(l_dmbtr) = |{ abs( l_amount_diff ) NUMBER = USER }|. " BDC外部格式,另外正负按记账码区分,所以都传正值
DATA(l_zfbdt) = |{ lr_clearing_ref->zfbdt DATE = USER }|. " BDC外部格式
" P-行项目
" 我这项目要求清账后生成无凭证行或一行差额的清账凭证
" 有生成多行要求的自己酌情修改
_ftpost lt_ftpost 'P' 1 'RF05A-NEWBS' l_bschl. " 过账码
_ftpost lt_ftpost 'P' 1 'RF05A-NEWKO' m_hkont. " 科目
_ftpost lt_ftpost 'P' 1 'RF05A-NEWUM' l_umskz. " 特别总账标识
_ftpost lt_ftpost 'P' 1 'BSEG-WRBTR' l_dmbtr. " 汇总后的差额
_ftpost lt_ftpost 'P' 1 'BSEG-ZFBDT' l_zfbdt. " 付款清算日期
_ftpost lt_ftpost 'P' 1 'BSEG-SGTXT' l_sgtxt. " 行项目文本必填
_ftpost lt_ftpost 'P' 1 'BSEG-ZUONR' lr_clearing_ref->zuonr. " 分配字段,一般都需要
ENDCASE.
ENDIF. " IF l_amount_diff <> 0.
" 检查是否有损益
get_profit_loss( ).
SORT lt_ftpost BY stype count fnam fval.
DELETE ADJACENT DUPLICATES FROM lt_ftpost COMPARING stype count fnam.
DATA l_mode TYPE char01 VALUE 'N'. " BDC导入模式
CALL FUNCTION 'POSTING_INTERFACE_START'
EXPORTING
i_client = sy-mandt
i_function = 'C'
i_mode = l_mode
i_keep = 'X'
i_update = 'S'
i_user = sy-uname.
IF sy-subrc <> 0.
ms_result-mtype = 'E'.
MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4
INTO ms_result-msg.
RETURN.
ENDIF.
CALL FUNCTION 'POSTING_INTERFACE_CLEARING'
EXPORTING
i_auglv = ms_t041a-auglv " 清账程序固定UMBUCHNG
i_tcode = 'FB05' " 固定
i_sgfunct = 'C'
i_no_auth = 'X' " 不考虑权限
IMPORTING
e_msgid = sy-msgid
e_msgno = sy-msgno
e_msgty = sy-msgty
e_msgv1 = sy-msgv1
e_msgv2 = sy-msgv2
e_msgv3 = sy-msgv3
e_msgv4 = sy-msgv4
TABLES
t_blntab = lt_blntab
t_ftclear = lt_ftclear
t_ftpost = lt_ftpost
t_fttax = lt_fttax
EXCEPTIONS
clearing_procedure_invalid = 1
clearing_procedure_missing = 2
table_t041a_empty = 3
transaction_code_invalid = 4
amount_format_error = 5
too_many_line_items = 6
company_code_invalid = 7
screen_not_found = 8
no_authorization = 9
OTHERS = 10.
IF sy-subrc <> 0.
ms_result-mtype = 'E'.
ENDIF.
MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4
INTO ms_result-msg.
CALL FUNCTION 'POSTING_INTERFACE_END'
EXCEPTIONS
session_not_processable = 1
OTHERS = 2.
IF sy-subrc <> 0.
ms_result-mtype = 'E'.
MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno
WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4
INTO ms_result-msg.
RETURN.
ENDIF.
READ TABLE lt_blntab INTO DATA(ls_blntab) INDEX 1.
IF sy-subrc = 0.
ms_result-mtype = 'S'.
ms_result-msg = '清账已处理'.
ms_result-belnr = ls_blntab-belnr.
ELSE.
ms_result-mtype = 'E'.
IF ms_result-msg IS INITIAL.
ms_result-msg = '清账处理失败'.
ENDIF.
ENDIF.
ENDMETHOD.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Static Public Method ZCL_FI_CLEARING=>POST_CUSTOMER
* +-------------------------------------------------------------------------------------------------+
* | [--->] I_BUKRS TYPE BUKRS
* | [--->] I_KUNNR TYPE KUNNR
* | [--->] I_AGUMS TYPE AGUMS (default ='A')
* | [--->] I_BUDAT TYPE BUDAT (default =SY-DATUM)
* | [--->] I_BKTXT TYPE BKTXT(optional)
* | [--->] IT_CLEARING_LINE TYPE TT_CLEARING_LINE
* | [--->] I_PROC TYPE CHAR01 (default ='M')
* | [--->] IT_FTPOST TYPE FAGL_T_FTPOST(optional)
* | [<-()] RESULT TYPE TY_RESULT
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD post_customer.
IF it_clearing_line IS INITIAL.
result-mtype = 'E'.
result-mtype = '没有未清项'.
RETURN.
ENDIF.
CLEAR ms_extra.
" A-先处理未清项,再由系统带出差异行,最后对差异行进行手工调整
" M-先手工生成并调整差异行,再处理未清项
ms_extra-diff_amount_proc = i_proc.
ms_extra-ftpost = it_ftpost. " 除了我预留的那些字段,按项目可能会有不同设置,可以通过该项设置
DATA(lo_ins) = NEW zcl_fi_clearing( ).
lo_ins->m_bukrs = i_bukrs.
lo_ins->m_koart = 'D'.
lo_ins->m_agums = i_agums.
lo_ins->m_hkont = i_kunnr.
lo_ins->m_budat = i_budat.
lo_ins->m_bktxt = i_bktxt.
lo_ins->get_bsid( it_clearing_line ).
lo_ins->posting_interface( ).
result = lo_ins->ms_result.
ENDMETHOD.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Static Public Method ZCL_FI_CLEARING=>POST_LEDGER
* +-------------------------------------------------------------------------------------------------+
* | [--->] I_BUKRS TYPE BUKRS
* | [--->] I_HKONT TYPE HKONT
* | [--->] I_AGUMS TYPE AGUMS(optional)
* | [--->] I_BUDAT TYPE BUDAT (default =SY-DATUM)
* | [--->] I_BKTXT TYPE BKTXT(optional)
* | [--->] IT_CLEARING_LINE TYPE TT_CLEARING_LINE
* | [--->] I_PROC TYPE CHAR01 (default ='M')
* | [--->] IT_FTPOST TYPE FAGL_T_FTPOST(optional)
* | [<-()] RESULT TYPE TY_RESULT
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD POST_LEDGER.
IF it_clearing_line IS INITIAL.
result-mtype = 'E'.
result-mtype = '没有未清项'.
RETURN.
ENDIF.
CLEAR ms_extra.
" A-先处理未清项,再由系统带出差异行,最后对差异行进行手工调整
" M-先手工生成并调整差异行,再处理未清项
ms_extra-diff_amount_proc = i_proc.
ms_extra-ftpost = it_ftpost. " 除了我预留的那些字段,按项目可能会有不同设置,可以通过该项设置
DATA(lo_ins) = NEW zcl_fi_clearing( ).
lo_ins->m_bukrs = i_bukrs.
lo_ins->m_koart = 'S'.
lo_ins->m_hkont = i_hkont.
lo_ins->m_budat = i_budat.
lo_ins->m_bktxt = i_bktxt.
lo_ins->get_bseg( it_clearing_line ).
lo_ins->posting_interface( ).
result = lo_ins->ms_result.
ENDMETHOD.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Static Public Method ZCL_FI_CLEARING=>POST_VENDOR
* +-------------------------------------------------------------------------------------------------+
* | [--->] I_BUKRS TYPE BUKRS
* | [--->] I_LIFNR TYPE LIFNR(optional)
* | [--->] I_AGUMS TYPE AGUMS (default ='A')
* | [--->] I_BUDAT TYPE BUDAT (default =SY-DATUM)
* | [--->] I_BKTXT TYPE BKTXT(optional)
* | [--->] IT_CLEARING_LINE TYPE TT_CLEARING_LINE
* | [--->] I_PROC TYPE CHAR01 (default ='M')
* | [--->] IT_FTPOST TYPE FAGL_T_FTPOST(optional)
* | [<-()] RESULT TYPE TY_RESULT
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD POST_VENDOR.
IF it_clearing_line IS INITIAL.
result-mtype = 'E'.
result-mtype = '没有未清项'.
RETURN.
ENDIF.
CLEAR ms_extra.
" A-先处理未清项,再由系统带出差异行,最后对差异行进行手工调整
" M-先手工生成并调整差异行,再处理未清项
ms_extra-diff_amount_proc = i_proc.
ms_extra-ftpost = it_ftpost. " 除了我预留的那些字段,按项目可能会有不同设置,可以通过该项设置
DATA(lo_ins) = NEW zcl_fi_clearing( ).
lo_ins->m_bukrs = i_bukrs.
lo_ins->m_koart = 'K'.
lo_ins->m_agums = i_agums.
lo_ins->m_hkont = i_lifnr.
lo_ins->m_budat = i_budat.
lo_ins->m_bktxt = i_bktxt.
lo_ins->get_bsik( it_clearing_line ).
lo_ins->posting_interface( ).
result = lo_ins->ms_result.
ENDMETHOD.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Private Method ZCL_FI_CLEARING->GET_EXCHANGERATE
* +-------------------------------------------------------------------------------------------------+
* | [--->] I_RATE_TYPE TYPE BAPI1093_1-RATE_TYPE (default ='M')
* | [--->] I_FROM TYPE WAERS
* | [--->] I_TO TYPE WAERS
* | [--->] I_DATE TYPE DATUM (default =SY-DATUM)
* | [<-()] R_EXCHANGERATE TYPE TY_EXCHANGE_RATE
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD get_exchangerate.
DATA ls_rate TYPE bapi1093_0.
DATA ls_bapiret1 TYPE bapiret1.
" 正向汇率
CALL FUNCTION 'BAPI_EXCHANGERATE_GETDETAIL'
EXPORTING
rate_type = i_rate_type
from_curr = i_from
to_currncy = i_to
date = i_date
IMPORTING
exch_rate = ls_rate
return = ls_bapiret1.
IF sy-subrc = 0.
IF ls_rate-to_factor IS NOT INITIAL.
r_exchangerate = ( ls_rate-from_factor / ls_rate-to_factor ) * ls_rate-exch_rate.
ENDIF.
RETURN.
ENDIF.
" 如果没有就按反向汇率计算
CLEAR ls_rate.
CLEAR ls_bapiret1.
CALL FUNCTION 'BAPI_EXCHANGERATE_GETDETAIL'
EXPORTING
rate_type = i_rate_type
from_curr = i_to
to_currncy = i_from
date = i_date
IMPORTING
exch_rate = ls_rate
return = ls_bapiret1.
IF sy-subrc <> 0.
IF ls_rate-exch_rate IS NOT INITIAL AND ls_rate-to_factor IS NOT INITIAL.
r_exchangerate = ( ls_rate-from_factor / ls_rate-to_factor ) / ls_rate-exch_rate.
ENDIF.
RETURN.
ENDIF.
ENDMETHOD.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Private Method ZCL_FI_CLEARING->GET_WRBTR_DIFF
* +-------------------------------------------------------------------------------------------------+
* | [<-()] R_WRBTR_DIFF TYPE GTY_AMOUNT
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD get_wrbtr_diff.
LOOP AT mt_bseg REFERENCE INTO DATA(lr_bseg).
IF lr_bseg->shkzg = 'S'.
r_wrbtr_diff = r_wrbtr_diff + lr_bseg->wrbtr.
ELSE.
r_wrbtr_diff = r_wrbtr_diff - lr_bseg->wrbtr.
ENDIF.
ENDLOOP.
" 外币清账的情况,需要计算外币的差异
IF m_waers <> m_waers_loc.
DATA(l_exchangerate) = get_exchangerate(
i_from = m_waers
i_to = m_waers_loc
i_date = m_budat
).
r_wrbtr_diff = r_wrbtr_diff * l_exchangerate.
ENDIF.
ENDMETHOD.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Private Method ZCL_FI_CLEARING->GET_PROFIT_LOSS
* +-------------------------------------------------------------------------------------------------+
* | [<-()] R_PROFIT_LOSS TYPE XFELD
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD get_profit_loss.
CLEAR ms_extra-profit_loss.
DATA l_exchangerate TYPE gty_exchange_rate.
DATA l_amount TYPE gty_amount.
" 本币差异计算
IF m_waers <> m_waers_loc.
l_exchangerate = get_exchangerate(
i_from = m_waers
i_to = m_waers_loc
i_date = m_budat
).
" 累计损益
DATA l_dmbtr_diff TYPE gty_amount.
LOOP AT mt_bseg REFERENCE INTO DATA(lr_bseg).
l_amount = lr_bseg->wrbtr * l_exchangerate.
l_dmbtr_diff = l_dmbtr_diff + l_amount - lr_bseg->dmbtr.
ENDLOOP.
" 累计的差异即为损益
IF l_dmbtr_diff <> 0.
ms_extra-profit_loss = abap_true.
RETURN.
ENDIF.
ENDIF.
" 集团货币差异计算
IF m_waers <> m_waers_grp.
CLEAR l_exchangerate.
l_exchangerate = get_exchangerate(
i_from = m_waers
i_to = m_waers_grp
i_date = m_budat
).
" 累计损益
DATA l_dmbe2_diff TYPE gty_amount.
LOOP AT mt_bseg REFERENCE INTO lr_bseg.
l_amount = lr_bseg->wrbtr * l_exchangerate.
l_dmbe2_diff = l_dmbe2_diff + l_amount - lr_bseg->dmbe2.
ENDLOOP.
" 累计的差异即为损益
IF l_dmbe2_diff <> 0.
ms_extra-profit_loss = abap_true.
RETURN.
ENDIF.
ENDIF.
ENDMETHOD.
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Private Method ZCL_FI_CLEARING->GET_WAERS
* +-------------------------------------------------------------------------------------------------+
* +--------------------------------------------------------------------------------------</SIGNATURE>
METHOD get_waers.
CLEAR m_waers.
" 凭证货币
READ TABLE mt_bseg REFERENCE INTO DATA(lr_bseg) INDEX 1.
IF sy-subrc = 0.
m_waers = lr_bseg->h_waers.
ENDIF.
" 公司本币
SELECT SINGLE waers FROM t001 WHERE bukrs = @m_bukrs INTO @m_waers_loc.
IF sy-subrc <> 0.
m_waers_loc = m_waers.
ENDIF.
" 集团货币
* SELECT SINGLE mwaer FROM t000 WHERE mandt = @sy-mandt INTO @m_waers_grp.
* IF sy-subrc <> 0.
* m_waers_grp = m_waers.
* ENDIF.
" 免得多次查询
m_waers_grp = ms_t000-mwaer.
IF m_waers_grp IS INITIAL.
m_waers_grp = m_waers.
ENDIF.
ENDMETHOD.
ENDCLASS.
增强处理¶
通过增强来调整差异金额的处理顺序,经分析,在函数 POSTING_INTERFACE_CLEARING 中选取了两个位置来处理:
也可以将 POSTING_INTERFACE_CLEARING 的函数组拷贝一个出来修改。函数组内容不多。
子例程 xftpost_loop 后加入
...
*------- Buchungsdatentabelle (XFTPOST) abarbeiten im Loop -------------
PERFORM xftpost_loop.
*{ INSERT S4DKXXXXXX 1
*
" 用于处理差异凭证行
CALL METHOD zcl_fi_clearing=>after_xftpost_loop
CHANGING
ft = ft[].
*} INSERT
...
子例程 fcode_f11 前加入
...
*------- Transaktion abschließen ---------------------------------------
*{ INSERT S4DKXXXXXX 2
*
" 用于处理差异凭证行
CALL METHOD zcl_fi_clearing=>before_fcode_f11
CHANGING
ft = ft[].
DESCRIBE TABLE ft LINES index. " 必须,后续代码是根据index新增保存命令
*} INSERT
PERFORM fcode_f11.
...
用例¶
客户手工清账
DATA lt_clearing_line TYPE lcl_clearing_account=>tt_clearing_line.
lt_clearing_line = VALUE #(
( bukrs = '1000' belnr = '90000001' gjahr = '2022' buzei = '001' )
( bukrs = '1000' belnr = '90000001' gjahr = '2022' buzei = '003' )
).
" 客户清账
DATA(ls_result) = zcl_fi_clearing=>post_customer(
i_bukrs = '1000'
i_kunnr = '1001'
it_clearing_line = lt_clearing_line ).
供应商手工清账
DATA lt_clearing_line TYPE lcl_clearing_account=>tt_clearing_line.
lt_clearing_line = VALUE #(
( bukrs = '1000' belnr = '90000002' gjahr = '2022' buzei = '002' )
( bukrs = '1000' belnr = '90000002' gjahr = '2022' buzei = '004' )
).
" 供应商清账
DATA(ls_result) = zcl_fi_clearing=>post_vendor(
i_bukrs = '1000'
i_lifnr = '8000001'
it_clearing_line = lt_clearing_line ).
先选择未清项,再处理差异行
DATA lt_clearing_line TYPE lcl_clearing_account=>tt_clearing_line.
lt_clearing_line = VALUE #(
( bukrs = '1000' belnr = '90000001' gjahr = '2022' buzei = '001' )
( bukrs = '1000' belnr = '90000001' gjahr = '2022' buzei = '003' )
).
DATA lt_ftpost TYPE lcl_clearing_account=>tt_clearing_line.
_ftpost lt_ftpost 'P' 1 'BSEG-ZUONR' '0000001001'. " 分配值
_ftpost lt_ftpost 'P' 1 'BSEG-HKONT' '2201000010'. " 总账默认客户总账,调整为另一个
_ftpost lt_ftpost 'P' 1 'BSEG-SGTXT' '行文本'.
" 客户清账
DATA(ls_result) = zcl_fi_clearing=>post_customer(
i_bukrs = '1000'
i_kunnr = '1001'
it_clearing_line = lt_clearing_line
i_porc = 'A'
it_ftpost = lt_ftpost ).
关于清账后是否有凭证行的说明¶
参考文章https://zhuanlan.zhihu.com/p/108487028
按文章说法,应该是清账的行项目,部分字段(比如功能范围,成本中心,WBS之类的)不一致,所以就会生成行项目。如果一致就不会生成行项目。
如果有强烈的要求,可以到MF05AFA0_AUSGLEICH_KONTAB_FUEL中通过增强调整控制。