誰かSAP CP界隈の購買事情をなんとかしてくれ。

だっふんだと電流とたらい以外で。(*1)

※この記事は [SAP Advent Calendar 2020](https://adventar.org/calendars/5047) の12月20日分の記事として執筆しています。

##はじめに
もうすぐクリスマスですね。
クリスマスといえばプレゼントです。
みなさん、家族に、恋人に、友人にプレゼントを送りあっているのではないでしょうか。

SAP界隈の方々においても例に漏れずプレゼントを買わんとする方がたくさんいると思いますが、ビジネスマンたるもの、プレゼントを買う際にはS/4HANAで購買発注伝票を作成しなければなりません。
しかし普通にGUIから作成するのでは、芸がありません。
できることならスマートに(≒[ダサくなく](https://qiita.com/tnmrknm/items/d76349a7ae0731b809d3))、センスの良いプレゼントを買いたいものです。

しかしどうやって購買発注すればよいものかと、界隈のブログを検索してみるのですが、**[だっふんだって言ってもドリフ大爆笑のDVDしか買えませんし](https://qiita.com/monamona/items/c55435580eaad482c7f7)**、あるいは**[電流を流してもマタタビしか買えません。](https://qiita.com/nsasaki/items/6695808342d30058e93f)**
この方たちにプレゼントを任せると大変なことになってしまいそうです。

どうしたものかと頭を思い悩ませていたのですが、解決方法は突如として私の頭に振ってきました！

![alexa_neko1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277725/79305624-5781-3c90-de26-aa928c27a2c8.png)


そう、最早どこの家庭にも必ず一人はいると言っても過言ではない、**Alexa**(Amazon Echo Dot 第3世代)です！
こいつならばスマートに、センスの良いプレゼントを注文してくれるはず…！
そう思い立ち、早速アプリケーションの作成に立ち上がりました。

ちなみに、まだこの時点で構想段階ではありますが念の為Alexaに聞いてみました。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">なんだかわからんがとにかくヨシ！ <a href="https://t.co/xvsQqiApQ6">pic.twitter.com/xvsQqiApQ6</a></p>&mdash; kanamen10 (@tnmrknm) <a href="https://twitter.com/tnmrknm/status/1340307969481691138?ref_src=twsrc%5Etfw">December 19, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

だめだこりゃ！

##アーキテクチャ概要

ということでAlexaとS/4HANAを連携させるアプリケーションを作っていきます。
アーキテクチャはこんな感じです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277725/428a8b7d-5e07-13da-a158-b1d4b6203503.png)



今回のアプリケーションでは**「アレクサ、プレゼントを注文して」**という発話をトリガーにして、発話情報がAWSの中のAlexa Skill Kit(以下ASK)という場所で受け取られます。
その後LambdaがASKから発話情報を受信した後、SAP Cloud Platform (以下SCP) のJavaを経由してS/4HANAで購買発注伝票が作成されます。作成に成功した場合は、アレクサが**「伝票番号はXXXです。」**と、伝票番号の情報を伝えてくれる流れとなっています。

今回の記事では特に、赤枠内に描いている

**1. SCPにMTAアプリケーションを移送(デプロイ)するまで**
**2. AWSのLambdaからトリガーされたSCP JavaとS/4HANAの連携処理**

の2点に重点を置いて書いていきたいと思います。

※AWS内の処理について詳細に記載した記事は[AWS LambdaとServerless Advent Calendar 2020](https://qiita.com/advent-calendar/2020/lambda)の12月22日分記事として公開予定です。

## MTAアプリケーションの移送
まずはMTAアプリケーションのデプロイ(移送)についてです。
アーキテクチャ概要で記載の通り、今回はSCP内のサービスであるBusiness Application Studio(以下BAS)を使用してMTAを作成していきます。

### 0. 今回やっていること要約
さて、これからSCPにおけるCI/CDとやらを説明していきます。
しかし、**SCPってなんだ**、あるいは**CI/CDってなんだかわからんがとにかくヨシ！**な方へ
これから解説することをERPに置き換えて説明しますと、

作ったアドオンプログラムやコンフィグを移送依頼に紐づけて、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277725/72657d6b-008d-837c-405a-1fcea9efa82c.png)

移送依頼をリリースして、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277725/b8c7bbb4-60e7-bb8b-bc35-c0da649b15ab.png)

管理画面から移送(インポート)する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277725/ed37d90d-1880-0ba4-71e1-b1c845922396.png)




これだけです。


ERPだとこんなに簡単なことです。
SCPだと少しばかり小難しく感じてしまうかもしれません。ですがそう憤慨しないでください。
ぜひこの記事を読んで、そんな方にこそフムフムしてもらえればと思っています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277725/99b5e751-95e0-aefd-4064-8078e7b67d7c.png)

