Googleフォームの回答を担当者へ振り分ける方法|通知と受領を追う手順

フォームの報告を受け、矢印で複数の担当者へつなぐイラスト デジタルツール
報告区分から担当者へ連絡を渡すイメージ。送信後は受領と対応も記録します。

フォームへ報告したのに、担当者が気付かず対応が止まっていた。そのような行き違いを減らすには、「品質不良は誰が受けるか」「区分を選べない報告は誰へ渡すか」を先に決めます。そのうえで、報告区分と宛先を対応させれば、回答を担当者へ通知する仕組みを作れます。この記事では、回答先の表、通知コード、受領後の記録までを順に説明します。

ただし、メールが送られたことと、担当者が内容を読んで対応したことは別です。設備の危険や品質への重大な影響が疑われるときは、フォーム通知を待たず、定めた停止・隔離・連絡手順に従ってください。ここで作る仕組みは報告の受け渡しを助けるものです。緊急連絡や対応の判断までメールへ任せず、誰が受領して次へ進めるかを記録します。

状況判断最初の対応次に見る点
報告が少なく、管理者が期限内に受領できる一律通知と手動割当でも開始できる回答シートに対応担当・状態の列を作る対応期限より受領が遅れる報告はないか
区分ごとの受領担当が決まっている区分別の自動通知が向く報告区分と通知先の対応表を作る区分が担当者を一意に決められるか
担当者の休みや異動が多い通常の担当者宛先を表で持つ設定表に一次担当・代替通知先を登録する更新担当者と更新時点が決まっているか
区分外・分類不能の報告が届く管理者へ例外として渡す未登録時の管理者通知先を決める例外が繰り返されていないか

担当者への振り分けは「報告区分を固定する」ことから始める

まず、フォームに「報告区分」を必須のプルダウンか選択肢として設けます。「機械が止まった」といった自由記述だけでは、保全担当へ渡すのか、生産管理にも伝えるのかを毎回判断することになるためです。詳しい状況を書く欄は別に残し、通知先を決める区分と、担当者が内容を読むための説明を分けましょう。

区分は「品質不良」「設備異常」「材料不足」など、最初に受け取る担当者を決められる単位にします。一方、「至急」だけでは宛先を決められないため、緊急度は別の項目で扱います。関係部署が複数あっても、受領と次の連絡を担う一次担当者は一人に決めると、全員が他の人の対応を待つ状態を避けやすくなります。

フォームの選択肢を変えるときは、同じ変更の中で「振り分け設定」も更新します。変更日、変更者、変更理由は表の右側などへ残してください。フォームだけを先に変えると、新しい区分が未登録として管理者へ送られます。更新後にテスト回答を送り、意図した担当者が受け取れるところまでを変更作業に含めましょう。

標準通知と担当別通知は役割を分けて考える

上の表は、自動化する前に受け渡し方を選ぶ目安です。Googleフォームの標準の新着通知は、回答が来たことを知らせる用途に使えます。回答内容で宛先を変える場合は、利用中のアカウントで使える機能を調べ、区分と宛先の関係を設定します。この記事では、その関係を表へ置き、Apps Scriptで読む方法を扱います。

回答が少なく、管理者が期限内に受け取れるなら、一律通知と手動割当から始めても構いません。しかし、不在時に止まる、受け取った人が分からない、といった問題が続くなら、担当別通知と受領記録を組み合わせます。スプレッドシートの条件付き通知にはアカウントや宛先の制約もあるため、使える機能だけを前提に、例外を受ける管理者を決めてください。

回答先スプレッドシートに振り分け設定表を作る

フォームの「回答」から、新規または既存のスプレッドシートを保存先に指定します。そのファイルに「振り分け設定」というシートを追加し、下の見出しを1行目へ置いてください。通常の担当者宛先を表で持てば、人の交代時にコード内を探す手間を減らせます。ただし、管理者の宛先と回答シート名は、後述するコードの先頭で指定します。

下の表は記入例で、アドレスは実際の宛先へ置き換えます。「有効」の区分だけを担当別通知に使い、「停止」や未登録の区分は管理者へ渡します。「優先度」は運用上の目安であり、このコードは通知順の変更や緊急連絡を行いません。また、管理者へ回る例外回答と、スクリプト自体の失敗は別です。処理エラーは実行履歴と回答一覧で追います。

報告区分通知先メールアドレスCC代替通知先優先度有効/停止
品質不良quality@example.commanager@example.comquality-sub@example.com高有効
設備異常maintenance@example.commaintenance-sub@example.com高有効
材料不足purchasing@example.commanager@example.com中有効

