【Google Apps Script(GAS)】Googleドキュメントを使って領収書を自動作成する

2020年4月22日Google Apps

テンプレートに合わせてデータを埋め込んで文書を作るという業務は色々なところで行われていることかと思います。

一例として、Spreadsheetに購入データを蓄積し、データに基づいてGoogle Documentで領収書を作成するプログラムを作って、自動化してみようと思います。

領収書以外でもデータを基に同じテンプレートを使って書類を出す業務を効率化できると思います。

処理概要

スポンサーリンク
処理概要

処理の概要です。
オレンジのマーカー部分が必要なオブジェクト、赤いマーカー部分が大まかな処理になります。

作成する文書のテンプレートをGoogle Documentで先に作っておきます。Google Spreadsheetに溜め込んだデータ読み取り、予め作っておいたテンプレートに従って、データを埋め込新しくGoogle Documentの文書作ります

Spreadsheetに溜め込んだデータの件数分の領収書を作成するので、

データ読み取り→テンプレートに埋め込み→新しい文書の作成

繰り返し、Google Documentの文書を作ります。

準備

プログラムを作る前に「作成する文書のテンプレート」と「データ」を準備します。

テンプレート作成

事前に領収書のテンプレートを準備します。

Google Documentを使ってテンプレートを作成します。
プログラムで変換をかけるために{{合計金額}}など{{}}で囲まれが部分があれば良いので、見た目はみやすいように変えても構いません。

{{宛名}}、{{合計金額}}、{{但書}}はデータから取得します。

{{日付}}にはプログラムの実行日で置き換えます。

{{税抜金額}}、{{消費税}}の部分は合計金額から計算して置き換えます。

データ作成

Google Spreadsheetにデータを溜め込みます。Google SpreadsheetにしたのはGoogle Formから簡単に連携が可能だからです。

データとして溜め込む項目は「氏名」「金額」「但書」の3つです。
「注文日」は特に必要ではありませんが、注文を管理する上ではあってもいいと思います。

シート名を「データ」に変更していますので、プログラムから読み込む際に注意してください。

必要に応じてデータの項目を増やすこともできます。

この記事ではプログラムに記述していませんが、領収書発行済みかどうかを記録しておく項目があると二重、三重で発行することを防止できます。

プロパティ設定

スクリプトエディタの「ファイル」→「プロジェクトのプロパティ」を選択し、開いたウィンドウでスクリプトプロパティタブを開き、プロパティを設定します。

プロパティ名設定値説明
tax10消費税率をパーセンテージで指定します。
2020年現在は10%なので10を設定します。
templateId作成したテンプレートのIDテンプレート作成」で作成したテンプレートのIDを設定します。
プロパティ設定

使用するサービス

スポンサーリンク

使用する主なサービスは処理概要でも登場したGoogle Spreadsheetを扱うSpreadsheetApp、Google Documentを扱うDocumentAppがあります。

処理概要に出てきていない部分ではGoogle Driveの操作を行うDriveAppや、プロパティから値を取得するPropertiesService、日付をフォーマットするためのUtilitiesを使用します。

SpreadSheetApp

Google Spreadsheetを操作するためのサービスクラスです。今回のプログラムではGoogle Spreadsheetに溜め込んだデータを読み取るために使用しますが、他にもデータを書き込んだり、グラフを作ったり様々なことができます。

今回のプログラムで使用するデータの読み取り部分です。
openById(id)でSpreadsheetを開き、getSheetByName(シート名)でシートを指定してsheet.getRange(row, 2).getValue()でデータを取得します。

for (var row = 2; row <= sheet.getLastRow(); row++)と書くことで、Spreadsheetに溜め込んでいるデータ分だけ繰り返し処理することができます。

// データを取得するスプレッドシートをオープン
var spreadsheet = SpreadsheetApp.openById('abcdefghijklmnopqrstuvwxyz');
var sheet = spreadsheet.getSheetByName('データ');
for (var row = 2; row <= sheet.getLastRow(); row++) {
  // データを取得する
  var name = sheet.getRange(row, 2).getValue();          // 氏名
  var totalAmount = sheet.getRange(row, 3).getValue();   // 金額
  var proviso = sheet.getRange(row, 4).getValue();       // 但書
  :
  :
}

こちらの記事でSpreadsheetから値を取得する方法とSpreadsheetに値を設定する方法を記載していますので、わかりづらければこちらの記事も参考にしてください。

DocumentApp

Google Documentを操作するためのサービスクラスです。今回のプログラムではGoogle Documentで作ったテンプレートをコピーして新しくGoogle Documentを作成し、プログラムで計算した値などに書き換えるために使用します。

openById(Id)でGoogle Documentを開き、getBody()で本文クラス(Bodyクラス)を取得します。本文クラスを使って本文を操作します。

本文クラスのreplaceText(searchPattern,replacement)で特定の文字列(searchPattern)を指定の値(replacement)に書き換えます。

var receipt = DocumentApp.openById(file.getId());
receipt.getBody().replaceText('{{日付}}', formatDate);
receipt.getBody().replaceText('{{宛名}}', name);
receipt.getBody().replaceText('{{合計金額}}', totalAmount.toLocaleString());
receipt.getBody().replaceText('{{但書}}', proviso);
receipt.getBody().replaceText('{{税抜金額}}', taxExcludedAmount.toLocaleString());
receipt.getBody().replaceText('{{消費税}}', taxAmount.toLocaleString());

