Mark Hammer's blog

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

Trailhead モジュール:App Development Without Limits

※この内容は2019/6時点のものです。

Learn About Daily Rate Limits

https://trailhead.salesforce.com/ja/content/learn/modules/app-development-without-limits/app-development-without-limits-rate

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

【Challenge要約】

  • 「標準ユーザ」プロファイルをコピーし、「Data Entry - No API Access」プロファイルを作成してください。
    • 「システム管理者権限」セクションにある「API の有効化」のチェックを外し、保存してください。
  • (まだ作成していない場合は)「Limits Trail Connected App」接続アプリケーションを作成してください。
    • 接続アプリケーション名:Limits Trail Connected App
    • API参照名:Limits_Trail_Connected_App
    • 取引先責任者 メール:あなたのメールアドレス
    • 「Web アプリケーション設定」セクションの「開始URL」:https://trailhead.salesforce.com
  • 「Limits Trail Connected App」接続アプリケーションへのアクセス許可を割り当てた権限セット「Connected App Access」を作成してください。

Avoid Concurrency

https://trailhead.salesforce.com/ja/content/learn/modules/app-development-without-limits/app-development-without-limits-concurrency

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

【Challenge要約】

  • この未管理パッケージをインストールしてください。インストールに失敗する場合はこの記事(英語)の手順に従ってください。
  • 設定画面より、Apexクラス「CalloutController」を探してください。
  • Apexクラス「CalloutController」を以下の通り編集してください。
    • 既存のコールアウト部分のコードを、新しくContinuationクラスの変数 con を使用してコールバックハンドラ「processResponse」を呼び出すよう変更してください。
    • サーバからのレスポンスを待つためのコールバックハンドラとして、「processResponse()」メソッドを追加してください。

Tune Transactions

https://trailhead.salesforce.com/ja/content/learn/modules/app-development-without-limits/app-development-without-limits-transactions

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

【Challenge要約】

  • まだインストールしていない場合は、この未管理パッケージをインストールしてください。インストールに失敗する場合はこの記事(英語)の手順に従ってください。
  • 設定画面より、Apexクラス「TrailLoop」を探してください。
  • AllBadThings()」メソッドを以下の通り編集し、保存してください。
    • if文をSOQLのWhere句で置き換えてください。
    • update文をfor文の外に出してください。
    • メソッド名を「AllGoodThings()」に変更してください。

※筆者注: 設定を正しく実施しても

System.QueryException: List has no rows for assignment to SObject

というエラーが発生する場合は、Challenge用組織のユーザ言語を[English]に変更し、再度Challengeの確認を実施してください。
これはChallengeチェック時に SELECT Id FROM Profile WHERE Name = 'System Administrator' LIMIT 1 というSOQLが実行されますが、ユーザ言語が日本語だとプロファイル名が「システム管理者」となるため、SOQL結果が0件になるのが原因と思われます。

Trailhead モジュール:Visualforce Mobile

※この内容は2019/6時点のものです。

Salesforce アプリケーションでの Visualforce の使用開始

https://trailhead.salesforce.com/ja/content/learn/modules/visualforce_mobile_salesforce1/visualforce_mobile_salesforce1_intro

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

ナビゲーションメニューへの Visualforce ページの追加

https://trailhead.salesforce.com/ja/content/learn/modules/visualforce_mobile_salesforce1/visualforce_mobile_salesforce1_nav_menu

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

【Challenge要約】

  • ページ名を「HelloMobile」としたVisualforceページを作成してください。
  • Visualforceページに「Hello mobile world」の文字列を表示するよう、コードを記述してください。
  • Visualforceページ「HelloMobile」をSalesforceモバイルアプリケーションで利用可能にしてください。
  • Visualforceページ「HelloMobile」を、タブ名を「HelloMobile」としたタブで表示するようにしてください。また、このタブをSalesforceナビゲーションに追加してください。

Salesforce Lightning Design System を使用した Visualforce ページのスタイル設定

https://trailhead.salesforce.com/ja/content/learn/modules/visualforce_mobile_salesforce1/visualforce_mobile_salesforce1_lightning_design

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

【Challenge要約】

  • ページ名を「MobileContactList」としたVisualforceページを作成してください。
  • VisualforceページにLightning Design Systemをインポートしてください。
  • Lightning Design SystemのName Value Listユーティリティクラスを使用してください。
  • 取引先責任者(Contact)の標準リストコントローラを使用してください。recordSetVar 属性には"contacts"を指定してください。
  • ”apex:repeat”コンポーネントを使用し、取引先責任者(Contact)のリストを反復処理してください。var 属性には “c” を設定してください。
  • Visualforceページに、取引先責任者(Contact)の「取引先責任者名(Name)」、「電話(Phone)」項目の値を表示してください。
  • Salesforceモバイルアプリケーションで使用できるよう設定してください。
  • 作成したVisualforceページを「MobileContacts」タブに含めてください。また、「MobileContacts」タブをSalesforce アプリケーションナビゲーションメニューに追加してください。

Visualforce ページを使用したグローバルアクションの実装

