【GAS】Googleフォームの入力情報から請求書を作成しPDF保存する

GAScreateFile,flat,formatDate,getActiveSheet,getFiles,getFolderById,getLastRow,getOAuthToken,getRange,getValues,hasNext,openById,setValue,UrlFetchApp.fetch

Googleフォームの入力情報から請求書を自動で作成し、PDFとして保存するところまでをGoogle Apps Script(GAS)を使って実行します。

Googleフォームから請求書を発行してPDF保存するステップ

フォームに情報を入力してから請求書を作成、保存するまで下記のようなステップを考えます。

  • 請求書情報を入力するGoogleフォームを作成する
  • フォームの入力情報が蓄積された情報を請求書に書き込む
  • 作成した請求書をPDF保存してGoogleドライブのフォルダに保存する

プログラムの流れとしては下記図式のとおりです。

フォームの入力内容が反映されたスプレッドシートの情報を読み取って請求書を作成するプログラムを記述し、最後にフォーム送信をトリガーに設定します。

事前準備

Googleフォームの作成

まずは請求書に記載する情報入植するためのGoogleフォームを用意します。適当なフォルダ配下で「新規」>「Googleフォーム」を開いてください。

請求書に必要な情報として、今回は下記の内容をフォームに入力することにします。簡易化のため品目は3つまでとしていますが、4つ以上の場合も3つまでの場合と同様に記述できます。

  • 取引先名
  • 請求日
  • 担当者
  • 品目1(品名・数量・単位・単価)
  • 品目2(品名・数量・単位・単価)
  • 品目3(品名・数量・単位・単価)

上記の情報を入力できるようにフォームを作ります。品目が1つも入ってないと請求書作れないので品目1の項目は必須としています。

入力項目が完成したら、「回答」タブに遷移してスプレッドシートマークを押すとスプレッドシートが自動で作成されます。

フォームを実際に入力し、スプレッドシートに回答が反映されているか確認してください。

請求書テンプレートの作成

既存のフォーマットがあればそれを、なければネットに落ちている請求書テンプレートをコピペしてテンプレートを作成してください。フォームで入力した情報をこのテンプレートに反映することで、都度請求書を作成できるプログラムを組んでいきます。

お持ちでない方はテンプレートを置いておくのでご自由にお使いください。こちらを使えば本日のスクリプトはほぼコピペで実行できます。
★請求書テンプレート

テンプレート内の情報反映箇所の確認

今回のスクリプトではフォームの入力情報から赤で囲った部分をGASで入力し、青で囲った部分を自動で計算されるように関数を入れます。後段パートで解説しますが、請求番号は回答フォームに入力された順番で請求書ごとに連番を付けます。

GASを使って請求書に情報入力していくので、入力個所を間違えないように対象のセル番号を確認しておいてください。↑のテンプレートを使う場合は下記のセル番号です。

  • 取引先名:B6
  • 請求日:N6
  • 請求番号:N7
  • 担当者:N16
  • 品名1:B18
  • 数量1:J18
  • 単位1:K18
  • 単価1:L18
  • 品名1:B19
  • 数量1:J19
  • 単位1:K19
  • 単価1:L19
  • 品名1:B20
  • 数量1:J20
  • 単位1:K20
  • 単価1:L20

PDF化した請求書を保存するフォルダの作成

Googleドライブ内で発行した請求書PDF保存用のフォルダを作成してください。保存先のフォルダはIDで指定するので場所はどこでも問題ないですが、作成したファイルとフォルダが同じ場所だとわかりやすいと思います。

ファイルID・フォルダIDの取得

コード内で情報取得・入力・保存にIDを指定します。「フォームの回答」と「請求書テンプレート」、ファイル保存フォルダのフォルダIDを取得してください。

確認方法はそれぞれのファイル・フォルダのURLを確認するのが一番手っ取り早いです。

ファイルIDの場合は「https://docs.google.com/spreadsheets/d/{ファイルID}/edit#gid={シートID}」、フォルダIDの場合は「https://drive.google.com/drive/u/0/folders/{フォルダID}」です。

