From 0e890b8650f3df5132d8c5f77c116cf826da23a4 Mon Sep 17 00:00:00 2001 From: Guillaume Dott Date: Tue, 12 Nov 2024 14:37:26 +0100 Subject: [PATCH] Add data and helper methods --- .gitignore | 2 + Rakefile | 6 +- lib/vat_rate.rb | 49 ++++++++++- lib/vat_rate.yml | 176 ++++++++++++++++++++++++++++++++++++++++ lib/vat_rate/version.rb | 2 +- sig/vat_rate.rbs | 4 - test/test_vat_rate.rb | 43 ++++++++++ vat_rate.gemspec | 13 +-- 8 files changed, 280 insertions(+), 15 deletions(-) create mode 100644 lib/vat_rate.yml delete mode 100644 sig/vat_rate.rbs create mode 100644 test/test_vat_rate.rb diff --git a/.gitignore b/.gitignore index 9106b2a..d7516a4 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,5 @@ /pkg/ /spec/reports/ /tmp/ +/vendor/ +/Gemfile.lock diff --git a/Rakefile b/Rakefile index cd510a0..61c6d63 100644 --- a/Rakefile +++ b/Rakefile @@ -1,4 +1,8 @@ # frozen_string_literal: true require "bundler/gem_tasks" -task default: %i[] + +require "minitest/test_task" +Minitest::TestTask.create + +task default: :test diff --git a/lib/vat_rate.rb b/lib/vat_rate.rb index 3c744d1..6431214 100644 --- a/lib/vat_rate.rb +++ b/lib/vat_rate.rb @@ -2,7 +2,50 @@ require_relative "vat_rate/version" -module VatRate - class Error < StandardError; end - # Your code goes here... +require 'psych' +require 'date' + +class VatRate + CATEGORIES = %i(standard parking reduced super_reduced) + RATES = Psych.safe_load_file(__dir__ + "/vat_rate.yml", permitted_classes: [Date]) + + def initialize(country:, category: :standard, date: Date.today, rate: nil) + raise ArgumentError unless CATEGORIES.include?(category) + + @category = category + @country = country.to_s + @date = date + + if rate + @rate = rate + elsif RATES["#{@category}_rates"].key?(@country) + @rate = RATES["#{@category}_rates"][@country].detect do |rate_data| + if rate + rate == rate_data['rate'] + else + !rate_data.key?('from') || date.to_date >= rate_data['from'] + end + end&.fetch('rate') || 0 + else + @rate = 0 + end + end + + attr_reader :rate + + def amount_with_vat(amount) + amount * (100 + @rate) / 100.0 + end + + def amount_without_vat(amount) + amount * 100.0 / (100 + @rate) + end + + def vat_amount(amount, has_vat: false) + if has_vat + amount * @rate / (100.0 + @rate) + else + amount * @rate / 100.0 + end + end end diff --git a/lib/vat_rate.yml b/lib/vat_rate.yml new file mode 100644 index 0000000..400a626 --- /dev/null +++ b/lib/vat_rate.yml @@ -0,0 +1,176 @@ +standard_rates: + AT: + - rate: 20 + BE: + - rate: 21 + BG: + - rate: 20 + HR: + - rate: 25 + CY: + - rate: 19 + CZ: + - rate: 21 + DK: + - rate: 25 + EE: + - rate: 22 + FI: + - rate: 24 + FR: + - rate: 20 + from: 2014-01-01 + - rate: 19.6 + DE: + - rate: 19 + GR: + - rate: 24 + HU: + - rate: 27 + IS: + - rate: 24 + IE: + - rate: 23 + IT: + - rate: 22 + LV: + - rate: 21 + LT: + - rate: 21 + LU: + - rate: 17 + MT: + - rate: 18 + NL: + - rate: 21 + "NO": + - rate: 25 + PL: + - rate: 23 + PT: + - rate: 23 + RO: + - rate: 19 + SK: + - rate: 20 + SI: + - rate: 22 + ES: + - rate: 21 + SE: + - rate: 25 + CH: + - rate: 8.1 + TR: + - rate: 20 + GB: + - rate: 20 +parking_rates: + AT: + - rate: 13 + BE: + - rate: 12 + IE: + - rate: 13.5 + LU: + - rate: 14 + PT: + - rate: 13 +reduced_rates: + AT: + - rate: 10 + - rate: 13 + BE: + - rate: 6 + - rate: 12 + BG: + - rate: 9 + HR: + - rate: 5 + - rate: 13 + CY: + - rate: 5 + - rate: 9 + CZ: + - rate: 12 + EE: + - rate: 9 + FI: + - rate: 10 + - rate: 14 + FR: + - rate: 5.5 + - rate: 10 + DE: + - rate: 7 + GR: + - rate: 6 + - rate: 13 + HU: + - rate: 5 + - rate: 18 + IS: + - rate: 11 + IE: + - rate: 9 + - rate: 13.5 + IT: + - rate: 5 + - rate: 10 + LV: + - rate: 5 + - rate: 12 + LT: + - rate: 5 + - rate: 9 + LU: + - rate: 8 + MT: + - rate: 5 + - rate: 7 + NL: + - rate: 9 + "NO": + - rate: 12 + - rate: 15 + PL: + - rate: 5 + - rate: 8 + PT: + - rate: 6 + - rate: 13 + RO: + - rate: 5 + - rate: 9 + SK: + - rate: 10 + SI: + - rate: 5 + - rate: 9.5 + ES: + - rate: 10 + SE: + - rate: 6 + - rate: 12 + CH: + - rate: 2.6 + - rate: 3.8 + TR: + - rate: 10 + GB: + - rate: 5 +super_reduced_rates: + FR: + - rate: 2.1 + IE: + - rate: 4.8 + IT: + - rate: 4 + LU: + - rate: 3 + SK: + - rate: 5 + ES: + - rate: 4 + TR: + - rate: 1 diff --git a/lib/vat_rate/version.rb b/lib/vat_rate/version.rb index 5499a87..8b8ce34 100644 --- a/lib/vat_rate/version.rb +++ b/lib/vat_rate/version.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true -module VatRate +class VatRate VERSION = "0.1.0" end diff --git a/sig/vat_rate.rbs b/sig/vat_rate.rbs deleted file mode 100644 index 1a68567..0000000 --- a/sig/vat_rate.rbs +++ /dev/null @@ -1,4 +0,0 @@ -module VatRate - VERSION: String - # See the writing guide of rbs: https://github.com/ruby/rbs#guides -end diff --git a/test/test_vat_rate.rb b/test/test_vat_rate.rb new file mode 100644 index 0000000..bd89ef0 --- /dev/null +++ b/test/test_vat_rate.rb @@ -0,0 +1,43 @@ +require 'minitest/autorun' +require 'vat_rate' + +class TestVatRate < Minitest::Test + def test_should_check_data + assert_kind_of Hash, VatRate::RATES + + VatRate::CATEGORIES.each do |category| + VatRate::RATES["#{category}_rates"].each do |country, rates| + assert_kind_of String, country + assert_kind_of Array, rates + rates.each do |rate| + assert rate.key?('rate') + assert_kind_of Numeric, rate['rate'] + assert_kind_of Date, rate['from'] if rate['from'] + end + end + end + end + + def test_should_initialize_rate + vat_rate = VatRate.new(country: :FR) + assert_equal 20, vat_rate.rate + + vat_rate = VatRate.new(country: "FR") + assert_equal 20, vat_rate.rate + end + + def test_should_get_correct_rate_for_date + vat_rate = VatRate.new(country: "FR", date: Date.new(2011)) + assert_equal 19.6, vat_rate.rate + end + + def test_should_get_amounts + vat_rate = VatRate.new(country: "FR") + assert_equal 20, vat_rate.rate + assert_equal 40, vat_rate.vat_amount(200) + assert_equal 40, vat_rate.vat_amount(200, has_vat: false) + assert_equal 40, vat_rate.vat_amount(240, has_vat: true) + assert_equal 240, vat_rate.amount_with_vat(200) + assert_equal 200, vat_rate.amount_without_vat(240) + end +end diff --git a/vat_rate.gemspec b/vat_rate.gemspec index 0b7de77..c5c022c 100644 --- a/vat_rate.gemspec +++ b/vat_rate.gemspec @@ -8,16 +8,15 @@ Gem::Specification.new do |spec| spec.authors = ["Guillaume Dott"] spec.email = ["guillaume+github@dott.fr"] - spec.summary = "TODO: Write a short summary, because RubyGems requires one." - spec.description = "TODO: Write a longer description or delete this line." - spec.homepage = "TODO: Put your gem's website or public repo URL here." + spec.summary = "VAT rates and helper methods" + spec.homepage = "https://code.amnesix.eu/guillaume/vat_rate" spec.required_ruby_version = ">= 3.0.0" - spec.metadata["allowed_push_host"] = "TODO: Set to your gem server 'https://example.com'" + spec.metadata["allowed_push_host"] = "https://rubygems.org" spec.metadata["homepage_uri"] = spec.homepage - spec.metadata["source_code_uri"] = "TODO: Put your gem's public repo URL here." - spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here." + spec.metadata["source_code_uri"] = "https://code.amnesix.eu/guillaume/vat_rate" + spec.metadata["changelog_uri"] = "https://code.amnesix.eu/guillaume/vat_rate/src/branch/main/CHANGELOG.md" # Specify which files should be added to the gem when it is released. # The `git ls-files -z` loads the files in the RubyGem that have been added into git. @@ -35,6 +34,8 @@ Gem::Specification.new do |spec| # Uncomment to register a new dependency of your gem # spec.add_dependency "example-gem", "~> 1.0" + spec.add_development_dependency "minitest" + # For more information and examples about making a new gem, check out our # guide at: https://bundler.io/guides/creating_gem.html end