GASでGoogleドキュメントを差し込み生成|名簿から報告書・議事録を一括作成

スポンサーリンク

Googleドキュメントの議事録を、毎週コピーして日付だけ書き換えている。スプレッドシートの名簿を見ながら、報告書を1件ずつ作っている。そんな手作業に時間を取られていませんか。

手作業のコピーでは、日付の直し忘れや前回の内容の消し残しが起きがち。GAS(Google Apps Script)を使えば、テンプレートに名簿の値を差し込んで文書を一括生成できます。

この記事では、コピペで動く完全コードを3パターン紹介します。議事録の毎週自動コピー、名簿からの報告書一括作成、PDF保存の3つです。うまく動かないときの対処法もまとめたので、最後まで手順どおりに進めてみてください。

対応バージョン: Google Apps Script(V8ランタイム)。個人のGoogleアカウントでもGoogle Workspaceでも使えます。Googleドキュメント・スプレッドシートはブラウザ版で操作します。

GASでGoogleドキュメントを差し込み生成する仕組み|スプレッドシートから作る全体の流れ

GASの差し込み生成は、Googleドキュメントのテンプレートを複製して使う方法です。複製した文書の中の目印を、スプレッドシートの値に置き換えます。

Wordの差し込み印刷に近いイメージですね。違いは、1件ごとに独立したGoogleドキュメントができる点。あとから個別に追記したり、共有したりしやすい形です。

結論:makeCopy→replaceText→保存の3手順

やることは、次の3手順だけです。

  1. テンプレートを複製する(DriveApp の makeCopy)
  2. 複製を開いて目印を置換する(DocumentApp の replaceText)
  3. 保存して閉じる(saveAndClose)

テンプレートの本文には、{{案件名}} のような目印を入れておきます。この記事では、この目印を「変数」と呼びましょう。テンプレート自体は書き換えないので、何度でも使い回せます。

最小コードで見る基本構文

まずは3手順だけを並べた最小のコードです。テンプレートを1つ複製し、{{日付}} を固定の文字に置き換えます。

function sampleMinimal() {
  // 1. テンプレートを複製する(戻り値は File)
  const copy = DriveApp.getFileById('テンプレートのID').makeCopy('テスト_差し込み');

  // 2. 複製したドキュメントを開いて、目印を置き換える
  const doc = DocumentApp.openById(copy.getId());
  doc.getBody().replaceText('\{\{日付\}\}', '2026年10月7日');

  // 3. 保存して閉じる
  doc.saveAndClose();

  Logger.log(copy.getUrl());
}

ポイントは3つあります。

1つ目は、makeCopy の戻り値がドライブの「ファイル(File)」だという点。そのままでは本文を編集できません。DocumentApp.openById にIDを渡し、ドキュメントとして開き直してから置換します。