早速やっていきましょう。


### 1. MTAアプリケーション作成
BASでのアプリケーション作成方法等は省略しますが、今回はMTAアプリケーション**PurchaseOrderAPI**を作成しました。
以下に主要な構成を記載しておきます。

```
PurchaseOrderAPI/
    ├ .che/
    ├ .git/
    ├ .pipeline/
    │      └ config.yaml
    ├ CICDSample_ui_deployer/
    ├ cicdsample-approuter/
    ├ CICDUI5/
    ├ PurchaseOrderJava/
    │      └ src/
    │          └ main/java/com/kanamen10/purchaseorderapi/purchaseorderjava
    │               └CreatePurchaseOrder.java
    │               └StorePurchaseOrderCommand.java
    │               └GetPurchaseOrderCommand.java
    │      └ pom.xml 
    ├ xs-security.json
    └ mta.yaml
```
今回のアプリケーションの主役はJavaでの購買発注伝票作成となります。
フロントエンド(トリガー)としてSAPUI5ではなくAlexaを使用している格好になるため、同じMTAアプリケーションの中に含めているUI5アプリケーションとJavaアプリケーションは無関係です。
ですが、後述するSAP Cloud Platform Continuous Integration and Deliveryで検証用に使用するために同じMTA内に作成しておきます。
また、Javaアプリケーションの中で行っている処理についても後述することにします。

### 2. SCP CI/CDとTMS
#### ~~ダサい~~ビルドとデプロイ
さて、MTAアプリケーションが出来上がったらいよいよビルド/デプロイです。
BASにおいては通常であれば、mta.yamlからBuild MTAを選択してビルド
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277725/f96d61ca-c5a0-ec57-8073-9e96dfbde48a.png)
そして出来上がったmtarファイルに対して、Deploy MTA Archivesを選択してデプロイすれば完了です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277725/39e1a26e-4b52-6e91-3d70-16fbe80f272d.png)

しかし、このポチポチとした手動ビルド/デプロイを過去に散々**[ダサい](https://qiita.com/tnmrknm/items/d76349a7ae0731b809d3)**と、こき下ろしてきた私としては、この行為をみすみす見逃してはなりません。ここで手動など言語道断、愚の骨頂、悪の枢軸です。
では悪に落ちないためにどうすればいいのでしょうか？
#### 今回の登場人物
もちろん過去記事で謳ったように、私の敬愛して止まないAzureのサービスの一つであるAzure DevOpsといったツールを利用する方法は健在です。
しかし、2020年10月から**SAP Cloud Platform Continuous Integration and Delivery**(以下SCP CI/CD)という、SCP内のCI/CDサービスが[SCP Trial環境で利用可能となっています。](https://blogs.sap.com/2020/10/14/try-it-out-sap-cloud-platform-continuous-integration-and-delivery-is-on-trial-now/)

これを利用すれば、GitへのプッシュをトリガーとしてMTAの自動ビルドや、テストスクリプトの実行、さらにはSCP実行環境への自動デプロイを実現可能です。
ですが、今回はあえてSCP実行環境への直接デプロイを避けて**SAP Cloud Platform Transport Management Service**(以下TMS)へ"デプロイ"し、TMSからSCP実行環境へ"移送"してみます。
今回はこの2つのサービスを駆使してアプリを展開しましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277725/72d95639-2dcf-52ce-0d87-1d59dab94d1d.png)
(アーキテクチャ概要再掲)

なお今回はTMSでの操作で些か手動の操作が入ってしまいますが、そこは検証の為仕方なくあっさりと悪に落ちることにします。

### 3. CI/CDのすゝめ
#### パイプラインの設定
SCP CI/CDに限らず、CI/CDツールを実行するトリガーはGit(リモートリポジトリ)へのプッシュであることが多いです。
例に漏れずまずはMTAアプリケーションのリモートリポジトリを作成します。
今回はSCP内で完結させたかったためSCP内のGit(Neo環境)を使用しました。
そしてメインとなるSCP CI/CDを、コンソール画面のSubscriptionsから立ち上げます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277725/2f2eea46-5da0-7444-46fd-3d33d95e474b.png)