https://trailhead.salesforce.com/ja/content/learn/modules/visualforce_mobile_salesforce1/visualforce_mobile_salesforce1_actions_global

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

【Challenge要約】

  • 表示ラベルを「My Business Card」、名前を「My_Business_Card」としたグローバルアクションを作成してください。
  • 作成したグローバルアクションには、Visualforceページ「BusinessCard」を関連付けしてください。
  • Visualforceページ「BusinessCard」はモバイルアプリケーションで利用可能としてください。
  • Visualforceページ「BusinessCard」には、適切なVisualforceグローバル変数を用いて以下の項目を表示してください。
    • ログインユーザの名(FirstName)
    • ログインユーザの姓(LastName)
    • ログインユーザのメール(Email)
    • ログインユーザの電話(Phone)
    • ログインユーザの役職(Title)
  • 作成したVisualforceページに、Eメールアドレスを入力するための入力フィールドと、連絡先情報を他のユーザに素早くメール送信することを可能にする「送信」ボタンを作成してください。(この機能追加は任意です。)

Visualforce ページを使用したオブジェクト固有のアクションの実装

https://trailhead.salesforce.com/ja/content/learn/modules/visualforce_mobile_salesforce1/visualforce_mobile_salesforce1_actions_object

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

【Challenge要約】

  • ページ名を「ShowAssistantInfo」としたVisualforceページを作成してください。
  • Visualforceページ「ShowAssistantInfo」には取引先責任者の標準コントローラを使用してください。
  • Visualforceページ「ShowAssistantInfo」にアシスタントの名前と電話番号を表示するため、それぞれ該当する取引先責任者標準項目を使用してください。
  • Visualforceページ「ShowAssistantInfo」に 'tel:'ハイパーリンク を用いてアシスタント電話番号を表示してください。
  • 取引先責任者オブジェクトのオブジェクト固有アクションを作成してください。アクションの表示ラベルは「Call Assistant」、名前を「Call_Assistant」としてください。
  • 「Call Assistant」アクションにはVisualforceページ「ShowAssistantInfo」を使用してください。
  • 「Call Assistant」アクションを取引先責任者ページレイアウト「Contact Layout」に追加してください。

ページレイアウトとモバイルカードでの Visualforce ページの使用

https://trailhead.salesforce.com/ja/content/learn/modules/visualforce_mobile_salesforce1/visualforce_mobile_salesforce1_layouts_cards

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

【Challenge要約】

  • ページ名を「OpportunityStageTips」としたVisualforceページを作成してください。
  • Visualforceページ「OpportunityStageTips」に対し、商談のフェーズ項目値(「Prospecting」、「Needs Analysis」、「Proposal/Price Quote」、「Negotiation/Review」)ごとに、そのフェーズに合ったアドバイスを個別に表示するようにしてください。各フェーズに表示するための任意のプレースホルダコンテンツを使用できます。
  • Visualforceページ「OpportunityStageTips」には商談の標準コントローラを使用してください。
  • Visualforceページ「OpportunityStageTips」を商談ページレイアウト「Opportunity Layout」のモバイルカードに追加してください。

ユーザインターフェースのガイドラインの採用

https://trailhead.salesforce.com/ja/content/learn/modules/visualforce_mobile_salesforce1/visualforce_mobile_salesforce1_ui_guidelines

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

CSS および JavaScript モバイルフレームワークの使用

https://trailhead.salesforce.com/ja/content/learn/modules/visualforce_mobile_salesforce1/visualforce_mobile_salesforce1_mobile_frameworks

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

フィード追跡から大量データのリカバリをやってみた

はじめに

Salesforceにて項目値の変更を記録する機能は項目履歴管理フィード追跡がありますが、データローダから簡単に項目値の変更履歴をエクスポートできる項目履歴管理と違い、フィード追跡は容易に変更履歴をエクスポートすることができません。
今回は項目履歴管理が使えず、フィード追跡から大量データのリカバリが必要になった場合の手順を記載します。

f:id:mark-hammer:20190602222948p:plain
画面では簡単に更新内容が分かるのに…

状況

  • ある日、特定日時に大量の取引先責任者レコードにある「取引先」項目値が削除されていることに気づいた。
  • 項目履歴管理は使用しておらず、デフォルトで設定されていたフィード追跡により各レコードに取引先名変更のChatter投稿がなされていた。
  • そのChatter投稿をもとに手作業でリカバリすることはできるが、対象レコードが数千件存在するため、データローダ等で一括リカバリしたい。

フィード追跡からのリカバリが難しい理由

項目履歴管理の場合はリカバリに必要な「対象レコードID」「変更前の値」「変更後の値」が1オブジェクトで取得可能です。

取引先責任者履歴のイメージ図

一方、フィード追跡の場合、

  • 「対象レコードID」と「該当フィード追跡の投稿を示すChatter投稿ID」を持つフィード項目(FeedItem)
  • 「該当フィード追跡の投稿を示すChatter投稿ID」と「変更前の値」「変更後の値」を持つフィード追跡履歴(FeedTrackedChange)

が別オブジェクトとなっているため、リカバリにはこの2オブジェクトの紐づけが必要となります。

