IT干货网

S/4 HANA中的ACDOCT和FAGLFLEXT

shasha 2022年06月08日 SAP 254 0

最近的几个需求让我对ACDOCT和FAGLFLEXT这两个财务相关表(准确地说是视图)产生了一些了解,同时也发现某些开发同行和业务顾问并没有认识到这些东西。因此我打算从技术角度来说明一下这两个视图在S4中的实际面貌。当然因为我并不了解FICO方面的任何业务知识,所以也只是进行技术角度的说明。

本文内容基于 S/4 1709版本;某些内容可能和其他版本不是完全符合,特别是具体的DDL代码部分。

本文链接:IT虾米网

财务报表开发中经常会出现有关期初余额和本期发生额的需求,传统上获取期初余额的方式是从FAGLFLEXT或ACDOCT中获取结转余额(HSLVT),再加上各期发生额(HSL01, HSL02....)。

比如要获取公司1000的2018年3月的期初余额,ABAP代码的写法是:

SELECT SUM( ( hslvt + hsl01 + hsl02 ) ) FROM faglflext WHERE rbukrs = '1000' 
                                                         AND ryear  = '2018' 
  INTO @DATA(l_balance).

这只是段简单的示例代码,实际需求中可能要按科目等条件取出各个期间分组汇总。并且,为了适应动态的查询条件,通常要使用Field Symbol,在内表中对数据进行累加操作,颇为繁琐。

那么,HSLVT和HSL01, HSL02....等数据是从哪里来的呢?如果把FAGLFLEXT和ACDOCT视为数据库表,读者可能会认为,它们是通过某些结转操作,由系统汇总计算后得来并存储到数据库中的。但实际上,FAGLFLEXT和ACDOCT都只是视图,而非透明表。可以理解成,它们当中的数据不是数据库中既有的,而是和各种报表程序一样、是对其它表的数据进行实时处理后临时生成的结果。

打开SE11可以看到:

可以看到,它们实际上是ABAP CDS视图。如果不了解ABAP CDS视图的话,可以暂时把它理解成使用SQL语言建立的数据模型,通过使用注解,还能为它赋予其它能力。

推荐通过Eclipse来查看CDS视图的定义,具体操作方式不提。

CDS视图中的数据通常是从透明表中取得的,这里以FAGLFLEXT为例:

观察视图的DDL源,寻找其中的FROM关键字:

@AbapCatalog.sqlViewName: 'FAGLFLEXT' 
@AccessControl.authorizationCheck: #NOT_REQUIRED 
@ObjectModel.usageType.serviceQuality: #X 
@ObjectModel.usageType.sizeCategory: #XL 
@ObjectModel.usageType.dataClass: #MIXED 
@EndUserText.label: 'FAGLFLEXT (Compatibility View) ' 
@AbapCatalog.preserveKey: true 
 
define view v_faglflext_view  
as 
    select key rclnt, key ryear, 
    key objnr00, key objnr01, key objnr02, key objnr03, key objnr04, key objnr05, key objnr06, key objnr07, key objnr08, 
    key drcrk, key rpmax, 
    activ, rmvct, rtcur, runit, awtyp, 
    rldnr, rrcty, rvers, logsys, racct, cost_elem, rbukrs, 
    rcntr, prctr, rfarea, rbusa, kokrs, segment, zzpaytype, zzinvunit, zzempnum, ps_posid, aufnr, kunnr, lifnr,   
//<$VF> 
//<$FIELDS>   
//<$VF> 
    scntr, pprctr, sfarea, sbusa, rassc, psegment, 
    tslvt, tsl01, tsl02, tsl03, tsl04, tsl05, tsl06, tsl07, tsl08, tsl09, tsl10, tsl11, tsl12, tsl13, tsl14, tsl15, tsl16, 
    hslvt, hsl01, hsl02, hsl03, hsl04, hsl05, hsl06, hsl07, hsl08, hsl09, hsl10, hsl11, hsl12, hsl13, hsl14, hsl15, hsl16, 
    kslvt, ksl01, ksl02, ksl03, ksl04, ksl05, ksl06, ksl07, ksl08, ksl09, ksl10, ksl11, ksl12, ksl13, ksl14, ksl15, ksl16, 
    oslvt, osl01, osl02, osl03, osl04, osl05, osl06, osl07, osl08, osl09, osl10, osl11, osl12, osl13, osl14, osl15, osl16, 
    mslvt, msl01, msl02, msl03, msl04, msl05, msl06, msl07, msl08, msl09, msl10, msl11, msl12, msl13, msl14, msl15, msl16, 
    timestamp 
    from faglflext_bck where rrcty = '1' 
