2022年7月8日金曜日

APEX 22.1の承認コンポーネント(3) - ユーザー・データの利用

 前回の記事では、承認コンポーネントを利用して休暇申請の承認と却下のフローを実装しました。しかし、これだけではアプリケーションとして動作はしますが、データとしては何も残りません。

休暇申請を保存する表を作成し、申請とその結果を保存するようにします。

SQLワークショップユーティリティに含まれているクイックSQLを使って、以下のモデルから表UNI_REQUETSを作成します。

uni_requests
    reason vc160
    start_date
    end_date
    status vc10
    requestor vc80
    request_date
    approver vc80
    approve_date

上記のモデルをクイックSQLに記述し、SQLの生成SQLスクリプトの保存レビューおよび実行を順次実施します。


この後、生成されたSQL(DDL)の即時実行までを実施します。アプリケーションはすでに作成済みのものを改変するため、アプリケーションの作成は行いません。

共有コンポーネントのタスク定義休暇申請を開き、タスクにたいして実施する操作をユーザーが用意した表UNI_REQUESTSに反映されるよう、設定を変更します。


設定アクション・ソースとして、表UNI_REQUESTSを指定します。

アクション・ソースとしてを選択します。表の所有者はデフォルトの解析対象スキーマ表名としてUNI_REQUESTS(表)を選択します。アクション表の主キー列として、表UNI_REQUESTSの主キー列である、ID(Number)を選択します。


この設定は、アクション・ソースSQL問合せを選択し、アクションSQL問合せとして以下を記述したことと同じです。

select * from uni_requests where id = :APEX$TASK_PK


アクション・ソースに複数の表を含めたい場合などは、アクション・ソースとしてSQL問合せを選びます。

設定件名は、変更したアクション・ソースを使うようにします。

&REASON. &START_DATE. - &END_DATE. &REQUESTER.

この後に定義済みのパラメータをすべて削除するため、&AC_REASON.&AC_START_DATE.および&AC_END_DATE.の代わりに表UNI_REQUESTSから得られる値&REASON.&START_DATE.および&END_DATE.を使います。標準の置換文字列である&APEX$TASK_INITIATOR.は有効ですが、表UNI_REQUESTSに登録されている申請者を確認できるように、表UNI_REQUESTSに保存されている値である&REQUESTER.を使います。


以上の変更は、アプリケーションの動作と見かけには影響しません。

参加者潜在的所有者を、静的な割り当てからPL/SQLのファンクション本体による割り当てに変更します。実用面を考慮すると、潜在的所有者を静的に割り当てるケースは稀でしょう。

今回は簡単に、申請花子さんの休暇申請は承認花子さんが承認者(所有者 - Actual Owner)、申請太郎さんの休暇申請の承認者は承認太郎さんが承認者となるようにします。それ以外管理者が承認します。

以下のコードを実装します。
begin
  if :APEX$TASK_INITIATOR = '申請太郎' then
      return '承認太郎';
  elsif :APEX$TASK_INITIATOR = '申請花子' then
      return '承認花子';
  else
      return '管理者';
  end if;
end;

パラメータはすべて削除します。

今回は、全てのパラメータを永続的に保存することにしました。タスクの作成から完了まで値を保持できればよく、タスクの完了後にタスクとともにパージされて問題がなければ、パラメータを利用します。


タスクが承認されたとき、または却下されたときに実行されるアクションを作成します。

アクションの追加をクリックします。


休暇申請が承認されたときに実行するコードを登録します。

アクション名前承認タイプとしてコードを実行を選択します。コードの実行の他に電子メールの送信を選ぶこともできます。アクションが実行されるタイミングであるイベント時として、完了を選択します。完了以外に、タスクにたいして行われる操作に対応した、作成要求委任取消リリースコメントの更新優先順位の更新も選択することができます。結果として承認済を選択します。

コードには以下を記述します。
begin
    update uni_requests 
        set status = 'APPROVED'
        , approver = :APEX$TASK_OWNER
        , approve_date = sysdate
    where id = :APEX$TASK_PK;
    commit;
end;
以上の設定で、タスクが承認されたときに表UNI_REQUESTSに保存されている休暇申請の列STATUSAPPROVEDとして更新されます。それと同時に承認者(APPROVER)と承認日(APPROVE_DATE)も更新されます。


却下のアクションでは、結果却下済を選択し、コードは列STATUSREJECTEDを更新するようにします。
begin
    update uni_requests 
        set status = 'REJECTED'
        , approver = :APEX$TASK_OWNER
        , approve_date = sysdate
    where id = :APEX$TASK_PK;
end;

以上で、タスク定義の更新は完了です。