f:id:mark-hammer:20190603234213p:plain
フィード追跡関連オブジェクトのイメージ図

これがフィード追跡からのリカバリを難しくしている一方で、この紐づけさえできればフィード追跡からでもリカバリはできる、ということになります。

リカバリ手順

データのエクスポート

フィード追跡からのリカバリに必要な2オブジェクトのうち、FeedTrackedChangeはウィークリーエクスポートから、FeedItemはデータローダからのExportが必要となります。

ウィークリーエクスポート

Salesforce からバックアップデータをエクスポートする のヘルプに従いエクスポートを実行します。
「エクスポートデータ」で選択するオブジェクトは、今回は FeedTrackedChange だけでよいです。*1
「画像、ドキュメント、および添付ファイルを含める」、「Salesforce Files および Salesforce CRM Content ドキュメントバージョンを含める」はお好みで選択してください。画像ファイルが多いとバックアップに時間がかかるので、画像、ドキュメント、添付ファイルのバックアップがとりたい方以外は不要だと思います。

f:id:mark-hammer:20190602225429p:plain
ウィークリーエクスポート 選択画面

エクスポートが完了するとzipファイルがダウンロードできるようになるので、ダウンロードして解凍し、FeedTrackedChange.csvが存在することを確認します。

データローダ

Chatter フィードのデータをエクスポートする のヘルプに従い「フィード項目 (FeedItem)」オブジェクトをエクスポートします。
以下、エクスポートしたファイル名をFeedItem.csvとします。

f:id:mark-hammer:20190602225346p:plain
データローダ Export画面

データリカバリー用CSVファイル作成

「データのエクスポート」で取得したデータの内容は以下です。(今回の作業に必要な項目のみ抜粋)

  • FeedTrackedChange.csv
    • FeedItemId: フィード追跡の投稿を示すChatter投稿ID
    • FieldName: 値が変更された項目のAPI参照名。取引先責任者オブジェクトにある「取引先」項目の場合は"Account"。
    • DataType: 変更された項目の型。文字列の場合は"TEXT"、参照関係の場合は"EntityId"。「取引先」項目のフィード追跡では、取引先名のうちテキスト分の"TEXT"、取引先レコードID分の"EntityId"と、2レコード作成されます。
    • OldValueString: (「取引先」項目の場合)変更前のレコードID
    • NewValueString: (「取引先」項目の場合)変更後のレコードID
    • OldValueLastName: (「取引先」項目の場合)変更前の取引先名
    • NewValueLastName: (「取引先」項目の場合)変更後の取引先名
  • FeedItem.csv
    • ID: Chatter投稿ID
    • PARENTID: Chatter投稿がどのレコードに投稿されたかを示すレコードID(取引先責任者の場合は"003"から始まる)。Chatter投稿がフィード追跡の場合は、値が変更されたレコードのIDを指す。
    • TYPE: Chatter投稿の種類。フィード追跡の場合は"TrackedChange"。

ここで、FeedTrackedChange.csvのFeedItemIdとFeedItem.csvのIDが同じ場合、その行は同じフィード追跡のChatter投稿の内容を指しています。
なので、FeedTrackedChange.csvのFeedItemIdとFeedItem.csvのIDで突合せを行い、一致するFeedItem.csvのPARENTID(=対象レコードID)とFeedTrackedChange.csvのOldValueString(=対象レコードの変更前の値)を取得できれば、データリカバリー用CSVファイルを作成できます。

今回のデータリカバリーに使うCSVファイルに必要な項目、及び対象となるエクスポートデータの項目は以下です。(その他項目はあってもいいですが、インポート時には使いません。)

  • リカバリー対象レコードID
    • FeedItem.csvのPARENTID
  • リカバリーしたい「取引先」項目のレコードID
    • FeedTrackedChange.csvのOldValueString

データインポート

データローダを使用したデータの挿入、更新、または削除 のヘルプを参考に、最初のメニューで"Update"を選択して取引先責任者(Contact)オブジェクトのレコードを更新します。 Step3: Mapping は以下の通り選択します。

Field Column Header Name
(FeedItem.csvのPARENTID部分のヘッダ名) Id
(FeedTrackedChange.csvのOldValueString部分のヘッダ名) AccountId

f:id:mark-hammer:20190602225347p:plain
データローダ Update画面

サンプルを用いた例

ここでは、「取引先責任者レコードIDが"003xxxxxxxxxxx2BBB"の【取引先名】項目が消された」という前提とします。 この場合、FeedTrackedChange.csv、FeedItem.csvは以下のようになります。(IDはサンプル、不要項目は省略)

  • FeedTrackedChange.csv
ID PARENTID TYPE
0D5xxxxxxxxxxx1AAA 003xxxxxxxxxxx2BBB TrackedChange
  • FeedItem.csv
FeedItemId FieldName DataType OldValueString NewValueString OldValueLastName NewValueLastName
0D5xxxxxxxxxxx1AAA Account Text サンプル取引先
0D5xxxxxxxxxxx1AAA Account EntityId 001xxxxxxxxxxx3CCC

