🧾

週N日稼働の最終営業日に「請求書を送る」というリマインドが欲しい

2022/08/08に公開

TL;DR

このスクリプトを一日一回、スケジュール実行しましょう。月末の営業日に Slack 通知が来るようになります。

https://github.com/ttanimichi/invoice-reminder

要件

  • 例えば、フリーランスで週3日の案件を受けているとします
  • 月火水の稼働
  • 毎月、最終営業日に「請求書を送る」というリマインドが欲しい
    • 祝日と重なった場合は、その前日が最終営業日になる
    • 月火水の稼働であっても、暦の周期の都合で「最終水曜日」が最終営業日とは限らない

最終水曜日が祝日と重なる例

「昭和の日」と重なる関係上、前日の 4/28(火)が最終営業日となる

最終水曜日が祝日と重なる例

暦の周期の都合で最終営業日が火曜日になる例

5/31(火)が最終営業日

最終営業日が火曜日になる例

事の発端

Slack の /remind は「月末」を指定できない

https://qiita.com/hnw/items/5986351cbaeceac64f22

iPhone の標準アプリ「リマインダー」であれば「最終水曜日」に通知を受け取ることは可能

が、「最終水曜日」の通知では不完全です。

  • 最終日が祝日と重なった場合、最終営業日はその前日となる
  • 月火水の稼働であっても、暦の周期の都合で「最終水曜日」が最終営業日とは限らない

というわけで、自分でスクリプトを書いて自動化しました。

Invoice Reminder

ソースコードを GitHub に公開しています。ご自由に fork して書き換えて使ってください。
https://github.com/ttanimichi/invoice-reminder

解説

すべてのプログラムをフィルタとして設計したいというか、リスト操作だけで実装したいですよね。

  • 月初から月末までのうち、月火水以外の曜日を除外する
  • 祝日を除外する
    • この処理のために holiday_jp という gem を使用しました
BUSINESS_DAYS = {
  Sunday: false,
  Monday: true,
  Tuesday: true,
  Wednesday: true,
  Thursday: false,
  Friday: false,
  Saturday: false,
}

def business_days_of_month(today: Date.today)
  business_days = BUSINESS_DAYS.select { _2 }.keys

  (today.beginning_of_month..today.end_of_month).select {
    _1.strftime("%A").to_sym.in? business_days
  }.reject {
    HolidayJp.holiday? _1
  }
end

その日が最終営業日の場合は Slack に通知します。

if business_days_of_month.last.today?
  notifier = Slack::Notifier.new ENV["INVOICE_REMINDER_WEBHOOK_URL"]
  notifier.ping "今日は最終営業日です。請求書を提出しましょう!"
end

today外部入力として、引数で渡して やればテストも簡単です。

RSpec.describe do
  subject { business_days_of_month(today: today).last }

  context "最終営業日が水曜日になる場合" do
    let(:today) { Date.new(2022, 7, 1) }
    it { is_expected.to eq Date.new(2022, 7, 27) }
  end

  context "最終営業日が水曜日ではない場合" do
    let(:today) { Date.new(2022, 5, 1) }
    it { is_expected.to eq Date.new(2022, 5, 31) }
  end

  context "最終水曜日が祝日と重なる場合" do
    let(:today) { Date.new(2020, 4, 1) }
    it { is_expected.to eq Date.new(2020, 4, 28) }
  end
end

月末ではなく月初に通知したい場合

2行書き換えるだけで簡単に月初に変更できます。下記のブランチをマージしてください。
https://github.com/ttanimichi/invoice-reminder/commit/91bea35b61812804f67212f815dfc7d087aa01e7

スケジュール実行の設定

スケジュール実行の実現方法は、本来なら運用の手間を減らすために EventBridge で AWS Lambda を定時実行する とかの方がマネージドサービスに頼ることができて良いと思うんですが、今回は、サーバー代を完全に無料にしたかったのと、個人的に OCI の Always Free インスタンス を試したかったので、普通に Linux サーバーを立てて、昔ながらの crontab でスケジュール実行しました。ランチに行く前に請求書の送付を済ませたいので午前 11:45 に設定してあります。

$ crontab -l

MAILTO=""
45 11 * * * /usr/bin/ruby /home/ubuntu/invoice_reminder/main.rb

まとめ

  • Slack の /remind も iPhone の「リマインダー」も最終営業日を指定できない
  • holiday_jp べんり
  • OCI の Always Free インスタンスべんり
  • 請求書の送付はランチに行く前に済ませるのが吉

Discussion