CAPベースのList Reportに数量単位と通貨を表示する

## はじめに
ABAP環境で作成したList Reportでは、数量項目や金額項目にはそれぞれ数量単位、通貨がセットで表示されます。さらに、数量、金額はそれぞれの単位のカスタマイズで定義された小数点以下桁数で表示されます。本記事では、同じことをCAPベースのList Reportで実現する方法について確認します。

イメージ（以下はBTP, ABAP Environmentで作成）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/5ea03ed0-4f02-b879-06ce-41cc498dcc97.png)

Gitリポジトリ：https://github.com/miyasuta/unit-and-currency

## 結論
金額に関してはABAP環境と同様に通貨に応じた小数点以下桁数が適用されます。数量については整数で表示されます。

CAPベースのList Report
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/c9d1407e-130c-58f8-0d98-057392d8702e.png)

通貨については[@sap/cds-common-content](https://www.npmjs.com/package/@sap/cds-common-content)というパッケージで通貨のエンティティ（小数点以下桁数の定義を含む）が提供されているのですが、数量単位についてはそのようなものがないためです。自力で何とかするのもハードルが高いため、あきらめました。

## 方法
1. @sap/cds-common-contentをインストール
1. スキーマの定義
1. アノテーションの定義

### 1. @sap/cds-common-contentをインストール
以下のコマンドでインストールします。

```
npm install @sap/cds-common-content --save
```
この結果、node_modulesフォルダに以下のコンテンツが作成されます。通貨、言語、国コードのエンティティの定義が含まれています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/703cff1b-94ef-b49e-6be7-be55106dae39.png)
※このパッケージが登場する前はこれらのファイルの定義は自分でする必要があったようです。
https://github.com/SAP-samples/cloud-cap-samples/blob/main/common/data/sap.common-Countries.csv

### 2. スキーマの定義
以下のようにスキーマを定義します。

```
namespace my.bookshop;
using { Currency } from '@sap/cds-common-content';

entity Books {
  key ID : Integer @title: 'ID';
  title  : String @title: 'Title';
  stock  : Decimal(13,3) @title: 'Stock';
  unit: String(3) @title: 'Unit';
  price: Decimal(13,3) @title: 'Price';
  currency: Currency @title: 'Currency';
}
```

ポイントは以下の2点です。
- `using { Currency } from '@sap/cds-common-content'`でCurrencyを取り込んでいること、
- `currency: Currency`でCurrencyとのアソシエーションを定義していること

※currency: Currencyはcurrency: Association to Currencyと同じ意味です。

### 3. アノテーションの定義
まず、以下のようにサービスを定義します。

```
using my.bookshop as my from '../db/data-model';

service CatalogService {
    @readonly entity Books as projection on my.Books;
}
```

サービスに対するアノテーションを以下のように定義します。

```
annotate CatalogService.Books with @(
    UI.LineItem: [
        {
            $Type : 'UI.DataField',
            Value : ID,
        },
        {
            $Type : 'UI.DataField',
            Value : title,
        },
        {
            $Type : 'UI.DataField',
            Value : stock,
        },
        {
            $Type : 'UI.DataField',
            Value : price,
        }

    ]
){
    stock @Measures.Unit: unit;
    price @Measures.ISOCurrency : currency_code;
};
```
ポイントは以下の部分です。数量、金額項目に対して数量単位、通貨の項目を指定しています。

```
    stock @Measures.Unit: unit;
    price @Measures.ISOCurrency : currency_code;
```

## 動作
サンプルデータを以下のように入力します。

```
ID;title;stock;unit;price;currency_code
1;Wuthering Heights;100;PC;100;USD
2;Jane Eyre;500;KG;100;JPY
```
List Reportでは以下のように表示されます。ABAPと違い日本円は100倍されることなくそのまま表示されます。サンプルデータを整数で入れても、通貨に合わせた小数点以下桁数の表示になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/ae76be52-60f4-e2b5-cc4f-d120c5cf76d7.png)

なお、数量について`@Measures.Unit: unit`のアノテーションを外すと項目の型で定義した小数点以下桁数で表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/f009a5ed-5377-cdc1-cdee-448c7636d45a.png)

## ビルドしてみる
以下のコマンドでデータベースにHANA Cloudを指定します。
```
cds add hana --for production
```
以下のコマンドでビルドします。
```
cds build --production
```
その結果、gen\db\src\genにsap-commonのネームスペースでテーブルとビューが作成されます。デプロイするとこのテーブルがデータとともにHDIコンテナに作成されます。
※スキーマの中で使用している通貨のエンティティだけデプロイされ、言語、国は対象になりません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/04b44394-d421-1652-e77c-843a35f77603.png)

## Fiori elementsで数量単位や通貨を扱う仕組み
以下のヘルプにOData V4で数量単位や通貨を使用する場合のメタデータが示されています。
https://sapui5.hana.ondemand.com/#/topic/4d1b9d44941f483f9b7f579873d38685
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/f7b75613-7c41-4e5a-d36b-ce67c384f553.png)
RAP（ABAP）で作成したODataは上記の定義の通りになっていましたがCAPは違いました。まず、com.sap.vocabularies.CodeList.v1.CurrencyCodesというアノテーションがありませんでした。どうやらRAPとCAPでは通貨の表し方が違うらしいのですが、Fiori elementsがどのようにその違いを吸収しているのかはわかりませんでした。