union all 
    select 
    key rclnt, 
    key cast(ryear as gjahr) as ryear, 
    key cast(objnr00 as g_objnr) as objnr00, 
    key cast(objnr01 as g_objnr) as objnr01, 
    key cast(objnr02 as g_objnr) as objnr02, 
    key cast(objnr03 as g_objnr) as objnr03, 
    key cast(objnr04 as g_objnr) as objnr04, 
    key cast(objnr05 as g_objnr) as objnr05, 
    key cast(objnr06 as g_objnr) as objnr06, 
    key cast(objnr07 as g_objnr) as objnr07, 
    key cast(objnr08 as g_objnr) as objnr08, 
    key cast(drcrk as shkzg) as drcrk, 
    key cast(rpmax as rpmax) as rpmax, 
    cast(activ as acti1) as activ, 
    cast(rmvct as rmvct) as rmvct, 
    cast(rtcur as rtcur) as rtcur, 
    runit, 
    cast(awtyp as awtyp) as awtyp, 
    cast(rldnr as fagl_rldnr) as rldnr, 
    cast(rrcty as rrcty) as rrcty, 
    cast(rvers as rvers) as rvers, 
    cast(logsys as logsys) as logsys, 
    cast(racct as racct) as racct, 
    cast(cost_elem as kstar) as cost_elem, 
    cast(rbukrs as bukrs) as rbukrs, 
    cast(rcntr as kostl) as rcntr, 
    cast(prctr as prctr) as prctr, 
    cast(rfarea as fkber) as rfarea, 
    cast(rbusa as gsber) as rbusa, 
    cast(kokrs as kokrs) as kokrs, 
    cast(segment as fb_segment) as segment,//<$VF> 
//<$FIELDS> 
//<$VF> 
    cast(scntr as skost) as scntr, 
    cast(pprctr as pprctr) as pprctr, 
    cast(sfarea as sfkber) as sfarea, 
    cast(sbusa as pargb) as sbusa, 
    cast(rassc as rassc) as rassc, 
    cast(psegment as fb_psegment) as psegment, 
    cast(tslvt as tslvt12) as tslvt, 
    cast(tsl01 as tslxx12) as tsl01, 
    cast(tsl02 as tslxx12) as tsl02, 
    cast(tsl03 as tslxx12) as tsl03, 
    cast(tsl04 as tslxx12) as tsl04, 
    cast(tsl05 as tslxx12) as tsl05, 
    cast(tsl06 as tslxx12) as tsl06, 
    cast(tsl07 as tslxx12) as tsl07, 
    cast(tsl08 as tslxx12) as tsl08, 
    cast(tsl09 as tslxx12) as tsl09, 
    cast(tsl10 as tslxx12) as tsl10, 
    cast(tsl11 as tslxx12) as tsl11, 
    cast(tsl12 as tslxx12) as tsl12, 
    cast(tsl13 as tslxx12) as tsl13, 
    cast(tsl14 as tslxx12) as tsl14, 
    cast(tsl15 as tslxx12) as tsl15, 
    cast(tsl16 as tslxx12) as tsl16, 
    cast(hslvt as hslvt12) as hslvt, 
    cast(hsl01 as hslxx12) as hsl01, 
    cast(hsl02 as hslxx12) as hsl02, 
    cast(hsl03 as hslxx12) as hsl03, 
    cast(hsl04 as hslxx12) as hsl04, 
    cast(hsl05 as hslxx12) as hsl05, 
    cast(hsl06 as hslxx12) as hsl06, 
    cast(hsl07 as hslxx12) as hsl07, 
    cast(hsl08 as hslxx12) as hsl08, 
    cast(hsl09 as hslxx12) as hsl09, 
    cast(hsl10 as hslxx12) as hsl10, 
    cast(hsl11 as hslxx12) as hsl11, 
    cast(hsl12 as hslxx12) as hsl12, 
    cast(hsl13 as hslxx12) as hsl13, 
    cast(hsl14 as hslxx12) as hsl14, 
    cast(hsl15 as hslxx12) as hsl15, 
    cast(hsl16 as hslxx12) as hsl16, 
    cast(kslvt as kslvt12) as kslvt, 
    cast(ksl01 as kslxx12) as ksl01, 
    cast(ksl02 as kslxx12) as ksl02, 
    cast(ksl03 as kslxx12) as ksl03, 
    cast(ksl04 as kslxx12) as ksl04, 
    cast(ksl05 as kslxx12) as ksl05, 
    cast(ksl06 as kslxx12) as ksl06, 
    cast(ksl07 as kslxx12) as ksl07, 
    cast(ksl08 as kslxx12) as ksl08, 
    cast(ksl09 as kslxx12) as ksl09, 
    cast(ksl10 as kslxx12) as ksl10, 
    cast(ksl11 as kslxx12) as ksl11, 
    cast(ksl12 as kslxx12) as ksl12, 
    cast(ksl13 as kslxx12) as ksl13, 
    cast(ksl14 as kslxx12) as ksl14, 
    cast(ksl15 as kslxx12) as ksl15, 
    cast(ksl16 as kslxx12) as ksl16, 
    cast(oslvt as oslvt12) as oslvt, 
    cast(osl01 as oslxx12) as osl01, 
    cast(osl02 as oslxx12) as osl02, 
    cast(osl03 as oslxx12) as osl03, 
    cast(osl04 as oslxx12) as osl04, 
    cast(osl05 as oslxx12) as osl05, 
    cast(osl06 as oslxx12) as osl06, 
    cast(osl07 as oslxx12) as osl07, 
    cast(osl08 as oslxx12) as osl08, 
    cast(osl09 as oslxx12) as osl09, 
    cast(osl10 as oslxx12) as osl10, 
    cast(osl11 as oslxx12) as osl11, 
    cast(osl12 as oslxx12) as osl12, 
    cast(osl13 as oslxx12) as osl13, 
    cast(osl14 as oslxx12) as osl14, 
    cast(osl15 as oslxx12) as osl15, 
    cast(osl16 as oslxx12) as osl16, 
    cast(mslvt as mslvt12) as mslvt, 
    cast(msl01 as mslxx12) as msl01, 
    cast(msl02 as mslxx12) as msl02, 
    cast(msl03 as mslxx12) as msl03, 
    cast(msl04 as mslxx12) as msl04, 
    cast(msl05 as mslxx12) as msl05, 
    cast(msl06 as mslxx12) as msl06, 
    cast(msl07 as mslxx12) as msl07, 
    cast(msl08 as mslxx12) as msl08, 
    cast(msl09 as mslxx12) as msl09, 
    cast(msl10 as mslxx12) as msl10, 
    cast(msl11 as mslxx12) as msl11, 
    cast(msl12 as mslxx12) as msl12, 
    cast(msl13 as mslxx12) as msl13, 
    cast(msl14 as mslxx12) as msl14, 
    cast(msl15 as mslxx12) as msl15, 
    cast(msl16 as mslxx12) as msl16, 
    cast(timestamp as timestamp) as timestamp 
    from FGL_FAGLFLEXT

