Oracle NetSuite の「保存検索」を使いこなす

これは🎄🎁🎅[Oracle NetSuite Advent Calendar 2024](https://qiita.com/advent-calendar/2024/netsuite)🤶🎁🎄の1日目の投稿です。

# 保存検索とは
[Oracle NetSuite](https://www.netsuite.co.jp/)をどのようなロール (役割)で使うにしても、知っておくと便利になるテクニックの1つが「**保存検索** ([Saved Searches](https://docs.oracle.com/en/cloud/saas/netsuite/ns-online-help/chapter_N675442.html))」の活用です。

NetSuiteもそうですが、ERP/CRMといった基幹業務系のソフトウェアは、顧客や商品等の「**マスタデータ**」、商談や購買等の「**トランザクションデータ**」が格納されたデータテーブルがお互いに複雑にリンクしあった巨大なデータベースであり、日々の業務ではここから**如何に自分が普段必要としている情報だけを定期的に抜き出してくるか**、がエンドユーザー視点からのERP/CRM使いこなしのポイントになります。

「保存検索」とは、自分が必要とする情報を抜き出すために作成した検索条件を、次回も使うためにシステムに保存しておくことができる機能です。保存した条件は、自分だけが使うようにすることも、パブリックにして他のユーザーに開放することもできます。また、NetSuite CData Driver等を通して外部システムから保存検索を呼び出すことも可能です。

:::note info
SAPやSalesforce等の別のERP/CRM製品でも似た機能がありますので、これらのソフトウェアを使ったことがあればイメージはすぐにつくものと思います。ちなみに、SAPでは英語では「Saved Search」という機能名になっていますが、日本語は「保存**済み**検索」となっていて、その他の製品でも「保存**済み**検索」となっているものが大半なので、日本語で「**保存検索**」というとNetSuiteの用語になります。
:::

# 保存検索と保存レポートとの違い
ここまで読んで、読者によっては疑問に思うかもしれません。ERP/CRMソフトウェアには、通常「レポート」や「ダッシュボード」と呼ばれる定型情報を表示てくれるビュー、ページがあるけど、それと何が違うの？と。NetSuiteにも**レポート**機能があり、メニューも「レポート」の下に保存レポートと保存検索のふたつがあります。これらは何が違うのでしょう？

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197689/f41c25f2-1821-efff-6c96-114816075ac3.png)
**`レポートの概要ページ (ユーザーのロールにより一覧されるレポートが変わります)`**

「**レポート**」は、決まった形の書式と検索条件で出力するために、あらかじめ構築しておくもので、書式を重視、表の列をグループ化・階層化したりドリルダウンできるような表形式の出力を行うことができます。キャッシュ・フロー計算書、損益計算書、貸借対照表、現金計算書といった**財務レポート**も標準で備わっており、特別な機能があります。表の内容はグラフで表示することもできます。標準で備わっている100種類を超えるレポートをベースにGUIベースの「レポート・ビルダー」で多少のカスタマイズが可能です。レイアウトの変更、表示するフィールドの変更、フィルタ条件の変更、並び替え順番の変更、そして行や列には合計、カウント、平均、最大、最小といった統計量を表示することができたり、2つのフィールド値の簡単な演算を入れることができます。一方、そのレポートテンプレートの標準にないリストのフィールドの追加等、高度なカスタマイズを行おうとすると、エンドユーザーのレベルでは難しく、SuiteCloud Platformでの開発と展開が必要になります。

一方、「**保存検索**」は、レポートと比べると、より柔軟で込み入った検索条件や出力をエンドユーザーのレベルで組み上げることが可能です。方法は2つあり、1つ目はマスタデータやトランザクションデータのリストの画面で、右上から「検索」を選んで検索条件を設定して「保存検索を作成」する方法、2つ目は既存の保存検索をベースにカスタマイズする方法です。複数のリストからフィールドを持ってきたり計算式やスクリプトで出力したり、概要レベルで表示したり、とより多彩な出力が可能です。一方、出力結果は、基本的には「検索結果」であり、レコードの一覧になります。レポートのように準備された書式があるわけではありません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197689/781a6794-d2de-3d9e-9681-8251646e7939.png)
**`商談リストの右上の「検索」から検索画面を表示し、作成した検索条件を「保存検索」として保存できる`**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197689/23cc6499-f29d-d494-c543-3c255cc31344.png)
**`検索画面で条件を設定後、「保存検索を作成」できる。「アドバンスト検索を使用」をチェックすると、より高度な条件設定ができる`**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197689/8cc1d3eb-9871-fb48-6cb4-a218d81325ac.png)
**`「アドバンスト検索を使用」では、Excelのフィルター条件式のようなUIで条件式の設定ができ、任意のフィールドに加えて計算式やスクリプトの呼び出しも可能。「式を使用」で、より生の条件文に近いGUIに変更できる。`**

