スプレッドシート自動化セミナー / 実践

案件が終わって1か月。
あの一通を、機械に用意させる。

今日つくるもの = 案件フォローアップの下書き係。
やった方がいいと分かっているのに、面倒でやらないものを、機械に用意させます。送信はしません。

今日、必要なものは2つだけです。 ① Googleアカウント(Gmailが使えていれば大丈夫です)と ② AIのアカウント(Claude・ChatGPT・Geminiのどれでも。無料で作れます)。
全7ステップ/約30分 使うのはブラウザだけ メールは1通も送りません

下へスクロール

⚠️ 白い画面のうち、STEP 1 の「ドキュメントのコピー」だけが実際の画面写真です。 残りは押す場所が分かるように描いた図で、文言と並びは実物に合わせていますが、 Google 側の更新で細部が変わることがあります。押す場所は同じです。

STEP1

台帳を、自分のものにする

めやす 5分

やることはコピー1回だけです。ただし今日いちばん詰まるのがここなので、1画面ずつ止まって進みます。

1下のボタンを押す

URLを打つ必要はありません。押すのは、これ1つだけです。

練習用の台帳を開く新しいタブで開きます

  1. 1この資料のタブは閉じないでください。台帳は別のタブで開きます。手順はこちらに残るので、行き来しながら進められます。
「アクセス権が必要です」と出た方は、手を挙げてください。こちら側の設定の問題で、みなさんの操作は間違っていません。その場で直します。

2ログインの画面が出たら

この画面が出るのは、まだGoogleにログインしていない方だけです。台帳がそのまま出た方は、次へ進んでください。

ログインしていないと、台帳の前にこの画面が出ます
accounts.google.com/signin
Google
ログイン

Google スプレッドシートに移動

メールアドレスまたは電話番号
アカウントを作成次へ
  1. 1ふだんお使いのGoogleアカウント(Gmail)でログインします。会社用と個人用の両方をお持ちの方は、今日ずっと使うほうを選んでください。あとで変えると、承認の画面で行き止まりになります。

3コピーする前に、右上の丸を見る

今日いちばん多い詰まりが、これです。アカウントを2つ以上お持ちの方は、いま開いているのがどちらかを、コピーする前に確かめてください。
見るのは、画面のいちばん右上だけ
docs.google.com/spreadsheets/…/copyあ
ここを押すと、いま使っているアカウントのメールアドレスが出ます。
  1. 1この丸を押して、出てきたメールアドレスを覚えておいてください。STEP 6 の承認の画面で、同じものを選びます。

4「コピーを作成」を押す

出るのはこの画面だけです。名前を決めたり、保存先を選んだりする必要はありません。押せば、あなたのマイドライブにコピーができます。

これは実際の画面です ── 押すのは、黄色で囲んだボタン1つだけ
docs.google.com/spreadsheets/…/copyあ
「ドキュメントのコピー」と大きく出て、その下に「『営業フォローアップ台帳(セミナー用)』のコピーを作成しますか?」と書かれ、さらに下に「コピーを作成」という青いボタンが1つだけある画面

押すと、そのままあなたのコピーが開きます。(別の画面は出ません)

5開いたのが「コピー」か確かめる

確かめるのは、題名の最後だけです。

題名の最後に「のコピー」がついていれば、あなたのものです
営業フォローアップ台帳(セミナー用)のコピー
ファイル編集表示挿入表示形式データツール拡張機能ヘルプ
ここから下が、コピーされた台帳です。
  1. 1「のコピー」がついていれば合格です。ついていない方は、元のファイルを開いています。手を挙げてください。

元のファイルは変わりません。コピーの中は、いくら書き換えても大丈夫です。

6台帳の中身を見る

シートは2枚。いま見えているのが「台帳」で、列は5つだけです。

営業フォローアップ台帳 ── 架空の会社が5件。下にシートのタブが2つあります
営業フォローアップ台帳(セミナー用)のコピー
ファイル編集表示挿入表示形式データツール拡張機能ヘルプ
ABCDE
1会社名担当者名メールアドレス案件完了日下書き作成日
2株式会社あさひ工務店田中 太郎jibun@example.com2026/07/10
3有限会社みなみ設備佐藤 花子jibun@example.com2026/07/28
4株式会社北山電機鈴木 一郎jibun@example.com2026/08/05
5大和建材株式会社高橋 健jibun@example.com2026/09/01
6株式会社さくら商会伊藤 美咲jibun@example.com2026/09/04
台帳設定
  1. 1D列「案件完了日」── 案件が終わった日です。この日から1か月たった相手にだけ、フォローの下書きを作ります。
  2. 2E列「下書き作成日」── いまは全部、空です。下書きを作った行には、ここに日付が自動で入ります。次に実行したとき、同じ相手に二重に作らないための印です。
  3. 3下のタブを見てください。シートはもう1枚、「設定」があります。できあがるメールの件名と本文が入っているところです。STEP 3 で開きます。