可以发现这个视图结合了2个源。其一是FAGLFLEXT_BCK,查询可知这是旧数据的备份表;另一个是FGL_FAGLFLEXT,

打开FGL_FAGLFLEXT,发现它还是个CDS视图(因为完整定义过长,只截取了部分代码,下同):

@AbapCatalog.sqlViewName: 'FGLV_FAGLFLEXT' 
@AccessControl.authorizationCheck: #NOT_REQUIRED 
@ObjectModel.usageType.serviceQuality: #X 
@ObjectModel.usageType.sizeCategory: #XL 
@ObjectModel.usageType.dataClass: #MIXED 
@EndUserText.label: 'Compatibility view for FAGLFLEXT' 
define view FGL_FAGLFLEXT as select from FGL_GLTT2 
{ 
key rclnt, 
key ryear, 
key cast(objnr00 as abap.int4(10)) as objnr00, 
key cast(objnr01 as abap.int4(10)) as objnr01, 
key cast(objnr02 as abap.int4(10)) as objnr02, 
key cast(objnr03 as abap.int4(10)) as objnr03, 
key cast(objnr04 as abap.int4(10)) as objnr04, 
key cast(objnr05 as abap.int4(10)) as objnr05, 
key cast(objnr06 as abap.int4(10)) as objnr06, 
key cast(objnr07 as abap.int4(10)) as objnr07, 
key cast(objnr08 as abap.int4(10)) as objnr08, 
key drcrk, 
key rpmax, 
activ, 
……………… 
……………… 
@Semantics.amount.currencyCode: 'T882G.CURR1' sum(hslvt) as hslvt, 
@Semantics.amount.currencyCode: 'T882G.CURR1' sum(hsl01) as hsl01, 
@Semantics.amount.currencyCode: 'T882G.CURR1' sum(hsl02) as hsl02, 
@Semantics.amount.currencyCode: 'T882G.CURR1' sum(hsl03) as hsl03, 
@Semantics.amount.currencyCode: 'T882G.CURR1' sum(hsl04) as hsl04, 
@Semantics.amount.currencyCode: 'T882G.CURR1' sum(hsl05) as hsl05, 
@Semantics.amount.currencyCode: 'T882G.CURR1' sum(hsl06) as hsl06, 
@Semantics.amount.currencyCode: 'T882G.CURR1' sum(hsl07) as hsl07, 
@Semantics.amount.currencyCode: 'T882G.CURR1' sum(hsl08) as hsl08, 
@Semantics.amount.currencyCode: 'T882G.CURR1' sum(hsl09) as hsl09, 
@Semantics.amount.currencyCode: 'T882G.CURR1' sum(hsl10) as hsl10, 
@Semantics.amount.currencyCode: 'T882G.CURR1' sum(hsl11) as hsl11, 
@Semantics.amount.currencyCode: 'T882G.CURR1' sum(hsl12) as hsl12, 
@Semantics.amount.currencyCode: 'T882G.CURR1' sum(hsl13) as hsl13, 
@Semantics.amount.currencyCode: 'T882G.CURR1' sum(hsl14) as hsl14, 
@Semantics.amount.currencyCode: 'T882G.CURR1' sum(hsl15) as hsl15, 
@Semantics.amount.currencyCode: 'T882G.CURR1' sum(hsl16) as hsl16, 
……………… 
……………… 
max(timestamp) as timestamp 
} 
group by 
……………… 
………………

