株式会社etika(エティカ)CRMサポートセンターです。
週報メールの集計ズレとは、Zoho CRMから自動送信している定期レポートの数字が、実際の件数と合わなくなる不具合です。この記事では、起業支援メディア運営企業のZoho CRMで起きた「今月累計:0件」の事例をもとに、原因の切り分け方と直し方を説明します。
この会社では、サービスを契約している顧客ごとに、毎週「先週の件数」「今月累計」「先月の件数」をメールで送っています。送信はDeluge(デリュージ:Zohoの独自スクリプト言語。基本ははじめてのDelugeで紹介しています)のスケジュール関数です。契約を更新した月に、その顧客へ「今月累計:0件」と届く問題が起きました。
結論(先に要点)
- 集計を「月次の計上レコードの終了日が今月か」で判定していたため、契約更新の月に0件になった
- 件数の元になるログから、日付の範囲で直接数えるように変えると、契約更新の影響を受けにくくなる見込み
- ログから数えると旧案件・新案件の両方に同じ数字が出るので、送信先を決めるフラグも合わせて用意する
症状:契約を更新した顧客だけ「今月累計0件」になる
0件になるのは、契約更新の月に旧案件が送信対象に残るケースです。
この会社では、契約の単位ごとに「案件」(Deals)のレコードを作ります。契約を更新すると、新しい契約期間の案件を別に作ります。案件には、月ごとの売上と件数を持つ「月次の計上レコード」(カスタムモジュール)が関連リストでぶら下がっています。
契約更新の月は、データが次の状態になっていました。
| ステータス | 紐づく計上レコード | |
|---|---|---|
| 旧案件 | 提供中(週報の送信対象) | 前月分まで |
| 新案件 | まだ提供中ではない(送信対象外) | 今月分から |
週報は「ステータスが提供中の案件」に送ります。送信対象は旧案件です。ところが、旧案件には今月の計上レコードがありません。今月の件数は新案件の計上レコード側にあります。その結果、旧案件の「今月累計」は0件になっていました。
原因:集計の基準が「計上レコードの終了日」だった
週報の関数は、今月累計を次の手順で出していました。
- 送信対象の案件(ステータスが提供中)をCOQLで取得する
- 案件に紐づく計上レコードを関連リストで取得する
- 計上レコードの終了日が今月のものだけを選び、その「消化数」を足す
コードにすると、集計部分はこの形です(項目名は一般的な名前に置き換えています)。
// 変更前:計上レコードの終了日で今月分かどうかを判定していた
thisMonthStr = zoho.currentdate.toString("yyyy-MM");
thisMonthCount = 0;
for each rec in relatedRecords
{
endDate = rec.get("End_Date");
if(endDate != null && rec.get("Consumed_Count") != null)
{
if(endDate.toString("yyyy-MM") == thisMonthStr)
{
thisMonthCount = thisMonthCount + rec.get("Consumed_Count").toNumber();
}
}
}
この作りだと、次のどれかに当てはまると「今月累計」が0件になります。
- 送信対象の案件に、終了日が今月の計上レコードがない(契約更新の月はこれに当たる)
- 今月の計上レコードはあるが、消化数が空、または0のまま
- 今月の計上レコードはあるが、終了日が空
問題は「案件の状態(提供中かどうか)」と「集計の基準(計上レコードの終了日)」が別々に動いていたことです。普段の月は両方がそろっているので、ずれに気づけません。契約更新の月だけ、ずれが表に出ます。
同じ理由で「先月の件数」も、新案件に切り替わった翌月は0件になり得ます。新案件には先月分の計上レコードがないためです。
「今月累計0件」の原因はどう切り分けるか
同じような症状が出たら、次の順番で確かめると早く原因にたどり着けます。
- 0件の顧客に共通点がないかを見る。 契約更新・プラン変更・担当の付け替えなど、レコードが入れ替わる操作が直前にあったかを確認します
- 送信対象になった案件のIDをログで確かめる。 週報の関数に
infoで案件IDを出しておくと、どの案件の数字が送られたかが分かります - その案件に、今月分の集計元のレコードがあるかを見る。 関連リストに今月のレコードがなければ、集計の基準がずれています
- 今月分のレコードがあるなら、件数の項目が空や0になっていないかを見る。 入力や更新の仕組みが別にあるなら、そちらが止まっていないかも確認します
- 件数の元になるログを、同じ期間で直接数えてみる。 ここで件数が出るなら、データはあり、集計の経路だけが問題だと切り分けられます
今回の事例では、コードを読んで3の状態(旧案件に今月の計上レコードがない)になっていることを確かめました。5は、直した後の確認としても使えます。
直し方:件数の元になるログから直接数える

