2022年7月7日木曜日

APEX 22.1の承認コンポーネント(2) - 基本的なアプリケーションの作成

前回の記事で紹介した、承認コンポーネントを使った休暇申請アプリケーションの作り方を記述します。承認コンポーネントが提供している機能に注目するため、承認コンポーネントとして提供されている機能だけでアプリケーションを作成します。

Always FreeのAutonomous Databaseで動作しているAPEX 22.1を使って作業を行なっています。

アプリケーション作成ウィザードを起動し、空のアプリケーションを作成します。アプリケーションの名前休暇申請とします。

アプリケーションの作成を実行します。

アプリケーションが作成されます。

承認コンポーネントのデモに使用するユーザーをワークスペースに登録したくないので、このアプリケーションにカスタム認証スキームを作成します。

サインインする際に与えられるユーザー名が、申請太郎申請花子承認太郎承認花子管理者のどれかであれば、アプリケーションへのサインインを許可します。

共有コンポーネント認証スキームを開きます。


作成済みの認証スキームが一覧されます。Oracle APEXアカウントがデフォルトで作成され、カレント(現在適用中)の認証スキームとなっています。

作成をクリックします。


スキームの作成として、ギャラリからの事前構成済スキームに基づくを選択します。

へ進みます。


ユーザー認証を行なうPL/SQLコードを登録します。

認証スキームの名前固定ユーザー認証とします。スキーム・タイプカスタムを選択します。設定認証ファンクション名uni_authとします。

ソースPL/SQLコードに、認証ファンクションであるuni_authを記述します。以下のコードがファンクションuni_authの内容です。デモに使用するユーザー名に一致すると認証は成功します。パスワードは考慮していません。
function uni_auth (
    p_username in varchar2,
    p_password in varchar2 )
    return boolean
is
begin
    if p_username in ('申請太郎', '申請花子', '承認太郎', '承認花子','管理者') then
        return true;
    end if;
    return false;
end;
以上を入力し、認証スキームの作成をクリックします。


認証スキーム固定ユーザー認証が作成され、カレント(現在適用中)になります。


続いて休暇申請をタスクとして定義します。共有コンポーネントを開きます。

ワークフローと自動化のセクションに含まれる、タスク定義を開きます。

作成済みのタスク定義が一覧されます。作成をクリックします。


タスク定義の作成を行なうダイアログが開きます。名前休暇申請件名として以下を入力します。件名は、誰がどのような休暇申請を行なったのか、人が読んで一意に認識できるような文字列にします。あまり長くしない方が良いでしょう。

&AC_REASON. &AC_START_DATE. - &AC_END_DATE. &APEX$TASK_INITIATOR.

静的IDとしてLEAVE_REQUESTを指定し、デフォルトの優先度2-高を選択します。潜在的所有者およびビジネス管理者は、タスクが作成された後に設定するため、このダイアログでは空白とします。

以上を入力し、作成をクリックします。


タスク定義が作成されます。この定義に含まれる期日間隔参加者パラメータを設定します。


期日間隔として30分後を指定します。

パイプライン表関数であるAPEX_APPROVAL.GET_TASKSが返す列DUE_ONDUE_IN_HOURSDUE_INDUE_CODEの値は期日間隔の指定により決まります。調べた範囲では、期限を過ぎたとしても発生するイベントはなく、アクションを呼び出すことはできないようです。


参加者の設定を行います。

参加者タイプ潜在的所有者値タイプ静的を選択し、承認太郎を入力します。行を増やす場合は、行の追加をクリックします。同様に潜在的所有者として、承認花子を追加します。参加者タイプビジネス管理者を選択し、値タイプ静的管理者を入力します。

以上の3行を入力します。


参加者値タイプとして静的以外に、SQL問合せファンクション本体を選択することができます。休暇を申請した人によって承認者を変えるといった要件がある場合は、これらの指定を使って実行時に潜在的所有者を決定します。

