毎月の請求書づくり、僕は最初「1件5分くらいだし、まあいいか」と思っていました。でも数えてみると、取引先ごとにテンプレをコピーして、日付と金額を打ち替えて、PDFに書き出して、メールに添付して……を10社ぶんやると、それだけで1時間近く溶けています。しかも打ち間違い(請求番号の重複、支払期限の書き忘れ)が地味に怖い。
このページでは、Googleスプレッドシートに数字を入れてボタンを押すだけで、請求書PDFを作ってメール送信まで終わる仕組みを作ります。使うのは無料のGAS(Google Apps Script)だけ。コードはそのままコピペできる形で全部載せます。
僕(シグ)はAIを使ってこういう業務自動化ツールを作って、自分で動かして確認してから公開しています。この記事のコードもGoogle Apps Script上で動く前提で書いています。
完成イメージ:ビフォーアフター
Before(手作業)
- テンプレをコピー → 取引先名・金額・日付を手で打ち替え → PDF書き出し → メール作成 → 添付 → 送信
- 10社で約60分。月末に必ず発生。打ち間違いのリスクつき。
After(自動化)
- スプレッドシートに「取引先・宛先メール・請求番号・明細」を入力
- メニューの「請求書 → 作成して送信」を押す
- → PDFがGoogleドライブに保存され、取引先にメール送信まで完了(数秒)
やることは「数字を入れる」だけになります。
用意するもの(3つ)
- スプレッドシート … 請求データを入力する場所
- Googleドキュメントのテンプレート … 請求書の見た目(ここに差し込む)
- Googleドライブのフォルダ … 作ったPDFの保存先
すべてGoogleアカウントがあれば無料です。
① スプレッドシートのレイアウト
「請求データ」という名前のシートを1枚作り、次のように置きます(セル位置は下のコードと合わせてあります)。
| セル | 内容 | 例 |
|---|---|---|
| B1 | 取引先名 | 株式会社サンプル |
| B2 | 送信先メール | keiri@example.com |
| B3 | 請求番号 | INV-2026-001 |
| B4 | 発行日 | 2026/7/31 |
| B5 | 支払期限 | 2026/8/31 |
そして 8行目から下に明細を、A列=品目 / B列=数量 / C列=単価 で入力します。
| 行 | A(品目) | B(数量) | C(単価) |
|---|---|---|---|
| 8 | Webサイト保守 | 1 | 30000 |
| 9 | 記事執筆 | 3 | 8000 |
金額(数量×単価)・小計・消費税・合計はコードが自動計算するので、入力は不要です。
② ドキュメントのテンプレート
Googleドキュメントで請求書の体裁を作り、差し替えたい場所に二重の波かっこでプレースホルダーを書いておきます。
請求書
{{取引先名}} 御中
請求番号: {{請求番号}}
発行日: {{発行日}}
お支払期限: {{支払期限}}
【明細】
{{明細}}
小計: {{小計}}
消費税: {{消費税}}
合計: {{合計}}
{{差出人}}
{{明細}} の1行が、明細の各行に置き換わります。ロゴや振込先などの固定情報は、テンプレートに直接書いておけばそのまま出力されます。
GASのコード(そのままコピペ)
スプレッドシートのメニューから 拡張機能 → Apps Script を開き、コード.gs の中身を全部消して、下を貼り付けてください。
/**
* スプレッドシートの請求データ → 請求書PDF → Gmail送信 まで自動化
* メニュー「請求書」から実行します。
*/
// ==== 設定(ここだけ自分の環境に合わせる)====
const TEMPLATE_DOC_ID = 'テンプレートDocのIDを貼る'; // ドキュメントURLの /d/ と /edit の間の文字列
const OUTPUT_FOLDER_ID = '出力フォルダのIDを貼る'; // フォルダURLの末尾の文字列
const SENDER_NAME = '株式会社サンプル'; // 差出人の表示名
const TAX_RATE = 0.10; // 消費税率(自社の状況に合わせる)
/** スプレッドシートを開くとメニューを追加 */
function onOpen() {
SpreadsheetApp.getUi()
.createMenu('請求書')
.addItem('作成して送信', 'createAndSendInvoice')
.addItem('PDFだけ作成(送信しない)', 'createInvoicePdfOnly')
.addToUi();
}
/** 請求書PDFを作って、そのままメール送信する */
function createAndSendInvoice() {
const inv = buildInvoice_();
if (!inv.toEmail) throw new Error('送信先メール(B2)が空です。');
// GmailApp で本文つき・PDF添付のメールを送信
GmailApp.sendEmail(
inv.toEmail,
'【請求書】' + inv.invoiceNo + ' のご送付',
inv.client + ' 御中\n\n' +
'いつもお世話になっております。\n' +
'請求書(No.' + inv.invoiceNo + ')をお送りいたします。\n' +
'ご確認のほど、よろしくお願いいたします。\n\n' +
SENDER_NAME,
{ name: SENDER_NAME, attachments: [inv.pdfBlob] }
);
SpreadsheetApp.getActiveSpreadsheet().toast('送信しました: ' + inv.toEmail, '完了', 5);
}
/** メール送信せず、PDFの作成だけ(下書き確認用) */
function createInvoicePdfOnly() {
const inv = buildInvoice_();
SpreadsheetApp.getActiveSpreadsheet().toast('PDFを作成しました: ' + inv.pdfName, '完了', 5);
}
/** 請求データを読み、テンプレに差し込んでPDFを作る(共通処理) */
function buildInvoice_() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName('請求データ');
if (!sheet) throw new Error('「請求データ」シートが見つかりません。シート名を確認してください。');
// --- ヘッダー項目(B列)---
const client = sheet.getRange('B1').getValue();
const toEmail = sheet.getRange('B2').getValue();
const invoiceNo = sheet.getRange('B3').getValue();
const issueDate = sheet.getRange('B4').getValue();
const dueDate = sheet.getRange('B5').getValue();
// --- 明細(8行目以降: A=品目 B=数量 C=単価)---
const numRows = Math.max(sheet.getLastRow() - 7, 0);
const raw = numRows > 0 ? sheet.getRange(8, 1, numRows, 3).getValues() : [];
const items = raw.filter(function (r) { return r[0] !== '' && r[0] !== null; });
if (items.length === 0) throw new Error('明細(8行目以降)が入力されていません。');
// --- 金額を計算 ---
let subtotal = 0;
const rows = items.map(function (r) {
const qty = Number(r[1]) || 0;
const price = Number(r[2]) || 0;
const amount = qty * price;
subtotal += amount;
return { name: r[0], qty: qty, price: price, amount: amount };
});
const tax = Math.floor(subtotal * TAX_RATE);
const total = subtotal + tax;
// --- 表示用の整形 ---
const tz = Session.getScriptTimeZone();
const fmtDate = function (d) {
return (d instanceof Date) ? Utilities.formatDate(d, tz, 'yyyy年MM月dd日') : String(d);
};
const yen = function (n) { return '¥' + Number(n).toLocaleString('ja-JP'); };
const lines = rows.map(function (r) {
return r.name + ' ' + r.qty + ' × ' + yen(r.price) + ' = ' + yen(r.amount);
}).join('\n');
// --- テンプレDocを複製して差し込み ---
const folder = DriveApp.getFolderById(OUTPUT_FOLDER_ID);
const pdfName = '請求書_' + invoiceNo + '_' + client;
const copy = DriveApp.getFileById(TEMPLATE_DOC_ID).makeCopy(pdfName, folder);
const doc = DocumentApp.openById(copy.getId());
const body = doc.getBody();
body.replaceText('{{取引先名}}', String(client));
body.replaceText('{{請求番号}}', String(invoiceNo));
body.replaceText('{{発行日}}', fmtDate(issueDate));
body.replaceText('{{支払期限}}', fmtDate(dueDate));
body.replaceText('{{差出人}}', SENDER_NAME);
body.replaceText('{{明細}}', lines);
body.replaceText('{{小計}}', yen(subtotal));
body.replaceText('{{消費税}}', yen(tax));
body.replaceText('{{合計}}', yen(total));
doc.saveAndClose();
// --- PDFに変換してフォルダに保存 ---
const pdfBlob = copy.getAs('application/pdf').setName(pdfName + '.pdf');
folder.createFile(pdfBlob);
copy.setTrashed(true); // 中間の複製Docは捨てる(PDFだけ残す)
return {
client: client, toEmail: toEmail, invoiceNo: invoiceNo,
pdfBlob: pdfBlob, pdfName: pdfName
};
}
IDの入れ方
コード上部の TEMPLATE_DOC_ID と OUTPUT_FOLDER_ID を、自分のURLから書き換えます。
- ドキュメント:
https://docs.google.com/document/d/ここがID/edit - フォルダ:
https://drive.google.com/drive/folders/ここがID
動かし方
- 上のコードを貼って保存し、いったんスプレッドシートを再読み込みします。
- メニューに「請求書」が増えるので、請求書 → 作成して送信を押します。
- 初回だけ「承認が必要です」と出ます。自分のアカウントで内容を確認して許可すればOK(自分のドライブとGmailを操作するための許可です)。
- まずは自分宛のメールでテストしてから、本番の取引先に使うのがおすすめです。
つまづきやすいポイント
{{明細}}が置き換わらない … テンプレの波かっこが全角になっていないか、前後に余計なスペースが入っていないかを確認。コピペで作るのが確実です。- 承認画面で止まる … 「詳細」→「(プロジェクト名)に移動」から進めます。これは自分のGoogleアカウント内だけで動くスクリプトなので、他人にデータが渡ることはありません。
- 送信数の上限 … Gmailの1日の送信数には上限があります(無料アカウントとGoogle Workspaceで異なります)。大量送信を考えている場合は、Googleの公式ヘルプで自分のプランの上限を確認してください。
- 消費税・インボイスの扱い … 税率や端数処理、適格請求書(インボイス)としての必須記載事項は、業種や登録状況で変わります。必ず国税庁など公式情報で自社の要件を確認してから、テンプレートに反映してください。ここでは仕組みの作り方だけを扱っています。
- 毎月自動で送りたい … スクリプトエディタの「トリガー」で
createAndSendInvoiceを月次実行に設定できます。ただし請求は金額ミスが致命的なので、僕は最終送信は手動ボタンにしておくことをおすすめします。
「自分でやるのは大変そう」と感じたら
ここまで読んで「仕組みは分かったけど、テンプレ作りや承認まわりでつまずきそう」「うちは請求のパターンが特殊で、そのままだと当てはまらない」という方もいると思います。
僕は、こういうスプレッドシート×GASの業務自動化やWebアプリを受託で作っています。御社の請求フロー(明細の形・取引先ごとの様式・締め日・振込先の出し分けなど)に合わせて、動く状態でお渡しします。AIを使うぶん速く・安く作れますが、要件のヒアリングと動作確認・修正は僕が責任を持って行います。
「この作業、自動化できる?」の相談だけでも歓迎です。まずはお気軽にどうぞ。