はじめに
最近、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
試していった順序
こんなふうに順番に試していきました。irb
やpry
などに貼り付けて試せるようになっているはずです。
動的にメソッドを追加
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にあります。
興味がありましたらどうぞー。