この記事は 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開発者の方々の役に立てば本望です。
最後までお読みいただきありがとうございました。