1.はじめに
社内のコミュニティ活動で、メンバー向けに書籍を購入しました。
関東と関西の拠点に1冊ずつ置いて、誰でも借りて読めるようにしようと思ったものの、ここで問題が…
「この本、今だれが持ってる?」
ほかにも、こんなことが気になりました。
- 借りられる本がどれなのか、ひと目で分からない
- 貸し借りの記録が残らず、いつの間にか本が行方不明になる
- 買った本が実際に読まれているのか、次に何を買えばいいのか分からない
最初は Excel 管理や Forms での申請も考えました。
ただ、Excel は書き忘れや上書きが起きやすく、Forms は申請するときに貸出状況が見えません。
そこで、一覧を見ながらボタン1つで借りて返せる書籍貸出システムを、SharePoint 上の Microsoft Lists1(以降、リスト)と Power Automate2 で作ってみました。
こんな方に向けた記事です。
- チームの書籍や備品の貸出管理に困っている方
- SharePoint や Power Automate の活用はこれからという方
- 普段の技術スタックとは別のもので、何か作ってみたい方
コードは要点を抜粋して載せています。設定手順の画面は一部省略しています。
2.作ってみたもの
まずは、完成した書籍貸出システムを紹介します。
完成イメージと、利用者の操作の流れを順に見ていきます。
2-1.完成イメージ
リストの一覧画面が、そのまま貸出システムの画面です。
1つの書籍(タイトル)につき1行で、関東と関西の2冊をまとめて管理しています。
関東・関西の列には、それぞれの貸出状況と「借りる」ボタンが並びます。
「詳細」を押すと、書籍の出版社や概要などを見ることができます。
どの本を借りるか迷ったときの参考になります。
できることを1枚の絵にまとめると、次のとおりです。
借りる・返すはボタン1つで、通知・記録・集計はすべて自動で行います。
2-2.操作の流れ
利用者の操作は、次の3ステップだけです。
STEP①.探す・借りる
一覧で本を選び、「借りる」を押します。
パネルが開くので、返却予定日・目的・補足を入れて実行します。
数秒後、Teams に貸出の通知が届きます。
返却予定日と保管場所が書かれているので、そのまま本を取りに行けます。
STEP②.受け取る・読む
通知に書かれた保管場所から本を取り、返却予定日までに読みます。
STEP③.返す・回答する
読み終わったら、一覧の「返す」を押して、本を元の保管場所に戻します。
Teams に返却の通知が届き、続けてアンケートが届くことも案内されます。
最後に、届いたアンケートで役立ち度・読了度・ひとことを答えれば完了です。
アンケートに答えなくても、返却は完了しています。
3.どうやって作ったか
ここからは、システムの作り方を紹介します。
Azure のリソースを使わない理由、方式の比較、全体構成、フローの処理、スクリプトでの構築の順に説明します。
3-1.Azure のリソースを使わない
普段の業務では Azure のサービスを中心に扱っています。
せっかく何か作るなら、普段あまり触らない技術にも挑戦してみたいと思いました。
そこで今回は、Azure のリソースは作らず、Microsoft 365(以降、M365)のライセンスに含まれる機能だけで作ると決めました。
今回使ったのは、Microsoft Lists・Teams と Power Automate の標準コネクタだけです。
既存の M365 ライセンスの範囲で作成しました。
Azure で作る場合に使うサービスを、次のように置き換えています。
| 役割 | Azure で作るなら | 今回使ったもの |
|---|---|---|
| データの保存 | Table Storage/Cosmos DB | Microsoft Lists |
| 画面 | App Service/Static Web Apps | リストの列の書式設定(JSON) |
| 処理 | Functions/Logic Apps | Power Automate |
| 通知 | Logic Apps + Teams | Power Automate + Teams |
| 権限 | App Service 認証+アプリロール | リストの共有・フローの実行のみのユーザー |
サインインの仕組み(認証)は、どちらの場合も Entra ID です。
画面を作らなくても、リストの「列の書式設定」でボタンまで作れるのは新鮮でした。
3-2.方式の比較
作る前に、いくつかの方式を比べました。
| No | 案 | 内容 | 判断 |
|---|---|---|---|
| 1 | Excel | 共有の表に直書き | 書き忘れ・上書きが起きる |
| 2 | Forms + リスト | フォームで申請し、リストに反映 | 申請するときに貸出状況が見えない |
| 3 |
リストにボタン (採用) |
一覧の各行に「借りる」「返す」ボタン | 貸出状況を見ながら、その場で借りられる |
| 4 | Power Apps | 専用アプリを作る | 今回の規模には作り込みすぎ |
決め手は、貸出状況を見ながら、その場で借りられることです。
貸出中の本には「借りる」ボタン自体が出ないので、借りられない本を申請してしまうこともありません。
今回の環境では、既存のライセンスの範囲で作成でき、新たな費用がかからないのもポイントです。
3-3.全体構成
全体の流れを図にしました。
ボタンを押すとフローが動き、書籍リスト・貸出履歴リストの更新から Teams への通知まで自動で行います。
番号の順に矢印をたどると、流れが分かります。
使っているのは、リスト2つとフロー2本です。
- 書籍リスト: 1タイトル1行。関東・関西それぞれの貸出状況、借りている人、返却予定日、保管場所を持つ
- 貸出履歴リスト: 1貸出1行。借りた人、日付、目的、アンケートの回答を残す
- フロー(関東用・関西用): ボタンから起動し、貸出・返却を処理する
フローを関東用・関西用に分けているのは、ボタンからは「どの本か」しか渡せず、「どちらの拠点か」を渡せないためです。
3-4.フローの処理
フローは、SharePoint の「選択したアイテムの場合」3というトリガーで起動します。
リストで選んだ行(本)の情報を受け取って動くトリガーで、列の書式設定のボタンから呼び出せます。
処理は、押された本の貸出状況によって分かれます。
- ボタンを押した本の情報を、書籍リストから取得する
- 在庫ありの本なら、貸出の処理をする
- 書籍リストを「貸出中」にし、借りている人と返却予定日を入れる
- 貸出履歴リストに1行追加する
- Teams で本人に貸出の通知を送る
- 本人が借りている本なら、返却の処理をする
- 貸出履歴リストに返却日を入れる
- 書籍リストを「在庫あり」に戻す
- Teams で本人に返却の通知とアンケートを送る
- ほかの人が借りている本なら、「ほかの方が貸出中です」と通知する
実際のフローは次のとおりです。
左側が貸出、右側が返却の処理です。
以降のコードに出てくる GetBook や UserMail などは図のアクション名、KantoStatus などは列の内部名(英語の列名)です。
返却予定日は、入力がなければ2週間後にしています。
if(empty(triggerBody()?['date']),
addDays(outputs('Today'), 14, 'yyyy-MM-dd'),
triggerBody()?['date'])
在庫ありかどうかは、選択肢の列の値で判定します。
equals(body('GetBook')?['KantoStatus']?['Value'], '在庫あり')
選択肢の列は、?['Value'] を付けないと比較できません。
返却するときは、貸出履歴リストから「対象の本・拠点で、まだ返却日が入っていない行」を探して更新します。
concat('Title eq ''', body('GetBook')?['Title'],
''' and Location eq ''関東'' and ReturnDate eq null')
3-5.スクリプトで構築
リストの列・初期データ・列の書式設定・ビューは、画面から1つずつ作らず、ブラウザのコンソールでスクリプトを実行して作りました。
SharePoint の REST API を使っています。
何度でも作り直せるので、見た目を直したいときも、スクリプトを直して再実行するだけで済みました。
列は、内部名を英語、表示名を日本語にして作っています。
// site : サイトの URL
// listPath : リストのパス(例:/sites/xxx/Lists/Books)
// digest : 書き込み用のトークン(/_api/contextinfo で取得)
const xml = `<Field Type="Choice" Name="KantoStatus" StaticName="KantoStatus"
DisplayName="関東" Format="Dropdown">
<Default>在庫あり</Default>
<CHOICES><CHOICE>在庫あり</CHOICE><CHOICE>貸出中</CHOICE></CHOICES>
</Field>`;
await fetch(`${site}/_api/web/GetList('${listPath}')/fields/createfieldasxml`, {
method: "POST",
headers: { Accept: "application/json;odata=nometadata",
"Content-Type": "application/json;odata=verbose",
"X-RequestDigest": digest },
body: JSON.stringify({ parameters: {
__metadata: { type: "SP.XmlSchemaFieldCreationInformation" },
SchemaXml: xml, Options: 12 } })
});
最初から日本語の列名で作ると、内部名が _x95a2__x6771_(「関東」の文字コード)のような形に変換されます。
Power Automate の式ではこの内部名を使うので、どの列なのか読み取れなくなります。
ボタンは、列の書式設定(JSON)の customRowAction でフローを呼び出しています。
在庫ありなら「借りる」を、借りている本人(@me)にだけ「返す」を表示します。
{
"elmType": "button",
"customRowAction": {
"action": "executeFlow",
"actionParams": "{\"id\": \"<フローID>\"}"
},
"txtContent": "=if(@currentField == '貸出中', '↩ 返す', '📖 借りる')",
"style": {
"display": "=if(@currentField == '在庫あり' || toLowerCase([$KantoBorrower.email]) == toLowerCase(@me), 'block', 'none')"
}
}
4.工夫したポイント
作っていく中で、使いやすさと運用のしやすさを考えて、いくつか工夫しました。
ここでは、特に効果が大きかった3つを紹介します。
4-1.1タイトル1行で管理
同じ本の2冊を別々の行にすると、一覧が倍の長さになり、貸出状況も見比べにくくなります。
そこで、1行に関東と関西の列を並べ、貸出状況・ボタン・保管場所をまとめて表示しました。
4-2.閲覧権限だけで使える
リストへの書き込みは、すべてフローが作成者の接続で行います。
利用者は、フローの実行のみのユーザーに追加しておくだけです。
リストへの権限は閲覧だけで、貸し借りができます。
利用者が手でリストを書き換えて、記録が崩れることも防げます。
実行のみのユーザーは、フローの詳細画面から追加できます。
追加するときに、SharePoint と Teams の接続を「この接続を使用」(作成者の接続)にしておくと、利用者は自分で接続を作らずに実行できます。
小規模な利用のため、フローは作成者の個人アカウントで動かしています。
作成者が異動・退職すると止まるので、本格的に運用する場合は、フロー専用のアカウントで動かすなどの対策が必要です。
4-3.分析用のデータも集める
貸出の記録だけでなく、次に買う本を決めるための情報も貸出履歴リストに残しています。
| 項目 | 取り方 | 分かること |
|---|---|---|
| 目的・補足 | 借りるときに入力 | 何のために借りられているか |
| 役立ち度・読了度・ひとこと | 返却時のアンケート | 本の評価 |
| 延滞日数 | 計算列(返却日 − 返却予定日) | 返却予定日が守られているか |
貸出履歴リストには、目的やアンケートの回答が1貸出1行で残ります。
アンケートは Teams のカードで送り、回答はフローがそのまま貸出履歴リストに書き込みます。
貸出回数だけでなく、何のために借りられて、役に立ったかまで分かるので、次に買う本を選ぶ材料になります。
5.ハマりどころと見送った機能
ここでは、作っている途中のハマりどころと、検討したものの見送った機能を紹介します。
5-1.実行した人のメールアドレス
フローで「ボタンを押した人」を扱うところで、2回ハマりました。
1つ目は、トリガーの「ユーザーのメール」を Teams の宛先にしたら、通知が届かなかったことです。
中身を見ると、エンコードされた値になっていたので、デコードして使っています。
coalesce(
triggerOutputs()?['headers']?['x-ms-user-email'],
base64ToString(triggerOutputs()?['headers']?['x-ms-user-email-encoded']))
2つ目は、借りた本人が「返す」を押しても、「ほかの方が貸出中です」と判定されたことです。
原因は、サインイン用のアドレスと、ユーザー列に入るメールアドレスが違っていたことでした。
ユーザー列の Claims(サインイン用のアドレスを含む)でも比較するようにしています。
or(
equals(toLower(coalesce(body('GetBook')?['KantoBorrower']?['Email'], '')),
toLower(outputs('UserMail'))),
contains(toLower(coalesce(body('GetBook')?['KantoBorrower']?['Claims'], '')),
toLower(outputs('UserMail'))))
5-2.関西まで更新される
関東の本を借りたら、関西の貸出状況や保管場所まで変わってしまいました。
「項目の更新」4アクションでは、既定値がある列に初期値が自動で入ります。
そこで、更新する列だけを残し、ほかの列はパラメーターごと外しました。
{
"parameters": {
"id": "@body('GetBook')?['ID']",
"item/KantoStatus/Value": "貸出中",
"item/KantoBorrower/Claims": "@outputs('UserMail')",
"item/KantoDueDate": "@outputs('Due')",
"item/LoanCount": "@add(int(coalesce(body('GetBook')?['LoanCount'], 0)), 1)"
}
}
5-3.ユーザー列を空にできない
返却時に「借りている人」を空に戻したかったのですが、「項目の更新」アクションではうまく空にできませんでした。
そこで、返却時の消去だけ「SharePoint に HTTP 要求を送信します」5アクションで行っています。
{
"formValues": [
{ "FieldName": "KantoStatus", "FieldValue": "在庫あり" },
{ "FieldName": "KantoBorrower", "FieldValue": "[]" },
{ "FieldName": "KantoDueDate", "FieldValue": "" }
],
"bNewDocumentUpdate": false
}
5-4.返却リマインドは見送り
返却予定日の前後に、Teams で何度もリマインドする仕組みも検討しました。
ただ、通知が多すぎると、かえって読まれなくなると考え、見送りました。
代わりに、返却予定日を過ぎると一覧の返却予定日が赤字になるようにしています。
今後は、返却予定日を過ぎた本に「貸出リクエスト」ボタンを付け、借りたい人が待っているときだけ知らせる仕組みを考えています。
6.おわりに
今回は、Azure のリソースを作らず、SharePoint 上の Microsoft Lists と Power Automate だけで書籍貸出システムを作ってみました。
はじめに挙げた困りごとは、次のように解決できました。
| はじめの困りごと | 作ってみた結果 |
|---|---|
| この本、今だれが持ってる? | 一覧で、借りている人と返却予定日が分かる |
| 借りられる本がどれなのか、ひと目で分からない | 貸出状況と「借りる」ボタンで、すぐに分かる |
| 貸し借りの記録が残らず、いつの間にか本が行方不明になる | 貸し借りが自動で貸出履歴リストに残る |
| 買った本が実際に読まれているのか、次に何を買えばいいのか分からない | 目的やアンケートの回答から判断できる |
普段使っていない技術を組み合わせるのは、思った以上に発見が多く、楽しかったです。
今後は、集まったデータをもとに次年度の書籍を選んだり、Power BI などで集計を見やすくしたりしていく予定です。
チームの書籍や備品の管理に困っている方の参考になれば嬉しいです。
ここまで読んでいただき、ありがとうございました。
We Are Hiring!
-
Excel の表のようにデータを管理できる Microsoft 365 のサービス。データは SharePoint のサイトに保存される。列ごとに種類(選択肢・ユーザー・日付など)や閲覧できる人を設定できる ↩
-
「ボタンが押されたら、リストを更新して Teams に通知する」のような処理を、ノーコード・ローコードで自動化できるサービス ↩
-
Power Automate の SharePoint コネクタにあるトリガー。リストで選んだアイテムを対象に、利用者が手動でフローを実行できる。リストから呼び出せるのは、既定の環境に作ったフローだけ(Microsoft Learn) ↩
-
Power Automate の SharePoint コネクタにある、リストのアイテムを更新するアクション(Microsoft Learn) ↩
-
Power Automate の SharePoint コネクタにある、SharePoint の REST API を直接呼び出せるアクション。標準コネクタなので、プレミアム ライセンスは不要(Microsoft Learn) ↩