フォームと設定表の区分は同じ表記にします。掲載コードは前後の空白を除きますが、全角・半角や似た言葉までは同じ区分と扱いません。有効な同一区分が重複していたら、どちらかへ勝手に送らず処理を止めます。担当の異動・休暇では設定表を更新し、元の人へ送るのか、代わりの人だけへ送るのかも決めてから試してください。

Apps Scriptで回答区分に応じたメール通知を作る

回答先スプレッドシートからApps Scriptを開きます。コードの前提は、回答シートの1行目に「タイムスタンプ」「報告区分」「対象」「内容」があり、右側へ「通知先」「通知日時」「送信結果」の列を各1個追加してあることです。「対象」は「設備・品番」でも使えます。結合セルを使わず、これらの列へ実行者が書き込めるようにします。先頭の管理者アドレスと回答シート名も実物に合わせてください。

const ADMIN_EMAIL = 'manager@example.com'; // 実際の管理者宛先へ変更
const CONFIG_SHEET = '振り分け設定';
const RESPONSE_SHEET = 'フォームの回答 1'; // 実際の回答シート名へ変更
const CATEGORY_LABEL = '報告区分';

function routeFormResponse(e) {
  if (!e || !e.range || !e.namedValues) {
    throw new Error('回答先スプレッドシートのフォーム送信時に動かしてください。');
  }
  const sheet = e.range.getSheet();
  const row = e.range.getRow();
  if (sheet.getName() !== RESPONSE_SHEET || row < 2) {
    throw new Error('対象外のシートまたは行です。');
  }
  const lock = LockService.getScriptLock();
  if (!lock.tryLock(10000)) throw new Error('別の通知処理が実行中です。実行履歴を調べてください。');
  try {
    const ss = sheet.getParent();
    const headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getDisplayValues()[0];
    const column = routingColumns(headers, [CATEGORY_LABEL, 'タイムスタンプ', '内容', '通知先', '通知日時', '送信結果']);
    const targetLabel = headers.includes('対象') ? '対象' : '設備・品番';
    routingColumns(headers, [targetLabel]);
    const resultCell = sheet.getRange(row, column['送信結果'] + 1);
    const previous = String(resultCell.getValue()).trim();
    if (['送信済み', '例外送信済み'].includes(previous)) return;
    if (previous) throw new Error('この行は処理履歴があります。送信状況を照合してから処置してください。');
    const answer = name => String((e.namedValues[name] || [''])[0]).trim();
    const category = answer(CATEGORY_LABEL);
    const target = answer(targetLabel);
    const detail = answer('内容');
    const admin = routingAddresses(ADMIN_EMAIL);
    if (admin.length !== 1) throw new Error('管理者宛先を1件にしてください。');
    const config = ss.getSheetByName(CONFIG_SHEET);
    if (!config) throw new Error('振り分け設定シートがありません。');
    const rows = config.getDataRange().getDisplayValues();
    const col = routingColumns(rows.shift() || [], ['報告区分', '通知先メールアドレス', 'CC', '代替通知先', '優先度', '有効/停止']);
    const active = new Map();
    rows.forEach(r => {
      if (r.every(v => String(v).trim() === '')) return;
      const state = String(r[col['有効/停止']] || '').trim();
      if (state === '停止') return;
      if (state !== '有効') throw new Error('有効/停止の記入を調べてください。');
      const key = String(r[col['報告区分']] || '').trim();
      if (!key || active.has(key)) throw new Error('空欄または重複した有効区分があります。');
      const to = routingAddresses(r[col['通知先メールアドレス']]);
      if (to.length !== 1) throw new Error('一次担当の宛先を1件にしてください。');
      const cc = routingAddresses([r[col['CC']], r[col['代替通知先']]].filter(Boolean).join(','));
      active.set(key, {to: to[0], cc: cc.filter(a => a.toLowerCase() !== to[0].toLowerCase())});
    });
    const match = active.get(category);
    const exception = !match || !target || !detail;
    const route = exception ? {to: admin[0], cc: []} : match;
    if (MailApp.getRemainingDailyQuota() < 1 + route.cc.length) throw new Error('本日の送信枠が不足しています。');
    const link = ss.getUrl() + '#gid=' + sheet.getSheetId() + '&range=A' + row;
    const body = '受付日時:' + answer('タイムスタンプ') + '\n報告区分:' + category
      + '\n対象:' + target + '\n内容:' + detail + '\n回答行:' + link
      + (exception ? '\n未登録・停止区分または対象/内容の不足を管理者へ渡しています。' : '');
    // 先に開始を残す。送信と記帳は一体で確定できないため、自動再送しない。
    resultCell.setValue('送信開始');
    sheet.getRange(row, column['通知先'] + 1).setValue(route.to + (route.cc.length ? '(CC:' + route.cc.join(',') + ')' : ''));
    SpreadsheetApp.flush();
    try {
      MailApp.sendEmail({to: route.to, cc: route.cc.join(','), subject: '【フォーム報告】' + (category || '区分未入力'), body: body});
    } catch (error) {
      try { resultCell.setValue('送信不明:要照合'); SpreadsheetApp.flush(); } catch (logError) { console.error(logError); }
      throw error;
    }
    sheet.getRange(row, column['通知日時'] + 1).setValue(new Date());
    resultCell.setValue(exception ? '例外送信済み' : '送信済み');
    SpreadsheetApp.flush();
  } finally {
    lock.releaseLock();
  }
}

