グループ階層とプリンシパルを利用したワークフローの制御

最終更新日: 2021年3月3日
R8 | R9

概要

人事異動や組織改編といったケースに対応したワークフローの運用を行うために、Wagbyが提供するグループ階層とプリンシパルを組み合わせた承認者、決裁者の制御方法を紹介します。ワークフローではスクリプトを用いて承認者や決裁者を動的に決めることができます。

このページのポイントは次の二点です。

  • アカウントの所属グループにグループ階層を設定する。
  • フロー参加者スクリプトでグループ階層、プリンシパルを条件に承認者、決裁者を動的に決定する。

準備

アカウント

次に示すアカウントを用意しておきます。

アカウント 所属グループ プリンシパル
amuro営業一課年休申請者
kinjo営業一課年休承認者
miyagi営業二課年休申請者
shinjo営業二課年休承認者
onaga営業部年休決裁者

グループ階層

営業一課、営業二課、営業部は次のグループ階層とします。グループ階層の作成方法はこちらをお読みください。

図1 グループ階層

年休申請・承認・決裁ルール

今回の例は次のルールとします。

申請者

  • すべてのユーザ

承認者

  • 申請者の同一グループに所属
  • 「年休承認者」プリンシパルを持つ

決裁者

  • 申請者より1階層上位のグループに所属
  • 「年休決裁者」プリンシパルを持つ

このルールに準じたアカウント設定の例を示します。

役割 アカウント グループ プリンシパル
申請者amuro営業1課
承認者kinjo営業1課年休承認者プリンシパル有
決裁者onaga営業部年休決裁者プリンシパル有

フロー参加者にスクリプトを設定する

今回は「順次・順次・順次フロー」をベースに、スクリプトを設定した例を示します。

図2 スクリプトを使った設定

申請者のスクリプト

全ユーザーを申請可能とする場合は、次のように記述します。

return wman.getLogonUserOrApplicant(modelname, modelpkey, p); 
  • 暗黙変数wmanを利用します。wmanは、Wagbyが標準で提供するワークフロー管理クラス(WorkflowManagerクラス)のインスタンスです。
  • 暗黙変数 modelname と modelpkey はそれぞれモデルIDと、主キーの値を保持します。
  • "すべてのユーザ" を申請者として表現するためにwmanが提供するgetLogonUserOrApplicantメソッドを使います。このメソッドは新規登録の場合、ログオンユーザのuseridを返します。申請済みの場合は申請者のuseridを返します。

承認者のスクリプト

return $applicant.group().findUsers(By.principal("nenkyu_authorizer")).name(); 
  • 暗黙変数$applicantを利用します。この$applicantは申請者を表すUserElementクラスのインスタンスです。
  • 申請者と同一のグループに所属するユーザで、年休承認者(nenkyu_authorizer)のプリンシパルを持つユーザのuseridを返すという式になっています。
    • $applicant.group()は、申請者の所属グループ(GroupElement)を取得します。
    • findUsers(By.principal("nenkyu_authorizer")).name()で、グループに属するユーザーのうち、プリンシパル“nenkyu_authorizer”を持つユーザーのIDを取得します。

決裁者のスクリプト

return $applicant.group().parent().findUsers(By.principal("nenkyu_finalist")).name(); 
  • 申請者の所属グループの一階層上のグループに所属するユーザで年休決裁者(nenkyu_finalist)のプリンシパルを持つユーザの useridを返す式になっています。
    • $applicant.group().parent()は、申請者の所属グループの1階層上のグループ(GroupElement)を取得します。
    • findUsers(By.principal("nenkyu_finalist")).name()で、グループに属するユーザーのうち、プリンシパル“nenkyu_finalist”を持つユーザーのIDを取得します。

このようなスクリプトを利用することで、一つの参加者設定(式)で複数のグループに対する承認ルールを表現できます。

図3 スクリプトによって表現した内容

実行

1. 営業一課 amuro による申請

はじめに amuro が年休を申請しました。

2. 営業一課による承認

同一グループに所属する承認者 kinjo でログオンすると、承認ボタンが表示されます。

図4 承認者kinjoは承認できる

異なるグループ(営業二課)に所属する承認者 shinjo では、承認ボタンは表示されません。

図5 承認者shinjoはグループが異なるため承認できない

kinjo が承認しました。次は決裁者になります。kinjo は決裁者スクリプトの条件を満たさないため、承認ボタンは表示されません。

図6 kinjoの承認後。kinjoは次の決裁者ではないため承認ボタンは表示されない

3. 営業部による決裁

申請者の一つ上のグループ階層(営業部)に所属し、決裁者プリンシパルを持つ onaga でログオンすると、承認ボタンが表示されます。

図7 決裁者onagaは決裁できる

決裁者 onaga による決裁後、ワークフローは完了となります。

図8 決裁が完了した

スクリプト内で利用できる暗黙変数

ワークフローのスクリプトで利用できる暗黙変数は次の通りです。

