Mark Hammer's Blog

SalesforceやTrailheadに関する情報を投稿しているブログです。

Sales Cloud, Service Cloud, Community Cloudコンサルタント資格試験の違いを考える

はじめに

今年の9月から10月にかけて、Sales Cloud, Service Cloud, Community Cloudの各コンサルタント資格試験に合格しました。

この試験を通じて、各試験で感じたことを書いてみます。
なお、Salesforceは試験問題を公開することを禁じているため、試験問題を直接記載はしません。

Trailhead Help: Salesforce Certification Program Agreement and Policies

medium.com

各試験の受験ガイドから

合格ライン

各試験の合格ラインと、そこから計算した必要な正解数(小数点以下切り上げ)は以下になります。

試験名 合格ライン 必要な正解数
Sales Cloudコンサルタント 62% 38/60
Service Cloudコンサルタント 67% 41/60
Community Cloudコンサルタント 62% 38/60

なぜService Cloudコンサルタントのみ合格ラインが高いのかはよく分かりませんが、40問前後正解する必要があることが分かります。

試験範囲

受験ガイドから、各試験の出題ジャンルと出題率を抜き出してみました。

  • Sales Cloudコンサルタント
ジャンル 出題率
業界の知識 7%
実装戦略 12%
Sales Cloud ソリューションの設計 21%
マーケティングとリード 8%
取引先と取引先責任者の管理 13%
商談の管理 13%
営業の生産性 9%
Sales Cloud分析 9%
連携とデータ管理 8%
  • Service Cloudコンサルタント
ジャンル 出題率
業界の知識 10%
実装戦略 15%
Sales Cloud ソリューションの設計 16%
ナレッジ管理 9%
双方向チャネル 10%
ケース管理 15%
コンタクトセンター分析 5%
連携とデータ管理 5%
サービスコンソール 15%
  • Community Cloudコンサルタント
ジャンル 出題率
実装 22%
共有とセキュリティ 20%
コミュニティの設定 18%
エクスペリエンスビルダー 12%
コミュニティ管理 11%
コンテンツ 9%
テンプレートとユースケース 8%

受験して思ったのは、Sales Cloud、Service Cloudにある赤字ジャンル(業界の知識、実装戦略)の存在です。
例えば、Service Cloudコンサルタント受験ガイドにある以下のサンプル問題。

コンタクトセンターの事業継続計画に含めるべき作業を、以下から3つ選んでください。
A. 代わりのセンターのエージェントにケースを割り当てる
B. 音声自動応答システムを無効にする
C. 臨時スタッフにケース処理のトレーニングを実施する
D. ケース状況項目の値を更新する
E. サービスレベル契約 (SLA) を監視してお客様に連絡する

他のジャンルが実際に組織の設定変更やレコード・レポート・ダッシュボード作成で実践しながら身に付けられるのに対し、上記例題のような業界の知識、実装戦略については座学の範囲となります。
例えば「プロジェクト上で〇〇の問題が発生した場合、どうするか」という問題に対し、「Salesforceが考える回答はこれ」という内容を覚える必要がありますが、これはSalesforce組織で手を動かすだけでは身に付けることができません。*1
業界の知識、実装戦略の出題割合は合わせて20%~25%となるため、ここで得点を取れないと合格に向けて大きなマイナスポイントとなります。

なお、Community Cloudコンサルタントについては業界の知識、実装戦略に関する問題は見受けられず、実際の操作で身に付けることができる内容でした。
そのため、Community CloudコンサルタントはDeveloper Editionにてできるだけコミュニティを作成し、設定を見る、変更することが他コンサルタント資格よりも重要になります。
特に、Community Cloudでは設定箇所が以下の通り分かれており、設定できる内容も異なります。

  • Salesforceの一般的な設定画面(共有設定、ユーザ作成など)
  • 「コミュニティ設定」(共有セットなど、全コミュニティに関わる設定)
  • エクスペリエンスビルダー
  • エクスペリエンスマネージャー

また、Community Cloudは特にユーザライセンスが多岐に渡るため、関連ヘルプ を事前に読むことも他コンサルタント試験と比べて重要になります。
Community Cloudコンサルタント合格後、以下のようなコメントを残したのは、そういった背景もあります。

試験に合格するには

  • 受験対策のTrailmixは一通りやる。

現在公式とされるTrailmixは以下です。

trailhead.salesforce.com

trailhead.salesforce.com

trailhead.salesforce.com

別件ですが、Community CloudコンサルタントのTrailmixでヘルプが非常に多いのも、ヘルプの重要性を語っていると思います。

  • 受験対策のTrailmixになくても、関連しそうなProjectがあればやる。

例えば、私はCommunity Cloudコンサルタント受験直前に以下のProjectをやりました。

trailhead.salesforce.com

trailhead.salesforce.com

その後、試験当日までヘルプを見ながらエクスペリエンスマネージャー、エクスペリエンスビルダーを触ることで、エクスペリエンスマネージャー、エクスペリエンスビルダーでそれぞれ何ができるのか把握でき、よい結果に繋がったと思っています。

  • 受験ガイドの出題範囲に書かれたジャンルの下にある説明文について、用語を説明できるようになっておく。

