スプレッドシートから請求書PDFをまとめて作る(Google Apps Script)
架空の業務:小さなデザイン事務所が、毎月末に取引先ごとの請求書を手作業で作っている。
これを「メニューを1回押すだけ」にした見本です。
やっていた作業 → 自動化した後
これまで(手作業)
- 請求一覧から1社ずつ金額をコピー
- ひな形に貼り付けて日付を直す
- PDFで書き出して名前を付ける
- ドライブのフォルダに入れる
- 一覧に「作成済み」と書く ……を社数ぶん
自動化した後
- メニュー「請求書」→「未作成の請求書をまとめて作る」を押す
- 終わり(PDFの作成・保存・一覧への記入まで自動)
画面(スプレッドシート)
| A | B | C | D | E | F | |
|---|---|---|---|---|---|---|
| 1 | 取引先 | 件名 | 金額(税込) | 支払期限 | 状態 | |
| 2 | 株式会社ミナモ商会 | 10月分 チラシ制作 | 66,000 | 2026/11/30 | 作成済み | 請求書_ミナモ商会_202610.pdf |
| 3 | 喫茶ミモザ堂 | 10月分 メニュー表 | 22,000 | 2026/11/30 | 作成済み | 請求書_喫茶ミモザ堂_202610.pdf |
| 4 | おそうじ便ひだまり | 10月分 LP更新 | 33,000 | 2026/11/30 | 作成済み | 請求書_おそうじ便ひだまり_202610.pdf |
請求一覧ひな形取引先マスタ
3件の請求書を作成しました
できあがるもの
請求書
2026/10/31
No. 2026-10-002
No. 2026-10-002
喫茶ミモザ堂 御中
下記のとおりご請求申し上げます。
ご請求金額 22,000円(税込)
| 内容 | 金額 |
|---|---|
| 10月分 メニュー表 | 20,000 |
| 消費税(10%) | 2,000 |
お支払期限:2026/11/30
(架空)デザイン事務所 見本
マイドライブ > 請求書 > 2026-10
請求書_ミナモ商会_202610.pdf
請求書_喫茶ミモザ堂_202610.pdf
請求書_おそうじ便ひだまり_202610.pdf
月ごとのフォルダが無ければ自動で作り、同じ名前のPDFがあれば上書きせず「_2」を付けて残します。
中身のコード(Google Apps Script)
// メニューを追加(スプレッドシートを開いたときに自動で動く) function onOpen() { SpreadsheetApp.getUi().createMenu('請求書') .addItem('未作成の請求書をまとめて作る', 'makeInvoices') .addToUi(); } function makeInvoices() { const ss = SpreadsheetApp.getActive(); const list = ss.getSheetByName('請求一覧'); const tpl = ss.getSheetByName('ひな形'); const ym = Utilities.formatDate(new Date(), 'Asia/Tokyo', 'yyyyMM'); const folder = monthFolder_(ym); const rows = list.getDataRange().getValues(); const head = rows.shift(); const col = name => head.indexOf(name); let made = 0; rows.forEach((r, i) => { if (r[col('状態')] !== '未作成') return; tpl.getRange('B3').setValue(r[col('取引先')] + ' 御中'); tpl.getRange('B8').setValue(r[col('件名')]); tpl.getRange('E8').setValue(r[col('金額(税込)')]); tpl.getRange('E12').setValue(r[col('支払期限')]); SpreadsheetApp.flush(); const name = `請求書_${r[col('取引先')]}_${ym}.pdf`; const file = folder.createFile(sheetToPdf_(ss, tpl, uniqueName_(folder, name))); list.getRange(i + 2, col('状態') + 1).setValue('作成済み'); list.getRange(i + 2, col('PDF') + 1) .setRichTextValue(SpreadsheetApp.newRichTextValue() .setText(file.getName()).setLinkUrl(file.getUrl()).build()); made++; }); SpreadsheetApp.getUi().alert(made + '件の請求書を作成しました'); } // ひな形シートだけをA4縦のPDFにする function sheetToPdf_(ss, sheet, name) { const url = `https://docs.google.com/spreadsheets/d/${ss.getId()}/export` + `?format=pdf&gid=${sheet.getSheetId()}&size=A4&portrait=true&fitw=true&gridlines=false`; const res = UrlFetchApp.fetch(url, { headers: { Authorization: 'Bearer ' + ScriptApp.getOAuthToken() } }); return res.getBlob().setName(name); } // 「請求書/2026-10」のような月フォルダ(無ければ作る) function monthFolder_(ym) { const root = DriveApp.getFolderById( PropertiesService.getScriptProperties().getProperty('INVOICE_FOLDER_ID')); const label = ym.slice(0, 4) + '-' + ym.slice(4); const it = root.getFoldersByName(label); return it.hasNext() ? it.next() : root.createFolder(label); } // 同じ名前があれば _2, _3 … を付ける(上書きしない) function uniqueName_(folder, name) { let n = name, k = 2; while (folder.getFilesByName(n).hasNext()) n = name.replace(/\.pdf$/, `_${k++}.pdf`); return n; }
作るときに気をつけたこと
列の順番が変わっても壊れない「状態」「金額(税込)」などの見出し名で列を探すので、列を足したり並べ替えたりしても動きます。
作り直しで上書きしない同じ名前のPDFがあれば番号を付けて残します。間違って消える心配がありません。
保存先は設定で変えられるフォルダのIDはスクリプトの設定に置いているので、コードを触らずに保存先を変えられます。
使う人はボタンを押すだけメニューから動かせるので、コードを開く必要はありません。使い方の説明書も一緒にお渡しします。
このページは制作見本です。会社名・取引先・金額はすべて架空のものです。画面はスプレッドシートの見た目を再現した図で、実際の顧客データは使っていません。