可以看到,FGL_FAGLFLEXT中的HSLVT和HSL01, HSL02字段,是对FGL_GLTT2中相应字段的汇总得来的。对应语句:sum(hslvt) as hslvt

打开FGL_GLTT2,会发现该视图中的金额汇总自FGL_GLTT1,所以这里就不贴FGL_GLTT2的定义了,直接看FGL_GLTT1:

@AbapCatalog.sqlViewName: 'FGLV_GLTT1' 
@AccessControl.authorizationCheck: #NOT_REQUIRED 
@ObjectModel.usageType.serviceQuality: #X 
@ObjectModel.usageType.sizeCategory: #XL 
@ObjectModel.usageType.dataClass: #MIXED 
@EndUserText.label: 'G/L totals: create the period block' 
define view FGL_GLTT1 as select from FGL_GLTT0 
{ 
rclnt, 
ryear, 
0 as objnr00, 
0 as objnr01, 
0 as objnr02, 
0 as objnr03, 
0 as objnr04, 
0 as objnr05, 
0 as objnr06, 
0 as objnr07, 
0 as objnr08, 
drcrk, 
 
case poper 
  when '000' then '016' 
  else cast( lpad( cast ( 16 * (1 + div( cast(poper as abap.int4) - 1, 16) ) as abap.char(12) ), 3, '0') as abap.numc(3)) 
end as rpmax, 
    activ, rmvct, rtcur, 
    runit, awtyp, rldnr, rrcty, rvers, logsys, racct, cost_elem, rbukrs, 
    rcntr, prctr, rfarea, rbusa, kokrs, segment, 
    scntr, pprctr, sfarea, sbusa, rassc, psegment,   
//<$VF> 
    fikrs, rfund, rgrant_nbr, rbudget_pd, sfund, sgrant_nbr, sbudget_pd, 
    re_bukrs, re_account, vname, egrup, recid, 
//<$FIELDS> 
//<$VF> 
……………… 
……………… 
case poper  when '000' then hsl else 0 end as hslvt, 
case mod( cast(poper as abap.int4 ), 16) when  1 then hsl else 0 end as hsl01, 
case mod( cast(poper as abap.int4 ), 16) when  2 then hsl else 0 end as hsl02, 
case mod( cast(poper as abap.int4 ), 16) when  3 then hsl else 0 end as hsl03, 
case mod( cast(poper as abap.int4 ), 16) when  4 then hsl else 0 end as hsl04, 
case mod( cast(poper as abap.int4 ), 16) when  5 then hsl else 0 end as hsl05, 
case mod( cast(poper as abap.int4 ), 16) when  6 then hsl else 0 end as hsl06, 
case mod( cast(poper as abap.int4 ), 16) when  7 then hsl else 0 end as hsl07, 
case mod( cast(poper as abap.int4 ), 16) when  8 then hsl else 0 end as hsl08, 
case mod( cast(poper as abap.int4 ), 16) when  9 then hsl else 0 end as hsl09, 
case mod( cast(poper as abap.int4 ), 16) when 10 then hsl else 0 end as hsl10, 
case mod( cast(poper as abap.int4 ), 16) when 11 then hsl else 0 end as hsl11, 
case mod( cast(poper as abap.int4 ), 16) when 12 then hsl else 0 end as hsl12, 
case mod( cast(poper as abap.int4 ), 16) when 13 then hsl else 0 end as hsl13, 
case mod( cast(poper as abap.int4 ), 16) when 14 then hsl else 0 end as hsl14, 
case mod( cast(poper as abap.int4 ), 16) when 15 then hsl else 0 end as hsl15, 
case mod( cast(poper as abap.int4 ), 16) 
  when  0 then 
    case poper 
      when '000' then 0 
      else hsl 
    end 
  else 0 end 
as hsl16, 
……………… 
……………… 
 
timestamp 
}