フォームからスクリプトエディタを立ち上げ

GASのスクリプトエディタを立ち上げます。Googleスプレッドシートから立ち上げることが多いですが、今回はフォームの送信をトリガーとしたいのでフォームから操作してください。

さて、これで準備は完了です。コード全体像をお見せしてから各パートの解説を行います。

サンプルコード

function main() {
  
  // スプレッドシート「フォームの回答」のファイルID
  const spIdForm = 'フォームの回答のファイルID';

  // スプレッドシート「請求書テンプレート」ファイルID
  const spIdInvoice = '「請求書テンプレート」ファイルID';

  // PDF化した請求書を保存するフォルダ
  const folderId = '保存用フォルダID';

  // 「フォームの回答」から作成する請求書の情報を取得
  let values = getInvoiceInfo(spIdForm);

  let info = values[0]; // フォームで入力した情報
  let num = values[1] - 1; // 請求書番号とする数字、「スプレッドシートに記載されている行番号 - 1」で連番になるように定義

  // 取得した情報を請求書テンプレートに入力
  inputInvoice(spIdInvoice, info, num);

  // 入力更新済みの請求書をPDF化してGoogleドライブのフォルダに保存
  createInvoice(spIdInvoice, info, num, folderId);

}

function getInvoiceInfo(id) {

  let sheet = SpreadsheetApp.openById(id).getActiveSheet();
  let lastRow = sheet.getLastRow(); 

  // 回答項目をすべて取得
  let range = sheet.getRange(lastRow,1,1,16)
  // 二次元配列になっているので展開しておく
  let invoiceInfo = range.getValues().flat();

  return [invoiceInfo, lastRow];//請求書番号の基準として行番号を使うのでlastRowも返しておく取得
}


function inputInvoice(id, info, num){

  let sheet = SpreadsheetApp.openById(id).getActiveSheet();

  sheet.getRange('B6').setValue(info[1]); //取引先名
  sheet.getRange('N6').setValue(info[2]); //請求日
  sheet.getRange('N7').setValue(num); //請求番号
  sheet.getRange('N16').setValue(info[3]); //担当者


 //品目1入力
  sheet.getRange('B18').setValue(info[4]); //品名1
  sheet.getRange('J18').setValue(info[5]); //数量1
  sheet.getRange('K18').setValue(info[6]); //単位1
  sheet.getRange('L18').setValue(info[7]); //単価1

 //品目2入力
  sheet.getRange('B19').setValue(info[8]); //品名2
  sheet.getRange('J19').setValue(info[9]); //数量2
  sheet.getRange('K19').setValue(info[10]); //単位2
  sheet.getRange('L19').setValue(info[11]); //単価2

 //品目3入力
  sheet.getRange('B20').setValue(info[12]); //品名3
  sheet.getRange('J20').setValue(info[13]); //数量3
  sheet.getRange('K20').setValue(info[14]); //単位3
  sheet.getRange('L20').setValue(info[15]); //単価3

  SpreadsheetApp.flush();

}

function createInvoice(id, info, num, folderId){

  let sheet = SpreadsheetApp.openById(id).getActiveSheet();
  let sheetId = sheet.getSheetId(); // スプレッドシート全体ではなくシートのID、PDF化するときに必要なので取得しておく

  // PDF保存する際のファイル名、「invoice_請求書番号_取引先名_請求日」となるように変数を作成
  let fileName = 'invoice_'
                + num
                + '_'
                + info[1]
                + '_'
                + Utilities.formatDate(info[2], 'JST', 'yyyy-MM-dd'); // 2022-10-18といった形式になるように変換


 //PDFのオプションを指定
  let pdfOptions = '&exportFormat=pdf&format=pdf'
              + '&size=A4' //用紙サイズ A4
              + '&portrait=true'  //用紙の向き:true→ 縦向き、false→ 横向き
              + '&fitw=true'  //幅を用紙にフィットさせるか
              + '&horizontal_alignment=CENTER' //水平方向
              + '&vertical_alignment=TOP' //垂直方向


 // PDF化でフェッチするURL、オプションはパラメータに入れる
  let url = 'https://docs.google.com/spreadsheets/d/'
          +  id
          + '/export?gid='
          + sheetId
          + pdfOptions;

  //アクセストークン取得
  let token = ScriptApp.getOAuthToken();
  let options = {
    headers: {
        'Authorization': 'Bearer ' +  token
    }
  };

  //保存用のGoogleドライブフォルダ呼び出し
  let folder = DriveApp.getFolderById(folderId);

  //PDF作成&保存実行
  let blob = UrlFetchApp.fetch(url, options).getBlob().setName(fileName + '.pdf');

  folder.createFile(blob);
      
}