例えばSales Cloudコンサルタント受験ガイドから抜粋しますと

マーケティングとリード
・与えられたシナリオに従って、リードスコアリングの適切な方法とリード評価の基準を推奨する
取引先と取引先責任者の管理
・与えられたシナリオに従って、取引先階層があることによる影響を説明する
・与えられたシナリオに従って、個人取引先実装の使用事例と影響を説明する

ここから「リードスコアリング」「取引先階層」「個人取引先」とは何か、注意事項があるならなにか、を分かっておくとアドバンテージになります。

  • ポイントスタディ研修は可能であれば受験する。また研修時の演習問題は全て答えられるようにする。

コンサルタント資格に限らず、ポイントスタディ研修の内容は非常に重要です。もし受講できた場合は有効活用しましょう。

おわりに

今回は実際の問題に触れずに受験ガイドなど公開された情報を基に書いたため、「もっと『こういった問題が出ます』のような直接的な情報が欲しいんだよ」という方にはお役に立てないかと思いますが、この投稿が皆様の合格に向けてお役に立てれば幸いです。

*1:回答だけ見ると、「どれも間違いとは言い切れない」という回答が選択肢として表示されていました

Salesforce数式: エラーを返す数式を含むAND, ORの条件判定

はじめに

本投稿の元ネタは、こちらのブログです。

sf.forum.circlace.com

内容を簡単に書くと「項目から日付を持ってきて、その月に日曜日が何回あるかを算出する」とのことで、以下の数式が紹介されていました。

4
+ IF( AND( DAY(LastDay__c)>=29, WEEKDAY( DATE( YEAR( BaseDay__c ), MONTH( BaseDay__c ), 29) ) = 1), 1, 0)
+ IF( AND( DAY(LastDay__c)>=30, WEEKDAY( DATE( YEAR( BaseDay__c ), MONTH( BaseDay__c ), 30) ) = 1), 1, 0)
+ IF( AND( DAY(LastDay__c)>=31, WEEKDAY( DATE( YEAR( BaseDay__c ), MONTH( BaseDay__c ), 31) ) = 1), 1, 0)

数式の意味についてはリンク先をご覧いただければと思いますが、上記数式のうち、
DATE( YEAR( BaseDay__c ), MONTH( BaseDay__c ), 31)
を見たときにこう思いました。

「この数式、BaseDay__c 次第ではエラーになるのでは?」

(でも、さすがにブログに書く前に動作確認はしているよなぁ…)と思いつつ、「よく考えたら、AND、ORの条件判定文でエラーが入ったときにどういう処理が行われるか知らないわ」と思ったので、調査することにしました。

AND関数にエラー数式を混ぜる

まず、日付を設定する項目 BaseDay__c と、 DATE( YEAR( BaseDay__c ), MONTH( BaseDay__c ), 29)DATE( YEAR( BaseDay__c ), MONTH( BaseDay__c ), 30)DATE( YEAR( BaseDay__c ), MONTH( BaseDay__c ), 31) の数式項目を作成します。 そして、BaseDay__c に様々な日付を設定し、数式項目の結果を確認します。

月末が異なる日付と数式の結果

予想通り、存在しない日付をDATE関数で出力しようとするとエラーになりました。

続いて、ブログを参考に LastDay__c を設定します。
そして、ブログに記載されていた以下数式を少し変更し、月末が異なる日付で出力させてみます。

  • 29日は日曜日?: AND( DAY(LastDay__c)>=29, WEEKDAY( DATE( YEAR( BaseDay__c ), MONTH( BaseDay__c ), 29) ) = 1)
  • 30日は日曜日?: AND( DAY(LastDay__c)>=30, WEEKDAY( DATE( YEAR( BaseDay__c ), MONTH( BaseDay__c ), 30) ) = 1)
  • 31日は日曜日?: AND( DAY(LastDay__c)>=31, WEEKDAY( DATE( YEAR( BaseDay__c ), MONTH( BaseDay__c ), 31) ) = 1)

数式の結果

AND関数の引数にエラーになる数式が入っているはずですが、数式全体としてはエラーになりませんでした。

次に、AND関数の引数の順番を入れ替えます。

  • 29日は日曜日?: AND( WEEKDAY( DATE( YEAR( BaseDay__c ), MONTH( BaseDay__c ), 29) ) = 1, DAY(LastDay__c)>=29)
  • 30日は日曜日?: AND( WEEKDAY( DATE( YEAR( BaseDay__c ), MONTH( BaseDay__c ), 30) ) = 1, DAY(LastDay__c)>=30)
  • 31日は日曜日?: AND( WEEKDAY( DATE( YEAR( BaseDay__c ), MONTH( BaseDay__c ), 31) ) = 1, DAY(LastDay__c)>=31)

論理式であれば X AND YY AND X は同じ結果になるはずですが、どうでしょうか。

数式の結果

今度は存在しない日付の場合はエラーを表示するようになりました。

