GoogleフォームとGASで請求書のPDFを自動生成する

## はじめに

業務で必ず必要になる請求書。皆さんはどうやって作成していますか？

最も有名な請求書作成サービスはおそらく[Misoca](https://www.misoca.jp/)ですが、[Misocaは弥生に買収されて9月より完全にサービスが有料化し](https://cloud.watch.impress.co.jp/docs/news/1057948.html)、なかなか便利かつ無料に使えるサービスがなかったりします。

Excelや時にはVBAも使っている会社もありますが、ローカルでの作成は請求書フォームの改定が徹底されなかったり、請求書の作成履歴が管理されなかったりと、不便な点がたくさんあります。

そこで当社では、Googleフォームを使用した請求書作成システムを運用しています。
これにより、

- 作成履歴が残り、改竄を防止できる
- フォーム送信時に自動でPDFが生成されてGoogleドライブに保存される
- ファイル名を請求書番号、請求先名など自由にカスタマイズし、自動で設定できる

などを実現することができます。

## フロー

![flow.png](https://qiita-image-store.s3.amazonaws.com/0/221009/aee38e09-e104-b5fc-492e-aacab4f9bfbb.png)

図の青色部分はGoogleフォームのサービスとしてデフォルトで行ってくれます。
問題となるのは「スプレッドシートに記録した情報から請求書をPDFで生成する方法」です。

## 作り方

PDF化するための請求書フォーマットをまず作りましょう。回答記録用のスプレッドシートに「PDFフォーム」シートを新規で作成します。

請求書のフォーマットは会社によって違うかと思いますが、とりあえず以下のように作ります。
合計欄、消費税計算欄は数式を入れてもいいですし、プログラムでの処理にしてもいいですが、ムダなフォームへの入力を避けるために、どの項目もなるべくシステム側で負担します。

![pdfform.png](https://qiita-image-store.s3.amazonaws.com/0/221009/65a6b584-0560-c23a-deb2-855a040130f6.png)

##サンプル
実際に動かせるものを作りました。
[請求書作成システム](https://docs.google.com/forms/d/e/1FAIpQLSeXecLFLY_jysjSiTuQfkwUY5KsibFcAGZ0BXf_EXGxotMBbw/viewform)
↑のGoogleフォームに入力した内容で、
[請求書PDF格納フォルダ](https://drive.google.com/drive/folders/1vHZjvecAMTM0dGxIxd4AEvAmhZoXQLFS?usp=sharing)
にPDFファイルが生成されます。
※あくまで動作をイメージしていただくためのサンプルです。入力した内容は公開されますので、本当に業務で使用したり、目的外で使用するのはご遠慮ください。

![pdf.png](https://qiita-image-store.s3.amazonaws.com/0/221009/f6521ab6-f182-465d-6649-951264db97dd.png)
こんな感じのものがPDFで出てきます。

## コード

```js:code.gs
function CreatePDF() {
  
  var ssid = "スプレッドシートIDを入力してください。";
  var ss = SpreadsheetApp.openById(ssid);
  var datass = ss.getSheetByName("請求書リスト");
  var pdfss =  ss.getSheetByName("PDFフォーム");
  var pdfsheetid = "「PDFフォーム」シートのIDを入力してください。"

  var lastrow = datass.getLastRow(); //最終行を取得します。フォーム回答後に実行されるので、先ほど入力した回答を取得するのと同義です。
  var DocumentNumber = String(lastrow-1); //請求書の採番用にインデックス行を除いて1から文書番号として設定します。"2017" + String(lastrow-1)としたり、応用の幅があります。
  
  pdfss.getRange("H2").setValue(DocumentNumber);
  pdfss.getRange("H3").setValue(datass.getRange(lastrow,2).getValue()); //請求日
  pdfss.getRange("B5").setValue(datass.getRange(lastrow,3).getValue()); //会社名
  pdfss.getRange("D14").setValue(datass.getRange(lastrow,4).getValue()); //支払期日
  pdfss.getRange("B15").setValue(datass.getRange(lastrow,5).getValue()); //内容
  pdfss.getRange("F15").setValue(datass.getRange(lastrow,6).getValue()); //単価。あとで消費税を計算するので税抜入力にしましょう
  pdfss.getRange("G15").setValue(datass.getRange(lastrow,7).getValue()); //数量
  
  var folder = DriveApp.getFolderById("フォルダIDを入力してください。");
  

  //以下PDF化のためのプログラムです。今回はA4サイズでPDF化するため、オプション設定も下記のようになります。

  var url = "https://docs.google.com/spreadsheets/d/SSID/export?".replace("SSID", ssid);
 
  var opts = {
    exportFormat: "pdf",
    format:       "pdf",
    size:         "A4",
    portrait:     "true",
    fitw:         "true",
    sheetnames:   "false",
    printtitle:   "false",
    pagenumbers:  "false",
    gridlines:    "false", // 忘れがちですがここをfalseにしないと薄い罫線が入ってしまいます。
    fzr:          "false",
    gid:          pdfsheetid
  };
  
  var url_ext = [];
  
  for( optName in opts ){
    url_ext.push( optName + "=" + opts[optName] );
  }
  
  var options = url_ext.join("&");
  var token = ScriptApp.getOAuthToken();
 
  var response = UrlFetchApp.fetch(url + options, {
    headers: {
      "Authorization": "Bearer " +  token
    }
  });

  var blob = response.getBlob().setName(DocumentNumber + "_" + datass.getRange(lastrow,3).getValue() + ".pdf");
// ここでファイル名を設定できます。今回は「文書番号_請求先名.pdf」という形式になります。日付のDate型を処理すれば請求日をファイル名にすることもできます。
 
  folder.createFile(blob);
  
}
```

## 注意点
Googleフォーム送信時に上記スクリプトが走るように、CreatePDF()に対して「スプレッドシートから→フォーム送信時」のトリガーを設定しましょう。
※参考：[Google Apps Script 実践メモ（Form Submit）](https://qiita.com/nurburg/items/f6d220e9bd6b11750b17)

## おわりに

今回は基本的な要素のみをピックアップして実装しています。他に請求書に必要な機能としては、例えば以下のようなものがあります。

- 請求項目を増やす
    - 単純に回答の列数が増えるので、行数分ループさせます。
- 備考欄に任意に入力できるようにする
    - 備考欄は任意回答項目になるので、回答がない場合に前回の備考内容が残らないようにPDFフォームをリセットする必要があります。 
- 誰がフォームに入力したのかを記録する
    - Session.getActiveUser().getEmail() を使えれば最高ですが、条件が厳しめなので社内システムとして閉じていて使用者が信用できる場合等に導入を検討しましょう。
    - 参考：[Class Session | Apps Script](https://developers.google.com/apps-script/reference/base/session)
    
上記システム導入は有償サポートも行いますのでコメント等でお気軽にご連絡ください。