概要
業務委託のパートナー(講師・スタッフなど)を多く抱える企業様から、「毎月届く請求書の作成・確認・保管に手間がかかっている。1 か所で受け付けて審査できるようにしたい」というご相談をいただき、パートナーがブラウザで請求書を作って提出し、管理者が同じシステムで審査・差し戻し・保存・集計まで行える仕組みを作りました。
パートナーは明細を入力するだけで、税金の計算と請求書 PDF(印刷用の文書ファイル)の作成が自動で終わり、領収書もその場で添付できます。管理者は届いた請求書を一覧で確認し、承認すると PDF が月ごとのフォルダに自動で保存されます。
課題
- パートナーごとに請求書の書式や計算方法がばらばらで、金額や源泉徴収税(報酬から差し引いて納める所得税)の計算違いを確認するのに手間がかかっていた
- 請求書・領収書・振込先の口座情報がメールやチャットなど別々の経路で届き、どれが最新か分かりにくかった
- 不備があったときの連絡や、承認後のファイル保管・集計表への転記を手作業で行っていた
- 振込先の口座が変わったことに気づくのが遅れると、振込のミスにつながるおそれがあった
- 口座情報など大切な個人情報を扱うため、本人と管理者以外が見られない仕組みが必要だった
作ったもの
パートナー(請求する人)の画面
- 登録とログイン: 氏名・会社名・振込先口座などを登録すると、メールに届くリンクを押すだけでログインできます。パスワードを覚える必要はありません
- 請求書の作成: 「基本情報 → 明細の入力 → 内容の確認 → 送信」の 4 ステップで進みます。品目は管理者が用意した一覧から選び、数量と単価を入れると合計・消費税・源泉徴収税が自動で計算されます
- 請求書 PDF の自動作成: 送信すると、確定した金額から請求書 PDF を作って保存します
- 領収書の添付: 経費の領収書を 1 回の提出につき 3 件まで(画像または PDF、1 件 25MB まで)添付できます
- マイページ: 承認された請求書をいつでも見返し、PDF を保存できます
- プロフィール: 会社情報や振込先口座を自分で更新できます。メールアドレスの変更も、確認メールのリンクを押す形で本人が行えます
管理画面
| 画面 | できること |
|---|---|
| 請求書・領収書の一覧 | 期間・状態で絞り込み、内容のプレビュー、1 件ずつまたはまとめて承認・差し戻し・無効にする。同じ月に同じ人から 2 枚出ている請求書には目印を表示 |
| 差し戻しの連絡 | 「金額の間違い」「ファイル名の間違い」「請求月の間違い」から理由を選ぶと、社内チャットに送る連絡文のひな形ができ、手直ししてコピーできる |
| 計上項目 | 承認した請求書の明細を項目ごとに一覧にし、集計表(Google スプレッドシート)へ出力。パートナー別の源泉徴収税の合計も出す |
| 口座情報 | パートナーの振込先を一覧で確認し、CSV ファイルで書き出す |
| 設定 | 品目の一覧とカテゴリ、請求先の会社情報、消費税・源泉徴収税の税率を画面から変更する |
自動で動く処理
- 提出のお知らせ: 請求書が出される(出し直される)と、名前・振込合計額・契約の種類などを社内チャット(Slack)に通知します
- 口座変更のお知らせ: 振込先が実際に変わったときだけ、専用のチャンネルに通知します。口座番号は末尾 4 桁だけを表示します
- PDF の作成失敗のお知らせ: パートナーの端末で PDF の作成に失敗した場合は、その請求書番号を管理者に通知します。PDF が無い請求書を確定した金額からまとめて作り直す管理者用の画面も用意しました
- 承認時の保存: 承認した請求書・領収書は Google ドライブの「年_月」のフォルダへ自動で保存します。法人の場合はファイル名に会社名を入れます
工夫したところ
金額はサーバーで確定し、後から変わらないようにする
- 金額の計算は、小数の誤差が出ない計算用のライブラリ(decimal.js)で行い、端数の切り捨てを 1 か所に集めました
- 画面で計算した値はそのまま使わず、サーバーで品目の一覧を見直して源泉徴収の要否を決め、金額を計算し直したものだけを正しい値として保存します
- 請求書には、作った時点の税率を一緒に保存します。管理画面で税率を変えても、過去の請求書の金額や PDF の表示は変わりません
- 源泉徴収税の対象額も計算し直さずに保存し、表示のたびに逆算して 1 円ずれる、ということが起きないようにしました
途中で失敗しても、中途半端なデータを残さない
- 請求書の作成では「請求書本体・明細・計上項目」の 3 つの表に書き込みます。これを 1 つのデータベースの処理(トランザクション)にまとめ、どれか 1 つでも失敗したら全部を取り消すようにしました。以前は明細だけ保存に失敗して請求書だけが残ることがあり、これをなくしています
- 承認・差し戻しなどの状態は、「未承認からは承認・差し戻し・無効へ」「承認済みからは承認の取り消し・無効へ」のように、移ってよい先を表で決めました。まとめて変更するときは「今の状態がこれなら変える」という条件付きで更新し、ほかの管理者が同時に変えていて件数が合わなければ全体を取り消します
- 状態や契約の種類などの値は、プログラムの 1 か所とデータベースの制約の両方で同じ範囲に絞り、決められた値以外は保存できないようにしました
- 承認後のドライブ保存が失敗しても承認は取り消さず、失敗したことを画面に表示します。保存側の処理は 1 件ずつ順番に行うようにし、同じ月のフォルダが 2 つできる不具合を防ぎました
本人と管理者以外は見られないようにする
- 書き込みはすべてサーバーを通し、その入口で「ログインしているか」「管理者か」を必ず確かめます。確かめる処理は 1 か所にまとめ、画面ごとに書かないようにしました
- データベース側にも、本人のデータしか読めない規則(行単位のアクセス制御)を設け、利用者が自分の権限を管理者に変えることもできないようにしました
- 請求書 PDF は公開の URL を作らず、閲覧のたびに本人か管理者かを確かめてから、短い時間だけ有効なリンクで渡します
- 領収書は拡張子だけでなくファイルの中身の先頭を確かめ、画像や PDF 以外のファイルを受け付けません
テストと自動のデプロイで、壊さずに直し続けられるようにする
- 税金の計算・状態の移り方・権限の確認などに、270 件を超える自動テストを用意しました。「ログインしていない人」「他人の請求書」を拒むことは、機能ごとに必ずテストしています
- 請求書の作成と状態変更の取り消しが正しく働くかは、本物と同じ種類のデータベースを一時的に立ち上げて確かめています
- 変更を GitHub に送ると、検査・テスト・ビルドが自動で走り、通ったものだけが検証環境と本番環境(Google Cloud Run)に反映されます。データベースの変更が本番に当たっていない場合は、反映の前で止まるようにしました
- 不具合を直すときは、まず不具合を再現するテストを書いて失敗を確かめてから直し、同じ不具合が戻らないようにしています
実際の業務に合わせて決め直す
- 当初は「同じ月の 2 枚目の請求書」をデータベースで受け付けない形にしましたが、分割での請求や修正版の提出が実際にあったため、受け付けたうえで画面と一覧の目印で注意を促す形に改めました
- 判断の理由や却下した案は設計の記録として残し、後から見ても経緯が分かるようにしています