変数名 説明
modelname 当該ワークフローのモデルID。 kianモデルで実行すると print(modelname) で "kian" と出力される。
modelpkey 当該ワークフローモデルデータの主キーの値。 主キー1000のデータで実行すると print(modelpkey) で "1000" と出力される。
p.user.username ログオンしているユーザーID。 "amuro"
p.request Javaが提供するrequestオブジェクト。 p.request.getParameter("foo") で、HTTPリクエストパラメータfooの値を取得できる。
p.errors Wagbyが提供するエラーメッセージを管理するオブジェクト。 -
p.appctx Springフレームワークが提供するApplicationContextオブジェクト。 p.appctx.getBean("ビーンID")で、Springが管理するオブジェクトを取得できる。
errorManager (エラーメッセージで解説) -
<モデルID> 当該ワークフローモデルのストアモデルオブジェクト。(*1) "年休申請(nenkyu)"の"理由(reason)"項目の値は nenkyu.reason で参照できる。
<モデルID_p> 当該ワークフローモデルのプレゼンテーションオブジェクト。
jfcworkstate 当該ワークフローモデルデータの申請状況。申請後のみ利用可能。承認履歴の最も最新の情報を表現するデータを保持している。
jfcworkstate.comment 直近のコメント
jfcworkstate.username 直近の処理者のユーザーID
jfcworkstate.updateDate 直近の処理日時
wman Wagbyが提供するワークフロー管理クラスWorkFlowManagerのインスタンス
JFCUtils,ExcelFunction 使い方はoperation-script-excelfunction.htmlを参照。
applicant 申請者のユーザーID。実体は juser オブジェクト。
$applicant 申請者の UserElement オブジェクト
$rootGroup 最上位のグループを表現するGroupElementオブジェクト
$allUsers すべてのアカウントを表現するUserElementオブジェクト

ストアモデルの詳細 (*1)

1. 標準では、ストアモデルオブジェクトは参照連動や自動計算の処理を行っていません。これはパフォーマンスを優先しているためです。参照連動項目を解決し、計算式の値を取得する場合は、次のように EntityService を経由してください。

var entityService = p.appctx.getBean("LeaveEntityService");
var leave = entityService.findById(modelpkey);

2. ここで得られるストアモデルは参照では利用できますが、更新では利用できません。[詳細...]

UserElementの詳細

UserElementは複数または単一のユーザーを表現するクラスとなっています。このクラスが提供する API は次のとおりです。

API 説明
group() 申請者の所属グループ (GroupElement) を取得する。複数の場合もある。
name() ユーザー名を取得する。複数のユーザー名の場合は区切り文字「|」で連結する。

GroupElementの詳細

上の API で説明した group() で取得できるグループは GroupElementとなっています。GroupElementは複数または単一のグループを表現するクラスです。このクラスが提供する API は次のとおりです。

API 説明
parent() 親グループを取得する。GroupElementが複数のグループの場合は、それぞれの親グループを取得できる。
children() 子グループを取得する。複数の場合もある。
descendants() 現在のグループと、すべての下位階層のグループを取得する。
jgroupid() グループID(数値)の配列を取得する。
userid() ユーザIDの配列を取得する。
users() グループに所属しているユーザーをUserElementとして取得する。GroupElementが複数のグループの場合は、それぞれのグループの所属ユーザーすべてを取得することができる。
findUsers() グループに所属しているユーザーを検索する。引数に By.principal を指定することができる。[後述]

$applicantの使い方

$applicantは申請者スクリプトでは利用できません。承認者および決裁者スクリプトで利用できます。

次の式は、申請者の1階層上位グループ(GroupElement)を取得します。

$applicant.group()

次の式は、申請者の所属グループのグループID(数値の配列)を取得します。

$applicant.group().jgroupid(); 

(例) 申請者がamuroの場合は1000となります。

図9 グループIDの取得

次の式は、申請者と同じグループに属するユーザー(UserElement)を取得します。

$applicant.group().users();

次の式は、申請者と同じグループに属するユーザーのユーザーIDを取得します。

$applicant.group().users().name();

(例) 申請者がamuroの場合は”amuro|kinjo”となります。

複数のユーザーIDは区切り文字「|」で連結される。
図10 グループに属するユーザIDの取得

次の式は、申請者と同じグループに属するユーザーのうち、プリンシパル“nenkyu_authorizer”を持つユーザー (UserElement)のユーザーIDを取得します。

$applicant.group().findUsers(By.principal("nenkyu_authorizer")).name();

ワークフローのスクリプトで、By.principal を使ってプリンシパルで絞り込みを行うことができます。

By.principal("プリンシパル名")
By.principal("プリンシパル名","プリンシパル名",...)

複数のプリンシパル名をコンマ区切りで指定することもできます。

(例) 申請者がamuroの場合は”yamashiro”となります。

図11 プリンシパルを指定したユーザIDの取得

次の式は、申請者の所属グループの1階層上のグループ(GroupElement)のグループIDを取得します。

$applicant.group().parent().jgroupid();

(例) 申請者がamuroの場合は”2000”(営業部)となります。

