毎月の請求書発行、いまだに次のような手作業を繰り返していませんか。
- 1社ずつテンプレートをコピーする
- 取引先名と金額を打ち替える
- PDFに書き出す
- メールに添付する
取引先が10社・20社と増えるほど、この単純作業に半日とられてしまいますよね。転記ミスや添付忘れも怖いところです。
実はこの一連の流れ、Google Apps Script(GAS)を使えばボタン1つで丸ごと自動化できます。スプレッドシートの請求データをテンプレートに差し込み、PDF化して、Driveに保存して、Gmailで送付するまでを全部やってくれます。
この記事では、コピペでそのまま動く完成コードと、つまずきやすいポイントの対処法まで、実務で使える形でまとめました。
GASで請求書PDFを自動生成する仕組みとは?|全体の流れを把握する
最初に、これから作るものの全体像をつかんでおきましょう。やっていることはシンプルで、次の5ステップを GAS が順番に実行するだけです。
- 請求データ(取引先・品目・金額)が並んだ「データシート」を1行ずつ読み込む
- レイアウトを整えた「テンプレートシート」にその値を差し込む
- テンプレートシートをPDFに変換する
- PDFをGoogle Driveの指定フォルダに保存する
- 取引先のメールアドレス宛にPDFを添付してGmailで送信する
ちょっとむずかしく見えますが、やっていることはシンプルです。「スプレッドシートを差し込み印刷して、PDFにして、メールで送る」という、ふだん手作業でやっていることをそのままコードにしただけなんです。
NOTE: GASは追加費用ゼロで使えます
GAS(Google Apps Script)はGoogleが提供する無料のスクリプト環境で、JavaScriptに似た言語で書きます。Googleアカウントさえあれば、インストール不要でブラウザ上のエディタからすぐに使えます。基本操作に不安がある方はGAS入門記事を先に読んでおくと、この記事がスムーズに理解できますよ。
必要な前提条件
実装を始める前に、次のものを準備しておいてください。
- Googleアカウント(無料のもので問題ありません)
- 編集権限のあるGoogleスプレッドシート1つ
- 請求書PDFを保存するGoogle Driveのフォルダ1つ
- 取引先のメールアドレスが分かっていること(自動送信する場合)
特別な有料サービスやプラグインは一切不要です。すべてGoogle標準の機能だけで完結します。
スプレッドシートを準備する|データシートとテンプレシートを作る
GASを書く前に、土台となるスプレッドシートを整えます。1つのスプレッドシートの中に「データシート」と「テンプレートシート」という2枚のシートを用意するのがポイントです。
データシートの作り方
まず1枚目に、請求データを並べる「データ」という名前のシートを作ります。1行目を見出しにして、2行目以降に取引先ごとのデータを1行ずつ入れていきましょう。
| 取引先名 | メールアドレス | 品目 | 金額 |
|---|---|---|---|
| 株式会社サンプル | sample@example.com | Webサイト保守 | 50000 |
| 田中商事株式会社 | tanaka@example.com | コンサルティング | 120000 |
金額の列は、カンマ区切りの文字列ではなく数値のまま入れておきましょう。あとでコード側で toLocaleString() を使ってカンマ表示に整えるので、ここでは生の数値にしておくのがポイントです。
テンプレートシートの作り方
次に2枚目に、請求書のレイアウトを整えた「テンプレート」という名前のシートを作ります。ここは実際の請求書として見栄えするように、セルの結合や罫線、フォントサイズを調整してデザインします。
差し込みたい場所には、あとでGASが置き換えるための目印(プレースホルダー)を入れておきます。たとえば次のように書いておきます。
- 宛名のセル:
{{取引先名}} 御中 - 品目のセル:
{{品目}} - 金額のセル:
{{金額}} - 発行日のセル:
{{発行日}}
このように {{ }} で囲んだ文字列を目印にしておけば、GASが「この目印を見つけたら、データシートの値に置き換える」という処理をしてくれますよ。Word差し込み印刷の差し込みフィールドと同じ発想ですね。
NOTE: 印刷範囲を意識してレイアウトする
テンプレートシートはそのままPDFになります。A4縦1枚にきれいに収まるよう、列幅と行の高さを調整しておきましょう。余計な列や行が広がっていると、PDFに余白として写り込んでしまいます。
請求書PDFを自動生成するGASコード【コピペOK】
それでは本体のコードです。スプレッドシートのメニューから「拡張機能」→「Apps Script」を開き、エディタに次のコードを貼り付けてください。
function createInvoicePdf() {
// --- 設定(ここだけ自分の環境に合わせて変更)---
const DATA_SHEET_NAME = 'データ'; // データシートの名前
const TEMPLATE_SHEET_NAME = 'テンプレート'; // テンプレシートの名前
const SAVE_FOLDER_ID = 'ここにDriveフォルダのIDを貼る'; // PDF保存先フォルダ
const SEND_MAIL = false; // true にするとメール送信も実行する
const ss = SpreadsheetApp.getActiveSpreadsheet();
const dataSheet = ss.getSheetByName(DATA_SHEET_NAME);
const templateSheet = ss.getSheetByName(TEMPLATE_SHEET_NAME);
const saveFolder = DriveApp.getFolderById(SAVE_FOLDER_ID);
// データシートを2行目から最終行まで読み込む
const lastRow = dataSheet.getLastRow();
const data = dataSheet.getRange(2, 1, lastRow - 1, 4).getValues();
// 発行日(今日の日付)を「2026年6月8日」形式で作る
const today = new Date();
const issueDate = Utilities.formatDate(today, 'JST', 'yyyy年M月d日');
// 1行ずつ処理する
data.forEach(function(row) {
const clientName = row[0]; // 取引先名
const email = row[1]; // メールアドレス
const item = row[2]; // 品目
const amount = row[3]; // 金額
if (!clientName) return; // 取引先名が空の行はスキップ
// テンプレートに差し込む
fillTemplate(templateSheet, {
'{{取引先名}}': clientName,
'{{品目}}': item,
'{{金額}}': '¥' + Number(amount).toLocaleString(),
'{{発行日}}': issueDate
});
// 差し込み結果を確実に反映させる
SpreadsheetApp.flush();
// テンプレシートをPDFに変換する
const fileName = issueDate.replace(/[年月]/g, '-').replace('日', '')
+ '_' + clientName + '_請求書.pdf';
const pdfBlob = exportSheetAsPdf(ss, templateSheet, fileName);
// Driveに保存する
const pdfFile = saveFolder.createFile(pdfBlob);
// メール送信する(SEND_MAIL が true のときだけ)
if (SEND_MAIL && email) {
sendInvoiceMail(email, clientName, pdfFile.getBlob());
}
Logger.log(clientName + ' のPDFを作成しました: ' + pdfFile.getUrl());
});
}
このコードは「設定」の部分だけ自分の環境に合わせれば、あとはそのまま動きます。それぞれの値を順番に説明します。
DATA_SHEET_NAME: 請求データを入れたシートの名前TEMPLATE_SHEET_NAME: 請求書レイアウトのシートの名前SAVE_FOLDER_ID: PDFを保存したいDriveフォルダのID(次の項目で取得方法を説明します)SEND_MAIL: 最初はfalseのままにして、PDF生成だけテストしてからtrueに切り替えるのが安全です
差し込みとPDF変換の関数を追加する
上のコードから呼び出している補助関数を、同じファイルの下に続けて貼り付けます。
// テンプレートのプレースホルダーを実際の値に置き換える
function fillTemplate(sheet, replaceMap) {
const range = sheet.getDataRange();
const values = range.getValues();
for (let r = 0; r < values.length; r++) {
for (let c = 0; c < values[r].length; c++) {
let cell = values[r][c];
if (typeof cell === 'string') {
for (const key in replaceMap) {
if (cell.indexOf(key) !== -1) {
cell = cell.split(key).join(replaceMap[key]);
}
}
values[r][c] = cell;
}
}
}
range.setValues(values);
}
ここで注意したいのは、fillTemplate でテンプレートシートの値を直接書き換えている点です。1社目の差し込みで {{取引先名}} が実際の名前に変わってしまうと、2社目では目印が見つからなくなってしまいます。
これを防ぐ仕組みは、このあと「うまくいかないときの対処法」で解説していきましょう。
PDF変換のURLパラメータを使いこなす|余白・向き・範囲を制御する
GASでスプレッドシートをPDF化するときは、Googleのエクスポート用URLにアクセスして変換します。このURLにパラメータを付けることで、用紙の向きや余白、印刷範囲を細かく指定できるのがポイントです。
// 指定したシートだけをPDFに変換する
function exportSheetAsPdf(ss, sheet, fileName) {
const ssId = ss.getId();
const gid = sheet.getSheetId();
// エクスポート用URLとパラメータ
const url = 'https://docs.google.com/spreadsheets/d/' + ssId
+ '/export?'
+ 'format=pdf' // PDF形式で出力
+ '&gid=' + gid // 対象シートのID
+ '&portrait=true' // 縦向き(横向きは false)
+ '&size=A4' // 用紙サイズ
+ '&fitw=true' // 幅をページに合わせる
+ '&gridlines=false' // 枠線(グリッド線)を非表示
+ '&sheetnames=false' // シート名を非表示
+ '&printtitle=false' // ファイル名を非表示
+ '&top_margin=0.5' // 上余白(インチ)
+ '&bottom_margin=0.5'
+ '&left_margin=0.5'
+ '&right_margin=0.5';
// 認証トークンを付けてアクセスする
const token = ScriptApp.getOAuthToken();
const response = UrlFetchApp.fetch(url, {
headers: { 'Authorization': 'Bearer ' + token }
});
// PDFのデータ(Blob)にファイル名を付けて返す
return response.getBlob().setName(fileName);
}
主要なURLパラメータの意味を整理しておきます。請求書の見た目を調整したいときは、ここを書き換えてください。
format=pdf: 出力形式。PDFのほかにxlsxやcsvも指定できますgid: PDF化したいシートのID。sheet.getSheetId()で取得しますportrait:trueで縦向き、falseで横向きgridlines:falseにすると、セルの枠線(グリッド線)が消えてすっきりしますfitw:trueにすると、列がページ幅に収まるよう自動調整されますtop_marginなどの余白: インチ単位で指定します(0.5でだいたい1.3cm程度)
NOTE: gid はシートのURLで確認できる
シートのIDは、対象シートを開いたときのURL末尾#gid=0の数字部分です。コードではgetSheetId()を使って自動取得するので手入力は不要ですが、仕組みを知っておくと応用が利きます。
ファイル名を動的に付ける|日付と取引先名で自動命名する
請求書PDFは、あとから探しやすいファイル名にしておくことが大切です。本体コードでは、次のようにファイル名を組み立てていました。
const fileName = issueDate.replace(/[年月]/g, '-').replace('日', '')
+ '_' + clientName + '_請求書.pdf';
これで「2026-6-8_株式会社サンプル_請求書.pdf」のような名前が自動で付きます。日付が先頭にあると、フォルダ内で日付順に並ぶので管理がラクになりますよ。
請求書番号を入れたい場合は、データシートに「請求番号」の列を追加して、次のように組み込むと便利です。
// 例: INV-2026-001 のような請求番号を先頭に付ける
const invoiceNo = row[4]; // データシートの5列目に請求番号があると仮定
const fileName = invoiceNo + '_' + clientName + '_請求書.pdf';
ファイル名に使えない記号(/ や : など)が取引先名に含まれる場合は、replace() で除去しておくと安全です。
const safeName = clientName.replace(/[\/:*?"<>|]/g, '');
Drive保存とGmail送信を組み込む|送付まで全自動にする
ここまでで「PDFを作ってDriveに保存する」までができました。最後に、そのPDFをGmailで取引先に送る関数を追加します。
// 請求書PDFをメールに添付して送信する
function sendInvoiceMail(email, clientName, pdfBlob) {
const subject = clientName + ' 御中|請求書のご送付';
const body = clientName + ' 御中nn'
+ 'いつもお世話になっております。n'
+ '今月分の請求書をお送りいたします。n'
+ '添付のPDFをご確認のうえ、お手続きをお願いいたします。nn'
+ '何卒よろしくお願い申し上げます。';
GmailApp.sendEmail(email, subject, body, {
attachments: [pdfBlob],
name: '請求担当'
});
}
GmailApp.sendEmail() の attachments オプションにPDFのBlobを渡すだけで、添付ファイル付きメールが送れます。差出人の表示名は name で変えられるので、会社名や担当部署を入れておくと親切です。
メール本文を取引先ごとに個別化したり、HTMLメールで送りたい場合は、スプレッドシートからGmailを一括送信する方法で差し込みメールのテクニックを詳しく解説しているので、あわせて読んでみてください。
実行ボタンを付けて誰でも使えるようにする
毎月使うものなので、スプレッドシートにカスタムメニューを追加して、ワンクリックで実行できるようにしておきましょう。
// スプレッドシートを開いたときにメニューを追加する
function onOpen() {
SpreadsheetApp.getUi()
.createMenu('請求書ツール')
.addItem('請求書PDFを作成', 'createInvoicePdf')
.addToUi();
}
このコードを保存してスプレッドシートを開き直すと、メニューバーに「請求書ツール」という項目が増えます。あとはそこから「請求書PDFを作成」を選ぶだけで、全自動で処理が走ります。
うまくいかないときの対処法|よくあるトラブルと解決策
実装中につまずきやすいポイントと、その解決策をまとめました。エラーが出ても落ち着いて確認してください。
2社目から差し込みがされない・プレースホルダーが残る
一番ハマりやすいのがこれです。前述のとおり、fillTemplate がテンプレートシートを直接書き換えるため、1社目の処理で {{取引先名}} が消えてしまうのが原因です。
解決策は、毎回テンプレートシートを元の状態に戻すことです。テンプレートを別シートとして複製してから差し込み、PDF化後に削除する方法が確実です。本体コードの差し込み部分を次のように書き換えてください。
data.forEach(function(row) {
// ...(変数の取り出しは同じ)...
// テンプレートをコピーして作業用シートを作る
const workSheet = templateSheet.copyTo(ss).setName('_work');
fillTemplate(workSheet, {
'{{取引先名}}': clientName,
'{{品目}}': item,
'{{金額}}': '¥' + Number(amount).toLocaleString(),
'{{発行日}}': issueDate
});
SpreadsheetApp.flush();
// 作業用シートをPDF化
const pdfBlob = exportSheetAsPdf(ss, workSheet, fileName);
saveFolder.createFile(pdfBlob);
// 作業用シートを削除して元に戻す
ss.deleteSheet(workSheet);
});
これなら原本のテンプレートは触らないので、何社処理しても目印が消えません。
「承認が必要です」と表示されて実行できない
GASが初めてDriveやGmailにアクセスするときは、Googleの認可(承認)が必要です。実行時に出るダイアログで「権限を確認」→自分のアカウントを選択→「詳細」→「(プロジェクト名)に移動」→「許可」の順に進めば承認できます。これは初回だけの操作です。
PDFの一部が切れる・2ページに分かれてしまう
テンプレートシートの内容がA4の1枚に収まっていないのが原因です。次の点を確認してください。
- テンプレートシートの列幅を狭めて、横幅をA4内に収める
- 不要な列・行が広がっていないか確認する
- URLパラメータに
fitw=true(幅をページに合わせる)を入れる - それでも収まらなければ
portrait=falseで横向きにする
スクリプトの実行時間が長すぎてエラーになる
GASには1回あたり6分という実行時間の上限があります。取引先が数十社を超えて時間切れになる場合は、処理する行数を分割したり、Utilities.sleep() の不要な待機を入れていないか確認しましょう。
請求業務に複数のGAS自動化を組み合わせたい方は、GASで締め切りリマインダーを自動通知する方法も参考になります。
まとめ|請求書発行を丸ごと自動化して毎月の手間を消す
GASを使えば、スプレッドシートの請求データから、PDF生成・Drive保存・メール送付までを全自動にできます。一度コードを組んでしまえば、翌月からはデータシートを更新してボタンを押すだけです。半日かかっていた請求業務が、文字どおり数分で終わるようになります。
最後に、実装のポイントを振り返っておきましょう。
- データシートとテンプレートシートを分けて作る
- テンプレートには
{{取引先名}}などの目印を置く - PDF変換はエクスポート用URLにパラメータを付けて制御する
- ファイル名は日付と取引先名で動的に命名する
- 2社目以降の差し込みは「テンプレートを複製してから差し込む」で解決する
まずは SEND_MAIL を false にしたまま、PDF生成だけテストしてみてください。狙いどおりのPDFができることを確認してから、メール送信を有効にするのが安全な進め方です。
GASをもっと活用したい方は、予約フォームとGoogleカレンダーを連携する方法など、ほかの自動化レシピもぜひ試してみてください。
