GASで請求書PDFを自動生成してGmailで送付する仕組み

「毎月の請求書作成に時間がかかりすぎる」「送り忘れが発生する」「ファイルの管理が煩雑」——こんな悩みをお持ちではないでしょうか?この記事では、Google Apps Script(GAS)を使って、スプレッドシートのデータから請求書PDFを自動生成し、Gmailで自動送付する仕組みを作る方法を解説します。

一度仕組みを作ってしまえば、毎月の請求書送付がボタン1つで完了します。プログラミング未経験でも、この記事のコードをそのままコピーして使えるよう丁寧に解説します。

この記事でできること

  • スプレッドシートに入力した顧客情報・金額から請求書PDFを自動生成
  • 生成したPDFをGoogleドライブに自動保存
  • 顧客のメールアドレスに請求書PDFを添付して自動送信
  • 毎月の定期実行を時間ベーストリガーで自動化

事前準備(10分)

以下を用意してください。

  • Googleアカウント(無料で使える)
  • 請求書テンプレートのGoogleスプレッドシート(この記事で作成します)
  • 顧客データ管理用スプレッドシート(この記事で作成します)

特別なツールやソフトウェアは不要です。すべてGoogleのサービスだけで完結します。

STEP 1:請求書テンプレートをGoogleスプレッドシートで作成する

まず請求書のテンプレートとなるスプレッドシートを作成します。Googleドライブで新しいスプレッドシートを作成し、以下のように設定してください。

シート名:「請求書テンプレート」

  • B1セル:「御請求書」(タイトル)
  • B3セル:「請求先:」、C3セル:顧客名を後でGASが入力(空白のまま)
  • B4セル:「請求日:」、C4セル:日付を後でGASが入力(空白のまま)
  • B5セル:「支払期限:」、C5セル:期限を後でGASが入力(空白のまま)
  • B7セル:「品目」、C7セル:「数量」、D7セル:「単価」、E7セル:「金額」
  • B8セル:品目名(空白)、C8〜E8:数量・単価・金額(空白)
  • E10セル:「合計:」、F10セル:合計金額(空白)
  • B12セル:振込先情報(銀行名・口座番号など)

次に、顧客データ管理用の別シートを作成します。

シート名:「顧客データ」

  • A列:顧客名
  • B列:メールアドレス
  • C列:品目名
  • D列:数量
  • E列:単価
  • F列:送付済みフラグ(空白→送付後に「済」と記入)

スプレッドシートのIDをメモしておいてください。URLの「/spreadsheets/d/」と「/edit」の間にある文字列がIDです。

STEP 2:GASスクリプトを作成する

スプレッドシートのメニューから「拡張機能」→「Apps Script」を開き、以下のコードを貼り付けます。

// 設定値(ご自身の環境に合わせて変更してください)
const SPREADSHEET_ID = 'YOUR_SPREADSHEET_ID'; // スプレッドシートのID
const TEMPLATE_SHEET = '請求書テンプレート';
const DATA_SHEET = '顧客データ';
const SAVE_FOLDER_ID = 'YOUR_FOLDER_ID'; // 保存先Googleドライブフォルダのフォルダ ID

function sendInvoices() {
  const ss = SpreadsheetApp.openById(SPREADSHEET_ID);
  const dataSheet = ss.getSheetByName(DATA_SHEET);
  const lastRow = dataSheet.getLastRow();

  for (let i = 2; i <= lastRow; i++) {
    const row = dataSheet.getRange(i, 1, 1, 6).getValues()[0];
    const customerName = row[0];
    const email = row[1];
    const itemName = row[2];
    const quantity = row[3];
    const unitPrice = row[4];
    const status = row[5];

    // 送付済みの行はスキップ
    if (status === '済') continue;

    // テンプレートをコピーしてPDF生成
    const pdfFile = createInvoicePdf(ss, customerName, itemName, quantity, unitPrice);

    // メール送信
    GmailApp.sendEmail(
      email,
      `【御請求書】${customerName} 様`,
      `${customerName} 様\n\nいつもお世話になっております。\n今月分の御請求書をPDFにてお送りいたします。\nご確認のほどよろしくお願いいたします。\n\n株式会社〇〇\n担当:〇〇`,
      {
        attachments: [pdfFile.getAs(MimeType.PDF)],
        name: '株式会社〇〇'
      }
    );

    // 送付済みフラグを更新
    dataSheet.getRange(i, 6).setValue('済');

    // API制限を避けるため少し待機
    Utilities.sleep(1000);
  }

  Logger.log('請求書送付が完了しました');
}

