APIとGASを活用してSalesforceを自社の業務フローに組み込む
こんにちは。マインディアの石渡です。
最近湿気が多いことに悩み、これを試してみたら意外と除湿ができてびっくりしてます。
そんなことはさておき、マインディアではSaaSモデルのビジネスを立ち上げていくにあたり、リードの管理や与実の精度を上げることなどを目的として最近Salesforceを導入しました。
自分が担当してセールスフォースさん、ベンダーさんとのやりとりを進めてようやく社内リリースができたのですが、リリース後も分からないことだらけで非常に苦労しています。こんなことなら前職で周りに人がたくさんいるときにSalesforceを使い倒しておけば良かった。。。
余談ですが、こんなただの愚痴ツイートにたくさんの反応をいただき、苦労されている方も多いんだなというので少し安心はしました笑。
導入に当たって特に気をつけているのは、セールスやコーポレートなど関わるメンバーに「導入したら前よりめんどくさくなったな」と思われないようにという点です。
そのため、Salesforceに業務フローを合わせるのではなく業務フローにSalesforceを合わせることを考えて導入・メンテナンスを行なっています。
本当はもっと攻めていろいろやっていきたいのですが、まずは守りの部分から入らないと「Salesforceめんどくさい」と社内で認知されてしまうと今後何もできなくなってしまうので、、、
今回は自分用のメモも兼ねて、SalesforceのAPIとGASを活用してセールスチームからオペレーションチームへの案件引き渡しを自動化する方法について書いていこうと思います。必要な手順が多く、メモしておかないと自分でも再現できる自信がないので。。。
何かご質問や、もっとこうした方が良いのではというアイデアありましたらぜひお気軽にtwitter@takahirostoneへのご連絡、noteへのコメントお願いします!
※まずはどんなものができあがるのか見てみたいという方は「実際にボタンを押してみる」のセクションまで飛ばしてください。
書いている人
株式会社マインディアというマーケティングDXのSaaSを扱う会社でBtoC・BtoB両方のマーケティングをしています。
Salesforceに関して完全に初心者レベルなので、自分が知らないだけでもっと便利なやり方があるんだろうなと思っています。App Exchangeとかでもっと楽にできないのかな。もしもっと便利な方法をご存知の方がいたら教えてください!!
自動化したフロー
商談が進み正式に受注したら、セールスがSalesforceに登録して「案件開始」のカスタムボタンを押す (※これは手動)
↓
案件進行に必要なGoogleスプレッドシートがテンプレートファイルからコピーされて新規ファイルが作成される & 案件に関わるファイルを格納するためのGoogleドライブフォルダが作成される
↓
Slackに案件開始が通知され、オペレーションチームに案件開始が通知される
従来は案件管理・売上管理をスプレッドシートで行っていたため、GASを使ってこのフローを自動化していました。
今回はGASにSalesforceのAPIとカスタムボタンを組み合わせて実装しています。
STEP 1. GASを作ってウェブアプリとしてデプロイする
Salesforce上でカスタムボタンが押されると実行されるGASを作成します。カスタムボタンにはURLを割り当てられるので、GASをURLから実行できるようにGASをウェブアプリとしてデプロイします。
今回の方法ではボタンの押し間違いを防ぐため2つのGASを作って一旦アラートページを挟んでから本体のGASを実行するようにしていますが、不要な場合はAのステップを飛ばしていきなりBでも大丈夫です。
A. Salesforce上でボタンを押してアラートページを表示するためのGAS
これは非常にシンプルで、Salesforceの案件に関する情報を引き継ぎつつ、次のGASを動かすためのボタンを表示するものです。
※GAS部分とhtml部分を分けています。
//コード.gs
function doGet(e) {
var html = HtmlService.createTemplateFromFile('index'); //htmlファイルを読み込みます。
//htmlファイルにSalesforceの項目から取得したパラメータを渡します
html.recordId = `${e.parameter.record_id}`;
html.companyName = `${e.parameter.company_name}`;
//省略していますので必要なパラメータを設定してください
return html.evaluate();
}
//index.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<!--ボタンのデザイン用にcssを入れてます->
<style>
button {
/* ブラウザ特有のスタイルを無効に */
-moz-appearance: none;
-webkit-appearance: none;
appearance: none;
/* 整える */
margin: 1em 0; /* 前後の隙間 */
padding: 0.6em 1em; /* 塗りの余白 */
font-size: 1em; /* フォントサイズ */
background-color: #1aa1ff; /* 背景色 */
color: #FFF; /* テキストカラー */
cursor: pointer; /* カーソルを指マークに */
border-radius: 3px; /* 角の丸み */
border: 0; /* 枠線を消す */
transition: 0.3s; /* ホバーの変化を滑らかに */
}
/* ホバー時(カーソルをのせた時)の見た目 */
button:hover {
background-color: #064fda; /* 背景色 */
}
div.main-text {
text-align: center;
}
</style>
</head>
<body>
<div class="main-text">
<p>案件を開始し、Slackに通知しますか?</p>
<p id="button">
<!-実際にBで作ったGASから取得した実行URLに置き換えてください。パラメータも必要なものを追加してください。->
<button type=“button” onclick="location.href='https://script.google.com/a/macros/*************/exec?record_id=<?=recordId?>&company_name=<?=companyName?>'">OK</button>
</p>
<p>キャンセルする場合は何も押さずにこのページを閉じてください</p>
</div>
<!--ボタンを押したあとに表示したままだと2回押してしまったりするので押したら消えるようにしています->
<script>
const target = document.getElementById('button');
target.addEventListener('click', () => {
target.style.display = "none";
}, false);
</script>
</body>
</html>
<参考>
新しいversionのGASで「ウェブアプリとしてデプロイ」する方法はこちらをご覧ください。
<参考>
デプロイできたら、GASを実行するためのURLを取得します。
※ デプロイ後にGASの内容を修正した場合は再度デプロイが必要になり、実行するためのURLも変わりますのでご注意ください。
※ 実行できる権限は自社ドメインを持っているユーザーのみにするのが安全かと思います。
B. アラートページでボタンを押すと実行されるGAS
このGASでは
1. Googleスプレッドシートのテンプレートをコピーして新しいスプレッドシートを作る ※ファイル名に案件名を引き継ぐ
2. できたファイルのURLをSalesforceの案件レコードの「個別シートURL」という項目に追記する ※この部分にSalesforceのAPIが必要になります。
3. Googleドライブにファイル格納用のフォルダを作成する ※フォルダ名に案件名を引き継ぐ
4. Slackに案件開始を通知する
ということを行なっています。
function authorization() {
var res = UrlFetchApp.fetch(
"https://login.salesforce.com/services/oauth2/token",
{
"method" : "POST",
"payload" : {
"grant_type": "password",
"client_id": "*****************", //コンシューマ鍵
"client_secret": "****************", //コンシューマの秘密
"username": "******@***.com", //ユーザ名
"password": "*******" //パスワード
},
"muteHttpExceptions": true
});
if (res.getResponseCode() == 200) {
var prop = PropertiesService.getScriptProperties();
prop.setProperty("session_info", res.getContentText());
}
}
function checkAuthorization() {
var prop = PropertiesService.getScriptProperties();
var sessionInfo = JSON.parse(prop.getProperty("session_info"));
var res = UrlFetchApp.fetch(sessionInfo.instance_url + "/services/data/v51.0", {
"method" : "GET",
"headers" : {
"Authorization": "Bearer " + sessionInfo.access_token
},
"muteHttpExceptions": true
});
if (res.getResponseCode() === 401) {
var res = UrlFetchApp.fetch("https://login.salesforce.com/services/oauth2/token", {
"method" : "POST",
"payload" : {
"grant_type": "password",
"client_id": "*****************", //コンシューマ鍵
"client_secret": "****************", //コンシューマの秘密
"username": "******@***.com", //ユーザ名
"password": "*******" //パスワード
},
"muteHttpExceptions": true
});
if (res.getResponseCode() == 200) {
var newSessionInfo = JSON.parse(res.getContentText());
newSessionInfo.refresh_token = sessionInfo.refresh_token;
prop.setProperty("session_info", res.getContentText());
}
}
}
function doGet(e) {
var folder = DriveApp.getFolderById('*************');
var spreadsheet = SpreadsheetApp.openById('*************0');
//必要なパラメータを追加します
var recordId = `${e.parameter.record_id}`;
var companyName = `${e.parameter.company_name}`;
var deliveryFolder = DriveApp.getFolderById('*************');
var folderName = recordId+'.'+companyName;
var newFileName = recordId + '_' + companyName + '_' + brandName + '_' + opportunityName;
var originalFile = DriveApp.getFileById('*************');
var newFile = originalFile.makeCopy(newFileName, folder);
var newFileUrl = newFile.getUrl();
deliveryFolder.createFolder(folderName);
// slackに通知
var textBody =
{'text' : '********' //必要な内容を記載します
};
var url = 'https://hooks.slack.com/services/************'; //slackのwebhook URL
var params = {
method: 'post',
contentType: 'application/json',
payload: JSON.stringify(textBody)
};
UrlFetchApp.fetch(url, params);
// salesforceの項目に記載
checkAuthorization();
var prop = PropertiesService.getScriptProperties();
var sessionInfo = JSON.parse(prop.getProperty("session_info"));
var kobetuSheetUrl = {
"**********__c" : newFileUrl //Salesforceから取得した項目のAPI名
};
UrlFetchApp.fetch(sessionInfo.instance_url + "/services/data/v51.0/sobjects/Opportunity/" + recordId, {
"method" : "PATCH",
"contentType": "application/json",
"headers" : {
"Authorization": "Bearer " + sessionInfo.access_token
},
"payload" : JSON.stringify(kobetuSheetUrl)
});
return HtmlService.createHtmlOutput('案件開始が通知されました!').setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
実際にこのGASで何を実行するかは企業により様々だと思うので、あまり細かく注釈は入れていません。
APIのアクセストークンの取得はこちらが参考になります。 ※今回はSoQLを使うわけではないので、その前段のトークン期限切れチェックまで。
<参考>
余談ですが、本当はアラートページを挟むのではなくSalesforce上でポップアップみたいなものを出そうと思っていました。
JavaScriptで書ければ「OKかキャンセルを選ぶポップアップを出し、OKなら指定のURLに遷移する」だけなのでめちゃくちゃ簡単なのですが、SalesforceのLightningページではカスタムボタンの動作にJavascriptを割り当てることができない仕様になっていました。
Lightningコンポーネントで作ればできそうだったのですが、仕様がめっちゃ独特でコード書くのが難しすぎて断念しました。。。書き方分かる方教えてください。
STEP 2. Salesforceでカスタムボタンを作る
カスタムボタンを作って、クライアント名や案件名など必要な項目の情報をパラメータで渡せるようにします。
ソースは「URL」を選択し、URLにはデプロイしたGASのURLを貼ります。URLには必要な項目をGASに渡すためのパラメータ設定が必要です。
<参考>
作ったらレイアウトに追加するのをお忘れなく。
STEP 3. 案件開始ボタンの表示を制御する
「受注していない案件を通知してしまう」「受注したのにフェーズを変更し忘れてしまう」ということを防ぐために、「案件開始」カスタムボタンを表示するかどうかをレコードタイプで切り分けます。
レコードタイプが分からない場合はこちらの記事が参考になります。
<参考>
まず、このボタンを表示するレイアウトとしないレイアウトをそれぞれ作成します。
レコードタイプも2つ作成し、案件開始ボタンを表示するレイアウトを紐づけたレコードタイプと表示しないレイアウトを紐づけたレコードタイプを用意します。
デフォルトは表示しないレコードタイプにしておき、受注フェーズに来たら表示するレコードタイプに変更するようにワークフロールールを作成します。
ワークフロールールについてはこちらが参考になります。
※最初から表示するレコードタイプで案件を作ってしまうことがないよう、表示するレコードタイプは一般ユーザーのプロファイルでは使えない設定にしておきましょう。
実際にボタンを押してみる
① Salesforceで「案件開始」ボタンを押す
② 表示されたアラートページで「OK」ボタンを押す
③ 実行完了画面が表示される
④ Slackに通知が飛ぶ
⑤ 新しく作られたスプレッドシートのURLがSalesforceに記録される
まとめ
今回ご紹介したように、SalesforceのAPIとGASを組み合わせることでかなり幅広いことができるなという印象でした。
Salesforceにはいろいろと便利な機能がありそうなので、今後もっと深ぼっていきたいなと思っています。
ぜひお話しましょう!
他にもSalesforce関連やその他マーケティング、Marketing Ops.関連で気になることがあればぜひお気軽にお話しましょう!
よろしければサポートお願いします!そのうちオリジナルドメインにしたいなと思っているのでその資金にさせていただきます!