図12 上位グループのグループIDの取得

次の式は、申請者の所属グループの1階層上のグループに属するユーザー(UserElement)のユーザーIDを取得します。

$applicant.group().parent().users().name();

(例) 申請者がamuroの場合は “uehara|onaga” となります。

複数のユーザーIDは区切り文字「|」で連結される。
図13 上位グループのグループに属するユーザIDの取得

次の式は、申請者の所属グループの1階層上のグループおよび、その下位階層グループ(GroupElement)のグループIDを取得します。

$applicant.group().parent().descendants().jgroupid();

(例) 申請者がamuroの場合は1000,1001,2000(営業1課、営業2課、営業部)となります。

複数のグループIDは区切り文字「|」で連結される。
図14 上位グループおよび下位グループのグループIDの取得

次の式は、申請者の所属グループの1階層上のグループおよび、その下位階層グループに属するユーザー(UserElement)を取得します。

$applicant.group().parent().descendants().users().name();

(例) 申請者がamuroの場合は “uehara|onaga|yamashiro|kinjo|amuro|shinjo|miyagi|taira” となります。

複数のユーザIDは区切り文字「|」で連結される。
図15 上位グループおよび下位グループに属するユーザIDの取得

次の式は、申請者の所属グループの1階層上のグループおよび、その下位階層グループに属するユーザー(UserElement)のうち、プリンシパル"nenkyu_finalist"を持つユーザーを取得します。

$applicant.group().parent().descendants().findUsers(By.principal("nenkyu_finalist")).name(); 

(例) 申請者がamuroの場合は”taira”となります。

図16 上位グループおよび下位グループに属するユーザのうち、指定されたプリンシパルをもつユーザIDの取得

次の式は、申請者の所属グループの1階層上のその下位階層グループ(GroupElement)のグループIDを取得します。

$applicant.group().parent().children().jgroupid();

(例) 申請者がamuroの場合は1000,1001 ("営業1課"、"営業2課")となります。"営業部" は含まれません。

図17 上位グループおよび下位グループに属するグループIDの取得

$allUsersの使い方

次の式は、全ユーザーのうちのうち、プリンシパル"nenkyu_finalist"を持つユーザIDを取得します。

$allUsers.find(By.principal("nenkyu_finalist"));

次の式は、全ユーザーのうちのうち、グループID1000に所属し、かつプリンシパル"nenkyu_finalist"を持つユーザIDを取得します。

$allUsers.find(By.jgroupId(1000), By.principal("nenkyu_finalist"));

By.principal と同様に、グループIDで絞り込むための By.jgroupid も用意されています。

By.jgroupid(グループID)
By.jgroupid(グループID,グループID,...)

コンマ区切りで複数のグループIDを指定することもできます。

次の式は、全ユーザーのうちのうち、グループIDが1000,1001,1002に所属し、かつプリンシパル"nenkyu_finalist"を持つユーザIDを取得します。

$allUsers.find(By.jgroupId(1000,1001,1002), By.principal("nenkyu_finalist"));
find メソッドの引数は可変長となっています。順序は問いません。find(By.principal("xxx"), By.jgroupId(1000)) と記述しても結果は同じです。

$rootGroupの使い方

$rootGroup は、グループ階層を設定している場合のみ利用できます。

次の式は、全グループを取得します。

$rootGroup.descendants();

次の式は、最上位グループを取得します。図18の例では"管理部"、"第二技術部"、"第一技術部" が該当します。

$rootGroup.children();
図18 最上位グループの取得

仕様・制約

  • フロー参加者をスクリプトで設定する場合、スクリプトは必ず return で対象者を返すようにしてください。null (対象者が定まらない) は許容されません。(*1)
  • フロー参加者をスクリプトで設定した場合、スクリプトで取得された "次の承認対象者" は、ワークフロー適用モデルの "承認対象者(jfcWorkflowAdmitUser)" 項目に保存されます。このため、申請・承認時にスクリプトで次の承認対象者が確定された後にスクリプトを変更しても、次の承認対象者は変更されません。この仕様から、例えばスクリプトを実行するたびに対象者が変わるような仕組みを実現することはできません。(*2)
  • フロー参加者スクリプトのデータベースの型は、各データベースの CLOB に相当する型を用いるようにしています。スクリプトのサイズの最大長は、データベースの CLOB 型の上限値となります。例えば Oracle では NCLOB となります。詳細は、自動生成された jfcparticipant_setting.ddl をご確認ください。
  • このスクリプトは、ワークフロー処理中に複数回、実行されることがあります。何回実行されるかはフロー設定により変わるため、複数回実行されても結果が変わらないようにしてください。(具体的には、N回呼び出されることを前提としたり、N回目の呼び出しのときに処理を変える、というような手順を加えないようにしてください。)
1. スクリプトがnullを返した場合、画面にエラーメッセージが表示されます。
2. 例えば対象者がランダムに決まる、曜日によって決まる(月曜日ならAさん、火曜日ならBさん)といったことは実現できません。

その他の情報