2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

RAPのアクションから更新系汎用モジュールを呼びたいとき

Last updated at Posted at 2025-04-22

はじめに

ABAP Restful Application Programming Model (RAP)のアクションから、更新系の汎用モジュールを呼び出したい場面があります。

しかし、単純にアクションのイベントハンドラから汎用モジュールを呼び出すと、このフェーズで更新が許可されていないため"BEHAVIOR_ILLEGAL_STATEMENT"という実行時エラーになります。

CALL FUNCTION DESTINATION 'NONE'は?

SAP Communityなどでは、そういった場合にCALL FUNCTION DESTINATION 'NONE'が使えるという情報を目にします。

ただ、BTP ABAP Environmentで試してみると"RFC_ILLEGAL_CALL_ON_SAPCP"という実行時エラーになりました。以下のブログのスレッドでは、「ABAP CloudではRFC宛先はシステム間の連携のみで許可され、DESTINATION 'NONE'は使えない」とありました。

よって、CALL FUNCTION DESTINATION 'NONE'はオンプレミス(言語バージョン:Standard ABAP)でのみ使える方法のようです。

ABAP Cloudではどうするか

クラウド、オンプレ、言語バージョンを問わず使える方法とはどのようなものでしょうか。私が知っている方法は以下のようなものです。

  1. アクションの中で、エンティティに「更新が必要である」ことを示すフラグを設定する
  2. save_modifiedメソッドで更新系汎用モジュールを呼ぶ

この方法はSAPのAndre Fisher氏が作成した以下のリポジトリでも使用されています(汎用モジュールの呼び出しではなく、BGPFの実行ですが)。

※save_modifiedメソッドを利用するにはUnmanaged SaveまたはAdditional Saveを使用します。CRUD処理をフレームワークに任せる場合はAdditional Saveを使用してください。

サンプルシナリオ

本のオーダーと在庫を管理するアプリケーションを作成します。
オーダーを入力した後、"sendOrder"というアクションを実行すると本の在庫が更新されます。

image.png

以下では、「アクションから更新系汎用モジュールを呼び出す」という箇所に絞って実装を紹介します。

CDS View

UpdateRequiredという項目が、save_modifiedに更新があることを伝える役割をします。この項目はDBに保持しません。

@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Book Order'
@Metadata.ignorePropagatedAnnotations: true
define root view entity ZI_BookOrder as select from zbook_order
{
    key order_id as OrderId,
    book_id as BookId,
    book_title as BookTitle,
    quantity as Quantity,
    order_date as OrderDate,
    creation_date as CreationDate,
    abap.char'' as UpdateRequired
}

Behavior Definition

ここでのポイントは、with unmanaged saveとすることでsave_modifiedメソッドを実装できるようにすることです。

※今回は登録処理も汎用モジュールで実施したためUnmanaged Saveを使用していますが、CRUD処理をフレームワークに任せる場合はwith additional saveとしてください。

managed implementation in class zbp_i_bookorder unique;
strict ( 2 );
with draft;

define behavior for ZI_BookOrder //alias <alias_name>
lock master
total etag CreationDate
authorization master ( instance )
with unmanaged save
late numbering
draft table zbook_order_d
//etag master <field_name>
{
  create;
  update;
  delete;

  draft action Edit;
  draft action Activate optimized;
  draft action Discard;
  draft action Resume;
  draft determine action Prepare;

  action sendOrder;

  field ( readonly )
    OrderId,
    CreationDate,
    BookTitle;
}

Behavior Implementation

アクションの実装

オーダー送信のアクションでは、はじめに在庫更新の汎用モジュールを呼んで在庫のチェックをします。save_modifiedメソッドではエラーを返せない(※)ため、アクションで事前チェックをしています。