上記データから、「取引先責任者レコードIDが"003xxxxxxxxxxx2BBB"の【取引先名】項目が【サンプル取引先】(ID:001xxxxxxxxxxx3CCC)から空データになった」ことが分かります。
ここから、リカバリー対象レコードIDであるFeedItem.csvのPARENTID(="003xxxxxxxxxxx2BBB")とリカバリーしたい「取引先」項目のレコードIDであるFeedTrackedChange.csvのOldValueString(="001xxxxxxxxxxx3CCC")を取り出し、以下のRecoveryData.csvを作成します。

  • RecoveryData.csv
    • ヘッダ名はデータローダでの取引先責任者Updateに使用する項目に合わせています。
Id AccountId
003xxxxxxxxxxx2BBB 001xxxxxxxxxxx3CCC

このRecoveryData.csvを使って、データローダで取引先責任者へのUpdateを実施します。

おわりに

今回は項目履歴管理が使えない状況でデータリカバリをしてみましたが、項目履歴管理が使えればデータローダからのExport1回でリカバリが可能になるため、項目履歴管理 のヘルプを参考に項目履歴管理の設定をすることを強くお勧めします。

*1:が、バックアップも兼ねて「すべてのデータを含める」を選択することを強くお勧めします。

Salesforce資格更新バッジ取得後もWebassessorに反映されないので問い合わせてみた

はじまり

私はSalesforce資格のうち、認定アドミニストレータとPlatform アプリケーションビルダーを持っているので、都度Trailheadで資格更新用のバッジを取得しています。 trailhead.salesforce.com

Spring'19の更新バッジは4/23に全て取得し、認定アドミニストレータは4/26に反映されたのですが、、

Platform アプリケーションビルダーの更新反映がGW明けても来ない。。

とりあえず2週間待っても反映されなければ問い合わせてもよかろう、ということで待ってみたのですが結局反映されず、問い合わせることにしたのでした。

問い合わせてみた

問い合わせ先ですが、以前Webassessorから届いた資格更新反映のメールの中に、

Have Questions? - ご質問がある場合
認定資格プログラムついてのお問い合わせは Webassessor ログイン ID を明記のうえ jcert [at] salesforce.com までお問い合わせください。

とあったので、そのメールアドレスに向けて、

  • Platform アプリケーションビルダーの資格更新が反映されていないこと
  • Webassessor ログイン ID

を記載してメールを送りました。

返事が来た

メールで問い合わせた当日に、担当者より以下の返信が届きました。

  • Webassessor ログインID にリンク済みの Trailheadアカウントでモジュール完了を確認した。
  • セールスフォースドットコム側で更新手続きを行う。完了後に連絡する。

そして、問い合わせ翌日にWebassessorから更新完了の連絡が、さらに翌日にセールスフォースドットコム担当者から更新完了の連絡が届きました。

おわりに

Webassessorでリリース試験を実施していた頃は全てWebassessor上で完結していたので更新反映の問題も少なかったのでしょうが、Trailheadとの外部連携が加わったことにより、更新が反映されない問題もそれなりに発生していそうです。 ただ、Trailheadバッジがリリース試験合格の証拠になるので、「Webassessorに更新が反映されないままリリース試験提供が終了したので、資格が無効になる」ということはおそらくないでしょう。

この投稿が同じような状況になった方の助けになれば幸いです。*1

*1:そもそも発生しないほうがいいのですがね。。

Web-to-リードフォームに、自動住所入力機能を追加する

SalesforceにはWeb-to-リードという、Webサイトからリードを作成するための機能があります。

このWeb-to-リードを簡単に利用するために、Salesforceにはリードの項目を選択すればその項目を登録するフォームを自動生成する「Web-to-リードフォームの作成」という機能があります。

この「Web-to-リードフォームの作成」機能は各項目を登録するためのテキストボックスを含む簡単なフォームを作成するだけのもので、その他の機能はありません。

今回は、このフォームに自動住所入力機能(郵便番号を入れると自動で住所を入力する機能)を加えてみます。

Web-to-リードフォームに加える項目

今回は、必要最小限と思われる以下項目を追加してみます。

  • メール
  • 会社名
  • 郵便番号
  • 都道府県
  • 市区郡
  • 町名・番地

Web-to-リードフォームが生成するHTMLコード

上記項目を選択した場合に生成されるHTMLコードは以下になります。(コメントアウト部分など、不要な個所は除きます)

<META HTTP-EQUIV="Content-type" CONTENT="text/html; charset=UTF-8">

<form action="https://webto.salesforce.com/servlet/servlet.WebToLead?encoding=UTF-8" method="POST">

<input type=hidden name="oid" value="00Dxxxxxxxxxxxx">
<input type=hidden name="retURL" value="http://www.google.com">

<label for="last_name">姓</label><input  id="last_name" maxlength="80" name="last_name" size="20" type="text" /><br>

<label for="first_name">名</label><input  id="first_name" maxlength="40" name="first_name" size="20" type="text" /><br>

<label for="email">メール</label><input  id="email" maxlength="80" name="email" size="20" type="text" /><br>

<label for="company">会社名</label><input  id="company" maxlength="40" name="company" size="20" type="text" /><br>