その他

これまでに紹介したサービスクラス以外にも使用するサービスクラスがいくつかあります。
色々な場面で活躍するサービスクラスがGoogleから提供されています。

DriveApp

Google Driveを操作するためのサービスクラスです。今回のプログラムではテンプレートをコピーする処理に使用しています。

実はBodyクラスにcopy()というメソッドが用意されていますが、こちらだと使い方がよくないのかうまくいきませんでしたのでDriveAppを使ってファイルごとコピーしました。

var file = DriveApp.getFileById(templateId).makeCopy(formatDate + '_' + name + '様');

DriveAppについては当ブログでもできることをまとめていますので、参考に覗いてみてください。

Utilities

日付をフォーマットするためにUtilitiesサービスを使用します。

プログラムの実行日はvar date = new Date();で取得していますが、そのまま書き換えると「Sun Feb 01 2020 00:00:00 GMT+0000 (GMT)」という形で書き換えられてしまいます。

Utilities.formatDate(date, timeZone, format)を使うことで好きな形にすることができます。
今回は「yyyy年MM月dd日」の形にして「2020年02月01日」の形にしています。

var formatDate = Utilities.formatDate(date, 'GMT', 'yyyy年MM月dd日');

PropertiesService

プロパティ設定」で設定したスクリプトのプロパティをプログラム内で読み込むためにPropertiesServiceを使用します。

var userProperties = PropertiesService.getScriptProperties();
var templateId = userProperties.getProperty('templateId');
var tax = userProperties.getProperty('tax') / 100; // パーセンテージを数値に変換

プログラム全体

プログラム全体はこんな感じになりました。

DocumentAppサービスを使って、テンプレートを書き換える処理をcreateReceipt()にまとめてmyFunction()から呼び出しています。
そうすることでmyFunction()の処理でプログラム全体を把握しやすくなります。

詳しく書いていませんがorgFloor(value, base)で小数点以下を切り捨てています。

function myFunction() {
  // プログラム初期設定
  var date = new Date();
  var userProperties = PropertiesService.getScriptProperties();
  var templateId = userProperties.getProperty('templateId');
  var tax = userProperties.getProperty('tax') / 100; // パーセンテージを数値に変換
  
  // データを取得するスプレッドシートをオープン
  var spreadsheet = SpreadsheetApp.openById('abcdefghijklmnopqrstuvwxyz');
  var sheet = spreadsheet.getSheetByName('データ');
  for (var row = 2; row <= sheet.getLastRow(); row++) {
    // データを取得する
    var name = sheet.getRange(row, 2).getValue();          // 氏名
    var totalAmount = sheet.getRange(row, 3).getValue();   // 金額
    var proviso = sheet.getRange(row, 4).getValue();       // 但書
    
    // 内訳の計算
    var taxExcludedAmount = orgFloor(totalAmount / (1 + tax), 1);
    var taxAmount = totalAmount - taxExcludedAmount;
    
    // 領収書を作成する
    var receipt = createReceipt(date, name, totalAmount, proviso, taxExcludedAmount, taxAmount, templateId);

    // PDFに変換する
    //var folder = DriveApp.getFolderById('a1b2c3d4e5f6g7h8i9j0k');
    //try {
    //  folder.createFile(receipt.getBlob().getAs(MimeType.PDF));
    //} catch (e) {
    //  Logger.log(e.message);
    //}
  }
}

/**
 * 領収書を作成する
 * @param date 実行日
 * @param name 氏名
 * @param totalAmount 合計金額
 * @param proviso 但書
 * @param taxExcludedAmount 税抜金額
 * @param taxAmount 消費税額
 * @param templateId 領収書テンプレートのID
 */
function createReceipt(date, name, totalAmount, proviso, taxExcludedAmount, taxAmount, templateId) {
  var formatDate = Utilities.formatDate(date, 'GMT', 'yyyy年MM月dd日');
  var file = DriveApp.getFileById(templateId).makeCopy(formatDate + '_' + name + '様');
  var receipt = DocumentApp.openById(file.getId());
  receipt.getBody().replaceText('{{日付}}', formatDate);
  receipt.getBody().replaceText('{{宛名}}', name);
  receipt.getBody().replaceText('{{合計金額}}', totalAmount.toLocaleString());
  receipt.getBody().replaceText('{{但書}}', proviso);
  receipt.getBody().replaceText('{{税抜金額}}', taxExcludedAmount.toLocaleString());
  receipt.getBody().replaceText('{{消費税}}', taxAmount.toLocaleString());
  return receipt;
}

/**
 * 任意の桁で切り捨てする関数
 * @param {number} value 切り捨てする数値
 * @param {number} base どの桁で切り捨てするか(10→10の位、0.1→小数第1位)
 * @return {number} 切り捨てした値
 */
function orgFloor(value, base) {
    return Math.floor(value * base) / base;
}

まとめ

スポンサーリンク

テンプレートに合わせてデータを埋め込んで文書を作る処理を領収書を作るでプログラム化してみました。
領収書以外にもデータの分だけ繰り返し文書を出力するという業務を効率化できると思います。

「領収書を作成する」の後でPDFに変換する処理を記述しましたが、PDF形式での出力は{{}}で囲まれが部分が書き換えられないまま、PDFファイルが出来上がってしまうため、コメントアウトしています。
うまく作る方法があったら更新しようと思います。