可以清楚地看到,原来结转余额HSLVT其实就是期间POPER = 000时的金额,HSL01,HSL02就是期间POPER = 001,002时的金额....

对应语句是:case poper when '000' then hsl else 0 end as hslvt,,和case mod( cast(poper as abap.int4 ), 16) when 1 then hsl else 0 end as hsl01

继续找下去可以得知FGL_GLTT1的数据来自于表ACDOCA(Universal Journal Entry Line Items)。也就是说,每次从FAGLFLEXT中查询数据,实际上相当于经过了FGL_GLSI_ACD->FGL_GLSI_ACD->FGL_GLTT1->FGL_GLTT2->FGL_FAGLFLEXT->V_FAGLFLEXT_DDL一系列中间层逻辑处理,其实际的数据源则是表ACDOCA。当然这些视图中可能还包含其它细节..

以上是对FAGLFLEXT的分析,ACDOCT也是差不多的,具体的条件有点差别。

FAGLFLEXT和ACDOCT的存在体现了CDS所具备的强大能力。不过,毕竟它们只是兼容视图,又包含这样多的中间层...按照一般的原则,程序中的中间层越多,性能就越差。因此,在了解到它们中的数据的实际来源后,也许在某些情况下,直接从ACDOCA中通过聚合函数获取期初余额之类的东西会有更好的性能。

按这样的思路改写文初获取1000公司在2018年3月的期初余额的代码,可以写成:

SELECT SUM( hsl ) FROM acdoca WHERE rbukrs = '1000' 
                                AND gjahr  = '2018' 
                                AND poper  < '003' 
  INTO @DATA(l_balance).

是不是简单了很多呢?

最后,再次重申,本文只是从技术角度介绍下我对ACDOCT,FAGLFLEXT的了解。在实际业务中,这两个视图中的数据可能和分类账、凭证状态之类的东西有关...其中的数据与ACDOCA中的汇总数据并不一定在任何情况下都完全相等。

示例代码:

从ACDOCA中获取公司代码1000,分类账0L,科目1122020000的期初余额、本期发生额、期末余额、本年累计发生额:

REPORT ztest_balance. 
 
DATA: p_gjahr TYPE acdoca-gjahr VALUE '2018', 
      p_poper TYPE acdoca-poper VALUE '002'. 
 
DATA: c_racct TYPE racct VALUE '1122020000'. 
 
WITH +itab AS ( 
  SELECT 
     CASE WHEN poper < @p_poper AND gjahr = @p_gjahr THEN hsl 
                                                     ELSE 0 
     END AS opening_balance, 
     CASE WHEN poper = @p_poper AND gjahr = @p_gjahr THEN hsl 
                                                     ELSE 0 
     END AS current_balance, 
     CASE WHEN poper <= @p_poper AND gjahr = @p_gjahr THEN hsl 
                                                      ELSE 0 
     END AS ending_balance, 
     CASE WHEN poper > '000'    AND poper <= '012' AND gjahr = @p_gjahr THEN hsl 
                                                                        ELSE 0 
     END AS current_year_balance, 
     racct 
     FROM acdoca 
     WHERE racct  = @c_racct
    AND gjahr = @p_gjahr
AND rbukrs = '1000' AND rldnr = '0L' ) SELECT racct, SUM( opening_balance ) AS opening_balance, SUM( current_balance ) AS current_balance, SUM( ending_balance ) AS ending_balance, SUM( current_year_balance ) AS current_year_balance FROM +itab GROUP BY racct INTO TABLE @DATA(lt_balance).

评论关闭
IT干货网

微信公众号号:IT虾米 (左侧二维码扫一扫)欢迎添加!

SAP SD 常用配置事物代码