Salesforce商談のフェーズと売上予測カテゴリ、後から「やらなければよかった」設計

後から「やらなければよかった」と思った設計:商談フェーズと売上予測カテゴリの密結合

当時の私は、Salesforceの標準機能を最大限に活用し、シンプルかつ保守性の高いシステムを構築することを目指していた。特にSales CloudのOpportunity Managementにおいては、商談フェーズ(Stage)と売上予測カテゴリ(Forecast Category)が標準で密結合している仕様を、そのまま利用することがベストプラクティスだと考えていた。つまり、商談フェーズを定義する際に、それに紐づく売上予測カテゴリを一つ一つ設定していく方式だ。

当時の判断

アーキテクトとして、この設計を採用した理由はいくつかあった。

  • シンプルさ:標準のデータモデルに従うことで、カスタム開発の必要がなく、システムの複雑性を最小限に抑えられる。
  • レポートの容易さ:Salesforce標準の売上予測レポートや、Report Builderで簡単にパイプラインレポートを作成できる。特別な結合や計算なしに、「Commit」「Best Case」といったカテゴリでの集計が可能だ。
  • 導入コストの削減:新しい概念を導入しないため、ユーザーへの教育コストも抑えられると見込んでいた。

具体的には、Setup -> Object Manager -> Opportunity -> Fields & Relationships -> Stage の設定画面で、各フェーズに対して「Closed Won」「Commit」「Best Case」「Pipeline」「Omitted」「Closed Lost」の中から売上予測カテゴリを割り当てていた。

密結合が引き起こした問題

しかし、実際の営業プロセスは、私の想定よりもはるかに複雑で多様だった。密結合の設計は、ビジネスの変化や細かい要件に対応しきれず、すぐに限界を迎えることになった。

1. 粒度の粗い売上予測カテゴリ

例えば、「Omitted(除外)」という売上予測カテゴリがある。当時は、「Closed Lost(失注)」と「Cancelled(キャンセル)」の両方のフェーズを「Omitted」に紐付けていた。「失注」は競合に負けた場合など営業活動の結果だが、「キャンセル」は顧客都合や社内都合で商談が中断された状態を指す。ビジネス上、この二つは全く異なる意味を持つにもかかわらず、売上予測カテゴリ上では同じ「Omitted」として扱われてしまった。

  • Closed Lost (失注): Omitted
  • Cancelled (キャンセル): Omitted

これにより、失注分析を行う際に、「Omitted」カテゴリで集計された商談が「失注」なのか「キャンセル」なのか、一目で判断できなくなってしまった。レポートでフィルタリングをかける際も、売上予測カテゴリだけでは不十分で、必ずフェーズ項目も参照する必要があり、二度手間が生じていた。

2. 営業担当者の混乱とデータの品質低下

フェーズと売上予測カテゴリが1対1で固定されているため、営業担当者は「このフェーズにすると、予測レポートではこのカテゴリになる」ということを意識せざるを得なかった。特に、微妙なニュアンスを持つフェーズ(例:「保留」「見送り」「ペンディング」など)が増えてきた際、どの売上予測カテゴリに紐付けるべきかという議論が頻繁に発生した。

「この商談は一旦『保留』だが、復活の可能性は低いから『Omitted』にしたい。でもフェーズは『保留』だから『Pipeline』に紐付けられている」といった不満が上がり、結果的に正確なフェーズ選択を怠ったり、意図的に間違ったフェーズを選択して予測レポートを調整しようとしたりするケースも散見された。これはデータの品質を著しく低下させる要因となった。

3. 変化するビジネス要件への対応不能

売上予測のロジックや集計方法自体が、事業の変化とともに変わることは珍しくない。例えば、ある期間だけ特定の種類の商談を「Omitted」から「Best Case」に一時的にカウントしたい、といった要件が出た際、フェーズと売上予測カテゴリが密結合していると、それを実現するには既存のフェーズ定義を変更するか、新たなフェーズを追加して無理やり対応するしかない。これは既存のプロセスにも影響を与え、アーキテクチャの柔軟性を著しく損なう。

