【コピペで動く】GASでスプレッドシートから請求書PDFを自動生成してメール送付する方法

スポンサーリンク

毎月の請求書発行、いまだに次のような手作業を繰り返していませんか。

  • 1社ずつテンプレートをコピーする
  • 取引先名と金額を打ち替える
  • PDFに書き出す
  • メールに添付する

取引先が10社・20社と増えるほど、この単純作業に半日とられてしまいますよね。転記ミスや添付忘れも怖いところです。

実はこの一連の流れ、Google Apps Script(GAS)を使えばボタン1つで丸ごと自動化できます。スプレッドシートの請求データをテンプレートに差し込み、PDF化して、Driveに保存して、Gmailで送付するまでを全部やってくれます。

この記事では、コピペでそのまま動く完成コードと、つまずきやすいポイントの対処法まで、実務で使える形でまとめました。

GASで請求書PDFを自動生成する仕組みとは?|全体の流れを把握する

最初に、これから作るものの全体像をつかんでおきましょう。やっていることはシンプルで、次の5ステップを GAS が順番に実行するだけです。

  1. 請求データ(取引先・品目・金額)が並んだ「データシート」を1行ずつ読み込む
  2. レイアウトを整えた「テンプレートシート」にその値を差し込む
  3. テンプレートシートをPDFに変換する
  4. PDFをGoogle Driveの指定フォルダに保存する
  5. 取引先のメールアドレス宛に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.comWebサイト保守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のほかに xlsxcsv も指定できます
  • 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_MAILfalse にしたまま、PDF生成だけテストしてみてください。狙いどおりのPDFができることを確認してから、メール送信を有効にするのが安全な進め方です。

GASをもっと活用したい方は、予約フォームとGoogleカレンダーを連携する方法など、ほかの自動化レシピもぜひ試してみてください。

タイトルとURLをコピーしました