[Apex]レポートの集計値を取得する方法


この記事は Lancers(ランサーズ) Advent Calendar 2020 14日目のエントリーです。

はじめに

ランサーズ株式会社でSalesforce管理者兼開発者を担当している久保です。
4月に新卒として入社し、6月ごろからSalesforceチームメンバーとして、ゴリゴリ開発させていただいております。(ランサーズでのSalesforce運用の歴史はコチラ

さて、今日はApexでレポートデータを取得する過程を深ぼろうと思います。
というのも、「公式のApex開発者ガイドに取得方法が載っているが、一体どういうロジックなのか分からない」という人の助けになれたらと思ったためです。

レポートの集計値を取りたいと思ったキッカケは、日次の売上金額推移をSlack通知したいと思ったことからです。レポート作成スナップショットで日次の金額をレコードに保存し、レポートでそれらのレコードの金額集計を弾き出して、Slack通知させるロジックを書くときに必要になりました。

前提

今回の解説では、下記レポートの枠部分の値を取得することを目標とします。
つまり、フェーズ別商談金額レポートのフェーズがClosedWonの時の金額合計値を取得します
(下記はTrailheadのPlayground組織のテストデータです。詳細行はカットしてます)

取得手順

①抽出対象のレポートからIdを取得、実行
②レポートの最初のダウングルーピングを取得
③ファクトマップの取得
④取りたいサマリー値の取得

//①抽出対象のレポートからIdを取得、実行
List <Report> reportList = [SELECT Id,DeveloperName FROM Report where DeveloperName = 'new_report_mdR'];
String reportId = (String)reportList.get(0).get('Id');
Reports.reportResults reportResults = Reports.ReportManager.runReport(reportId, true);

//②レポートの最初のダウングルーピングを取得
Reports.Dimension dim = reportResults.getGroupingsDown();
Reports.GroupingValue groupingVal = dim.getGroupings()[8];

//③ファクトマップの取得
String factMapKey = groupingVal.getKey() + '!T';
Reports.ReportFactWithDetails factDetails = (Reports.ReportFactWithDetails)reportResults.getFactMap().get(factMapKey);

//④取りたいサマリー値の取得
 Reports.SummaryValue sumVal = factDetails.getAggregates()[1];

①〜④でそれぞれ何をしているのかを以下で解説します。

①抽出対象のレポートからIdを取得、実行

List <Report> reportList = [SELECT Id,DeveloperName FROM Report where DeveloperName = 'new_report_mdR'];
String reportId = (String)reportList.get(0).get('Id');
Reports.reportResults reportResults = Reports.ReportManager.runReport(reportId, true);

まず、SOQL文で取得対象のレポートの抽出をします。
ここで条件にあるDeveloperName とは「レポートの一意の名前」のことを指します。
下記キャプチャのように、レポート編集画面のプロパティで確認できます

そして、取得したリストからget(0).get('Id')でレポートのIDを取得します。
取得条件により、リストの中の要素は唯一つですので、get(0)で対象のレポートを選択できます。

次に、ReportManagerクラスのrunReportメソッドにより、レポートを実行します。
ただし、2番目の引数は詳細行を含むか含まないかの選択を意味します。
(※因みにrunAsyncReportメソッドというのもあり、非同期にレポート実行させることもできます)

②レポートの最初のダウングルーピングを取得

Reports.Dimension dim = reportResults.getGroupingsDown();
Reports.GroupingValue groupingVal = dim.getGroupings()[8];

今度は、①のrunRepotメソッドにより実行した結果からグルーピングの情報を抽出します。
具体的にはrepotrResultsクラスのgetGroupingsDownメソッドを用いて、ダウングルーピング(=行グルーピング)の情報を抜き取ります。(※因みに、getGroupingsAcrossメソッドで列グルーピングの情報を取得できます)

実際、System.debug(dim)で中身を見てみると、確かに行グルーピングしたフェーズの情報が順に書いてあります。

DEBUG|Reports.Dimension[groupings=(Reports.GroupingValue[groupings=null, key=0, label=Prospecting, value=Prospecting], Reports.GroupingValue[groupings=null, key=1, label=Qualification, value=Qualification], Reports.GroupingValue[groupings=null, key=2, label=Needs Analysis, value=Needs Analysis],,,,

そして、DimensionクラスのgetGroupingメソッドで、取得したグルーピング情報をリストとして受け取ります。
今は、取得したい値はレポートのグルーピングの上から9番目の項目である、ClosedWonの時の値なのでdim.getGroupings()[8]とすれば、ClosedWonのキーやラベルが手に入ります。

③ファクトマップの取得

String factMapKey = groupingVal.getKey() + '!T';
Reports.ReportFactWithDetails factDetails = (Reports.ReportFactWithDetails)reportResults.getFactMap().get(factMapKey);

さて、GroupingValue クラスのgetKeyメソッドで、行グルーピングのキーを取得し、ファクトマップキーを作成します。
ここでファクトマップキーとは、「どのグルーピング項目か」を表すキーのことを言います

例えば、0!Tなら「第1グルーピングの最初の項目」、1!Tなら「第1グルーピングの2番目の項目」、0_1!Tなら「第1グルーピングの最初の項目と第2グルーピングの2番目の項目」のことを指します。

このファクトマップキーを用いて、reportResults.getFactMapメソッドでファクトマップを取得します。
取得したファクトマップは以下です。確かに、ClosedWonの時の小計値である期待収益や金額が並んでいます

Reports.ReportFactWithDetails[aggregates=(Reports.SummaryValue[label=¥3,645,000, value=3645000.000000000000000000], Reports.SummaryValue[label=¥3,645,000, value=3645000.000000000000000000], Reports.SummaryValue[label=18, value=18]), key=8!T, rows=(Reports.ReportDetailRow[dataCells=(Reports.ReportDataCell[label=Express Logistics and Transport, value=0012r000006VpxRAAS], Reports.ReportDataCell[label=Express Logistics Standby Generator, value=0062r000003ymAnAAI],,,

④取りたいサマリー値の取得

 Reports.SummaryValue sumVal = factDetails.getAggregates()[1];

最後に、ファクトマップから取りたい小計値を取得します。
ReportFactWithDetailsクラスのgetAggregatesメソッドで小計値のリストを取得します。
そして、今回の場合、レポートの左から2番目の行項目である「金額」を取得したいので、リストの2番目を指定すれば、完了となります。

最後に

レポートの集計値取得はフロー等ではできない(と思われる)ため、Apexを駆使することになります。
しかし、取得したい集計値は特定可能な数個になるかと思います。そのため、フローのApexアクションで呼び出すことができるので、取得結果を活用する事に限っては、十分フローが使えます。

以上の事柄がSalesforce開発者の方々の役に立てば本望です。
最後までお読みいただきありがとうございました。

参考

Apex開発者ガイド レポートデータの取得
Apex開発者ガイド ファクトマップの復号化