<label for="zip">郵便番号</label><input  id="zip" maxlength="20" name="zip" size="20" type="text" /><br>

<label for="country">国</label><input  id="country" maxlength="40" name="country" size="20" type="text" /><br>

<label for="state">都道府県</label><input  id="state" maxlength="20" name="state" size="20" type="text" /><br>

<label for="city">市区郡</label><input  id="city" maxlength="40" name="city" size="20" type="text" /><br>

<label for="street">町名・番地</label><textarea name="street"></textarea><br>

<input type="submit" name="submit">

</form>

ブラウザから見るとこう見えます。

f:id:mark-hammer:20190507003420p:plain
Web-to-リード画面

自動住所入力機能を追加する

今回は、ajaxzip3を使用して機能を追加してみます。

github.com

まず、ヘッダ部分に

<script src="https://ajaxzip3.github.io/ajaxzip3.js" charset="UTF-8"></script>

を追加します。

次に、「郵便番号」の行を

<label for="zip">郵便番号</label><input id="zip" maxlength="20" name="zip" size="20" type="text" onKeyUp="AjaxZip3.zip2addr(this,'','state','city','street');"/><br>

に変更します。

変更後のHTMLコードは以下になります。

<META HTTP-EQUIV="Content-type" CONTENT="text/html; charset=UTF-8">
<script src="https://ajaxzip3.github.io/ajaxzip3.js" charset="UTF-8"></script>

<form action="https://webto.salesforce.com/servlet/servlet.WebToLead?encoding=UTF-8" method="POST">

<input type=hidden name="oid" value="00Dxxxxxxxxxxxx">
<input type=hidden name="retURL" value="http://www.google.com">

<label for="last_name">姓</label><input  id="last_name" maxlength="80" name="last_name" size="20" type="text" /><br>

<label for="first_name">名</label><input  id="first_name" maxlength="40" name="first_name" size="20" type="text" /><br>

<label for="email">メール</label><input  id="email" maxlength="80" name="email" size="20" type="text" /><br>

<label for="company">会社名</label><input  id="company" maxlength="40" name="company" size="20" type="text" /><br>

<label for="zip">郵便番号</label><input  id="zip" maxlength="20" name="zip" size="20" type="text" onKeyUp="AjaxZip3.zip2addr(this,'','state','city','street');"/><br>

<label for="country">国</label><input  id="country" maxlength="40" name="country" size="20" type="text" /><br>

<label for="state">都道府県</label><input  id="state" maxlength="20" name="state" size="20" type="text" /><br>

<label for="city">市区郡</label><input  id="city" maxlength="40" name="city" size="20" type="text" /><br>

<label for="street">町名・番地</label><textarea name="street"></textarea><br>

<input type="submit" name="submit">

</form>

f:id:mark-hammer:20190507004733p:plain
機能追加後

おわりに

Web-to-リードフォームで生成されるHTMLコードは非常に単純なため、このまま使用することは推奨できません*1が、「もし生成コードに自動住所入力機能を追加するなら」ということで書いてみました。
参考になれば幸いです。

*1:Web-to-リード作成に必要な組織IDがHTMLソースコードから見えるため、スパムが大量に送られる危険がある

Salesforceでコーディングをしない3つの理由

Twitterのタイムラインを眺めていると、以下のブログ投稿を見かけました。

tyoshikawa1106.hatenablog.com

この投稿を読んで、思うところを書いてみます。

TL; DR

  • 引用したyoshikawa-sanのブログの内容は間違いではないよ。
  • でも、「コーディング導入により発生する考慮点」もあるよ。
  • Apex等の開発機能を使うか決めるときは、「標準機能では必要な機能を満たせないのか」や、「Salesforceの運用にかけられる費用」を考慮した方がいいよ。

はじめに

ここでいう「標準機能」とは、Salesforce.com Inc. における、開発者サポート に含まれない機能をいいます。

具体例

  • 標準機能対象
    • カスタムオブジェクト、カスタム項目
    • プロファイル、ロール、共有ルール
    • ワークフロールール
    • プロセスビルダー
    • レポート、ダッシュボード
  • 標準機能対象外
    • Apexクラス、Apexトリガ
    • Visualforce
    • Lightning Aura Component, Lightning Web Component
    • フロー
    • API

引用したブログについて

内容としては、以下のようなものでした。

  • 「Apex開発やコーディングはやったらダメ」という話があるけど、Apex開発やコーディングを考慮に含めることで、実装可能な機能に幅が広がるよ。
  • ワークフロールール、プロセスビルダーでは手が回らないところを、適切にコーディングすれば実現できるよ。

上記については、まさにその通りだと思います。
ワークフロールールやプロセスビルダーでは、起動条件が複雑な場合の表現が難しく、また項目更新順の指定ができないことから、「特定の順序で項目を更新したい」という希望の実現ができなかったりします。

ただし、Salesforceでの開発は、標準機能のみの利用では考慮する必要がなかった部分を考慮する必要があります。

追加考慮1: Salesforce運用コストの増加

