Zoho CRMで「月次レポートを蓄積する」仕組みの作り方

Zoho CRMのレポートは、開くたびに最新データで再計算されます。これはリアルタイムに現状を見るには便利ですが、「毎月のレポートを残していきたい」という要望とは相性がよくありません。ステータスが動けば過去月の数字も変わってしまい、「◯月版」として作ったはずのレポートが、いつの間にか別の数字になっているからです。
先日、Salesforceから移行したZoho CRM環境で「月別の成約率レポートを月ごとに蓄積したい」という要件をいただき、標準機能の範囲でこれを実現する構成を組みました。この記事では、その仕組みを汎用化して紹介します。
結論:日付をそのまま使わず、「年」と「月」を項目として持たせる
構成はシンプルで、次の3点セットです。
- 集計の基準日から「年(数値)」「月(選択リスト)」を自動転記する項目を作る
- レポートは期間をフィルターで固定し、「[2026年8月] ◯◯レポート」のように期間ごとに複製して保存する
- 集計から外したいレコードは除外用チェックボックスで運用者がコントロールする
「蓄積」というと外部への書き出しやスナップショット用モジュールを考えがちですが、多くのケースでは「期間を固定したレポートを複製で残す」だけで要件を満たせます。ポイントは、期間の固定をやりやすくするデータ側の下ごしらえです。
なぜ日付フィルターだけではだめなのか
レポートの日付フィルターには「今月」「先月」などの相対指定と、開始日〜終了日のカスタム指定があります。相対指定は毎月自動で切り替わるので定点観測用には便利ですが、「◯月版を残す」用途には使えません。カスタム指定で毎月日付を打ち替えるのは手間ですし、四半期や半期のような期間になると年度またぎの指定が煩雑になります。
さらに、Zoho CRMのレポートは日付のグループ化単位が限られており、たとえば「半期」でのグループ化は標準機能にありません。日付項目のままでは、期間の切り出しに柔軟性がないのです。
手順1:年・月の転記項目を作る
集計の基準日(問い合わせ日、商談日、活動日など、レポートの軸になる日付)から、次の2項目に値を転記します。
- 集計年:数値項目(例:2026)
- 集計月:選択リスト項目(1月〜12月)
月を選択リストにしているのは、フィルターで「4月・5月・6月」のように複数選択で四半期を表現できるからです。年は長期運用を考えて数値にしておくと、「2025〜2026」のような範囲指定もできます。
転記の自動化には**ワークフロー+カスタム関数(Deluge)**を使います。当初はClient Script(画面上のJavaScript)での転記も試しましたが、詳細画面のインライン編集では保存対象に含まれず値が入らない、インポートやAPI経由の更新には反応しない、という穴がありました。ワークフロー起点の関数なら、画面入力・インライン編集・インポート・API・Delugeのどの経路でも拾えます。
// ワークフロー(作成/編集・繰り返し実行オン)から呼び出す関数のイメージ
rec = zoho.crm.getRecordById("モジュール名", recordId);
d = rec.get("基準日のAPI名");
m = Map();
if(d != null)
{
ds = d.toString(); // "2026-08-15" 形式
m.put("集計年のAPI名", ds.subString(0,4).toLong());
m.put("集計月のAPI名", ds.subString(5,7).toLong().toString() + "月");
}
else
{
m.put("集計年のAPI名", null);
m.put("集計月のAPI名", null);
}
zoho.crm.updateRecord("モジュール名", recordId, m);
関数側で基準日を書き換えない限りワークフローがループすることはありません。また、Delugeタスク経由の更新は既定でワークフローを発火させないため、後述の一括転記が自動化を巻き込む心配もありません。
手順2:既存レコードへ一括転記する
新しく作った項目は当然ながら既存レコードでは空です。スタンドアロン関数で、基準日を読み取って年・月を一括投入します。
updatecount = 0;
for each pg in {1,2,3,4,5,6,7,8,9,10}
{
recs = zoho.crm.getRecords("モジュール名", pg, 200, {"sort_by":"id","sort_order":"asc"});
updlist = List();
for each r in recs
{
d = r.get("基準日のAPI名");
if(d != null && r.get("集計年のAPI名") == null)
{
ds = d.toString();
m = Map();
m.put("id", r.get("id"));
m.put("集計年のAPI名", ds.subString(0,4).toLong());
m.put("集計月のAPI名", ds.subString(5,7).toLong().toString() + "月");
updlist.add(m);
}
}
// 100件ずつ zoho.crm.bulkUpdate() で更新
}
実装上のポイントは2つあります。
- ページングのソートをIDで固定すること。既定の並び順は更新日時の影響を受けるため、更新しながらページを進めると取りこぼしが発生し得ます
- 「未転記のレコードのみ」を条件にすること。冪等になり、途中で止まっても再実行するだけで続きから処理できます
なお、一括更新は全レコードの「最終更新日時」「最終更新者」を書き換えます。更新日時を運用に使っている場合は、実施前に関係者へ共有しておくべきです。
手順3:レポートを期間フィルターで固定して複製する
ここまで来れば、レポート側は普通のフィルター操作だけです。
- 詳細フィルター:
集計年 = 2026かつ集計月 = 8月(+対象の絞り込みや除外条件) - レポート名:
[2026年8月] ◯◯レポート
翌月は複製して月の条件を変えるだけ。四半期版は 集計月 = 4月・5月・6月、半期版は6か月分の複数選択で表現できます。年度またぎの下期も、条件パターン(AND/OR の組み合わせ)で「(年=2025 かつ 月=10〜12月) または (年=2026 かつ 月=1〜3月)」と1本で書けます。
現行のレポートはフィルター条件を25件まで組めるので、除外条件が多少増えても数式項目に逃がす必要はありません。
補足1:率の集計は「0/1フラグの平均」で
成約率のような割合をレポートに出したい場合は、判定用の数式項目を作るのが定番です。
- 数式:
If(ステータスが成約系, 1, 0) - レポートの集計:件数(分母)/フラグの合計(分子)/フラグの平均(=率)
注意点として、数式項目は作成・変更しても既存レコードには自動反映されません。数式の参加項目を同値で再保存する一括処理(手順2と同じ要領)が必要です。ここはZohoの仕様として押さえておくと、「フラグが機能しない」と悩む時間を節約できます。
補足2:除外はチェックボックスに寄せる
「既存取引先は除外したい」「テストデータは除外したい」といった除外要件は、判定ロジックをフィルターに詰め込むより、除外用チェックボックス1個に寄せる方が運用が安定します。
- フィルターは
除外フラグ = チェックなしの1条件で済む - 除外基準が変わっても、レポートを触らず運用側でチェックを付け替えるだけ
- 「なぜこのレコードが集計に入っていないのか」がレコードを見れば分かる
判定材料がデータとして揃っていない段階では特に有効で、レポートの構造と除外運用を分離できます。
ハマりどころまとめ
最後に、この構成を組む中で実際に踏んだ落とし穴を並べておきます。
- 数式項目・数式変更は既存レコードに反映されない → 参加項目の同値再保存で再計算
- Client Scriptはインライン編集・インポートに反応しない → 転記はワークフロー+関数に寄せる
- 数式項目は作成時に標準レイアウトにしか配置されない → 複数レイアウト運用では未配置レイアウトのレコードが計算されない
- 一括更新のページングは並び順を固定する →
sort_by: idを明示 - レポートは前回実行時の結果を表示する → データ更新後は再実行(↻)が必要
- 月の選択リストは文字列ソートに注意 → グループ化の並びが崩れる場合は表示順を確認
まとめ
「レポートを蓄積したい」という要望は、突き詰めると「期間を固定したレポートを簡単に量産できる状態にしたい」ということです。基準日から年・月を転記する項目とワークフローを一度仕込んでしまえば、月次・四半期・半期・年次のどの粒度でも同じ作法で切り出せるようになります。外部ツールや有償オプションを足す前に、まずこの構成を検討してみてください。