ついカっとなって、本の購入申請ワークフローを作った話

この記事は、[JSL (日本システム技研) Advent Calendar 2019 - Qiita ](https://qiita.com/advent-calendar/2019/jsl) の5日目の記事です

## TL;DR
社内の書籍の購入申請の仕組みを紙から電子に切り替えようと試みた話です。
（Slackのワークフローと`GAS`を使って）

## ことの始まり
>「Grid Systems in Graphic Design」の和訳版があった。

<img width="841" alt="スクリーンショット 2019-12-05 10.29.24.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434453/aa54fcce-9989-45be-d015-886fc3ef0e24.png">
[Grid systems in graphic design｜グリッドシステム　グラフィックデザインのために \| ボーンデジタル](https://www.borndigital.co.jp/book/15731.html)

社内Slackの分報にそんなポストが。欲しくてたまらなかったけど、試用期間中の僕の手元にそんなお金はなかった。


<img width="419" alt="スクリーンショット 2019-12-05 10.29.36.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434453/131fd19d-a8fc-43c9-6499-d6293a99ca2a.png">
「そうだ。本の購入申請の仕組みがあるじゃないか」


技術書や業務に関係のある書籍なら会社に購入してもらう仕組みが我が社にはある。
これを利用しない手はない。そう思って僕は手続きを確認した。

Google Driveから申請用紙を探しだし、それを紙を印刷して、必要事項を記入して、ハンコを押して上長承認、社長承認を得れば、購入可能、そんなよくある仕組みだ。

早速紙を印刷して必要事項を記入しハンコを押して、、これで上長承認さえもらえすれば、あとは買うだけ。

ただこれはだいぶ時間がかかりそうだ。。。上長に「業務で役に立つのか」「費用の回収計画を説明してくれ」
など購入理由を問い詰められ、書類を書き直し再度レビューしてもらい。。
２ヶ月はかかるな。。すぐに読みたいのに。。
まあ本自体もWEBだと軒並み在庫はないし。。高値をつけて転売されている。。ちょうど良いか。。
などと考えていた。

その日はすでに上長が客先で不在であったため、書類をもって総務のひとに相談した。
「はい。分かりました。あしたには買えますよ」
「！？＞＜＋L＋L <POKL`*」

驚愕だ。早すぎる。
メンバーが隣の街の書店で本の在庫を見つけてくれて、さらに買いに走ってくれて、
<img width="762" alt="スクリーンショット 2019-12-05 11.14.05.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434453/244032c9-d75a-54f3-ace3-520de0d733a9.png">


週明けには本を手に入れることができた。
![IMG_6180.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434453/0538fabf-78e4-5232-93d3-1963bab66497.jpeg)



そんな最高な仕組みがあるにもかかわらず、社内ではあまり使われていないようだ。（去年度9冊、本年度7冊）
メンバにヒヤリングしてみたところ、

* 申請書を探すのがだるい
* 申請書を印刷して書くのがだるい
* そもそもそんな仕組みがあったの？？

ということで、Slackで簡単に申請できるように試みてみました。

## 技術
* Slack
    * ワークフロー
    * InCommingWebhhok
    * OutGoingWebhook
* GAS

## 実装の流れ
1. チャンネルを作成し、Slackワークフローを登録する
2. Botを作成し、IncomingWebhookを利用可能にする
3. GAS
4. GASをOutgoingWebhookに登録

以下にかいつまんで書きます。

## チャンネルを作成し、Slackワークフローを登録する
本の購入申請用のSlackチャンネルを作成し、次にワークフロービルダーを選択します。
<img width="254" alt="スクリーンショット 2019-12-05 10.38.02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434453/0dee9118-25b2-2822-4c76-e3f74b1d72ee.png">


作成ボタンをより、名前をつけて 次へ を押下。
<img width="494" alt="スクリーンショット 2019-12-05 10.40.15.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434453/0609cf86-b62c-a168-5caf-2bb4ba5f4075.png">




アクションメニューを選択し、適宜設定。
新規に追加したチャンネルを設定します。
<img width="481" alt="スクリーンショット 2019-12-05 10.41.02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434453/c616d9e9-ad01-0f4f-aaf6-b0d27459515e.png">

必要事項を記入するフォームを追加し、右上の公開するボタンを押下。
<img width="1013" alt="スクリーンショット 2019-12-05 22.19.13.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434453/7c54fdad-8be6-eaac-2434-c9af1a97aa3c.png">

すると、チャンネルの右上にかみなりのようなマークが追加されワークフローが利用可能になります。
![workflow.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434453/f920e803-3898-2f16-574f-515e30331b01.gif)

フォームに記入し、送信を押下すると、以下のような感じでSlackに投稿されます。
<img width="839" alt="スクリーンショット 2019-12-05 22.31.12.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434453/1bfb63f4-cb4d-b87f-e99d-fd405386fa74.png">


## Botを作成し、IncomingWebhookを利用可能にする
[Slack API: Applications \| Slack](https://api.slack.com/apps) で「Create α Slack App」を選択。
App Name,Development Slack Workspace を設定。
![スクリーンショット 2019-12-05 10.54.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434453/6873aa7e-dd63-2fb3-0657-72780dfa2fd7.png)

Activate Incoming Webhooks を有効にし、投稿したいチャンネルを設定。
![スクリーンショット 2019-12-05 10.55.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434453/bfb6a85a-b2fb-4377-c033-60712729e3f4.png)

COPYボタンを押下しWebhook URLを取得しておきます。（これがGASからSlackへ投稿するときに必要）
![スクリーンショット 2019-12-05 10.59.45-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434453/9d6bbb32-b9c7-c33b-e7c6-6f141712592f.png)


## GAS
Google Driveにて任意のスプレッドシートを作成し整えます。
<img width="1499" alt="スクリーンショット 2019-12-05 22.26.24.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434453/cf46e39f-7b3f-9056-c13e-d0ad13ef6779.png">

スプレッドシートのメニューより「ツール->スクリプトエディタ」を選択。
<img width="286" alt="スクリーンショット 2019-12-05 21.59.55.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434453/9b95bc01-0d60-38b0-3a1f-e2aa59081d55.png">

コードは以下のような感じ。

```js
//Outgoing Webhook のTOKEN（後述）
var WEBHOOK_TOKEN = "xxx";
// SlackからのPostを受け取る
function doPost(e) {
  if (WEBHOOK_TOKEN != e.parameter.token) {
    postSlack("debug:tokenが不正なアクセスがあったらぼ〜" ); 
    throw new Error("invalid token.");
  }
  try{
    var data = e.parameter.text;
    record(data);
    postSlack("申請完了ラボ！\n承認をしばしまつラボ〜！"); 
  }
  catch (e) {
    postSlack("debug:失敗ラボ〜〜！\nたぶんフォーマットエラーらぼ〜 :tsurai: \n ```" +  e + "```" ); 
  }
}

// Slackへ投稿する
function postSlack(text){
  //incomingwebhookを設定する
  var url = "前述のWebhook URL";
  var options = {
    "method" : "POST",
    "headers": {"Content-type": "application/json"},
    "payload" : '{"text":"' + text + '"}'
  };
  UrlFetchApp.fetch(url, options);
}

// スプレッドシートへ記入する
function record (data) {
  var recordsheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('購入申請'); 
  
  //入力する行のオブジェクトを取得
  var lastrow = recordsheet.getLastRow();
  var recordrow = lastrow + 1;
  
  //投稿日時を取得
  var formatdate = Utilities.formatDate(new Date(), 'Asia/Tokyo', 'yyyy-MM-dd HH:mm:ss');
  //Slackのポストを整える
  var lists = data.split(/\*/)
  //ユーザ名を整える
  applicant_user = lists[2].split(/\<|\>/)
  auth_user = lists[12].split(/\<|\>/)
  //スプレッドシートを更新する
  recordsheet.getRange("A" + recordrow).setValue("=ROW()-1"); // No
  recordsheet.getRange("B" + recordrow).setValue(formatdate); // 申請日
  recordsheet.getRange("C" + recordrow).setValue('=VLOOKUP("' + applicant_user[1] + '",ID!A:B, 2,FALSE)'); // 申請者
  recordsheet.getRange("D" + recordrow).setValue(lists[4]); // 本のタイトル
  recordsheet.getRange("E" + recordrow).setValue(lists[6]); // 税込金額
  recordsheet.getRange("F" + recordrow).setValue(lists[8]); // URL
  recordsheet.getRange("G" + recordrow).setValue(lists[10]); // 購入希望理由
  recordsheet.getRange("H" + recordrow).setValue('=VLOOKUP("' + auth_user[1] + '",ID!A:B, 2,FALSE)'); // 承認者
}
```
SlackからPOSTされた本文は、e.parameter.textの中に全て入っており、今回はsplitし整えています。

また、IDは普段見ないほうのユニークな`id`が含まれているため、`id`と名前の紐付けをどこかでやってあげる必要がありました。
今回はスプレッドシートの別シートに`id`と名前の一覧を作成しています。
`GAS`ではそこを参照するような式をスプレッドシートに出力しています。

参考までにSlack全メンバの`id`は以下より取得可能。
[users\.list method \| Slack](https://api.slack.com/methods/users.list)

## GASをOutgoingWebhookに登録

`GAS`のメニューより「公開->ウェブアプリケーションとして導入...」を選択。
<img width="301" alt="スクリーンショット 2019-12-05 22.56.54.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434453/fcfc5eeb-323a-e884-5d66-22755b1f433b.png">

適宜入力し、web app URL を取得。
（これをSlackのOutgoingWebhookに登録します。）
<img width="451" alt="スクリーンショット 2019-12-05 22.58.43.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434453/ef50031a-f880-22f3-3951-10a4b00fe882.png">

Slackにて「Slackをカスタマイズ」を選択。
<img width="264" alt="スクリーンショット 2019-12-05 23.01.43.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434453/d746b581-4c51-8c1d-eeb1-bc88b09102ed.png">

「App 管理」を選択し
<img width="264" alt="スクリーンショット 2019-12-05 23.02.04.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434453/fc18bca5-2fdb-c095-9ba9-c53ea882ea37.png">

App ディレクトリよりOutGoing Webhookを選択
<img width="707" alt="スクリーンショット 2019-12-05 23.03.00.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434453/c75fce87-afc1-9357-d414-a99113ef42b1.png">

チャンネルや引き金となる言葉を入力し、URLには`GAS`のweb app URLを設定。
前後してしまいますが、ここで取得可能な**トークン**の値を`GAS`のコードの先頭部分に貼ってください。(グレーの部分)
<img width="945" alt="スクリーンショット 2019-12-06 9.15.44.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434453/dabea4b8-1494-650e-141c-57218ba9b6e9.png">

これで完了です。

## 使ってみた
Slackでワークフローを使うと
<img width="463" alt="スクリーンショット 2019-12-05 23.10.18.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434453/7af326ab-9ce0-c9e3-910e-922d1e781bfa.png">

投稿されて、
<img width="724" alt="スクリーンショット 2019-12-05 23.24.26.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434453/a2ba026a-8f10-93b8-198a-1494e0c4bc33.png">

スプレッドシートも更新されました！
<img width="1467" alt="スクリーンショット 2019-12-05 23.25.17.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434453/b22b8823-fac1-ec39-f30f-1bb8ff3daf51.png">

これで承認されれば本を買ってもらますね！

## 作ってみて

本を購入できる制度があるのは素晴らしいですが、使われなけば意味がありません。
これを機にもっと社内でこの制度が使われれば良いなあと思います。

みなさんもご自身の会社で確認してみてはいかがでしょうか？

また`GAS`(+スプレッドシート)とSlackの連携によって、他の紙で管理している申請なども
どんどん電子化してしまいたい所存です。


## 謝辞（参考URL）
本記事の実装の多くは以下を参考にしています。感謝を。

Slack Bot、GASまわり
[Slackとスプレッドシートを連携して筋トレ管理をする \| GMOアドパートナーズグループ TECH BLOG byGMO](https://techblog.gmo-ap.jp/2019/09/06/slack%E3%81%A8%E3%82%B9%E3%83%97%E3%83%AC%E3%83%83%E3%83%89%E3%82%B7%E3%83%BC%E3%83%88%E3%82%92%E9%80%A3%E6%90%BA%E3%81%97%E3%81%A6%E7%AD%8B%E3%83%88%E3%83%AC%E7%AE%A1%E7%90%86%E3%82%92%E3%81%99/)

Slackワークフロー
[Slack に便利機能 "ワークフロービルダー" が増えたので勇み足で使ってみた \- Feedforce Developer Blog](https://developer.feedforce.jp/entry/2019/10/16/140901)