# 保存検索を使いこなすためのテクニック
NetSuiteを使い始めて、ほしい情報をうまく抜き出すために実践すべきこと、お勧めの3つを挙げてみたいと思います。

## 1. 既存の保存検索をよく研究する
まずは、既存の保存検索をそれぞれ実行してみて、自分がほしい情報に近いものを見つけてください。その上で、その保存検索の設定を隅から隅まで見て、条件やビューの設定を変更してみてください。自分の思い通りの変更ができたら自分用の名前を付けて保存してください。これを何回も繰り返していくうちに、だんだん自分がほしい情報を抜き出すための設定について理解が深まっていくはずです。

標準でテンプレートとして保存されている「買掛金」を例にとって、出力結果、フィルター条件 (条件)、ビューの設定 (結果)、ハイライト条件 (ハイライト)を見てみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197689/d2245aca-0c87-a675-40ac-bdd0c2151f30.png)
**`「買掛金」の保存検索の出力結果の例`**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197689/7fa524ca-d783-63ed-d981-e7181468be3b.png)
**`「買掛金」のフィルター条件式の例`**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197689/f79998b1-0ade-2af2-ae3d-7c45c3a441e2.png)
**`「買掛金」のビューの設定の例。2列目の「概要の種類 (Summary Type)」が名前フィールドで「グループ」になっているため、出力結果は概要ビューと詳細ビューに分かれ、概要ビューでは名前 (会社名)での「合計」が表示され、名前フィールドのリンクをクリックすると詳細 (トランザクションレコードのひとつひとつ)が表示される。`**