中に入るとこんな感じの画面となっています。
JobsとCredentialsという言葉が並んでいますが、それぞれJobsはビルドやテスト、デプロイを実行する場所、Credentialsでは環境へのログインIDとパスワードや、SCP サービスインスタンスへのアクセス情報など、Jobsへ渡される様々な環境変数だと思ってください。
ここでまず最初にCredentialsを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277725/9d7d2b5e-b42f-759e-cc7c-41fe93313bcf.png)


まずは先ほど作成したリモートリポジトリの保管先である、Neo環境へのログイン情報をCredentialとして保存します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277725/18788662-0819-5679-b98e-e7864e36d3ea.png)

これで準備完了です。あとはパイプラインを構築することになります。

パイプラインの構築は他のCI/CDツールと比較してもシンプルな作りです。
Job(パイプライン)の名前とリモートリポジトリのURL、リモートリポジトリへアクセスするための先ほどのCredentialオブジェクト、トリガーとなるブランチを選択すればほぼ完了です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277725/abc41b43-da6b-acb5-c6f4-7f69997fe770.png)

##### パイプライン実行をGitトリガーに設定するには
ジョブの作成が完了すると、Webhookに関する情報が出てきます。
これはGithubを使った際の話で、記載のシークレットキーを使用することでリモートリポジトリへのプッシュをSCP CI/CD側で検知してパイプラインを動かしてくれるとのことです。
つまりGithub以外のリポジトリ(≒Webhook連携できないリモートリポジトリ)では、現状ではパイプラインのトリガーは手動になってしまうようです。(なんとダサい…)
今回は便宜的にNeo環境のCredential情報を設定しましたが、無くても動作は変わりません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277725/e358a8cb-ad0e-1eda-dbb5-9312bec75556.png)
任意のブランチにPushされた際に自動的にパイプラインを実行するには[Webhook連携の設定](https://qiita.com/tz2i5i_ebinuma/items/528ea5163bb2df379852)が必要です。

#### パイプラインの中身
パイプラインの処理自体はSCP CI/CD内ではなく、MTAプロジェクトの.pipeline/配下のconfig.yamlにて定義します。
今回は検証目的であるため、ごく簡単にビルド、テスト(QUnit)、デプロイを行うパイプラインを組みました。

```yaml:config.yaml
# Project Setup
general:
  pipeline: "sap-cloud-sdk"

# Stage build and test
steps:
  mtaBuild:
    applicationName: "PurchaseOrderAPI"
stages:
  testStage:
    enable: true

# Stage deployment
stages:
  deployStage:
    enable: false  # enableをfalseにして今回はSCP実行環境への直接デプロイを避けます

#SCP実行環境デプロイのサンプルコード
# 実際には今回は実行されないように設定していますが、SCP実行環境(dev)へ直接デプロイするように書いています
stages:
  productionDeployment:
    cfTargets:
    - org: "<orgname>"
      space: "dev"
      appName: "PurchaseOrderAPI"
      apiEndpoint: "https://api.cf.us10.hana.ondemand.com"
      credentialsId: "cf-ls-login" #credentialsIdはSCP CI/CD内に予め作成する必要あり
      
# Stage tms
stages:
  tmsStage:
    enable: true

# SCP実行環境(dev)に直接デプロイする代わりに、TMS(移送管理画面)にデプロイするように設定しています
steps:
  tmsUpload:
    nodeName: "DEV_node" #nodeはTMS内に予め作成する必要あり
    credentialsId: "dev-tms-service-key" #credentialsIdはSCP CI/CD内に予め作成する必要あり

```
パイプラインを組んだところで、早速実行したいところですが、このままパイプラインを実行するとまだ何も設定されていないTMSへMTAをデプロイすることになってしまいます。
そのため、パイプライン実行前にTMSの設定を行っていきます。

### 4. 移送、つまりそれはトラック
ここまでしつこくデプロイと移送という言葉を書いてきましたが、本質的に指している意味自体はどちらも同じです。
しかし、SAPでは各環境へプログラムやコンフィグなどを持っていくことをデプロイではなく移送と呼ぶことが多いです。
特にこのTMSはSAPの文化を色濃く踏襲したサービスであるため、TMSを使った実行は敢えてデプロイではなく移送と呼んでみることにします。

#### 前準備
さて、TMSへMTAアプリケーションを移送するために、まずはSCP実行環境の整備をしていきます。
実行環境は開発、検証の2ランドスケープ構成と想定して、今回はdev, qasという2つのSpaceを作成しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277725/33ee8643-9f1a-1058-4a06-a7db460d3e7c.png)