Salesforceの勉強会等でよく聞く悩みに、以下のようなパターンがあります。

  • ケース1
    • 導入時に開発会社にコーディング含む機能開発を依頼した。
    • その後、Salesforceバージョンアップによりコーディング部分が正しく動作しなくなった。
    • 結果、機能変更がないのに追加で費用が発生した。
  • ケース2
    • 導入時に、社内のSalesforce開発者がコーディング含む機能開発を行った。
    • その後、Salesforce開発者が退職したため、何も分からない状況で運用を引き継ぐこととなった。
    • また、Salesforceバージョンアップによりコーディング部分が正しく動作しなくなったため、Salesforceの保守に想定以上の時間を取られている。

これらは標準機能のみ使用した環境でも起こりえますが、以下の理由により、改修に係るコストは一般的にコーディングを行った環境の方が多くなると考えられます。

  • コーディング箇所の改修の場合、当然ながらApex等の開発者スキルが必要になる
  • 問題となっている機能の無効化が簡単にできない(下記「追加考慮2」)
  • Salesforceサポートを使用できないケースが発生する(下記「追加考慮3」)

追加考慮2: 機能無効化のための作業

「トラブルが発生したため、まずは動作を無効化したい」となった場合、ワークフロールール、プロセスビルダーは「無効化」ボタンがあるため、「まずは無効化し、トラブルを止める」ことがだれでも可能です。
一方、Apexクラス、Apexトリガの場合、このナレッジに従い、外部ツールをインストールするところから始める必要があり、「だれでも対応可能」なものではありません。

このように、Salesforceで開発を行った場合、「改修だけではなく、機能無効化も難しい」ことを考慮する必要があります。

追加考慮3: Salesforceサポート範囲の違い

www.salesforce.com

「はじめに」に記載した標準機能の場合、Salesforceサポートではライセンス費用のみで提供されるBasic Success Plan でもサポート対象となります。
一方、開発者サポート の範囲は、追加費用が必要なPremier Success Plan が必要となります。

また、コーディング部分の開発者サポートは、「200行以下」という条件があります。
つまり、「コーディング部分でよく分からないエラーが起きたのでなんとかしてほしい」はもちろん、「1000行程度のApexクラスでエラーが発生したので解決方法を教えてほしい」という問い合わせも対象外になり、「エラー発生個所を200行程度まで絞る」作業が発生します。
このように、トラブル発生時の問い合わせも、ある程度の開発者スキルがないと難しいものになります。

おわりに

「開発機能を使えば、実装可能な機能の幅も広がる。」これについて異論はありません。
しかし、開発機能を使うことで、導入時には見えなかった縛りが発生することも、知っておいて損はないでしょう。

個人的には、Salesforceでコーディングを導入する際は安易に考えず、

  • 標準機能では実装が難しい機能が、業務上必須である。

かつ、

  • 導入、保守まで対応できる開発会社に依頼し、保守分の費用を支払うことができる。
  • または、Premierサポートプランを契約でき、かつ一定以上の開発者スキルを持つ人物が社内に複数おり、「1人辞めたらSalesforce運用が立ち行かなくなる」状況ではない。

という条件を満たすか考慮したうえで決定した方が良いと思います。

おまけ

この投稿では「コーディング導入によって発生するネガティブな面」を主に記載しました。

では、「標準機能を使うメリット」は何か、について資料探しをしたところ、以下のSalesforce公式ブログを見つけました。
標準オブジェクト、標準機能を用いることのメリットについて5つ+おまけの理由が記載されています。

www.salesforce.com

Salesforce導入時にコーディングを導入するか、しないかについては、「コーディング導入による機能の幅の広がり」だけではなく、「コーディング導入によるデメリット」や「標準機能のみを使用するメリット」も知っていただいたうえでご判断いただいた方がいいと考えております。
この投稿がご覧になった方々の参考になれば幸いです。

追記

