【営業マンのための】営業活動を分析するために日々の予定をスプレッドシートに吐き出す【仕事効率化】
もはや週末の趣味になりつつあります。
営業マンのための仕事効率化シリーズ第3弾です。
背景
営業チームが今週何をして、どんな進捗があったのかを報告するために、
日々の営業活動を明らかにする必要があります。
例えば新規提案の打ち合わせが何件あったのか、社内の打ち合わせは何件あったのかなどなど。
1週間ごとにサマって報告しているのですが、
1週間分のスケジュールを目視で確認し、どういうアクションが何件あったのかを把握するのは意外と面倒です。
そこでgoogle calendarから予定を取得し、それがどういうアクションだったのかをスプレッドシートに書き出し、
それぞれのアクションを週次、Qごと、合計値で算出するGASを書いてみました。
実装
function myFunction() { var cal = CalendarApp.getCalendarById('メールアドレス'); var events = cal.getEventsForDay(new Date()); var spreadsheet = SpreadsheetApp.openByUrl("スプレッドシートのURL"); var sheet = spreadsheet.getSheetByName("マスタ"); for(var i in events){ if(isAttending(events[i].getMyStatus())){ addScheduleData(sheet, events[i]); } } } function addScheduleData(sheet, event){ sheet.appendRow([dateFormat(event.getStartTime()), event.getTitle()]); } function dateFormat(scheduleDate){ date = new Date(scheduleDate); var year = date.getFullYear(); var month = date.getMonth() + 1; var day = date.getDate(); return year + "/" + month + "/" + day; } function isAttending(status){ if(status == "YES" || status == "OWNER"){ return true; } return false; }
特に難しいことはしていません。
これまでやったことを組み合わせて実装できました。
var cal = CalendarApp.getCalendarById('メールアドレス'); var events = cal.getEventsForDay(new Date()); var spreadsheet = SpreadsheetApp.openByUrl("スプレッドシートのURL"); var sheet = spreadsheet.getSheetByName("マスタ");
メールアドレスに紐づくカレンダー情報を取得し、今日の予定を取得してきます。
そして予定を書き出すスプレッドシートのURLとシートを指定します。
for(var i in events){ if(isAttending(events[i].getMyStatus())){ addScheduleData(sheet, events[i]); } }
そしてそれぞれのイベントに対し、自分が出席するイベントのみスプレッドシートに書き出します。
自分が出席するもののみ書き出すのは、他の人に同期だけされた予定を自分の営業活動としてカウントしないためです。
function addScheduleData(sheet, event){ sheet.appendRow([dateFormat(event.getStartTime()), event.getTitle()]); }
スプレッドシートに追記しいてくのはこちら。
最後の行にどんどん追加していきます。
function dateFormat(scheduleDate){ date = new Date(scheduleDate); var year = date.getFullYear(); var month = date.getMonth() + 1; var day = date.getDate(); return year + "/" + month + "/" + day; }
スプレッドシートには、その予定の日付と予定名を書き出します。
event.getStartTime()では日付だけでなく時刻まで取得してきます。
今回は時刻は必要ないので、YYYY/MM/DDの形式で書き出すようにフォーマットしています。
function isAttending(status){ if(status == "YES" || status == "OWNER"){ return true; } return false; }
こちらは自分が出席するイベントかどうかを判別する関数です。
それぞれのイベントに対し、自分の参加ステータスが決められています。
Enumで定義されているようで、それぞれ以下のように定義されています。
他人に同期され、出席と回答したもの:YES
他人に同期され、欠席と回答したもの:NO
自分が作成したイベント:OWNER
同期されたが出席とも欠席とも回答していないもの:INVITED
未定と回答したもの:MAYBE
自分が出席するイベントは、YESとOWNERが該当するため、この二つのステータスを持つイベントのみスプレッドシートに書き出す対象としています。
これでスプレッドシートに予定が書き出されていきます。
あとはスプレッドシート側で予定をサマっていき、週次、クォーターの報告として活用します。
スプレッドシートには以下のように追記されていきます。
本当は予定を書き出した段階でその予定がどのカテゴリに入るのかまで判別できれば良いのですが、他の人に同期された予定名はプレフィックスのルールがバラバラだったりするので、予定のクラスタリングだけは手動でやるようにします。
該当するカテゴリのところに1を入力していきます。
このデータを元に、集計する数式を埋め込みます。
完成系はこんな感じ
今週のアクション数を出すところは、WEEKDAY()を利用します。
=TODAY() - weekday(now(), 3)
2018/3/12のところは上記の関数を埋め込みます。
2018/3/17のところはTODAY()でOKです。
週次の報告を上げるのは基本金曜日なので、いったんTODAY()で運用します。
これで今週の日付も自動で変わっていってくれるので、便利ですね。
そうして月曜日を起点にし、今週の月曜日以降、金曜日までの日付に該当する予定の各合計数を算出します。
これはSUMIFS()を使えば1発です。
sumifs('各カテゴリの列', '予定の日付列', ’月曜日以降' , '予定の日付列', ’今日まで')
こんな感じですね。
最後に
自動化楽しい!
【営業マンのための】google calendarの予定に応じて自動でメールを作成する【仕事効率化】
背景
たかだか1本のメール作成であっても、チリも積もれば山となり、バカにならないくらいの時間がかかってきます。
イレギュラーな内容のメールは仕方がないにせよ、御礼メールやリマインドメール、週報メールなどはある程度文章の方向性が決まっており、効率的にメール作成ができるはずだ、ということでやってみました。
やること
google calendarから予定を取得し、その予定に応じてメールを作成し下書きに保存する
使うもの
google app script, google calendar, gmail
実装
メールのテンプレートはスプレッドシートに保存しておきます。
メールの作成のタイミングでスプレッドシートを読みこみ、テンプレートを取得し下書きに保存します。
コードは以下の通り。
function myFunction() { var cal = CalendarApp.getCalendarById('メールアドレス'); var events = cal.getEventsForDay(new Date()); for(var i in events){ var event = new Event(events[i]) if(event.isGoOutTask()){ createDraft(event.companyName(), "往訪"); } } } function createDraft(companyName, mailType){ var mail = new Mail(mailType, companyName, "spreadsheetのURL"); GmailApp.createDraft('', mail.subject(), mail.body()); } Event = function(event){ this.event = event; } Event.prototype.isGoOutTask = function(){ if(this.event.getTitle().match(/^往訪/)){ return true; } return false; } Event.prototype.companyName = function(){ var companyName = this.event.getTitle().match(/【(.*)】/); return companyName[1]; } Mail = function(mailType, companyName, url){ var spreadsheet = SpreadsheetApp.openByUrl(url); var sheet = spreadsheet.getSheetByName(mailType); var startrow = 1; var startcol = 1; var sheetdata = sheet.getSheetValues(startrow, startcol, sheet.getLastRow(), sheet.getLastColumn()); this.companyName = companyName; this.subjectData = sheetdata[0][1]; this.bodyData = sheetdata[1][1]; } Mail.prototype.subject = function(){ return this.subjectData; } Mail.prototype.body = function(){ return this.companyName + String.fromCharCode(10) + this.bodyData; }
イベントの発火は時間駆動型とし、明け方1~4時の寝てる間くらいに実行されるようにセットしています。
発火するとmyFunction()が呼ばれ、google calendarからその日の予定(events)を取得してきます。
var cal = CalendarApp.getCalendarById('メールアドレス'); var events = cal.getEventsForDay(new Date());
そしてそのイベントが往訪であれば、gmailにメールを作成します。
以前もありましたが、予定入力のルールとして、予定名のプレフィクスに予定の種類を入力しています。
takaaki-z.hatenablog.com
MTGの予定なら「会議:ほげほげ打ち合わせ」みたいな感じです。
外出の予定の場合は「往訪:ほげほげ」と入力しているので、これをチェックし予定の種類を判別します。
if(event.isGoOutTask()){ createDraft(event.companyName(), "往訪"); } Event.prototype.isGoOutTask = function(){ if(this.event.getTitle().match(/^往訪/)){ return true; } return false; }
メール作成はもともと用意されてるGmailオブジェクトのcreateDraftメソッドを叩くだけです。
function createDraft(companyName, mailType){ var mail = new Mail(mailType, companyName, "spreadsheetのURL"); GmailApp.createDraft('', mail.subject(), mail.body()); }
今回はメールの本文の宛名として相手の会社名を入れたいので、google calendarの予定のタイトルから宛名となる情報を取得してきます。
予定入力のルールとして、往訪する会社の名前を「【】」でくくって入力するようにしているので、こちらも正規表現で引っ張ってきます。
Event = function(event){ this.event = event; } Event.prototype.companyName = function(){ var companyName = this.event.getTitle().match(/【(.*)】/); return companyName[1]; }
メールの本文はスプレッドシートからテンプレを引っ張ってきてます。
Mail = function(mailType, companyName, url){ var spreadsheet = SpreadsheetApp.openByUrl(url); var sheet = spreadsheet.getSheetByName(mailType); var startrow = 1; var startcol = 1; var sheetdata = sheet.getSheetValues(startrow, startcol, sheet.getLastRow(), sheet.getLastColumn()); this.companyName = companyName; this.subjectData = sheetdata[0][1]; this.bodyData = sheetdata[1][1]; } Mail.prototype.subject = function(){ return this.subjectData; } Mail.prototype.body = function(){ return this.companyName + String.fromCharCode(10) + this.bodyData; }
件名と本文をあらかじめスプレッドシートに入力しておき、対象となるセルを参照し取得します。
件名はそのままひっぱってくればOK。
本文は、宛名のみgoogle calendarの予定に合わせて入力するので、整形したものを返します。
改行は文字コードを直接指定して入力するようにしました。
これで、朝起きたら御礼メールの下書きが作成されます。
あとは実際に往訪時に話した内容に合わせて少し編集すればメール作成が完了します。
さすがに往訪時の話の内容を自動入力するのは難しいので、ここは手動で編集します。
また送信先アドレスも今回は手動入力を想定しています。
往訪前にアドレスがわからないパターンもあるので、今回は自動化しませんでした。
実際に運用してみて
実際にこれを1週間運用してみましたが、想像以上にメール作成が楽になりました。
こういった細かいところからちょっとずつ自動化していき、より本質的な仕事に時間が割けるようにしていきます。
これも働き方改革!
【営業マンのための】google app scriptを使って、翌日に外出の予定があれば交通費精算のタスクをtrelloに自動で登録する【仕事効率化】
やりたいこと
google calendarにて翌日の予定を確認し、外出の予定があれば交通費精算のタスクをtrelloに追加する
つかうもの
google app acript / google calendar / trello
背景
基本的に自分のタスクは全てtrelloで管理しています。
カンバン方式のようにレーンを作り、それぞれ以下のようなレーンでタスクを分類しています。
緊急度は高くないがやらないといけないこと / 今週やること / 今日やること / 今やってること / Blocker / 完了
営業になって、外出の予定がすごく増え、ほぼ毎日外に出ています(?)
外出に伴う交通費の申請処理を月末に一気にやると時間がかかる & 漏れが出てくるので、基本的には日ごとに入力を行うようにしています。
毎日やるタスクを毎日trelloに登録するのもめんどくさいし、でも登録していないとたまに入力を忘れる日が出てくるので、なんとかしたいなあと思ったのが背景です。
ちなみに何か仕事が入ってきたらとりあえずtrelloに登録、緊急度を他のタスクと比較し、次にやるタスクの選定を行っています。
基本的に何かをずっと覚えておくのはつらいので、trelloに登録したらそのタスクのことは一旦忘れます。
覚えておかないといけないこと(タスクの詳細やメモなど)は説明欄やコメントに残し、目の前の仕事に集中できるようにします。
そんなやり方をやっていると、交通費申請なんてどうでもいいタスク(よくないけど)、忘れてしまうわけです。
trelloは必ず頻繁に確認するので、自動でタスク登録してくれたら毎日忘れずに処理できるかなと思ったわけです。
実装
trelloのAPIトークンを取得する
さて本題。
まずはAPIを叩くためのトークンの発行を行います。
trelloにログインした状態で以下のURLにアクセスします。
https://trello.com/1/appKey/generate
まずは開発者向けのキーが表示されるので、これをメモ。
次に「あなたは手動でTokenを作られます。」のTokenの部分がリンクになっているのでこれをクリック。
遷移後に認証を求められるので、READとWRITEの権限があることを確認し、認証します。
認証後、APIトークンが表示されるので、こちらもメモ。
この段階で、まずは認証が正しく完了したかを確認します。
以下のURLをブラウザに入力します。
https://trello.com/1/members/"USERNAME"/boards?key="KEY"&token="TOKEN"&fields=name
"USERNAME"にはtrelloのユーザIDを
"KEY"と"TOKEN"にはそれぞれ先ほどメモしておいた値を入力します。
正しく認証が済んでいると、自分のアカウントに登録しているボードの情報が表示されると思います。
ちなみに最後の「fields=name」を入れることで、そのボードに対する名前とIDだけを表示させることができます。
これでtrelloを操作する準備が整いました。
google calendarの予定を取得する
calendarの予定を取得するメソッドは既にあり、取得対象となるメールアドレスのみ指定すればOK。
```
CalendarApp.getCalendarById('自分のメールアドレス');
```
あとは検査対象となる日付を与え、予定を取得します。
なお、予定を入力する際にその予定のカテゴリ(外出、来客、会議など)をプレフィックスとしてつけているので、外出の予定かどうかはこのプレフィックスを用いて判別します。
最終的に出来上がったコードは以下の通り。
function myFunction() { var date = new Date(); var day = date.getDate(); if (!isExistedCard(day) && isOutOfOffice()) { addCard(day); } } function isOutOfOffice(){ var cal = CalendarApp.getCalendarById('自分のメールアドレス'); var date = new Date(); var events = cal.getEventsForDay(date); for(var i in events){ if(events[i].getTitle().match(/^外出/)){ return true; } } return false; } function isExistedCard(day){ var data; var url = 'https://api.trello.com/1/lists/"LIST_ID"/cards?key="KEY"&token="TOKEN"&fields=name'; var options = { 'method' : 'get', 'muteHttpExceptions' : true, } data = UrlFetchApp.fetch(url, options); var tasks = JSON.parse(data); for(var i in tasks){ var task_name = tasks[i]['name']; if(task_name == day + '日交通費処理'){ return true; } } return false; } function addCard(day){ var listId = 'LIST_ID; var trelloKey = 'KEY'; var trelloToken = 'TOKEN'; var url = 'https://api.trello.com/1/cards/?key=' + trelloKey + '&token=' + trelloToken; var options = { 'method' : 'post', 'muteHttpExceptions' : true, 'payload' : { 'name' : day + '日交通費処理', 'desc' : 'This is test.', 'due' : '', 'idList' : listId, 'urlSource' : '' } } UrlFetchApp.fetch(url, options); }
正直ソースコードはすっちゃかめっちゃかです。いずれクラス化(ライブラリ化?)したりして最適化したいとは思いますが、今回は諦めました。
cronのように特定の時刻になればこのプログラムが起動されるように設定しております。
すでに同じタスクが登録されているかどうか、外出の予定があるかどうかを確認し、交通費申請タスクを「今日やるタスク」リストに追加しています。
ユニットテストを書かずにコードを書いたのは久々で、めんどくさいな〜と思いながらも手動テストでせこせこやってました。
コードの最適化もそうだし、テスト環境も入れたいと思ってはいますが、もうちょっと本格的に自動化を進める段階で検討します。
実際に導入してみて
交通費処理漏れがなくなりました。タスクはtrelloに入れる、trelloを見る習慣がこびりついているので、絶対に思い出せます。
今回得られる恩恵は小さいものですが、今後も自分の作業を自動化していきたいと思います。
第5回アジャイルディスカッションに参加してきた!
今回も参加してきました、アジャイルディスカッション!
前回参加したレポートはこちら。
takaaki-z.hatenablog.com
今回我々のチームで取り上げられたテーマは以下の通りでした。
- 振り返りのやり方
- どのようなメトリクスを収集し、どのように活用しているか
- ペア固定によるペアごとの属人化について
ディスカッション
議論のスタートは「KPT以外の振り返りの方法はどんなものがあるのか」でした。
ずっとKPTでやっているとマンネリ化してくる、どうしてもProblemに意識がいってしまいポジティブになれない、あがってくるKeepやProblemの質が低いなどなど、様々な話が出てきました。
ディスカッション中に出ていたやり方としては、YWTM・公園で飲みながらやる・お絵かきをするなどが上げられていました。
YWMTは我々も取り入れていて、なかなか良いなと感じています。
いきなりKeepを出すのではなく、まずはスプリントでやったことを洗い出し、出てきたYに対して気づきを上げていくので、KPTよりももれなく気づきを抽出できると感じています。
私は、振り返りはチームのパフォーマンスをもっと高めていくためにやるものだと思っているので、パフォーマンス向上に大きく寄与しないような、単なる「よかったね」をできるだけ減らしたいなあと思っていました。
しかし、まずは「よかったね」が気軽にたくさん言える雰囲気になっているのはチームとして健全だ、と言われ、ハッ!としました。
そしてその「よかったね」から何がよかったのか、どうしたらもっとよくなりそうかなどを引き出していくのがファシリテーターの役割だと言われ、これまたハッ!と。
良い気づきでした。
あとは、プロダクトオーナーに対して、振り返りやトライの内容をオープンにし共有していくのも面白いなと感じました。
POも巻き込んで全体で問題に取り組んでいく雰囲気の醸成、そして開発者とは違った目線からの意見が出てきたりなどいいことがたくさんあるとのことでした。
リモートワーク×ペアプロにトライしてみた話
結論
リモートペアプロ超快適やん!
という話です。
さて、こんにちは。
働き方改革で今年の3月くらいから全社でリモートワークを導入しました。
リモートワークについてはエンジニア交流会でもLT登壇させていただき、リモートワーク導入の話から、現在のスクラムチームでの活用方法をお話しさせていただきました。
詳細についてはこちらのブログをご覧ください。
gaiax.hatenablog.com
さて、今回のお話ですが、リモートワークしながらペアプロをしてみた話をします。
ちなみに、最近はモブプログラミングがすごく取り上げられていますね。
モブプロはまだトライしたことはありませんが、うちのチームではペアプロ・ペア作業は当たり前のように行っています。
最近はリモートワークをよくやるので、一度リモートでペアプロやってみよう、となったのでそのお話。
リモートペアプロをやってみて
リモートペアプロの際に使うのはslackだけです。
slackで音声通話しながら、画面共有機能を使って画面を共有するだけ。
ドライバーが画面を共有し、ナビはそれを見てナビする。
本当にたったこれだけなのですが、普通にペアプロ実施できました。
画質も音声もキレイにいけてました。
また、出社したとしても、自社の執務室だけでなく、同ビルの地下や屋上などで作業をすることもあります。
実際に今働いている場所がNagatacho GRIDというところです。
こちらのブログでも紹介されているように、非常に多様な働き方ができるビルです。
外部ディスプレイをわざわざ地下に運んでペアプロすることもありますが、いかんせん重たいのでめんどくさいのです。
このように、外部モニタがないときは、一緒にいるときでも、slackの画面共有機能を使ってペアプロをすることもあります。
やりとりは一緒にいるため、マイクをミュートにし、直接行います。
ちなみに音声通話の代わりにテキストでもやってみたのですが、これは微妙でした。
音声通話よりもリアルタイム性がないし、なにより文字入力がめんどくさい。
あとPCのスペックにもよるんでしょうが、slackの画面共有中のテキスト入力がすごく重かったです。
もちろん、slackじゃなくても、音声通話と画面共有ができるツールならなんでもいいと思います。
まとめ
以上、ビデオ会議ツールの画面共有機能を使ってのリモートペアプロがすごい快適やっていう話でした。
想像以上に快適に作業できたので、今後も続けていこうと思います。
アジャイルディスカッションに参加してきた!
先日アジャイルディスカッションに参加してきました!
アジャイルディスカッションについてはこちらを参照ください。
takubon.hatenablog.com
本イベントでは、アジャイルを導入したり進めていく上での悩みごとなどを共有し、ディスカションしていきます。
進め方
はじめに主催者の木村さんからのイントロダクションがあり、それぞれがディスカッションしたいテーマを列挙します。
そして4~5人くらいのグループに分かれ、グループごとにテーマを選び、ディスカッション開始となります。
なお、イベント中はビールを飲みながらのディスカッションOKというスタイルでした。
ディスカッション内容と気づき、次のアクション
V字モデルの下の方だけスクラムやっているチームが多い
これは確かに、と思いました。まさにウチのチームも同じような感じで、最初に決めた受け入れ条件を守ることを大事にしていたように感じました。実際にストーリーの実装を開始した後に、「こっちの方がいいんじゃね?」と思っても、「でも受け入れ条件がこうやから・・・」と受け入れ条件をひっくり返したりしてなかったなと。まさにV字の上の方の変化を受け入れず、下の方だけスクラムしていたように思います。
どうすれば成功するかなんて誰にもわかりません。だからこそ、実際にやってみて違和感を感じたり、もっとこうしたほうがいい!と感じたのなら、素直にそれを提案し、受け入れ条件の見直しから柔軟にやっていくべきだなと思いました。
プロダクトバックログの上から機械的にストーリーをこなすだけになってないか。スプリントゴールが「価値を届けること」ではなく、「スプリントを終わらせること」になっていないか
ただ機械的にこなすだけなら、当然ミニマムしかあげないし、ストーリーの実現に伴う副作用的な変化は排除するようにしか動かんなあと。やはりそのストーリーを提供することでどんな価値が届くのか、お客さんのどんな課題が解決されるのかをしっかり意識することが大事ですね。課題解決のための価値提供であることをしっかり認識しておけば、常に最適解を探すようになり、V字モデルの下だけスクラムとかからも脱却できるのかなあと思いました。
これからは「この価値を届けることで、お客さんの抱えているどんな課題が解決されるのか」を意識していこうと思います。具体的にはリファインメントの時に、その質問を投げてみることや、ストーリーの着手前に、この価値が届き、お客さんがどうその価値を体験するのかをストーリーベースで妄想すること、それをペア作業者と一緒にやること、などをトライしていこうと思います。
全員で一つのプロダクトを作り上げる意識
開発チームだけでなく、営業やサポート、ステークホルダー全員で1つのプロダクトを作りあげ提供していく、そこに関わるものとしての当事者意識を持つことが大事だという話も上がりました。
これに関しては、最近社内で雰囲気が少しずつ変わりつつあるように感じます。私個人としても、開発以外の視点で捉える必要があると思います。例えば、作っておしまいじゃなくて、営業の人がもっと売ってきやすくするために何ができるかなどを考えたりすることが必要かなと思いました。
失敗を恐れず行動していくためには、失敗を褒める文化を作ることが大事
これも思い当たる節がちらほら。最近完成させたストーリーの中で、あくまでストーリーの実現がメイン。それに伴う副作用的な変化はできるだけ排除しよう、という思いがありました。これは、変化による失敗を恐れていたからだなあと感じ反省しました。たとえ副作用的な変化があったとしても、より価値が届くのはどちらかを考えた結果であれば、どんどん変化を受け入れていくべきだと思ったし、行動していくべきだなと感じました。
チームのミーティングをLEAN COFFEEで実施してみた
久しぶりのブログ更新です。
先日、以下のブログを読んでLEAN COFFEEというミーティングの手法を知ったので、早速チームのミーティングで実践してみました。
やってみて思ったこと
ダラダラと議論が続くことはない
やっぱりこれが一番大きいなと思いました。
タイムボックスを決めて議論するため、ダラダラと議論が続くことはありません。
時間がきたら継続判断を全員で行うので、議題の切り替えも非常にスムーズでした。
やってみると、意外と2、3回目の継続判断で次のトピックに移ることが多く、いつもよりコンパクトに議論ができた感じがありました。
また、全員の意思を確認してから次の議題へと移るので、「もっと話したかったのに・・・!」という不満がたまることもなかったように感じます。
普段は結構議論が長くなりがちなので、実施前は、今回も時間内に議論し尽くせるかな〜と若干不安でしたが、アクションプランを出すところまで含めてきっちり議論しきり、時間内に終わることができました。
全員が本当に議論したいと思っているトピックが挙がる
最初のステップで、議論したいトピックを挙げ全員で投票し、投票数が高いものから議論していくため、全員が議論したいと思っているトピックが優先度高く取り上げられます。
ファシリテーターがトピックをあらかじめ用意してくるより、良いなと感じました。
多くのトピックについて議論できる
また、ダラダラと議論しないことで、時間内に多くのトピックを取り上げることができました。
まとめ
議論したいトピックを全員で決めて、タイムボックスを切り、議論の継続判断を行う
今後も、この手法を取り入れていってみようと思います。