function routingColumns(headers, names) {
  const result = {};
  names.forEach(name => {
    if (headers.filter(h => h === name).length !== 1) throw new Error('見出しを1個だけ置いてください:' + name);
    result[name] = headers.indexOf(name);
  });
  return result;
}

function routingAddresses(value) {
  const addresses = String(value || '').split(',').map(a => a.trim()).filter(Boolean);
  const seen = new Set();
  return addresses.filter(a => {
    if (!/^[^\s@,;<>]+@[^\s@,;<>]+\.[^\s@,;<>]+$/.test(a) || /@example\.com$/i.test(a)) {
      throw new Error('宛先の書式と例示アドレスを調べてください。');
    }
    const key = a.toLowerCase();
    if (seen.has(key)) return false;
    seen.add(key);
    return true;
  });
}

このコードは見出し名で列を探すため、列番号だけに依存しません。ただし、質問名を変えたら参照名も変えます。一次担当は1アドレス、CCと代替通知先はカンマ区切りで指定でき、同じ宛先は重複を除きます。代替先は一次担当の置き換えではなくCCへの追加です。対象や内容が空なら、担当者へ通常通知せず管理者へ渡します。

送信前に「送信開始」を残し、メール送信処理が戻った後に日時と結果を書きます。「送信済み」は受信・既読・対応完了の証拠ではありません。メール送信とシート記帳は一体で確定できないため、途中停止した行は自動再送せず照合へ回します。送信枠は宛先人数で点検しますが、同じアカウントの他処理でも消費されるため、枠が残っていても成功を保証しません。回答原本は並べ替え・行移動・削除せず、見せ方を変えるときは別の表示用シートを使います。

フォーム送信時に動くトリガーを設定する

コードの保存後、トリガーで「routeFormResponse」、イベントのソースでスプレッドシート、種類でフォーム送信時を選びます。要求される権限を読んで承認し、試験はフォームから回答を送って行ってください。編集画面の実行ボタンではイベント情報が渡らないため、その操作で通知を試すコードではありません。同じ処理のトリガーを重ねて作らないよう、作成者と設定を記録します。

フォーム本体の送信トリガーと、回答先スプレッドシートの送信トリガーでは、渡される情報が違います。上のコードは後者の回答行と設問名付きの値を使うため、置き場所とトリガーを対で合わせます。また、ロックで止められるのは同じスクリプト内の同時処理です。人の編集や別プロジェクトは止まらないので、稼働中に原本の行・列を移さない運用も設けます。

トリガーは作成者の権限で動きます。引継ぎでは、組織のアカウント運用に従って担当者を決め、旧トリガーの停止と新担当による設定・試験を行ってください。他人が作ったトリガーは自分の一覧に出ない場合もあるため、一覧だけで二重登録なしとは決めません。フォームと回答先の共有範囲もそれぞれ点検し、メールのリンクを受け取っただけで閲覧権限が付くわけではないことを受領者へ伝えます。

公開前は4パターンを起点に受信と記帳を試す

登録済みの区分だけで試すと、例外時の行き先を見落とします。まずテスト用のフォームと回答先で下の4パターンを試し、実際の宛先で受信を照合してください。その後、送信枠不足、見出し違い、同一区分の重複、処理履歴が残る行も試験に加えます。掲載コードのローカル模擬試験と、各社のGoogle環境での動作試験は別です。

  • 登録済み区分:一次担当者・CCが受信し、件名・本文・送信結果が想定と合うかを照合する。
  • 未登録区分:管理者へ届き、例外送信済みと残るかを試す。停止中の区分も合わせて試す。
  • 空欄・不足情報:必須設問が未入力を止めるかを試し、不足した回答は管理者へ回るかを照合する。
  • 担当変更後:表を更新して回答を送り、新旧の担当者とCCの行き先、回答一覧の記帳を照合する。

届かないときは、実行履歴、権限、設定表、回答行の結果、受信側の状態を順にたどります。「送信開始」や「送信不明:要照合」が残った行は、受領者と実行履歴を照らし、再通知するかを管理者が決めます。履歴を消して機械的に再実行すると二重通知になり得ます。同じ行の再処理は止めますが、同じ内容を別の回答として送った場合まで重複除外するコードではありません。

