midPoint にCSVの源泉データをインポートする（ユーザーと組織のアサイン編）

[midPoint by OpenStandia Advent Calendar 2024](https://qiita.com/advent-calendar/2024/midpoint-by-openstandia) の9日目は、[5日目](https://qiita.com/wadahiro/items/a239a743af5404a7325f)と[8日目](https://qiita.com/wadahiro/items/dc4fe8a20f4e648e01dc)で源泉から取り込むようにした、ユーザーと組織の **アサイン** を自動的に行うようにします。

:::note info
8日目までで構築した環境が前提となります。
:::

# 源泉取り込みにおける自動アサイン

[5日目](https://qiita.com/wadahiro/items/a239a743af5404a7325f)で使用したユーザーCSVにて、以下のように所属組織IDが設定されています（`primaryOrgId`と`secondaryOrgIds`の部分）。この情報を利用して、源泉取り込み時に自動的にユーザーに組織をアサインするようにします。

```csvs:users.csv
employeeNumber,givenName,familyName,email,primaryOrgId,secondaryOrgIds
1001,太郎,山田,taro.yamada@example.com,005,006;007
1002,花子,佐藤,hanako.sato@example.com,006,005;009
1003,次郎,鈴木,jiro.suzuki@example.com,007,006
1004,美咲,高橋,misaki.takahashi@example.com,008,009
1005,健一,田中,kenichi.tanaka@example.com,009,008
1006,陽菜,伊藤,hina.ito@example.com,005,
1007,翔,渡辺,sho.watanabe@example.com,006,
1008,さくら,小林,sakura.kobayashi@example.com,007,
1009,優,中村,yu.nakamura@example.com,008,005;007
1010,一郎,山本,ichiro.yamamoto@example.com,009,006
```


また、アサインを行うには、所属組織IDに対応する組織オブジェクトがmidPoint上に存在する必要があります。そのためには、[8日目](https://qiita.com/wadahiro/items/dc4fe8a20f4e648e01dc)で紹介した組織情報を含むCSVの源泉データ取り込みを先に実施しておく必要があります。


```csvs:orgs.csv
orgId,orgName,parentOrgId
001,サンプル株式会社,
002,営業本部,001
003,総務本部,001
004,IT本部,001
005,営業第一部,002
006,営業第二部,002
007,総務部,003
008,IT開発部,004
009,ITインフラ部,004
010,営業第一課,005
011,営業第二課,006
```

自動アサインの設定は、いくつかやり方があります。

- 「HR Users」のリソース設定で、マッピング設定で組織をアサインする
- オブジェクトテンプレートによるマッピング設定で組織をアサインする

今回は前者のリソース設定による方法で実施します。


# HR Usersリソース設定の修正

実は、同じようなアサインのマッピングは、[8日目の記事内の「親組織をアサインするマッピングを設定」](https://qiita.com/wadahiro/items/dc4fe8a20f4e648e01dc#parentorgid%E3%82%88%E3%82%8A%E8%A6%AA%E7%B5%84%E7%B9%94%E3%82%92%E3%82%A2%E3%82%B5%E3%82%A4%E3%83%B3%E3%81%99%E3%82%8B%E3%83%9E%E3%83%83%E3%83%94%E3%83%B3%E3%82%B0%E3%82%92%E8%A8%AD%E5%AE%9A)で実施しています。この時は、組織に対して親組織をアサインしていました。今回はユーザーに対して所属組織をアサインさせるだけです。ただし、CSVには主務組織（`primaryOrgId`）と兼務組織（`secondaryOrgId`）がありますので、それぞれ設定する必要があります。

`<assignmentTargetSearch>`を使用するので、リソース設定のRaw更新で設定を行います。

## 主務組織のアサイン設定

親組織のマッピングとほぼ同じです。1点異なるのは、`<assignmentProperties>`で設定する`subtype`の値です。**源泉データから設定した主務組織**であることが分かるように、今回は`HR_PRIMARY`という値を設定します。このように、ここでも[7日目の記事で解説した高度なRange Mapping設定](https://qiita.com/wadahiro/items/e37b406385da722f54b5#%E9%AB%98%E5%BA%A6%E3%81%AArange-mapping%E8%A8%AD%E5%AE%9A)を使用し、源泉データで所属組織が変わった際には、主務組織アサインに限定してアサインを洗い替えるようにします。

:::note info
同一のソースとなる属性（`primaryOrgId`）に対して、複数のインバウンドマッピングが設定可能です。マッピング設定をGUIで参照すると以下のように並びます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35233/4f10755a-65e2-95fa-bee6-45279614bb13.png)

なお、アサインだけが目的であれば、ユーザオブジェクトの`organization`属性に対する既存のインバウンドマッピングを消しても問題ありません。
:::

```diff_xml: HR UsersリソースのRaw編集
            <attribute id="15">
                <ref>ri:primaryOrgId</ref>
                <inbound id="16">
                    <strength>strong</strength>
                    <target>
                        <path>organization</path>
                        <set>
                            <predefined>all</predefined>
                        </set>
                    </target>
                </inbound>
+               <inbound>
+                   <strength>strong</strength>
+                   <expression>
+                       <assignmentTargetSearch>
+                           <targetType>c:OrgType</targetType>
+                           <filter>
+                               <q:equal>
+                                   <q:path>name</q:path>
+                                   <expression>
+                                       <queryInterpretationOfNoValue>filterNone</queryInterpretationOfNoValue>
+                                       <script>
+                                           <code>
+                                               input
+                                           </code>
+                                       </script>
+                                   </expression>
+                               </q:equal>
+                           </filter>
+                           <assignmentProperties>
+                               <subtype>HR_PRIMARY</subtype>
+                           </assignmentProperties>
+                       </assignmentTargetSearch>
+                   </expression>
+                   <target>
+                       <c:path>assignment</c:path>
+                       <set>
+                           <condition>
+                               <script>
+                                   <code>
+                                       assignment?.subtype.contains("HR_PRIMARY")
+                                   </code>
+                               </script>
+                           </condition>
+                       </set>
+                   </target>
                </inbound>
            </attribute>
```

## 兼務組織のアサイン設定

同様に、兼務組織に対するマッピングを設定します。**源泉データから設定した兼務組織**であることが分かるように、今回は`HR_SECONDARY`という値を設定します。

```diff_xml: HR UsersリソースのRaw編集
            <attribute id="17">
                <ref>ri:secondaryOrgIds</ref>
                <inbound id="18">
                    <strength>strong</strength>
                    <target>
                        <path>organization</path>
                        <set>
                            <predefined>all</predefined>
                        </set>
                    </target>
                </inbound>
+               <inbound>
+                   <strength>strong</strength>
+                   <expression>
+                       <assignmentTargetSearch>
+                           <targetType>c:OrgType</targetType>
+                           <filter>
+                               <q:equal>
+                                   <q:path>name</q:path>
+                                   <expression>
+                                       <queryInterpretationOfNoValue>filterNone</queryInterpretationOfNoValue>
+                                       <script>
+                                           <code>
+                                               input
+                                           </code>
+                                       </script>
+                                   </expression>
+                               </q:equal>
+                           </filter>
+                           <assignmentProperties>
+                               <subtype>HR_SECONDARY</subtype>
+                           </assignmentProperties>
+                       </assignmentTargetSearch>
+                   </expression>
+                   <target>
+                       <c:path>assignment</c:path>
+                       <set>
+                           <condition>
+                               <script>
+                                   <code>
+                                       assignment?.subtype.contains("HR_SECONDARY")
+                                   </code>
+                               </script>
+                           </condition>
+                       </set>
+                   </target>
                </inbound>
            </attribute>
```

# タスクの実行

[5日目](https://qiita.com/wadahiro/items/a239a743af5404a7325f)で作成したユーザー取り込み用のインポートタスク、または、[6日目](https://qiita.com/wadahiro/items/5bcf320b752e696b1abb)で作成したリコンシリエーションタスクを実行し、ユーザーを再度取り込みします。既に取り込み済みの環境の場合、通常であれば何も起きませんが、インバウンドマッピングを変更したためその情報を使ってユーザーの更新作業が行われます。

タスクの実行完了後、`1001`のユーザー詳細画面を開いてアサインを確認してみましょう。以下のように、midPointの組織がアサインされていることが分かります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35233/15fb7e03-f0dc-d101-30e2-c3335aee7ca3.png)

主務と兼務の区別も確認してみましょう。「営業第一部」の部分をクリックすると、アサインの詳細情報を参照できます。「サブタイプ」属性に、`HR_PRIMARY`と設定されており、これは主務を示すアサインであることが分かります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35233/806847e5-cceb-356c-0b1c-58c42cb341df.png)

一方、「営業第二部」と「総務部」のアサイン詳細を開くと、`HR_SECONDARY`が設定されており、これらは兼務を示すアサインであることが分かります。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35233/b0edaf8a-2af0-99e5-98c6-79ae1c8629a6.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35233/3ba6af82-a973-c231-cc8b-43bcc6c6e0bd.png)


# 人事異動による所属組織変更

以下のようにCSVファイルを更新して、同様にタスクを再実行します。

```csvs:users.csv
1001,太郎,山田,taro.yamada@example.com,005,006;007
↓
1001,太郎,山田,taro.yamada@example.com,006,007;009
```

タスクの実行完了後、`1001`のユーザー詳細画面を開いてアサインを確認してみましょう。以下のように、組織のアサインが想定通り洗い替えられていることが分かります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35233/fb9718c6-6b6c-f344-c7e5-07215fd71a6f.png)


# まとめ

9日目では、[5日目](https://qiita.com/wadahiro/items/a239a743af5404a7325f)と[8日目](https://qiita.com/wadahiro/items/dc4fe8a20f4e648e01dc)で源泉から取り込むようにした、ユーザーと組織の **アサイン** を自動的に行うようにしました。midPointでは、オブジェクトとオブジェクトの関連は、汎用的なアサインという仕組みで行っていますので、1つやり方を覚えれば、同じ仕組みで色々なパターンに対応することができます。

次回ですが、[5日目の記事でリソース設定のCorrelation設定（名寄せ設定）](https://qiita.com/wadahiro/items/a239a743af5404a7325f#correlation%E3%81%AE%E8%A8%AD%E5%AE%9A)を行っていましたが、この動作確認についてはこれまで特に言及していなかったので、補足をしたいと思います。