1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

GASで請求書PDF自動生成+Gmail下書きを実装した(コード全公開・MIT)

1
Last updated at Posted at 2026-06-14

毎月末に請求書を手作業で作っていて、「これは絶対に自動化できる」と思ったので作りました。

Googleスプレッドシートにデータを入力してボタンを押すだけで、取引先ごとに請求書PDFを生成し、Gmailの下書きを作成します。確認してから送信ボタンを押す、というフローです。

コードはMITライセンスで全公開しています。

🔗 mamagotolab/gas-invoice-automation


できること

  • スプレッドシートの請求データから請求書PDFを自動生成(HTMLベース・テンプレート不要)
  • 取引先ごとにGmailの下書きを作成(PDF添付・本文自動生成)
  • 確認後にまとめて自動送信するメニューも用意
  • 複数品目を1枚の請求書にまとめる(同じ請求書番号でグルーピング)
  • 小計・消費税(10%)・合計の自動計算
  • 全件バリデーションを処理前に実施(1件でもエラーがあれば何も始めない)
  • 処理済みフラグの自動記録(二重送付防止)
  • initSheets() で初期シートとサンプルデータを自動生成

導入手順

  1. Googleスプレッドシートを新規作成
  2. メニュー 拡張機能 → Apps Script を開く
  3. Code.gs の中身を貼り付けて保存
  4. スプレッドシートを再読み込みすると 「請求書ツール」 メニューが表示される
  5. 「① 初期セットアップ(サンプル作成)」 を実行
  6. 「設定」シートに自社情報を入力して完成

Googleドキュメントのテンプレートもフォルダも不要です。スプレッドシート1つで動きます。


処理フロー

「② 請求書を作成(Gmail下書き)」を押す
  ↓
設定シートと全請求データをバリデーション(1件でもNGなら中止)
  ↓
請求書番号でグルーピング(同番号の複数行 → 1枚の請求書)
  ↓
取引先ごとにHTML請求書を生成 → PDF変換
  ↓
Gmail下書きを作成(PDF添付)
  ↓
「送信済み」フラグを記録

確認後に「③ 請求書を送信(自動)」を押すと一括送信できます。


スプレッドシートの列構成

項目 備考
A 請求書番号 同じ番号の行が1枚の請求書にまとまる
B 発行日 例:2026-06-30
C 取引先名
D 宛先メール
E 品目
F 数量
G 単価(税抜)
H ステータス 自動更新
I 処理日時 自動更新

「初期セットアップ」を実行するとサンプルが入った状態で作られるので、そのまま書き換えて使えます。


コードの解説

設計の軸:「下書き作成」がデフォルト

function createInvoiceDrafts() {
  processInvoices_(false);  // false = 下書き作成
}

function sendInvoices() {
  const ui = SpreadsheetApp.getUi();
  const res = ui.alert(
    '確認',
    '請求書を取引先へ自動送信します。よろしいですか?\n(不安な場合は「請求書を作成(Gmail下書き)」をおすすめします)',
    ui.ButtonSet.OK_CANCEL
  );
  if (res !== ui.Button.OK) return;
  processInvoices_(true);  // true = 送信
}

送信は別メニュー+確認ダイアログの2ステップにしています。請求書のような「間違えたら困る」処理は、自動化するほど事故の影響が大きくなるので、人が目視で確認する余地を必ず残します。


処理前の一括バリデーション

function processInvoices_(send) {
  const config = readConfig_();
  const invoices = groupByInvoice_(dataSheet);

  // 処理を始める前に全件検証
  const errors = validateInvoices_(invoices, config);
  if (errors.length > 0) {
    ui.alert('入力エラー', '処理を中止しました。以下を修正してください。\n\n' + errors.join('\n'), ui.ButtonSet.OK);
    return;
  }

  // ← ここから先は全件OKが保証されている
  Object.keys(invoices).forEach(function (no) { /* PDF生成・メール */ });
}