フォームの回答情報から請求書の内容を取得する

フォーム回答情報シートのファイルIDを引数に、関数 getInvoiceInfoを呼び出し、getInvoiceInfoの中で最終行のデータを取得します。今回のプログラムはフォーム送信直後に発動するので、最終行を取得=今入力された情報を取得、というわけです。

//関数mail
///////

  // スプレッドシート「フォームの回答」のファイルID
  const spIdForm = 'フォームの回答のファイルID';

  // 「フォームの回答」から作成する請求書の情報を取得
  let values = getInvoiceInfo(spIdForm);
function getInvoiceInfo(id) {

  let sheet = SpreadsheetApp.openById(id).getActiveSheet();
  let lastRow = sheet.getLastRow(); 

  // 回答項目をすべて取得
  let range = sheet.getRange(lastRow,1,1,16)
  // 二次元配列になっているので展開しておく
  let invoiceInfo = range.getValues().flat();

  return [invoiceInfo, lastRow];//請求書番号の基準として行番号を使うのでlastRowも返しておく取得
}

取得対象のデータ範囲を指定するメソッドRangeの使い方は、sheet.getRange(取得したい開始行番号, 開始列番号, 取得行数, 取得列数)です。

  // 回答項目をすべて取得
  let range = sheet.getRange(lastRow,1,1,16)

  // 二次元配列になっているので展開しておく
  let invoiceInfo = range.getValues().flat();

請求書なので請求書番号が必要ですね。固有番号かつ連番にできる行番号を採用することにして、後で利用するために返り値として一緒に渡しておきます。

  return [invoiceInfo, lastRow];//請求書番号の基準として行番号を使うのでlastRowも返しておく取得

請求書テンプレートの各項目にデータを書き込む

請求書の各項目に情報を入力する

「請求書テンプレート」のファイルID、請求書情報、請求書番号を引数に関数 inputInvoice実行して、先ほど整理した各項目に対応するセル番号を指定して書き込みを行っていきます。

function inputInvoice(id, info, num){

  let sheet = SpreadsheetApp.openById(id).getActiveSheet();

  sheet.getRange('B6').setValue(info[1]); //取引先名
  sheet.getRange('N6').setValue(info[2]); //請求日
  sheet.getRange('N7').setValue(num); //請求番号
  sheet.getRange('N16').setValue(info[3]); //担当者


 //品目1入力
  sheet.getRange('B18').setValue(info[4]); //品名1
  sheet.getRange('J18').setValue(info[5]); //数量1
  sheet.getRange('K18').setValue(info[6]); //単位1
  sheet.getRange('L18').setValue(info[7]); //単価1

 //品目2入力
  sheet.getRange('B19').setValue(info[8]); //品名2
  sheet.getRange('J19').setValue(info[9]); //数量2
  sheet.getRange('K19').setValue(info[10]); //単位2
  sheet.getRange('L19').setValue(info[11]); //単価2

 //品目3入力
  sheet.getRange('B20').setValue(info[12]); //品名3
  sheet.getRange('J20').setValue(info[13]); //数量3
  sheet.getRange('K20').setValue(info[14]); //単位3
  sheet.getRange('L20').setValue(info[15]); //単価3

  SpreadsheetApp.flush();

}

【重要】PDF化する前に一度スプレッドシートを更新する(flushメソッド)

