APEXのワークフローを使ってみる Vol. 2

# はじめに
この記事では、Oracle Live Labsのワークショップ[「Simplify Business Process Management Using APEX Workflows」](https://apexapps.oracle.com/pls/apex/r/dbpm/livelabs/run-workshop?p210_wid=3880)をもとに、Oracle APEXで
APEXアプリケーション「Doctor Appointments Made Easy！」を作成し、ワークフローの機能を使って医師の診察を予約・管理するシステムを実装してみます。

[こちらの記事](https://qiita.com/bambooseven/items/621b009355f5ebbfdc04)で、ワークフローを実装するためのアプリケーションの作成まで行いました。
今回は、Oracle APEXを使用して医師予約ワークフローを作成する手順をご紹介します。

### 本記事で行うこと
* ワークフローの作成
* ワークフローの詳細の変更、設定
* ワークフローデータの定義
* ワークフロー変数の追加

# ワークフローを作成
APEXアプリケーション「Doctor Appointments Made Easy！」を設計することで、医師の診察を予約・管理する効率的なシステムを実装します。実現のために、以下のアプローチに従っていきます。
* ビジネスロジックを、管理可能で実行可能なステップに分解
* これらのステップの実行を制御するシーケンスと条件を特定
* このビジネスプロセスに関与する組織内の主要な人物を特定

まず、診療予約のビジネスプロセスをステップに分解し、各ステップに関連する登場人物を特定します。以下に概要を示します：


### アプリケーションの設計
*  ワークフローの作成
1. アプリケーションビルダーで、「Doctor Appointments Made Easy!」アプリケーションに移動し、「共有コンポーネント」を選択します。
    ![APEXワークフロー_3-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3058002/a5c36fd0-eac7-c286-a7ca-3397f844c3d8.png)
    ![APEXワークフロー_3-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3058002/a2ed8537-8e0b-313e-586d-0d5c89cde9f5.png)

1. [ワークフローと自動化] で [ワークフロー] を選択します。
    ![APEXワークフロー_3-3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3058002/b5027904-6de8-7638-baa5-58c296bf821f.png)

1. 「ワークフロー」ページで、「作成」をクリックします。
    
1. ワークフローデザイナーが表示されます。開始アクティビティ、コード実行アクティビティ、終了アクティビティを持つ新しいワークフローが自動で作成されます。
    ![APEXワークフロー_3-4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3058002/68acee18-8d58-2289-0103-1b95f814d502.png)

* ワークフローの詳細を変更する
1. 画面左側のレンダリング ツリーで[Doctor Appointment]を選択した状態で、画面右側のプロパティ・エディタで次のように入力/選択します。

    * 識別>名前:Doctor Appointment
    *  詳細 > 静的ID: DOC_APPT
    ![APEXワークフロー_3-5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3058002/ab195196-67d0-060f-ba5b-3ae6cd6dc7e8.png)
1. 次に、左側のペインで「新規[開発]」を選択し、プロパティ・エディタで「ワークフローバージョン」に「1.0」と入力します。
    ![APEXワークフロー_3-6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3058002/02d4a2aa-8a17-4804-3f75-ce62b3716e52.png)

1. 検証エラーが発生したため、ワークフローを保存できません。画面上部のエラーアイコン（黄色いマーク）をクリックすると、エラー内容が表示されます。
    ![APEXワークフロー_3-7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3058002/aab1d1d4-d321-584e-9653-7efa3010419b.png)

1. この時点で、後に新しいアクティビティを紹介するので、このアクティビティは削除します。これを行うには、ワークフローツリーで強調表示された 赤いアクティビティを右クリックして「削除」を選択するか、ダイアグラムのアクティビティの右下隅にある 3つのドットをクリックします。メニューが開き、表示されたオプションから[削除]を選択できます。
    
    ![APEXワークフロー_3-8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3058002/d9cca0f9-0d0b-d3a1-2861-6033919e841b.png)
    ![APEXワークフロー_3-9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3058002/f9d2041d-fb2f-627c-45d7-3816775871ee.png)


1. これで、ワークフローには [開始] アクティビティと [終了] アクティビティのみが含まれるようになりました。[開始]アクティビティと[終了]アクティビティを矢印を伸ばして接続し、画面右上隅にある[保存]ボタンをクリックします。

    ![APEXワークフロー_3-10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3058002/4a7e277c-6a83-7198-832a-2bb28bf2f78d.png)
※ワークフローモデルは、検証エラーが修正されるまで保存できません。

* ワークフロー要素の理解
    ここでは、ワークフローの基本的な要素と用語についてみていきます。アクティビティ、接続、接続タイプなど、さまざまなタイプのワークフロー要素と用語について説明します。
    
    アクティビティの種類の一覧は、デザイナーの下部にあるアクティビティパレットにあります。
    
    |アクティビティの種類  |説明  |実行が完了するまでワークフローエンジンをブロックするか  |
    |---|---|---|
    |コードを実行 |PL/SQLコードの実行  |はい |
    |API呼び出し	  |PL/SQLパッケージ内のプロシージャまたはファンクション、またはRESTデータ・ソースからAPIを呼び出します。  |はい  |
    |プッシュ通知の送信	 |PWA を使用してプッシュ通知を送信する |はい  |
    |電子メールの送信	  |メール設定に基づいてメールを送信します。  |はい  |
    |ヒューマンタスク - 作成 |指定されたタスク定義に基づいて承認タスクまたはアクションタスクを作成します。タスクが作成されると、アクティビティとワークフローは、タスクが所有者によって完了/承認/拒否されるか、エラーが出力されるか、期限切れになるまで待機状態になります。   |いいえ |
    |待機		  |ワークフロー実行の意図的な一時停止を示します。実行時に、ワークフローは待機アクティビティを検出すると実行を一時停止します。待機アクティビティには、タイムアウトを指定できます。指定した時間が経過すると、ワークフローの実行が再開されます。待機アクティビティは、待機アクティビティの静的IDとワークフロー・インスタンスIDをパラメータとして渡して apex_workflow.continue_activity() を呼び出すことによっても中断できます。  |いいえ  |
    |切替え		 |ワークフロー実行におけるフォークまたは条件分岐を表します。スイッチ アクティビティを追加した後、開発者は、適切な条件を指定して、スイッチ アクティビティから分岐または接続を定義する必要があります。 |はい  |
    |ワークフロー開始	  |ワークフローの開始を表します。ワークフローモデルには、開始アクティビティが 1 つだけ必要です。	  |はい  |
    |ワークフローの終了		 |ワークフローの終了を表します。このアクティビティの「終了状態」属性に応じて、ワークフローは「完了」または「終了」になります。 |はい  |

    接続タイプは以下のようになります。
    |アクティビティの種類  |説明  |
    |---|---|
    |正常	 |ワークフロー内の2 つのアクティビティを接続します。切替えアクティビティには適用されません  |
    |API呼び出しタイムアウト		  |アクティビティに期限が指定されている場合、現在のアクティビティの実行がタイムアウトした場合、タイムアウト接続を使用してワークフローを代替のターゲットアクティビティに移動できます。切替えアクティビティと待機アクティビティには適用されません。 |
    |エラー		 |ワークフローを例外状態にすることなく、ワークフロー内でエラー管理を可能にします。切替えアクティビティには適用されません。|
    |切替え接続	  |切替えアクティビティからつながる接続  |
* 医師の予約ワークフローへの入力の追加

構築中の診療予約アプリケーションをもとに、パラメータ、変数、アクティビティ変数、追加データの理解していきます。

予約プロセスは、病院スタッフが患者の詳細をシステムに送信して、ワークフローを開始しています。

ワークフローへ入力するのは、患者の詳細です。ワークフローの用語では、ワークフローパラメーターといいます。Doctor Appointment ワークフローのパラメーターを次のように定義します：
PATIENT_NAME、PATIENT_AGE、PATIENT_EMAIL、問題、REQUEST_DATE

1. ワークフローパラメータを作成するには、画面左のレンダリングツリーでDoctor Appointment Workflowを右クリックし、[パラメータの作成]を選択します。
    ![APEXワークフロー_3-11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3058002/d089b51a-ba85-a2c6-da7b-41b339588137.png)

1. ツリーに「新規」というラベルの付いたパラメータが追加されます。プロパティエディタで、以下を入力します。
ラベル:　Patient Name
静的 ID: PATIENT_NAME
データ型: VARCHAR2
    ![APEXワークフロー_3-12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3058002/94da8bfc-0e5f-6094-4f46-c360c63e40bf.png)

1. 同様にして、以下の表のようにパラメータを追加します。

    |静的ID  |ラベル  |データ型  |アプリケーション書式マスク> セッション状態書式マスク|
    |---|---|---|---|
    |PATIENT_AGE |Patient Age  |Number ||
    |REQUEST_DATE  |Request Date  |Timestamp  |DD-MON-YYYY HH24:MI:SS|
    |PATIENT_EMAIL |Patient Email  |Varchar2  ||
    |PROBLEM  |Problem  |Varchar2  ||

* ワークフロー・サブジェクトのパラメータの使用
 ワークフローには、ワークフローの説明的なタイトルである [タイトル] フィールドがあり、病院のスタッフが最終的に予約プロセスの進行状況を監視するために使用するワークフローコンソールに表示されます。タイトルは、ワークフローパラメーターの静的IDを置換文字列として使用することをサポートしています。

1. ワークフロータイトルを設定するには、左側のレンダリングツリーでDoctor Appointmentワークフローを選択します。

1. プロパティエディタで、タイトルを「Doctor Appointment Workflow for patient &PATIENT_NAME」に変更します。
    ![APEXワークフロー_3-13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3058002/4e263409-7893-63c7-5a7c-d2f11d11f4df.png)

1. 保存を押します。

* ワークフローへのデータの追加

1. 入力パラメータに加えて、ワークフローには患者の問題に基づき、対応可能な医師に関する情報が必要になってきます。予約プロセスを理解すると、ワークフローのすべてのアクティビティに医師データが必要であることがわかります。

1. 追加データを定義するには、レンダリングツリーでワークフローバージョン1.0[Dev]をクリックします。

1. プロパティエディタで、以下を入力します。

    [追加データ] で、次の操作を行います。
    タイプ:　表/ビュー
    テーブル名: DOCTOR
    主キー列>列マッピング: DNAME
        ![APEXワークフロー_3-14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3058002/4ba3d575-510d-5b11-e65f-bfc5a4882260.png)

1. 保存を押します。
これで、DOCTOR表のすべての列の値が、ワークフローの実行中のすべてのポイントやアクティビティでバインド変数または置換文字列として使用可能になります。

* ワークフロー変数の追加

ワークフローに提供される入力は読み取り専用です。つまり、ワークフローが送信されると、その値は変更されません。ワークフローではデータを処理し、あるアクティビティから次のアクティビティに渡していく必要があります。

例えば、次のシナリオについて考えてみます。

予約が確定する前に、ワークフローは医師のその時点の予約スケジュールに基づいて医師の空き状況を算出する必要があります。したがって、医師の空きはこの計算に基づいて決定される変数になります。

また予約が確定すると、予約ID は料金の計算や更新をするアクティビティに引き継がれます。

空き状況、予約ID、料金などのデータは、ワークフローのアクティビティによって更新できるようにする必要があります。このようなデータは、ワークフロー変数として定義されます。

1. レンダリングツリーで1.0 [開発](ワークフローバージョン)を右クリックします。

1. [変数の作成] を選択します。
    ![APEXワークフロー_3-15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3058002/2c52be8a-361c-a5a3-1ec7-b5bab945a58a.png)

1. 新規の新しい変数がツリーに作成されます。それをクリックし、プロパティエディタで以下を入力します。
    
    静的ID >識別:BOOKING_ID
    
    ラベル > ラベル: Booking ID
    
    変数>データ型: NUMBER
    
    値>型: NULL　
        予約IDは、予定が確認された後、ワークフローの後半でのみ生成されることがわかっているめ、NULLに初期化する必要があるためです。

    

1. 同様に、Availabilityという変数を作成します。プロパティエディタで以下を入力します。

    データ型: BOOLEAN
    値>型: NULL
    「アプリケーションの書式マスク」で、次のように設定します。
    True値: AVAILABLE
    False値: BUSY
    ![APEXワークフロー_3-17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3058002/9a04d84e-0130-66a6-a234-d67d8b47b03c.png)

1. 最後に変数Feeを作成します。プロパティエディタで以下を入力します。

    データ型: NUMBER
    [値] にて、
    タイプ: 静的値
    静的な値: 0
    ![APEXワークフロー_3-18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3058002/aab6c0a0-7758-0a05-7518-fa7e176b6f20.png)

1. 保存を押します。


# まとめ
これで、Oracle APEXを使用して医師予約ワークフローを作成するプロセスができました。
次回は、ヒューマンタスクをワークフローに追加していきます。
# 参考情報
 * [Oracle Live labs- Simplify Business Process Management Using APEX Workflows](https://apexapps.oracle.com/pls/apex/r/dbpm/livelabs/run-workshop?p210_wid=3880)