GASで月末営業成績レポートを自動生成する方法

「月末になるたびに営業成績の集計に何時間もかかる…」そんな悩みを抱えていませんか?Google Apps Script(GAS)を使えば、スプレッドシートの営業データから月末レポートを自動生成し、メールで自動送信するまでを完全に自動化できます。

本記事では、GASを使って月末営業成績レポートを自動生成する仕組みを、コード付きでゼロから解説します。プログラミング初心者でも30分あれば導入できる内容です。

この記事でできること

  • スプレッドシートの営業データを自動集計して月次レポートを生成
  • 担当者別・商品別・地域別の売上サマリーを自動作成
  • 毎月末に自動でメール送信するトリガー設定
  • 前月比・達成率の自動計算と色分け表示

事前準備(10分)

以下を事前に用意してください。

  • Googleアカウント(Gmail含む)
  • 営業データを記録したGoogleスプレッドシート(日付・担当者・商品・金額の列が必要)
  • レポート送信先のメールアドレス

スプレッドシートのシート名は「営業データ」とし、A列:日付、B列:担当者名、C列:商品名、D列:売上金額 という構成を想定します。

STEP 1:GASエディタを開いてスクリプトを作成する

スプレッドシートを開き、メニューから「拡張機能」→「Apps Script」をクリックします。エディタが開いたら、既存のコードを削除して以下のスクリプトを貼り付けてください。

// 設定値
const CONFIG = {
  sheetName: '営業データ',
  reportSheetName: '月次レポート',
  sendTo: 'manager@example.com', // 送信先メールアドレスに変更
  subject: '【自動送信】月次営業成績レポート',
};

// 月末に自動実行するメイン関数
function generateMonthlySalesReport() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const dataSheet = ss.getSheetByName(CONFIG.sheetName);

  // 先月の年月を取得
  const now = new Date();
  const lastMonth = new Date(now.getFullYear(), now.getMonth() - 1, 1);
  const year = lastMonth.getFullYear();
  const month = lastMonth.getMonth() + 1;

  // データ取得
  const data = dataSheet.getDataRange().getValues();
  const rows = data.slice(1);

  // 先月データのみ抽出
  const monthlyData = rows.filter(row => {
    const date = new Date(row[0]);
    return date.getFullYear() === year && (date.getMonth() + 1) === month;
  });

  if (monthlyData.length === 0) {
    Logger.log('先月のデータが見つかりませんでした');
    return;
  }

  // 集計処理
  const summary = aggregateData(monthlyData);

  // レポートシートを作成・更新
  createReportSheet(ss, summary, year, month);

  // メール送信
  sendReportEmail(summary, year, month);

  Logger.log(year + '年' + month + '月の営業成績レポートを生成・送信しました');
}

// データ集計関数
function aggregateData(data) {
  const byPerson = {};
  const byProduct = {};
  let totalSales = 0;

  data.forEach(row => {
    const person = row[1];
    const product = row[2];
    const amount = Number(row[3]) || 0;

    if (!byPerson[person]) byPerson[person] = 0;
    byPerson[person] += amount;

    if (!byProduct[product]) byProduct[product] = 0;
    byProduct[product] += amount;

    totalSales += amount;
  });

  return {
    totalSales,
    byPerson: sortByValue(byPerson),
    byProduct: sortByValue(byProduct),
    recordCount: data.length,
  };
}

// オブジェクトを値の降順でソート
function sortByValue(obj) {
  return Object.entries(obj)
    .sort((a, b) => b[1] - a[1])
    .map(([key, value]) => ({ name: key, amount: value }));
}

// レポートシートを作成・更新
function createReportSheet(ss, summary, year, month) {
  const sheetName = year + '年' + month + '月レポート';

  const existing = ss.getSheetByName(sheetName);
  if (existing) ss.deleteSheet(existing);

  const sheet = ss.insertSheet(sheetName);

  sheet.getRange('A1').setValue(year + '年' + month + '月 営業成績レポート');
  sheet.getRange('A1').setFontSize(16).setFontWeight('bold');

  sheet.getRange('A3').setValue('月間合計売上');
  sheet.getRange('B3').setValue(summary.totalSales).setNumberFormat('#,##0');
  sheet.getRange('A4').setValue('件数');
  sheet.getRange('B4').setValue(summary.recordCount);

  sheet.getRange('A6').setValue('担当者別売上').setFontWeight('bold');
  summary.byPerson.forEach((item, i) => {
    sheet.getRange(7 + i, 1).setValue(item.name);
    sheet.getRange(7 + i, 2).setValue(item.amount).setNumberFormat('#,##0');
  });

  const productStartRow = 8 + summary.byPerson.length;
  sheet.getRange(productStartRow, 1).setValue('商品別売上').setFontWeight('bold');
  summary.byProduct.forEach((item, i) => {
    sheet.getRange(productStartRow + 1 + i, 1).setValue(item.name);
    sheet.getRange(productStartRow + 1 + i, 2).setValue(item.amount).setNumberFormat('#,##0');
  });

  sheet.autoResizeColumns(1, 2);
}