GASにおいて、スプレッドシートへの書き込みはコードがすべで実行された後に更新されます。同じスクリプト内で請求書テンプレート更新とPDFを実行しようとすると、デフォルト状態ではテンプレート更新前の情報でPDF化されてしまうことになるので、スプレッドシートアプリのfliushメソッドを実行しておきます。

これを挟むことで入力した情報を更新してから次のスクリプトを実行できます。

  SpreadsheetApp.flush();

ここまでのスクリプトを実行してみると無事に「請求書テンプレート」が入力されている情報で更新されたことが確認できます。

作成した請求書をPDF化してフォルダに保存する

ファイル名指定

先ほど更新した「請求書テンプレート」のファイルID、請求書情報(ファイル名に使う)、請求書番号(ファイル名に使う)、保存用のフォルダIDを引数にしてcreateInvoiceを実行します。

ファイル名は「invoice_請求書番号_取引先名_請求日」となるように変数を作成してますが、お好きなように定義してください。

function createInvoice(id, info, num, folderId){

  let sheet = SpreadsheetApp.openById(id).getActiveSheet();
  let sheetId = sheet.getSheetId(); // スプレッドシート全体ではなくシートのID、PDF化するときに必要なので取得しておく

  // PDF保存する際のファイル名「invoice_請求書番号_取引先名_請求日」
  let fileName = 'invoice_'
                + num
                + '_'
                + info[1]
                + '_'
                + Utilities.formatDate(info[2], 'JST', 'yyyy-MM-dd'); // 2022-10-18といった形式になるように変換

PDFのオプション指定・PDF化

PDFファイル実行時にオプションを指定することで細かいアウトプットの調整ができます。今回は基本的な用紙サイズ・向きやポジションのみですが、余白なども指定可能です。

 //PDFのオプションを指定
  let pdfOptions = '&exportFormat=pdf&format=pdf'
              + '&size=A4' //用紙サイズ A4
              + '&portrait=true'  //用紙の向き:true→ 縦向き、false→ 横向き
              + '&fitw=true'  //幅を用紙にフィットさせるか
              + '&horizontal_alignment=CENTER' //水平方向
              + '&vertical_alignment=TOP' //垂直方向

あとはおまじないだと思って下記のスクリプトを実行してください。

 // PDF化でフェッチするURL、オプションはパラメータに入れる
  let url = 'https://docs.google.com/spreadsheets/d/'
          +  id
          + '/export?gid='
          + sheetId
          + pdfOptions;

  //アクセストークン取得
  let token = ScriptApp.getOAuthToken();
  let options = {
    headers: {
        'Authorization': 'Bearer ' +  token
    }
  };

  //保存用のGoogleドライブフォルダ呼び出し
  let folder = DriveApp.getFolderById(folderId);

  //PDF作成&保存実行
  let blob = UrlFetchApp.fetch(url, options).getBlob().setName(fileName + '.pdf');

  folder.createFile(blob);
      
}

無事にPDFを保存することができました。

フォーム送信をトリガーに設定

一連の操作を手動ではなくフォームが送信された瞬間に実行されるようにトリガーを設定します。左のメニューバーから「トリガー」を選択してください。

「トリガーを追加」をクリックします。

実行する関数を「main」であることを確認しつつ、イベントの種類の選択を「フォーム送信時」に変更してください。最後に「保存」して完了です。

これで請求書フォームが入力されるたびに新しい請求書が作られ、PDF保存してくれることろまでを実行するGASスクリプトが完成しました。

まとめ

Google Apps Scriptを使ってGoogleフォームから請求書を作成してPDF保存する方法をご紹介しました。今回は請求書でしたが、定型化されたいろいろな書類に使える内容かと思います。ぜひ参考にしてみてください。

次回は今回作成した請求書を題材に、GASで発行したファイルをスプレッドシート上で一覧化して管理する方法を解説します。

この記事を書いた人

てつお
広告代理店出身、事業会社でWebマーケティングや開発の仕事をしている26歳です。プロフィール詳細はこちら
■Google広告認定資格|Google アナリティクス個人認定資格(GAIQ)|TOEIC920点