続いてもSCPのDestinationサービスから、先程作成したSpaceのdevを指定したDestinationを作成します。
TypeはHTTP、 AuthenticationはBasic Auth.で作成します。
URLは[https://deploy-service.cfapps.<"region">.hana.ondemand.com/slprot/<"orgName">/<"spaceName">/slp]()
という形式で記述します。
(<>内の記述以外は他環境でも変更不要です。このURLパターンで先ほど作成したSpaceを指しています)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277725/78821b11-c12d-e715-6aa0-fc28848ddb70.png)
また、devと同様にqasも作成しておきます。

#### Node作成

SCP環境の準備ができたらTMSに入ります。サービスを立ち上げた画面はこんな感じです。
徹底したSAPの移送=トラック愛に畏敬の念を禁じ得ません。
![スクリーンショット 2020-12-18 0.21.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277725/c5278a30-339e-a738-d679-1d8a8249be17.png)

まずはLandscape VisualizationからNodeを作成します。
NodeというのはここではSCPの実行環境のことだと思ってください。
実行環境の場所をNode内で指定したいので、Destinationで先程作成したdevを指定します。
今回はDEV_nodeという名前でdev環境、QAS_nodeという名前でqas環境それぞれのNodeを作成しました。
また、Typeに関しては今回はMTAを移送したいのでMulti Target Applicationを選択します。
![スクリーンショット 2020-12-18 0.27.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277725/45972e36-cb59-e9e3-b2b2-2eedf6256fd4.png)

#### Route作成
続いて移送ルートを作成します。
今回はdev環境からqas環境へ移送を行いたいため、Transport Routesから source nodeをDEV_node、target nodeをQAS_nodeに設定します。
dev2qasという名前でルートを作成しました。
![スクリーンショット 2020-12-18 0.33.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277725/b0efa3df-56fb-cb27-f19f-0c103bc01b95.png)

ルートまで作成完了するとLandscape Visualizationから、DEV_nodeからQAS_nodeへのルートがつながっていることが確認できます。(他のnodeは別の検証用なので無関係)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277725/e934fcce-387c-ce55-fa6f-c19b261fce4b.png)

これで移送ルートが作成できました。
ここまでで準備は完了です。

### 5. "移送" と "デプロイ"

ここでSCP CI/CDのパイプラインを実行すると、こんな感じの流れで処理が進みます。(一部手動ですが)

1. パイプライン実行(MTAビルド、テスト、TMS DEV_nodeへデプロイ)
2. DEV_nodeにMTAビルドファイルがインポートされる(node内のキューに入る)
3. TMSのDEV_nodeから、インポートされたMTAのリリースを実行する
4. DEV_nodeのDestinationであるSCP実行環境SpaceのdevにMTAアプリケーションが立ち上がる
5. Routeを通ってMTAがQAS_nodeにインポートされる
6. 3,4と同様


つまり0.でやっていた流れであるというイメージを持ってもらえれば理解しやすいかもしれません。
SCP CI/CDを使えば2,3の手順を飛ばして4の"デプロイ"を一気に実行できるのですが、敢えてそれをせずに従来のERPのように移送管理画面から"移送"する、ワンクッション挟むというところが今回のポイントです。