さらに最初のAND数式を少し変更し、「AND関数の最初の引数は必ずTRUEを返す数式」を設定します。

  • 29日は日曜日?: AND( DAY(LastDay__c)>=28, WEEKDAY( DATE( YEAR( BaseDay__c ), MONTH( BaseDay__c ), 29) ) = 1)
  • 30日は日曜日?: AND( DAY(LastDay__c)>=28, WEEKDAY( DATE( YEAR( BaseDay__c ), MONTH( BaseDay__c ), 30) ) = 1)
  • 31日は日曜日?: AND( DAY(LastDay__c)>=28, WEEKDAY( DATE( YEAR( BaseDay__c ), MONTH( BaseDay__c ), 31) ) = 1)

これは、「LastDay__cの日は28以上である」という、BaseDay__cにどの日付を入れても必ずTRUEになる数式をAND変数の1番目の引数に設定しています。

数式の結果

エラー数式をAND関数の1番目の引数に設定した場合と同じ動作でした。

AND関数の動作推察

このことから、以下の動作になっていると推測できます。

  • AND関数は左の引数からTRUE、FALSEを判定している。
  • 引数の途中でFALSEと判定された場合は、そこで判定を打ち切り、AND関数はFALSEを返す。
    • 最初の日曜日判定で存在しない日付でもエラーにならなかった理由は、 DAY(LastDay__c)>=XX の時点でFALSEと判定され、その後のエラー数式が判定されなかったためと推測できます。
  • 引数の途中でエラーと判定された場合は、そこで判定を打ち切り、AND関数はエラーを返す。
  • AND関数の全ての引数がTRUEの場合はTRUEを返す。

OR関数にエラー数式を混ぜる

今度は、OR関数の1番目の引数に必ずTRUEになる数式と必ずFALSEになる数式を設定し、2番目の引数にエラー数式を設定して動作を見てみます。

1番目の引数に必ずTRUEになる数式を設定

  • 29日は日曜日?: OR( DAY(LastDay__c)>=28, WEEKDAY( DATE( YEAR( BaseDay__c ), MONTH( BaseDay__c ), 29) ) = 1)
  • 30日は日曜日?: OR( DAY(LastDay__c)>=28, WEEKDAY( DATE( YEAR( BaseDay__c ), MONTH( BaseDay__c ), 30) ) = 1)
  • 31日は日曜日?: OR( DAY(LastDay__c)>=28, WEEKDAY( DATE( YEAR( BaseDay__c ), MONTH( BaseDay__c ), 31) ) = 1)

動作結果は以下になりました。

数式の結果

OR関数は引数のうち1つでもTRUEがあればTRUEを返すため、期待通りの動作と言えます。

1番目の引数に必ずFALSEになる数式を設定

  • 29日は日曜日?: OR( DAY(LastDay__c)>=32, WEEKDAY( DATE( YEAR( BaseDay__c ), MONTH( BaseDay__c ), 29) ) = 1)
  • 30日は日曜日?: OR( DAY(LastDay__c)>=32, WEEKDAY( DATE( YEAR( BaseDay__c ), MONTH( BaseDay__c ), 30) ) = 1)
  • 31日は日曜日?: OR( DAY(LastDay__c)>=32, WEEKDAY( DATE( YEAR( BaseDay__c ), MONTH( BaseDay__c ), 31) ) = 1)

動作結果は以下になりました。

数式の結果

AND関数にて検証した、「AND数式の最初の引数は必ずTRUEを返す数式」を設定したときと同じ動作でした。

OR関数の動作推察

このことから、以下の動作になっていると推測できます。

  • OR関数は左の引数からTRUE、FALSEを判定している。
  • 引数の途中でTRUEと判定された場合は、そこで判定を打ち切り、OR関数はTRUEを返す。
  • 引数の途中でエラーと判定された場合は、そこで判定を打ち切り、OR関数はエラーを返す。
  • OR関数の全ての引数がFALSEの場合はFALSEを返す。

終わりに

今回の検証にて確認できた、「AND、OR関数にて引数の途中で結果が確定した場合は、最後まで処理を行わずに結果を返す」動作は「短絡評価」といい、Salesforce外のプログラミング言語でも使われています。

ja.wikipedia.org

Salesforce数式を作成するうえではあまり気にしなくてもよい動作かと思いますが*1、「こういう動作なんだ」ということを頭の片隅にでも入れていただければ幸いです。

*1:条件式にエラーになりえる数式を積極的に使う場合は必要ですが…

埋め込みチャットの姓と名を入れ替えたい

はじめに

Salesforce では、主に顧客とチャットでやり取りするための Web チャットチャネルを作成することができます。

trailhead.salesforce.com

例えば、上のTrailheadモジュールに従えば簡単なWeb埋め込みチャットを作ることができるのですが、ここで1つ問題が発生します。
それは、顧客情報を入力する事前チャット画面で姓と名が逆に表示される、という点です。

f:id:mark-hammer:20201108001618p:plain:w250
姓と名が逆の事前チャット画面

おそらく名前の欄は英語圏を前提としているのでしょうが、Salesforce内部設定と異なりチャット設定には地域の設定がありません。
そのため、Salesforce設定レベルでは姓と名を入れ替えることができません。

そこで今回は、CSSを使って強引に姓と名の配置を変えることで入れ替えを実現します。

姓と名の入れ替えを実現する

※注意:以下コードはブログ投稿時点で動作を確認しておりますが、今後のリリースにより動作しない場合があります。
また、チャット画面はデフォルトを前提としています。既にチャット画面をカスタマイズ済みの場合は期待通り動作しない場合があります。

姓と名を入れ替えるには、組み込みサービスのコードスニペットのうち、CSS部分に以下を追記してください。

4項目バージョン

    label[for="FirstName"] {
        position: relative;
        left: 142px
    }
    input[id="FirstName"] {
        position: relative;
        left: 142px
    }
    label[for="LastName"] {
        position: relative;
        right: 142px
    }
    input[id="LastName"] {
        position: relative;
        right: 142px
    }

コードスニペットがデフォルトのままの場合、CSS全体だとこうなります。

<style type='text/css'>
    .embeddedServiceHelpButton .helpButton .uiButton {
        background-color: #005290;
        font-family: "Arial", sans-serif;
    }
    .embeddedServiceHelpButton .helpButton .uiButton:focus {
        outline: 1px solid #005290;
    }
    label[for="FirstName"] {
        position: relative;
        left: 142px
    }
    input[id="FirstName"] {
        position: relative;
        left: 142px
    }
    label[for="LastName"] {
        position: relative;
        right: 142px
    }
    input[id="LastName"] {
        position: relative;
        right: 142px
    }
</style>

2項目バージョン

    .embeddedServiceSidebarFormField.inputSplitName:nth-of-type(1) {
        position: relative;
        left: 142px
    }
    .embeddedServiceSidebarFormField.inputSplitName:nth-of-type(2) {
        position: relative;
        right: 142px
    }

コードスニペットがデフォルトのままの場合、CSS全体だとこうなります。

<style type='text/css'>
    .embeddedServiceHelpButton .helpButton .uiButton {
        background-color: #005290;
        font-family: "Arial", sans-serif;
    }
    .embeddedServiceHelpButton .helpButton .uiButton:focus {
        outline: 1px solid #005290;
    }
    .embeddedServiceSidebarFormField.inputSplitName:nth-of-type(1) {
        position: relative;
        left: 142px
    }
    .embeddedServiceSidebarFormField.inputSplitName:nth-of-type(2) {
        position: relative;
        right: 142px
    }
</style>

表示結果

f:id:mark-hammer:20201108004743p:plain:w250
姓と名を正しい配置にした事前チャット画面

この設定を見つけるまでの経緯は、長くなるため末尾に記載します。

CSS変更時の注意

ヘルプに以下記載がある通り、Salesforceはユーザが作成したCSSが正常に動作することを保証していません。

重要 組み込みコンポーネントへのカスタム CSS の追加はサポートされていません。ブランドオプションの使用がサポートされるのは、設定およびコードスニペットのカスタマイズ可能なパラメータのみです。組み込みコンポーネントでカスタム CSS を使用する場合は、リリースごとにチャットウィンドウをテストし、適切に機能することを確認してください。

今回のCSSも、タグの内容が変わってしまえば無効化されますし、ある日のSalesforceリリースによって見栄えが変わってしまうこともあり得ます。*1
そのため、見栄えが変わってしまった場合にどう対応するかは知っておいた方がいいでしょう。

まぁ一番いいのはチャット設定に地域項目を追加して、それによって姓と名を自動で入れ替える機能が追加されることですけどね…。Salesforceさんお願いします。

*1:ここでいうリリースは、年3回のメジャーリリースだけでなく、バグフィックス等のユーザ通知がないリリースも含みます

続きを読む

Chatterメンション:手打ちとコピペの違い

はじめに

先日、会社でこんな問い合わせを受けました。

Chatterグループがメンションされればメール通知するよう設定をしています。
先日、ユーザから「Chatterグループをメンションしているのに何も連絡がない」と問い合わせを受けました。
確認すると、Chatterグループがメンションされているのにメール通知がなく、Chatterグループページにも表示されていません。
なぜでしょうか。

確認すると、投稿された内容は違いがないのに、確かに片方はChatterグループページに表示されていませんでした。

f:id:mark-hammer:20201022230309p:plain
片方が通知あり、もう片方は通知なしの投稿です

最初は全く違いが分からなかったので、「これSalesforceサポートに投げようかなぁ…」と内心思いながら調査を行ったのでした。

調査

調べたところ、これはChatterグループ名を手打ちし、正しくメンションした投稿と、その投稿をコピペした結果、正しくメンションされていない投稿の差だと判断しました。

手打ちで正しくメンションしたとき

  • Chatter 投稿画面で、 @[<グループ名>] の書式になっている。 f:id:mark-hammer:20201022230817p:plain

  • Chatter 投稿のうち、グループ名にマウスカーソルを当てるとグループ詳細情報が表示される。

  • Chatter 投稿で、グループ名に点線の下線がある。 f:id:mark-hammer:20201022230940p:plain

文面をコピペし、正しくメンションされていないとき

  • Chatter 投稿画面で、 @<グループ名> の書式になっている。 f:id:mark-hammer:20201022231046p:plain

  • Chatter 投稿のうち、グループ名にマウスカーソルを当てるとリンク先URLのみ表示される。

  • Chatter 投稿で、グループ名に点線の下線がない。*1 f:id:mark-hammer:20201022231147p:plain

おわりに

Chatter投稿のコピペ、ダメ、絶対。

*1:下画像はグループ名にマウスカーソルを当てているため下線があるように見えます。「はじめに」節右側の画像を見ていただくと違いが分かると思います。

Salesforce DevTools を使うには「すべてのデータの参照」権限が必要

はじめに

Salesforce DevTools、便利ですよね。
以前便利ツールとして会社で紹介したのですが、このツール、使用するためのSalesforce権限が見つからなかったんですよね。*1
そこで2020/8に自分のDeveloper Edition環境で確認したのですが、「すべてのデータの編集/参照」権限が使えないForce.com - App Subscriptionライセンス用プロファイルでも一応使えたので、「APIアクセス可能であれば一応ログインまではできるのだろう」と思い紹介したのでした。

その後、非システム管理者ユーザから「ログインしようとしてもActiveボタンしか出ず、何度Activeボタンを押してもログインできない」と言われ、GitHub経由で開発者に質問しました。

github.com

開発者の回答

その結果、開発者からは「『すべてのデータの参照』権限を付与して再度確認してもらえないか」とコメントいただきました。
また、動作確認にて「すべてのデータの参照」権限があればActiveボタンから先に進めたため、現状の必須権限は「すべてのデータの参照」権限でよいと思われます。*2

f:id:mark-hammer:20200915234242p:plain
開発者からの返答

おわりに

Salesforce DevTools には当然IssueのSLAというものはなく、放置されているIssueも結構あったので「質問はしてみるけど期待しないでね」と言ってあったのですが、即日返答いただけたので好印象でした。
今後の Salesforce DevTools の機能拡張を楽しみにしております。*3

*1:まぁシステム管理者しか使わないだろ、というのはあるのですが

*2:では「はじめに」に書いた事前チェックは何だったのか、となりますが、「直近アップデートで動きが変わった」「私の記憶違い」のどちらかかと思われます

*3:全ページレイアウトの内容がエクスポートできる機能が欲しいです…。

Trailhead モジュール:Lightning Experience の Service Cloud

※この内容は2020/9時点のものです。

サービスジャーニーの開始

https://trailhead.salesforce.com/ja/content/learn/modules/service_lex/service_lex_cloud

  • 説明:日本語
  • Challenge:日本語選択問題

Service Cloud の管理

https://trailhead.salesforce.com/ja/content/learn/modules/service_lex/service_lex_connect

  • 説明:日本語
  • Challenge:日本語選択問題

ケース管理の自動化

https://trailhead.salesforce.com/ja/content/learn/modules/service_lex/service_lex_case_manage

  • 説明:日本語
  • Challenge:英語ハンズオン

【Challenge要約】

  • ケースオブジェクト用のキュー「Panel1」を作成してください。
  • ケースを「Panel1」キューに割り当てるためのケース割り当てルール「Assign Panel1」を作成してください。
  • ケース割り当てルール「Assign Panel1」にて、「ケース: 説明」に「Panel1」を含む場合に「Panel1」キューに割り当てるよう設定してください。
  • エスカレーションルール「Panel1」を作成してください。
  • エスカレーションルール「Panel1」にて、30分経過後もクローズされなかったケースをエスカレーションするよう設定してください。なお、「営業時間の設定」は「ケースに指定された営業時間を使用」、「エスカレーション時刻の設定方法」は「ケースの作成時」としてください。
  • エスカレーションアクションではあなたのユーザにケースが割り当てられるよう設定してください。
  • 作成したケース割り当てルール、エスカレーションルールはどちらも有効にしてください。

複数のチャネルでのデジタルエンゲージメントの作成

https://trailhead.salesforce.com/ja/content/learn/modules/service_lex/service_lex_channels

  • 説明:日本語
  • Challenge:日本語選択問題

フローで項目値を空文字に更新しようとしたけど更新されなかった話

これは以前の投稿の続編です。
フローの要件等は↓を読んでいただいた前提で進みますのでご了承ください。 sfblog.markhammer.net

はじめに

以前、行動の被招集者のうち、ユーザ一覧をテキスト項目に投入するフローを作成したのですが、実際は投入先のテキスト項目は100文字制限がありました。
そのため、被招集者が20人程度になるとエラーを返すようになります。

f:id:mark-hammer:20200906004234p:plain

そこで、作成したテキストが100文字を超える場合は100文字に切り捨てるよう、以下のように変更しました。

  • 変更前
    • 「同行者」項目に作成したテキスト変数をそのまま割り当てる
  • 変更後
    • LEFT(<作成したテキスト変数>, 100) で100文字に切り捨てたテキストを「同行者」項目に割り当てる

その後動作確認をすると、確かに100文字切り捨ては正しく動作したのですが…、
空文字更新しようとしても前のデータが残ってしまう動作になってしまいました。

f:id:mark-hammer:20200906010047p:plain

デバッグログで動作を確認する