AppExchange(パッケージ)についてはこの記事を書いているときに頭になかったのですが、

  • パッケージ自体はボタンをクリックすればアンインストール可能(ヘルプ
  • ただし、パッケージの内容前提でカスタムオブジェクトやワークフロー、プロセスビルダー等を組み込んだ場合、アンインストール不可
  • パッケージの中には「サポート提供なし」のものも多く、トラブルがあったとしても対応してくれないケースあり(Salesforce Labs提供のパッケージもサポート提供なし)

と、コーディングほどではないにしろ安易に本番環境に導入するのは控えた方がいいものになっています。

もちろん便利な機能を組み込んだパッケージやサポート付きのものもありますので、そのあたりはケースバイケースで利用するものだと思います。

追記2

以下の引用Tweetをいただきました。ありがとうございます。

IdeaExchange自体はユーザの声から機能を追加する、いい仕組みなのですが、「7万Point集めても3年以上Updateなし」といったケースもあるので、ステータスが「COMING IN THE NEXT RELEASE」とかになったものを参考にするものがいいのかな、と思います。*1

参考

IdeaExchange の概要とステータスについて

*1:ちなみにSandboxプレビューの後にリリースノートから消された機能も存在します

Trailhead モジュール:Programmatic Development with Custom Metadata Types

※この内容は2019/4時点のものです。

Use Metadata Relationship Fields

https://trailhead.salesforce.com/ja/content/learn/modules/custom_metadata_types_adv/cmt_md_relationships

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

【Challenge要約】

  • 以下内容のカスタムメタデータ型を作成してください。
    • 表示ラベル:Service Provider
    • オブジェクト名:Service_Provider
  • 「Service Provider」メタデータ型に、以下カスタム項目を追加してください。
    • データ型:メタデータリレーション
    • 関連先:Entity Definition
    • 項目の表示ラベル:Object Type
    • 項目名:Object_Type
  • 「Service Provider」メタデータ型に、以下カスタム項目を追加してください。
    • データ型:メタデータリレーション
    • 関連先:Field Definition
    • 項目の表示ラベル:Provider Field
    • 制御項目:Object Type
    • 項目名:Provider_Field
  • 「Service Provider」メタデータ型に、以下カスタム項目を追加してください。
    • データ型:メール
    • 項目の表示ラベル:Provider Owner Email
    • 項目名:Provider_Owner_Email

Develop with and Test with Custom Metadata Types

https://trailhead.salesforce.com/ja/content/learn/modules/custom_metadata_types_adv/cmt_develop

  • 説明:英語
  • Challenge:英語選択問題

Protect Custom Metadata Types and Records

https://trailhead.salesforce.com/ja/content/learn/modules/custom_metadata_types_adv/cmt_manageability

  • 説明:英語
  • Challenge:英語選択問題

Package Custom Metadata Types

https://trailhead.salesforce.com/ja/content/learn/modules/custom_metadata_types_adv/cmt_packaging

  • 説明:英語
  • Challenge:英語選択問題

Convert Custom Settings

https://trailhead.salesforce.com/ja/content/learn/modules/custom_metadata_types_adv/cmt_convert

  • 説明:英語
  • Challenge:英語選択問題

Trailhead モジュール:Lightning コンポーネントのセキュリティ

※この内容は2019/4時点のものです。

Lightning セキュリティの使用開始

https://trailhead.salesforce.com/ja/content/learn/modules/security-for-lightning-components/get-started-with-lightning-security

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

セキュアな Lightning コンポーネントの作成

https://trailhead.salesforce.com/ja/content/learn/modules/security-for-lightning-components/create-secure-lightning-components

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

【Challenge要約】

このChallengeは、通常のDeveloper Edition組織やTrailhead Playground組織ではなく、Kingdom Management DE組織を使用してください。
このChallengeの前に、Kingdom Management DE組織にサインアップしてください。

  • 「Secure Lightning」アプリケーションの「Secure Component Challenge」タブをクリックしてください。
  • 開発者コンソールを起動し、「LTNG_Secure_Component_Challenge」Lightningコンポーネントを開いてください。
  • 「LTNG_Secure_Component_Challenge」Lightningコンポーネントのコードから、脆弱性がある2か所を修正してください。
  • 「Secure Component Challenge」タブに戻り、検索実行後に表示される説明文にて、ページ動作が変化しないことを確認してください。

Lightning JavaScript コードの保護

https://trailhead.salesforce.com/ja/content/learn/modules/security-for-lightning-components/secure-your-lightning-java-script-code

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

【Challenge要約】

このChallengeは、通常のDeveloper Edition組織やTrailhead Playground組織ではなく、Kingdom Management DE組織を使用してください。
このChallengeの前に、Kingdom Management DE組織にサインアップしてください。

  • 開発者コンソールを開き、secureFilters という名前で MIME タイプが text/javascript の静的リソースを新規作成し、このファイルをアップロードしてください。
  • 開発者コンソールを起動し、「LTNG_SecureFilters_Challenge」Lightningコンポーネントコントローラを開き、「secureFilters」静的リソースを使用してユーザデータをエンコードしてください。
  • 「Secure Lightning」アプリケーションの「SecureFilters Challenge」タブをクリックし、動作を確認してください。
  • ヒント:ユーザデータはHTML要素に変換されるため、それに合わせた適切なエンコード機能を使用してください。

コンポーネント通信の保護

https://trailhead.salesforce.com/ja/content/learn/modules/security-for-lightning-components/secure-your-component-communication

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

【Challenge要約】

このChallengeは、通常のDeveloper Edition組織やTrailhead Playground組織ではなく、Kingdom Management DE組織を使用してください。
このChallengeの前に、Kingdom Management DE組織にサインアップしてください。

  • 「Secure Lightning」アプリケーションの「Access Control Challenge」タブをクリックしてください。
  • 開発者コンソールを起動し、「LTNG_Access_Control_Challenge_Wrapper」、「LTNG_Access_Control_Challenge」Lightningコンポーネントコントローラを開いてください。
  • 内部管理用属性が他のコンポーネントに公開されないよう、属性のアクセスレベルを変更してください。
  • 「Access Control Challenge」タブに戻り、動作を確認してください。
  • ヒント:一部の属性は公開されることを意図した作りになっています。しかし、王国(Kingdom Management DE組織)のためのアクセス制御を考えると、属性のアクセスレベルをコンポーネント内に保つことが常に最善です。

Trailhead モジュール:アプリケーションロジックの脆弱性防止

※この内容は2019/4時点のものです。

オープンリダイレクトの概要

https://trailhead.salesforce.com/ja/content/learn/modules/secdev_application_logic_vulnerabilities/secdev_app_logic_open_redirect

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

【Challenge要約】

  • 「Open Redirect」アプリケーションの「Open Redirect Basics Challenge」タブをクリックしてください。
  • 表示されたVisualforceページにてリダイレクト機能を見つけ、外部のWebサイト(例:https://www.google.com )にリダイレクトさせてください。
  • オープンリダイレクト攻撃が成功したら、「Challengeを確認して500ポイントを獲得」ボタンをクリックしてください。

標準のオープンリダイレクト防止の概要

https://trailhead.salesforce.com/ja/content/learn/modules/secdev_application_logic_vulnerabilities/secdev_app_logic_standard_open_redirect

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

【Challenge要約】

  • 「Open Redirect」アプリケーションの「Standard Redirect Protections Challenge」タブをクリックしてください。
  • 表示されたVisualforceページにてリダイレクト機能を見つけ、外部のWebサイト(例:https://www.google.com )にリダイレクトさせてください。
  • オープンリダイレクト攻撃が成功したら、「Challengeを確認して500ポイントを獲得」ボタンをクリックしてください。

コード内のオープンリダイレクトの防止

https://trailhead.salesforce.com/ja/content/learn/modules/secdev_application_logic_vulnerabilities/secdev_app_logic_preventing_open_redirect

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

【Challenge要約】

  • 「Open Redirect」アプリケーションの「Prevent Open Redirect Challenge」タブをクリックしてください。
  • 「Apex Controller」リンクをクリックし、ローカルリダイレクトのみ強制する方法でオープンリダイレクトを防止するようsave()関数を修正してください。

クロスサイトリクエストフォージェリ (CSRF) の防止

https://trailhead.salesforce.com/ja/content/learn/modules/secdev_application_logic_vulnerabilities/secdev_app_logic_csrf

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

【Challenge要約】

  • 「Cross-Site Request Forgery (CSRF)」アプリケーションの「CSRF Challenge」タブをクリックしてください。
  • 「Apex Controller」リンクをクリックし、「CSRF_Challenge」Visualforce ページからアクションハンドラを削除してください。
  • 「Approval Action (NO CSRF)」がある行にて、以下内容の変更を行ってください。
    • アクションに「{!approveReqNOCSRF}」を設定した「<apex:commandlink>」タグを追加してください。このメソッドは既にApexコントローラに追加されています。
    • <apex:commandlink>」タグの中に、value を「{!req.id}」、assignTo を「{!approve}」とした「<apex:param>」タグを追加してください。

クリックジャックの防止

https://trailhead.salesforce.com/ja/content/learn/modules/secdev_application_logic_vulnerabilities/secdev_app_logic_clickjacking

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

セキュアではないリモートリソースのやりとりの防止

https://trailhead.salesforce.com/ja/content/learn/modules/secdev_application_logic_vulnerabilities/secdev_app_logic_insecure_remote_resource

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

【Challenge要約】

  • 「Insecure Remote Resources」アプリケーションの「Insecure Remote Resource Challenge」タブをクリックしてください。
  • 「Visualforce Page」リンクをクリックし、セキュアではないリモートリソースが使用されている2箇所について、静的リソース(Challenge_Resourcesというzip形式のフォルダ)を参照する差し込み項目に差し替えてください。

Trailhead モジュール:Platform アプリケーションビルダー認定資格の更新 (Spring '19)

※この内容は2019/4時点のものです。

アプリケーション構築者向けの新機能の学習

https://trailhead.salesforce.com/ja/content/learn/modules/platform-app-builder-maintenance-spring19/learn-whats-new-for-appbuilders

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

カスタマイズされたホームページとコンポーネントを使ってみる

https://trailhead.salesforce.com/ja/content/learn/modules/platform-app-builder-maintenance-spring19/get-hands-on-with-customized-home-pages-and-components

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

【Challenge要約】

  • 新しいTrailhead Playground組織を起動してください。
    • 起動時にエラーになる場合は、数分待ってから再度起動してください。Trailhead Playground組織が読み込まれるまでに最大15分かかることがあります。
  • 以下内容で、新規Lightningページを作成してください。
    • 「ホームページ」を選択
    • 表示ラベル:Sales Home Page
    • テンプレート:標準ホームページ
  • 以下の標準コンポーネントを追加してください。
    • リッチテキスト
    • Chatter フィード
    • アシスタント
    • 今日の行動
    • 今日のToDo
  • 「リッチテキスト」コンポーネントに以下を設定してください。
    • テキストに「Welcome!」を追加してください。
    • フォントを「Salesforce Sans」に設定してください。
  • 「リッチテキスト」コンポーネントで、コンポーネントの表示を設定するためのフィルタを以下の通り追加してください。
    • 項目:権限>標準権限>Lightning Experience ユーザ
    • 演算子:等号
    • 値:True
  • Lightningページを保存、有効化し、組織のデフォルトとして割り当ててください。