2つ目は、replaceText の検索側(第1引数)が正規表現として扱われる点。{ や } は正規表現の記号なので、最小コードでは \{ のように手で打ち消しています。後で紹介する共通関数では、この処理を自動化しました。

3つ目は saveAndClose について。公式リファレンスでは、実行終了時に開いている文書へ自動で呼ばれると説明されています。ただし同じ実行の中でPDF化するなら、直前に明示的に呼んで変更を確定させておきましょう。この記事のコードは、すべて明示的に呼ぶ形でそろえています。

makeCopy には引数の渡し方が4通りあり、どれも戻り値は File です。

書き方動き
makeCopy()名前・保存先を指定せずに複製
makeCopy(フォルダ)指定フォルダに複製
makeCopy('名前')名前を付けて複製
makeCopy('名前', フォルダ)名前を付けて指定フォルダに複製

実務では、名前と保存先の両方を指定する4つ目を使うのが基本。ファイルが散らばらず、あとから探しやすくなります。

対応環境と前提(V8ランタイム・権限承認)

この記事のコードは const や let を使っているため、V8ランタイムが前提です。新しく作ったプロジェクトは通常V8になっています。気になる場合は、エディタ左側の歯車アイコン[プロジェクトの設定]で確認しましょう。

スクリプトエディタの開き方や、権限承認の基本操作はこの記事では省略します。初めてGASを触る方は、GAS入門|コピペで動くスプレッドシート自動化レシピで一度動かしてみてください。

初回の実行時には、ドライブとドキュメントへのアクセス許可を求める画面が出ます。自分のアカウントを選んで許可すれば、実行の準備は完了です。

テンプレートと名簿の作り方|{{変数名}}の設計ルール

差し込みがうまくいくかどうかは、ほぼテンプレートと名簿の作り方で決まります。コードを書く前に、ここで土台を整えておきましょう。

テンプレ側を作る手順チェックリスト

次の6ステップで、テンプレートと保存先を準備します。

ステップ1:Googleドキュメントで新しい文書を作る

Googleドライブの[新規]から[Googleドキュメント]を選びます。ファイル名は「【テンプレ】案件報告書」など、テンプレートだとわかる名前にしておきましょう。

ステップ2:見出しや定型文を入れてレイアウトを整える

毎回変わらない部分を先に完成させます。表題・項目名・署名などを入れ、フォントや余白も整えておきましょう。

ステップ3:差し替える箇所に変数を入力する

毎回変わる部分を、半角の波括弧2つで囲んだ変数に置き換えます。報告書なら、たとえば次のような形です。

案件報告書

案件名:{{案件名}}
顧客名:{{顧客名}} 様
担当者:{{担当者}}
期限:{{期限}}

■ 進捗
{{進捗メモ}}

ステップ4:Googleドライブに保存先フォルダを作る

生成した文書をまとめるフォルダを1つ作りましょう。テンプレートと同じフォルダに保存すると、どれが原本かわかりにくくなりがち。保存先は分けておくのがおすすめです。

ステップ5:テンプレートのIDをURLからコピーする

テンプレートを開いた状態で、ブラウザのアドレスバーを見てください。/d/ と /edit に挟まれた長い英数字がIDです。

ステップ6:保存先フォルダのIDをURLからコピーする

フォルダを開き、アドレスバーの /folders/ より後ろの部分をコピーします。2つのIDの位置を、下にまとめました。

https://docs.google.com/document/d/【ここがテンプレートのID】/edit

Error 404 (見つかりませんでした)!!1
【ここがフォルダのID】

コピーしたIDは、あとでコードに貼り付けます。メモ帳などに控えておいてください。

{{変数名}}設計ルール表

変数の書き方には、守っておきたいルールがあります。置換されずに {{ }} が残るときは、まずこの表を見直してみてください。

ルール良い例NG例理由
半角の波括弧2つで囲む{{顧客名}}{{顧客名}}全角の括弧は別の文字なので一致しない
名簿の列見出しと完全一致させる見出し「顧客名」→{{顧客名}}{{お客様名}}・{{顧客 名}}1文字でも違うと置換されない
日付は文字列に整形して渡す2026年10月07日Wed Oct 07 2026 …日付のまま渡すと英語表記の長い文字列になる
同じ変数は何か所に置いてもよい表題と本文の両方に {{案件名}}―replaceText はすべての出現を置換する
1行に複数の変数を並べてもよい{{顧客名}} 様({{担当者}})―変数ごとに順番に置換される
改行を含む長文は1件目で確認する進捗メモをまとめて1つの変数に―置換後の見た目を実際の文書で確かめる

日付の整形は、後で紹介する共通関数が自動で行います。見出しの前後の空白も、コード側で取り除く作りです。

名簿シートの列設計

名簿は、1行目を見出し、2行目以降を1件ずつのデータにします。見出しの文字が、そのまま変数名になる仕組みです。

案件名顧客名担当者期限進捗メモ生成済みPDF
サイト改修山田商事佐藤2026/10/31デザイン確定済み(空欄)(空欄)
研修資料作成鈴木工業田中2026/11/15初稿レビュー中(空欄)(空欄)

「生成済み」列はパターン2、「PDF」列はパターン3で使います。どちらも最初は空欄のままにしておきましょう。生成が終わると、コードがファイルのURLを書き込みます。

シート名は「名簿」にしてください。別の名前にする場合は、コード内のシート名も合わせて書き換えます。

スクリプトエディタに共通関数を貼る

3つのパターンで共通して使う関数を、先に貼っておきます。続けて、次のステップで進めてください。

ステップ7:名簿のスプレッドシートを開く

パターン2・3は「開いているスプレッドシート」を読み込みます。必ず名簿のスプレッドシートからエディタを開いてください。

ステップ8:メニューの[拡張機能]>[Apps Script]を選ぶ

新しいタブでスクリプトエディタが開きます。

ステップ9:最初からあるコードを消して共通関数を貼る

function myFunction() {} を消して、次のコードをそのまま貼り付けましょう。

// ===== 共通関数(1つのプロジェクトに1回だけ貼る) =====

// 正規表現の記号を打ち消す(検索キー用)
function escapeRegex(text) {
  return String(text).replace(/[.*+?^${}()|[]\]/g, '\$&');
}

// セルの値を差し込み用の文字列にする
function formatValue(value) {
  if (value === null || value === undefined) return '';
  if (value instanceof Date) {
    return Utilities.formatDate(value, 'Asia/Tokyo', 'yyyy年MM月dd日');
  }
  return String(value);
}

// 本文・ヘッダー・フッターの {{キー}} を値に置き換える
function replacePlaceholders(doc, data) {
  const sections = [doc.getBody(), doc.getHeader(), doc.getFooter()];
  Object.keys(data).forEach(function (key) {
    const pattern = escapeRegex('{{' + key + '}}');
    const value = formatValue(data[key]);
    sections.forEach(function (section) {
      if (section) section.replaceText(pattern, value); // ない部分(null)は飛ばす
    });
  });
}

// テンプレートを複製し、差し込み・保存したファイル(File)を返す
function createDocFromTemplate(templateId, fileName, folderId, data) {
  const folder = DriveApp.getFolderById(folderId);
  const copy = DriveApp.getFileById(templateId).makeCopy(fileName, folder);
  const doc = DocumentApp.openById(copy.getId());
  replacePlaceholders(doc, data);
  doc.saveAndClose();
  return copy;
}

ステップ10:保存アイコンをクリックする

エディタ上部の保存アイコンで、プロジェクトを保存します。共通関数だけでは何も起きないので、まだ実行しなくてかまいません。

4つの関数の役割は次のとおりです。

  • escapeRegex:キーに含まれる { } ( . などの記号を打ち消し、文字どおりに検索させる
  • formatValue:日付を「2026年10月07日」形式に整え、値がない(null など)ときは空文字にする
  • replacePlaceholders:本文に加え、ヘッダー・フッターにも置換をかける
  • createDocFromTemplate:複製・置換・保存の3手順を1回の呼び出しにまとめる

ヘッダーとフッターは、getHeader()・getFooter() で本文とは別に取り出せます。ヘッダーがない文書では null が返るため、if (section) で飛ばす作りにしました。

パターン1 GASで議事録テンプレートを毎週自動コピーする

1つ目は、定例会議の議事録を毎週1ファイルずつ用意するパターンです。名簿は使わず、日付だけを差し込みます。

議事録テンプレートには、次のような変数を入れておきましょう。

{{会議名}} 議事録

日時:{{日付}}
次回予定:{{次回日付}}

■ 出席者

■ 議題

■ 決定事項

完全コード

共通関数とは別のファイルに貼ると管理しやすくなります。エディタ左の[ファイル]横の[+]から[スクリプト]を選び、新しいファイルを作ってください。

// ===== パターン1:議事録を毎週自動コピー =====
const MINUTES_TEMPLATE_ID = 'ここに議事録テンプレートのID';
const MINUTES_FOLDER_ID = 'ここに保存先フォルダのID';
const MINUTES_TITLE = '定例ミーティング';

function createWeeklyMinutes() {
  const today = new Date();
  const nextWeek = new Date(today.getTime() + 7 * 24 * 60 * 60 * 1000);
  const fileName = '議事録_' + Utilities.formatDate(today, 'Asia/Tokyo', 'yyyyMMdd');

  const data = {
    '会議名': MINUTES_TITLE,
    '日付': today,
    '次回日付': nextWeek
  };

  const copy = createDocFromTemplate(MINUTES_TEMPLATE_ID, fileName, MINUTES_FOLDER_ID, data);
  Logger.log('作成しました:' + copy.getUrl());
}

// 毎週月曜の8時ごろに自動実行するトリガーを作る(1回だけ実行)
function setWeeklyMinutesTrigger() {
  ScriptApp.newTrigger('createWeeklyMinutes')
    .timeBased()
    .onWeekDay(ScriptApp.WeekDay.MONDAY)
    .atHour(8)
    .create();
}

上の2つのIDを書き換えたら保存します。関数の選択欄で createWeeklyMinutes を選び、[実行]をクリックしましょう。保存先フォルダに「議事録_20261007」のようなファイルができれば成功です。

コードの各行の解説

冒頭の3行は設定値です。定数名には MINUTES_ を付けました。ほかのパターンと同じプロジェクトに貼っても、名前がぶつからないようにするためです。

同じ名前の const が2つあると、エラーで止まってしまうので注意しましょう。

nextWeek は、今日の日時に7日分のミリ秒を足して求めています。隔週の会議なら、7 を 14 に変えましょう。あわせて、後述のトリガーに .everyWeeks(2) を足して2週ごとの実行にします。

data の左側('会議名' など)が変数名で、右側が差し込む値です。テンプレートに {{会議名}} と書いた箇所が「定例ミーティング」に置き換わります。日付の書式は、共通関数の formatValue が自動で整えるので安心してください。

最後の Logger.log は、作ったファイルのURLを実行ログに出す処理。実行ログのURLをクリックすれば、そのまま文書を開けます。

毎週の自動実行はトリガーで設定する

毎週自動で作るには、時間主導型のトリガーを設定します。上のコードの setWeeklyMinutesTrigger を、1回だけ実行してみてください。毎週月曜の8時ごろに createWeeklyMinutes が動くようになります。

atHour(8) は「8時ちょうど」ではなく、前後に幅を持って実行されます。会議の直前に必要なら、少し早めの時刻を指定しておきましょう。

注意したいのは、setWeeklyMinutesTrigger を2回実行した場合です。トリガーも2つでき、議事録が重複して作られます。エディタ左の時計アイコン[トリガー]を開き、余分な1つを削除してください。

画面からトリガーを設定する手順は、GASのトリガーで集計・通知を自動化する方法で解説しています。動かないときの確認方法も載っているので、参考にしてみてください。

パターン2 スプレッドシートの名簿から報告書を一括生成する

2つ目は、名簿の1行から1つの報告書を作るパターンです。顧客別・案件別の文書を、まとめて量産したいときに便利。

見出し行の文字がそのまま変数名になるので、列を増やせば変数も増やせます。コードを書き換える必要はありません。

完全コード(生成済み列にURLを書き戻し)

パターン1と同じ手順で新しいファイルを作り、次のコードを貼り付けてください。

// ===== パターン2:名簿から報告書を一括生成 =====
const REPORT_TEMPLATE_ID = 'ここに報告書テンプレートのID';
const REPORT_FOLDER_ID = 'ここに保存先フォルダのID';
const REPORT_SHEET_NAME = '名簿';
const REPORT_DONE_HEADER = '生成済み';
const REPORT_MAX_PER_RUN = 30;               // 1回の実行で作る上限件数
const REPORT_TIME_LIMIT_MS = 5 * 60 * 1000;  // 開始から5分を超えたら打ち切る

function createReportsFromList() {
  const startTime = Date.now();
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(REPORT_SHEET_NAME);
  if (!sheet) throw new Error('「' + REPORT_SHEET_NAME + '」シートが見つかりません');

  const values = sheet.getDataRange().getValues();
  const headers = values[0].map(function (h) { return String(h).trim(); });
  const doneCol = headers.indexOf(REPORT_DONE_HEADER);
  if (doneCol === -1) throw new Error('「' + REPORT_DONE_HEADER + '」列が見つかりません');

  let count = 0;
  for (let i = 1; i < values.length; i++) {
    const row = values[i];
    if (row[doneCol] !== '') continue;                          // 生成済みの行は飛ばす
    if (row.every(function (v) { return v === ''; })) continue; // 空行は飛ばす
    if (count >= REPORT_MAX_PER_RUN) break;                     // 上限件数に達したら終了
    if (Date.now() - startTime > REPORT_TIME_LIMIT_MS) break;   // 5分を超えたら終了

    // 見出しをキーにして、この行のデータをまとめる
    const data = {};
    headers.forEach(function (h, j) {
      if (h !== '' && j !== doneCol) data[h] = row[j];
    });

    const fileName = '報告書_' + (formatValue(data['案件名']) || (i + 1) + '行目');
    const copy = createDocFromTemplate(REPORT_TEMPLATE_ID, fileName, REPORT_FOLDER_ID, data);

    // 生成済み列にURLを書き戻す
    sheet.getRange(i + 1, doneCol + 1).setValue(copy.getUrl());
    SpreadsheetApp.flush();
    count++;
  }
  Logger.log(count + '件作成しました');
}

IDを書き換えて保存したら、createReportsFromList を選んで実行します。名簿の「生成済み」列にURLが入り、保存先フォルダに「報告書_サイト改修」などのファイルが並べば完了です。

二重生成を防ぐ仕組み

処理の対象は、「生成済み」列が空の行だけ。作り終えた行にはURLが書き込まれるため、もう一度実行しても同じ報告書は作られません。

名簿に新しい行を追加して再実行すれば、追加分だけが生成されます。毎回「どこまで作ったか」を覚えておく必要がない点が、この作りのメリット。

作り直したい行があるときは、その行の「生成済み」のURLを消してから実行しましょう。古いファイルは自動では消えないので、ドライブから手動で削除してください。

SpreadsheetApp.flush() は、URLの書き込みを1件ごとにシートへ反映させる命令です。途中で止まっても、作り終えた行の記録が残りやすくなります。

表・画像を含む置換の限界

replaceText で差し込めるのは文字列だけです。画像を差し込んだり、データの件数に合わせて表の行を増やしたりはできません。

そうした処理には、表や画像を直接操作する別のコードが必要になります。ロゴや印影のように毎回同じ画像なら、テンプレート側に最初から貼っておくのが手軽な方法です。

テンプレートの表のセル内に変数を置く場合は、まず1件だけ生成してみましょう。意図どおり置換されるかを、実際の文書で確かめてから量産するのがおすすめです。

パターン3 生成したGoogleドキュメントをPDFで保存する

3つ目は、差し込んだ文書をPDFにして保存するパターンです。社外に渡す報告書や、改変されたくない通知文書に向いています。

流れは「パターン2の手順で文書を作る → PDFに変換する → フォルダに保存する」の3段階。名簿は前述の「名簿」シートをそのまま使い、「PDF」列にPDFのURLを書き戻します。

完全コード(getAs でPDF化)

新しいファイルを作り、次のコードを貼り付けてください。共通関数が同じプロジェクトにあることが前提です。

// ===== パターン3:名簿から作ってPDFで保存 =====
const PDF_TEMPLATE_ID = 'ここに報告書テンプレートのID';
const PDF_FOLDER_ID = 'ここに保存先フォルダのID';
const PDF_SHEET_NAME = '名簿';
const PDF_DONE_HEADER = 'PDF';
const PDF_MAX_PER_RUN = 20;               // 1回の実行で作る上限件数
const PDF_TIME_LIMIT_MS = 5 * 60 * 1000;  // 開始から5分を超えたら打ち切る
const KEEP_DOC = true;                    // false にすると元のドキュメントをゴミ箱へ移す

function createPdfReportsFromList() {
  const startTime = Date.now();
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(PDF_SHEET_NAME);
  if (!sheet) throw new Error('「' + PDF_SHEET_NAME + '」シートが見つかりません');

  const values = sheet.getDataRange().getValues();
  const headers = values[0].map(function (h) { return String(h).trim(); });
  const doneCol = headers.indexOf(PDF_DONE_HEADER);
  if (doneCol === -1) throw new Error('「' + PDF_DONE_HEADER + '」列が見つかりません');

  let count = 0;
  for (let i = 1; i < values.length; i++) {
    const row = values[i];
    if (row[doneCol] !== '') continue;
    if (row.every(function (v) { return v === ''; })) continue;
    if (count >= PDF_MAX_PER_RUN) break;
    if (Date.now() - startTime > PDF_TIME_LIMIT_MS) break;

    const data = {};
    headers.forEach(function (h, j) {
      if (h !== '' && j !== doneCol) data[h] = row[j];
    });

    const fileName = '報告書_' + (formatValue(data['案件名']) || (i + 1) + '行目');
    const copy = createDocFromTemplate(PDF_TEMPLATE_ID, fileName, PDF_FOLDER_ID, data);
    const pdfFile = saveAsPdf(copy, PDF_FOLDER_ID);
    if (!KEEP_DOC) copy.setTrashed(true);

    sheet.getRange(i + 1, doneCol + 1).setValue(pdfFile.getUrl());
    SpreadsheetApp.flush();
    count++;
  }
  Logger.log(count + '件のPDFを作成しました');
}

// 保存済みのドキュメントを開き直してPDFに変換し、フォルダに保存する
function saveAsPdf(docFile, folderId) {
  const pdfBlob = DocumentApp.openById(docFile.getId()).getAs('application/pdf');
  return DriveApp.getFolderById(folderId)
    .createFile(pdfBlob)
    .setName(docFile.getName() + '.pdf');
}

createPdfReportsFromList を実行すると、保存先フォルダにドキュメントとPDFが1組ずつできます。名簿の「PDF」列には、PDFのURLが書き込まれる仕組み。

処理の順序には意味があります。共通関数の中で、saveAndClose まで済ませているのがポイント。

そのあと openById で開き直し、getAs でPDFにする流れです。閉じた文書はそのままでは扱えないため、PDF化の前に開き直しています。

getAs('application/pdf') は、ドキュメントの内容をPDFのデータ(Blob)として返します。それを createFile でフォルダにファイルとして保存し、setName で名前を付けています。

元Docsを残す/ゴミ箱に移す選択肢

PDFを作ったあと、元のドキュメントをどうするかは KEEP_DOC で切り替えましょう。

設定動き向いている場面
KEEP_DOC = trueドキュメントとPDFの両方を残すPDFを作ったあとに修正が入りそうなとき
KEEP_DOC = falseドキュメントをゴミ箱へ移し、PDFだけ残す配布用のPDFだけあればよいとき

setTrashed(true) はゴミ箱へ移すだけで、すぐには完全削除しません。間違えても、ドライブのゴミ箱から元に戻せます。

最初は true のまま動かし、PDFの見た目を確認してから false に切り替えると安心ですね。

請求書PDF・メール送付は別記事へ

この記事のPDF化は、ドキュメントをそのままPDFにする最小の形です。余白・用紙の向き・範囲を細かく指定したいなら、スプレッドシートをPDF化する方法も検討しましょう。

請求書のように表計算が中心の帳票なら、請求書PDFを自動生成してメール送付する方法が参考になります。作ったPDFを相手ごとにメールで送りたい場合は、スプレッドシートからGmailを一括送信する方法と組み合わせてみてください。

GASのドキュメント差し込みがうまくいかないときの対処法とよくある質問

最後に、つまずきやすいエラーと、よく聞かれる質問をまとめます。エラーが出たら、まずエディタ下部の実行ログでメッセージを確認してみてください。

権限承認・追加承認のエラー

初回の実行で承認画面が出たら、自分のアカウントを選んで許可します。許可しないまま閉じると、権限がない旨のエラーで止まってしまうので気をつけましょう。

すでに承認済みのプロジェクトに新しいパターンを追加した場合も、もう一度承認を求められることがあります。たとえば、スプレッドシート操作だけのスクリプトに、ドキュメントを扱うコードを足したとき。新しく使うサービスへの許可が必要になるためで、異常ではありません。

承認の詳しい流れは、前述のGAS入門記事で確認できます。

Cannot find item with ID(テンプレID取り違え)

IDが間違っていると、ファイルやフォルダが見つからない旨のエラーで止まります。英語表示では「Cannot find item with ID」などの文言になることがあります。

よくある原因は、次の3つ。

  • テンプレートのIDとフォルダのIDを逆に貼っている
  • URLの /edit や ? 以降まで一緒にコピーしている
  • IDの前後に空白や改行が入っている

もう1つ見落としがちなのが、ファイルへのアクセス権。他人のテンプレートを使う場合、スクリプトを実行するアカウントに閲覧以上の権限が必要です。

特殊文字(正規表現)・空セルの扱い

replaceText の検索側は正規表現として扱われます。キーに ( . $ などの記号が入ると、文字どおりに検索されず、置換に失敗する原因になります。

この記事の共通関数では、escapeRegex がキーの記号を自動で打ち消す作り。列見出しに「金額(税込)」のような記号を使っても、そのまま変数名にできます。

置換する値の側に記号を含む場合も、念のため確認しておきましょう。値に $ や を含む行で結果がおかしいときは、その1件だけ生成して文書を見比べてみてください。

空のセルは、空文字として置換処理に渡ります。formatValue では、値が null や undefined の場合も空文字に変換する作り。想定外の値がそのまま置換処理に渡らないようにするための備えです。

実行時間6分制限とバッチ分割

GASには、1回の実行時間に上限があります。主な上限を、2026年10月時点の公式の値でまとめました。

項目個人のGoogleアカウントGoogle Workspace
1回の実行時間6分6分
トリガーの合計実行時間(1日)90分6時間
ドキュメントの作成数(1日)2501,500
ファイルの変換数(1日)2,0004,000
トリガーの数1ユーザー・1スクリプトあたり201ユーザー・1スクリプトあたり20

これらの上限は、予告なく変更される場合があります。日次の上限は、最初のリクエストから24時間でリセットされる仕組みです。

名簿が数百件あると、1回の実行では6分に収まらないかもしれません。パターン2・3のコードは、上限件数に達するか、開始から5分を超えた時点で処理を打ち切る作りです。

残りは「生成済み」列(パターン3は「PDF」列)が空のままなので、もう一度実行すれば続きから作れます。手動での再実行が面倒なら、時間主導型トリガーで定期的に動かすのも1つの方法でしょう。

1日に大量の文書を作る場合は、表の日次上限にも気を配りましょう。個人アカウントで数百件を作るなら、何日かに分けて実行すると安心です。

よくある質問(FAQ)

Q. 置換されずに {{案件名}} がそのまま残るのはなぜ?

A. 変数名と列見出しが1文字でも違うと置換されません。全角の括弧、前後のスペース、表記ゆれ(「案件名」と「案件 名」など)を確認してください。ヘッダー・フッターの変数は、共通関数の replacePlaceholders で置換対象にしています。

Q. 金額が「1000」と表示され、桁区切りのカンマが付きません。

A. getValues() はセルの見た目ではなく、中身の値を返すためです。シートの表示どおりに差し込むなら、getDisplayValues() を使う方法があります。パターン2・3の getValues() を書き換えて、1件試してみてください。

Q. 日付の書式を「2026/10/07」にするには?

A. 共通関数 formatValue の 'yyyy年MM月dd日' を 'yyyy/MM/dd' に書き換えます。曜日も入れたい場合は、書式を変えて1件試してから使いましょう。

Q. Googleフォームの回答から報告書を作れますか。

A. フォームの回答シートを名簿として読み込めば作れます。フォーム回答を起点にした自動化は、Googleフォーム×GASで回答を自動通知・集計する方法も参考にしてみてください。

Q. 生成したら担当者に通知できますか。

A. 生成後に、Chatやメールへ通知する処理を足せば実現できます。通知の作り方は、GASで期限リマインド・未対応タスクを自動アラートする方法を参考にしてみてください。

まとめ

GASでのGoogleドキュメントの差し込み生成は、「複製 → 置換 → 保存」の3手順が基本。使うのは makeCopy・replaceText・saveAndClose の3つです。この記事の要点を振り返っておきましょう。

  • テンプレートには半角の {{変数名}} を入れ、名簿の列見出しと完全に一致させる
  • 共通関数で、記号のエスケープ・日付の整形・ヘッダーとフッターの置換をまとめて処理する
  • パターン1は日付だけを差し込み、トリガーで毎週の議事録を自動で用意する
  • パターン2は「生成済み」列にURLを書き戻し、二重生成を防ぐ
  • パターン3は保存後に開き直して getAs でPDF化し、元の文書を残すか選べる
  • 大量に作るときは、上限件数と5分の打ち切りで分割して実行する

一方で、件数が非常に多い場合や、画像・可変の表を含む複雑なレイアウトでは、GASだけでは手間が増えます。どこまでGASで作るかは、GASが向く業務・向かない業務の見分け方で判断してみてください。

まずはテンプレートを1つ作り、名簿の1行から報告書を生成してみましょう。1件動けば、あとは行を足すだけで何十件でも同じ形で作れます。

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