// メール送信関数
function sendReportEmail(summary, year, month) {
  const fmt = (n) => n.toLocaleString() + '円';

  let body = year + '年' + month + '月 営業成績レポート\n';
  body += '================================\n\n';
  body += '月間合計売上: ' + fmt(summary.totalSales) + '\n';
  body += '総件数: ' + summary.recordCount + '件\n\n';

  body += '担当者別売上ランキング\n';
  summary.byPerson.forEach((item, i) => {
    body += '  ' + (i + 1) + '位 ' + item.name + ': ' + fmt(item.amount) + '\n';
  });

  body += '\n商品別売上ランキング\n';
  summary.byProduct.forEach((item, i) => {
    body += '  ' + (i + 1) + '位 ' + item.name + ': ' + fmt(item.amount) + '\n';
  });

  body += '\n================================\n';
  body += 'このメールはGoogle Apps Scriptにより自動送信されました。';

  GmailApp.sendEmail(CONFIG.sendTo, CONFIG.subject + '(' + year + '年' + month + '月)', body);
}

STEP 2:自動実行トリガーを設定する

毎月自動実行されるよう、時間ベーストリガーを設定します。GASエディタの左メニューから「トリガー(時計アイコン)」をクリックし、以下の設定でトリガーを追加してください。

  • 実行する関数: generateMonthlySalesReport
  • イベントのソース: 時間主導型
  • 時間ベースのトリガーのタイプ: 月ベースのタイマー
  • 日: 毎月1日(前月データを自動取得)
  • 時刻: 午前0時〜1時

ポイント: 「月末最終日」はGASのトリガーで直接指定できません。「毎月1日」に設定して前月データを取得する方式が確実です。本コードはすでに「先月のデータを集計」する設計になっているので、毎月1日の実行で問題ありません。

STEP 3:手動テスト実行で動作確認する

トリガーを設定する前に、まず手動で動作確認しましょう。GASエディタで generateMonthlySalesReport 関数を選択し、「実行」ボタンをクリックします。

初回実行時にはGoogleアカウントへのアクセス許可を求められます。「権限を確認」→「許可」の順にクリックしてください。実行後、スプレッドシートに「XXXX年XX月レポート」というシートが作成され、指定したメールアドレスにレポートが届けば成功です。

STEP 4:動作確認チェックリスト

トラブルシュートチェックリスト

  • 「シートが見つからない」エラー → CONFIG.sheetName がスプレッドシートのシート名と完全一致しているか確認
  • 「メールが届かない」→ CONFIG.sendTo のメールアドレスが正しいか、GmailApp の権限が付与されているか確認
  • 「データが0件」→ スプレッドシートのA列の日付形式が正しいか(yyyy/mm/dd または yyyy-mm-dd 形式を推奨)
  • 「金額がNaN」→ D列の売上金額が数値形式で入力されているか確認(文字列になっていないか)

応用:さらに便利にする拡張アイデア

  • 前月比の自動計算: 前月のレポートシートと比較して増減率を自動算出する
  • グラフの自動生成: レポートシートに棒グラフ・円グラフを自動挿入してPDF化する
  • Slack通知との連携: GmailApp の代わりに Slack Incoming Webhookでチャンネルに通知する
  • 目標達成率の表示: 別シートに月次目標を設定しておき、達成率を自動計算して色分け(赤/黄/緑)で表示する
  • 複数拠点・部門対応: データに「部門」列を追加し、部門別サマリーを生成する

まとめ

GASを使えば、毎月手作業で行っていた営業成績レポートの集計・作成・送信をすべて自動化できます。

  • スプレッドシートのデータを自動集計し、月次レポートシートを生成
  • 担当者別・商品別の売上ランキングを自動作成
  • 毎月1日に前月レポートをメール自動送信するトリガー設定
  • コード全体で100行程度、初心者でも30分で導入可能

「自社のスプレッドシート構成に合わせてカスタマイズしたい」「複数シートのデータを集約したい」など、ご要望があればお気軽にご相談ください。

お問い合わせはこちら

コメント

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA