diff --git a/ecommerce/configuration.rb b/ecommerce/configuration.rb index 2af4d70bf..06caa161b 100644 --- a/ecommerce/configuration.rb +++ b/ecommerce/configuration.rb @@ -13,10 +13,9 @@ module Ecommerce class Configuration - def initialize(number_generator: nil, payment_gateway: nil, available_vat_rates: []) + def initialize(number_generator: nil, payment_gateway: nil) @number_generator = number_generator @payment_gateway = payment_gateway - @available_vat_rates = available_vat_rates end def call(event_store, command_bus) @@ -37,7 +36,7 @@ def configure_bounded_contexts(event_store, command_bus) Payments::Configuration.new(@payment_gateway), Shipping::Configuration.new, Pricing::Configuration.new, - Taxes::Configuration.new(@available_vat_rates), + Taxes::Configuration.new, ProductCatalog::Configuration.new, Fulfillment::Configuration.new ].each { |c| c.call(event_store, command_bus) } diff --git a/ecommerce/taxes/lib/taxes.rb b/ecommerce/taxes/lib/taxes.rb index f1c9db7a7..d18aa918d 100644 --- a/ecommerce/taxes/lib/taxes.rb +++ b/ecommerce/taxes/lib/taxes.rb @@ -7,18 +7,10 @@ module Taxes class Configuration - def self.available_vat_rates - @@available_vat_rates - end - - def initialize(available_vat_rates = []) - @available_vat_rates = available_vat_rates - end - def call(event_store, command_bus) - @@available_vat_rates = @available_vat_rates command_bus.register(SetVatRate, SetVatRateHandler.new(event_store)) command_bus.register(DetermineVatRate, DetermineVatRateHandler.new(event_store)) + command_bus.register(AddAvailableVatRate, AddAvailableVatRateHandler.new(event_store)) end end end diff --git a/ecommerce/taxes/lib/taxes/commands.rb b/ecommerce/taxes/lib/taxes/commands.rb index b3bb5153a..0e958579a 100644 --- a/ecommerce/taxes/lib/taxes/commands.rb +++ b/ecommerce/taxes/lib/taxes/commands.rb @@ -1,11 +1,16 @@ module Taxes class SetVatRate < Infra::Command attribute :product_id, Infra::Types::UUID - attribute :vat_rate, Infra::Types::VatRate + attribute :vat_rate_code, Infra::Types::String end class DetermineVatRate < Infra::Command attribute :product_id, Infra::Types::UUID attribute :order_id, Infra::Types::UUID end -end \ No newline at end of file + + class AddAvailableVatRate < Infra::Command + attribute :available_vat_rate_id, Infra::Types::UUID + attribute :vat_rate, Infra::Types::VatRate + end +end diff --git a/ecommerce/taxes/lib/taxes/events.rb b/ecommerce/taxes/lib/taxes/events.rb index 7f3f308b9..e92c1a117 100644 --- a/ecommerce/taxes/lib/taxes/events.rb +++ b/ecommerce/taxes/lib/taxes/events.rb @@ -9,4 +9,9 @@ class VatRateDetermined < Infra::Event attribute :product_id, Infra::Types::UUID attribute :vat_rate, Infra::Types::VatRate end -end \ No newline at end of file + + class AvailableVatRateAdded < Infra::Event + attribute :available_vat_rate_id, Infra::Types::UUID + attribute :vat_rate, Infra::Types::VatRate + end +end diff --git a/ecommerce/taxes/lib/taxes/product.rb b/ecommerce/taxes/lib/taxes/product.rb index 493c1825c..8fe6c027e 100644 --- a/ecommerce/taxes/lib/taxes/product.rb +++ b/ecommerce/taxes/lib/taxes/product.rb @@ -2,23 +2,16 @@ module Taxes class Product include AggregateRoot - VatRateNotApplicable = Class.new(StandardError) - def initialize(id) @id = id end def set_vat_rate(vat_rate) - raise VatRateNotApplicable unless vat_rate_applicable?(vat_rate) apply(VatRateSet.new(data: { product_id: @id, vat_rate: vat_rate })) end private - def vat_rate_applicable?(vat_rate) - Configuration.available_vat_rates.include?(vat_rate) - end - on(VatRateSet) { |_| } end end diff --git a/ecommerce/taxes/lib/taxes/services.rb b/ecommerce/taxes/lib/taxes/services.rb index eb639f8bf..97c4d0b90 100644 --- a/ecommerce/taxes/lib/taxes/services.rb +++ b/ecommerce/taxes/lib/taxes/services.rb @@ -1,12 +1,17 @@ module Taxes + VatRateAlreadyExists = Class.new(StandardError) + VatRateNotApplicable = Class.new(StandardError) class SetVatRateHandler def initialize(event_store) @repository = Infra::AggregateRootRepository.new(event_store) + @catalog = VatRateCatalog.new(event_store) end def call(cmd) + vat_rate = @catalog.vat_rate_by_code(cmd.vat_rate_code) + raise VatRateNotApplicable unless vat_rate @repository.with_aggregate(Product, cmd.product_id) do |product| - product.set_vat_rate(cmd.vat_rate) + product.set_vat_rate(vat_rate) end end end @@ -34,4 +39,34 @@ def stream_name(order_id) "Taxes::Order$#{order_id}" end end -end \ No newline at end of file + + class AddAvailableVatRateHandler + def initialize(event_store) + @catalog = VatRateCatalog.new(event_store) + @event_store = event_store + end + + def call(cmd) + raise VatRateAlreadyExists if catalog.vat_rate_by_code(cmd.vat_rate.code) + + event_store.publish(available_vat_rate_added_event(cmd), stream_name: stream_name(cmd)) + end + + private + + attr_reader :event_store, :catalog + + def available_vat_rate_added_event(cmd) + AvailableVatRateAdded.new( + data: { + available_vat_rate_id: cmd.available_vat_rate_id, + vat_rate: cmd.vat_rate + } + ) + end + + def stream_name(cmd) + "Taxes::AvailableVatRate$#{cmd.vat_rate.code}" + end + end +end diff --git a/ecommerce/taxes/lib/taxes/vat_rate_catalog.rb b/ecommerce/taxes/lib/taxes/vat_rate_catalog.rb index d00fd4266..98f5de33b 100644 --- a/ecommerce/taxes/lib/taxes/vat_rate_catalog.rb +++ b/ecommerce/taxes/lib/taxes/vat_rate_catalog.rb @@ -14,5 +14,15 @@ def vat_rate_for(product_id) &.data &.fetch(:vat_rate) end + + def vat_rate_by_code(vat_rate_code) + @event_store + .read + .stream("Taxes::AvailableVatRate$#{vat_rate_code}") + .last + &.data + &.fetch(:vat_rate) + &.then { |vat_rate| Infra::Types::VatRate.new(vat_rate) } + end end -end \ No newline at end of file +end diff --git a/ecommerce/taxes/test/taxes_test.rb b/ecommerce/taxes/test/taxes_test.rb index 77e58d57f..ca61b8064 100644 --- a/ecommerce/taxes/test/taxes_test.rb +++ b/ecommerce/taxes/test/taxes_test.rb @@ -3,51 +3,74 @@ module Taxes class TaxesTest < Test def test_setting_available_vat_rate + vat_rate = Infra::Types::VatRate.new(code: "50", rate: 50) + add_available_vat_rate(vat_rate) + product_id = SecureRandom.uuid - vat_rate_set = VatRateSet.new(data: { product_id: product_id, vat_rate: available_vat_rate }) + vat_rate_set = VatRateSet.new(data: { product_id: product_id, vat_rate: vat_rate }) assert_events("Taxes::Product$#{product_id}", vat_rate_set) do - set_vat_rate(product_id, available_vat_rate) + set_vat_rate(product_id, vat_rate.code) end end def test_setting_unavailable_vat_rate_should_raise_error product_id = SecureRandom.uuid - assert_raises(Product::VatRateNotApplicable) do - set_vat_rate(product_id, unavailable_vat_rate) + unavailable_vat_rate = Infra::Types::VatRate.new(code: "20", rate: 20) + + assert_raises(Taxes::VatRateNotApplicable) do + set_vat_rate(product_id, unavailable_vat_rate.code) end end def test_determining_vat_rate + vat_rate = Infra::Types::VatRate.new(code: "50", rate: 50) + add_available_vat_rate(vat_rate) + order_id = SecureRandom.uuid product_id = SecureRandom.uuid another_product_id = SecureRandom.uuid - set_vat_rate(product_id, available_vat_rate) - vat_rate_determined = VatRateDetermined.new(data: { order_id: order_id, product_id: product_id, vat_rate: available_vat_rate }) + set_vat_rate(product_id, vat_rate.code) + vat_rate_determined = VatRateDetermined.new(data: { order_id: order_id, product_id: product_id, vat_rate: vat_rate }) assert_events("Taxes::Order$#{order_id}", vat_rate_determined) do - determine_vat_rate(order_id, product_id, available_vat_rate) + determine_vat_rate(order_id, product_id, vat_rate) end assert_events("Taxes::Order$#{order_id}") do - determine_vat_rate(order_id, another_product_id, available_vat_rate) + determine_vat_rate(order_id, another_product_id, vat_rate) + end + end + + def test_adding_available_vat_rate + available_vat_rate_id = SecureRandom.uuid + vat_rate = Infra::Types::VatRate.new(code: "50", rate: 50) + available_vat_rate_added = AvailableVatRateAdded.new(data: { available_vat_rate_id: available_vat_rate_id, vat_rate: vat_rate }) + + assert_events("Taxes::AvailableVatRate$#{vat_rate.code}", available_vat_rate_added) do + add_available_vat_rate(vat_rate, available_vat_rate_id) + end + end + + def test_should_not_allow_for_double_registration + vat_rate = Infra::Types::VatRate.new(code: "50", rate: 50) + add_available_vat_rate(vat_rate) + + assert_raises(VatRateAlreadyExists) do + add_available_vat_rate(vat_rate) end end private - def set_vat_rate(product_id, vat_rate) - run_command(SetVatRate.new(product_id: product_id, vat_rate: vat_rate)) + def set_vat_rate(product_id, vat_rate_code) + run_command(SetVatRate.new(product_id: product_id, vat_rate_code: vat_rate_code)) end def determine_vat_rate(order_id, product_id, vat_rate) run_command(DetermineVatRate.new(order_id: order_id, product_id: product_id, vat_rate: vat_rate)) end - def available_vat_rate - Configuration.available_vat_rates.first - end - - def unavailable_vat_rate - Infra::Types::VatRate.new(code: "50", rate: 50) + def add_available_vat_rate(vat_rate, available_vat_rate_id = SecureRandom.uuid) + run_command(AddAvailableVatRate.new(available_vat_rate_id: available_vat_rate_id, vat_rate: vat_rate)) end end end diff --git a/ecommerce/taxes/test/test_helper.rb b/ecommerce/taxes/test/test_helper.rb index a23c2d113..6a1f5333e 100644 --- a/ecommerce/taxes/test/test_helper.rb +++ b/ecommerce/taxes/test/test_helper.rb @@ -9,13 +9,7 @@ class Test < Infra::InMemoryTest def before_setup super - Configuration.new([dummy_vat_rate]).call(event_store, command_bus) - end - - private - - def dummy_vat_rate - Infra::Types::VatRate.new(code: "20", rate: 20) + Configuration.new.call(event_store, command_bus) end end end diff --git a/ecommerce/taxes/test/vat_rate_catalog_test.rb b/ecommerce/taxes/test/vat_rate_catalog_test.rb new file mode 100644 index 000000000..d253925c7 --- /dev/null +++ b/ecommerce/taxes/test/vat_rate_catalog_test.rb @@ -0,0 +1,30 @@ +require_relative "test_helper" + +module Taxes + class VatRateCatalogTest < Test + class VatRateByCodeTest < VatRateCatalogTest + def setup + @vat_rate = Infra::Types::VatRate.new(code: "50", rate: 50) + add_available_vat_rate(@vat_rate) + end + + def test_returns_available_vat_rate + assert_equal @vat_rate, catalog.vat_rate_by_code("50") + end + + def test_returns_nil_when_vat_rate_is_not_available + assert_nil catalog.vat_rate_by_code("60") + end + end + + private + + def catalog + VatRateCatalog.new(@event_store) + end + + def add_available_vat_rate(vat_rate, available_vat_rate_id = SecureRandom.uuid) + run_command(AddAvailableVatRate.new(available_vat_rate_id: available_vat_rate_id, vat_rate: vat_rate)) + end + end +end diff --git a/rails_application/app/controllers/available_vat_rates_controller.rb b/rails_application/app/controllers/available_vat_rates_controller.rb new file mode 100644 index 000000000..34f3debf5 --- /dev/null +++ b/rails_application/app/controllers/available_vat_rates_controller.rb @@ -0,0 +1,56 @@ +class AvailableVatRatesController < ApplicationController + class AvailableVatRateForm + include ActiveModel::Model + include ActiveModel::Validations + + attr_reader :code, :rate + + def initialize(params) + @code = params[:code] + @rate = params[:rate] + end + + validates :code, presence: true + validates :rate, presence: true, numericality: { only_numeric: true, greater_than: 0 } + end + + def new + end + + def create + available_vat_rate_id = SecureRandom.uuid + available_vat_rate_form = AvailableVatRateForm.new(available_vat_rate_params) + + unless available_vat_rate_form.valid? + return render "new", locals: { errors: available_vat_rate_form.errors }, status: :unprocessable_entity + end + + add_available_vat_rate(available_vat_rate_form.code, available_vat_rate_form.rate, available_vat_rate_id) + rescue Taxes::VatRateAlreadyExists + flash.now[:notice] = "VAT rate already exists" + render "new", status: :unprocessable_entity + else + redirect_to available_vat_rates_path, notice: "VAT rate was successfully created" + end + + def index + @available_vat_rates = VatRates::AvailableVatRate.all + end + + private + + def add_available_vat_rate(code, rate, available_vat_rate_id) + command_bus.(add_available_vat_rate_cmd(code, rate, available_vat_rate_id)) + end + + def add_available_vat_rate_cmd(code, rate, available_vat_rate_id) + Taxes::AddAvailableVatRate.new( + available_vat_rate_id: available_vat_rate_id, + vat_rate: Infra::Types::VatRate.new(code: code, rate: rate) + ) + end + + def available_vat_rate_params + params.permit(:code, :rate) + end +end diff --git a/rails_application/app/controllers/products_controller.rb b/rails_application/app/controllers/products_controller.rb index 87b0e14ca..08498a691 100644 --- a/rails_application/app/controllers/products_controller.rb +++ b/rails_application/app/controllers/products_controller.rb @@ -46,15 +46,15 @@ def create if product_form.vat_rate.present? set_product_vat_rate(product_form.product_id, product_form.vat_rate) end - rescue ProductCatalog::AlreadyRegistered - flash[:notice] = "Product was already registered" - render "new" - rescue Taxes::Product::VatRateNotApplicable - flash[:notice] = "Selected VAT rate not applicable" - render "new" - else - redirect_to products_path, notice: "Product was successfully created" end + + redirect_to products_path, notice: "Product was successfully created" + rescue ProductCatalog::AlreadyRegistered + flash[:notice] = "Product was already registered" + render "new" + rescue Taxes::VatRateNotApplicable + flash[:notice] = "Selected VAT rate is not applicable" + render "new" end def update @@ -91,8 +91,7 @@ def set_future_product_price(product_id, price, valid_since) command_bus.(set_product_future_price_cmd(product_id, price, valid_since)) end - def set_product_vat_rate(product_id, vat_rate_code) - vat_rate = Taxes::Configuration.available_vat_rates.find{|rate| rate.code == vat_rate_code} + def set_product_vat_rate(product_id, vat_rate) command_bus.(set_product_vat_rate_cmd(product_id, vat_rate)) end @@ -113,7 +112,7 @@ def set_product_price_cmd(product_id, price) end def set_product_vat_rate_cmd(product_id, vat_rate) - Taxes::SetVatRate.new(product_id: product_id, vat_rate: vat_rate) + Taxes::SetVatRate.new(product_id: product_id, vat_rate_code: vat_rate) end def set_product_future_price_cmd(product_id, price, valid_since) diff --git a/rails_application/app/read_models/vat_rates/add_available_vat_rate.rb b/rails_application/app/read_models/vat_rates/add_available_vat_rate.rb new file mode 100644 index 000000000..5f418bb14 --- /dev/null +++ b/rails_application/app/read_models/vat_rates/add_available_vat_rate.rb @@ -0,0 +1,11 @@ +module VatRates + class AddAvailableVatRate + def call(event) + AvailableVatRate.create!( + uid: event.data.fetch(:available_vat_rate_id), + code: event.data.fetch(:vat_rate).fetch(:code), + rate: event.data.fetch(:vat_rate).fetch(:rate) + ) + end + end +end diff --git a/rails_application/app/read_models/vat_rates/configuration.rb b/rails_application/app/read_models/vat_rates/configuration.rb new file mode 100644 index 000000000..1d41b3d02 --- /dev/null +++ b/rails_application/app/read_models/vat_rates/configuration.rb @@ -0,0 +1,11 @@ +module VatRates + class AvailableVatRate < ApplicationRecord + self.table_name = "available_vat_rates" + end + + class Configuration + def call(event_store) + event_store.subscribe(AddAvailableVatRate, to: [Taxes::AvailableVatRateAdded]) + end + end +end diff --git a/rails_application/app/views/application/_top_navigation.html.erb b/rails_application/app/views/application/_top_navigation.html.erb index 16a1b7ead..0f2dd6a75 100644 --- a/rails_application/app/views/application/_top_navigation.html.erb +++ b/rails_application/app/views/application/_top_navigation.html.erb @@ -15,6 +15,7 @@ <%= navigation_link "Coupons", coupons_path %> <%= navigation_link "Time Promotions", time_promotions_path %> <%= navigation_link "Customers", customers_path %> + <%= navigation_link "VAT Rates", available_vat_rates_path %> <%= navigation_link "Client View", clients_path %> diff --git a/rails_application/app/views/available_vat_rates/index.html.erb b/rails_application/app/views/available_vat_rates/index.html.erb new file mode 100644 index 000000000..3632b7e76 --- /dev/null +++ b/rails_application/app/views/available_vat_rates/index.html.erb @@ -0,0 +1,27 @@ +<% content_for(:header) do %> + VAT Rates +<% end %> + +<% content_for(:actions) do %> + <%= primary_action_button do %> + <%= link_to 'New VAT Rate', new_available_vat_rate_path %> + <% end %> +<% end %> + + + + + + + + + + + <% @available_vat_rates.each do |available_vat_rate| %> + + + + + <% end %> + +
CodeRate
<%= available_vat_rate.code %><%= available_vat_rate.rate %>
diff --git a/rails_application/app/views/available_vat_rates/new.html.erb b/rails_application/app/views/available_vat_rates/new.html.erb new file mode 100644 index 000000000..d5e72a8fe --- /dev/null +++ b/rails_application/app/views/available_vat_rates/new.html.erb @@ -0,0 +1,37 @@ +<% content_for(:header) do %> + New VAT Rate +<% end %> + +<% content_for(:actions) do %> + <%= secondary_action_button do %> + <%= link_to 'Back', available_vat_rates_path %> + <% end %> + + <%= primary_form_action_button do %> + Create VAT Rate + <% end %> +<% end %> + + +<%= form_tag({ controller: "available_vat_rates", action: "create" }, method: "post", id: "form") do %> +
+ + <%= text_field_tag :code, "", required: true, class: "mt-1 focus:ring-blue-500 focus:border-blue-500 block shadow-sm sm:text-sm border-gray-300 rounded-md", data: { turbo_permanent: true } %> +
+
+ + <%= number_field_tag :rate, nil, min: 0, step: 1.0, required: true, class: "mt-1 focus:ring-blue-500 focus:border-blue-500 block shadow-sm sm:text-sm border-gray-300 rounded-md", data: { turbo_permanent: true } %> +
+ + <% if defined?(errors) %> + <% errors.each do |error| %> +
+ <%= error.full_message %> +
+ <% end %> + <% end %> +<% end %> diff --git a/rails_application/app/views/products/new.html.erb b/rails_application/app/views/products/new.html.erb index bb2480093..da28481d1 100644 --- a/rails_application/app/views/products/new.html.erb +++ b/rails_application/app/views/products/new.html.erb @@ -32,7 +32,7 @@ - <%= select_tag :vat_rate, options_from_collection_for_select(Taxes::Configuration.available_vat_rates, :code, :code), class: "mt-1 focus:ring-blue-500 focus:border-blue-500 block shadow-sm sm:text-sm border-gray-300 rounded-md", data: { turbo_permanent: true } %> + <%= select_tag :vat_rate, options_from_collection_for_select(VatRates::AvailableVatRate.all, :code, :code), class: "mt-1 focus:ring-blue-500 focus:border-blue-500 block shadow-sm sm:text-sm border-gray-300 rounded-md", data: { turbo_permanent: true } %> <% if defined?(errors) %> @@ -43,4 +43,4 @@ <% end %> <% end %> <% end %> -<% end %> \ No newline at end of file +<% end %> diff --git a/rails_application/config/routes.rb b/rails_application/config/routes.rb index e193db270..916297ddf 100644 --- a/rails_application/config/routes.rb +++ b/rails_application/config/routes.rb @@ -46,6 +46,9 @@ post :remove_item end end + + resources :available_vat_rates, only: [:new, :create, :index] + post :login, to: "client/clients#login" get :logout, to: "client/clients#logout" get "clients", to: "client/clients#index" diff --git a/rails_application/db/migrate/20240812080357_create_available_vat_rates.rb b/rails_application/db/migrate/20240812080357_create_available_vat_rates.rb new file mode 100644 index 000000000..080759089 --- /dev/null +++ b/rails_application/db/migrate/20240812080357_create_available_vat_rates.rb @@ -0,0 +1,11 @@ +class CreateAvailableVatRates < ActiveRecord::Migration[7.0] + def change + create_table :available_vat_rates do |t| + t.uuid :uid, null: false + t.string :code, null: false + t.decimal :rate, null: false + + t.timestamps + end + end +end diff --git a/rails_application/db/schema.rb b/rails_application/db/schema.rb index 6a06abb5d..5b991f319 100644 --- a/rails_application/db/schema.rb +++ b/rails_application/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2023_09_06_105318) do +ActiveRecord::Schema[7.0].define(version: 2024_08_12_080357) do # These are extensions that must be enabled in order to support this database enable_extension "pgcrypto" enable_extension "plpgsql" @@ -26,6 +26,14 @@ t.integer "available" end + create_table "available_vat_rates", force: :cascade do |t| + t.uuid "uid", null: false + t.string "code", null: false + t.decimal "rate", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + create_table "client_order_lines", force: :cascade do |t| t.string "order_uid" t.string "product_name" diff --git a/rails_application/db/seeds.rb b/rails_application/db/seeds.rb index a60141af3..6b2a88b2a 100644 --- a/rails_application/db/seeds.rb +++ b/rails_application/db/seeds.rb @@ -36,6 +36,18 @@ ) end +[ + ["20", 20], + ["10", 10] +].each do |vat_rate| + command_bus.call( + Taxes::AddAvailableVatRate.new( + available_vat_rate_id: SecureRandom.uuid, + vat_rate: Infra::Types::VatRate.new(code: vat_rate[0], rate: vat_rate[1]) + ) + ) +end + [ ["Fearless Refactoring: Rails controllers", 49], ["Rails meets React.js", 49], @@ -47,7 +59,7 @@ ProductCatalog::RegisterProduct.new(product_id: product_id), ProductCatalog::NameProduct.new(product_id: product_id, name: name_price_tuple[0]), Pricing::SetPrice.new(product_id: product_id, price: name_price_tuple[1]), - Taxes::SetVatRate.new(product_id: product_id, vat_rate: Taxes::Configuration.available_vat_rates.first) + Taxes::SetVatRate.new(product_id: product_id, vat_rate_code: "20") ].each do |command| command_bus.call(command) end diff --git a/rails_application/lib/configuration.rb b/rails_application/lib/configuration.rb index 078f236c2..512be0705 100644 --- a/rails_application/lib/configuration.rb +++ b/rails_application/lib/configuration.rb @@ -16,14 +16,11 @@ def call(event_store, command_bus) enable_shipments_read_model(event_store) enable_availability_read_model(event_store) enable_authentication_read_model(event_store) + enable_vat_rates_read_model(event_store) Ecommerce::Configuration.new( number_generator: Rails.configuration.number_generator, - payment_gateway: Rails.configuration.payment_gateway, - available_vat_rates: [ - Infra::Types::VatRate.new(code: "10", rate: 10), - Infra::Types::VatRate.new(code: "20", rate: 20) - ] + payment_gateway: Rails.configuration.payment_gateway ).call(event_store, command_bus) end @@ -80,4 +77,8 @@ def enable_availability_read_model(event_store) def enable_authentication_read_model(event_store) ClientAuthentication::Configuration.new.call(event_store) end + + def enable_vat_rates_read_model(event_store) + VatRates::Configuration.new.call(event_store) + end end diff --git a/rails_application/test/integration/available_vat_rates_test.rb b/rails_application/test/integration/available_vat_rates_test.rb new file mode 100644 index 000000000..1b08d1e99 --- /dev/null +++ b/rails_application/test/integration/available_vat_rates_test.rb @@ -0,0 +1,52 @@ +require "test_helper" + +class AvailableVatRatesTest < InMemoryRESIntegrationTestCase + def test_happy_path + get "/available_vat_rates/new" + assert_select "h1", "New VAT Rate" + + post "/available_vat_rates", + params: { + "authenticity_token" => "[FILTERED]", + "code" => "10.0", + "rate" => 10.0 + } + follow_redirect! + + assert_equal "VAT rate was successfully created", flash[:notice] + assert_select "h1", "VAT Rates" + end + + def test_validation_errors + post "/available_vat_rates", + params: { + "authenticity_token" => "[FILTERED]", + "code" => "", + "rate" => "" + } + assert_response :unprocessable_entity + + assert_select "h1", "New VAT Rate" + assert_select "span", "Code can't be blank" + assert_select "span", "Rate can't be blank" + end + + def test_vat_rate_already_exists + post "/available_vat_rates", + params: { + "authenticity_token" => "[FILTERED]", + "code" => "10.0", + "rate" => 10.0 + } + + post "/available_vat_rates", + params: { + "authenticity_token" => "[FILTERED]", + "code" => "10.0", + "rate" => 10.0 + } + + assert_response :unprocessable_entity + assert_select "#notice", "VAT rate already exists" + end +end diff --git a/rails_application/test/integration/client_orders_test.rb b/rails_application/test/integration/client_orders_test.rb index d2ea08c1c..e5dc691a4 100644 --- a/rails_application/test/integration/client_orders_test.rb +++ b/rails_application/test/integration/client_orders_test.rb @@ -9,6 +9,7 @@ def setup ClientOrders::Client.destroy_all ClientOrders::Order.destroy_all Orders::Order.destroy_all + add_available_vat_rate(10) end def test_happy_path diff --git a/rails_application/test/integration/customers_test.rb b/rails_application/test/integration/customers_test.rb index e6c187010..805b3e9db 100644 --- a/rails_application/test/integration/customers_test.rb +++ b/rails_application/test/integration/customers_test.rb @@ -1,6 +1,11 @@ require "test_helper" class CustomersTest < InMemoryRESIntegrationTestCase + def setup + super + add_available_vat_rate(10) + end + def test_list_customers get "/customers" assert_response :success diff --git a/rails_application/test/integration/discount_test.rb b/rails_application/test/integration/discount_test.rb index 804b855bc..a96cfbe9f 100644 --- a/rails_application/test/integration/discount_test.rb +++ b/rails_application/test/integration/discount_test.rb @@ -4,6 +4,7 @@ class DiscountTest < InMemoryRESIntegrationTestCase def setup super Orders::Order.destroy_all + add_available_vat_rate(10) end def test_reset_discount @@ -40,4 +41,3 @@ def apply_discount_10_percent(order_id) assert_select("td", "$123.30") end end - diff --git a/rails_application/test/integration/orders_test.rb b/rails_application/test/integration/orders_test.rb index 6725d0d91..08e11bf37 100644 --- a/rails_application/test/integration/orders_test.rb +++ b/rails_application/test/integration/orders_test.rb @@ -5,6 +5,7 @@ def setup super Rails.configuration.payment_gateway.call.reset Orders::Order.destroy_all + add_available_vat_rate(10) end def test_submitting_empty_order diff --git a/rails_application/test/integration/products_test.rb b/rails_application/test/integration/products_test.rb index e9f8b6943..5df6046ff 100644 --- a/rails_application/test/integration/products_test.rb +++ b/rails_application/test/integration/products_test.rb @@ -8,6 +8,7 @@ def setup end def test_happy_path + add_available_vat_rate(10) register_customer("Arkency") product_id = SecureRandom.uuid diff --git a/rails_application/test/integration/public_offer_test.rb b/rails_application/test/integration/public_offer_test.rb index dd8f0281e..1342ba841 100644 --- a/rails_application/test/integration/public_offer_test.rb +++ b/rails_application/test/integration/public_offer_test.rb @@ -3,6 +3,7 @@ class PublicOfferTest < InMemoryRESIntegrationTestCase def setup super + add_available_vat_rate(10) end def test_happy_path diff --git a/rails_application/test/invoices/invoices_test.rb b/rails_application/test/invoices/invoices_test.rb index 70ef0220e..c9359fe1c 100644 --- a/rails_application/test/invoices/invoices_test.rb +++ b/rails_application/test/invoices/invoices_test.rb @@ -29,27 +29,28 @@ def test_product_name_change_affects_existing_invoices product_id = SecureRandom.uuid initial_product_name = "Initial Name" updated_product_name = "Updated Name" - + + add_available_vat_rate(20) product_id = register_product(initial_product_name, 100, 20) customer_id = register_customer("Test Customer") - + order_id = SecureRandom.uuid add_product_to_basket(order_id, product_id) submit_order(customer_id, order_id) - + update_product_name(product_id, updated_product_name) - + assert_invoice_product_name(order_id, initial_product_name) new_order_id = SecureRandom.uuid add_product_to_basket(new_order_id, product_id) submit_order(customer_id, new_order_id) - + assert_invoice_product_name(new_order_id, updated_product_name) end - + private - + def update_product_name(product_id, new_name) patch "/products/#{product_id}", params: { @@ -58,11 +59,11 @@ def update_product_name(product_id, new_name) name: new_name, } end - + def assert_invoice_product_name(order_id, expected_name) get "/invoices/#{order_id}" assert_response :success assert_select ".py-2", text: expected_name end end -end \ No newline at end of file +end diff --git a/rails_application/test/read_model_handler_test.rb b/rails_application/test/read_model_handler_test.rb index ae91ab73b..b2c809456 100644 --- a/rails_application/test/read_model_handler_test.rb +++ b/rails_application/test/read_model_handler_test.rb @@ -101,7 +101,7 @@ def vat_rate_set end def available_vat_rate - Taxes::Configuration.available_vat_rates.first + Infra::Types::VatRate.new(code: "10", rate: 10) end def event_store diff --git a/rails_application/test/test_helper.rb b/rails_application/test/test_helper.rb index 7afc620ad..c9d34ca64 100644 --- a/rails_application/test/test_helper.rb +++ b/rails_application/test/test_helper.rb @@ -86,6 +86,10 @@ def register_product(name, price, vat_rate) product_id end + def add_available_vat_rate(rate, code = rate.to_s) + post "/available_vat_rates", params: { code: code, rate: rate } + end + def supply_product(product_id, quantity) post "/products/#{product_id}/supplies", params: { quantity: quantity } end diff --git a/rails_application/test/vat_rates/available_vat_rate_added_test.rb b/rails_application/test/vat_rates/available_vat_rate_added_test.rb new file mode 100644 index 000000000..d7558c1ee --- /dev/null +++ b/rails_application/test/vat_rates/available_vat_rate_added_test.rb @@ -0,0 +1,39 @@ +require "test_helper" + +module VatRates + class AvailableVatRateAddedTest < InMemoryTestCase + cover "VatRates*" + + def test_adding_available_vat_rate + uid = SecureRandom.uuid + code = "50" + rate = 50 + + event_store.publish(available_vat_rate_added_event(uid, code, rate)) + available_vat_rate = AvailableVatRate.find_by_uid(uid) + + assert_equal(uid, available_vat_rate.uid) + assert_equal(code, available_vat_rate.code) + assert_equal(rate, available_vat_rate.rate) + end + + private + + def event_store + Rails.configuration.event_store + end + + def available_vat_rate_added_event(uid, code, rate) + Taxes::AvailableVatRateAdded.new( + data: { + available_vat_rate_id: uid, + vat_rate: + { + code: code, + rate: rate + } + } + ) + end + end +end