Autonomous Databaseの場合、SYSDATEはUTCでの時刻を返します。ローカル(例えば東京)の時刻を扱う場合は、セッション・パラメータSYSDATE_AT_DBTIMEZONEが有効になるように構成します。このような場合にアクションのコード中でSYSDATEを扱う際には、SYSDATE_AT_DBTIMEZONEを明示的に有効にする必要があるかもしれません。
begin
    execute immediate 'alter session set sysdate_at_dbtimezone = true';
    update uni_requests 
        set status = 'APPROVED'
        , approver = :APEX$TASK_OWNER
        , approve_date = sysdate
    where id = :APEX$TASK_PK;
    commit;
end;
タイムゾーン付きの日付データ型(TIMESTAMP WITH LOCAL TIME ZONE, TIMESTAMP WITH TIME ZONE)を使用している場合は、こういった考慮は不要になります。

休暇申請を行うページを修正します。タスクの生成に加えて、表UNI_REQUESTSへの休暇申請の書き込みを行い、生成したタスクと紐づけます。

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

リージョン休暇申請を選択し、作成済みのページ・アイテムをすべて選択し削除します。ボタンB_SUBMITはそのまま残します。


リージョン休暇申請識別タイプを、静的コンテンツからフォームへ変更します。その後、ソース表名としてUNI_REQUESTSを選択します。


ページ・アイテムP1_IDを選択し、識別タイプ非表示に変更します。ソース主キーONにします。このページ・アイテムが、作成した休暇申請(表UNI_REQUESTSに含まれる1行)を一意に特定する値を保持します。


ページ・アイテムP1_STATUSP1_APPROVERP1_APPROVE_DATEを選択し、ビルド・オプションを使ってコメント・アウトします。これらのページ・アイテムに対応する列STATUS、APPROVER、APPROVE_DATEはタスクのアクションによって設定され、画面からは設定しません。


ページ・アイテムP1_REQUESTERを選択し、タイプ非表示にします。デフォルトタイプアイテムを選択し、アイテムAPP_USERを指定します。休暇申請の申請者は、かならずサインインをしているユーザーになります。


ページ・アイテムP1_REQUEST_DATEにも同様にデフォルト値を設定します。識別タイプ非表示にし、デフォルトタイプとして言語SQLを選択し、SQL式としてsysdateを記述します。


ボタンB_SUBMITを選択し、動作データベース・アクションとしてSQL INSERT操作を選択します。今回のフォームは休暇申請の作成のみを行います。この処理は、表UNI_REQUESTSへのINSERT処理に当たります。


プロセス・ビュー
に移り、タスクを作成するプロセス休暇申請を選択します。設定ディテール主キー・アイテムとしてP1_IDを選択します。この指定により作成されるタスクと表UNI_REQUESTS内の行が紐づけられます。


作成されたタスクは標準ビューAPEX_TASKSより確認できます。ディテール主キー・アイテムとして与えられた値は、列DETAIL_PKから確認できます。

select task_id, subject, detail_pk from apex_tasks where task_def_static_id = 'LEAVE_REQUEST' and detail_pk is not null;


フォームを処理するプロセスを追加します。プロセスを新たに作成し、休暇申請の上に配置します。

識別名前プロセス - フォーム休暇申請とします。タイプとしてフォーム - 行の自動処理(DML)を選択し、フォーム・リージョンとして休暇申請を選びます。サーバー側の条件ボタン押下時B_SUBMITを指定します。


以上で、一通りの実装は完了しました。

表UNI_REQUESTSの内容を確認するために、対話モード・レポートのページを作成します。

ページ作成ウィザードを起動し、対話モード・レポートを選択します。


名前休暇申請一覧とします。ページ・モード標準、編集はしないのでフォーム・ページを含めるOFFです。データ・ソース表/ビューの名前としてUNI_REQUESTSを選択します。このページをサイド・メニューから呼び出せるように、ナビゲーションONにし、ナビゲーションのプリファレンスとして、エントリを新規作成しますを選択します。

おおむね以上の設定にて、ページの作成を実行します。


作成したページを実行すると、以下のように表示されます。


承認コンポーネント単体で複雑な処理フローを実装できるわけではありませんが、タスク定義を作成することにより、画面上のボタンを押したときに実行する処理を、プロセスとしてページに作成する以外に、タスク定義のアクションとして作成することができるようになりました。結果として、複数のページより実行する処理を、タスク定義アクションにまとめることが可能になっています。今回の例でいうと、私のタスクから休暇申請を承認できるし、タスク詳細のページから休暇申請を承認することもできますが、実行されるコードはタスク定義アクションとして記述されています。アクションのコードを変更すると、どのページで承認を行うかにかかわらず、すべての承認処理が変更されます。

今回作成したAPEXアプリケーションを以下に起きました。
https://github.com/ujnak/apexapps/blob/master/exports/simple-approvals-component-ud.sql

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