VA03 と値が合わない(数時間溶かしたやつ)
S/4HANA で受注の Schedule Line 単位の納入済数量を取りたくて、最初はこのあたりを見ていました。
I_SALESDOCUMENTSCHEDULELINE-DeliveryQuantityVBEP-DLVQTY_SU/VBEP-DLVQTY_BU
ところが、VA03 を開いて明細の Schedule Line タブの Delivered Quantity と突き合わせると、合いません。テーブル直で見ているのに何で?というやつです。
結論だけ先に言いますと、VA03 は Schedule Line の Delivered Quantity を画面表示する直前に再計算しています。DB の値そのままを出しているのではありません。なので VBEP-DLVQTY_SU も I_SALESDOCUMENTSCHEDULELINE も VA03 と一致しないことがあります。
ちなみに最初は RV_SCHEDULE_CHECK_DELIVERIES で取れるかと思って試しましたが、これも VA03 とは合いませんでした。ECC 時代は明細全体の納入済を Schedule Line に動的配賦する考え方だったのでまだしも、S/4HANA でも一致しないので別ルートが必要です。
VA03 の中で何が起きているか
VA03 を起動して XVBEP-VSMNG の変更箇所を追っていくと、まずここで DB の VBEP-DLVQTY_SU から VSMNG を組み立てています。
Program : SAPFV45E
Include : FV45EFEP_XVBEP_AUFBAUEN
FORM : XVBEP_AUFBAUENCode language: PHP (php)
ただこの後ろでさらに VSMNG と DLVQTY_SU が書き換わります。本命はこちらです。
Program : SAPFV45E
Include : FV45EF0V_VBEP_ODQ_DETERMINE
FORM : VBEP_ODQ_DETERMINECode language: PHP (php)
中で呼ばれているのが CL_SDOC_PROCESS_UTIL=>GET_INSTANCE( )->SET_SLINE_DLVQTY。これが再計算している本体です。
DATA(lo_sdoc_process) = cl_sdoc_process_util=>get_instance( ).
lo_sdoc_process->set_sline_dlvqty(
EXPORTING
iv_vbeln = us_xvbap-vbeln
iv_posnr = us_xvbap-posnr
iv_lfgsa = xvbup-lfgsa
iv_kzfme = us_xvbap-kzfme
iv_umvkz = us_xvbap-umvkz
iv_umvkn = us_xvbap-umvkn
iv_fixmg = us_xvbap-fixmg
iv_netwr = us_xvbap-netwr
iv_kwmeng = us_xvbap-kwmeng
iv_kbmeng = us_xvbap-kbmeng
it_xvbfa = xvbfa[]
iv_set_update_indicator = abap_true
IMPORTING
ev_changed = lv_changed
CHANGING
ct_xvbep = lt_xvbep[] ).Code language: JavaScript (javascript)
戻ってきた値を XVBEP に書き戻しています。
xvbep-vsmng = <ls_xvbep>-vsmng.
xvbep-olfmng = <ls_xvbep>-olfmng.
xvbep-dlvqty_bu = <ls_xvbep>-dlvqty_bu.
xvbep-dlvqty_su = <ls_xvbep>-dlvqty_su.
xvbep-ocdqty_bu = <ls_xvbep>-ocdqty_bu.
xvbep-ocdqty_su = <ls_xvbep>-ocdqty_su.
xvbep-ordqty_bu = <ls_xvbep>-ordqty_bu.
xvbep-ordqty_su = <ls_xvbep>-ordqty_su.
MODIFY xvbep INDEX <ls_xvbep>-tabix.Code language: HTML, XML (xml)
つまり VA03 と同じ値を取りたければ、自分でも同じ SET_SLINE_DLVQTY を呼ぶしかありません。標準内部ロジックなのでリリース済み API としての保証はないのですが、現状この経路を通さないと VA03 と数量が揃いません。
自前で再計算する
ここからは、VBAP / VBEP の SELECT 結果がすでに lt_so_details に入っている前提で進めます。少なくとも以下のフィールドを持つこと。
vbelnposnretenr— 受注 + 明細 + Schedule Line のキーdlvqty_su— ここを VA03 相当の値で上書きする
SELECT のときに気をつけることが一つあります。vbep~dlvqty_su IS NOT INITIAL のような条件を JOIN や WHERE に入れて事前に絞り込まないこと。DB 上は初期値でも再計算後に数量が出る Schedule Line を取りこぼします。絞り込みは再計算後の DELETE でやります。
再計算本体です。VBAP / VBEP / VBFA をまとめて取って、明細単位で SET_SLINE_DLVQTY を回します。ループ内 SELECT は避けたいので、必要なデータは事前にまとめて取得しておきます。
TYPES: BEGIN OF ty_so_item_key,
vbeln TYPE vbap-vbeln,
posnr TYPE vbap-posnr,
END OF ty_so_item_key.
TYPES: BEGIN OF ty_dlvqty_result,
vbeln TYPE vbep-vbeln,
posnr TYPE vbep-posnr,
etenr TYPE vbep-etenr,
dlvqty_su TYPE vbep-dlvqty_su,
END OF ty_dlvqty_result.
DATA:
lt_item_keys TYPE STANDARD TABLE OF ty_so_item_key,
lt_vbeln_keys TYPE STANDARD TABLE OF vbak-vbeln,
lt_vbap_full TYPE STANDARD TABLE OF vbapvb,
lt_vbep_full TYPE STANDARD TABLE OF vbepvb,
lt_vbfa_full TYPE STANDARD TABLE OF vbfavb,
lt_xvbep TYPE STANDARD TABLE OF vbepvb,
lt_dlvqty_result TYPE HASHED TABLE OF ty_dlvqty_result
WITH UNIQUE KEY vbeln posnr etenr.
DATA:
ls_vbap_full TYPE vbapvb,
lv_changed TYPE abap_bool.
FIELD-SYMBOLS:
<ls_so_detail> LIKE LINE OF lt_so_details,
<ls_vbep> TYPE vbepvb,
<ls_result> TYPE ty_dlvqty_result.
CHECK lt_so_details IS NOT INITIAL.
" 一意な (VBELN, POSNR) のキー集合を作る
LOOP AT lt_so_details ASSIGNING <ls_so_detail>
WHERE vbeln IS NOT INITIAL
AND posnr IS NOT INITIAL.
APPEND VALUE ty_so_item_key(
vbeln = <ls_so_detail>-vbeln
posnr = <ls_so_detail>-posnr ) TO lt_item_keys.
ENDLOOP.
SORT lt_item_keys BY vbeln posnr.
DELETE ADJACENT DUPLICATES FROM lt_item_keys COMPARING vbeln posnr.
CHECK lt_item_keys IS NOT INITIAL.
" SET_SLINE_DLVQTY が必要とする項目を VBAP / VBEP からまとめて取る
SELECT *
FROM vbap
INTO CORRESPONDING FIELDS OF TABLE @lt_vbap_full
FOR ALL ENTRIES IN @lt_item_keys
WHERE vbeln = @lt_item_keys-vbeln
AND posnr = @lt_item_keys-posnr.
SELECT *
FROM vbep
INTO CORRESPONDING FIELDS OF TABLE @lt_vbep_full
FOR ALL ENTRIES IN @lt_item_keys
WHERE vbeln = @lt_item_keys-vbeln
AND posnr = @lt_item_keys-posnr.
" Document Flow は受注単位で取る
lt_vbeln_keys = VALUE #( FOR k IN lt_item_keys ( k-vbeln ) ).
SORT lt_vbeln_keys.
DELETE ADJACENT DUPLICATES FROM lt_vbeln_keys.
IF lt_vbeln_keys IS NOT INITIAL.
SELECT *
FROM vbfa
INTO CORRESPONDING FIELDS OF TABLE @lt_vbfa_full
FOR ALL ENTRIES IN @lt_vbeln_keys
WHERE vbelv = @lt_vbeln_keys-table_line.
ENDIF.
SORT lt_vbap_full BY vbeln posnr.
SORT lt_vbep_full BY vbeln posnr etenr.
SORT lt_vbfa_full BY vbelv posnv vbeln posnn.
DATA(lo_sdoc_process) = cl_sdoc_process_util=>get_instance( ).
LOOP AT lt_item_keys INTO DATA(ls_key).
READ TABLE lt_vbap_full INTO ls_vbap_full
WITH KEY vbeln = ls_key-vbeln
posnr = ls_key-posnr
BINARY SEARCH.
CHECK sy-subrc = 0.
CLEAR lt_xvbep.
LOOP AT lt_vbep_full ASSIGNING <ls_vbep>
WHERE vbeln = ls_key-vbeln
AND posnr = ls_key-posnr.
APPEND <ls_vbep> TO lt_xvbep.
ENDLOOP.
CHECK lt_xvbep IS NOT INITIAL.
" 累積データがバッファに無ければ Document Flow を渡してやる
IF lo_sdoc_process->get_cum_item_data(
iv_vbeln = ls_key-vbeln
iv_posnr = ls_key-posnr ) IS INITIAL.
lo_sdoc_process->set_sline_dlvqty(
EXPORTING
iv_vbeln = ls_key-vbeln
iv_posnr = ls_key-posnr
iv_lfgsa = ls_vbap_full-lfgsa
iv_kzfme = ls_vbap_full-kzfme
iv_umvkz = ls_vbap_full-umvkz
iv_umvkn = ls_vbap_full-umvkn
iv_fixmg = ls_vbap_full-fixmg
iv_netwr = ls_vbap_full-netwr
iv_kwmeng = ls_vbap_full-kwmeng
iv_kbmeng = ls_vbap_full-kbmeng
it_xvbfa = lt_vbfa_full
iv_set_update_indicator = abap_false
IMPORTING
ev_changed = lv_changed
CHANGING
ct_xvbep = lt_xvbep ).
ELSE.
lo_sdoc_process->set_sline_dlvqty(
EXPORTING
iv_vbeln = ls_key-vbeln
iv_posnr = ls_key-posnr
iv_lfgsa = ls_vbap_full-lfgsa
iv_kzfme = ls_vbap_full-kzfme
iv_umvkz = ls_vbap_full-umvkz
iv_umvkn = ls_vbap_full-umvkn
iv_fixmg = ls_vbap_full-fixmg
iv_netwr = ls_vbap_full-netwr
iv_kwmeng = ls_vbap_full-kwmeng
iv_kbmeng = ls_vbap_full-kbmeng
iv_set_update_indicator = abap_false
IMPORTING
ev_changed = lv_changed
CHANGING
ct_xvbep = lt_xvbep ).
ENDIF.
LOOP AT lt_xvbep INTO DATA(ls_xvbep).
INSERT VALUE ty_dlvqty_result(
vbeln = ls_xvbep-vbeln
posnr = ls_xvbep-posnr
etenr = ls_xvbep-etenr
dlvqty_su = ls_xvbep-dlvqty_su )
INTO TABLE lt_dlvqty_result.
ENDLOOP.
ENDLOOP.
" 元の結果テーブルに再計算値を反映
LOOP AT lt_so_details ASSIGNING <ls_so_detail>.
READ TABLE lt_dlvqty_result ASSIGNING <ls_result>
WITH TABLE KEY vbeln = <ls_so_detail>-vbeln
posnr = <ls_so_detail>-posnr
etenr = <ls_so_detail>-etenr.
IF sy-subrc = 0.
<ls_so_detail>-dlvqty_su = <ls_result>-dlvqty_su.
ELSE.
CLEAR <ls_so_detail>-dlvqty_su.
ENDIF.
ENDLOOP.
" ここでようやく初期値を落とす
DELETE lt_so_details WHERE dlvqty_su IS INITIAL.Code language: HTML, XML (xml)
ちなみに iv_lfgsa に本来渡すべきは VBUP-LFGSA ですが、データによっては VBUP が取れないことがあります。今回は VBAP-LFGSA で代用しました。S/4HANA はステータス系項目が VBAP に統合されているケースがあるのでこれで動きます。
これで VA03 の Schedule Line と数量が一致します。