これは制作見本です。架空の会社・架空の取引先で作っており、実在の業務・受託実績ではありません。

スプレッドシートから請求書PDFをまとめて作る(Google Apps Script)

架空の業務:小さなデザイン事務所が、毎月末に取引先ごとの請求書を手作業で作っている。
これを「メニューを1回押すだけ」にした見本です。

やっていた作業 → 自動化した後

これまで(手作業)
  1. 請求一覧から1社ずつ金額をコピー
  2. ひな形に貼り付けて日付を直す
  3. PDFで書き出して名前を付ける
  4. ドライブのフォルダに入れる
  5. 一覧に「作成済み」と書く ……を社数ぶん
自動化した後
  1. メニュー「請求書」→「未作成の請求書をまとめて作る」を押す
  2. 終わり(PDFの作成・保存・一覧への記入まで自動)

画面(スプレッドシート)

2026年請求管理(見本) ファイル編集表示挿入表示形式データ請求書
未作成の請求書をまとめて作る
選んだ行だけ作り直す
今月分をメールの下書きにする
ABCDEF
1取引先件名金額(税込)支払期限状態PDF
2株式会社ミナモ商会10月分 チラシ制作66,0002026/11/30作成済み請求書_ミナモ商会_202610.pdf
3喫茶ミモザ堂10月分 メニュー表22,0002026/11/30作成済み請求書_喫茶ミモザ堂_202610.pdf
4おそうじ便ひだまり10月分 LP更新33,0002026/11/30作成済み請求書_おそうじ便ひだまり_202610.pdf
請求一覧ひな形取引先マスタ
3件の請求書を作成しました

できあがるもの

請求書

2026/10/31
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はスクリプトの設定に置いているので、コードを触らずに保存先を変えられます。
使う人はボタンを押すだけメニューから動かせるので、コードを開く必要はありません。使い方の説明書も一緒にお渡しします。

このページは制作見本です。会社名・取引先・金額はすべて架空のものです。画面はスプレッドシートの見た目を再現した図で、実際の顧客データは使っていません。