条件式はこのような複雑なものも設定可能です。
```sql:「現在」フィールドの内容
CASE WHEN ({recordtype} <> 'vendorbill' AND {recordtype} <> 'vendorpayment' AND {recordtype} <> 'vendorcredit' AND TRUNC({today})-{trandate} < 1) THEN {amount} WHEN ({recordtype} = 'vendorbill' AND {duedate} IS NOT NULL AND TRUNC({today})-{duedate} < 1) THEN {amountremaining} WHEN ({recordtype} = 'vendorbill' AND {duedate} IS NULL AND TRUNC({today})-{trandate} < 1) THEN {amountremaining} WHEN ({recordtype} = 'vendorpayment' AND {duedate} IS NULL AND TRUNC({today})-{trandate} < 1) THEN -({amountremaining})  WHEN ({recordtype} = 'vendorcredit' AND TRUNC({today})-{trandate} < 1) THEN -({amountremaining}) END
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197689/fc9a1da1-e396-8646-6961-9223e8cda61a.png)
**`計算式の指定には、このような入力支援機能がある。`**

関数は以下のようなSQL Expressionが使えます。これらのほかにAND, END, NULL, OR, THEN, WHENといった予約語がいくつか利用できます。リファレンスは[こちら](https://docs.oracle.com/en/cloud/saas/netsuite/ns-online-help/section_N2833020.html)。(英語です)

```
ABS
ACOS
ADD_MONTHS
ASCII
ASIN
ATAN
ATAN2
BITAND
CASE
CEIL
CHR
COALESCE
CONCAT
COS
COSH
DECODE
DENSE_RANK
EXP
FLOOR
INITCAP
INSTR
KEEP
LAST_DAY
LENGTH
LN
LOG
LOWER
LPAD
LTRIM
MOD
MONTHS_BETWEEN
NANVL
NEXT_DAY
NULLIF
NVL
NVL2
POWER
RANK
REGEXP_INSTR
REGEXP_REPLACE
REGEXP_SUBSTR
REMINDER
REPLACE
ROUND(DATE)
ROUND(number)
RPAD
RTRIM
SIGN
SINH
SOUNDEX
SQRT
SUBSTR
TAN
TANH
TO_CHAR()
TO_DATE()
TO_NUMBER()
TRANSLATE
TREAT
TRIM
TRUNC(number)
TRUNC(DATE)
UPPER
```
また、この他に非公開関数
```
NS_CONCAT
```
も使うことができます。これは普通の`CONCAT`関数が2つの引数の文字列を単につなげるのに対して`NS_CONCAT`関数は概要の種類がグループに指定されている出力結果ビューで複数行の内容をカンマ区切りでひとつの文字列としてひとつの行にまとめて表示する仕組みです。(後述)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197689/4c1039cf-d537-4f1c-69c9-dcb6ad0a6192.png)
**`「買掛金」のハイライト条件の例。詳細ビューで超過日数が90日を超えるものは背景がピンクでハイライトされる`**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197689/2b3c55aa-63b7-8cd4-d3ac-90446f4be9b2.png)
**`ハイライト条件を変更して60日以上にして文字も赤くするようにした。詳細ビューでは、60日超のレコードは指定した書式でハイライトされる。`**

## 2. 「メイン・ライン」フィールドで一覧するレベルを切り替える
保存検索の出力をデザインするときに考えなければならないのは、どの階層レベルで結果を一覧するか、ということです。ひとつは、先ほど出てきた、「結果」タブでの各フィールドの「概要の種類 (Summary Type)」を指定することで、もうひとつはフィルター条件で「メイン・ライン ([Main Line](https://docs.oracle.com/en/cloud/saas/netsuite/ns-online-help/section_4459563851.html))」フィールドを設定することです。

「メイン・ライン」とは、トランザクションデータのうちの「主要情報」フィールドグループにあたる部分 (以下の図中の❶)で、下のほうのトランザクションタブに表示されている明細情報 (Line Item)が複数紐づいています(以下の図中の❷)。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197689/a6258639-2b43-a49f-b464-dbd631c19223.png)
**`トランザクションの「主要情報」と「明細情報」`**

フィルター条件で「メイン・ライン」フィールドを追加しようとすると、以下のボックスが表示されますので、「いずれか」から「はい」または「いいえ」を選択して「設定」します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197689/d2637bd8-e631-8d93-1204-295466d406b3.png)
**`「メイン・ライン」フィールドの選択肢`**


既定の状態では、**各トランザクションの主要情報レベルと明細情報レベルがそれぞれ別々の行で検索結果に表示**されています。

**「はい」を選んだ場合**
トランザクション1つに対して主要情報1行の出力になり、明細レベルの詳細は省略されます。

**「いいえ」を選んだ場合**
トランザクションの中の明細レベルが表示され、主要情報の行は表示されなくなります。明細レベルの出力に主要情報も含まれます。

「買掛金」で実際に行ってみるとこんな感じになります。

**フィルターがない、または「いずれか」の場合**
フィルター条件:
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197689/458d63ac-9324-dd58-e070-9ff0f29df511.png)

概要ビュー: (18行)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197689/d9780cda-c329-3291-28e7-a76b26c3b500.png)

詳細ビュー: (103行)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197689/151918d4-19bd-3f1e-0649-f8343be58470.png)

**「はい」を選んだ場合**
必要なデータが含まれた行だけが表示され、こちらのビューのほうがすっきりします。数字の合計は(一か所の小さい数字を除き)変わっていないため、必要なデータは(ほぼ)抜け落ちていません。

フィルター条件:
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197689/cfbcf0c0-5963-72fe-994e-cdcf2a39941b.png)

概要ビュー:(9行)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197689/0e23f67a-7a87-ecb8-ad73-ce7a5833a27d.png)

詳細ビュー:(17行)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197689/394ab85b-d905-d6c1-53ca-a9e52263b738.png)

**「いいえ」を選んだ場合**
必要な値を持つ「主要情報」が抜けてしまったため、ゼロばかりになり結果出力としては成り立ちません。

フィルター条件:
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197689/f419fa83-e3ea-38f4-4b98-5774916f30a3.png)

概要ビュー:(15行)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197689/3aa8b2d0-8110-ca92-fa5d-7ebb959a13fb.png)


詳細ビュー: (86行)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197689/3a4f128d-9831-2eae-f532-98dbef7d2532.png)

※ 詳細ビューにおいては「はい」「いいえ」の場合のレコードの合計が「いずれか」の場合の数字に一致します。概要ビューではそうはなっていません。

## 3. 計算式で出力結果の書式をうまく整える
ビューの出力結果で使える「計算式」にはいくつかの種類があり、「計算式 (HTML)」ではHTMLタグやスクリプト (`<script>`タグで囲った内側に記載)を利用して出力文字列の書式を整えることが可能です。

:::note warn
かつては「計算式(テキスト)」でもHTMLタグが使えていましたが、XSSセキュリティ攻撃への対応強化により、[2023.2リリース以降では「計算式(HTML)」に限定](https://docs.oracle.com/en/cloud/saas/netsuite/ns-online-help/article_0403035152.html)されました。
:::

それぞれの計算式の種類では、以下のような表現が可能です。([リファレンス](https://docs.oracle.com/en/cloud/saas/netsuite/ns-online-help/section_N661334.html))

|計算式の種類|例|
|:-|:-|
|計算式(通貨)|`TO_NUMBER({amount})`|
|計算式(日付)|`TO_DATE('11/16/2020','MM/DD/YYYY')`|
|計算式(日付/時刻)|`{now}`|
|計算式(数値)|`{field_id}`|
|計算式(パーセント)|`% of total 30/100`|
|計算式(テキスト)|● `{quantity} \|\| 'x ' \|\| {item}` <br/>● `CASE WHEN {duedate} < CURRENT_DATE THEN 'Overdue' ELSE 'On Time' END`|
|計算式(HTML)|● `'<a href="/app/common/entity/entity.nl?id=' \|\| {‌entity.id} \|\| '" target="_blank">' \|\| {entity} \|\| '</a>'` <br/>● `NVL2({approvalstatus}, '<span style="color:green;"> Approved</span>', '<span style="color:red;"> Not Approved</span>')`|

文字列をつなぐ場合は「\|\|」を使います。HTMLコードを記述する際はシングルクオート (')で囲ってください。

加えて、概要の種類が設定された出力結果ビューでは、前述の`NS_CONCAT`関数を使って

```
REPLACE(NS_CONCAT({item}),',','<br/>')
```
というように指定することで、たとえば

```
1,2,3,4,5
```
と出力されていた内容が

```
1
2
3
4
5
```
というように出力結果の行で表示されるようになります。カンマ区切りで一行に表示されるのではなく、明細行の数だけ縦に表示したい場合に便利です。([リファレンス](https://community.oracle.com/netsuite/japanese/discussion/4495531/%E3%82%A2%E3%82%A4%E3%83%86%E3%83%A0%E3%83%AC%E3%82%B3%E3%83%BC%E3%83%89%E3%81%AE%E8%A1%A8%E7%A4%BA%E5%90%8D%E3%81%AE%E9%87%8D%E8%A4%87%E3%82%92%E9%98%B2%E3%81%90))

また、`<script>`タグで囲った領域でスクリプトを書いて正規表現によるマッチングなどとも組み合わせると、より複雑な書式変換を行うことも可能です。


続きの記事はこちら:

https://qiita.com/skomeda/items/c658041b25548cebe504