タスクに紐づける情報をパラメータとして設定します。

申請理由AC_REASON開始日としてAC_START_DATE終了日としてAC_END_DATEをパラメータとして定義します。それぞれ、ラベルまたは静的IDとして指定します。データ型として選択できるのは、現状は文字列のみです。必須および表示可能の値は、すべての列でYesとします。


以上を設定し、変更の適用をクリックします。


タスク定義休暇申請が作成されます。


ページ・デザイナホーム・ページを開きます。

作成したタスク定義休暇申請から、タスクを作成する機能を実装します。

Bodyリージョンを作成します。識別タイトル休暇申請タイプ静的コンテンツです。このリージョンにページ・アイテムやボタンを配置します。


申請理由を入力するページ・アイテムを作成します。識別名前P1_REASONタイプテキスト・フィールドを選択します。ラベル理由とします。


開始日を入力するページ・アイテムを作成します。識別名前P1_START_DATEタイプ日付ピッカーを選択します。ラベル開始日とします。


終了日を入力するページ・アイテムを作成します。識別名前P1_END_DATEタイプ日付ピッカーを選択します。ラベル終了日とします。


ページ・アイテムを送信し、タスクを作成するボタンを作成します。

識別ボタン名B_SUBMITラベル送信とします。動作アクションページの送信です。


プロセス・ビューを開き、プロセスを作成します。

識別名前休暇申請タイプとしてヒューマン・タスク - 作成を選択します。設定定義に、共有コンポーネントのタスク定義として作成済みの休暇申請を選択します。成功メッセージとして、「休暇申請を受け付けました。」と記述し、サーバー側の条件ボタン押下時B_SUBMITを指定します。

今回作成するアプリケーションは承認コンポーネントだけを使うため、これ以外の属性は設定しません。実際には、ディテール主キー・アイテムは、ユーザー・データとタスクを紐づけるために必須の設定になります。


タスク定義休暇申請で設定したパラメータと、ページ・アイテムを紐づけます。

パラメータ開始日を選択し、タイプとしてアイテムアイテムとしてP1_START_DATEを選択します。


同様にパラメータ終了日をページ・アイテムP1_END_DATEに紐づけます。


最後にパラメータ申請理由をページ・アイテムP1_RESONに紐づけます。


以上で、ホーム・ページから休暇を申請できる(休暇申請のタスクを作成できる)ようになりました。

アプリケーションを実行して、休暇を申請してみます。

アプリケーションを実行します。

サインインの画面です。ユーザー名申請花子と入力し、サインインをクリックします。


ホーム・ページに休暇申請を実装しているため、サインイン後に休暇申請画面が開きます。

理由、開始日、終了日に適当な値を入力し、送信をクリックします。

休暇申請を受け付けました。と画面に表示されます。


今のところ、作成された休暇申請(タスク定義休暇申請を元に作成されたタスク)の情報を参照したり操作したりする画面がありません。そのため、これ以上、このアプリケーションを使ってできることがありません。

これより、承認コンポーネントとともに提供されている統合タスク・リストのページとタスク詳細のページを、ウィザードを使って作成します。

ページの作成をクリックし、ページ作成ウィザードを呼び出します。


コンポーネントから統合タスク・リストを選択します。


統合タスク・リストの作成のダイアログにて、名前自分で開始とし、レポート・コンテキストとして自分で開始を選択します。ナビゲーションブレッドカラムおよびナビゲーションは双方デフォルトのONのまま変更しません。

以上の設定で、ページの作成を実行します。


同じ手順を繰り返します。名前私のタスクレポート・コンテキストマイ・タスクの統合タスク・リストのページを作成します。


名前管理レポート・コンテキスト管理タスクの統合タスク・リストのページも作成します。


以上でアプリケーションを実行します。

申請花子でサインインし、作成済みの休暇申請を確認します。