当時の代替案と選ばなかった理由

当時、いくつかの代替案も検討した。例えば、商談にカスタムの「売上予測対象フラグ」のような選択リスト項目を持たせる案だ。これはフェーズとは独立して売上予測の対象とするかどうかを判断するもので、Report Builderでフィルタリングをかけることで、より柔軟な集計が可能になるはずだった。

しかし、この案は「標準機能の売上予測レポートが使えなくなる」「カスタム項目が増えることでシステムが複雑になる」「ユーザーへの教育コストが増える」といった理由で却下した。当時の私は、標準機能の維持を最優先しすぎていたのかもしれない。

また、Salesforceの「カスタム売上予測タイプ」の利用も検討したが、当時の私の理解とSalesforceのバージョンでは、そこまで複雑な予測ロジックを実装するほどの要件ではなかった、と判断した。実際、カスタム売上予測タイプは、組織の売上予測プロセスをより細かく調整できる機能だが、導入にはそれなりの設計とテストが必要になる。

今ならどう設計するか

もし今、同じような要件のプロジェクトにアーキテクトとして関わるなら、私はフェーズと売上予測カテゴリの密結合を避ける、あるいは緩やかにする設計を選ぶだろう。

  • 売上予測カテゴリの利用は最低限に留める:Salesforce標準の売上予測カテゴリは、あくまでSalesforceの基本機能に沿った大枠として捉え、ビジネス固有の細かい売上予測ロジックやステータスは、カスタム項目で定義する。
  • カスタムの「売上予測ステータス」項目を導入:商談オブジェクトに、フェーズとは別に「売上予測ステータス」のような選択リスト項目を設ける。これは、フェーズに応じて自動的に設定されるが、特定条件下では営業担当者が手動で上書きできる、といった柔軟なルールを適用する。これにより、営業担当者は現状のフェーズを正確に保ちつつ、売上予測への影響を適切にコントロールできる。
  •     // 例: 商談フェーズ変更時に売上予測ステータスを自動更新するApexトリガー
        trigger OpportunityForecastStatusUpdate on Opportunity (before insert, before update) {
            for (Opportunity opp : Trigger.new) {
                if (opp.StageName == 'Closed Lost' || opp.StageName == 'Cancelled') {
                    opp.Forecast_Status__c = 'Excluded_From_Forecast'; // カスタム項目
                } else if (opp.StageName == 'Negotiation') {
                    opp.Forecast_Status__c = 'Commit';
                }
                // ... 他のフェーズとForecast_Status__cのマッピング
                
                // 例: 特定の権限セットを持つユーザーは手動で上書き可能
                // if (UserInfo.hasPermissionSet('Override_Forecast_Status')) {
                //     // ユーザーが手動で設定した場合は上書きしないロジック
                // }
            }
        }
        

    上記のコードはあくまで概念的なものであり、実装にはビジネスロジックに応じた細かな条件分岐や、トリガー以外の自動化ツール(Flowなど)の検討が必要となる。今ならまずFlowを検討するだろう。

  • 売上予測レポートの再構築:標準の売上予測レポートではなく、カスタムレポートタイプを基盤としたレポートやダッシュボードを構築し、フェーズ、カスタム売上予測ステータス、そして商談金額などを組み合わせて、ビジネス要件に合致した集計を実現する。
  • Salesforce Revenue Cloud の検討:もし売上予測や収益管理が非常に複雑で、Salesforceの標準機能では対応しきれない場合は、Salesforce Revenue Cloud(旧 CPQ & Billing)のような専門ソリューションの導入も視野に入れるべきだった。これはコストもかかるため、費用対効果の検証は必須だが、当時もっと広い視点を持つべきだったと後悔している。

これは当時の自分向けのメモだ。システムのアーキテクチャを設計する際、標準機能のシンプルさに囚われすぎず、ビジネスプロセスの将来的な変化と複雑性を予見することの重要性を痛感した一件だった。

コメント