フォームの報告をシートに自動で集め、週次レポートにしてメールで届けられます
現場の担当者がGoogle フォームで出した報告を、Google Apps Script(GAS)でスプレッドシートに自動で入れ、週ごとに集計してGoogle ドキュメントのレポートにまとめ、PDFにして責任者へメールで送る仕組みの見本です。題材は宿泊施設の清掃報告にしました。転記や集計の手作業がなくなり、気になる不具合は週の頭に一覧で届きます。
画面は動きを再現したもので、データはすべて架空です
約3.5年、GASの仕組みを作って運用してきました。
- 1フォームの回答をシートに自動で入れる
- 2週ごとに施設別の回数・作業時間・不具合を集計
- 3ドキュメントのレポートをPDFにしてメールで送る
再現画面※再現画面です(実際のGoogleの画面ではありません)。
清掃報告のシート(見本)
10/19〜10/25の週です。最初は3件だけ入っています。「フォームの回答を取り込む」で、届いた5件がシートに入ります。
この一覧は、フォームの回答8件を取り込んだあとの例です。JavaScriptが有効なら、取り込みとレポート作成を順に試せます。
| 受付番号 | 報告日時 | 施設 | 担当 | 作業時間 | 補充した備品 | 不具合 |
|---|---|---|---|---|---|---|
| R-301 | 10/19 11:20 | 施設A | 山田 | 95分 | タオル・洗剤 | なし |
| R-302 | 10/19 14:05 | 施設B | 佐々木 | 80分 | なし | シャワーの水圧が弱い |
| R-303 | 10/20 10:40 | 施設C | 田村 | 110分 | トイレットペーパー | なし |
| R-304 | 10/21 12:15 | 施設A | 中川 | 90分 | なし | なし |
| R-305 | 10/22 10:30 | 施設B | 山田 | 85分 | タオル | なし |
| R-306 | 10/23 13:50 | 施設C | 佐々木 | 120分 | 洗剤 | エアコンのリモコン電池切れ |
| R-307 | 10/24 11:00 | 施設A | 田村 | 100分 | タオル・トイレットペーパー | 玄関の電球切れ |
| R-308 | 10/25 10:10 | 施設B | 中川 | 75分 | なし | なし |
- シートの件数8件
- 作業時間の合計755分
- 不具合の報告4件
「週次レポートを作る」を押すと、レポートと送るメールがここに出ます。
清掃報告 週次レポート(10/19〜10/25)
ドキュメントの雛形に差し込み、PDFで保存した内容の例
| 施設 | 清掃回数 | 作業時間 | 不具合 |
|---|---|---|---|
| 施設A | 3回 | 285分 | 1件 |
| 施設B | 3回 | 240分 | 1件 |
| 施設C | 2回 | 230分 | 1件 |
対応が必要な不具合
- 10/19 14:05 施設B:シャワーの水圧が弱い
- 10/23 13:50 施設C:エアコンのリモコン電池切れ
- 10/24 11:00 施設A:玄関の電球切れ
宛先:運営責任者 件名:【週次】清掃報告レポート 10/19〜10/25 添付:清掃報告_1019-1025.pdf 今週の清掃は8回、作業時間は合計755分でした。不具合の報告が4件あります。詳しくは添付をご確認ください。
GASの関数(抜粋)
フォームの送信時に1つ目の関数、毎週月曜の朝に2つ目の関数をトリガーで動かす想定です。項目や集計の単位、レポートの雛形、送り先は、今の業務の流れを伺って決めます。
// フォーム送信時のトリガー:回答をシートに入れ、受付番号を振る
function onReportSubmit(e) {
const sheet = SpreadsheetApp.getActive().getSheetByName('清掃報告');
const v = e.namedValues;
const no = 'R-' + (300 + sheet.getLastRow());
sheet.appendRow([no, new Date(), v['施設'][0], v['担当'][0],
Number(v['作業時間(分)'][0]), v['補充した備品'][0] || 'なし', v['不具合'][0] || 'なし']);
}
// 毎週月曜8時のトリガー:先週分を集計し、PDFにしてメールで送る
function sendWeeklyReport() {
const end = new Date(); end.setHours(0, 0, 0, 0);
const start = new Date(end.getTime() - 7 * 24 * 3600 * 1000);
const rows = SpreadsheetApp.getActive().getSheetByName('清掃報告')
.getDataRange().getValues().slice(1)
.filter(r => r[1] >= start && r[1] < end);
const byPlace = {};
const issues = [];
rows.forEach(([no, at, place, staff, min, items, issue]) => {
const p = byPlace[place] = byPlace[place] || { count: 0, min: 0, issue: 0 };
p.count++; p.min += min;
if (issue !== 'なし') { p.issue++; issues.push(`${fmt_(at)} ${place}:${issue}`); }
});
const copy = DriveApp.getFileById(TEMPLATE_ID).makeCopy(`清掃報告_${fmt_(start)}`);
const doc = DocumentApp.openById(copy.getId());
const body = doc.getBody();
body.replaceText('{{期間}}', `${fmt_(start)}〜${fmt_(end)}`);
const table = Object.keys(byPlace).map(k =>
[k, byPlace[k].count + '回', byPlace[k].min + '分', byPlace[k].issue + '件']);
body.appendTable([['施設', '清掃回数', '作業時間', '不具合'], ...table]);
body.appendParagraph(issues.length ? issues.join('\n') : '不具合の報告はありません');
doc.saveAndClose();
const total = rows.reduce((s, r) => s + r[4], 0);
GmailApp.sendEmail(MANAGER_ADDRESS, `【週次】清掃報告レポート ${fmt_(start)}〜`,
`今週の清掃は${rows.length}回、作業時間は合計${total}分でした。不具合の報告が${issues.length}件あります。`,
{ attachments: [copy.getAs(MimeType.PDF)] });
}
const fmt_ = d => Utilities.formatDate(d, 'Asia/Tokyo', 'M/d');
作業時間や不具合の件数は、施設ごとの比較や外注先との精算にも使えます。最初に今の報告の流れを見て、どこで手が止まっているかを洗い出してから組みます。