自分で申請のページを開きます。タスク詳細のページが未作成であるため、件名からタスク詳細を参照することができません。


共有コンポーネントのタスク定義休暇申請を開き、タスクの詳細ページの作成をクリックします。確認のダイアログが開くので、OKをクリックします。


タスク詳細のページが生成され、タスク定義の一覧画面へ戻ります。再度、休暇申請のタスクを開くと、タスクの詳細ページのURLに値が設定されていることが確認できます。


タスク詳細のページが作成されたので、統合タスク・リストに表示されている休暇申請(タスク)より、タスクの詳細画面を表示できるようになります。


以上で承認コンポーネントだけを使ったアプリケーションの作成は完了です。

このアプリケーションにより、タスクの作成(つまり休暇申請の作成)、要求、承認、却下、委任、優先度の変更、コメントの追加、取消、リリースといった、タスクに対して行える操作はすべて実行できます。

次の記事では、ここで実装されたタスクと、ユーザー・アプリケーションの連携を実装してみます。

2022年7月6日水曜日

APEX 22.1の承認コンポーネント(1) - 概要

 Oracle APEX 22.1より承認コンポーネント統合タスク・リストという機能が追加されています。この機能を使ってみます。

承認コンポーネントが提供する機能を理解するために、承認コンポーネントによって提供されている機能のみを使用したOracle APEXのアプリケーションを作成しました。

https://apex.oracle.com/pls/apex/r/japancommunity/simple-approvals-component/

アプリケーションのエクスポートは以下に置いています。
https://github.com/ujnak/apexapps/blob/master/exports/simple-approvals-component.sql

作成したサンプルを使って、以下の操作を行なってみます。

  1. 申請花子さんが、ホーム・ページから夏季休暇を申請します。
  2. 申請花子さんは、申請後、自分で開始(これは統合タスク・リストです)のページを開き、作成されたタスクを一覧します。
  3. 申請花子さんは、件名をクリックしタスクの詳細を開き、この申請の所有者が割り当たっていない(状態としては未割当て)ことを確認します。
  4. 承認太郎さんが、私のタスクのページを開き、申請された休暇を確認します。承認太郎さんは休暇申請を承認する権限を持っているため(タスク定義潜在的所有者 - Potential Owner - となっている)、私のタスク(これも統合タスク・リストです)に申請花子さんによる夏季休暇の申請が表示されます。まだタスクは未割当ての状態です。
  5. 管理者は、申請花子さんによる夏季休暇の申請の承認者として、承認太郎さんを割り当てます(委任 - Delegate - を実行しています)。承認太郎さんがこの申請の所有者 - Actual Owner - になります。
  6. 承認太郎さんが私のタスク統合タスク・リスト)のページを開き、申請花子さんによる夏季休暇の申請を承認します。
  7. 申請花子さんが、自分で開始のページを開いて結果を確認します。タスクの詳細を開き、履歴を参照することで、承認までの経緯が分かります。
タスクの潜在的所有者 - Potential Owner - は、そのタスクに要求 - Claim - を実行することにより所有者 - Actual Owner - になれます。所有者になる(タスクが割り当てられるともいいます)と、タスクに対して承認 - Approve - や却下 - Reject - を実行できるため、管理者による委任は不要になります。

実際は、管理者による委任が行われることは稀で、潜在的所有者要求を行い所有者となり、承認または却下を実施するでしょう。

上記の操作を実施している動画です。


上記のアプリケーションを構成しているページについて説明します。

ホーム・ページは、通常の静的コンテンツのリージョンに、ページ・アイテムとボタンを配置しています。ボタン送信B_SUBMIT)をクリックしたときに、タスク定義からタスクを作成します。


ボタンを押した時に実行するプロセスを作成し、識別タイプとしてヒューマン・タスク - 作成を選択します。これは、承認コンポーネントとして、APEX 22.1で新たに追加されたプロセスのタイプです。


自分で開始のページです。


