GASで経費申請フォームを作ってみた(2/6)ーWebアプリで、自分の作業に合う入力画面を作るー
このブログでは、Google Apps Script(GAS)で作ったツールや、作る途中で気づいたことを紹介しています。「こんな使い方もできるんだ」と、普段の作業を見直すヒントになればうれしいです。
前回は、GASで作った経費申請フォームの全体像を紹介しました。今回は、ブラウザから使う入力画面を作る機能についてです。
やりたかったこと
以前のGoogleフォームは、レシート1枚につき1回回答する運用でした。複数枚あると、同じ操作を繰り返すことになります。
また、金額などの入力ミスも多く、申請金額と内訳の合計や、消費税の整合性を入力時に確認できるようにしたいと考えていました。
そこで、必要な分だけ経費の入力欄を追加でき、スマホで撮影したレシートも一緒に申請できる画面を目指しました。
使ったロジック・ポイント
① HTMLの画面をWebアプリとして公開する
GASでは、HTMLで作った画面をWebアプリとして公開し、URLを開いてブラウザから利用できます。
画面の構成にはHTML、見た目にはCSS、入力欄の追加や表示の切り替えにはJavaScriptを使っています。スプレッドシートへの記録などは、画面からGAS側の処理を呼び出して実行します。
公開するには、まずGASのプロジェクト内に画面用のHTMLファイルを用意します。そして、URLへアクセスしたときに呼ばれる「doGet」という関数で、その画面を返すようにします。
画面を用意したあとは、GASの編集画面から次の順番で公開します。
- 右上の「デプロイ」から「新しいデプロイ」を開きます。
- デプロイの種類に「ウェブアプリ」を選びます。
- 処理を実行するユーザーと、アクセスできるユーザーの範囲を設定します。
- 「デプロイ」を実行し、必要に応じて権限を承認します。
- 発行されたWebアプリのURLを、利用者に共有します。
このフォームでは、アクセスしているユーザーの権限で処理する設定にしています。そのため、公開範囲の設定に加えて、利用者が保存先のスプレッドシートやフォルダを使えるかも確認しました。
② 共通する情報と、経費ごとの情報を分ける
最初に名前・メールアドレス・部署を入力し、そのあと経費の入力へ進む流れにしています。
経費は1件ごとにカードのようにまとめ、「経費を追加」で必要な分だけ増やせるようにしました。入力済みの内容を残したまま次の経費へ進め、不要なものは削除できます。
申請者情報を共通で使いながら、件数に合わせて入力欄を増減させる形です。
③ 入力ミスを、該当する欄の下で知らせる
入力内容に問題があるときは、どこを見直せばよいかを画面で伝えるようにしました。
例えば、送信操作をしたときは、次の流れで確認します。
- 経費の入力欄から、入力されている内容を取り出します。
- チェック処理で、空欄や金額の不整合などを確認します。
- 問題があれば、その項目に対応する指摘メッセージを用意します。
- 画面側の処理で、該当する入力欄の下にメッセージを表示します。
- 指摘が残っている場合は保存処理へ進まず、入力内容を修正してもらいます。
「入力内容を確認してください」という全体への案内だけでなく、「どの項目を直すか」が入力欄の近くで分かる形にしています。
利用者が修正して再び送信すると、もう一度チェックします。問題が解消した項目ではメッセージを消し、すべてのチェックを通過してから保存処理へ進みます。
チェックする処理と、その結果を画面に表示する処理を分けることで、項目ごとの指摘をまとめて反映できるようにしています。
④ スマホのカメラで撮影したレシートを添付する
経費の入力欄には、レシート画像を添付する欄も置いています。スマホのカメラで撮影した画像を、そのまま申請につなげるためです。
この操作には、HTMLのファイル入力欄を使っています。画像を受け付ける指定と、カメラでの撮影を促す指定を組み合わせることで、対応するスマホでは入力画面から撮影へ進めます。
GASがカメラを直接操作するのではなく、スマホやブラウザが用意している撮影機能を利用する形です。カメラの起動やファイル選択の表示は、端末やブラウザによって異なります。
選んだ画像を確認する表示と、取り消す操作も用意しました。画像を選び直したいときも、入力画面の中で進められます。
撮影した画像を申請内容と一緒に送れるため、レシートを別の方法で送る手間を減らせるようにしています。画像をGoogleドライブへ保存する処理については、別の回で紹介します。
コード例
GASでコード例を試す際の基本操作や認証は、次のガイドで確認できます。この記事のファイル名・設定などは、各コード例の説明に従ってください。
各回のコード例は独立しています。順番に追加して、経費申請フォーム全体を完成させるものではありません。
以下は、仕組みを紹介するために簡略化した例です。実際の経費申請フォームのコード一式ではありません。
HTMLの画面を表示する
GAS側の「Code.gs」には、画面を返す処理を記述します。
function doGet() {
return HtmlService.createHtmlOutputFromFile('Index');
}
「Index.html」には、表示する内容を記述します。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<base target="_top">
</head>
<body>
<h1>経費申請フォーム</h1>
<p>ここに申請用の入力欄を作ります。</p>
</body>
</html>
「createHtmlOutputFromFile」で、指定したHTMLファイルを画面として返しています。この組み合わせを用意してWebアプリとしてデプロイすると、URLから画面を開けます。
チェック結果を、入力欄の下に表示する
HTMLには、入力欄と、その項目への指摘を表示する場所を用意します。
<div class="expense-card">
<label>申請金額 <input class="amount" type="number"></label>
<p class="amount-error" role="alert"></p>
</div>
JavaScript側では、チェック処理で用意したメッセージを、対応する表示場所へ入れます。
function displayExpenseErrors(card, errors) {
card.querySelector('.amount-error').textContent = errors.amount || '';
}
const card = document.querySelector('.expense-card');
displayExpenseErrors(card, {
amount: '申請金額と内訳の合計を確認してください。'
});
「querySelector」でカード内の表示場所を選び、「textContent」で指摘メッセージを表示しています。
再チェックで問題がなくなった場合は、同じ処理へ空のメッセージを渡します。例えば「amount」を空文字にすると、表示していた指摘も消えます。
この例は、チェック結果を画面へ反映する部分に絞っています。実際のフォームでは、各経費カードの項目ごとにメッセージを反映し、問題があれば送信時の保存処理へ進まないようにしています。
レシート撮影用の入力欄を用意する
<label for="receipt">レシート画像</label>
<input id="receipt" type="file" accept="image/*" capture="environment">
「type="file"」はファイルを受け取る入力欄、「accept="image/*"」は画像を選択対象にする指定です。「capture="environment"」は、対応する端末で外側のカメラを使った撮影を促す指定です。
この指定だけで、すべての端末に同じ撮影画面を出せるわけではありません。また、画像の表示・取り消し・保存には、それぞれ別の処理を用意しています。
メモ・反省・次回やりたいこと
今回苦戦したのは、デプロイ時のアカウントや公開範囲の設定でした。
Webアプリには、誰のアカウントとして処理を実行するかという設定と、誰がアクセスできるかという設定があります。また、保存先のスプレッドシートやフォルダにもアクセス権があります。
画面を作って公開するところだけでなく、利用者のアカウントで開いたときに、必要なデータへアクセスできるかまで確認する必要がありました。
動作確認では、自分の開発用アカウントだけでなく、別のGoogleアカウントでもWebアプリを開けるかを確認しました。さらに、権限があるアカウントとないアカウントで画面が適切に分かれるかを、PC・スマホのChromeで確認しました。
Webアプリを作るときは、入力画面の使いやすさと合わせて、誰が使い、誰の権限で処理するのかも考えることが大切だと感じました。
今回のような入力欄の追加やチェックは、備品の購入申請や作業報告などにも使えそうです。次回は、画面から受け付けた内容をスプレッドシートへ記録する機能を紹介します。
自分の業務に自動化機能を追加したい方へ
経費申請や画像保存、申請後の連絡など、今の業務に自動化機能を追加したい方はご相談ください。現在の作業と希望する自動化を伺い、対応できる範囲をご案内します。