【Salesforce】Apex+VFでExcel帳票作成

PDF帳票の作り方ばかりでExcel帳票については殆どなかったのでまとめてみました。

# 1.前提
- 拡張子はXMLを使用する
- Apex、Visualforceを使用し、VF内でXMLを記述する
- XML作成にMicrosoft Excelを使用
- （参考）https://www.systemforest.com/campus/2016/06/salesforce_excel/

# 2.Excelで帳票レイアウトを作成

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2003505/3c569760-8fb2-6ff0-615f-f851680c6103.png)

合計金額は関数で作成した

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2003505/6579051c-8b14-7888-9832-c5d45d4510c4.png)

レイアウトの作成が終わったらXMLで保存する

# 3.ApexとVisualforceを作成
① 適当な名前のVFを作成
② 同じくApexを作成
③ VFのapex:pageタグにcontenttypeなどを記載（下記記載例）
```
<apex:page contenttype="text/xml;charset=UTF-8#ExcelName.xml" controller="ExcelOutCls">
```

④ 「<?」で始まるタグはベタ書きできないので、Apexから変数で出力

```cls:Apex
public String getXmlVer() {
        return '<?xml version="1.0"?>' + '\r\n';
}
public String getXmlApp() {
    return '<?mso-application progid="Excel.Sheet"?>' + '\r\n';
}
```

```page:Visualforce
<apex:outputText escape="true" value="{!xmlVer}" />
<apex:outputText escape="true" value="{!xmlApp}" />
```

⑤ XMLコードをVFに転記する
先ほど作成したXMLファイルをテキストエディタで開くとコードが表示されるので、必要な部分をコピーしてVFに貼り付けます

```
<apex:page contenttype="text/xml;charset=UTF-8#ExcelName.xml" controller="ExcelOutCls">
<apex:outputText escape="true" value="{!xmlVer}" />
<apex:outputText escape="true" value="{!xmlApp}" />
<!-- Workbook以下XMLコード -->
<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"
 xmlns:o="urn:schemas-microsoft-com:office:office"
 xmlns:x="urn:schemas-microsoft-com:office:excel"
 xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
 xmlns:html="http://www.w3.org/TR/REC-html40">
・
・
・
</Workbook>
</apex:page>
```

# 4.実際に出力してみる
作成したVFページからプレビューを押下して出力できる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2003505/c88b307c-9b67-7060-412e-c2e26470b12c.png)

指定したファイル名で出力され、ちゃんと関数も反映されていることが確認できた
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2003505/d2c6acff-63c5-b179-6d75-fa6c68890ce9.png)

# 5.応用編
意外と簡単に実装できそうだということが分かったところで、実際に開発した時に使用した機能の一部を解説します。

## Excel名の日本語化
今回は「ExcelName.xml」としたので問題ありませんでしたが、実際には取引先名や出力OBJ名などをファイル名に入れたりするのではないでしょうか。
その場合、このままだとうまく出力することができないので、Apex側に日本語出力用のロジックを書いてあげる必要があります。
作例を載せておくので、環境に応じて改変してください。

```
/**
 *  日本語対応ファイル名生成
 *  @param filename ファイル名
 *  @param encfn ファイル名のURLエンコード
 */
String filename = '請求書' + ' ' + '-' + ' ' + a.Test__c + '.xml';
//　そのままだと半角スペースが「+」に置換されてしまうので、replace('+',' ')で戻してます
String encfn = EncodingUtil.urlEncode(filename, 'UTF-8').replace('+',' ');
//　レスポンスヘッダのContent-Dispositionにattachment, filename, filename*フィールドをセット
Apexpages.currentPage().getHeaders().put('Content-Disposition', 'attachment;filename*=UTF-8\'\'{fn};filename={fn}'.replace('{fn}', encfn) );
```
## 明細行のループ処理
テストでは明細行がベタ書きで固定されていますが、実際開発する場合は可変します。
Excel出力の場合でもrepeatが使えるので、取得した行数分出力できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2003505/a6d3a46e-294d-be91-4c86-54a7001037b4.png)

## 行数可変時の考慮事項
XMLはTableタグのExpandedRowCountで行数を設定しています。
行数が可変する場合はここの値も可変させる必要があるので注意してください。
私の場合は最低行数を初期値としてそこから明細分加算させる形で実現しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2003505/3f5735b8-fc4e-3d6e-b957-e1c21c28c78e.png)

# 6.まとめ
出力後にExcelで編集できるので、OPROやSVFだと費用が、、という場合の代替案としても良さげです。
好きなフォントが使えたり、関数使えたり、ページ送りとか改行とかあまり考えなくていいのでPDF形式がマストでないならExcel帳票も全然ありだと思います。（renderAs="pdf"がイケてないので。。）
構築難易度もそこまで高くないので、是非試してみてください！