バリデーションは処理を始める前にまとめて実行します。「3件目でエラーが出て1〜2件目だけ下書きが作られた」という中途半端な状態を防ぎます。チェック内容は設定シートの必須項目、メールアドレスの形式、品目の空白、数量のゼロ以下など。

function validateInvoices_(invoices, config) {
  const errors = [];
  const required = ['自社名', '住所', '振込先'];
  required.forEach(function (key) {
    if (!config[key]) errors.push('・「設定」シートの『' + key + '』が空です');
  });

  const emailRe = /^[^@\s]+@[^@\s]+\.[^@\s]+$/;
  Object.keys(invoices).forEach(function (no) {
    const inv = invoices[no];
    if (!inv.client) errors.push('' + no + ':取引先名が空です');
    if (!emailRe.test(inv.email)) errors.push('' + no + ':宛先メールが不正です(' + inv.email + '');
    inv.items.forEach(function (it) {
      if (!it.item) errors.push('' + no + ':品目が空の行があります');
      if (it.qty <= 0) errors.push('' + no + ':数量が0以下の行があります(' + it.item + '');
    });
  });
  return errors;
}

請求書番号でのグルーピング

function groupByInvoice_(sheet) {
  const values = sheet.getRange(2, 1, last - 1, COL.processedAt).getValues();
  values.forEach(function (row, i) {
    const no = String(row[COL.invoiceNo - 1]).trim();
    if (!no) return;
    if (!invoices[no]) {
      invoices[no] = { no, client, email, items: [], rows: [] };
    }
    invoices[no].items.push({ item, qty, unitPrice });
    invoices[no].rows.push(i + 2);
  });
}

同じ請求書番号の行を1枚にまとめます。INV-001 が3行あれば、品目3つの請求書が1通作られます。行を削除せず「番号だけ変える」で品目を追加・分割できるのが使いやすいポイントです。


HTMLからPDFを生成(テンプレートファイル不要)

const html = buildInvoiceHtml_(config, inv);
const pdf = Utilities.newBlob(html, 'text/html', '請求書_' + no + '.html')
  .getAs('application/pdf')
  .setName('請求書_' + no + '.pdf');

GASの Blob.getAs('application/pdf') でHTMLをPDFに変換しています。Googleドキュメントのテンプレートを用意する必要がないので、導入の手間がゼロです。レイアウトは buildInvoiceHtml_() の中のHTMLを直接編集して変えられます。


動作環境・制限事項

  • GASのメール送信上限:無料Googleアカウントで1日100通(受信者数ベース)、Google Workspaceで1,500通
  • GASの実行時間上限:1回6分。大量処理の場合は請求書番号で分割実行してください
  • PDFのフォントUtilities.newBlob 経由のPDFはGoogleのサーバーサイドでレンダリングされるため、日本語フォントはシステム依存です。フォントにこだわる場合はGoogleドキュメントテンプレート方式に切り替えてください

カスタマイズポイント

よくある拡張:

  • インボイス制度対応buildInvoiceHtml_() のHTMLに登録番号・税率区分の行を追加するだけ
  • 支払期限:設定シートの「支払期限(発行日から日数)」で変更できます(デフォルト30日)
  • Google Driveへの保存DriveApp.getFolderById(id).createFile(pdf) を追加すれば保存できます
  • Slack通知:送信完了後に UrlFetchApp.fetch() でIncoming Webhookを叩く

おわりに

このツールを作ったきっかけは「毎月末に同じ作業を繰り返すこと」への違和感でした。請求書を1枚ずつ手で作るのは難しくないですが、ミスが許されない緊張感だけが積み重なっていく。それをなくしたかった。

コードはMITライセンスで公開しているので、自由に使って、改造して、壊してもらって構いません。IssueやPRも歓迎です。

カスタマイズや業務フローとの統合(会計ソフト連携・督促メール自動化・入金消込など)については個別に対応しています。

🔗 コード:mamagotolab/gas-invoice-automation
🔗 開発ラボ:mamagotolab.com

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?