Salesforce で法人番号を検索して取引先を更新する画面をノーコードで構築する (フロー+外部サービス)

顧客情報を整備することは、CRM の活用において必要不可欠な活動の 1 つです。特に BtoB 企業の場合は、[国税庁が指定する法人番号](https://www.houjin-bangou.nta.go.jp/)を使用して法人情報を一意に特定することができます。法人番号を含む法人データは、[gBizINFO](https://info.gbiz.go.jp/) という経済産業省が管理する情報提供サイトを使用しても検索が可能ですが、gBizINFO は [REST API](https://info.gbiz.go.jp/hojin/swagger-ui.html#!/gBizINFO_REST_API) も提供されています。

Salesforce では、[外部サービス](https://help.salesforce.com/articleView?id=sf.external_services.htm&type=5)と呼ばれる、OpenAPI (Swagger) で定義された REST API の仕様をインポートし、フローやプロセスで使用できる呼び出し可能アクションを生成する機能が提供されています。gBizINFO の REST API もこの外部サービス機能を用いてインポートすることができます。この外部サービス機能と画面フローを組み合わせることで、以下のように、取引先の名前で法人情報を検索し、検索結果に基づいて取引先の項目を更新するような高度な機能をノーコードで実装することができます。

![gbizinfo_sfgif.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184624/a275f629-ed0c-2e68-6825-66ea79e236cf.gif)

この画面フローは、インストールして編集可能なパッケージとして公開しています。ぜひご自身の Sandbox や Trailhead Playground 環境等でも試してみてください。以下では画面フローの使用方法と実装内容のポイントについて説明します。

## 事前準備

### gBizINFO の API キーを申請する
gBizINFO REST API の使用には　API キーの発行が必要です。[利用申請ページ](https://info.gbiz.go.jp/hojin/api_registration/form)から各自申請してください。発行された API キーは画面フロー内で設定するため、控えておいてください。

### 本パッケージのインストール

Trailhead Playground の場合は、パッケージ Id `04t5G000004kBPiQAM` を使用するか、以下のリンクからインストールしてください。

* [Sandbox](https://test.salesforce.com/packaging/installPackage.apexp?p0=04t5G000004kBPiQAM)
* [Developer Edition](https://login.salesforce.com/packaging/installPackage.apexp?p0=04t5G000004kBPiQAM)

### メタデータ一式

https://github.com/shunkosa/sfdx-gbizinfo-screenflow-jp

## 使い方

### API キーの設定
インストールされた画面フローを開き、左サイドバーから定数 `constant_gBizInfo_ApiKey` を選択します。値を事前準備で取得した API キーに置き換えてください。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184624/a1000543-336b-6446-be49-257bcdf2738c.png" width="600">

### 画面フローの使用
フローを有効化してください。その後、取引先の Lightning レコードページに画面フローを直接配置するか、クイックアクションを作成してページレイアウトに配置しても構いません。

## 実装方針
![Screen Flow Overview](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184624/c6b416d5-74b7-2cc5-9ac5-6a7679d339f2.png)

画面フローは大まかに以下のような処理を行なっています。

1. 取引先の情報を取得する (※本来は recordId 変数はテキストではなく sObject 型を用いると明示的なレコード取得要素は不要になりますが、デバッグを利用するためにあえてテキスト型を使用しています)
2. 外部サービスにより生成されたアクションを呼び出す(=gBizINFO の APIコール)
3. 結果を加工してラジオボタンとして表示する(=LWCを使う)
4. 選択された値で元の取引先を更新する

法人情報は構造化されたフォーマットになっており、フロー内では自動生成される Apex 型の変数 (詳細は後述) として扱います。一方、画面表示する選択肢にはテキスト型の変数を使用する必要があるため、データの変換が必要になります。元のフローの見通しをよくするため、この変換処理はサブフローに移しています。初めての方にはやや複雑かもしれませんが、実際の API レスポンスと比較しながらフローの処理を眺めると理解が進むでしょう。

## サンプルフローの補足

### 外部サービスは OpenAPI のスキーマを Apex クラスとして定義する
コールアウトの結果が複雑なオブジェクトの場合に、それに対応する Apex クラスを定義することは、Salesforce で開発経験のある方には馴染みがあるかと思います。外部サービスでも同様に、 API Spec をインポートすると、呼び出し可能アクションが作成されるだけではなく、その API で定義されているスキーマ (型の情報) が Apex クラスとして作成されます。例えば、`/v1/hojin` の 200 レスポンスは以下のようになりますが、
 
```json
{
  "id": null,
  "errors": null,
  "message": "200 - OK.",
  "hojin-infos": [
    {
      "corporate_number": "6600000000000",
      "postal_code": "1510000",
      "location": "東京都渋谷区1-2-3 XXXビル",
      "name": "テスト株式会社",
      "status": "-",
      "number_of_activity": "1",
      "update_date": "2018-06-14T00:00:00+09:00"
    },
    {
      "corporate_number": "7000000000000",
      "postal_code": "1000000",
      "location": "東京都港区１-２-３ YYYタワー",
      "name": "テスト株式会社",
      "status": "-",
      "number_of_activity": "10",
      "update_date": "2018-05-11T00:00:00+09:00"
    }
  ]
}
```

これは以下のようなスキーマに基づいています。

```json
"HojinInfoResponse": {
  "type": "object",
  "properties": {
    "id": { "type": "string", "description": "リクエストid" },
    "message": { "type": "string", "description": "メッセージ" },
    "hojin-infos": {
      "type": "array",
      "description": "gBizINFOデータ",
      "items": { "$ref": "#/definitions/HojinInfo" }
    },
    "errors": {
      "type": "array",
      "description": "エラー情報(エラーがある場合に出力します。)",
      "items": { "$ref": "#/definitions/ApiError" }
    },
  }
}
```

上記のスキーマに基づいて、Salesforce 内部では以下のような Apex クラスが生成されます。

```ExternalService__gBizInfo_HojinInfoResponse.cls
public class ExternalService__gBizInfo_HojinInfoReponse {
  @AuraEnabled
  public static String id;
  
  @AuraEnabled
  public static String message;

  @AuraEnabled
  public static List<ExternalService__gBizInfo_HojinInfo> hojinx2sinfos;

  @AuraEnabled
  public static List<ExternalService__gBizInfo_ApiErrors> errors;
}

```

よってフロー内ではこの Apex クラスの型の変数を作成することで、法人検索の API レスポンスを受け取れるようになります。同様に `hojin-infos` の 1 つ 1 つの要素に対応する Apex クラスは `ExternalService__gBizInfo_HojinInfo` で、フロー内ではこの型のコレクション変数を用いて `hojin-infos` の情報を取得・操作しています。

### コレクション変数を選択肢にするラジオボタン

画面フロー標準のラジオボタンでは、コレクション変数に基づいて動的に選択肢を表示することができません。そのため、ここだけ LWC を使用しています。この LWC は、選択肢を表示するため、選択肢の表示ラベル用と選択肢の値用にそれぞれテキスト型のコレクション変数を 2 つ受け取る必要があります。

`gBizInfoの法人情報リストから選択肢のラベルと値を設定するサブフロー`では、法人情報が保存されているApex型のコレクション変数をループして、2 つのテキスト型のコレクション変数に変換する操作を行なっており、この結果を LWC に設定しています。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184624/f3f67fcc-6deb-fa51-600e-6673a3d9926d.png" width="400" />


### コレクション変数に対する操作

コレクション変数から条件に合致する要素を取り出したり、コレクション変数を分割・マージしたりする操作は、プログラムであれば簡易にできますが、まだフローでは弱いところです。コレクション変数を操作するための[サードパーティの呼び出し可能アクション](https://unofficialsf.com/list-actions-for-flow)もありますが、今回は画面で選択された法人番号 (テキスト型) に一致する法人情報を、法人情報のリストから取得する というシンプルな操作のため、別のサブフローで、法人情報のリストをループして、条件に合致する要素を出力変数に格納する操作を行なっています。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184624/93e7b270-ec06-2570-ccbc-d0e3491321aa.png" width="450">


## 参考リンク
[Salesforce Help - 外部サービスを使用したフローの作成](https://help.salesforce.com/articleView?id=sf.enhanced_external_services_example_create_flow.htm&type=5)