通知後の対応担当と完了状態を回答一覧に残す

通知先と、実際に処置する担当者は分けて記録します。回答一覧には自動記入の「通知先」「通知日時」「送信結果」とは別に、「対応担当」「受付状態」「対応期限」「完了日」「対応メモ」を設けましょう。上の通知コードは、これらの対応欄を完了へ変えません。担当者が受領し、引継ぎや処置の結果を記入することで、次に誰が動くかを追えるようになります。

項目記入者記入する時点記入例
対応担当一次担当者自分で対応しないと判断した時点設備担当へ引継ぎ
受付状態一次担当者受領・引継ぎ・処置完了の各時点未受領/対応中/完了
対応期限対応担当または管理者影響と対応の期限を判断した時点次回生産前
対応メモ対応担当処置または引継ぎをした直後点検依頼済み、担当者へ連絡済み

上の表の「連絡済み」は、引継ぎ先が受け取ったことや点検完了とは別です。一次担当者は受領後に担当と期限を記入し、引継ぎ時には相手の受領まで追ってください。管理者は業務に合う時刻で、未受領・期限超過だけでなく、結果が空欄の回答、送信開始で止まる回答も読みます。設備異常などの初動は、この定期点検まで先送りしません。

月次では、例外が繰り返される区分や、担当変更の漏れ、長く止まる案件を振り返ります。日々の受領と処置を済ませたうえで、区分や通知先を見直す材料にしてください。入力内容が業務ごとに大きく違うなら、Googleスプレッドシートで作業者別の入力欄を分ける方法も参考になります。入力経路を分ける場合も、報告がどの一覧へ集まり、誰が受けるかを明示します。

よくある質問

担当者が休みの日だけ、別の人にも通知するにはどう設定すればよいですか?

この例では「代替通知先」をCCへ加えるため、一次担当者にも送られます。休暇中は代わりの人だけへ送りたいなら、一次担当の宛先を変更し、CCも含めて元の人が残らないか点検してください。設定を変える人、適用期間、復帰時に戻す人を決め、送信試験を行います。代わりの人にも回答シートを読める権限があるかを合わせて調べましょう。

報告区分が複数当てはまる場合、1件の回答を複数担当者へ送れますか?

関係者をCCへ加えることはできますが、掲載コードは区分の複数選択には対応しません。まず一つの報告区分と一次担当を決め、他部署への引継ぎを対応メモへ残す方法から始めます。複数区分を使うなら、宛先の重複除外だけでなく、受領を担う人、権限の範囲、例外時の行き先まで設計し直してください。人数を増やすだけでは責任の受け渡しは決まりません。

メールは届くのに送信結果が更新されないときは、どう調べますか?

送信後に記帳だけが失敗する場合があります。保護設定、書込権限、見出しや列の変更、実行履歴を調べ、受領者へ届いたかを照合してください。結果が空欄や途中状態でも「送っていない」とは決めません。再通知するときは理由と担当者を別の対応記録へ残します。この例は処理失敗時に管理者へさらにメールを送らないため、失敗通知メールの到着だけに頼らず回答一覧と実行履歴を見ます。

質問名や選択肢を変えたら、どこまで試し直しますか?

質問名、区分、設定見出し、宛先を変えた後は、4パターンを一通り再実施します。列を動かしたときも、記帳先と結果の扱いを試してください。変更前の設定と試験結果を残し、不具合が出たら新規通知を止めて手動の受け渡しへ切り替えます。旧版へ戻す際も、変更中に届いた回答が未処理にならないよう、期間内の回答を一覧から追います。

参考にした公式情報

下の公式資料は、回答先、標準通知、イベント情報、権限と送信枠を調べる際の参照先です。ロックの対象範囲はGoogle公式のLockService(https://developers.google.com/apps-script/reference/lock/lock-service)も参照しました。2026年10月4日に仕様を照合していますが、利用中のアカウントと設定で試し、届かない場合の手動連絡まで決めてから使ってください。

まとめ

担当別通知は、区分と宛先を表に置くだけで終わりません。一次担当と例外を受ける管理者を決め、回答先の見出し、権限、トリガーを合わせたうえで、受信と記帳を試します。最初は扱う区分を絞ると、どの条件で誰へ届いたかを追いやすくなります。送信開始で止まった行を見つける担当も、開始時に決めておきましょう。

運用後は、通知、受領、対応、完了を分けて記録します。日々は未受領や途中停止を追い、月次では例外が続く理由を振り返る流れです。フォームは連絡を助ける道具なので、緊急時の手順や業務責任まで置き換えません。報告を出す人と受ける人の両方が、送った後に何をするか分かる状態から始めてください。

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