⚠️ C列は架空のアドレスのままで構いません(送信しないので事故は起きません)。ご自身のアドレスに変えると、下書きの見た目まで確かめられます。余裕のある方だけどうぞ。

ここまで来ていればOK

題名に「のコピー」がついた台帳が、自分のドライブで開いている。それだけです。タブは閉じないでください。

STEP2

先に、動く環境かどうかを確かめる

めやす 2分

20分かけて作った後に「動きません」は避けたいので、先に30秒で確かめます。

会社のアカウントでは、管理者がこの機能を止めていることがあります。いま分かれば、すぐ切り替えられます。

1「拡張機能」から「Apps Script」を押してみる

台帳のいちばん上のメニュー ──「拡張機能」という文字を探します
営業フォローアップ台帳(セミナー用)のコピー
ファイル編集表示挿入表示形式データツール拡張機能ヘルプ
Apps Script アドオン マクロ
  1. 1「拡張機能」を押します。
  2. 2「Apps Script」を押します。新しいタブが開けば合格です。開いたタブは、そのまま開いたままにしておいてください。あとで使います。
新しいタブが開いた方 → 大丈夫です。そのタブは閉じずに、台帳のタブに戻ってお待ちください。
開かない/エラーになった方 → 手を挙げてください。管理者が Apps Script をオフにしているとこうなります。
対処は1つ。個人のGmailアカウントで、配られたリンクをもう一度開いてください。あなたの操作のせいではありません。
ここまで来ていればOK

タブが2つ開いている(台帳と Apps Script)。ここを越えられれば、あとは詰まりません。

STEP3

2枚のシートを、写真に撮る

めやす 3分

AIにシートの形を伝えます。説明を書くより、画面を見せるほうが速い。

1画面の一部を切り取って撮る

撮り方(お使いのパソコンに合わせて)
Windows の方  Windows キー + Shift + S
Mac の方    ⌘(コマンド)+ Shift + 4

押すと画面が少し暗くなります。そのまま、撮りたいところを
マウスでドラッグして囲んでください。
囲むのは、表の上から「いちばん下のシートのタブ」までです。タブに出ているシートの名前(台帳/設定)が写っていないと、AIはどちらのシートの話なのか分かりません。

21枚目 ──「台帳」のタブで撮る

囲むのは、この赤い枠のあたり ── 見出しの行と、下の数行と、シートのタブ
ABCDE
1会社名担当者名メールアドレス案件完了日下書き作成日
2株式会社あさひ工務店田中 太郎jibun@example.com2026/07/10
3有限会社みなみ設備佐藤 花子jibun@example.com2026/07/28
4株式会社北山電機鈴木 一郎jibun@example.com2026/08/05
台帳設定
  1. 1A〜E の列名と、1行目の見出し、そして下のタブが入っていることが大事です。データは2〜3行で十分。全部写らなくても構いません。

32枚目 ──「設定」のタブに切り替えて撮る

下のタブで「設定」を押します。ここに、できあがるメールの件名と本文が入っています。

「設定」シート ── 件名・本文・経過日数の3つだけです
AB
1項目内容
2件名【その後のご状況伺い】{{会社名}} {{担当者名}}様
3本文{{会社名}} / {{担当者名}} 様 / いつもお世話になっております。……
4経過日数30
5
6差し込みタグ{{会社名}} / {{担当者名}} / {{案件完了日}} が台帳の各行の値に置き換わります
台帳設定
  1. 1二重かっこで囲まれたものは「差し込みタグ」です。台帳の各行の会社名・担当者名・案件完了日に置き換わります。いまは触りません。

文面を変えたくなったら、このシートを直すだけです。コードのほうは触らなくて済みます。

今日は架空の会社しか入っていない練習用の台帳です。自社の本物でやるときの注意は③でお伝えします(見出しだけ残してデータをダミーに書き換えてから撮るだけです)。
ここまで来ていればOK

写真が2枚(台帳と設定)撮れている。どちらも下のタブが写っている。保存しなくても、次の画面にそのまま貼れます。

STEP4

AIに頼む

めやす 5分

頼む文はたった3行。これで100行のプログラムが返ってきます。

1この3行をコピーする

AIに貼る文(これで全部です)
GASで、メールの下書きを作るコードを書いてほしいです。
対象は「台帳」シートのうち、下書き作成日が空で、案件完了日から「設定」シートの経過日数がたっている行です。
件名と本文も「設定」シートのとおりに作ってください。画像は、その2つのシートです。

これだけです。専門用語も細かい指定もありません。列の名前も、文面も、日数も、全部2枚の写真から読み取ってもらいます。

2AIを開いて、文と写真を貼って、送る

Claude・ChatGPT・Gemini、どれでも構いません。