### 6. 実行
残念ながらSCPのGitを使用すると、ブランチへのプッシュでパイプライン実行がトリガーできないため、SCP CI/CDの画面から実行ボタンを押します。(屈辱感)

#### 自動ビルド/デプロイ実行
パイプラインを構築していれば、下記のボタンから実行開始できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277725/116be4b5-676f-15b9-acc4-1fff7e5a5805.png)

実行開始したパイプラインを選択すると、config.yamlで作成したステージごとの進行状況(右ペイン)が確認できます。
それぞれのステージをクリックするとログを確認することができるため、エラーが発生した際はここから障害分析を行うことができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277725/bf34c1ac-0754-47ba-d54d-8152cc010e84.png)

#### 実行環境への"移送"
SCP CI/CDのタスクが正常終了した時点でTMSの管理画面を見るとTransports Waiting for ImportというところのDEV_nodeに移送待ち件数が1件増えています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277725/ee4bda77-d062-318b-ccbc-995cb4c313d9.png)

DEV_nodeの中身を見てみると、いくつかキューに移送依頼が溜まっているように見えています。
この中でStatusがInitialとなっている移送依頼が今回SCP CI/CDから送られてきたものです。
これを選択してImport Selectedを選択することで、まずはSCP実行環境であるdev(DEV_nodeのDestination設定先)にMTAが展開されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277725/f1f0ba16-f024-e44c-f79a-d2852d0a1be8.png)

MTAに含めていたUI5、Javaのアプリケーションがdev内で起動していることが確認できました！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277725/44e8a4f4-e85e-049a-757b-a6345b496f68.png)


さらにTMSに戻って、QAS_nodeを見てみると先ほど作成したRouteを通ってこちらのnodeにも移送依頼が送られています。
こちらでも同様にImport Selectedを選択することでqasへMTAを展開可能です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277725/61551f9c-78b9-e2fc-4ac8-8bc5c054ba04.png)



#### いよいよここからが本題
ということで無事にSCPにアプリを展開することができました。
ですが、今回はただ検証目的でサンプルアプリを置ければいいわけではありません！
購買発注伝票をAlexaに作成させるという大きなミッションがあります。

ここまで長い前置きでしたが、Javaにどのような処理をさせているのかを説明していきます。

## SCP↔S/4HANA連携
今回はアーキテクチャ概要でも記載の通り、AWS Lambdaからリクエストが発せられ、SCPを経由してS/4HANAの購買発注伝票作成ODataを呼び出すことで伝票を作成しています。
以下にその処理の流れを説明していきます。