ここで示すコードは、当社が同じ会社で組んだ別の関数(先週分の件数を集計する関数)と同じ方式で書いた例です。週報の関数にこの形で組み込んで本番で動かした結果は、まだ確かめていません。お客様の環境で動作確認してから使ってください。
計上レコードの消化数は、もともと「件数のログ」を月ごとにまとめた値です。そこで、計上レコードを経由せず、ログのモジュールから直接数えるように変えます。今回の会社でも、別の関数(先週の件数を案件に書き込む関数)はすでにログから数えていました。週報の「今月累計」だけが計上レコード経由になっていたのです。
前提として、ログのモジュールに次の項目があるとします。
| 項目(API名) | 中身 |
|---|---|
Item_Name |
どの提供物への件数か(案件と結びつけるキー) |
Requested_At |
発生日時(日時型) |
Is_Excluded |
集計から外すログのフラグ(重複・取り消しなど) |
手順
- 週報の関数で、今月1日から今日までの範囲を日時の文字列にする
- 案件から、ログと結びつけるキー(ここでは提供物の名前)を取り出す
- COQLでログを200件ずつ取得し、件数を足していく
more_recordsが false になったら止める- 足した件数を「今月累計」としてメール本文に入れる
コード
週報の関数のうち、今月累計を出していた部分を、次のコードに置き換える例です。itemName には案件から取り出したキーが入ります。文字列をつないでクエリを作っているため、名前に '(引用符)が含まれるとクエリが壊れます。名前に引用符が入りうる場合は、ルックアップのIDで絞る形にしてください。connection には、CRMのモジュールを読める接続(スコープ ZohoCRM.coql.READ と、対象モジュールの読み取り)を指定してください。URLの zohoapis.jp は、お使いのデータセンターに合わせて変えます。
// 変更後:件数のログから、今月1日〜今日の件数を直接数える
today = zoho.currentdate;
fromIso = today.toStartOfMonth().toString("yyyy-MM-dd'T'00:00:00+09:00");
toIso = today.toString("yyyy-MM-dd'T'23:59:59+09:00");
thisMonthCount = 0;
offset = 0;
pages = {1,2,3,4,5,6,7,8,9,10}; // 200件 × 10ページ = 最大2,000件まで数える
for each p in pages
{
query = "select id from Request_Logs where (((Item_Name = '" + itemName + "') and (Is_Excluded = false)) and ((Requested_At >= '" + fromIso + "') and (Requested_At <= '" + toIso + "'))) limit 200 offset " + offset;
queryMap = Map();
queryMap.put("select_query", query);
resp = invokeurl
[
url: "https://www.zohoapis.jp/crm/v8/coql"
type: POST
parameters: queryMap.toString()
connection: "crm_conn"
];
// 該当が0件のときは data が返らないため、ここで抜ける
if(resp == null || resp.get("data") == null)
{
break;
}
rows = resp.get("data");
thisMonthCount = thisMonthCount + rows.size();
offset = offset + rows.size();
if(resp.get("info") == null || resp.get("info").get("more_records") != true)
{
break;
}
}
info itemName + " の今月累計: " + thisMonthCount;
ポイントは3つです。
- Delugeにはwhile文がないため、決まった数の要素を持つリストで回します。 上の例は最大10ページです。1か月の件数がそれを超える見込みなら、要素を増やしてください
- 0件のときの扱いを先に書きます。 当社の環境では、該当がないとき、COQLは本文のない応答を返しました。
dataが取れないことを前提に、0件として抜けるようにします(ウィジェットから同じ現象に当たった例はCOQLの件数制限と空応答(204)の落とし穴で紹介しています) - 提供物の名前など文字列をキーにする場合は、表記ゆれに注意します。 名前を変えるとログと結びつかなくなります。ルックアップ(他のレコードへの参照)でつなげられるなら、IDで絞るほうが安全です
COQLの1回の取得件数は、公式ドキュメントでは既定200件・最大2,000件とされています(COQL Overview)。件数だけが欲しい場合は COUNT() を使う方法もあります。ただし、集計関数を使える項目の種類には制限があると書かれているため、対象の項目で使えるか確かめてから採用してください(Get Records through COQL)。
ログから数えると、どちらの案件に送ればよいか
ログから直接数えると、別の問題が出てきます。契約更新の月に、旧案件と新案件の両方が「提供中」になっていると、両方に同じ件数が出て、週報が2通届きます。提供物の名前が同じなら、どちらから数えても同じ数字になるためです。
そこで、送信先を決める項目を案件に足します。
| 項目(API名) | 型 | 使い方 |
|---|---|---|
Is_Report_Target |
チェックボックス | 週報を送る案件だけオン。契約更新時に旧案件をオフ、新案件をオンにする |
Previous_Deal |
ルックアップ(案件) | 新案件から旧案件をたどれるようにする |
送信対象を取るCOQLに、このフラグを条件として足します。
queryMap = Map();
queryMap.put("select_query", "select id, Account_Name, Item_Name from Deals where ((Stage = '提供中') and (Is_Report_Target = true))");
提供物の名前が契約更新で変わる場合は、旧案件の名前も新案件から数える対象に入れます。Previous_Deal をたどって旧案件のキーを取り出し、上の集計をキーの数だけ回して合算します。
フラグの切り替えは人の操作です。切り替え忘れを防ぐには、新案件のステータスが「提供中」になったときに、旧案件のフラグを自動でオフにするワークフローを用意しておく方法もあります(当社の提案段階の案です)。
運用で補うこと
集計の基準を直したうえで、運用でも次の2点を決めておくと、再発を防ぎやすくなります。
- 契約更新の手順に「ステータスとフラグの切り替え」を入れる。 旧案件は契約終了日で提供中から外す、新案件は開始日で提供中にする、をチェックリストにします
- 月初の1回目の週報の前に、二重送信と0件がないかを見る。 送信対象の案件と件数を
infoで出し、件数が0の案件だけ目で確かめます
本番に戻す前に何をテストするか
週報のように顧客へ直接届くメールは、修正後に必ず本番と同じデータで確かめます。送信先を自分のアドレスに差し替え、次の3パターンを用意して実行してください。
- 月初の実行:今月のログがまだ少ない(または0件)状態で、0件の扱いが正しいか
- 契約更新の月:旧案件と新案件が両方ある状態で、フラグがオンの1件だけに送られ、件数がログと一致するか
- 計上レコードがない案件:計上レコードを持たない案件でも、ログから件数が出るか
3パターンとも、メールの数字とログを直接数えた数字が合っていれば、送信先を元に戻します。
まとめ
週報の「今月累計0件」は、コードを読んだ限りでは、データが欠けていたのではなく、集計の経路が契約更新に追いつけていなかったことが原因でした。直し方は、ここで示した考え方とコード例を、実際のデータで確かめながら適用してください。
- 件数は、まとめた値(計上レコード)ではなく、元のログから数える
- 送信先は、ステータスだけでなくフラグで明示的に決める
- 顧客に届く数字は、普段と違う月(月初・契約更新)でテストする
自動送信のレポートは、作った時点の業務の流れを前提にしています。契約の形や更新の手順が変わったときは、集計の基準も合わせて見直してください。日付を基準に動く自動処理の別の落とし穴は、日付ベースのワークフローが発火しないときの代わりの設計で紹介しています。
CRMサポートセンターでは、Zoho CRMの自動化(Delugeの関数・ワークフロー)の不具合調査や改修をお手伝いしています(Zoho CRMカスタマイズ・設定代行サービス)。「数字が合わない」「どこで止まっているか分からない」といったご相談も、お気軽にお寄せください。
よくある質問
計上レコードの「消化数」を毎月きちんと入れれば、今の作りのままでも直りますか?
当月の計上レコードが旧案件に紐づいていない限り、入力しても0件のままです。契約更新の月は計上レコードが新案件側にあるため、運用を徹底しても同じ形で再発します。集計の基準そのものを変えれば、再発を防げる見込みです。
COQLのCOUNTで件数だけ取れば、ページ送りは要らないのでは?
公式ドキュメントでは、COUNTなどの集計関数を使える項目の種類に制限が書かれています(数値・ルックアップ・選択リスト)。対象の項目で使えるかを確かめてから使ってください。本記事のコードは、レコードIDを取得して数える方法にしています。
旧案件のステータスを契約終了と同時に変えれば済みませんか?
ステータスの切り替えが毎回正しく行われれば再発は減ります。ただ、人の操作に依存するため、切り替え忘れや前後のずれで同じ症状が出ます。集計の基準を直したうえで、運用ルールは補助として使うのがおすすめです。