入力欄に「文」を貼り、続けて「写真を2枚」貼ります
claude.ai / chatgpt.com / gemini.google.com
GASで、メールの下書きを作るコードを書いてほしいです。
対象は「台帳」シートのうち、下書き作成日が空で、案件完了日から「設定」シートの経過日数がたっている行です。
件名と本文も「設定」シートのとおりに作ってください。画像は、その2つのシートです。
「台帳」のスクリーンショット
「設定」のスクリーンショット
  1. 1入力欄をクリックして、Ctrl + V(Macは ⌘ + V)で文を貼ります。
  2. 2続けて、写真を2枚とも貼ります。1枚貼って、もう1枚撮り直してから、また貼る ── の順でも構いません。小さな画像の札が2つ出れば成功です。
  3. 3送信ボタン(上向きの矢印)を押します。30秒〜1分でコードが返ってきます。

⚠️ 写真が貼れないときは、入力欄の左にあるクリップまたは「+」のボタンから、画像を選んでも同じです。

3返ってきたコードを、まるごとコピーする

コードの枠の右上に、コピーのボタンがあります
claude.ai

承知しました。画像から列の並びを読み取りました。以下をそのまま貼り付けてください。

javascript コピー
/** シートを開いたときに、メニューを足す */ function onOpen() { SpreadsheetApp.getUi() .createMenu('フォローアップ') …(このあと100行ほど続きます)
  1. 1このコピーのボタンを押します。マウスで文字をなぞって選ばないでください。途中が抜けて、動かなくなります。
返ってきたコードは、1文字も読まなくて大丈夫です。今日やるのは運ぶことと、結果を見て判断することの2つだけ。
質問が返ってきたときは 「おまかせします。まず動くものを出してください」とだけ送れば進みます。止まらなくて大丈夫です。
ここまで来ていればOK

コードが返ってきて、コピーのボタンを押した。うまく出なかった方は、下の「うまくいかないときは」の答えから合流できます。

STEP5

コードを貼って、保存する

めやす 5分

STEP 2 で開いた、もうひとつのタブに戻ります。

1この画面が何なのか、ざっと見る

覚える必要はありません。今日使うのは3か所だけです。

Apps Script の画面 ── 見るところは3つだけ
無題のプロジェクト
保存 実行 デバッグ myFunction ▾
コード.gs
1function myFunction() { 2 3} 4
① ファイルの名前「コード.gs」と出ているところ。今日はこの1つだけです。触りません。
② コードを書く場所いちばん広いところ。今日、貼り付けるのはここだけです。
③ 上に並ぶボタン「保存」「実行」と、その右の「関数の名前」。使うのはこの3つだけです。

⚠️ 実物には、このほかにもボタンがあります(左端の細い列など)。今日は一度も触りません。上の3つを、書いてある文字で探してください。

2最初から入っている4行を、全部消す

すでに短いコードが入っています。残したまま貼ると動きません。

消し方
1. コードの文字の上を、どこでもいいのでクリック
2. Ctrl + A (Macは ⌘ + A) … 全部が青く選ばれます
3. Delete キー(Macは delete) … まっさらになります

⚠️ 消して大丈夫です。この4行は何もしないひな型で、台帳には影響しません。

3貼り付けて、保存する

貼り付けたら、必ず「保存」を押します(関数名は一例です。人によって違います)
保存 実行 デバッグ createFollowUpDrafts ▾
コード.gs
1/** 2 * 案件フォローアップの下書き係 3 */ 4 5const SHEET_DATA = '台帳'; 6const SHEET_CONF = '設定'; 7…(このあと100行ほど続きます)
  1. 1まっさらになったところに Ctrl + V(Macは ⌘ + V)で貼り付けます。画面いっぱいに文字が出れば成功です。
  2. 2「保存」を押します。ここを忘れる方が、いちばん多いです。
  3. 3保存できたかは、「実行」の右にある「関数の名前」で分かります。「myFunction」だった表示が、AIの書いた別の名前に変われば、保存できています。⚠️ 名前はAIが決めるので、人によって違います。隣の人と違っていても正常です。この場所を覚えておいてください。すぐ次で使います。
コードの中身は読まなくて構いません。メールの件名と本文は、コードではなく台帳の「設定」シートにあります。あとから書き換えられます。いまは触らず、先に動かしましょう。
ここまで来ていればOK

エディタに100行ほどのコードが入っている。そして「関数の名前」が「myFunction」から変わっている。この2つです。

STEP6

動かす

めやす 8分

はじめて動かすときだけ、Googleが確認の画面を5つ出してきます。順番に越えます。

1動かす関数を選ぶ

台帳のメニューからではなく、エディタから動かします。そのほうが、どんなコードでも確実だからです。

使うのは、STEP 5 で「myFunction」から名前が変わったところです。「実行」「デバッグ」の右に並んでいます。

関数の名前を押すと、一覧が「その真下」に開きます
保存 実行 デバッグ createFollowUpDrafts ▾ onOpen createFollowUpDrafts
コード.gs
1/** 案件フォローアップの下書き係 */ 2 3 4 5
  1. 1関数の名前が出ているところを押します。一覧が、その真下に開きます。
  2. 2「下書きをつくる」という意味の名前を選びます。名前はAIが決めるので、人によって違います(createFollowUpDrafts、makeDrafts など)。

⚠️ 「onOpen」は選ばないでください。これはメニューを足すだけの関数で、下書きは作りません。どれを選べばいいか迷ったら、手を挙げてください。

⚠️ ボタンの並びは、Googleの更新で変わることがあります。位置ではなく「保存」「実行」という文字で探してください。関数の名前はそのすぐ右です。

台帳の上のメニューから動かす方法もあります。ただしメニューが出るかどうかは、AIが書いたコード次第です。出ない方もいるので、今日は全員エディタから動かします。

2「実行」を押す

選んだ名前が出ていることを確かめてから、「実行」を押します
保存 実行 デバッグ createFollowUpDrafts ▾
  1. 1「実行」を押します。行を選ぶ必要はありません。条件に合う行を、コードが自分で探します。
  2. 2押す前に、ここが「onOpen」になっていないかだけ見てください。なっていたら、押し直して選び直します。
「実行中」のまま、いつまでも終わらないときは、台帳のタブに切り替えてみてください。コードによっては、そちらに小さな確認の窓を出していることがあります。ボタンを押せば、実行が続きます。

3【確認 1/5】「承認が必要です」

承認が必要です

このプロジェクトがデータにアクセスするには、あなたの許可が必要です。

キャンセル権限を確認
  1. 1「権限を確認」を押します。新しい小さな窓が開きます。

4【確認 2/5】アカウントを選ぶ

ログイン
アカウントの選択

「無題のプロジェクト」に移動

あ
あなたのお名前
jibun@example.com
+
別のアカウントを使用
  1. 1STEP 1 で確かめたアカウントを押します。ここで違うものを選ぶと、先に進めません。

5【確認 3/5】「Googleで確認されていません」

ここで、ほとんどの方が手を止めます。止まって正解です。

ログイン
このアプリはGoogleで確認されていません

このアプリは、Googleによる確認が済んでいません。よく知っている開発者のアプリのみご使用ください。

詳細
安全なページに戻る
  1. 1左下の小さな「詳細」を押します。目立たない場所にあります。見つからない方は手を挙げてください。

!なぜ、この警告が出るのか

この警告は、あなたが今つくったものだから出ます。Googleの審査を受けていない、という意味です。5分前に自分で作ったものなので、進んで問題ありません。

ただし ── 他人から送られてきたスクリプトで、この画面が出たときは、進んではいけません。 今日、ここだけは覚えて帰ってください。

6【確認 4/5】「安全ではないページに移動」

ログイン
このアプリはGoogleで確認されていません

このアプリは、Googleによる確認が済んでいません。よく知っている開発者のアプリのみご使用ください。

詳細を非表示
Googleは、このアプリがまだ確認されていないため、まだ検証していません。確認が済むまでは、信頼できるデベロッパーのアプリのみを使用してください。 無題のプロジェクト(安全ではないページ)に移動
  1. 1この青い文字を押します。「安全ではない」と書かれていますが、意味は「Googleの審査を受けていない」です。自分で作ったものなので、進みます。

7【確認 5/5】「許可」を押す

ログイン
無題のプロジェクトがGoogleアカウントへのアクセスをリクエストしています
あ
jibun@example.com

無題のプロジェクトに以下を許可します:

Googleスプレッドシートのすべてのスプレッドシートの参照、編集、作成、削除
メールの表示、作成、送信、完全な削除
キャンセル許可
  1. 1「許可」を押します。これで終わりです。次からは一度も出ません。
「送信」「削除」と書かれていて驚かれたと思います。これはGmailを扱う道具に共通の言い方で、何をするかはコードが決めます。今日のコードに送信も削除も入っていません。
「このアプリはブロックされます」と出た方 → 手を挙げてください。STEP 2 とは別の制限です。この場合も、個人のGmailアカウントで開き直せば進めます。

8「実行ログ」を見る

動いたかどうかは、エディタの下に出る「実行ログ」で分かります。

実行ログはコードの下に出ます ── この2行は Google が出すもので、どんなコードでも必ず出ます
コード.gs
1/** 案件フォローアップの下書き係 */ 2function createFollowUpDrafts() { 3 …
14:32:05 実行開始 14:32:09 実行完了
  1. 1「実行完了」と出れば、コードは最後まで動きました。赤い「エラー」が出た方は、下の「うまくいかないときは」へ。

⚠️ この2行のあいだに「◯件つくりました」などの行が出ることもあります。コードがそう書かれていた場合だけです。出なくても異常ではありません。できたかどうかは、次のGmailで確かめます。

9Gmail の下書きを見る

ここが今日のゴールです。5件のうち3件だけできていれば正解 ── あとの2件はまだ30日たっていないので対象外です。

件名も本文も「設定」シートのとおりに作られます
mail.google.com/mail/u/0/#drafts
【その後のご状況伺い】株式会社あさひ工務店 田中 太郎様
To: jibun@example.com
株式会社あさひ工務店 田中 太郎 様 いつもお世話になっております。 2026年7月10日に完了した案件から1か月ほど経ちましたので、 その後のご状況を伺いたくご連絡いたしました。 (このあと数行つづきます) -- 〔ご自身の署名に書き換えてください〕

⚠️ 文面がこれと違っていても、失敗ではありません。AIが「設定」シートを使わずに自分で文章を書いた、というだけです。「件名と本文は設定シートのとおりにして」と追加で頼めば直せます。

10台帳に記録が残る

「台帳」のタブに戻ると、E列に日付が入っています
ABCDE
1会社名担当者名メールアドレス案件完了日下書き作成日
2株式会社あさひ工務店田中 太郎jibun@example.com2026/07/102026/09/07
3有限会社みなみ設備佐藤 花子jibun@example.com2026/07/282026/09/07
4株式会社北山電機鈴木 一郎jibun@example.com2026/08/052026/09/07
5大和建材株式会社高橋 健jibun@example.com2026/09/01
6株式会社さくら商会伊藤 美咲jibun@example.com2026/09/04
台帳設定
  1. 1作った行にだけ、日付が入ります。もう一度実行しても、この3件は二度と作られません。同じ相手に二重に送る事故が、これで起きなくなります。

⚠️ E列が空のままだった方へ。AIがそこまで書かなかっただけです。「下書きを作った行は、E列に日付を入れて、二度作らないようにして」と追加で頼めば足せます。

おつかれさまでした。ここが今日のゴールです

実行ログに「実行完了」、そしてGmailの下書きが3通。ここまで来た方は、もう自分ひとりで作れます。

うまくいかないときは

動かないのは、失敗ではありません

今日いちばん持ち帰る価値のある部分です

AIが書いたコードは、一度で動かないことがあります。そのときにやることは、たった1つです。

赤い文字を、そのままAIに渡す。

意味は分からなくていいです。文字をコピーして貼るか、写真を撮って貼るだけ。ここだけ覚えて帰れば、明日から自分ひとりで前に進めます。

行き止まりは、ありません
貼る 動かす ゴール 動いた エラーが出た AIに渡して直してもらう また貼る 何度でも戻れます。壊れることはありません

1赤い文字は、画面の下に出ます

コード.gs
1/** 案件フォローアップの下書き係 */ 2function createFollowUpDrafts() { 3 …
14:32:07 エラー TypeError: Cannot read properties of undefined (reading 'getTime') createFollowUpDrafts @ コード.gs:62
  1. 1この赤い部分を、まるごと選んでコピーします。1文字も読まなくて構いません。写真を撮って貼るのでも、まったく同じです。

2AIに、こう送ります

エラーが出たときに貼る文
さきほどのコードを実行したら、次のエラーが出ました。
直したコードを、また全部まとめて出してください。

(ここに、赤い文字をそのまま貼るか、写真を貼ってください)

あとは STEP 5 と同じ。全部消して、貼って、保存。何度やっても壊れません。

表よくある詰まりと、その場での直し方

こうなったらこうします
「拡張機能 → Apps Script」が開かない会社のアカウントで、管理者が Apps Script をオフにしています。個人のGmailアカウントで配られたリンクを開き直してください。
関数の一覧に、それらしい名前が出てこないエディタで「保存」を押し忘れています。保存してから、「実行」の右の関数名をもう一度押してください。
実行は終わったのに、下書きが1通もできない正常なこともあります。「E列が空」かつ「案件完了日から30日以上」の行が無いだけです。D列の日付を1か月以上前に書き換えてもう一度実行してみてください。
承認の画面で先に進めないログイン中のアカウントが違います。画面右上で確かめてください。「このアプリはブロックされます」と出た場合も個人のGmailアカウントで。
「詳細」が見つからない画面の左下にある小さな文字です。ウィンドウが小さいと隠れるので、大きくするか下へスクロールしてください。
赤い文字(エラー)が出たそのままコピーして、AIに貼ります。上の枠の文と一緒に送ってください。
そもそもコードが出てこない/変になった下の「答え」をそのまま使ってください。今日は先に進むことが目的です。

答それでも動かない方へ

答え ── AIがうまく書いてくれなかった方は、こちらをそのまま使ってください

件名と本文は、このコードの中ではなく台帳の「設定」シートにあります。文面を変えたいときは、そちらを直してください。

コード.gs(全部消してから、そのまま貼り付けてください)
/**
 * 案件フォローアップの下書き係
 *
 * 実行すると、「台帳」シートのうち
 *   ・E列(下書き作成日)がまだ空
 *   ・D列(案件完了日)から「設定」シートの経過日数がたっている
 * この2つに当てはまる行について、Gmail に下書きをつくる。
 * 送信はしない。送信ボタンは、最後まで人が押す。
 *
 * 「台帳」シート  A 会社名 / B 担当者名 / C メールアドレス / D 案件完了日 / E 下書き作成日
 * 「設定」シート  A 項目 / B 内容(件名・本文・経過日数)
 *
 * ⚠️ シートは名前で指定する。getActiveSheet() だと、
 *    「設定」のタブを開いたまま実行した人が、別のシートを読んでしまう。
 * ⚠️ 結果はダイアログ(ui.alert)ではなく実行ログに出す。
 *    ダイアログは台帳のタブに出るので、エディタから実行すると
 *    ボタンを押すまで終わらず、エディタ側は「実行中」のまま止まって見える。
 */

const SHEET_DATA   = '台帳';   // 案件のデータが入っているシート
const SHEET_CONF   = '設定';   // 件名・本文・経過日数が入っているシート
const DEFAULT_DAYS = 30;       // 「設定」に経過日数が無かったときに使う日数

/** シートを開いたときにメニューを足す。エディタから実行してもよい */
function onOpen() {
  SpreadsheetApp.getUi()
    .createMenu('フォローアップ')
    .addItem('下書きをつくる', 'createFollowUpDrafts')
    .addToUi();
}

/** 条件に合う行について、Gmail の下書きをつくる */
function createFollowUpDrafts() {
  const book  = SpreadsheetApp.getActiveSpreadsheet();
  const sheet = book.getSheetByName(SHEET_DATA);
  if (!sheet) {
    return report(book, '「' + SHEET_DATA + '」という名前のシートが見つかりませんでした。');
  }

  const conf    = readSettings(book);
  const lastRow = sheet.getLastRow();
  if (lastRow < 2) {
    return report(book, '「' + SHEET_DATA + '」にデータがありません。2行目から下に入れてください。');
  }

  const rows  = sheet.getRange(2, 1, lastRow - 1, 5).getValues();
  const today = new Date();
  const made  = [];
  let skipped = 0;

  rows.forEach(function (row, i) {
    const rowNumber = i + 2;
    const company   = String(row[0]).trim();
    const person    = String(row[1]).trim();
    const address   = String(row[2]).trim();
    const doneDate  = row[3];
    const draftDate = row[4];

    // もう下書きをつくった行は、対象にしない
    if (String(draftDate).trim() !== '') return;

    // 会社名・メールアドレス・案件完了日が揃っていない行は飛ばす
    if (company === '' || address === '' || !(doneDate instanceof Date)) {
      skipped += 1;
      return;
    }

    // 案件完了から何日たったかを数える(86400000 は 1日のミリ秒)
    const days = Math.floor((today.getTime() - doneDate.getTime()) / 86400000);
    if (days < conf.afterDays) return;

    GmailApp.createDraft(
      address,
      fillIn(conf.subject, company, person, doneDate),
      fillIn(conf.body, company, person, doneDate)
    );
    sheet.getRange(rowNumber, 5).setValue(today);   // E列に下書き作成日を入れる
    made.push(rowNumber);
  });

  let message;
  if (made.length === 0) {
    message = '今回つくるものはありませんでした。'
      + '(「下書き作成日が空」かつ「案件完了日から' + conf.afterDays + '日以上」の行がありません)';
  } else {
    message = made.length + ' 件の下書きをつくりました。(' + made.join('行目・') + '行目)'
      + ' Gmail の「下書き」を開いて、中身を確かめてから送信してください。';
  }
  if (skipped > 0) {
    message += ' ※ 会社名・メールアドレス・案件完了日のどれかが空だった '
      + skipped + ' 行は飛ばしました。';
  }
  report(book, message);
}

/** 「設定」シートから、件名・本文・経過日数を読む */
function readSettings(book) {
  // 「設定」シートが無い・空のときでも止まらないように、最低限の文面を持っておく
  const fallback = {
    subject: 'その後のご状況について({{会社名}} {{担当者名}}様)',
    body: '{{会社名}}\n{{担当者名}} 様\n\nいつもお世話になっております。\n\n'
        + '{{案件完了日}}に完了した案件から、しばらく経ちました。\n'
        + 'その後、お困りのことなどはございませんでしょうか。',
    afterDays: DEFAULT_DAYS
  };

  const sheet = book.getSheetByName(SHEET_CONF);
  if (!sheet || sheet.getLastRow() < 2) return fallback;

  // A列を項目名、B列を内容として読む
  const rows  = sheet.getRange(1, 1, sheet.getLastRow(), 2).getValues();
  const found = {};
  rows.forEach(function (row) {
    found[String(row[0]).trim()] = row[1];
  });

  const days = Number(found['経過日数']);
  return {
    subject:   String(found['件名'] || fallback.subject),
    body:      String(found['本文'] || fallback.body),
    afterDays: (days > 0 ? days : DEFAULT_DAYS)
  };
}

/** {{会社名}} などの差し込みタグを、その行の値に置き換える */
function fillIn(text, company, person, doneDate) {
  const day = Utilities.formatDate(doneDate, Session.getScriptTimeZone(), 'yyyy年M月d日');
  return String(text)
    .split('{{会社名}}').join(company)
    .split('{{担当者名}}').join(person === '' ? 'ご担当者' : person)
    .split('{{案件完了日}}').join(day);
}

/** 結果を知らせる。エディタの実行ログと、台帳の右下に出る小さな帯の両方に出す */
function report(book, message) {
  Logger.log(message);
  book.toast(message, '下書き係', 8);
}
STEP7

直してみる ── 送る相手を、自分で選べるようにする

めやす 3分

実務では「この会社は今回は送らない」があります。それを選べるようにします。

作ったものを直す ── これができれば、あとは何でも足せます。やることは、さっきと同じ「日本語で頼む」だけ。

1台帳に「対象」の列を足す

「台帳」のタブで作業します。「設定」シートは触りません。

F列に見出しを入れて、チェックボックスにします(図はB・C列と5〜6行目を省略)
ファイル編集表示挿入表示形式データツール拡張機能ヘルプ
行列セル チェックボックス 関数
ADEF
1会社名案件完了日下書き作成日対象
2株式会社あさひ工務店2026/07/102026/09/07
3有限会社みなみ設備2026/07/282026/09/07
4株式会社北山電機2026/08/052026/09/07
  1. 1F1 のセルに「対象」と入力します。そのあと F2 から F6 までをドラッグして選びます。
  2. 2上のメニューの「挿入」を押します。
  3. 3「チェックボックス」を押します。5つの四角が出たら、全部クリックしてチェックを入れておいてください。

2AIに、2行だけ足して頼む

作り直す必要はありません。さきほどの会話の続きに、これを送るだけ。

さきほどの会話の続きに貼る文
台帳に「対象」という列(F列)を追加して、チェックボックスを入れました。
チェックが入っている行だけを対象にするように直してください。
  1. 1返ってきたコードをコピーして、エディタで全部消してから貼り、保存します。STEP 5 と同じ作業です。
  2. 2エディタで、さっきと同じように関数を選んで「実行」します。承認の画面は、もう出ません。

3確かめる ── チェックを外した行は、作られない

E列の日付を3つとも消して対象に戻し、1件だけチェックを外して実行してみてください。

3行目のチェックを外して実行すると ──
ADEF
1会社名案件完了日下書き作成日対象
2株式会社あさひ工務店2026/07/102026/09/07
3有限会社みなみ設備2026/07/28(作られない)
4株式会社北山電機2026/08/052026/09/07
  1. 1チェックを外した3行目だけ、下書きが作られません。条件は満たしているのに、あなたが「今回は送らない」と決めたからです。これで、機械に任せながら、最後の判断は人が持てるようになりました。
答え ── 「対象」列に対応させたコード

さきほどのコードとの違いは3か所だけです(読む必要はありません)。

コード.gs(「対象」列つき)
/**
 * 案件フォローアップの下書き係(「対象」列つき)
 *
 * 基本の版との違いは3か所だけ(F列「対象」のチェックを見るようにした)。
 *
 * 実行すると、「台帳」シートのうち
 *   ・F列(対象)にチェックが入っている      ← 足したのはここ
 *   ・E列(下書き作成日)がまだ空
 *   ・D列(案件完了日)から「設定」シートの経過日数がたっている
 * この3つに当てはまる行について、Gmail に下書きをつくる。
 * 送信はしない。送信ボタンは、最後まで人が押す。
 *
 * 「台帳」シート  A 会社名 / B 担当者名 / C メールアドレス / D 案件完了日 / E 下書き作成日 / F 対象
 * 「設定」シート  A 項目 / B 内容(件名・本文・経過日数)
 *
 * ⚠️ シートは名前で指定する。getActiveSheet() だと、
 *    「設定」のタブを開いたまま実行した人が、別のシートを読んでしまう。
 * ⚠️ 結果はダイアログ(ui.alert)ではなく実行ログに出す。
 *    ダイアログは台帳のタブに出るので、エディタから実行すると
 *    ボタンを押すまで終わらず、エディタ側は「実行中」のまま止まって見える。
 */

const SHEET_DATA   = '台帳';   // 案件のデータが入っているシート
const SHEET_CONF   = '設定';   // 件名・本文・経過日数が入っているシート
const DEFAULT_DAYS = 30;       // 「設定」に経過日数が無かったときに使う日数

/** シートを開いたときにメニューを足す。エディタから実行してもよい */
function onOpen() {
  SpreadsheetApp.getUi()
    .createMenu('フォローアップ')
    .addItem('下書きをつくる', 'createFollowUpDrafts')
    .addToUi();
}

/** 条件に合う行について、Gmail の下書きをつくる */
function createFollowUpDrafts() {
  const book  = SpreadsheetApp.getActiveSpreadsheet();
  const sheet = book.getSheetByName(SHEET_DATA);
  if (!sheet) {
    return report(book, '「' + SHEET_DATA + '」という名前のシートが見つかりませんでした。');
  }

  const conf    = readSettings(book);
  const lastRow = sheet.getLastRow();
  if (lastRow < 2) {
    return report(book, '「' + SHEET_DATA + '」にデータがありません。2行目から下に入れてください。');
  }

  const rows  = sheet.getRange(2, 1, lastRow - 1, 6).getValues();   // ① F列まで読む
  const today = new Date();
  const made  = [];
  let skipped = 0;

  rows.forEach(function (row, i) {
    const rowNumber = i + 2;
    const company   = String(row[0]).trim();
    const person    = String(row[1]).trim();
    const address   = String(row[2]).trim();
    const doneDate  = row[3];
    const draftDate = row[4];
    const wanted    = row[5];   // ② F列「対象」のチェック(入っていれば true)

    // チェックが入っていない行は、条件を満たしていても対象にしない
    if (wanted !== true) return;   // ③ ここが足した1行

    // もう下書きをつくった行は、対象にしない
    if (String(draftDate).trim() !== '') return;

    // 会社名・メールアドレス・案件完了日が揃っていない行は飛ばす
    if (company === '' || address === '' || !(doneDate instanceof Date)) {
      skipped += 1;
      return;
    }

    // 案件完了から何日たったかを数える(86400000 は 1日のミリ秒)
    const days = Math.floor((today.getTime() - doneDate.getTime()) / 86400000);
    if (days < conf.afterDays) return;

    GmailApp.createDraft(
      address,
      fillIn(conf.subject, company, person, doneDate),
      fillIn(conf.body, company, person, doneDate)
    );
    sheet.getRange(rowNumber, 5).setValue(today);   // E列に下書き作成日を入れる
    made.push(rowNumber);
  });

  let message;
  if (made.length === 0) {
    message = '今回つくるものはありませんでした。'
      + '(「対象にチェック」かつ「下書き作成日が空」かつ'
      + '「案件完了日から' + conf.afterDays + '日以上」の行がありません)';
  } else {
    message = made.length + ' 件の下書きをつくりました。(' + made.join('行目・') + '行目)'
      + ' Gmail の「下書き」を開いて、中身を確かめてから送信してください。';
  }
  if (skipped > 0) {
    message += ' ※ 会社名・メールアドレス・案件完了日のどれかが空だった '
      + skipped + ' 行は飛ばしました。';
  }
  report(book, message);
}

/** 「設定」シートから、件名・本文・経過日数を読む */
function readSettings(book) {
  // 「設定」シートが無い・空のときでも止まらないように、最低限の文面を持っておく
  const fallback = {
    subject: 'その後のご状況について({{会社名}} {{担当者名}}様)',
    body: '{{会社名}}\n{{担当者名}} 様\n\nいつもお世話になっております。\n\n'
        + '{{案件完了日}}に完了した案件から、しばらく経ちました。\n'
        + 'その後、お困りのことなどはございませんでしょうか。',
    afterDays: DEFAULT_DAYS
  };

  const sheet = book.getSheetByName(SHEET_CONF);
  if (!sheet || sheet.getLastRow() < 2) return fallback;

  // A列を項目名、B列を内容として読む
  const rows  = sheet.getRange(1, 1, sheet.getLastRow(), 2).getValues();
  const found = {};
  rows.forEach(function (row) {
    found[String(row[0]).trim()] = row[1];
  });

  const days = Number(found['経過日数']);
  return {
    subject:   String(found['件名'] || fallback.subject),
    body:      String(found['本文'] || fallback.body),
    afterDays: (days > 0 ? days : DEFAULT_DAYS)
  };
}

/** {{会社名}} などの差し込みタグを、その行の値に置き換える */
function fillIn(text, company, person, doneDate) {
  const day = Utilities.formatDate(doneDate, Session.getScriptTimeZone(), 'yyyy年M月d日');
  return String(text)
    .split('{{会社名}}').join(company)
    .split('{{担当者名}}').join(person === '' ? 'ご担当者' : person)
    .split('{{案件完了日}}').join(day);
}

/** 結果を知らせる。エディタの実行ログと、台帳の右下に出る小さな帯の両方に出す */
function report(book, message) {
  Logger.log(message);
  book.toast(message, '下書き係', 8);
}
完成です

「作って、動かして、直す」を全部やりました。この3つができれば、あとは日本語で頼むだけ。次のページで、何が足せるかをお話しします。

③ 完成後の話へ ── できること・注意点・質問
← ① これから に戻る