### 1. 大まかな処理の流れ
AWS内の処理については[別記事](https://qiita.com/advent-calendar/2020/lambda)で解説予定なので、ここではLambdaから送られたリクエストをSCPでどう処理しているのかを書きます。

SCPでの処理の流れとしては、以下のような感じです。
1. LambdaからSCP JavaのPostメソッドに発話情報を含んだJSON形式のリクエストが送られる
2. リクエスト情報の処理とJavaエンティティへのマッピング
3. 購買発注伝票作成ODataの呼び出し実行(Cloud SDK)
4. 作成した購買発注伝票の伝票番号取得(購買発注伝票照会ODataの呼び出し)

順番にまずはリクエスト処理の部分から説明していきます。

### 2. リクエスト処理
リクエストの受け口を作成し、パラメーター処理を行う処理を実装します。
本当であれば、Alexa側で動的に品目や個数といったパラメーターを切り替えたかったのですが、今回はお試しなのでほとんどハードコードしてしまっています。。
リクエストのJSONの中から情報を取り出せるかを確認する目的で、会社コードのみハードコードではなくリクエストから取得しています。

ここでは購買発注伝票情報のセットまで行っています。
また、返り値に対して購買発注伝票情報の全てを受け取って返してしまうとAlexaが購買発注伝票情報を延々と話してしまうので、伝票番号のみ返すようにしています。

```java:CreatePurchaseOrder.java

import com.sap.cloud.sdk.cloudplatform.connectivity.DestinationAccessor;
import com.sap.cloud.sdk.s4hana.connectivity.DefaultErpHttpDestination;
import com.sap.cloud.sdk.s4hana.connectivity.ErpHttpDestination;
import com.sap.cloud.sdk.s4hana.datamodel.odata.namespaces.purchaseorder.*;
import com.sap.cloud.sdk.s4hana.datamodel.odata.namespaces.purchaseorder.field.PurchaseOrderField;
import com.sap.cloud.sdk.s4hana.datamodel.odata.services.DefaultPurchaseOrderService;
import com.sap.cloud.sdk.s4hana.datamodel.odata.services.PurchaseOrderService;


@WebServlet("purchaseorder")
public class CreatePurchaseOrder extends HttpServlet {
    private static final Logger logger = LoggerFactory.getLogger(CreatePurchaseOrder.class);
    
    // 予めS/4HANAのURLを設定したDestinationをSCPに登録しておく
    private static final ErpHttpDestination destination = DestinationAccessor.getDestination("DESTINATION")
                                                          .asHttp().decorate(DefaultErpHttpDestination::new);

    // 発話情報を含んだJSON形式のリクエストがLambdaから送られる
    @Override
    protected void doPost(final HttpServletRequest request, final HttpServletResponse response)
            throws ServletException, IOException {

        logger.info("Start get method: " + request.getRequestURI());

        BigDecimal orderQuantity = new BigDecimal("10");
        BigDecimal netPriceAmount = new BigDecimal("10000")
        final String companycode = request.getParameter("companycode");  // 会社コードのみリクエストに含まれるJSONから取得する

        // 購買発注伝票の明細情報をJavaエンティティに紐づける
        // 各項目はS/4HANAで入力される形式に揃える
        final PurchaseOrderItem poi = PurchaseOrderItem.builder()
                                      .material("materialName")
                                      .orderQuantity(orderQuantity)
                                      .plant("plantNo")
                                      .purchaseOrderItemCategory("itemCategoryNo")
                                      .netPriceAmount(netPriceAmount)
                                      .taxCode("taxCode")
                                      .build();

        // 購買発注伝票のヘッダー情報をJavaエンティティに紐づける
        // 各項目はS/4HANAで入力される形式に揃える
        final PurchaseOrder po = PurchaseOrder.builder()
                                .purchaseOrderType("NB")
                                .purchasingOrganization("organization")
                                .purchasingGroup("group")
                                // 会社コードのみリクエストに含まれるJSONから取得する
                                .companyCode(companycode)
                                .supplier("supplier")
                                .language("JA") 
                                // 設定した明細情報を紐づける
                                .purchaseOrderItem(poi)
                                .build();

        String responseBody;

        try {
            final PurchaseOrder storedPurchaseOrder = new StorePurchaseOrderCommand(destination, new DefaultPurchaseOrderService(), po).execute();

            // 返り値設定
            // Alexaに作成した伝票番号情報のみを返したいため、getPurchaseOrderを使う
            responseBody = new Gson().toJson(storedPurchaseOrder.getPurchaseOrder());


        } catch (final Exception e) {
            logger.error(e.getMessage(), e);
            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            responseBody = e.getMessage();
        }

        response.setContentType("application/json");
        response.getWriter().write(responseBody);

    }

    // 疎通確認用getメソッド
    @Override
    protected void doGet(final HttpServletRequest request, final HttpServletResponse response)
        throws IOException {

        logger.info("Start get method: " + request.getRequestURI());

        try{
            final List<PurchaseOrder> customers =
                    new DefaultPurchaseOrderService()
                            .getAllPurchaseOrder()
                            .select(PurchaseOrder.ALL_FIELDS)
                            .top(5)
                            .execute(destination);
            response.setContentType("application/json");
            response.setCharacterEncoding("UTF-8");  //日本語用
            response.getWriter().write(new Gson().toJson(customers));

        } catch (final ODataException e){
            logger.error(e.getMessage(), e);
            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            response.getWriter().write(e.getMessage());
        }
    }
}
```

### 3. OData呼出し処理
StorePurchaseOrderCommandクラスに実装されたexecute(run)メソッドを呼出すことでS/4HANAのOData実行が可能です。
また、伝票作成処理の呼出し後に作成した伝票を照会する処理を同じクラスを利用して取得しています。

```java:StorePurchaseOrderCommand.java

import com.sap.cloud.sdk.cloudplatform.connectivity.HttpDestination;
import com.sap.cloud.sdk.cloudplatform.resilience.ResilienceConfiguration;
import com.sap.cloud.sdk.cloudplatform.resilience.ResilienceDecorator;
import com.sap.cloud.sdk.cloudplatform.resilience.ResilienceRuntimeException;

public class StorePurchaseOrderCommand {

    private final PurchaseOrderService purchaseOrderService;
    private final PurchaseOrder purchaseOrder;
    private final HttpDestination httpDestination;

    public StorePurchaseOrderCommand(HttpDestination httpDestination, PurchaseOrderService purchaseOrderService, PurchaseOrder purchaseOrder) {
        this.purchaseOrderService = purchaseOrderService;
        this.purchaseOrder = purchaseOrder;
        this.httpDestination = httpDestination;
    }

    public PurchaseOrder execute() {
      return ResilienceDecorator.executeSupplier(this::run, ResilienceConfiguration.of(GetPurchaseOrderCommand.class));
    }

    private PurchaseOrder run() {
        try {
            return purchaseOrderService
                    .createPurchaseOrder(purchaseOrder)
                    .execute(httpDestination);
        } catch (final ODataException e) {
            throw new ResilienceRuntimeException(e);
        }
    }

}
```

```java:GetPurchaseOrderCommand.java

import com.sap.cloud.sdk.cloudplatform.resilience.ResilienceConfiguration;
import com.sap.cloud.sdk.cloudplatform.resilience.ResilienceDecorator;
import com.sap.cloud.sdk.cloudplatform.resilience.ResilienceRuntimeException;

public class GetPurchaseOrderCommand {

  private final PurchaseOrderService purchaseOrderService;
  private final HttpDestination httpDestination;

  public GetPurchaseOrderCommand(HttpDestination destination) {
    this(destination, new DefaultPurchaseOrderService());
  }

  public GetPurchaseOrderCommand(HttpDestination httpDestination, PurchaseOrderService purchaseOrderService) {
    this.purchaseOrderService = purchaseOrderService;
    this.httpDestination = httpDestination;
  }

  public List<PurchaseOrder> execute() {
    return ResilienceDecorator.executeSupplier(this::run, ResilienceConfiguration.of(GetPurchaseOrderCommand.class));
  }

  private List<PurchaseOrder> run() {
    try {
      return purchaseOrderService.getAllPurchaseOrder()
        .execute(httpDestination);

    } catch (ODataException e) {
      throw new ResilienceRuntimeException(e);
    }
  }
}
```

SCPで行っている処理はこれだけです。
あとはこのJavaアプリケーションがBasic認証を通って呼び出されるように、Javaの前にApp Routerを置くようにしました。
実装方法は[こちら](https://blogs.sap.com/2020/05/25/basic%e8%aa%8d%e8%a8%bc%e3%81%82%e3%82%8a%e3%81%aejava-rest-api%e3%82%92sap-cloud-sdk-for-java%e3%81%a7%e4%bd%9c%e6%88%90/)の記載に従いました。

これでSCP↔S/4HANAの連携ができるようになりました！

## Alexaに購買発注伝票を作ってもらう

ということで再びAlexaに呼び掛けて、今度こそ購買発注伝票を作ってもらいます！
すると、、

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">プレゼントは注文できるのか…！ <a href="https://t.co/wJ9lRbXIOn">pic.twitter.com/wJ9lRbXIOn</a></p>&mdash; kanamen10 (@tnmrknm) <a href="https://twitter.com/tnmrknm/status/1340307122806902785?ref_src=twsrc%5Etfw">December 19, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

ちょっと盛りすぎたブルゾン〇えみのようになってしまいました。。
ですが、S/4HANAを確認すると
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277725/14bf3438-d51c-63b6-4b1e-e892b4453a03.png)
確かに作成できました！
これで無事にクリスマスを乗り切ることができそうです！！


…ん？

そういえば、Alexaは一体何を注文してくれたんだ？

<blockquote class="twitter-tweet"><p lang="und" dir="ltr"><a href="https://t.co/a8zvaMDHoU">pic.twitter.com/a8zvaMDHoU</a></p>&mdash; kanamen10 (@tnmrknm) <a href="https://twitter.com/tnmrknm/status/1340307567428308994?ref_src=twsrc%5Etfw">December 19, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


お前もそっち側の人間だったのか…！
だめだこりゃ！

## いくつか改善点と気づき
ここまで実装してみての真面目なまとめです。
<dl>
  <dt>入力事項の多いタスクにAlexaは向かない</dt>
  <dd>今回はハードコードしてしまいましたが、例えばAlexaへの呼びかけを「〇〇を注文して」、「会社コードXXXで」、などとして、動的に伝票の内容を切り替えられたとしても、Alexaとの会話のラリーが増えるとミスも多くなりそうなので、Voice UIとGUIの向き不向きを検討したいと思いました。
今回の内容で言うとAlexaに向いているのは、例えばTMSのキューに入った移送依頼情報をなんらか方法で取得し、移送依頼が発生していることをAlexaから通知させて、移送を命令するといったもののように、会話のラリーが少ないアプリケーションは比較的有用かなと思います。
ただ、現時点では、コンソール以外の外部からの移送情報取得は難しく、そういったAPIも存在しないようです。</dd>
  <dt>今後のSCP開発におけるテストコードの重要性</dt>
  <dd>今回は深く触れませんでしたが、SCP CI/CDを使うことによってテスト環境が提供されるようになりました。
SAPUI5のテストに加えてJavaのテストも実行可能となっているので、そろそろテストコードを避けて通れなくなりつつあるのを感じています。</dd>
  <dt>サーバレス化対象の見極め</dt>
  <dd>今回のような単純なAPIであれば、CFアプリではなくSCPのkymmaを使ってサーバレス化(関数化)してしまった方が処理速度やコスト面でメリットがあるかもしれません。
ですが気を付けなければいけないのはHANA DBを使用するパターンで、[AWSのようにコネクションプールマネージャがないと](https://qiita.com/G-awa/items/b9138cc1c9e4867a905e)サーバレス↔HANAはアンチパターンになってしまいます。この辺りの共存方法を上手く考える必要を感じました。</dd>
  <dt>TMSは単体だと効果半減</dt>
  <dd>SCP CI/CDは開発者目線から見るとビルド、テスト、デプロイといった作業のコストを減らせるので、単体でも様々な効果が期待できます。一方でTMSはSCP CI/CDを使わないと手動でMTAの添付をしなければならないので、かえって手間になってしまいます。
また、SCP CI/CD以外のCI/CD実現の選択肢としてはCircle CIや、筆者が愛して止まないAzure DevOpsがあります。
これらを採択せず、SAP CI/CDを選ぶべき最大のメリットはやはりTMSとの連携が可能という点にあるかと思います。
一般的にはコミット先のbranchを分けるなどすることでデプロイ先環境を切り替える方法を取るかと思いますが、
SCPの場合だと例えばS/4HANAにあるODataの移送依頼との、移送タイミングの兼ね合いなどを考える必要があるため、
移送タイミングを開発者側でコントロールすることがベストでないこともあります。そのためbranchで環境切り替えを実現するのは難しくなってしまいます。
TMSとSCP CI/CDを組み合わせることでこれを解決できることからも、2つのサービスはSCPにおけるCI/CDのベストプラクティスと言えるかと思います。</dd>
</dl>

## Special Thanks
(*1) 
偉大なる先人たちに拍手とLGTMを！

@monamona 
[「だっふんだ 」と言うとSAP S/4HANAで購買依頼伝票を打てるiOSアプリを実装する](https://qiita.com/monamona/items/c55435580eaad482c7f7)

@nsasaki 
[にゃんテック x SAP(電気ショックver)やってみた](https://qiita.com/nsasaki/items/6695808342d30058e93f)
[たらい落としでSAP S/4 HANA上に購買依頼伝票を作成してみた](https://qiita.com/nsasaki/items/af608c09e687f58e6db0)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277725/8ede3e13-5a4e-821f-9941-7a600ff69a67.png)