I_SALESDOCUMENTSCHEDULELINE と VA03 の Schedule Line が合わない件

VA03 と値が合わない(数時間溶かしたやつ)

S/4HANA で受注の Schedule Line 単位の納入済数量を取りたくて、最初はこのあたりを見ていました。

  • I_SALESDOCUMENTSCHEDULELINE-DeliveryQuantity
  • VBEP-DLVQTY_SU / VBEP-DLVQTY_BU

ところが、VA03 を開いて明細の Schedule Line タブの Delivered Quantity と突き合わせると、合いません。テーブル直で見ているのに何で?というやつです。

結論だけ先に言いますと、VA03 は Schedule Line の Delivered Quantity を画面表示する直前に再計算しています。DB の値そのままを出しているのではありません。なので VBEP-DLVQTY_SUI_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)

ただこの後ろでさらに VSMNGDLVQTY_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 に入っている前提で進めます。少なくとも以下のフィールドを持つこと。

  • vbeln posnr etenr — 受注 + 明細 + 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 と数量が一致します。

コメントする