6
2

More than 3 years have passed since last update.

消費税改正に備えて

Last updated at Posted at 2020-04-15

はじめに

最近、COVID-19の流行で本当に大変なことになっています。
経済への影響もかなり大きく、その対策として消費税改正の話が出たり引っ込んだりしています。

消費税改正…もし行われることがあれば、速やかに従いたいところです。
自分の関わっているサービスでは、サイト内いくつかの場所で税込価格を表示していますが、消費税の処理の記述も複数箇所に書かれており、速やかな対応をするのがちょっと大変そうです…。

…というわけで、こうやったら税処理の部分をうまく書けるのではないか?ということをまとめました。
(テストの話もありますが、長いと大変なのでこちらに分割しています。)

ソースはこちら

理想の姿

消費税を扱うクラスでattr_with_taxによって属性を指定してあげると、属性_with_taxメソッドが使えるようになり、これが処理日なり指定日で自動で税率が変わってくれるようにしたいです。

class SampleItem
  extend WithTax

  attr_accessor :price
  attr_with_tax :price

  def initialize(price)
    @price = price
  end
end

sample_item = SampleItem.new(123)
sample_item.price                                    #=> 123
sample_item.price_with_tax                           #=> 135
sample_item.price_with_tax(Date.parse('2019/09/30')) #=> 132

試していった順序

こんなふうに順番に試していきました。irbpryなどに貼り付けて試せるようになっているはずです。

動的にメソッドを追加

RubyはModule#define_methodを使って動的にメソッドを定義することができるので、それでよろしくできそうです。

下の例では、.attr_with_taxというクラスメソッドで#price_with_methodを作っています。

class SampleItem
  attr_accessor :price

  def initialize(price)
    @price = price
  end

  def self.attr_with_tax(attr_name)
    method_name = "#{attr_name}_with_tax"
    define_method(method_name) do
      (send(attr_name) * 1.10).floor
    end
  end

  attr_with_tax :price
end

sample_item = SampleItem.new(123)
sample_item.price          #=> 123
sample_item.price_with_tax #=> 135

モジュールでまとめる

いちいち全部のクラスでメソッドを定義すると大変なので、モジュールにまとめて、必要なクラスにextendして、税込み計算をさせたい属性を指定します。

module WithTax
  def attr_with_tax(attr_name)
    method_name = "#{attr_name}_with_tax"
    define_method(method_name) do
      (send(attr_name) * 1.10).floor
    end
  end
end

class SampleItem
  extend WithTax

  attr_accessor :price
  attr_with_tax :price

  def initialize(price)
    @price = price
  end
end

sample_item = SampleItem.new(123)
sample_item.price          #=> 123
sample_item.price_with_tax #=> 135

適用される日によって税率が変化

消費税が改正されることになったとき、できれば何もせず待っているくらいに済ませたいです。
ですので、施行日と税率のハッシュを作り、実行日や指定日で検索するようにします。

require 'date'

module WithTax
  TAX_TABLE = {
    '2019/10/01': 0.10,
    '2014/04/01': 0.08,
    '1997/04/01': 0.05,
    '1989/04/01': 0.03,
  }

  def attr_with_tax(attr_name)
    method_name = "#{attr_name}_with_tax"
    define_method(method_name) do |effective_date = nil|
      effective_date ||= Date.today
      _k, rate = TAX_TABLE.find { |k, _v| Date.parse(k.to_s) <= effective_date }
      rate ||= 0.00
      (send(attr_name) * (1 + rate)).floor
    end
  end
end

class SampleItem
  extend WithTax

  attr_accessor :price
  attr_with_tax :price

  def initialize(price)
    @price = price
  end
end

sample_item = SampleItem.new(123)
sample_item.price                                    #=> 123
sample_item.price_with_tax                           #=> 135
sample_item.price_with_tax(Date.parse('2019/09/30')) #=> 132

Railsのアプリケーションに入れるには

上で作ったWithTaxモジュールをapp/models/concerns/with_tax.rbに保存、消費税の計算が必要なモデルで先の例と同じようにattr_with_taxを定義すればよいです。

おわりに

Rubyは動的にメソッド定義できたり、DSLっぽい書き方ができて好きなんですが、普段の仕事ではあんまりしないので、楽しかったですヽ(=´▽`=)ノ

おまけ(gem)

ここにまとめたことをwith_taxというgemにしました。
ソースはgithubにあります。
興味がありましたらどうぞー。

6
2
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
6
2