「だっふんだ 」と言うとSAP S/4HANAで購買依頼伝票を打てるiOSアプリを実装する(2/2)

お久しぶりです、Mona Losacoです。

[「ドリフ大爆笑」大流行の影響で「ドリフ大爆笑DVD」品切れの危機に瀕しているとある雑貨店の麹町一丁目店を救うべく](https://qiita.com/monamona/items/c55435580eaad482c7f7)開発に着手した本アプリですが、うかうかしているうちにクリスマスもお正月も過ぎ去ってしまいました……。

アプリの開発が間に合わなかったために麹町一丁目店はクリスマスの品切れクレーム対応に苦しんだという風の噂も聞かれますが、「ドリフ大爆笑」人気熱の冷めやらぬ中、来たるバレンタインには大切な人に「ドリフ大爆笑DVD」を贈るのが界隈で密かに流行するとも囁かれています。

というわけで、「だっふんだ」という音声コマンドによってSAP　S/4HANAの実機で品目「ドリフ大爆笑DVD」の購買依頼が登録できるiOSモバイルアプリを、諦めずに完成させたいと思います。

（※ この記事は [SAP Advent Calendar 2018](https://adventar.org/calendars/3262) の12月12日分として執筆した記事の続編です）


![dafunda01.png](https://qiita-image-store.s3.amazonaws.com/0/320955/7c0fbeaa-37ae-421c-92d1-32de31d7f5af.png)
![dafunda02.png](https://qiita-image-store.s3.amazonaws.com/0/320955/cd4f0262-3a45-2177-ca20-ea92b975448c.png)


# 構築手順
大きく下記の手順で構築していきます。
1. XcodeでフロントエンドUIおよび音声認識処理を実装
2. バックエンド（SAP S/4HANA）の環境整備をした上で、SDK for iOS Assistantでバックエンドと連携するXcodeプロジェクトを自動生成
3. 2で生成したXcodeプロジェクトに1の実装をマージする

今回は手順2および手順3について紹介していきます。
手順1については[前回の記事](https://qiita.com/monamona/items/c55435580eaad482c7f7)をご参考ください。

前回の記事では、AppleのSpeechフレームワークを用いて、音声が「だっふんだ」と認識された場合にメソッドdafundaRecognized()が呼び出され、ドリフのオチの音が再生されるようなプログラムを作りました。
今回は、メソッドdafundaRecognized()がさらにバックエンドの購買依頼登録メソッドを呼び出すように、バックエンド環境を整備し、フロントエンドを改修していきます。


# 環境
- フロントエンド環境　iPhone8／iOS12.1
- フロントエンド開発環境　Xcode 10.1
- バックエンド環境　SAP　S/4HANA環境（オンプレミス）　※　SCPとSAP HANA Cloud Connector(SCC)で連携されていること。
- クラウド環境　SAP Cloud Platform（NEO）


# 品目「ドリフ大爆笑DVD」を登録する
バックエンド（SAP S/4HANA）の環境整備として、まずなにはともあれ品目「ドリフ大爆笑DVD」が存在しなくてはいけません。
Transaction Code MM01から新規品目を登録し、品目テキストを「ドリフ大爆笑DVD」としておきましょう。

![dvd.png](https://qiita-image-store.s3.amazonaws.com/0/320955/f1cc29e0-0550-a3a4-5eb3-a69160e7391c.png)

品目コードが自動生成されます（今回は300280）。
この値をフロントエンドからバックエンドに渡してやることで、「ドリフ大爆笑DVD」の購買依頼を登録することができます。


# 購買依頼を登録するODataを実装し、Xcodeプロジェクトを生成する

フロントエンドから購買依頼登録メソッドが呼び出された際に実行されるOdata（ABAPプログラム）を実装します。

Odataの詳しい実装方法については[こちらの記事](https://qiita.com/zukahira/items/c97f49f211a70dc1b555#1-s4hana%E3%81%AE%E3%83%87%E3%83%BC%E3%82%BF%E5%8F%96%E5%BE%97%E5%87%A6%E7%90%86%E3%82%92%E5%AE%9F%E8%A3%85%E3%81%99%E3%82%8B)をご参照ください。

今回は、プロジェクト名を`YIOSBLOG02`、Entity Typeを`PurchaseReq`としたうえで、Propertyを下記のように登録しました。

| Name    | Key  |EdmCoreType |Prec |Scale|Max  |Label           |ABAP Field|
|:--------|:-----|:-----------|:----|:----|:----|:---------------|:---------|
| Matnr   |X     |Edm.String  |0    |0    |40   |Material        |MATNR     |
| Menge   |      |Edm.Decimal |13   |0    |0    |Quantity        |MENGE     |
| Meins   |      |Edm.String  |0    |0    |3    |Unit of Measure |MEINS     |

Matnrが品目コード、Mengeが数量、Meinsが数量単位のプロパティです。

上掲の記事はデータ取得のためGetEntitySetメソッドを作成していますが、今回は購買依頼登録のためCreateEntityメソッドをABAPで実装します。

ABAPプログラムは下記のように実装します。
フロントからの連携値を購買依頼登録用の汎用モジュール　BAPI_PR_CREATE　に渡すことで購買依頼を作成するものです。（なお、品目の設定次第で汎用モジュールに渡すべき項目は変化します。）

```PURCHASESET_CREATE_ENTITY
method PURCHASEREQSET_CREATE_ENTITY.
* データ宣言
    TYPES :
      BEGIN OF LT_PRSET,
        MATNR TYPE EBAN-MATNR,
        MENGE TYPE EBAN-MENGE,
        MEINS TYPE EBAN-MEINS,
      END OF LT_PRSET.

    DATA :
      LW_PR_NUMBER TYPE BAPIMEREQHEADER-PREQ_NO,
      LIT_ITEM     TYPE STANDARD TABLE  OF BAPIMEREQITEMIMP,
      LIT_ITEMX    TYPE STANDARD TABLE OF BAPIMEREQITEMX,
      LIT_RETURN   TYPE STANDARD TABLE OF BAPIRET2,
      LW_ITEM      TYPE BAPIMEREQITEMIMP,
      LW_ITEMX     TYPE BAPIMEREQITEMX,
      LW_HEADER    TYPE BAPIMEREQHEADER,
      LW_HEADERX   TYPE BAPIMEREQHEADERX,
      LW_PRSET     TYPE LT_PRSET.

* フロントからの連携値を取得
    CLEAR LW_PRSET.
    IO_DATA_PROVIDER->READ_ENTRY_DATA(
      IMPORTING ES_DATA = LW_PRSET
    ).

* 連携値をBAPIテーブルLIT_ITEMに割当
    LW_ITEM-MATERIAL   =  LW_PRSET-MATNR.//購買依頼したい品目の品目コード
    LW_ITEM-PLANT        =  '<任意のプラント>'.
    LW_ITEM-QUANTITY   =  LW_PRSET-MENGE.//数量
    LW_ITEM-UNIT           =  LW_PRSET-MEINS.//数量単位

    APPEND LW_ITEM TO LIT_ITEM.

* 連携値をBAPIテーブルLIT_ITEMXに割当
    LW_ITEMX-MATERIAL    = 'X'.
    LW_ITEMX-PLANT       = 'X'.
    LW_ITEMX-QUANTITY    = 'X'.
    LW_ITEM-UNIT         = 'X'.

    APPEND LW_ITEMX TO LIT_ITEMX.

* ヘッダ情報を割当
    LW_HEADER-PR_TYPE     = 'NB'.
    LW_HEADERX-PR_TYPE   =  'X'.

* 購買依頼を作成
    CALL FUNCTION 'BAPI_PR_CREATE'
      EXPORTING
        PRHEADER  = LW_HEADER
        PRHEADERX = LW_HEADERX
      IMPORTING
        NUMBER    = LW_PR_NUMBER
      TABLES
        RETURN    = LIT_RETURN
        PRITEM    = LIT_ITEM
        PRITEMX   = LIT_ITEMX
      EXCEPTIONS
        OTHERS    = 1.

* エラーハンドリング
    IF SY-SUBRC = 0."購買依頼作成に成功、コミット
      CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'.
    ELSE."購買依頼作成に失敗、ロールバック
      CALL FUNCTION 'BAPI_TRANSACTION_ROLLBACK'.
    ENDIF.

  endmethod.
```

フロントから「ドリフ大爆笑DVD」の品目コード300280、任意の数量、所定の数量単位を渡し、CreateEntityメソッドを呼び出せば、汎用モジュール　BAPI_PR_CREATE　が「ドリフ大爆笑DVD」の購買依頼をじゃんじゃん登録してくれることになります。

CreateEntityメソッドを実装し終えたら、上掲の記事の手順に従って[「OData Serviceを有効化」](https://qiita.com/zukahira/items/c97f49f211a70dc1b555#odata-service%E3%82%92%E6%9C%89%E5%8A%B9%E5%8C%96%E3%81%99%E3%82%8B)しましょう。サービス名が自動提案されます。（今回は`YIOSBLOG０２_SRV`となりました。）

さらに、同記事の手順に従って、SCP SDK for iOSで[「Xcodeプロジェクトを生成」](https://qiita.com/zukahira/items/c97f49f211a70dc1b555#2-xcode%E3%83%97%E3%83%AD%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%82%92%E7%94%9F%E6%88%90%E3%81%99%E3%82%8B)します。

# 生成されたXcodeプロジェクトと前回作っただっふんだアプリをマージする

生成されたXcodeプロジェクトはすでにバックエンドとの連携に関わる設定が自動生成され、そのままビルドすれば動作確認もできるようになっています。
これに前回作っただっふんだアプリをマージしていきましょう。

前回作成したXcodeプロジェクトを開き、`ViewController.swift` `Main.storyboard` `ochi.mp3` をコピーします。
 `Main.storyboard`と同名のStoryboardが自動生成されたプロジェクトに既に含まれているので、ファイル名をクリックして`SayDafunda.storyboard`に変更します。

追加したファイルを分かりやすいように一つのフォルダ`SayDafunda`にまとめておきましょう。
ファイルを右クリック　→　「New Group」で新しいフォルダを作ることができます。

![xcode2.png](https://qiita-image-store.s3.amazonaws.com/0/320955/e552a6c5-65a9-76a0-cd2e-7fbd46cbfa5d.png)

`Info.plist`ファイルを更新します。
下記のように、「Information Property List」に`Privacy - Speech Recognition Usage Description`および`Privacy - Microphone Usage Description`を追加しましょう。

![plist.png](https://qiita-image-store.s3.amazonaws.com/0/320955/90943f81-492a-fc38-b659-1437ae75fc14.png)

また、自動生成されたプロジェクトでは初期表示のStoryboardが別のStoryboardになっているので、こちらの手順[「初期表示Storyboardを変更する」](https://qiita.com/zukahira/items/d8fedb18b109f77a5283#%E5%88%9D%E6%9C%9F%E8%A1%A8%E7%A4%BAstoryboard%E3%82%92%E5%A4%89%E6%9B%B4%E3%81%99%E3%82%8B)を参考に、これを`SayDafunda.storyboard`に変更します。


# ODataメソッドを呼び出すようフロントを改修する

さて、ここからはいよいよ`ViewController.swift`を書き換えて、バックエンドのCreateEntityメソッドを呼び出せるようにしていきましょう。

まずは追加で必要なフレームワークをインポートします。

```ViewController.swift
import Foundation
import SAPCommon
import SAPFiori
import SAPFoundation
import SAPOData
```

プログラムの冒頭で、前回宣言しただっふんだ変数に加えて、各種Delegeteやプロパティをセットしていきます。

```ViewController.swift
    private let appDelegate = UIApplication.shared.delegate as! AppDelegate
    private var yiosblog02SRVEntities: YIOSBLOG02SRVEntities<OnlineODataProvider> {
        return self.appDelegate.yiosblog02SRVEntities
    }
```

次に、OdataのCreateEntityメソッドを呼び出すメソッドcreatePurchaseReq()を実装します。
このとき、「ドリフ大爆笑DVD」の品目コードおよび数量・数量単位を渡します。
せっかくなのでどしどし品目を調達できるよう、「だっふんだ」一回に対して「ドリフ大爆笑DVD」が10単位購買依頼できるようにしてしまいましょう。

すでにSDKによって自動生成されているメソッドyiosblog02SRVEntities.createEntity()に値を渡すだけでOdataのCreateEntityメソッドを呼び出せるため非常に簡単です。

```ViewController.swift
    private func createPurchaseReq(){
        var newEntity = PurchaseReq() //値を渡すための変数を宣言
        newEntity.matnr = "300280"　//「ドリフ大爆笑DVD」の品目コード
        newEntity.menge = SAPOData.BigDecimal(10) //購買依頼したい個数
        newEntity.meins = "EA" //数量単位
        try! self.yiosblog02SRVEntities.createEntity(newEntity)//OdataのCreateEntityメソッドを呼び出す
            }
```
最後に、前回実装済みのdafundaRecognized()メソッドの末尾で、今作ったcreatePurchaseReq()を呼び出せるようにすれば実装は完了です。

```ViewController.swift
createPurchaseReq()
```

# だっふんだ！

完成したアプリをビルドしiPhoneやiPadにインストールしたら、アプリに向かって「だっふんだ！」と言ってみて下さい。
フロントエンドではドリフのオチの音が再生され、「『ドリフ大爆笑DVD』購買依頼登録しました」と画面に表示されます。

<img width="375" alt="iphone.png" src="https://qiita-image-store.s3.amazonaws.com/0/320955/5c4f71f2-b40e-9e43-873b-7812805a3834.png">

バックエンドで本当に購買依頼が登録できているか確かめるために、PCを開いてS/4HANAのテーブルを見てみましょう。

![eban.png](https://qiita-image-store.s3.amazonaws.com/0/320955/4b7e2498-b691-35fc-e763-1a4aa3e3bef8.png)

だっふんだと言えば言うほど購買が登録されます。（だっふんだと認識されない場合フロントエンドで「へんなおじさん」の文字列が倍増し、購買依頼は登録されません。）
忙しい麹町一丁目店の店員さんもこれならいちいちPCを開く手間なしに購買依頼を登録可能です。

以上のように、「だっふんだ」と言うとドリフのオチの音が鳴る楽しいアプリを、とても便利なアプリに発展させることができました。
ODataの他のメソッドを実装すればバックエンドからデータを取得することもできますし、CreateEntityメソッドには購買依頼以外の登録も実装可能なので、アイデア次第でドリフ危機以外にも様々な状況で役に立ちそうです。