ページ作成ウィザードにて統合タスク・リストを選択して作成します。

APEX 22.1では、ページ作成ウィザードコンポーネントに、統合タスク・リストが追加されています。


統合タスク・リストのページを作成する際に、レポート・コンテキストを選択します。レポート・コンテキストの選択肢として、以下の3種類からひとつを選択できます。
  1. 自分で開始 - 作成者が自分自身であるタスクを一覧する。
  2. マイ・タスク - 自分自身が潜在的所有者(または所有者)であるタスクを一覧する。(自分自身が潜在的所有者であっても、作成者でもあるタスクは一覧から除外されます。自分が作成したタスクを自分自身で承認または却下することはできません。
  3. 管理タスク - すべてのタスクを一覧する。
自分で開始のページは、レポート・コンテキストとして自分で開始を選択して作成しています。


作成されたページは、スマート・フィルタカード・リージョンより構成されます。ページ作成ウィザードが、APEXの標準コンポーネントを組み合わせてページを作成しています。作成されたページは、Oracle APEXの通常の手順(プロパティの変更、CSSの追加やJavaScriptの記述)にて、見栄えや動作をカスタマイズすることが可能です。


私のタスクのページも同様に、統合タスク・リストのページとして作成しています。レポート・コンテキストマイ・タスクを選択しています。


マイ・タスクでの一覧では、承認および却下を行なうボタンが表示されます。実際の処理は動的アクションとして実装されています。動的アクションで呼び出されるサーバー側の処理の中で、APEX 22.1で新設されたパッケージAPEX_APPROVALのプロシージャを呼び出しています。


管理ページは、統合タスク・リストのページを作成する際に、レポート・コンテキストとして管理タスクを選択して作成します。


統合タスク・リストに一覧されているタスクの件名をクリックすると、タスクの詳細ページが画面右側にドロワーとして開きます。


このページは、共有コンポーネントタスク定義設定に含まれる、タスクの詳細ページの作成をクリックして作成します。


作成されたページでは、タスクに実行できるさまざま処理(要求承認却下リリース委任優先度の設定)を実行するプロセスが作成されています。これらのプロセスのタイプはAPEX 22.1で新設されたヒューマン・タスク - 管理です。


Oracle APEXで新しく追加された承認コンポーネントですが、実質的に追加されているのは以下になります。
  1. ページ作成ウィザードによる統合タスク・リストのページ生成機能
  2. タスクの詳細ページの生成機能
  3. プロセス・タイプヒューマン・タスク - 生成ヒューマン・タスク - 管理
  4. パッケージAPEX_APPROVAL
  5. 実行時ビューAPEX_TASKSその他
  6. 共有コンポーネントのタスク定義
タスク定義は、共有コンポーネントワークフローと自動化に含まれます。


承認コンポーネントを使用するには、最初にタスク定義を作成します。

参加者としタスクを操作できる人を設定します。参加者である潜在的所有者として、それぞれのユーザーを直接登録することもできますし、SQLの結果などから動的に登録することもできます。ただし、潜在的所有者はタスクが作成されたときに評価されます。タスクの生成後にタスク定義を変更し潜在的所有者を増やしても、すでに作成済みのタスクは影響を受けません。(実行時に潜在的所有者を追加するには、APEX_APPROVAL.ADD_TASK_POTENTIAL_OWNERを呼び出します。)

ビジネス管理者の登録も、参加者のセクションに含まれます。

潜在的所有者がひとりしかいない場合、タスクが作成された直後にその潜在的所有者がタスクにアサインされます。作成されたタスクは即座に割り当てられた状態になり、未割当ての状態はスキップされます。

パラメータはタスクに紐づいて保存される値になります。タスクにはライフサイクルがあり、承認却下キャンセルまたはエラーが発生すると終了します。一定期間を経過すると、終了したタスクは削除されます。タスクに紐づいているパラメータも同時に削除されます。そのため、パラメータは承認や却下の判断に使うが、永続的に保存する必要がない値を保持するために使用します。

アクションとは、サーバー側でのPL/SQLコードの実行もしくはメールの送信処理です。タスクに対して要求や完了(承認または却下)といった操作が行われたときに、それらのイベントに紐付けられたアクションが実行されます。

タスク定義を元に作成されたタスクの状態は、Oracle APEXが管理します。アクションによって、タスクとAPEXアプリケーションとの連携が実装されます。


では、この承認コンポーネントを使ってどのようなワークフローが実装できるのか、について説明します。この点については、Oracle APEXのマニュアルの19.6.8 About Task States and Transitionsが参考になります。

タスク定義を元に作成されるタスクがとり得る状態は、以下の状態遷移図で定義されてます。アプリケーション開発者による変更はできません。

主なフローだけに注目すると、作成されたタスクは、誰かにアサインされて、その人がタスクを承認か却下することにより終了します。


つまり、承認コンポーネントはその名の通り、承認を行なうためのコンポーネントであり、任意のワークフローが実装できるわけではありません。

ワークフローを設計し、Oracle APEXのアプリケーションに組み込むために、Flows for APEXが利用できます。また、Oracle APEXが提供している承認コンポーネントは、Oracle APEXの将来バージョンにて、Flows for APEXで定義したワークフローに含まれる、ひとつのステップとして活用できるようになる予定です。

次の記事では、今回のサンプル・アプリケーションの作成手順を紹介します。

2022年7月4日月曜日

ページ・アイテムの値を次に開くページに引き継ぐ方法について

 フォームを開いているページから別のページのフォームを開く際に、ページ・アイテムの値を引き継ぎたいケースがあります。この機能を実装するにあたって、以下の3つの方法について紹介します。

  1. ターゲットに指定する。
  2. コレクション(APEX_COLLECTION)を使う。
  3. セッション・ステートに保存する。
サンプルとなるアプリケーションを作成し、実行した結果です。


おおむね、同じ動作をするように実装しています。


サンプル・アプリケーションについて



送信元となるページには、以下のように4つのページ・アイテムを作成しています。P1_NUM(数値フィールド)P1_DATE(日付ピッカー)P1_TEXT(テキスト・フィールド)P1_HTML(リッチ・テキスト・エディタ、書式はHTML)です。

ページを送信するボタンとしてB_SUBMITを作成しています。動作アクションページの送信になります。


ボタンB_SUBMITをクリックして送信したページ・アイテムの値は、移動先のページのページ・アイテムに設定します。ページ・アイテム名のページ番号部分は変わりますが、それ以外は同じ設定のページ・アイテムです。


コレクションとセッション・ステートは、ぞれぞれ別のページに実装しています。ページ・アイテムの名前はコレクションの場合はP3P4、セッション・ステートの場合はP5P6で始まります。


ターゲットに指定する



送信元のページのプロセス・ビューを開き、ブランチを作成します。動作タイプページまたはURL(リダイレクト)を選択し、ターゲットとして移動先となるページを指定します。

サーバー側の条件として、ボタン押下時B_SUBMITを選択します。


ターゲットアイテムの設定として、移動先のページ・アイテムに渡す値を指定します。


実際のブランチの処理は、以下のURLへのリダイレクトになります。

f?p= &APP_ID.:2:&SESSION.::&DEBUG.:2:P2_NUM,P2_DATE,P2_TEXT,P2_HTML:\&P1_NUM.\,\&P1_DATE.\,\&P1_TEXT.\,\&P1_HTML.\&success_msg=#SUCCESS_MSG#

ページ・アイテムの値を設定している部分に注目すると、以下のようにカンマでそれぞれのページ・アイテムの値が区切られています。

\&P1_NUM.\,\&P1_DATE.\,\&P1_TEXT.\,\&P1_HTML.\

カンマ(およびコロン:)がページ・アイテムの値に含まれると、ページ・アイテムの値はそこで終了と認識されます。バックスラッシュで囲むことにより、その間にあるカンマは区切り文字と認識されないようになります。

また、データが大きいとURLとしても長くなり、上限値(ブラウザ依存)を超える場合も起こり得ます。結果として意図した通りに値が渡されません。

ターゲットに指定して渡すページ・アイテムは、表の主キーなどに限定した方が良いでしょう。任意の文字列などを含めるのは避けるべきです。


コレクション(APEX_COLLECTION)を使う



送信するページ・アイテムとして、非表示のページ・アイテムP3_SEQ_IDを追加します。その値を受信するページ・アイテムとしてP4_SEQ_IDも追加します。


ボタンB_SUBMITを押し時に受け取ったページ・アイテムを、コレクションに保存するプロセスを作成します。

ソースPL/SQLコードとして、以下を記述します。
begin
    apex_collection.create_or_truncate_collection('SEND_ITEMS');
    :P3_SEQ_ID := apex_collection.add_member(
          p_collection_name => 'SEND_ITEMS'
          , p_c001 => :P3_TEXT
          , p_n001 => :P3_NUM
          , p_d001 => :P3_DATE
          , p_clob001 => :P3_HTML
      );
end;

コレクションSEND_ITEMSに、ページ・アイテムの値を保存します。コレクションに保存された値を取り出す際に使用するシーケンス番号を、ページ・アイテムP3_SEQ_IDに保存します。


ページの移動は、ターゲットで指定した場合と同様にブランチを使います。


ターゲットの設定では、シーケンス番号P3_SEQ_IDのみを移動先のページのページ・アイテムP4_SEQ_IDに渡します。


移動先のページに、コレクションに保存された値をページ・アイテムに設定するプロセスを作成します。

ソースPL/SQLコードは以下になります。
select c001, n001, d001, clob001
into :P4_TEXT, :P4_NUM, :P4_DATE, :P4_HTML
from apex_collections
where collection_name = 'SEND_ITEMS' and seq_id = :P4_SEQ_ID;

シーケンス番号を保持するページ・アイテムP4_SEQ_IDセキュリティセッション・ステート保護は、チェックサムが必要 - セッション・レベルを選択します。

コレクションの利用は一番手間がかかりますが、その分クリーンな実装になります。


セッション・ステートに保存する



送信元となるページ・アイテムのソースセッション・ステートの保持をセッションごと(ディスク)にすると、ページの送信の実行によってサーバーに送信されたページ・アイテムの値は、セッション・ステートとして保存されます。簡単にいうと、データベースにコミットされます


移動先のページでは、送信されたページ・アイテムの値を直接参照することができます。ブランチでのページ・アイテムの設定は不要です。

初期化を行うプロセスの、ソースPL/SQLコードは以下になります。
:P6_NUM  := :P5_NUM;
:P6_DATE := :P5_DATE;
:P6_TEXT := :P5_TEXT;
:P6_HTML := :P5_HTML;

ただし、このように設定すると送信元のページ・アイテムをどこのページからでも設定および参照できるようになります。送信元のページを編集するような場合、その影響を調べるのが難しくなります。

セッション・ステートの保持セッションごと(ディスク)に設定したページ・アイテムに、値を設定したり値を参照したりする処理は、できるだけ限定します。その方が、メンテナンスしやすいアプリケーションになります。

ページ・アイテムの値がセッション・ステートに保存されていると、ページを開いた時に以前の値がページ・アイテムに初期値として設定されます。ナビゲーション・メニューからページを開いたときに必ず空白にする場合は、ナビゲーション・メニューリスト・エントリターゲットキャッシュのクリアページ番号を指定します。


以上で、ページ・アイテムの値を引き継ぐ方法の紹介は終了です。

今回のサンプル・アプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/send-page-items.sql

Oracle APEXのアプリケーション作成の参考になれば幸いです。