なぜこうなってしまったのか、デバッグログで更新中の動作を確認すると…。

  • 変更前(LEFT関数未使用)
    • FLOW_VALUE_ASSIGNMENT|xxx|$Record|{Id=00U2w000006slhNEAQ,(中略), doukousya__c=, ...(後略)
  • 変更後(LEFT関数使用)
    • FLOW_VALUE_ASSIGNMENT|xxx|$Record|{Id=00U2w000006slhNEAQ,(中略), doukousya__c=null, ...(後略)

つまり、

  • 変数に空文字を代入すると、レコード項目も空文字で更新される
  • 変数にnullを代入すると、レコード項目は更新されない

という動作だったのです。

どうすればよかったのか

そもそも100文字以下の場合はLEFT関数を使用する必要はなかったのですから、「同行者」項目に割り当てる内容を IF(LEN(<作成したテキスト変数>) > 100, LEFT(<作成したテキスト変数>, 100), <作成したテキスト変数>) の数式にすればよかったのです。*1

IF関数を用いた数式に変更した後は、無事空文字更新も行われるようになりました。

おわりに

今回の話は、

  • LEFT関数で空文字の変数を設定すると、nullが返却される
  • nullをフローの割り当てでレコード項目に割り当てると、項目値は変更されない

ということが原因でした。
nullと空文字の違いは初めて知りましたが、今後このようなフローを作るときは気にしないといけませんね。

*1:最初はこちらにすべきか少し悩んだのですが「100文字以下でも動作変わらないからまとめてLEFT関数でいいだろ」と思ったのです

Salesforceサポートで紙での申請が必要なケース

はじめに

Salesforceサポートのやり取りは、基本的にはケース上のコメントで行います。
そのため、個人取引先の有効化のようなリクエストも、ケースを起票して依頼します。

その一方、実はリクエストに紙(PDF)が必要なパターンもいくつか存在します。
今回はそのパターンについて書いてみます。

注:以下の情報は私が把握している範囲で正しい情報を記載していますが、現在の運用が異なっている場合はごめんなさい。その場合は何かしらの方法で連絡いただけますと幸いです。

システム管理者のメールアドレス変更

これは以下の条件を満たす場合にリクエストします。

  • システム管理者アカウントに誰もログインできない
  • システム管理者アカウントに設定されているメールアドレスに届くメールを誰も受信できない
    • つまりパスワードリセット後のメールを誰も受信できない

申請の詳細はSalesforceヘルプ: Salesforce のシステム管理者を変更することができない に記載されています。
注意事項として、この申請を行う際の承認者欄には最高経営責任者レベルの役員(たとえば、CEO、CIO、CFO、または事業主 )の署名が必要になる、という点です*1
なので大企業など、最高経営責任者レベルの役員の押印を頂くのが難しいような会社では、複数名システム管理者を設定するなどして依頼をしないような体制を取ることが大事です。

ちなみに、「システム管理者アカウントに誰もログインできないが、メールは受信できる」場合は上記の申請をしなくても、電話でSalesforceサポートに依頼すればパスワードリセットしてもらえます。*2

ログインIPアドレス制限の解除

これは、システム管理者用プロファイルにログインIPアドレス制限を設定しているが、社内のIPアドレスが変更したなどでシステム管理者が誰もログインできなくなった場合にリクエストします。
リクエスト方法はSalesforceサポートに電話で問い合わせた後に、こちらのファイルに必要事項を記載してメール or FAXで送付する形になります。
ちなみにこれも承認者の欄がありますが、この場合は申請者の上長などで大丈夫です。

ちなみに一般ユーザ用のプロファイルに設定したログインIPアドレス制限のため一般ユーザがログインできなくなっているが、システム管理者がログインできる場合は、システム管理者にてログインIPアドレス制限の解除ができるのでこの申請は不要です。
あくまでSalesforceサポートへのリクエストは「ログインできない原因となっている設定の変更が誰もできない」場合のみ可能、ということです。*3

メールアドレス変更時の確認メール送信を停止

Salesforce上でメールアドレスを変更するときは、通常以下の手順になります。

  • Salesforceでユーザのメールアドレスを変更する
  • 変更前、変更後メールアドレスに確認メールが送信されるので、変更後メールアドレスに届いたメールに記載されたURLにアクセスする

しかし、会社のドメインを変更したなどの理由で全ユーザのメールアドレスの変更が必要になった場合、全ユーザに「Salesforceから届くメールアドレス変更のメールに記載されたURLにアクセスしてください」と伝える必要があり、システム管理者、Salesforceユーザいずれにとっても面倒です。
このような、メールアドレス変更時の確認メール送信を停止してメールアドレス変更を即反映したい場合にこのリクエストを行います。

申請の詳細はSalesforceヘルプ: メールアドレスの変更通知の無効化 に記載されています。
なお、上記ヘルプには「設定変更依頼書」について記載されていませんが、こちらのファイルに必要事項を記載の上、ケースで依頼する流れになります。(ファイルはケースに添付する)
承認者については「ログインIPアドレス制限の解除」同様、申請者の上長などでよいです。

おわりに

上記ケースのうち、「システム管理者のメールアドレス変更」、「ログインIPアドレス制限の解除」が必要なケースではシステム管理者が誰もログインできない状態のため、混乱するかと思います。
そういった場合はSalesforceサポートに電話して、対応策を検討した方がよいでしょう。*4

*1:この申請を行うと申請者がシステム管理者の権限を持つことができるので、Salesforceサポートは通常のリクエストより厳しい制約をつけています

*2:他のシステム管理者がいない、または誰もログインできない状態であることが条件です

*3:反対に一般ユーザはログインできますがシステム管理者がログインIPアドレス制限のためログインできない場合は、一般ユーザはログインIPアドレスの制限を解除できないためSalesforceサポートへの申請ができます。

*4:Basicサポートは緊急度1のみ電話サポート可能と記載されていますが、システム管理者がSalesforceにログインできないなど、ケース起票できない場合は電話を受け付けてもらえます

行動のActivityDateとActivityDateTimeの関係

はじめに

このブログの基となったのはこちらの投稿です。

sf.forum.circlace.com

この投稿を読んだ当時、仕事で行動のActivityDateを利用したプロセスビルダーを作成した頃だったため、

「『終日行動にチェックを入れない → ActivityDateTime に開始時刻が保存される』…?あれ、仕事で作ったプロセスビルダー、終日行動関係なしにActivityDateを条件に使用してたよマズいよ。」

「あれ、でも動作確認では期待通り動作したよな。あれ?」

ということで、詳しく調べてみることにしたのでした。

ActivityDate はいつ保存される?

まず、終日行動にチェックを入れた行動とチェックを入れていない行動を作成し、SOQLで内容を確認します。
すると以下表の通り、ActivityDateは終日行動の内容にかかわらず値が入っていることが分かります。

項目 終日行動=TRUE 終日行動=FALSE
ActivityDate 値あり 値あり
ActivityDateTime 空白 値あり

f:id:mark-hammer:20200830145551p:plain
終日行動とActivityDate, ActivityDateTime の差

(なんだ、やっぱりActivityDateは必ず値が入るんじゃないか)と思いましたが、念のためActivityDateに関する入力規則を作成し、動作を見ることにしました。
すると…、

確かに、ActivityDateだけを見る入力規則では、終日行動のチェックがない行動をスルーしてしまいました。

何故だ、と思いデバッグログで入力規則の判定部分を見ると…、

  • 終日行動のチェックあり
VALIDATION_FORMULA|ActivityDate + 2 <= TODAY()|ActivityDate=2020-08-25 00:00:00
VALIDATION_FAIL
  • 終日行動のチェックなし
VALIDATION_FORMULA|ActivityDate + 2 <= TODAY()|ActivityDate=null
VALIDATION_PASS

終日行動のチェックがない場合、入力規則時点でのActivityDateはnullになっていたため、入力規則がスルーされていました。

ちなみに入力規則に用いる項目がActivityDateTimeの場合、以下の通り終日行動のチェックがない場合のみ判定されます。

  • 終日行動のチェックあり
VALIDATION_FORMULA|ActivityDateTime + 2 <= NOW()|ActivityDateTime=null
VALIDATION_PASS
  • 終日行動のチェックなし
VALIDATION_FORMULA|ActivityDateTime + 2 <= NOW()|ActivityDateTime=2020-08-25 07:00:00
VALIDATION_FAIL

終日行動のチェックがない行動レコードのActivityDateはいつ登録されているのか?

終日行動のチェックがない行動のActivityDateは入力規則判定時点では空でした。
一方、SOQLで検索した場合はActivityDateに値は入っているので、どこかのタイミングで値は入っているはずです。
そこで、フロー、ワークフロー、プロセスビルダーでActivityDateの内容を他項目にコピーするよう設定し、動作を確認してみます。

結果は以下のようになりました。

設定内容 結果
フロー(保存前更新) 空白
フロー(保存後更新) 値が設定される
ワークフロー 値が設定される
プロセスビルダー 値が設定される

これをトリガと実行の順序に当てはめてみると、おそらく「7. レコードはデータベースに保存されますが、まだ確定されません。」の時点でAcitivityDateが登録されているのではないか、という推測ができます。

終わりに

AcitivityDate はSalesforce側が登録する「システム項目」に近いものですが、その登録のタイミングは必ずしも想定と合わない、ということが分かりました。
この投稿が皆様の役に立てれば幸いです。

おまけ

本動作について、行動オブジェクトからのありがたいお言葉をどうぞ。

ありがとうございました。

行動の被招集者をまとめたテキスト項目を作りたい

要件

行動には、「被招集者」という標準項目があります。

f:id:mark-hammer:20200801223946p:plain
行動レコード画面

「被招集者」項目には同じ行動に参加予定のユーザを追加しています。

さて、行動と被招集者をレポート表示する場合、標準レポートタイプの「被招集者が関連する行動」だと、以下のように被招集者の人数分の行ができてしまいます。

f:id:mark-hammer:20200801224206p:plain
「被招集者が関連する行動」レポートタイプの表示

こんな表示はいやだ、下記キャプチャのように行動ごとに被招集者のユーザを一覧できる項目を作ってくれ、というのが今回の要件となります。

f:id:mark-hammer:20200801224431p:plain
期待するレポート表示

具体的な要件

  • 「被招集者」項目に表示されているユーザの氏名をまとめてテキスト項目「同行者」にカンマ区切りで設定してほしい。同行者の並び順は任意とする。
  • 「被招集者」項目に行動任命先ユーザ以外の値を設定していない場合は「被招集者が関連する行動」レポートタイプのレポートには表示されないので、「同行者」項目もそれに合わせ空白とする。
  • 「被招集者」項目にはユーザの他にリード、取引先責任者、リソースも設定できるが、今回はユーザ以外のものは「同行者」項目に反映しない。

「被招集者」項目の内容はどうやって保存されるのか

Salesforce標準オブジェクトの中でも、ToDo、行動は特殊なオブジェクトです。
以下のオブジェクトER図を見てもそれが分かります。(出典元)

f:id:mark-hammer:20200801225237p:plain
ToDo・行動オブジェクトER図

このうち、「被招集者」項目のデータはEventRelationオブジェクトに保存されています。
例えば、ある行動にユーザ、取引先責任者、リード、リソースを保存した場合、以下のように保存されます。

EventId RelationId 備考
00U112222233333AAA 005aaBBBBBcccccZZZ 被招集者のユーザ
00U112222233333AAA 003bbCCCCCdddddYYY 被招集者の取引先責任者
00U112222233333AAA 00QccDDDDDeeeeeXXX 被招集者のリード
00U112222233333AAA 023ddEEEEEfffffWWW 被招集者のリソース

ここで問題は、【「被招集者が関連する行動」レポートタイプのレポートには任命先ユーザも表示されるが、EventRelationオブジェクトには任命先ユーザは入っていない】ことです。
行動を作成すると、「被招集者」項目にはデフォルトで任命先ユーザが入りますが、この任命先ユーザはEventRelationオブジェクトには存在しないのです。

実現方法

以上を踏まえ、要件に合う仕組みを考えると、以下となりました。

  • 要件実現にはEventRelationオブジェクトのデータを取得する必要があるが、プロセスビルダーでは取れないのでフローの利用が必須。
  • フロー処理内容は以下のようにする。
    • 作成/編集された行動オブジェクトのレコードIDに紐づくEventRelationオブジェクトのデータを全て取得する
    • 取得したデータからユーザのデータだけ取り出し、ユーザ氏名を変数に代入する。
    • EventRelationオブジェクトのデータにユーザのデータが1つ以上あった場合は、任命先ユーザ氏名も追加して「同行者」項目を更新する。
    • EventRelationオブジェクトのデータにユーザのデータがない場合は、「同行者」項目を空白で更新する。

作成したフロー

全体図

f:id:mark-hammer:20201123181433p:plain
フロー全体図

EventRelationオブジェクトデータ取得処理部分

f:id:mark-hammer:20201123181516p:plain

当初は「EventRelationオブジェクトのデータからユーザだけ取り出す」処理は、以下の通り最初のレコード取得要素で処理しようと考えていました。

f:id:mark-hammer:20200801231801p:plain

しかし、「ID項目は【次の文字列で始まる(starts with)】は使用不可」というエラーが出て、この方法は不可となりました。*1

f:id:mark-hammer:20200801231922p:plain

最終的に、ここでの条件は「行動レコードIDに紐づくEventRelationオブジェクトのデータを全て取得する」だけにしました。

f:id:mark-hammer:20200801232325p:plain
修正後の条件

EventRelationオブジェクトデータからユーザ情報抽出処理部分

f:id:mark-hammer:20200801232529p:plain

一部文字被りがありますが、ここでの処理は以下です。

  • 前の処理で取り出したRelationIdが005以外から始まる場合はループ要素に戻す。
  • RelationIdが005から始まる場合はユーザオブジェクトから名前(Name)を取り出し、変数に保存する。
  • RelationIdが005から始まるレコードが複数存在する場合は、名前をカンマ区切りでつなぎ合わせる。

ユーザオブジェクトからのレコード取得要素を入れている場合は、RelationIdが複数オブジェクトのレコードを受け入れる仕様のため、RelationId.Name という取り出し方ができないためです。*2

ユーザ情報抽出後処理部分

f:id:mark-hammer:20200801233420p:plain

ここでの処理は、以下となります。

  • 前の処理で名前保存用変数が空白の場合は、「任命先以外に被招集者となったユーザが存在しない」ため、「同行者」項目を空白で更新する。
  • 名前保存用変数が空白ではない場合は、任命先ユーザの名前も追加し、「同行者」項目を変数の内容で更新する。

ToDoの場合は任命先にキューも設定できるため、「任命先がユーザか否か」の決定要素も必要ですが、行動は現状キューが使えないため、決定要素は外しました。

終わりに

改めて、どのオブジェクトレコードも項目値に設定できる項目を持つToDo、行動に対する自動処理を考えるのが難しいことを学びました。
標準オブジェクトのER図も把握した上でデータの取得方法を考えるのはなかなか大変でした。

Trailheadではない、独自の要件からフローを作ったのは今回が初ですが、Flow Builderになってかなり機能拡張され、直感的にわかりやすくなったと思います。
ただ、要素を組み合わせることによる自由度の高さと、複雑さどちらも備える機能のため、使いどころは考えたいと思います。

*1:プロセスビルダーだと「行動の関連先が003から始まる(取引先責任者)」を条件にできますが、フローだとダメなんですね

*2:ループ内にレコード取得要素を入れるのはガバナ制限を考えるとよくないというヒントも表示されましたが、やむなしとしました