Deep Insertとは
Deep Insertとは、ODataの更新方法の一種です。
ODataの更新には他にUPDATEメソッドがありますが、UPDATEはEntity Setごとに実行されます。受注などはヘッダと明細がセットなのでもし失敗したときにどちらか一方だけが登録されることは避けたいです。
Deep Insertは受注のように関連する複数のデータを一度に更新するための方法です。
今回の記事では、Deep Insertを使ってデータを更新するFioriアプリケーションを実装します。ボリュームが大きいので3回くらいに分ける予定です。
Deep Insertを使った実装
受注のヘッダと明細を同時に登録するアプリケーションを作ってみます。
ODataの練習用に用意されている以下のテーブル、BAPIを利用します。
テーブル
SNWD_SO:受注ヘッダ
SNWD_SO_I:受注明細
BAPI
BAPI_EPM_SO_CREATE:受注登録用のBAPI
全体のステップ
- ベースになるODataサービスの登録(今回)
- Deep Insertのための処理実装
- Fioriアプリケーションの登録
ベースになるODataサービスの実装
GETメソッドでデータを取得できるところまで作ります。
ステップ
- Entity Setの登録
- Associationの登録
- ODataサービスの登録
- データ取得メソッドの実装
- ODataサービスのテスト
Entity Setの登録
新規のプロジェクトを作成し、SalesOrderとSalesOrderItemを登録します。
型は受注登録BAPIで使われる以下の構造をそのまま使っています。
BAPI_EPM_SO_HEADER、BAPI_EPM_SO_ITEM
データを登録できるように、各項目のCreatable属性にチェックを入れます。(今回更新はしませんが、一応Updatable属性にもチェックを入れました)
SalesOrder
SalesOrderItem
Associationの登録
ヘッダと明細の関連を登録します。
ヘッダに対して1つ以上の明細がつくので、Cardinalityは1...nとします。
ODataサービスの登録
ボタンを押して、MPC, DPCクラスを生成します。
サービスを登録します。
この環境はバックエンドとゲートウェイを兼ねているので、システムエイリアスにLOCALを指定します。(ゲートウェイの導入シナリオについてはこちらの記事を参照)
データ取得メソッドの実装
Entity(単数)、Entity Set(複数)の取得用メソッドを実装します。
DPC_EXTとついたクラスをダブルクリックします。
ヘッダを1件取得する(GET_ENTITY)
METHOD salesorderset_get_entity.
DATA: lv_so_id TYPE bapi_epm_so_id,
ls_headerdata TYPE bapi_epm_so_header,
lt_return TYPE TABLE OF bapiret2.
"URIで指定された受注番号を取得
READ TABLE it_key_tab INTO DATA(ls_key) WITH KEY name = 'SoId'.
"内部書式に変換する(例:500000000 → 0500000000)
CALL FUNCTION 'CONVERSION_EXIT_ALPHA_INPUT'
EXPORTING
input = ls_key-value
IMPORTING
output = lv_so_id.
"1件の受注を取得する
CALL FUNCTION 'BAPI_EPM_SO_GET_DETAIL'
EXPORTING
so_id = lv_so_id
IMPORTING
headerdata = ls_headerdata
TABLES
return = lt_return.
"エラーの場合、例外を発生させてメッセージを返す
IF lt_return IS NOT INITIAL.
me->mo_context->get_message_container( )->add_messages_from_bapi(
EXPORTING
it_bapi_messages = lt_return " Return parameter table
).
RAISE EXCEPTION TYPE /iwbep/cx_mgw_busi_exception
EXPORTING
message_container = me->mo_context->get_message_container( ).
ENDIF.
"出力用の構造にデータをセット
MOVE-CORRESPONDING ls_headerdata TO er_entity.
ENDMETHOD.
ヘッダを複数件取得(GET_ENTITYSET)
METHOD salesorderset_get_entityset.
DATA: ls_row TYPE bapi_epm_max_rows,
lt_header TYPE TABLE OF bapi_epm_so_header,
lt_return TYPE TABLE OF bapiret2,
ls_entity LIKE LINE OF et_entityset.
"bapimaxrow = top(表示件数) + skip(読み飛ばす件数)
"例:top:10, skip5 → 6件目以降のデータを10個表示
ls_row-bapimaxrow = is_paging-top + is_paging-skip.
CALL FUNCTION 'BAPI_EPM_SO_GET_LIST'
EXPORTING
max_rows = ls_row
TABLES
soheaderdata = lt_header
return = lt_return.
"エラーの場合、例外を発生させてメッセージを返す
IF lt_return IS NOT INITIAL.
me->mo_context->get_message_container( )->add_messages_from_bapi(
EXPORTING
it_bapi_messages = lt_return " Return parameter table
).
RAISE EXCEPTION TYPE /iwbep/cx_mgw_busi_exception
EXPORTING
message_container = me->mo_context->get_message_container( ).
ENDIF.
"top, skipを考慮する場合
IF is_paging-skip IS NOT INITIAL.
DATA(lv_start) = is_paging-skip + 1. " 読み込み開始行
DATA(lv_end) = ls_row-bapimaxrow. " 読み込み終了行
LOOP AT lt_header INTO DATA(ls_header) FROM lv_start TO lv_end.
MOVE-CORRESPONDING ls_header TO ls_entity.
APPEND ls_entity TO et_entityset.
ENDLOOP.
ELSE.
"top, skipを考慮しない場合
LOOP AT lt_header INTO ls_header.
MOVE-CORRESPONDING ls_header TO ls_entity.
APPEND ls_entity TO et_entityset.
ENDLOOP.
ENDIF.
ENDMETHOD.
受注データは大量になるので、指定した件数だけ取得できるようtop, skipのクエリオプションに対応した作りにしておきます。(top, skipについてはこちら)
明細を取得(GET_ENTITYSET)
今回はヘッダにひもづく複数件の明細を取得するという使い方なので、GET_ENTITYSETメソッドのみ実装します。
METHOD salesorderitemse_get_entityset.
DATA: lv_so_id TYPE bapi_epm_so_id,
lt_item TYPE TABLE OF bapi_epm_so_item,
lt_return TYPE TABLE OF bapiret2.
"受注番号を取得
READ TABLE it_key_tab INTO DATA(ls_key) WITH KEY name = 'SoId'.
"内部書式に変換
CALL FUNCTION 'CONVERSION_EXIT_ALPHA_INPUT'
EXPORTING
input = ls_key-value
IMPORTING
OUTPUT = lv_so_id
.
"明細を取得
CALL FUNCTION 'BAPI_EPM_SO_GET_DETAIL'
EXPORTING
so_id = lv_so_id
* IMPORTING
* HEADERDATA =
TABLES
itemdata = lt_item
return = lt_return.
"エラーの場合、例外を発生させてメッセージを返す
IF lt_return IS NOT INITIAL.
me->mo_context->get_message_container( )->add_messages_from_bapi(
EXPORTING
it_bapi_messages = lt_return " Return parameter table
).
RAISE EXCEPTION TYPE /iwbep/cx_mgw_busi_exception
EXPORTING
message_container = me->mo_context->get_message_container( ).
ENDIF.
"出力用のテーブルにデータをセット
MOVE-CORRESPONDING lt_item TO et_entityset.
ENDMETHOD.
ODataサービスのテスト
ここまでの実装が終わったら、サービスをテストしてみましょう。
トランザクション:/IWFND/GW_CLIENTを起動します。
①受注ヘッダを取得
Request URIに以下のように入力します。先頭3件の受注を取得するという意味です。
/sap/opu/odata/SAP/<サービス名>/SalesOrderSet?$top=3&$format=json
3件の受注が返ってくれば成功です。
②受注明細を取得
結果を下のほうにするスクロールすると、"ToSalesOrderItem"という項目があります。これはナビゲーションプロパティです。ここに書かれたuriを指定すると明細に遷移できるこいうことを教えてくれています。
Request URIに以下のように入力します。
/sap/opu/odata/SAP/<サービス名>/SalesOrderSet('500000001')/ToSalesOrderItem?$format=json
受注にひもづく複数件の明細が取得できれば成功です。
③ヘッダと明細をセットで取得
最後に、ヘッダと明細を並べて取得します。並べて取得するためにはクエリオプションexpandを使います。
Request URIに以下のように入力します。
/sap/opu/odata/SAP/<サービス名>/SalesOrderSet('500000001')?$expand=ToSalesOrderItem&$format=json
この形がDeepInsertの元になります。(そのままではありませんが)
次回はDeep Insertのためのメソッドを実装します。