function createInvoicePdf(ss, customerName, itemName, quantity, unitPrice) {
  // テンプレートシートをコピー
  const templateSheet = ss.getSheetByName(TEMPLATE_SHEET);
  const newSheet = templateSheet.copyTo(ss);
  newSheet.setName('請求書_一時');

  // データを入力
  const today = new Date();
  const invoiceDate = Utilities.formatDate(today, 'Asia/Tokyo', 'yyyy年MM月dd日');
  const dueDate = new Date(today.getFullYear(), today.getMonth() + 1, 25);
  const dueDateStr = Utilities.formatDate(dueDate, 'Asia/Tokyo', 'yyyy年MM月dd日');
  const totalAmount = quantity * unitPrice;

  newSheet.getRange('C3').setValue(customerName + ' 御中');
  newSheet.getRange('C4').setValue(invoiceDate);
  newSheet.getRange('C5').setValue(dueDateStr);
  newSheet.getRange('B8').setValue(itemName);
  newSheet.getRange('C8').setValue(quantity);
  newSheet.getRange('D8').setValue(unitPrice);
  newSheet.getRange('E8').setValue(totalAmount);
  newSheet.getRange('F10').setValue(totalAmount);

  // SpreadsheetApp.flush()で変更を確定
  SpreadsheetApp.flush();

  // PDFとして書き出し
  const pdfName = `請求書_${customerName}_${Utilities.formatDate(today, 'Asia/Tokyo', 'yyyyMM')}.pdf`;
  const url = `https://docs.google.com/spreadsheets/d/${ss.getId()}/export?format=pdf&gid=${newSheet.getSheetId()}&portrait=true&fitw=true`;
  const token = ScriptApp.getOAuthToken();
  const response = UrlFetchApp.fetch(url, {
    headers: { Authorization: `Bearer ${token}` }
  });
  const pdfBlob = response.getBlob().setName(pdfName);

  // Googleドライブに保存
  const folder = DriveApp.getFolderById(SAVE_FOLDER_ID);
  const pdfFile = folder.createFile(pdfBlob);

  // 一時シートを削除
  ss.deleteSheet(newSheet);

  return pdfFile;
}

コードを貼り付けたら、上部の定数(SPREADSHEET_IDとSAVE_FOLDER_ID)をご自身の環境に合わせて変更します。

STEP 3:スクリプトプロパティで設定を安全に管理する(推奨)

上記のコードではIDを直接記述していますが、スクリプトプロパティを使うとより安全に管理できます。GASエディタで「プロジェクトの設定」→「スクリプトプロパティ」から以下を設定してください。

  • プロパティ名:SPREADSHEET_ID / 値:スプレッドシートのID
  • プロパティ名:SAVE_FOLDER_ID / 値:保存先フォルダのID

スクリプトプロパティを使う場合は、コード冒頭の定数部分を以下のように変更します。

const props = PropertiesService.getScriptProperties();
const SPREADSHEET_ID = props.getProperty('SPREADSHEET_ID');
const SAVE_FOLDER_ID = props.getProperty('SAVE_FOLDER_ID');

STEP 4:動作確認とトリガー設定

まず手動で実行してテストします。GASエディタで「sendInvoices」関数を選択し、▶(実行)ボタンをクリックします。初回実行時はGoogleアカウントへのアクセス権限の許可を求めるダイアログが表示されるので、「許可」をクリックしてください。

動作確認が取れたら、毎月自動実行するトリガーを設定します。GASエディタ左のメニューから「トリガー」を選択し、「トリガーを追加」をクリックして以下のように設定します。

  • 実行する関数:sendInvoices
  • イベントのソース:時間主導型
  • 時間ベースのトリガーのタイプ:月ベースのタイマー
  • 実行する日:月の初日(または任意の日)
  • 時刻:午前9時〜10時

トラブルシュートチェックリスト

  • 「権限エラー」が出る場合:スクリプトエディタで「承認が必要」ダイアログが出たら「許可」をクリック。GmailAppとDriveAppのアクセス許可が必要です
  • PDFが空白になる場合:SpreadsheetApp.flush()の後にUtilities.sleep(2000)を追加して処理を待つようにしてください
  • 「ファイルが見つからない」エラー:SPREADSHEET_IDとSAVE_FOLDER_IDが正しくコピーされているか確認してください
  • メールが届かない場合:迷惑メールフォルダを確認。また、1日のGmail送信数上限(100通)に注意してください

応用:さらに便利にする拡張アイデア

  • 請求書番号の自動採番:スプレッドシートのA列に連番を設け、PDFファイル名や請求書本文に含めることで管理が楽になります
  • 消費税の自動計算:コードにtax = totalAmount * 0.1の計算を追加し、税込金額と税抜金額を別々に表示させることができます
  • 送付完了をSlackに通知:UrlFetchApp.fetch()でSlackのWebhook URLにPOSTすれば、送付完了をリアルタイムで通知できます
  • 顧客ごとに異なるテンプレートを使用:Dシートに「テンプレートシート名」列を追加して、顧客ごとに異なるデザインの請求書を送ることも可能です

まとめ

GASを使えば、請求書PDFの自動生成・自動送付という面倒な作業を完全自動化できます。一度仕組みを作ってしまえば、毎月の請求業務が大幅に削減されます。

  • スプレッドシートのデータを元に請求書PDFを自動生成できる
  • GmailAppでPDF添付メールを顧客に自動送信できる
  • 時間ベーストリガーで毎月の定期実行が可能
  • 「送付済み」フラグで二重送付を防止できる

GASの活用でお困りの点や、ご自身の業務に合わせたカスタマイズについてはお気軽にお問い合わせください。

お問い合わせはこちら

コメント

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA