結論:これまで同期してきた「案件一覧」と「作業ログ」のシートから、GASで月次の売上・稼働時間・平均時給を自動集計し、月初にメールで届くようにしました。手動で振り返る手間がなくなり、数字を見る習慣そのものができました。
この記事でわかること
- 月次振り返りをしていなかった頃の課題
- レポート自動生成の設計(売上・稼働時間・案件数)
- GASでの自動集計・メール送信の仕組み
- 初回レポートを送ってみて気づいたこと
前回の記事で、作業時間を集計して案件ごとの実質時給を出せるようになりました。今回は、この数字を毎月自動でまとめて届けてくれる仕組みを作ります。
月次振り返りをしていなかった頃の課題
案件ごとの時給は見えるようになりましたが、「今月トータルでどれくらい稼いだか」「どれくらい稼働したか」は、結局スプレッドシートを自分で開いて数字を拾わないとわかりませんでした。忙しい月ほど振り返りを後回しにしてしまい、気づけば数ヶ月分の集計をまとめてやる羽目になる、というのがよくあるパターンでした。
レポート自動生成の設計
「案件一覧(同期)」と「作業ログ(同期)」の2つのシートから、以下を月次で集計します。
- 対象月の案件数
- 対象月の合計稼働時間
- 対象月の売上合計
- 平均時給(売上 ÷ 稼働時間)
これを毎月1日の朝に自動計算し、メールで自分宛てに送るところまでを1セットにしました。
GASでの自動集計・送信の仕組み
function sendMonthlyReport() {
const { start, end, label } = getPreviousMonthRange_();
const projects = getProjectSheetData_();
const worklogs = getWorklogSheetData_();
const monthlyWorklogs = worklogs.filter(w => w.date >= start && w.date < end);
const totalMinutes = monthlyWorklogs.reduce((sum, w) => sum + w.minutes, 0);
const totalHours = totalMinutes / 60;
const projectNames = [...new Set(monthlyWorklogs.map(w => w.project))];
const monthlyProjects = projects.filter(p => projectNames.includes(p.name));
const totalRevenue = monthlyProjects.reduce((sum, p) => sum + (p.amount || 0), 0);
const avgHourlyRate = totalHours > 0 ? Math.round(totalRevenue / totalHours) : 0;
const body = `${label}のレポート
案件数: ${monthlyProjects.length}件
稼働時間: ${totalHours.toFixed(1)}時間
売上合計: ${totalRevenue.toLocaleString()}円
平均時給: ${avgHourlyRate.toLocaleString()}円`;
GmailApp.sendEmail('自分のメールアドレス', `【ツクリログ】${label}の月次レポート`, body);
}
function getPreviousMonthRange_() {
const now = new Date();
const start = new Date(now.getFullYear(), now.getMonth() - 1, 1);
const end = new Date(now.getFullYear(), now.getMonth(), 1);
const label = `${start.getFullYear()}年${start.getMonth() + 1}月`;
return { start, end, label };
}getProjectSheetData_とgetWorklogSheetData_は、前回・前々回作った同期シートを読み込んで配列に変換するだけの関数なので、詳細は割愛します。トリガーは「時間主導型・月タイマー・1日」で設定しています。
つまずいたポイント:月初の日付判定で、最初は1日分だけ集計から漏れる不具合がありました。原因はGASプロジェクトのタイムゾーン設定がUTCのままで、日本時間との9時間のズレで日付の境界がずれていたことです。appsscript.jsonのtimeZoneをAsia/Tokyoに明示することで直りました。
初回レポートを送ってみて気づいたこと
実際に届いたレポートを見て、稼働時間より売上の増減の方が自分の体感とズレていることに気づきました。稼働時間はほぼ横ばいなのに売上は思ったより変動していて、単価の低い案件に時間を使いすぎていた月がある、ということが数字で初めて見えました。まだ1回分のデータなので、これが傾向なのかは来月以降も見ないと判断できません。
よくある質問
メールではなくSlackに送ることはできますか?
GmailApp.sendEmailの部分を、以前の記事で使ったSlack Webhookへの送信処理に差し替えれば対応できます。
月の途中で経過を確認したい場合はどうすればいいですか?
getPreviousMonthRange_の範囲を「今月の1日から今日まで」に変更した関数を別に用意すれば、同じ仕組みで月中の速報版が作れます。
メール送信に失敗することはありますか?
GASのGmailApp送信には1日あたりの送信数上限があります。個人利用の範囲であれば通常は問題になりません。
次回は、複数クライアントの締切をスプレッドシートで自動アラート化する仕組みを作ります。