※Saverクラスの継承元をデフォルトのcl_abap_behavior_saverからcl_abap_behavior_saver_failedに変えるとエラーを返すことができます(以下のブログ参照)。
https://community.sap.com/t5/technology-blogs-by-sap/exposing-bapi-as-odata-api-using-rap-facade/ba-p/13571926

  METHOD sendOrder.
    DATA modify_order TYPE TABLE FOR UPDATE ZI_BookOrder.

    READ ENTITIES OF ZI_BookOrder IN LOCAL MODE
      ENTITY ZI_BookOrder
      FIELDS ( BookId Quantity )
      WITH CORRESPONDING #( keys )
      RESULT DATA(orders).

    " check stock
    LOOP AT orders INTO DATA(order).
      CALL FUNCTION 'Z_F_UPDATE_BOOKSTOCK'
        EXPORTING
          i_book_id          = order-BookId
          i_quantity         = order-Quantity
          i_check_only       = abap_true
        EXCEPTIONS
          insufficient_stock = 1
          OTHERS             = 2.
      IF sy-subrc <> 0.
        APPEND VALUE #( %tky = order-%tky ) TO failed-zi_bookorder.
        APPEND VALUE #( %tky = order-%tky
                                           %msg = new_message(
                                                    id       = 'ZBOOK_ORDER'
                                                    number   = 001
                                                    severity = if_abap_behv_message=>severity-error
                                                    v1       = order-BookId
                                                  ) ) TO reported-zi_bookorder.
      ELSE.
        APPEND VALUE #( %tky = order-%tky
                        UpdateRequired = abap_true ) TO modify_order.

        APPEND VALUE #( %tky = order-%tky
                                           %msg = new_message(
                                                    id       = 'ZBOOK_ORDER'
                                                    number   = 002
                                                    severity = if_abap_behv_message=>severity-success
                                                    v1       = order-OrderId
                                                  ) ) TO reported-zi_bookorder.
      ENDIF.
    ENDLOOP.

    "notify the save_modified method that update is required
    MODIFY ENTITIES OF ZI_BookOrder IN LOCAL MODE
      ENTITY ZI_BookOrder
      UPDATE FIELDS ( UpdateRequired )
      WITH modify_order.

  ENDMETHOD.

save_modifiedの実装

アクションの中でUpdateRequiredの項目を更新したため、save_modifiedメソッドのupdateパラメータに更新したレコードが入ってきます。UpdateRequiredにフラグが立っていれば在庫更新の汎用モジュールを呼び出します。

  METHOD save_modified.
      " get book id
      READ ENTITIES OF ZI_BookOrder IN LOCAL MODE
        ENTITY ZI_BookOrder
        FIELDS ( BookId UpdateRequired )
        WITH VALUE #( for updated in update-zi_bookorder
                      ( %key = updated-%key ) )
        RESULT DATA(orders).

    LOOP AT orders INTO DATA(order).
      IF order-UpdateRequired = abap_true.
        CALL FUNCTION 'Z_F_UPDATE_BOOKSTOCK'
          EXPORTING
            i_book_id          = order-BookId
            i_quantity         = order-Quantity
            i_check_only       = abap_false
          EXCEPTIONS
            insufficient_stock = 1
            OTHERS             = 2.
        IF sy-subrc <> 0.
          "error should not happen at this point
        ENDIF.
      ENDIF.
    ENDLOOP.
  ENDMETHOD.

在庫更新の汎用モジュール

以下が在庫更新の汎用モジュールの定義です。重要なことは、この中でコミットしないことです。コミットはRAPのフレームワークが行います。本来はテーブルロックをおこなうべきですが、ここでは省略しています。

FUNCTION z_f_update_bookstock
  IMPORTING
    VALUE(i_book_id) TYPE int4
    VALUE(i_quantity) TYPE int4
    VALUE(i_check_only) TYPE abap_boolean OPTIONAL
  EXCEPTIONS
    insufficient_stock.



  "Check stock
  "In a real life scenario, table lock is required
  SELECT SINGLE stock FROM zbook_stock
   WHERE book_id = @i_book_id
   INTO @DATA(current_stock).

  IF current_stock < i_quantity.
    RAISE insufficient_stock.
  ENDIF.

  IF i_check_only = abap_true.
    RETURN.
  ENDIF.

  DATA(new_stock) = current_stock - i_quantity.
  DATA(updated_at) = cl_abap_context_info=>get_system_date( ).

  UPDATE zbook_stock
    SET stock = @new_stock,
        updated_at = @updated_at
    WHERE book_id = @i_book_id.

ENDFUNCTION.

動作確認

Bookd ID"1"の在庫は98です。
image.png

"Send Order"アクションを実行します。
image.png
image.png

在庫が98になりました。
image.png

おわりに

このパターンは汎用モジュールの呼び出しに限らず、アクションから以下のような処理を行いたいときに使うことができます。

  • DB更新
  • メール送信
  • リモートサービスへの接続
  • BGPFの呼び出し
  • アプリケーションジョブのスケジュール
2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?