Skip to content

Commit

Permalink
Merge pull request #383 from RailsEventStore/revert-382-revert-377-av…
Browse files Browse the repository at this point in the history
…ailable-vat-rates

Revert "Revert "Allow creating custom vat rates""
  • Loading branch information
marlena-b authored Aug 21, 2024
2 parents 831dadc + 2c98985 commit e779047
Show file tree
Hide file tree
Showing 34 changed files with 458 additions and 79 deletions.
5 changes: 2 additions & 3 deletions ecommerce/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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) }
Expand Down
10 changes: 1 addition & 9 deletions ecommerce/taxes/lib/taxes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
9 changes: 7 additions & 2 deletions ecommerce/taxes/lib/taxes/commands.rb
Original file line number Diff line number Diff line change
@@ -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

class AddAvailableVatRate < Infra::Command
attribute :available_vat_rate_id, Infra::Types::UUID
attribute :vat_rate, Infra::Types::VatRate
end
end
7 changes: 6 additions & 1 deletion ecommerce/taxes/lib/taxes/events.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,9 @@ class VatRateDetermined < Infra::Event
attribute :product_id, Infra::Types::UUID
attribute :vat_rate, Infra::Types::VatRate
end
end

class AvailableVatRateAdded < Infra::Event
attribute :available_vat_rate_id, Infra::Types::UUID
attribute :vat_rate, Infra::Types::VatRate
end
end
7 changes: 0 additions & 7 deletions ecommerce/taxes/lib/taxes/product.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
39 changes: 37 additions & 2 deletions ecommerce/taxes/lib/taxes/services.rb
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -34,4 +39,34 @@ def stream_name(order_id)
"Taxes::Order$#{order_id}"
end
end
end

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
12 changes: 11 additions & 1 deletion ecommerce/taxes/lib/taxes/vat_rate_catalog.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
end
55 changes: 39 additions & 16 deletions ecommerce/taxes/test/taxes_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
8 changes: 1 addition & 7 deletions ecommerce/taxes/test/test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
30 changes: 30 additions & 0 deletions ecommerce/taxes/test/vat_rate_catalog_test.rb
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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: { 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
Loading

0 comments on commit e779047

Please sign in to comment.