0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

# Slack × Google Apps Scriptで「スマホだけで請求書を作成・送付できる仕組み」を作ってみた

0
Posted at

はじめに

私は請求書作成の際、PCを開いてExcelを編集してPDF化してメール送信して……という流れで作業していました。

ただ、

  • 外出先
  • 客先
  • 移動中

など、PCを開きたくない場面も多く、

「スマホだけで請求書を作成し、そのまま送付できたら便利なのでは?」

と思ったことが今回の開発のきっかけです。

完成すると、Slackで /invoice と入力するだけで請求書作成から送付まで完結します。


完成イメージ

slashコマンド.png

請求先情報入力.png

承認依頼.png

承認.png


システム構成

今回の構成は次のようになっています。

構成図.png

ポイントは

  • Slack Integration
  • Invoice

を分離したことです。

Slack
    │
    ▼
Slack Integration(GAS)
    │
    ▼
Invoice Library(GAS)
    │
    ├── 請求書作成
    ├── PDF作成
    ├── Gmail下書き作成
    ├── 承認
    ├── メール送信
    └── Queue

Slack IntegrationはSlackとのやり取りだけを担当します。

請求書のロジックはすべてInvoiceライブラリへ委譲しています。

function handleInteraction(e) {

  const payload = JSON.parse(e.parameter.payload);
  const action = payload.actions[0];

  if (action.action_id.includes("invoice")) {

    Invoice.handleInteractionInvoice(e);

  }

}

最初はSlack Integration側にも請求書の処理を書いていました。

しかし、Slack Integrationは修正すると再デプロイが必要になります。

そのため、

業務ロジックはすべてInvoice側へ寄せる

構成に変更しました。

これにより、請求書機能を改修してもSlack Integrationは基本的に変更不要になっています。


処理の流れ

全体のシーケンスは次のようになります。

シーケンス図.png

Slack

↓

/invoice

↓

Modal表示

↓

入力

↓

Queue登録

↓

請求書作成

↓

PDF生成

↓

Gmail下書き

↓

Slack承認依頼

↓

承認

↓

メール送信

Google FormではなくSlack Modalにした理由

最初はGoogle Formを使っていました。

しかし、

  • Slack→ブラウザへ遷移
  • Form入力
  • Slackへ戻る

という操作がかなり煩雑でした。

そこでSlack Modalへ変更しました。

結果として

  • Slackアプリだけで完結
  • 入力が速い
  • UXが大きく改善

しました。


会社一覧はSpreadsheetから動的取得

取引先は増減するため、

static_select

ではなく

external_select

を利用しています。

会社一覧はSpreadsheetから取得しています。

function getInvoiceCompanyOptions(keyword) {

  return getDataTable()
    .filter(row => row.company.includes(keyword))
    .map(row => ({
      text: {
        type: "plain_text",
        text: row.company
      },
      value: row.company
    }));

}

これにより、

Spreadsheetへ会社を追加するだけでSlack側にも反映されます。


Slackの3秒制限

SlackではSlash CommandやModal Submitは

約3秒以内に応答

しなければなりません。

しかし請求書作成では

  • Spreadsheet生成
  • PDF生成
  • Gmail下書き

など時間のかかる処理があります。

普通に実行すると

operation_timeout

になってしまいます。


解決方法

そこで直接請求書を作成するのではなく、

まずQueueへ登録する方式にしました。

function enqueueInvoice(job) {

  queueSheet.appendRow([
    Utilities.getUuid(),
    "WAITING",
    JSON.stringify(job)
  ]);

}

処理の流れは

Modal Submit

↓

Queue登録

↓

Slackへ即応答

↓

バックグラウンド処理

となります。

これでSlack側は数百ms程度で応答できるようになりました。


Queue処理

Queueは1分ごとの時間主導トリガーで処理しています。

function processInvoiceQueue() {

  const job = dequeueInvoice();

  if (!job) {
    return;
  }

  createInvoice(job);

}

ジョブが存在しない場合はすぐ終了するため、

毎分実行でも負荷はほとんどありません。


ワンショットトリガーも検討した

当初は

Queue追加

ワンショットトリガー生成

実行後削除

という方式も検討しました。

メリットは

  • 必要な時だけ実行
  • 空実行ゼロ

です。

一方で、

ジョブ実行中に例外が発生した場合、

再実行の仕組みを自前で作る必要があります。

最終的には

シンプルさ

障害復旧のしやすさ

を優先して毎分実行方式を採用しました。


二重実行防止

時間主導トリガーでは重複実行も考慮する必要があります。

そこでLockServiceを利用しています。

const lock = LockService.getScriptLock();

if (!lock.tryLock(300)) {
  return;
}

try {

  processInvoiceQueue();

} finally {

  lock.releaseLock();

}

これで同じ請求書が二重に作成されることを防いでいます。


最終的なプロジェクト構成

最終的には

Slack Integration
Invoice

の2プロジェクト構成になりました。

Invoiceには

  • Queue
  • Approval
  • PDF
  • Gmail
  • Drive
  • Spreadsheet

など請求書業務だけを実装しています。

これにより、

今後

  • 見積書
  • 納品書
  • 経費精算

など別業務を追加しても、

Slack Integration側は

if(actionId.includes("invoice")){

    Invoice.handleInteractionInvoice(e);

}
else if(actionId.includes("expenses")){

    Expenses.handleInteractionExpenses(e);

}

程度の修正だけで済みます。

業務ロジックは各ライブラリ側に閉じ込められるため、

保守性も高くなりました。


今後やりたいこと

現在は請求書のみですが、

同じ仕組みで

  • 見積書
  • 納品書
  • レポート
  • 経費精算

なども実装していきたいと考えています。


おわりに

Google Apps Scriptというと

「ちょっとした自動化」

というイメージを持たれがちですが、

設計を工夫することで

  • Slack
  • Gmail
  • Drive
  • Spreadsheet

を組み合わせた実用的な業務システムを構築できます。

今回開発した仕組みによって、PCを開かなくてもスマホだけで請求書を作成・送付できるようになりました。

同じように「日々の定型業務をもっと手軽にしたい」と考えている方の参考になれば幸いです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?