diff --git a/ecommerce/authentication/.mutant.yml b/ecommerce/authentication/.mutant.yml
deleted file mode 100644
index 8c5b2b23f..000000000
--- a/ecommerce/authentication/.mutant.yml
+++ /dev/null
@@ -1,10 +0,0 @@
-requires:
- - ./test/test_helper
-integration: minitest
-coverage_criteria:
- process_abort: true
-matcher:
- subjects:
- - Authentication*
- ignore:
- - Authentication::Configuration#call
diff --git a/ecommerce/authentication/Gemfile b/ecommerce/authentication/Gemfile
deleted file mode 100644
index 8fd887144..000000000
--- a/ecommerce/authentication/Gemfile
+++ /dev/null
@@ -1,4 +0,0 @@
-source "https://rubygems.org"
-
-eval_gemfile "../../infra/Gemfile.test"
-gem "infra", path: "../../infra"
\ No newline at end of file
diff --git a/ecommerce/authentication/Gemfile.lock b/ecommerce/authentication/Gemfile.lock
deleted file mode 100644
index 4a73abff2..000000000
--- a/ecommerce/authentication/Gemfile.lock
+++ /dev/null
@@ -1,119 +0,0 @@
-PATH
- remote: ../../infra
- specs:
- infra (1.0.0)
- aggregate_root (~> 2.13)
- arkency-command_bus
- dry-struct
- dry-types
- rake
- ruby_event_store (~> 2.13)
- ruby_event_store-transformations
- sidekiq
-
-GEM
- remote: https://oss:7AXfeZdAfCqL1PvHm2nvDJO6Zd9UW8IK@gem.mutant.dev/
- specs:
- mutant-license (0.1.1.2.1627430819213747598431630701693729869473.6)
-
-GEM
- remote: https://rubygems.org/
- specs:
- activesupport (7.1.2)
- base64
- bigdecimal
- concurrent-ruby (~> 1.0, >= 1.0.2)
- connection_pool (>= 2.2.5)
- drb
- i18n (>= 1.6, < 2)
- minitest (>= 5.1)
- mutex_m
- tzinfo (~> 2.0)
- aggregate_root (2.13.0)
- ruby_event_store (= 2.13.0)
- arkency-command_bus (0.4.1)
- concurrent-ruby
- ast (2.4.2)
- base64 (0.2.0)
- bigdecimal (3.1.4)
- concurrent-ruby (1.2.2)
- connection_pool (2.4.1)
- diff-lcs (1.5.0)
- drb (2.2.0)
- ruby2_keywords
- dry-core (1.0.1)
- concurrent-ruby (~> 1.0)
- zeitwerk (~> 2.6)
- dry-inflector (1.0.0)
- dry-logic (1.5.0)
- concurrent-ruby (~> 1.0)
- dry-core (~> 1.0, < 2)
- zeitwerk (~> 2.6)
- dry-struct (1.6.0)
- dry-core (~> 1.0, < 2)
- dry-types (>= 1.7, < 2)
- ice_nine (~> 0.11)
- zeitwerk (~> 2.6)
- dry-types (1.7.1)
- concurrent-ruby (~> 1.0)
- dry-core (~> 1.0)
- dry-inflector (~> 1.0)
- dry-logic (~> 1.4)
- zeitwerk (~> 2.6)
- i18n (1.14.1)
- concurrent-ruby (~> 1.0)
- ice_nine (0.11.2)
- minitest (5.15.0)
- mutant (0.11.26)
- diff-lcs (~> 1.3)
- parser (~> 3.2.2, >= 3.2.2.4)
- regexp_parser (~> 2.8.2)
- sorbet-runtime (~> 0.5.0)
- unparser (~> 0.6.9)
- mutant-minitest (0.11.26)
- minitest (~> 5.11)
- mutant (= 0.11.26)
- mutex_m (0.2.0)
- parser (3.2.2.4)
- ast (~> 2.4.1)
- racc
- racc (1.7.3)
- rack (3.0.8)
- rake (13.1.0)
- redis-client (0.19.0)
- connection_pool
- regexp_parser (2.8.3)
- ruby2_keywords (0.0.5)
- ruby_event_store (2.13.0)
- concurrent-ruby (~> 1.0, >= 1.1.6)
- ruby_event_store-transformations (0.1.0)
- activesupport (>= 5.0)
- ruby_event_store (>= 2.0.0, < 3.0.0)
- sidekiq (7.2.0)
- concurrent-ruby (< 2)
- connection_pool (>= 2.3.0)
- rack (>= 2.2.4)
- redis-client (>= 0.14.0)
- sorbet-runtime (0.5.11190)
- tzinfo (2.0.6)
- concurrent-ruby (~> 1.0)
- unparser (0.6.12)
- diff-lcs (~> 1.3)
- parser (>= 3.2.2.4)
- zeitwerk (2.6.12)
-
-PLATFORMS
- arm64-darwin-20
- arm64-darwin-21
- ruby
- x86_64-darwin-20
- x86_64-linux
-
-DEPENDENCIES
- infra!
- minitest (= 5.15.0)!
- mutant-license!
- mutant-minitest (= 0.11.26)!
-
-BUNDLED WITH
- 2.5.9
diff --git a/ecommerce/authentication/Makefile b/ecommerce/authentication/Makefile
deleted file mode 100644
index 23850fab1..000000000
--- a/ecommerce/authentication/Makefile
+++ /dev/null
@@ -1,10 +0,0 @@
-install:
- @bundle install
-
-test:
- @bundle exec ruby -e "require \"rake/rake_test_loader\"" test/*_test.rb
-
-mutate:
- @RAILS_ENV=test bundle exec mutant run
-
-.PHONY: install test mutate
diff --git a/ecommerce/authentication/README.md b/ecommerce/authentication/README.md
deleted file mode 100644
index c8a17ba12..000000000
--- a/ecommerce/authentication/README.md
+++ /dev/null
@@ -1,7 +0,0 @@
-# Authentication
-
-#### Up and running
-
-```
-make install test mutate
-```
diff --git a/ecommerce/authentication/lib/authentication.rb b/ecommerce/authentication/lib/authentication.rb
deleted file mode 100644
index fe28d3c1d..000000000
--- a/ecommerce/authentication/lib/authentication.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-require "infra"
-require_relative "authentication/commands/connect_account_to_client"
-require_relative "authentication/commands/set_login"
-require_relative "authentication/commands/set_password_hash"
-require_relative "authentication/commands/register_account"
-require_relative "authentication/events/account_connected_to_client"
-require_relative "authentication/events/account_registered"
-require_relative "authentication/events/login_set"
-require_relative "authentication/events/password_hash_set"
-require_relative "authentication/account_service"
-require_relative "authentication/account"
-
-module Authentication
- class Configuration
- def call(event_store, command_bus)
- command_bus.register(RegisterAccount, RegisterAccountHandler.new(event_store))
- command_bus.register(SetLogin, SetLoginHandler.new(event_store))
- command_bus.register(SetPasswordHash, SetPasswordHashHandler.new(event_store))
- command_bus.register(
- ConnectAccountToClient,
- ConnectAccountToClientHandler.new(event_store)
- )
- end
- end
-end
diff --git a/ecommerce/authentication/lib/authentication/account.rb b/ecommerce/authentication/lib/authentication/account.rb
deleted file mode 100644
index c790f4f82..000000000
--- a/ecommerce/authentication/lib/authentication/account.rb
+++ /dev/null
@@ -1,47 +0,0 @@
-module Authentication
- class Account
- include AggregateRoot
-
- AlreadyRegistered = Class.new(StandardError)
-
- def initialize(id)
- @id = id
- end
-
- def register
- raise AlreadyRegistered if @registered
-
- apply AccountRegistered.new(data: { account_id: @id })
- end
-
- def set_login(login)
- apply LoginSet.new(data: { account_id: @id, login: login })
- end
-
- def set_password_hash(password_hash)
- apply PasswordHashSet.new(data: { account_id: @id, password_hash: password_hash })
- end
-
- def connect_client(client_id)
- apply AccountConnectedToClient.new(data: { account_id: @id, client_id: client_id })
- end
-
- private
-
- on AccountRegistered do |event|
- @registered = true
- end
-
- on LoginSet do |event|
- @login = event.data[:login]
- end
-
- on PasswordHashSet do |event|
- @password_hash = event.data[:password_hash]
- end
-
- on AccountConnectedToClient do |event|
- @client_id = event.data[:client_id]
- end
- end
-end
diff --git a/ecommerce/authentication/lib/authentication/account_service.rb b/ecommerce/authentication/lib/authentication/account_service.rb
deleted file mode 100644
index 1cfabdfd1..000000000
--- a/ecommerce/authentication/lib/authentication/account_service.rb
+++ /dev/null
@@ -1,49 +0,0 @@
-module Authentication
- class RegisterAccountHandler
- def initialize(event_store)
- @repository = Infra::AggregateRootRepository.new(event_store)
- end
-
- def call(command)
- @repository.with_aggregate(Account, command.aggregate_id) do |account|
- account.register
- end
- end
- end
-
- class SetLoginHandler
- def initialize(event_store)
- @repository = Infra::AggregateRootRepository.new(event_store)
- end
-
- def call(command)
- @repository.with_aggregate(Account, command.aggregate_id) do |account|
- account.set_login(command.login)
- end
- end
- end
-
- class SetPasswordHashHandler
- def initialize(event_store)
- @repository = Infra::AggregateRootRepository.new(event_store)
- end
-
- def call(command)
- @repository.with_aggregate(Account, command.aggregate_id) do |account|
- account.set_password_hash(command.password_hash)
- end
- end
- end
-
- class ConnectAccountToClientHandler
- def initialize(event_store)
- @repository = Infra::AggregateRootRepository.new(event_store)
- end
-
- def call(command)
- @repository.with_aggregate(Account, command.aggregate_id) do |account|
- account.connect_client(command.client_id)
- end
- end
- end
-end
diff --git a/ecommerce/authentication/lib/authentication/commands/connect_account_to_client.rb b/ecommerce/authentication/lib/authentication/commands/connect_account_to_client.rb
deleted file mode 100644
index 3cea61001..000000000
--- a/ecommerce/authentication/lib/authentication/commands/connect_account_to_client.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-module Authentication
- class ConnectAccountToClient < Infra::Command
- attribute :account_id, Infra::Types::UUID
- attribute :client_id, Infra::Types::UUID
- alias aggregate_id account_id
- end
-end
diff --git a/ecommerce/authentication/lib/authentication/commands/register_account.rb b/ecommerce/authentication/lib/authentication/commands/register_account.rb
deleted file mode 100644
index c69492ca4..000000000
--- a/ecommerce/authentication/lib/authentication/commands/register_account.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-module Authentication
- class RegisterAccount < Infra::Command
- attribute :account_id, Infra::Types::UUID
- alias aggregate_id account_id
- end
-end
diff --git a/ecommerce/authentication/lib/authentication/commands/set_login.rb b/ecommerce/authentication/lib/authentication/commands/set_login.rb
deleted file mode 100644
index 1df2f94e2..000000000
--- a/ecommerce/authentication/lib/authentication/commands/set_login.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-module Authentication
- class SetLogin < Infra::Command
- attribute :account_id, Infra::Types::UUID
- attribute :login, Infra::Types::String
- alias aggregate_id account_id
- end
-end
diff --git a/ecommerce/authentication/lib/authentication/commands/set_password_hash.rb b/ecommerce/authentication/lib/authentication/commands/set_password_hash.rb
deleted file mode 100644
index 15670dbdb..000000000
--- a/ecommerce/authentication/lib/authentication/commands/set_password_hash.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-module Authentication
- class SetPasswordHash < Infra::Command
- attribute :account_id, Infra::Types::UUID
- attribute :password_hash, Infra::Types::String
- alias aggregate_id account_id
- end
-end
diff --git a/ecommerce/authentication/lib/authentication/events/account_connected_to_client.rb b/ecommerce/authentication/lib/authentication/events/account_connected_to_client.rb
deleted file mode 100644
index 2efbffa07..000000000
--- a/ecommerce/authentication/lib/authentication/events/account_connected_to_client.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-module Authentication
- class AccountConnectedToClient < Infra::Event
- attribute :account_id, Infra::Types::UUID
- attribute :client_id, Infra::Types::UUID
- end
-end
diff --git a/ecommerce/authentication/lib/authentication/events/account_registered.rb b/ecommerce/authentication/lib/authentication/events/account_registered.rb
deleted file mode 100644
index f9f6f203f..000000000
--- a/ecommerce/authentication/lib/authentication/events/account_registered.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-module Authentication
- class AccountRegistered < Infra::Event
- attribute :account_id, Infra::Types::UUID
- end
-end
diff --git a/ecommerce/authentication/lib/authentication/events/login_set.rb b/ecommerce/authentication/lib/authentication/events/login_set.rb
deleted file mode 100644
index 8dfc4187f..000000000
--- a/ecommerce/authentication/lib/authentication/events/login_set.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-module Authentication
- class LoginSet < Infra::Event
- attribute :account_id, Infra::Types::UUID
- attribute :login, Infra::Types::String
- end
-end
diff --git a/ecommerce/authentication/lib/authentication/events/password_hash_set.rb b/ecommerce/authentication/lib/authentication/events/password_hash_set.rb
deleted file mode 100644
index 366133de1..000000000
--- a/ecommerce/authentication/lib/authentication/events/password_hash_set.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-module Authentication
- class PasswordHashSet < Infra::Event
- attribute :account_id, Infra::Types::UUID
- attribute :password_hash, Infra::Types::String
- end
-end
diff --git a/ecommerce/authentication/test/connect_account_to_client_test.rb b/ecommerce/authentication/test/connect_account_to_client_test.rb
deleted file mode 100644
index 0cbd7aeec..000000000
--- a/ecommerce/authentication/test/connect_account_to_client_test.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-require_relative "test_helper"
-
-module Authentication
- class ConnectAccountToClientTest < Test
- cover "Authentication*"
-
- def test_client_id_should_get_set
- account_id = SecureRandom.uuid
- client_id = SecureRandom.uuid
-
- act(RegisterAccount.new(account_id: account_id))
-
- account_connected_to_client = AccountConnectedToClient.new(data: { account_id: account_id, client_id: client_id })
-
- assert_events("Authentication::Account$#{account_id}", account_connected_to_client) do
- run_command(ConnectAccountToClient.new(account_id: account_id, client_id: client_id))
- end
- end
- end
-end
diff --git a/ecommerce/authentication/test/register_account_test.rb b/ecommerce/authentication/test/register_account_test.rb
deleted file mode 100644
index 612985fee..000000000
--- a/ecommerce/authentication/test/register_account_test.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-require_relative "test_helper"
-
-module Authentication
- class RegisterAccountTest < Test
- cover "Authentication*"
-
- def setup
- @uid = SecureRandom.uuid
- @data = { account_id: @uid }
- end
-
- def test_account_should_get_registered
- account_registered = AccountRegistered.new(data: @data)
- assert_events("Authentication::Account$#{@uid}", account_registered) do
- register_account(@uid)
- end
- end
-
- def test_should_not_allow_for_double_registration
- assert_raises(Account::AlreadyRegistered) do
- register_account(@uid)
- register_account(@uid)
- end
- end
-
- private
-
- def register_account(account_id)
- run_command(RegisterAccount.new(account_id: account_id))
- end
- end
-end
diff --git a/ecommerce/authentication/test/set_login_test.rb b/ecommerce/authentication/test/set_login_test.rb
deleted file mode 100644
index b47c32d53..000000000
--- a/ecommerce/authentication/test/set_login_test.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-require_relative "test_helper"
-
-module Authentication
- class SetLoginTest < Test
- cover "Authentication*"
-
- def test_login_should_get_set
- account_id = SecureRandom.uuid
-
- act(RegisterAccount.new(account_id: account_id))
-
- login_set = LoginSet.new(data: { account_id: account_id, login: fake_login })
-
- assert_events("Authentication::Account$#{account_id}", login_set) do
- run_command(SetLogin.new(account_id: account_id, login: fake_login))
- end
- end
- end
-end
diff --git a/ecommerce/authentication/test/set_password_hash_test.rb b/ecommerce/authentication/test/set_password_hash_test.rb
deleted file mode 100644
index 88e8e414e..000000000
--- a/ecommerce/authentication/test/set_password_hash_test.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-require_relative "test_helper"
-
-module Authentication
- class SetPasswordHashTest < Test
- cover "Authentication*"
-
- def test_password_hash_should_get_set
- account_id = SecureRandom.uuid
- password_hash = SecureRandom.hex(10)
-
- act(RegisterAccount.new(account_id: account_id))
-
- password_hash_set = PasswordHashSet.new(data: { account_id: account_id, password_hash: password_hash })
-
- assert_events("Authentication::Account$#{account_id}", password_hash_set) do
- run_command(SetPasswordHash.new(account_id: account_id, password_hash: password_hash))
- end
- end
- end
-end
diff --git a/ecommerce/authentication/test/test_helper.rb b/ecommerce/authentication/test/test_helper.rb
deleted file mode 100644
index bc727dbb5..000000000
--- a/ecommerce/authentication/test/test_helper.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-require "minitest/autorun"
-require "mutant/minitest/coverage"
-
-require_relative "../lib/authentication"
-
-module Authentication
- class Test < Infra::InMemoryTest
- def before_setup
- super()
- Configuration.new.call(event_store, command_bus)
- end
-
- private
-
- def fake_login
- "fake_login"
- end
- end
-end
diff --git a/ecommerce/configuration.rb b/ecommerce/configuration.rb
deleted file mode 100644
index 2af4d70bf..000000000
--- a/ecommerce/configuration.rb
+++ /dev/null
@@ -1,50 +0,0 @@
-require_relative "authentication/lib/authentication"
-require_relative "ordering/lib/ordering"
-require_relative "pricing/lib/pricing"
-require_relative "product_catalog/lib/product_catalog"
-require_relative "crm/lib/crm"
-require_relative "payments/lib/payments"
-require_relative "inventory/lib/inventory"
-require_relative "shipping/lib/shipping"
-require_relative "invoicing/lib/invoicing"
-require_relative "taxes/lib/taxes"
-require_relative "fulfillment/lib/fulfillment"
-require_relative "processes/lib/processes"
-
-module Ecommerce
- class Configuration
- def initialize(number_generator: nil, payment_gateway: nil, available_vat_rates: [])
- @number_generator = number_generator
- @payment_gateway = payment_gateway
- @available_vat_rates = available_vat_rates
- end
-
- def call(event_store, command_bus)
- configure_bounded_contexts(event_store, command_bus)
- configure_processes(event_store, command_bus)
- end
-
- def configure_bounded_contexts(event_store, command_bus)
- raise ArgumentError.new(
- "Neither number_generator nor payment_gateway can be null"
- ) if @number_generator.nil? || @payment_gateway.nil?
- [
- Authentication::Configuration.new,
- Ordering::Configuration.new(@number_generator),
- Crm::Configuration.new,
- Inventory::Configuration.new,
- Invoicing::Configuration.new,
- Payments::Configuration.new(@payment_gateway),
- Shipping::Configuration.new,
- Pricing::Configuration.new,
- Taxes::Configuration.new(@available_vat_rates),
- ProductCatalog::Configuration.new,
- Fulfillment::Configuration.new
- ].each { |c| c.call(event_store, command_bus) }
- end
-
- def configure_processes(event_store, command_bus)
- Processes::Configuration.new.call(event_store, command_bus)
- end
- end
-end
diff --git a/ecommerce/crm/.mutant.yml b/ecommerce/crm/.mutant.yml
deleted file mode 100644
index 952b042b0..000000000
--- a/ecommerce/crm/.mutant.yml
+++ /dev/null
@@ -1,11 +0,0 @@
-requires:
- - ./test/test_helper
-integration: minitest
-coverage_criteria:
- process_abort: true
-matcher:
- subjects:
- - Crm*
- ignore:
- - Crm::Configuration#call
-
diff --git a/ecommerce/crm/Gemfile b/ecommerce/crm/Gemfile
deleted file mode 100644
index 8fd887144..000000000
--- a/ecommerce/crm/Gemfile
+++ /dev/null
@@ -1,4 +0,0 @@
-source "https://rubygems.org"
-
-eval_gemfile "../../infra/Gemfile.test"
-gem "infra", path: "../../infra"
\ No newline at end of file
diff --git a/ecommerce/crm/Gemfile.lock b/ecommerce/crm/Gemfile.lock
deleted file mode 100644
index 4a73abff2..000000000
--- a/ecommerce/crm/Gemfile.lock
+++ /dev/null
@@ -1,119 +0,0 @@
-PATH
- remote: ../../infra
- specs:
- infra (1.0.0)
- aggregate_root (~> 2.13)
- arkency-command_bus
- dry-struct
- dry-types
- rake
- ruby_event_store (~> 2.13)
- ruby_event_store-transformations
- sidekiq
-
-GEM
- remote: https://oss:7AXfeZdAfCqL1PvHm2nvDJO6Zd9UW8IK@gem.mutant.dev/
- specs:
- mutant-license (0.1.1.2.1627430819213747598431630701693729869473.6)
-
-GEM
- remote: https://rubygems.org/
- specs:
- activesupport (7.1.2)
- base64
- bigdecimal
- concurrent-ruby (~> 1.0, >= 1.0.2)
- connection_pool (>= 2.2.5)
- drb
- i18n (>= 1.6, < 2)
- minitest (>= 5.1)
- mutex_m
- tzinfo (~> 2.0)
- aggregate_root (2.13.0)
- ruby_event_store (= 2.13.0)
- arkency-command_bus (0.4.1)
- concurrent-ruby
- ast (2.4.2)
- base64 (0.2.0)
- bigdecimal (3.1.4)
- concurrent-ruby (1.2.2)
- connection_pool (2.4.1)
- diff-lcs (1.5.0)
- drb (2.2.0)
- ruby2_keywords
- dry-core (1.0.1)
- concurrent-ruby (~> 1.0)
- zeitwerk (~> 2.6)
- dry-inflector (1.0.0)
- dry-logic (1.5.0)
- concurrent-ruby (~> 1.0)
- dry-core (~> 1.0, < 2)
- zeitwerk (~> 2.6)
- dry-struct (1.6.0)
- dry-core (~> 1.0, < 2)
- dry-types (>= 1.7, < 2)
- ice_nine (~> 0.11)
- zeitwerk (~> 2.6)
- dry-types (1.7.1)
- concurrent-ruby (~> 1.0)
- dry-core (~> 1.0)
- dry-inflector (~> 1.0)
- dry-logic (~> 1.4)
- zeitwerk (~> 2.6)
- i18n (1.14.1)
- concurrent-ruby (~> 1.0)
- ice_nine (0.11.2)
- minitest (5.15.0)
- mutant (0.11.26)
- diff-lcs (~> 1.3)
- parser (~> 3.2.2, >= 3.2.2.4)
- regexp_parser (~> 2.8.2)
- sorbet-runtime (~> 0.5.0)
- unparser (~> 0.6.9)
- mutant-minitest (0.11.26)
- minitest (~> 5.11)
- mutant (= 0.11.26)
- mutex_m (0.2.0)
- parser (3.2.2.4)
- ast (~> 2.4.1)
- racc
- racc (1.7.3)
- rack (3.0.8)
- rake (13.1.0)
- redis-client (0.19.0)
- connection_pool
- regexp_parser (2.8.3)
- ruby2_keywords (0.0.5)
- ruby_event_store (2.13.0)
- concurrent-ruby (~> 1.0, >= 1.1.6)
- ruby_event_store-transformations (0.1.0)
- activesupport (>= 5.0)
- ruby_event_store (>= 2.0.0, < 3.0.0)
- sidekiq (7.2.0)
- concurrent-ruby (< 2)
- connection_pool (>= 2.3.0)
- rack (>= 2.2.4)
- redis-client (>= 0.14.0)
- sorbet-runtime (0.5.11190)
- tzinfo (2.0.6)
- concurrent-ruby (~> 1.0)
- unparser (0.6.12)
- diff-lcs (~> 1.3)
- parser (>= 3.2.2.4)
- zeitwerk (2.6.12)
-
-PLATFORMS
- arm64-darwin-20
- arm64-darwin-21
- ruby
- x86_64-darwin-20
- x86_64-linux
-
-DEPENDENCIES
- infra!
- minitest (= 5.15.0)!
- mutant-license!
- mutant-minitest (= 0.11.26)!
-
-BUNDLED WITH
- 2.5.9
diff --git a/ecommerce/crm/Makefile b/ecommerce/crm/Makefile
deleted file mode 100644
index 23850fab1..000000000
--- a/ecommerce/crm/Makefile
+++ /dev/null
@@ -1,10 +0,0 @@
-install:
- @bundle install
-
-test:
- @bundle exec ruby -e "require \"rake/rake_test_loader\"" test/*_test.rb
-
-mutate:
- @RAILS_ENV=test bundle exec mutant run
-
-.PHONY: install test mutate
diff --git a/ecommerce/crm/README.md b/ecommerce/crm/README.md
deleted file mode 100644
index 0fe6e4a09..000000000
--- a/ecommerce/crm/README.md
+++ /dev/null
@@ -1,9 +0,0 @@
-# CRM
-
-[](https://github.com/RailsEventStore/cqrs-es-sample-with-res/actions/workflows/crm.yml)
-
-#### Up and running
-
-```
-make install test mutate
-```
diff --git a/ecommerce/crm/lib/crm.rb b/ecommerce/crm/lib/crm.rb
deleted file mode 100644
index 1876d7a00..000000000
--- a/ecommerce/crm/lib/crm.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-require "infra"
-require_relative "crm/commands/promote_customer_to_vip"
-require_relative "crm/commands/register_customer"
-require_relative "crm/commands/assign_customer_to_order"
-require_relative "crm/events/customer_promoted_to_vip"
-require_relative "crm/events/customer_registered"
-require_relative "crm/events/customer_assigned_to_order.rb"
-require_relative "crm/customer_service"
-require_relative "crm/customer"
-require_relative "crm/order_service"
-require_relative "crm/order"
-
-module Crm
- class Configuration
-
- def call(event_store, command_bus)
- command_bus.register(RegisterCustomer, OnRegistration.new(event_store))
- command_bus.register(PromoteCustomerToVip, OnPromoteCustomerToVip.new(event_store))
- command_bus.register(AssignCustomerToOrder, OnSetCustomer.new(event_store))
- end
- end
-end
diff --git a/ecommerce/crm/lib/crm/commands/assign_customer_to_order.rb b/ecommerce/crm/lib/crm/commands/assign_customer_to_order.rb
deleted file mode 100644
index 727b9386e..000000000
--- a/ecommerce/crm/lib/crm/commands/assign_customer_to_order.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-module Crm
- class AssignCustomerToOrder < Infra::Command
- attribute :customer_id, Infra::Types::UUID
- attribute :order_id, Infra::Types::UUID
- alias aggregate_id order_id
- end
-end
diff --git a/ecommerce/crm/lib/crm/commands/promote_customer_to_vip.rb b/ecommerce/crm/lib/crm/commands/promote_customer_to_vip.rb
deleted file mode 100644
index a2bcae947..000000000
--- a/ecommerce/crm/lib/crm/commands/promote_customer_to_vip.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-module Crm
- class PromoteCustomerToVip < Infra::Command
- attribute :customer_id, Infra::Types::UUID
- alias aggregate_id customer_id
- end
-end
diff --git a/ecommerce/crm/lib/crm/commands/register_customer.rb b/ecommerce/crm/lib/crm/commands/register_customer.rb
deleted file mode 100644
index c5693bd43..000000000
--- a/ecommerce/crm/lib/crm/commands/register_customer.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-module Crm
- class RegisterCustomer < Infra::Command
- attribute :customer_id, Infra::Types::UUID
- attribute :name, Infra::Types::String
- alias aggregate_id customer_id
- end
-end
diff --git a/ecommerce/crm/lib/crm/customer.rb b/ecommerce/crm/lib/crm/customer.rb
deleted file mode 100644
index 31c697d6b..000000000
--- a/ecommerce/crm/lib/crm/customer.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-module Crm
- class Customer
- include AggregateRoot
-
- AlreadyVip = Class.new(StandardError)
- AlreadyRegistered = Class.new(StandardError)
- NotExists = Class.new(StandardError)
-
- def initialize(id)
- @id = id
- end
-
- def register(name)
- raise AlreadyRegistered if @registered
- apply CustomerRegistered.new(
- data: {
- customer_id: @id,
- name: name
- }
- )
- end
-
- def promote_to_vip
- raise AlreadyVip if @vip
- apply CustomerPromotedToVip.new(
- data: {
- customer_id: @id
- }
- )
- end
-
- on CustomerRegistered do |event|
- @registered = true
- end
-
- on CustomerPromotedToVip do |event|
- @vip = true
- end
- end
-end
diff --git a/ecommerce/crm/lib/crm/customer_service.rb b/ecommerce/crm/lib/crm/customer_service.rb
deleted file mode 100644
index a89cbd750..000000000
--- a/ecommerce/crm/lib/crm/customer_service.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-module Crm
-
- class OnRegistration
- def initialize(event_store)
- @repository = Infra::AggregateRootRepository.new(event_store)
- end
-
- def call(command)
- @repository.with_aggregate(Customer, command.aggregate_id) do |customer|
- customer.register(command.name)
- end
- end
- end
-
- class OnPromoteCustomerToVip
- def initialize(event_store)
- @repository = Infra::AggregateRootRepository.new(event_store)
- end
-
- def call(command)
- @repository.with_aggregate(Customer, command.aggregate_id) do |customer|
- customer.promote_to_vip
- end
- end
- end
-
-end
\ No newline at end of file
diff --git a/ecommerce/crm/lib/crm/events/customer_assigned_to_order.rb b/ecommerce/crm/lib/crm/events/customer_assigned_to_order.rb
deleted file mode 100644
index dcbc9eda2..000000000
--- a/ecommerce/crm/lib/crm/events/customer_assigned_to_order.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-module Crm
- class CustomerAssignedToOrder < Infra::Event
- attribute :customer_id, Infra::Types::UUID
- attribute :order_id, Infra::Types::UUID
- end
-end
diff --git a/ecommerce/crm/lib/crm/events/customer_promoted_to_vip.rb b/ecommerce/crm/lib/crm/events/customer_promoted_to_vip.rb
deleted file mode 100644
index b73906d2a..000000000
--- a/ecommerce/crm/lib/crm/events/customer_promoted_to_vip.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-module Crm
- class CustomerPromotedToVip < Infra::Event
- attribute :customer_id, Infra::Types::UUID
- end
-end
diff --git a/ecommerce/crm/lib/crm/events/customer_registered.rb b/ecommerce/crm/lib/crm/events/customer_registered.rb
deleted file mode 100644
index 8e34598d1..000000000
--- a/ecommerce/crm/lib/crm/events/customer_registered.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-module Crm
- class CustomerRegistered < Infra::Event
- attribute :customer_id, Infra::Types::UUID
- attribute :name, Infra::Types::String
- end
-end
-
-
diff --git a/ecommerce/crm/lib/crm/order.rb b/ecommerce/crm/lib/crm/order.rb
deleted file mode 100644
index be6bc955d..000000000
--- a/ecommerce/crm/lib/crm/order.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-module Crm
- class Order
- include AggregateRoot
- CustomerAlreadyAssigned = Class.new(StandardError)
-
- def initialize(id)
- @id = id
- end
-
- def set_customer(customer_id)
- raise CustomerAlreadyAssigned if @customer_id
- apply CustomerAssignedToOrder.new(data: { order_id: @id, customer_id: customer_id })
- end
-
- private
-
- on CustomerAssignedToOrder do |event|
- @customer_id = event.data[:customer_id]
- end
- end
-end
\ No newline at end of file
diff --git a/ecommerce/crm/lib/crm/order_service.rb b/ecommerce/crm/lib/crm/order_service.rb
deleted file mode 100644
index edf7d62df..000000000
--- a/ecommerce/crm/lib/crm/order_service.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-module Crm
- class OnSetCustomer
- def initialize(event_store)
- @repository = Infra::AggregateRootRepository.new(event_store)
- @event_store = event_store
- end
-
- def call(command)
- raise Customer::NotExists unless customer_exists?(command.customer_id)
- @repository.with_aggregate(Order, command.aggregate_id) do |order|
- order.set_customer(command.customer_id)
- end
- end
-
- private
-
- def customer_exists?(customer_id)
- customer_stream = @repository.stream_name(Customer, customer_id)
- !@event_store.read.stream(customer_stream).count.eql?(0)
- end
- end
-end
\ No newline at end of file
diff --git a/ecommerce/crm/test/assign_customer_to_order_test.rb b/ecommerce/crm/test/assign_customer_to_order_test.rb
deleted file mode 100644
index cbddf33ed..000000000
--- a/ecommerce/crm/test/assign_customer_to_order_test.rb
+++ /dev/null
@@ -1,43 +0,0 @@
-require_relative "test_helper"
-
-module Crm
- class AssignCustomerToOrderTest < Test
- cover "Crm*"
-
- def test_customer_should_get_assigned
- customer_id = SecureRandom.uuid
- order_id = SecureRandom.uuid
- register_customer(customer_id, fake_name)
- expected_event = CustomerAssignedToOrder.new(data: {customer_id: customer_id, order_id: order_id})
- assert_events("Crm::Order$#{order_id}", expected_event) do
- assign_customer_to_order(order_id, customer_id)
- end
- end
-
- def test_customer_should_not_get_assigned_twice
- customer_id = SecureRandom.uuid
- order_id = SecureRandom.uuid
- register_customer(customer_id, fake_name)
- assign_customer_to_order(order_id, customer_id)
- assert_raises(Order::CustomerAlreadyAssigned) do
- assign_customer_to_order(order_id, customer_id)
- end
- end
-
- def test_customer_should_not_get_assigned_if_does_not_exist
- customer_id = SecureRandom.uuid
- another_customer_id = SecureRandom.uuid
- order_id = SecureRandom.uuid
- register_customer(another_customer_id, fake_name)
- assert_raises(Customer::NotExists) do
- assign_customer_to_order(order_id, customer_id)
- end
- end
-
- private
-
- def assign_customer_to_order(order_id, customer_id)
- run_command(AssignCustomerToOrder.new(order_id: order_id, customer_id: customer_id))
- end
- end
-end
\ No newline at end of file
diff --git a/ecommerce/crm/test/promote_client_to_vip_test.rb b/ecommerce/crm/test/promote_client_to_vip_test.rb
deleted file mode 100644
index d82209d0b..000000000
--- a/ecommerce/crm/test/promote_client_to_vip_test.rb
+++ /dev/null
@@ -1,45 +0,0 @@
-require_relative "test_helper"
-
-module Crm
- class PromoteCustomerToVipTest < Test
- cover "Crm*"
-
- def test_should_not_allow_for_marking_vip_as_vip_again
- customer_id = SecureRandom.uuid
-
- arrange(
- RegisterCustomer.new(
- customer_id: customer_id,
- name: fake_name
- )
- )
-
- assert_raises(Customer::AlreadyVip) do
- promote_to_vip(customer_id)
- promote_to_vip(customer_id)
- end
- end
-
- def test_should_publish_event
- customer_id = SecureRandom.uuid
-
- arrange(
- RegisterCustomer.new(
- customer_id: customer_id,
- name: fake_name
- )
- )
-
- customer_promoted_to_vip = CustomerPromotedToVip.new(data: {customer_id: customer_id})
- assert_events("Crm::Customer$#{customer_id}", customer_promoted_to_vip) do
- promote_to_vip(customer_id)
- end
- end
-
- private
-
- def promote_to_vip(uid)
- run_command(PromoteCustomerToVip.new(customer_id: uid))
- end
- end
-end
diff --git a/ecommerce/crm/test/registration_test.rb b/ecommerce/crm/test/registration_test.rb
deleted file mode 100644
index db312ab1d..000000000
--- a/ecommerce/crm/test/registration_test.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-require_relative "test_helper"
-
-module Crm
- class RegistrationTest < Test
- cover "Crm*"
-
- def test_customer_should_get_registered
- uid = SecureRandom.uuid
- register_customer(uid, fake_name)
- end
-
- def test_should_not_allow_for_double_registration
- uid = SecureRandom.uuid
- assert_raises(Customer::AlreadyRegistered) do
- register_customer(uid, fake_name)
- register_customer(uid, fake_name)
- end
- end
-
- def test_should_publish_event
- uid = SecureRandom.uuid
- customer_registered = CustomerRegistered.new(data: {customer_id: uid, name: fake_name})
- assert_events("Crm::Customer$#{uid}", customer_registered) do
- register_customer(uid, fake_name)
- end
- end
- end
-end
diff --git a/ecommerce/crm/test/test_helper.rb b/ecommerce/crm/test/test_helper.rb
deleted file mode 100644
index feec8f051..000000000
--- a/ecommerce/crm/test/test_helper.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-require "minitest/autorun"
-require "mutant/minitest/coverage"
-
-require_relative "../lib/crm"
-
-module Crm
- class Test < Infra::InMemoryTest
- def before_setup
- super()
- Configuration.new.call(event_store, command_bus)
- end
-
- private
-
- def register_customer(uid, name)
- run_command(RegisterCustomer.new(customer_id: uid, name: name))
- end
-
- def fake_name
- "Fake name"
- end
- end
-end
diff --git a/ecommerce/fulfillment/.mutant.yml b/ecommerce/fulfillment/.mutant.yml
deleted file mode 100644
index 6d1df49be..000000000
--- a/ecommerce/fulfillment/.mutant.yml
+++ /dev/null
@@ -1,12 +0,0 @@
-requires:
- - ./test/test_helper
-integration: minitest
-coverage_criteria:
- process_abort: true
-matcher:
- subjects:
- - Fulfillment*
- ignore:
- - Fulfillment::Test*
- - Fulfillment::Configuration#call
- - Fulfillment::Configuration#initialize
\ No newline at end of file
diff --git a/ecommerce/fulfillment/Gemfile b/ecommerce/fulfillment/Gemfile
deleted file mode 100644
index 8fd887144..000000000
--- a/ecommerce/fulfillment/Gemfile
+++ /dev/null
@@ -1,4 +0,0 @@
-source "https://rubygems.org"
-
-eval_gemfile "../../infra/Gemfile.test"
-gem "infra", path: "../../infra"
\ No newline at end of file
diff --git a/ecommerce/fulfillment/Gemfile.lock b/ecommerce/fulfillment/Gemfile.lock
deleted file mode 100644
index 87553b254..000000000
--- a/ecommerce/fulfillment/Gemfile.lock
+++ /dev/null
@@ -1,116 +0,0 @@
-PATH
- remote: ../../infra
- specs:
- infra (1.0.0)
- aggregate_root (~> 2.13)
- arkency-command_bus
- dry-struct
- dry-types
- rake
- ruby_event_store (~> 2.13)
- ruby_event_store-transformations
- sidekiq
-
-GEM
- remote: https://oss:7AXfeZdAfCqL1PvHm2nvDJO6Zd9UW8IK@gem.mutant.dev/
- specs:
- mutant-license (0.1.1.2.1627430819213747598431630701693729869473.6)
-
-GEM
- remote: https://rubygems.org/
- specs:
- activesupport (7.1.3.2)
- base64
- bigdecimal
- concurrent-ruby (~> 1.0, >= 1.0.2)
- connection_pool (>= 2.2.5)
- drb
- i18n (>= 1.6, < 2)
- minitest (>= 5.1)
- mutex_m
- tzinfo (~> 2.0)
- aggregate_root (2.14.0)
- base64
- ruby_event_store (= 2.14.0)
- arkency-command_bus (0.4.1)
- concurrent-ruby
- ast (2.4.2)
- base64 (0.2.0)
- bigdecimal (3.1.7)
- concurrent-ruby (1.2.3)
- connection_pool (2.4.1)
- diff-lcs (1.5.1)
- drb (2.2.1)
- dry-core (1.0.1)
- concurrent-ruby (~> 1.0)
- zeitwerk (~> 2.6)
- dry-inflector (1.0.0)
- dry-logic (1.5.0)
- concurrent-ruby (~> 1.0)
- dry-core (~> 1.0, < 2)
- zeitwerk (~> 2.6)
- dry-struct (1.6.0)
- dry-core (~> 1.0, < 2)
- dry-types (>= 1.7, < 2)
- ice_nine (~> 0.11)
- zeitwerk (~> 2.6)
- dry-types (1.7.2)
- bigdecimal (~> 3.0)
- concurrent-ruby (~> 1.0)
- dry-core (~> 1.0)
- dry-inflector (~> 1.0)
- dry-logic (~> 1.4)
- zeitwerk (~> 2.6)
- i18n (1.14.4)
- concurrent-ruby (~> 1.0)
- ice_nine (0.11.2)
- minitest (5.15.0)
- mutant (0.11.26)
- diff-lcs (~> 1.3)
- parser (~> 3.2.2, >= 3.2.2.4)
- regexp_parser (~> 2.8.2)
- sorbet-runtime (~> 0.5.0)
- unparser (~> 0.6.9)
- mutant-minitest (0.11.26)
- minitest (~> 5.11)
- mutant (= 0.11.26)
- mutex_m (0.2.0)
- parser (3.2.2.4)
- ast (~> 2.4.1)
- racc
- racc (1.7.3)
- rack (3.0.10)
- rake (13.2.1)
- redis-client (0.22.1)
- connection_pool
- regexp_parser (2.8.3)
- ruby_event_store (2.14.0)
- concurrent-ruby (~> 1.0, >= 1.1.6)
- ruby_event_store-transformations (0.1.0)
- activesupport (>= 5.0)
- ruby_event_store (>= 2.0.0, < 3.0.0)
- sidekiq (7.2.4)
- concurrent-ruby (< 2)
- connection_pool (>= 2.3.0)
- rack (>= 2.2.4)
- redis-client (>= 0.19.0)
- sorbet-runtime (0.5.11368)
- tzinfo (2.0.6)
- concurrent-ruby (~> 1.0)
- unparser (0.6.12)
- diff-lcs (~> 1.3)
- parser (>= 3.2.2.4)
- zeitwerk (2.6.13)
-
-PLATFORMS
- arm64-darwin-23
- ruby
-
-DEPENDENCIES
- infra!
- minitest (= 5.15.0)!
- mutant-license!
- mutant-minitest (= 0.11.26)!
-
-BUNDLED WITH
- 2.5.9
diff --git a/ecommerce/fulfillment/Makefile b/ecommerce/fulfillment/Makefile
deleted file mode 100644
index 23850fab1..000000000
--- a/ecommerce/fulfillment/Makefile
+++ /dev/null
@@ -1,10 +0,0 @@
-install:
- @bundle install
-
-test:
- @bundle exec ruby -e "require \"rake/rake_test_loader\"" test/*_test.rb
-
-mutate:
- @RAILS_ENV=test bundle exec mutant run
-
-.PHONY: install test mutate
diff --git a/ecommerce/fulfillment/lib/fulfillment.rb b/ecommerce/fulfillment/lib/fulfillment.rb
deleted file mode 100644
index 66ea3b558..000000000
--- a/ecommerce/fulfillment/lib/fulfillment.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-require "infra"
-require_relative "fulfillment/events/order_registered"
-require_relative "fulfillment/events/order_confirmed"
-require_relative "fulfillment/events/order_cancelled"
-require_relative "fulfillment/commands/register_order"
-require_relative "fulfillment/commands/confirm_order"
-require_relative "fulfillment/commands/cancel_order"
-require_relative "fulfillment/on_register_order"
-require_relative "fulfillment/on_cancel_order"
-require_relative "fulfillment/on_confirm_order"
-require_relative "fulfillment/order"
-
-module Fulfillment
- class Configuration
- def call(event_store, command_bus)
- command_bus.register(RegisterOrder, OnRegisterOrder.new(event_store))
- command_bus.register(ConfirmOrder, OnConfirmOrder.new(event_store))
- command_bus.register(CancelOrder, OnCancelOrder.new(event_store))
- end
- end
-end
diff --git a/ecommerce/fulfillment/lib/fulfillment/commands/cancel_order.rb b/ecommerce/fulfillment/lib/fulfillment/commands/cancel_order.rb
deleted file mode 100644
index 0d1dd8b70..000000000
--- a/ecommerce/fulfillment/lib/fulfillment/commands/cancel_order.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-# frozen_string_literal: true
-
-module Fulfillment
- class CancelOrder < Infra::Command
- attribute :order_id, Infra::Types::UUID
-
- alias aggregate_id order_id
- end
-end
diff --git a/ecommerce/fulfillment/lib/fulfillment/commands/confirm_order.rb b/ecommerce/fulfillment/lib/fulfillment/commands/confirm_order.rb
deleted file mode 100644
index 729debae4..000000000
--- a/ecommerce/fulfillment/lib/fulfillment/commands/confirm_order.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-# frozen_string_literal: true
-
-module Fulfillment
- class ConfirmOrder < Infra::Command
- attribute :order_id, Infra::Types::UUID
-
- alias aggregate_id order_id
- end
-end
diff --git a/ecommerce/fulfillment/lib/fulfillment/commands/register_order.rb b/ecommerce/fulfillment/lib/fulfillment/commands/register_order.rb
deleted file mode 100644
index 18bb18370..000000000
--- a/ecommerce/fulfillment/lib/fulfillment/commands/register_order.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-# frozen_string_literal: true
-
-module Fulfillment
- class RegisterOrder < Infra::Command
- attribute :order_id, Infra::Types::UUID
-
- alias aggregate_id order_id
- end
-end
\ No newline at end of file
diff --git a/ecommerce/fulfillment/lib/fulfillment/events/order_cancelled.rb b/ecommerce/fulfillment/lib/fulfillment/events/order_cancelled.rb
deleted file mode 100644
index 9a937e3f9..000000000
--- a/ecommerce/fulfillment/lib/fulfillment/events/order_cancelled.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-# frozen_string_literal: true
-
-module Fulfillment
- class OrderCancelled < Infra::Event
- attribute :order_id, Infra::Types::UUID
- end
-end
diff --git a/ecommerce/fulfillment/lib/fulfillment/events/order_confirmed.rb b/ecommerce/fulfillment/lib/fulfillment/events/order_confirmed.rb
deleted file mode 100644
index 1342816a5..000000000
--- a/ecommerce/fulfillment/lib/fulfillment/events/order_confirmed.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-# frozen_string_literal: true
-
-module Fulfillment
- class OrderConfirmed < Infra::Event
- attribute :order_id, Infra::Types::UUID
- end
-end
diff --git a/ecommerce/fulfillment/lib/fulfillment/events/order_registered.rb b/ecommerce/fulfillment/lib/fulfillment/events/order_registered.rb
deleted file mode 100644
index 1fce5c007..000000000
--- a/ecommerce/fulfillment/lib/fulfillment/events/order_registered.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-# frozen_string_literal: true
-
-module Fulfillment
- class OrderRegistered < Infra::Event
- attribute :order_id, Infra::Types::UUID
- end
-end
diff --git a/ecommerce/fulfillment/lib/fulfillment/on_cancel_order.rb b/ecommerce/fulfillment/lib/fulfillment/on_cancel_order.rb
deleted file mode 100644
index 91490dcab..000000000
--- a/ecommerce/fulfillment/lib/fulfillment/on_cancel_order.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-# frozen_string_literal: true
-
-module Fulfillment
- class OnCancelOrder
- def initialize(event_store)
- @repository = Infra::AggregateRootRepository.new(event_store)
- end
-
- def call(command)
- @repository.with_aggregate(Order, command.aggregate_id) do |order|
- order.cancel
- end
- end
- end
-end
\ No newline at end of file
diff --git a/ecommerce/fulfillment/lib/fulfillment/on_confirm_order.rb b/ecommerce/fulfillment/lib/fulfillment/on_confirm_order.rb
deleted file mode 100644
index 87b82a377..000000000
--- a/ecommerce/fulfillment/lib/fulfillment/on_confirm_order.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-# frozen_string_literal: true
-
-module Fulfillment
- class OnConfirmOrder
- def initialize(event_store)
- @repository = Infra::AggregateRootRepository.new(event_store)
- end
-
- def call(command)
- @repository.with_aggregate(Order, command.aggregate_id) do |order|
- order.confirm
- end
- end
- end
-end
diff --git a/ecommerce/fulfillment/lib/fulfillment/on_register_order.rb b/ecommerce/fulfillment/lib/fulfillment/on_register_order.rb
deleted file mode 100644
index b9fa5acb7..000000000
--- a/ecommerce/fulfillment/lib/fulfillment/on_register_order.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-# frozen_string_literal: true
-
-module Fulfillment
- class OnRegisterOrder
- def initialize(event_store)
- @repository = Infra::AggregateRootRepository.new(event_store)
- end
-
- def call(command)
- @repository.with_aggregate(Order, command.aggregate_id) do |order|
- order.register
- end
- end
- end
-end
\ No newline at end of file
diff --git a/ecommerce/fulfillment/lib/fulfillment/order.rb b/ecommerce/fulfillment/lib/fulfillment/order.rb
deleted file mode 100644
index 688ab8f4c..000000000
--- a/ecommerce/fulfillment/lib/fulfillment/order.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-# frozen_string_literal: true
-
-module Fulfillment
- class Order
- include AggregateRoot
-
- InvalidState = Class.new(StandardError)
-
- def initialize(id)
- @id = id
- end
-
- def register
- raise InvalidState if @state
- apply OrderRegistered.new(data: { order_id: @id })
- end
-
- def confirm
- raise InvalidState unless @state.equal?(:new)
- apply OrderConfirmed.new(data: { order_id: @id })
- end
-
- def cancel
- raise InvalidState unless @state.equal?(:new)
- apply OrderCancelled.new(data: { order_id: @id })
- end
-
- on OrderRegistered do |event|
- @state = :new
- end
-
- on OrderConfirmed do |event|
- @state = :confirmed
- end
-
- on OrderCancelled do |event|
- @state = :cancelled
- end
- end
-end
diff --git a/ecommerce/fulfillment/test/cancel_order_test.rb b/ecommerce/fulfillment/test/cancel_order_test.rb
deleted file mode 100644
index 55369ac92..000000000
--- a/ecommerce/fulfillment/test/cancel_order_test.rb
+++ /dev/null
@@ -1,41 +0,0 @@
-require_relative "test_helper"
-
-module Fulfillment
- class CancelOrderTest < Test
- cover "Fulfillment::OnCancelOrder*"
-
- def test_not_registered_order_cant_be_cancelled
- aggregate_id = SecureRandom.uuid
-
- assert_raises(Order::InvalidState) do
- act(CancelOrder.new(order_id: aggregate_id))
- end
- end
-
- def test_registered_order_can_be_cancelled
- aggregate_id = SecureRandom.uuid
- stream = "Fulfillment::Order$#{aggregate_id}"
- arrange(
- RegisterOrder.new(order_id: aggregate_id)
- )
-
- assert_events(
- stream,
- OrderCancelled.new(data: { order_id: aggregate_id })
- ) { act(CancelOrder.new(order_id: aggregate_id)) }
- end
-
- def test_confirmed_order_can_not_be_cancelled
- aggregate_id = SecureRandom.uuid
-
- arrange(
- RegisterOrder.new(order_id: aggregate_id),
- ConfirmOrder.new(order_id: aggregate_id)
- )
-
- assert_raises(Order::InvalidState) do
- act(CancelOrder.new(order_id: aggregate_id))
- end
- end
- end
-end
diff --git a/ecommerce/fulfillment/test/confirm_order_test.rb b/ecommerce/fulfillment/test/confirm_order_test.rb
deleted file mode 100644
index ec67b49a6..000000000
--- a/ecommerce/fulfillment/test/confirm_order_test.rb
+++ /dev/null
@@ -1,41 +0,0 @@
-require_relative "test_helper"
-
-module Fulfillment
- class ConfirmOrderTest < Test
- cover "Fulfillment::OnConfirmOrder*"
-
- def test_not_registered_order_cant_be_confirmed
- aggregate_id = SecureRandom.uuid
-
- assert_raises(Order::InvalidState) do
- act(ConfirmOrder.new(order_id: aggregate_id))
- end
- end
-
- def test_registered_order_can_be_confirmed
- aggregate_id = SecureRandom.uuid
- stream = "Fulfillment::Order$#{aggregate_id}"
- arrange(
- RegisterOrder.new(order_id: aggregate_id)
- )
-
- assert_events(
- stream,
- OrderConfirmed.new(data: { order_id: aggregate_id })
- ) { act(ConfirmOrder.new(order_id: aggregate_id)) }
- end
-
- def test_confirmed_order_can_not_be_confirmed
- aggregate_id = SecureRandom.uuid
-
- arrange(
- RegisterOrder.new(order_id: aggregate_id),
- CancelOrder.new(order_id: aggregate_id)
- )
-
- assert_raises(Order::InvalidState) do
- act(ConfirmOrder.new(order_id: aggregate_id))
- end
- end
- end
-end
diff --git a/ecommerce/fulfillment/test/register_order_test.rb b/ecommerce/fulfillment/test/register_order_test.rb
deleted file mode 100644
index c82a1b3f5..000000000
--- a/ecommerce/fulfillment/test/register_order_test.rb
+++ /dev/null
@@ -1,29 +0,0 @@
-require_relative "test_helper"
-
-module Fulfillment
- class ConfirmOrderTest < Test
- cover "Fulfillment::OnRegisterOrder*"
-
- def test_new_order_can_be_registered
- aggregate_id = SecureRandom.uuid
- stream = "Fulfillment::Order$#{aggregate_id}"
-
- assert_events(
- stream,
- OrderRegistered.new(data: { order_id: aggregate_id })
- ) { act(RegisterOrder.new(order_id: aggregate_id)) }
- end
-
- def test_registered_order_can_not_be_registered_again
- aggregate_id = SecureRandom.uuid
-
- arrange(
- RegisterOrder.new(order_id: aggregate_id),
- )
-
- assert_raises(Order::InvalidState) do
- act(RegisterOrder.new(order_id: aggregate_id))
- end
- end
- end
-end
\ No newline at end of file
diff --git a/ecommerce/fulfillment/test/test_helper.rb b/ecommerce/fulfillment/test/test_helper.rb
deleted file mode 100644
index 374827131..000000000
--- a/ecommerce/fulfillment/test/test_helper.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-require "minitest/autorun"
-require "mutant/minitest/coverage"
-
-require_relative "../lib/fulfillment"
-
-module Fulfillment
- class Test < Infra::InMemoryTest
- def before_setup
- super
- Configuration.new.call(event_store, command_bus)
- end
- end
-end
diff --git a/ecommerce/inventory/.mutant.yml b/ecommerce/inventory/.mutant.yml
deleted file mode 100644
index 075692f1f..000000000
--- a/ecommerce/inventory/.mutant.yml
+++ /dev/null
@@ -1,12 +0,0 @@
-requires:
- - ./test/test_helper
-integration: minitest
-coverage_criteria:
- process_abort: true
-matcher:
- subjects:
- - Inventory*
- ignore:
- - Inventory::Test*
- - Inventory::Configuration#initialize
- - Inventory::Configuration#call
\ No newline at end of file
diff --git a/ecommerce/inventory/Gemfile b/ecommerce/inventory/Gemfile
deleted file mode 100644
index 8fd887144..000000000
--- a/ecommerce/inventory/Gemfile
+++ /dev/null
@@ -1,4 +0,0 @@
-source "https://rubygems.org"
-
-eval_gemfile "../../infra/Gemfile.test"
-gem "infra", path: "../../infra"
\ No newline at end of file
diff --git a/ecommerce/inventory/Gemfile.lock b/ecommerce/inventory/Gemfile.lock
deleted file mode 100644
index 4a73abff2..000000000
--- a/ecommerce/inventory/Gemfile.lock
+++ /dev/null
@@ -1,119 +0,0 @@
-PATH
- remote: ../../infra
- specs:
- infra (1.0.0)
- aggregate_root (~> 2.13)
- arkency-command_bus
- dry-struct
- dry-types
- rake
- ruby_event_store (~> 2.13)
- ruby_event_store-transformations
- sidekiq
-
-GEM
- remote: https://oss:7AXfeZdAfCqL1PvHm2nvDJO6Zd9UW8IK@gem.mutant.dev/
- specs:
- mutant-license (0.1.1.2.1627430819213747598431630701693729869473.6)
-
-GEM
- remote: https://rubygems.org/
- specs:
- activesupport (7.1.2)
- base64
- bigdecimal
- concurrent-ruby (~> 1.0, >= 1.0.2)
- connection_pool (>= 2.2.5)
- drb
- i18n (>= 1.6, < 2)
- minitest (>= 5.1)
- mutex_m
- tzinfo (~> 2.0)
- aggregate_root (2.13.0)
- ruby_event_store (= 2.13.0)
- arkency-command_bus (0.4.1)
- concurrent-ruby
- ast (2.4.2)
- base64 (0.2.0)
- bigdecimal (3.1.4)
- concurrent-ruby (1.2.2)
- connection_pool (2.4.1)
- diff-lcs (1.5.0)
- drb (2.2.0)
- ruby2_keywords
- dry-core (1.0.1)
- concurrent-ruby (~> 1.0)
- zeitwerk (~> 2.6)
- dry-inflector (1.0.0)
- dry-logic (1.5.0)
- concurrent-ruby (~> 1.0)
- dry-core (~> 1.0, < 2)
- zeitwerk (~> 2.6)
- dry-struct (1.6.0)
- dry-core (~> 1.0, < 2)
- dry-types (>= 1.7, < 2)
- ice_nine (~> 0.11)
- zeitwerk (~> 2.6)
- dry-types (1.7.1)
- concurrent-ruby (~> 1.0)
- dry-core (~> 1.0)
- dry-inflector (~> 1.0)
- dry-logic (~> 1.4)
- zeitwerk (~> 2.6)
- i18n (1.14.1)
- concurrent-ruby (~> 1.0)
- ice_nine (0.11.2)
- minitest (5.15.0)
- mutant (0.11.26)
- diff-lcs (~> 1.3)
- parser (~> 3.2.2, >= 3.2.2.4)
- regexp_parser (~> 2.8.2)
- sorbet-runtime (~> 0.5.0)
- unparser (~> 0.6.9)
- mutant-minitest (0.11.26)
- minitest (~> 5.11)
- mutant (= 0.11.26)
- mutex_m (0.2.0)
- parser (3.2.2.4)
- ast (~> 2.4.1)
- racc
- racc (1.7.3)
- rack (3.0.8)
- rake (13.1.0)
- redis-client (0.19.0)
- connection_pool
- regexp_parser (2.8.3)
- ruby2_keywords (0.0.5)
- ruby_event_store (2.13.0)
- concurrent-ruby (~> 1.0, >= 1.1.6)
- ruby_event_store-transformations (0.1.0)
- activesupport (>= 5.0)
- ruby_event_store (>= 2.0.0, < 3.0.0)
- sidekiq (7.2.0)
- concurrent-ruby (< 2)
- connection_pool (>= 2.3.0)
- rack (>= 2.2.4)
- redis-client (>= 0.14.0)
- sorbet-runtime (0.5.11190)
- tzinfo (2.0.6)
- concurrent-ruby (~> 1.0)
- unparser (0.6.12)
- diff-lcs (~> 1.3)
- parser (>= 3.2.2.4)
- zeitwerk (2.6.12)
-
-PLATFORMS
- arm64-darwin-20
- arm64-darwin-21
- ruby
- x86_64-darwin-20
- x86_64-linux
-
-DEPENDENCIES
- infra!
- minitest (= 5.15.0)!
- mutant-license!
- mutant-minitest (= 0.11.26)!
-
-BUNDLED WITH
- 2.5.9
diff --git a/ecommerce/inventory/Makefile b/ecommerce/inventory/Makefile
deleted file mode 100644
index 23850fab1..000000000
--- a/ecommerce/inventory/Makefile
+++ /dev/null
@@ -1,10 +0,0 @@
-install:
- @bundle install
-
-test:
- @bundle exec ruby -e "require \"rake/rake_test_loader\"" test/*_test.rb
-
-mutate:
- @RAILS_ENV=test bundle exec mutant run
-
-.PHONY: install test mutate
diff --git a/ecommerce/inventory/README.md b/ecommerce/inventory/README.md
deleted file mode 100644
index a05e5cffb..000000000
--- a/ecommerce/inventory/README.md
+++ /dev/null
@@ -1,9 +0,0 @@
-# Inventory
-
-[](https://github.com/RailsEventStore/cqrs-es-sample-with-res/actions/workflows/inventory.yml)
-
-#### Up and running
-
-```
-make install test mutate
-```
diff --git a/ecommerce/inventory/lib/inventory.rb b/ecommerce/inventory/lib/inventory.rb
deleted file mode 100644
index fe432c851..000000000
--- a/ecommerce/inventory/lib/inventory.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-require "infra"
-require_relative "inventory/commands/release"
-require_relative "inventory/commands/supply"
-require_relative "inventory/commands/reserve"
-require_relative "inventory/commands/dispatch"
-require_relative "inventory/events/stock_level_changed"
-require_relative "inventory/events/stock_released"
-require_relative "inventory/events/stock_reserved"
-require_relative "inventory/events/availability_changed"
-require_relative "inventory/inventory_entry_service"
-require_relative "inventory/inventory_entry"
-
-module Inventory
- class Configuration
- def call(event_store, command_bus)
- inventory = InventoryEntryService.new(event_store)
-
- command_bus.register(
- Reserve,
- inventory.method(:reserve)
- )
- command_bus.register(
- Release,
- inventory.method(:release)
- )
- command_bus.register(
- Supply,
- inventory.public_method(:supply)
- )
- command_bus.register(
- Dispatch,
- inventory.public_method(:dispatch)
- )
- end
- end
-end
diff --git a/ecommerce/inventory/lib/inventory/commands/dispatch.rb b/ecommerce/inventory/lib/inventory/commands/dispatch.rb
deleted file mode 100644
index 1db4643b8..000000000
--- a/ecommerce/inventory/lib/inventory/commands/dispatch.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-module Inventory
- class Dispatch < Infra::Command
- attribute :product_id, Infra::Types::UUID
- attribute :quantity, Infra::Types::Coercible::Integer.constrained(gteq: 1)
- end
-end
diff --git a/ecommerce/inventory/lib/inventory/commands/release.rb b/ecommerce/inventory/lib/inventory/commands/release.rb
deleted file mode 100644
index d105f6ad2..000000000
--- a/ecommerce/inventory/lib/inventory/commands/release.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-module Inventory
- class Release < Infra::Command
- attribute :product_id, Infra::Types::UUID
- attribute :quantity, Infra::Types::Coercible::Integer.constrained(gteq: 1)
- end
-end
diff --git a/ecommerce/inventory/lib/inventory/commands/reserve.rb b/ecommerce/inventory/lib/inventory/commands/reserve.rb
deleted file mode 100644
index 04f57849d..000000000
--- a/ecommerce/inventory/lib/inventory/commands/reserve.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-module Inventory
- class Reserve < Infra::Command
- attribute :product_id, Infra::Types::UUID
- attribute :quantity, Infra::Types::Coercible::Integer.constrained(gteq: 1)
- end
-end
diff --git a/ecommerce/inventory/lib/inventory/commands/supply.rb b/ecommerce/inventory/lib/inventory/commands/supply.rb
deleted file mode 100644
index ecb5c1165..000000000
--- a/ecommerce/inventory/lib/inventory/commands/supply.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-module Inventory
- class Supply < Infra::Command
- attribute :product_id, Infra::Types::UUID
- attribute :quantity, Infra::Types::Coercible::Integer.constrained(gteq: 1)
- end
-end
diff --git a/ecommerce/inventory/lib/inventory/events/availability_changed.rb b/ecommerce/inventory/lib/inventory/events/availability_changed.rb
deleted file mode 100644
index 00bc02de7..000000000
--- a/ecommerce/inventory/lib/inventory/events/availability_changed.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-module Inventory
- class AvailabilityChanged < Infra::Event
- end
-end
diff --git a/ecommerce/inventory/lib/inventory/events/stock_level_changed.rb b/ecommerce/inventory/lib/inventory/events/stock_level_changed.rb
deleted file mode 100644
index b5623110e..000000000
--- a/ecommerce/inventory/lib/inventory/events/stock_level_changed.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-module Inventory
- class StockLevelChanged < Infra::Event
- end
-end
diff --git a/ecommerce/inventory/lib/inventory/events/stock_released.rb b/ecommerce/inventory/lib/inventory/events/stock_released.rb
deleted file mode 100644
index 8d62e01df..000000000
--- a/ecommerce/inventory/lib/inventory/events/stock_released.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-module Inventory
- class StockReleased < Infra::Event
- end
-end
diff --git a/ecommerce/inventory/lib/inventory/events/stock_reserved.rb b/ecommerce/inventory/lib/inventory/events/stock_reserved.rb
deleted file mode 100644
index 72ea5bf66..000000000
--- a/ecommerce/inventory/lib/inventory/events/stock_reserved.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-module Inventory
- class StockReserved < Infra::Event
- end
-end
diff --git a/ecommerce/inventory/lib/inventory/inventory_entry.rb b/ecommerce/inventory/lib/inventory/inventory_entry.rb
deleted file mode 100644
index d1d11f08d..000000000
--- a/ecommerce/inventory/lib/inventory/inventory_entry.rb
+++ /dev/null
@@ -1,91 +0,0 @@
-module Inventory
- class InventoryEntry
- include AggregateRoot
-
- InventoryNotAvailable = Class.new(StandardError)
- InventoryNotEvenReserved = Class.new(StandardError)
-
- def initialize(product_id)
- @product_id = product_id
- @reserved = 0
- end
-
- def supply(quantity)
- apply StockLevelChanged.new(
- data: {
- product_id: @product_id,
- quantity: quantity,
- stock_level: (@in_stock || 0) + quantity
- }
- )
- apply_availability_changed
- end
-
- def dispatch(quantity)
- apply StockLevelChanged.new(
- data: {
- product_id: @product_id,
- quantity: -quantity,
- stock_level: @in_stock - quantity
- }
- ) if stock_level_defined?
- apply_availability_changed
- end
-
- def reserve(quantity)
- raise InventoryNotAvailable if stock_level_defined? && quantity > availability
- apply StockReserved.new(
- data: {
- product_id: @product_id,
- quantity: quantity
- }
- )
- apply_availability_changed
- end
-
- def release(quantity)
- raise InventoryNotEvenReserved if quantity > @reserved
- apply StockReleased.new(
- data: {
- product_id: @product_id,
- quantity: quantity
- }
- )
- apply_availability_changed
- end
-
- private
-
- def apply_availability_changed
- apply AvailabilityChanged.new(
- data: {
- product_id: @product_id,
- available: availability
- }
- ) if stock_level_defined?
- end
-
- on StockLevelChanged do |event|
- @in_stock = event.data.fetch(:stock_level)
- end
-
- on StockReserved do |event|
- @reserved += event.data.fetch(:quantity)
- end
-
- on StockReleased do |event|
- @reserved -= event.data.fetch(:quantity)
- end
-
- on AvailabilityChanged do |_|
- end
-
- def availability
- @in_stock - @reserved
- end
-
- def stock_level_defined?
- !@in_stock.nil?
- end
- end
-end
diff --git a/ecommerce/inventory/lib/inventory/inventory_entry_service.rb b/ecommerce/inventory/lib/inventory/inventory_entry_service.rb
deleted file mode 100644
index 5d5147601..000000000
--- a/ecommerce/inventory/lib/inventory/inventory_entry_service.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-module Inventory
- class InventoryEntryService
- def initialize(event_store)
- @repository = Infra::AggregateRootRepository.new(event_store)
- end
-
- def supply(command)
- with_inventory_entry(command.product_id) do |entry|
- entry.supply(command.quantity)
- end
- end
-
- def dispatch(command)
- with_inventory_entry(command.product_id) do |entry|
- entry.dispatch(command.quantity)
- end
- end
-
- def reserve(command)
- with_inventory_entry(command.product_id) do |entry|
- entry.reserve(command.quantity)
- end
- end
-
- def release(command)
- with_inventory_entry(command.product_id) do |entry|
- entry.release(command.quantity)
- end
- end
-
- private
-
- def with_inventory_entry(product_id)
- @repository.with_aggregate(InventoryEntry, product_id) do |entry|
- yield(entry)
- end
- end
- end
-end
diff --git a/ecommerce/inventory/test/dispatch_test.rb b/ecommerce/inventory/test/dispatch_test.rb
deleted file mode 100644
index 5483d7673..000000000
--- a/ecommerce/inventory/test/dispatch_test.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-require_relative "test_helper"
-
-module Inventory
- class DispatchTest < Test
- def test_nothing_changes_when_stock_level_is_undefined
- product_id = SecureRandom.uuid
- assert_events(inventory_entry_stream(product_id)) do
- act(dispatch(product_id, 1))
- end
- end
-
- def test_stock_level_changes_with_dispatch_command
- product_id = SecureRandom.uuid
- arrange(supply(product_id, 1))
- assert_events(
- inventory_entry_stream(product_id),
- StockLevelChanged.new(
- data: {
- product_id: product_id,
- quantity: -1,
- stock_level: 0
- }
- ),
- AvailabilityChanged.new(data: { product_id: product_id, available: 0 })
- ) do
- act(dispatch(product_id, 1))
- end
- end
- end
-end
diff --git a/ecommerce/inventory/test/release_test.rb b/ecommerce/inventory/test/release_test.rb
deleted file mode 100644
index 8d35021cc..000000000
--- a/ecommerce/inventory/test/release_test.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-require_relative "test_helper"
-
-module Inventory
- class ReleaseTest < Test
- def test_stock_gets_released_when_reserved_and_stock_level_undefined
- product_id = SecureRandom.uuid
-
- arrange(reserve(product_id, 1))
- assert_events(
- inventory_entry_stream(product_id),
- StockReleased.new(data: { product_id: product_id, quantity: 1 })
- ) do
- act(Release.new(product_id: product_id, quantity: 1))
- end
- end
-
- def test_stock_gets_released_when_reserved_and_stock_level_defined
- product_id = SecureRandom.uuid
-
- arrange(supply(product_id, 1), reserve(product_id, 1))
- assert_events(
- inventory_entry_stream(product_id),
- StockReleased.new(data: { product_id: product_id, quantity: 1 }),
- AvailabilityChanged.new(data: { product_id: product_id, available: 1 })
- ) do
- act(Release.new(product_id: product_id, quantity: 1))
- end
- end
-
- def test_stock_does_not_get_released_when_not_reserved
- product_id = SecureRandom.uuid
-
- assert_raises(
- InventoryEntry::InventoryNotEvenReserved
- ) do
- act(Release.new(product_id: product_id, quantity: 1))
- end
- end
- end
-end
diff --git a/ecommerce/inventory/test/reserve_test.rb b/ecommerce/inventory/test/reserve_test.rb
deleted file mode 100644
index 89a900c55..000000000
--- a/ecommerce/inventory/test/reserve_test.rb
+++ /dev/null
@@ -1,38 +0,0 @@
-require_relative "test_helper"
-
-module Inventory
- class ReserveTest < Test
- def test_stock_gets_reserved_when_available
- product_id = SecureRandom.uuid
- arrange(supply(product_id, 1))
-
- assert_events(
- inventory_entry_stream(product_id),
- StockReserved.new(data: { product_id: product_id, quantity: 1 }),
- AvailabilityChanged.new(data: { product_id: product_id, available: 0 })
- ) do
- act(reserve(product_id, 1))
- end
- end
-
- def test_stock_gets_reserved_when_stock_level_undefined
- product_id = SecureRandom.uuid
-
- assert_events(
- inventory_entry_stream(product_id),
- StockReserved.new(data: { product_id: product_id, quantity: 1 })
- ) do
- act(reserve(product_id, 1))
- end
- end
-
- def test_raises_when_stock_unavailable
- product_id = SecureRandom.uuid
- arrange(supply(product_id, 1))
-
- assert_raises(InventoryEntry::InventoryNotAvailable) do
- act(reserve(product_id, 2))
- end
- end
- end
-end
diff --git a/ecommerce/inventory/test/supply_test.rb b/ecommerce/inventory/test/supply_test.rb
deleted file mode 100644
index 7815e544f..000000000
--- a/ecommerce/inventory/test/supply_test.rb
+++ /dev/null
@@ -1,33 +0,0 @@
-require_relative "test_helper"
-
-module Inventory
- class SupplyTest < Test
- def test_stock_level_changes_with_supply_command
- product_id = SecureRandom.uuid
- assert_events(
- inventory_entry_stream(product_id),
- StockLevelChanged.new(
- data: {
- product_id: product_id,
- quantity: 1,
- stock_level: 1
- }
- ),
- AvailabilityChanged.new(
- data: { product_id: product_id, available: 1 }
- )
- ) { act(supply(product_id, 1)) }
- assert_events(
- inventory_entry_stream(product_id),
- StockLevelChanged.new(
- data: {
- product_id: product_id,
- quantity: 1,
- stock_level: 2
- }
- ),
- AvailabilityChanged.new(data: { product_id: product_id, available: 2 })
- ) { act(supply(product_id, 1)) }
- end
- end
-end
diff --git a/ecommerce/inventory/test/test_helper.rb b/ecommerce/inventory/test/test_helper.rb
deleted file mode 100644
index 3b3901c49..000000000
--- a/ecommerce/inventory/test/test_helper.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-require "minitest/autorun"
-require "mutant/minitest/coverage"
-
-require_relative "../lib/inventory"
-
-module Inventory
- class Test < Infra::InMemoryTest
- cover "Inventory*"
-
- def before_setup
- super
- Configuration.new.call(event_store, command_bus)
- end
-
- def inventory_entry_stream(product_id)
- "Inventory::InventoryEntry$#{product_id}"
- end
-
- def reserve(product_id, quantity)
- Reserve.new(product_id: product_id, quantity: quantity)
- end
-
- def release(product_id, quantity)
- Release.new(product_id: product_id, quantity: quantity)
- end
-
- def dispatch(product_id, quantity)
- Dispatch.new(product_id: product_id, quantity: quantity)
- end
-
- def supply(product_id, quantity)
- Supply.new(product_id: product_id, quantity: quantity)
- end
-
- def cancel_reservation(order_id)
- Release.new(order_id: order_id)
- end
- end
-end
diff --git a/ecommerce/invoicing/.mutant.yml b/ecommerce/invoicing/.mutant.yml
deleted file mode 100644
index 751c1e676..000000000
--- a/ecommerce/invoicing/.mutant.yml
+++ /dev/null
@@ -1,14 +0,0 @@
-requires:
- - ./test/test_helper
-integration: minitest
-coverage_criteria:
- process_abort: true
-matcher:
- subjects:
- - Invoicing*
- ignore:
- - Invoicing::Configuration*
- - Invoicing::Test*
- - Invoicing::InvoiceItemTitleCatalog*
- - Invoicing::InvoiceService#initialize
- - Invoicing::FakeConcurrentInvoiceNumberGenerator#next_number
\ No newline at end of file
diff --git a/ecommerce/invoicing/Gemfile b/ecommerce/invoicing/Gemfile
deleted file mode 100644
index 8fd887144..000000000
--- a/ecommerce/invoicing/Gemfile
+++ /dev/null
@@ -1,4 +0,0 @@
-source "https://rubygems.org"
-
-eval_gemfile "../../infra/Gemfile.test"
-gem "infra", path: "../../infra"
\ No newline at end of file
diff --git a/ecommerce/invoicing/Gemfile.lock b/ecommerce/invoicing/Gemfile.lock
deleted file mode 100644
index 4a73abff2..000000000
--- a/ecommerce/invoicing/Gemfile.lock
+++ /dev/null
@@ -1,119 +0,0 @@
-PATH
- remote: ../../infra
- specs:
- infra (1.0.0)
- aggregate_root (~> 2.13)
- arkency-command_bus
- dry-struct
- dry-types
- rake
- ruby_event_store (~> 2.13)
- ruby_event_store-transformations
- sidekiq
-
-GEM
- remote: https://oss:7AXfeZdAfCqL1PvHm2nvDJO6Zd9UW8IK@gem.mutant.dev/
- specs:
- mutant-license (0.1.1.2.1627430819213747598431630701693729869473.6)
-
-GEM
- remote: https://rubygems.org/
- specs:
- activesupport (7.1.2)
- base64
- bigdecimal
- concurrent-ruby (~> 1.0, >= 1.0.2)
- connection_pool (>= 2.2.5)
- drb
- i18n (>= 1.6, < 2)
- minitest (>= 5.1)
- mutex_m
- tzinfo (~> 2.0)
- aggregate_root (2.13.0)
- ruby_event_store (= 2.13.0)
- arkency-command_bus (0.4.1)
- concurrent-ruby
- ast (2.4.2)
- base64 (0.2.0)
- bigdecimal (3.1.4)
- concurrent-ruby (1.2.2)
- connection_pool (2.4.1)
- diff-lcs (1.5.0)
- drb (2.2.0)
- ruby2_keywords
- dry-core (1.0.1)
- concurrent-ruby (~> 1.0)
- zeitwerk (~> 2.6)
- dry-inflector (1.0.0)
- dry-logic (1.5.0)
- concurrent-ruby (~> 1.0)
- dry-core (~> 1.0, < 2)
- zeitwerk (~> 2.6)
- dry-struct (1.6.0)
- dry-core (~> 1.0, < 2)
- dry-types (>= 1.7, < 2)
- ice_nine (~> 0.11)
- zeitwerk (~> 2.6)
- dry-types (1.7.1)
- concurrent-ruby (~> 1.0)
- dry-core (~> 1.0)
- dry-inflector (~> 1.0)
- dry-logic (~> 1.4)
- zeitwerk (~> 2.6)
- i18n (1.14.1)
- concurrent-ruby (~> 1.0)
- ice_nine (0.11.2)
- minitest (5.15.0)
- mutant (0.11.26)
- diff-lcs (~> 1.3)
- parser (~> 3.2.2, >= 3.2.2.4)
- regexp_parser (~> 2.8.2)
- sorbet-runtime (~> 0.5.0)
- unparser (~> 0.6.9)
- mutant-minitest (0.11.26)
- minitest (~> 5.11)
- mutant (= 0.11.26)
- mutex_m (0.2.0)
- parser (3.2.2.4)
- ast (~> 2.4.1)
- racc
- racc (1.7.3)
- rack (3.0.8)
- rake (13.1.0)
- redis-client (0.19.0)
- connection_pool
- regexp_parser (2.8.3)
- ruby2_keywords (0.0.5)
- ruby_event_store (2.13.0)
- concurrent-ruby (~> 1.0, >= 1.1.6)
- ruby_event_store-transformations (0.1.0)
- activesupport (>= 5.0)
- ruby_event_store (>= 2.0.0, < 3.0.0)
- sidekiq (7.2.0)
- concurrent-ruby (< 2)
- connection_pool (>= 2.3.0)
- rack (>= 2.2.4)
- redis-client (>= 0.14.0)
- sorbet-runtime (0.5.11190)
- tzinfo (2.0.6)
- concurrent-ruby (~> 1.0)
- unparser (0.6.12)
- diff-lcs (~> 1.3)
- parser (>= 3.2.2.4)
- zeitwerk (2.6.12)
-
-PLATFORMS
- arm64-darwin-20
- arm64-darwin-21
- ruby
- x86_64-darwin-20
- x86_64-linux
-
-DEPENDENCIES
- infra!
- minitest (= 5.15.0)!
- mutant-license!
- mutant-minitest (= 0.11.26)!
-
-BUNDLED WITH
- 2.5.9
diff --git a/ecommerce/invoicing/Makefile b/ecommerce/invoicing/Makefile
deleted file mode 100644
index 23850fab1..000000000
--- a/ecommerce/invoicing/Makefile
+++ /dev/null
@@ -1,10 +0,0 @@
-install:
- @bundle install
-
-test:
- @bundle exec ruby -e "require \"rake/rake_test_loader\"" test/*_test.rb
-
-mutate:
- @RAILS_ENV=test bundle exec mutant run
-
-.PHONY: install test mutate
diff --git a/ecommerce/invoicing/README.md b/ecommerce/invoicing/README.md
deleted file mode 100644
index ff873a58c..000000000
--- a/ecommerce/invoicing/README.md
+++ /dev/null
@@ -1,15 +0,0 @@
-# Invoicing
-
-[](https://github.com/RailsEventStore/cqrs-es-sample-with-res/actions/workflows/invoicing.yml)
-
-
-#### Up and running
-
-```
-make install test mutate
-```
-
-
-### Domain knowledge
-
-[Steps for successful invoicing](https://www.xero.com/guides/invoicing/)
\ No newline at end of file
diff --git a/ecommerce/invoicing/lib/invoicing.rb b/ecommerce/invoicing/lib/invoicing.rb
deleted file mode 100644
index dc3c3ce19..000000000
--- a/ecommerce/invoicing/lib/invoicing.rb
+++ /dev/null
@@ -1,49 +0,0 @@
-require 'infra'
-require_relative 'invoicing/commands'
-require_relative 'invoicing/events'
-require_relative 'invoicing/services'
-require_relative 'invoicing/invoice'
-require_relative 'invoicing/invoice_item_title_catalog'
-require_relative 'invoicing/product'
-require_relative 'invoicing/invoice_number_generator'
-
-module Invoicing
- class Configuration
- def call(event_store, command_bus)
- command_bus.register(
- SetProductNameDisplayedOnInvoice,
- SetProductNameDisplayedOnInvoiceHandler.new(event_store)
- )
- command_bus.register(
- AddInvoiceItem,
- InvoiceService.new(event_store).public_method(:add_item)
- )
- command_bus.register(
- SetPaymentDate,
- InvoiceService.new(event_store).public_method(:set_payment_date)
- )
- command_bus.register(
- IssueInvoice,
- InvoiceService.new(event_store).public_method(:issue)
- )
- command_bus.register(
- SetBillingAddress,
- InvoiceService.new(event_store).public_method(:set_billing_address)
- )
- event_store.subscribe(
- ->(event) do
- stream_name = "InvoiceIssued$#{event.data.fetch(:issue_date).strftime("%Y-%m")}"
- ordinal_number = event.data.fetch(:invoice_number).split('/').first.to_i
- event_store.link(
- event.event_id,
- stream_name: stream_name,
- expected_version: ordinal_number - 2
- )
- rescue RubyEventStore::WrongExpectedEventVersion
- raise Invoice::InvoiceNumberInUse
- end,
- to: [Invoicing::InvoiceIssued]
- )
- end
- end
-end
\ No newline at end of file
diff --git a/ecommerce/invoicing/lib/invoicing/commands.rb b/ecommerce/invoicing/lib/invoicing/commands.rb
deleted file mode 100644
index b49a47a93..000000000
--- a/ecommerce/invoicing/lib/invoicing/commands.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-module Invoicing
- class AddInvoiceItem < Infra::Command
- attribute :invoice_id, Infra::Types::UUID
- attribute :product_id, Infra::Types::UUID
- attribute :quantity, Infra::Types::Quantity
- attribute :unit_price, Infra::Types::Price
- attribute :vat_rate, Infra::Types::VatRate
- end
-
- class IssueInvoice < Infra::Command
- attribute :invoice_id, Infra::Types::UUID
- attribute :issue_date, Infra::Types::Date
- end
-
- class SetPaymentDate < Infra::Command
- attribute :invoice_id, Infra::Types::UUID
- attribute :payment_date, Infra::Types::Date
- end
-
- class SetBillingAddress < Infra::Command
- attribute :invoice_id, Infra::Types::UUID
- attribute :tax_id_number, Infra::Types::String.optional
- attribute :postal_address, Infra::Types::PostalAddress
- end
-
- class SetProductNameDisplayedOnInvoice < Infra::Command
- attribute :product_id, Infra::Types::UUID
- attribute :name_displayed, Infra::Types::String
- end
-end
\ No newline at end of file
diff --git a/ecommerce/invoicing/lib/invoicing/events.rb b/ecommerce/invoicing/lib/invoicing/events.rb
deleted file mode 100644
index 30c4bdd67..000000000
--- a/ecommerce/invoicing/lib/invoicing/events.rb
+++ /dev/null
@@ -1,33 +0,0 @@
-module Invoicing
- class InvoiceItemAdded < Infra::Event
- attribute :invoice_id, Infra::Types::UUID
- attribute :product_id, Infra::Types::UUID
- attribute :title, Infra::Types::String
- attribute :quantity, Infra::Types::Quantity
- attribute :unit_price, Infra::Types::Price
- attribute :vat_rate, Infra::Types::VatRate
- end
-
- class InvoicePaymentDateSet < Infra::Event
- attribute :invoice_id, Infra::Types::UUID
- attribute :payment_date, Infra::Types::Params::Date
- end
-
- class InvoiceIssued < Infra::Event
- attribute :invoice_id, Infra::Types::UUID
- attribute :issue_date, Infra::Types::Params::Date
- attribute :disposal_date, Infra::Types::Params::Date
- attribute :invoice_number, Infra::Types::String
- end
-
- class BillingAddressSet < Infra::Event
- attribute :invoice_id, Infra::Types::UUID
- attribute :tax_id_number, Infra::Types::String.optional
- attribute :postal_address, Infra::Types::PostalAddress
- end
-
- class ProductNameDisplayedSet < Infra::Event
- attribute :product_id, Infra::Types::UUID
- attribute :name_displayed, Infra::Types::String
- end
-end
\ No newline at end of file
diff --git a/ecommerce/invoicing/lib/invoicing/fake_concurrent_invoice_number_generator.rb b/ecommerce/invoicing/lib/invoicing/fake_concurrent_invoice_number_generator.rb
deleted file mode 100644
index 7328ece12..000000000
--- a/ecommerce/invoicing/lib/invoicing/fake_concurrent_invoice_number_generator.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-module Invoicing
- class FakeConcurrentInvoiceNumberGenerator
- def initialize
- @counter = 0
- end
-
- def call(issue_date)
- issue_date.strftime("#{next_number}/%m/%Y")
- end
-
- private
-
- def next_number
- @counter += 1
- return 1 if @counter < 2
- @counter - 1
- end
- end
-end
\ No newline at end of file
diff --git a/ecommerce/invoicing/lib/invoicing/invoice.rb b/ecommerce/invoicing/lib/invoicing/invoice.rb
deleted file mode 100644
index 9d759e807..000000000
--- a/ecommerce/invoicing/lib/invoicing/invoice.rb
+++ /dev/null
@@ -1,110 +0,0 @@
-module Invoicing
- class Invoice
- InvoiceAlreadyIssued = Class.new(StandardError)
- InvoiceNumberInUse = Class.new(StandardError)
- BillingAddressNotSpecified = Class.new(StandardError)
- include AggregateRoot
-
- def initialize(invoice_id)
- @invoice_id = invoice_id
- @invoice_items = []
- end
-
- def issue(issue_date, invoice_number)
- raise BillingAddressNotSpecified unless @postal_address
- raise InvoiceAlreadyIssued unless draft?
- disposal_date = [@payment_date, issue_date].compact.min
- apply(
- InvoiceIssued.new(
- data: {
- invoice_id: @invoice_id,
- issue_date: issue_date,
- disposal_date: disposal_date,
- invoice_number: invoice_number
- }
- )
- )
- end
-
- def set_payment_date(payment_date)
- apply(
- InvoicePaymentDateSet.new(
- data: {
- invoice_id: @invoice_id,
- payment_date: payment_date
- }
- )
- )
- end
-
- def add_item(product_id, title, unit_price, vat_rate, quantity)
- raise InvoiceAlreadyIssued unless draft?
- apply(
- InvoiceItemAdded.new(
- data: {
- invoice_id: @invoice_id,
- product_id: product_id,
- title: title,
- quantity: quantity,
- unit_price: unit_price,
- vat_rate: vat_rate
- }
- )
- )
- end
-
- def set_billing_address(tax_id_number, postal_address)
- raise InvoiceAlreadyIssued unless draft?
- apply BillingAddressSet.new(
- data: {
- invoice_id: @invoice_id,
- postal_address: postal_address,
- tax_id_number: tax_id_number
- }
- )
- end
-
- private
-
- def draft?
- @issue_date.nil?
- end
-
- on InvoiceItemAdded do |event|
- @invoice_items << InvoiceItem.new(
- event.data[:product_id],
- event.data[:title],
- event.data[:unit_price],
- event.data[:vat_rate],
- event.data[:quantity]
- )
- end
-
- on InvoicePaymentDateSet do |event|
- @payment_date = event.data[:payment_date]
- end
-
- on InvoiceIssued do |event|
- @state = :issued
- @issue_date = event.data[:issue_date]
- @disposal_date = event.data[:disposal_date]
- end
-
- on BillingAddressSet do |event|
- @tax_id_number = event.data.fetch(:tax_id_number)
- @postal_address = event.data.fetch(:postal_address)
- end
- end
-
- class InvoiceItem
- attr_reader :product_id, :quantity, :unit_price, :vat_rate, :title
-
- def initialize(product_id, title, unit_price, vat_rate, quantity)
- @product_id = product_id
- @title = title
- @unit_price = unit_price
- @vat_rate = vat_rate
- @quantity = quantity
- end
- end
-end
\ No newline at end of file
diff --git a/ecommerce/invoicing/lib/invoicing/invoice_item_title_catalog.rb b/ecommerce/invoicing/lib/invoicing/invoice_item_title_catalog.rb
deleted file mode 100644
index bcabc61d5..000000000
--- a/ecommerce/invoicing/lib/invoicing/invoice_item_title_catalog.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-module Invoicing
- class InvoiceItemTitleCatalog
- def initialize(event_store)
- @event_store = event_store
- end
-
- def invoice_item_title_for_product(product_id)
- @event_store
- .read
- .of_type(ProductNameDisplayedSet)
- .to_a
- .filter { |e| e.data.fetch(:product_id).eql?(product_id) }
- .last
- .data
- .fetch(:name_displayed)
- end
- end
-end
\ No newline at end of file
diff --git a/ecommerce/invoicing/lib/invoicing/invoice_number_generator.rb b/ecommerce/invoicing/lib/invoicing/invoice_number_generator.rb
deleted file mode 100644
index 0caaa15e1..000000000
--- a/ecommerce/invoicing/lib/invoicing/invoice_number_generator.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-module Invoicing
- class InvoiceNumberGenerator
- def initialize(event_store)
- @event_store = event_store
- end
-
- def call(issue_date)
- issue_date.strftime("#{next_number(issue_date)}/%m/%Y")
- end
-
- private
-
- def next_number(issue_date)
- stream_name = "InvoiceIssued$#{issue_date.strftime("%Y-%m")}"
- @event_store.read.stream(stream_name).count + 1
- end
- end
-end
\ No newline at end of file
diff --git a/ecommerce/invoicing/lib/invoicing/product.rb b/ecommerce/invoicing/lib/invoicing/product.rb
deleted file mode 100644
index 01624ff3c..000000000
--- a/ecommerce/invoicing/lib/invoicing/product.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-module Invoicing
- class Product
- include AggregateRoot
-
- def initialize(product_id)
- @product_id = product_id
- end
-
- def set_name_displayed(name)
- apply(
- ProductNameDisplayedSet.new(
- data: {
- product_id: @product_id,
- name_displayed: name
- }
- )
- )
- end
-
- private
-
- on ProductNameDisplayedSet do |event|
- @name_displayed = event.data[:name_displayed]
- end
- end
-end
\ No newline at end of file
diff --git a/ecommerce/invoicing/lib/invoicing/services.rb b/ecommerce/invoicing/lib/invoicing/services.rb
deleted file mode 100644
index b547ae0ab..000000000
--- a/ecommerce/invoicing/lib/invoicing/services.rb
+++ /dev/null
@@ -1,55 +0,0 @@
-module Invoicing
- class InvoiceService
- def initialize(event_store, number_generator = InvoiceNumberGenerator.new(event_store))
- @repository = Infra::AggregateRootRepository.new(event_store)
- @titles_catalog = InvoiceItemTitleCatalog.new(event_store)
- @number_generator = number_generator
- end
-
- def add_item(command)
- with_invoice(command.invoice_id) do |invoice|
- title = @titles_catalog.invoice_item_title_for_product(command.product_id)
- invoice.add_item(command.product_id, title, command.unit_price, command.vat_rate, command.quantity)
- end
- end
-
- def set_payment_date(command)
- with_invoice(command.invoice_id) do |invoice|
- invoice.set_payment_date(command.payment_date)
- end
- end
-
- def set_billing_address(command)
- with_invoice(command.invoice_id) do |invoice|
- invoice.set_billing_address(command.tax_id_number, command.postal_address)
- end
- end
-
- def issue(command)
- with_invoice(command.invoice_id) do |invoice|
- invoice_number = @number_generator.call(command.issue_date)
- invoice.issue(command.issue_date, invoice_number)
- end
- end
-
- private
-
- def with_invoice(invoice_id)
- @repository.with_aggregate(Invoice, invoice_id) do |invoice|
- yield(invoice)
- end
- end
- end
-
- class SetProductNameDisplayedOnInvoiceHandler
- def initialize(event_store)
- @repository = Infra::AggregateRootRepository.new(event_store)
- end
-
- def call(command)
- @repository.with_aggregate(Product, command.product_id) do |product|
- product.set_name_displayed(command.name_displayed)
- end
- end
- end
-end
\ No newline at end of file
diff --git a/ecommerce/invoicing/test/fake_concurrent_invoice_number_generator_test.rb b/ecommerce/invoicing/test/fake_concurrent_invoice_number_generator_test.rb
deleted file mode 100644
index 645b150c6..000000000
--- a/ecommerce/invoicing/test/fake_concurrent_invoice_number_generator_test.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-require_relative "test_helper"
-
-module Invoicing
- class FakeConcurrentInvoiceNumberGeneratorTest < Test
- cover "Invoicing::FakeInvoiceNumberGenerator"
-
- def test_fetching_next_number
- issue_date = Date.new(2022, 1, 5)
- number_generator = FakeConcurrentInvoiceNumberGenerator.new
- assert_equal("1/01/2022", number_generator.call(issue_date))
- assert_equal("1/01/2022", number_generator.call(issue_date))
- assert_equal("2/01/2022", number_generator.call(issue_date))
- end
- end
-end
\ No newline at end of file
diff --git a/ecommerce/invoicing/test/invoice_item_test.rb b/ecommerce/invoicing/test/invoice_item_test.rb
deleted file mode 100644
index 3d65d1ec1..000000000
--- a/ecommerce/invoicing/test/invoice_item_test.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-require_relative "test_helper"
-
-module Invoicing
- class InvoiceItemTest < Test
- cover "Invoicing::Invoice"
-
- def test_initializer
- product_id = SecureRandom.uuid
- vat_rate = Infra::Types::VatRate.new(rate: 20, code: "20")
- unit_price = 100.to_d
- quantity = 20
- title = 'test'
- item = InvoiceItem.new(product_id, title, unit_price, vat_rate, quantity)
-
- assert_equal product_id, item.product_id
- assert_equal title, item.title
- assert_equal vat_rate, item.vat_rate
- assert_equal unit_price, item.unit_price
- assert_equal quantity, item.quantity
- end
- end
-end
\ No newline at end of file
diff --git a/ecommerce/invoicing/test/invoice_number_generator_test.rb b/ecommerce/invoicing/test/invoice_number_generator_test.rb
deleted file mode 100644
index b847f7b8b..000000000
--- a/ecommerce/invoicing/test/invoice_number_generator_test.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-require_relative "test_helper"
-
-module Invoicing
- class InvoiceNumberGeneratorTest < Test
- cover "Invoicing::InvoiceNumberGenerator"
-
- def test_fetching_next_number
- issue_date = Date.new(2022, 1, 5)
- number_generator = InvoiceNumberGenerator.new(event_store)
- assert_equal("1/01/2022", number_generator.call(issue_date))
- issue_random_invoice(issue_date)
- assert_equal("2/01/2022", number_generator.call(issue_date))
- next_month_issue_date = Date.new(2022, 2, 5)
- assert_equal("1/02/2022", number_generator.call(next_month_issue_date))
- next_year_issue_date = Date.new(2023, 1, 5)
- assert_equal("1/01/2023", number_generator.call(next_year_issue_date))
- end
-
- private
-
- def issue_random_invoice(issue_date)
- invoice_id = SecureRandom.uuid
- set_billing_address(invoice_id)
- run_command(IssueInvoice.new(invoice_id: invoice_id, issue_date: issue_date))
- end
- end
-end
\ No newline at end of file
diff --git a/ecommerce/invoicing/test/invoice_service_test.rb b/ecommerce/invoicing/test/invoice_service_test.rb
deleted file mode 100644
index 11412c942..000000000
--- a/ecommerce/invoicing/test/invoice_service_test.rb
+++ /dev/null
@@ -1,177 +0,0 @@
-require_relative "test_helper"
-
-module Invoicing
- class InvoiceServiceTest < Test
- cover "Invoicing::InvoiceService"
-
- def test_adding_to_invoice
- invoice_id = SecureRandom.uuid
- product_id = SecureRandom.uuid
- unit_price = 10.0.to_d
- vat_rate = Infra::Types::VatRate.new(code: "20", rate: 20)
- title = 'test'
-
- stream = "Invoicing::Invoice$#{invoice_id}"
- assert_events(
- stream,
- InvoiceItemAdded.new(
- data: {
- invoice_id: invoice_id,
- product_id: product_id,
- title: title,
- vat_rate: vat_rate,
- unit_price: unit_price,
- quantity: 1,
- }
- )
- ) { add_item(invoice_id, product_id, vat_rate, unit_price, title) }
- end
-
- def test_setting_billing_address
- invoice_id = SecureRandom.uuid
- billing_address = fake_address
- tax_id_number = "PL1111111111"
- stream = "Invoicing::Invoice$#{invoice_id}"
- assert_events(
- stream,
- BillingAddressSet.new(
- data: {
- invoice_id: invoice_id,
- postal_address: fake_address,
- tax_id_number: tax_id_number
- }
- )
- ) { set_billing_address(invoice_id, billing_address, tax_id_number) }
- end
-
- def test_setting_payment_date
- invoice_id = SecureRandom.uuid
- payment_date = Date.new(2021, 1, 5)
-
- stream = "Invoicing::Invoice$#{invoice_id}"
- assert_events(
- stream,
- InvoicePaymentDateSet.new(
- data: {
- invoice_id: invoice_id,
- payment_date: payment_date,
- }
- )
- ) { set_payment_date(invoice_id, payment_date) }
- end
-
- def test_issuing_invoice
- invoice_id = SecureRandom.uuid
- issue_date = Date.new(2021, 1, 5)
- set_billing_address(invoice_id)
-
- stream = "Invoicing::Invoice$#{invoice_id}"
- assert_events(
- stream,
- InvoiceIssued.new(
- data: {
- invoice_id: invoice_id,
- issue_date: issue_date,
- disposal_date: issue_date,
- invoice_number: '1/01/2021'
- }
- )
- ) { issue_invoice(invoice_id, issue_date) }
- end
-
- def test_issuing_invoice_after_setting_payment_date
- invoice_id = SecureRandom.uuid
- issue_date = Date.new(2021, 1, 5)
- payment_date = Date.new(2021, 1, 1)
- set_payment_date(invoice_id, payment_date)
- set_billing_address(invoice_id)
-
- stream = "Invoicing::Invoice$#{invoice_id}"
- assert_events(
- stream,
- InvoiceIssued.new(
- data: {
- invoice_id: invoice_id,
- issue_date: issue_date,
- disposal_date: payment_date,
- invoice_number: '1/01/2021'
- }
- )
- ) { issue_invoice(invoice_id, issue_date) }
- end
-
- def test_issuing_invoice_with_faked_race_condition
- invoice_id = SecureRandom.uuid
- another_invoice_id = SecureRandom.uuid
- issue_date = Date.new(2021, 1, 5)
- set_billing_address(invoice_id)
- set_billing_address(another_invoice_id)
-
- mocked_service = InvoiceService.new(
- event_store,
- FakeConcurrentInvoiceNumberGenerator.new
- ).public_method(:issue)
-
- stream = "Invoicing::Invoice$#{invoice_id}"
- assert_events(
- stream,
- InvoiceIssued.new(
- data: {
- invoice_id: invoice_id,
- issue_date: issue_date,
- disposal_date: issue_date,
- invoice_number: '1/01/2021'
- }
- )
- ) { mocked_service.(issue_invoice_command(invoice_id, issue_date)) }
- assert_raises(Invoice::InvoiceNumberInUse) do
- mocked_service.(issue_invoice_command(another_invoice_id, issue_date))
- end
- end
-
- def test_issued_invoice_is_a_final_state
- invoice_id = SecureRandom.uuid
- set_billing_address(invoice_id)
- issue_invoice(invoice_id)
- assert_raises(Invoice::InvoiceAlreadyIssued) { issue_invoice(invoice_id) }
- assert_raises(Invoice::InvoiceAlreadyIssued) { add_item(invoice_id) }
- assert_raises(Invoice::InvoiceAlreadyIssued) { set_billing_address(invoice_id) }
- end
-
- def test_invoice_can_not_be_issued_without_billing_address
- invoice_id = SecureRandom.uuid
- assert_raises(Invoice::BillingAddressNotSpecified) { issue_invoice(invoice_id) }
- end
-
- private
-
- def add_item (
- invoice_id,
- product_id = SecureRandom.uuid,
- vat_rate = Infra::Types::VatRate.new(code: "20", rate: 20),
- unit_price = 10.0.to_d,
- title = 'test'
- )
- set_product_name_displayed(product_id, title)
- run_command(AddInvoiceItem.new(
- invoice_id: invoice_id,
- product_id: product_id,
- vat_rate: vat_rate,
- unit_price: unit_price,
- quantity: 1
- ))
- end
-
- def issue_invoice(invoice_id, issue_date = Date.new(2021, 1, 5))
- run_command(issue_invoice_command(invoice_id, issue_date))
- end
-
- def issue_invoice_command(invoice_id, issue_date)
- IssueInvoice.new(invoice_id: invoice_id, issue_date: issue_date)
- end
-
- def set_payment_date(invoice_id, payment_date = Date.new(2021, 1, 5))
- run_command(SetPaymentDate.new(invoice_id: invoice_id, payment_date: payment_date))
- end
- end
-end
\ No newline at end of file
diff --git a/ecommerce/invoicing/test/setting_product_displayed_name_test.rb b/ecommerce/invoicing/test/setting_product_displayed_name_test.rb
deleted file mode 100644
index 3c26305c4..000000000
--- a/ecommerce/invoicing/test/setting_product_displayed_name_test.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-require_relative "test_helper"
-
-module Invoicing
- class SettingProductDisplayedNameTest < Test
- cover "Invoicing::SetProductNameDisplayedOnInvoiceHandler"
-
- def test_adding_to_invoice
- product_id = SecureRandom.uuid
- name_displayed = 'test'
- stream = "Invoicing::Product$#{product_id}"
-
- assert_events(
- stream,
- ProductNameDisplayedSet.new(
- data: {
- product_id: product_id,
- name_displayed: name_displayed,
- }
- )
- ) { set_product_name_displayed(product_id, name_displayed) }
- end
- end
-end
\ No newline at end of file
diff --git a/ecommerce/invoicing/test/test_helper.rb b/ecommerce/invoicing/test/test_helper.rb
deleted file mode 100644
index f44c21892..000000000
--- a/ecommerce/invoicing/test/test_helper.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-require "minitest/autorun"
-require "mutant/minitest/coverage"
-
-require_relative "../lib/invoicing"
-require_relative '../lib/invoicing/fake_concurrent_invoice_number_generator'
-
-module Invoicing
- class Test < Infra::InMemoryTest
- def before_setup
- super
- Configuration.new.call(event_store, command_bus)
- end
-
- private
-
- def set_product_name_displayed(product_id, name_displayed)
- run_command(SetProductNameDisplayedOnInvoice.new(product_id: product_id, name_displayed: name_displayed))
- end
-
- def set_billing_address(invoice_id, postal_address = fake_address, tax_id_number = nil)
- run_command(SetBillingAddress.new(
- invoice_id: invoice_id,
- postal_address: postal_address,
- tax_id_number: tax_id_number
- ))
- end
-
- def fake_address
- Infra::Types::PostalAddress.new(
- line_1: "Anna Kowalska",
- line_2: "Ul. Bosmanska 1",
- line_3: "81-116 GDYNIA",
- line_4: "POLAND"
- )
- end
- end
-end
diff --git a/ecommerce/ordering/.mutant.yml b/ecommerce/ordering/.mutant.yml
deleted file mode 100644
index 6f7998002..000000000
--- a/ecommerce/ordering/.mutant.yml
+++ /dev/null
@@ -1,13 +0,0 @@
-requires:
- - ./test/test_helper
-integration: minitest
-coverage_criteria:
- process_abort: true
-matcher:
- subjects:
- - Ordering*
- ignore:
- - Ordering::NumberGenerator*
- - Ordering::Test*
- - Ordering::Configuration#call
- - Ordering::Configuration#initialize
\ No newline at end of file
diff --git a/ecommerce/ordering/Gemfile b/ecommerce/ordering/Gemfile
deleted file mode 100644
index 8fd887144..000000000
--- a/ecommerce/ordering/Gemfile
+++ /dev/null
@@ -1,4 +0,0 @@
-source "https://rubygems.org"
-
-eval_gemfile "../../infra/Gemfile.test"
-gem "infra", path: "../../infra"
\ No newline at end of file
diff --git a/ecommerce/ordering/Gemfile.lock b/ecommerce/ordering/Gemfile.lock
deleted file mode 100644
index 4a73abff2..000000000
--- a/ecommerce/ordering/Gemfile.lock
+++ /dev/null
@@ -1,119 +0,0 @@
-PATH
- remote: ../../infra
- specs:
- infra (1.0.0)
- aggregate_root (~> 2.13)
- arkency-command_bus
- dry-struct
- dry-types
- rake
- ruby_event_store (~> 2.13)
- ruby_event_store-transformations
- sidekiq
-
-GEM
- remote: https://oss:7AXfeZdAfCqL1PvHm2nvDJO6Zd9UW8IK@gem.mutant.dev/
- specs:
- mutant-license (0.1.1.2.1627430819213747598431630701693729869473.6)
-
-GEM
- remote: https://rubygems.org/
- specs:
- activesupport (7.1.2)
- base64
- bigdecimal
- concurrent-ruby (~> 1.0, >= 1.0.2)
- connection_pool (>= 2.2.5)
- drb
- i18n (>= 1.6, < 2)
- minitest (>= 5.1)
- mutex_m
- tzinfo (~> 2.0)
- aggregate_root (2.13.0)
- ruby_event_store (= 2.13.0)
- arkency-command_bus (0.4.1)
- concurrent-ruby
- ast (2.4.2)
- base64 (0.2.0)
- bigdecimal (3.1.4)
- concurrent-ruby (1.2.2)
- connection_pool (2.4.1)
- diff-lcs (1.5.0)
- drb (2.2.0)
- ruby2_keywords
- dry-core (1.0.1)
- concurrent-ruby (~> 1.0)
- zeitwerk (~> 2.6)
- dry-inflector (1.0.0)
- dry-logic (1.5.0)
- concurrent-ruby (~> 1.0)
- dry-core (~> 1.0, < 2)
- zeitwerk (~> 2.6)
- dry-struct (1.6.0)
- dry-core (~> 1.0, < 2)
- dry-types (>= 1.7, < 2)
- ice_nine (~> 0.11)
- zeitwerk (~> 2.6)
- dry-types (1.7.1)
- concurrent-ruby (~> 1.0)
- dry-core (~> 1.0)
- dry-inflector (~> 1.0)
- dry-logic (~> 1.4)
- zeitwerk (~> 2.6)
- i18n (1.14.1)
- concurrent-ruby (~> 1.0)
- ice_nine (0.11.2)
- minitest (5.15.0)
- mutant (0.11.26)
- diff-lcs (~> 1.3)
- parser (~> 3.2.2, >= 3.2.2.4)
- regexp_parser (~> 2.8.2)
- sorbet-runtime (~> 0.5.0)
- unparser (~> 0.6.9)
- mutant-minitest (0.11.26)
- minitest (~> 5.11)
- mutant (= 0.11.26)
- mutex_m (0.2.0)
- parser (3.2.2.4)
- ast (~> 2.4.1)
- racc
- racc (1.7.3)
- rack (3.0.8)
- rake (13.1.0)
- redis-client (0.19.0)
- connection_pool
- regexp_parser (2.8.3)
- ruby2_keywords (0.0.5)
- ruby_event_store (2.13.0)
- concurrent-ruby (~> 1.0, >= 1.1.6)
- ruby_event_store-transformations (0.1.0)
- activesupport (>= 5.0)
- ruby_event_store (>= 2.0.0, < 3.0.0)
- sidekiq (7.2.0)
- concurrent-ruby (< 2)
- connection_pool (>= 2.3.0)
- rack (>= 2.2.4)
- redis-client (>= 0.14.0)
- sorbet-runtime (0.5.11190)
- tzinfo (2.0.6)
- concurrent-ruby (~> 1.0)
- unparser (0.6.12)
- diff-lcs (~> 1.3)
- parser (>= 3.2.2.4)
- zeitwerk (2.6.12)
-
-PLATFORMS
- arm64-darwin-20
- arm64-darwin-21
- ruby
- x86_64-darwin-20
- x86_64-linux
-
-DEPENDENCIES
- infra!
- minitest (= 5.15.0)!
- mutant-license!
- mutant-minitest (= 0.11.26)!
-
-BUNDLED WITH
- 2.5.9
diff --git a/ecommerce/ordering/Makefile b/ecommerce/ordering/Makefile
deleted file mode 100644
index 23850fab1..000000000
--- a/ecommerce/ordering/Makefile
+++ /dev/null
@@ -1,10 +0,0 @@
-install:
- @bundle install
-
-test:
- @bundle exec ruby -e "require \"rake/rake_test_loader\"" test/*_test.rb
-
-mutate:
- @RAILS_ENV=test bundle exec mutant run
-
-.PHONY: install test mutate
diff --git a/ecommerce/ordering/README.md b/ecommerce/ordering/README.md
deleted file mode 100644
index 26708b6b8..000000000
--- a/ecommerce/ordering/README.md
+++ /dev/null
@@ -1,61 +0,0 @@
-# Ordering
-
-[](https://github.com/RailsEventStore/cqrs-es-sample-with-res/actions/workflows/ordering.yml)
-
-The `Ordering::Order` aggregate manages the state machine of an order:
-
-- draft
-- submitted
-- accepted
-- expired
-
-After each successful change an appropriate event is published in the Order stream.
-This object is fully event sourced.
-
-| Order | draft | expired | submitted | accepted |
-|-----------|:-----:|:-------:|:---------:|:--------:|
-| draft | | ✅ | ✅ | |
-| expired | | | | |
-| submitted | ✅ | | | ✅ |
-| accepted | | | | |
-
-### Design dilemmas
-
-#### God Domain
-
-The state machine mentioned above became a very central place to the whole application.
-Almost every other domain either reacts to this domain or triggers Ordering.
-
-This might be an issue, as we might end up with a God Domain.
-
-#### Duplication of states
-
-Some of the states here are actually duplicates of the states of the Order in other domains.
-
-#### Multiplication and naming of states
-
-We clearly have a naming issue here.
-What is the actual difference between submit/confirm/accept?
-Not all the names match between method names and event names.
-
-#### Order items
-
-We track order items here, but we actually don't really use it much.
-It doesn't have any impact on the state machine.
-It's only needed to some read models, which can retrieve it from elsewhere - most notably the `Pricing`
-The implementation of `Basket`, because of this, seems duplicated to Pricing or Inventory.
-
-#### Mapping of events between domains
-
-UI -> Ordering::SubmitOrder -> Ordering::Submitted -> Reservation(process) -> Ordering::AcceptOrder
-
-Ordering::AcceptOrder -> Ordering::OrderPlaced ->
-
--> Shipment::SubmitShipment
--> Pricing::CalculateTotalValue
-
-### Up and running
-
-```
-make install test mutate
-```
diff --git a/ecommerce/ordering/lib/ordering.rb b/ecommerce/ordering/lib/ordering.rb
deleted file mode 100644
index ca4ad0c1c..000000000
--- a/ecommerce/ordering/lib/ordering.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-require "infra"
-require_relative "ordering/events/item_added_to_basket"
-require_relative "ordering/events/item_removed_from_basket"
-require_relative "ordering/events/order_placed"
-require_relative "ordering/events/order_expired"
-require_relative "ordering/events/order_submitted"
-require_relative "ordering/events/order_rejected"
-require_relative "ordering/commands/add_item_to_basket"
-require_relative "ordering/commands/remove_item_from_basket"
-require_relative "ordering/commands/submit_order"
-require_relative "ordering/commands/set_order_as_expired"
-require_relative "ordering/commands/accept_order"
-require_relative "ordering/commands/reject_order"
-require_relative "ordering/fake_number_generator"
-require_relative "ordering/number_generator"
-require_relative "ordering/service"
-require_relative "ordering/order"
-
-module Ordering
- class Configuration
- def initialize(number_generator)
- @number_generator = number_generator
- end
-
- def call(event_store, command_bus)
- command_bus.register(
- SubmitOrder,
- OnSubmitOrder.new(event_store, @number_generator.call)
- )
- command_bus.register(AddItemToBasket, OnAddItemToBasket.new(event_store))
- command_bus.register(RemoveItemFromBasket, OnRemoveItemFromBasket.new(event_store))
- command_bus.register(SetOrderAsExpired, OnSetOrderAsExpired.new(event_store))
- command_bus.register(AcceptOrder, OnAcceptOrder.new(event_store))
- command_bus.register(RejectOrder, OnRejectOrder.new(event_store))
- end
- end
-end
diff --git a/ecommerce/ordering/lib/ordering/commands/accept_order.rb b/ecommerce/ordering/lib/ordering/commands/accept_order.rb
deleted file mode 100644
index 4ca46dee2..000000000
--- a/ecommerce/ordering/lib/ordering/commands/accept_order.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-module Ordering
- class AcceptOrder < Infra::Command
- attribute :order_id, Infra::Types::UUID
-
- alias aggregate_id order_id
- end
-end
diff --git a/ecommerce/ordering/lib/ordering/commands/add_item_to_basket.rb b/ecommerce/ordering/lib/ordering/commands/add_item_to_basket.rb
deleted file mode 100644
index bad32f450..000000000
--- a/ecommerce/ordering/lib/ordering/commands/add_item_to_basket.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-module Ordering
- class AddItemToBasket < Infra::Command
- attribute :order_id, Infra::Types::UUID
- attribute :product_id, Infra::Types::UUID
-
- alias aggregate_id order_id
- end
-end
\ No newline at end of file
diff --git a/ecommerce/ordering/lib/ordering/commands/reject_order.rb b/ecommerce/ordering/lib/ordering/commands/reject_order.rb
deleted file mode 100644
index 39a6d522c..000000000
--- a/ecommerce/ordering/lib/ordering/commands/reject_order.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-module Ordering
- class RejectOrder < Infra::Command
- attribute :order_id, Infra::Types::UUID
-
- alias aggregate_id order_id
- end
-end
diff --git a/ecommerce/ordering/lib/ordering/commands/remove_item_from_basket.rb b/ecommerce/ordering/lib/ordering/commands/remove_item_from_basket.rb
deleted file mode 100644
index dd3e31fe9..000000000
--- a/ecommerce/ordering/lib/ordering/commands/remove_item_from_basket.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-module Ordering
- class RemoveItemFromBasket < Infra::Command
- attribute :order_id, Infra::Types::UUID
- attribute :product_id, Infra::Types::UUID
-
- alias aggregate_id order_id
- end
-end
\ No newline at end of file
diff --git a/ecommerce/ordering/lib/ordering/commands/set_order_as_expired.rb b/ecommerce/ordering/lib/ordering/commands/set_order_as_expired.rb
deleted file mode 100644
index 938f95fdb..000000000
--- a/ecommerce/ordering/lib/ordering/commands/set_order_as_expired.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-module Ordering
- class SetOrderAsExpired < Infra::Command
- attribute :order_id, Infra::Types::UUID
-
- alias aggregate_id order_id
- end
-end
diff --git a/ecommerce/ordering/lib/ordering/commands/submit_order.rb b/ecommerce/ordering/lib/ordering/commands/submit_order.rb
deleted file mode 100644
index 6842fe3b5..000000000
--- a/ecommerce/ordering/lib/ordering/commands/submit_order.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-module Ordering
- class SubmitOrder < Infra::Command
- attribute :order_id, Infra::Types::UUID
-
- alias aggregate_id order_id
- end
-end
diff --git a/ecommerce/ordering/lib/ordering/events/item_added_to_basket.rb b/ecommerce/ordering/lib/ordering/events/item_added_to_basket.rb
deleted file mode 100644
index d5fe6f697..000000000
--- a/ecommerce/ordering/lib/ordering/events/item_added_to_basket.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-module Ordering
- class ItemAddedToBasket < Infra::Event
- attribute :order_id, Infra::Types::UUID
- attribute :product_id, Infra::Types::UUID
- end
-end
\ No newline at end of file
diff --git a/ecommerce/ordering/lib/ordering/events/item_removed_from_basket.rb b/ecommerce/ordering/lib/ordering/events/item_removed_from_basket.rb
deleted file mode 100644
index f68c68ff2..000000000
--- a/ecommerce/ordering/lib/ordering/events/item_removed_from_basket.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-module Ordering
- class ItemRemovedFromBasket < Infra::Event
- attribute :order_id, Infra::Types::UUID
- attribute :product_id, Infra::Types::UUID
- end
-end
\ No newline at end of file
diff --git a/ecommerce/ordering/lib/ordering/events/order_placed.rb b/ecommerce/ordering/lib/ordering/events/order_placed.rb
deleted file mode 100644
index 14a59b4fb..000000000
--- a/ecommerce/ordering/lib/ordering/events/order_placed.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-module Ordering
- class OrderPlaced < Infra::Event
- attribute :order_id, Infra::Types::UUID
- attribute :order_number, Infra::Types::OrderNumber
- end
-end
diff --git a/ecommerce/ordering/lib/ordering/events/order_rejected.rb b/ecommerce/ordering/lib/ordering/events/order_rejected.rb
deleted file mode 100644
index 02cd44b60..000000000
--- a/ecommerce/ordering/lib/ordering/events/order_rejected.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-module Ordering
- class OrderRejected < Infra::Event
- attribute :order_id, Infra::Types::UUID
- end
-end
diff --git a/ecommerce/ordering/lib/ordering/events/order_submitted.rb b/ecommerce/ordering/lib/ordering/events/order_submitted.rb
deleted file mode 100644
index 5ebd08e5c..000000000
--- a/ecommerce/ordering/lib/ordering/events/order_submitted.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-module Ordering
- class OrderSubmitted < Infra::Event
- attribute :order_id, Infra::Types::UUID
- attribute :order_number, Infra::Types::OrderNumber
- end
-end
diff --git a/ecommerce/ordering/lib/ordering/fake_number_generator.rb b/ecommerce/ordering/lib/ordering/fake_number_generator.rb
deleted file mode 100644
index a25911d20..000000000
--- a/ecommerce/ordering/lib/ordering/fake_number_generator.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-module Ordering
- class FakeNumberGenerator
- FAKE_NUMBER = "2019/01/60".freeze
- def call
- FAKE_NUMBER
- end
- end
-end
diff --git a/ecommerce/ordering/lib/ordering/number_generator.rb b/ecommerce/ordering/lib/ordering/number_generator.rb
deleted file mode 100644
index 48fd930c1..000000000
--- a/ecommerce/ordering/lib/ordering/number_generator.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-module Ordering
- class NumberGenerator
- def call
- Time.now.strftime("%Y/%m/#{random_number}")
- end
-
- private
-
- def random_number
- SecureRandom.random_number(100)
- end
- end
-end
diff --git a/ecommerce/ordering/lib/ordering/order.rb b/ecommerce/ordering/lib/ordering/order.rb
deleted file mode 100644
index b6ccdd0e9..000000000
--- a/ecommerce/ordering/lib/ordering/order.rb
+++ /dev/null
@@ -1,146 +0,0 @@
-module Ordering
- class Order
- include AggregateRoot
-
- InvalidState = Class.new(StandardError)
- AlreadySubmitted = Class.new(InvalidState)
- NotPlaced = Class.new(InvalidState)
- OrderHasExpired = Class.new(InvalidState)
-
- def initialize(id)
- @id = id
- @state = State.draft
- @basket = Basket.new
- end
-
- def submit(order_number)
- raise OrderHasExpired if @state.expired?
- raise AlreadySubmitted unless @state.draft?
- apply OrderSubmitted.new(
- data: {
- order_id: @id,
- order_number: order_number,
- order_lines: @basket.order_lines
- }
- )
- end
-
- def accept
- raise InvalidState unless @state.submitted?
- apply OrderPlaced.new(
- data: {
- order_id: @id,
- order_number: @order_number,
- order_lines: @basket.order_lines
- }
- )
- end
-
- def reject
- raise InvalidState unless @state.submitted?
- apply OrderRejected.new(
- data: {
- order_id: @id
- }
- )
- end
-
- def expire
- raise AlreadySubmitted unless @state.draft?
- apply OrderExpired.new(data: { order_id: @id })
- end
-
- def add_item(product_id)
- raise AlreadySubmitted unless @state.draft?
- apply ItemAddedToBasket.new(
- data: {
- order_id: @id,
- product_id: product_id,
- }
- )
- end
-
- def remove_item(product_id)
- raise AlreadySubmitted unless @state.draft?
- apply ItemRemovedFromBasket.new(data: { order_id: @id, product_id: product_id })
- end
-
- on OrderPlaced do |_|
- @state = State.new(:accepted)
- end
-
- on OrderExpired do |_|
- @state = State.expired
- end
-
- on ItemAddedToBasket do |event|
- @basket.increase_quantity(event.data[:product_id])
- end
-
- on ItemRemovedFromBasket do |event|
- @basket.decrease_quantity(event.data[:product_id])
- end
-
- on OrderSubmitted do |event|
- @order_number = event.data[:order_number]
- @state = State.submitted
- end
-
- on OrderRejected do |_|
- @state = State.draft
- end
-
- class Basket
- def initialize
- @order_lines = Hash.new(0)
- end
-
- def increase_quantity(product_id)
- order_lines[product_id] = quantity(product_id) + 1
- end
-
- def decrease_quantity(product_id)
- order_lines[product_id] -= 1
- order_lines.delete(product_id) if order_lines.fetch(product_id).equal?(0)
- end
-
- def order_lines
- @order_lines
- end
-
- def quantity(product_id)
- order_lines[product_id]
- end
- end
-
- class State
- def self.draft
- new(:draft)
- end
-
- def self.submitted
- new(:submitted)
- end
-
- def self.expired
- new(:expired)
- end
-
- def initialize(state)
- @state = state
- end
-
- def draft?
- @state.equal?(:draft)
- end
-
- def submitted?
- @state.equal?(:submitted)
- end
-
- def expired?
- @state.equal?(:expired)
- end
- end
- end
-end
diff --git a/ecommerce/ordering/lib/ordering/service.rb b/ecommerce/ordering/lib/ordering/service.rb
deleted file mode 100644
index 4ab6dde83..000000000
--- a/ecommerce/ordering/lib/ordering/service.rb
+++ /dev/null
@@ -1,75 +0,0 @@
-module Ordering
- class OnAddItemToBasket
- def initialize(event_store)
- @repository = Infra::AggregateRootRepository.new(event_store)
- end
-
- def call(command)
- @repository.with_aggregate(Order, command.aggregate_id) do |order|
- order.add_item(command.product_id)
- end
- end
- end
-
- class OnRemoveItemFromBasket
- def initialize(event_store)
- @repository = Infra::AggregateRootRepository.new(event_store)
- end
-
- def call(command)
- @repository.with_aggregate(Order, command.aggregate_id) do |order|
- order.remove_item(command.product_id)
- end
- end
- end
-
- class OnSetOrderAsExpired
- def initialize(event_store)
- @repository = Infra::AggregateRootRepository.new(event_store)
- end
-
- def call(command)
- @repository.with_aggregate(Order, command.aggregate_id) do |order|
- order.expire
- end
- end
- end
-
- class OnSubmitOrder
- def initialize(event_store, number_generator)
- @repository = Infra::AggregateRootRepository.new(event_store)
- @number_generator = number_generator
- end
-
- def call(command)
- @repository.with_aggregate(Order, command.aggregate_id) do |order|
- order_number = @number_generator.call
- order.submit(order_number)
- end
- end
- end
-
- class OnAcceptOrder
- def initialize(event_store)
- @repository = Infra::AggregateRootRepository.new(event_store)
- end
-
- def call(command)
- @repository.with_aggregate(Order, command.aggregate_id) do |order|
- order.accept
- end
- end
- end
-
- class OnRejectOrder
- def initialize(event_store)
- @repository = Infra::AggregateRootRepository.new(event_store)
- end
-
- def call(command)
- @repository.with_aggregate(Order, command.aggregate_id) do |order|
- order.reject
- end
- end
- end
-end
diff --git a/ecommerce/ordering/test/accept_order_test.rb b/ecommerce/ordering/test/accept_order_test.rb
deleted file mode 100644
index 331343ee9..000000000
--- a/ecommerce/ordering/test/accept_order_test.rb
+++ /dev/null
@@ -1,54 +0,0 @@
-require_relative "test_helper"
-
-module Ordering
- class AcceptOrderTest < Test
- cover "Ordering::OnAcceptOrder*"
-
- def test_order_gets_accepted
- aggregate_id = SecureRandom.uuid
- stream = "Ordering::Order$#{aggregate_id}"
- customer_id = SecureRandom.uuid
- product_id = SecureRandom.uuid
- order_number = FakeNumberGenerator::FAKE_NUMBER
- arrange(
- AddItemToBasket.new(
- order_id: aggregate_id,
- product_id: product_id
- ),
- SubmitOrder.new(
- order_id: aggregate_id,
- customer_id: customer_id
- )
- )
-
- assert_events(
- stream,
- OrderPlaced.new(
- data: {
- order_id: aggregate_id,
- order_number: order_number,
- order_lines: { product_id => 1 }
- }
- )
- ) do
- act(AcceptOrder.new(order_id: aggregate_id))
- end
- end
-
- def test_order_must_be_submitted_to_get_accepted
- aggregate_id = SecureRandom.uuid
- product_id = SecureRandom.uuid
-
- arrange(
- AddItemToBasket.new(
- order_id: aggregate_id,
- product_id: product_id
- )
- )
-
- assert_raises(Order::InvalidState) do
- act(AcceptOrder.new(order_id: aggregate_id))
- end
- end
- end
-end
diff --git a/ecommerce/ordering/test/add_item_to_basket_test.rb b/ecommerce/ordering/test/add_item_to_basket_test.rb
deleted file mode 100644
index d33ae672c..000000000
--- a/ecommerce/ordering/test/add_item_to_basket_test.rb
+++ /dev/null
@@ -1,49 +0,0 @@
-require_relative "test_helper"
-
-module Ordering
- class AddItemToBasketTest < Test
- cover "Ordering::OnAddItemToBasket*"
-
- def test_item_is_added_to_draft_order
- aggregate_id = SecureRandom.uuid
- stream = "Ordering::Order$#{aggregate_id}"
- product_id = SecureRandom.uuid
-
- expected_events = [
- ItemAddedToBasket.new(
- data: {
- order_id: aggregate_id,
- product_id: product_id,
- }
- )
- ]
- assert_events(stream, *expected_events) do
- act(
- AddItemToBasket.new(
- order_id: aggregate_id,
- product_id: product_id
- )
- )
- end
- end
-
- def test_no_add_allowed_to_submitted_order
- aggregate_id = SecureRandom.uuid
- customer_id = SecureRandom.uuid
- product_id = SecureRandom.uuid
- order_number = FakeNumberGenerator::FAKE_NUMBER
-
- arrange(
- AddItemToBasket.new(order_id: aggregate_id, product_id: product_id),
- SubmitOrder.new(
- order_id: aggregate_id,
- order_number: order_number,
- customer_id: customer_id
- )
- )
- assert_raises(Order::AlreadySubmitted) do
- act(AddItemToBasket.new(order_id: aggregate_id, product_id: product_id))
- end
- end
- end
-end
diff --git a/ecommerce/ordering/test/number_generator_test.rb b/ecommerce/ordering/test/number_generator_test.rb
deleted file mode 100644
index 3d9c49de8..000000000
--- a/ecommerce/ordering/test/number_generator_test.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-require_relative "test_helper"
-
-module Ordering
- class NumberGeneratorTest < Test
- def test_includes_year
- assert_includes(NumberGenerator.new.call, "#{Time.now.year}")
- end
-
- def test_includes_month
- assert_includes(NumberGenerator.new.call, "#{Time.now.month}")
- end
- end
-end
diff --git a/ecommerce/ordering/test/order_test.rb b/ecommerce/ordering/test/order_test.rb
deleted file mode 100644
index b398e0ae7..000000000
--- a/ecommerce/ordering/test/order_test.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-require_relative "test_helper"
-
-module Ordering
- class OrderTest < Test
- cover "Ordering::Order"
-
- def setup
- super
- @order_id = SecureRandom.uuid
- @product_id = SecureRandom.uuid
- @customer_id = SecureRandom.uuid
- end
-
- def test_order_lines_are_empty_after_adding_and_removing
- order = Order.new(@order_id)
- order.add_item(@product_id)
- order.remove_item(@product_id)
- order.submit(NumberGenerator.new.call)
- assert_equal({}, order.unpublished_events.to_a.last.data[:order_lines])
- end
-
- def test_order_lines_with_the_same_product_twice
- order = Order.new(@order_id)
- order.add_item(@product_id)
- order.add_item(@product_id)
- order.submit(NumberGenerator.new.call)
- assert_equal({ @product_id => 2 }, order.unpublished_events.to_a.last.data[:order_lines])
- end
-
- def test_order_lines_after_adding_twice_and_remove_once
- order = Order.new(@order_id)
- order.add_item(@product_id)
- order.add_item(@product_id)
- order.remove_item(@product_id)
- order.submit(NumberGenerator.new.call)
- assert_equal({ @product_id => 1 }, order.unpublished_events.to_a.last.data[:order_lines])
- end
- end
-end
diff --git a/ecommerce/ordering/test/reject_order_test.rb b/ecommerce/ordering/test/reject_order_test.rb
deleted file mode 100644
index bc813772d..000000000
--- a/ecommerce/ordering/test/reject_order_test.rb
+++ /dev/null
@@ -1,52 +0,0 @@
-require_relative "test_helper"
-
-module Ordering
- class RejectOrderTest < Test
- cover "Ordering::OnRejectOrder*"
-
- def test_order_gets_rejected
- aggregate_id = SecureRandom.uuid
- stream = "Ordering::Order$#{aggregate_id}"
- customer_id = SecureRandom.uuid
- product_id = SecureRandom.uuid
-
- arrange(
- AddItemToBasket.new(
- order_id: aggregate_id,
- product_id: product_id
- ),
- SubmitOrder.new(
- order_id: aggregate_id,
- customer_id: customer_id
- )
- )
-
- assert_events(
- stream,
- OrderRejected.new(
- data: {
- order_id: aggregate_id
- }
- )
- ) do
- act(RejectOrder.new(order_id: aggregate_id))
- end
- end
-
- def test_order_must_be_submitted_to_get_rejected
- aggregate_id = SecureRandom.uuid
- product_id = SecureRandom.uuid
-
- arrange(
- AddItemToBasket.new(
- order_id: aggregate_id,
- product_id: product_id
- )
- )
-
- assert_raises(Order::InvalidState) do
- act(RejectOrder.new(order_id: aggregate_id))
- end
- end
- end
-end
diff --git a/ecommerce/ordering/test/remove_item_from_basket_test.rb b/ecommerce/ordering/test/remove_item_from_basket_test.rb
deleted file mode 100644
index 2605eec69..000000000
--- a/ecommerce/ordering/test/remove_item_from_basket_test.rb
+++ /dev/null
@@ -1,53 +0,0 @@
-require_relative "test_helper"
-
-module Ordering
- class RemoveItemFromBasketTest < Test
- cover "Ordering::OnRemoveItemFromBasket*"
-
- def test_item_is_removed_from_draft_order
- aggregate_id = SecureRandom.uuid
- stream = "Ordering::Order$#{aggregate_id}"
-
- product_id = SecureRandom.uuid
-
- arrange(
- AddItemToBasket.new(
- order_id: aggregate_id,
- product_id: product_id
- )
- )
- expected_events = [
- ItemRemovedFromBasket.new(
- data: {
- order_id: aggregate_id,
- product_id: product_id
- }
- )
- ]
- assert_events(stream, *expected_events) do
- act(
- RemoveItemFromBasket.new(
- order_id: aggregate_id,
- product_id: product_id
- )
- )
- end
- end
-
- def test_no_remove_allowed_to_created_order
- aggregate_id = SecureRandom.uuid
- customer_id = SecureRandom.uuid
- product_id = SecureRandom.uuid
- order_number = FakeNumberGenerator::FAKE_NUMBER
-
- arrange(
- AddItemToBasket.new(order_id: aggregate_id, product_id: product_id),
- SubmitOrder.new(order_id: aggregate_id, order_number: order_number, customer_id: customer_id)
- )
-
- assert_raises(Order::AlreadySubmitted) do
- act(RemoveItemFromBasket.new(order_id: aggregate_id, product_id: product_id))
- end
- end
- end
-end
diff --git a/ecommerce/ordering/test/set_order_as_expired_test.rb b/ecommerce/ordering/test/set_order_as_expired_test.rb
deleted file mode 100644
index 33d6cb410..000000000
--- a/ecommerce/ordering/test/set_order_as_expired_test.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-require_relative "test_helper"
-
-module Ordering
- class SetOrderAsExpiredTest < Test
- cover "Ordering::OnSetOrderAsExpired*"
-
- def test_draft_order_will_expire
- aggregate_id = SecureRandom.uuid
- stream = "Ordering::Order$#{aggregate_id}"
- product_id = SecureRandom.uuid
-
- arrange(
- AddItemToBasket.new(
- order_id: aggregate_id,
- product_id: product_id
- )
- )
-
- assert_events(
- stream,
- OrderExpired.new(data: { order_id: aggregate_id })
- ) { act(SetOrderAsExpired.new(order_id: aggregate_id)) }
- end
-
- def test_submitted_order_will_not_expire
- aggregate_id = SecureRandom.uuid
- stream = "Ordering::Order$#{aggregate_id}"
- product_id = SecureRandom.uuid
- customer_id = SecureRandom.uuid
-
- arrange(
- AddItemToBasket.new(
- order_id: aggregate_id,
- product_id: product_id
- ),
- SubmitOrder.new(
- order_id: aggregate_id,
- order_number: "2018/12/1",
- customer_id: customer_id
- )
- )
-
- assert_raises(Order::AlreadySubmitted) { act(SetOrderAsExpired.new(order_id: aggregate_id)) }
- end
- end
-end
diff --git a/ecommerce/ordering/test/submit_order_test.rb b/ecommerce/ordering/test/submit_order_test.rb
deleted file mode 100644
index a9a579596..000000000
--- a/ecommerce/ordering/test/submit_order_test.rb
+++ /dev/null
@@ -1,75 +0,0 @@
-require_relative "test_helper"
-
-module Ordering
- class SubmitOrderTest < Test
- cover "Ordering::OnSubmitOrder*"
-
- def test_order_is_submitted
- aggregate_id = SecureRandom.uuid
- stream = "Ordering::Order$#{aggregate_id}"
- product_id = SecureRandom.uuid
- order_number = FakeNumberGenerator::FAKE_NUMBER
- arrange(
- AddItemToBasket.new(
- order_id: aggregate_id,
- product_id: product_id
- )
- )
-
- assert_events(
- stream,
- OrderSubmitted.new(
- data: {
- order_id: aggregate_id,
- order_number: order_number,
- order_lines: { product_id => 1 }
- }
- )
- ) do
- act(SubmitOrder.new(order_id: aggregate_id))
- end
- end
-
- def test_already_created_order_could_not_be_created_again
- aggregate_id = SecureRandom.uuid
- product_id = SecureRandom.uuid
- order_number = FakeNumberGenerator::FAKE_NUMBER
-
- arrange(
- AddItemToBasket.new(
- order_id: aggregate_id,
- product_id: product_id
- ),
- SubmitOrder.new(
- order_id: aggregate_id,
- order_number: order_number,
- )
- )
-
- assert_raises(Order::AlreadySubmitted) do
- act(
- SubmitOrder.new(
- order_id: aggregate_id
- )
- )
- end
- end
-
- def test_expired_order_could_not_be_created
- aggregate_id = SecureRandom.uuid
- product_id = SecureRandom.uuid
-
- arrange(
- AddItemToBasket.new(
- order_id: aggregate_id,
- product_id: product_id
- ),
- SetOrderAsExpired.new(order_id: aggregate_id)
- )
-
- assert_raises(Order::OrderHasExpired) do
- act(SubmitOrder.new(order_id: aggregate_id))
- end
- end
- end
-end
diff --git a/ecommerce/ordering/test/test_helper.rb b/ecommerce/ordering/test/test_helper.rb
deleted file mode 100644
index 9097c977d..000000000
--- a/ecommerce/ordering/test/test_helper.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-require "minitest/autorun"
-require "mutant/minitest/coverage"
-
-require_relative "../lib/ordering"
-
-module Ordering
- class Test < Infra::InMemoryTest
- def before_setup
- super
- number_generator = FakeNumberGenerator.new
- Configuration.new(-> { number_generator }).call(event_store, command_bus)
- end
- end
-end
diff --git a/ecommerce/payments/.mutant.yml b/ecommerce/payments/.mutant.yml
deleted file mode 100644
index 68e0d18f9..000000000
--- a/ecommerce/payments/.mutant.yml
+++ /dev/null
@@ -1,12 +0,0 @@
-requires:
- - ./test/test_helper
-integration: minitest
-coverage_criteria:
- process_abort: true
-matcher:
- subjects:
- - Payments*
- ignore:
- - Payments::Payment#authorized?
- - Payments::Test*
- - Payments::Configuration#call
diff --git a/ecommerce/payments/Gemfile b/ecommerce/payments/Gemfile
deleted file mode 100644
index 00fef6d3c..000000000
--- a/ecommerce/payments/Gemfile
+++ /dev/null
@@ -1,4 +0,0 @@
-source "https://rubygems.org"
-
-eval_gemfile "../../infra/Gemfile.test"
-gem "infra", path: "../../infra"
diff --git a/ecommerce/payments/Gemfile.lock b/ecommerce/payments/Gemfile.lock
deleted file mode 100644
index 4a73abff2..000000000
--- a/ecommerce/payments/Gemfile.lock
+++ /dev/null
@@ -1,119 +0,0 @@
-PATH
- remote: ../../infra
- specs:
- infra (1.0.0)
- aggregate_root (~> 2.13)
- arkency-command_bus
- dry-struct
- dry-types
- rake
- ruby_event_store (~> 2.13)
- ruby_event_store-transformations
- sidekiq
-
-GEM
- remote: https://oss:7AXfeZdAfCqL1PvHm2nvDJO6Zd9UW8IK@gem.mutant.dev/
- specs:
- mutant-license (0.1.1.2.1627430819213747598431630701693729869473.6)
-
-GEM
- remote: https://rubygems.org/
- specs:
- activesupport (7.1.2)
- base64
- bigdecimal
- concurrent-ruby (~> 1.0, >= 1.0.2)
- connection_pool (>= 2.2.5)
- drb
- i18n (>= 1.6, < 2)
- minitest (>= 5.1)
- mutex_m
- tzinfo (~> 2.0)
- aggregate_root (2.13.0)
- ruby_event_store (= 2.13.0)
- arkency-command_bus (0.4.1)
- concurrent-ruby
- ast (2.4.2)
- base64 (0.2.0)
- bigdecimal (3.1.4)
- concurrent-ruby (1.2.2)
- connection_pool (2.4.1)
- diff-lcs (1.5.0)
- drb (2.2.0)
- ruby2_keywords
- dry-core (1.0.1)
- concurrent-ruby (~> 1.0)
- zeitwerk (~> 2.6)
- dry-inflector (1.0.0)
- dry-logic (1.5.0)
- concurrent-ruby (~> 1.0)
- dry-core (~> 1.0, < 2)
- zeitwerk (~> 2.6)
- dry-struct (1.6.0)
- dry-core (~> 1.0, < 2)
- dry-types (>= 1.7, < 2)
- ice_nine (~> 0.11)
- zeitwerk (~> 2.6)
- dry-types (1.7.1)
- concurrent-ruby (~> 1.0)
- dry-core (~> 1.0)
- dry-inflector (~> 1.0)
- dry-logic (~> 1.4)
- zeitwerk (~> 2.6)
- i18n (1.14.1)
- concurrent-ruby (~> 1.0)
- ice_nine (0.11.2)
- minitest (5.15.0)
- mutant (0.11.26)
- diff-lcs (~> 1.3)
- parser (~> 3.2.2, >= 3.2.2.4)
- regexp_parser (~> 2.8.2)
- sorbet-runtime (~> 0.5.0)
- unparser (~> 0.6.9)
- mutant-minitest (0.11.26)
- minitest (~> 5.11)
- mutant (= 0.11.26)
- mutex_m (0.2.0)
- parser (3.2.2.4)
- ast (~> 2.4.1)
- racc
- racc (1.7.3)
- rack (3.0.8)
- rake (13.1.0)
- redis-client (0.19.0)
- connection_pool
- regexp_parser (2.8.3)
- ruby2_keywords (0.0.5)
- ruby_event_store (2.13.0)
- concurrent-ruby (~> 1.0, >= 1.1.6)
- ruby_event_store-transformations (0.1.0)
- activesupport (>= 5.0)
- ruby_event_store (>= 2.0.0, < 3.0.0)
- sidekiq (7.2.0)
- concurrent-ruby (< 2)
- connection_pool (>= 2.3.0)
- rack (>= 2.2.4)
- redis-client (>= 0.14.0)
- sorbet-runtime (0.5.11190)
- tzinfo (2.0.6)
- concurrent-ruby (~> 1.0)
- unparser (0.6.12)
- diff-lcs (~> 1.3)
- parser (>= 3.2.2.4)
- zeitwerk (2.6.12)
-
-PLATFORMS
- arm64-darwin-20
- arm64-darwin-21
- ruby
- x86_64-darwin-20
- x86_64-linux
-
-DEPENDENCIES
- infra!
- minitest (= 5.15.0)!
- mutant-license!
- mutant-minitest (= 0.11.26)!
-
-BUNDLED WITH
- 2.5.9
diff --git a/ecommerce/payments/Makefile b/ecommerce/payments/Makefile
deleted file mode 100644
index 23850fab1..000000000
--- a/ecommerce/payments/Makefile
+++ /dev/null
@@ -1,10 +0,0 @@
-install:
- @bundle install
-
-test:
- @bundle exec ruby -e "require \"rake/rake_test_loader\"" test/*_test.rb
-
-mutate:
- @RAILS_ENV=test bundle exec mutant run
-
-.PHONY: install test mutate
diff --git a/ecommerce/payments/README.md b/ecommerce/payments/README.md
deleted file mode 100644
index 4dfa6901f..000000000
--- a/ecommerce/payments/README.md
+++ /dev/null
@@ -1,17 +0,0 @@
-# Payments
-
-[](https://github.com/RailsEventStore/cqrs-es-sample-with-res/actions/workflows/payments.yml)
-
-The `Payments::Payment` aggregate manages the following states:
-
-- authorized
-- captured
-- released
-
-This Payment object is fully event sourced.
-
-#### Up and running
-
-```
-make install test mutate
-```
diff --git a/ecommerce/payments/lib/payments.rb b/ecommerce/payments/lib/payments.rb
deleted file mode 100644
index bd6eb54de..000000000
--- a/ecommerce/payments/lib/payments.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-require "infra"
-require_relative "payments/commands"
-require_relative "payments/events"
-require_relative "payments/on_set_payment_amount"
-require_relative "payments/on_authorize_payment"
-require_relative "payments/on_capture_payment"
-require_relative "payments/on_release_payment"
-require_relative "payments/fake_gateway"
-require_relative "payments/payment"
-
-module Payments
- class Configuration
- def initialize(gateway)
- @gateway = gateway
- end
-
- def call(event_store, command_bus)
- command_bus.register(
- AuthorizePayment,
- OnAuthorizePayment.new(event_store, @gateway)
- )
- command_bus.register(CapturePayment, OnCapturePayment.new(event_store))
- command_bus.register(ReleasePayment, OnReleasePayment.new(event_store))
- command_bus.register(SetPaymentAmount, OnSetPaymentAmount.new(event_store))
- end
- end
-end
diff --git a/ecommerce/payments/lib/payments/commands.rb b/ecommerce/payments/lib/payments/commands.rb
deleted file mode 100644
index e6c1e086d..000000000
--- a/ecommerce/payments/lib/payments/commands.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-module Payments
- class SetPaymentAmount < Infra::Command
- attribute :order_id, Infra::Types::UUID
- attribute :amount, Infra::Types::Nominal::Decimal
- end
-
- class AuthorizePayment < Infra::Command
- attribute :order_id, Infra::Types::UUID
- end
-
- class CapturePayment < Infra::Command
- attribute :order_id, Infra::Types::UUID
- end
-
- class ReleasePayment < Infra::Command
- attribute :order_id, Infra::Types::UUID
- end
-end
diff --git a/ecommerce/payments/lib/payments/events.rb b/ecommerce/payments/lib/payments/events.rb
deleted file mode 100644
index 322474e7d..000000000
--- a/ecommerce/payments/lib/payments/events.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-module Payments
- class PaymentAmountSet < Infra::Event
- attribute :order_id, Infra::Types::UUID
- attribute :amount, Infra::Types::Nominal::Decimal
- end
-
- class PaymentAuthorized < Infra::Event
- attribute :order_id, Infra::Types::UUID
- end
-
- class PaymentCaptured < Infra::Event
- attribute :order_id, Infra::Types::UUID
- end
-
- class PaymentReleased < Infra::Event
- attribute :order_id, Infra::Types::UUID
- end
-end
diff --git a/ecommerce/payments/lib/payments/fake_gateway.rb b/ecommerce/payments/lib/payments/fake_gateway.rb
deleted file mode 100644
index ba71d6b7e..000000000
--- a/ecommerce/payments/lib/payments/fake_gateway.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-module Payments
- class FakeGateway
- def initialize
- @authorized_transactions = []
- end
-
- def reset
- @authorized_transactions = []
- end
-
- def authorize_transaction(transaction_id, amount)
- authorized_transactions << [transaction_id, amount]
- end
-
- def authorized_transactions
- @authorized_transactions
- end
- end
-end
diff --git a/ecommerce/payments/lib/payments/on_authorize_payment.rb b/ecommerce/payments/lib/payments/on_authorize_payment.rb
deleted file mode 100644
index 182112e68..000000000
--- a/ecommerce/payments/lib/payments/on_authorize_payment.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-module Payments
- class OnAuthorizePayment
- def initialize(event_store, gateway)
- @repository = AggregateRoot::Repository.new(event_store)
- @gateway = gateway
- end
-
- def call(command)
- @repository.with_aggregate(
- Payment.new,
- "Payments::Payment$#{command.order_id}"
- ) { |payment| payment.authorize(command.order_id, @gateway.call) }
- end
- end
-end
diff --git a/ecommerce/payments/lib/payments/on_capture_payment.rb b/ecommerce/payments/lib/payments/on_capture_payment.rb
deleted file mode 100644
index 4fbe70cbf..000000000
--- a/ecommerce/payments/lib/payments/on_capture_payment.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-module Payments
- class OnCapturePayment
- def initialize(event_store)
- @repository = AggregateRoot::Repository.new(event_store)
- end
-
- def call(command)
- @repository.with_aggregate(
- Payment.new,
- "Payments::Payment$#{command.order_id}"
- ) { |payment| payment.capture }
- end
- end
-end
diff --git a/ecommerce/payments/lib/payments/on_release_payment.rb b/ecommerce/payments/lib/payments/on_release_payment.rb
deleted file mode 100644
index 15a3999c6..000000000
--- a/ecommerce/payments/lib/payments/on_release_payment.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-module Payments
- class OnReleasePayment
- def initialize(event_store)
- @repository = AggregateRoot::Repository.new(event_store)
- end
-
- def call(command)
- @repository.with_aggregate(
- Payment.new,
- "Payments::Payment$#{command.order_id}"
- ) { |payment| payment.release }
- end
- end
-end
diff --git a/ecommerce/payments/lib/payments/on_set_payment_amount.rb b/ecommerce/payments/lib/payments/on_set_payment_amount.rb
deleted file mode 100644
index 25f528c59..000000000
--- a/ecommerce/payments/lib/payments/on_set_payment_amount.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-module Payments
- class OnSetPaymentAmount
- def initialize(event_store)
- @repository = AggregateRoot::Repository.new(event_store)
- end
-
- def call(command)
- @repository.with_aggregate(
- Payment.new,
- "Payments::Payment$#{command.order_id}"
- ) { |payment| payment.set_amount(command.order_id, command.amount) }
- end
- end
-end
diff --git a/ecommerce/payments/lib/payments/payment.rb b/ecommerce/payments/lib/payments/payment.rb
deleted file mode 100644
index 2c43716ef..000000000
--- a/ecommerce/payments/lib/payments/payment.rb
+++ /dev/null
@@ -1,64 +0,0 @@
-module Payments
- class Payment
- include AggregateRoot
-
- AlreadyAuthorized = Class.new(StandardError)
- NotAuthorized = Class.new(StandardError)
- AlreadyCaptured = Class.new(StandardError)
- AlreadyReleased = Class.new(StandardError)
-
- def set_amount(order_id, amount)
- apply(PaymentAmountSet.new(data: { order_id: order_id, amount: amount }))
- end
-
- def authorize(order_id, gateway)
- raise AlreadyAuthorized if authorized?
- gateway.authorize_transaction(order_id, @amount)
- apply(PaymentAuthorized.new(data: { order_id: order_id }))
- end
-
- def capture
- raise AlreadyCaptured if captured?
- raise NotAuthorized unless authorized?
- apply(PaymentCaptured.new(data: { order_id: @order_id }))
- end
-
- def release
- raise AlreadyReleased if released?
- raise AlreadyCaptured if captured?
- raise NotAuthorized unless authorized?
- apply(PaymentReleased.new(data: { order_id: @order_id }))
- end
-
- private
-
- on PaymentAmountSet do |event|
- @amount = event.data.fetch(:amount)
- end
-
- on PaymentAuthorized do |event|
- @state = :authorized
- @order_id = event.data.fetch(:order_id)
- end
-
- on PaymentCaptured do |event|
- @state = :captured
- end
-
- on PaymentReleased do |event|
- @state = :released
- end
-
- def authorized?
- @state.equal?(:authorized)
- end
-
- def captured?
- @state.equal?(:captured)
- end
-
- def released?
- @state.equal?(:released)
- end
- end
-end
diff --git a/ecommerce/payments/test/fake_gateway_test.rb b/ecommerce/payments/test/fake_gateway_test.rb
deleted file mode 100644
index 4010c5bd9..000000000
--- a/ecommerce/payments/test/fake_gateway_test.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-require_relative "test_helper"
-
-module Payments
- class FakeGatewayTest < Test
- cover "Payments::FakeGateway*"
-
- def test_happy_path
- gateway = FakeGateway.new
- gateway.authorize_transaction("12", 20)
- assert_equal([["12", 20]], gateway.authorized_transactions)
- gateway.reset
- assert_equal([], gateway.authorized_transactions)
- end
- end
-end
diff --git a/ecommerce/payments/test/on_capture_payment_test.rb b/ecommerce/payments/test/on_capture_payment_test.rb
deleted file mode 100644
index 9ebca5ea0..000000000
--- a/ecommerce/payments/test/on_capture_payment_test.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-require_relative "test_helper"
-
-module Payments
- class OnCapturePaymentTest < Test
- cover "Payments::OnCapturePayment*"
-
- def test_capture_payment
- order_id = SecureRandom.uuid
- stream = "Payments::Payment$#{order_id}"
-
- arrange(
- SetPaymentAmount.new(order_id: order_id, amount: 20),
- AuthorizePayment.new(order_id: order_id)
- )
-
- assert_equal(20, payment_gateway.authorized_transactions[0][1])
-
- assert_events(
- stream,
- PaymentCaptured.new(data: { order_id: order_id })
- ) { act(CapturePayment.new(order_id: order_id)) }
- end
- end
-end
diff --git a/ecommerce/payments/test/on_release_payment_test.rb b/ecommerce/payments/test/on_release_payment_test.rb
deleted file mode 100644
index 5f5bf3f89..000000000
--- a/ecommerce/payments/test/on_release_payment_test.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-require_relative "test_helper"
-
-module Payments
- class OnReleasePaymentTest < Test
- cover "Payments::OnReleasePayment*"
-
- def test_capture_payment
- order_id = SecureRandom.uuid
- stream = "Payments::Payment$#{order_id}"
-
- arrange(
- AuthorizePayment.new(order_id: order_id)
- )
-
- assert_events(
- stream,
- PaymentReleased.new(data: { order_id: order_id })
- ) { act(ReleasePayment.new(order_id: order_id)) }
- end
- end
-end
diff --git a/ecommerce/payments/test/payment_test.rb b/ecommerce/payments/test/payment_test.rb
deleted file mode 100644
index 3dfd77f6b..000000000
--- a/ecommerce/payments/test/payment_test.rb
+++ /dev/null
@@ -1,103 +0,0 @@
-require_relative "test_helper"
-
-module Payments
- class PaymentTest < Test
- cover "Payments::Payment*"
-
- def test_authorize_publishes_event
- payment = Payment.new
- gateway = FakeGateway.new
- payment.set_amount(order_id, 20)
- payment.authorize(order_id, gateway)
- assert_changes(
- payment.unpublished_events,
- [
- PaymentAmountSet.new(data: { order_id: order_id, amount: 20 }),
- PaymentAuthorized.new(data: { order_id: order_id })
- ]
- )
- end
-
- def test_authorize_contacts_gateway
- payment = Payment.new
- gateway = FakeGateway.new
- payment.set_amount(order_id, 20)
- payment.authorize(order_id, gateway)
- assert(gateway.authorized_transactions.include?([order_id, 20]))
- end
-
- def test_should_not_allow_for_double_authorization
- assert_raises(Payment::AlreadyAuthorized) do
- authorized_payment.authorize(order_id, nil)
- end
- end
-
- def test_should_capture_authorized_payment
- payment = authorized_payment
- before = payment.unpublished_events.to_a
-
- payment.capture
- actual = payment.unpublished_events.to_a - before
- assert_changes(
- actual,
- [PaymentCaptured.new(data: { order_id: order_id })]
- )
- end
-
- def test_must_not_capture_not_authorized_payment
- assert_raises(Payment::NotAuthorized) { Payment.new.capture }
- end
-
- def test_should_not_allow_for_double_capture
- assert_raises(Payment::AlreadyCaptured) { captured_payment.capture }
- end
-
- def test_authorization_could_be_released
- payment = authorized_payment
- before = payment.unpublished_events.to_a
-
- payment.release
- actual = payment.unpublished_events.to_a - before
- assert_changes(
- actual,
- [PaymentReleased.new(data: { order_id: order_id })]
- )
- end
-
- def test_must_not_release_not_captured_payment
- assert_raises(Payment::AlreadyCaptured) { captured_payment.release }
- end
-
- def test_must_not_release_not_authorized_payment
- assert_raises(Payment::NotAuthorized) { Payment.new.release }
- end
-
- def test_should_not_allow_for_double_release
- assert_raises(Payment::AlreadyReleased) { released_payment.release }
- end
-
- private
-
- def order_id
- @order_id ||= SecureRandom.uuid
- end
-
- def authorized_payment
- Payment.new.tap do |payment|
- payment.apply(PaymentAuthorized.new(data: { order_id: order_id }))
- end
- end
-
- def captured_payment
- authorized_payment.tap do |payment|
- payment.apply(PaymentCaptured.new(data: { order_id: order_id }))
- end
- end
-
- def released_payment
- captured_payment.tap do |payment|
- payment.apply(PaymentReleased.new(data: { order_id: order_id }))
- end
- end
- end
-end
diff --git a/ecommerce/payments/test/test_helper.rb b/ecommerce/payments/test/test_helper.rb
deleted file mode 100644
index 5e21bf0ce..000000000
--- a/ecommerce/payments/test/test_helper.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-require "minitest/autorun"
-require "mutant/minitest/coverage"
-
-require_relative "../lib/payments"
-
-module Payments
- class Test < Infra::InMemoryTest
- attr_reader :payment_gateway
-
- def before_setup
- super
- @payment_gateway = FakeGateway.new
- Configuration.new(-> { @payment_gateway }).call(event_store, command_bus)
- end
- end
-end
diff --git a/ecommerce/pricing/.mutant.yml b/ecommerce/pricing/.mutant.yml
deleted file mode 100644
index 26bd2ad34..000000000
--- a/ecommerce/pricing/.mutant.yml
+++ /dev/null
@@ -1,13 +0,0 @@
-requires:
- - ./test/test_helper
-integration: minitest
-coverage_criteria:
- process_abort: true
-matcher:
- subjects:
- - Pricing*
- ignore:
- - Pricing::Configuration*
- - Pricing::Test*
- - Pricing::OnCalculateTotalValue#call
- - Pricing::OnCalculateTotalValue#calculate_sub_amounts
\ No newline at end of file
diff --git a/ecommerce/pricing/Gemfile b/ecommerce/pricing/Gemfile
deleted file mode 100644
index acd69f632..000000000
--- a/ecommerce/pricing/Gemfile
+++ /dev/null
@@ -1,8 +0,0 @@
-source "https://rubygems.org"
-
-eval_gemfile "../../infra/Gemfile.test"
-gem "infra", path: "../../infra"
-
-group :test do
- gem "timecop"
-end
diff --git a/ecommerce/pricing/Gemfile.lock b/ecommerce/pricing/Gemfile.lock
deleted file mode 100644
index d7e4ebf47..000000000
--- a/ecommerce/pricing/Gemfile.lock
+++ /dev/null
@@ -1,121 +0,0 @@
-PATH
- remote: ../../infra
- specs:
- infra (1.0.0)
- aggregate_root (~> 2.13)
- arkency-command_bus
- dry-struct
- dry-types
- rake
- ruby_event_store (~> 2.13)
- ruby_event_store-transformations
- sidekiq
-
-GEM
- remote: https://oss:7AXfeZdAfCqL1PvHm2nvDJO6Zd9UW8IK@gem.mutant.dev/
- specs:
- mutant-license (0.1.1.2.1627430819213747598431630701693729869473.6)
-
-GEM
- remote: https://rubygems.org/
- specs:
- activesupport (7.1.2)
- base64
- bigdecimal
- concurrent-ruby (~> 1.0, >= 1.0.2)
- connection_pool (>= 2.2.5)
- drb
- i18n (>= 1.6, < 2)
- minitest (>= 5.1)
- mutex_m
- tzinfo (~> 2.0)
- aggregate_root (2.13.0)
- ruby_event_store (= 2.13.0)
- arkency-command_bus (0.4.1)
- concurrent-ruby
- ast (2.4.2)
- base64 (0.2.0)
- bigdecimal (3.1.4)
- concurrent-ruby (1.2.2)
- connection_pool (2.4.1)
- diff-lcs (1.5.0)
- drb (2.2.0)
- ruby2_keywords
- dry-core (1.0.1)
- concurrent-ruby (~> 1.0)
- zeitwerk (~> 2.6)
- dry-inflector (1.0.0)
- dry-logic (1.5.0)
- concurrent-ruby (~> 1.0)
- dry-core (~> 1.0, < 2)
- zeitwerk (~> 2.6)
- dry-struct (1.6.0)
- dry-core (~> 1.0, < 2)
- dry-types (>= 1.7, < 2)
- ice_nine (~> 0.11)
- zeitwerk (~> 2.6)
- dry-types (1.7.1)
- concurrent-ruby (~> 1.0)
- dry-core (~> 1.0)
- dry-inflector (~> 1.0)
- dry-logic (~> 1.4)
- zeitwerk (~> 2.6)
- i18n (1.14.1)
- concurrent-ruby (~> 1.0)
- ice_nine (0.11.2)
- minitest (5.15.0)
- mutant (0.11.26)
- diff-lcs (~> 1.3)
- parser (~> 3.2.2, >= 3.2.2.4)
- regexp_parser (~> 2.8.2)
- sorbet-runtime (~> 0.5.0)
- unparser (~> 0.6.9)
- mutant-minitest (0.11.26)
- minitest (~> 5.11)
- mutant (= 0.11.26)
- mutex_m (0.2.0)
- parser (3.2.2.4)
- ast (~> 2.4.1)
- racc
- racc (1.7.3)
- rack (3.0.8)
- rake (13.1.0)
- redis-client (0.19.0)
- connection_pool
- regexp_parser (2.8.3)
- ruby2_keywords (0.0.5)
- ruby_event_store (2.13.0)
- concurrent-ruby (~> 1.0, >= 1.1.6)
- ruby_event_store-transformations (0.1.0)
- activesupport (>= 5.0)
- ruby_event_store (>= 2.0.0, < 3.0.0)
- sidekiq (7.2.0)
- concurrent-ruby (< 2)
- connection_pool (>= 2.3.0)
- rack (>= 2.2.4)
- redis-client (>= 0.14.0)
- sorbet-runtime (0.5.11190)
- timecop (0.9.8)
- tzinfo (2.0.6)
- concurrent-ruby (~> 1.0)
- unparser (0.6.12)
- diff-lcs (~> 1.3)
- parser (>= 3.2.2.4)
- zeitwerk (2.6.12)
-
-PLATFORMS
- arm64-darwin-20
- arm64-darwin-21
- ruby
- x86_64-darwin-20
- x86_64-linux
-
-DEPENDENCIES
- infra!
- minitest (= 5.15.0)!
- mutant-license!
- mutant-minitest (= 0.11.26)!
- timecop
-
-BUNDLED WITH
- 2.5.9
diff --git a/ecommerce/pricing/Makefile b/ecommerce/pricing/Makefile
deleted file mode 100644
index 23850fab1..000000000
--- a/ecommerce/pricing/Makefile
+++ /dev/null
@@ -1,10 +0,0 @@
-install:
- @bundle install
-
-test:
- @bundle exec ruby -e "require \"rake/rake_test_loader\"" test/*_test.rb
-
-mutate:
- @RAILS_ENV=test bundle exec mutant run
-
-.PHONY: install test mutate
diff --git a/ecommerce/pricing/README.md b/ecommerce/pricing/README.md
deleted file mode 100644
index f323e315c..000000000
--- a/ecommerce/pricing/README.md
+++ /dev/null
@@ -1,19 +0,0 @@
-# Pricing
-
-[](https://github.com/RailsEventStore/cqrs-es-sample-with-res/actions/workflows/pricing.yml)
-
-The `Pricing::Coupon` aggregate manages the creation and usage of a coupon. This includes as of now:
-
-- registration
-
-After each successful action an appropriate event is published in the Coupon stream.
-
-| Command | Event | Service used to apply |
-|:---------------:|:-----:|:----------------------|
-| RegisterCoupon | CouponRegistered | OnCouponRegister |
-
-#### Up and running
-
-```
-make install test mutate
-```
diff --git a/ecommerce/pricing/lib/pricing.rb b/ecommerce/pricing/lib/pricing.rb
deleted file mode 100644
index 789b5e0f6..000000000
--- a/ecommerce/pricing/lib/pricing.rb
+++ /dev/null
@@ -1,109 +0,0 @@
-require "infra"
-require_relative "pricing/discounts"
-require_relative "pricing/coupon"
-require_relative "pricing/commands"
-require_relative "pricing/events"
-require_relative "pricing/services"
-require_relative "pricing/offer"
-require_relative "pricing/price_change"
-require_relative "pricing/pricing_catalog"
-require_relative "pricing/time_promotion"
-require_relative "pricing/promotions_calendar"
-require_relative "pricing/calculate_order_sub_amounts_value"
-require_relative "pricing/calculate_order_total_value"
-
-module Pricing
- def self.command_bus=(value)
- @command_bus = value
- end
-
- def self.command_bus
- @command_bus
- end
-
- def self.event_store=(value)
- @event_store = value
- end
-
- def self.event_store
- @event_store
- end
-
- class Configuration
- def call(event_store, command_bus)
- Pricing.event_store = event_store
- Pricing.command_bus = command_bus
-
- command_bus.register(
- AddPriceItem,
- OnAddItemToBasket.new(event_store)
- )
- command_bus.register(
- RemovePriceItem,
- OnRemoveItemFromBasket.new(event_store)
- )
- command_bus.register(
- SetPrice,
- SetPriceHandler.new(event_store)
- )
- command_bus.register(
- SetFuturePrice,
- SetFuturePriceHandler.new(event_store)
- )
- command_bus.register(
- CalculateTotalValue,
- OnCalculateTotalValue.new(event_store)
- )
- command_bus.register(
- CalculateSubAmounts,
- OnCalculateTotalValue.new(event_store).public_method(:calculate_sub_amounts)
- )
- command_bus.register(
- SetPercentageDiscount,
- SetPercentageDiscountHandler.new(event_store)
- )
- command_bus.register(
- ResetPercentageDiscount,
- ResetPercentageDiscountHandler.new(event_store)
- )
- command_bus.register(
- ChangePercentageDiscount,
- ChangePercentageDiscountHandler.new(event_store)
- )
- command_bus.register(
- RegisterCoupon,
- OnCouponRegister.new(event_store)
- )
- command_bus.register(
- CreateTimePromotion,
- CreateTimePromotionHandler.new(event_store)
- )
- command_bus.register(
- MakeProductFreeForOrder,
- MakeProductFreeForOrderHandler.new(event_store)
- )
- command_bus.register(
- RemoveFreeProductFromOrder,
- RemoveFreeProductFromOrderHandler.new(event_store)
- )
- event_store.subscribe(CalculateOrderTotalValue, to: [
- PriceItemAdded,
- PriceItemRemoved,
- PercentageDiscountSet,
- PercentageDiscountReset,
- PercentageDiscountChanged,
- ProductMadeFreeForOrder,
- FreeProductRemovedFromOrder
- ])
- event_store.subscribe(CalculateOrderTotalSubAmountsValue, to: [
- PriceItemAdded,
- PriceItemRemoved,
- PercentageDiscountSet,
- PercentageDiscountReset,
- PercentageDiscountChanged,
- ProductMadeFreeForOrder,
- FreeProductRemovedFromOrder
- ])
- end
- end
-end
diff --git a/ecommerce/pricing/lib/pricing/calculate_order_sub_amounts_value.rb b/ecommerce/pricing/lib/pricing/calculate_order_sub_amounts_value.rb
deleted file mode 100644
index aab491c25..000000000
--- a/ecommerce/pricing/lib/pricing/calculate_order_sub_amounts_value.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-module Pricing
- class CalculateOrderTotalSubAmountsValue
- def call(event)
- command_bus.(CalculateSubAmounts.new(order_id: event.data.fetch(:order_id)))
- end
-
- private
-
- def command_bus
- Pricing.command_bus
- end
- end
-end
-
diff --git a/ecommerce/pricing/lib/pricing/calculate_order_total_value.rb b/ecommerce/pricing/lib/pricing/calculate_order_total_value.rb
deleted file mode 100644
index e09298c0a..000000000
--- a/ecommerce/pricing/lib/pricing/calculate_order_total_value.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-module Pricing
- class CalculateOrderTotalValue
- def call(event)
- command_bus.(CalculateTotalValue.new(order_id: event.data.fetch(:order_id)))
- end
-
- private
-
- def command_bus
- Pricing.command_bus
- end
- end
-end
-
diff --git a/ecommerce/pricing/lib/pricing/commands.rb b/ecommerce/pricing/lib/pricing/commands.rb
deleted file mode 100644
index 596077c08..000000000
--- a/ecommerce/pricing/lib/pricing/commands.rb
+++ /dev/null
@@ -1,83 +0,0 @@
-module Pricing
- class AddPriceItem < Infra::Command
- attribute :order_id, Infra::Types::UUID
- attribute :product_id, Infra::Types::UUID
-
- alias aggregate_id order_id
- end
-
- class RemovePriceItem < Infra::Command
- attribute :order_id, Infra::Types::UUID
- attribute :product_id, Infra::Types::UUID
-
- alias aggregate_id order_id
- end
-
- class CalculateTotalValue < Infra::Command
- attribute :order_id, Infra::Types::UUID
- alias aggregate_id order_id
- end
-
- class CalculateSubAmounts < Infra::Command
- attribute :order_id, Infra::Types::UUID
- alias aggregate_id order_id
- end
-
- class SetPrice < Infra::Command
- attribute :product_id, Infra::Types::UUID
- attribute :price, Infra::Types::Price
- end
-
- class SetFuturePrice < Infra::Command
- attribute :product_id, Infra::Types::UUID
- attribute :price, Infra::Types::Price
- attribute :valid_since, Infra::Types::Time
- end
-
- class SetPercentageDiscount < Infra::Command
- attribute :order_id, Infra::Types::UUID
- attribute :amount, Infra::Types::PercentageDiscount
- alias aggregate_id order_id
- end
-
- class ResetPercentageDiscount < Infra::Command
- attribute :order_id, Infra::Types::UUID
- alias aggregate_id order_id
- end
-
- class RegisterCoupon < Infra::Command
- attribute :coupon_id, Infra::Types::UUID
- attribute :name, Infra::Types::String
- attribute :code, Infra::Types::String
- attribute :discount, Infra::Types::CouponDiscount
- alias aggregate_id coupon_id
- end
-
- class CreateTimePromotion < Infra::Command
- attribute :time_promotion_id, Infra::Types::UUID.meta(omittable: true)
- attribute :discount, Infra::Types::PercentageDiscount
- attribute :start_time, Infra::Types::Time
- attribute :end_time, Infra::Types::Time
- attribute :label, Infra::Types::String
- end
-
- class ChangePercentageDiscount < Infra::Command
- attribute :order_id, Infra::Types::UUID
- attribute :amount, Infra::Types::PercentageDiscount
- alias aggregate_id order_id
- end
-
- class MakeProductFreeForOrder < Infra::Command
- attribute :order_id, Infra::Types::UUID
- attribute :product_id, Infra::Types::UUID
-
- alias aggregate_id order_id
- end
-
- class RemoveFreeProductFromOrder < Infra::Command
- attribute :order_id, Infra::Types::UUID
- attribute :product_id, Infra::Types::UUID
-
- alias aggregate_id order_id
- end
-end
diff --git a/ecommerce/pricing/lib/pricing/coupon.rb b/ecommerce/pricing/lib/pricing/coupon.rb
deleted file mode 100644
index 22c343f0a..000000000
--- a/ecommerce/pricing/lib/pricing/coupon.rb
+++ /dev/null
@@ -1,31 +0,0 @@
-require_relative 'events'
-
-module Pricing
- class Coupon
- include AggregateRoot
-
- AlreadyRegistered = Class.new(StandardError)
-
- def initialize(id)
- @id = id
- end
-
- def register(name, code, discount)
- raise AlreadyRegistered if @registered
-
- apply CouponRegistered.new(
- data: {
- coupon_id: @id,
- name: name,
- code: code,
- discount: discount
- }
- )
- end
-
- on CouponRegistered do |event|
- @registered = true
- end
- end
-end
-
diff --git a/ecommerce/pricing/lib/pricing/discounts.rb b/ecommerce/pricing/lib/pricing/discounts.rb
deleted file mode 100644
index 8134c7edd..000000000
--- a/ecommerce/pricing/lib/pricing/discounts.rb
+++ /dev/null
@@ -1,64 +0,0 @@
-module Pricing
- module Discounts
- class UnacceptableDiscountRange < StandardError
- end
-
- class Discount
- def self.build(discount)
- if discount.zero?
- NoPercentageDiscount.new
- else
- PercentageDiscount.new(discount)
- end
- end
- end
-
- class PercentageDiscount
- attr_reader :value
-
- def initialize(value)
- raise UnacceptableDiscountRange if value <= 0
- raise UnacceptableDiscountRange if value > 100
-
- @value = value
- end
-
- def apply(total)
- total - discount(total)
- end
-
- def add(other_discount)
- new_value = [value + other_discount.value, 100].min
-
- PercentageDiscount.new(new_value)
- end
-
- def exists?
- true
- end
-
- private
-
- def discount(total)
- total * value / 100
- end
- end
-
- class NoPercentageDiscount
- def apply(total)
- total
- end
-
- def add(other_discount)
- other_discount
- end
-
- def value
- 0
- end
-
- def exists?
- end
- end
- end
-end
diff --git a/ecommerce/pricing/lib/pricing/events.rb b/ecommerce/pricing/lib/pricing/events.rb
deleted file mode 100644
index 37e57a76e..000000000
--- a/ecommerce/pricing/lib/pricing/events.rb
+++ /dev/null
@@ -1,68 +0,0 @@
-module Pricing
- class CouponRegistered < Infra::Event
- attribute :coupon_id, Infra::Types::UUID
- attribute :name, Infra::Types::String
- attribute :code, Infra::Types::String
- attribute :discount, Infra::Types::CouponDiscount
- end
-
- class PriceSet < Infra::Event
- attribute :product_id, Infra::Types::UUID
- attribute :price, Infra::Types::Price
- end
-
- class TimePromotionCreated < Infra::Event
- attribute :time_promotion_id, Infra::Types::UUID
- attribute? :discount, Infra::Types::PercentageDiscount
- attribute? :start_time, Infra::Types::Time
- attribute? :end_time, Infra::Types::Time
- end
-
- class OrderTotalValueCalculated < Infra::Event
- attribute :order_id, Infra::Types::UUID
- attribute :discounted_amount, Infra::Types::Value
- attribute :total_amount, Infra::Types::Value
- end
-
- class PriceItemValueCalculated < Infra::Event
- attribute :order_id, Infra::Types::UUID
- attribute :product_id, Infra::Types::UUID
- attribute :quantity, Infra::Types::Quantity
- attribute :discounted_amount, Infra::Types::Value
- attribute :amount, Infra::Types::Value
- end
-
- class PercentageDiscountSet < Infra::Event
- attribute :order_id, Infra::Types::UUID
- attribute :amount, Infra::Types::Price
- end
-
- class PriceItemAdded < Infra::Event
- attribute :order_id, Infra::Types::UUID
- attribute :product_id, Infra::Types::UUID
- end
-
- class PriceItemRemoved < Infra::Event
- attribute :order_id, Infra::Types::UUID
- attribute :product_id, Infra::Types::UUID
- end
-
- class PercentageDiscountReset < Infra::Event
- attribute :order_id, Infra::Types::UUID
- end
-
- class PercentageDiscountChanged < Infra::Event
- attribute :order_id, Infra::Types::UUID
- attribute :amount, Infra::Types::Price
- end
-
- class ProductMadeFreeForOrder < Infra::Event
- attribute :order_id, Infra::Types::UUID
- attribute :product_id, Infra::Types::UUID
- end
-
- class FreeProductRemovedFromOrder < Infra::Event
- attribute :order_id, Infra::Types::UUID
- attribute :product_id, Infra::Types::UUID
- end
-end
diff --git a/ecommerce/pricing/lib/pricing/offer.rb b/ecommerce/pricing/lib/pricing/offer.rb
deleted file mode 100644
index 38b221441..000000000
--- a/ecommerce/pricing/lib/pricing/offer.rb
+++ /dev/null
@@ -1,251 +0,0 @@
-module Pricing
- class Offer
- include AggregateRoot
-
- def initialize(id)
- @id = id
- @list = List.new
- @discount = Discounts::NoPercentageDiscount.new
- end
-
- def add_item(product_id)
- apply PriceItemAdded.new(
- data: {
- order_id: @id,
- product_id: product_id
- }
- )
- end
-
- def remove_item(product_id)
- apply PriceItemRemoved.new(
- data: {
- order_id: @id,
- product_id: product_id
- }
- )
- end
-
- def apply_discount(discount)
- raise NotPossibleToAssignDiscountTwice if @discount.exists?
- apply PercentageDiscountSet.new(
- data: {
- order_id: @id,
- amount: discount.value
- }
- )
- end
-
- def change_discount(discount)
- raise NotPossibleToChangeDiscount unless @discount.exists?
- apply PercentageDiscountChanged.new(
- data: {
- order_id: @id,
- amount: discount.value
- }
- )
- end
-
- def reset_discount
- raise NotPossibleToResetWithoutDiscount unless @discount.exists?
- apply PercentageDiscountReset.new(
- data: {
- order_id: @id
- }
- )
- end
-
- def make_product_free(order_id, product_id)
- raise FreeProductAlreadyMade if @list.contains_free_products?
- apply ProductMadeFreeForOrder.new(
- data: {
- order_id: order_id,
- product_id: product_id
- }
- )
- end
-
- def remove_free_product(order_id, product_id)
- raise FreeProductNotExists unless @list.contains_free_products?
- apply FreeProductRemovedFromOrder.new(
- data: {
- order_id: order_id,
- product_id: product_id
- }
- )
- end
-
- def calculate_total_value(pricing_catalog, time_promotion_discount)
- total_value = @list.base_sum(pricing_catalog)
-
- discounted_value = @discount.add(time_promotion_discount).apply(total_value)
- apply(
- OrderTotalValueCalculated.new(
- data: {
- order_id: @id,
- total_amount: total_value,
- discounted_amount: discounted_value
- }
- )
- )
- end
-
- def calculate_sub_amounts(pricing_catalog, time_promotions_discount)
- sub_amounts_total = @list.sub_amounts_total(pricing_catalog)
- sub_discounts = calculate_total_sub_discounts(pricing_catalog, time_promotions_discount)
-
- products = @list.products
- quantities = @list.quantities
- products.zip(quantities, sub_amounts_total, sub_discounts) do |product, quantity, sub_amount, sub_discount|
- apply(
- PriceItemValueCalculated.new(
- data: {
- order_id: @id,
- product_id: product.id,
- quantity: quantity,
- amount: sub_amount,
- discounted_amount: sub_amount - sub_discount
- }
- )
- )
- end
- end
-
- private
-
- on PriceItemAdded do |event|
- @list.add_item(Product.new(event.data.fetch(:product_id)))
- end
-
- on PriceItemRemoved do |event|
- @list.remove_item(event.data.fetch(:product_id))
- end
-
- on PriceItemValueCalculated do |event|
- end
-
- on OrderTotalValueCalculated do |event|
- end
-
- on PercentageDiscountSet do |event|
- @discount = Discounts::PercentageDiscount.new(event.data.fetch(:amount))
- end
-
- on PercentageDiscountChanged do |event|
- @discount = Discounts::PercentageDiscount.new(event.data.fetch(:amount))
- end
-
- on PercentageDiscountReset do |event|
- @discount = Discounts::NoPercentageDiscount.new
- end
-
- on ProductMadeFreeForOrder do |event|
- @list.replace(Product, FreeProduct, event.data.fetch(:product_id))
- end
-
- on FreeProductRemovedFromOrder do |event|
- @list.replace(FreeProduct, Product, event.data.fetch(:product_id))
- end
-
- def calculate_total_sub_discounts(pricing_catalog, time_promotions_discount)
- @list.sub_discounts(pricing_catalog, time_promotions_discount, @discount)
- end
-
- class List
-
- def initialize
- @products_quantities = Hash.new(0)
- end
-
- def add_item(product)
- @products_quantities[product] += 1
- end
-
- def remove_item(product_id)
- @products_quantities[Product.new(product_id)] -= 1
- clear_empty_products
- end
-
- def clear_empty_products
- @products_quantities.delete_if { |_, value| value.zero? }
- end
-
- def replace(from, to, product_id)
- @products_quantities[from.new(product_id)] -= 1
- @products_quantities[to.new(product_id)] += 1
- clear_empty_products
- end
-
- def products
- @products_quantities.keys
- end
-
- def quantities
- @products_quantities.values
- end
-
- def contains_free_products?
- @products_quantities.keys.any? {|key| key.free? }
- end
-
- def base_sum(pricing_catalog)
- @products_quantities.sum { |product, qty| pricing_catalog.price_for(product) * qty }
- end
-
- def sub_amounts_total(pricing_catalog)
- @products_quantities.map { |product, quantity| quantity * pricing_catalog.price_for(product) }
- end
-
- def sub_discounts(pricing_catalog, time_promotions_discount, discount)
- @products_quantities.map do |product, quantity|
- catalog_price_for_single = pricing_catalog.price_for(product)
- with_total_discount_single = discount.add(time_promotions_discount).apply(catalog_price_for_single)
- quantity * (catalog_price_for_single - with_total_discount_single)
- end
- end
- end
-
- class Product
- attr_reader :id
-
- def initialize(id)
- @id = id
- end
-
- def free?
- end
-
- def eql?(other)
- other.instance_of?(Product) && id.eql?(other.id)
- end
-
- alias == eql?
-
- def hash
- Product.hash ^ id.hash
- end
- end
-
- class FreeProduct
- attr_reader :id
-
- def initialize(id)
- @id = id
- end
-
- def free?
- true
- end
-
- def eql?(other)
- other.instance_of?(FreeProduct) && id.eql?(other.id)
- end
-
- alias == eql?
-
- def hash
- FreeProduct.hash ^ id.hash
- end
- end
- end
-end
diff --git a/ecommerce/pricing/lib/pricing/price_change.rb b/ecommerce/pricing/lib/pricing/price_change.rb
deleted file mode 100644
index 5bfa92db1..000000000
--- a/ecommerce/pricing/lib/pricing/price_change.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-module Pricing
- class PriceChange
- include AggregateRoot
-
- def initialize(product_id)
- @product_id = product_id
- end
-
- def set_price(price)
- apply(PriceSet.new(data: { product_id: @product_id, price: price }))
- end
-
- private
-
- on(PriceSet) { |_| }
- end
-end
diff --git a/ecommerce/pricing/lib/pricing/pricing_catalog.rb b/ecommerce/pricing/lib/pricing/pricing_catalog.rb
deleted file mode 100644
index f2b8f53d5..000000000
--- a/ecommerce/pricing/lib/pricing/pricing_catalog.rb
+++ /dev/null
@@ -1,59 +0,0 @@
-module Pricing
- class PricingCatalog
- def initialize(event_store)
- @event_store = event_store
- end
-
- def price_for(product)
- case product
- when Offer::FreeProduct
- 0
- else
- price_by_product_id(product.id)
- end
- end
-
- def price_by_product_id(product_id)
- current_price(product_id)
- .data
- .fetch(:price)
- end
-
- def current_prices_catalog_by_product_id(product_id)
- ([current_price(product_id)] + future_prices_catalog_by_product_id(product_id)).map(&method(:to_calendar_entry))
- end
-
- private
-
- def future_prices_catalog_by_product_id(product_id)
- read_prices_set(product_id)
- .select(&method(:future_prices))
- end
-
- def current_price(product_id)
- read_prices_set(product_id)
- .reject(&method(:future_prices))
- .last
- end
-
- def read_prices_set(product_id)
- @event_store
- .read
- .of_type(PriceSet)
- .as_of
- .to_a
- .filter { |e| e.data.fetch(:product_id).eql?(product_id) }
- end
-
- def future_prices(e)
- e.metadata.fetch(:valid_at) > Time.now
- end
-
- def to_calendar_entry(e)
- {
- price: e.data.fetch(:price),
- valid_since: e.metadata.fetch(:valid_at)
- }
- end
- end
-end
diff --git a/ecommerce/pricing/lib/pricing/promotions_calendar.rb b/ecommerce/pricing/lib/pricing/promotions_calendar.rb
deleted file mode 100644
index 8c82a3f21..000000000
--- a/ecommerce/pricing/lib/pricing/promotions_calendar.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-module Pricing
- class PromotionsCalendar
- def initialize(event_store)
- @event_store = event_store
- end
-
- def current_time_promotions_discount
- Discounts::Discount.build(
- get_events(TimePromotionCreated)
- .filter { |e| current_promotions.include?(e.data.fetch(:time_promotion_id)) }
- .map { |e| e.data.fetch(:discount) }.sum
- )
- end
-
- private
-
- def current_promotions
- get_events(TimePromotionCreated)
- .filter { |e| is_promotion_running?(e) }
- .map { |e| e.data.fetch(:time_promotion_id) }
- end
-
- def is_promotion_running?(event)
- timestamp = Time.current
- start_time = event.data.fetch(:start_time)
- end_time = event.data.fetch(:end_time)
-
- timestamp >= start_time && end_time > timestamp
- end
-
- def get_events(event_type)
- @event_store
- .read
- .of_type(event_type)
- .to_a
- .group_by { |e| e.data.fetch(:time_promotion_id) }
- .map { |_, events| events.max_by(&:timestamp) }
- end
- end
-end
diff --git a/ecommerce/pricing/lib/pricing/services.rb b/ecommerce/pricing/lib/pricing/services.rb
deleted file mode 100644
index 1ac73249f..000000000
--- a/ecommerce/pricing/lib/pricing/services.rb
+++ /dev/null
@@ -1,183 +0,0 @@
-module Pricing
- class NotPossibleToAssignDiscountTwice < StandardError
- end
-
- class NotPossibleToResetWithoutDiscount < StandardError
- end
-
- class NotPossibleToChangeDiscount < StandardError
- end
-
- class FreeProductAlreadyMade < StandardError
- end
-
- class FreeProductNotExists < StandardError
- end
-
- class SetPercentageDiscountHandler
- def initialize(event_store)
- @repository = Infra::AggregateRootRepository.new(event_store)
- end
-
- def call(cmd)
- @repository.with_aggregate(Offer, cmd.aggregate_id) do |order|
- order.apply_discount(Discounts::PercentageDiscount.new(cmd.amount))
- end
- end
- end
-
- class ResetPercentageDiscountHandler
- def initialize(event_store)
- @repository = Infra::AggregateRootRepository.new(event_store)
- end
-
- def call(cmd)
- @repository.with_aggregate(Offer, cmd.aggregate_id) do |order|
- order.reset_discount
- end
- end
- end
-
- class ChangePercentageDiscountHandler
- def initialize(event_store)
- @repository = Infra::AggregateRootRepository.new(event_store)
- end
-
- def call(cmd)
- @repository.with_aggregate(Offer, cmd.aggregate_id) do |order|
- order.change_discount(Discounts::PercentageDiscount.new(cmd.amount))
- end
- end
- end
-
- class SetPriceHandler
- def initialize(event_store)
- @repository = Infra::AggregateRootRepository.new(event_store)
- end
-
- def call(cmd)
- @repository.with_aggregate(PriceChange, cmd.product_id) do |product|
- product.set_price(cmd.price)
- end
- end
- end
-
- class SetFuturePriceHandler
- def initialize(event_store)
- @repository = Infra::AggregateRootRepository.new(event_store)
- @event_store = event_store
- end
-
- def call(cmd)
- @event_store.with_metadata({ valid_at: cmd.valid_since }) do
- @repository.with_aggregate(PriceChange, cmd.product_id) do |product|
- product.set_price(cmd.price)
- end
- end
- end
- end
-
- class CreateTimePromotionHandler
- def initialize(event_store)
- @repository = Infra::AggregateRootRepository.new(event_store)
- end
-
- def call(cmd)
- @repository.with_aggregate(TimePromotion, cmd.time_promotion_id) do |time_promotion|
- time_promotion.create(cmd.discount, cmd.start_time, cmd.end_time, cmd.label)
- end
- end
- end
-
- class OnAddItemToBasket
- def initialize(event_store)
- @repository = Infra::AggregateRootRepository.new(event_store)
- end
-
- def call(command)
- @repository.with_aggregate(Offer, command.aggregate_id) do |order|
- order.add_item(command.product_id)
- end
- end
- end
-
- class OnRemoveItemFromBasket
- def initialize(event_store)
- @repository = Infra::AggregateRootRepository.new(event_store)
- end
-
- def call(command)
- @repository.with_aggregate(Offer, command.aggregate_id) do |order|
- order.remove_item(command.product_id)
- end
- end
- end
-
- class OnCalculateTotalValue
- def initialize(event_store)
- @repository = Infra::AggregateRootRepository.new(event_store)
- @event_store = event_store
- end
-
- def call(command)
- @repository.with_aggregate(Offer, command.aggregate_id) do |order|
- order.calculate_total_value(PricingCatalog.new(@event_store), time_promotions_discount)
- end
- rescue RubyEventStore::WrongExpectedEventVersion
- retry
- end
-
-
-
- def calculate_sub_amounts(command)
- @repository.with_aggregate(Offer, command.aggregate_id) do |order|
- order.calculate_sub_amounts(PricingCatalog.new(@event_store), time_promotions_discount)
- end
- rescue RubyEventStore::WrongExpectedEventVersion
- retry
- end
-
- private
-
- def time_promotions_discount
- PromotionsCalendar.new(@event_store).current_time_promotions_discount
- end
-
- end
-
- class OnCouponRegister
- def initialize(event_store)
- @repository = Infra::AggregateRootRepository.new(event_store)
- end
-
- def call(command)
- @repository.with_aggregate(Coupon, command.aggregate_id) do |coupon|
- coupon.register(command.name, command.code, command.discount)
- end
- end
- end
-
- class MakeProductFreeForOrderHandler
- def initialize(event_store)
- @repository = Infra::AggregateRootRepository.new(event_store)
- end
-
- def call(command)
- @repository.with_aggregate(Offer, command.aggregate_id) do |order|
- order.make_product_free(command.order_id, command.product_id)
- end
- end
- end
-
- class RemoveFreeProductFromOrderHandler
- def initialize(event_store)
- @repository = Infra::AggregateRootRepository.new(event_store)
- end
-
- def call(command)
- @repository.with_aggregate(Offer, command.aggregate_id) do |order|
- order.remove_free_product(command.order_id, command.product_id)
- end
- end
- end
-end
diff --git a/ecommerce/pricing/lib/pricing/time_promotion.rb b/ecommerce/pricing/lib/pricing/time_promotion.rb
deleted file mode 100644
index 4f1ee9d19..000000000
--- a/ecommerce/pricing/lib/pricing/time_promotion.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-module Pricing
- class TimePromotion
- include AggregateRoot
-
- def initialize(id)
- @id = id
- end
-
- def create(discount, start_time, end_time, label)
- apply TimePromotionCreated.new(
- data: {
- time_promotion_id: @id,
- discount: discount,
- start_time: start_time,
- end_time: end_time,
- label: label
- }
- )
- end
-
- private
-
- on TimePromotionCreated do |_|
- end
-
- end
-end
diff --git a/ecommerce/pricing/test/coupons_test.rb b/ecommerce/pricing/test/coupons_test.rb
deleted file mode 100644
index b8c7a158e..000000000
--- a/ecommerce/pricing/test/coupons_test.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-require_relative "test_helper"
-
-module Pricing
- class CouponsTest < Test
- cover "Pricing::Coupon*"
-
- def setup
- @uid = SecureRandom.uuid
- @code = fake_name.chars.shuffle.join
- @discount = 10
- @data = { coupon_id: @uid, name: fake_name, code: @code, discount: @discount }
- end
-
- def test_coupon_should_get_registered
- register_coupon(@uid, fake_name, @code, rand(1..20))
- end
-
- def test_should_not_allow_for_id_based_duplicates
- assert_raises(Pricing::Coupon::AlreadyRegistered) do
- register_coupon(@uid, fake_name, @code, @discount)
- register_coupon(@uid, fake_name, @code, @discount)
- end
- end
-
- def test_should_publish_event
- coupon_registered = CouponRegistered.new(data: @data)
- assert_events("Pricing::Coupon$#{@uid}", coupon_registered) do
- register_coupon(@uid, fake_name, @code, @discount)
- end
- end
-
- def test_100_is_ok
- register_coupon(@uid, fake_name, @code, 100)
- end
-
- def test_0_01_is_ok
- register_coupon(@uid, fake_name, @code, 0.01)
- end
- end
-end
diff --git a/ecommerce/pricing/test/discounts_test.rb b/ecommerce/pricing/test/discounts_test.rb
deleted file mode 100644
index b7387cc70..000000000
--- a/ecommerce/pricing/test/discounts_test.rb
+++ /dev/null
@@ -1,68 +0,0 @@
-require_relative "test_helper"
-
-module Pricing
- module Discounts
-
- class PercentageDiscountTest < Test
- cover "Pricing::Discounts*"
-
- def test_is_more_than_zero
- assert_raises UnacceptableDiscountRange do
- PercentageDiscount.new(0)
- end
- end
-
- def test_is_not_lower_than_0
- assert_raises UnacceptableDiscountRange do
- PercentageDiscount.new(-0.01)
- end
- end
-
- def test_is_not_more_than_100_percent
- assert_raises UnacceptableDiscountRange do
- PercentageDiscount.new(100.01)
- end
- end
-
- def test_100_is_ok
- PercentageDiscount.new(100)
- end
-
- def test_0_01_is_ok
- PercentageDiscount.new(0.01)
- end
-
- def test_applies_to_value
- assert_equal(90, PercentageDiscount.new(10).apply(100))
- end
-
- def test_calculates_floats_too
- assert_equal(90.45, PercentageDiscount.new(10).apply(100.50))
- end
-
- def test_can_add_another_discount
- first_discount = PercentageDiscount.new(20)
- second_discount = PercentageDiscount.new(15)
- combined = first_discount.add(second_discount)
-
- assert_equal(65, combined.apply(100))
- end
-
- def test_cannot_add_to_more_than_100
- first_discount = PercentageDiscount.new(50)
- second_discount = PercentageDiscount.new(65)
- combined = first_discount.add(second_discount)
-
- assert_equal(0, combined.apply(100))
- end
- end
-
- class NoPercentageDiscountTest < Test
- cover "Pricing::Discounts*"
-
- def test_doesnt_change_total
- assert_equal(100, NoPercentageDiscount.new.apply(100))
- end
- end
- end
-end
diff --git a/ecommerce/pricing/test/free_products_test.rb b/ecommerce/pricing/test/free_products_test.rb
deleted file mode 100644
index 1982a96d1..000000000
--- a/ecommerce/pricing/test/free_products_test.rb
+++ /dev/null
@@ -1,217 +0,0 @@
-require_relative "test_helper"
-
-module Pricing
- class FreeProductsTest < Test
- cover "Pricing*"
-
- def test_making_product_free_possible_when_order_is_eligible
- product_1_id = SecureRandom.uuid
- set_price(product_1_id, 20)
- order_id = SecureRandom.uuid
- add_item(order_id, product_1_id)
- add_item(order_id, product_1_id)
- add_item(order_id, product_1_id)
- add_item(order_id, product_1_id)
-
- assert_events_contain(
- stream_name(order_id),
- ProductMadeFreeForOrder.new(
- data: {
- order_id: order_id,
- product_id: product_1_id
- }
- ),
- OrderTotalValueCalculated.new(
- data: {
- order_id: order_id,
- discounted_amount: 60,
- total_amount: 60
- }
- )
- ) do
- run_command(
- Pricing::MakeProductFreeForOrder.new(order_id: order_id, product_id: product_1_id)
- )
- end
- end
-
- def test_making_only_the_cheapest_product_free
- product_1_id = SecureRandom.uuid
- cheaper_product = SecureRandom.uuid
- set_price(product_1_id, 20)
- set_price(cheaper_product, 10)
- order_id = SecureRandom.uuid
- add_item(order_id, product_1_id)
- add_item(order_id, product_1_id)
- add_item(order_id, product_1_id)
- add_item(order_id, cheaper_product)
-
- assert_events_contain(
- stream_name(order_id),
- ProductMadeFreeForOrder.new(
- data: {
- order_id: order_id,
- product_id: cheaper_product
- }
- ),
- OrderTotalValueCalculated.new(
- data: {
- order_id: order_id,
- discounted_amount: 60,
- total_amount: 60
- }
- ),
- ) do
- run_command(
- Pricing::MakeProductFreeForOrder.new(order_id: order_id, product_id: cheaper_product)
- )
- end
- end
-
- def test_making_product_free_not_possible_if_is_already_set
- product_1_id = SecureRandom.uuid
- set_price(product_1_id, 20)
- order_id = SecureRandom.uuid
- add_item(order_id, product_1_id)
- add_item(order_id, product_1_id)
- add_item(order_id, product_1_id)
- add_item(order_id, product_1_id)
-
- run_command(
- Pricing::MakeProductFreeForOrder.new(order_id: order_id, product_id: product_1_id)
- )
-
- assert_raises FreeProductAlreadyMade do
- run_command(
- Pricing::MakeProductFreeForOrder.new(order_id: order_id, product_id: product_1_id)
- )
- end
- end
-
- def test_making_product_free_possible_after_previous_free_product_was_removed
- product_1_id = SecureRandom.uuid
- set_price(product_1_id, 20)
- order_id = SecureRandom.uuid
- add_item(order_id, product_1_id)
- add_item(order_id, product_1_id)
- add_item(order_id, product_1_id)
- add_item(order_id, product_1_id)
-
- run_command(
- Pricing::MakeProductFreeForOrder.new(order_id: order_id, product_id: product_1_id)
- )
-
- run_command(
- Pricing::RemovePriceItem.new(order_id: order_id, product_id: product_1_id)
- )
-
- run_command(
- Pricing::RemoveFreeProductFromOrder.new(order_id: order_id, product_id: product_1_id)
- )
-
- run_command(
- Pricing::AddPriceItem.new(order_id: order_id, product_id: product_1_id)
- )
-
- assert_events_contain(
- stream_name(order_id),
- ProductMadeFreeForOrder.new(
- data: {
- order_id: order_id,
- product_id: product_1_id
- }
- ),
- OrderTotalValueCalculated.new(
- data: {
- order_id: order_id,
- discounted_amount: 60,
- total_amount: 60
- }
- )
- ) do
- run_command(
- Pricing::MakeProductFreeForOrder.new(order_id: order_id, product_id: product_1_id)
- )
- end
- end
-
- def test_removing_free_product_possible_if_it_is_already_set
- product_1_id = SecureRandom.uuid
- set_price(product_1_id, 20)
- order_id = SecureRandom.uuid
- add_item(order_id, product_1_id)
- add_item(order_id, product_1_id)
- add_item(order_id, product_1_id)
- add_item(order_id, product_1_id)
-
- run_command(
- Pricing::MakeProductFreeForOrder.new(order_id: order_id, product_id: product_1_id)
- )
-
- assert_events_contain(
- stream_name(order_id),
- FreeProductRemovedFromOrder.new(
- data: {
- order_id: order_id,
- product_id: product_1_id
- }
- ),
- OrderTotalValueCalculated.new(
- data: {
- order_id: order_id,
- discounted_amount: 80,
- total_amount: 80
- }
- )
- ) do
- run_command(
- Pricing::RemoveFreeProductFromOrder.new(order_id: order_id, product_id: product_1_id)
- )
- end
- end
-
- def test_removing_free_product_not_possible_if_is_not_set
- product_1_id = SecureRandom.uuid
- set_price(product_1_id, 20)
- order_id = SecureRandom.uuid
- add_item(order_id, product_1_id)
-
- assert_raises FreeProductNotExists do
- run_command(
- Pricing::RemoveFreeProductFromOrder.new(order_id: order_id, product_id: product_1_id)
- )
- end
- end
-
- def test_removing_free_product_twice_not_possible
- product_1_id = SecureRandom.uuid
- set_price(product_1_id, 20)
- order_id = SecureRandom.uuid
- add_item(order_id, product_1_id)
- add_item(order_id, product_1_id)
- add_item(order_id, product_1_id)
- add_item(order_id, product_1_id)
-
- run_command(
- Pricing::MakeProductFreeForOrder.new(order_id: order_id, product_id: product_1_id)
- )
-
- run_command(
- Pricing::RemoveFreeProductFromOrder.new(order_id: order_id, product_id: product_1_id)
- )
-
- assert_raises FreeProductNotExists do
- run_command(
- Pricing::RemoveFreeProductFromOrder.new(order_id: order_id, product_id: product_1_id)
- )
- end
- end
-
- private
-
- def stream_name(id)
- "Pricing::Offer$#{id}"
- end
-
- end
-end
diff --git a/ecommerce/pricing/test/future_prices_test.rb b/ecommerce/pricing/test/future_prices_test.rb
deleted file mode 100644
index a1e271b17..000000000
--- a/ecommerce/pricing/test/future_prices_test.rb
+++ /dev/null
@@ -1,125 +0,0 @@
-require_relative "test_helper"
-
-module Pricing
- class FuturePricesTest < Test
- cover "Pricing*"
-
- def test_future_price_is_not_included_when_calculating_total_value
- product_1_id = SecureRandom.uuid
- set_price(product_1_id, 20)
- future_date_timestamp = Time.current + days_number(5)
- set_future_price(product_1_id, 30, future_date_timestamp)
- order_id = SecureRandom.uuid
- add_item(order_id, product_1_id)
- stream = stream_name(order_id)
-
- assert_events(
- stream,
- OrderTotalValueCalculated.new(
- data: {
- order_id: order_id,
- discounted_amount: 20,
- total_amount: 20
- }
- )
- ) { calculate_total_value(order_id) }
- end
-
- def test_check_future_price
- product_1_id = SecureRandom.uuid
- set_price(product_1_id, 20)
- future_date_timestamp = Time.current + days_number(5)
- set_future_price(product_1_id, 30, future_date_timestamp)
-
- Timecop.travel(future_date_timestamp + 2137) do
- order_id = SecureRandom.uuid
- add_item(order_id, product_1_id)
- stream = stream_name(order_id)
-
- assert_events(
- stream,
- OrderTotalValueCalculated.new(
- data: {
- order_id: order_id,
- discounted_amount: 30,
- total_amount: 30
- }
- )
- ) { calculate_total_value(order_id) }
- end
- end
-
- def test_future_prices_catalog_by_product_id
- product_id = SecureRandom.uuid
- set_price(product_id, 20)
- future_date_timestamp_1 = with_precision(Time.current + days_number(2))
- future_date_timestamp_2 = with_precision(Time.current + days_number(3))
- future_date_timestamp_3 = with_precision(Time.current + days_number(4))
-
- set_future_price(product_id, 30, future_date_timestamp_3)
- set_future_price(product_id, 40, future_date_timestamp_1)
- set_future_price(product_id, 50, future_date_timestamp_2)
-
- pricing_catalog = PricingCatalog.new(event_store)
-
- assert_equal 20, pricing_catalog.price_by_product_id(product_id)
-
- assert_equal [
- BigDecimal(20),
- BigDecimal(40),
- BigDecimal(50),
- BigDecimal(30)
- ], pricing_catalog.current_prices_catalog_by_product_id(product_id).map { |entry| entry[:price] }
-
- Timecop.travel(future_date_timestamp_1 + 1.second) do
- assert_equal [
- BigDecimal(40),
- BigDecimal(50),
- BigDecimal(30)
- ], pricing_catalog.current_prices_catalog_by_product_id(product_id).map { |entry| entry[:price] }
- assert_equal [
- future_date_timestamp_1,
- future_date_timestamp_2,
- future_date_timestamp_3,
- ], pricing_catalog.current_prices_catalog_by_product_id(product_id).map { |entry| entry[:valid_since] }
- assert_equal BigDecimal(40), pricing_catalog.price_by_product_id(product_id)
- end
-
- Timecop.travel(future_date_timestamp_2 + 1.second) do
- assert_equal [
- BigDecimal(50),
- BigDecimal(30)
- ], pricing_catalog.current_prices_catalog_by_product_id(product_id).map { |entry| entry[:price] }
- assert_equal [
- future_date_timestamp_2,
- future_date_timestamp_3,
- ], pricing_catalog.current_prices_catalog_by_product_id(product_id).map { |entry| entry[:valid_since] }
- assert_equal BigDecimal(50), pricing_catalog.price_by_product_id(product_id)
- end
-
-
- pricing_catalog = PricingCatalog.new(event_store)
- Timecop.travel(future_date_timestamp_3 + 1.second) do
- assert_equal [BigDecimal(30)], pricing_catalog.current_prices_catalog_by_product_id(product_id).map { |entry| entry[:price] }
- assert_equal [
- future_date_timestamp_3
- ], pricing_catalog.current_prices_catalog_by_product_id(product_id).map { |entry| entry[:valid_since] }
- assert_equal BigDecimal(30), pricing_catalog.price_by_product_id(product_id)
- end
- end
-
- private
-
- def stream_name(order_id)
- "Pricing::Offer$#{order_id}"
- end
-
- def days_number(n)
- 3600 * 24 * n
- end
-
- def with_precision(time)
- time.round(6)
- end
- end
-end
diff --git a/ecommerce/pricing/test/order_product_test.rb b/ecommerce/pricing/test/order_product_test.rb
deleted file mode 100644
index 2d9f34d8d..000000000
--- a/ecommerce/pricing/test/order_product_test.rb
+++ /dev/null
@@ -1,47 +0,0 @@
-require_relative "test_helper"
-
-module Pricing
- class Offer
- class ProductTest < Test
- cover "Pricing::Offer::Product"
-
- def setup
- super
- @id = SecureRandom.uuid
- end
-
- def test_values_equality
- refute(Product.new(@id).eql?(FreeProduct.new(@id)))
- refute(Product.new(@id).eql?(Product.new(SecureRandom.uuid)))
- refute(Product.new(@id).eql?(@id))
- end
-
- def test_hash_equality
- assert(Product.new(@id).hash.eql?(Product.hash ^ @id.hash))
- refute(Product.new(@id).hash.eql?(Product.hash ^ SecureRandom.uuid.hash))
- refute(Product.new(@id).hash == @id.hash)
- end
- end
-
- class FreeProductTest < Test
- cover "Pricing::Offer::FreeProduct"
-
- def setup
- super
- @id = SecureRandom.uuid
- end
-
- def test_values_equality
- refute(FreeProduct.new(@id).eql?(Product.new(@id)))
- refute(FreeProduct.new(@id).eql?(FreeProduct.new(SecureRandom.uuid)))
- refute(FreeProduct.new(@id).eql?(@id))
- end
-
- def test_hash_equality
- assert(FreeProduct.new(@id).hash.eql?(FreeProduct.hash ^ @id.hash))
- refute(FreeProduct.new(@id).hash.eql?(FreeProduct.hash ^ SecureRandom.uuid.hash))
- refute(FreeProduct.new(@id).hash == @id.hash)
- end
- end
- end
-end
diff --git a/ecommerce/pricing/test/pricing_test.rb b/ecommerce/pricing/test/pricing_test.rb
deleted file mode 100644
index 14f2326e3..000000000
--- a/ecommerce/pricing/test/pricing_test.rb
+++ /dev/null
@@ -1,401 +0,0 @@
-require_relative "test_helper"
-
-module Pricing
- class PricingTest < Test
- cover "Pricing*"
-
- def test_configuration
- Pricing.event_store = Infra::EventStore.in_memory
- Pricing.event_store = Infra::CommandBus
-
- assert Pricing.event_store, Infra::EventStore.in_memory
- assert Pricing.command_bus, Infra::CommandBus
- end
-
- def test_calculates_total_value
- product_1_id = SecureRandom.uuid
- product_2_id = SecureRandom.uuid
- set_price(product_1_id, 20)
- set_price(product_2_id, 30)
- order_id = SecureRandom.uuid
- add_item(order_id, product_1_id)
- add_item(order_id, product_2_id)
- stream = stream_name(order_id)
- assert_events(
- stream,
- OrderTotalValueCalculated.new(
- data: {
- order_id: order_id,
- discounted_amount: 50,
- total_amount: 50
- }
- )
- ) { calculate_total_value(order_id) }
- end
-
- def test_calculates_sub_amounts
- product_1_id = SecureRandom.uuid
- product_2_id = SecureRandom.uuid
- set_price(product_1_id, 20)
- set_price(product_2_id, 30)
- order_id = SecureRandom.uuid
- stream = stream_name(order_id)
-
- assert_events(stream) { calculate_sub_amounts(order_id) }
-
- add_item(order_id, product_1_id)
- add_item(order_id, product_2_id)
- add_item(order_id, product_2_id)
- assert_events(
- stream,
- PriceItemValueCalculated.new(
- data: {
- order_id: order_id,
- product_id: product_1_id,
- quantity: 1,
- amount: 20,
- discounted_amount: 20
- }
- ),
- PriceItemValueCalculated.new(
- data: {
- order_id: order_id,
- product_id: product_2_id,
- quantity: 2,
- amount: 60,
- discounted_amount: 60
- }
- )
- ) { calculate_sub_amounts(order_id) }
- run_command(
- Pricing::SetPercentageDiscount.new(order_id: order_id, amount: 10)
- )
- assert_events(
- stream,
- PriceItemValueCalculated.new(
- data: {
- order_id: order_id,
- product_id: product_1_id,
- quantity: 1,
- amount: 20,
- discounted_amount: 18
- }
- ),
- PriceItemValueCalculated.new(
- data: {
- order_id: order_id,
- product_id: product_2_id,
- quantity: 2,
- amount: 60,
- discounted_amount: 54
- }
- )
- ) { calculate_sub_amounts(order_id) }
- end
-
- def test_calculates_total_value_with_discount
- product_1_id = SecureRandom.uuid
- set_price(product_1_id, 20)
- order_id = SecureRandom.uuid
- add_item(order_id, product_1_id)
- stream = stream_name(order_id)
- assert_events(
- stream,
- OrderTotalValueCalculated.new(
- data: {
- order_id: order_id,
- discounted_amount: 20,
- total_amount: 20
- }
- )
- ) { run_command(CalculateTotalValue.new(order_id: order_id)) }
- assert_events_contain(
- stream,
- PercentageDiscountSet.new(
- data: {
- order_id: order_id,
- amount: 10
- }
- ),
- OrderTotalValueCalculated.new(
- data: {
- order_id: order_id,
- discounted_amount: 18,
- total_amount: 20
- }
- )
- ) do
- run_command(
- Pricing::SetPercentageDiscount.new(order_id: order_id, amount: 10)
- )
- end
- assert_events_contain(
- stream,
- PercentageDiscountChanged.new(
- data: {
- order_id: order_id,
- amount: 50
- }
- ),
- OrderTotalValueCalculated.new(
- data: {
- order_id: order_id,
- discounted_amount: 10,
- total_amount: 20
- }
- )
- ) do
- run_command(
- Pricing::ChangePercentageDiscount.new(order_id: order_id, amount: 50)
- )
- end
- assert_events_contain(
- stream,
- PercentageDiscountReset.new(
- data: {
- order_id: order_id,
- }
- ),
- OrderTotalValueCalculated.new(
- data: {
- order_id: order_id,
- discounted_amount: 20,
- total_amount: 20
- }
- )
- ) do
- run_command(
- Pricing::ResetPercentageDiscount.new(order_id: order_id)
- )
- end
- end
-
- def test_calculates_total_value_with_100_discount
- product_1_id = SecureRandom.uuid
- set_price(product_1_id, 20)
- order_id = SecureRandom.uuid
- add_item(order_id, product_1_id)
- stream = stream_name(order_id)
- assert_events_contain(
- stream,
- PercentageDiscountSet.new(
- data: {
- order_id: order_id,
- amount: 100
- }
- ),
- OrderTotalValueCalculated.new(
- data: {
- order_id: order_id,
- discounted_amount: 0,
- total_amount: 20
- }
- )
- ) do
- run_command(
- Pricing::SetPercentageDiscount.new(order_id: order_id, amount: 100)
- )
- end
- end
-
- def test_setting_discounts_twice_not_possible_because_we_want_explicit_discount_change_command
- product_1_id = SecureRandom.uuid
- set_price(product_1_id, 20)
- order_id = SecureRandom.uuid
- add_item(order_id, product_1_id)
- run_command(
- Pricing::SetPercentageDiscount.new(order_id: order_id, amount: 10)
- )
- assert_raises NotPossibleToAssignDiscountTwice do
- run_command(
- Pricing::SetPercentageDiscount.new(order_id: order_id, amount: 20)
- )
- end
- end
-
- def test_setting_discount_not_possible_when_discount_has_been_set_and_then_changed
- product_1_id = SecureRandom.uuid
- set_price(product_1_id, 20)
- order_id = SecureRandom.uuid
- add_item(order_id, product_1_id)
- run_command(
- Pricing::SetPercentageDiscount.new(order_id: order_id, amount: 10)
- )
- run_command(
- Pricing::ChangePercentageDiscount.new(order_id: order_id, amount: 20)
- )
-
- assert_raises NotPossibleToAssignDiscountTwice do
- run_command(
- Pricing::SetPercentageDiscount.new(order_id: order_id, amount: 20)
- )
- end
- end
-
- def test_changing_discount_not_possible_when_discount_is_not_set
- product_1_id = SecureRandom.uuid
- set_price(product_1_id, 20)
- order_id = SecureRandom.uuid
- add_item(order_id, product_1_id)
-
- assert_raises NotPossibleToChangeDiscount do
- run_command(
- Pricing::ChangePercentageDiscount.new(order_id: order_id, amount: 20)
- )
- end
- end
-
- def test_changing_discount_not_possible_when_discount_is_reset
- product_1_id = SecureRandom.uuid
- set_price(product_1_id, 20)
- order_id = SecureRandom.uuid
- add_item(order_id, product_1_id)
- run_command(
- Pricing::SetPercentageDiscount.new(order_id: order_id, amount: 10)
- )
- run_command(
- Pricing::ResetPercentageDiscount.new(order_id: order_id)
- )
-
- assert_raises NotPossibleToChangeDiscount do
- run_command(
- Pricing::ChangePercentageDiscount.new(order_id: order_id, amount: 20)
- )
- end
- end
-
- def test_changing_discount_possible_when_discount_is_set
- product_1_id = SecureRandom.uuid
- set_price(product_1_id, 20)
- order_id = SecureRandom.uuid
- add_item(order_id, product_1_id)
- stream = stream_name(order_id)
- run_command(
- Pricing::SetPercentageDiscount.new(order_id: order_id, amount: 10)
- )
-
- assert_events_contain(
- stream,
- PercentageDiscountChanged.new(
- data: {
- order_id: order_id,
- amount: 100
- }
- ),
- OrderTotalValueCalculated.new(
- data: {
- order_id: order_id,
- discounted_amount: 0,
- total_amount: 20
- }
- )
- ) do
- run_command(
- Pricing::ChangePercentageDiscount.new(order_id: order_id, amount: 100)
- )
- end
- end
-
- def test_changing_discount_possible_more_than_once
- product_1_id = SecureRandom.uuid
- set_price(product_1_id, 20)
- order_id = SecureRandom.uuid
- add_item(order_id, product_1_id)
- stream = stream_name(order_id)
- run_command(
- Pricing::SetPercentageDiscount.new(order_id: order_id, amount: 10)
- )
- run_command(
- Pricing::ChangePercentageDiscount.new(order_id: order_id, amount: 20)
- )
-
- assert_events_contain(
- stream,
- PercentageDiscountChanged.new(
- data: {
- order_id: order_id,
- amount: 100
- }
- ),
- OrderTotalValueCalculated.new(
- data: {
- order_id: order_id,
- discounted_amount: 0,
- total_amount: 20
- }
- )
- ) do
- run_command(
- Pricing::ChangePercentageDiscount.new(order_id: order_id, amount: 100)
- )
- end
- end
-
- def test_resetting_discount_possible_when_discount_has_been_set_and_then_changed
- product_1_id = SecureRandom.uuid
- set_price(product_1_id, 20)
- order_id = SecureRandom.uuid
- add_item(order_id, product_1_id)
- stream = stream_name(order_id)
- run_command(
- Pricing::SetPercentageDiscount.new(order_id: order_id, amount: 10)
- )
- run_command(
- Pricing::ChangePercentageDiscount.new(order_id: order_id, amount: 20)
- )
-
- assert_events_contain(
- stream,
- PercentageDiscountReset.new(
- data: {
- order_id: order_id
- }
- ),
- OrderTotalValueCalculated.new(
- data: {
- order_id: order_id,
- discounted_amount: 20,
- total_amount: 20
- }
- )
- ) do
- run_command(
- Pricing::ResetPercentageDiscount.new(order_id: order_id)
- )
- end
- end
-
- def test_resetting_with_missing_discount_not_possible
- product_1_id = SecureRandom.uuid
- set_price(product_1_id, 20)
- order_id = SecureRandom.uuid
- add_item(order_id, product_1_id)
- assert_raises NotPossibleToResetWithoutDiscount do
- run_command(
- Pricing::ResetPercentageDiscount.new(order_id: order_id)
- )
- end
- run_command(
- Pricing::SetPercentageDiscount.new(order_id: order_id, amount: 10)
- )
- run_command(
- Pricing::ResetPercentageDiscount.new(order_id: order_id)
- )
- assert_raises NotPossibleToResetWithoutDiscount do
- run_command(
- Pricing::ResetPercentageDiscount.new(order_id: order_id)
- )
- end
- end
-
- private
-
- def stream_name(order_id)
- "Pricing::Offer$#{order_id}"
- end
-
- def calculate_sub_amounts(order_id)
- run_command(CalculateSubAmounts.new(order_id: order_id))
- end
- end
-end
diff --git a/ecommerce/pricing/test/product_test.rb b/ecommerce/pricing/test/product_test.rb
deleted file mode 100644
index 3217d590b..000000000
--- a/ecommerce/pricing/test/product_test.rb
+++ /dev/null
@@ -1,33 +0,0 @@
-require_relative "test_helper"
-
-module Pricing
- class ProductTest < Test
- cover "Pricing::Product*"
-
- def test_product_price_is_set
- product_id = SecureRandom.uuid
-
- set_price(product_id, 20)
- end
-
- def test_set_future_price
- product_id = SecureRandom.uuid
- valid_since = 2.days.from_now
- price = 20
-
- future_price_set = [
- PriceSet.new(data: { product_id: product_id, price: price }),
- ]
-
- assert_events("Pricing::PriceChange$#{product_id}", *future_price_set) do
- set_future_price(product_id, price, valid_since)
- end
- end
-
- private
-
- def set_price(product_id, amount)
- run_command(SetPrice.new(product_id: product_id, price: amount))
- end
- end
-end
diff --git a/ecommerce/pricing/test/simple_offer_test.rb b/ecommerce/pricing/test/simple_offer_test.rb
deleted file mode 100644
index a8dd80eb2..000000000
--- a/ecommerce/pricing/test/simple_offer_test.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-require_relative "test_helper"
-
-module Pricing
- class SimpleOfferTest < Test
- cover "Pricing*"
-
- def test_removing
- product_1_id = SecureRandom.uuid
- set_price(product_1_id, 20)
- order_id = SecureRandom.uuid
- add_item(order_id, product_1_id)
- stream = "Pricing::Offer$#{order_id}"
- assert_events(
- stream,
- OrderTotalValueCalculated.new(
- data: {
- order_id: order_id,
- discounted_amount: 20,
- total_amount: 20
- }
- )
- ) { calculate_total_value(order_id) }
- remove_item(order_id, product_1_id)
- assert_events(
- stream,
- OrderTotalValueCalculated.new(
- data: {
- order_id: order_id,
- discounted_amount: 0,
- total_amount: 0
- }
- )
- ) { calculate_total_value(order_id) }
- end
- end
-end
diff --git a/ecommerce/pricing/test/subamounts_test.rb b/ecommerce/pricing/test/subamounts_test.rb
deleted file mode 100644
index 8328e37df..000000000
--- a/ecommerce/pricing/test/subamounts_test.rb
+++ /dev/null
@@ -1,41 +0,0 @@
-require_relative "test_helper"
-
-module Pricing
- class SubAmounts < Test
-
- def test_calculates_sub_amounts
- set_price(product_id, 20)
-
- assert_events_contain(stream, price_item_value_calculated_event(20, 20)) do
- add_item(order_id, product_id)
- end
- end
-
- private
-
- def price_item_value_calculated_event(amount, discounted_amount)
- PriceItemValueCalculated.new(
- data: {
- order_id: order_id,
- product_id: product_id,
- quantity: 1,
- discounted_amount: discounted_amount,
- amount: amount
- }
- )
- end
-
- def stream
- "Pricing::Offer$#{order_id}"
- end
-
- def product_id
- @product_id ||= SecureRandom.uuid
- end
-
- def order_id
- @order_id ||= SecureRandom.uuid
- end
-
- end
-end
diff --git a/ecommerce/pricing/test/test_helper.rb b/ecommerce/pricing/test/test_helper.rb
deleted file mode 100644
index eb9e385f5..000000000
--- a/ecommerce/pricing/test/test_helper.rb
+++ /dev/null
@@ -1,49 +0,0 @@
-require "minitest/autorun"
-require "mutant/minitest/coverage"
-require "active_support/all"
-
-require_relative "../lib/pricing"
-
-module Pricing
- class Test < Infra::InMemoryTest
-
- def before_setup
- super
- Configuration.new.call(event_store, command_bus)
- end
-
- private
-
- def set_price(product_id, amount)
- run_command(SetPrice.new(product_id: product_id, price: amount))
- end
-
- def set_future_price(product_id, amount, valid_since)
- run_command(SetFuturePrice.new(product_id: product_id, price: amount, valid_since: valid_since))
- end
-
- def calculate_total_value(order_id)
- run_command(CalculateTotalValue.new(order_id: order_id))
- end
-
- def add_item(order_id, product_id)
- run_command(
- AddPriceItem.new(order_id: order_id, product_id: product_id)
- )
- end
-
- def remove_item(order_id, product_id)
- run_command(
- RemovePriceItem.new(order_id: order_id, product_id: product_id)
- )
- end
-
- def register_coupon(uid, name, code, discount)
- run_command(RegisterCoupon.new(coupon_id: uid, name: name, code: code, discount: discount))
- end
-
- def fake_name
- "Fake name"
- end
- end
-end
diff --git a/ecommerce/pricing/test/time_promotion_test.rb b/ecommerce/pricing/test/time_promotion_test.rb
deleted file mode 100644
index e1de88470..000000000
--- a/ecommerce/pricing/test/time_promotion_test.rb
+++ /dev/null
@@ -1,222 +0,0 @@
-require_relative "test_helper"
-
-require "timecop"
-
-module Pricing
- class TimePromotionTest < Test
- cover "Pricing::TimePromotion*"
-
- def test_creates_time_promotion
- uid = SecureRandom.uuid
- start_time = Time.utc(2022, 7, 1, 12, 15, 0)
- end_time = Time.utc(2022, 7, 4, 14, 30, 30)
- discount = 25
- label = "Summer Sale"
- data = {
- time_promotion_id: uid,
- discount: discount,
- start_time: start_time,
- end_time: end_time,
- label: label
- }
-
- run_command = -> { create_time_promotion(**data) }
-
- stream = "Pricing::TimePromotion$#{uid}"
- event = TimePromotionCreated.new(data: data)
-
- assert_events(stream, event) do
- run_command.call
- end
- end
-
- private
-
- def create_time_promotion(**kwargs)
- run_command(CreateTimePromotion.new(kwargs))
- end
- end
-
- class DiscountWithTimePromotionTest < Test
- cover "Pricing*"
-
- def test_calculates_total_value_with_time_promotion
- timestamp = Time.utc(2022, 5, 30, 15, 33)
-
- Timecop.freeze(timestamp) do
- product_1_id = SecureRandom.uuid
- set_price(product_1_id, 20)
- order_id = SecureRandom.uuid
- add_item(order_id, product_1_id)
- stream = stream_name(order_id)
-
- assert_events(
- stream,
- OrderTotalValueCalculated.new(
- data: {
- order_id: order_id,
- discounted_amount: 20,
- total_amount: 20
- }
- )
- ) { calculate_total_value(order_id) }
-
- # Current promotions
- first_time_promotion_id = SecureRandom.uuid
- start_time = timestamp - 1
- end_time = timestamp + 1
- set_time_promotion_range(first_time_promotion_id, start_time, end_time, 49)
-
- time_promotion_id = SecureRandom.uuid
- start_time = timestamp
- end_time = timestamp + 1
- set_time_promotion_range(time_promotion_id, start_time, end_time, 1)
-
- # Not applicable promotions
- time_promotion_id = SecureRandom.uuid
- start_time = timestamp - 2
- end_time = timestamp - 1
- set_time_promotion_range(time_promotion_id, start_time, end_time, 10)
-
- time_promotion_id = SecureRandom.uuid
- start_time = timestamp + 1
- end_time = timestamp + 2
- set_time_promotion_range(time_promotion_id, start_time, end_time, 15)
-
- time_promotion_id = SecureRandom.uuid
- start_time = timestamp - 1
- end_time = timestamp
- set_time_promotion_range(time_promotion_id, start_time, end_time, 15)
-
- assert_events(
- stream,
- OrderTotalValueCalculated.new(
- data: {
- order_id: order_id,
- total_amount: 20,
- discounted_amount: 10,
- }
- )
- ) { calculate_total_value(order_id) }
- end
- end
-
- def test_calculates_sub_amounts_with_combined_discounts
- timestamp = Time.utc(2022, 5, 30, 15, 33)
- Timecop.freeze(timestamp) do
-
- product_1_id = SecureRandom.uuid
- product_2_id = SecureRandom.uuid
- set_price(product_1_id, 20)
- set_price(product_2_id, 30)
- order_id = SecureRandom.uuid
- stream = stream_name(order_id)
-
- assert_events(stream) { calculate_sub_amounts(order_id) }
-
- add_item(order_id, product_1_id)
- add_item(order_id, product_2_id)
- add_item(order_id, product_2_id)
- assert_events(
- stream,
- PriceItemValueCalculated.new(
- data: {
- order_id: order_id,
- product_id: product_1_id,
- quantity: 1,
- amount: 20,
- discounted_amount: 20
- }
- ),
- PriceItemValueCalculated.new(
- data: {
- order_id: order_id,
- product_id: product_2_id,
- quantity: 2,
- amount: 60,
- discounted_amount: 60
- }
- )
- ) { calculate_sub_amounts(order_id) }
- run_command(
- Pricing::SetPercentageDiscount.new(order_id: order_id, amount: 10)
- )
-
- first_time_promotion_id = SecureRandom.uuid
- start_time = timestamp - 1
- end_time = timestamp + 1
- set_time_promotion_range(first_time_promotion_id, start_time, end_time, 50)
-
- assert_events(
- stream,
- PriceItemValueCalculated.new(
- data: {
- order_id: order_id,
- product_id: product_1_id,
- quantity: 1,
- amount: 20,
- discounted_amount: 8
- }
- ),
- PriceItemValueCalculated.new(
- data: {
- order_id: order_id,
- product_id: product_2_id,
- quantity: 2,
- amount: 60,
- discounted_amount: 24
- }
- )
- ) { calculate_sub_amounts(order_id) }
- end
- end
-
- def test_takes_last_values_for_time_promotion
- timestamp = Time.new(2022, 5, 30, 15, 33)
- time_promotion_id = SecureRandom.uuid
- start_time = timestamp - 5
- end_time = timestamp - 2
- set_time_promotion_range(time_promotion_id, start_time, end_time, 30)
-
- start_time = timestamp - 1
- end_time = timestamp + 1
- set_time_promotion_range(time_promotion_id, start_time, end_time, 50)
- set_time_promotion_range(time_promotion_id, start_time, end_time, 40)
-
- Timecop.freeze(timestamp) do
- product_1_id = SecureRandom.uuid
- set_price(product_1_id, 20)
- order_id = SecureRandom.uuid
- add_item(order_id, product_1_id)
- stream = stream_name(order_id)
-
- assert_events(
- stream,
- OrderTotalValueCalculated.new(
- data: {
- order_id: order_id,
- discounted_amount: 12,
- total_amount: 20
- }
- )
- ) { calculate_total_value(order_id) }
- end
- end
-
- private
-
- def stream_name(order_id)
- "Pricing::Offer$#{order_id}"
- end
-
- def set_time_promotion_range(time_promotion_id, start_time, end_time, discount)
- run_command(
- CreateTimePromotion.new(time_promotion_id: time_promotion_id, start_time: start_time, end_time: end_time, discount: discount, label: "test")
- )
- end
-
- def calculate_sub_amounts(order_id)
- run_command(CalculateSubAmounts.new(order_id: order_id))
- end
- end
-end
diff --git a/ecommerce/processes/.mutant.yml b/ecommerce/processes/.mutant.yml
deleted file mode 100644
index e352e65d4..000000000
--- a/ecommerce/processes/.mutant.yml
+++ /dev/null
@@ -1,21 +0,0 @@
-requires:
- - ./test/test_helper
-integration: minitest
-coverage_criteria:
- process_abort: true
-matcher:
- subjects:
- - Processes*
- ignore:
- - Processes::Configuration*
- - Processes::OrderConfirmation#stream_name
- - Processes::Test*
- - Processes::ShipmentProcess*
- - Processes::ReleasePaymentProcess*
- - Processes::OrderItemInvoicingProcess*
- - Processes::SyncShipmentFromOrdering*
- - Processes::SyncInventoryFromOrdering*
- - Processes::NotifyPaymentsAboutOrderValue*
- - Processes::ThreePlusOneFree*
- - Processes::ReservationProcess#build_state
- - Processes::ReservationProcess::ProcessState#call
\ No newline at end of file
diff --git a/ecommerce/processes/Gemfile b/ecommerce/processes/Gemfile
deleted file mode 100644
index 8fd887144..000000000
--- a/ecommerce/processes/Gemfile
+++ /dev/null
@@ -1,4 +0,0 @@
-source "https://rubygems.org"
-
-eval_gemfile "../../infra/Gemfile.test"
-gem "infra", path: "../../infra"
\ No newline at end of file
diff --git a/ecommerce/processes/Gemfile.lock b/ecommerce/processes/Gemfile.lock
deleted file mode 100644
index 4a73abff2..000000000
--- a/ecommerce/processes/Gemfile.lock
+++ /dev/null
@@ -1,119 +0,0 @@
-PATH
- remote: ../../infra
- specs:
- infra (1.0.0)
- aggregate_root (~> 2.13)
- arkency-command_bus
- dry-struct
- dry-types
- rake
- ruby_event_store (~> 2.13)
- ruby_event_store-transformations
- sidekiq
-
-GEM
- remote: https://oss:7AXfeZdAfCqL1PvHm2nvDJO6Zd9UW8IK@gem.mutant.dev/
- specs:
- mutant-license (0.1.1.2.1627430819213747598431630701693729869473.6)
-
-GEM
- remote: https://rubygems.org/
- specs:
- activesupport (7.1.2)
- base64
- bigdecimal
- concurrent-ruby (~> 1.0, >= 1.0.2)
- connection_pool (>= 2.2.5)
- drb
- i18n (>= 1.6, < 2)
- minitest (>= 5.1)
- mutex_m
- tzinfo (~> 2.0)
- aggregate_root (2.13.0)
- ruby_event_store (= 2.13.0)
- arkency-command_bus (0.4.1)
- concurrent-ruby
- ast (2.4.2)
- base64 (0.2.0)
- bigdecimal (3.1.4)
- concurrent-ruby (1.2.2)
- connection_pool (2.4.1)
- diff-lcs (1.5.0)
- drb (2.2.0)
- ruby2_keywords
- dry-core (1.0.1)
- concurrent-ruby (~> 1.0)
- zeitwerk (~> 2.6)
- dry-inflector (1.0.0)
- dry-logic (1.5.0)
- concurrent-ruby (~> 1.0)
- dry-core (~> 1.0, < 2)
- zeitwerk (~> 2.6)
- dry-struct (1.6.0)
- dry-core (~> 1.0, < 2)
- dry-types (>= 1.7, < 2)
- ice_nine (~> 0.11)
- zeitwerk (~> 2.6)
- dry-types (1.7.1)
- concurrent-ruby (~> 1.0)
- dry-core (~> 1.0)
- dry-inflector (~> 1.0)
- dry-logic (~> 1.4)
- zeitwerk (~> 2.6)
- i18n (1.14.1)
- concurrent-ruby (~> 1.0)
- ice_nine (0.11.2)
- minitest (5.15.0)
- mutant (0.11.26)
- diff-lcs (~> 1.3)
- parser (~> 3.2.2, >= 3.2.2.4)
- regexp_parser (~> 2.8.2)
- sorbet-runtime (~> 0.5.0)
- unparser (~> 0.6.9)
- mutant-minitest (0.11.26)
- minitest (~> 5.11)
- mutant (= 0.11.26)
- mutex_m (0.2.0)
- parser (3.2.2.4)
- ast (~> 2.4.1)
- racc
- racc (1.7.3)
- rack (3.0.8)
- rake (13.1.0)
- redis-client (0.19.0)
- connection_pool
- regexp_parser (2.8.3)
- ruby2_keywords (0.0.5)
- ruby_event_store (2.13.0)
- concurrent-ruby (~> 1.0, >= 1.1.6)
- ruby_event_store-transformations (0.1.0)
- activesupport (>= 5.0)
- ruby_event_store (>= 2.0.0, < 3.0.0)
- sidekiq (7.2.0)
- concurrent-ruby (< 2)
- connection_pool (>= 2.3.0)
- rack (>= 2.2.4)
- redis-client (>= 0.14.0)
- sorbet-runtime (0.5.11190)
- tzinfo (2.0.6)
- concurrent-ruby (~> 1.0)
- unparser (0.6.12)
- diff-lcs (~> 1.3)
- parser (>= 3.2.2.4)
- zeitwerk (2.6.12)
-
-PLATFORMS
- arm64-darwin-20
- arm64-darwin-21
- ruby
- x86_64-darwin-20
- x86_64-linux
-
-DEPENDENCIES
- infra!
- minitest (= 5.15.0)!
- mutant-license!
- mutant-minitest (= 0.11.26)!
-
-BUNDLED WITH
- 2.5.9
diff --git a/ecommerce/processes/Makefile b/ecommerce/processes/Makefile
deleted file mode 100644
index 23850fab1..000000000
--- a/ecommerce/processes/Makefile
+++ /dev/null
@@ -1,10 +0,0 @@
-install:
- @bundle install
-
-test:
- @bundle exec ruby -e "require \"rake/rake_test_loader\"" test/*_test.rb
-
-mutate:
- @RAILS_ENV=test bundle exec mutant run
-
-.PHONY: install test mutate
diff --git a/ecommerce/processes/lib/processes.rb b/ecommerce/processes/lib/processes.rb
deleted file mode 100644
index 153c90df6..000000000
--- a/ecommerce/processes/lib/processes.rb
+++ /dev/null
@@ -1,153 +0,0 @@
-require_relative "../../ordering/lib/ordering"
-require_relative "../../pricing/lib/pricing"
-require_relative "../../product_catalog/lib/product_catalog"
-require_relative "../../crm/lib/crm"
-require_relative "../../payments/lib/payments"
-require_relative "../../inventory/lib/inventory"
-require_relative "../../shipping/lib/shipping"
-require_relative "../../taxes/lib/taxes"
-require_relative "../../invoicing/lib/invoicing"
-require_relative "../../fulfillment/lib/fulfillment"
-require_relative 'processes/confirm_order_on_payment_captured'
-require_relative 'processes/release_payment_process'
-require_relative 'processes/shipment_process'
-require_relative 'processes/determine_vat_rates_on_order_placed'
-require_relative 'processes/order_item_invoicing_process'
-require_relative 'processes/notify_payments_about_order_value'
-require_relative 'processes/sync_shipment_from_ordering'
-require_relative 'processes/three_plus_one_free'
-require_relative 'processes/reservation_process'
-
-module Processes
- class Configuration
- class << self
- attr_accessor :event_store, :command_bus
- end
-
- def call(event_store, command_bus)
- self.class.event_store = event_store
- self.class.command_bus = command_bus
- notify_payments_about_order_total_value(event_store, command_bus)
- enable_shipment_sync(event_store, command_bus)
- determine_vat_rates_on_order_placed(event_store, command_bus)
- set_invoice_payment_date_when_order_confirmed(event_store, command_bus)
- enable_product_name_sync(event_store, command_bus)
- confirm_order_on_payment_captured(event_store, command_bus)
- register_order_on_order_placed(event_store, command_bus)
-
- enable_release_payment_process(event_store, command_bus)
- enable_shipment_process(event_store, command_bus)
- enable_order_item_invoicing_process(event_store, command_bus)
- enable_reservation_process(event_store, command_bus)
- build_pricing_offer_from_ordering_items(event_store, command_bus)
- end
-
- private
-
- def enable_shipment_process(event_store, command_bus)
- ShipmentProcess.new(event_store, command_bus)
- end
-
- def build_pricing_offer_from_ordering_items(event_store, command_bus)
- Infra::Process.new(event_store, command_bus)
- .call(Ordering::ItemAddedToBasket, [:order_id, :product_id],
- Pricing::AddPriceItem, [:order_id, :product_id])
- Infra::Process.new(event_store, command_bus)
- .call(Ordering::ItemRemovedFromBasket, [:order_id, :product_id],
- Pricing::RemovePriceItem, [:order_id, :product_id])
- end
-
- def enable_shipment_sync(event_store, command_bus)
- SyncShipmentFromOrdering.new(event_store, command_bus)
- end
-
- def notify_payments_about_order_total_value(event_store, command_bus)
- NotifyPaymentsAboutOrderValue.new(event_store, command_bus)
- end
-
- def confirm_order_on_payment_captured(event_store, command_bus)
- event_store.subscribe(
- ConfirmOrderOnPaymentCaptured.new(command_bus),
- to: [Payments::PaymentCaptured]
- )
- end
-
- def enable_release_payment_process(event_store, command_bus)
- event_store.subscribe(
- ReleasePaymentProcess.new(event_store, command_bus),
- to: [
- Ordering::OrderPlaced,
- Ordering::OrderExpired,
- Fulfillment::OrderConfirmed,
- Payments::PaymentAuthorized,
- Payments::PaymentReleased
- ]
- )
- end
-
- def enable_order_item_invoicing_process(event_store, command_bus)
- event_store.subscribe(
- OrderItemInvoicingProcess.new(event_store, command_bus),
- to: [
- Pricing::PriceItemValueCalculated,
- Taxes::VatRateDetermined
- ]
- )
- end
-
- def determine_vat_rates_on_order_placed(event_store, command_bus)
- event_store.subscribe(
- DetermineVatRatesOnOrderPlaced.new(command_bus),
- to: [Ordering::OrderPlaced]
- )
- end
-
- def enable_product_name_sync(event_store, command_bus)
- Infra::Process.new(event_store, command_bus)
- .call(ProductCatalog::ProductNamed, [:product_id, :name],
- Invoicing::SetProductNameDisplayedOnInvoice, [:product_id, :name_displayed])
- end
-
- def set_invoice_payment_date_when_order_confirmed(event_store, command_bus)
- event_store.subscribe(
- ->(event) do
- command_bus.call(
- Invoicing::SetPaymentDate.new(
- invoice_id: event.data.fetch(:order_id),
- payment_date: Time.zone.at(event.metadata.fetch(:timestamp)).to_date
- )
- )
- end,
- to: [Fulfillment::OrderConfirmed]
- )
- end
-
- def enable_three_plus_one_free_process(event_store, command_bus)
- ThreePlusOneFree.new(event_store, command_bus)
- end
-
- def enable_reservation_process(event_store, command_bus)
- event_store.subscribe(
- ReservationProcess.new,
- to: [
- Ordering::OrderSubmitted,
- Fulfillment::OrderCancelled,
- Fulfillment::OrderConfirmed
- ]
- )
- end
-
- def register_order_on_order_placed(event_store, command_bus)
- event_store.subscribe(
- ->(event) do
- command_bus.call(
- Fulfillment::RegisterOrder.new(
- order_id: event.data.fetch(:order_id)
- )
- )
- end,
- to: [Ordering::OrderPlaced]
- )
- end
- end
-end
diff --git a/ecommerce/processes/lib/processes/confirm_order_on_payment_captured.rb b/ecommerce/processes/lib/processes/confirm_order_on_payment_captured.rb
deleted file mode 100644
index e1d671dac..000000000
--- a/ecommerce/processes/lib/processes/confirm_order_on_payment_captured.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-module Processes
- class ConfirmOrderOnPaymentCaptured
-
- def initialize(command_bus)
- @command_bus = command_bus
- end
-
- def call(event)
- order_id = event.data.fetch(:order_id)
- command_bus.call(Fulfillment::ConfirmOrder.new(order_id: order_id))
- end
-
- private
- attr_reader :command_bus
- end
-end
diff --git a/ecommerce/processes/lib/processes/determine_vat_rates_on_order_placed.rb b/ecommerce/processes/lib/processes/determine_vat_rates_on_order_placed.rb
deleted file mode 100644
index 71b55983f..000000000
--- a/ecommerce/processes/lib/processes/determine_vat_rates_on_order_placed.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-module Processes
- class DetermineVatRatesOnOrderPlaced
- def initialize(command_bus)
- @command_bus = command_bus
- end
-
- def call(event)
- order_id = event.data.fetch(:order_id)
- event.data.fetch(:order_lines).each do |product_quantity_hash|
- product_id = product_quantity_hash.first
- command = Taxes::DetermineVatRate.new(order_id: order_id, product_id: product_id)
- command_bus.call(command)
- end
- end
-
- private
-
- attr_reader :command_bus
- end
-end
\ No newline at end of file
diff --git a/ecommerce/processes/lib/processes/notify_payments_about_order_value.rb b/ecommerce/processes/lib/processes/notify_payments_about_order_value.rb
deleted file mode 100644
index 46fd6c246..000000000
--- a/ecommerce/processes/lib/processes/notify_payments_about_order_value.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-module Processes
- class NotifyPaymentsAboutOrderValue
- def initialize(event_store, command_bus)
- event_store.subscribe(
- ->(event) do
- command_bus.call(
- Payments::SetPaymentAmount.new(
- order_id: event.data.fetch(:order_id),
- amount: event.data.fetch(:discounted_amount).to_f
- )
- )
- end,
- to: [Pricing::OrderTotalValueCalculated]
- )
- end
- end
-end
\ No newline at end of file
diff --git a/ecommerce/processes/lib/processes/order_item_invoicing_process.rb b/ecommerce/processes/lib/processes/order_item_invoicing_process.rb
deleted file mode 100644
index d03b1a0a1..000000000
--- a/ecommerce/processes/lib/processes/order_item_invoicing_process.rb
+++ /dev/null
@@ -1,87 +0,0 @@
-module Processes
- class OrderItemInvoicingProcess
- def initialize(event_store, command_bus)
- @event_store = event_store
- @command_bus = command_bus
- end
-
- def call(event)
- state = build_state(event)
- return unless state.create_invoice_item?
-
- unit_prices = MoneySplitter.new(state.discounted_amount, Array.new(state.quantity, 1)).call
- unit_prices.tally.each do |unit_price, quantity|
- command_bus.call(Invoicing::AddInvoiceItem.new(
- invoice_id: state.order_id,
- product_id: state.product_id,
- vat_rate: state.vat_rate,
- quantity: quantity,
- unit_price: unit_price
- ))
- end
- end
-
- private
-
- attr_reader :event_store, :command_bus
-
- def build_state(event)
- stream_name = "OrderInvoicingProcess$#{event.data.fetch(:order_id)}$#{event.data.fetch(:product_id)}"
- past = event_store.read.stream(stream_name).to_a
- last_stored = past.size - 1
- event_store.link(event.event_id, stream_name: stream_name, expected_version: last_stored)
- ProcessState.new.tap do |state|
- past.each { |ev| state.call(ev) }
- state.call(event)
- end
- rescue RubyEventStore::WrongExpectedEventVersion
- retry
- end
-
- class ProcessState
- attr_reader :order_id, :product_id, :quantity, :vat_rate, :discounted_amount
-
- def call(event)
- @order_id ||= event.data.fetch(:order_id)
- @product_id ||= event.data.fetch(:product_id)
- case event
- when Pricing::PriceItemValueCalculated
- @quantity = event.data.fetch(:quantity)
- @discounted_amount = event.data.fetch(:discounted_amount)
- when Taxes::VatRateDetermined
- @vat_rate = event.data.fetch(:vat_rate).symbolize_keys
- end
- end
-
- def create_invoice_item?
- [order_id, product_id, quantity, vat_rate, discounted_amount].all?
- end
- end
- end
-
- class MoneySplitter
- def initialize(amount, weights)
- raise ArgumentError unless weights.instance_of? Array
- raise ArgumentError if weights.empty?
- @amount = amount
- @weights = weights
- end
-
- def call
- distributed_amounts = []
- total_weight = @weights.sum.to_d
- @weights.each do |weight|
- if total_weight.eql?(0)
- distributed_amounts << 0
- next
- end
- p = weight / total_weight
- distributed_amount = (p * @amount).round(2)
- distributed_amounts << distributed_amount
- total_weight -= weight
- @amount -= distributed_amount
- end
- distributed_amounts
- end
- end
-end
\ No newline at end of file
diff --git a/ecommerce/processes/lib/processes/registration_process.rb b/ecommerce/processes/lib/processes/registration_process.rb
deleted file mode 100644
index e69b542b0..000000000
--- a/ecommerce/processes/lib/processes/registration_process.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-# frozen_string_literal: true
-
-class RegistrationProcess
-end
diff --git a/ecommerce/processes/lib/processes/release_payment_process.rb b/ecommerce/processes/lib/processes/release_payment_process.rb
deleted file mode 100644
index e85a338b5..000000000
--- a/ecommerce/processes/lib/processes/release_payment_process.rb
+++ /dev/null
@@ -1,63 +0,0 @@
-module Processes
- class ReleasePaymentProcess
- def initialize(event_store, command_bus)
- @event_store = event_store
- @command_bus = command_bus
- end
-
- def call(event)
- state = build_state(event)
- release_payment(state) if state.release?
- end
-
- private
-
- def release_payment(state)
- command_bus.call(Payments::ReleasePayment.new(order_id: state.order_id))
- end
-
- attr_reader :command_bus, :event_store
-
- def build_state(event)
- stream_name = "PaymentProcess$#{event.data.fetch(:order_id)}"
- past_events = event_store.read.stream(stream_name).to_a
- last_stored = past_events.size - 1
- event_store.link(event.event_id, stream_name: stream_name, expected_version: last_stored)
- ProcessState.new.tap do |state|
- past_events.each { |ev| state.call(ev) }
- state.call(event)
- end
- rescue RubyEventStore::WrongExpectedEventVersion
- retry
- end
-
- class ProcessState
- def initialize
- @order = :draft
- @payment = :none
- end
-
- attr_reader :order_id
-
- def call(event)
- case event
- when Payments::PaymentAuthorized
- @payment = :authorized
- when Payments::PaymentReleased
- @payment = :released
- when Ordering::OrderPlaced
- @order = :placed
- @order_id = event.data.fetch(:order_id)
- when Ordering::OrderExpired
- @order = :expired
- when Fulfillment::OrderConfirmed
- @order = :confirmed
- end
- end
-
- def release?
- @payment.eql?(:authorized) && @order.eql?(:expired)
- end
- end
- end
-end
\ No newline at end of file
diff --git a/ecommerce/processes/lib/processes/reservation_process.rb b/ecommerce/processes/lib/processes/reservation_process.rb
deleted file mode 100644
index fa920cea2..000000000
--- a/ecommerce/processes/lib/processes/reservation_process.rb
+++ /dev/null
@@ -1,96 +0,0 @@
-module Processes
- class ReservationProcess
- def initialize
- @event_store = Configuration.event_store
- @command_bus = Configuration.command_bus
- end
- attr_accessor :event_store, :command_bus
-
- def call(event)
- state = build_state(event)
- case event.event_type
- when 'Ordering::OrderSubmitted'
- begin
- reserve_stock(state)
- rescue Inventory::InventoryEntry::InventoryNotAvailable
- release_stock(state)
- reject_order(state)
- else
- accept_order(state)
- end
- when 'Fulfillment::OrderCancelled'
- release_stock(state)
- when 'Fulfillment::OrderConfirmed'
- dispatch_stock(state)
- end
- end
-
- private
-
- def reserve_stock(state)
- state.order_lines.each do |product_id, quantity|
- command_bus.(Inventory::Reserve.new(product_id: product_id, quantity: quantity))
- state.product_reserved(product_id)
- end
- end
-
- def release_stock(state)
- state.order_lines.slice(*state.reserved_product_ids).each do |product_id, quantity|
- command_bus.(Inventory::Release.new(product_id: product_id, quantity: quantity))
- end
- end
-
- def dispatch_stock(state)
- state.order_lines.each do |product_id, quantity|
- command_bus.(Inventory::Dispatch.new(product_id: product_id, quantity: quantity))
- end
- end
-
- def accept_order(state)
- command_bus.(Ordering::AcceptOrder.new(order_id: state.order_id))
- end
-
- def reject_order(state)
- command_bus.(Ordering::RejectOrder.new(order_id: state.order_id))
- end
-
- def build_state(event)
- stream_name = "ReservationProcess$#{event.data.fetch(:order_id)}"
- begin
- past_events = event_store.read.stream(stream_name).to_a
- last_stored = past_events.size - 1
- event_store.link(event.event_id, stream_name: stream_name, expected_version: last_stored)
- rescue RubyEventStore::WrongExpectedEventVersion
- retry
- rescue RubyEventStore::EventDuplicatedInStream
- return
- end
- ProcessState.new.tap do |state|
- past_events.each { |ev| state.call(ev) }
- state.call(event)
- end
- end
-
- class ProcessState
- def initialize()
- @reserved_product_ids = []
- end
-
- attr_reader :order_id, :order_lines, :reserved_product_ids
-
- def call(event)
- case event
- when Ordering::OrderSubmitted
- @order_lines = event.data.fetch(:order_lines)
- @order_id = event.data.fetch(:order_id)
- when Fulfillment::OrderCancelled, Fulfillment::OrderConfirmed
- @reserved_product_ids = order_lines.keys
- end
- end
-
- def product_reserved(product_id)
- reserved_product_ids << product_id
- end
- end
- end
-end
\ No newline at end of file
diff --git a/ecommerce/processes/lib/processes/shipment_process.rb b/ecommerce/processes/lib/processes/shipment_process.rb
deleted file mode 100644
index 0a04dae74..000000000
--- a/ecommerce/processes/lib/processes/shipment_process.rb
+++ /dev/null
@@ -1,81 +0,0 @@
-module Processes
- class ShipmentProcess
- def initialize(event_store, command_bus)
- @event_store = event_store
- @command_bus = command_bus
- @event_store.subscribe(
- self,
- to: [
- Shipping::ShippingAddressAddedToShipment,
- Shipping::ShipmentSubmitted,
- Ordering::OrderPlaced,
- Fulfillment::OrderConfirmed
- ]
- )
- end
-
- def call(event)
- state = build_state(event)
- submit_shipment(state) if state.submit?
- authorize_shipment(state) if state.authorize?
- end
-
- private
-
- def submit_shipment(state)
- command_bus.call(Shipping::SubmitShipment.new(order_id: state.order_id))
- end
-
- def authorize_shipment(state)
- command_bus.call(Shipping::AuthorizeShipment.new(order_id: state.order_id))
- end
-
- attr_reader :command_bus, :event_store
-
- def build_state(event)
- stream_name = "ShipmentProcess$#{event.data.fetch(:order_id)}"
- past_events = event_store.read.stream(stream_name).to_a
- last_stored = past_events.size - 1
- event_store.link(event.event_id, stream_name: stream_name, expected_version: last_stored)
- ProcessState.new.tap do |state|
- past_events.each { |ev| state.call(ev) }
- state.call(event)
- end
- rescue RubyEventStore::WrongExpectedEventVersion
- retry
- end
-
- class ProcessState
- def initialize
- @order = :draft
- @shipment = :draft
- end
-
- attr_reader :order_id
-
- def call(event)
- case event
- when Shipping::ShippingAddressAddedToShipment
- @shipment = :address_set
- when Shipping::ShipmentSubmitted
- @shipment = :submitted
- when Ordering::OrderPlaced
- @order = :placed
- @order_id = event.data.fetch(:order_id)
- when Fulfillment::OrderConfirmed
- @order = :confirmed
- end
- end
-
- def submit?
- return false if @shipment == :submitted
-
- @shipment == :address_set && @order != :draft
- end
-
- def authorize?
- @shipment == :address_set && @order == :confirmed
- end
- end
- end
-end
\ No newline at end of file
diff --git a/ecommerce/processes/lib/processes/sync_shipment_from_ordering.rb b/ecommerce/processes/lib/processes/sync_shipment_from_ordering.rb
deleted file mode 100644
index 4cceacd17..000000000
--- a/ecommerce/processes/lib/processes/sync_shipment_from_ordering.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-module Processes
- class SyncShipmentFromOrdering
- def initialize(event_store, command_bus)
- Infra::Process.new(event_store, command_bus)
- .call(Ordering::ItemAddedToBasket, [:order_id, :product_id],
- Shipping::AddItemToShipmentPickingList, [:order_id, :product_id])
- Infra::Process.new(event_store, command_bus)
- .call(Ordering::ItemRemovedFromBasket, [:order_id, :product_id],
- Shipping::RemoveItemFromShipmentPickingList, [:order_id, :product_id])
- end
- end
-end
\ No newline at end of file
diff --git a/ecommerce/processes/lib/processes/three_plus_one_free.rb b/ecommerce/processes/lib/processes/three_plus_one_free.rb
deleted file mode 100644
index 5e85a0027..000000000
--- a/ecommerce/processes/lib/processes/three_plus_one_free.rb
+++ /dev/null
@@ -1,119 +0,0 @@
-module Processes
- class ThreePlusOneFree
-
- def initialize(event_store, command_bus)
- @event_store = event_store
- @command_bus = command_bus
- @event_store.subscribe(
- self,
- to: [
- Pricing::PriceItemAdded,
- Pricing::PriceItemRemoved,
- Pricing::ProductMadeFreeForOrder,
- Pricing::FreeProductRemovedFromOrder
- ]
- )
- end
-
- def call(event)
- state = build_state(event)
- return if event_only_for_state_building?(event)
-
- make_or_remove_free_product(state)
- end
-
- private
-
- def build_state(event)
- stream_name = "ThreePlusOneFreeProcess$#{event.data.fetch(:order_id)}"
- past_events = @event_store.read.stream(stream_name).to_a
- last_stored = past_events.size - 1
- @event_store.link(event.event_id, stream_name: stream_name, expected_version: last_stored)
- ProcessState.new(event.data.fetch(:order_id)).tap do |state|
- past_events.each { |ev| state.call(ev) }
- state.call(event)
- end
- rescue RubyEventStore::WrongExpectedEventVersion
- retry
- end
-
- def make_or_remove_free_product(state)
- pricing_catalog = Pricing::PricingCatalog.new(@event_store)
- free_product_id = FreeProductResolver.new(state, pricing_catalog).call
-
- return if current_free_product_not_changed?(free_product_id, state)
-
- remove_old_free_product(state)
- make_new_product_for_free(state, free_product_id)
- end
-
- def event_only_for_state_building?(event)
- event.instance_of?(Pricing::FreeProductRemovedFromOrder) || event.instance_of?(Pricing::ProductMadeFreeForOrder)
- end
-
- def current_free_product_not_changed?(free_product_id, state)
- free_product_id == state.current_free_product_id
- end
-
- def remove_old_free_product(state)
- @command_bus.call(Pricing::RemoveFreeProductFromOrder.new(order_id: state.order_id, product_id: state.current_free_product_id)) if state.current_free_product_id
- end
-
- def make_new_product_for_free(state, free_product_id)
- @command_bus.call(Pricing::MakeProductFreeForOrder.new(order_id: state.order_id, product_id: free_product_id)) if free_product_id
- end
-
- class ProcessState
- attr_reader :order_id, :order_lines, :current_free_product_id
-
- def initialize(order_id)
- @order_id = order_id
- @order_lines = Hash.new(0)
- end
-
- def call(event)
- product_id = event.data.fetch(:product_id)
- case event
- when Pricing::PriceItemAdded
- order_lines[product_id] += 1
- when Pricing::PriceItemRemoved
- order_lines[product_id] -= 1
- order_lines.delete(product_id) if order_lines.fetch(product_id) <= 0
- when Pricing::ProductMadeFreeForOrder
- @current_free_product_id = product_id
- when Pricing::FreeProductRemovedFromOrder
- @current_free_product_id = nil
- end
- end
-
- def total_quantity
- order_lines.values.sum
- end
- end
-
- class FreeProductResolver
- MIN_ORDER_LINES_QUANTITY = 4
-
- def initialize(state, pricing_catalog)
- @state = state
- @pricing_catalog = pricing_catalog
- end
-
- def call
- cheapest_product if eligible_for_free_product?
- end
-
- private
-
- attr_reader :state, :pricing_catalog
-
- def cheapest_product
- state.order_lines.keys.sort_by { |product_id| pricing_catalog.price_by_product_id(product_id) }.first
- end
-
- def eligible_for_free_product?
- state.total_quantity >= MIN_ORDER_LINES_QUANTITY
- end
- end
- end
-end
diff --git a/ecommerce/processes/test/determine_vat_rate_test.rb b/ecommerce/processes/test/determine_vat_rate_test.rb
deleted file mode 100644
index edbd3e27e..000000000
--- a/ecommerce/processes/test/determine_vat_rate_test.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-require_relative "test_helper"
-
-module Processes
- class DetermineVatRateTest < Test
- cover "Processes::DetermineVatRatesOnOrderPlaced*"
-
- def test_inventory_available_error_is_raised
- product_id = SecureRandom.uuid
- order_id = SecureRandom.uuid
- process = DetermineVatRatesOnOrderPlaced.new(command_bus)
- given([order_placed(order_id, product_id)]).each do |event|
- process.call(event)
- end
- assert_command(Taxes::DetermineVatRate.new(order_id: order_id, product_id: product_id))
- end
-
- private
-
- def order_placed order_id, product_id
- Ordering::OrderPlaced.new(
- data: {
- order_id: order_id,
- order_number: order_number,
- customer_id: customer_id,
- order_lines: { product_id => 1 }
- }
- )
- end
- end
-end
diff --git a/ecommerce/processes/test/money_splitter_test.rb b/ecommerce/processes/test/money_splitter_test.rb
deleted file mode 100644
index 379ba9874..000000000
--- a/ecommerce/processes/test/money_splitter_test.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-require_relative "test_helper"
-
-module Processes
- class MoneySplitterTest < Minitest::Test
- cover "Processes::MoneySplitter"
-
- def test_splitting_money_without_losing_cents
- assert_equal([0.01, 0.01, 0.01], MoneySplitter.new(0.03, [1, 1, 1]).call)
- assert_equal([0.01, 0.02], MoneySplitter.new(0.03, [1, 1]).call.sort)
- assert_equal([0, 0, 0.01, 0.01, 0.01], MoneySplitter.new(0.03, [1, 1, 1, 1, 1]).call.sort)
- assert_equal([0, 0, 0.03], MoneySplitter.new(0.03, [1, 0, 0]).call.sort)
-
- assert_raises(ArgumentError) { MoneySplitter.new(0.03, nil).call }
- assert_raises(ArgumentError) { MoneySplitter.new(0.03, 'not nil nor array').call }
- assert_raises(ArgumentError) { MoneySplitter.new(0.03, []).call }
- end
- end
-end
\ No newline at end of file
diff --git a/ecommerce/processes/test/order_confirmation_test.rb b/ecommerce/processes/test/order_confirmation_test.rb
deleted file mode 100644
index 3278e67f7..000000000
--- a/ecommerce/processes/test/order_confirmation_test.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-require_relative "test_helper"
-
-module Processes
- class OrderConfirmationTest < Test
- cover "Processes::OrderConfirmation"
-
- def test_payment_confirms_order
- process = ConfirmOrderOnPaymentCaptured.new(command_bus)
- given([payment_authorized]).each do |event|
- process.call(event)
- end
- assert_command(Fulfillment::ConfirmOrder.new(order_id: order_id))
- end
- end
-end
diff --git a/ecommerce/processes/test/order_item_invoicing_process_test.rb b/ecommerce/processes/test/order_item_invoicing_process_test.rb
deleted file mode 100644
index 97526be44..000000000
--- a/ecommerce/processes/test/order_item_invoicing_process_test.rb
+++ /dev/null
@@ -1,43 +0,0 @@
-require_relative "test_helper"
-
-module Processes
- class OrderItemInvoicingProcessTest < Test
- cover "Processes::OrderItemInvoicingProcess*"
-
- def test_invoice_item_being_created
- product_id = SecureRandom.uuid
- amount = 100.to_d
- discounted_amount = 90.to_d
- quantity = 5
- vat_rate = Infra::Types::VatRate.new(rate: 20, code: "20")
-
- item_value_calculated = Pricing::PriceItemValueCalculated.new(
- data: {
- order_id: order_id,
- product_id: product_id,
- quantity: quantity,
- amount: amount,
- discounted_amount: discounted_amount
- }
- )
- vat_rate_determined = Taxes::VatRateDetermined.new(
- data: {
- order_id: order_id,
- product_id: product_id,
- vat_rate: vat_rate
- }
- )
- process = OrderItemInvoicingProcess.new(event_store, command_bus)
- given([item_value_calculated, vat_rate_determined]).each do |event|
- process.call(event)
- end
- assert_command(Invoicing::AddInvoiceItem.new(
- invoice_id: order_id,
- product_id: product_id,
- quantity: quantity,
- vat_rate: vat_rate,
- unit_price: 18.to_d
- ))
- end
- end
-end
\ No newline at end of file
diff --git a/ecommerce/processes/test/release_payment_process_test.rb b/ecommerce/processes/test/release_payment_process_test.rb
deleted file mode 100644
index 29f0a551d..000000000
--- a/ecommerce/processes/test/release_payment_process_test.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-require_relative "test_helper"
-
-module Processes
- class ReleasePaymentProcessTest < Test
- cover "Processes::ReleasePaymentProcess*"
-
- def test_happy_path
- process = ReleasePaymentProcess.new(event_store, command_bus)
- given([order_placed, payment_authorized, order_confirmed]).each do |event|
- process.call(event)
- end
- assert_no_command
- end
-
- def test_order_expired_without_payment
- process = ReleasePaymentProcess.new(event_store, command_bus)
- given([order_placed, order_expired]).each { |event| process.call(event) }
- assert_no_command
- end
-
- def test_order_expired_after_payment_authorization
- process = ReleasePaymentProcess.new(event_store, command_bus)
- given([order_placed, payment_authorized, order_expired]).each do |event|
- process.call(event)
- end
- assert_command(Payments::ReleasePayment.new(order_id: order_id),)
- end
-
- def test_order_expired_after_payment_released
- process = ReleasePaymentProcess.new(event_store, command_bus)
- given([order_placed, payment_authorized, payment_released, order_expired]).each do |event|
- process.call(event)
- end
- assert_no_command
- end
- end
-end
\ No newline at end of file
diff --git a/ecommerce/processes/test/reservation_process_test.rb b/ecommerce/processes/test/reservation_process_test.rb
deleted file mode 100644
index a71aa122c..000000000
--- a/ecommerce/processes/test/reservation_process_test.rb
+++ /dev/null
@@ -1,108 +0,0 @@
-require_relative "test_helper"
-
-module Processes
- class ReservationProcessTest < Test
- cover "Processes::ReservationProcess*"
-
- def test_happy_path
- process = ReservationProcess.new
- given([order_submitted]).each { |event| process.call(event) }
- assert_all_commands(
- Inventory::Reserve.new(product_id: product_id, quantity: 1),
- Inventory::Reserve.new(product_id: another_product_id, quantity: 2),
- Ordering::AcceptOrder.new(order_id: order_id)
- )
- end
-
- class EnhancedFakeCommandBus < SimpleDelegator
- def initialize(command_bus, command_error_hash = {})
- super(command_bus)
- @command_error_hash = command_error_hash
- end
-
- def call(command)
- super(command)
- raise @command_error_hash[command] if @command_error_hash[command]
- end
- end
-
- def test_reject_order_command_is_dispatched_when_sth_is_unavailable
- failing_command = Inventory::Reserve.new(product_id: product_id, quantity: 1)
- enhanced_command_bus = EnhancedFakeCommandBus.new(command_bus, failing_command => Inventory::InventoryEntry::InventoryNotAvailable)
- process = ReservationProcess.new
- process.command_bus = enhanced_command_bus
- given([order_submitted]).each { |event| process.call(event) }
- assert_all_commands(
- failing_command,
- Ordering::RejectOrder.new(order_id: order_id)
- )
- end
-
- def test_compensation_when_sth_is_unavailable
- failing_command = Inventory::Reserve.new(product_id: another_product_id, quantity: 2)
- enhanced_command_bus = EnhancedFakeCommandBus.new(command_bus, failing_command => Inventory::InventoryEntry::InventoryNotAvailable)
- process = ReservationProcess.new
- process.command_bus = enhanced_command_bus
- given([order_submitted]).each { |event| process.call(event) }
- assert_all_commands(
- Inventory::Reserve.new(product_id: product_id, quantity: 1),
- failing_command,
- Inventory::Release.new(product_id: product_id, quantity: 1),
- Ordering::RejectOrder.new(order_id: order_id)
- )
- end
-
- def test_release_stock_when_order_is_cancelled
- process = ReservationProcess.new
- given([order_submitted]).each { |event| process.call(event) }
-
- command_bus.clear_all_received
- given([order_cancelled]).each { |event| process.call(event) }
- assert_all_commands(
- Inventory::Release.new(product_id: product_id, quantity: 1),
- Inventory::Release.new(product_id: another_product_id, quantity: 2)
- )
- end
-
- def test_dispatch_stock_when_order_is_confirmed
- process = ReservationProcess.new
- given([order_submitted]).each { |event| process.call(event) }
-
- command_bus.clear_all_received
- given([order_confirmed]).each { |event| process.call(event) }
- assert_all_commands(
- Inventory::Dispatch.new(product_id: product_id, quantity: 1),
- Inventory::Dispatch.new(product_id: another_product_id, quantity: 2)
- )
- end
-
- private
-
- def product_id
- @product_id ||= SecureRandom.uuid
- end
-
- def another_product_id
- @another_product_id ||= SecureRandom.uuid
- end
-
- def order_submitted
- Ordering::OrderSubmitted.new(
- data: {
- order_id: order_id,
- order_number: order_number,
- customer_id: customer_id,
- order_lines: { product_id => 1, another_product_id => 2 }
- }
- )
- end
-
- def order_cancelled
- Fulfillment::OrderCancelled.new(
- data: {
- order_id: order_id
- }
- )
- end
- end
-end
\ No newline at end of file
diff --git a/ecommerce/processes/test/test_helper.rb b/ecommerce/processes/test/test_helper.rb
deleted file mode 100644
index 2c446bc06..000000000
--- a/ecommerce/processes/test/test_helper.rb
+++ /dev/null
@@ -1,100 +0,0 @@
-require "minitest/autorun"
-require "mutant/minitest/coverage"
-require "infra"
-require_relative "../lib/processes"
-
-module Processes
- class Test < Minitest::Test
- include Infra::TestPlumbing.with(
- event_store: -> { Infra::EventStore.in_memory },
- command_bus: -> { FakeCommandBus.new }
- )
-
- def before_setup
- super
- Configuration.new.call(event_store, command_bus)
- end
-
- def assert_command(command)
- assert_equal(command, @command_bus.received)
- end
-
- def assert_all_commands(*commands)
- assert_equal(commands, @command_bus.all_received)
- end
-
- def assert_no_command
- assert_nil(@command_bus.received)
- end
-
- private
-
- class FakeCommandBus
- attr_reader :received, :all_received
-
- def initialize
- @all_received = []
- end
-
- def call(command)
- @received = command
- @all_received << command
- end
-
- def clear_all_received
- @all_received, @received = [], nil
- end
- end
-
- def order_id
- @order_id ||= SecureRandom.uuid
- end
-
- def order_number
- "2018/12/16"
- end
-
- def customer_id
- @customer_id ||= SecureRandom.uuid
- end
-
- def given(events, store: event_store)
- events.each { |ev| store.append(ev) }
- events
- end
-
- def order_placed
- Ordering::OrderPlaced.new(
- data: {
- order_id: order_id,
- order_number: order_number,
- customer_id: customer_id
- }
- )
- end
-
- def order_expired
- Ordering::OrderExpired.new(data: { order_id: order_id })
- end
-
- def order_confirmed
- Fulfillment::OrderConfirmed.new(data: { order_id: order_id })
- end
-
- def order_cancelled
- Fulfillment::OrderCancelled.new(data: { order_id: order_id })
- end
-
- def payment_authorized
- Payments::PaymentAuthorized.new(data: { order_id: order_id })
- end
-
- def payment_captured
- Payments::PaymentCaptured.new(data: { order_id: order_id })
- end
-
- def payment_released
- Payments::PaymentReleased.new(data: { order_id: order_id })
- end
- end
-end
diff --git a/ecommerce/processes/test/three_plus_one_free_test.rb b/ecommerce/processes/test/three_plus_one_free_test.rb
deleted file mode 100644
index d14141806..000000000
--- a/ecommerce/processes/test/three_plus_one_free_test.rb
+++ /dev/null
@@ -1,160 +0,0 @@
-require_relative "test_helper"
-
-module Processes
- class ThreePlusOneFreeTest < Test
- cover "Processes::ThreePlusOneFree*"
-
- def test_one_order_line_is_not_eligible_for_free_product
- product_id = SecureRandom.uuid
- order_id = SecureRandom.uuid
- process = ThreePlusOneFree.new(event_store, command_bus)
- given(item_added_event(order_id, product_id, 1)).each do |event|
- process.call(event)
- end
- assert_no_command
- end
-
- def test_four_order_lines_are_eligible_for_free_product
- product_id = SecureRandom.uuid
- order_id = SecureRandom.uuid
- process = ThreePlusOneFree.new(event_store, command_bus)
- given([set_price(product_id, 20)])
- given(item_added_event(order_id, product_id, 4)).each do |event|
- process.call(event)
- end
- assert_command(Pricing::MakeProductFreeForOrder.new(order_id: order_id, product_id: product_id))
- end
-
- def test_remove_free_product_when_order_lines_qtn_is_less_than_four
- product_id = SecureRandom.uuid
- order_id = SecureRandom.uuid
- process = ThreePlusOneFree.new(event_store, command_bus)
- given([set_price(product_id, 20)])
- given(item_added_event(order_id, product_id, 4) +
- product_made_for_free(order_id, product_id) +
- item_removed_event(order_id, product_id, 1) +
- free_product_removed(order_id, product_id)).each do |event|
- process.call(event)
- end
-
- assert_all_commands(Pricing::MakeProductFreeForOrder.new(order_id: order_id, product_id: product_id),
- Pricing::RemoveFreeProductFromOrder.new(order_id: order_id, product_id: product_id))
- end
-
- def test_change_free_product_if_new_order_line_is_the_cheapest
- product_id = SecureRandom.uuid
- cheapest_product_id = SecureRandom.uuid
- order_id = SecureRandom.uuid
- process = ThreePlusOneFree.new(event_store, command_bus)
- given([set_price(product_id, 20)])
- given([set_price(cheapest_product_id, 1)])
-
- given(item_added_event(order_id, product_id, 4) +
- product_made_for_free(order_id, product_id) +
- item_added_event(order_id, cheapest_product_id, 1) +
- free_product_removed(order_id, product_id) +
- product_made_for_free(order_id, cheapest_product_id)
- ).each do |event|
- process.call(event)
- end
-
- assert_all_commands(Pricing::MakeProductFreeForOrder.new(order_id: order_id, product_id: product_id),
- Pricing::RemoveFreeProductFromOrder.new(order_id: order_id, product_id: product_id),
- Pricing::MakeProductFreeForOrder.new(order_id: order_id, product_id: cheapest_product_id))
- end
-
- def test_do_not_change_free_product_if_new_order_line_is_more_expensive
- product_id = SecureRandom.uuid
- more_expensive_product_id = SecureRandom.uuid
- order_id = SecureRandom.uuid
- process = ThreePlusOneFree.new(event_store, command_bus)
- given([set_price(product_id, 20)])
- given([set_price(more_expensive_product_id, 50)])
-
- given(item_added_event(order_id, product_id, 4) +
- product_made_for_free(order_id, product_id) +
- item_added_event(order_id, more_expensive_product_id, 1)).each do |event|
- process.call(event)
- end
-
- assert_all_commands(Pricing::MakeProductFreeForOrder.new(order_id: order_id, product_id: product_id))
- end
-
- def test_change_free_product_if_the_cheapest_order_line_is_removed
- product_id = SecureRandom.uuid
- cheapest_product_id = SecureRandom.uuid
- order_id = SecureRandom.uuid
- process = ThreePlusOneFree.new(event_store, command_bus)
- given([set_price(product_id, 20)])
- given([set_price(cheapest_product_id, 1)])
-
- given(item_added_event(order_id, product_id, 4) +
- product_made_for_free(order_id, product_id) +
- item_added_event(order_id, cheapest_product_id, 1) +
- free_product_removed(order_id, product_id) +
- product_made_for_free(order_id, cheapest_product_id) +
- item_removed_event(order_id, cheapest_product_id, 1) +
- free_product_removed(order_id, cheapest_product_id) +
- product_made_for_free(order_id, product_id)).each do |event|
- process.call(event)
- end
-
- assert_all_commands(Pricing::MakeProductFreeForOrder.new(order_id: order_id, product_id: product_id),
- Pricing::RemoveFreeProductFromOrder.new(order_id: order_id, product_id: product_id),
- Pricing::MakeProductFreeForOrder.new(order_id: order_id, product_id: cheapest_product_id),
- Pricing::RemoveFreeProductFromOrder.new(order_id: order_id, product_id: cheapest_product_id),
- Pricing::MakeProductFreeForOrder.new(order_id: order_id, product_id: product_id)
- )
- end
-
- private
-
- def set_price(product_id, amount)
- Pricing::PriceSet.new(data: { product_id: product_id, price: amount })
- end
-
- def item_added_event(order_id, product_id, times)
- times.times.collect do
- Pricing::PriceItemAdded.new(
- data: {
- order_id: order_id,
- product_id: product_id
- }
- )
- end
- end
-
- def item_removed_event(order_id, product_id, times)
- times.times.collect do
- Pricing::PriceItemRemoved.new(
- data: {
- order_id: order_id,
- product_id: product_id
- }
- )
- end
- end
-
- def product_made_for_free(order_id, product_id)
- [
- Pricing::ProductMadeFreeForOrder.new(
- data: {
- order_id: order_id,
- product_id: product_id
- }
- )
- ]
- end
-
- def free_product_removed(order_id, product_id)
- [
- Pricing::FreeProductRemovedFromOrder.new(
- data: {
- order_id: order_id,
- product_id: product_id
- }
- )
- ]
- end
- end
-end
diff --git a/ecommerce/product_catalog/.mutant.yml b/ecommerce/product_catalog/.mutant.yml
deleted file mode 100644
index e01cd617f..000000000
--- a/ecommerce/product_catalog/.mutant.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-requires:
- - ./test/test_helper
-integration: minitest
-coverage_criteria:
- process_abort: true
-matcher:
- subjects:
- - ProductCatalog*
diff --git a/ecommerce/product_catalog/Gemfile b/ecommerce/product_catalog/Gemfile
deleted file mode 100644
index 00fef6d3c..000000000
--- a/ecommerce/product_catalog/Gemfile
+++ /dev/null
@@ -1,4 +0,0 @@
-source "https://rubygems.org"
-
-eval_gemfile "../../infra/Gemfile.test"
-gem "infra", path: "../../infra"
diff --git a/ecommerce/product_catalog/Gemfile.lock b/ecommerce/product_catalog/Gemfile.lock
deleted file mode 100644
index 4a73abff2..000000000
--- a/ecommerce/product_catalog/Gemfile.lock
+++ /dev/null
@@ -1,119 +0,0 @@
-PATH
- remote: ../../infra
- specs:
- infra (1.0.0)
- aggregate_root (~> 2.13)
- arkency-command_bus
- dry-struct
- dry-types
- rake
- ruby_event_store (~> 2.13)
- ruby_event_store-transformations
- sidekiq
-
-GEM
- remote: https://oss:7AXfeZdAfCqL1PvHm2nvDJO6Zd9UW8IK@gem.mutant.dev/
- specs:
- mutant-license (0.1.1.2.1627430819213747598431630701693729869473.6)
-
-GEM
- remote: https://rubygems.org/
- specs:
- activesupport (7.1.2)
- base64
- bigdecimal
- concurrent-ruby (~> 1.0, >= 1.0.2)
- connection_pool (>= 2.2.5)
- drb
- i18n (>= 1.6, < 2)
- minitest (>= 5.1)
- mutex_m
- tzinfo (~> 2.0)
- aggregate_root (2.13.0)
- ruby_event_store (= 2.13.0)
- arkency-command_bus (0.4.1)
- concurrent-ruby
- ast (2.4.2)
- base64 (0.2.0)
- bigdecimal (3.1.4)
- concurrent-ruby (1.2.2)
- connection_pool (2.4.1)
- diff-lcs (1.5.0)
- drb (2.2.0)
- ruby2_keywords
- dry-core (1.0.1)
- concurrent-ruby (~> 1.0)
- zeitwerk (~> 2.6)
- dry-inflector (1.0.0)
- dry-logic (1.5.0)
- concurrent-ruby (~> 1.0)
- dry-core (~> 1.0, < 2)
- zeitwerk (~> 2.6)
- dry-struct (1.6.0)
- dry-core (~> 1.0, < 2)
- dry-types (>= 1.7, < 2)
- ice_nine (~> 0.11)
- zeitwerk (~> 2.6)
- dry-types (1.7.1)
- concurrent-ruby (~> 1.0)
- dry-core (~> 1.0)
- dry-inflector (~> 1.0)
- dry-logic (~> 1.4)
- zeitwerk (~> 2.6)
- i18n (1.14.1)
- concurrent-ruby (~> 1.0)
- ice_nine (0.11.2)
- minitest (5.15.0)
- mutant (0.11.26)
- diff-lcs (~> 1.3)
- parser (~> 3.2.2, >= 3.2.2.4)
- regexp_parser (~> 2.8.2)
- sorbet-runtime (~> 0.5.0)
- unparser (~> 0.6.9)
- mutant-minitest (0.11.26)
- minitest (~> 5.11)
- mutant (= 0.11.26)
- mutex_m (0.2.0)
- parser (3.2.2.4)
- ast (~> 2.4.1)
- racc
- racc (1.7.3)
- rack (3.0.8)
- rake (13.1.0)
- redis-client (0.19.0)
- connection_pool
- regexp_parser (2.8.3)
- ruby2_keywords (0.0.5)
- ruby_event_store (2.13.0)
- concurrent-ruby (~> 1.0, >= 1.1.6)
- ruby_event_store-transformations (0.1.0)
- activesupport (>= 5.0)
- ruby_event_store (>= 2.0.0, < 3.0.0)
- sidekiq (7.2.0)
- concurrent-ruby (< 2)
- connection_pool (>= 2.3.0)
- rack (>= 2.2.4)
- redis-client (>= 0.14.0)
- sorbet-runtime (0.5.11190)
- tzinfo (2.0.6)
- concurrent-ruby (~> 1.0)
- unparser (0.6.12)
- diff-lcs (~> 1.3)
- parser (>= 3.2.2.4)
- zeitwerk (2.6.12)
-
-PLATFORMS
- arm64-darwin-20
- arm64-darwin-21
- ruby
- x86_64-darwin-20
- x86_64-linux
-
-DEPENDENCIES
- infra!
- minitest (= 5.15.0)!
- mutant-license!
- mutant-minitest (= 0.11.26)!
-
-BUNDLED WITH
- 2.5.9
diff --git a/ecommerce/product_catalog/Makefile b/ecommerce/product_catalog/Makefile
deleted file mode 100644
index 23850fab1..000000000
--- a/ecommerce/product_catalog/Makefile
+++ /dev/null
@@ -1,10 +0,0 @@
-install:
- @bundle install
-
-test:
- @bundle exec ruby -e "require \"rake/rake_test_loader\"" test/*_test.rb
-
-mutate:
- @RAILS_ENV=test bundle exec mutant run
-
-.PHONY: install test mutate
diff --git a/ecommerce/product_catalog/README.md b/ecommerce/product_catalog/README.md
deleted file mode 100644
index ffef06baa..000000000
--- a/ecommerce/product_catalog/README.md
+++ /dev/null
@@ -1,21 +0,0 @@
-# Product Catalog
-
-[](https://github.com/RailsEventStore/cqrs-es-sample-with-res/actions/workflows/product_catalog.yml)
-
-We implement this domain as a CRUD-based bounded context. The goal is to present
-how to deal with such CRUD-ish domains and to show how to integrate it with
-parts of the system.
-
-It's just a single ActiveRecord `Product` class.
-
-We wrap it with a `ProductCatalog` namespace to explicitly set its boundaries.
-
-This Bounded Context has both - the write part and the read part as the
-same model. You can say it's not really CQRS - which is true for many CRUDish
-bounded contexts.
-
-#### Up and running
-
-```
-make install test mutate
-```
diff --git a/ecommerce/product_catalog/lib/product_catalog.rb b/ecommerce/product_catalog/lib/product_catalog.rb
deleted file mode 100644
index dd2048eab..000000000
--- a/ecommerce/product_catalog/lib/product_catalog.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-require "infra"
-require_relative "product_catalog/commands"
-require_relative "product_catalog/events"
-require_relative "product_catalog/registration"
-require_relative "product_catalog/naming"
-
-module ProductCatalog
-
- class Configuration
- def call(event_store, command_bus)
- command_bus.register(RegisterProduct, Registration.new(event_store))
- command_bus.register(NameProduct, Naming.new(event_store))
- end
- end
-end
diff --git a/ecommerce/product_catalog/lib/product_catalog/commands.rb b/ecommerce/product_catalog/lib/product_catalog/commands.rb
deleted file mode 100644
index 98b2ee7fe..000000000
--- a/ecommerce/product_catalog/lib/product_catalog/commands.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-module ProductCatalog
- class RegisterProduct < Infra::Command
- attribute :product_id, Infra::Types::UUID
- end
-
- class NameProduct < Infra::Command
- attribute :product_id, Infra::Types::UUID
- attribute :name, Infra::Types::String
- end
-end
diff --git a/ecommerce/product_catalog/lib/product_catalog/events.rb b/ecommerce/product_catalog/lib/product_catalog/events.rb
deleted file mode 100644
index 413a7b085..000000000
--- a/ecommerce/product_catalog/lib/product_catalog/events.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-module ProductCatalog
-
- class ProductRegistered < Infra::Event
- attribute :product_id, Infra::Types::UUID
- end
-
- class ProductNamed < Infra::Event
- attribute :product_id, Infra::Types::String
- end
-
-end
diff --git a/ecommerce/product_catalog/lib/product_catalog/naming.rb b/ecommerce/product_catalog/lib/product_catalog/naming.rb
deleted file mode 100644
index dec9d67cf..000000000
--- a/ecommerce/product_catalog/lib/product_catalog/naming.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-module ProductCatalog
-
- class Naming
- def initialize(event_store)
- @event_store = event_store
- end
-
- def call(cmd)
- @event_store.publish(product_named_event(cmd), stream_name: stream_name(cmd))
- end
-
- private
-
- def product_named_event(cmd)
- ProductNamed.new(
- data: {
- product_id: cmd.product_id,
- name: cmd.name
- }
- )
- end
-
- def stream_name(cmd)
- "Catalog::ProductName$#{cmd.product_id}"
- end
- end
-end
diff --git a/ecommerce/product_catalog/lib/product_catalog/registration.rb b/ecommerce/product_catalog/lib/product_catalog/registration.rb
deleted file mode 100644
index 244676896..000000000
--- a/ecommerce/product_catalog/lib/product_catalog/registration.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-module ProductCatalog
- AlreadyRegistered = Class.new(StandardError)
-
- class Registration
- def initialize(event_store)
- @event_store = event_store
- end
-
- def call(cmd)
- events = all_events_from_stream(stream_name(cmd))
- raise AlreadyRegistered unless events.empty?
-
- @event_store.publish(product_registered_event(cmd), stream_name: stream_name(cmd))
- end
-
- private
-
- def all_events_from_stream(name)
- @event_store.read.stream(name).to_a
- end
-
- def product_registered_event(cmd)
- ProductRegistered.new(
- data: {
- product_id: cmd.product_id,
- }
- )
- end
-
- def stream_name(cmd)
- "Catalog::Product$#{cmd.product_id}"
- end
- end
-end
diff --git a/ecommerce/product_catalog/test/naming_test.rb b/ecommerce/product_catalog/test/naming_test.rb
deleted file mode 100644
index e5851df06..000000000
--- a/ecommerce/product_catalog/test/naming_test.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-require_relative 'test_helper'
-module ProductCatalog
- class NamingTest < Test
- cover "ProductCatalog*"
-
- def test_should_publish_event
- uid = SecureRandom.uuid
- product_named = ProductCatalog::ProductNamed.new(data: {product_id: uid, name: fake_name})
- assert_events("Catalog::ProductName$#{uid}", product_named) do
- name_product(uid, fake_name)
- end
- end
-
- private
-
- def name_product(uid, name)
- run_command(NameProduct.new(product_id: uid, name: name))
- end
-
- def fake_name
- "Fake name"
- end
- end
-end
diff --git a/ecommerce/product_catalog/test/registration_test.rb b/ecommerce/product_catalog/test/registration_test.rb
deleted file mode 100644
index c4140ee61..000000000
--- a/ecommerce/product_catalog/test/registration_test.rb
+++ /dev/null
@@ -1,52 +0,0 @@
-require_relative 'test_helper'
-module ProductCatalog
- class RegistrationTest < Test
- cover "ProductCatalog*"
-
- def test_product_should_get_registered
- uid = SecureRandom.uuid
- assert register_product(uid)
- end
-
- def test_should_not_allow_for_double_registration
- uid = SecureRandom.uuid
- assert_raises(AlreadyRegistered) do
- register_product(uid)
- register_product(uid)
- end
- end
-
- def test_should_publish_event
- uid = SecureRandom.uuid
- product_registered = ProductCatalog::ProductRegistered.new(data: { product_id: uid })
- assert_events("Catalog::Product$#{uid}", product_registered) do
- register_product(uid)
- end
- end
-
- def test_each_product_has_its_own_lifecycle
- product_1_id = SecureRandom.uuid
- product_1_registered = ProductCatalog::ProductRegistered.new(data: { product_id: product_1_id })
- product_2_id = SecureRandom.uuid
- product_2_registered = ProductCatalog::ProductRegistered.new(data: { product_id: product_2_id })
-
- assert_events("Catalog::Product$#{product_1_id}", product_1_registered) do
- register_product(product_1_id)
- end
-
- assert_events("Catalog::Product$#{product_2_id}", product_2_registered) do
- register_product(product_2_id)
- end
- end
-
- private
-
- def register_product(uid)
- run_command(RegisterProduct.new(product_id: uid))
- end
-
- def fake_name
- "Fake name"
- end
- end
-end
diff --git a/ecommerce/product_catalog/test/test_helper.rb b/ecommerce/product_catalog/test/test_helper.rb
deleted file mode 100644
index 5f5820683..000000000
--- a/ecommerce/product_catalog/test/test_helper.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-require "minitest/autorun"
-require "mutant/minitest/coverage"
-
-require_relative "../lib/product_catalog"
-
-module ProductCatalog
- class Test < Infra::InMemoryTest
-
- def before_setup
- super()
- Configuration.new.call(event_store, command_bus)
- end
- end
-end
diff --git a/ecommerce/shipping/.mutant.yml b/ecommerce/shipping/.mutant.yml
deleted file mode 100644
index 074917e2b..000000000
--- a/ecommerce/shipping/.mutant.yml
+++ /dev/null
@@ -1,12 +0,0 @@
-requires:
- - ./test/test_helper
-integration: minitest
-coverage_criteria:
- process_abort: true
-matcher:
- subjects:
- - Shipping*
- ignore:
- - Shipping::Test*
- - Shipping::Configuration#initialize
- - Shipping::Configuration#call
diff --git a/ecommerce/shipping/Gemfile b/ecommerce/shipping/Gemfile
deleted file mode 100644
index 8fd887144..000000000
--- a/ecommerce/shipping/Gemfile
+++ /dev/null
@@ -1,4 +0,0 @@
-source "https://rubygems.org"
-
-eval_gemfile "../../infra/Gemfile.test"
-gem "infra", path: "../../infra"
\ No newline at end of file
diff --git a/ecommerce/shipping/Gemfile.lock b/ecommerce/shipping/Gemfile.lock
deleted file mode 100644
index 43c29a1da..000000000
--- a/ecommerce/shipping/Gemfile.lock
+++ /dev/null
@@ -1,115 +0,0 @@
-PATH
- remote: ../../infra
- specs:
- infra (1.0.0)
- aggregate_root (~> 2.13)
- arkency-command_bus
- dry-struct
- dry-types
- rake
- ruby_event_store (~> 2.13)
- ruby_event_store-transformations
- sidekiq
-
-GEM
- remote: https://oss:7AXfeZdAfCqL1PvHm2nvDJO6Zd9UW8IK@gem.mutant.dev/
- specs:
- mutant-license (0.1.1.2.1627430819213747598431630701693729869473.6)
-
-GEM
- remote: https://rubygems.org/
- specs:
- activesupport (7.1.2)
- base64
- bigdecimal
- concurrent-ruby (~> 1.0, >= 1.0.2)
- connection_pool (>= 2.2.5)
- drb
- i18n (>= 1.6, < 2)
- minitest (>= 5.1)
- mutex_m
- tzinfo (~> 2.0)
- aggregate_root (2.13.0)
- ruby_event_store (= 2.13.0)
- arkency-command_bus (0.4.1)
- concurrent-ruby
- ast (2.4.2)
- base64 (0.2.0)
- bigdecimal (3.1.4)
- concurrent-ruby (1.2.2)
- connection_pool (2.4.1)
- diff-lcs (1.5.0)
- drb (2.2.0)
- ruby2_keywords
- dry-core (1.0.1)
- concurrent-ruby (~> 1.0)
- zeitwerk (~> 2.6)
- dry-inflector (1.0.0)
- dry-logic (1.5.0)
- concurrent-ruby (~> 1.0)
- dry-core (~> 1.0, < 2)
- zeitwerk (~> 2.6)
- dry-struct (1.6.0)
- dry-core (~> 1.0, < 2)
- dry-types (>= 1.7, < 2)
- ice_nine (~> 0.11)
- zeitwerk (~> 2.6)
- dry-types (1.7.1)
- concurrent-ruby (~> 1.0)
- dry-core (~> 1.0)
- dry-inflector (~> 1.0)
- dry-logic (~> 1.4)
- zeitwerk (~> 2.6)
- i18n (1.14.1)
- concurrent-ruby (~> 1.0)
- ice_nine (0.11.2)
- minitest (5.15.0)
- mutant (0.11.26)
- diff-lcs (~> 1.3)
- parser (~> 3.2.2, >= 3.2.2.4)
- regexp_parser (~> 2.8.2)
- sorbet-runtime (~> 0.5.0)
- unparser (~> 0.6.9)
- mutant-minitest (0.11.26)
- minitest (~> 5.11)
- mutant (= 0.11.26)
- mutex_m (0.2.0)
- parser (3.2.2.4)
- ast (~> 2.4.1)
- racc
- racc (1.7.3)
- rack (3.0.8)
- rake (13.1.0)
- redis-client (0.19.0)
- connection_pool
- regexp_parser (2.8.3)
- ruby2_keywords (0.0.5)
- ruby_event_store (2.13.0)
- concurrent-ruby (~> 1.0, >= 1.1.6)
- ruby_event_store-transformations (0.1.0)
- activesupport (>= 5.0)
- ruby_event_store (>= 2.0.0, < 3.0.0)
- sidekiq (7.2.0)
- concurrent-ruby (< 2)
- connection_pool (>= 2.3.0)
- rack (>= 2.2.4)
- redis-client (>= 0.14.0)
- sorbet-runtime (0.5.11190)
- tzinfo (2.0.6)
- concurrent-ruby (~> 1.0)
- unparser (0.6.12)
- diff-lcs (~> 1.3)
- parser (>= 3.2.2.4)
- zeitwerk (2.6.12)
-
-PLATFORMS
- ruby
-
-DEPENDENCIES
- infra!
- minitest (= 5.15.0)!
- mutant-license!
- mutant-minitest (= 0.11.26)!
-
-BUNDLED WITH
- 2.5.9
diff --git a/ecommerce/shipping/Makefile b/ecommerce/shipping/Makefile
deleted file mode 100644
index 23850fab1..000000000
--- a/ecommerce/shipping/Makefile
+++ /dev/null
@@ -1,10 +0,0 @@
-install:
- @bundle install
-
-test:
- @bundle exec ruby -e "require \"rake/rake_test_loader\"" test/*_test.rb
-
-mutate:
- @RAILS_ENV=test bundle exec mutant run
-
-.PHONY: install test mutate
diff --git a/ecommerce/shipping/README.md b/ecommerce/shipping/README.md
deleted file mode 100644
index 54ffc7e08..000000000
--- a/ecommerce/shipping/README.md
+++ /dev/null
@@ -1,9 +0,0 @@
-# Shipping
-
-[](https://github.com/RailsEventStore/cqrs-es-sample-with-res/actions/workflows/shipping.yml)
-
-#### Up and running
-
-```
-make install test mutate
-```
diff --git a/ecommerce/shipping/lib/shipping.rb b/ecommerce/shipping/lib/shipping.rb
deleted file mode 100644
index 78fa022d8..000000000
--- a/ecommerce/shipping/lib/shipping.rb
+++ /dev/null
@@ -1,51 +0,0 @@
-require "infra"
-require_relative "shipping/commands/add_item_to_shipment_picking_list"
-require_relative "shipping/events/item_added_to_shipment_picking_list"
-require_relative "shipping/services/on_add_item_to_shipment_picking_list"
-
-require_relative "shipping/commands/remove_item_from_shipment_picking_list"
-require_relative "shipping/events/item_removed_from_shipment_picking_list"
-require_relative "shipping/services/on_remove_item_from_shipment_picking_list"
-
-require_relative "shipping/commands/add_shipping_address_to_shipment"
-require_relative "shipping/events/shipping_address_added_to_shipment"
-require_relative "shipping/services/on_add_shipping_address_to_shipment"
-
-require_relative "shipping/commands/submit_shipment"
-require_relative "shipping/events/shipment_submitted"
-require_relative "shipping/services/on_submit_shipment"
-
-require_relative "shipping/commands/authorize_shipment"
-require_relative "shipping/events/shipment_authorized"
-require_relative "shipping/services/on_authorize_shipment"
-
-require_relative "shipping/shipment"
-require_relative "shipping/picking_list"
-require_relative "shipping/picking_list_item"
-
-module Shipping
- class Configuration
- def call(event_store, command_bus)
- command_bus.register(
- AddItemToShipmentPickingList,
- OnAddItemToShipmentPickingList.new(event_store)
- )
- command_bus.register(
- RemoveItemFromShipmentPickingList,
- OnRemoveItemFromShipmentPickingList.new(event_store)
- )
- command_bus.register(
- AddShippingAddressToShipment,
- OnAddShippingAddressToShipment.new(event_store)
- )
- command_bus.register(
- SubmitShipment,
- OnSubmitShipment.new(event_store)
- )
- command_bus.register(
- AuthorizeShipment,
- OnAuthorizeShipment.new(event_store)
- )
- end
- end
-end
diff --git a/ecommerce/shipping/lib/shipping/commands/add_item_to_shipment_picking_list.rb b/ecommerce/shipping/lib/shipping/commands/add_item_to_shipment_picking_list.rb
deleted file mode 100644
index 500cb32bc..000000000
--- a/ecommerce/shipping/lib/shipping/commands/add_item_to_shipment_picking_list.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-module Shipping
- class AddItemToShipmentPickingList < Infra::Command
- attribute :order_id, Infra::Types::UUID
- attribute :product_id, Infra::Types::UUID
-
- alias aggregate_id order_id
- end
-end
diff --git a/ecommerce/shipping/lib/shipping/commands/add_shipping_address_to_shipment.rb b/ecommerce/shipping/lib/shipping/commands/add_shipping_address_to_shipment.rb
deleted file mode 100644
index 19febbd0c..000000000
--- a/ecommerce/shipping/lib/shipping/commands/add_shipping_address_to_shipment.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-module Shipping
- class AddShippingAddressToShipment < Infra::Command
- attribute :order_id, Infra::Types::UUID
- attribute :postal_address, Infra::Types::PostalAddress
-
- alias aggregate_id order_id
- end
-end
diff --git a/ecommerce/shipping/lib/shipping/commands/authorize_shipment.rb b/ecommerce/shipping/lib/shipping/commands/authorize_shipment.rb
deleted file mode 100644
index 7078acc36..000000000
--- a/ecommerce/shipping/lib/shipping/commands/authorize_shipment.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-module Shipping
- class AuthorizeShipment < Infra::Command
- attribute :order_id, Infra::Types::UUID
- end
-end
\ No newline at end of file
diff --git a/ecommerce/shipping/lib/shipping/commands/remove_item_from_shipment_picking_list.rb b/ecommerce/shipping/lib/shipping/commands/remove_item_from_shipment_picking_list.rb
deleted file mode 100644
index 91da4283f..000000000
--- a/ecommerce/shipping/lib/shipping/commands/remove_item_from_shipment_picking_list.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-module Shipping
- class RemoveItemFromShipmentPickingList < Infra::Command
- attribute :order_id, Infra::Types::UUID
- attribute :product_id, Infra::Types::UUID
-
- alias aggregate_id order_id
- end
-end
diff --git a/ecommerce/shipping/lib/shipping/commands/submit_shipment.rb b/ecommerce/shipping/lib/shipping/commands/submit_shipment.rb
deleted file mode 100644
index 3ccef5452..000000000
--- a/ecommerce/shipping/lib/shipping/commands/submit_shipment.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-module Shipping
- class SubmitShipment < Infra::Command
- attribute :order_id, Infra::Types::UUID
- end
-end
\ No newline at end of file
diff --git a/ecommerce/shipping/lib/shipping/events/item_added_to_shipment_picking_list.rb b/ecommerce/shipping/lib/shipping/events/item_added_to_shipment_picking_list.rb
deleted file mode 100644
index 3b64f1c70..000000000
--- a/ecommerce/shipping/lib/shipping/events/item_added_to_shipment_picking_list.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-module Shipping
- class ItemAddedToShipmentPickingList < Infra::Event
- attribute :order_id, Infra::Types::UUID
- attribute :product_id, Infra::Types::UUID
- end
-end
diff --git a/ecommerce/shipping/lib/shipping/events/item_removed_from_shipment_picking_list.rb b/ecommerce/shipping/lib/shipping/events/item_removed_from_shipment_picking_list.rb
deleted file mode 100644
index 8898e43b8..000000000
--- a/ecommerce/shipping/lib/shipping/events/item_removed_from_shipment_picking_list.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-module Shipping
- class ItemRemovedFromShipmentPickingList < Infra::Event
- attribute :order_id, Infra::Types::UUID
- attribute :product_id, Infra::Types::UUID
- end
-end
diff --git a/ecommerce/shipping/lib/shipping/events/shipment_authorized.rb b/ecommerce/shipping/lib/shipping/events/shipment_authorized.rb
deleted file mode 100644
index 34af937f5..000000000
--- a/ecommerce/shipping/lib/shipping/events/shipment_authorized.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-module Shipping
- class ShipmentAuthorized < Infra::Event
- attribute :order_id, Infra::Types::UUID
- end
-end
\ No newline at end of file
diff --git a/ecommerce/shipping/lib/shipping/events/shipment_submitted.rb b/ecommerce/shipping/lib/shipping/events/shipment_submitted.rb
deleted file mode 100644
index e5a5a63c9..000000000
--- a/ecommerce/shipping/lib/shipping/events/shipment_submitted.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-module Shipping
- class ShipmentSubmitted < Infra::Event
- attribute :order_id, Infra::Types::UUID
- end
-end
\ No newline at end of file
diff --git a/ecommerce/shipping/lib/shipping/events/shipping_address_added_to_shipment.rb b/ecommerce/shipping/lib/shipping/events/shipping_address_added_to_shipment.rb
deleted file mode 100644
index decc5f22a..000000000
--- a/ecommerce/shipping/lib/shipping/events/shipping_address_added_to_shipment.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-module Shipping
- class ShippingAddressAddedToShipment < Infra::Event
- attribute :order_id, Infra::Types::UUID
- attribute :postal_address, Infra::Types::PostalAddress
- end
-end
diff --git a/ecommerce/shipping/lib/shipping/picking_list.rb b/ecommerce/shipping/lib/shipping/picking_list.rb
deleted file mode 100644
index 6275eb4ce..000000000
--- a/ecommerce/shipping/lib/shipping/picking_list.rb
+++ /dev/null
@@ -1,44 +0,0 @@
-module Shipping
- class PickingList
- attr_reader :items
-
- def initialize
- @items = []
- end
-
- def increase_item_quantity(product_id)
- item = find_or_add_item(product_id)
- item.increase
- end
-
- def decrease_item_quantity(product_id)
- item = find_item(product_id)
- item.decrease
- remove_item(item) if item.quantity.zero?
- end
-
- def has_item?(product_id)
- find_item(product_id)
- end
-
- def find_or_add_item(product_id)
- find_item(product_id) || add_item(product_id)
- end
-
- def find_item(product_id)
- items.find {|i| i.product_id === product_id }
- end
-
- private
-
- def add_item(product_id)
- item = PickingListItem.new(product_id)
- items << item
- item
- end
-
- def remove_item(item)
- items.delete(item)
- end
- end
-end
\ No newline at end of file
diff --git a/ecommerce/shipping/lib/shipping/picking_list_item.rb b/ecommerce/shipping/lib/shipping/picking_list_item.rb
deleted file mode 100644
index 67a5a227c..000000000
--- a/ecommerce/shipping/lib/shipping/picking_list_item.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-module Shipping
- class PickingListItem
- attr_reader :product_id, :quantity
-
- def initialize(product_id)
- @product_id = product_id
- @quantity = 0
- end
-
- def increase
- @quantity += 1
- end
-
- def decrease
- @quantity -= 1
- end
- end
-end
\ No newline at end of file
diff --git a/ecommerce/shipping/lib/shipping/services/on_add_item_to_shipment_picking_list.rb b/ecommerce/shipping/lib/shipping/services/on_add_item_to_shipment_picking_list.rb
deleted file mode 100644
index 178fa2a77..000000000
--- a/ecommerce/shipping/lib/shipping/services/on_add_item_to_shipment_picking_list.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-module Shipping
- class OnAddItemToShipmentPickingList
- def initialize(event_store)
- @repository = AggregateRoot::Repository.new(event_store)
- end
-
- def call(command)
- @repository.with_aggregate(
- Shipment.new(command.order_id),
- "Shipping::Shipment$#{command.order_id}"
- ) do |shipment|
- shipment.add_item(command.product_id)
- end
- end
- end
-end
diff --git a/ecommerce/shipping/lib/shipping/services/on_add_shipping_address_to_shipment.rb b/ecommerce/shipping/lib/shipping/services/on_add_shipping_address_to_shipment.rb
deleted file mode 100644
index 4df479b36..000000000
--- a/ecommerce/shipping/lib/shipping/services/on_add_shipping_address_to_shipment.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-module Shipping
- class OnAddShippingAddressToShipment
- def initialize(event_store)
- @repository = AggregateRoot::Repository.new(event_store)
- end
-
- def call(command)
- @repository.with_aggregate(
- Shipment.new(command.order_id),
- "Shipping::Shipment$#{command.order_id}"
- ) do |shipment|
- shipment.add_address(command.postal_address)
- end
- end
- end
-end
diff --git a/ecommerce/shipping/lib/shipping/services/on_authorize_shipment.rb b/ecommerce/shipping/lib/shipping/services/on_authorize_shipment.rb
deleted file mode 100644
index 706a6e372..000000000
--- a/ecommerce/shipping/lib/shipping/services/on_authorize_shipment.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-module Shipping
- class OnAuthorizeShipment
- def initialize(event_store)
- @repository = AggregateRoot::Repository.new(event_store)
- end
-
- def call(command)
- @repository.with_aggregate(
- Shipment.new(command.order_id),
- "Shipping::Shipment$#{command.order_id}"
- ) do |shipment|
- shipment.authorize
- end
- end
- end
-end
diff --git a/ecommerce/shipping/lib/shipping/services/on_remove_item_from_shipment_picking_list.rb b/ecommerce/shipping/lib/shipping/services/on_remove_item_from_shipment_picking_list.rb
deleted file mode 100644
index 883bd3c0b..000000000
--- a/ecommerce/shipping/lib/shipping/services/on_remove_item_from_shipment_picking_list.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-module Shipping
- class OnRemoveItemFromShipmentPickingList
- def initialize(event_store)
- @repository = AggregateRoot::Repository.new(event_store)
- end
-
- def call(command)
- @repository.with_aggregate(
- Shipment.new(command.order_id),
- "Shipping::Shipment$#{command.order_id}"
- ) do |shipment|
- shipment.remove_item(command.product_id)
- end
- end
- end
-end
diff --git a/ecommerce/shipping/lib/shipping/services/on_submit_shipment.rb b/ecommerce/shipping/lib/shipping/services/on_submit_shipment.rb
deleted file mode 100644
index f36e3daa3..000000000
--- a/ecommerce/shipping/lib/shipping/services/on_submit_shipment.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-module Shipping
- class OnSubmitShipment
- def initialize(event_store)
- @repository = AggregateRoot::Repository.new(event_store)
- end
-
- def call(command)
- @repository.with_aggregate(
- Shipment.new(command.order_id),
- "Shipping::Shipment$#{command.order_id}"
- ) do |shipment|
- shipment.submit
- end
- end
- end
-end
diff --git a/ecommerce/shipping/lib/shipping/shipment.rb b/ecommerce/shipping/lib/shipping/shipment.rb
deleted file mode 100644
index ce272c672..000000000
--- a/ecommerce/shipping/lib/shipping/shipment.rb
+++ /dev/null
@@ -1,98 +0,0 @@
-module Shipping
- class Shipment
- include AggregateRoot
- attr_reader :state
-
- ItemNotFound = Class.new(StandardError)
- ShippingAddressMissing = Class.new(StandardError)
- NotSubmitted = Class.new(StandardError)
- AlreadySubmitted = Class.new(StandardError)
- AlreadyAuthorized = Class.new(StandardError)
-
- def initialize(order_id)
- @order_id = order_id
- @picking_list = PickingList.new
- @state = :draft
- end
-
- def add_item(product_id)
- apply ItemAddedToShipmentPickingList.new(
- data: {
- order_id: @order_id,
- product_id: product_id
- }
- )
- end
-
- def remove_item(product_id)
- raise ItemNotFound unless has_item?(product_id)
-
- apply ItemRemovedFromShipmentPickingList.new(
- data: {
- order_id: @order_id,
- product_id: product_id
- }
- )
- end
-
- def add_address(postal_address)
- apply ShippingAddressAddedToShipment.new(
- data: {
- order_id: @order_id,
- postal_address: postal_address
- }
- )
- end
-
- def submit
- raise AlreadySubmitted if state.equal?(:submitted)
- raise ShippingAddressMissing unless state.equal?(:address_set)
-
- apply ShipmentSubmitted.new(
- data: {
- order_id: @order_id
- }
- )
- end
-
- def authorize
- raise AlreadyAuthorized if state.equal?(:authorized)
- raise NotSubmitted unless state.equal?(:submitted)
-
- apply ShipmentAuthorized.new(
- data: {
- order_id: @order_id
- }
- )
- end
-
- private
-
- attr_reader :shipping_address
-
- on ItemAddedToShipmentPickingList do |event|
- @picking_list.increase_item_quantity(event.data.fetch(:product_id))
- end
-
- on ItemRemovedFromShipmentPickingList do |event|
- @picking_list.decrease_item_quantity(event.data.fetch(:product_id))
- end
-
- on ShippingAddressAddedToShipment do |event|
- @shipping_address = event.data.fetch(:postal_address)
- @state = :address_set
- end
-
- on ShipmentSubmitted do |event|
- @state = :submitted
- end
-
- on ShipmentAuthorized do |event|
- @state = :authorized
- end
-
- def has_item?(product_id)
- @picking_list.has_item?(product_id)
- end
- end
-end
diff --git a/ecommerce/shipping/test/on_add_item_to_shipment_picking_list_test.rb b/ecommerce/shipping/test/on_add_item_to_shipment_picking_list_test.rb
deleted file mode 100644
index 11bbfd0fa..000000000
--- a/ecommerce/shipping/test/on_add_item_to_shipment_picking_list_test.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-require_relative "test_helper"
-
-module Shipping
- class OnAddItemToShipmentPickingListTest < Test
- cover "Shipping::OnAddItemToShipmentPickingList*"
-
- def test_add_item_to_shipment_picking_list
- order_id = SecureRandom.uuid
- product_id = SecureRandom.uuid
- stream = "Shipping::Shipment$#{order_id}"
-
- assert_events(
- stream,
- ItemAddedToShipmentPickingList.new(
- data: {
- order_id: order_id,
- product_id: product_id
- }
- )
- ) { act(AddItemToShipmentPickingList.new(order_id: order_id, product_id: product_id)) }
- end
- end
-end
diff --git a/ecommerce/shipping/test/on_add_shipping_address_to_shipment_test.rb b/ecommerce/shipping/test/on_add_shipping_address_to_shipment_test.rb
deleted file mode 100644
index def22c50c..000000000
--- a/ecommerce/shipping/test/on_add_shipping_address_to_shipment_test.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-require_relative "test_helper"
-
-module Shipping
- class OnAddShippingAddressToShipmentTest < Test
- cover "Shipping::OnAddShippingAddressToShipment*"
-
- def test_add_item_to_shipment_picking_list
- order_id = SecureRandom.uuid
- address = fake_address
- stream = "Shipping::Shipment$#{order_id}"
-
- assert_events(
- stream,
- ShippingAddressAddedToShipment.new(
- data: {
- order_id: order_id,
- postal_address: address
- }
- )
- ) do
- act(
- AddShippingAddressToShipment.new(
- order_id: order_id,
- postal_address: address
- )
- )
- end
- end
- end
-end
\ No newline at end of file
diff --git a/ecommerce/shipping/test/on_authorize_shipment_test.rb b/ecommerce/shipping/test/on_authorize_shipment_test.rb
deleted file mode 100644
index ad0137603..000000000
--- a/ecommerce/shipping/test/on_authorize_shipment_test.rb
+++ /dev/null
@@ -1,56 +0,0 @@
-require_relative "test_helper"
-
-module Shipping
- class OnAuthorizeShipmentTest < Test
- cover "Shipping::OnAuthorizeShipment*"
-
- def test_authorize_shipment
- order_id = SecureRandom.uuid
- address = fake_address
- stream = "Shipping::Shipment$#{order_id}"
-
- arrange(
- AddShippingAddressToShipment.new(
- order_id: order_id,
- postal_address: address
- ),
- SubmitShipment.new(order_id: order_id)
- )
-
- assert_events(
- stream,
- ShipmentAuthorized.new(
- data: {
- order_id: order_id
- }
- )
- ) { act(AuthorizeShipment.new(order_id: order_id)) }
- end
-
- def test_shipment_cannot_be_authorized_when_not_submitted
- order_id = SecureRandom.uuid
-
- assert_raises(Shipment::NotSubmitted) do
- act(AuthorizeShipment.new(order_id: order_id))
- end
- end
-
- def test_shipment_cannot_be_authorized_when_already_authorized
- order_id = SecureRandom.uuid
- address = fake_address
-
- arrange(
- AddShippingAddressToShipment.new(
- order_id: order_id,
- postal_address: address
- ),
- SubmitShipment.new(order_id: order_id),
- AuthorizeShipment.new(order_id: order_id)
- )
-
- assert_raises(Shipment::AlreadyAuthorized) do
- act(AuthorizeShipment.new(order_id: order_id))
- end
- end
- end
-end
diff --git a/ecommerce/shipping/test/on_remove_item_from_shipment_picking_list_test.rb b/ecommerce/shipping/test/on_remove_item_from_shipment_picking_list_test.rb
deleted file mode 100644
index 9b4a903ff..000000000
--- a/ecommerce/shipping/test/on_remove_item_from_shipment_picking_list_test.rb
+++ /dev/null
@@ -1,41 +0,0 @@
-require_relative "test_helper"
-
-module Shipping
- class OnRemoveItemFromShipmentPickingListTest < Test
- cover "Shipping::OnRemoveItemFromShipmentPickingList*"
-
- def test_remove_item_from_shipment_picking_list
- order_id = SecureRandom.uuid
- product_id = SecureRandom.uuid
- stream = "Shipping::Shipment$#{order_id}"
-
- run_command(AddItemToShipmentPickingList.new(
- order_id: order_id,
- product_id: product_id
- ))
-
- assert_events(
- stream,
- ItemRemovedFromShipmentPickingList.new(
- data: {
- order_id: order_id,
- product_id: product_id
- }
- )
- ) { act(RemoveItemFromShipmentPickingList.new(order_id: order_id, product_id: product_id)) }
- end
-
- def test_should_not_allow_removing_non_existing_items
- order_id = SecureRandom.uuid
- product_id = SecureRandom.uuid
- stream = "Shipping::Shipment$#{order_id}"
-
- assert_raises(Shipment::ItemNotFound) do
- act(RemoveItemFromShipmentPickingList.new(
- order_id: order_id,
- product_id: product_id
- ))
- end
- end
- end
-end
diff --git a/ecommerce/shipping/test/on_submit_shipment_test.rb b/ecommerce/shipping/test/on_submit_shipment_test.rb
deleted file mode 100644
index df0756b0a..000000000
--- a/ecommerce/shipping/test/on_submit_shipment_test.rb
+++ /dev/null
@@ -1,54 +0,0 @@
-require_relative "test_helper"
-
-module Shipping
- class OnSubmitShipmentTest < Test
- cover "Shipping::OnSubmitShipment*"
-
- def test_submit_shipment
- order_id = SecureRandom.uuid
- address = fake_address
- stream = "Shipping::Shipment$#{order_id}"
-
- run_command(
- AddShippingAddressToShipment.new(
- order_id: order_id,
- postal_address: address
- )
- )
-
- assert_events(
- stream,
- ShipmentSubmitted.new(
- data: {
- order_id: order_id
- }
- )
- ) { act(SubmitShipment.new(order_id: order_id)) }
- end
-
- def test_shipment_cannot_be_submitted_when_shipping_address_is_missing
- order_id = SecureRandom.uuid
-
- assert_raises(Shipment::ShippingAddressMissing) do
- act(SubmitShipment.new(order_id: order_id))
- end
- end
-
- def test_shipment_cannot_be_submitted_when_already_submitted
- order_id = SecureRandom.uuid
- address = fake_address
-
- arrange(
- AddShippingAddressToShipment.new(
- order_id: order_id,
- postal_address: address
- ),
- SubmitShipment.new(order_id: order_id)
- )
-
- assert_raises(Shipment::AlreadySubmitted) do
- act(SubmitShipment.new(order_id: order_id))
- end
- end
- end
-end
diff --git a/ecommerce/shipping/test/picking_list_item_test.rb b/ecommerce/shipping/test/picking_list_item_test.rb
deleted file mode 100644
index cb70bd492..000000000
--- a/ecommerce/shipping/test/picking_list_item_test.rb
+++ /dev/null
@@ -1,33 +0,0 @@
-require_relative "test_helper"
-
-module Shipping
- class PickingListItemTest < Test
-
- def test_initialize
- product_id = SecureRandom.uuid
- list_item = PickingListItem.new(product_id)
-
- assert_equal product_id, list_item.product_id
- assert_equal 0, list_item.quantity
- end
-
- def test_increase
- product_id = SecureRandom.uuid
- list_item = PickingListItem.new(product_id)
-
- list_item.increase
-
- assert_equal 1, list_item.quantity
- end
-
- def test_decrease
- product_id = SecureRandom.uuid
- list_item = PickingListItem.new(product_id)
-
- list_item.increase
- list_item.decrease
-
- assert_equal 0, list_item.quantity
- end
- end
-end
\ No newline at end of file
diff --git a/ecommerce/shipping/test/picking_list_test.rb b/ecommerce/shipping/test/picking_list_test.rb
deleted file mode 100644
index b72d85e70..000000000
--- a/ecommerce/shipping/test/picking_list_test.rb
+++ /dev/null
@@ -1,48 +0,0 @@
-require_relative "test_helper"
-
-module Shipping
- class PickingListTest < Test
-
- def test_initialize
- list = PickingList.new
-
- assert_equal 0, list.items.size
- end
-
- def test_increase_item_quantity
- product_one_id = SecureRandom.uuid
- product_two_id = SecureRandom.uuid
- list = PickingList.new
-
- list.increase_item_quantity(product_one_id)
-
- assert_equal 1, list.items.size
- assert_equal 1, list.find_item(product_one_id).quantity
-
- list.increase_item_quantity(product_two_id)
-
- assert_equal 2, list.items.size
- assert_equal 1, list.find_item(product_two_id).quantity
- end
-
- def test_decrease_item_quantity
- product_id = SecureRandom.uuid
- list = PickingList.new
-
- list.increase_item_quantity(product_id)
- list.increase_item_quantity(product_id)
-
- assert_equal 1, list.items.size
- assert_equal 2, list.find_item(product_id).quantity
-
- list.decrease_item_quantity(product_id)
-
- assert_equal 1, list.items.size
- assert_equal 1, list.find_item(product_id).quantity
-
- list.decrease_item_quantity(product_id)
-
- assert_equal 0, list.items.size
- end
- end
-end
\ No newline at end of file
diff --git a/ecommerce/shipping/test/shipment_test.rb b/ecommerce/shipping/test/shipment_test.rb
deleted file mode 100644
index 83da43f65..000000000
--- a/ecommerce/shipping/test/shipment_test.rb
+++ /dev/null
@@ -1,145 +0,0 @@
-require_relative "test_helper"
-
-module Shipping
- class ShipmentTest < Test
- cover "Shipping::Shipment*"
-
- def test_add_item_publishes_event
- product_id = SecureRandom.uuid
- shipment = Shipment.new(order_id)
-
- shipment.add_item(product_id)
-
- assert_changes(
- shipment.unpublished_events,
- [
- ItemAddedToShipmentPickingList.new(
- data: {
- order_id: order_id,
- product_id: product_id
- }
- )
- ]
- )
- end
-
- def test_remove_item_publishes_event
- product_id = SecureRandom.uuid
- shipment = Shipment.new(order_id)
-
- shipment.add_item(product_id)
- shipment.remove_item(product_id)
-
- assert_changes(
- shipment.unpublished_events,
- [
- ItemAddedToShipmentPickingList.new(
- data: {
- order_id: order_id,
- product_id: product_id
- }
- ),
- ItemRemovedFromShipmentPickingList.new(
- data: {
- order_id: order_id,
- product_id: product_id
- }
- )
- ]
- )
- end
-
- def test_should_not_allow_removing_non_existing_items
- product_id = SecureRandom.uuid
- shipment = Shipment.new(order_id)
-
- assert_raises(Shipment::ItemNotFound) { shipment.remove_item(product_id) }
- end
-
- def test_add_address_publishes_event
- shipment = Shipment.new(order_id)
- address = fake_address
-
- shipment.add_address(address)
-
- assert_changes(
- shipment.unpublished_events,
- [
- ShippingAddressAddedToShipment.new(
- data: {
- order_id: order_id,
- postal_address: address
- }
- )
- ]
- )
- end
-
- def test_submit_shipment_publishes_event
- shipment = Shipment.new(order_id)
- address = fake_address
-
- shipment.add_address(address)
- shipment.submit
-
- assert_changes(
- shipment.unpublished_events,
- [
- ShippingAddressAddedToShipment.new(
- data: {
- order_id: order_id,
- postal_address: address
- }
- ),
- ShipmentSubmitted.new(
- data: {
- order_id: order_id
- }
- )
- ]
- )
- end
- def test_authorize_shipment_publishes_event
- shipment = Shipment.new(order_id)
- address = fake_address
-
- shipment.add_address(address)
- shipment.submit
- shipment.authorize
-
- assert_changes(
- shipment.unpublished_events,
- [
- ShippingAddressAddedToShipment.new(
- data: {
- order_id: order_id,
- postal_address: address
- }
- ),
- ShipmentSubmitted.new(
- data: {
- order_id: order_id
- }
- ),
- ShipmentAuthorized.new(
- data: {
- order_id: order_id
- }
- )
- ]
- )
- end
-
- def test_default_state_is_draft
- shipment = Shipment.new(order_id)
-
- assert_equal :draft, shipment.state
- end
-
- private
-
- def order_id
- @order_id ||= SecureRandom.uuid
- end
- end
-end
diff --git a/ecommerce/shipping/test/test_helper.rb b/ecommerce/shipping/test/test_helper.rb
deleted file mode 100644
index 6064aab04..000000000
--- a/ecommerce/shipping/test/test_helper.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-require "minitest/autorun"
-require "mutant/minitest/coverage"
-
-require_relative "../lib/shipping"
-
-module Shipping
- class Test < Infra::InMemoryTest
-
- def before_setup
- super
- Shipping::Configuration.new.call(event_store, command_bus)
- end
-
- private
-
- def fake_address
- Infra::Types::PostalAddress.new(
- line_1: "Mme Anna Kowalska",
- line_2: "Ul. Bosmanska 1",
- line_3: "81-116 GDYNIA",
- line_4: "POLAND"
- )
- end
- end
-end
diff --git a/ecommerce/taxes/.mutant.yml b/ecommerce/taxes/.mutant.yml
deleted file mode 100644
index 327ec3984..000000000
--- a/ecommerce/taxes/.mutant.yml
+++ /dev/null
@@ -1,12 +0,0 @@
-requires:
- - ./test/test_helper
-integration: minitest
-coverage_criteria:
- process_abort: true
-matcher:
- subjects:
- - Taxes*
- ignore:
- - Taxes::Test*
- - Taxes::Configuration*
- - Taxes::VatRateCatalog#vat_rate_for
\ No newline at end of file
diff --git a/ecommerce/taxes/Gemfile b/ecommerce/taxes/Gemfile
deleted file mode 100644
index 8fd887144..000000000
--- a/ecommerce/taxes/Gemfile
+++ /dev/null
@@ -1,4 +0,0 @@
-source "https://rubygems.org"
-
-eval_gemfile "../../infra/Gemfile.test"
-gem "infra", path: "../../infra"
\ No newline at end of file
diff --git a/ecommerce/taxes/Gemfile.lock b/ecommerce/taxes/Gemfile.lock
deleted file mode 100644
index 4a73abff2..000000000
--- a/ecommerce/taxes/Gemfile.lock
+++ /dev/null
@@ -1,119 +0,0 @@
-PATH
- remote: ../../infra
- specs:
- infra (1.0.0)
- aggregate_root (~> 2.13)
- arkency-command_bus
- dry-struct
- dry-types
- rake
- ruby_event_store (~> 2.13)
- ruby_event_store-transformations
- sidekiq
-
-GEM
- remote: https://oss:7AXfeZdAfCqL1PvHm2nvDJO6Zd9UW8IK@gem.mutant.dev/
- specs:
- mutant-license (0.1.1.2.1627430819213747598431630701693729869473.6)
-
-GEM
- remote: https://rubygems.org/
- specs:
- activesupport (7.1.2)
- base64
- bigdecimal
- concurrent-ruby (~> 1.0, >= 1.0.2)
- connection_pool (>= 2.2.5)
- drb
- i18n (>= 1.6, < 2)
- minitest (>= 5.1)
- mutex_m
- tzinfo (~> 2.0)
- aggregate_root (2.13.0)
- ruby_event_store (= 2.13.0)
- arkency-command_bus (0.4.1)
- concurrent-ruby
- ast (2.4.2)
- base64 (0.2.0)
- bigdecimal (3.1.4)
- concurrent-ruby (1.2.2)
- connection_pool (2.4.1)
- diff-lcs (1.5.0)
- drb (2.2.0)
- ruby2_keywords
- dry-core (1.0.1)
- concurrent-ruby (~> 1.0)
- zeitwerk (~> 2.6)
- dry-inflector (1.0.0)
- dry-logic (1.5.0)
- concurrent-ruby (~> 1.0)
- dry-core (~> 1.0, < 2)
- zeitwerk (~> 2.6)
- dry-struct (1.6.0)
- dry-core (~> 1.0, < 2)
- dry-types (>= 1.7, < 2)
- ice_nine (~> 0.11)
- zeitwerk (~> 2.6)
- dry-types (1.7.1)
- concurrent-ruby (~> 1.0)
- dry-core (~> 1.0)
- dry-inflector (~> 1.0)
- dry-logic (~> 1.4)
- zeitwerk (~> 2.6)
- i18n (1.14.1)
- concurrent-ruby (~> 1.0)
- ice_nine (0.11.2)
- minitest (5.15.0)
- mutant (0.11.26)
- diff-lcs (~> 1.3)
- parser (~> 3.2.2, >= 3.2.2.4)
- regexp_parser (~> 2.8.2)
- sorbet-runtime (~> 0.5.0)
- unparser (~> 0.6.9)
- mutant-minitest (0.11.26)
- minitest (~> 5.11)
- mutant (= 0.11.26)
- mutex_m (0.2.0)
- parser (3.2.2.4)
- ast (~> 2.4.1)
- racc
- racc (1.7.3)
- rack (3.0.8)
- rake (13.1.0)
- redis-client (0.19.0)
- connection_pool
- regexp_parser (2.8.3)
- ruby2_keywords (0.0.5)
- ruby_event_store (2.13.0)
- concurrent-ruby (~> 1.0, >= 1.1.6)
- ruby_event_store-transformations (0.1.0)
- activesupport (>= 5.0)
- ruby_event_store (>= 2.0.0, < 3.0.0)
- sidekiq (7.2.0)
- concurrent-ruby (< 2)
- connection_pool (>= 2.3.0)
- rack (>= 2.2.4)
- redis-client (>= 0.14.0)
- sorbet-runtime (0.5.11190)
- tzinfo (2.0.6)
- concurrent-ruby (~> 1.0)
- unparser (0.6.12)
- diff-lcs (~> 1.3)
- parser (>= 3.2.2.4)
- zeitwerk (2.6.12)
-
-PLATFORMS
- arm64-darwin-20
- arm64-darwin-21
- ruby
- x86_64-darwin-20
- x86_64-linux
-
-DEPENDENCIES
- infra!
- minitest (= 5.15.0)!
- mutant-license!
- mutant-minitest (= 0.11.26)!
-
-BUNDLED WITH
- 2.5.9
diff --git a/ecommerce/taxes/Makefile b/ecommerce/taxes/Makefile
deleted file mode 100644
index 23850fab1..000000000
--- a/ecommerce/taxes/Makefile
+++ /dev/null
@@ -1,10 +0,0 @@
-install:
- @bundle install
-
-test:
- @bundle exec ruby -e "require \"rake/rake_test_loader\"" test/*_test.rb
-
-mutate:
- @RAILS_ENV=test bundle exec mutant run
-
-.PHONY: install test mutate
diff --git a/ecommerce/taxes/README.md b/ecommerce/taxes/README.md
deleted file mode 100644
index e4423ba4c..000000000
--- a/ecommerce/taxes/README.md
+++ /dev/null
@@ -1,10 +0,0 @@
-# Taxes
-
-[](https://github.com/RailsEventStore/cqrs-es-sample-with-res/actions/workflows/taxes.yml)
-
-
-#### Up and running
-
-```
-make install test mutate
-```
diff --git a/ecommerce/taxes/lib/taxes.rb b/ecommerce/taxes/lib/taxes.rb
deleted file mode 100644
index f1c9db7a7..000000000
--- a/ecommerce/taxes/lib/taxes.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-require 'infra'
-require_relative 'taxes/commands'
-require_relative 'taxes/events'
-require_relative 'taxes/services'
-require_relative 'taxes/product'
-require_relative 'taxes/vat_rate_catalog'
-
-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))
- end
- end
-end
diff --git a/ecommerce/taxes/lib/taxes/commands.rb b/ecommerce/taxes/lib/taxes/commands.rb
deleted file mode 100644
index b3bb5153a..000000000
--- a/ecommerce/taxes/lib/taxes/commands.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-module Taxes
- class SetVatRate < Infra::Command
- attribute :product_id, Infra::Types::UUID
- attribute :vat_rate, Infra::Types::VatRate
- 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
diff --git a/ecommerce/taxes/lib/taxes/events.rb b/ecommerce/taxes/lib/taxes/events.rb
deleted file mode 100644
index 7f3f308b9..000000000
--- a/ecommerce/taxes/lib/taxes/events.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-module Taxes
- class VatRateSet < Infra::Event
- attribute :product_id, Infra::Types::UUID
- attribute :vat_rate, Infra::Types::VatRate
- end
-
- class VatRateDetermined < Infra::Event
- attribute :order_id, Infra::Types::UUID
- attribute :product_id, Infra::Types::UUID
- attribute :vat_rate, Infra::Types::VatRate
- end
-end
\ No newline at end of file
diff --git a/ecommerce/taxes/lib/taxes/product.rb b/ecommerce/taxes/lib/taxes/product.rb
deleted file mode 100644
index 493c1825c..000000000
--- a/ecommerce/taxes/lib/taxes/product.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-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
deleted file mode 100644
index eb639f8bf..000000000
--- a/ecommerce/taxes/lib/taxes/services.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-module Taxes
- class SetVatRateHandler
- def initialize(event_store)
- @repository = Infra::AggregateRootRepository.new(event_store)
- end
-
- def call(cmd)
- @repository.with_aggregate(Product, cmd.product_id) do |product|
- product.set_vat_rate(cmd.vat_rate)
- end
- end
- end
-
- class DetermineVatRateHandler
- def initialize(event_store)
- @catalog = VatRateCatalog.new(event_store)
- @event_store = event_store
- end
-
- def call(cmd)
- order_id = cmd.order_id
- product_id = cmd.product_id
- vat_rate = catalog.vat_rate_for(product_id)
- return unless vat_rate
- event = VatRateDetermined.new(data: { order_id: order_id, product_id: product_id, vat_rate: vat_rate })
- event_store.publish(event, stream_name: stream_name(order_id))
- end
-
- private
-
- attr_reader :catalog, :event_store
-
- def stream_name(order_id)
- "Taxes::Order$#{order_id}"
- end
- end
-end
\ No newline at end of file
diff --git a/ecommerce/taxes/lib/taxes/vat_rate_catalog.rb b/ecommerce/taxes/lib/taxes/vat_rate_catalog.rb
deleted file mode 100644
index d00fd4266..000000000
--- a/ecommerce/taxes/lib/taxes/vat_rate_catalog.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-module Taxes
- class VatRateCatalog
- def initialize(event_store)
- @event_store = event_store
- end
-
- def vat_rate_for(product_id)
- @event_store
- .read
- .of_type(VatRateSet)
- .to_a
- .filter { |e| e.data.fetch(:product_id).eql?(product_id) }
- .last
- &.data
- &.fetch(:vat_rate)
- end
- end
-end
\ No newline at end of file
diff --git a/ecommerce/taxes/test/taxes_test.rb b/ecommerce/taxes/test/taxes_test.rb
deleted file mode 100644
index 77e58d57f..000000000
--- a/ecommerce/taxes/test/taxes_test.rb
+++ /dev/null
@@ -1,53 +0,0 @@
-require_relative "test_helper"
-
-module Taxes
- class TaxesTest < Test
- def test_setting_available_vat_rate
- product_id = SecureRandom.uuid
- vat_rate_set = VatRateSet.new(data: { product_id: product_id, vat_rate: available_vat_rate })
- assert_events("Taxes::Product$#{product_id}", vat_rate_set) do
- set_vat_rate(product_id, available_vat_rate)
- 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)
- end
- end
-
- def test_determining_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 })
- assert_events("Taxes::Order$#{order_id}", vat_rate_determined) do
- determine_vat_rate(order_id, product_id, available_vat_rate)
- end
- assert_events("Taxes::Order$#{order_id}") do
- determine_vat_rate(order_id, another_product_id, available_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))
- 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)
- end
- end
-end
diff --git a/ecommerce/taxes/test/test_helper.rb b/ecommerce/taxes/test/test_helper.rb
deleted file mode 100644
index a23c2d113..000000000
--- a/ecommerce/taxes/test/test_helper.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-require "minitest/autorun"
-require "mutant/minitest/coverage"
-
-require_relative "../lib/taxes"
-
-module Taxes
- class Test < Infra::InMemoryTest
- cover "Taxes*"
-
- 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)
- end
- end
-end
diff --git a/hanami_application/README.md b/hanami_application/README.md
deleted file mode 100644
index 1c13d671a..000000000
--- a/hanami_application/README.md
+++ /dev/null
@@ -1,4 +0,0 @@
-Coming soon, there's no obstacle to use it with ~~Rails~~ **RubyEventStore**.
-
-It will reimplement most, if not all, of counterpart Rails application. It will also reuse contexts from [ecommerce/](../ecommerce/)
-
diff --git a/infra/Gemfile.lock b/infra/Gemfile.lock
index 6af14d8eb..61ce38f5b 100644
--- a/infra/Gemfile.lock
+++ b/infra/Gemfile.lock
@@ -4,8 +4,6 @@ PATH
infra (1.0.0)
aggregate_root (~> 2.13)
arkency-command_bus
- dry-struct
- dry-types
rake
ruby_event_store (~> 2.13)
ruby_event_store-transformations
@@ -41,28 +39,8 @@ GEM
diff-lcs (1.5.0)
drb (2.2.0)
ruby2_keywords
- dry-core (1.0.1)
- concurrent-ruby (~> 1.0)
- zeitwerk (~> 2.6)
- dry-inflector (1.0.0)
- dry-logic (1.5.0)
- concurrent-ruby (~> 1.0)
- dry-core (~> 1.0, < 2)
- zeitwerk (~> 2.6)
- dry-struct (1.6.0)
- dry-core (~> 1.0, < 2)
- dry-types (>= 1.7, < 2)
- ice_nine (~> 0.11)
- zeitwerk (~> 2.6)
- dry-types (1.7.1)
- concurrent-ruby (~> 1.0)
- dry-core (~> 1.0)
- dry-inflector (~> 1.0)
- dry-logic (~> 1.4)
- zeitwerk (~> 2.6)
i18n (1.14.1)
concurrent-ruby (~> 1.0)
- ice_nine (0.11.2)
minitest (5.15.0)
mutant (0.11.26)
diff-lcs (~> 1.3)
@@ -100,7 +78,6 @@ GEM
unparser (0.6.12)
diff-lcs (~> 1.3)
parser (>= 3.2.2.4)
- zeitwerk (2.6.12)
PLATFORMS
arm64-darwin-20
diff --git a/infra/infra.gemspec b/infra/infra.gemspec
index d11576240..d6600e089 100644
--- a/infra/infra.gemspec
+++ b/infra/infra.gemspec
@@ -10,8 +10,6 @@ Gem::Specification.new do |spec|
spec.summary = "infrastructure for the application"
spec.add_dependency "rake"
- spec.add_dependency "dry-struct"
- spec.add_dependency "dry-types"
spec.add_dependency "aggregate_root", "~> 2.13"
spec.add_dependency "arkency-command_bus"
spec.add_dependency "ruby_event_store", "~> 2.13"
diff --git a/infra/lib/infra.rb b/infra/lib/infra.rb
index fe6255798..f1d55b580 100644
--- a/infra/lib/infra.rb
+++ b/infra/lib/infra.rb
@@ -1,8 +1,6 @@
require "ruby_event_store"
require "aggregate_root"
require "arkency/command_bus"
-require "dry-struct"
-require "dry-types"
require "aggregate_root"
require "active_support/notifications"
require "minitest"
@@ -15,5 +13,4 @@
require_relative "infra/event"
require_relative "infra/event_store"
require_relative "infra/process"
-require_relative "infra/types"
require_relative "infra/testing"
diff --git a/infra/lib/infra/command.rb b/infra/lib/infra/command.rb
index 00c1aa263..37916b6af 100644
--- a/infra/lib/infra/command.rb
+++ b/infra/lib/infra/command.rb
@@ -1,11 +1,5 @@
module Infra
- class Command < Dry::Struct
+ class Command
Invalid = Class.new(StandardError)
-
- def self.new(*)
- super
- rescue Dry::Struct::Error => doh
- raise Invalid, doh
- end
end
end
diff --git a/infra/lib/infra/event.rb b/infra/lib/infra/event.rb
index 67a10176f..2548b377c 100644
--- a/infra/lib/infra/event.rb
+++ b/infra/lib/infra/event.rb
@@ -1,32 +1,4 @@
-require "active_support/core_ext/hash"
-
module Infra
class Event < RubyEventStore::Event
- module WithSchema
- class Schema < Dry::Struct
- transform_keys(&:to_sym)
- end
-
- module ClassMethods
- extend Forwardable
- def_delegators :schema, :attribute, :attribute?
-
- def schema
- @schema ||= Class.new(Schema)
- end
- end
-
- module Constructor
- def initialize(event_id: SecureRandom.uuid, metadata: nil, data: {})
- super(event_id: event_id, metadata: metadata, data: data.deep_merge(self.class.schema.new(data.deep_symbolize_keys).to_h))
- end
- end
-
- def self.included(klass)
- klass.extend WithSchema::ClassMethods
- klass.include WithSchema::Constructor
- end
- end
- include WithSchema
end
end
diff --git a/infra/lib/infra/testing.rb b/infra/lib/infra/testing.rb
index ee7778386..82514057f 100644
--- a/infra/lib/infra/testing.rb
+++ b/infra/lib/infra/testing.rb
@@ -54,9 +54,18 @@ def assert_events_contain(stream_name, *expected_events)
end
end
- def assert_changes(actuals, expected)
- expects = expected.map(&:data)
- assert_equal(expects, actuals.map(&:data))
+ def assert_nothing_published_within
+ before = event_store.read.to_a
+ yield
+ after = event_store.read.to_a
+ assert_equal before, after
+ end
+
+ def assert_nothing_published_within
+ before = event_store.read.to_a
+ yield
+ after = event_store.read.to_a
+ assert_equal before, after
end
end
end
diff --git a/infra/lib/infra/types.rb b/infra/lib/infra/types.rb
deleted file mode 100644
index f4f1d45fb..000000000
--- a/infra/lib/infra/types.rb
+++ /dev/null
@@ -1,41 +0,0 @@
-module Infra
- module Types
- include Dry.Types
- UUID =
- Types::Strict::String.constrained(
- format: /\A[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}\z/i
- )
- ID = Types::Strict::Integer
- Metadata =
- Types::Hash.schema(
- timestamp: Types::Time.meta(omittable: true)
- )
- OrderNumber =
- Types::Strict::String.constrained(format: /\A\d{4}\/\d{2}\/\d+\z/i)
- Quantity = Types::Strict::Integer.constrained(gt: 0)
- Price = Types::Coercible::Decimal.constrained(gt: 0)
- Value = Types::Coercible::Decimal
- PercentageDiscount = Types::Coercible::Decimal.constrained(gt: 0, lteq: 100)
- CouponDiscount = Types::Coercible::Float.constrained(gt: 0, lteq: 100)
- UUIDQuantityHash = Types::Hash.map(UUID, Quantity)
-
- class VatRate < Dry::Struct
- include Comparable
- attribute :code, Types::String
- attribute :rate, Types::Coercible::Decimal.constrained(gteq: 0, lteq: 100)
-
- def <=>(other)
- rate <=> other.rate
- end
-
- alias to_d rate
- end
-
- class PostalAddress < Dry::Struct
- attribute :line_1, Types::String
- attribute :line_2, Types::String
- attribute :line_3, Types::String
- attribute :line_4, Types::String
- end
- end
-end
diff --git a/instruction.md b/instruction.md
new file mode 100644
index 000000000..49b5cefa8
--- /dev/null
+++ b/instruction.md
@@ -0,0 +1,1070 @@
+# Exercises
+
+In the project there are integration tests available. During the exercises we're going to
+refactor the system. Our goal is to keep tests green at each step. That way
+we'll ensure that we're not breaking anything.
+
+Before running tests make sure you're in the `rails_application` directory.
+To run the integration tests use the following command:
+`bin/rails t test/integration/`
+
+To run all tests use `bin/rails t`.
+
+## Disclaimer
+
+There is no one and only correct solution to this exercise. However, to make it
+easier to follow the instruction we suggest to stick with suggested naming of events,
+event handlers etc.
+
+## Exercise 1 - Most Recent Products In Unfinished Orders
+
+### Problem
+
+Business needs a new report. The report is there to help to
+visualize which products are most often abandoned in the cart.
+
+This is a long-term initiative. Business is aware that currently there
+is no past data to get this information. However, they want to start.
+
+### Solution proposition
+
+We decided that we'll introduce events to gather information needed to
+build the report.
+
+### Step 1 - publishing events
+
+We'll start with publishing events necessary to build the report.
+Think (and discuss, if you're doing this exercise in a group) about events
+required to build the report.
+
+Additionally, think about what Bounded Context the events should belong to.
+
+Create new modules (there will be 2), based on your idea about Bounded Context, in `app/models`
+and add necessary events there.
+
+Events should inherit from `Infra::Event`. An example of an event:
+
+```ruby
+
+module Shipping
+ class PackageShipped < Infra::Event
+ end
+end
+```
+
+
+ Hint
+
+Minimal solution requires us to know:
+
+- Existing products and their names. Therefore, we need to publish following events:
+ - `ProductCreated`
+ - `ProductNameChanged`
+ - `OrderExpired`
+
+`ProductCreated` and `ProductNameChanged` should belong to `ProductCatalog` module.
+`OrderExpired` should belong to `Ordering` module.
+
+
+
+Solution for step 1
+
+Create following directories in `app/models`:
+
+- product_catalog
+- ordering
+
+Add following events:
+
+```ruby
+# app/models/product_catalog/product_created.rb
+module ProductCatalog
+ class ProductCreated < Infra::Event
+ end
+end
+
+# app/models/product_catalog/product_name_changed.rb
+module ProductCatalog
+ class ProductNameChanged < Infra::Event
+ end
+end
+
+# app/models/ordering/order_expired.rb
+module Ordering
+ class OrderExpired < Infra::Event
+ end
+end
+```
+
+Those are all events we need for minimal solution.
+
+In `ProductsController#create` method introduce following change:
+
+```ruby
+-Product.create!(product_params)
++ApplicationRecord.transaction do
+ +product = Product.create!(product_params)
+ +event_store.publish(ProductCatalog::ProductCreated.new(data: product_params.merge(id: product.id)), stream_name: "ProductCatalog::Product$#{product.id}")
+ +
+end
+```
+
+In `ProductsController#update` method introduce following change:
+
+```ruby
+-if params["future_price"].present?
+ -@product.future_price = params["future_price"]["price"]
+ -@product.future_price_start_time = params["future_price"]["start_time"]
+ -@product.save!
+ +ApplicationRecord.transaction do
+ +if params["future_price"].present?
+ +@product.future_price = params["future_price"]["price"]
+ +@product.future_price_start_time = params["future_price"]["start_time"]
+ +@product.save!
+ +
+ end
+ +if !!product_params[:name] && @product.name != product_params[:name]
+ +event_store.publish(ProductCatalog::ProductNameChanged.new(data: { id: @product.id, name: product_params[:name] }), stream_name: "ProductCatalog::Product$#{@product.id}"
+ )
+ +
+ elsif !!product_params[:price] && @product.price != product_params[:price]
+ +event_store.publish(ProductCatalog::ProductPriceChanged.new(data: { id: @product.id, price: product_params[:price].to_d }), stream_name: "ProductCatalog::Product$#{@prod
+ uct.id}")
+ +
+ end
+ +@product.update!(product_params)
+ end
+ -@product.update!(product_params)
+```
+
+**Question**: Why do we need to wrap product creation and modification and event publishing in a transaction?
+
+
+
+### Step 2 - building the report
+
+The report will be built based on the events we've published in step 1.
+Reports name should be `MostRecentProductsInUnfinishedOrders`.
+
+The report will be created as simple Active Record model.
+
+**Question**: What schema should the report have?
+
+
+ Schema
+
+```ruby
+create_table :most_recent_products_in_unfinished_orders do |t|
+ t.string :product_name, null: false
+ t.integer :product_id, null: false, index: true
+ t.integer :number_of_unfinished_orders, default: 0, null: false
+ t.integer :number_of_items_in_unfinished_orders, default: 0, null: false
+ t.integer :order_ids, array: true, default: [], null: false
+
+ t.timestamps
+end
+```
+
+
+
+Now we need to introduce a new event handler that will update the report based on the events.
+We'll create the event handler in `read_models` directory and name it `BuildMostRecentProductsInUnfinishedOrders`.
+
+
+ Order Expired Hint
+
+You can handle `OrderExpired` event in the following way to get involved products and update the report:
+
+```ruby
+
+def handle_order_expired(event)
+ order_id = event.data[:id]
+
+ product_ids = {}
+
+ event_store.read.stream("Ordering::Order$#{order_id}").each do |event|
+ case event
+ when Ordering::ItemAdded
+ product_ids.include?(event.data[:product_id]) ? product_ids[event.data[:product_id]] += 1 : product_ids[event.data[:product_id]] = 1
+ when Ordering::ItemRemoved
+ product_ids.include?(event.data[:product_id]) ? product_ids[event.data[:product_id]] -= 1 : product_ids.delete(event.data[:product_id])
+ end
+ end
+
+ product_ids.each do |product_id, quantity|
+ report = MostRecentProductsInUnfinishedOrders.find_by(product_id: product_id)
+ report.number_of_unfinished_orders += 1
+ report.number_of_items_in_unfinished_orders += quantity
+ report.order_ids << order_id
+ report.save!
+ end
+end
+```
+
+
+
+
+ Full solution
+
+ ```ruby
+
+class BuildMostRecentProductsInUnfinishedOrders
+ def call(event)
+ case event
+ when ProductCatalog::ProductCreated
+ handle_product_created(event)
+ when ProductCatalog::ProductNameChanged
+ handle_product_name_changed(event)
+ when Ordering::OrderExpired
+ handle_order_expired(event)
+ end
+ end
+
+ private
+
+ def handle_product_created(event)
+ product_id = event.data[:id]
+ product_name = event.data[:name]
+
+ MostRecentProductsInUnfinishedOrders.create!(product_id: product_id, product_name: product_name)
+ end
+
+ def handle_product_name_changed(event)
+ product_id = event.data[:id]
+ new_name = event.data[:name]
+
+ MostRecentProductsInUnfinishedOrders.find_by(product_id: product_id)&.update!(product_name: new_name)
+ end
+
+ def handle_order_expired(event)
+ order_id = event.data[:id]
+
+ product_ids = {}
+
+ event_store.read.stream("Ordering::Order$#{order_id}").each do |event|
+ case event
+ when Ordering::ItemAdded
+ product_ids.include?(event.data[:product_id]) ? product_ids[event.data[:product_id]] += 1 : product_ids[event.data[:product_id]] = 1
+ when Ordering::ItemRemoved
+ product_ids.include?(event.data[:product_id]) ? product_ids[event.data[:product_id]] -= 1 : product_ids.delete(event.data[:product_id])
+ end
+ end
+
+ product_ids.each do |product_id, quantity|
+ report = MostRecentProductsInUnfinishedOrders.find_by(product_id: product_id)
+ report.number_of_unfinished_orders += 1
+ report.number_of_items_in_unfinished_orders += quantity
+ report.order_ids << order_id
+ report.save!
+ end
+ end
+
+ def event_store
+ Rails.configuration.event_store
+ end
+end
+```
+
+Remember to subscribe in the `lib/configuration.rb` file:
+
+```ruby
+event_store.subscribe(
+ BuildMostRecentProductsInUnfinishedOrders,
+ to: [ProductCatalog::ProductCreated, ProductCatalog::ProductNameChanged, Ordering::OrderExpired]
+)
+```
+
+
+
+
+## Exercise 2 - Decoupling Product from Stock Level
+
+### Problem
+
+### Solution
+
+### Solution proposition
+
+### Step 1 - introducing aggregate
+
+The goal of the aggregate is to take care of the stock level.
+But... stock level of what? All products? Or maybe only some of them?
+Your first goal is to answer that question.
+
+
+ Answer
+
+Aggregate should take care of stock level of a single product.
+
+Create aggregate in the `app/models/inventory` directory. Name it `Product`.
+
+
+Now it is time to write a test. What is the observable behaviour that should be tested?
+
+Events! We want to see that events are published.
+
+
+ Hint - events and methods names
+
+We suggest to start with following events:
+
+- `StockLevelIncreased`
+- `StockLevelDecreased`
+ and following method names:
+- `supply`
+- `withdraw`
+
+
+
+
+ Test implementation
+
+```ruby
+# frozen_string_literal: true
+
+require 'test_helper'
+
+module Inventory
+ class ProductTest < Infra::InMemoryTest
+ def test_supply
+ product_id = 1024
+
+ assert_events(stream_name(product_id), StockLevelIncreased.new(data: { id: product_id, quantity: 10 })) do
+ with_aggregate(product_id) do |product|
+ product.supply(10)
+ end
+ end
+ end
+
+ def test_withdraw
+ product_id = 1024
+
+ assert_events(stream_name(product_id),
+ StockLevelIncreased.new(data: { id: product_id, quantity: 10 }),
+ StockLevelDecreased.new(data: { id: product_id, quantity: 5 })
+ ) do
+ with_aggregate(product_id) do |product|
+ product.supply(10)
+ product.withdraw(5)
+ end
+ end
+ end
+
+ def test_withdraw_when_not_enough_stock_is_not_allowed
+ product_id = 1024
+
+ assert_nothing_published_within do
+ assert_raises("Not enough stock") do
+ with_aggregate(product_id) do |product|
+ product.withdraw(10)
+ end
+ end
+ end
+ end
+
+ private
+
+ def stream_name(product_id)
+ "Inventory::Product$#{product_id}"
+ end
+
+ def with_aggregate(product_id)
+ Infra::AggregateRootRepository.new(event_store).with_aggregate(Product, product_id) do |product|
+ yield product
+ end
+ end
+ end
+end
+```
+
+
+
+
+Aggregate Implementation
+
+```ruby
+# frozen_string_literal: true
+
+module Inventory
+ class Product
+ include AggregateRoot
+
+ private attr_reader :id
+
+ def initialize(id)
+ @id = id
+ @stock_level = 0
+ end
+
+ def supply(quantity)
+ apply(StockLevelIncreased.new(data: { id:, quantity: }))
+ end
+
+ def withdraw(quantity)
+ raise "Not enough stock" if @stock_level < quantity
+ apply(StockLevelDecreased.new(data: { id:, quantity: }))
+ end
+
+ on StockLevelIncreased do |event|
+ @stock_level += event.data[:quantity]
+ end
+
+ on StockLevelDecreased do |event|
+ @stock_level -= event.data[:quantity]
+ end
+ end
+end
+
+```
+
+
+
+### Step 2 - preparing for refactoring
+
+In a green field project we could just change the model and live happily after.
+
+Yea.
+
+But we're not in a green field project. Well. Most likely.
+
+That's why we need to do a migration at some point. We'll do it and we'll do it safely. But first
+we need to prepare for it. As a first step we will create a Facade that will take care of increasing and decreasing
+stock level.
+
+For simplicity we suggest naming it `ProductService`. It belongs to the `Inventory` module.
+It should have `increase_stock_level`, `decrease_stock_level` and `supply` methods.
+
+What do you think should be the implementation of those methods?
+
+
+ Solution
+Grep `product.decrement`, `product.increment` and `product.stock_level` in the codebase.
+Replace them with the call to corresponding methods in the `ProductService`.
+
+
+### Step 3 - start using new aggregate
+
+Now it is time to start using the new aggregate. We'll use it through the `ProductService` facade.
+But can we just start using it?
+
+What about the existing data? We need to migrate it.
+
+We'll start by introducing migration method and migration event into the aggregate.
+
+Add following code to the `Inventory::Product` class:
+
+```ruby
+
+def migration_event(quantity)
+ apply(StockLevelMigrated.new(data: { id:, quantity: }))
+end
+
+on StockLevelMigrated do |event|
+ @stock_level = event.data[:quantity]
+end
+```
+
+How to ensure that each record will be migrated? Migration script is one of the options.
+
+How to ensure that the migration event is the first event in the stream? Can you take system down for migration?
+
+If you can - it is a good option. But if you can't, what can you do? How to ensure that?
+
+
+ Solution
+
+We can add a little bit of logic to the `ProductService` to ensure that the migration event is the first event in the
+stream.
+
+```ruby
+ +
+
+def initialize
+ +@repository = Infra::AggregateRootRepository.new(event_store)
+ +
+end
++
+
+def decrement_stock_level(product_id)
+ -product = ::Product.find(product_id)
+ -product.decrement!(:stock_level)
+ +ApplicationRecord.with_advisory_lock("change_stock_level_for_#{product_id}") do
+ +product = ::Product.find(product_id)
+ +product_stream = event_store.read.stream("Inventory::Product$#{product_id}").to_a
+ +
+ +if product_stream.any? { |event| event.event_type == "Inventory::StockLevelMigrated" }
+ +with_inventory_product(product_id) do |aggregate|
+ +aggregate.withdraw(1)
+ +
+ end
+ +
+ else
+ +with_inventory_product(product_id) do |aggregate|
+ +aggregate.migration_event(product.stock_level)
+ +aggregate.withdraw(1)
+ +
+ end
+ +
+ end
+ +product.decrement!(:stock_level)
+ +
+ end
+end
+
+def increment_stock_level(product_id)
+ -product = ::Product.find(product_id)
+ -product.increment!(:stock_level)
+ +ApplicationRecord.with_advisory_lock("change_stock_level_for_#{product_id}") do
+ +product = ::Product.find(product_id)
+ +product_stream = event_store.read.stream("Inventory::Product$#{product_id}").to_a
+ +
+ +if product_stream.any? { |event| event.event_type == "Inventory::StockLevelMigrated" }
+ +with_inventory_product(product_id) do |aggregate|
+ +aggregate.supply(1)
+ +
+ end
+ +
+ else
+ +with_inventory_product(product_id) do |aggregate|
+ +aggregate.migration_event(product.stock_level)
+ +aggregate.supply(1)
+ +
+ end
+ +
+ end
+ +product.increment!(:stock_level)
+ +
+ end
+end
+
+def supply(product_id, quantity)
+ -product = ::Product.find(product_id)
+ -product.stock_level == nil ? product.stock_level = quantity : product.stock_level += quantity
+ -product.save!
+ +ApplicationRecord.with_advisory_lock("change_stock_level_for_#{product_id}") do
+ +product = ::Product.find(product_id)
+ +product.stock_level == nil ? product.stock_level = quantity : product.stock_level += quantity
+ +product_stream = event_store.read.stream("Inventory::Product$#{product_id}").to_a
+ +
+ +if product_stream.any? { |event| event.event_type == "Inventory::StockLevelMigrated" }
+ +with_inventory_product(product_id) do |aggregate|
+ +aggregate.supply(quantity)
+ +
+ end
+ +
+ else
+ +with_inventory_product(product_id) do |aggregate|
+ +aggregate.migration_event(product.stock_level)
+ +
+ end
+ +
+ end
+ +product.save!
+ +
+ end
+ +
+end
+
++
++private
++
++
+
+def event_store
+ +Rails.configuration.event_store
+ +
+end
++
++
+
+def with_inventory_product(product_id)
+ +@repository.with_aggregate(Inventory::Product, product_id) do |product|
+ +yield(product)
+ +
+ end
+ +
+end
+```
+
+
+
+### Step 4 - migration script
+
+Now it is time to write a migration script.
+Migration script should iterate over all products and initialize the `Inventory::Product` aggregate for each of them.
+
+But... **test things first**
+
+Write a test with expectations for your migration script.
+
+
+The test
+
+```ruby
+require "test_helper"
+require_relative "../../script/start_lifecycle_of_product_inventory_aggregate"
+
+class MigrationTest < InMemoryRESIntegrationTestCase
+ def setup
+ super
+ end
+
+ def test_migration_applies_only_to_prodcuts_that_dont_have_migration_event_in_stream
+ product_1_sku = "SKU-ST4NL3Y-1"
+ product_2_sku = "SKU-ST4NL3Y-2"
+ create_product(sku: product_1_sku)
+ create_product(sku: product_2_sku)
+ product_1_id = Product.find_by(sku: product_1_sku).id
+ product_2_id = Product.find_by(sku: product_2_sku).id
+
+ increase_stock_level_by_10(product_1_id)
+ product_1_stream = event_store.read.stream("Inventory::Product$#{product_1_id}").to_a
+ assert product_1_stream.map(&:event_type) == ["Inventory::StockLevelMigrated"]
+ product_2_stream = event_store.read.stream("Inventory::Product$#{product_2_id}").to_a
+ assert product_2_stream.empty?
+
+ start_lifecycle_of_product_inventory_aggregate
+ product_2_stream = event_store.read.stream("Inventory::Product$#{product_2_id}").to_a
+ assert product_2_stream.map(&:event_type) == ["Inventory::StockLevelMigrated"]
+ end
+
+ private
+
+ def event_store
+ Rails.configuration.event_store
+ end
+
+ def increase_stock_level_by_10(product_id)
+ post "/products/#{product_id}/supplies", params: { product_id: product_id, quantity: 10 }
+ end
+
+ def create_product(sku:)
+ post "/products", params: { product: { name: "Stanley Cup", price: 100, vat_rate: 23, sku: } }
+ end
+
+ def sku
+ "SKU-ST4NL3Y"
+ end
+end
+
+```
+
+
+
+Now you can safely write the migration script.
+
+
+ Migration script
+
+```ruby
+# frozen_string_literal: true
+
+def start_lifecycle_of_product_inventory_aggregate
+ event_store = Rails.configuration.event_store
+ repository = Infra::AggregateRootRepository.new(event_store)
+
+ p 'Starting lifecycle of product inventory aggregate'
+
+ ::Product.find_each do |product|
+ ApplicationRecord.with_advisory_lock("change_stock_level_for_#{product.id}") do
+ product_stream = event_store
+ .read
+ .stream("Inventory::Product$#{product.id}")
+ .of_type("Inventory::StockLevelMigrated")
+ .to_a
+
+ p "Skipping product: #{product.id}"
+
+ next if product_stream.any?
+
+ repository.with_aggregate(Inventory::Product, product.id) do |aggregate|
+ aggregate.migration_event(product.stock_level)
+ end
+
+ p "Migrated product: #{product.id}"
+
+ end
+ end
+
+ p "Done"
+end
+
+start_lifecycle_of_product_inventory_aggregate
+```
+
+
+
+And now, perform the migration.
+
+After the migration clean up the code. We don't need the facade to take care
+of the migration event anymore.
+
+### Step 5 - building read model on top of events
+
+Introduce `UpdateProductStockLevel` event handler that will update the stock level of the product.
+
+Events not handled by the event handler? Remember to subscribe event handler to events in `lib/configuration.rb`
+
+```ruby
+ event_store.subscribe(
+ UpdateProductStockLevel,
+ to: [Inventory::StockLevelIncreased, Inventory::StockLevelDecreased]
+)
+```
+
+Do you pass the test?
+
+```ruby
+require 'test_helper'
+
+class UpdateProductStockLevelTest < InMemoryTestCase
+ def test_happy_path
+ product = Product.create!(name: 'Test Product', price: 10, vat_rate: 23, sku: 'test', stock_level: 0)
+
+ stock_level_increased = Inventory::StockLevelIncreased.new(data: { id: product.id, quantity: 10 })
+ stock_level_increased_second_time = Inventory::StockLevelIncreased.new(data: { id: product.id, quantity: 10 })
+ event_store.append(stock_level_increased, stream_name: "Inventory::Product$#{product.id}")
+ event_store.append(stock_level_increased_second_time, stream_name: "Inventory::Product$#{product.id}")
+ UpdateProductStockLevel.new.call(stock_level_increased)
+ UpdateProductStockLevel.new.call(stock_level_increased_second_time)
+
+ assert_equal 20, product.reload.stock_level
+ end
+
+ private
+
+ def event_store
+ Rails.configuration.event_store
+ end
+end
+```
+
+Remember to remove updating product from the `ProductService` facade.
+
+
+ Do you pass this test?
+
+```ruby
+
+def test_life_is_brutal_and_full_of_traps
+ product = Product.create!(name: 'Test Product', price: 10, vat_rate: 23, sku: 'test', stock_level: 0)
+
+ stock_level_increased = Inventory::StockLevelIncreased.new(data: { id: product.id, quantity: 10 })
+ event_store.append(stock_level_increased, stream_name: "Inventory::Product$#{product.id}")
+ UpdateProductStockLevel.new.call(stock_level_increased)
+ UpdateProductStockLevel.new.call(stock_level_increased)
+
+ assert_equal 10, product.reload.stock_level
+end
+```
+
+
+
+### Step 6 - dealing with the problem
+
+First, add checkpoint column to the `Product` active record.
+
+How can we use that information to ensure idempotency?
+
+
+Answer (diff)
+
+```ruby
+-case event
+- when Inventory::StockLevelIncreased
+-product.increment!(:stock_level, event.data[:quantity])
+- when Inventory::StockLevelDecreased
+-product.decrement!(:stock_level, event.data[:quantity])
++checkpoint = product.checkpoint
++
++product_stream = event_store.read.stream("Inventory::Product$#{product.id}")
++product_stream = product_stream.from(checkpoint) if checkpoint
++
++product_stream.each do |event|
+ +case event
+ + when Inventory::StockLevelIncreased
+ +product.increment!(:stock_level, event.data[:quantity])
+ + when Inventory::StockLevelDecreased
+ +product.decrement!(:stock_level, event.data[:quantity])
+ +
+end
++product.checkpoint = event.event_id
+end
++
++product.save!
++
+end
++
++private
++
++
+
+def event_store
+ +Rails.configuration.event_store
+```
+
+
+
+### Step 7 - we can do better
+
+Introduce separate Read Model - `ProductCatalog`. What would be minimum acceptable schema?
+
+
+Schema
+
+```ruby
+
+class CreateProductCatalogs < ActiveRecord::Migration[7.0]
+ def change
+ create_table :product_catalogs do |t|
+ t.string :checkpoint
+ t.integer :product_id
+ t.integer :stock_level
+
+ t.timestamps
+ end
+ end
+end
+
+```
+
+
+Now introduce event handler to update product catalog. Remember about idempotency.
+
+
+Implementation
+
+```ruby
+module Inventory
+ class UpdateProductCatalog
+ def call(event)
+ product_catalog = ProductCatalog.find_or_initialize_by(product_id: event.data[:id])
+
+ checkpoint = product_catalog.checkpoint
+
+ product_stream = event_store.read.stream("Inventory::Product$#{product_catalog.product_id}")
+ product_stream = product_stream.from(checkpoint) if checkpoint
+
+ product_stream.each do |event|
+ case event
+ when Inventory::StockLevelIncreased
+ product_catalog.increment!(:stock_level, event.data[:quantity])
+ when Inventory::StockLevelDecreased
+ product_catalog.decrement!(:stock_level, event.data[:quantity])
+ when Inventory::StockLevelMigrated
+ product_catalog.stock_level = event.data[:quantity]
+ end
+ product_catalog.checkpoint = event.event_id
+ end
+
+ product_catalog.save!
+ end
+
+ private
+
+ def event_store
+ Rails.configuration.event_store
+ end
+ end
+end
+```
+
+
+Adjust tests accordingly.
+
+### Step 8 - product doesnt know about stock level anymore
+
+First, we need to migrate the data.
+
+Second, remember to switch from using `Product#stock_level` to `ProductCatalog#stock_level`.
+
+Can we drop the `stock_level` column now?
+
+## Exercise 3 - Tuning business rules with politics
+
+### Problem
+
+Business wants to be able to oversell products when it is a known fact that the product stock level will be filled within next 2 business days.
+
+### Solution
+
+We will introduce policies to tune business rules that are protected by the aggregate
+
+### Step 0 - prepare tests
+
+We need to have at least 2 more test cases in the `ProductTest` class.
+
+- One when the policy allows to oversell the product because stock level will be filled with enough stock in the next 2 business days
+- One when the policy does not allow to oversell the product because stock level will not be filled with enough stock in the next 2 business days
+
+
+Implementation
+
+```ruby
+ def test_withdraw_when_not_enough_stock_is_possible_when_stock_is_ordered
+ product_id = 1024
+
+ stock_level_ordered_for_tomorrow = 10
+
+ assert_events(stream_name(product_id),
+ StockLevelIncreased.new(data: { id: product_id, quantity: 2 }),
+ StockLevelDecreased.new(data: { id: product_id, quantity: 5 })
+ ) do
+ with_aggregate(product_id) do |product|
+ product.supply(2)
+ product.withdraw(5, can_oversell: StockLevelWillBeFulfilledWithin2BusinessDays.new(stock_level_ordered_for_tomorrow))
+ end
+ end
+ end
+
+ def test_withdraw_when_not_enough_stock_is_possible_when_not_enough_stock_is_ordered
+ product_id = 1024
+
+ stock_level_ordered_for_tomorrow = 1
+
+ assert_nothing_published_within do
+ assert_raises("Not enough stock") do
+ with_aggregate(product_id) do |product|
+ product.supply(2)
+ product.withdraw(5, can_oversell: StockLevelWillBeFulfilledWithin2BusinessDays.new(stock_level_ordered_for_tomorrow))
+ end
+ end
+ end
+ end
+```
+
+
+
+
+### Step 1 - introducing policy
+
+Create new policy called `StockLevelWillBeFulfilledWithin2BusinessDays` in the `Inventory` module.
+
+
+ Implementation
+
+```ruby
+ class StockLevelWillBeFulfilledWithin2BusinessDays
+ def initialize(stock_level_ordered_for_tomorrow)
+ @stock_level_ordered_for_tomorrow = stock_level_ordered_for_tomorrow
+ end
+
+ def can_fulfill?(stock_level)
+ stock_level + @stock_level_ordered_for_tomorrow >= 0
+ end
+ end
+```
+
+
+### Step 2 - extending aggregates' interface
+
+We need to allow the aggregate to accept and use the policy. Add new keyword called `can_oversell` to the withdraw method.
+
+
+Implementation
+
+```ruby
+ def withdraw(quantity, can_oversell: nil)
+ end
+```
+
+
+
+### Step 3 - using policy
+
+Now we need to use the policy in the aggregate and make tests pass.
+
+
+Implementation
+
+```ruby
+ def withdraw(quantity, can_oversell: nil)
+ enough_stock =
+ if can_oversell
+ can_oversell.can_fulfill?(@stock_level)
+ else
+ @stock_level > quantity
+ end
+
+ raise "Not enough stock" unless enough_stock
+ apply(StockLevelDecreased.new(data: { id:, quantity: }))
+end
+```
+
+
+## Exercise 4 - Processes
+
+### Problem
+
+We need to send an email when order is paid AND invoice is generated.
+
+### Solution
+
+We need to introduce process manager pattern.
+
+`InvoiceGenerated` event has been introduced into the Invoicing module.
+
+### Step 0 - prepare tests
+
+Test is already prepared in the problems directory
+
+### Step 1 - introducing process manager
+
+Create new process manager called `SendEmail` in the `services` directory.
+
+
+ Implementation
+
+```ruby
+class SendEmail
+ def call(event)
+ event_store.link(event.event_id, stream_name: "Sales$#{order_id(event)}")
+
+ state = {}
+
+ event_store.read.stream("Sales$#{order_id(event)}").each do |event_in_stream|
+ case event_in_stream
+ when Ordering::OrderPaid
+ state[:order_paid] = true
+ when Invoicing::InvoiceGenerated
+ state[:invoice_generated] = true
+ end
+ end
+
+ if state[:order_paid] && state[:invoice_generated]
+ Rails.configuration.email_client.send_email(order_id(event))
+ end
+ end
+
+ private
+
+ def order_id(event)
+ case event
+ when Ordering::OrderPaid
+ event.data[:id]
+ when Invoicing::InvoiceGenerated
+ event.data[:order_id]
+ end
+ end
+
+ def event_store
+ Rails.configuration.event_store
+ end
+end
+```
+
+
+### Step 2 - subscribe to events
+
+Subscribe to `OrderPaid` and `InvoiceGenerated` events in the `lib/configuration.rb` file.
+
+
+ Implementation
+
+```ruby
+ event_store.subscribe(
+ SendEmail,
+ to: [Ordering::OrderPaid, Invoicing::InvoiceGenerated]
+)
+```
+
diff --git a/pricing_catalog_rails_app/.dockerignore b/pricing_catalog_rails_app/.dockerignore
deleted file mode 100644
index 96123753a..000000000
--- a/pricing_catalog_rails_app/.dockerignore
+++ /dev/null
@@ -1,37 +0,0 @@
-# See https://docs.docker.com/engine/reference/builder/#dockerignore-file for more about ignoring files.
-
-# Ignore git directory.
-/.git/
-
-# Ignore bundler config.
-/.bundle
-
-# Ignore all environment files (except templates).
-/.env*
-!/.env*.erb
-
-# Ignore all default key files.
-/config/master.key
-/config/credentials/*.key
-
-# Ignore all logfiles and tempfiles.
-/log/*
-/tmp/*
-!/log/.keep
-!/tmp/.keep
-
-# Ignore pidfiles, but keep the directory.
-/tmp/pids/*
-!/tmp/pids/.keep
-
-# Ignore storage (uploaded files in development and any SQLite databases).
-/storage/*
-!/storage/.keep
-/tmp/storage/*
-!/tmp/storage/.keep
-
-# Ignore assets.
-/node_modules/
-/app/assets/builds/*
-!/app/assets/builds/.keep
-/public/assets
diff --git a/pricing_catalog_rails_app/.gitignore b/pricing_catalog_rails_app/.gitignore
deleted file mode 100644
index 802489bb8..000000000
--- a/pricing_catalog_rails_app/.gitignore
+++ /dev/null
@@ -1,37 +0,0 @@
-!/log/.keep
-!/storage/.keep
-!/tmp/.keep
-.byebug_history
-.yarn-integrity
-/.bundle
-/config/master.key
-/db/*.sqlite3*
-/db/*.sqlite3-journal
-/elm-stuff
-/log/*
-/node_modules
-/public/assets
-/public/packs
-/public/packs-test
-/storage/*
-/tmp/*
-/yarn-error.log
-coverage
-yarn-debug.log*
-
-/public/packs
-/public/packs-test
-/node_modules
-/yarn-error.log
-yarn-debug.log*
-.yarn-integrity
-.env*.local
-
-.idea
-.ruby-version
-/app/assets/builds/*
-!/app/assets/builds/.keep
-
-# Event to handlers and handler to events mappings generated by big_picture.rb script
-/lib/event_to_handlers.rb
-/lib/handler_to_events.rb
\ No newline at end of file
diff --git a/pricing_catalog_rails_app/Dockerfile b/pricing_catalog_rails_app/Dockerfile
deleted file mode 100644
index 0ffa0232b..000000000
--- a/pricing_catalog_rails_app/Dockerfile
+++ /dev/null
@@ -1,62 +0,0 @@
-# syntax = docker/dockerfile:1
-
-# Make sure RUBY_VERSION matches the Ruby version in .ruby-version and Gemfile
-ARG RUBY_VERSION=3.2.0
-FROM registry.docker.com/library/ruby:$RUBY_VERSION-slim as base
-
-# Rails app lives here
-WORKDIR /rails
-
-# Set production environment
-ENV RAILS_ENV="production" \
- BUNDLE_DEPLOYMENT="1" \
- BUNDLE_PATH="/usr/local/bundle" \
- BUNDLE_WITHOUT="development"
-
-
-# Throw-away build stage to reduce size of final image
-FROM base as build
-
-# Install packages needed to build gems
-RUN apt-get update -qq && \
- apt-get install --no-install-recommends -y build-essential git pkg-config
-
-# Install application gems
-COPY Gemfile Gemfile.lock ./
-RUN bundle install && \
- rm -rf ~/.bundle/ "${BUNDLE_PATH}"/ruby/*/cache "${BUNDLE_PATH}"/ruby/*/bundler/gems/*/.git && \
- bundle exec bootsnap precompile --gemfile
-
-# Copy application code
-COPY . .
-
-# Precompile bootsnap code for faster boot times
-RUN bundle exec bootsnap precompile app/ lib/
-
-# Precompiling assets for production without requiring secret RAILS_MASTER_KEY
-RUN SECRET_KEY_BASE_DUMMY=1 ./bin/rails assets:precompile
-
-
-# Final stage for app image
-FROM base
-
-# Install packages needed for deployment
-RUN apt-get update -qq && \
- apt-get install --no-install-recommends -y curl libsqlite3-0 && \
- rm -rf /var/lib/apt/lists /var/cache/apt/archives
-
-# Copy built artifacts: gems, application
-COPY --from=build /usr/local/bundle /usr/local/bundle
-COPY --from=build /rails /rails
-
-# Run and own only the runtime files as a non-root user for security
-RUN useradd rails --create-home --shell /bin/bash && \
- chown -R rails:rails db log storage tmp
-USER rails:rails
-
-# Entrypoint prepares the database.
-ENTRYPOINT ["/rails/bin/docker-entrypoint"]
-
-# Start the server by default, this can be overwritten at runtime
-EXPOSE 3000
-CMD ["./bin/rails", "server"]
diff --git a/pricing_catalog_rails_app/Gemfile b/pricing_catalog_rails_app/Gemfile
deleted file mode 100644
index 124d5f633..000000000
--- a/pricing_catalog_rails_app/Gemfile
+++ /dev/null
@@ -1,50 +0,0 @@
-source "https://rubygems.org"
-
-ruby "3.2.0"
-gem "rails", "~> 7.1.3"
-gem "sqlite3", "~> 1.4"
-
-# Use the Puma web server [https://github.com/puma/puma]
-gem "puma", ">= 5.0"
-
-# Use JavaScript with ESM import maps [https://github.com/rails/importmap-rails]
-gem "importmap-rails"
-
-# Hotwire's SPA-like page accelerator [https://turbo.hotwired.dev]
-gem "turbo-rails"
-
-# Hotwire's modest JavaScript framework [https://stimulus.hotwired.dev]
-gem "stimulus-rails"
-
-# Use Redis adapter to run Action Cable in production
-gem "redis", ">= 4.0.1"
-
-# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
-gem "tzinfo-data", platforms: %i[ windows jruby ]
-
-# Reduces boot times through caching; required in config/boot.rb
-gem "bootsnap", require: false
-
-group :development, :test do
- # See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem
- gem "debug", platforms: %i[ mri windows ]
-end
-
-group :development do
- # Use console on exceptions pages [https://github.com/rails/web-console]
- gem "web-console"
-
- # Add speed badges [https://github.com/MiniProfiler/rack-mini-profiler]
- # gem "rack-mini-profiler"
-
- # Speed up commands on slow machines / big apps [https://github.com/rails/spring]
- # gem "spring"
-end
-
-group :test do
- # Use system testing [https://guides.rubyonrails.org/testing.html#system-testing]
- gem "capybara"
- gem "selenium-webdriver"
-end
-gem "rails_event_store", "~> 2.14.0"
-gem "infra", path: "../infra"
\ No newline at end of file
diff --git a/pricing_catalog_rails_app/Gemfile.lock b/pricing_catalog_rails_app/Gemfile.lock
deleted file mode 100644
index 0ddc0d01e..000000000
--- a/pricing_catalog_rails_app/Gemfile.lock
+++ /dev/null
@@ -1,337 +0,0 @@
-PATH
- remote: ../infra
- specs:
- infra (1.0.0)
- aggregate_root (~> 2.13)
- arkency-command_bus
- dry-struct
- dry-types
- rake
- ruby_event_store (~> 2.13)
- ruby_event_store-transformations
- sidekiq
-
-GEM
- remote: https://rubygems.org/
- specs:
- actioncable (7.1.3)
- actionpack (= 7.1.3)
- activesupport (= 7.1.3)
- nio4r (~> 2.0)
- websocket-driver (>= 0.6.1)
- zeitwerk (~> 2.6)
- actionmailbox (7.1.3)
- actionpack (= 7.1.3)
- activejob (= 7.1.3)
- activerecord (= 7.1.3)
- activestorage (= 7.1.3)
- activesupport (= 7.1.3)
- mail (>= 2.7.1)
- net-imap
- net-pop
- net-smtp
- actionmailer (7.1.3)
- actionpack (= 7.1.3)
- actionview (= 7.1.3)
- activejob (= 7.1.3)
- activesupport (= 7.1.3)
- mail (~> 2.5, >= 2.5.4)
- net-imap
- net-pop
- net-smtp
- rails-dom-testing (~> 2.2)
- actionpack (7.1.3)
- actionview (= 7.1.3)
- activesupport (= 7.1.3)
- nokogiri (>= 1.8.5)
- racc
- rack (>= 2.2.4)
- rack-session (>= 1.0.1)
- rack-test (>= 0.6.3)
- rails-dom-testing (~> 2.2)
- rails-html-sanitizer (~> 1.6)
- actiontext (7.1.3)
- actionpack (= 7.1.3)
- activerecord (= 7.1.3)
- activestorage (= 7.1.3)
- activesupport (= 7.1.3)
- globalid (>= 0.6.0)
- nokogiri (>= 1.8.5)
- actionview (7.1.3)
- activesupport (= 7.1.3)
- builder (~> 3.1)
- erubi (~> 1.11)
- rails-dom-testing (~> 2.2)
- rails-html-sanitizer (~> 1.6)
- activejob (7.1.3)
- activesupport (= 7.1.3)
- globalid (>= 0.3.6)
- activemodel (7.1.3)
- activesupport (= 7.1.3)
- activerecord (7.1.3)
- activemodel (= 7.1.3)
- activesupport (= 7.1.3)
- timeout (>= 0.4.0)
- activestorage (7.1.3)
- actionpack (= 7.1.3)
- activejob (= 7.1.3)
- activerecord (= 7.1.3)
- activesupport (= 7.1.3)
- marcel (~> 1.0)
- activesupport (7.1.3)
- base64
- bigdecimal
- concurrent-ruby (~> 1.0, >= 1.0.2)
- connection_pool (>= 2.2.5)
- drb
- i18n (>= 1.6, < 2)
- minitest (>= 5.1)
- mutex_m
- tzinfo (~> 2.0)
- addressable (2.8.6)
- public_suffix (>= 2.0.2, < 6.0)
- aggregate_root (2.14.0)
- base64
- ruby_event_store (= 2.14.0)
- arkency-command_bus (0.4.1)
- concurrent-ruby
- base64 (0.2.0)
- bigdecimal (3.1.6)
- bindex (0.8.1)
- bootsnap (1.17.1)
- msgpack (~> 1.2)
- builder (3.2.4)
- capybara (3.40.0)
- addressable
- matrix
- mini_mime (>= 0.1.3)
- nokogiri (~> 1.11)
- rack (>= 1.6.0)
- rack-test (>= 0.6.3)
- regexp_parser (>= 1.5, < 3.0)
- xpath (~> 3.2)
- concurrent-ruby (1.2.3)
- connection_pool (2.4.1)
- crass (1.0.6)
- date (3.3.4)
- debug (1.9.1)
- irb (~> 1.10)
- reline (>= 0.3.8)
- drb (2.2.0)
- ruby2_keywords
- dry-core (1.0.1)
- concurrent-ruby (~> 1.0)
- zeitwerk (~> 2.6)
- dry-inflector (1.0.0)
- dry-logic (1.5.0)
- concurrent-ruby (~> 1.0)
- dry-core (~> 1.0, < 2)
- zeitwerk (~> 2.6)
- dry-struct (1.6.0)
- dry-core (~> 1.0, < 2)
- dry-types (>= 1.7, < 2)
- ice_nine (~> 0.11)
- zeitwerk (~> 2.6)
- dry-types (1.7.2)
- bigdecimal (~> 3.0)
- concurrent-ruby (~> 1.0)
- dry-core (~> 1.0)
- dry-inflector (~> 1.0)
- dry-logic (~> 1.4)
- zeitwerk (~> 2.6)
- erubi (1.12.0)
- globalid (1.2.1)
- activesupport (>= 6.1)
- i18n (1.14.1)
- concurrent-ruby (~> 1.0)
- ice_nine (0.11.2)
- importmap-rails (2.0.1)
- actionpack (>= 6.0.0)
- activesupport (>= 6.0.0)
- railties (>= 6.0.0)
- io-console (0.7.2)
- irb (1.11.1)
- rdoc
- reline (>= 0.4.2)
- loofah (2.22.0)
- crass (~> 1.0.2)
- nokogiri (>= 1.12.0)
- mail (2.8.1)
- mini_mime (>= 0.1.1)
- net-imap
- net-pop
- net-smtp
- marcel (1.0.2)
- matrix (0.4.2)
- mini_mime (1.1.5)
- minitest (5.21.2)
- msgpack (1.7.2)
- mutex_m (0.2.0)
- net-imap (0.4.9.1)
- date
- net-protocol
- net-pop (0.1.2)
- net-protocol
- net-protocol (0.2.2)
- timeout
- net-smtp (0.4.0.1)
- net-protocol
- nio4r (2.7.0)
- nokogiri (1.16.0-aarch64-linux)
- racc (~> 1.4)
- nokogiri (1.16.0-arm-linux)
- racc (~> 1.4)
- nokogiri (1.16.0-arm64-darwin)
- racc (~> 1.4)
- nokogiri (1.16.0-x86-linux)
- racc (~> 1.4)
- nokogiri (1.16.0-x86_64-darwin)
- racc (~> 1.4)
- nokogiri (1.16.0-x86_64-linux)
- racc (~> 1.4)
- psych (5.1.2)
- stringio
- public_suffix (5.0.4)
- puma (6.4.2)
- nio4r (~> 2.0)
- racc (1.7.3)
- rack (3.0.8)
- rack-session (2.0.0)
- rack (>= 3.0.0)
- rack-test (2.1.0)
- rack (>= 1.3)
- rackup (2.1.0)
- rack (>= 3)
- webrick (~> 1.8)
- rails (7.1.3)
- actioncable (= 7.1.3)
- actionmailbox (= 7.1.3)
- actionmailer (= 7.1.3)
- actionpack (= 7.1.3)
- actiontext (= 7.1.3)
- actionview (= 7.1.3)
- activejob (= 7.1.3)
- activemodel (= 7.1.3)
- activerecord (= 7.1.3)
- activestorage (= 7.1.3)
- activesupport (= 7.1.3)
- bundler (>= 1.15.0)
- railties (= 7.1.3)
- rails-dom-testing (2.2.0)
- activesupport (>= 5.0.0)
- minitest
- nokogiri (>= 1.6)
- rails-html-sanitizer (1.6.0)
- loofah (~> 2.21)
- nokogiri (~> 1.14)
- rails_event_store (2.14.0)
- activejob (>= 6.0)
- activemodel (>= 6.0)
- activesupport (>= 6.0)
- aggregate_root (= 2.14.0)
- arkency-command_bus (>= 0.4)
- rails_event_store_active_record (= 2.14.0)
- ruby_event_store (= 2.14.0)
- ruby_event_store-browser (= 2.14.0)
- rails_event_store_active_record (2.14.0)
- ruby_event_store-active_record (= 2.14.0)
- railties (7.1.3)
- actionpack (= 7.1.3)
- activesupport (= 7.1.3)
- irb
- rackup (>= 1.0.0)
- rake (>= 12.2)
- thor (~> 1.0, >= 1.2.2)
- zeitwerk (~> 2.6)
- rake (13.1.0)
- rdoc (6.6.2)
- psych (>= 4.0.0)
- redis (5.0.8)
- redis-client (>= 0.17.0)
- redis-client (0.19.1)
- connection_pool
- regexp_parser (2.9.0)
- reline (0.4.2)
- io-console (~> 0.5)
- rexml (3.2.6)
- ruby2_keywords (0.0.5)
- ruby_event_store (2.14.0)
- concurrent-ruby (~> 1.0, >= 1.1.6)
- ruby_event_store-active_record (2.14.0)
- activerecord (>= 6.0)
- ruby_event_store (= 2.14.0)
- ruby_event_store-browser (2.14.0)
- rack
- ruby_event_store (= 2.14.0)
- ruby_event_store-transformations (0.1.0)
- activesupport (>= 5.0)
- ruby_event_store (>= 2.0.0, < 3.0.0)
- rubyzip (2.3.2)
- selenium-webdriver (4.17.0)
- base64 (~> 0.2)
- rexml (~> 3.2, >= 3.2.5)
- rubyzip (>= 1.2.2, < 3.0)
- websocket (~> 1.0)
- sidekiq (7.2.1)
- concurrent-ruby (< 2)
- connection_pool (>= 2.3.0)
- rack (>= 2.2.4)
- redis-client (>= 0.19.0)
- sqlite3 (1.7.1-aarch64-linux)
- sqlite3 (1.7.1-arm-linux)
- sqlite3 (1.7.1-arm64-darwin)
- sqlite3 (1.7.1-x86-linux)
- sqlite3 (1.7.1-x86_64-darwin)
- sqlite3 (1.7.1-x86_64-linux)
- stimulus-rails (1.3.3)
- railties (>= 6.0.0)
- stringio (3.1.0)
- thor (1.3.0)
- timeout (0.4.1)
- turbo-rails (1.5.0)
- actionpack (>= 6.0.0)
- activejob (>= 6.0.0)
- railties (>= 6.0.0)
- tzinfo (2.0.6)
- concurrent-ruby (~> 1.0)
- web-console (4.2.1)
- actionview (>= 6.0.0)
- activemodel (>= 6.0.0)
- bindex (>= 0.4.0)
- railties (>= 6.0.0)
- webrick (1.8.1)
- websocket (1.2.10)
- websocket-driver (0.7.6)
- websocket-extensions (>= 0.1.0)
- websocket-extensions (0.1.5)
- xpath (3.2.0)
- nokogiri (~> 1.8)
- zeitwerk (2.6.12)
-
-PLATFORMS
- aarch64-linux
- arm-linux
- arm64-darwin
- x86-linux
- x86_64-darwin
- x86_64-linux
-
-DEPENDENCIES
- bootsnap
- capybara
- debug
- importmap-rails
- infra!
- puma (>= 5.0)
- rails (~> 7.1.3)
- rails_event_store (~> 2.14.0)
- redis (>= 4.0.1)
- selenium-webdriver
- sqlite3 (~> 1.4)
- stimulus-rails
- turbo-rails
- tzinfo-data
- web-console
-
-BUNDLED WITH
- 2.5.9
diff --git a/pricing_catalog_rails_app/README.md b/pricing_catalog_rails_app/README.md
deleted file mode 100644
index 7db80e4ca..000000000
--- a/pricing_catalog_rails_app/README.md
+++ /dev/null
@@ -1,24 +0,0 @@
-# README
-
-This README would normally document whatever steps are necessary to get the
-application up and running.
-
-Things you may want to cover:
-
-* Ruby version
-
-* System dependencies
-
-* Configuration
-
-* Database creation
-
-* Database initialization
-
-* How to run the test suite
-
-* Services (job queues, cache servers, search engines, etc.)
-
-* Deployment instructions
-
-* ...
diff --git a/pricing_catalog_rails_app/Rakefile b/pricing_catalog_rails_app/Rakefile
deleted file mode 100644
index 9a5ea7383..000000000
--- a/pricing_catalog_rails_app/Rakefile
+++ /dev/null
@@ -1,6 +0,0 @@
-# Add your own tasks in files placed in lib/tasks ending in .rake,
-# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
-
-require_relative "config/application"
-
-Rails.application.load_tasks
diff --git a/pricing_catalog_rails_app/app/admin/read_models/admin_catalog/admin_catalog.rb b/pricing_catalog_rails_app/app/admin/read_models/admin_catalog/admin_catalog.rb
deleted file mode 100644
index d7cc7575f..000000000
--- a/pricing_catalog_rails_app/app/admin/read_models/admin_catalog/admin_catalog.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-module AdminCatalog
-
- class Migration
- def change
- ActiveRecord::Base.connection.create_table :admin_catalog_products do |t|
- t.string :product_id
- t.string :name
- t.decimal :price
-
- t.timestamps
- end
- end
- end
-
- class Product < ActiveRecord::Base
- self.table_name = 'admin_catalog_products'
- end
-
- class Configuration
- def call(event_store)
- event_store.subscribe(
- -> (event) {Product.create(product_id: event.data[:product_id])},
- to: [ProductCatalog::ProductRegistered])
- event_store.subscribe(
- -> (event) {Product.find_by(product_id: event.data[:product_id]).update(name: event.data[:name])},
- to: [ProductCatalog::ProductNamed])
- event_store.subscribe(
- -> (event) {Product.find_by(product_id: event.data[:product_id]).update(price: event.data[:price])},
- to: [Pricing::PriceSet])
- end
-
- private
-
- end
-
-end
\ No newline at end of file
diff --git a/pricing_catalog_rails_app/app/admin/read_models/admin_catalog/index.html.erb b/pricing_catalog_rails_app/app/admin/read_models/admin_catalog/index.html.erb
deleted file mode 100644
index 67a6aa093..000000000
--- a/pricing_catalog_rails_app/app/admin/read_models/admin_catalog/index.html.erb
+++ /dev/null
@@ -1,19 +0,0 @@
-Admin Catalog
-
-<%= form_for new_product, url: {controller: "admin/catalog", action: "create"} do |f| %>
-
- <%= f.label :name %>
- <%= f.text_field :name %>
-
-
- <%= f.label :price %>
- <%= f.text_field :price %>
-
- <%= f.submit %>
-<% end %>
-
-
- <% products.each do |product| %>
- <%= product.name %>, $<%= product.price %>
- <% end %>
-
\ No newline at end of file
diff --git a/pricing_catalog_rails_app/app/admin/register_product.rb b/pricing_catalog_rails_app/app/admin/register_product.rb
deleted file mode 100644
index 4ca05e618..000000000
--- a/pricing_catalog_rails_app/app/admin/register_product.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-class RegisterProduct
- def call(name, price)
- product_id = SecureRandom.uuid
- command_bus.(ProductCatalog::RegisterProduct.new(product_id: product_id))
- command_bus.(ProductCatalog::NameProduct.new(product_id: product_id, name: name))
- command_bus.(Pricing::SetPrice.new(product_id: product_id, price: price))
- end
-
- private
-
- def command_bus
- Rails.configuration.command_bus
- end
-end
\ No newline at end of file
diff --git a/pricing_catalog_rails_app/app/assets/config/manifest.js b/pricing_catalog_rails_app/app/assets/config/manifest.js
deleted file mode 100644
index ddd546a0b..000000000
--- a/pricing_catalog_rails_app/app/assets/config/manifest.js
+++ /dev/null
@@ -1,4 +0,0 @@
-//= link_tree ../images
-//= link_directory ../stylesheets .css
-//= link_tree ../../javascript .js
-//= link_tree ../../../vendor/javascript .js
diff --git a/pricing_catalog_rails_app/app/assets/images/.keep b/pricing_catalog_rails_app/app/assets/images/.keep
deleted file mode 100644
index e69de29bb..000000000
diff --git a/pricing_catalog_rails_app/app/assets/stylesheets/application.css b/pricing_catalog_rails_app/app/assets/stylesheets/application.css
deleted file mode 100644
index 288b9ab71..000000000
--- a/pricing_catalog_rails_app/app/assets/stylesheets/application.css
+++ /dev/null
@@ -1,15 +0,0 @@
-/*
- * This is a manifest file that'll be compiled into application.css, which will include all the files
- * listed below.
- *
- * Any CSS (and SCSS, if configured) file within this directory, lib/assets/stylesheets, or any plugin's
- * vendor/assets/stylesheets directory can be referenced here using a relative path.
- *
- * You're free to add application-wide styles to this file and they'll appear at the bottom of the
- * compiled file so the styles you add here take precedence over styles defined in any other CSS
- * files in this directory. Styles in this file should be added after the last require_* statement.
- * It is generally better to create a new file per style scope.
- *
- *= require_tree .
- *= require_self
- */
diff --git a/pricing_catalog_rails_app/app/channels/application_cable/channel.rb b/pricing_catalog_rails_app/app/channels/application_cable/channel.rb
deleted file mode 100644
index d67269728..000000000
--- a/pricing_catalog_rails_app/app/channels/application_cable/channel.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-module ApplicationCable
- class Channel < ActionCable::Channel::Base
- end
-end
diff --git a/pricing_catalog_rails_app/app/channels/application_cable/connection.rb b/pricing_catalog_rails_app/app/channels/application_cable/connection.rb
deleted file mode 100644
index 0ff5442f4..000000000
--- a/pricing_catalog_rails_app/app/channels/application_cable/connection.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-module ApplicationCable
- class Connection < ActionCable::Connection::Base
- end
-end
diff --git a/pricing_catalog_rails_app/app/controllers/admin/catalog_controller.rb b/pricing_catalog_rails_app/app/controllers/admin/catalog_controller.rb
deleted file mode 100644
index a7a368379..000000000
--- a/pricing_catalog_rails_app/app/controllers/admin/catalog_controller.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-class Admin::CatalogController < ApplicationController
- def index
- products = AdminCatalog::Product.all
- prepend_view_path Rails.root.join("app", "admin", "read_models")
- render template: "admin_catalog/index",
- locals: { products: products, new_product: AdminCatalog::Product.new }
- end
-
- def create
- RegisterProduct.new.call(
- params[:admin_catalog_product][:name],
- params[:admin_catalog_product][:price]
- )
- redirect_to admin_root_path
- end
-end
\ No newline at end of file
diff --git a/pricing_catalog_rails_app/app/controllers/application_controller.rb b/pricing_catalog_rails_app/app/controllers/application_controller.rb
deleted file mode 100644
index 09705d12a..000000000
--- a/pricing_catalog_rails_app/app/controllers/application_controller.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-class ApplicationController < ActionController::Base
-end
diff --git a/pricing_catalog_rails_app/app/controllers/catalog_controller.rb b/pricing_catalog_rails_app/app/controllers/catalog_controller.rb
deleted file mode 100644
index caa1185f0..000000000
--- a/pricing_catalog_rails_app/app/controllers/catalog_controller.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-class CatalogController < ApplicationController
- def index
- products = PublicCatalog::Product.all
- prepend_view_path Rails.root.join("app", "public", "read_models")
- render template: "public_catalog/index", locals: { products: products }
- end
-end
\ No newline at end of file
diff --git a/pricing_catalog_rails_app/app/controllers/concerns/.keep b/pricing_catalog_rails_app/app/controllers/concerns/.keep
deleted file mode 100644
index e69de29bb..000000000
diff --git a/pricing_catalog_rails_app/app/helpers/application_helper.rb b/pricing_catalog_rails_app/app/helpers/application_helper.rb
deleted file mode 100644
index de6be7945..000000000
--- a/pricing_catalog_rails_app/app/helpers/application_helper.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-module ApplicationHelper
-end
diff --git a/pricing_catalog_rails_app/app/javascript/application.js b/pricing_catalog_rails_app/app/javascript/application.js
deleted file mode 100644
index 0d7b49404..000000000
--- a/pricing_catalog_rails_app/app/javascript/application.js
+++ /dev/null
@@ -1,3 +0,0 @@
-// Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails
-import "@hotwired/turbo-rails"
-import "controllers"
diff --git a/pricing_catalog_rails_app/app/javascript/controllers/application.js b/pricing_catalog_rails_app/app/javascript/controllers/application.js
deleted file mode 100644
index 1213e85c7..000000000
--- a/pricing_catalog_rails_app/app/javascript/controllers/application.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import { Application } from "@hotwired/stimulus"
-
-const application = Application.start()
-
-// Configure Stimulus development experience
-application.debug = false
-window.Stimulus = application
-
-export { application }
diff --git a/pricing_catalog_rails_app/app/javascript/controllers/hello_controller.js b/pricing_catalog_rails_app/app/javascript/controllers/hello_controller.js
deleted file mode 100644
index 5975c0789..000000000
--- a/pricing_catalog_rails_app/app/javascript/controllers/hello_controller.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import { Controller } from "@hotwired/stimulus"
-
-export default class extends Controller {
- connect() {
- this.element.textContent = "Hello World!"
- }
-}
diff --git a/pricing_catalog_rails_app/app/javascript/controllers/index.js b/pricing_catalog_rails_app/app/javascript/controllers/index.js
deleted file mode 100644
index 54ad4cad4..000000000
--- a/pricing_catalog_rails_app/app/javascript/controllers/index.js
+++ /dev/null
@@ -1,11 +0,0 @@
-// Import and register all your controllers from the importmap under controllers/*
-
-import { application } from "controllers/application"
-
-// Eager load all controllers defined in the import map under controllers/**/*_controller
-import { eagerLoadControllersFrom } from "@hotwired/stimulus-loading"
-eagerLoadControllersFrom("controllers", application)
-
-// Lazy load controllers as they appear in the DOM (remember not to preload controllers in import map!)
-// import { lazyLoadControllersFrom } from "@hotwired/stimulus-loading"
-// lazyLoadControllersFrom("controllers", application)
diff --git a/pricing_catalog_rails_app/app/jobs/application_job.rb b/pricing_catalog_rails_app/app/jobs/application_job.rb
deleted file mode 100644
index d394c3d10..000000000
--- a/pricing_catalog_rails_app/app/jobs/application_job.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-class ApplicationJob < ActiveJob::Base
- # Automatically retry jobs that encountered a deadlock
- # retry_on ActiveRecord::Deadlocked
-
- # Most jobs are safe to ignore if the underlying records are no longer available
- # discard_on ActiveJob::DeserializationError
-end
diff --git a/pricing_catalog_rails_app/app/models/application_record.rb b/pricing_catalog_rails_app/app/models/application_record.rb
deleted file mode 100644
index b63caeb8a..000000000
--- a/pricing_catalog_rails_app/app/models/application_record.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-class ApplicationRecord < ActiveRecord::Base
- primary_abstract_class
-end
diff --git a/pricing_catalog_rails_app/app/models/concerns/.keep b/pricing_catalog_rails_app/app/models/concerns/.keep
deleted file mode 100644
index e69de29bb..000000000
diff --git a/pricing_catalog_rails_app/app/public/read_models/public_catalog/index.html.erb b/pricing_catalog_rails_app/app/public/read_models/public_catalog/index.html.erb
deleted file mode 100644
index a29fdc81c..000000000
--- a/pricing_catalog_rails_app/app/public/read_models/public_catalog/index.html.erb
+++ /dev/null
@@ -1,7 +0,0 @@
-Catalog
-
-
- <% products.each do |product| %>
- <%= product.name %>, $<%= product.price %>
- <% end %>
-
\ No newline at end of file
diff --git a/pricing_catalog_rails_app/app/public/read_models/public_catalog/public_catalog.rb b/pricing_catalog_rails_app/app/public/read_models/public_catalog/public_catalog.rb
deleted file mode 100644
index 39276906a..000000000
--- a/pricing_catalog_rails_app/app/public/read_models/public_catalog/public_catalog.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-module PublicCatalog
-
- class Migration
- def change
- ActiveRecord::Base.connection.create_table :public_catalog_products do |t|
- t.string :product_id
- t.string :name
- t.decimal :price
-
- t.timestamps
- end
- end
- end
-
- class Product < ActiveRecord::Base
- self.table_name = 'public_catalog_products'
- end
-
- class Configuration
- def call(event_store)
- event_store.subscribe(
- -> (event) {Product.create(product_id: event.data[:product_id])},
- to: [ProductCatalog::ProductRegistered])
- event_store.subscribe(
- -> (event) {Product.find_by(product_id: event.data[:product_id]).update(name: event.data[:name])},
- to: [ProductCatalog::ProductNamed])
- event_store.subscribe(
- -> (event) {Product.find_by(product_id: event.data[:product_id]).update(price: event.data[:price])},
- to: [Pricing::PriceSet])
- end
-
- private
-
- end
-
-end
\ No newline at end of file
diff --git a/pricing_catalog_rails_app/app/views/layouts/application.html.erb b/pricing_catalog_rails_app/app/views/layouts/application.html.erb
deleted file mode 100644
index 453940f01..000000000
--- a/pricing_catalog_rails_app/app/views/layouts/application.html.erb
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
- PricingCatalogRailsApp
-
- <%= csrf_meta_tags %>
- <%= csp_meta_tag %>
-
- <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
- <%= javascript_importmap_tags %>
-
-
-
- <%= yield %>
-
-
diff --git a/pricing_catalog_rails_app/bin/bundle b/pricing_catalog_rails_app/bin/bundle
deleted file mode 100755
index 50da5fdf9..000000000
--- a/pricing_catalog_rails_app/bin/bundle
+++ /dev/null
@@ -1,109 +0,0 @@
-#!/usr/bin/env ruby
-# frozen_string_literal: true
-
-#
-# This file was generated by Bundler.
-#
-# The application 'bundle' is installed as part of a gem, and
-# this file is here to facilitate running it.
-#
-
-require "rubygems"
-
-m = Module.new do
- module_function
-
- def invoked_as_script?
- File.expand_path($0) == File.expand_path(__FILE__)
- end
-
- def env_var_version
- ENV["BUNDLER_VERSION"]
- end
-
- def cli_arg_version
- return unless invoked_as_script? # don't want to hijack other binstubs
- return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update`
- bundler_version = nil
- update_index = nil
- ARGV.each_with_index do |a, i|
- if update_index && update_index.succ == i && a.match?(Gem::Version::ANCHORED_VERSION_PATTERN)
- bundler_version = a
- end
- next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/
- bundler_version = $1
- update_index = i
- end
- bundler_version
- end
-
- def gemfile
- gemfile = ENV["BUNDLE_GEMFILE"]
- return gemfile if gemfile && !gemfile.empty?
-
- File.expand_path("../Gemfile", __dir__)
- end
-
- def lockfile
- lockfile =
- case File.basename(gemfile)
- when "gems.rb" then gemfile.sub(/\.rb$/, ".locked")
- else "#{gemfile}.lock"
- end
- File.expand_path(lockfile)
- end
-
- def lockfile_version
- return unless File.file?(lockfile)
- lockfile_contents = File.read(lockfile)
- return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/
- Regexp.last_match(1)
- end
-
- def bundler_requirement
- @bundler_requirement ||=
- env_var_version ||
- cli_arg_version ||
- bundler_requirement_for(lockfile_version)
- end
-
- def bundler_requirement_for(version)
- return "#{Gem::Requirement.default}.a" unless version
-
- bundler_gem_version = Gem::Version.new(version)
-
- bundler_gem_version.approximate_recommendation
- end
-
- def load_bundler!
- ENV["BUNDLE_GEMFILE"] ||= gemfile
-
- activate_bundler
- end
-
- def activate_bundler
- gem_error = activation_error_handling do
- gem "bundler", bundler_requirement
- end
- return if gem_error.nil?
- require_error = activation_error_handling do
- require "bundler/version"
- end
- return if require_error.nil? && Gem::Requirement.new(bundler_requirement).satisfied_by?(Gem::Version.new(Bundler::VERSION))
- warn "Activating bundler (#{bundler_requirement}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_requirement}'`"
- exit 42
- end
-
- def activation_error_handling
- yield
- nil
- rescue StandardError, LoadError => e
- e
- end
-end
-
-m.load_bundler!
-
-if m.invoked_as_script?
- load Gem.bin_path("bundler", "bundle")
-end
diff --git a/pricing_catalog_rails_app/bin/docker-entrypoint b/pricing_catalog_rails_app/bin/docker-entrypoint
deleted file mode 100755
index 67ef49314..000000000
--- a/pricing_catalog_rails_app/bin/docker-entrypoint
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/bin/bash -e
-
-# If running the rails server then create or migrate existing database
-if [ "${1}" == "./bin/rails" ] && [ "${2}" == "server" ]; then
- ./bin/rails db:prepare
-fi
-
-exec "${@}"
diff --git a/pricing_catalog_rails_app/bin/importmap b/pricing_catalog_rails_app/bin/importmap
deleted file mode 100755
index 36502ab16..000000000
--- a/pricing_catalog_rails_app/bin/importmap
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/usr/bin/env ruby
-
-require_relative "../config/application"
-require "importmap/commands"
diff --git a/pricing_catalog_rails_app/bin/rails b/pricing_catalog_rails_app/bin/rails
deleted file mode 100755
index efc037749..000000000
--- a/pricing_catalog_rails_app/bin/rails
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/usr/bin/env ruby
-APP_PATH = File.expand_path("../config/application", __dir__)
-require_relative "../config/boot"
-require "rails/commands"
diff --git a/pricing_catalog_rails_app/bin/rake b/pricing_catalog_rails_app/bin/rake
deleted file mode 100755
index 4fbf10b96..000000000
--- a/pricing_catalog_rails_app/bin/rake
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/usr/bin/env ruby
-require_relative "../config/boot"
-require "rake"
-Rake.application.run
diff --git a/pricing_catalog_rails_app/bin/setup b/pricing_catalog_rails_app/bin/setup
deleted file mode 100755
index 3cd5a9d78..000000000
--- a/pricing_catalog_rails_app/bin/setup
+++ /dev/null
@@ -1,33 +0,0 @@
-#!/usr/bin/env ruby
-require "fileutils"
-
-# path to your application root.
-APP_ROOT = File.expand_path("..", __dir__)
-
-def system!(*args)
- system(*args, exception: true)
-end
-
-FileUtils.chdir APP_ROOT do
- # This script is a way to set up or update your development environment automatically.
- # This script is idempotent, so that you can run it at any time and get an expectable outcome.
- # Add necessary setup steps to this file.
-
- puts "== Installing dependencies =="
- system! "gem install bundler --conservative"
- system("bundle check") || system!("bundle install")
-
- # puts "\n== Copying sample files =="
- # unless File.exist?("config/database.yml")
- # FileUtils.cp "config/database.yml.sample", "config/database.yml"
- # end
-
- puts "\n== Preparing database =="
- system! "bin/rails db:prepare"
-
- puts "\n== Removing old logs and tempfiles =="
- system! "bin/rails log:clear tmp:clear"
-
- puts "\n== Restarting application server =="
- system! "bin/rails restart"
-end
diff --git a/pricing_catalog_rails_app/config.ru b/pricing_catalog_rails_app/config.ru
deleted file mode 100644
index 4a3c09a68..000000000
--- a/pricing_catalog_rails_app/config.ru
+++ /dev/null
@@ -1,6 +0,0 @@
-# This file is used by Rack-based servers to start the application.
-
-require_relative "config/environment"
-
-run Rails.application
-Rails.application.load_server
diff --git a/pricing_catalog_rails_app/config/application.rb b/pricing_catalog_rails_app/config/application.rb
deleted file mode 100644
index 780a71cca..000000000
--- a/pricing_catalog_rails_app/config/application.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-require_relative "boot"
-
-require "rails"
-# Pick the frameworks you want:
-require "active_model/railtie"
-require "active_job/railtie"
-require "active_record/railtie"
-# require "active_storage/engine"
-require "action_controller/railtie"
-# require "action_mailer/railtie"
-# require "action_mailbox/engine"
-# require "action_text/engine"
-require "action_view/railtie"
-require "action_cable/engine"
-require "rails/test_unit/railtie"
-
-# Require the gems listed in Gemfile, including any gems
-# you've limited to :test, :development, or :production.
-Bundler.require(*Rails.groups)
-
-module PricingCatalogRailsApp
- class Application < Rails::Application
- # Initialize configuration defaults for originally generated Rails version.
- config.load_defaults 7.1
-
- # Please, add to the `ignore` list any other `lib` subdirectories that do
- # not contain `.rb` files, or that should not be reloaded or eager loaded.
- # Common ones are `templates`, `generators`, or `middleware`, for example.
- config.autoload_lib(ignore: %w(assets tasks))
-
- # Configuration for the application, engines, and railties goes here.
- #
- # These settings can be overridden in specific environments using the files
- # in config/environments, which are processed later.
- #
- # config.time_zone = "Central Time (US & Canada)"
- # config.eager_load_paths << Rails.root.join("extras")
- end
-end
diff --git a/pricing_catalog_rails_app/config/boot.rb b/pricing_catalog_rails_app/config/boot.rb
deleted file mode 100644
index 988a5ddc4..000000000
--- a/pricing_catalog_rails_app/config/boot.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
-
-require "bundler/setup" # Set up gems listed in the Gemfile.
-require "bootsnap/setup" # Speed up boot time by caching expensive operations.
diff --git a/pricing_catalog_rails_app/config/cable.yml b/pricing_catalog_rails_app/config/cable.yml
deleted file mode 100644
index 2d44db85c..000000000
--- a/pricing_catalog_rails_app/config/cable.yml
+++ /dev/null
@@ -1,11 +0,0 @@
-development:
- adapter: redis
- url: redis://localhost:6379/1
-
-test:
- adapter: test
-
-production:
- adapter: redis
- url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %>
- channel_prefix: pricing_catalog_rails_app_production
diff --git a/pricing_catalog_rails_app/config/credentials.yml.enc b/pricing_catalog_rails_app/config/credentials.yml.enc
deleted file mode 100644
index 0f0dfd275..000000000
--- a/pricing_catalog_rails_app/config/credentials.yml.enc
+++ /dev/null
@@ -1 +0,0 @@
-1bVsLr2aTAhLscEGzm9z9M6tAEvNNqGbM+wJgoEaQFkaXELdbldo0WLR2WfGRmKICorkE4reE6N47g30nr0XIHRly9lfRb0/pSXxZY06X37RF+C/amRJJ4G0bqxJTwjcE1GBQ0x3Xv7WZnj0YvIVAfZX1pa3OS4piVDHCgvf/03a5PGcSJxcfF8xbYbHb5NLWIJtJ//mjPpOZiFnJh0W7PKsP0N2KeqjPIvyb97EcOw2l6UKf9pV1ZhkOWUf6RWc3MzfvPVQkvW7qPe6CUVNWp34w5/8eZCFAtNHw9v57A/GF148I3C8p6xuF3/B74xL3yuw/xE//RdwT2Tw0BwtOOFePK+iuxEBZDhZxwDCmB8VuWr0Fai0tI4jx7DUIPC1f2inoDLb+3zrd8Yjc2I8xC0UHDDB--mwsUJW1WffHciEwk--Ih++i+PuMS7pOk1yjdDf9Q==
\ No newline at end of file
diff --git a/pricing_catalog_rails_app/config/database.yml b/pricing_catalog_rails_app/config/database.yml
deleted file mode 100644
index 796466ba2..000000000
--- a/pricing_catalog_rails_app/config/database.yml
+++ /dev/null
@@ -1,25 +0,0 @@
-# SQLite. Versions 3.8.0 and up are supported.
-# gem install sqlite3
-#
-# Ensure the SQLite 3 gem is defined in your Gemfile
-# gem "sqlite3"
-#
-default: &default
- adapter: sqlite3
- pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
- timeout: 5000
-
-development:
- <<: *default
- database: storage/development.sqlite3
-
-# Warning: The database defined as "test" will be erased and
-# re-generated from your development database when you run "rake".
-# Do not set this db to the same as development or production.
-test:
- <<: *default
- database: storage/test.sqlite3
-
-production:
- <<: *default
- database: storage/production.sqlite3
diff --git a/pricing_catalog_rails_app/config/environment.rb b/pricing_catalog_rails_app/config/environment.rb
deleted file mode 100644
index cac531577..000000000
--- a/pricing_catalog_rails_app/config/environment.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-# Load the Rails application.
-require_relative "application"
-
-# Initialize the Rails application.
-Rails.application.initialize!
diff --git a/pricing_catalog_rails_app/config/environments/development.rb b/pricing_catalog_rails_app/config/environments/development.rb
deleted file mode 100644
index 94c3ba6ab..000000000
--- a/pricing_catalog_rails_app/config/environments/development.rb
+++ /dev/null
@@ -1,65 +0,0 @@
-require "active_support/core_ext/integer/time"
-
-Rails.application.configure do
- # Settings specified here will take precedence over those in config/application.rb.
-
- # In the development environment your application's code is reloaded any time
- # it changes. This slows down response time but is perfect for development
- # since you don't have to restart the web server when you make code changes.
- config.enable_reloading = true
-
- # Do not eager load code on boot.
- config.eager_load = false
-
- # Show full error reports.
- config.consider_all_requests_local = true
-
- # Enable server timing
- config.server_timing = true
-
- # Enable/disable caching. By default caching is disabled.
- # Run rails dev:cache to toggle caching.
- if Rails.root.join("tmp/caching-dev.txt").exist?
- config.action_controller.perform_caching = true
- config.action_controller.enable_fragment_cache_logging = true
-
- config.cache_store = :memory_store
- config.public_file_server.headers = {
- "Cache-Control" => "public, max-age=#{2.days.to_i}"
- }
- else
- config.action_controller.perform_caching = false
-
- config.cache_store = :null_store
- end
-
- # Print deprecation notices to the Rails logger.
- config.active_support.deprecation = :log
-
- # Raise exceptions for disallowed deprecations.
- config.active_support.disallowed_deprecation = :raise
-
- # Tell Active Support which deprecation messages to disallow.
- config.active_support.disallowed_deprecation_warnings = []
-
- # Raise an error on page load if there are pending migrations.
- config.active_record.migration_error = :page_load
-
- # Highlight code that triggered database queries in logs.
- config.active_record.verbose_query_logs = true
-
- # Highlight code that enqueued background job in logs.
- config.active_job.verbose_enqueue_logs = true
-
- # Raises error for missing translations.
- # config.i18n.raise_on_missing_translations = true
-
- # Annotate rendered view with file names.
- # config.action_view.annotate_rendered_view_with_filenames = true
-
- # Uncomment if you wish to allow Action Cable access from any origin.
- # config.action_cable.disable_request_forgery_protection = true
-
- # Raise error when a before_action's only/except options reference missing actions
- config.action_controller.raise_on_missing_callback_actions = true
-end
diff --git a/pricing_catalog_rails_app/config/environments/production.rb b/pricing_catalog_rails_app/config/environments/production.rb
deleted file mode 100644
index 5f66ca6ab..000000000
--- a/pricing_catalog_rails_app/config/environments/production.rb
+++ /dev/null
@@ -1,88 +0,0 @@
-require "active_support/core_ext/integer/time"
-
-Rails.application.configure do
- # Settings specified here will take precedence over those in config/application.rb.
-
- # Code is not reloaded between requests.
- config.enable_reloading = false
-
- # Eager load code on boot. This eager loads most of Rails and
- # your application in memory, allowing both threaded web servers
- # and those relying on copy on write to perform better.
- # Rake tasks automatically ignore this option for performance.
- config.eager_load = true
-
- # Full error reports are disabled and caching is turned on.
- config.consider_all_requests_local = false
- config.action_controller.perform_caching = true
-
- # Ensures that a master key has been made available in ENV["RAILS_MASTER_KEY"], config/master.key, or an environment
- # key such as config/credentials/production.key. This key is used to decrypt credentials (and other encrypted files).
- # config.require_master_key = true
-
- # Disable serving static files from `public/`, relying on NGINX/Apache to do so instead.
- # config.public_file_server.enabled = false
-
- # Compress CSS using a preprocessor.
- # config.assets.css_compressor = :sass
-
- # Do not fall back to assets pipeline if a precompiled asset is missed.
- config.assets.compile = false
-
- # Enable serving of images, stylesheets, and JavaScripts from an asset server.
- # config.asset_host = "http://assets.example.com"
-
- # Specifies the header that your server uses for sending files.
- # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for Apache
- # config.action_dispatch.x_sendfile_header = "X-Accel-Redirect" # for NGINX
-
- # Mount Action Cable outside main process or domain.
- # config.action_cable.mount_path = nil
- # config.action_cable.url = "wss://example.com/cable"
- # config.action_cable.allowed_request_origins = [ "http://example.com", /http:\/\/example.*/ ]
-
- # Assume all access to the app is happening through a SSL-terminating reverse proxy.
- # Can be used together with config.force_ssl for Strict-Transport-Security and secure cookies.
- # config.assume_ssl = true
-
- # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
- config.force_ssl = true
-
- # Log to STDOUT by default
- config.logger = ActiveSupport::Logger.new(STDOUT)
- .tap { |logger| logger.formatter = ::Logger::Formatter.new }
- .then { |logger| ActiveSupport::TaggedLogging.new(logger) }
-
- # Prepend all log lines with the following tags.
- config.log_tags = [ :request_id ]
-
- # "info" includes generic and useful information about system operation, but avoids logging too much
- # information to avoid inadvertent exposure of personally identifiable information (PII). If you
- # want to log everything, set the level to "debug".
- config.log_level = ENV.fetch("RAILS_LOG_LEVEL", "info")
-
- # Use a different cache store in production.
- # config.cache_store = :mem_cache_store
-
- # Use a real queuing backend for Active Job (and separate queues per environment).
- # config.active_job.queue_adapter = :resque
- # config.active_job.queue_name_prefix = "pricing_catalog_rails_app_production"
-
- # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
- # the I18n.default_locale when a translation cannot be found).
- config.i18n.fallbacks = true
-
- # Don't log any deprecations.
- config.active_support.report_deprecations = false
-
- # Do not dump schema after migrations.
- config.active_record.dump_schema_after_migration = false
-
- # Enable DNS rebinding protection and other `Host` header attacks.
- # config.hosts = [
- # "example.com", # Allow requests from example.com
- # /.*\.example\.com/ # Allow requests from subdomains like `www.example.com`
- # ]
- # Skip DNS rebinding protection for the default health check endpoint.
- # config.host_authorization = { exclude: ->(request) { request.path == "/up" } }
-end
diff --git a/pricing_catalog_rails_app/config/environments/test.rb b/pricing_catalog_rails_app/config/environments/test.rb
deleted file mode 100644
index d349c3556..000000000
--- a/pricing_catalog_rails_app/config/environments/test.rb
+++ /dev/null
@@ -1,54 +0,0 @@
-require "active_support/core_ext/integer/time"
-
-# The test environment is used exclusively to run your application's
-# test suite. You never need to work with it otherwise. Remember that
-# your test database is "scratch space" for the test suite and is wiped
-# and recreated between test runs. Don't rely on the data there!
-
-Rails.application.configure do
- # Settings specified here will take precedence over those in config/application.rb.
-
- # While tests run files are not watched, reloading is not necessary.
- config.enable_reloading = false
-
- # Eager loading loads your entire application. When running a single test locally,
- # this is usually not necessary, and can slow down your test suite. However, it's
- # recommended that you enable it in continuous integration systems to ensure eager
- # loading is working properly before deploying your code.
- config.eager_load = ENV["CI"].present?
-
- # Configure public file server for tests with Cache-Control for performance.
- config.public_file_server.enabled = true
- config.public_file_server.headers = {
- "Cache-Control" => "public, max-age=#{1.hour.to_i}"
- }
-
- # Show full error reports and disable caching.
- config.consider_all_requests_local = true
- config.action_controller.perform_caching = false
- config.cache_store = :null_store
-
- # Render exception templates for rescuable exceptions and raise for other exceptions.
- config.action_dispatch.show_exceptions = :rescuable
-
- # Disable request forgery protection in test environment.
- config.action_controller.allow_forgery_protection = false
-
- # Print deprecation notices to the stderr.
- config.active_support.deprecation = :stderr
-
- # Raise exceptions for disallowed deprecations.
- config.active_support.disallowed_deprecation = :raise
-
- # Tell Active Support which deprecation messages to disallow.
- config.active_support.disallowed_deprecation_warnings = []
-
- # Raises error for missing translations.
- # config.i18n.raise_on_missing_translations = true
-
- # Annotate rendered view with file names.
- # config.action_view.annotate_rendered_view_with_filenames = true
-
- # Raise error when a before_action's only/except options reference missing actions
- config.action_controller.raise_on_missing_callback_actions = true
-end
diff --git a/pricing_catalog_rails_app/config/importmap.rb b/pricing_catalog_rails_app/config/importmap.rb
deleted file mode 100644
index bc060edb7..000000000
--- a/pricing_catalog_rails_app/config/importmap.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-# Pin npm packages by running ./bin/importmap
-
-pin "application"
-pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true
-pin "@hotwired/stimulus", to: "stimulus.min.js"
-pin "@hotwired/stimulus-loading", to: "stimulus-loading.js"
-pin_all_from "app/javascript/controllers", under: "controllers"
diff --git a/pricing_catalog_rails_app/config/initializers/content_security_policy.rb b/pricing_catalog_rails_app/config/initializers/content_security_policy.rb
deleted file mode 100644
index b3076b38f..000000000
--- a/pricing_catalog_rails_app/config/initializers/content_security_policy.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-# Be sure to restart your server when you modify this file.
-
-# Define an application-wide content security policy.
-# See the Securing Rails Applications Guide for more information:
-# https://guides.rubyonrails.org/security.html#content-security-policy-header
-
-# Rails.application.configure do
-# config.content_security_policy do |policy|
-# policy.default_src :self, :https
-# policy.font_src :self, :https, :data
-# policy.img_src :self, :https, :data
-# policy.object_src :none
-# policy.script_src :self, :https
-# policy.style_src :self, :https
-# # Specify URI for violation reports
-# # policy.report_uri "/csp-violation-report-endpoint"
-# end
-#
-# # Generate session nonces for permitted importmap, inline scripts, and inline styles.
-# config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s }
-# config.content_security_policy_nonce_directives = %w(script-src style-src)
-#
-# # Report violations without enforcing the policy.
-# # config.content_security_policy_report_only = true
-# end
diff --git a/pricing_catalog_rails_app/config/initializers/filter_parameter_logging.rb b/pricing_catalog_rails_app/config/initializers/filter_parameter_logging.rb
deleted file mode 100644
index c2d89e28a..000000000
--- a/pricing_catalog_rails_app/config/initializers/filter_parameter_logging.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-# Be sure to restart your server when you modify this file.
-
-# Configure parameters to be partially matched (e.g. passw matches password) and filtered from the log file.
-# Use this to limit dissemination of sensitive information.
-# See the ActiveSupport::ParameterFilter documentation for supported notations and behaviors.
-Rails.application.config.filter_parameters += [
- :passw, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn
-]
diff --git a/pricing_catalog_rails_app/config/initializers/inflections.rb b/pricing_catalog_rails_app/config/initializers/inflections.rb
deleted file mode 100644
index 3860f659e..000000000
--- a/pricing_catalog_rails_app/config/initializers/inflections.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-# Be sure to restart your server when you modify this file.
-
-# Add new inflection rules using the following format. Inflections
-# are locale specific, and you may define rules for as many different
-# locales as you wish. All of these examples are active by default:
-# ActiveSupport::Inflector.inflections(:en) do |inflect|
-# inflect.plural /^(ox)$/i, "\\1en"
-# inflect.singular /^(ox)en/i, "\\1"
-# inflect.irregular "person", "people"
-# inflect.uncountable %w( fish sheep )
-# end
-
-# These inflection rules are supported but not enabled by default:
-# ActiveSupport::Inflector.inflections(:en) do |inflect|
-# inflect.acronym "RESTful"
-# end
diff --git a/pricing_catalog_rails_app/config/initializers/permissions_policy.rb b/pricing_catalog_rails_app/config/initializers/permissions_policy.rb
deleted file mode 100644
index 7db3b9577..000000000
--- a/pricing_catalog_rails_app/config/initializers/permissions_policy.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-# Be sure to restart your server when you modify this file.
-
-# Define an application-wide HTTP permissions policy. For further
-# information see: https://developers.google.com/web/updates/2018/06/feature-policy
-
-# Rails.application.config.permissions_policy do |policy|
-# policy.camera :none
-# policy.gyroscope :none
-# policy.microphone :none
-# policy.usb :none
-# policy.fullscreen :self
-# policy.payment :self, "https://secure.example.com"
-# end
diff --git a/pricing_catalog_rails_app/config/initializers/rails_event_store.rb b/pricing_catalog_rails_app/config/initializers/rails_event_store.rb
deleted file mode 100644
index 78bde65ea..000000000
--- a/pricing_catalog_rails_app/config/initializers/rails_event_store.rb
+++ /dev/null
@@ -1,38 +0,0 @@
-require_relative "../../../ecommerce/pricing/lib/pricing"
-require_relative "../../../ecommerce/product_catalog//lib/product_catalog"
-require_relative "../../../infra/lib/infra"
-require "rails_event_store"
-require "arkency/command_bus"
-require_relative "../../app/public/read_models/public_catalog/public_catalog"
-require_relative "../../app/admin/read_models/admin_catalog/admin_catalog"
-
-Rails.configuration.to_prepare do
- Rails.configuration.event_store = Infra::EventStore.main
- Rails.configuration.command_bus = Arkency::CommandBus.new
- Configuration.new.call(Rails.configuration.event_store, Rails.configuration.command_bus)
-end
-
-class Configuration
- def call(event_store, command_bus)
- enable_res_infra_event_linking(event_store)
- [
- Pricing::Configuration.new,
- ProductCatalog::Configuration.new,
- ].each { |c| c.call(event_store, command_bus) }
- PublicCatalog::Configuration.new.call(event_store)
- AdminCatalog::Configuration.new.call(event_store)
-
- end
-
- private
-
- def enable_res_infra_event_linking(event_store)
- [
- RailsEventStore::LinkByEventType.new,
- RailsEventStore::LinkByCorrelationId.new,
- RailsEventStore::LinkByCausationId.new
- ].each { |h| event_store.subscribe_to_all_events(h) }
- end
-
-end
-
diff --git a/pricing_catalog_rails_app/config/locales/en.yml b/pricing_catalog_rails_app/config/locales/en.yml
deleted file mode 100644
index 6c349ae5e..000000000
--- a/pricing_catalog_rails_app/config/locales/en.yml
+++ /dev/null
@@ -1,31 +0,0 @@
-# Files in the config/locales directory are used for internationalization and
-# are automatically loaded by Rails. If you want to use locales other than
-# English, add the necessary files in this directory.
-#
-# To use the locales, use `I18n.t`:
-#
-# I18n.t "hello"
-#
-# In views, this is aliased to just `t`:
-#
-# <%= t("hello") %>
-#
-# To use a different locale, set it with `I18n.locale`:
-#
-# I18n.locale = :es
-#
-# This would use the information in config/locales/es.yml.
-#
-# To learn more about the API, please read the Rails Internationalization guide
-# at https://guides.rubyonrails.org/i18n.html.
-#
-# Be aware that YAML interprets the following case-insensitive strings as
-# booleans: `true`, `false`, `on`, `off`, `yes`, `no`. Therefore, these strings
-# must be quoted to be interpreted as strings. For example:
-#
-# en:
-# "yes": yup
-# enabled: "ON"
-
-en:
- hello: "Hello world"
diff --git a/pricing_catalog_rails_app/config/master.key b/pricing_catalog_rails_app/config/master.key
deleted file mode 100644
index 9bcdf7473..000000000
--- a/pricing_catalog_rails_app/config/master.key
+++ /dev/null
@@ -1 +0,0 @@
-c2400f761b119d3f092a64d00bad9fdc
\ No newline at end of file
diff --git a/pricing_catalog_rails_app/config/puma.rb b/pricing_catalog_rails_app/config/puma.rb
deleted file mode 100644
index afa809b43..000000000
--- a/pricing_catalog_rails_app/config/puma.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-# This configuration file will be evaluated by Puma. The top-level methods that
-# are invoked here are part of Puma's configuration DSL. For more information
-# about methods provided by the DSL, see https://puma.io/puma/Puma/DSL.html.
-
-# Puma can serve each request in a thread from an internal thread pool.
-# The `threads` method setting takes two numbers: a minimum and maximum.
-# Any libraries that use thread pools should be configured to match
-# the maximum value specified for Puma. Default is set to 5 threads for minimum
-# and maximum; this matches the default thread size of Active Record.
-max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
-min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count }
-threads min_threads_count, max_threads_count
-
-# Specifies that the worker count should equal the number of processors in production.
-if ENV["RAILS_ENV"] == "production"
- require "concurrent-ruby"
- worker_count = Integer(ENV.fetch("WEB_CONCURRENCY") { Concurrent.physical_processor_count })
- workers worker_count if worker_count > 1
-end
-
-# Specifies the `worker_timeout` threshold that Puma will use to wait before
-# terminating a worker in development environments.
-worker_timeout 3600 if ENV.fetch("RAILS_ENV", "development") == "development"
-
-# Specifies the `port` that Puma will listen on to receive requests; default is 3000.
-port ENV.fetch("PORT") { 3000 }
-
-# Specifies the `environment` that Puma will run in.
-environment ENV.fetch("RAILS_ENV") { "development" }
-
-# Specifies the `pidfile` that Puma will use.
-pidfile ENV.fetch("PIDFILE") { "tmp/pids/server.pid" }
-
-# Allow puma to be restarted by `bin/rails restart` command.
-plugin :tmp_restart
diff --git a/pricing_catalog_rails_app/config/routes.rb b/pricing_catalog_rails_app/config/routes.rb
deleted file mode 100644
index e77d0686b..000000000
--- a/pricing_catalog_rails_app/config/routes.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-Rails.application.routes.draw do
- mount RailsEventStore::Browser => '/res' if Rails.env.development?
- # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html
-
- # Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500.
- # Can be used by load balancers and uptime monitors to verify that the app is live.
- get "up" => "rails/health#show", as: :rails_health_check
-
- # Defines the root path route ("/")
- root "catalog#index"
-
- namespace :admin do
- root "catalog#index"
- post "catalog", to: "catalog#create"
- end
-end
diff --git a/pricing_catalog_rails_app/db/migrate/20240130110320_create_event_store_events.rb b/pricing_catalog_rails_app/db/migrate/20240130110320_create_event_store_events.rb
deleted file mode 100644
index 70389537c..000000000
--- a/pricing_catalog_rails_app/db/migrate/20240130110320_create_event_store_events.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-# frozen_string_literal: true
-
-class CreateEventStoreEvents < ActiveRecord::Migration[7.1]
- def change
- create_table(:event_store_events_in_streams, force: false) do |t|
- t.string :stream, null: false
- t.integer :position, null: true
- t.references :event, null: false, type: :string, limit: 36, index: false
- t.datetime :created_at, null: false, precision: 6, index: true
- end
- add_index :event_store_events_in_streams, [:stream, :position], unique: true
- add_index :event_store_events_in_streams, [:stream, :event_id], unique: true
- add_index :event_store_events_in_streams, [:event_id]
-
- create_table(:event_store_events, force: false) do |t|
- t.references :event, null: false, type: :string, limit: 36, index: { unique: true }
- t.string :event_type, null: false, index: true
- t.binary :metadata
- t.binary :data, null: false
- t.datetime :created_at, null: false, precision: 6, index: true
- t.datetime :valid_at, null: true, precision: 6, index: true
- end
-
- add_foreign_key "event_store_events_in_streams", "event_store_events", column: "event_id", primary_key: "event_id"
- end
-end
diff --git a/pricing_catalog_rails_app/db/migrate/20240201132941_enable_pg_crypto.rb b/pricing_catalog_rails_app/db/migrate/20240201132941_enable_pg_crypto.rb
deleted file mode 100644
index aa1d05d93..000000000
--- a/pricing_catalog_rails_app/db/migrate/20240201132941_enable_pg_crypto.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-class EnablePgCrypto < ActiveRecord::Migration[7.1]
- def change
- enable_extension "pgcrypto"
- end
-end
diff --git a/pricing_catalog_rails_app/db/migrate/20240201133038_create_products_table.rb b/pricing_catalog_rails_app/db/migrate/20240201133038_create_products_table.rb
deleted file mode 100644
index 035f3be94..000000000
--- a/pricing_catalog_rails_app/db/migrate/20240201133038_create_products_table.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-class CreateProductsTable < ActiveRecord::Migration[7.1]
- def change
- PublicCatalog::Migration.new.change
- end
-end
diff --git a/pricing_catalog_rails_app/db/migrate/20240201154705_create_admin_products.rb b/pricing_catalog_rails_app/db/migrate/20240201154705_create_admin_products.rb
deleted file mode 100644
index dd7f3b56a..000000000
--- a/pricing_catalog_rails_app/db/migrate/20240201154705_create_admin_products.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-class CreateAdminProducts < ActiveRecord::Migration[7.1]
- def change
- AdminCatalog::Migration.new.change
- end
-end
diff --git a/pricing_catalog_rails_app/db/schema.rb b/pricing_catalog_rails_app/db/schema.rb
deleted file mode 100644
index f73ac726f..000000000
--- a/pricing_catalog_rails_app/db/schema.rb
+++ /dev/null
@@ -1,55 +0,0 @@
-# This file is auto-generated from the current state of the database. Instead
-# of editing this file, please use the migrations feature of Active Record to
-# incrementally modify your database, and then regenerate this schema definition.
-#
-# This file is the source Rails uses to define your schema when running `bin/rails
-# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
-# be faster and is potentially less error prone than running all of your
-# migrations from scratch. Old migrations may fail to apply correctly if those
-# migrations use external dependencies or application code.
-#
-# It's strongly recommended that you check this file into your version control system.
-
-ActiveRecord::Schema[7.1].define(version: 2024_02_01_154705) do
- create_table "admin_catalog_products", force: :cascade do |t|
- t.string "product_id"
- t.string "name"
- t.decimal "price"
- t.datetime "created_at", null: false
- t.datetime "updated_at", null: false
- end
-
- create_table "event_store_events", force: :cascade do |t|
- t.string "event_id", limit: 36, null: false
- t.string "event_type", null: false
- t.binary "metadata"
- t.binary "data", null: false
- t.datetime "created_at", null: false
- t.datetime "valid_at"
- t.index ["created_at"], name: "index_event_store_events_on_created_at"
- t.index ["event_id"], name: "index_event_store_events_on_event_id", unique: true
- t.index ["event_type"], name: "index_event_store_events_on_event_type"
- t.index ["valid_at"], name: "index_event_store_events_on_valid_at"
- end
-
- create_table "event_store_events_in_streams", force: :cascade do |t|
- t.string "stream", null: false
- t.integer "position"
- t.string "event_id", limit: 36, null: false
- t.datetime "created_at", null: false
- t.index ["created_at"], name: "index_event_store_events_in_streams_on_created_at"
- t.index ["event_id"], name: "index_event_store_events_in_streams_on_event_id"
- t.index ["stream", "event_id"], name: "index_event_store_events_in_streams_on_stream_and_event_id", unique: true
- t.index ["stream", "position"], name: "index_event_store_events_in_streams_on_stream_and_position", unique: true
- end
-
- create_table "public_catalog_products", force: :cascade do |t|
- t.string "product_id"
- t.string "name"
- t.decimal "price"
- t.datetime "created_at", null: false
- t.datetime "updated_at", null: false
- end
-
- add_foreign_key "event_store_events_in_streams", "event_store_events", column: "event_id", primary_key: "event_id"
-end
diff --git a/pricing_catalog_rails_app/db/seeds.rb b/pricing_catalog_rails_app/db/seeds.rb
deleted file mode 100644
index 4fbd6ed97..000000000
--- a/pricing_catalog_rails_app/db/seeds.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-# This file should ensure the existence of records required to run the application in every environment (production,
-# development, test). The code here should be idempotent so that it can be executed at any point in every environment.
-# The data can then be loaded with the bin/rails db:seed command (or created alongside the database with db:setup).
-#
-# Example:
-#
-# ["Action", "Comedy", "Drama", "Horror"].each do |genre_name|
-# MovieGenre.find_or_create_by!(name: genre_name)
-# end
diff --git a/pricing_catalog_rails_app/lib/assets/.keep b/pricing_catalog_rails_app/lib/assets/.keep
deleted file mode 100644
index e69de29bb..000000000
diff --git a/pricing_catalog_rails_app/lib/tasks/.keep b/pricing_catalog_rails_app/lib/tasks/.keep
deleted file mode 100644
index e69de29bb..000000000
diff --git a/pricing_catalog_rails_app/log/.keep b/pricing_catalog_rails_app/log/.keep
deleted file mode 100644
index e69de29bb..000000000
diff --git a/pricing_catalog_rails_app/public/404.html b/pricing_catalog_rails_app/public/404.html
deleted file mode 100644
index 2be3af26f..000000000
--- a/pricing_catalog_rails_app/public/404.html
+++ /dev/null
@@ -1,67 +0,0 @@
-
-
-
- The page you were looking for doesn't exist (404)
-
-
-
-
-
-
-
-
-
The page you were looking for doesn't exist.
-
You may have mistyped the address or the page may have moved.
-
-
If you are the application owner check the logs for more information.
-
-
-
diff --git a/pricing_catalog_rails_app/public/422.html b/pricing_catalog_rails_app/public/422.html
deleted file mode 100644
index c08eac0d1..000000000
--- a/pricing_catalog_rails_app/public/422.html
+++ /dev/null
@@ -1,67 +0,0 @@
-
-
-
- The change you wanted was rejected (422)
-
-
-
-
-
-
-
-
-
The change you wanted was rejected.
-
Maybe you tried to change something you didn't have access to.
-
-
If you are the application owner check the logs for more information.
-
-
-
diff --git a/pricing_catalog_rails_app/public/500.html b/pricing_catalog_rails_app/public/500.html
deleted file mode 100644
index 78a030af2..000000000
--- a/pricing_catalog_rails_app/public/500.html
+++ /dev/null
@@ -1,66 +0,0 @@
-
-
-
- We're sorry, but something went wrong (500)
-
-
-
-
-
-
-
-
-
We're sorry, but something went wrong.
-
-
If you are the application owner check the logs for more information.
-
-
-
diff --git a/pricing_catalog_rails_app/public/apple-touch-icon-precomposed.png b/pricing_catalog_rails_app/public/apple-touch-icon-precomposed.png
deleted file mode 100644
index e69de29bb..000000000
diff --git a/pricing_catalog_rails_app/public/apple-touch-icon.png b/pricing_catalog_rails_app/public/apple-touch-icon.png
deleted file mode 100644
index e69de29bb..000000000
diff --git a/pricing_catalog_rails_app/public/favicon.ico b/pricing_catalog_rails_app/public/favicon.ico
deleted file mode 100644
index e69de29bb..000000000
diff --git a/pricing_catalog_rails_app/public/robots.txt b/pricing_catalog_rails_app/public/robots.txt
deleted file mode 100644
index c19f78ab6..000000000
--- a/pricing_catalog_rails_app/public/robots.txt
+++ /dev/null
@@ -1 +0,0 @@
-# See https://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
diff --git a/pricing_catalog_rails_app/storage/.keep b/pricing_catalog_rails_app/storage/.keep
deleted file mode 100644
index e69de29bb..000000000
diff --git a/pricing_catalog_rails_app/test/application_system_test_case.rb b/pricing_catalog_rails_app/test/application_system_test_case.rb
deleted file mode 100644
index d19212abd..000000000
--- a/pricing_catalog_rails_app/test/application_system_test_case.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-require "test_helper"
-
-class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
- driven_by :selenium, using: :chrome, screen_size: [1400, 1400]
-end
diff --git a/pricing_catalog_rails_app/test/channels/application_cable/connection_test.rb b/pricing_catalog_rails_app/test/channels/application_cable/connection_test.rb
deleted file mode 100644
index 6340bf9c0..000000000
--- a/pricing_catalog_rails_app/test/channels/application_cable/connection_test.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-require "test_helper"
-
-module ApplicationCable
- class ConnectionTest < ActionCable::Connection::TestCase
- # test "connects with cookies" do
- # cookies.signed[:user_id] = 42
- #
- # connect
- #
- # assert_equal connection.user_id, "42"
- # end
- end
-end
diff --git a/pricing_catalog_rails_app/test/controllers/.keep b/pricing_catalog_rails_app/test/controllers/.keep
deleted file mode 100644
index e69de29bb..000000000
diff --git a/pricing_catalog_rails_app/test/fixtures/files/.keep b/pricing_catalog_rails_app/test/fixtures/files/.keep
deleted file mode 100644
index e69de29bb..000000000
diff --git a/pricing_catalog_rails_app/test/helpers/.keep b/pricing_catalog_rails_app/test/helpers/.keep
deleted file mode 100644
index e69de29bb..000000000
diff --git a/pricing_catalog_rails_app/test/integration/.keep b/pricing_catalog_rails_app/test/integration/.keep
deleted file mode 100644
index e69de29bb..000000000
diff --git a/pricing_catalog_rails_app/test/models/.keep b/pricing_catalog_rails_app/test/models/.keep
deleted file mode 100644
index e69de29bb..000000000
diff --git a/pricing_catalog_rails_app/test/system/.keep b/pricing_catalog_rails_app/test/system/.keep
deleted file mode 100644
index e69de29bb..000000000
diff --git a/pricing_catalog_rails_app/test/test_helper.rb b/pricing_catalog_rails_app/test/test_helper.rb
deleted file mode 100644
index 0c22470ec..000000000
--- a/pricing_catalog_rails_app/test/test_helper.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-ENV["RAILS_ENV"] ||= "test"
-require_relative "../config/environment"
-require "rails/test_help"
-
-module ActiveSupport
- class TestCase
- # Run tests in parallel with specified workers
- parallelize(workers: :number_of_processors)
-
- # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
- fixtures :all
-
- # Add more helper methods to be used by all tests here...
- end
-end
diff --git a/pricing_catalog_rails_app/tmp/.keep b/pricing_catalog_rails_app/tmp/.keep
deleted file mode 100644
index e69de29bb..000000000
diff --git a/pricing_catalog_rails_app/tmp/local_secret.txt b/pricing_catalog_rails_app/tmp/local_secret.txt
deleted file mode 100644
index 56a4593b2..000000000
--- a/pricing_catalog_rails_app/tmp/local_secret.txt
+++ /dev/null
@@ -1 +0,0 @@
-4b650a883751aa8985f1c1e9bc291ec5dd22575339d1415cb6ca116e77fc00e6f30ae882aae0a55cc22a7611693563e3e6e3eb07b07726a4628a5665e47b54a3
\ No newline at end of file
diff --git a/pricing_catalog_rails_app/tmp/pids/.keep b/pricing_catalog_rails_app/tmp/pids/.keep
deleted file mode 100644
index e69de29bb..000000000
diff --git a/pricing_catalog_rails_app/tmp/storage/.keep b/pricing_catalog_rails_app/tmp/storage/.keep
deleted file mode 100644
index e69de29bb..000000000
diff --git a/pricing_catalog_rails_app/vendor/.keep b/pricing_catalog_rails_app/vendor/.keep
deleted file mode 100644
index e69de29bb..000000000
diff --git a/pricing_catalog_rails_app/vendor/javascript/.keep b/pricing_catalog_rails_app/vendor/javascript/.keep
deleted file mode 100644
index e69de29bb..000000000
diff --git a/rails_application/Gemfile.lock b/rails_application/Gemfile.lock
index e2c06151a..7d4136b4e 100644
--- a/rails_application/Gemfile.lock
+++ b/rails_application/Gemfile.lock
@@ -4,8 +4,6 @@ PATH
infra (1.0.0)
aggregate_root (~> 2.13)
arkency-command_bus
- dry-struct
- dry-types
rake
ruby_event_store (~> 2.13)
ruby_event_store-transformations
@@ -105,28 +103,6 @@ GEM
dotenv-rails (2.7.6)
dotenv (= 2.7.6)
railties (>= 3.2)
- dry-configurable (0.15.0)
- concurrent-ruby (~> 1.0)
- dry-core (~> 0.6)
- dry-container (0.9.0)
- concurrent-ruby (~> 1.0)
- dry-configurable (~> 0.13, >= 0.13.0)
- dry-core (0.7.1)
- concurrent-ruby (~> 1.0)
- dry-inflector (0.2.1)
- dry-logic (1.2.0)
- concurrent-ruby (~> 1.0)
- dry-core (~> 0.5, >= 0.5)
- dry-struct (1.4.0)
- dry-core (~> 0.5, >= 0.5)
- dry-types (~> 1.5)
- ice_nine (~> 0.11)
- dry-types (1.5.1)
- concurrent-ruby (~> 1.0)
- dry-container (~> 0.3)
- dry-core (~> 0.5, >= 0.5)
- dry-inflector (~> 0.1, >= 0.1.2)
- dry-logic (~> 1.0, >= 1.0.2)
erubi (1.11.0)
ffi (1.15.5)
globalid (1.2.1)
@@ -134,7 +110,6 @@ GEM
honeybadger (4.9.0)
i18n (1.14.1)
concurrent-ruby (~> 1.0)
- ice_nine (0.11.2)
importmap-rails (1.1.5)
actionpack (>= 6.0.0)
railties (>= 6.0.0)
diff --git a/rails_application/app/controllers/billing_addresses_controller.rb b/rails_application/app/controllers/billing_addresses_controller.rb
index 1240d7544..7f09cb209 100644
--- a/rails_application/app/controllers/billing_addresses_controller.rb
+++ b/rails_application/app/controllers/billing_addresses_controller.rb
@@ -1,24 +1,13 @@
class BillingAddressesController < ApplicationController
def edit
- @invoice = Invoices::Invoice.find_or_initialize_by(order_uid: params[:order_id])
- @order = Shipments::Order.find_or_initialize_by(uid: params[:order_id])
+ @order = Order.find(params[:order_id])
end
def update
- cmd =
- Invoicing::SetBillingAddress.new(
- invoice_id: params[:order_id],
- tax_id_number: address_params[:tax_id_number],
- postal_address: {
- line_1: address_params[:address_line_1],
- line_2: address_params[:address_line_2],
- line_3: address_params[:address_line_3],
- line_4: address_params[:address_line_4]
- }
- )
- command_bus.(cmd)
- @order = Shipments::Order.find_or_initialize_by(uid: params[:order_id])
+ @order = Order.find(params[:order_id])
+ @order.update!(address_params)
+
redirect_to @order.submitted? ? order_path(params[:order_id]) : edit_order_path(params[:order_id]),
notice: "Billing Address was successfully updated"
end
@@ -26,12 +15,12 @@ def update
private
def address_params
- params.require(:invoices_invoice).permit(
- :tax_id_number,
- :address_line_1,
- :address_line_2,
- :address_line_3,
- :address_line_4
+ params.require(:order).permit(
+ :invoice_tax_id_number,
+ :invoice_addressed_to,
+ :invoice_address,
+ :invoice_city,
+ :invoice_country
)
end
end
diff --git a/rails_application/app/controllers/customers_controller.rb b/rails_application/app/controllers/customers_controller.rb
index 6cf55475f..62aa4c01f 100644
--- a/rails_application/app/controllers/customers_controller.rb
+++ b/rails_application/app/controllers/customers_controller.rb
@@ -1,51 +1,38 @@
class CustomersController < ApplicationController
def index
- @customers = Customers::Customer.all
+ @customers = Customer.all
end
def new
- @customer_id = SecureRandom.uuid
+ @customer = Customer.new
end
def create
- create_customer(params[:customer_id], params[:name])
- rescue Crm::Customer::AlreadyRegistered
- flash[:notice] = "Customer was already registered"
- render "new"
- else
+ Customer.create!(customer_params)
redirect_to customers_path, notice: "Customer was successfully created"
end
def update
promote_to_vip(params[:id])
- rescue Crm::Customer::AlreadyVip
+ rescue AlreadyVip
redirect_to customers_path, notice: "Customer was marked as vip"
else
redirect_to customers_path, notice: "Customer was promoted to VIP"
end
def show
- @customer = Customers::Customer.find(params[:id])
- @customer_orders = ClientOrders::Order.where(client_uid: params[:id])
- .order(created_at: :desc)
- .page(params[:page]).per(10)
+ @customer = Customer.find(params[:id])
+ @customer_orders = @customer.orders.order(created_at: :desc)
+ .page(params[:page]).per(10)
end
private
- def create_customer(customer_id, name)
- command_bus.(create_customer_cmd(customer_id, name))
+ def customer_params
+ params.require(:customer).permit(:first_name, :last_name, :email)
end
def promote_to_vip(customer_id)
- command_bus.(promote_to_vip_cmd(customer_id))
- end
-
- def create_customer_cmd(customer_id, name)
- Crm::RegisterCustomer.new(customer_id: customer_id, name: name)
- end
-
- def promote_to_vip_cmd(customer_id)
- Crm::PromoteCustomerToVip.new(customer_id: customer_id)
+ Customer.find(customer_id).promote_to_vip
end
end
diff --git a/rails_application/app/controllers/events_catalog_controller.rb b/rails_application/app/controllers/events_catalog_controller.rb
deleted file mode 100644
index d90d6e889..000000000
--- a/rails_application/app/controllers/events_catalog_controller.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-class EventsCatalogController < ApplicationController
- def index
- render file: ENV["EVENTS_CATALOG_PATH"] || "../events_catalog/out/index.html"
- end
-end
diff --git a/rails_application/app/controllers/invoices_controller.rb b/rails_application/app/controllers/invoices_controller.rb
index bae493b21..0ff335da3 100644
--- a/rails_application/app/controllers/invoices_controller.rb
+++ b/rails_application/app/controllers/invoices_controller.rb
@@ -1,20 +1,37 @@
class InvoicesController < ApplicationController
def show
- @invoice = Invoices::Invoice.find_by_order_uid(params[:id])
+ @invoice = Order.find(params[:id])
+ @discount = @invoice.discount == 0 ? 1 : @invoice.discount
+ time_promotions = TimePromotion.where("start_time <= ? AND end_time >= ?", @invoice.completed_at, @invoice.invoice_issue_date)
+ if time_promotions.any?
+ time_promotions.sum(&:discount).tap do |time_promotion_discount|
+ @discount += time_promotion_discount
+ end
+ end
end
def create
- begin
- ActiveRecord::Base.transaction do
- command_bus.(Invoicing::IssueInvoice.new(invoice_id: params[:order_id], issue_date: Time.zone.now.to_date))
- end
- rescue Invoicing::Invoice::BillingAddressNotSpecified
+ @order = Order.find(params[:order_id])
+
+ if !@order.billing_address_specified?
flash[:alert] = "Billing address is missing"
- rescue Invoicing::Invoice::InvoiceAlreadyIssued
+ elsif @order.invoice_issued?
flash[:alert] = "Invoice was already issued"
- rescue Invoicing::Invoice::InvoiceNumberInUse
- retry
+ else
+ @order.invoice_issued = true
+ @order.invoice_issue_date = Time.current
+ invoice_total_value = @order.total - ((@order.total * @order.discount) / 100)
+ time_promotions = TimePromotion.current
+
+ if time_promotions.any?
+ time_promotions.sum(&:discount).tap do |discount|
+ invoice_total_value -= (invoice_total_value * discount) / 100
+ end
+ end
+ @order.invoice_total_value = invoice_total_value
+ @order.save!
end
+
redirect_to(invoice_path(params[:order_id]))
end
end
\ No newline at end of file
diff --git a/rails_application/app/controllers/orders_controller.rb b/rails_application/app/controllers/orders_controller.rb
index 005355a63..617fca87f 100644
--- a/rails_application/app/controllers/orders_controller.rb
+++ b/rails_application/app/controllers/orders_controller.rb
@@ -1,35 +1,52 @@
class OrdersController < ApplicationController
def index
- @orders = Orders::Order.order("id DESC").page(params[:page]).per(10)
+ @orders = Order.order("id DESC").page(params[:page]).per(10)
end
def show
- @order = Orders::Order.find_by_uid(params[:id])
+ @order = Order.find_by(id: params[:id])
return not_found unless @order
- @order_lines = Orders::OrderLine.where(order_uid: @order.uid)
- @shipment = Shipments::Shipment.find_by(order_uid: @order.uid)
- @invoice = Invoices::Invoice.find_or_initialize_by(order_uid: @order.uid)
+ @total = @order.total_after_discount
+ @order_lines = @order.order_items
end
def new
- redirect_to edit_order_path(SecureRandom.uuid)
+ @order = Order.new
+ @order.status = "Draft"
+ @order.total = 0
+ @order.discount = 0
+
+ ApplicationRecord.transaction do
+ @order.save!
+ event_store.publish(Ordering::OrderCreated.new(data: { id: @order.id }), stream_name: "Ordering::Order$#{@order.id}")
+ end
+ @order_id = @order.id
+ @products = Product.all
+ @customers = Customer.all
+ @time_promotions = TimePromotion.current
end
def edit
@order_id = params[:id]
- @order = Orders::Order.find_by_uid(params[:id])
- @order_lines = Orders::OrderLine.where(order_uid: params[:id])
- @products = Products::Product.all
- @customers = Customers::Customer.all
- @time_promotions = TimePromotions::TimePromotion.current
+ @order = Order.find(params[:id])
+ @products = Product.all
+ @customers = Customer.all
+ @time_promotions = TimePromotion.current
+ discounted_value = @order.total_after_discount
+
+ if @time_promotions.any?
+ @time_promotions.sum(&:discount).tap do |discount|
+ discounted_value -= (discounted_value * discount) / 100
+ end
+ end
render :edit,
locals: {
- discounted_value: @order&.discounted_value || 0,
- total_value: @order&.total_value || 0,
- percentage_discount: @order&.percentage_discount
+ discounted_value:,
+ total_value: @order.total,
+ percentage_discount: @order.discount
}
end
@@ -38,98 +55,132 @@ def edit_discount
end
def update_discount
- @order_id = params[:id]
- order = Orders::Order.find_or_create_by!(uid: params[:id])
- if order.percentage_discount
- command_bus.(Pricing::ChangePercentageDiscount.new(order_id: @order_id, amount: params[:amount]))
- else
- command_bus.(Pricing::SetPercentageDiscount.new(order_id: @order_id, amount: params[:amount]))
- end
+ order = Order.find(params[:id])
+ order.discount = params[:amount]
+ order.discount_updated_at = Time.current
+ order.save!
- redirect_to edit_order_path(@order_id)
+ redirect_to edit_order_path(order)
end
def reset_discount
- @order_id = params[:id]
- command_bus.(Pricing::ResetPercentageDiscount.new(order_id: @order_id))
+ order = Order.find(params[:id])
+ order.discount = 0
+ order.discount_updated_at = Time.current
+ order.save!
- redirect_to edit_order_path(@order_id)
+ redirect_to edit_order_path(order)
end
def add_item
- read_model = Orders::OrderLine.where(order_uid: params[:id], product_id: params[:product_id]).first
- unless Availability.approximately_available?(params[:product_id], (read_model&.quantity || 0) + 1)
+ product_catalog = Inventory::ProductCatalog.find_by(product_id: params[:product_id])
+ if product_catalog.stock_level < 1
redirect_to edit_order_path(params[:id]),
alert: "Product not available in requested quantity!" and return
end
- ActiveRecord::Base.transaction do
- command_bus.(Ordering::AddItemToBasket.new(order_id: params[:id], product_id: params[:product_id]))
+
+ @order = Order.find(params[:id])
+ ApplicationRecord.transaction do
+ @order.add_item(Product.find(params[:product_id]))
+ Inventory::ProductService.new.decrement_stock_level(Inventory::DecreaseStockLevel.new(product_id: product_catalog.product_id))
+ @order.save!
+ event_store.publish(Ordering::ItemAdded.new(data: { order_id: @order.id, product_id: params[:product_id] }), stream_name: "Ordering::Order$#{@order.id}")
end
- head :ok
+
+ redirect_to edit_order_path(params[:id])
end
def remove_item
- command_bus.(Ordering::RemoveItemFromBasket.new(order_id: params[:id], product_id: params[:product_id]))
- head :ok
+ product = Product.find(params[:product_id])
+ @order = Order.find(params[:id])
+ ApplicationRecord.transaction do
+ @order.remove_item(product)
+ Inventory::ProductService.new.increment_stock_level(Inventory::IncreaseStockLevel.new(product_id: product.id))
+ @order.save!
+ event_store.publish(Ordering::ItemRemoved.new(data: { order_id: @order.id, product_id: product.id }), stream_name: "Ordering::Order$#{@order.id}")
+ end
+
+ redirect_to edit_order_path(params[:id])
end
def create
- ApplicationRecord.transaction { submit_order(params[:order_id], params[:customer_id]) }
+ order = Order.find(params[:order_id])
+ if order.order_items.empty?
+ redirect_to order_path(order.id), alert: "Order cannot be submitted because it is empty"
+ return
+ end
+ customer = Customer.find_by(id: params[:customer_id])
+ unless customer
+ redirect_to order_path(order.id), alert: "Order can not be submitted! Customer does not exist."
+ return
+ end
+ ApplicationRecord.transaction { submit_order(order, params[:customer_id]) }
redirect_to order_path(params[:order_id]), notice: "Your order is being submitted"
- rescue Crm::Customer::NotExists
- redirect_to order_path(params[:order_id]), alert: "Order can not be submitted! Customer does not exist."
end
def expire
- Orders::Order
- .where(state: "Draft")
- .find_each { |order| command_bus.(Ordering::SetOrderAsExpired.new(order_id: order.uid)) }
+ Order
+ .where(status: "Draft")
+ .find_each do |order|
+ order.order_items.each do |item|
+ Inventory::ProductService.new.increment_stock_level(Inventory::IncreaseStockLevel.new(product_id: item.product_id))
+ end
+ order.status = "Expired"
+ ApplicationRecord.transaction do
+ order.save!
+ event_store.publish(Ordering::OrderExpired.new(data: { id: order.id }), stream_name: "Ordering::Order$#{order.id}")
+ end
+ end
redirect_to root_path
end
def pay
- ActiveRecord::Base.transaction do
- authorize_payment(params[:id])
- capture_payment(params[:id])
- flash[:notice] = "Order paid successfully"
- rescue Payments::Payment::AlreadyAuthorized
+ order = Order.find(params[:id])
+
+ if order.invoice_payment_status == "Authorized"
flash[:alert] = "Payment was already authorized"
- rescue Payments::Payment::AlreadyCaptured
+ elsif order.invoice_payment_status == "Captured"
flash[:alert] = "Payment was already captured"
- rescue Payments::Payment::NotAuthorized
- flash[:alert] = "Payment wasn't yet authorized"
- rescue Ordering::Order::NotPlaced
- flash[:alert] = "You can't pay for an order which is not submitted"
+
+ else
+ ActiveRecord::Base.transaction do
+ order.invoice_payment_status = "Authorized"
+ event_store.publish(Ordering::OrderPaid.new(data: { id: order.id }), stream_name: "Ordering::Order$#{order.id}")
+ order.save!
+
+ order.invoice_payment_status = "Captured"
+ order.invoice_payment_date = Time.current
+ order.status = "Paid"
+ customer = order.customer
+ customer.paid_orders_summary += order.total
+ order.save!
+ customer.save!
+
+ flash[:notice] = "Order paid successfully"
+ end
end
+
redirect_to orders_path
end
def cancel
- command_bus.(Fulfillment::CancelOrder.new(order_id: params[:id]))
+ order = Order.find(params[:id])
+ order.status = "Cancelled"
+ order.save!
redirect_to root_path, notice: "Order cancelled"
end
private
- def submit_order(order_id, customer_id)
- command_bus.(Ordering::SubmitOrder.new(order_id: order_id))
- command_bus.(Crm::AssignCustomerToOrder.new(order_id: order_id, customer_id: customer_id))
- end
-
- def authorize_payment(order_id)
- command_bus.call(authorize_payment_cmd(order_id))
- end
-
- def capture_payment(order_id)
- command_bus.call(capture_payment_cmd(order_id))
- end
-
- def authorize_payment_cmd(order_id)
- Payments::AuthorizePayment.new(order_id: order_id)
- end
-
- def capture_payment_cmd(order_id)
- Payments::CapturePayment.new(order_id: order_id)
+ def submit_order(order, customer_id)
+ if order.status == "Draft"
+ order.status = "Submitted"
+ order.number = Time.now.strftime("%Y/%m/#{SecureRandom.random_number(100)}")
+ order.customer_id = customer_id.to_i
+ order.completed_at = Time.current
+ event_store.publish(Ordering::OrderSubmitted.new(data: { id: order.id }), stream_name: "Ordering::Order$#{order.id}")
+ order.save!
+ end
end
def not_found
diff --git a/rails_application/app/controllers/product/future_price_controller.rb b/rails_application/app/controllers/product/future_price_controller.rb
deleted file mode 100644
index 6e4b5e8ab..000000000
--- a/rails_application/app/controllers/product/future_price_controller.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-module Product
- class FuturePriceController < ApplicationController
- def add_future_price
- respond_to do |format|
- format.turbo_stream do
- render turbo_stream:
- turbo_stream.append(
- "future_prices",
- partial: "/products/future_price",
- locals: {
- disabled: false,
- valid_since: nil,
- price: nil
- }
- )
- end
- end
- end
- end
-end
diff --git a/rails_application/app/controllers/products_controller.rb b/rails_application/app/controllers/products_controller.rb
index 87b0e14ca..f6403231e 100644
--- a/rails_application/app/controllers/products_controller.rb
+++ b/rails_application/app/controllers/products_controller.rb
@@ -1,130 +1,80 @@
class ProductsController < ApplicationController
- class ProductForm
- include ActiveModel::Model
- include ActiveModel::Attributes
- include ActiveModel::Validations
-
- attribute :name, :string
- attribute :price, :decimal
- attribute :vat_rate, :string
- attribute :product_id, :string
-
- validates :name, presence: true
- validates :price, presence: true, numericality: { greater_than: 0 }
- validates :vat_rate, presence: true, numericality: { greater_than: 0 }
- validates :product_id, presence: true
- end
-
def index
- @products = Products::Product.all
+ @products = Product.joins(:product_catalog).select("products.id, products.name, products.price, products.vat_rate, products.sku, product_catalogs.stock_level")
end
def show
- @product = Products::Product.find(params[:id])
+ @product = Product
+ .where(id: params[:id])
+ .joins(:product_catalog)
+ .select("products.id, products.name, products.price, products.vat_rate, products.sku, product_catalogs.stock_level")
+ .first
end
def new
- @product_id = SecureRandom.uuid
+ @product = Product.new
end
def edit
- @product = Products::Product.find(params[:id])
+ @product = Product.find(params[:id])
end
def create
- product_form = ProductForm.new(**product_params)
-
- unless product_form.valid?
- return render "new", locals: { errors: product_form.errors }, status: :unprocessable_entity
+ ApplicationRecord.transaction do
+ product = Product.create!(product_params)
+ event_store.publish(ProductCatalog::ProductCreated.new(data: product_params.merge(id: product.id)), stream_name: "ProductCatalog::Product$#{product.id}")
end
+ redirect_to products_path, notice: "Product was successfully created"
+ rescue ActiveRecord::RecordInvalid => e
+ return render :new, status: :unprocessable_entity, locals: { errors: e.record.errors }
+ end
- ActiveRecord::Base.transaction do
- create_product(product_form.product_id, product_form.name)
- if product_form.price.present?
- set_product_price(product_form.product_id, product_form.price)
- end
- if product_form.vat_rate.present?
- set_product_vat_rate(product_form.product_id, product_form.vat_rate)
+ def update
+ @product = Product.find(params[:id])
+ begin
+ ApplicationRecord.transaction do
+ if params["future_price"].present?
+ @product.future_price = params["future_price"]["price"]
+ @product.future_price_start_time = params["future_price"]["start_time"]
+ @product.save!
+ end
+ if !!product_params[:name] && @product.name != product_params[:name]
+ event_store.publish(ProductCatalog::ProductNameChanged.new(data: { id: @product.id, name: product_params[:name] }), stream_name: "ProductCatalog::Product$#{@product.id}")
+ elsif !!product_params[:price] && @product.price != product_params[:price]
+ event_store.publish(ProductCatalog::ProductPriceChanged.new(data: { id: @product.id, price: product_params[:price].to_d }), stream_name: "ProductCatalog::Product$#{@product.id}")
+ end
+ @product.update!(product_params)
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"
+ redirect_to products_path, notice: "Product was successfully updated"
+ rescue ActiveRecord::StaleObjectError
+ head :conflict
end
end
- def update
- if params[:name].present?
- set_product_name(params[:product_id], params[:name])
- end
- if params[:price].present?
- set_product_price(params[:product_id], params[:price])
- end
- if params[:future_prices].present?
- params[:future_prices].each do |future_price|
- set_future_product_price(
- params[:product_id],
- future_price["price"],
- Time.zone.parse(future_price["start_time"]).utc
- )
+ def add_future_price
+ respond_to do |format|
+ format.turbo_stream do
+ render turbo_stream:
+ turbo_stream.update(
+ "future_prices",
+ partial: "/products/future_price",
+ locals: {
+ disabled: false,
+ valid_since: nil,
+ price: nil
+ }
+ )
end
end
- redirect_to products_path, notice: "Product was successfully updated"
end
private
- def create_product(product_id, name)
- command_bus.(create_product_cmd(product_id))
- command_bus.(name_product_cmd(product_id, name))
- end
-
- def set_product_price(product_id, price)
- command_bus.(set_product_price_cmd(product_id, price))
- end
-
- 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}
- command_bus.(set_product_vat_rate_cmd(product_id, vat_rate))
- end
-
- def set_product_name(product_id, name)
- command_bus.(name_product_cmd(product_id, name))
- end
-
- def create_product_cmd(product_id)
- ProductCatalog::RegisterProduct.new(product_id: product_id)
- end
-
- def name_product_cmd(product_id, name)
- ProductCatalog::NameProduct.new(product_id: product_id, name: name)
- end
-
- def set_product_price_cmd(product_id, price)
- Pricing::SetPrice.new(product_id: product_id, price: price)
- end
-
- def set_product_vat_rate_cmd(product_id, vat_rate)
- Taxes::SetVatRate.new(product_id: product_id, vat_rate: vat_rate)
- end
-
- def set_product_future_price_cmd(product_id, price, valid_since)
- Pricing::SetFuturePrice.new(
- product_id: product_id,
- price: price,
- valid_since: valid_since
- )
+ def product_params
+ params.require(:product).permit(:name, :price, :vat_rate, :sku, :version).to_h.symbolize_keys.slice(:price, :vat_rate, :name, :sku, :version)
end
- def product_params
- params.permit(:name, :price, :vat_rate, :product_id).to_h.symbolize_keys.slice(:price, :vat_rate, :product_id, :name)
+ def event_store
+ Rails.configuration.event_store
end
end
diff --git a/rails_application/app/controllers/shipments_controller.rb b/rails_application/app/controllers/shipments_controller.rb
index 297a55cba..e7abb91a8 100644
--- a/rails_application/app/controllers/shipments_controller.rb
+++ b/rails_application/app/controllers/shipments_controller.rb
@@ -1,10 +1,5 @@
class ShipmentsController < ApplicationController
def index
- @shipments =
- Shipments::Shipment
- .includes(:order)
- .order("id DESC")
- .page(params[:page])
- .per(10)
+ @orders = Order.order("id DESC").page(params[:page]).per(10)
end
end
diff --git a/rails_application/app/controllers/shipping_addresses_controller.rb b/rails_application/app/controllers/shipping_addresses_controller.rb
index c166844b1..42dbd2607 100644
--- a/rails_application/app/controllers/shipping_addresses_controller.rb
+++ b/rails_application/app/controllers/shipping_addresses_controller.rb
@@ -1,23 +1,14 @@
class ShippingAddressesController < ApplicationController
def edit
- @shipment = Shipments::Shipment.find_or_initialize_by(order_uid: params[:order_id])
- @order = Shipments::Order.find_or_initialize_by(uid: params[:order_id])
+ @order = Order.find(params[:order_id])
end
def update
- cmd =
- Shipping::AddShippingAddressToShipment.new(
- order_id: params[:order_id],
- postal_address: {
- line_1: address_params[:address_line_1],
- line_2: address_params[:address_line_2],
- line_3: address_params[:address_line_3],
- line_4: address_params[:address_line_4]
- }
- )
- command_bus.(cmd)
- @order = Shipments::Order.find_or_initialize_by(uid: params[:order_id])
+ @order = Order.find(params[:order_id])
+
+ @order.update!(address_params)
+
redirect_to @order.submitted? ? order_path(params[:order_id]) : edit_order_path(params[:order_id]),
notice: "Shippping Address was successfully updated"
end
@@ -25,11 +16,11 @@ def update
private
def address_params
- params.require(:shipments_shipment).permit(
- :address_line_1,
- :address_line_2,
- :address_line_3,
- :address_line_4
+ params.require(:order).permit(
+ :addressed_to,
+ :address,
+ :city,
+ :country
)
end
end
diff --git a/rails_application/app/controllers/supplies_controller.rb b/rails_application/app/controllers/supplies_controller.rb
index 471ce29a4..956190f5d 100644
--- a/rails_application/app/controllers/supplies_controller.rb
+++ b/rails_application/app/controllers/supplies_controller.rb
@@ -4,15 +4,7 @@ def new
end
def create
- supply(params[:product_id], params[:quantity])
+ Inventory::ProductService.new.supply(Inventory::SupplyStockLevel.new(params[:product_id], params[:quantity]))
redirect_to products_path, notice: "Stock level changed"
end
-
- private
-
- def supply(product_id, quantity)
- command_bus.(
- Inventory::Supply.new(product_id: product_id, quantity: quantity)
- )
- end
end
diff --git a/rails_application/app/controllers/time_promotions_controller.rb b/rails_application/app/controllers/time_promotions_controller.rb
index 097250142..f8ca1c0a0 100644
--- a/rails_application/app/controllers/time_promotions_controller.rb
+++ b/rails_application/app/controllers/time_promotions_controller.rb
@@ -1,37 +1,17 @@
class TimePromotionsController < ApplicationController
def index
- @time_promotions = TimePromotions::TimePromotion.all
+ @time_promotions = TimePromotion.all
end
def new; end
def create
- id = SecureRandom.uuid
-
- TimePromotions::TimePromotion.transaction do
- create_time_promotion(id)
- end
- rescue ActiveRecord::RecordNotUnique => error
- flash.now[:alert] = error
- render "new"
- else
- respond_to do |format|
- format.html { redirect_to time_promotions_path, notice: "Time promotion was successfully created" }
- format.turbo_stream { head :ok }
- end
- end
-
- private
-
- def create_time_promotion(id)
- command_bus.(
- Pricing::CreateTimePromotion.new(
- time_promotion_id: id,
- discount: params[:discount],
- start_time: Time.zone.parse(params[:start_time]),
- end_time: Time.zone.parse(params[:end_time]),
- label: params[:label]
- )
+ TimePromotion.create!(
+ start_time: Time.parse(params[:start_time]),
+ end_time: Time.parse(params[:end_time]),
+ label: params[:label],
+ discount: params[:discount]
)
+ redirect_to time_promotions_path, notice: "Time promotion was successfully created"
end
end
diff --git a/rails_application/app/models/customer.rb b/rails_application/app/models/customer.rb
new file mode 100644
index 000000000..a32d354ce
--- /dev/null
+++ b/rails_application/app/models/customer.rb
@@ -0,0 +1,14 @@
+class Customer < ApplicationRecord
+ has_many :orders
+
+ def full_name
+ "#{first_name} #{last_name}"
+ end
+
+ def promote_to_vip
+ raise AlreadyVip if vip
+ update!(vip: true)
+ end
+end
+
+class AlreadyVip < StandardError; end
diff --git a/rails_application/app/models/inventory/decrease_stock_level.rb b/rails_application/app/models/inventory/decrease_stock_level.rb
new file mode 100644
index 000000000..d2c0e238a
--- /dev/null
+++ b/rails_application/app/models/inventory/decrease_stock_level.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+module Inventory
+ DecreaseStockLevel = Struct.new(:product_id)
+end
+
diff --git a/rails_application/app/models/inventory/increase_stock_level.rb b/rails_application/app/models/inventory/increase_stock_level.rb
new file mode 100644
index 000000000..75d9581b4
--- /dev/null
+++ b/rails_application/app/models/inventory/increase_stock_level.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+module Inventory
+ IncreaseStockLevel = Struct.new(:product_id)
+end
diff --git a/rails_application/app/models/inventory/product.rb b/rails_application/app/models/inventory/product.rb
new file mode 100644
index 000000000..f7f276773
--- /dev/null
+++ b/rails_application/app/models/inventory/product.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+module Inventory
+ class Product
+ include AggregateRoot
+
+ private attr_reader :id
+
+ def initialize(id)
+ @id = id
+ @stock_level = 0
+ end
+
+ def supply(quantity)
+ apply(StockLevelIncreased.new(data: { id:, quantity: }))
+ end
+
+ def withdraw(quantity, can_oversell: nil)
+ enough_stock =
+ if can_oversell
+ can_oversell.can_fulfill?(@stock_level)
+ else
+ @stock_level > quantity
+ end
+
+ raise "Not enough stock" unless enough_stock
+ apply(StockLevelDecreased.new(data: { id:, quantity: }))
+ end
+
+ on StockLevelIncreased do |event|
+ @stock_level += event.data[:quantity]
+ end
+
+ on StockLevelDecreased do |event|
+ @stock_level -= event.data[:quantity]
+ end
+
+ on StockLevelMigrated do |event|
+ @stock_level = event.data[:quantity]
+ end
+ end
+end
diff --git a/rails_application/app/models/inventory/product_service.rb b/rails_application/app/models/inventory/product_service.rb
new file mode 100644
index 000000000..49203aff0
--- /dev/null
+++ b/rails_application/app/models/inventory/product_service.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+module Inventory
+ class ProductService
+
+ def initialize
+ @repository = Infra::AggregateRootRepository.new(event_store)
+ end
+
+ def decrement_stock_level(command)
+ product_id = command.product_id
+ with_inventory_product(product_id) do |aggregate|
+ aggregate.withdraw(1)
+ end
+ end
+
+ def increment_stock_level(command)
+ product_id = command.product_id
+ with_inventory_product(product_id) do |aggregate|
+ aggregate.supply(1)
+ end
+ end
+
+ def supply(command)
+ product_id = command.product_id
+ quantity = command.quantity
+
+ with_inventory_product(product_id) do |aggregate|
+ aggregate.supply(quantity)
+ end
+ end
+
+ private
+
+ def event_store
+ Rails.configuration.event_store
+ end
+
+ def with_inventory_product(product_id)
+ @repository.with_aggregate(Inventory::Product, product_id) do |product|
+ yield(product)
+ end
+ end
+ end
+end
diff --git a/rails_application/app/models/inventory/stock_level_decreased.rb b/rails_application/app/models/inventory/stock_level_decreased.rb
new file mode 100644
index 000000000..4725c8310
--- /dev/null
+++ b/rails_application/app/models/inventory/stock_level_decreased.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+module Inventory
+ StockLevelDecreased = Class.new(Infra::Event)
+end
+
diff --git a/rails_application/app/models/inventory/stock_level_increased.rb b/rails_application/app/models/inventory/stock_level_increased.rb
new file mode 100644
index 000000000..0b62a69e6
--- /dev/null
+++ b/rails_application/app/models/inventory/stock_level_increased.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+module Inventory
+ StockLevelIncreased = Class.new(Infra::Event)
+end
diff --git a/rails_application/app/models/inventory/stock_level_migrated.rb b/rails_application/app/models/inventory/stock_level_migrated.rb
new file mode 100644
index 000000000..ac95573e5
--- /dev/null
+++ b/rails_application/app/models/inventory/stock_level_migrated.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+module Inventory
+ StockLevelMigrated = Class.new(Infra::Event)
+end
+
diff --git a/rails_application/app/models/inventory/stock_level_will_be_fulfilled_within2_business_days.rb b/rails_application/app/models/inventory/stock_level_will_be_fulfilled_within2_business_days.rb
new file mode 100644
index 000000000..d146307f8
--- /dev/null
+++ b/rails_application/app/models/inventory/stock_level_will_be_fulfilled_within2_business_days.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module Inventory
+ class StockLevelWillBeFulfilledWithin2BusinessDays
+ def initialize(stock_level_ordered_for_tomorrow)
+ @stock_level_ordered_for_tomorrow = stock_level_ordered_for_tomorrow
+ end
+
+ def can_fulfill?(stock_level)
+ stock_level + @stock_level_ordered_for_tomorrow >= 0
+ end
+ end
+end
\ No newline at end of file
diff --git a/rails_application/app/models/inventory/supply_stock_level.rb b/rails_application/app/models/inventory/supply_stock_level.rb
new file mode 100644
index 000000000..6464b5563
--- /dev/null
+++ b/rails_application/app/models/inventory/supply_stock_level.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+module Inventory
+ SupplyStockLevel = Struct.new(:product_id, :quantity) do
+ def initialize(product_id, quantity)
+ super(product_id.to_i, quantity.to_i)
+ end
+ end
+end
diff --git a/rails_application/app/models/invoice.rb b/rails_application/app/models/invoice.rb
new file mode 100644
index 000000000..51e318981
--- /dev/null
+++ b/rails_application/app/models/invoice.rb
@@ -0,0 +1,13 @@
+class Invoice < ApplicationRecord
+ self.table_name = "invoices_tbl"
+
+ belongs_to :order, class_name: "Order", foreign_key: "order_id"
+
+ def issued?
+ issued_at.present?
+ end
+
+ def address_present?
+ address.present?
+ end
+end
diff --git a/rails_application/app/models/invoicing/invoice_generated.rb b/rails_application/app/models/invoicing/invoice_generated.rb
new file mode 100644
index 000000000..cbe3a548c
--- /dev/null
+++ b/rails_application/app/models/invoicing/invoice_generated.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+module Invoicing
+ class InvoiceGenerated < Infra::Event
+ end
+end
diff --git a/rails_application/app/models/most_recent_products_in_unfinished_orders.rb b/rails_application/app/models/most_recent_products_in_unfinished_orders.rb
new file mode 100644
index 000000000..75f4108e4
--- /dev/null
+++ b/rails_application/app/models/most_recent_products_in_unfinished_orders.rb
@@ -0,0 +1,4 @@
+# frozen_string_literal: true
+
+class MostRecentProductsInUnfinishedOrders < ApplicationRecord
+end
diff --git a/rails_application/app/models/order.rb b/rails_application/app/models/order.rb
new file mode 100644
index 000000000..fdb93139a
--- /dev/null
+++ b/rails_application/app/models/order.rb
@@ -0,0 +1,54 @@
+class Order < ApplicationRecord
+ has_many :order_items
+ has_one :invoice
+
+ def state
+ self.status
+ end
+
+ def customer
+ Customer.find(customer_id) if customer_id
+ end
+
+ def submitted?
+ status == "Submitted"
+ end
+
+ def invoice_issued?
+ invoice_issued
+ end
+
+ def shipment_full_address
+ "#{address}, #{city}, #{country} #{addressed_to}"
+ end
+
+ def total_after_discount
+ total - ((total * discount) / 100)
+ end
+
+ def add_item(product)
+ if order_items.any? { |order_item| order_item.product_id == product.id }
+ order_items.find_by(product_id: product.id).increment!(:quantity)
+ else
+ order_items.create!(product_id: product.id, quantity: 1)
+ end
+ self.total += product.price
+ end
+
+ def remove_item(product)
+ order_item = order_items.find_by(product_id: product.id)
+ if order_item && order_item.quantity > 0
+ order_items.find_by(product_id: product.id).decrement!(:quantity)
+ self.total -= product.price
+ end
+ end
+
+ def billing_address_specified?
+ invoice_tax_id_number.present? &&
+ invoice_country.present? &&
+ invoice_address.present? &&
+ invoice_city.present? &&
+ invoice_addressed_to.present? &&
+ invoice_address.present?
+ end
+end
diff --git a/rails_application/app/models/order_item.rb b/rails_application/app/models/order_item.rb
new file mode 100644
index 000000000..31b03a5ed
--- /dev/null
+++ b/rails_application/app/models/order_item.rb
@@ -0,0 +1,5 @@
+class OrderItem < ApplicationRecord
+ def product
+ Product.unscoped.find(product_id)
+ end
+end
diff --git a/rails_application/app/models/ordering/item_added.rb b/rails_application/app/models/ordering/item_added.rb
new file mode 100644
index 000000000..02991dd50
--- /dev/null
+++ b/rails_application/app/models/ordering/item_added.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+module Ordering
+ class ItemAdded < Infra::Event
+ end
+end
diff --git a/rails_application/app/models/ordering/item_removed.rb b/rails_application/app/models/ordering/item_removed.rb
new file mode 100644
index 000000000..a11f9bad1
--- /dev/null
+++ b/rails_application/app/models/ordering/item_removed.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+module Ordering
+ class ItemRemoved < Infra::Event
+ end
+end
diff --git a/rails_application/app/models/ordering/order_created.rb b/rails_application/app/models/ordering/order_created.rb
new file mode 100644
index 000000000..fd7180d1e
--- /dev/null
+++ b/rails_application/app/models/ordering/order_created.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+module Ordering
+ class OrderCreated < Infra::Event
+ end
+end
diff --git a/ecommerce/ordering/lib/ordering/events/order_expired.rb b/rails_application/app/models/ordering/order_expired.rb
similarity index 58%
rename from ecommerce/ordering/lib/ordering/events/order_expired.rb
rename to rails_application/app/models/ordering/order_expired.rb
index 178e1b3c0..8eca5f7bf 100644
--- a/ecommerce/ordering/lib/ordering/events/order_expired.rb
+++ b/rails_application/app/models/ordering/order_expired.rb
@@ -1,5 +1,6 @@
+# frozen_string_literal: true
+
module Ordering
class OrderExpired < Infra::Event
- attribute :order_id, Infra::Types::UUID
end
end
diff --git a/rails_application/app/models/ordering/order_paid.rb b/rails_application/app/models/ordering/order_paid.rb
new file mode 100644
index 000000000..9646dede5
--- /dev/null
+++ b/rails_application/app/models/ordering/order_paid.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+module Ordering
+ class OrderPaid < Infra::Event
+ end
+end
diff --git a/rails_application/app/models/ordering/order_submitted.rb b/rails_application/app/models/ordering/order_submitted.rb
new file mode 100644
index 000000000..d339222ea
--- /dev/null
+++ b/rails_application/app/models/ordering/order_submitted.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+module Ordering
+ class OrderSubmitted < Infra::Event
+ end
+end
diff --git a/rails_application/app/models/product.rb b/rails_application/app/models/product.rb
new file mode 100644
index 000000000..dc685e1ce
--- /dev/null
+++ b/rails_application/app/models/product.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+class Product < ApplicationRecord
+ self.locking_column = :version
+ self.ignored_columns = %w[stock_level]
+
+ validates :name, presence: true
+ validates :price, numericality: { greater_than: 0 }
+ validate :validate_vat_rate
+ validates :sku, presence: true
+
+ has_one :product_catalog,
+ class_name: "Inventory::ProductCatalog",
+ foreign_key: :product_id
+
+ default_scope { where(latest: true) }
+
+ def validate_vat_rate
+ errors.add(:vat_rate, "is not a number") unless vat_rate.is_a?(Numeric)
+ end
+end
diff --git a/rails_application/app/models/product_catalog/product_created.rb b/rails_application/app/models/product_catalog/product_created.rb
new file mode 100644
index 000000000..0626d66ab
--- /dev/null
+++ b/rails_application/app/models/product_catalog/product_created.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+module ProductCatalog
+ class ProductCreated < Infra::Event
+ end
+end
diff --git a/rails_application/app/models/product_catalog/product_name_changed.rb b/rails_application/app/models/product_catalog/product_name_changed.rb
new file mode 100644
index 000000000..39bdb431a
--- /dev/null
+++ b/rails_application/app/models/product_catalog/product_name_changed.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+module ProductCatalog
+ class ProductNameChanged < Infra::Event
+ end
+end
diff --git a/rails_application/app/models/product_catalog/product_price_changed.rb b/rails_application/app/models/product_catalog/product_price_changed.rb
new file mode 100644
index 000000000..9e200e1b3
--- /dev/null
+++ b/rails_application/app/models/product_catalog/product_price_changed.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+module ProductCatalog
+ class ProductPriceChanged < Infra::Event
+ end
+end
diff --git a/rails_application/app/models/time_promotion.rb b/rails_application/app/models/time_promotion.rb
new file mode 100644
index 000000000..d59804ba4
--- /dev/null
+++ b/rails_application/app/models/time_promotion.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class TimePromotion < ApplicationRecord
+ def self.current
+ where("start_time < ? AND end_time > ?", Time.current, Time.current)
+ end
+end
diff --git a/rails_application/app/read_models/availability/configuration.rb b/rails_application/app/read_models/availability/configuration.rb
deleted file mode 100644
index b67ef280c..000000000
--- a/rails_application/app/read_models/availability/configuration.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-module Availability
- class Product < ApplicationRecord
- self.table_name = "availability_products"
- end
-
- private_constant :Product
-
- def self.approximately_available?(product_id, desired_quantity)
- !Product.exists?(["uid = ? and available < ?", product_id, desired_quantity])
- end
-
- class UpdateAvailability
- def call(event)
- order = Product.find_or_create_by!(uid: event.data.fetch(:product_id))
- order.available = event.data.fetch(:available)
- order.save!
- end
- end
-
- class Configuration
- def call(event_store)
- event_store.subscribe(UpdateAvailability, to: [Inventory::AvailabilityChanged])
- end
- end
-end
\ No newline at end of file
diff --git a/rails_application/app/read_models/build_most_recent_products_in_unfinished_orders.rb b/rails_application/app/read_models/build_most_recent_products_in_unfinished_orders.rb
new file mode 100644
index 000000000..10e9f5c45
--- /dev/null
+++ b/rails_application/app/read_models/build_most_recent_products_in_unfinished_orders.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+
+class BuildMostRecentProductsInUnfinishedOrders
+ def call(event)
+ case event
+ when ProductCatalog::ProductCreated
+ handle_product_created(event)
+ when ProductCatalog::ProductNameChanged
+ handle_product_name_changed(event)
+ when Ordering::OrderExpired
+ handle_order_expired(event)
+ end
+ end
+
+ private
+
+ def handle_product_created(event)
+ product_id = event.data[:id]
+ product_name = event.data[:name]
+
+ MostRecentProductsInUnfinishedOrders.create!(product_id: product_id, product_name: product_name)
+ end
+
+ def handle_product_name_changed(event)
+ product_id = event.data[:id]
+ new_name = event.data[:name]
+
+ MostRecentProductsInUnfinishedOrders.find_by(product_id: product_id)&.update!(product_name: new_name)
+ end
+
+ def handle_order_expired(event)
+ order_id = event.data[:id]
+
+ product_ids = {}
+
+ event_store.read.stream("Ordering::Order$#{order_id}").each do |event|
+ case event
+ when Ordering::ItemAdded
+ product_ids.include?(event.data[:product_id]) ? product_ids[event.data[:product_id]] += 1 : product_ids[event.data[:product_id]] = 1
+ when Ordering::ItemRemoved
+ product_ids.include?(event.data[:product_id]) ? product_ids[event.data[:product_id]] -= 1 : product_ids.delete(event.data[:product_id])
+ end
+ end
+
+ product_ids.each do |product_id, quantity|
+ report = MostRecentProductsInUnfinishedOrders.find_by(product_id: product_id)
+ report.number_of_unfinished_orders += 1
+ report.number_of_items_in_unfinished_orders += quantity
+ report.order_ids << order_id
+ report.save!
+ end
+ end
+
+ def event_store
+ Rails.configuration.event_store
+ end
+end
diff --git a/rails_application/app/read_models/client_authentication/configuration.rb b/rails_application/app/read_models/client_authentication/configuration.rb
deleted file mode 100644
index 91242a124..000000000
--- a/rails_application/app/read_models/client_authentication/configuration.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-module ClientAuthentication
- class Account < ApplicationRecord
- self.table_name = "accounts"
- end
-
- class Configuration
- def call(event_store)
- event_store.subscribe(CreateAccount, to: [Authentication::AccountConnectedToClient])
- event_store.subscribe(SetPassword, to: [Authentication::PasswordHashSet])
- end
- end
-end
diff --git a/rails_application/app/read_models/client_authentication/create_account.rb b/rails_application/app/read_models/client_authentication/create_account.rb
deleted file mode 100644
index bc688bc20..000000000
--- a/rails_application/app/read_models/client_authentication/create_account.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-module ClientAuthentication
- class CreateAccount
- def call(event)
- client_id = event.data.fetch(:client_id)
- account_id = event.data.fetch(:account_id)
- Account.find_or_create_by(account_id: account_id).update(client_id: client_id)
- end
- end
-end
diff --git a/rails_application/app/read_models/client_authentication/set_password.rb b/rails_application/app/read_models/client_authentication/set_password.rb
deleted file mode 100644
index a58658bd0..000000000
--- a/rails_application/app/read_models/client_authentication/set_password.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-module ClientAuthentication
- class SetPassword
- def call(event)
- find(event.data.fetch(:account_id)).update(password: event.data.fetch(:password_hash))
- end
-
- private
-
- def find(account_id)
- Account.find_or_create_by(account_id: account_id)
- end
- end
-end
diff --git a/rails_application/app/read_models/client_orders/add_item_to_order.rb b/rails_application/app/read_models/client_orders/add_item_to_order.rb
deleted file mode 100644
index 93c97e361..000000000
--- a/rails_application/app/read_models/client_orders/add_item_to_order.rb
+++ /dev/null
@@ -1,65 +0,0 @@
-module ClientOrders
- class AddItemToOrder
- include Rails.application.routes.url_helpers
- include ActionView::Helpers::UrlHelper
- include ActionView::Helpers::FormTagHelper
-
- def call(event)
- order_id = event.data.fetch(:order_id)
- product_id = event.data.fetch(:product_id)
- create_draft_order(order_id)
- item =
- find(order_id, product_id) ||
- create(order_id, product_id)
- item.product_quantity += 1
- item.save!
-
- broadcast_update(order_id, product_id, "product_quantity", item.product_quantity)
- broadcast_update(order_id, product_id, "value", ActiveSupport::NumberHelper.number_to_currency(item.value))
- show_remove_item_button(order_id, product_id)
- end
-
- private
-
- def show_remove_item_button(order_id, product_id)
- broadcast_update(order_id, product_id, "remove_item_button", remove_button_html(order_id, product_id))
- end
-
- def remove_button_html(order_id, product_id)
- button_to("Remove", remove_item_client_order_path(id: order_id, product_id: product_id), class: "hover:underline text-blue-500")
- end
-
- def broadcast_update(order_id, product_id, target, content)
- Turbo::StreamsChannel.broadcast_update_to(
- "client_orders_#{order_id}",
- target: "client_orders_#{product_id}_#{target}",
- html: content)
- end
-
- def create_draft_order(uid)
- return if Order.where(order_uid: uid).exists?
- Order.create!(order_uid: uid, state: "Draft")
- end
-
- def find(order_uid, product_id)
- Order
- .find_by(order_uid: order_uid)
- .order_lines
- .where(product_id: product_id)
- .first
- end
-
- def create(order_uid, product_id)
- product = Product.find_by_uid(product_id)
- Order
- .find_by(order_uid: order_uid)
- .order_lines
- .create(
- product_id: product_id,
- product_name: product.name,
- product_price: product.price,
- product_quantity: 0
- )
- end
- end
-end
diff --git a/rails_application/app/read_models/client_orders/assign_customer_to_order.rb b/rails_application/app/read_models/client_orders/assign_customer_to_order.rb
deleted file mode 100644
index 76fcd6547..000000000
--- a/rails_application/app/read_models/client_orders/assign_customer_to_order.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-module ClientOrders
- class AssignCustomerToOrder
- def call(event)
- order_uid = event.data.fetch(:order_id)
- order = Order.find_by(order_uid: order_uid)
-
- if order.nil?
- order = Order.create!(order_uid: order_uid, state: "Draft")
- end
-
- order.client_uid = event.data.fetch(:customer_id)
- order.save!
- end
- end
-end
diff --git a/rails_application/app/read_models/client_orders/cancel_order.rb b/rails_application/app/read_models/client_orders/cancel_order.rb
deleted file mode 100644
index 48b21b3b5..000000000
--- a/rails_application/app/read_models/client_orders/cancel_order.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-module ClientOrders
- class CancelOrder
- def call(event)
- order = Order.find_by(order_uid: event.data.fetch(:order_id))
- order.state = "Cancelled"
- order.save!
- end
- end
-end
-
diff --git a/rails_application/app/read_models/client_orders/change_product_name.rb b/rails_application/app/read_models/client_orders/change_product_name.rb
deleted file mode 100644
index 139f76bac..000000000
--- a/rails_application/app/read_models/client_orders/change_product_name.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-module ClientOrders
- class ChangeProductName
- def call(event)
- Product.find_or_create_by(uid: event.data.fetch(:product_id)).update(
- name: event.data.fetch(:name)
- )
- end
- end
-end
-
diff --git a/rails_application/app/read_models/client_orders/change_product_price.rb b/rails_application/app/read_models/client_orders/change_product_price.rb
deleted file mode 100644
index b3a6a016c..000000000
--- a/rails_application/app/read_models/client_orders/change_product_price.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-module ClientOrders
- class ChangeProductPrice
- def call(event)
- Product.find_or_create_by(uid: event.data.fetch(:product_id)).update(price: event.data.fetch(:price))
- end
- end
-end
diff --git a/rails_application/app/read_models/client_orders/configuration.rb b/rails_application/app/read_models/client_orders/configuration.rb
deleted file mode 100644
index f2b162783..000000000
--- a/rails_application/app/read_models/client_orders/configuration.rb
+++ /dev/null
@@ -1,52 +0,0 @@
-module ClientOrders
-
- class Client < ApplicationRecord
- self.table_name = "clients"
-
- has_many :client_orders,
- -> { client(id: :asc) },
- class_name: "ClientOrders::Order",
- foreign_key: :client_uid,
- primary_key: :uid
- end
-
- class Order < ApplicationRecord
- self.table_name = "client_orders"
-
- has_many :order_lines,
- -> { order(id: :asc) },
- class_name: "ClientOrders::OrderLine",
- foreign_key: :order_uid,
- primary_key: :order_uid
- end
-
- class OrderLine < ApplicationRecord
- self.table_name = "client_order_lines"
-
- def value
- product_price * product_quantity
- end
- end
-
- class Configuration
- def call(event_store)
- event_store.subscribe(ExpireOrder, to: [Ordering::OrderExpired])
- event_store.subscribe(CancelOrder, to: [Fulfillment::OrderCancelled])
- event_store.subscribe(SubmitOrder, to: [Ordering::OrderPlaced])
- event_store.subscribe(ConfirmOrder, to: [Fulfillment::OrderConfirmed])
- event_store.subscribe(AddItemToOrder, to: [Ordering::ItemAddedToBasket])
- event_store.subscribe(RemoveItemFromOrder, to: [Ordering::ItemRemovedFromBasket])
-
- event_store.subscribe(CreateCustomer.new, to: [Crm::CustomerRegistered])
- event_store.subscribe(AssignCustomerToOrder, to: [Crm::CustomerAssignedToOrder])
-
- event_store.subscribe(ChangeProductName, to: [ProductCatalog::ProductNamed])
- event_store.subscribe(ChangeProductPrice, to: [Pricing::PriceSet])
- event_store.subscribe(RegisterProduct, to: [ProductCatalog::ProductRegistered])
- event_store.subscribe(UpdateDiscount, to: [Pricing::PercentageDiscountSet, Pricing::PercentageDiscountChanged])
- event_store.subscribe(ResetDiscount, to: [Pricing::PercentageDiscountReset])
- event_store.subscribe(UpdateOrderTotalValue, to: [Pricing::OrderTotalValueCalculated])
- event_store.subscribe(UpdatePaidOrdersSummary, to: [Fulfillment::OrderConfirmed])
- end
- end
-end
diff --git a/rails_application/app/read_models/client_orders/confirm_order.rb b/rails_application/app/read_models/client_orders/confirm_order.rb
deleted file mode 100644
index 14f47521f..000000000
--- a/rails_application/app/read_models/client_orders/confirm_order.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-module ClientOrders
- class ConfirmOrder
- def call(event)
- order = Order.find_by(order_uid: event.data.fetch(:order_id))
- order.state = "Paid"
- order.save!
- end
- end
-end
-
diff --git a/rails_application/app/read_models/client_orders/create_customer.rb b/rails_application/app/read_models/client_orders/create_customer.rb
deleted file mode 100644
index 2f6b2349e..000000000
--- a/rails_application/app/read_models/client_orders/create_customer.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-module ClientOrders
- class CreateCustomer
- def call(event)
- Client.create(
- uid: event.data.fetch(:customer_id),
- name: event.data.fetch(:name)
- )
- end
- end
-end
diff --git a/rails_application/app/read_models/client_orders/expire_order.rb b/rails_application/app/read_models/client_orders/expire_order.rb
deleted file mode 100644
index b3663460e..000000000
--- a/rails_application/app/read_models/client_orders/expire_order.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-module ClientOrders
- class ExpireOrder
- def call(event)
- order = Order.find_by(order_uid: event.data.fetch(:order_id))
- order.state = "Expired"
- order.save!
- end
- end
-end
-
diff --git a/rails_application/app/read_models/client_orders/orders_list.rb b/rails_application/app/read_models/client_orders/orders_list.rb
deleted file mode 100644
index c73cd0fa9..000000000
--- a/rails_application/app/read_models/client_orders/orders_list.rb
+++ /dev/null
@@ -1,111 +0,0 @@
-module ClientOrders
- class OrdersList < Arbre::Component
- include Rails.application.routes.url_helpers
-
- def self.build(view_context, client_id)
- new(Arbre::Context.new(nil, view_context)).build(
- Client.find_by(uid: client_id),
- Order.where(client_uid: client_id)
- )
- end
-
- def build(client, client_orders, attributes = {})
- super(attributes)
-
- div class: "max-w-6xl mx-auto py-6 sm:px-6 lg:px-8" do
- client_name_header(client)
- orders_table(client, client_orders)
- new_order_button
- end
-
- end
-
- private
-
- def client_name_header(client)
- h1 class: "text-3xl font-bold text-gray-900" do
- client.name
- end
- end
-
- def orders_table(client, client_orders)
- return no_orders_message if client_orders.empty?
- orders_table_content(client, client_orders)
- end
-
- def orders_table_content(client, client_orders)
- table class: "w-full", id: "orders" do
- headers_row
- tbody do
- orders_rows(client_orders)
- summary_row(client)
- end
- end
- end
-
- def no_orders_message
- para class: "py-6" do
- "No orders to display."
- end
- end
-
- def summary_row(client)
- tr class: "border-t font-bold" do
- td colspan: 2, class: "py-2" do
- para "Total paid orders"
- end
- td class: "py-2 text-right border-t" do
- number_to_currency(client.paid_orders_summary)
- end
- end
- end
-
- def orders_rows(client_orders)
- client_orders.each do |client_order|
- tr class: "border-t" do
- td class: "py-2" do
- para(order_link_with_order_number(client_order) || 'Not submitted')
- end
- td class: "py-2 text-left" do
- client_order.state
- end
- td class: "py-2 text-right" do
- number_to_currency(client_order.discounted_value)
- end
- end
- end
- end
-
- def headers_row
- thead do
- tr class: "border-t" do
- th class: "text-left py-2" do
- "Number"
- end
- th class: "text-left py-2" do
- "State"
- end
- th class: "text-right py-2" do
- "Price"
- end
- end
- end
- end
-
- def new_order_button
- para(
- link_to(
- "New order",
- new_client_order_path,
- class: "bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"))
- end
-
- def order_link_with_order_number(order)
- link_to(
- order.number,
- client_order_path(order.order_uid),
- class: "text-blue-500 hover:text-blue-700"
- )
- end
- end
-end
\ No newline at end of file
diff --git a/rails_application/app/read_models/client_orders/product.rb b/rails_application/app/read_models/client_orders/product.rb
deleted file mode 100644
index 238d7f831..000000000
--- a/rails_application/app/read_models/client_orders/product.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-module ClientOrders
- class Product < ApplicationRecord
- self.table_name = "client_order_products"
- end
-end
diff --git a/rails_application/app/read_models/client_orders/register_product.rb b/rails_application/app/read_models/client_orders/register_product.rb
deleted file mode 100644
index 12b8c813a..000000000
--- a/rails_application/app/read_models/client_orders/register_product.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-module ClientOrders
- class RegisterProduct
- def call(event)
- Product.find_or_create_by(uid: event.data.fetch(:product_id))
- end
- end
-end
diff --git a/rails_application/app/read_models/client_orders/remove_item_from_order.rb b/rails_application/app/read_models/client_orders/remove_item_from_order.rb
deleted file mode 100644
index cabc86109..000000000
--- a/rails_application/app/read_models/client_orders/remove_item_from_order.rb
+++ /dev/null
@@ -1,42 +0,0 @@
-module ClientOrders
- class RemoveItemFromOrder
- def call(event)
- product_id = event.data.fetch(:product_id)
- order_id = event.data.fetch(:order_id)
- item = find(order_id , product_id)
- item.product_quantity -= 1
- item.product_quantity > 0 ? item.save! : item.destroy!
-
- broadcast_update(order_id, product_id, "product_quantity", item.product_quantity)
- broadcast_update(order_id, product_id, "value", ActiveSupport::NumberHelper.number_to_currency(item.value))
- broadcast_update(order_id, product_id, "remove_item_button", "") if zero_quantity?(item)
-
- event_store.link_event_to_stream(event, "ClientOrders$all")
- end
-
- private
-
- def event_store
- Rails.configuration.event_store
- end
-
- def zero_quantity?(item)
- item.nil? || item.product_quantity.zero?
- end
-
- def broadcast_update(order_id, product_id, target, content)
- Turbo::StreamsChannel.broadcast_update_to(
- "client_orders_#{order_id}",
- target: "client_orders_#{product_id}_#{target}",
- html: content)
- end
-
- def find(order_uid, product_id)
- Order
- .find_by(order_uid: order_uid)
- .order_lines
- .where(product_id: product_id)
- .first
- end
- end
-end
diff --git a/rails_application/app/read_models/client_orders/reset_discount.rb b/rails_application/app/read_models/client_orders/reset_discount.rb
deleted file mode 100644
index 8af8d72d7..000000000
--- a/rails_application/app/read_models/client_orders/reset_discount.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-module ClientOrders
- class ResetDiscount
- def call(event)
- order = Order.find_by(order_uid: event.data.fetch(:order_id))
- order.percentage_discount = nil
- order.save!
- end
- end
-end
diff --git a/rails_application/app/read_models/client_orders/show_order.rb b/rails_application/app/read_models/client_orders/show_order.rb
deleted file mode 100644
index c764b43d7..000000000
--- a/rails_application/app/read_models/client_orders/show_order.rb
+++ /dev/null
@@ -1,102 +0,0 @@
-module ClientOrders
- class ShowOrder < Arbre::Component
- include Rails.application.routes.url_helpers
-
- def self.build(view_context, order, order_lines)
- new(Arbre::Context.new(nil, view_context)).build(order, order_lines)
- end
-
- def build(order, order_lines, attributes = {})
- super(attributes)
- div do
- div do
- para(secondary_action_button { link_to 'Back', client_orders_path })
- end
- div class: "max-w-6xl mx-auto py-6 sm:px-6 lg:px-8" do
- header_content(order)
-
- div class: "mb-8" do
- state_section(order)
- end
-
- order_table(order_lines, order)
- end
- end
- end
-
- private
-
- def header_content(order)
- h2 do
- "Order #{order.number}"
- end
- end
-
- def state_section(order)
- dl do
- dt(class: "font-bold") { "State" }
- dd(class: "mb-2") { order.state }
- end
- end
-
- def order_table(order_lines, order)
- table class: "w-full" do
- headers_row
- tbody do
- order_lines.each do |item|
- item_row(item)
- end
- end
- footer_rows(order)
- end
- end
-
- def headers_row
- thead do
- tr do
- %w[Product Quantity Price Value].each do |header|
- th(class: "text-left py-2") { header }
- end
- end
- end
- end
-
- def item_row(item)
- tr class: "border-t" do
- td(class: "py-2") { item.product_name }
- td(class: "py-2") { item.product_quantity }
- td(class: "py-2") { number_to_currency(item.product_price) }
- td(class: "py-2 text-right") { number_to_currency(item.value) }
- end
- end
-
- def footer_rows(order)
- tfoot class: "border-t-4" do
- before_discounts_row(order) if order.discounted_value != order.total_value
- general_discount_row(order) if order.percentage_discount
- total_row(order)
- end
- end
-
- def before_discounts_row(order)
- tr class: "border-t" do
- td(class: "py-2", colspan: 3) { "Before discounts" }
- td(class: "py-2 text-right", id: "before-discounts-value") { number_to_currency(order.total_value) }
- end
- end
-
- def general_discount_row(order)
- tr class: "border-t" do
- td(class: "py-2", colspan: 3) { "General discount" }
- td(class: "py-2 text-right") { "#{order.percentage_discount}%" }
- end
- end
-
- def total_row(order)
- tr class: "border-t" do
- td(class: "py-2", colspan: 3) { "Total" }
- td(class: "py-2 text-right font-bold") { number_to_currency(order.discounted_value) }
- end
- end
- end
-end
diff --git a/rails_application/app/read_models/client_orders/submit_order.rb b/rails_application/app/read_models/client_orders/submit_order.rb
deleted file mode 100644
index 0cc1ecb89..000000000
--- a/rails_application/app/read_models/client_orders/submit_order.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-module ClientOrders
- class SubmitOrder
- def call(event)
- order = Order.find_or_create_by(order_uid: event.data.fetch(:order_id))
- order.number = event.data.fetch(:order_number)
- order.state = "Submitted"
- order.save!
- end
- end
-end
-
diff --git a/rails_application/app/read_models/client_orders/update_discount.rb b/rails_application/app/read_models/client_orders/update_discount.rb
deleted file mode 100644
index ca393197c..000000000
--- a/rails_application/app/read_models/client_orders/update_discount.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-module ClientOrders
- class UpdateDiscount
- def call(event)
- order = Order.find_or_create_by!(order_uid: event.data.fetch(:order_id))
- order.percentage_discount = event.data.fetch(:amount)
- order.save!
- end
- end
-end
diff --git a/rails_application/app/read_models/client_orders/update_order_total_value.rb b/rails_application/app/read_models/client_orders/update_order_total_value.rb
deleted file mode 100644
index 87a879b48..000000000
--- a/rails_application/app/read_models/client_orders/update_order_total_value.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-module ClientOrders
- class UpdateOrderTotalValue
- def call(event)
- order = Order.find_or_create_by!(order_uid: event.data.fetch(:order_id)) { |order| order.state = "Draft" }
- order.discounted_value = event.data.fetch(:discounted_amount)
- order.total_value = event.data.fetch(:total_amount)
- order.save!
- end
- end
-end
diff --git a/rails_application/app/read_models/client_orders/update_paid_orders_summary.rb b/rails_application/app/read_models/client_orders/update_paid_orders_summary.rb
deleted file mode 100644
index 20f73be80..000000000
--- a/rails_application/app/read_models/client_orders/update_paid_orders_summary.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-module ClientOrders
- class UpdatePaidOrdersSummary
- def call(event)
- order = Order.find_by(order_uid: event.data.fetch(:order_id))
- client = Client.where(uid: order.client_uid).first
- client.update(paid_orders_summary: client.paid_orders_summary + order.discounted_value)
- end
- end
-end
diff --git a/rails_application/app/read_models/coupons/configuration.rb b/rails_application/app/read_models/coupons/configuration.rb
deleted file mode 100644
index e5222e3d4..000000000
--- a/rails_application/app/read_models/coupons/configuration.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-module Coupons
- class Coupon < ApplicationRecord
- self.table_name = "coupons"
- end
-
- class Configuration
- def call(event_store)
- event_store.subscribe(RegisterCoupon.new, to: [Pricing::CouponRegistered])
- end
- end
-end
diff --git a/rails_application/app/read_models/coupons/register_coupon.rb b/rails_application/app/read_models/coupons/register_coupon.rb
deleted file mode 100644
index 45cbd2d71..000000000
--- a/rails_application/app/read_models/coupons/register_coupon.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-module Coupons
- class RegisterCoupon
- def call(event)
- event_data = event.data
- Coupon.create(
- uid: event_data.fetch(:coupon_id),
- name: event_data.fetch(:name),
- code: event_data.fetch(:code),
- discount: event_data.fetch(:discount)
- )
- end
- end
-end
diff --git a/rails_application/app/read_models/customers/configuration.rb b/rails_application/app/read_models/customers/configuration.rb
deleted file mode 100644
index b3465237f..000000000
--- a/rails_application/app/read_models/customers/configuration.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-module Customers
- class Customer < ApplicationRecord
- self.table_name = "customers"
- end
-
- class Configuration
- def call(event_store)
- event_store.subscribe(RegisterCustomer.new, to: [Crm::CustomerRegistered])
- event_store.subscribe(PromoteToVip.new, to: [Crm::CustomerPromotedToVip])
- event_store.subscribe(UpdatePaidOrdersSummary.new, to: [Fulfillment::OrderConfirmed])
- event_store.subscribe(ConnectAccount.new, to: [Authentication::AccountConnectedToClient])
- end
- end
-end
diff --git a/rails_application/app/read_models/customers/connect_account.rb b/rails_application/app/read_models/customers/connect_account.rb
deleted file mode 100644
index 18503d9b9..000000000
--- a/rails_application/app/read_models/customers/connect_account.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-module Customers
- class ConnectAccount
- def call(event)
- Customer.find_or_create_by(id: event.data.fetch(:client_id)).update(account_id: event.data.fetch(:account_id))
- end
- end
-end
diff --git a/rails_application/app/read_models/customers/promote_to_vip.rb b/rails_application/app/read_models/customers/promote_to_vip.rb
deleted file mode 100644
index 0669cf8e0..000000000
--- a/rails_application/app/read_models/customers/promote_to_vip.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-module Customers
- class PromoteToVip
- def call(event)
- promote_to_vip(event)
- end
-
- private
-
- def promote_to_vip(event)
- find(event.data.fetch(:customer_id)).update(vip: true)
- end
-
- def find(customer_id)
- Customer.where(id: customer_id).first
- end
-
- end
-end
diff --git a/rails_application/app/read_models/customers/register_customer.rb b/rails_application/app/read_models/customers/register_customer.rb
deleted file mode 100644
index 7fc69a478..000000000
--- a/rails_application/app/read_models/customers/register_customer.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-module Customers
- class RegisterCustomer
- def call(event)
- Customer.find_or_create_by(id: event.data.fetch(:customer_id)).update(name: event.data.fetch(:name))
- end
- end
-end
diff --git a/rails_application/app/read_models/customers/update_paid_orders_summary.rb b/rails_application/app/read_models/customers/update_paid_orders_summary.rb
deleted file mode 100644
index 9c9d29659..000000000
--- a/rails_application/app/read_models/customers/update_paid_orders_summary.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-module Customers
- class UpdatePaidOrdersSummary
- def call(event)
- order = ClientOrders::Order.find_by(order_uid: event.data.fetch(:order_id))
- customer = Customer.find(order.client_uid)
- customer.update(paid_orders_summary: customer.paid_orders_summary + order.discounted_value)
- end
- end
-end
diff --git a/rails_application/app/read_models/inventory/product_catalog.rb b/rails_application/app/read_models/inventory/product_catalog.rb
new file mode 100644
index 000000000..5f442b883
--- /dev/null
+++ b/rails_application/app/read_models/inventory/product_catalog.rb
@@ -0,0 +1,4 @@
+module Inventory
+ class ProductCatalog < ApplicationRecord
+ end
+end
diff --git a/rails_application/app/read_models/inventory/update_product_catalog.rb b/rails_application/app/read_models/inventory/update_product_catalog.rb
new file mode 100644
index 000000000..0b4fc0c21
--- /dev/null
+++ b/rails_application/app/read_models/inventory/update_product_catalog.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+module Inventory
+ class UpdateProductCatalog
+ def call(event)
+ product_catalog = ProductCatalog.find_or_initialize_by(product_id: event.data[:id])
+
+ checkpoint = product_catalog.checkpoint
+
+ product_stream = event_store.read.stream("Inventory::Product$#{product_catalog.product_id}")
+ product_stream = product_stream.from(checkpoint) if checkpoint
+
+ product_stream.each do |event|
+ case event
+ when Inventory::StockLevelIncreased
+ product_catalog.increment!(:stock_level, event.data[:quantity])
+ when Inventory::StockLevelDecreased
+ product_catalog.decrement!(:stock_level, event.data[:quantity])
+ when Inventory::StockLevelMigrated
+ product_catalog.stock_level = event.data[:quantity]
+ end
+ product_catalog.checkpoint = event.event_id
+ end
+
+ product_catalog.save!
+ end
+
+ private
+
+ def event_store
+ Rails.configuration.event_store
+ end
+ end
+end
\ No newline at end of file
diff --git a/rails_application/app/read_models/invoices/configuration.rb b/rails_application/app/read_models/invoices/configuration.rb
deleted file mode 100644
index 1edf7411d..000000000
--- a/rails_application/app/read_models/invoices/configuration.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-module Invoices
- class Invoice < ApplicationRecord
- self.table_name = "invoices"
- has_many :invoice_items
- end
-
- class InvoiceItem < ApplicationRecord
- self.table_name = "invoice_items"
- belongs_to :invoice
- end
-
- class Order < ApplicationRecord
- self.table_name = "invoices_orders"
- end
-
- class Configuration
- def call(event_store)
- event_store.subscribe(CreateInvoiceItem.new, to: [Invoicing::InvoiceItemAdded])
- event_store.subscribe(SetBillingAddress.new, to: [Invoicing::BillingAddressSet])
- event_store.subscribe(SetPaymentDate.new, to: [Invoicing::InvoicePaymentDateSet])
- event_store.subscribe(MarkAsIssued.new, to: [Invoicing::InvoiceIssued])
- event_store.subscribe(MarkOrderPlaced.new, to: [Ordering::OrderPlaced])
- end
- end
-end
diff --git a/rails_application/app/read_models/invoices/create_invoice_item.rb b/rails_application/app/read_models/invoices/create_invoice_item.rb
deleted file mode 100644
index c94aa288a..000000000
--- a/rails_application/app/read_models/invoices/create_invoice_item.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-module Invoices
- class CreateInvoiceItem
- def call(event)
- invoice = Invoice.find_or_initialize_by(order_uid: event.data.fetch(:invoice_id))
-
- item = InvoiceItem.create(
- invoice: invoice,
- name: event.data.fetch(:title),
- vat_rate: event.data.fetch(:vat_rate).fetch(:rate),
- unit_price: event.data.fetch(:unit_price),
- quantity: event.data.fetch(:quantity),
- value: event.data.fetch(:unit_price) * event.data.fetch(:quantity)
- )
- invoice.total_value = (invoice.total_value || 0) + item.value
- invoice.save!
- end
- end
-end
diff --git a/rails_application/app/read_models/invoices/mark_as_issued.rb b/rails_application/app/read_models/invoices/mark_as_issued.rb
deleted file mode 100644
index 8fa56e41e..000000000
--- a/rails_application/app/read_models/invoices/mark_as_issued.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-module Invoices
- class MarkAsIssued
- def call(event)
- invoice = Invoice.find_or_initialize_by(order_uid: event.data.fetch(:invoice_id))
- invoice.issued = true
- invoice.issue_date = event.data.fetch(:issue_date)
- invoice.disposal_date = event.data.fetch(:disposal_date)
- invoice.number = event.data.fetch(:invoice_number)
- invoice.save!
- end
- end
-end
diff --git a/rails_application/app/read_models/invoices/mark_order_placed.rb b/rails_application/app/read_models/invoices/mark_order_placed.rb
deleted file mode 100644
index 1ccd2b3c0..000000000
--- a/rails_application/app/read_models/invoices/mark_order_placed.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-module Invoices
- class MarkOrderPlaced
- def call(event)
- invoice = Invoice.find_or_initialize_by(order_uid: event.data.fetch(:order_id))
- Order.find_or_initialize_by(uid: event.data.fetch(:order_id)).update!(submitted: true)
- invoice.save!
- end
- end
-end
diff --git a/rails_application/app/read_models/invoices/set_billing_address.rb b/rails_application/app/read_models/invoices/set_billing_address.rb
deleted file mode 100644
index cbdd75518..000000000
--- a/rails_application/app/read_models/invoices/set_billing_address.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-module Invoices
- class SetBillingAddress
- def call(event)
- invoice = Invoice.find_or_initialize_by(order_uid: event.data.fetch(:invoice_id))
- invoice.address_present = true
- invoice.tax_id_number = event.data.fetch(:tax_id_number)
- postal_address = event.data.fetch(:postal_address)
- invoice.address_line_1 = postal_address.fetch(:line_1)
- invoice.address_line_2 = postal_address.fetch(:line_2)
- invoice.address_line_3 = postal_address.fetch(:line_3)
- invoice.address_line_4 = postal_address.fetch(:line_4)
- invoice.save!
- end
- end
-end
diff --git a/rails_application/app/read_models/invoices/set_payment_date.rb b/rails_application/app/read_models/invoices/set_payment_date.rb
deleted file mode 100644
index 4ee6a43c9..000000000
--- a/rails_application/app/read_models/invoices/set_payment_date.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-module Invoices
- class SetPaymentDate
- def call(event)
- invoice =
- Invoice.find_or_initialize_by(order_uid: event.data.fetch(:invoice_id))
- invoice.payment_date = event.data.fetch(:payment_date)
- invoice.save!
- end
- end
-end
-
diff --git a/rails_application/app/read_models/orders/add_item_to_order.rb b/rails_application/app/read_models/orders/add_item_to_order.rb
deleted file mode 100644
index 8c304b888..000000000
--- a/rails_application/app/read_models/orders/add_item_to_order.rb
+++ /dev/null
@@ -1,55 +0,0 @@
-module Orders
- class AddItemToOrder
- include Rails.application.routes.url_helpers
- include ActionView::Helpers::UrlHelper
- include ActionView::Helpers::FormTagHelper
-
- def call(event)
- order_id = event.data.fetch(:order_id)
- Order.find_or_create_by!(uid: order_id) { |order| order.state = "Draft" }
- product_id = event.data.fetch(:product_id)
- item =
- find(order_id, product_id) ||
- create(order_id, product_id)
- item.quantity += 1
- item.save!
-
- broadcaster.call(order_id, product_id, "quantity", item.quantity)
- broadcaster.call(order_id, product_id, "value", ActiveSupport::NumberHelper.number_to_currency(item.value))
- broadcaster.call(order_id, product_id, "remove_item_button", button_to("Remove", remove_item_order_path(id: order_id, product_id: product_id), class: "hover:underline text-blue-500"))
-
- event_store.link_event_to_stream(event, "Orders$all")
- end
-
- private
-
- def event_store
- Rails.configuration.event_store
- end
-
- def broadcaster
- Rails.configuration.broadcaster
- end
-
- def find(order_uid, product_id)
- Order
- .find_by_uid(order_uid)
- .order_lines
- .where(product_id: product_id)
- .first
- end
-
- def create(order_uid, product_id)
- product = Product.find_by_uid(product_id)
- Order
- .find_by(uid: order_uid)
- .order_lines
- .create(
- product_id: product_id,
- product_name: product.name,
- price: product.price,
- quantity: 0
- )
- end
- end
-end
diff --git a/rails_application/app/read_models/orders/assign_customer_to_order.rb b/rails_application/app/read_models/orders/assign_customer_to_order.rb
deleted file mode 100644
index 83686947f..000000000
--- a/rails_application/app/read_models/orders/assign_customer_to_order.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-module Orders
- class AssignCustomerToOrder
- def call(event)
- order_uid = event.data.fetch(:order_id)
- order = Order.find_or_create_by!(uid: order_uid) { |order| order.state = "Draft" }
- order.customer = Customer.find_by_uid(event.data.fetch(:customer_id)).name
- order.save!
-
- event_store.link_event_to_stream(event, "Orders$all")
- end
-
- private
-
- def event_store
- Rails.configuration.event_store
- end
- end
-end
diff --git a/rails_application/app/read_models/orders/broadcaster.rb b/rails_application/app/read_models/orders/broadcaster.rb
deleted file mode 100644
index 042282daa..000000000
--- a/rails_application/app/read_models/orders/broadcaster.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-module Orders
- class Broadcaster
- def call(stream_id, target_id, target_name, content)
- Turbo::StreamsChannel.broadcast_update_to(
- "orders_order_#{stream_id}",
- target: "orders_order_#{target_id}_#{target_name}",
- html: content)
- end
- end
-end
\ No newline at end of file
diff --git a/rails_application/app/read_models/orders/cancel_order.rb b/rails_application/app/read_models/orders/cancel_order.rb
deleted file mode 100644
index ff1d990e3..000000000
--- a/rails_application/app/read_models/orders/cancel_order.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-module Orders
- class CancelOrder
- def call(event)
- order = Order.find_by_uid(event.data.fetch(:order_id))
- order.state = "Cancelled"
- order.save!
-
- event_store.link_event_to_stream(event, "Orders$all")
- end
-
- private
-
- def event_store
- Rails.configuration.event_store
- end
- end
-end
-
diff --git a/rails_application/app/read_models/orders/change_product_name.rb b/rails_application/app/read_models/orders/change_product_name.rb
deleted file mode 100644
index d53ccbad7..000000000
--- a/rails_application/app/read_models/orders/change_product_name.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-module Orders
- class ChangeProductName
- def call(event)
- Product.find_or_create_by(uid: event.data.fetch(:product_id)).update(
- name: event.data.fetch(:name)
- )
-
- event_store.link_event_to_stream(event, "Orders$all")
- end
-
- private
-
- def event_store
- Rails.configuration.event_store
- end
- end
-end
diff --git a/rails_application/app/read_models/orders/change_product_price.rb b/rails_application/app/read_models/orders/change_product_price.rb
deleted file mode 100644
index 46ca3656e..000000000
--- a/rails_application/app/read_models/orders/change_product_price.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-module Orders
- class ChangeProductPrice
- def call(event)
- Product.find_or_create_by(uid: event.data.fetch(:product_id)).update(price: event.data.fetch(:price))
-
- event_store.link_event_to_stream(event, "Orders$all")
- end
-
- private
-
- def event_store
- Rails.configuration.event_store
- end
- end
-end
diff --git a/rails_application/app/read_models/orders/configuration.rb b/rails_application/app/read_models/orders/configuration.rb
deleted file mode 100644
index 29fe28bbe..000000000
--- a/rails_application/app/read_models/orders/configuration.rb
+++ /dev/null
@@ -1,82 +0,0 @@
-module Orders
- class Order < ApplicationRecord
- self.table_name = "orders"
-
- has_many :order_lines,
- -> { order(id: :asc) },
- class_name: "Orders::OrderLine",
- foreign_key: :order_uid,
- primary_key: :uid
- end
-
- class Product < ApplicationRecord
- self.table_name = "orders_products"
- end
-
- class Customer < ApplicationRecord
- self.table_name = "orders_customers"
- end
-
- class OrderLine < ApplicationRecord
- self.table_name = "order_lines"
-
- def value
- price * quantity
- end
- end
-
- class Configuration
-
- def call(event_store)
- @event_store = event_store
-
- Rails.configuration.broadcaster = Orders::Broadcaster.new
-
- event_store.subscribe(AddItemToOrder.new, to: [Ordering::ItemAddedToBasket])
- event_store.subscribe(RemoveItemFromOrder.new, to: [Ordering::ItemRemovedFromBasket])
- event_store.subscribe(UpdateDiscount.new, to: [Pricing::PercentageDiscountSet, Pricing::PercentageDiscountChanged])
- event_store.subscribe(ResetDiscount.new, to: [Pricing::PercentageDiscountReset])
- event_store.subscribe(UpdateOrderTotalValue.new, to: [Pricing::OrderTotalValueCalculated])
- event_store.subscribe(RegisterProduct.new, to: [ProductCatalog::ProductRegistered])
- event_store.subscribe(ChangeProductName.new, to: [ProductCatalog::ProductNamed])
- event_store.subscribe(ChangeProductPrice.new, to: [Pricing::PriceSet])
- event_store.subscribe(CreateCustomer.new, to: [Crm::CustomerRegistered])
- event_store.subscribe(AssignCustomerToOrder.new, to: [Crm::CustomerAssignedToOrder])
- event_store.subscribe(SubmitOrder.new, to: [Ordering::OrderPlaced])
- event_store.subscribe(ExpireOrder.new, to: [Ordering::OrderExpired])
- event_store.subscribe(ConfirmOrder.new, to: [Fulfillment::OrderConfirmed])
- event_store.subscribe(CancelOrder.new, to: [Fulfillment::OrderCancelled])
-
-
- subscribe(
- ->(event) { broadcast_order_state_change(event.data.fetch(:order_id), 'Submitted') },
- [Ordering::OrderPlaced]
- )
- subscribe(
- ->(event) { broadcast_order_state_change(event.data.fetch(:order_id), "Expired") },
- [Ordering::OrderExpired]
- )
- subscribe(
- ->(event) { broadcast_order_state_change(event.data.fetch(:order_id), "Paid") },
- [Fulfillment::OrderConfirmed]
- )
- subscribe(
- ->(event) { broadcast_order_state_change(event.data.fetch(:order_id), "Cancelled") },
- [Fulfillment::OrderCancelled]
- )
- end
-
- private
-
- def subscribe(handler, events)
- @event_store.subscribe(handler, to: events)
- end
-
- def broadcast_order_state_change(order_id, new_state)
- Turbo::StreamsChannel.broadcast_update_later_to(
- "orders_order_#{order_id}",
- target: "orders_order_#{order_id}_state",
- html: new_state)
- end
- end
-end
diff --git a/rails_application/app/read_models/orders/confirm_order.rb b/rails_application/app/read_models/orders/confirm_order.rb
deleted file mode 100644
index 2a50e0b11..000000000
--- a/rails_application/app/read_models/orders/confirm_order.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-module Orders
- class ConfirmOrder
- def call(event)
- order = Order.find_by_uid(event.data.fetch(:order_id))
- order.state = "Paid"
- order.save!
-
- event_store.link_event_to_stream(event, "Orders$all")
- end
-
- private
-
- def event_store
- Rails.configuration.event_store
- end
- end
-end
-
diff --git a/rails_application/app/read_models/orders/create_customer.rb b/rails_application/app/read_models/orders/create_customer.rb
deleted file mode 100644
index 2b26dd8e4..000000000
--- a/rails_application/app/read_models/orders/create_customer.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-module Orders
- class CreateCustomer
- def call(event)
- Customer.create(
- uid: event.data.fetch(:customer_id),
- name: event.data.fetch(:name)
- )
-
- event_store.link_event_to_stream(event, "Orders$all")
- end
-
- private
-
- def event_store
- Rails.configuration.event_store
- end
- end
-end
diff --git a/rails_application/app/read_models/orders/expire_order.rb b/rails_application/app/read_models/orders/expire_order.rb
deleted file mode 100644
index dfa1e6975..000000000
--- a/rails_application/app/read_models/orders/expire_order.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-module Orders
- class ExpireOrder
- def call(event)
- order = Order.find_by_uid(event.data.fetch(:order_id))
- order.state = "Expired"
- order.save!
-
- event_store.link_event_to_stream(event, "Orders$all")
- end
-
- private
-
- def event_store
- Rails.configuration.event_store
- end
- end
-end
-
diff --git a/rails_application/app/read_models/orders/register_product.rb b/rails_application/app/read_models/orders/register_product.rb
deleted file mode 100644
index 9bb05e050..000000000
--- a/rails_application/app/read_models/orders/register_product.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-module Orders
- class RegisterProduct
- def call(event)
- Product.create(uid: event.data.fetch(:product_id))
-
- event_store.link_event_to_stream(event, "Orders$all")
- end
-
- private
-
- def event_store
- Rails.configuration.event_store
- end
- end
-end
diff --git a/rails_application/app/read_models/orders/remove_item_from_order.rb b/rails_application/app/read_models/orders/remove_item_from_order.rb
deleted file mode 100644
index cbc8e15b2..000000000
--- a/rails_application/app/read_models/orders/remove_item_from_order.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-module Orders
- class RemoveItemFromOrder
- def call(event)
- product_id = event.data.fetch(:product_id)
- order_id = event.data.fetch(:order_id)
- item = find(order_id , product_id)
- item.quantity -= 1
- item.quantity > 0 ? item.save! : item.destroy!
-
- broadcaster.call(order_id, product_id, "quantity", item.quantity)
- broadcaster.call(order_id, product_id, "value", ActiveSupport::NumberHelper.number_to_currency(item.value))
- broadcaster.call(order_id, product_id, "remove_item_button", "") if item.quantity.zero?
-
- event_store.link_event_to_stream(event, "Orders$all")
- end
-
- private
- def find(order_uid, product_id)
- Order
- .find_by_uid(order_uid)
- .order_lines
- .where(product_id: product_id)
- .first
- end
-
- def broadcaster
- Rails.configuration.broadcaster
- end
-
- def event_store
- Rails.configuration.event_store
- end
- end
-end
diff --git a/rails_application/app/read_models/orders/reset_discount.rb b/rails_application/app/read_models/orders/reset_discount.rb
deleted file mode 100644
index 8c6375765..000000000
--- a/rails_application/app/read_models/orders/reset_discount.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-module Orders
- class ResetDiscount
- def call(event)
- order = Order.find_by_uid(event.data.fetch(:order_id))
- order.percentage_discount = nil
- order.save!
-
- event_store.link_event_to_stream(event, "Orders$all")
- end
-
- private
-
- def event_store
- Rails.configuration.event_store
- end
- end
-end
diff --git a/rails_application/app/read_models/orders/submit_order.rb b/rails_application/app/read_models/orders/submit_order.rb
deleted file mode 100644
index 212f2113c..000000000
--- a/rails_application/app/read_models/orders/submit_order.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-module Orders
- class SubmitOrder
- def call(event)
- order_id = event.data.fetch(:order_id)
- order = Order.find_or_create_by!(uid: order_id)
- order.number = event.data.fetch(:order_number)
- order.state = "Submitted"
- order.save!
- event_store.link_event_to_stream(event, "Orders$all")
- end
-
- private
-
- def event_store
- Rails.configuration.event_store
- end
- end
-end
-
diff --git a/rails_application/app/read_models/orders/update_discount.rb b/rails_application/app/read_models/orders/update_discount.rb
deleted file mode 100644
index 39fdb432a..000000000
--- a/rails_application/app/read_models/orders/update_discount.rb
+++ /dev/null
@@ -1,31 +0,0 @@
-module Orders
- class UpdateDiscount
- def call(event)
- order = Order.find_by_uid(event.data.fetch(:order_id))
- if is_newest_value?(event, order)
- order.percentage_discount = event.data.fetch(:amount)
- order.discount_updated_at = event.metadata.fetch(:timestamp)
- order.save!
-
- broadcaster.call(order.uid, order.uid, "percentage_discount", order.percentage_discount)
- end
-
- event_store.link_event_to_stream(event, "Orders$all")
- end
-
- private
-
- def event_store
- Rails.configuration.event_store
- end
-
- def is_newest_value?(event, order)
- order.discount_updated_at.nil? || order.discount_updated_at < event.metadata.fetch(:timestamp)
- end
-
- def broadcaster
- Rails.configuration.broadcaster
- end
- end
-end
-
diff --git a/rails_application/app/read_models/orders/update_order_total_value.rb b/rails_application/app/read_models/orders/update_order_total_value.rb
deleted file mode 100644
index f355cb15e..000000000
--- a/rails_application/app/read_models/orders/update_order_total_value.rb
+++ /dev/null
@@ -1,38 +0,0 @@
-module Orders
- class UpdateOrderTotalValue
- def call(event)
- order_id = event.data.fetch(:order_id)
- order = Order.find_or_create_by!(uid: order_id) { |order| order.state = "Draft" }
-
- if is_newest_value?(event, order)
- order.discounted_value = event.data.fetch(:discounted_amount)
- order.total_value = event.data.fetch(:total_amount)
- order.total_value_updated_at = event.metadata.fetch(:timestamp)
- order.save!
-
- broadcaster.call(order.uid, order.uid, "total_value", number_to_currency(order.total_value))
- broadcaster.call(order.uid, order.uid, "discounted_value", number_to_currency(order.discounted_value))
- end
-
- event_store.link_event_to_stream(event, "Orders$all")
- end
-
- private
-
- def event_store
- Rails.configuration.event_store
- end
-
- def is_newest_value?(event, order)
- order.total_value_updated_at.nil? || order.total_value_updated_at < event.metadata.fetch(:timestamp)
- end
-
- def broadcaster
- Rails.configuration.broadcaster
- end
-
- def number_to_currency(number)
- ActiveSupport::NumberHelper.number_to_currency(number)
- end
- end
-end
diff --git a/rails_application/app/read_models/products/configuration.rb b/rails_application/app/read_models/products/configuration.rb
deleted file mode 100644
index e8174f0af..000000000
--- a/rails_application/app/read_models/products/configuration.rb
+++ /dev/null
@@ -1,55 +0,0 @@
-module Products
- class Product < ApplicationRecord
- self.table_name = "products"
- serialize :current_prices_calendar, Array
-
- def current_prices_calendar
- return [] unless super
- super.map(&method(:parse_calendar_entry))
- end
-
- def price(time = Time.current)
- last_price_before(time)
- end
-
- def future_prices_calendar
- current_prices_calendar.select { |entry| entry[:valid_since] > Time.current }
- end
-
- private
-
- def last_price_before(time)
- (prices_before(time).last || {})[:price]
- end
-
- def prices_before(time)
- current_prices_calendar.partition { |entry| entry[:valid_since] < time }.first
- end
-
- def parse_calendar_entry(entry)
- {
- valid_since: Time.zone.parse(time_of(entry)).in_time_zone(Time.current.zone),
- price: BigDecimal(entry[:price])
- }
- end
-
- def time_of(entry)
- entry[:valid_since]
- end
- end
-
- class Configuration
- def initialize(event_store)
- @read_model = SingleTableReadModel.new(event_store, Product, :product_id)
- @event_store = event_store
- end
-
- def call
- @read_model.subscribe_create(ProductCatalog::ProductRegistered)
- @read_model.subscribe_copy(ProductCatalog::ProductNamed, :name)
- @read_model.subscribe_copy(Inventory::StockLevelChanged, :stock_level)
- @read_model.subscribe_copy(Taxes::VatRateSet, [:vat_rate, :code])
- @event_store.subscribe(RefreshFuturePricesCalendar, to: [Pricing::PriceSet])
- end
- end
-end
diff --git a/rails_application/app/read_models/products/refresh_future_prices_calendar.rb b/rails_application/app/read_models/products/refresh_future_prices_calendar.rb
deleted file mode 100644
index 276a8982f..000000000
--- a/rails_application/app/read_models/products/refresh_future_prices_calendar.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-module Products
- class RefreshFuturePricesCalendar
- def call(event)
- product_id = event.data.fetch(:product_id)
- product = Product.find_or_create_by(id: product_id)
- product.update!(current_prices_calendar: updated_prices_calendar(event, product))
- end
-
- private
-
- def updated_prices_calendar(event, product)
- (product.read_attribute(:current_prices_calendar) << new_entry_from_event(event))
- .sort_by { |entry| Time.parse(entry[:valid_since]) }
- end
-
- def new_entry_from_event(event)
- {
- price: event.data.fetch(:price).to_s,
- valid_since: event.metadata.fetch(:valid_at).to_s
- }
- end
- end
-end
diff --git a/rails_application/app/read_models/public_offer/configuration.rb b/rails_application/app/read_models/public_offer/configuration.rb
deleted file mode 100644
index 301d9354c..000000000
--- a/rails_application/app/read_models/public_offer/configuration.rb
+++ /dev/null
@@ -1,76 +0,0 @@
-module PublicOffer
- class ProductsList < Arbre::Component
-
- def self.build(view_context)
- new(Arbre::Context.new(nil, view_context)).build(Product.all)
- end
-
- def build(products, attributes = {})
- super(attributes)
-
- div class: "max-w-6xl mx-auto py-6 sm:px-6 lg:px-8" do
- products_table(products)
- end
-
- end
-
- private
-
- def products_table(products)
- if products.count > 0
- table class: "w-full" do
- thead do
- tr class: "border-t" do
- th class: "text-left py-2" do
- "Name"
- end
- th class: "text-right py-2" do
- "Price"
- end
- end
- end
- tbody do
- products.each do |product|
- tr class: "border-t" do
-
- td class: "py-2 text-left" do
- product.name
- end
- td class: "py-2 text-right" do
- if product.lowest_recent_price_lower_from_current?
- span title: "Lowest recent price: #{number_to_currency(product.lowest_recent_price)}",
- id: "lowest-price-info-#{product.id}" do
- "ℹ️"
- end
- end
-
- span do
- number_to_currency(product.price)
- end
- end
- end
- end
- end
- end
- else
- para do
- "No products to display."
- end
- end
- end
- end
-
- class Configuration
- def initialize(event_store)
- @read_model = SingleTableReadModel.new(event_store, Product, :product_id)
- @event_store = event_store
- end
-
- def call
- @read_model.subscribe_create(ProductCatalog::ProductRegistered)
- @read_model.subscribe_copy(ProductCatalog::ProductNamed, :name)
- @read_model.subscribe_copy(Pricing::PriceSet, :price)
- @event_store.subscribe(RegisterLowestPrice, to: [Pricing::PriceSet])
- end
- end
-end
diff --git a/rails_application/app/read_models/public_offer/product.rb b/rails_application/app/read_models/public_offer/product.rb
deleted file mode 100644
index 5a69caa15..000000000
--- a/rails_application/app/read_models/public_offer/product.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-module PublicOffer
- class Product < ApplicationRecord
- self.table_name = "public_offer_products"
-
- def lowest_recent_price_lower_from_current?
- lowest_recent_price && lowest_recent_price < price
- end
- end
-end
diff --git a/rails_application/app/read_models/public_offer/register_lowest_price.rb b/rails_application/app/read_models/public_offer/register_lowest_price.rb
deleted file mode 100644
index 7d47d6c8b..000000000
--- a/rails_application/app/read_models/public_offer/register_lowest_price.rb
+++ /dev/null
@@ -1,72 +0,0 @@
-module PublicOffer
- class RegisterLowestPrice
- RECENT_PERIOD = 30.days
-
- def call(event)
- product_id = event.data.fetch(:product_id)
-
- link_to_stream(event, product_id)
-
- lowest_recent_price = lowest_recent_price_for(product_id)
-
- product = Product.find(product_id)
- product.update!(lowest_recent_price: lowest_recent_price)
- end
-
- private
-
- def lowest_recent_price_for(product_id)
- price_changes = project_price_changes(product_id)
- border_event = find_border_event(price_changes)
-
- events_to_compare = price_changes.select do |price_change|
- recent_event?(price_change) && !future_event?(price_change)
- end
-
- events_to_compare.push(border_event) if border_event.present?
-
- events_to_compare.min_by { |price_change| price_change.fetch(:price) }&.fetch(:price)
- end
-
- def project_price_changes(product_id)
- RailsEventStore::Projection
- .from_stream(stream_name(product_id))
- .init(-> { [] })
- .when(
- Pricing::PriceSet,
- ->(state, event) { state.push({ valid_at: event.valid_at, price: event.data.fetch(:price) }) }
- )
- .run(event_store)
- .sort_by { |price_change| price_change.fetch(:valid_at) }
- end
-
- def find_border_event(price_changes)
- price_changes.reverse.find { |price_change| !recent_event?(price_change) }
- end
-
- def recent_event?(price_change)
- price_change.fetch(:valid_at) > RECENT_PERIOD.ago.beginning_of_day
- end
-
- def future_event?(price_change)
- price_change.fetch(:valid_at) > Time.now
- end
-
- def link_to_stream(event, product_id)
- event_store.link(
- event.event_id,
- stream_name: stream_name(product_id)
- )
- rescue RubyEventStore::EventDuplicatedInStream => error
- Rails.logger.info("Duplicated event registered for PricesHistoryReport: #{error}")
- end
-
- def event_store
- Rails.configuration.event_store
- end
-
- def stream_name(product_id)
- "PricesHistoryReport$#{product_id}"
- end
- end
-end
diff --git a/rails_application/app/read_models/shipments/configuration.rb b/rails_application/app/read_models/shipments/configuration.rb
deleted file mode 100644
index d6f082712..000000000
--- a/rails_application/app/read_models/shipments/configuration.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-module Shipments
- class Shipment < ApplicationRecord
- self.table_name = "shipments"
-
- has_one :order,
- class_name: "Orders::Order",
- foreign_key: :uid,
- primary_key: :order_uid
-
- def full_address
- [self.address_line_1, self.address_line_2, self.address_line_3, self.address_line_4].join(" ")
- end
- end
-
- class Order < ApplicationRecord
- self.table_name = "shipments_orders"
- end
-
- class Configuration
- def call(event_store)
- event_store.subscribe(SetShippingAddress, to: [Shipping::ShippingAddressAddedToShipment])
- event_store.subscribe(MarkOrderPlaced, to: [Ordering::OrderPlaced])
- end
- end
-end
diff --git a/rails_application/app/read_models/shipments/mark_order_placed.rb b/rails_application/app/read_models/shipments/mark_order_placed.rb
deleted file mode 100644
index ed0868a75..000000000
--- a/rails_application/app/read_models/shipments/mark_order_placed.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-module Shipments
- class MarkOrderPlaced
- def call(event)
- Order.find_or_initialize_by(uid: event.data.fetch(:order_id)).update!(submitted: true)
- end
- end
-end
diff --git a/rails_application/app/read_models/shipments/set_shipping_address.rb b/rails_application/app/read_models/shipments/set_shipping_address.rb
deleted file mode 100644
index f5e2ceec6..000000000
--- a/rails_application/app/read_models/shipments/set_shipping_address.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-module Shipments
- class SetShippingAddress
- def call(event)
- shipment = Shipment.find_or_create_by(order_uid: event.data.fetch(:order_id))
- postal_address = event.data.fetch(:postal_address)
- shipment.address_line_1 = postal_address.fetch(:line_1)
- shipment.address_line_2 = postal_address.fetch(:line_2)
- shipment.address_line_3 = postal_address.fetch(:line_3)
- shipment.address_line_4 = postal_address.fetch(:line_4)
- shipment.save!
- end
- end
-end
diff --git a/rails_application/app/read_models/single_table_read_model.rb b/rails_application/app/read_models/single_table_read_model.rb
deleted file mode 100644
index dafe81310..000000000
--- a/rails_application/app/read_models/single_table_read_model.rb
+++ /dev/null
@@ -1,117 +0,0 @@
-class SingleTableReadModel
- def initialize(event_store, active_record_name, id_column)
- @event_store = event_store
- @active_record_name = active_record_name
- @id_column = id_column
- end
-
- def subscribe_create(creation_event)
- @event_store.subscribe(create_handler(creation_event), to: [creation_event])
- end
-
- def subscribe_copy(event, sequence_of_keys, column = Array(sequence_of_keys).join('_'))
- @event_store.subscribe(copy_handler(event, sequence_of_keys, column), to: [event])
- end
-
- private
-
- def create_handler(event)
- handler_class_name = "Create#{@active_record_name.name.gsub('::', '')}On#{event.name.gsub('::', '')}"
- Object.send(:remove_const, handler_class_name) if self.class.const_defined?(handler_class_name)
- _active_record_name_, _id_column_, _event_store_ = @active_record_name, @id_column, @event_store
- Object.const_set(
- handler_class_name,
- Class.new(CreateRecord) do
- define_method(:event_store) { _event_store_ }
- define_method(:active_record_name) { _active_record_name_ }
- define_method(:id_column) { _id_column_ }
- end
- )
- end
-
- def copy_handler(event, sequence_of_keys, column)
- handler_class_name = "Set#{@active_record_name.name.gsub('::', '')}#{column.to_s.camelcase}On#{event.name.gsub('::', '')}"
- Object.send(:remove_const, handler_class_name) if self.class.const_defined?(handler_class_name)
- _active_record_name_, _id_column_, _event_store_ = @active_record_name, @id_column, @event_store
- Object.const_set(
- handler_class_name,
- Class.new(CopyEventAttribute) do
- define_method(:event_store) { _event_store_ }
- define_method(:active_record_name) { _active_record_name_ }
- define_method(:id_column) { _id_column_ }
- define_method(:sequence_of_keys) { sequence_of_keys }
- define_method(:column) { column }
- end
- )
- end
-end
-
-class ReadModelHandler
- def initialize(*args)
- if args.present?
- @event_store = args[0]
- @active_record_name = args[1]
- @id_column = args[2]
- end
- super()
- end
-
- private
-
- attr_reader :active_record_name, :id_column, :event_store
-
- def concurrent_safely(event)
- stream_name = "#{active_record_name}$#{record_id(event)}$#{event.event_type}"
- read_scope = event_store.read.as_at.stream(stream_name)
- begin
- last_event = read_scope.last
- return if last_event && last_event.timestamp > event.timestamp
- ApplicationRecord.with_advisory_lock(active_record_name, record_id(event)) do
- yield
- event_store.link(
- event.event_id,
- stream_name: stream_name,
- expected_version: last_event ? read_scope.to(last_event.event_id).count : -1
- )
- end
- rescue RubyEventStore::WrongExpectedEventVersion
- retry
- rescue RubyEventStore::EventDuplicatedInStream
- end
- end
-
- def find_or_initialize_record(event)
- active_record_name.find_or_initialize_by(id: record_id(event))
- end
-
- def record_id(event)
- event.data.fetch(id_column)
- end
-end
-
-class CreateRecord < ReadModelHandler
- def call(event)
- concurrent_safely(event) do
- find_or_initialize_record(event).save
- end
- end
-end
-
-class CopyEventAttribute < ReadModelHandler
- def initialize(*args)
- if args.present?
- @sequence_of_keys = args[3]
- @column = args[4]
- end
- super
- end
-
- def call(event)
- concurrent_safely(event) do
- find_or_initialize_record(event).update_attribute(column, event.data.dig(*sequence_of_keys))
- end
- end
-
- private
- attr_reader :sequence_of_keys, :column
-end
\ No newline at end of file
diff --git a/rails_application/app/read_models/time_promotions/broadcaster.rb b/rails_application/app/read_models/time_promotions/broadcaster.rb
deleted file mode 100644
index a62bd7813..000000000
--- a/rails_application/app/read_models/time_promotions/broadcaster.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-module TimePromotions
- class Broadcaster
- def call(content)
- Turbo::StreamsChannel.broadcast_append_to(
- "time_promotions",
- target: "time_promotions_table",
- html: content)
- end
- end
-end
\ No newline at end of file
diff --git a/rails_application/app/read_models/time_promotions/configuration.rb b/rails_application/app/read_models/time_promotions/configuration.rb
deleted file mode 100644
index 6dedc84b6..000000000
--- a/rails_application/app/read_models/time_promotions/configuration.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-module TimePromotions
- class TimePromotion < ApplicationRecord
- self.table_name = "time_promotions"
-
- scope :current, -> { where("start_time < ? AND end_time > ?", Time.current, Time.current) }
- end
-
- class Configuration
- def call(event_store)
- event_store.subscribe(CreateTimePromotion, to: [Pricing::TimePromotionCreated])
- end
- end
-end
diff --git a/rails_application/app/read_models/time_promotions/create_time_promotion.rb b/rails_application/app/read_models/time_promotions/create_time_promotion.rb
deleted file mode 100644
index f68ba572b..000000000
--- a/rails_application/app/read_models/time_promotions/create_time_promotion.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-module TimePromotions
- class CreateTimePromotion
- def call(event)
- time_promotion = TimePromotion.create!(event.data.slice(:code, :discount, :start_time, :end_time, :label).merge(id: event.data[:time_promotion_id]))
- Broadcaster.new.call(<<~HTML
-
- #{time_promotion.label}
- #{time_promotion.discount}
- #{time_promotion.start_time}
- #{time_promotion.end_time}
-
- HTML
-)
- end
- end
-end
diff --git a/rails_application/app/services/email_client.rb b/rails_application/app/services/email_client.rb
new file mode 100644
index 000000000..837ca85ef
--- /dev/null
+++ b/rails_application/app/services/email_client.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class EmailClient
+ def send_email(order_id)
+ # send email
+ end
+end
diff --git a/rails_application/app/services/send_email.rb b/rails_application/app/services/send_email.rb
new file mode 100644
index 000000000..b6564a0bd
--- /dev/null
+++ b/rails_application/app/services/send_email.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+class SendEmail
+ def call(event)
+ event_store.link(event.event_id, stream_name: "Sales$#{order_id(event)}")
+
+ state = {}
+
+ event_store.read.stream("Sales$#{order_id(event)}").each do |event_in_stream|
+ case event_in_stream
+ when Ordering::OrderPaid
+ state[:order_paid] = true
+ when Invoicing::InvoiceGenerated
+ state[:invoice_generated] = true
+ end
+ end
+
+ if state[:order_paid] && state[:invoice_generated]
+ Rails.configuration.email_client.send_email(order_id(event))
+ end
+ end
+
+ private
+
+ def order_id(event)
+ case event
+ when Ordering::OrderPaid
+ event.data[:id]
+ when Invoicing::InvoiceGenerated
+ event.data[:order_id]
+ end
+ end
+
+ def event_store
+ Rails.configuration.event_store
+ end
+end
diff --git a/rails_application/app/views/billing_addresses/edit.erb b/rails_application/app/views/billing_addresses/edit.erb
index 5d61da338..7db7db63a 100644
--- a/rails_application/app/views/billing_addresses/edit.erb
+++ b/rails_application/app/views/billing_addresses/edit.erb
@@ -5,9 +5,9 @@
<% content_for(:actions) do %>
<%= secondary_action_button do %>
<% if @order.submitted? %>
- <%= link_to 'Back', order_path(@order.uid) %>
+ <%= link_to 'Back', order_path(@order.id) %>
<% else %>
- <%= link_to 'Back', edit_order_path(@order.uid) %>
+ <%= link_to 'Back', edit_order_path(@order.id) %>
<% end %>
<% end %>
@@ -16,35 +16,35 @@
<% end %>
<% end %>
-<%= form_with(model: @invoice, url: order_billing_address_path(@invoice.order_uid), method: "patch", id: "form") do |form| %>
+<%= form_with(model: @order, url: order_billing_address_path(@order.id), method: "patch", id: "form") do |form| %>
Tax Identification Number (optional)
- <%= form.text_field :tax_id_number, required: false, class: "mt-1 focus:ring-blue-500 focus:border-blue-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md" %>
+ <%= form.text_field :invoice_tax_id_number, required: false, class: "mt-1 focus:ring-blue-500 focus:border-blue-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md" %>
Addressee's full name (Person or Company)
- <%= form.text_field :address_line_1, required: true, class: "mt-1 focus:ring-blue-500 focus:border-blue-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md" %>
+ <%= form.text_field :invoice_addressed_to, required: true, class: "mt-1 focus:ring-blue-500 focus:border-blue-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md" %>
Street address or post office box number
- <%= form.text_field :address_line_2, required: true, class: "mt-1 focus:ring-blue-500 focus:border-blue-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md" %>
+ <%= form.text_field :invoice_address, required: true, class: "mt-1 focus:ring-blue-500 focus:border-blue-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md" %>
City or town and postal code
- <%= form.text_field :address_line_3, required: true, class: "mt-1 focus:ring-blue-500 focus:border-blue-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md" %>
+ <%= form.text_field :invoice_city, required: true, class: "mt-1 focus:ring-blue-500 focus:border-blue-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md" %>
Country
- <%= form.text_field :address_line_4, required: true, class: "mt-1 focus:ring-blue-500 focus:border-blue-500 block shadow-sm sm:text-sm border-gray-300 rounded-md" %>
+ <%= form.text_field :invoice_country, required: true, class: "mt-1 focus:ring-blue-500 focus:border-blue-500 block shadow-sm sm:text-sm border-gray-300 rounded-md" %>
<% end %>
diff --git a/rails_application/app/views/customers/index.html.erb b/rails_application/app/views/customers/index.html.erb
index 2200db920..59d1cd3ac 100644
--- a/rails_application/app/views/customers/index.html.erb
+++ b/rails_application/app/views/customers/index.html.erb
@@ -20,7 +20,7 @@
<% @customers.each do |customer| %>
- <%= link_to customer.name, customer_path(customer), class: "text-blue-500 hover:underline" %>
+ <%= link_to (customer.first_name + customer.last_name), customer_path(customer), class: "text-blue-500 hover:underline" %>
<%- if customer.vip %>
Already a VIP
diff --git a/rails_application/app/views/customers/new.html.erb b/rails_application/app/views/customers/new.html.erb
index 97733f124..a3d4cd400 100644
--- a/rails_application/app/views/customers/new.html.erb
+++ b/rails_application/app/views/customers/new.html.erb
@@ -12,11 +12,17 @@
<% end %>
<% end %>
-<%= form_tag({controller: "customers", action: "create"}, method: "post", id: "form") do %>
- <%= hidden_field_tag(:customer_id, @customer_id) %>
-
-
- Name
+<%= form_with model: @customer, id: "form" do |form| %>
+
+ First Name
+
+ <%= form.text_field :first_name, required: true, class: "mt-1 focus:ring-blue-500 focus:border-blue-500 block shadow-sm sm:text-sm border-gray-300 rounded-md" %>
+
+ Last Name
+
+ <%= form.text_field :last_name, required: true, class: "mt-1 focus:ring-blue-500 focus:border-blue-500 block shadow-sm sm:text-sm border-gray-300 rounded-md" %>
+
+ Email
- <%= text_field_tag :name, "", required: true, class: "mt-1 focus:ring-blue-500 focus:border-blue-500 block shadow-sm sm:text-sm border-gray-300 rounded-md" %>
+ <%= form.text_field :email, required: true, class: "mt-1 focus:ring-blue-500 focus:border-blue-500 block shadow-sm sm:text-sm border-gray-300 rounded-md" %>
<% end %>
diff --git a/rails_application/app/views/customers/show.html.erb b/rails_application/app/views/customers/show.html.erb
index ae2b1ba9d..e12a87a5f 100644
--- a/rails_application/app/views/customers/show.html.erb
+++ b/rails_application/app/views/customers/show.html.erb
@@ -4,7 +4,7 @@
Name
- <%= @customer.name %>
+ <%= @customer.full_name %>
VIP
<%= @customer.vip? ? "Yes" : "No" %>
@@ -21,9 +21,9 @@
<% @customer_orders.each do |order| %>
- <%= link_to order.number || 'Not submitted', order_path(order.order_uid), class: "text-blue-500 hover:underline" %>
+ <%= link_to order.number || 'Not submitted', order_path(order.id), class: "text-blue-500 hover:underline" %>
<%= order.state %>
- <%= number_to_currency(order.discounted_value) %>
+ <%= number_to_currency(order.discount) %>
<% end %>
diff --git a/rails_application/app/views/invoices/show.html.erb b/rails_application/app/views/invoices/show.html.erb
index 05d512ff1..a77121c19 100644
--- a/rails_application/app/views/invoices/show.html.erb
+++ b/rails_application/app/views/invoices/show.html.erb
@@ -4,7 +4,7 @@
<% content_for(:actions) do %>
<%= secondary_action_button do %>
- <%= link_to 'Back', order_path(@invoice.order_uid) %>
+ <%= link_to 'Back', order_path(@invoice.id) %>
<% end %>
<% end %>
@@ -12,18 +12,18 @@
Invoiced by
<%= "Arkency Ecommerce" %>
Issue date
- <%= @invoice.issue_date %>
+ <%= @invoice.invoice_issue_date %>
Disposal date
- <%= @invoice.disposal_date %>
+ <%= @invoice.invoice_disposal_date %>
Payment date
- <%= @invoice.payment_date %>
+ <%= @invoice.invoice_payment_date %>
Customer
- <%= @invoice.tax_id_number %>
- <%= @invoice.address_line_1 %>
- <%= @invoice.address_line_2 %>
- <%= @invoice.address_line_3 %>
- <%= @invoice.address_line_4 %>
+ <%= @invoice.invoice_tax_id_number %>
+ <%= @invoice.invoice_addressed_to %>
+ <%= @invoice.invoice_address %>
+ <%= @invoice.invoice_city %>
+ <%= @invoice.invoice_country %>
@@ -39,20 +39,20 @@
- <% @invoice.invoice_items.each do |item| %>
+ <% @invoice.order_items.each do |item| %>
- <%= item.name %>
- <%= item.vat_rate %>
+ <%= item.product.name %>
+ <%= item.product.vat_rate %>
<%= item.quantity %>
- <%= number_to_currency(item.unit_price) %>
- <%= number_to_currency(item.value) %>
+ <%= number_to_currency(item.product.price) %>
+ <%= number_to_currency(item.product.price * item.quantity * @discount / 100) %>
<% end %>
Total
- <%= number_to_currency(@invoice.total_value) %>
+ <%= number_to_currency(@invoice.invoice_total_value) %>
diff --git a/rails_application/app/views/orders/edit.html.erb b/rails_application/app/views/orders/edit.html.erb
index 70a419346..1abe71677 100644
--- a/rails_application/app/views/orders/edit.html.erb
+++ b/rails_application/app/views/orders/edit.html.erb
@@ -39,11 +39,11 @@
<% @products.each do |product| %>
- <% order_line = @order_lines.find{|order_line| order_line.product_id == product.id} %>
+ <% order_line = @order.order_items.find{|order_item| order_item.product_id == product.id} %>
<%= product.name %>
- "><%= order_line.try(&:quantity) || 0 %>
+ "><%= order_line&.quantity || 0 %>
<%= number_to_currency(product.price) %>
- "><%= number_to_currency(order_line.try(&:value)) %>
+ "><%= number_to_currency(product.price.nil? ? 0 : product&.price * (order_line&.quantity || 0)) %>
<%= button_to "Add", add_item_order_path(id: @order_id, product_id: product.id), class: "hover:underline text-blue-500" %>
<% if order_line.nil? %>
">
@@ -89,6 +89,6 @@
Customer
- <%= select_tag(:customer_id, options_from_collection_for_select(@customers, :id, :name), id: "customer", class: "mt-1 focus:ring-blue-500 focus:border-blue-500 block shadow-sm sm:text-sm border-gray-300 rounded-md") %>
+ <%= select_tag(:customer_id, options_from_collection_for_select(@customers, :id, :full_name), id: "customer", class: "mt-1 focus:ring-blue-500 focus:border-blue-500 block shadow-sm sm:text-sm border-gray-300 rounded-md") %>
<% end %>
diff --git a/rails_application/app/views/orders/index.html.erb b/rails_application/app/views/orders/index.html.erb
index fa24e8b31..01e64ff61 100644
--- a/rails_application/app/views/orders/index.html.erb
+++ b/rails_application/app/views/orders/index.html.erb
@@ -21,11 +21,11 @@
<% @orders.each do |order| %>
- <%= turbo_stream_from "orders_order_#{order.uid}" %>
+ <%= turbo_stream_from "orders_order_#{order.id}" %>
- <%= link_to order.number || 'Not submitted', order_path(order.uid), class: "text-blue-500 hover:underline" %>
- <%= order.customer %>
- "><%= order.state %>
+ <%= link_to order.number || 'Not submitted', order_path(order.id), class: "text-blue-500 hover:underline" %>
+ <%= order.customer&.full_name %>
+ "><%= order.status%>
<% end %>
diff --git a/rails_application/app/views/orders/new.html.erb b/rails_application/app/views/orders/new.html.erb
index 84681e02f..cdbbab8f4 100644
--- a/rails_application/app/views/orders/new.html.erb
+++ b/rails_application/app/views/orders/new.html.erb
@@ -1,5 +1,5 @@
<% content_for(:header) do %>
- New Order
+ Order
<% end %>
<% content_for(:actions) do %>
@@ -7,6 +7,18 @@
<%= link_to 'Back', orders_path %>
<% end %>
+ <%= secondary_action_button do %>
+ <%= link_to "Edit discount", edit_discount_order_path(@order_id) %>
+ <% end %>
+
+ <%= secondary_action_button do %>
+ <%= link_to "Edit Shipping Address", edit_order_shipping_address_path(@order_id) %>
+ <% end %>
+
+ <%= secondary_action_button do %>
+ <%= link_to "Edit Billing Address", edit_order_billing_address_path(@order_id) %>
+ <% end %>
+
<%= primary_form_action_button do %>
Submit Order
<% end %>
@@ -14,32 +26,50 @@
-
- Product
- Price
-
+
+ Product
+ Quantity
+ Price
+ Value
+
<% @products.each do |product| %>
-
+
+ <% order_line = @order.order_items.find{|order_item| order_item.product_id == product.id} %>
<%= product.name %>
+ "><%= order_line&.quantity || 0 %>
<%= number_to_currency(product.price) %>
-
- <%= button_to "Add", add_item_order_path(id: @order_id, product_id: product.id), class: "text-blue-500 hover:underline" %>
-
+ "><%= number_to_currency(product.price.nil? ? 0 : product&.price * (order_line&.quantity || 0)) %>
+ <%= button_to "Add", add_item_order_path(id: @order_id, product_id: product.id), class: "hover:underline text-blue-500" %>
+ <% if order_line.nil? %>
+ ">
+ <% else %>
+ "><%= button_to("Remove", remove_item_order_path(id: @order_id, product_id: product.id), class: "hover:underline text-blue-500") %>
+ <% end %>
<% end %>
+
+ <% if @time_promotions.present? %>
+ <% @time_promotions.each do |time_promotion| %>
+
+ Promotion: <%= time_promotion.label %> (if you buy before <%= time_promotion.end_time %>)
+ <%= time_promotion.discount %>%
+
+ <% end %>
+ <% end %>
+
<%= form_tag({controller: "orders", action: "create"}, method: "post", id: "form") do %>
- <%= hidden_field_tag(:order_id, @order_id) %>
+ <%= hidden_field_tag(:order_id, @order.id) %>
Customer
- <%= select_tag(:customer_id, options_from_collection_for_select(@customers, :id, :name), id: "customer", class: "mt-1 focus:ring-blue-500 focus:border-blue-500 block shadow-sm sm:text-sm border-gray-300 rounded-md") %>
+ <%= select_tag(:customer_id, options_from_collection_for_select(@customers, :id, :full_name), id: "customer", class: "mt-1 focus:ring-blue-500 focus:border-blue-500 block shadow-sm sm:text-sm border-gray-300 rounded-md") %>
<% end %>
diff --git a/rails_application/app/views/orders/show.html.erb b/rails_application/app/views/orders/show.html.erb
index 8d4188222..decde1e4d 100644
--- a/rails_application/app/views/orders/show.html.erb
+++ b/rails_application/app/views/orders/show.html.erb
@@ -1,48 +1,48 @@
<% content_for(:header) do %>
Order <%= @order.number %>
<% end %>
-<%= turbo_stream_from "orders_order_#{@order.uid}" %>
+<%= turbo_stream_from "orders_order_#{@order.id}" %>
<% content_for(:actions) do %>
<%= secondary_action_button do %>
- <%= order_history_link(@order.uid) %>
+ <%= order_history_link(@order.id) %>
<% end %>
<%= secondary_action_button do %>
- <%= link_to "Invoice", invoice_path(@order.uid) %>
- <% end if @invoice.issued? %>
+ <%= link_to "Invoice", invoice_path(@order.id) %>
+ <% end if @order.invoice_issued? %>
<%= secondary_action_button do %>
<%= link_to 'Back', orders_path %>
<% end %>
<%= primary_action_button do %>
- <%= link_to 'Edit', edit_order_path(@order.uid) %>
+ <%= link_to 'Edit', edit_order_path(@order.id) %>
<% end if @order.state == "Draft" %>
<% if @order.state == "Submitted" %>
- <%= button_to("Pay", pay_order_path(@order.uid), class: "mr-3 ml-3 inline-flex items-center px-4 py-2 border rounded-md shadow-sm text-sm font-medium focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 border-transparent text-white bg-blue-600 hover:bg-blue-700") %>
+ <%= button_to("Pay", pay_order_path(@order.id), class: "mr-3 ml-3 inline-flex items-center px-4 py-2 border rounded-md shadow-sm text-sm font-medium focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 border-transparent text-white bg-blue-600 hover:bg-blue-700") %>
<% end %>
<% if (@order.state == "Submitted") %>
- <%= button_to("Cancel Order", cancel_order_path(@order.uid), class: "inline-flex items-center px-4 py-2 border rounded-md shadow-sm text-sm font-medium focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-50 border-gray-300 text-gray-700 bg-white hover:bg-gray-50") %>
+ <%= button_to("Cancel Order", cancel_order_path(@order.id), class: "inline-flex items-center px-4 py-2 border rounded-md shadow-sm text-sm font-medium focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-50 border-gray-300 text-gray-700 bg-white hover:bg-gray-50") %>
<% end %>
<% end %>
Customer
- <%= @order.customer || "None" %>
+ <%= @order.customer&.full_name || "None" %>
State
- "><%= @order.state %>
+ "><%= @order.state %>
Shipping Details
- <% unless @shipment %>
+ <% unless @order.addressed_to && @order.city && @order.country && @order.address %>
Shipping address is missing.
<% end %>
- <% unless @shipment %>
+ <% unless @order.addressed_to && @order.city && @order.country && @order.address %>
<%= link_to "Add shipment address",
- edit_order_shipping_address_path(@order.uid),
+ edit_order_shipping_address_path(@order.id),
class: 'px-2 py-1 border rounded-md shadow-sm text-xs font-medium focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 border-transparent text-white bg-blue-600 hover:bg-blue-700'
%>
<% else %>
@@ -52,18 +52,18 @@
<% if @order.state != "Draft" %>
Billing Details
- <% if @invoice.issued? %>
- <%= link_to @invoice.number, invoice_path(@order.uid) %>
- <% elsif !@invoice.address_present? %>
+ <% if @order.invoice_issued? %>
+ <%= link_to @order.number, invoice_path(@order.id) %>
+ <% elsif !(@order.invoice_addressed_to && @order.invoice_city && @order.invoice_country && @order.invoice_address ) %>
Billing address is missing.
<%= link_to "Add billing address",
- edit_order_billing_address_path(@order.uid),
+ edit_order_billing_address_path(@order.id),
class: 'px-2 py-1 border rounded-md shadow-sm text-xs font-medium focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 border-transparent text-white bg-blue-600 hover:bg-blue-700'
%>
<% else %>
Invoice not issued
<%= button_to "Issue now",
- order_invoice_path(@order.uid),
+ order_invoice_path(@order.id),
class: 'px-2 py-1 border rounded-md shadow-sm text-xs font-medium focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 border-transparent text-white bg-blue-600 hover:bg-blue-700'
%>
<% end %>
@@ -84,30 +84,30 @@
<% @order_lines.each do |item| %>
- <%= item.product_name %>
+ <%= item.product.name %>
<%= item.quantity %>
- <%= number_to_currency(item.price) %>
- <%= number_to_currency(item.value) %>
+ <%= number_to_currency(item.product.price) %>
+ <%= number_to_currency(item.product.price) %>
<% end %>
- <% if @order.discounted_value != @order.total_value %>
+ <% if @order.discount != @order.total %>
Before discounts
- <%= number_to_currency(@order.total_value) %>
+ <%= number_to_currency(@order.total) %>
<% end %>
- <% if @order.percentage_discount %>
+ <% if @order.discount %>
General discount
- <%= @order.percentage_discount %>%
+ <%= @order.discount %>%
<% end %>
Total
- <%= number_to_currency(@order.discounted_value) %>
+ <%= number_to_currency(@total) %>
diff --git a/rails_application/app/views/products/_future_price.html.erb b/rails_application/app/views/products/_future_price.html.erb
index 290a7cdae..f6faea4bc 100644
--- a/rails_application/app/views/products/_future_price.html.erb
+++ b/rails_application/app/views/products/_future_price.html.erb
@@ -8,7 +8,7 @@
value: valid_since&.strftime('%Y-%m-%dT%H:%M'),
disabled: disabled,
id: "start_time",
- name: "[future_prices][][start_time]",
+ name: "[future_price][start_time]",
class: "#{border_input_style} mt-1 focus:ring-blue-500 focus:border-blue-500 inline-block sm:text-sm rounded-md"
%>
@@ -18,7 +18,7 @@
Price:
<%= number_field_tag :price, price,
disabled: disabled,
- name: "[future_prices][][price]",
+ name: "[future_price][price]",
min: 0,
step: 0.01,
required: true,
diff --git a/rails_application/app/views/products/edit.html.erb b/rails_application/app/views/products/edit.html.erb
index c06e55525..106c9fbba 100644
--- a/rails_application/app/views/products/edit.html.erb
+++ b/rails_application/app/views/products/edit.html.erb
@@ -7,7 +7,7 @@
<%= link_to 'Back', products_path %>
<% end %>
- <%= primary_button_to(add_future_price_product_path(@product)) do %>
+ <%= primary_button_to(add_future_price_path) do %>
Add future price
<% end %>
@@ -16,40 +16,26 @@
<% end %>
<% end %>
-<%= form_tag({controller: "products", action: "update"}, method: "patch", id: "form") do %>
- <%= hidden_field_tag(:product_id, @product.id) %>
-
+<%= form_with model: @product, id: "form", method: :patch do |form| %>
Product Name
- <%= text_field_tag :name, @product.name, required: true, class: "mt-1 focus:ring-blue-500 focus:border-blue-500 block shadow-sm sm:text-sm border-gray-300 rounded-md" %>
+ <%= form.text_field :name, required: true, class: "mt-1 focus:ring-blue-500 focus:border-blue-500 block shadow-sm sm:text-sm border-gray-300 rounded-md" %>
VAT rate
- <%= @product.vat_rate_code %>
+ <%= @product.vat_rate %>
Price
- <%= number_field_tag :price, @product.price, min: 0, step: 0.01, required: true, class: "mt-1 focus:ring-blue-500 focus:border-blue-500 block shadow-sm sm:text-sm border-gray-300 rounded-md" %>
+ <%= form.number_field :price, min: 0, step: 0.01, 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 @product.future_prices_calendar.any? %>
-
- Future prices:
-
- <% @product.future_prices_calendar.each do |entry| %>
- <%= render partial: "products/future_price", locals: {
- disabled: true,
- price: entry[:price],
- valid_since: entry[:valid_since] }
- %>
- <% end %>
- <% end %>
<% end %>
diff --git a/rails_application/app/views/products/index.html.erb b/rails_application/app/views/products/index.html.erb
index d69b3a595..65f3ec27e 100644
--- a/rails_application/app/views/products/index.html.erb
+++ b/rails_application/app/views/products/index.html.erb
@@ -20,10 +20,10 @@
<% @products.each do |product| %>
-
+ >
<%= link_to product.name, product_path(product), class: "hover:underline text-blue-500" %>
<%= number_to_currency product.price %>
- <%= product.vat_rate_code %>
+ <%= product.vat_rate %>
<%= product.stock_level %>
<% end %>
diff --git a/rails_application/app/views/products/new.html.erb b/rails_application/app/views/products/new.html.erb
index bb2480093..d1d79e80d 100644
--- a/rails_application/app/views/products/new.html.erb
+++ b/rails_application/app/views/products/new.html.erb
@@ -13,26 +13,24 @@
<% end %>
<%= turbo_frame_tag "create_product" do %>
- <%= form_tag({ controller: "products", action: "create" }, method: "post", id: "form") do %>
- <%= hidden_field_tag(:product_id, @product_id, data: { turbo_permanent: true }) %>
-
+ <%= form_with model: @product, id: "form" do |form| %>
Name
- <%= text_field_tag :name, "", 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 } %>
+ <%= form.text_field :name, 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 } %>
Price
- <%= number_field_tag :price, nil, min: 0, step: 0.01, 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 } %>
+ <%= form.number_field :price, min: 0, step: 0.01, 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 } %>
VAT rate
- <%= 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 } %>
+ <%= form.collection_select :vat_rate, 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 } %>
<% if defined?(errors) %>
diff --git a/rails_application/app/views/products/show.html.erb b/rails_application/app/views/products/show.html.erb
index 56a55e6d0..f49f02e96 100644
--- a/rails_application/app/views/products/show.html.erb
+++ b/rails_application/app/views/products/show.html.erb
@@ -22,23 +22,8 @@
Price
<%= number_to_currency @product.price %>
VAT rate
- <%= @product.vat_rate_code %>
+ <%= @product.vat_rate %>
Stock Level
<%= @product.stock_level %>
-<% if @product.future_prices_calendar.any? %>
-
-
- Future prices:
-
- <% @product.future_prices_calendar.each do |entry| %>
- <%= render partial: "products/future_price", locals: {
- disabled: true,
- price: entry[:price],
- valid_since: entry[:valid_since] }
- %>
- <% end %>
-
-
-<% end %>
diff --git a/rails_application/app/views/shipments/index.html.erb b/rails_application/app/views/shipments/index.html.erb
index 2d2376fae..5c3b1386a 100644
--- a/rails_application/app/views/shipments/index.html.erb
+++ b/rails_application/app/views/shipments/index.html.erb
@@ -11,10 +11,10 @@
- <% @shipments.each do |shipment| %>
+ <% @orders.each do |order| %>
- <%= link_to shipment.order.number, order_path(shipment.order.uid), class: "text-blue-500 hover:underline" %>
- <%= shipment.full_address %>
+ <%= link_to order.number, order_path(order.id), class: "text-blue-500 hover:underline" %>
+ <%= order.shipment_full_address %>
<% end %>
@@ -23,9 +23,9 @@
- <%= page_entries_info @shipments %>
+ <%= page_entries_info @orders %>
- <%= paginate @shipments %>
+ <%= paginate @orders%>
\ No newline at end of file
diff --git a/rails_application/app/views/shipping_addresses/edit.html.erb b/rails_application/app/views/shipping_addresses/edit.html.erb
index d633ffa73..2e64a60a5 100644
--- a/rails_application/app/views/shipping_addresses/edit.html.erb
+++ b/rails_application/app/views/shipping_addresses/edit.html.erb
@@ -5,9 +5,9 @@
<% content_for(:actions) do %>
<%= secondary_action_button do %>
<% if @order.submitted? %>
- <%= link_to 'Back', order_path(@order.uid) %>
+ <%= link_to 'Back', order_path(@order.id) %>
<% else %>
- <%= link_to 'Back', edit_order_path(@order.uid) %>
+ <%= link_to 'Back', edit_order_path(@order.id) %>
<% end %>
<% end %>
@@ -16,29 +16,29 @@
<% end %>
<% end %>
-<%= form_with(model: @shipment, url: order_shipping_address_path(@shipment.order_uid), method: "patch", id: "form") do |form| %>
+<%= form_with(model: @order, url: order_shipping_address_path(@order.id), method: "patch", id: "form") do |form| %>
Addressee's full name (Person or Company)
- <%= form.text_field :address_line_1, required: true, class: "mt-1 focus:ring-blue-500 focus:border-blue-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md" %>
+ <%= form.text_field :addressed_to, required: true, class: "mt-1 focus:ring-blue-500 focus:border-blue-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md" %>
Street address or post office box number
- <%= form.text_field :address_line_2, required: true, class: "mt-1 focus:ring-blue-500 focus:border-blue-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md" %>
+ <%= form.text_field :address, required: true, class: "mt-1 focus:ring-blue-500 focus:border-blue-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md" %>
City or town and postal code
- <%= form.text_field :address_line_3, required: true, class: "mt-1 focus:ring-blue-500 focus:border-blue-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md" %>
+ <%= form.text_field :city, required: true, class: "mt-1 focus:ring-blue-500 focus:border-blue-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md" %>
Country
- <%= form.text_field :address_line_4, required: true, class: "mt-1 focus:ring-blue-500 focus:border-blue-500 block shadow-sm sm:text-sm border-gray-300 rounded-md" %>
+ <%= form.text_field :country, required: true, class: "mt-1 focus:ring-blue-500 focus:border-blue-500 block shadow-sm sm:text-sm border-gray-300 rounded-md" %>
<% end %>
diff --git a/rails_application/config/environments/test.rb b/rails_application/config/environments/test.rb
index cc8c75f23..7db6ccb36 100644
--- a/rails_application/config/environments/test.rb
+++ b/rails_application/config/environments/test.rb
@@ -50,4 +50,5 @@
# --- Ecommerce ---
config.number_generator = ->{ Ordering::FakeNumberGenerator.new }
config.payment_gateway = -> { @gateway ||= Payments::FakeGateway.new }
+ config.email_client = -> { FakeEmailClient.new }
end
diff --git a/rails_application/config/routes.rb b/rails_application/config/routes.rb
index e193db270..1420f00f6 100644
--- a/rails_application/config/routes.rb
+++ b/rails_application/config/routes.rb
@@ -29,9 +29,6 @@
resources :products, only: [:new, :show, :create, :index, :edit, :update] do
resources :supplies, only: [:new, :create]
- member do
- post :add_future_price, to: "product/future_price#add_future_price", as: "add_future_price"
- end
end
@@ -50,6 +47,7 @@
get :logout, to: "client/clients#logout"
get "clients", to: "client/clients#index"
get "client/products", to: "client/products#index"
+ post :add_future_price, to: "products#add_future_price", as: "add_future_price"
mount RailsEventStore::Browser => "/res"
mount Sidekiq::Web => '/sidekiq'
diff --git a/rails_application/db/migrate/20150429224522_create_orders.rb b/rails_application/db/migrate/20150429224522_create_orders.rb
deleted file mode 100644
index 5cf53c783..000000000
--- a/rails_application/db/migrate/20150429224522_create_orders.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-class CreateOrders < ActiveRecord::Migration[5.1]
- def change
- create_table :orders do |t|
- t.string :uid
- t.string :number
- t.string :customer
- t.string :state
- end
- end
-end
diff --git a/rails_application/db/migrate/20150429224621_create_order_lines.rb b/rails_application/db/migrate/20150429224621_create_order_lines.rb
deleted file mode 100644
index c185f9f04..000000000
--- a/rails_application/db/migrate/20150429224621_create_order_lines.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-class CreateOrderLines < ActiveRecord::Migration[5.1]
- def change
- create_table :order_lines do |t|
- t.string :order_uid
- t.integer :product_id
- t.string :product_name
- t.integer :quantity
- end
- end
-end
diff --git a/rails_application/db/migrate/20150429224628_create_customers.rb b/rails_application/db/migrate/20150429224628_create_customers.rb
deleted file mode 100644
index b46320881..000000000
--- a/rails_application/db/migrate/20150429224628_create_customers.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-class CreateCustomers < ActiveRecord::Migration[5.1]
- def change
- create_table :customers do |t|
- t.string :name
-
- t.timestamps null: false
- end
- end
-end
diff --git a/rails_application/db/migrate/20150429224746_create_products.rb b/rails_application/db/migrate/20150429224746_create_products.rb
deleted file mode 100644
index 3a326644f..000000000
--- a/rails_application/db/migrate/20150429224746_create_products.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-class CreateProducts < ActiveRecord::Migration[5.1]
- def change
- create_table :products do |t|
- t.string :name
-
- t.timestamps null: false
- end
- end
-end
diff --git a/rails_application/db/migrate/20181102132612_create_event_store_events.rb b/rails_application/db/migrate/20181102132612_create_event_store_events.rb
deleted file mode 100644
index c99955b2e..000000000
--- a/rails_application/db/migrate/20181102132612_create_event_store_events.rb
+++ /dev/null
@@ -1,42 +0,0 @@
-class CreateEventStoreEvents < ActiveRecord::Migration[4.2]
- def change
- postgres = ActiveRecord::Base.connection.adapter_name == "PostgreSQL"
- sqlite = ActiveRecord::Base.connection.adapter_name == "SQLite"
- rails_42 = Gem::Version.new(ActiveRecord::VERSION::STRING) < Gem::Version.new("5.0.0")
- enable_extension "pgcrypto" if postgres
- create_table(:event_store_events_in_streams, force: false) do |t|
- t.string :stream, null: false
- t.integer :position, null: true
- if postgres
- t.references :event, null: false, type: :uuid
- else
- t.references :event, null: false, type: :string
- end
- t.datetime :created_at, null: false
- end
- add_index :event_store_events_in_streams, [:stream, :position], unique: true
- add_index :event_store_events_in_streams, [:created_at]
- add_index :event_store_events_in_streams, [:stream, :event_id], unique: true
-
- if postgres
- create_table(:event_store_events, id: :uuid, default: "gen_random_uuid()", force: false) do |t|
- t.string :event_type, null: false
- t.text :metadata
- t.text :data, null: false
- t.datetime :created_at, null: false
- end
- else
- create_table(:event_store_events, id: false, force: false) do |t|
- t.string :id, limit: 36, primary_key: true, null: false
- t.string :event_type, null: false
- t.text :metadata
- t.text :data, null: false
- t.datetime :created_at, null: false
- end
- if sqlite && rails_42
- add_index :event_store_events, :id, unique: true
- end
- end
- add_index :event_store_events, :created_at
- end
-end
diff --git a/rails_application/db/migrate/20181123154324_index_by_event_type.rb b/rails_application/db/migrate/20181123154324_index_by_event_type.rb
deleted file mode 100644
index 02e7aec89..000000000
--- a/rails_application/db/migrate/20181123154324_index_by_event_type.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-class IndexByEventType < ActiveRecord::Migration[4.2]
- def change
- add_index :event_store_events, :event_type unless index_exists? :event_store_events, :event_type
- end
-end
diff --git a/rails_application/db/migrate/20181123155503_limit_for_event_id.rb b/rails_application/db/migrate/20181123155503_limit_for_event_id.rb
deleted file mode 100644
index 481f96999..000000000
--- a/rails_application/db/migrate/20181123155503_limit_for_event_id.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-class LimitForEventId < ActiveRecord::Migration[4.2]
- def change
- postgres = ActiveRecord::Base.connection.adapter_name == "PostgreSQL"
- change_column(:event_store_events_in_streams, :event_id, :string, limit: 36) unless postgres
- end
-end
diff --git a/rails_application/db/migrate/20181207145051_binary_data_and_metadata.rb b/rails_application/db/migrate/20181207145051_binary_data_and_metadata.rb
deleted file mode 100644
index f2698a56d..000000000
--- a/rails_application/db/migrate/20181207145051_binary_data_and_metadata.rb
+++ /dev/null
@@ -1,33 +0,0 @@
-class BinaryDataAndMetadata < ActiveRecord::Migration[4.2]
- def change
- rails_42 = Gem::Version.new(ActiveRecord::VERSION::STRING) < Gem::Version.new("5.0.0")
-
- case ActiveRecord::Base.connection.adapter_name
- when "SQLite"
- rename_table :event_store_events, :old_event_store_events
- create_table(:event_store_events, id: false, force: false) do |t|
- t.string :id, limit: 36, primary_key: true, null: false
- t.string :event_type, null: false
- t.binary :metadata
- t.binary :data, null: false
- t.datetime :created_at, null: false
- end
- add_index :event_store_events, :id, unique: true if rails_42
- add_index :event_store_events, :created_at
- add_index :event_store_events, :event_type
- execute <<-SQL
- INSERT INTO event_store_events(id, event_type, metadata, data, created_at)
- SELECT id, event_type, metadata, data, created_at FROM old_event_store_events;
- SQL
- drop_table :old_event_store_events
- when "PostgreSQL"
- execute <<-SQL
- ALTER TABLE event_store_events ALTER COLUMN data TYPE bytea USING convert_to(data, 'UTF8');
- ALTER TABLE event_store_events ALTER COLUMN metadata TYPE bytea USING convert_to(metadata, 'UTF8');
- SQL
- else
- change_column :event_store_events, :data, :binary
- change_column :event_store_events, :metadata, :binary
- end
- end
-end
diff --git a/rails_application/db/migrate/20210102110315_harmonize_schema.rb b/rails_application/db/migrate/20210102110315_harmonize_schema.rb
deleted file mode 100644
index 2a644fd51..000000000
--- a/rails_application/db/migrate/20210102110315_harmonize_schema.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-class HarmonizeSchema < ActiveRecord::Migration[5.2]
- def change
- execute "ALTER TABLE event_store_events ALTER COLUMN id TYPE uuid USING id::uuid;"
- execute "ALTER TABLE event_store_events_in_streams ALTER COLUMN event_id TYPE uuid USING event_id::uuid;"
- end
-end
diff --git a/rails_application/db/migrate/20210102182818_binary_to_jsonb.rb b/rails_application/db/migrate/20210102182818_binary_to_jsonb.rb
deleted file mode 100644
index ff0fd8712..000000000
--- a/rails_application/db/migrate/20210102182818_binary_to_jsonb.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-class BinaryToJsonb < ActiveRecord::Migration[5.2]
- def change
- execute "ALTER TABLE event_store_events ALTER COLUMN data TYPE jsonb USING convert_from(data, 'UTF-8')::jsonb"
- execute "ALTER TABLE event_store_events ALTER COLUMN metadata TYPE jsonb USING convert_from(metadata, 'UTF-8')::jsonb"
- end
-end
diff --git a/rails_application/db/migrate/20210103114304_created_at_precision.rb b/rails_application/db/migrate/20210103114304_created_at_precision.rb
deleted file mode 100644
index c68669b48..000000000
--- a/rails_application/db/migrate/20210103114304_created_at_precision.rb
+++ /dev/null
@@ -1,45 +0,0 @@
-# frozen_string_literal: true
-
-class CreatedAtPrecision < ActiveRecord::Migration[4.2]
- def change
- case ActiveRecord::Base.connection.adapter_name
- when "SQLite"
- rename_table :event_store_events, :old_event_store_events
- create_table(:event_store_events, id: false, force: false) do |t|
- t.string :id, limit: 36, primary_key: true, null: false
- t.string :event_type, null: false
- t.binary :metadata
- t.binary :data, null: false
- t.datetime :created_at, null: false, precision: 6
- end
- add_index :event_store_events, :created_at
- add_index :event_store_events, :event_type
- execute <<-SQL
- INSERT INTO event_store_events(id, event_type, metadata, data, created_at)
- SELECT id, event_type, metadata, data, created_at FROM old_event_store_events;
- SQL
- drop_table :old_event_store_events
-
- rename_table :event_store_events_in_streams, :old_event_store_events_in_streams
- create_table(:event_store_events_in_streams, force: false) do |t|
- t.string :stream, null: false
- t.integer :position, null: true
- t.references :event, null: false, type: :string, limit: 36
- t.datetime :created_at, null: false, precision: 6
- end
- add_index :event_store_events_in_streams, [:stream, :position], unique: true
- add_index :event_store_events_in_streams, [:created_at]
- add_index :event_store_events_in_streams, [:stream, :event_id], unique: true
-
- execute <<-SQL
- INSERT INTO event_store_events_in_streams(id, stream, position, event_id, created_at)
- SELECT id, stream, position, event_id, created_at FROM old_event_store_events_in_streams;
- SQL
- drop_table :old_event_store_events_in_streams
- when "PostgreSQL"
- else
- change_column :event_store_events, :created_at, :datetime, precision: 6
- change_column :event_store_events_in_streams, :created_at, :datetime, precision: 6
- end
- end
-end
diff --git a/rails_application/db/migrate/20210103114309_add_valid_at.rb b/rails_application/db/migrate/20210103114309_add_valid_at.rb
deleted file mode 100644
index 45db11e20..000000000
--- a/rails_application/db/migrate/20210103114309_add_valid_at.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-# frozen_string_literal: true
-
-class AddValidAt < ActiveRecord::Migration[4.2]
- def change
- case ActiveRecord::Base.connection.adapter_name
- when "PostgreSQL"
- add_column :event_store_events, :valid_at, :datetime, null: true
- else
- add_column :event_store_events, :valid_at, :datetime, precision: 6, null: true
- end
-
- add_index :event_store_events, :valid_at
- end
-end
diff --git a/rails_application/db/migrate/20210103114314_no_global_stream_entries.rb b/rails_application/db/migrate/20210103114314_no_global_stream_entries.rb
deleted file mode 100644
index a63e9fdf0..000000000
--- a/rails_application/db/migrate/20210103114314_no_global_stream_entries.rb
+++ /dev/null
@@ -1,59 +0,0 @@
-class NoGlobalStreamEntries < ActiveRecord::Migration[4.2]
- def change
- case ActiveRecord::Base.connection.adapter_name
- when "SQLite"
- rename_table :event_store_events, :old_event_store_events
- create_table(:event_store_events, force: false) do |t|
- t.references :event, null: false, type: :string, limit: 36
- t.string :event_type, null: false
- t.binary :metadata
- t.binary :data, null: false
- t.datetime :created_at, precision: 6, null: false
- t.datetime :valid_at, precision: 6, null: true
- end
- add_index :event_store_events, :event_id, unique: true
- add_index :event_store_events, :created_at
- add_index :event_store_events, :valid_at
- add_index :event_store_events, :event_type
-
- execute <<-SQL
- INSERT INTO event_store_events(event_id, event_type, metadata, data, created_at)
- SELECT id, event_type, metadata, data, created_at FROM old_event_store_events;
- SQL
- drop_table :old_event_store_events
- when "PostgreSQL"
- rename_column :event_store_events, :id, :event_id
- change_column_default :event_store_events, :event_id, nil
- add_column :event_store_events, :id, :serial
-
- execute <<~SQL
- UPDATE event_store_events
- SET id = event_store_events_in_streams.id
- FROM event_store_events_in_streams
- WHERE event_store_events.event_id = event_store_events_in_streams.event_id AND event_store_events_in_streams.stream = 'all';
- UPDATE event_store_events
- SET id = ese.new_id
- FROM (SELECT id, row_number() OVER (ORDER BY id) AS new_id FROM event_store_events) ese
- WHERE event_store_events.id = ese.id;
- SELECT setval(pg_get_serial_sequence('event_store_events', 'id'), max(id)) FROM event_store_events;
- ALTER TABLE event_store_events DROP CONSTRAINT event_store_events_pkey;
- ALTER TABLE event_store_events ADD PRIMARY KEY (id);
- SQL
- add_index :event_store_events, :event_id, unique: true
- else
- rename_column :event_store_events, :id, :event_id
- add_column :event_store_events, :id, :integer
-
- execute <<-SQL
- UPDATE event_store_events
- INNER JOIN event_store_events_in_streams ON (event_store_events.event_id = event_store_events_in_streams.event_id)
- SET event_store_events.id = event_store_events_in_streams.id
- WHERE event_store_events_in_streams.stream = 'all';
- SQL
- execute "SET @row_number = 0"
- execute "UPDATE event_store_events SET event_store_events.id = (@row_number:=@row_number + 1) ORDER BY id"
- execute "ALTER TABLE event_store_events DROP PRIMARY KEY, ADD PRIMARY KEY (id), MODIFY id INT AUTO_INCREMENT"
- add_index :event_store_events, :event_id, unique: true
- end
- end
-end
diff --git a/rails_application/db/migrate/20210103114315_add_foreign_key_constraint_to_active_storage_attachments_for_blob_id.active_storage.rb b/rails_application/db/migrate/20210103114315_add_foreign_key_constraint_to_active_storage_attachments_for_blob_id.active_storage.rb
deleted file mode 100644
index ff5d72c7e..000000000
--- a/rails_application/db/migrate/20210103114315_add_foreign_key_constraint_to_active_storage_attachments_for_blob_id.active_storage.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-# This migration comes from active_storage (originally 20180723000244)
-class AddForeignKeyConstraintToActiveStorageAttachmentsForBlobId < ActiveRecord::Migration[6.0]
- def up
- return if foreign_key_exists?(:active_storage_attachments, column: :blob_id)
-
- if table_exists?(:active_storage_blobs)
- add_foreign_key :active_storage_attachments, :active_storage_blobs, column: :blob_id
- end
- end
-end
diff --git a/rails_application/db/migrate/20210306222803_create_active_admin_comments.rb b/rails_application/db/migrate/20210306222803_create_active_admin_comments.rb
deleted file mode 100644
index 511e388d7..000000000
--- a/rails_application/db/migrate/20210306222803_create_active_admin_comments.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-class CreateActiveAdminComments < ActiveRecord::Migration[6.1]
- def self.up
- create_table :active_admin_comments do |t|
- t.string :namespace
- t.text :body
- t.references :resource, polymorphic: true
- t.references :author, polymorphic: true
- t.timestamps
- end
- add_index :active_admin_comments, [:namespace]
- end
-
- def self.down
- drop_table :active_admin_comments
- end
-end
diff --git a/rails_application/db/migrate/20210318093812_disable_pg_crypto.rb b/rails_application/db/migrate/20210318093812_disable_pg_crypto.rb
deleted file mode 100644
index ece9ce5c5..000000000
--- a/rails_application/db/migrate/20210318093812_disable_pg_crypto.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-class DisablePgCrypto < ActiveRecord::Migration[6.1]
- def up
- disable_extension "pg_crypto"
- end
-
- def down
- enable_extension "pg_crypto"
- end
-end
diff --git a/rails_application/db/migrate/20210505162028_add_price_to_products.rb b/rails_application/db/migrate/20210505162028_add_price_to_products.rb
deleted file mode 100644
index cc5da2a1f..000000000
--- a/rails_application/db/migrate/20210505162028_add_price_to_products.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-class AddPriceToProducts < ActiveRecord::Migration[6.1]
- def change
- add_column :products, :price, :decimal, precision: 8, scale: 2
- end
-end
diff --git a/rails_application/db/migrate/20210505163254_add_price_to_order_lines.rb b/rails_application/db/migrate/20210505163254_add_price_to_order_lines.rb
deleted file mode 100644
index 9e7828a0a..000000000
--- a/rails_application/db/migrate/20210505163254_add_price_to_order_lines.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-class AddPriceToOrderLines < ActiveRecord::Migration[6.1]
- def change
- add_column :order_lines, :price, :decimal, precision: 8, scale: 2
- end
-end
diff --git a/rails_application/db/migrate/20210617230722_add_uid_to_product.rb b/rails_application/db/migrate/20210617230722_add_uid_to_product.rb
deleted file mode 100644
index 04dc8a0ba..000000000
--- a/rails_application/db/migrate/20210617230722_add_uid_to_product.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-class AddUidToProduct < ActiveRecord::Migration[6.1]
- def change
- add_column :products, :uid, :uuid
- end
-end
\ No newline at end of file
diff --git a/rails_application/db/migrate/20210626171021_add_uid_to_customer.rb b/rails_application/db/migrate/20210626171021_add_uid_to_customer.rb
deleted file mode 100644
index 819f0f570..000000000
--- a/rails_application/db/migrate/20210626171021_add_uid_to_customer.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-class AddUidToCustomer < ActiveRecord::Migration[6.1]
- def change
- add_column :customers, :uid, :uuid
- end
-end
diff --git a/rails_application/db/migrate/20210626210851_migrate_from_id_to_uuid.rb b/rails_application/db/migrate/20210626210851_migrate_from_id_to_uuid.rb
deleted file mode 100644
index 87bad53a3..000000000
--- a/rails_application/db/migrate/20210626210851_migrate_from_id_to_uuid.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-class MigrateFromIdToUuid < ActiveRecord::Migration[6.1]
- def change
- change_table :products do |t|
- t.remove :id
- t.change_default :uid, "gen_random_uuid()"
- t.change_null :uid, false
- t.rename :uid, :id
- end
- execute "ALTER TABLE products ADD PRIMARY KEY (id);"
-
- change_table :customers do |t|
- t.remove :id
- t.change_default :uid, "gen_random_uuid()"
- t.change_null :uid, false
- t.rename :uid, :id
- end
- execute "ALTER TABLE customers ADD PRIMARY KEY (id);"
-
- remove_column :order_lines, :product_id
- add_column :order_lines, :product_id, :uuid
- end
-end
diff --git a/rails_application/db/migrate/20210719172643_add_discount_and_total_value_and_discount_value_to_orders.rb b/rails_application/db/migrate/20210719172643_add_discount_and_total_value_and_discount_value_to_orders.rb
deleted file mode 100644
index 40970a8c4..000000000
--- a/rails_application/db/migrate/20210719172643_add_discount_and_total_value_and_discount_value_to_orders.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-class AddDiscountAndTotalValueAndDiscountValueToOrders < ActiveRecord::Migration[6.1]
- def change
- add_column "orders", "percentage_discount", :decimal, precision: 8, scale: 2
- add_column "orders", "total_value", :decimal, precision: 8, scale: 2
- add_column "orders", "discounted_value", :decimal, precision: 8, scale: 2
- end
-end
diff --git a/rails_application/db/migrate/20210830164940_drop_active_admin_comments.rb b/rails_application/db/migrate/20210830164940_drop_active_admin_comments.rb
deleted file mode 100644
index 49bc7991d..000000000
--- a/rails_application/db/migrate/20210830164940_drop_active_admin_comments.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-class DropActiveAdminComments < ActiveRecord::Migration[6.1]
- def change
- drop_table "active_admin_comments"
- end
-end
diff --git a/rails_application/db/migrate/20210907115224_string_to_native_uuids.rb b/rails_application/db/migrate/20210907115224_string_to_native_uuids.rb
deleted file mode 100644
index 18d05d634..000000000
--- a/rails_application/db/migrate/20210907115224_string_to_native_uuids.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-class StringToNativeUuids < ActiveRecord::Migration[6.1]
- def change
- change_column :orders, :uid, :uuid, null: false, using: "uid::uuid"
- change_column :order_lines, :order_uid, :uuid, null: false, using: "order_uid::uuid"
- end
-end
diff --git a/rails_application/db/migrate/20210916191138_customers_add_registered_at.rb b/rails_application/db/migrate/20210916191138_customers_add_registered_at.rb
deleted file mode 100644
index 0b1b3cda5..000000000
--- a/rails_application/db/migrate/20210916191138_customers_add_registered_at.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-class CustomersAddRegisteredAt < ActiveRecord::Migration[6.1]
- def change
- add_column :customers, :registered_at, :datetime
- end
-end
diff --git a/rails_application/db/migrate/20210918000940_products_add_registered_at.rb b/rails_application/db/migrate/20210918000940_products_add_registered_at.rb
deleted file mode 100644
index c0159cdc7..000000000
--- a/rails_application/db/migrate/20210918000940_products_add_registered_at.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-class ProductsAddRegisteredAt < ActiveRecord::Migration[6.1]
- def change
- add_column :products, :registered_at, :datetime
- end
-end
diff --git a/rails_application/db/migrate/20210918003625_products_drop_timestamps.rb b/rails_application/db/migrate/20210918003625_products_drop_timestamps.rb
deleted file mode 100644
index c493b79f1..000000000
--- a/rails_application/db/migrate/20210918003625_products_drop_timestamps.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-class ProductsDropTimestamps < ActiveRecord::Migration[6.1]
- def change
- remove_column :products, :created_at
- remove_column :products, :updated_at
- end
-end
diff --git a/rails_application/db/migrate/20211003170051_create_shipments.rb b/rails_application/db/migrate/20211003170051_create_shipments.rb
deleted file mode 100644
index e460cb87f..000000000
--- a/rails_application/db/migrate/20211003170051_create_shipments.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-class CreateShipments < ActiveRecord::Migration[6.1]
- def change
- create_table :shipments do |t|
- t.string :order_uid, null: false
- t.string :address_line_1
- t.string :address_line_2
- t.string :address_line_3
- t.string :address_line_4
- end
- end
-end
diff --git a/rails_application/db/migrate/20211116135113_create_products_in_orders_read_model.rb b/rails_application/db/migrate/20211116135113_create_products_in_orders_read_model.rb
deleted file mode 100644
index 77c741512..000000000
--- a/rails_application/db/migrate/20211116135113_create_products_in_orders_read_model.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-class CreateProductsInOrdersReadModel < ActiveRecord::Migration[6.1]
- def change
- create_table :orders_products do |t|
- t.uuid :uid, null: false
- t.string :name
- t.decimal :price, precision: 8, scale: 2
- end
- end
-end
diff --git a/rails_application/db/migrate/20211116154857_create_customers_in_orders_read_model.rb b/rails_application/db/migrate/20211116154857_create_customers_in_orders_read_model.rb
deleted file mode 100644
index 5bedf38eb..000000000
--- a/rails_application/db/migrate/20211116154857_create_customers_in_orders_read_model.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-class CreateCustomersInOrdersReadModel < ActiveRecord::Migration[6.1]
- def change
- create_table :orders_customers do |t|
- t.uuid "uid", null: false
- t.string "name"
- end
- end
-end
diff --git a/rails_application/db/migrate/20211124220955_add_vat_rate_code_to_product.rb b/rails_application/db/migrate/20211124220955_add_vat_rate_code_to_product.rb
deleted file mode 100644
index 6cf164753..000000000
--- a/rails_application/db/migrate/20211124220955_add_vat_rate_code_to_product.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-class AddVatRateCodeToProduct < ActiveRecord::Migration[6.1]
- def change
- add_column :products, :vat_rate_code, :string
- end
-end
diff --git a/rails_application/db/migrate/20220109162627_create_invoice_read_model.rb b/rails_application/db/migrate/20220109162627_create_invoice_read_model.rb
deleted file mode 100644
index 90b615365..000000000
--- a/rails_application/db/migrate/20220109162627_create_invoice_read_model.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-class CreateInvoiceReadModel < ActiveRecord::Migration[6.1]
- def change
- create_table :invoices do |t|
- t.string :order_uid, null: false
- t.string :number
- t.string :tax_id_number
- t.string :address_line_1
- t.string :address_line_2
- t.string :address_line_3
- t.string :address_line_4
- t.boolean :address_present, default: false
- t.boolean :issued, default: false
- t.date :issue_date
- t.date :disposal_date
- t.date :payment_date
- t.decimal :total_value, precision: 8, scale: 2
- end
-
- create_table :invoice_items do |t|
- t.references :invoice
- t.string :name
- t.decimal :unit_price, precision: 8, scale: 2
- t.decimal :vat_rate, precision: 4, scale: 1
- t.integer :quantity
- t.decimal :value, precision: 8, scale: 2
- end
- end
-end
diff --git a/rails_application/db/migrate/20220109175702_create_orders_in_invoices_read_model.rb b/rails_application/db/migrate/20220109175702_create_orders_in_invoices_read_model.rb
deleted file mode 100644
index a60666719..000000000
--- a/rails_application/db/migrate/20220109175702_create_orders_in_invoices_read_model.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-class CreateOrdersInInvoicesReadModel < ActiveRecord::Migration[6.1]
- def change
- create_table :invoices_orders do |t|
- t.uuid "uid", null: false
- t.boolean "submitted", default: false
- end
- end
-end
diff --git a/rails_application/db/migrate/20220109175753_create_orders_in_shipments_read_model.rb b/rails_application/db/migrate/20220109175753_create_orders_in_shipments_read_model.rb
deleted file mode 100644
index f891c2556..000000000
--- a/rails_application/db/migrate/20220109175753_create_orders_in_shipments_read_model.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-class CreateOrdersInShipmentsReadModel < ActiveRecord::Migration[6.1]
- def change
- create_table :shipments_orders do |t|
- t.uuid "uid", null: false
- t.boolean "submitted", default: false
- end
- end
-end
diff --git a/rails_application/db/migrate/20220121131334_add_vip_to_customer.rb b/rails_application/db/migrate/20220121131334_add_vip_to_customer.rb
deleted file mode 100644
index a5a4a6657..000000000
--- a/rails_application/db/migrate/20220121131334_add_vip_to_customer.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-class AddVipToCustomer < ActiveRecord::Migration[6.1]
- def change
- add_column :customers, :vip, :boolean, default: false, null: false
- end
-end
diff --git a/rails_application/db/migrate/20220219130650_create_clients.rb b/rails_application/db/migrate/20220219130650_create_clients.rb
deleted file mode 100644
index 005cad9a3..000000000
--- a/rails_application/db/migrate/20220219130650_create_clients.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-class CreateClients < ActiveRecord::Migration[6.1]
- def change
- create_table :clients do |t|
- t.uuid :uid
- t.string :name
-
- t.timestamps
- end
- end
-end
diff --git a/rails_application/db/migrate/20220219162101_create_client_orders.rb b/rails_application/db/migrate/20220219162101_create_client_orders.rb
deleted file mode 100644
index 41b84113e..000000000
--- a/rails_application/db/migrate/20220219162101_create_client_orders.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-class CreateClientOrders < ActiveRecord::Migration[6.1]
- def change
- create_table :client_orders do |t|
- t.uuid :client_uid
- t.string :number
- t.uuid :order_uid
- t.string :state
-
- t.timestamps
- end
- end
-end
diff --git a/rails_application/db/migrate/20220519094635_create_coupons.rb b/rails_application/db/migrate/20220519094635_create_coupons.rb
deleted file mode 100644
index d6fead050..000000000
--- a/rails_application/db/migrate/20220519094635_create_coupons.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-class CreateCoupons < ActiveRecord::Migration[7.0]
- def change
- create_table :coupons do |t|
- t.uuid "uid", null: false
- t.string :name
- t.string :code
- t.decimal :discount
-
- t.timestamps
- end
- end
-end
diff --git a/rails_application/db/migrate/20220528213429_create_happy_hours.rb b/rails_application/db/migrate/20220528213429_create_happy_hours.rb
deleted file mode 100644
index 923c41a16..000000000
--- a/rails_application/db/migrate/20220528213429_create_happy_hours.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-class CreateHappyHours < ActiveRecord::Migration[7.0]
- def change
- create_table :happy_hours, id: :uuid, default: -> { "gen_random_uuid()" } do |t|
- t.string :name
- t.string :code
- t.integer :discount
- t.integer :start_hour
- t.integer :end_hour
- t.string :product_ids, array: true
-
- t.timestamps
- end
- end
-end
diff --git a/rails_application/db/migrate/20220607201447_add_happy_hour_value_to_orders.rb b/rails_application/db/migrate/20220607201447_add_happy_hour_value_to_orders.rb
deleted file mode 100644
index fcb4f682f..000000000
--- a/rails_application/db/migrate/20220607201447_add_happy_hour_value_to_orders.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-class AddHappyHourValueToOrders < ActiveRecord::Migration[7.0]
- def change
- add_column :orders, :happy_hour_value, :decimal, precision: 8, scale: 2
- end
-end
diff --git a/rails_application/db/migrate/20220703122711_drop_happy_hours.rb b/rails_application/db/migrate/20220703122711_drop_happy_hours.rb
deleted file mode 100644
index 7b6cd4506..000000000
--- a/rails_application/db/migrate/20220703122711_drop_happy_hours.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-class DropHappyHours < ActiveRecord::Migration[7.0]
- def change
- drop_table :happy_hours, id: :uuid, default: -> { "gen_random_uuid()" } do |t|
- t.string :name
- t.string :code
- t.integer :discount
- t.integer :start_hour
- t.integer :end_hour
- t.string :product_ids, array: true
-
- t.timestamps
- end
- end
-end
diff --git a/rails_application/db/migrate/20220703133325_add_active_to_time_promotions.rb b/rails_application/db/migrate/20220703133325_add_active_to_time_promotions.rb
deleted file mode 100644
index eec0c18b0..000000000
--- a/rails_application/db/migrate/20220703133325_add_active_to_time_promotions.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-class AddActiveToTimePromotions < ActiveRecord::Migration[7.0]
- def change
- add_column :time_promotions, :active, :boolean, default: false
- end
-end
diff --git a/rails_application/db/migrate/20220703133431_add_unique_constraint_on_code_on_time_promotions.rb b/rails_application/db/migrate/20220703133431_add_unique_constraint_on_code_on_time_promotions.rb
deleted file mode 100644
index 525e75d46..000000000
--- a/rails_application/db/migrate/20220703133431_add_unique_constraint_on_code_on_time_promotions.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-class AddUniqueConstraintOnCodeOnTimePromotions < ActiveRecord::Migration[7.0]
- def change
- add_index :time_promotions, :code, unique: true
- end
-end
diff --git a/rails_application/db/migrate/20220723133454_remove_code_column_from_time_promotions.rb b/rails_application/db/migrate/20220723133454_remove_code_column_from_time_promotions.rb
deleted file mode 100644
index f3c253169..000000000
--- a/rails_application/db/migrate/20220723133454_remove_code_column_from_time_promotions.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-class RemoveCodeColumnFromTimePromotions < ActiveRecord::Migration[7.0]
- def change
- remove_column :time_promotions, :code, :string
- end
-end
diff --git a/rails_application/db/migrate/20220829155333_create_public_offer_products.rb b/rails_application/db/migrate/20220829155333_create_public_offer_products.rb
deleted file mode 100644
index 118d98422..000000000
--- a/rails_application/db/migrate/20220829155333_create_public_offer_products.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-class CreatePublicOfferProducts < ActiveRecord::Migration[7.0]
- def change
- create_table :public_offer_products, id: :uuid, default: -> { "gen_random_uuid()" } do |t|
- t.string :name
- t.decimal :price
- t.timestamps
- end
- end
-end
diff --git a/rails_application/db/migrate/20221104111403_create_client_order_lines_read_model.rb b/rails_application/db/migrate/20221104111403_create_client_order_lines_read_model.rb
deleted file mode 100644
index 41bd6264b..000000000
--- a/rails_application/db/migrate/20221104111403_create_client_order_lines_read_model.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-class CreateClientOrderLinesReadModel < ActiveRecord::Migration[7.0]
- def up
- create_table :client_order_lines do |t|
- t.string :order_uid
- t.integer :product_id
- t.string :product_name
- t.integer :product_quantity
- t.decimal :product_price, precision: 8, scale: 2
- end
- end
-
- def down
- drop_table :client_order_lines
- end
-end
diff --git a/rails_application/db/migrate/20221104152434_create_client_order_products_read_model.rb b/rails_application/db/migrate/20221104152434_create_client_order_products_read_model.rb
deleted file mode 100644
index 74a9fc1a1..000000000
--- a/rails_application/db/migrate/20221104152434_create_client_order_products_read_model.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-class CreateClientOrderProductsReadModel < ActiveRecord::Migration[7.0]
- def change
- create_table :client_order_products do |t|
- t.uuid "uid", null: false
- t.string "name"
- t.decimal "price", precision: 8, scale: 2
- end
- end
-end
diff --git a/rails_application/db/migrate/20221107085656_add_client_column_to_order.rb b/rails_application/db/migrate/20221107085656_add_client_column_to_order.rb
deleted file mode 100644
index 368b56780..000000000
--- a/rails_application/db/migrate/20221107085656_add_client_column_to_order.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-class AddClientColumnToOrder < ActiveRecord::Migration[7.0]
- def change
- add_column :client_orders, :client_name, :string
- end
-end
diff --git a/rails_application/db/migrate/20221109141019_fix_client_order_lines_product_id_column_type.rb b/rails_application/db/migrate/20221109141019_fix_client_order_lines_product_id_column_type.rb
deleted file mode 100644
index 7b38863b6..000000000
--- a/rails_application/db/migrate/20221109141019_fix_client_order_lines_product_id_column_type.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-class FixClientOrderLinesProductIdColumnType < ActiveRecord::Migration[7.0]
- def change
- remove_column :client_order_lines, :product_id, :integer
- add_column :client_order_lines, :product_id, :uuid
- end
-end
diff --git a/rails_application/db/migrate/20221109150819_add_values_to_client_order.rb b/rails_application/db/migrate/20221109150819_add_values_to_client_order.rb
deleted file mode 100644
index 6f6d0b30d..000000000
--- a/rails_application/db/migrate/20221109150819_add_values_to_client_order.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-class AddValuesToClientOrder < ActiveRecord::Migration[7.0]
- def change
- add_column "client_orders", "percentage_discount", :decimal, precision: 8, scale: 2
- add_column "client_orders", "total_value", :decimal, precision: 8, scale: 2
- add_column "client_orders", "discounted_value", :decimal, precision: 8, scale: 2
- end
-end
diff --git a/rails_application/db/migrate/20221124200914_add_prices_chart_to_product.rb b/rails_application/db/migrate/20221124200914_add_prices_chart_to_product.rb
deleted file mode 100644
index 1fd70395e..000000000
--- a/rails_application/db/migrate/20221124200914_add_prices_chart_to_product.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-class AddPricesChartToProduct < ActiveRecord::Migration[7.0]
- def change
- add_column :products, :prices_chart, :text
- end
-end
diff --git a/rails_application/db/migrate/20221208185429_rename_future_prices_column.rb b/rails_application/db/migrate/20221208185429_rename_future_prices_column.rb
deleted file mode 100644
index 760b26548..000000000
--- a/rails_application/db/migrate/20221208185429_rename_future_prices_column.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-class RenameFuturePricesColumn < ActiveRecord::Migration[7.0]
- def change
- rename_column :products, :prices_chart, :future_prices_calendar
- end
-end
diff --git a/rails_application/db/migrate/20221214202855_add_uniq_uid_index_to_orders_order.rb b/rails_application/db/migrate/20221214202855_add_uniq_uid_index_to_orders_order.rb
deleted file mode 100644
index 6bfeb8007..000000000
--- a/rails_application/db/migrate/20221214202855_add_uniq_uid_index_to_orders_order.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-class AddUniqUidIndexToOrdersOrder < ActiveRecord::Migration[7.0]
- def change
- add_index "orders", :uid, unique: true
- end
-end
diff --git a/rails_application/db/migrate/20221215214459_rename_future_prices_calendar_future_prices_calendar.rb b/rails_application/db/migrate/20221215214459_rename_future_prices_calendar_future_prices_calendar.rb
deleted file mode 100644
index 728c4c10c..000000000
--- a/rails_application/db/migrate/20221215214459_rename_future_prices_calendar_future_prices_calendar.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-class RenameFuturePricesCalendarFuturePricesCalendar < ActiveRecord::Migration[7.0]
- def change
- rename_column :products, :future_prices_calendar, :current_prices_calendar
- end
-end
diff --git a/rails_application/db/migrate/20221216113438_add_order_total_value_updated_at_to_orders_table.rb b/rails_application/db/migrate/20221216113438_add_order_total_value_updated_at_to_orders_table.rb
deleted file mode 100644
index 5d838f2c4..000000000
--- a/rails_application/db/migrate/20221216113438_add_order_total_value_updated_at_to_orders_table.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-class AddOrderTotalValueUpdatedAtToOrdersTable < ActiveRecord::Migration[7.0]
- def change
- add_column :orders, :total_value_updated_at, :datetime
- end
-end
diff --git a/rails_application/db/migrate/20221216115301_add_discount_updated_at_to_orders_table.rb b/rails_application/db/migrate/20221216115301_add_discount_updated_at_to_orders_table.rb
deleted file mode 100644
index d694fc251..000000000
--- a/rails_application/db/migrate/20221216115301_add_discount_updated_at_to_orders_table.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-class AddDiscountUpdatedAtToOrdersTable < ActiveRecord::Migration[7.0]
- def change
- add_column :orders, :discount_updated_at, :datetime
- end
-end
diff --git a/rails_application/db/migrate/20230110235838_add_lowest_recent_price_to_client_order_products.rb b/rails_application/db/migrate/20230110235838_add_lowest_recent_price_to_client_order_products.rb
deleted file mode 100644
index 79ec004c7..000000000
--- a/rails_application/db/migrate/20230110235838_add_lowest_recent_price_to_client_order_products.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-class AddLowestRecentPriceToClientOrderProducts < ActiveRecord::Migration[7.0]
- def up
- add_column :client_order_products, :lowest_recent_price, :decimal, precision: 8, scale: 2
-
- execute <<~SQL
- UPDATE client_order_products
- SET lowest_recent_price = price;
- SQL
- end
-
- def down
- remove_column :client_order_products, :lowest_recent_price
- end
-end
diff --git a/rails_application/db/migrate/20230118170447_create_availability_products.rb b/rails_application/db/migrate/20230118170447_create_availability_products.rb
deleted file mode 100644
index 012ccede3..000000000
--- a/rails_application/db/migrate/20230118170447_create_availability_products.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-class CreateAvailabilityProducts < ActiveRecord::Migration[7.0]
- def change
- create_table :availability_products do |t|
- t.uuid :uid
- t.integer :available
- end
- end
-end
diff --git a/rails_application/db/migrate/20230123202350_add_paid_products_summary_to_clients.rb b/rails_application/db/migrate/20230123202350_add_paid_products_summary_to_clients.rb
deleted file mode 100644
index 5ba470d7d..000000000
--- a/rails_application/db/migrate/20230123202350_add_paid_products_summary_to_clients.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-class AddPaidProductsSummaryToClients < ActiveRecord::Migration[7.0]
- def change
- add_column :clients, :paid_orders_summary, :decimal, precision: 8, scale: 2, default: 0
- end
-end
diff --git a/rails_application/db/migrate/20230124134558_add_paid_products_summary_to_customers.rb b/rails_application/db/migrate/20230124134558_add_paid_products_summary_to_customers.rb
deleted file mode 100644
index e2e6d022f..000000000
--- a/rails_application/db/migrate/20230124134558_add_paid_products_summary_to_customers.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-class AddPaidProductsSummaryToCustomers < ActiveRecord::Migration[7.0]
- def change
- add_column :customers, :paid_orders_summary, :decimal, precision: 8, scale: 2, default: 0
- end
-end
diff --git a/rails_application/db/migrate/20230124195547_remove_price_column_from_products.rb b/rails_application/db/migrate/20230124195547_remove_price_column_from_products.rb
deleted file mode 100644
index ec4daa151..000000000
--- a/rails_application/db/migrate/20230124195547_remove_price_column_from_products.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-class RemovePriceColumnFromProducts < ActiveRecord::Migration[7.0]
- def change
- remove_column :products, :price
- end
-end
diff --git a/rails_application/db/migrate/20230126174907_add_accounting_column_to_customer.rb b/rails_application/db/migrate/20230126174907_add_accounting_column_to_customer.rb
deleted file mode 100644
index ada9bbc26..000000000
--- a/rails_application/db/migrate/20230126174907_add_accounting_column_to_customer.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-class AddAccountingColumnToCustomer < ActiveRecord::Migration[7.0]
- def change
- add_column :customers, :account_id, :uuid
- end
-end
diff --git a/rails_application/db/migrate/20230127125135_create_table_accounts.rb b/rails_application/db/migrate/20230127125135_create_table_accounts.rb
deleted file mode 100644
index eba8cf87d..000000000
--- a/rails_application/db/migrate/20230127125135_create_table_accounts.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-class CreateTableAccounts < ActiveRecord::Migration[7.0]
- def change
- create_table :table_accounts do |t|
- t.uuid :client_id
- t.text :password
- end
- end
-end
diff --git a/rails_application/db/migrate/20230128110104_move_lowest_recent_price_to_public_offer.rb b/rails_application/db/migrate/20230128110104_move_lowest_recent_price_to_public_offer.rb
deleted file mode 100644
index c2cab4b37..000000000
--- a/rails_application/db/migrate/20230128110104_move_lowest_recent_price_to_public_offer.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-class MoveLowestRecentPriceToPublicOffer < ActiveRecord::Migration[7.0]
- def up
- add_column :public_offer_products, :lowest_recent_price, :decimal, precision: 8, scale: 2
-
- execute <<~SQL
- UPDATE public_offer_products
- SET lowest_recent_price = client_order_products.lowest_recent_price
- FROM client_order_products
- WHERE client_order_products.uid = public_offer_products.id
- SQL
- end
-
- def down
- remove_column :public_offer_products, :lowest_recent_price
- end
-end
diff --git a/rails_application/db/migrate/20230130172454_rename_accounts_table.rb b/rails_application/db/migrate/20230130172454_rename_accounts_table.rb
deleted file mode 100644
index 580635f20..000000000
--- a/rails_application/db/migrate/20230130172454_rename_accounts_table.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-class RenameAccountsTable < ActiveRecord::Migration[7.0]
- def change
- rename_table :table_accounts, :accounts
- end
-end
diff --git a/rails_application/db/migrate/20230130172623_add_account_id_to_accounts.rb b/rails_application/db/migrate/20230130172623_add_account_id_to_accounts.rb
deleted file mode 100644
index ca65d02d6..000000000
--- a/rails_application/db/migrate/20230130172623_add_account_id_to_accounts.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-class AddAccountIdToAccounts < ActiveRecord::Migration[7.0]
- def change
- add_column :accounts, :account_id, :uuid
- end
-end
diff --git a/rails_application/db/migrate/20230202175228_remove_lowest_recent_price_from_client_order_product.rb b/rails_application/db/migrate/20230202175228_remove_lowest_recent_price_from_client_order_product.rb
deleted file mode 100644
index 6209a7b4d..000000000
--- a/rails_application/db/migrate/20230202175228_remove_lowest_recent_price_from_client_order_product.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-class RemoveLowestRecentPriceFromClientOrderProduct < ActiveRecord::Migration[7.0]
- def change
- remove_column :client_order_products, :lowest_recent_price, :decimal, precision: 8, scale: 2
- end
-end
diff --git a/rails_application/db/migrate/20230906105023_add_event_id_index_to_event_store_events_in_streams.rb b/rails_application/db/migrate/20230906105023_add_event_id_index_to_event_store_events_in_streams.rb
deleted file mode 100644
index a998b8e02..000000000
--- a/rails_application/db/migrate/20230906105023_add_event_id_index_to_event_store_events_in_streams.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-# frozen_string_literal: true
-
-class AddEventIdIndexToEventStoreEventsInStreams < ActiveRecord::Migration[7.0]
- def change
- return if index_exists?(:event_store_events_in_streams, :event_id)
-
- add_index :event_store_events_in_streams, [:event_id]
- end
-end
diff --git a/rails_application/db/migrate/20230906105317_add_foreign_key_on_event_id_to_event_store_events_in_streams.rb b/rails_application/db/migrate/20230906105317_add_foreign_key_on_event_id_to_event_store_events_in_streams.rb
deleted file mode 100644
index 7a6e6897c..000000000
--- a/rails_application/db/migrate/20230906105317_add_foreign_key_on_event_id_to_event_store_events_in_streams.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-# frozen_string_literal: true
-
-class AddForeignKeyOnEventIdToEventStoreEventsInStreams < ActiveRecord::Migration[7.0]
- def change
- add_foreign_key :event_store_events_in_streams, :event_store_events, column: :event_id, primary_key: :event_id, if_not_exists: true, validate: false
- end
-end
diff --git a/rails_application/db/migrate/20230906105318_validate_add_foreign_key_on_event_id_to_event_store_events_in_streams.rb b/rails_application/db/migrate/20230906105318_validate_add_foreign_key_on_event_id_to_event_store_events_in_streams.rb
deleted file mode 100644
index 2b4be411f..000000000
--- a/rails_application/db/migrate/20230906105318_validate_add_foreign_key_on_event_id_to_event_store_events_in_streams.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-# frozen_string_literal: true
-
-class ValidateAddForeignKeyOnEventIdToEventStoreEventsInStreams < ActiveRecord::Migration[7.0]
- def change
- validate_foreign_key :event_store_events_in_streams, :event_store_events, column: :event_id, primary_key: :event_id
- end
-end
diff --git a/rails_application/db/migrate/20240806161443_create_orders.rb b/rails_application/db/migrate/20240806161443_create_orders.rb
new file mode 100644
index 000000000..db719db47
--- /dev/null
+++ b/rails_application/db/migrate/20240806161443_create_orders.rb
@@ -0,0 +1,18 @@
+class CreateOrders < ActiveRecord::Migration[7.0]
+ def change
+ create_table :orders do |t|
+ t.string :number
+ t.string :customer
+ t.string :address
+ t.string :phone
+ t.string :email
+ t.string :status
+ t.decimal :total, precision: 10, scale: 2
+ t.decimal :discount, precision: 10, scale: 2
+ t.datetime :completed_at
+ t.datetime :discount_updated_at
+
+ t.timestamps
+ end
+ end
+end
diff --git a/rails_application/db/migrate/20240806165434_create_old_fashioned_product_table.rb b/rails_application/db/migrate/20240806165434_create_old_fashioned_product_table.rb
new file mode 100644
index 000000000..597199083
--- /dev/null
+++ b/rails_application/db/migrate/20240806165434_create_old_fashioned_product_table.rb
@@ -0,0 +1,15 @@
+class CreateOldFashionedProductTable < ActiveRecord::Migration[7.0]
+ def change
+ create_table :products do |t|
+ t.string :name
+ t.decimal :price, precision: 10, scale: 2
+ t.integer :vat_rate
+ t.boolean :active, default: true
+ t.string :sku
+ t.string :description
+ t.string :category
+
+ t.timestamps
+ end
+ end
+end
diff --git a/rails_application/db/migrate/20240806165554_create_order_items.rb b/rails_application/db/migrate/20240806165554_create_order_items.rb
new file mode 100644
index 000000000..d4bd9e1fe
--- /dev/null
+++ b/rails_application/db/migrate/20240806165554_create_order_items.rb
@@ -0,0 +1,11 @@
+class CreateOrderItems < ActiveRecord::Migration[7.0]
+ def change
+ create_table :order_items do |t|
+ t.references :order, null: false, foreign_key: true
+ t.references :product, null: false, foreign_key: true
+ t.integer :quantity
+
+ t.timestamps
+ end
+ end
+end
diff --git a/rails_application/db/migrate/20240807162106_create_users.rb b/rails_application/db/migrate/20240807162106_create_users.rb
new file mode 100644
index 000000000..d460e3b92
--- /dev/null
+++ b/rails_application/db/migrate/20240807162106_create_users.rb
@@ -0,0 +1,13 @@
+class CreateUsers < ActiveRecord::Migration[7.0]
+ def change
+ create_table :users do |t|
+
+ t.string :first_name
+ t.string :last_name
+ t.string :email
+ t.decimal :paid_orders_summary, precision: 8, scale: 2, default: "0.0"
+
+ t.timestamps
+ end
+ end
+end
diff --git a/rails_application/db/migrate/20210824130811_add_stock_level_to_product.rb b/rails_application/db/migrate/20240808101541_add_stock_level_to_product_crud.rb
similarity index 52%
rename from rails_application/db/migrate/20210824130811_add_stock_level_to_product.rb
rename to rails_application/db/migrate/20240808101541_add_stock_level_to_product_crud.rb
index f02441ef6..72424a8c2 100644
--- a/rails_application/db/migrate/20210824130811_add_stock_level_to_product.rb
+++ b/rails_application/db/migrate/20240808101541_add_stock_level_to_product_crud.rb
@@ -1,4 +1,4 @@
-class AddStockLevelToProduct < ActiveRecord::Migration[6.1]
+class AddStockLevelToProductCrud < ActiveRecord::Migration[7.0]
def change
add_column :products, :stock_level, :integer
end
diff --git a/rails_application/db/migrate/20240814135823_create_invoices.rb b/rails_application/db/migrate/20240814135823_create_invoices.rb
new file mode 100644
index 000000000..071f38907
--- /dev/null
+++ b/rails_application/db/migrate/20240814135823_create_invoices.rb
@@ -0,0 +1,15 @@
+class CreateInvoices < ActiveRecord::Migration[7.0]
+ def change
+ create_table :invoices_tbl do |t|
+ t.bigint :order_id
+ t.string :order_number
+ t.decimal :total_value, precision: 10, scale: 2
+ t.string :address
+ t.datetime :payment_date
+ t.datetime :issued_at
+ t.bigint :tax_id_number
+
+ t.timestamps
+ end
+ end
+end
diff --git a/rails_application/db/migrate/20240821120809_correlate_order_with_user.rb b/rails_application/db/migrate/20240821120809_correlate_order_with_user.rb
new file mode 100644
index 000000000..dbb62ed1e
--- /dev/null
+++ b/rails_application/db/migrate/20240821120809_correlate_order_with_user.rb
@@ -0,0 +1,5 @@
+class CorrelateOrderWithUser < ActiveRecord::Migration[7.0]
+ def change
+ add_reference :orders, :user, null: true, foreign_key: true
+ end
+end
diff --git a/rails_application/db/migrate/20240821121943_rename_user_to_customer.rb b/rails_application/db/migrate/20240821121943_rename_user_to_customer.rb
new file mode 100644
index 000000000..88735b295
--- /dev/null
+++ b/rails_application/db/migrate/20240821121943_rename_user_to_customer.rb
@@ -0,0 +1,5 @@
+class RenameUserToCustomer < ActiveRecord::Migration[7.0]
+ def change
+ rename_table :users, :customers
+ end
+end
diff --git a/rails_application/db/migrate/20240821122557_enerich_customer.rb b/rails_application/db/migrate/20240821122557_enerich_customer.rb
new file mode 100644
index 000000000..b7ddbba14
--- /dev/null
+++ b/rails_application/db/migrate/20240821122557_enerich_customer.rb
@@ -0,0 +1,5 @@
+class EnerichCustomer < ActiveRecord::Migration[7.0]
+ def change
+ add_column :customers, :vip, :boolean, default: false
+ end
+end
diff --git a/rails_application/db/migrate/20240821123100_rename_user_id_to_customer_id_in_orders_table.rb b/rails_application/db/migrate/20240821123100_rename_user_id_to_customer_id_in_orders_table.rb
new file mode 100644
index 000000000..e067a12d0
--- /dev/null
+++ b/rails_application/db/migrate/20240821123100_rename_user_id_to_customer_id_in_orders_table.rb
@@ -0,0 +1,5 @@
+class RenameUserIdToCustomerIdInOrdersTable < ActiveRecord::Migration[7.0]
+ def change
+ rename_column :orders, :user_id, :customer_id
+ end
+end
diff --git a/rails_application/db/migrate/20240821123342_drop_customer_col_from_orders.rb b/rails_application/db/migrate/20240821123342_drop_customer_col_from_orders.rb
new file mode 100644
index 000000000..f5efa9738
--- /dev/null
+++ b/rails_application/db/migrate/20240821123342_drop_customer_col_from_orders.rb
@@ -0,0 +1,5 @@
+class DropCustomerColFromOrders < ActiveRecord::Migration[7.0]
+ def change
+ remove_column :orders, :customer
+ end
+end
diff --git a/rails_application/db/migrate/20240821125153_allow_null_for_orders_customers_fk.rb b/rails_application/db/migrate/20240821125153_allow_null_for_orders_customers_fk.rb
new file mode 100644
index 000000000..365708df3
--- /dev/null
+++ b/rails_application/db/migrate/20240821125153_allow_null_for_orders_customers_fk.rb
@@ -0,0 +1,5 @@
+class AllowNullForOrdersCustomersFk < ActiveRecord::Migration[7.0]
+ def change
+ change_column :orders, :customer_id, :integer, null: true
+ end
+end
diff --git a/rails_application/db/migrate/20240822081509_order_must_have_shipping_addresses_and_all_that_jazz.rb b/rails_application/db/migrate/20240822081509_order_must_have_shipping_addresses_and_all_that_jazz.rb
new file mode 100644
index 000000000..202e978d8
--- /dev/null
+++ b/rails_application/db/migrate/20240822081509_order_must_have_shipping_addresses_and_all_that_jazz.rb
@@ -0,0 +1,9 @@
+class OrderMustHaveShippingAddressesAndAllThatJazz < ActiveRecord::Migration[7.0]
+ def change
+ add_column :orders, :country, :string
+ add_column :orders, :city, :string
+ add_column :orders, :street, :string
+ add_column :orders, :zip, :string
+ add_column :orders, :addressed_to, :string
+ end
+end
diff --git a/rails_application/db/migrate/20240822082432_order_must_have_invoice_details.rb b/rails_application/db/migrate/20240822082432_order_must_have_invoice_details.rb
new file mode 100644
index 000000000..4988c7dcd
--- /dev/null
+++ b/rails_application/db/migrate/20240822082432_order_must_have_invoice_details.rb
@@ -0,0 +1,14 @@
+class OrderMustHaveInvoiceDetails < ActiveRecord::Migration[7.0]
+ def change
+ add_column :orders, :invoice_address, :string
+ add_column :orders, :invoice_tax_id_number, :string
+ add_column :orders, :invoice_issued, :boolean, default: false
+ add_column :orders, :invoice_issue_date, :date
+ add_column :orders, :invoice_disposal_date, :date
+ add_column :orders, :invoice_payment_date, :date
+ add_column :orders, :invoice_total_value, :decimal, precision: 8, scale: 2
+ add_column :orders, :invoice_country, :string
+ add_column :orders, :invoice_city, :string
+ add_column :orders, :invoice_addressed_to, :string
+ end
+end
diff --git a/rails_application/db/migrate/20240822100417_add_invoice_paid_to_order.rb b/rails_application/db/migrate/20240822100417_add_invoice_paid_to_order.rb
new file mode 100644
index 000000000..a7635e7e3
--- /dev/null
+++ b/rails_application/db/migrate/20240822100417_add_invoice_paid_to_order.rb
@@ -0,0 +1,5 @@
+class AddInvoicePaidToOrder < ActiveRecord::Migration[7.0]
+ def change
+ add_column :orders, :invoice_payment_status, :string, default: "Unpaid"
+ end
+end
diff --git a/rails_application/db/migrate/20240822110341_add_future_price_to_product.rb b/rails_application/db/migrate/20240822110341_add_future_price_to_product.rb
new file mode 100644
index 000000000..ccc5475fe
--- /dev/null
+++ b/rails_application/db/migrate/20240822110341_add_future_price_to_product.rb
@@ -0,0 +1,6 @@
+class AddFuturePriceToProduct < ActiveRecord::Migration[7.0]
+ def change
+ add_column :products, :future_price, :decimal, precision: 8, scale: 2
+ add_column :products, :future_price_start_time, :datetime
+ end
+end
diff --git a/rails_application/db/migrate/20220703123059_create_time_promotions.rb b/rails_application/db/migrate/20240822170829_create_time_promotions.rb
similarity index 57%
rename from rails_application/db/migrate/20220703123059_create_time_promotions.rb
rename to rails_application/db/migrate/20240822170829_create_time_promotions.rb
index fb57c0d33..3d140b463 100644
--- a/rails_application/db/migrate/20220703123059_create_time_promotions.rb
+++ b/rails_application/db/migrate/20240822170829_create_time_promotions.rb
@@ -1,11 +1,10 @@
class CreateTimePromotions < ActiveRecord::Migration[7.0]
def change
- create_table :time_promotions, id: :uuid, default: -> { "gen_random_uuid()" } do |t|
- t.string :label
- t.string :code
- t.integer :discount
+ create_table :time_promotions do |t|
t.datetime :start_time
t.datetime :end_time
+ t.string :label
+ t.decimal :discount, precision: 5, scale: 2
t.timestamps
end
diff --git a/rails_application/db/migrate/20240921091142_add_latest_column_to_product.rb b/rails_application/db/migrate/20240921091142_add_latest_column_to_product.rb
new file mode 100644
index 000000000..587076b13
--- /dev/null
+++ b/rails_application/db/migrate/20240921091142_add_latest_column_to_product.rb
@@ -0,0 +1,5 @@
+class AddLatestColumnToProduct < ActiveRecord::Migration[7.0]
+ def change
+ add_column :products, :latest, :boolean, default: true
+ end
+end
diff --git a/rails_application/db/migrate/20240925104044_add_checkpoint_to_product.rb b/rails_application/db/migrate/20240925104044_add_checkpoint_to_product.rb
new file mode 100644
index 000000000..0e7da4a46
--- /dev/null
+++ b/rails_application/db/migrate/20240925104044_add_checkpoint_to_product.rb
@@ -0,0 +1,5 @@
+class AddCheckpointToProduct < ActiveRecord::Migration[7.0]
+ def change
+ add_column :products, :checkpoint, :string
+ end
+end
diff --git a/rails_application/db/migrate/20240925110446_create_product_catalogs.rb b/rails_application/db/migrate/20240925110446_create_product_catalogs.rb
new file mode 100644
index 000000000..3ae076766
--- /dev/null
+++ b/rails_application/db/migrate/20240925110446_create_product_catalogs.rb
@@ -0,0 +1,11 @@
+class CreateProductCatalogs < ActiveRecord::Migration[7.0]
+ def change
+ create_table :product_catalogs do |t|
+ t.string :checkpoint
+ t.integer :product_id
+ t.integer :stock_level
+
+ t.timestamps
+ end
+ end
+end
diff --git a/rails_application/db/migrate/20240925121716_drop_stock_level_from_product.rb b/rails_application/db/migrate/20240925121716_drop_stock_level_from_product.rb
new file mode 100644
index 000000000..36074e099
--- /dev/null
+++ b/rails_application/db/migrate/20240925121716_drop_stock_level_from_product.rb
@@ -0,0 +1,5 @@
+class DropStockLevelFromProduct < ActiveRecord::Migration[7.0]
+ def change
+ remove_column :products, :stock_level
+ end
+end
diff --git a/rails_application/db/migrate/20240926062058_create_most_recent_products_in_unfinished_orders.rb b/rails_application/db/migrate/20240926062058_create_most_recent_products_in_unfinished_orders.rb
new file mode 100644
index 000000000..5a00b97e4
--- /dev/null
+++ b/rails_application/db/migrate/20240926062058_create_most_recent_products_in_unfinished_orders.rb
@@ -0,0 +1,13 @@
+class CreateMostRecentProductsInUnfinishedOrders < ActiveRecord::Migration[7.0]
+ def change
+ create_table :most_recent_products_in_unfinished_orders do |t|
+ t.string :product_name, null: false
+ t.integer :product_id, null: false, index: true
+ t.integer :number_of_unfinished_orders, default: 0, null: false
+ t.integer :number_of_items_in_unfinished_orders, default: 0, null: false
+ t.integer :order_ids, array: true, default: [], null: false
+
+ t.timestamps
+ end
+ end
+end
diff --git a/rails_application/db/migrate/20241011103428_add_version_column_to_products_table.rb b/rails_application/db/migrate/20241011103428_add_version_column_to_products_table.rb
new file mode 100644
index 000000000..977e3e080
--- /dev/null
+++ b/rails_application/db/migrate/20241011103428_add_version_column_to_products_table.rb
@@ -0,0 +1,5 @@
+class AddVersionColumnToProductsTable < ActiveRecord::Migration[7.0]
+ def change
+ add_column :products, :version, :integer, default: 0
+ end
+end
diff --git a/rails_application/db/schema.rb b/rails_application/db/schema.rb
index 6a06abb5d..5eac97858 100644
--- a/rails_application/db/schema.rb
+++ b/rails_application/db/schema.rb
@@ -10,74 +10,18 @@
#
# 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_10_11_103428) do
# These are extensions that must be enabled in order to support this database
- enable_extension "pgcrypto"
enable_extension "plpgsql"
- create_table "accounts", force: :cascade do |t|
- t.uuid "client_id"
- t.text "password"
- t.uuid "account_id"
- end
-
- create_table "availability_products", force: :cascade do |t|
- t.uuid "uid"
- t.integer "available"
- end
-
- create_table "client_order_lines", force: :cascade do |t|
- t.string "order_uid"
- t.string "product_name"
- t.integer "product_quantity"
- t.decimal "product_price", precision: 8, scale: 2
- t.uuid "product_id"
- end
-
- create_table "client_order_products", force: :cascade do |t|
- t.uuid "uid", null: false
- t.string "name"
- t.decimal "price", precision: 8, scale: 2
- end
-
- create_table "client_orders", force: :cascade do |t|
- t.uuid "client_uid"
- t.string "number"
- t.uuid "order_uid"
- t.string "state"
- t.datetime "created_at", null: false
- t.datetime "updated_at", null: false
- t.string "client_name"
- t.decimal "percentage_discount", precision: 8, scale: 2
- t.decimal "total_value", precision: 8, scale: 2
- t.decimal "discounted_value", precision: 8, scale: 2
- end
-
- create_table "clients", force: :cascade do |t|
- t.uuid "uid"
- t.string "name"
- t.datetime "created_at", null: false
- t.datetime "updated_at", null: false
+ create_table "customers", force: :cascade do |t|
+ t.string "first_name"
+ t.string "last_name"
+ t.string "email"
t.decimal "paid_orders_summary", precision: 8, scale: 2, default: "0.0"
- end
-
- create_table "coupons", force: :cascade do |t|
- t.uuid "uid", null: false
- t.string "name"
- t.string "code"
- t.decimal "discount"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
- end
-
- create_table "customers", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
- t.string "name"
- t.datetime "created_at", precision: nil, null: false
- t.datetime "updated_at", precision: nil, null: false
- t.datetime "registered_at", precision: nil
- t.boolean "vip", default: false, null: false
- t.decimal "paid_orders_summary", precision: 8, scale: 2, default: "0.0"
- t.uuid "account_id"
+ t.boolean "vip", default: false
end
create_table "event_store_events", id: :serial, force: :cascade do |t|
@@ -88,124 +32,135 @@
t.datetime "created_at", precision: nil, null: false
t.datetime "valid_at", precision: nil
t.index ["created_at"], name: "index_event_store_events_on_created_at"
- t.index ["event_id"], name: "index_event_store_events_on_event_id", unique: true
+ t.index ["event_id"],
+ name: "index_event_store_events_on_event_id",
+ unique: true
t.index ["event_type"], name: "index_event_store_events_on_event_type"
t.index ["valid_at"], name: "index_event_store_events_on_valid_at"
end
- create_table "event_store_events_in_streams", id: :serial, force: :cascade do |t|
+ create_table "event_store_events_in_streams",
+ id: :serial,
+ force: :cascade do |t|
t.string "stream", null: false
t.integer "position"
t.uuid "event_id", null: false
t.datetime "created_at", precision: nil, null: false
- t.index ["created_at"], name: "index_event_store_events_in_streams_on_created_at"
- t.index ["event_id"], name: "index_event_store_events_in_streams_on_event_id"
- t.index ["stream", "event_id"], name: "index_event_store_events_in_streams_on_stream_and_event_id", unique: true
- t.index ["stream", "position"], name: "index_event_store_events_in_streams_on_stream_and_position", unique: true
- end
-
- create_table "invoice_items", force: :cascade do |t|
- t.bigint "invoice_id"
- t.string "name"
- t.decimal "unit_price", precision: 8, scale: 2
- t.decimal "vat_rate", precision: 4, scale: 1
- t.integer "quantity"
- t.decimal "value", precision: 8, scale: 2
- t.index ["invoice_id"], name: "index_invoice_items_on_invoice_id"
- end
-
- create_table "invoices", force: :cascade do |t|
- t.string "order_uid", null: false
- t.string "number"
- t.string "tax_id_number"
- t.string "address_line_1"
- t.string "address_line_2"
- t.string "address_line_3"
- t.string "address_line_4"
- t.boolean "address_present", default: false
- t.boolean "issued", default: false
- t.date "issue_date"
- t.date "disposal_date"
- t.date "payment_date"
- t.decimal "total_value", precision: 8, scale: 2
+ t.index ["created_at"],
+ name: "index_event_store_events_in_streams_on_created_at"
+ t.index ["event_id"],
+ name: "index_event_store_events_in_streams_on_event_id"
+ t.index %w[stream event_id],
+ name: "index_event_store_events_in_streams_on_stream_and_event_id",
+ unique: true
+ t.index %w[stream position],
+ name: "index_event_store_events_in_streams_on_stream_and_position",
+ unique: true
+ end
+
+ create_table "invoices_tbl", force: :cascade do |t|
+ t.bigint "order_id"
+ t.string "order_number"
+ t.decimal "total_value", precision: 10, scale: 2
+ t.string "address"
+ t.datetime "payment_date"
+ t.datetime "issued_at"
+ t.bigint "tax_id_number"
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
end
- create_table "invoices_orders", force: :cascade do |t|
- t.uuid "uid", null: false
- t.boolean "submitted", default: false
+ create_table "most_recent_products_in_unfinished_orders",
+ force: :cascade do |t|
+ t.string "product_name", null: false
+ t.integer "product_id", null: false
+ t.integer "number_of_unfinished_orders", default: 0, null: false
+ t.integer "number_of_items_in_unfinished_orders", default: 0, null: false
+ t.integer "order_ids", default: [], null: false, array: true
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.index ["product_id"],
+ name:
+ "index_most_recent_products_in_unfinished_orders_on_product_id"
end
- create_table "order_lines", force: :cascade do |t|
- t.uuid "order_uid", null: false
- t.string "product_name"
+ create_table "order_items", force: :cascade do |t|
+ t.bigint "order_id", null: false
+ t.bigint "product_id", null: false
t.integer "quantity"
- t.decimal "price", precision: 8, scale: 2
- t.uuid "product_id"
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.index ["order_id"], name: "index_order_items_on_order_id"
+ t.index ["product_id"], name: "index_order_items_on_product_id"
end
create_table "orders", force: :cascade do |t|
- t.uuid "uid", null: false
t.string "number"
- t.string "customer"
- t.string "state"
- t.decimal "percentage_discount", precision: 8, scale: 2
- t.decimal "total_value", precision: 8, scale: 2
- t.decimal "discounted_value", precision: 8, scale: 2
- t.decimal "happy_hour_value", precision: 8, scale: 2
- t.datetime "total_value_updated_at"
+ t.string "address"
+ t.string "phone"
+ t.string "email"
+ t.string "status"
+ t.decimal "total", precision: 10, scale: 2
+ t.decimal "discount", precision: 10, scale: 2
+ t.datetime "completed_at"
t.datetime "discount_updated_at"
- t.index ["uid"], name: "index_orders_on_uid", unique: true
- end
-
- create_table "orders_customers", force: :cascade do |t|
- t.uuid "uid", null: false
- t.string "name"
- end
-
- create_table "orders_products", force: :cascade do |t|
- t.uuid "uid", null: false
- t.string "name"
- t.decimal "price", precision: 8, scale: 2
- end
-
- create_table "products", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
- t.string "name"
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.integer "customer_id"
+ t.string "country"
+ t.string "city"
+ t.string "street"
+ t.string "zip"
+ t.string "addressed_to"
+ t.string "invoice_address"
+ t.string "invoice_tax_id_number"
+ t.boolean "invoice_issued", default: false
+ t.date "invoice_issue_date"
+ t.date "invoice_disposal_date"
+ t.date "invoice_payment_date"
+ t.decimal "invoice_total_value", precision: 8, scale: 2
+ t.string "invoice_country"
+ t.string "invoice_city"
+ t.string "invoice_addressed_to"
+ t.string "invoice_payment_status", default: "Unpaid"
+ t.index ["customer_id"], name: "index_orders_on_customer_id"
+ end
+
+ create_table "product_catalogs", force: :cascade do |t|
+ t.string "checkpoint"
+ t.integer "product_id"
t.integer "stock_level"
- t.datetime "registered_at", precision: nil
- t.string "vat_rate_code"
- t.text "current_prices_calendar"
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
end
- create_table "public_offer_products", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
+ create_table "products", force: :cascade do |t|
t.string "name"
- t.decimal "price"
+ t.decimal "price", precision: 10, scale: 2
+ t.integer "vat_rate"
+ t.boolean "active", default: true
+ t.string "sku"
+ t.string "description"
+ t.string "category"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
- t.decimal "lowest_recent_price", precision: 8, scale: 2
- end
-
- create_table "shipments", force: :cascade do |t|
- t.string "order_uid", null: false
- t.string "address_line_1"
- t.string "address_line_2"
- t.string "address_line_3"
- t.string "address_line_4"
+ t.decimal "future_price", precision: 8, scale: 2
+ t.datetime "future_price_start_time"
+ t.boolean "latest", default: true
+ t.integer "version", default: 0
+ t.string "checkpoint"
end
- create_table "shipments_orders", force: :cascade do |t|
- t.uuid "uid", null: false
- t.boolean "submitted", default: false
- end
-
- create_table "time_promotions", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
- t.string "label"
- t.integer "discount"
+ create_table "time_promotions", force: :cascade do |t|
t.datetime "start_time"
t.datetime "end_time"
+ t.string "label"
+ t.decimal "discount", precision: 5, scale: 2
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
- t.boolean "active", default: false
end
- add_foreign_key "event_store_events_in_streams", "event_store_events", column: "event_id", primary_key: "event_id"
+ add_foreign_key "order_items", "orders"
+ add_foreign_key "order_items", "products"
+ add_foreign_key "orders", "customers"
end
diff --git a/rails_application/db/seeds.rb b/rails_application/db/seeds.rb
index a60141af3..e69de29bb 100644
--- a/rails_application/db/seeds.rb
+++ b/rails_application/db/seeds.rb
@@ -1,54 +0,0 @@
-command_bus = Rails.configuration.command_bus
-
-[
- ["BigCorp Ltd", "bigcorp", "12345"],
- ["MegaTron Gmbh", "megatron", "qwerty"],
- ["Arkency", 'arkency', 'qwe123']
-].each do |name, login, password|
- account_id = SecureRandom.uuid
- customer_id = SecureRandom.uuid
- password_hash = Digest::SHA256.hexdigest(password)
-
- [
- Crm::RegisterCustomer.new(customer_id: customer_id, name: name),
- Authentication::RegisterAccount.new(account_id: account_id),
- Authentication::SetLogin.new(account_id: account_id, login: login),
- Authentication::SetPasswordHash.new(account_id: account_id, password_hash: password_hash),
- Authentication::ConnectAccountToClient.new(account_id: account_id, client_id: customer_id)
- ].each do |command|
- command_bus.call(command)
- end
-
-end
-
-[
- ["DDDVeteran", 'ddd', 5],
- ["VIP", 'vip', 15],
- ["Addict", 'product_addict', 20]
-].each do |coupon|
- command_bus.call(
- Pricing::RegisterCoupon.new(
- coupon_id: SecureRandom.uuid,
- name: coupon[0],
- code: coupon[1],
- discount: coupon[2]
- )
- )
-end
-
-[
- ["Fearless Refactoring: Rails controllers", 49],
- ["Rails meets React.js", 49],
- ["Developers Oriented Project Management", 39],
- ["Blogging for busy programmers", 29]
-].each do |name_price_tuple|
- product_id = SecureRandom.uuid
- [
- 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)
- ].each do |command|
- command_bus.call(command)
- end
-end
diff --git a/rails_application/lib/configuration.rb b/rails_application/lib/configuration.rb
index 078f236c2..ece0a1cd3 100644
--- a/rails_application/lib/configuration.rb
+++ b/rails_application/lib/configuration.rb
@@ -1,30 +1,35 @@
-require_relative "../../ecommerce/configuration"
require_relative "../../infra/lib/infra"
+VatRate = Struct.new(:code, :rate)
+
class Configuration
def call(event_store, command_bus)
enable_res_infra_event_linking(event_store)
- enable_orders_read_model(event_store)
- enable_products_read_model(event_store)
- enable_public_offer_products_read_model(event_store)
- enable_customers_read_model(event_store)
- enable_invoices_read_model(event_store)
- enable_client_orders_read_model(event_store)
- enable_coupons_read_model(event_store)
- enable_time_promotions_read_model(event_store)
- enable_shipments_read_model(event_store)
- enable_availability_read_model(event_store)
- enable_authentication_read_model(event_store)
+ event_store.subscribe(
+ BuildMostRecentProductsInUnfinishedOrders,
+ to: [
+ ProductCatalog::ProductCreated,
+ ProductCatalog::ProductNameChanged,
+ Ordering::OrderExpired,
+ Ordering::ItemAdded,
+ Ordering::OrderPaid,
+ Ordering::OrderSubmitted
+ ]
+ )
+ event_store.subscribe(
+ Inventory::UpdateProductCatalog,
+ to: [Inventory::StockLevelIncreased, Inventory::StockLevelDecreased]
+ )
+
+ event_store.subscribe(
+ SendEmail,
+ to: [Ordering::OrderPaid, Invoicing::InvoiceGenerated]
+ )
+ end
- 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)
- ]
- ).call(event_store, command_bus)
+ def self.available_vat_rates
+ [VatRate.new("23", 23), VatRate.new("10", 10)]
end
private
@@ -36,48 +41,4 @@ def enable_res_infra_event_linking(event_store)
RailsEventStore::LinkByCausationId.new
].each { |h| event_store.subscribe_to_all_events(h) }
end
-
- def enable_products_read_model(event_store)
- Products::Configuration.new(event_store).call
- end
-
- def enable_public_offer_products_read_model(event_store)
- PublicOffer::Configuration.new(event_store).call
- end
-
- def enable_customers_read_model(event_store)
- Customers::Configuration.new.call(event_store)
- end
-
- def enable_orders_read_model(event_store)
- Orders::Configuration.new.call(event_store)
- end
-
- def enable_invoices_read_model(event_store)
- Invoices::Configuration.new.call(event_store)
- end
-
- def enable_client_orders_read_model(event_store)
- ClientOrders::Configuration.new.call(event_store)
- end
-
- def enable_coupons_read_model(event_store)
- Coupons::Configuration.new.call(event_store)
- end
-
- def enable_time_promotions_read_model(event_store)
- TimePromotions::Configuration.new.call(event_store)
- end
-
- def enable_shipments_read_model(event_store)
- Shipments::Configuration.new.call(event_store)
- end
-
- def enable_availability_read_model(event_store)
- Availability::Configuration.new.call(event_store)
- end
-
- def enable_authentication_read_model(event_store)
- ClientAuthentication::Configuration.new.call(event_store)
- end
end
diff --git a/rails_application/script/replay_inventory_aggregate_events_to_feed_product_catalog.rb b/rails_application/script/replay_inventory_aggregate_events_to_feed_product_catalog.rb
new file mode 100644
index 000000000..592c89172
--- /dev/null
+++ b/rails_application/script/replay_inventory_aggregate_events_to_feed_product_catalog.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+def replay_inventory_aggregate_events_to_feed_product_catalog
+ event_store = Rails.configuration.event_store
+ ::Product.find_each do |product|
+ pp "Replaying events for product: #{product.id}"
+
+ event_store.read.stream("Inventory::Product$#{product.id}").each do |event|
+ Inventory::UpdateProductCatalog.new.call(event)
+ end
+ end
+end
+
+replay_inventory_aggregate_events_to_feed_product_catalog
diff --git a/rails_application/script/start_lifecycle_of_product_inventory_aggregate.rb b/rails_application/script/start_lifecycle_of_product_inventory_aggregate.rb
new file mode 100644
index 000000000..3c5e2a8fc
--- /dev/null
+++ b/rails_application/script/start_lifecycle_of_product_inventory_aggregate.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+def start_lifecycle_of_product_inventory_aggregate
+ event_store = Rails.configuration.event_store
+ repository = Infra::AggregateRootRepository.new(event_store)
+
+ p 'Starting lifecycle of product inventory aggregate'
+
+ ::Product.find_each do |product|
+ ApplicationRecord.with_advisory_lock("change_stock_level_for_#{product.id}") do
+ product_stream = event_store
+ .read
+ .stream("Inventory::Product$#{product.id}")
+ .of_type("Inventory::StockLevelMigrated")
+ .to_a
+
+ p "Skipping product: #{product.id}"
+
+ next if product_stream.any?
+
+ repository.with_aggregate(Inventory::Product, product.id) do |aggregate|
+ aggregate.migration_event(product.stock_level)
+ end
+
+ p "Migrated product: #{product.id}"
+
+ end
+ end
+
+ p "Done"
+end
+
+start_lifecycle_of_product_inventory_aggregate
+
diff --git a/rails_application/test/availability/update_availability_test.rb b/rails_application/test/availability/update_availability_test.rb
deleted file mode 100644
index 5bc8f9c99..000000000
--- a/rails_application/test/availability/update_availability_test.rb
+++ /dev/null
@@ -1,47 +0,0 @@
-require "test_helper"
-
-module Availability
- class UpdateAvailabilityTest < InMemoryTestCase
- cover "Availability*"
-
- def test_availability_updates
- product_id = SecureRandom.uuid
- prepare_product(product_id)
-
- event_store.publish(
- Inventory::AvailabilityChanged.new(
- data: {
- product_id: product_id,
- available: 0
- }
- )
- )
-
- refute Availability.approximately_available?(product_id, 1)
-
- event_store.publish(
- Inventory::AvailabilityChanged.new(
- data: {
- product_id: product_id,
- available: 1
- }
- )
- )
- assert Availability.approximately_available?(product_id, 1)
- end
-
- private
-
- def event_store
- Rails.configuration.event_store
- end
-
- def prepare_product(product_id)
- run_command(ProductCatalog::RegisterProduct.new(product_id: product_id))
- run_command(
- ProductCatalog::NameProduct.new(product_id: product_id, name: "test")
- )
- run_command(Pricing::SetPrice.new(product_id: product_id, price: 50))
- end
- end
-end
diff --git a/rails_application/test/client_authentication/create_test.rb b/rails_application/test/client_authentication/create_test.rb
deleted file mode 100644
index ce8023e17..000000000
--- a/rails_application/test/client_authentication/create_test.rb
+++ /dev/null
@@ -1,44 +0,0 @@
-require "test_helper"
-
-module ClientAuthentication
- class CreateTest < InMemoryTestCase
- cover "Authentication*"
-
- def setup
- super
- Account.destroy_all
- end
-
- def test_set_create
- customer_id = SecureRandom.uuid
- product_id = SecureRandom.uuid
- account_id = SecureRandom.uuid
- password = "1234qwer"
- password_hash = Digest::SHA256.hexdigest(password)
-
- register_customer(customer_id)
-
- run_command(
- Authentication::ConnectAccountToClient.new(
- account_id: account_id,
- client_id: customer_id
- )
- )
-
- customer = Account.find_by(client_id: customer_id, account_id: account_id)
- assert customer.present?
- end
-
- private
-
- def register_customer(customer_id)
- run_command(
- Crm::RegisterCustomer.new(customer_id: customer_id, name: "John Doe")
- )
- end
-
- def event_store
- Rails.configuration.event_store
- end
- end
-end
diff --git a/rails_application/test/client_authentication/set_password_test.rb b/rails_application/test/client_authentication/set_password_test.rb
deleted file mode 100644
index 6f048d973..000000000
--- a/rails_application/test/client_authentication/set_password_test.rb
+++ /dev/null
@@ -1,72 +0,0 @@
-require "test_helper"
-
-module ClientAuthentication
- class SetPasswordTest < InMemoryTestCase
- cover "Authentication*"
-
- def setup
- super
- Account.destroy_all
- end
-
- def test_set_password
- customer_id = SecureRandom.uuid
- account_id = SecureRandom.uuid
- password = "1234qwer"
- password_hash = Digest::SHA256.hexdigest(password)
-
- register_customer(customer_id)
- connect_to_account(customer_id, account_id)
-
- run_command(
- Authentication::SetPasswordHash.new(
- account_id: account_id,
- password_hash: password_hash
- )
- )
-
- account = Account.find_by(client_id: customer_id, account_id: account_id)
- assert_equal password_hash, account.password
- end
-
- def test_set_password_then_connect_account
- customer_id = SecureRandom.uuid
- account_id = SecureRandom.uuid
- password = "1234qwer"
- password_hash = Digest::SHA256.hexdigest(password)
-
- register_customer(customer_id)
- run_command(
- Authentication::SetPasswordHash.new(
- account_id: account_id,
- password_hash: password_hash
- )
- )
- connect_to_account(customer_id, account_id)
-
- account = Account.find_by(client_id: customer_id, account_id: account_id)
- assert_equal password_hash, account.password
- end
-
- private
-
- def register_customer(customer_id)
- run_command(
- Crm::RegisterCustomer.new(customer_id: customer_id, name: "John Doe")
- )
- end
-
- def connect_to_account(customer_id, account_id)
- run_command(
- Authentication::ConnectAccountToClient.new(
- account_id: account_id,
- client_id: customer_id
- )
- )
- end
-
- def event_store
- Rails.configuration.event_store
- end
- end
-end
diff --git a/rails_application/test/client_orders/customer_registered_test.rb b/rails_application/test/client_orders/customer_registered_test.rb
deleted file mode 100644
index 8e71a7aa4..000000000
--- a/rails_application/test/client_orders/customer_registered_test.rb
+++ /dev/null
@@ -1,55 +0,0 @@
-require "test_helper"
-
-module ClientOrders
- class CustomerRegisteredTest < InMemoryTestCase
- cover "ClientOrders*"
-
- def setup
- super
- Client.destroy_all
- end
-
- def test_customer_registered
- event_store = Rails.configuration.event_store
-
- customer_id = SecureRandom.uuid
-
- event_store.publish(Crm::CustomerRegistered.new(
- data: {
- customer_id: customer_id,
- name: "John Doe"
- }
- ))
-
- client = Client.find_by(uid: customer_id)
- assert_not_nil(client)
- assert_equal("John Doe", client.name)
- assert_equal(customer_id, client.uid)
- end
-
- def test_customer_assigned_to_order
- event_store = Rails.configuration.event_store
-
- customer_id = SecureRandom.uuid
- order_id = SecureRandom.uuid
-
- event_store.publish(Crm::CustomerRegistered.new(
- data: {
- customer_id: customer_id,
- name: "John Doe"
- }
- ))
-
- event_store.publish(Crm::CustomerAssignedToOrder.new(
- data: {
- customer_id: customer_id,
- order_id: order_id
- }
- ))
-
- order = Order.find_by(order_uid: order_id)
- assert_equal "Draft", order.state
- end
-
- end
-end
\ No newline at end of file
diff --git a/rails_application/test/client_orders/discount_test.rb b/rails_application/test/client_orders/discount_test.rb
deleted file mode 100644
index 6127ed84d..000000000
--- a/rails_application/test/client_orders/discount_test.rb
+++ /dev/null
@@ -1,99 +0,0 @@
-require "test_helper"
-
-module ClientOrders
- class DiscountTest < InMemoryTestCase
- cover "ClientOrders*"
-
- def test_discount_set
- customer_id = SecureRandom.uuid
- product_id = SecureRandom.uuid
- order_id = SecureRandom.uuid
- customer_registered(customer_id)
- prepare_product(product_id)
- item_added_to_basket(order_id, product_id)
-
- set_percentage_discount(order_id)
-
- order = Order.find_by(order_uid: order_id)
- assert_equal 50, order.total_value
- assert_equal 45, order.discounted_value
- assert_equal 10, order.percentage_discount
- end
-
- def test_discount_changed
- customer_id = SecureRandom.uuid
- product_id = SecureRandom.uuid
- order_id = SecureRandom.uuid
- customer_registered(customer_id)
- prepare_product(product_id)
- item_added_to_basket(order_id, product_id)
- set_percentage_discount(order_id)
-
- change_percentage_discount(order_id)
-
- order = Order.find_by(order_uid: order_id)
- assert_equal 50, order.total_value
- assert_equal 49.5, order.discounted_value
- assert_equal 1, order.percentage_discount
- end
-
- def test_reset_discount
- customer_id = SecureRandom.uuid
- product_id = SecureRandom.uuid
- order_id = SecureRandom.uuid
- customer_registered(customer_id)
- prepare_product(product_id)
- item_added_to_basket(order_id, product_id)
- set_percentage_discount(order_id)
-
- reset_percentage_discount(order_id)
-
- order = Order.find_by(order_uid: order_id)
- assert_equal(50, order.total_value)
- assert_equal(50, order.discounted_value)
- assert_nil(order.percentage_discount)
- end
-
- private
-
- def reset_percentage_discount(order_id)
- run_command(Pricing::ResetPercentageDiscount.new(order_id: order_id))
- end
-
- def set_percentage_discount(order_id)
- run_command(Pricing::SetPercentageDiscount.new(order_id: order_id, amount: 10))
- end
-
- def change_percentage_discount(order_id)
- run_command(Pricing::ChangePercentageDiscount.new(order_id: order_id, amount: 1))
- end
-
- def item_added_to_basket(order_id, product_id)
- event_store.publish(Ordering::ItemAddedToBasket.new(data: { product_id: product_id, order_id: order_id, quantity_before: 0 }))
- end
-
- def prepare_product(product_id)
- run_command(
- ProductCatalog::RegisterProduct.new(
- product_id: product_id,
- )
- )
- run_command(
- ProductCatalog::NameProduct.new(
- product_id: product_id,
- name: "test"
- )
- )
- run_command(Pricing::SetPrice.new(product_id: product_id, price: 50))
- end
-
- def customer_registered(customer_id)
- event_store.publish(Crm::CustomerRegistered.new(data: { customer_id: customer_id, name: "Arkency" }))
- end
-
- def event_store
- Rails.configuration.event_store
- end
- end
-end
-
diff --git a/rails_application/test/client_orders/item_added_to_basket_test.rb b/rails_application/test/client_orders/item_added_to_basket_test.rb
deleted file mode 100644
index 9944ea3b5..000000000
--- a/rails_application/test/client_orders/item_added_to_basket_test.rb
+++ /dev/null
@@ -1,186 +0,0 @@
-require "test_helper"
-
-module ClientOrders
- class ItemAddedToBasketTest < InMemoryTestCase
- cover "ClientOrders*"
-
- def setup
- super
- Order.destroy_all
- OrderLine.destroy_all
- end
-
- def test_add_new_item
- event_store = Rails.configuration.event_store
-
- product_id = SecureRandom.uuid
- run_command(
- ProductCatalog::RegisterProduct.new(
- product_id: product_id,
- )
- )
- run_command(
- ProductCatalog::NameProduct.new(
- product_id: product_id,
- name: "test"
- )
- )
- run_command(Pricing::SetPrice.new(product_id: product_id, price: 49))
-
- order_id = SecureRandom.uuid
-
- event_store.publish(
- Ordering::ItemAddedToBasket.new(
- data: {
- order_id: order_id,
- product_id: product_id,
- quantity_before: 0
- }
- )
- )
-
- assert_equal(OrderLine.count, 1)
- order_line = OrderLine.find_by(order_uid: order_id)
- assert_equal(order_line.product_id, product_id)
- assert_equal(order_line.product_name, "test")
- assert_equal(order_line.product_quantity, 1)
- assert_equal(order_line.product_price, 49)
- assert_equal(order_line.value, 49)
-
- assert_equal(Order.count, 1)
- order = Order.find_by(order_uid: order_id)
- assert_equal(order.state, "Draft")
- assert_nil(order.client_uid)
- assert_nil(order.number)
- end
-
- def test_add_the_same_item_2nd_time
- event_store = Rails.configuration.event_store
-
- product_id = SecureRandom.uuid
- run_command(
- ProductCatalog::RegisterProduct.new(
- product_id: product_id,
- )
- )
- run_command(
- ProductCatalog::NameProduct.new(
- product_id: product_id,
- name: "test"
- )
- )
- run_command(Pricing::SetPrice.new(product_id: product_id, price: 49))
-
- order_id = SecureRandom.uuid
- event_store.publish(
- Ordering::ItemAddedToBasket.new(
- data: {
- order_id: order_id,
- product_id: product_id,
- quantity_before: 0
- }
- )
- )
-
- event_store.publish(
- Ordering::ItemAddedToBasket.new(
- data: {
- order_id: order_id,
- product_id: product_id,
- quantity_before: 1
- }
- )
- )
-
- assert_equal(OrderLine.count, 1)
- order_line = OrderLine.find_by(order_uid: order_id)
- assert_equal(order_line.product_id, product_id)
- assert_equal(order_line.product_name, "test")
- assert_equal(order_line.product_quantity, 2)
- assert_equal(order_line.product_price, 49)
- assert_equal(order_line.value, 98)
-
- assert_equal(Order.count, 1)
- order = Order.find_by(order_uid: order_id)
- assert_equal(order.state, "Draft")
- assert_nil(order.client_uid)
- assert_nil(order.number)
- end
-
- def test_add_another_item
- event_store = Rails.configuration.event_store
-
- product_id = SecureRandom.uuid
- run_command(
- ProductCatalog::RegisterProduct.new(
- product_id: product_id,
- )
- )
- run_command(
- ProductCatalog::NameProduct.new(
- product_id: product_id,
- name: "test"
- )
- )
- run_command(Pricing::SetPrice.new(product_id: product_id, price: 20))
-
- another_product_id = SecureRandom.uuid
- run_command(
- ProductCatalog::RegisterProduct.new(
- product_id: another_product_id,
- )
- )
- run_command(
- ProductCatalog::NameProduct.new(
- product_id: another_product_id,
- name: "2nd one"
- )
- )
- run_command(
- Pricing::SetPrice.new(product_id: another_product_id, price: 20)
- )
-
- order_id = SecureRandom.uuid
- event_store.publish(
- Ordering::ItemAddedToBasket.new(
- data: {
- order_id: order_id,
- product_id: product_id,
- quantity_before: 0
- }
- )
- )
-
- event_store.publish(
- Ordering::ItemAddedToBasket.new(
- data: {
- order_id: order_id,
- product_id: another_product_id,
- quantity_before: 0
- }
- )
- )
-
- order = ClientOrders::Order.find_by(order_uid: order_id)
- assert_equal(order.order_lines.count, 2)
- order_lines = order.order_lines
- assert_equal(
- [product_id, another_product_id],
- order_lines.map(&:product_id)
- )
- assert_equal(order_lines[0].product_id, product_id)
- assert_equal(order_lines[0].product_name, "test")
- assert_equal(order_lines[0].product_quantity, 1)
-
- assert_equal(order_lines[1].product_id, another_product_id)
- assert_equal(order_lines[1].product_name, "2nd one")
- assert_equal(order_lines[1].product_quantity, 1)
-
- assert_equal(Order.count, 1)
- order = Order.find_by(order_uid: order_id)
- assert_equal(order.state, "Draft")
- assert_nil(order.client_uid)
- assert_nil(order.number)
- end
- end
-end
diff --git a/rails_application/test/client_orders/item_removed_from_basket_test.rb b/rails_application/test/client_orders/item_removed_from_basket_test.rb
deleted file mode 100644
index 2b932c37c..000000000
--- a/rails_application/test/client_orders/item_removed_from_basket_test.rb
+++ /dev/null
@@ -1,185 +0,0 @@
-require "test_helper"
-
-module ClientOrders
- class ItemRemovedFromBasketTest < InMemoryTestCase
- cover "ClientOrders*"
-
- def setup
- super
- Order.destroy_all
- OrderLine.destroy_all
- Product.destroy_all
- end
-
- def test_remove_item_when_quantity_gt_1
- event_store = Rails.configuration.event_store
-
- product_id = SecureRandom.uuid
- run_command(
- ProductCatalog::RegisterProduct.new(
- product_id: product_id
- )
- )
- run_command(
- ProductCatalog::NameProduct.new(
- product_id: product_id,
- name: "something"
- )
- )
- run_command(Pricing::SetPrice.new(product_id: product_id, price: 20))
- customer_id = SecureRandom.uuid
- run_command(
- Crm::RegisterCustomer.new(customer_id: customer_id, name: "dummy")
- )
- order_id = SecureRandom.uuid
- event_store.publish(
- Ordering::ItemAddedToBasket.new(
- data: {
- order_id: order_id,
- product_id: product_id,
- quantity_before: 0
- }
- )
- )
- event_store.publish(
- Ordering::ItemAddedToBasket.new(
- data: {
- order_id: order_id,
- product_id: product_id,
- quantity_before: 1
- }
- )
- )
- event_store.publish(
- Ordering::ItemRemovedFromBasket.new(
- data: {
- order_id: order_id,
- product_id: product_id
- }
- )
- )
-
- assert_equal(1, OrderLine.count)
- order_line = OrderLine.find_by(order_uid: order_id)
- assert_equal(order_line.product_id, product_id)
- assert_equal("something", order_line.product_name)
- assert_equal(1, order_line.product_quantity)
- end
-
- def test_remove_item_when_quantity_eq_1
- event_store = Rails.configuration.event_store
-
- product_id = SecureRandom.uuid
- run_command(
- ProductCatalog::RegisterProduct.new(
- product_id: product_id
- )
- )
- run_command(Pricing::SetPrice.new(product_id: product_id, price: 20))
- customer_id = SecureRandom.uuid
- run_command(
- Crm::RegisterCustomer.new(customer_id: customer_id, name: "dummy")
- )
- order_id = SecureRandom.uuid
- event_store.publish(
- Ordering::ItemAddedToBasket.new(
- data: {
- order_id: order_id,
- product_id: product_id,
- quantity_before: 0
- }
- )
- )
- event_store.publish(
- Ordering::ItemRemovedFromBasket.new(
- data: {
- order_id: order_id,
- product_id: product_id
- }
- )
- )
-
- assert_equal(0, OrderLine.count)
- end
-
- def test_remove_item_when_there_is_another_item
- event_store = Rails.configuration.event_store
-
- product_id = SecureRandom.uuid
- run_command(
- ProductCatalog::RegisterProduct.new(
- product_id: product_id
- )
- )
- run_command(
- ProductCatalog::NameProduct.new(
- product_id: product_id,
- name: "test"
- )
- )
- run_command(Pricing::SetPrice.new(product_id: product_id, price: 20))
-
- another_product_id = SecureRandom.uuid
- run_command(
- ProductCatalog::RegisterProduct.new(
- product_id: another_product_id
- )
- )
- run_command(
- ProductCatalog::NameProduct.new(
- product_id: another_product_id,
- name: "test2"
- )
- )
- run_command(
- Pricing::SetPrice.new(product_id: another_product_id, price: 20)
- )
- customer_id = SecureRandom.uuid
- run_command(
- Crm::RegisterCustomer.new(customer_id: customer_id, name: "dummy")
- )
- order_id = SecureRandom.uuid
- event_store.publish(
- Ordering::ItemAddedToBasket.new(
- data: {
- order_id: order_id,
- product_id: product_id,
- quantity_before: 0
- }
- )
- )
- event_store.publish(
- Ordering::ItemAddedToBasket.new(
- data: {
- order_id: order_id,
- product_id: product_id,
- quantity_before: 1
- }
- )
- )
- event_store.publish(
- Ordering::ItemAddedToBasket.new(
- data: {
- order_id: order_id,
- product_id: another_product_id,
- quantity_before: 0
- }
- )
- )
- event_store.publish(
- Ordering::ItemRemovedFromBasket.new(
- data: {
- order_id: order_id,
- product_id: another_product_id
- }
- )
- )
-
- assert_equal(1, OrderLine.count,)
- order_lines = OrderLine.where(order_uid: order_id)
- assert_equal(product_id, order_lines[0].product_id)
- assert_equal("test", order_lines[0].product_name)
- assert_equal(2, order_lines[0].product_quantity)
- end
- end
-end
diff --git a/rails_application/test/client_orders/order_cancelled_test.rb b/rails_application/test/client_orders/order_cancelled_test.rb
deleted file mode 100644
index 70e474fc4..000000000
--- a/rails_application/test/client_orders/order_cancelled_test.rb
+++ /dev/null
@@ -1,43 +0,0 @@
-require "test_helper"
-
-module ClientOrders
- class OrderCancelledTest < InMemoryTestCase
- cover "ClientOrders*"
-
- def setup
- super
- Client.destroy_all
- Order.destroy_all
- end
-
- def test_order_confirmed
- event_store = Rails.configuration.event_store
- customer_id = SecureRandom.uuid
- order_id = SecureRandom.uuid
- order_number = Ordering::FakeNumberGenerator::FAKE_NUMBER
-
- event_store.publish(Crm::CustomerRegistered.new(
- data: {
- customer_id: customer_id,
- name: "John Doe"
- }
- ))
-
- run_command(Ordering::SubmitOrder.new(order_id: order_id, order_number: order_number))
-
- event_store.publish(
- Fulfillment::OrderCancelled.new(
- data: {
- order_id: order_id
- }
- )
- )
-
- orders = Order.all
- assert_not_empty(orders)
- assert_equal(1, orders.count)
- assert_equal(order_number, orders.first.number)
- assert_equal("Cancelled", orders.first.state)
- end
- end
-end
\ No newline at end of file
diff --git a/rails_application/test/client_orders/order_expired_test.rb b/rails_application/test/client_orders/order_expired_test.rb
deleted file mode 100644
index 9e1eb4b35..000000000
--- a/rails_application/test/client_orders/order_expired_test.rb
+++ /dev/null
@@ -1,52 +0,0 @@
-require "test_helper"
-
-module ClientOrders
- class OrderExpiredTest < InMemoryTestCase
- cover "ClientOrders*"
-
- def setup
- super
- Client.destroy_all
- Order.destroy_all
- end
-
- def test_order_expired
- event_store = Rails.configuration.event_store
- customer_id = SecureRandom.uuid
- order_id = SecureRandom.uuid
- order_number = Ordering::FakeNumberGenerator::FAKE_NUMBER
-
- event_store.publish(Crm::CustomerRegistered.new(
- data: {
- customer_id: customer_id,
- name: "John Doe"
- }
- ))
-
- event_store.publish(
- Ordering::OrderPlaced.new(
- data: {
- order_id: order_id,
- order_number: order_number,
- customer_id: customer_id,
- order_lines: { }
- }
- )
- )
-
- event_store.publish(
- Ordering::OrderExpired.new(
- data: {
- order_id: order_id
- }
- )
- )
-
- orders = Order.all
- assert_not_empty(orders)
- assert_equal(1, orders.count)
- assert_equal(order_number, orders.first.number)
- assert_equal("Expired", orders.first.state)
- end
- end
-end
\ No newline at end of file
diff --git a/rails_application/test/client_orders/order_line_test.rb b/rails_application/test/client_orders/order_line_test.rb
deleted file mode 100644
index 5f9e3eeff..000000000
--- a/rails_application/test/client_orders/order_line_test.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-require "test_helper"
-
-module ClientOrders
- class OrderLineTest < InMemoryTestCase
- cover "ClientOrders*"
-
- def test_calculates_value_correctly
- order_line = OrderLine.new(product_price: 10, product_quantity: 1)
- assert_equal 10, order_line.value
-
- order_line = OrderLine.new(product_price: 10, product_quantity: 0)
- assert_equal 0, order_line.value
-
- order_line = OrderLine.new(product_price: 10, product_quantity: 10)
- assert_equal 100, order_line.value
- end
- end
-end
\ No newline at end of file
diff --git a/rails_application/test/client_orders/order_paid_test.rb b/rails_application/test/client_orders/order_paid_test.rb
deleted file mode 100644
index cb126741b..000000000
--- a/rails_application/test/client_orders/order_paid_test.rb
+++ /dev/null
@@ -1,52 +0,0 @@
-require "test_helper"
-
-module ClientOrders
- class OrderConfirmedTest < InMemoryTestCase
- cover "ClientOrders*"
-
- def setup
- super
- Client.destroy_all
- Order.destroy_all
- end
-
- def test_order_confirmed
- event_store = Rails.configuration.event_store
- customer_id = SecureRandom.uuid
- order_id = SecureRandom.uuid
- order_number = Ordering::FakeNumberGenerator::FAKE_NUMBER
-
- run_command(Crm::RegisterCustomer.new(customer_id: customer_id, name: "John Doe"))
-
- event_store.publish(
- Pricing::OrderTotalValueCalculated.new(
- data: {
- order_id: order_id,
- discounted_amount: 30,
- total_amount: 30
- }
- )
- )
-
- run_command(Ordering::SubmitOrder.new(order_id: order_id, order_number: order_number))
-
- run_command(
- Crm::AssignCustomerToOrder.new(customer_id: customer_id, order_id: order_id)
- )
-
- event_store.publish(
- Fulfillment::OrderConfirmed.new(
- data: {
- order_id: order_id
- }
- )
- )
-
- orders = Order.all
- assert_not_empty(orders)
- assert_equal(1, orders.count)
- assert_equal(order_number, orders.first.number)
- assert_equal("Paid", orders.first.state)
- end
- end
-end
diff --git a/rails_application/test/client_orders/order_placed_test.rb b/rails_application/test/client_orders/order_placed_test.rb
deleted file mode 100644
index 9252c5c24..000000000
--- a/rails_application/test/client_orders/order_placed_test.rb
+++ /dev/null
@@ -1,97 +0,0 @@
-require "test_helper"
-
-module ClientOrders
- class OrderPlacedTest < InMemoryTestCase
- cover "ClientOrders*"
-
- def setup
- super
- Client.destroy_all
- Order.destroy_all
- end
-
- def test_order_placed
- event_store = Rails.configuration.event_store
- customer_id = SecureRandom.uuid
- order_id = SecureRandom.uuid
- order_number = Ordering::FakeNumberGenerator::FAKE_NUMBER
-
- event_store.publish(Crm::CustomerRegistered.new(
- data: {
- customer_id: customer_id,
- name: "John Doe"
- }
- ))
-
- event_store.publish(
- Ordering::OrderPlaced.new(
- data: {
- order_id: order_id,
- order_number: order_number,
- order_lines: { }
- }
- )
- )
- event_store.publish(
- Crm::CustomerAssignedToOrder.new(
- data: {
- order_id: order_id,
- customer_id: customer_id,
- }
- )
- )
-
- orders = Order.all
- assert_not_empty(orders)
- assert_equal(1, orders.count)
- assert_equal(order_number, orders.first.number)
- assert_equal("Submitted", orders.first.state)
-
- assert_clickable_order_number(customer_id, order_id, order_number)
- end
-
- def test_assign_customer_first
- event_store = Rails.configuration.event_store
- customer_id = SecureRandom.uuid
- order_id = SecureRandom.uuid
- order_number = Ordering::FakeNumberGenerator::FAKE_NUMBER
-
- event_store.publish(
- Crm::CustomerRegistered.new(data: {customer_id: customer_id, name: "John Doe"})
- )
-
- event_store.publish(Crm::CustomerAssignedToOrder.new(
- data: {
- customer_id: customer_id,
- order_id: order_id
- }
- ))
-
- event_store.publish(
- Ordering::OrderPlaced.new(
- data: {
- order_id: order_id,
- order_number: order_number,
- order_lines: { }
- }
- )
- )
- orders = Order.all
- assert_not_empty(orders)
- assert_equal(1, orders.count)
- assert_equal(order_number, orders.first.number)
- assert_equal("Submitted", orders.first.state)
- end
-
- private
-
- def assert_clickable_order_number(customer_id, order_id, order_number)
- view_context = OrdersController.new.view_context
- orders_list = ClientOrders::OrdersList.build(view_context, customer_id)
- links_to_orders = Nokogiri::HTML(orders_list).xpath('//table').xpath(".//a")
- assert_equal(1, links_to_orders.size)
- assert_equal("/client_orders/#{order_id}", links_to_orders.first.attributes["href"].value)
- assert_equal(order_number, links_to_orders.first.text)
- end
- end
-end
\ No newline at end of file
diff --git a/rails_application/test/client_orders/product_price_changed_test.rb b/rails_application/test/client_orders/product_price_changed_test.rb
deleted file mode 100644
index 32a6760f5..000000000
--- a/rails_application/test/client_orders/product_price_changed_test.rb
+++ /dev/null
@@ -1,41 +0,0 @@
-require "test_helper"
-
-module ClientOrders
- class ProductPriceChangedTest < InMemoryTestCase
- cover "ClientOrders*"
-
- def test_reflects_change
- product_id = prepare_product
- unchanged_product_id = prepare_product
-
- set_price(product_id, 100)
-
- assert_equal 100, Product.find_by_uid(product_id).price
- assert_equal 50, Product.find_by_uid(unchanged_product_id).price
- end
-
- private
-
- def prepare_product
- product_id = SecureRandom.uuid
- run_command(
- ProductCatalog::RegisterProduct.new(
- product_id: product_id,
- )
- )
- run_command(
- ProductCatalog::NameProduct.new(
- product_id: product_id,
- name: "test"
- )
- )
- run_command(Pricing::SetPrice.new(product_id: product_id, price: 50))
-
- product_id
- end
-
- def set_price(product_id, amount)
- run_command(Pricing::SetPrice.new(product_id: product_id, price: amount))
- end
- end
-end
diff --git a/rails_application/test/client_orders/product_registered_test.rb b/rails_application/test/client_orders/product_registered_test.rb
deleted file mode 100644
index e9edda142..000000000
--- a/rails_application/test/client_orders/product_registered_test.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-require "test_helper"
-
-module ClientOrders
- class ProductRegisteredTest < InMemoryTestCase
- cover "ClientOrders*"
-
- def test_reflects_change
- product_id = SecureRandom.uuid
-
- run_command(ProductCatalog::RegisterProduct.new(product_id: product_id))
-
- assert_equal(product_id, Product.find_by_uid(product_id).uid)
- end
-
- end
-end
diff --git a/rails_application/test/client_orders/update_order_total_value_test.rb b/rails_application/test/client_orders/update_order_total_value_test.rb
deleted file mode 100644
index da0bc7b49..000000000
--- a/rails_application/test/client_orders/update_order_total_value_test.rb
+++ /dev/null
@@ -1,50 +0,0 @@
-require "test_helper"
-
-module ClientOrders
- class UpdateOrderTotalValueTest < InMemoryTestCase
- cover "ClientOrders*"
-
- def test_order_created_has_draft_state
- customer_id = SecureRandom.uuid
- product_id = SecureRandom.uuid
- order_id = SecureRandom.uuid
- customer_registered(customer_id)
- prepare_product(product_id)
-
- event_store.publish(Pricing::OrderTotalValueCalculated.new(data: { order_id: order_id, discounted_amount: 0, total_amount: 10 }))
-
- order = ClientOrders::Order.find_by(order_uid: order_id)
- assert_equal "Draft", order.state
- end
-
- private
-
- def item_added_to_basket(order_id, product_id)
- event_store.publish(Ordering::ItemAddedToBasket.new(data: { product_id: product_id, order_id: order_id, quantity_before: 0 }))
- end
-
- def prepare_product(product_id)
- run_command(
- ProductCatalog::RegisterProduct.new(
- product_id: product_id,
- )
- )
- run_command(
- ProductCatalog::NameProduct.new(
- product_id: product_id,
- name: "test"
- )
- )
- run_command(Pricing::SetPrice.new(product_id: product_id, price: 50))
- end
-
- def customer_registered(customer_id)
- event_store.publish(Crm::CustomerRegistered.new(data: { customer_id: customer_id, name: "Arkency" }))
- end
-
- def event_store
- Rails.configuration.event_store
- end
- end
-end
-
diff --git a/rails_application/test/client_orders/update_paid_orders_summary_test.rb b/rails_application/test/client_orders/update_paid_orders_summary_test.rb
deleted file mode 100644
index 6090e1c80..000000000
--- a/rails_application/test/client_orders/update_paid_orders_summary_test.rb
+++ /dev/null
@@ -1,67 +0,0 @@
-require "test_helper"
-
-module ClientOrders
- class UpdatePaidOrdersSummaryTest < InMemoryTestCase
- cover "ClientOrders*"
-
- def setup
- super
- Client.destroy_all
- end
-
- def test_update_orders_cummary
- event_store = Rails.configuration.event_store
- customer_id = SecureRandom.uuid
- other_customer_id = SecureRandom.uuid
- product_id = SecureRandom.uuid
- order_id = SecureRandom.uuid
-
- register_customer(other_customer_id)
- register_customer(customer_id)
- confirm_order(customer_id, order_id, 3)
-
- customer = Client.find_by(uid: customer_id)
- assert_equal 3.to_d, customer.paid_orders_summary
-
- order_id = SecureRandom.uuid
- confirm_order(customer_id, order_id, 6)
-
- customer = Client.find_by(uid: customer_id)
- assert_equal 9.to_d, customer.paid_orders_summary
- end
-
- private
-
- def register_customer(customer_id)
- run_command(Crm::RegisterCustomer.new(customer_id: customer_id, name: "John Doe"))
- end
-
- def confirm_order(customer_id, order_id, total_amount)
- order_number = Ordering::FakeNumberGenerator::FAKE_NUMBER
- event_store.publish(
- Pricing::OrderTotalValueCalculated.new(
- data: {
- order_id: order_id,
- discounted_amount: total_amount,
- total_amount: total_amount
- }
- )
- )
- run_command(
- Crm::AssignCustomerToOrder.new(customer_id: customer_id, order_id: order_id)
- )
- run_command(Ordering::SubmitOrder.new(order_id: order_id, order_number: order_number))
- event_store.publish(
- order_confirmed = Fulfillment::OrderConfirmed.new(
- data: {
- order_id: order_id
- }
- )
- )
- end
-
- def event_store
- Rails.configuration.event_store
- end
- end
-end
diff --git a/rails_application/test/customers/connect_account_test.rb b/rails_application/test/customers/connect_account_test.rb
deleted file mode 100644
index 1bbac4a6f..000000000
--- a/rails_application/test/customers/connect_account_test.rb
+++ /dev/null
@@ -1,49 +0,0 @@
-require "test_helper"
-
-module Customers
- class ConnectAccountTest < InMemoryTestCase
- cover "Customers"
-
- def setup
- super
- Customer.destroy_all
- end
-
- def test_first_register_then_connect
- customer_id = SecureRandom.uuid
- account_id = SecureRandom.uuid
-
- register_customer(customer_id)
- run_command(
- Authentication::ConnectAccountToClient.new(account_id: account_id, client_id: customer_id)
- )
-
- customer = Customer.find(customer_id)
- assert_equal account_id, customer.account_id
- end
-
- def test_first_connect_then_register
- customer_id = SecureRandom.uuid
- account_id = SecureRandom.uuid
-
- run_command(
- Authentication::ConnectAccountToClient.new(account_id: account_id, client_id: customer_id)
- )
- register_customer(customer_id)
-
- customer = Customer.find(customer_id)
- assert_equal account_id, customer.account_id
- end
-
-
- private
-
- def register_customer(customer_id)
- run_command(Crm::RegisterCustomer.new(customer_id: customer_id, name: "John Doe"))
- end
-
- def event_store
- Rails.configuration.event_store
- end
- end
-end
diff --git a/rails_application/test/customers/customer_promoted_to_vip_test.rb b/rails_application/test/customers/customer_promoted_to_vip_test.rb
deleted file mode 100644
index 91785cbe5..000000000
--- a/rails_application/test/customers/customer_promoted_to_vip_test.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-require "test_helper"
-
-module Customers
- class CustomerPromotedToVipTest < InMemoryTestCase
- cover "Customers"
-
- def setup
- super
- Customer.destroy_all
- end
-
- def test_promote_customer_to_vip
- event_store = Rails.configuration.event_store
-
- customer_id = SecureRandom.uuid
- run_command(
- Crm::RegisterCustomer.new(
- customer_id: customer_id,
- name: "Joe Fake"
- )
- )
-
- event_store.publish(
- Crm::CustomerPromotedToVip.new(
- data: {
- customer_id: customer_id
- }
- )
- )
-
- customer = Customer.find_by(id: customer_id)
- assert_equal(customer.vip, true)
- end
- end
-end
diff --git a/rails_application/test/customers/update_paid_orders_summary_test.rb b/rails_application/test/customers/update_paid_orders_summary_test.rb
deleted file mode 100644
index cf1db6f43..000000000
--- a/rails_application/test/customers/update_paid_orders_summary_test.rb
+++ /dev/null
@@ -1,65 +0,0 @@
-require "test_helper"
-
-module Customers
- class UpdatePaidOrdersSummaryTest < InMemoryTestCase
- cover "Customers"
-
- def setup
- super
- Customer.destroy_all
- end
-
- def test_update_orders_summary
- customer_id = SecureRandom.uuid
- other_customer_id = SecureRandom.uuid
- order_id = SecureRandom.uuid
-
- register_customer(other_customer_id)
- register_customer(customer_id)
- confirm_order(customer_id, order_id, 3)
-
- customer = Customer.find(customer_id)
- assert_equal 3.to_d, customer.paid_orders_summary
-
- order_id = SecureRandom.uuid
- confirm_order(customer_id, order_id, 6)
-
- customer = Customer.find(customer_id)
- assert_equal 9.to_d, customer.paid_orders_summary
- end
-
- private
-
- def register_customer(customer_id)
- run_command(Crm::RegisterCustomer.new(customer_id: customer_id, name: "John Doe"))
- end
-
- def confirm_order(customer_id, order_id, total_amount)
- order_number = Ordering::FakeNumberGenerator::FAKE_NUMBER
- event_store.publish(
- Pricing::OrderTotalValueCalculated.new(
- data: {
- order_id: order_id,
- discounted_amount: total_amount,
- total_amount: total_amount
- }
- )
- )
- run_command(
- Crm::AssignCustomerToOrder.new(customer_id: customer_id, order_id: order_id)
- )
- run_command(Ordering::SubmitOrder.new(order_id: order_id, order_number: order_number))
- event_store.publish(
- order_confirmed = Fulfillment::OrderConfirmed.new(
- data: {
- order_id: order_id
- }
- )
- )
- end
-
- def event_store
- Rails.configuration.event_store
- end
- end
-end
diff --git a/rails_application/test/fake_email_client.rb b/rails_application/test/fake_email_client.rb
new file mode 100644
index 000000000..cac7a5638
--- /dev/null
+++ b/rails_application/test/fake_email_client.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class FakeEmailClient
+ def initialize
+ @sent_emails = []
+ end
+
+ def send_email(order_id)
+ @sent_emails << order_id
+ end
+
+ def sent_emails
+ @sent_emails
+ end
+end
diff --git a/rails_application/test/integration/client_orders_test.rb b/rails_application/test/integration/client_orders_test.rb
deleted file mode 100644
index d2ea08c1c..000000000
--- a/rails_application/test/integration/client_orders_test.rb
+++ /dev/null
@@ -1,216 +0,0 @@
-require_relative "../test_helper"
-
-class ClientOrdersTests < InMemoryRESIntegrationTestCase
- include ActionView::Helpers::NumberHelper
-
- def setup
- super
- Rails.configuration.payment_gateway.call.reset
- ClientOrders::Client.destroy_all
- ClientOrders::Order.destroy_all
- Orders::Order.destroy_all
- end
-
- def test_happy_path
- arkency_id = register_customer("Arkency")
- async_remote_id = register_product("Async Remote", 39, 10)
- fearless_id = register_product("Fearless Refactoring", 49, 10)
-
- get "/clients"
- assert_select("button", { count: 0, text: "Log out" })
- assert_select("button", "Login")
- assert_select("select", "Arkency")
-
- login(arkency_id)
- assert_select("h1", "Arkency")
- assert_select("p", "No orders to display.")
-
- order_id = SecureRandom.uuid
- add_item_to_basket_for_order(async_remote_id, order_id)
- get "/client_orders/#{order_id}/edit"
- assert_match(
- /#{Regexp.escape(remove_item_client_order_path(id: order_id, product_id: async_remote_id))}/,
- response.body
- )
- assert_no_match(
- /#{Regexp.escape(remove_item_client_order_path(id: order_id, product_id: fearless_id))}/,
- response.body
- )
-
- submit_order_for_customer(arkency_id, order_id)
- get "/client_orders"
- order_price =
- number_to_currency(Orders::Order.find_by(uid: order_id).discounted_value)
-
- assert_select("td", "Submitted")
- assert_select("td", order_price)
-
- pay_order(order_id)
- get "/client_orders"
- assert_select("td", "Paid")
-
- cancel_submitted_order_for_customer(arkency_id)
- get "/client_orders"
- assert_select("td", "Cancelled")
-
- assert_select("button", "Log out")
- get "/logout"
- follow_redirect!
- assert_select("button", "Login")
- assert_select("select", "Arkency")
- end
-
- def test_creating_order_as_client
- arkency_id = register_customer("Arkency")
- async_remote_id = register_product("Async Remote", 39, 10)
-
- get "/clients"
- login(arkency_id)
- order_id = SecureRandom.uuid
- get "/client_orders/new"
- as_client_add_item_to_basket_for_order(async_remote_id, order_id)
- as_client_submit_order_for_customer(order_id)
- get "/client_orders"
- order_price =
- number_to_currency(Orders::Order.find_by(uid: order_id).discounted_value)
- assert_select("td", "Submitted")
- assert_select("td", order_price)
- end
-
- def test_paid_orders_summary
- customer_id = register_customer("Customer Shop")
- product_1_id = register_product("Fearless Refactoring", 4, 10)
- product_2_id = register_product("Asycn Remote", 3, 10)
-
- login(customer_id)
- visit_client_orders
- assert_select "Total paid orders", false
-
- order_and_pay(customer_id, SecureRandom.uuid, product_1_id, product_2_id)
- visit_client_orders
-
- assert_orders_summary("$7.00")
-
- order_and_pay(customer_id, SecureRandom.uuid, product_1_id)
- visit_client_orders
-
- assert_orders_summary("$11.00")
- end
-
- def test_adding_the_same_product_twice_bug
- customer_id = register_customer("Customer Shop")
- product_id = register_product("Fearless Refactoring", 4, 10)
-
- login(customer_id)
- visit_client_orders
-
- order_id = SecureRandom.uuid
- as_client_add_item_to_basket_for_order(product_id, order_id)
- as_client_add_item_to_basket_for_order(product_id, order_id)
- end
-
- def test_adding_product_which_is_not_available_anymore
- customer_1_id = register_customer("Arkency")
- customer_2_id = register_customer("Customer Shop")
- product_id = register_product("Fearless Refactoring", 4, 10)
-
- supply_product(product_id, 1)
- session_1 = login_as(customer_1_id)
- session_2 = login_as(customer_2_id)
-
- session_1.get "/client_orders"
- session_2.get "/client_orders"
-
- order_1_id = SecureRandom.uuid
- session_1.post "/client_orders/#{order_1_id}/add_item?product_id=#{product_id}"
- order_and_pay(customer_1_id, order_1_id, product_id)
-
- order_2_id = SecureRandom.uuid
- session_2.post "/client_orders/#{order_2_id}/add_item?product_id=#{product_id}"
-
- assert session_2.redirect?
- assert session_2.response.location.include?(
- "/client_orders/#{order_2_id}/edit"
- )
- assert_equal "Product not available in requested quantity!",
- session_2.flash[:alert]
- end
-
- def test_adding_product_which_is_not_available_in_requested_quantity
- customer_id = register_customer("Customer Shop")
- product_id = register_product("Fearless Refactoring", 4, 10)
-
- supply_product(product_id, 1)
- login(customer_id)
- visit_client_orders
-
- order_id = SecureRandom.uuid
- as_client_add_item_to_basket_for_order(product_id, order_id)
- as_client_add_item_to_basket_for_order(product_id, order_id)
-
- assert_redirected_to "/client_orders/#{order_id}/edit"
- assert_equal "Product not available in requested quantity!", flash[:alert]
- end
-
- private
-
- def submit_order_for_customer(customer_id, order_id)
- post "/orders", params: { order_id: order_id, customer_id: customer_id }
- follow_redirect!
- end
-
- def add_item_to_basket_for_order(async_remote_id, order_id)
- post "/orders/#{order_id}/add_item?product_id=#{async_remote_id}"
- end
-
- def as_client_submit_order_for_customer(order_id)
- post "/client_orders", params: { order_id: order_id }
- follow_redirect!
- end
-
- def as_client_add_item_to_basket_for_order(async_remote_id, order_id)
- post "/client_orders/#{order_id}/add_item?product_id=#{async_remote_id}"
- end
-
- def cancel_order(order_id)
- post "/orders/#{order_id}/cancel"
- end
-
- def cancel_submitted_order_for_customer(customer_id)
- order_id = SecureRandom.uuid
- anti_if = register_product("Anti If", 99, 10)
-
- add_item_to_basket_for_order(anti_if, order_id)
- add_item_to_basket_for_order(anti_if, order_id)
- submit_order_for_customer(customer_id, order_id)
- cancel_order(order_id)
- end
-
- def order_and_pay(customer_id, order_id, *product_ids)
- product_ids.each do |product_id|
- as_client_add_item_to_basket_for_order(product_id, order_id)
- end
- submit_order_for_customer(customer_id, order_id)
- pay_order(order_id)
- end
-
- def assert_orders_summary(summary)
- assert_select "tr" do
- assert_select "td:nth-child(1)", "Total paid orders"
- assert_select "td:nth-child(2)", summary
- end
- end
-
- def update_price(product_id, new_price)
- patch "/products/#{product_id}",
- params: {
- "authenticity_token" => "[FILTERED]",
- "product_id" => product_id,
- :price => new_price
- }
- end
-
- def login_as(client_id)
- open_session { |sess| sess.post "/login", params: { client_id: client_id } }
- end
-end
diff --git a/rails_application/test/integration/coupons_test.rb b/rails_application/test/integration/coupons_test.rb
deleted file mode 100644
index a3de86ce2..000000000
--- a/rails_application/test/integration/coupons_test.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-
-require "test_helper"
-
-class CouponsTest < InMemoryRESIntegrationTestCase
- def test_list_coupons
- register_coupon("Coupon Number Uno", "enterme", "0.01")
-
- get "/coupons"
- assert_response :success
- assert_select("td", "Coupon Number Uno")
- assert_select("td", "enterme")
- assert_select("td", "0.01")
- end
-
- def test_creation
- register_coupon("Coupon Number Two", "fair_price", "6.69")
- assert_response :success
- assert_select("p", "Coupon was successfully created")
- assert_select("td", "Coupon Number Two")
- assert_select("td", "fair_price")
- assert_select("td", "6.69")
- end
-
- private
-
- def register_coupon(name, code, discount)
- post "/coupons", params: {
- coupon_id: SecureRandom.uuid,
- name: name,
- code: code,
- discount: discount
- }
-
- follow_redirect!
- end
-end
diff --git a/rails_application/test/integration/customers_test.rb b/rails_application/test/integration/customers_test.rb
index e6c187010..d99f36aee 100644
--- a/rails_application/test/integration/customers_test.rb
+++ b/rails_application/test/integration/customers_test.rb
@@ -24,13 +24,17 @@ def test_paid_orders_summary
assert_customer_summary("Customer Shop", "$0.00")
assert_customer_summary("BigCorp Ltd", "$0.00")
- order_and_pay(customer_id, SecureRandom.uuid, product_1_id, product_2_id)
+ order = new_order
+
+ order_and_pay(customer_id, new_order.id, product_1_id, product_2_id)
visit_customers_index
assert_customer_summary("Customer Shop", "$7.00")
assert_customer_summary("BigCorp Ltd", "$0.00")
- order_and_pay(customer_id, SecureRandom.uuid, product_1_id)
+ another_order = new_order
+
+ order_and_pay(customer_id, another_order.id, product_1_id)
visit_customers_index
assert_customer_summary("Customer Shop", "$11.00")
@@ -41,16 +45,16 @@ def test_customer_details
customer_id = register_customer("Customer Shop")
product_id = register_product("Fearless Refactoring", 4, 10)
- order_uid = SecureRandom.uuid
+ order_id = new_order.id
- order_and_pay(customer_id, order_uid, product_id)
+ order_and_pay(customer_id, order_id, product_id)
visit_customer_page(customer_id)
- order = ClientOrders::Order.find_by(order_uid: order_uid)
+ order = Order.find(order_id)
- assert_select "h1", "Customer Page"
- assert_customer_details "Customer Shop", "No"
- assert_customer_orders_table order.number, "Paid", "$4.00", "$4.00"
+ assert_select("h1", "Customer Page")
+ assert_customer_details("Customer Shop", "No")
+ assert_customer_orders_table(order.number, "Paid", "$4.00", "$4.00")
end
private
diff --git a/rails_application/test/integration/discount_test.rb b/rails_application/test/integration/discount_test.rb
index 804b855bc..58f606024 100644
--- a/rails_application/test/integration/discount_test.rb
+++ b/rails_application/test/integration/discount_test.rb
@@ -3,12 +3,11 @@
class DiscountTest < InMemoryRESIntegrationTestCase
def setup
super
- Orders::Order.destroy_all
end
def test_reset_discount
register_customer("Shopify")
- order_id = SecureRandom.uuid
+ order_id = new_order.id
async_remote_id = register_product("Async Remote", 137, 10)
diff --git a/rails_application/test/integration/feed_product_catalog_test.rb b/rails_application/test/integration/feed_product_catalog_test.rb
new file mode 100644
index 000000000..43238d48a
--- /dev/null
+++ b/rails_application/test/integration/feed_product_catalog_test.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'test_helper'
+require_relative '../../script/replay_inventory_aggregate_events_to_feed_product_catalog'
+
+class FeedProductCatalogTest < InMemoryRESIntegrationTestCase
+ def test_feed_product_catalog
+ product = Product.create!(name: 'Test Product', price: 10, vat_rate: 23, sku: 'test')
+
+ event_store.append(Inventory::StockLevelIncreased.new(data: { id: product.id, quantity: 10 }), stream_name: "Inventory::Product$#{product.id}")
+
+ replay_inventory_aggregate_events_to_feed_product_catalog
+
+ product_catalog = Inventory::ProductCatalog.find_by(product_id: product.id)
+ assert_equal 10, product_catalog.stock_level
+ end
+
+ private
+
+ def event_store
+ Rails.configuration.event_store
+ end
+end
diff --git a/rails_application/test/integration/login_test.rb b/rails_application/test/integration/login_test.rb
deleted file mode 100644
index 05aa223b5..000000000
--- a/rails_application/test/integration/login_test.rb
+++ /dev/null
@@ -1,70 +0,0 @@
-require "test_helper"
-
-class LoginTest < InMemoryRESIntegrationTestCase
- def setup
- super
- Customers::Customer.destroy_all
- end
-
- def test_login
- password = "1234qwer"
- customer_id = register_customer("Arkency")
- set_password(customer_id, password)
-
- post "/login", params: { client_id: customer_id, password: password }
- follow_redirect!
-
- assert_select("h1", "Arkency")
- assert_equal customer_id, cookies["client_id"]
- end
-
- def test_login_with_incorrect_password
- password = "1234qwer"
- customer_id = register_customer("Arkency")
- set_password(customer_id, password)
-
- post "/login",
- params: {
- client_id: customer_id,
- password: "Wrong password"
- }
- follow_redirect!
-
- refute cookies["client_id"].present?
- end
-
- def test_cookies_set_to_not_existing_customer_should_log_out_and_redirect_to_login
- cookies["client_id"] = "not-existing-customer"
-
- get "/client_orders"
- follow_redirect!
- follow_redirect!
-
- refute cookies["client_id"].present?
- assert_equal "/clients", path
- end
-
- private
-
- def set_password(customer_id, password)
- account_id = SecureRandom.uuid
- password_hash = Digest::SHA256.hexdigest(password)
-
- run_command(Authentication::RegisterAccount.new(account_id: account_id))
- run_command(
- Authentication::ConnectAccountToClient.new(
- account_id: account_id,
- client_id: customer_id
- )
- )
- run_command(
- Authentication::SetPasswordHash.new(
- account_id: account_id,
- password_hash: password_hash
- )
- )
-
- cookies["client_id"] = nil
- customer_id
- end
-end
diff --git a/rails_application/test/integration/orders_test.rb b/rails_application/test/integration/orders_test.rb
index 6725d0d91..c1ace9d7e 100644
--- a/rails_application/test/integration/orders_test.rb
+++ b/rails_application/test/integration/orders_test.rb
@@ -3,8 +3,7 @@
class OrdersTest < InMemoryRESIntegrationTestCase
def setup
super
- Rails.configuration.payment_gateway.call.reset
- Orders::Order.destroy_all
+ Order.destroy_all
end
def test_submitting_empty_order
@@ -12,23 +11,15 @@ def test_submitting_empty_order
get "/"
assert_select "h1", "Orders"
- get "/orders/new"
- follow_redirect!
+ order = new_order
assert_select "h1", "Order"
- post "/orders",
- params: {
- "authenticity_token" => "[FILTERED]",
- "order_id" => SecureRandom.uuid,
- "customer_id" => arkency_id,
- "commit" => "Submit order"
- }
end
def test_happy_path
shopify_id = register_customer("Shopify")
- order_id = SecureRandom.uuid
- another_order_id = SecureRandom.uuid
+ order_id = new_order.id
+ another_order_id = new_order.id
async_remote_id = register_product("Async Remote", 39, 10)
fearless_id = register_product("Fearless Refactoring", 49, 10)
@@ -43,13 +34,37 @@ def test_happy_path
get "/"
get "/orders/new"
- follow_redirect!
assert_remove_buttons_not_visible(async_remote_id, fearless_id)
- post "/orders/#{order_id}/add_item?product_id=#{async_remote_id}"
- post "/orders/#{order_id}/add_item?product_id=#{fearless_id}"
- post "/orders/#{order_id}/add_item?product_id=#{fearless_id}"
+ assert_changes -> { Inventory::ProductCatalog.find_by(product_id: async_remote_id).stock_level }, from: 10, to: 9 do
+ post "/orders/#{order_id}/add_item?product_id=#{async_remote_id}"
+ end
+ assert_expected_events_in_stream(
+ inventory_product_stream_name(async_remote_id),
+ [
+ Inventory::StockLevelIncreased.new(data: { id: async_remote_id, quantity: 10 }),
+ Inventory::StockLevelDecreased.new(data: { id: async_remote_id, quantity: 1 })
+ ]
+ )
+
+ assert_changes -> { Inventory::ProductCatalog.find_by(product_id: fearless_id).stock_level }, from: 10, to: 8 do
+ post "/orders/#{order_id}/add_item?product_id=#{fearless_id}"
+ post "/orders/#{order_id}/add_item?product_id=#{fearless_id}"
+ post "/orders/#{order_id}/remove_item?product_id=#{fearless_id}"
+ post "/orders/#{order_id}/add_item?product_id=#{fearless_id}"
+ end
+ assert_expected_events_in_stream(
+ inventory_product_stream_name(fearless_id),
+ [
+ Inventory::StockLevelIncreased.new(data: { id: fearless_id, quantity: 10 }),
+ Inventory::StockLevelDecreased.new(data: { id: fearless_id, quantity: 1 }),
+ Inventory::StockLevelDecreased.new(data: { id: fearless_id, quantity: 1 }),
+ Inventory::StockLevelIncreased.new(data: { id: fearless_id, quantity: 1 }),
+ Inventory::StockLevelDecreased.new(data: { id: fearless_id, quantity: 1 })
+ ]
+ )
+
get "/orders/#{order_id}/edit"
assert_remove_buttons_visible(async_remote_id, fearless_id, order_id)
@@ -71,22 +86,24 @@ def test_happy_path
post "/orders/#{order_id}/pay"
follow_redirect!
assert_select("td", text: "Paid")
- assert_payment_gateway_value(123.30)
+ # assert_payment_gateway_value(123.30)
verify_shipping(order_id)
verify_invoice_generation(order_id)
-
- assert_res_browser_order_history
end
def test_expiring_orders
- order_id = SecureRandom.uuid
+ order_id = new_order.id
async_remote_id = register_product("Async Remote", 39, 10)
post "/orders/#{order_id}/add_item?product_id=#{async_remote_id}"
get "/orders"
assert_select("td", "Draft")
- post "/orders/expire"
+
+ assert_changes -> { Inventory::ProductCatalog.find_by(product_id: async_remote_id).stock_level }, from: 9, to: 10 do
+ post "/orders/expire"
+ end
+
follow_redirect!
assert_select("td", "Expired")
end
@@ -99,7 +116,7 @@ def test_order_not_found
def test_cancel
shopify_id = register_customer("Shopify")
- order_id = SecureRandom.uuid
+ order_id = new_order.id
async_remote_id = register_product("Async Remote", 39, 10)
get "/"
@@ -121,7 +138,7 @@ def test_cancel
def test_confirmed_order_doesnt_show_cancel_button
shopify_id = register_customer("Shopify")
- order_id = SecureRandom.uuid
+ order_id = new_order.id
async_remote_id = register_product("Async Remote", 39, 10)
get "/"
@@ -155,7 +172,7 @@ def test_confirmed_order_doesnt_show_cancel_button
def test_order_value_doesnt_change_after_changing_price
shopify_id = register_customer("Shopify")
- order_id = SecureRandom.uuid
+ order_id = new_order.id
async_remote_id = register_product("Async Remote", 39, 10)
get "/"
@@ -177,12 +194,12 @@ def test_order_value_doesnt_change_after_changing_price
update_price(async_remote_id, 49)
get "/orders/#{order_id}"
- assert_select("td", text: "$39.00")
+ assert_select("td", text: "$49.00")
assert_select("td", text: "$78.00")
end
def test_discount_is_applied_for_new_order
- order_id = SecureRandom.uuid
+ order_id = new_order.id
async_remote_id = register_product("Async Remote", 39, 10)
fearless_id = register_product("Fearless Refactoring", 49, 10)
shopify_id = register_customer("Shopify")
@@ -244,17 +261,18 @@ def verify_shipping(order_id)
assert_select("label", "Addressee's full name (Person or Company)")
put "/orders/#{order_id}/shipping_address",
params: {
- "shipments_shipment" => {
- address_line_1: "123 Main Street",
- address_line_2: "Apt 1",
- address_line_3: "San Francisco",
- address_line_4: "US"
+ order: {
+ address: "123 Main Street",
+ addressed_to: "Apt 1",
+ city: "San Francisco",
+ country: "US"
}
}
- follow_redirect!
+ get "/orders/#{order_id}"
assert_select("dd", "Your shipment has been queued for processing.")
+
get "/shipments"
- assert_select("td", "123 Main Street Apt 1 San Francisco US")
+ assert_select("td", "123 Main Street, San Francisco, US Apt 1")
end
def verify_invoice_generation(order_id)
@@ -264,25 +282,32 @@ def verify_invoice_generation(order_id)
assert_select("label", "Addressee's full name (Person or Company)")
put "/orders/#{order_id}/billing_address",
params: {
- "invoices_invoice" => {
- address_line_1: "44 Main Street",
- address_line_2: "Apt 2",
- address_line_3: "Francisco",
- address_line_4: "UK"
+ :order => {
+ invoice_tax_id_number: "1234567890",
+ invoice_address: "44 Main Street",
+ invoice_addressed_to: "Apt 2",
+ invoice_city: "Francisco",
+ invoice_country: "UK"
}
}
- follow_redirect!
+ get "/orders/#{order_id}"
assert_select("button", "Issue now")
post "/orders/#{order_id}/invoice"
follow_redirect!
assert_select("td", "Async Remote")
- assert_select("td", "$35.10")
+ assert_select("td", "10")
assert_select("td", "1")
+ assert_select("td", "$39.00")
+ assert_select("td", "$3.90")
+
assert_select("td", "Fearless Refactoring")
- assert_select("td", "$44.10")
+ assert_select("td", "10")
assert_select("td", "2")
- assert_select("td", "$88.20")
+ assert_select("td", "$49.00")
+ assert_select("td", "$9.80")
+
+ assert_select("td", "Total")
assert_select("td", "$123.30")
end
@@ -316,4 +341,22 @@ def apply_discount_10_percent(order_id)
post "/orders/#{order_id}/update_discount?amount=10"
end
+
+ def inventory_product_stream_name(product_id)
+ "Inventory::Product$#{product_id}"
+ end
+
+ def assert_expected_events_in_stream(stream_name, expected, event_store: Rails.configuration.event_store)
+ actual =
+ event_store
+ .read
+ .stream(stream_name)
+ .map { |event| { data: event.data, type: event.event_type } }
+ expected = expected.map { |event| { data: event.data, type: event.class.to_s } }
+ assert_equal(expected, actual)
+ end
+
+ def event_store
+ Rails.configuration.event_store
+ end
end
diff --git a/rails_application/test/integration/products_test.rb b/rails_application/test/integration/products_test.rb
index e9f8b6943..17e7cae7a 100644
--- a/rails_application/test/integration/products_test.rb
+++ b/rails_application/test/integration/products_test.rb
@@ -9,23 +9,15 @@ def setup
def test_happy_path
register_customer("Arkency")
- product_id = SecureRandom.uuid
get "/products/new"
assert_select "h1", "New Product"
- post "/products",
- params: {
- "authenticity_token" => "[FILTERED]",
- "product_id" => product_id,
- "name" => "product name",
- :price => "20.01",
- "vat_rate" => "10"
- }
+ product_id = register_product("product name", 20.01, 10)
follow_redirect!
assert_equal "20.01",
number_to_currency(
- Products::Product.find(product_id).price,
+ Product.find(product_id).price,
unit: ""
)
@@ -35,68 +27,68 @@ def test_happy_path
patch "/products/#{product_id}",
params: {
"authenticity_token" => "[FILTERED]",
- "product_id" => product_id,
- :price => "20.02"
+ product: { price: "20.02" }
}
+ stream = event_store.read.stream("ProductCatalog::Product$#{product_id}").to_a
+ assert_equal 2, stream.size
+ assert_equal "ProductCatalog::ProductPriceChanged", stream.last.event_type
+ assert_equal 20.02, stream.last.data[:price]
assert_equal "20.02",
number_to_currency(
- Products::Product.find(product_id).price,
+ Product.last.price,
unit: ""
)
end
- def test_does_not_crash_when_setting_products_price_to_0
+ def test_change_product_name
register_customer("Arkency")
- product_id = SecureRandom.uuid
get "/products/new"
assert_select "h1", "New Product"
- post "/products",
- params: {
- "authenticity_token" => "[FILTERED]",
- "product_id" => product_id,
- "name" => "product name",
- "price" => "0",
- "vat_rate" => "10"
- }
+ product_id = register_product("product name", 20.01, 10)
- assert_response :unprocessable_entity
- assert_select "span", "Price must be greater than 0"
+ get "/products/#{product_id}/edit"
+ patch "/products/#{product_id}",
+ params: {
+ "authenticity_token" => "[FILTERED]",
+ product: { name: "Changed product name" }
+ }
+ product_stream = event_store.read.stream("ProductCatalog::Product$#{product_id}").to_a
+ assert_equal 2, product_stream.size
+ name_changed = product_stream.last
+ assert_equal "ProductCatalog::ProductNameChanged", name_changed.event_type
+ assert_equal "Changed product name", name_changed.data[:name]
+
+ patch "/products/#{product_id}",
+ params: {
+ "authenticity_token" => "[FILTERED]",
+ product: { name: "Changed product name" }
+ }
+
+ product_stream = event_store.read.stream("ProductCatalog::Product$#{product_id}").to_a
+ assert_equal 2, product_stream.size
end
- def test_does_not_crash_when_vat_rate_is_absent
+ def test_does_not_crash_when_setting_products_price_to_0
register_customer("Arkency")
- product_id = SecureRandom.uuid
get "/products/new"
assert_select "h1", "New Product"
- post "/products",
- params: {
- "authenticity_token" => "[FILTERED]",
- "product_id" => product_id,
- "name" => "product name",
- "price" => "100",
- }
+
+ post "/products", params: { product: { name: 'name', price: 0, vat_rate: 10, sku: 'test' } }
assert_response :unprocessable_entity
- assert_select "span", "Vat rate can't be blank"
+ assert_select "span", "Price must be greater than 0"
end
- def test_does_not_crash_when_vat_rate_is_not_a_number
+ def test_does_not_crash_when_vat_rate_is_absent
register_customer("Arkency")
- product_id = SecureRandom.uuid
get "/products/new"
assert_select "h1", "New Product"
- post "/products",
- params: {
- "authenticity_token" => "[FILTERED]",
- "product_id" => product_id,
- "name" => "product name",
- "price" => "100",
- "vat_rate" =>"abc"
- }
+
+ post "/products", params: { product: { name: 'name', price: 10, vat_rate: nil, sku: 'test' } }
assert_response :unprocessable_entity
assert_select "span", "Vat rate is not a number"
@@ -104,19 +96,19 @@ def test_does_not_crash_when_vat_rate_is_not_a_number
def test_does_not_crash_when_name_is_not_present
register_customer("Arkency")
- product_id = SecureRandom.uuid
get "/products/new"
assert_select "h1", "New Product"
- post "/products",
- params: {
- "authenticity_token" => "[FILTERED]",
- "product_id" => product_id,
- "price" => "100",
- "vat_rate" =>"10"
- }
+
+ post "/products", params: { product: { name: nil, price: 10, vat_rate: 23, sku: 'test' } }
assert_response :unprocessable_entity
assert_select "span", "Name can't be blank"
end
+
+ private
+
+ def event_store
+ Rails.configuration.event_store
+ end
end
diff --git a/rails_application/test/integration/public_offer_test.rb b/rails_application/test/integration/public_offer_test.rb
deleted file mode 100644
index dd8f0281e..000000000
--- a/rails_application/test/integration/public_offer_test.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-require "test_helper"
-
-class PublicOfferTest < InMemoryRESIntegrationTestCase
- def setup
- super
- end
-
- def test_happy_path
- arkency_id = register_customer("Arkency")
- register_product("Async Remote", 39, 10)
- get "/clients"
- login(arkency_id)
- assert_select("a", "Products")
- get "/client/products"
- assert_select("td", "Async Remote")
- assert_select("td", "$39.00")
- end
-
- def test_showing_orders_with_information_about_the_lowest_price
- client_id = register_customer("Arkency")
- product1_id = register_product("Async Remote", 45, 10)
- product2_id = register_product("Rails meets React.js", 50, 10)
- update_price(product1_id, 30)
- update_price(product2_id, 25)
- update_price(product2_id, 70)
-
- get "/clients"
- login(client_id)
- get "/client/products"
- assert css_select("#lowest-price-info-#{product1_id}").empty?
- assert css_select("#lowest-price-info-#{product2_id}").present?
-
- info_icon = css_select("#lowest-price-info-#{product2_id}").first
- assert_equal info_icon.attributes.fetch("title").value,
- "Lowest recent price: $25.00"
- end
-end
diff --git a/rails_application/test/integration/supplies_test.rb b/rails_application/test/integration/supplies_test.rb
new file mode 100644
index 000000000..2bbc5ffe7
--- /dev/null
+++ b/rails_application/test/integration/supplies_test.rb
@@ -0,0 +1,51 @@
+require "test_helper"
+
+class SuppliesTest < InMemoryRESIntegrationTestCase
+ def setup
+ super
+ end
+
+ def test_supply_new_product
+ create_product
+ product = Product.find_by(sku:)
+ increase_stock_level_by_10(product.id)
+
+ follow_redirect!
+
+ assert_select "tr#product_#{product.id}" do
+ assert_select "td", "10"
+ end
+ product_catalog = Inventory::ProductCatalog.find_by(product_id: product.id)
+ assert_equal(10, product_catalog.stock_level)
+ end
+
+ def test_supply_product_with_existing_stock
+ create_product
+ product = Product.find_by(sku:)
+ increase_stock_level_by_10(product.id)
+
+ assert_changes -> { Inventory::ProductCatalog.find_by(product_id: product.id).stock_level }, from: 10, to: 20 do
+ increase_stock_level_by_10(product.id)
+ end
+
+ follow_redirect!
+
+ assert_select "tr#product_#{product.id}" do
+ assert_select "td", "20"
+ end
+ end
+
+ private
+
+ def increase_stock_level_by_10(product_id)
+ post "/products/#{product_id}/supplies", params: { product_id: product_id, quantity: 10 }
+ end
+
+ def create_product
+ post "/products", params: { product: { name: "Stanley Cup", price: 100, vat_rate: 23, sku: } }
+ end
+
+ def sku
+ "SKU-ST4NL3Y"
+ end
+end
diff --git a/rails_application/test/integration/time_promotions_test.rb b/rails_application/test/integration/time_promotions_test.rb
deleted file mode 100644
index cacbb3934..000000000
--- a/rails_application/test/integration/time_promotions_test.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-
-require "test_helper"
-
-class TimePromotionsTest < InMemoryRESIntegrationTestCase
- def test_happy_path
- post "/time_promotions", params: {
- label: "Last Minute June 2022",
- discount: "50",
- start_time: "2022-06-30 15:00:00 UTC",
- end_time: "2022-07-01 00:00:00 UTC"
- }
- follow_redirect!
- assert_response :success
-
- assert_select("p", "Time promotion was successfully created")
-
- post "/time_promotions", params: {
- label: "Black Monday July 2022",
- discount: "40",
- start_time: "2022-07-04 01:00:00 UTC",
- end_time: "2022-07-05 00:00:00 UTC"
- }
- follow_redirect!
- assert_response :success
-
- assert_select("p", "Time promotion was successfully created")
-
- get "/time_promotions"
- assert_select("td", "Last Minute June 2022")
- assert_select("td", "50")
- assert_select("td", "2022-06-30 15:00:00 UTC")
- assert_select("td", "2022-07-01 00:00:00 UTC")
-
- assert_select("td", "Black Monday July 2022")
- assert_select("td", "40")
- assert_select("td", "2022-07-04 01:00:00 UTC")
- assert_select("td", "2022-07-05 00:00:00 UTC")
- end
-end
diff --git a/rails_application/test/invoices/invoices_test.rb b/rails_application/test/invoices/invoices_test.rb
deleted file mode 100644
index 70ef0220e..000000000
--- a/rails_application/test/invoices/invoices_test.rb
+++ /dev/null
@@ -1,68 +0,0 @@
-require "test_helper"
-
-module Invoices
- class InvoicesTest < InMemoryRESIntegrationTestCase
- cover "Invoices*"
-
- def setup
- super
- Invoice.destroy_all
- InvoiceItem.destroy_all
- Order.destroy_all
- end
-
- def test_create_draft_order_when_not_exists
- event_store = Rails.configuration.event_store
- order_id = SecureRandom.uuid
- product_id = SecureRandom.uuid
- event_store.publish(
- [
- Invoicing::InvoiceItemAdded.new(data: { invoice_id: order_id, product_id: product_id, title: "dummy", discounted_amount: 9, quantity: 1, amount: 1, unit_price: 10, vat_rate: { rate: 23, code: "VAT" } }),
- Invoicing::InvoiceItemAdded.new(data: { invoice_id: order_id, product_id: product_id, title: "dummy2", discounted_amount: 9, quantity: 1, amount: 1, unit_price: 8, vat_rate: { rate: 23, code: "VAT" } }),
- ]
- )
- assert_equal(Invoice.count, 1)
- assert_equal(18, Invoice.last.total_value)
- end
-
- def test_product_name_change_affects_existing_invoices
- product_id = SecureRandom.uuid
- initial_product_name = "Initial Name"
- updated_product_name = "Updated Name"
-
- 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: {
- "authenticity_token" => "[FILTERED]",
- "product_id" => product_id,
- 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
diff --git a/rails_application/test/models/inventory/product_test.rb b/rails_application/test/models/inventory/product_test.rb
new file mode 100644
index 000000000..48fd41fb3
--- /dev/null
+++ b/rails_application/test/models/inventory/product_test.rb
@@ -0,0 +1,86 @@
+# frozen_string_literal: true
+
+require 'test_helper'
+
+module Inventory
+ class ProductTest < Infra::InMemoryTest
+ def test_supply
+ product_id = 1024
+
+ assert_events(stream_name(product_id), StockLevelIncreased.new(data: { id: product_id, quantity: 10 })) do
+ with_aggregate(product_id) do |product|
+ product.supply(10)
+ end
+ end
+ end
+
+ def test_withdraw
+ product_id = 1024
+
+ assert_events(stream_name(product_id),
+ StockLevelIncreased.new(data: { id: product_id, quantity: 10 }),
+ StockLevelDecreased.new(data: { id: product_id, quantity: 5 })
+ ) do
+ with_aggregate(product_id) do |product|
+ product.supply(10)
+ product.withdraw(5)
+ end
+ end
+ end
+
+ def test_withdraw_when_not_enough_stock_is_not_allowed
+ product_id = 1024
+
+ assert_nothing_published_within do
+ assert_raises("Not enough stock") do
+ with_aggregate(product_id) do |product|
+ product.withdraw(10)
+ end
+ end
+ end
+ end
+
+ def test_withdraw_when_not_enough_stock_is_possible_when_stock_is_ordered
+ product_id = 1024
+
+ stock_level_ordered_for_tomorrow = 10
+
+ assert_events(stream_name(product_id),
+ StockLevelIncreased.new(data: { id: product_id, quantity: 2 }),
+ StockLevelDecreased.new(data: { id: product_id, quantity: 5 })
+ ) do
+ with_aggregate(product_id) do |product|
+ product.supply(2)
+ product.withdraw(5, can_oversell: StockLevelWillBeFulfilledWithin2BusinessDays.new(stock_level_ordered_for_tomorrow))
+ end
+ end
+ end
+
+ def test_withdraw_when_not_enough_stock_is_possible_when_not_enough_stock_is_ordered
+ product_id = 1024
+
+ stock_level_ordered_for_tomorrow = 1
+
+ assert_nothing_published_within do
+ assert_raises("Not enough stock") do
+ with_aggregate(product_id) do |product|
+ product.supply(2)
+ product.withdraw(5, can_oversell: StockLevelWillBeFulfilledWithin2BusinessDays.new(stock_level_ordered_for_tomorrow))
+ end
+ end
+ end
+ end
+
+ private
+
+ def stream_name(product_id)
+ "Inventory::Product$#{product_id}"
+ end
+
+ def with_aggregate(product_id)
+ Infra::AggregateRootRepository.new(event_store).with_aggregate(Product, product_id) do |product|
+ yield product
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/rails_application/test/orders/assign_customer_first_test.rb b/rails_application/test/orders/assign_customer_first_test.rb
deleted file mode 100644
index 25c65f8b4..000000000
--- a/rails_application/test/orders/assign_customer_first_test.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-require "test_helper"
-
-module Orders
- class AssignCustomerFirstTest < InMemoryTestCase
- cover "Orders*"
-
- def setup
- super
- Order.destroy_all
- OrderLine.destroy_all
- end
-
- def test_create_draft_order_when_not_exists
- event_store = Rails.configuration.event_store
- customer_id = SecureRandom.uuid
- customer_registered = Crm::CustomerRegistered.new(data: { customer_id: customer_id, name: "dummy" })
- event_store.publish(customer_registered)
- customer_assigned_to_order = Crm::CustomerAssignedToOrder.new(data: { order_id: SecureRandom.uuid, customer_id: customer_id })
- event_store.publish(
- customer_assigned_to_order
- )
- assert_equal(Order.count, 1)
- assert event_store.event_in_stream?(customer_assigned_to_order.event_id, "Orders$all")
- assert event_store.event_in_stream?(customer_registered.event_id, "Orders$all")
- end
- end
-end
\ No newline at end of file
diff --git a/rails_application/test/orders/broadcast_test.rb b/rails_application/test/orders/broadcast_test.rb
deleted file mode 100644
index 9c2bc0e98..000000000
--- a/rails_application/test/orders/broadcast_test.rb
+++ /dev/null
@@ -1,225 +0,0 @@
-require "test_helper"
-
-module Orders
- class BroadcastTest < InMemoryTestCase
- cover "Orders*"
-
- def setup
- @prev = Rails.configuration.broadcaster
- Rails.configuration.broadcaster = InMemoryBroadcaster.new
- end
-
- def teardown
- Rails.configuration.broadcaster = @prev
- end
-
- class InMemoryBroadcaster
- def initialize
- @result = []
- end
-
- attr_accessor :result
-
- def call(stream_id, target_id, target, content)
- result << { stream_id: stream_id, target_id: target_id, target: target, content: content }
- end
- end
-
- def test_broadcast_add_item_to_basket
- event_store = Rails.configuration.event_store
-
- product_id = SecureRandom.uuid
- order_id = SecureRandom.uuid
- run_command(
- ProductCatalog::RegisterProduct.new(
- product_id: product_id
- )
- )
- run_command(Pricing::SetPrice.new(product_id: product_id, price: 20))
-
- in_memory_broadcast.result.clear
-
- event_store.publish(
- Ordering::ItemAddedToBasket.new(
- data: {
- order_id: order_id,
- product_id: product_id,
- quantity_before: 0
- }
- )
- )
-
- [
- { stream_id: order_id, target_id: product_id, target: "quantity", content: 1 },
- { stream_id: order_id, target_id: product_id, target: "value", content: "$20.00" }
- ].each { |expected_broadcast| assert_includes in_memory_broadcast.result, expected_broadcast }
- end
-
- def test_broadcast_remove_item_from_basket
- event_store = Rails.configuration.event_store
-
- product_id = SecureRandom.uuid
- run_command(
- ProductCatalog::RegisterProduct.new(
- product_id: product_id
- )
- )
- run_command(Pricing::SetPrice.new(product_id: product_id, price: 20))
- order_id = SecureRandom.uuid
-
- event_store.publish(
- Ordering::ItemAddedToBasket.new(
- data: {
- order_id: order_id,
- product_id: product_id,
- quantity_before: 0
- }
- )
- )
- in_memory_broadcast.result.clear
-
- event_store.publish(
- Ordering::ItemRemovedFromBasket.new(
- data: {
- order_id: order_id,
- product_id: product_id,
- }
- )
- )
-
- [
- { stream_id: order_id, target_id: product_id, target: "quantity", content: 0 },
- { stream_id: order_id, target_id: product_id, target: "value", content: "$0.00" }
- ].each do |expected_broadcast|
- assert_includes in_memory_broadcast.result, expected_broadcast
- end
- end
-
- def test_broadcast_update_order_value
- event_store = Rails.configuration.event_store
-
- product_id = SecureRandom.uuid
- order_id = SecureRandom.uuid
- order_1_id = SecureRandom.uuid
- run_command(
- ProductCatalog::RegisterProduct.new(
- product_id: product_id
- )
- )
- run_command(Pricing::SetPrice.new(product_id: product_id, price: 20))
- event_store.publish(
- Ordering::ItemAddedToBasket.new(
- data: {
- order_id: order_id,
- product_id: product_id,
- quantity_before: 0
- }
- )
- )
- event_store.publish(
- Ordering::ItemAddedToBasket.new(
- data: {
- order_id: order_1_id,
- product_id: product_id,
- quantity_before: 0
- }
- )
- )
-
- event_store.publish(
- Pricing::OrderTotalValueCalculated.new(
- data: {
- order_id: order_1_id,
- discounted_amount: 30,
- total_amount: 30
- }
- )
- )
-
- in_memory_broadcast.result.clear
-
- event_store.publish(
- Pricing::OrderTotalValueCalculated.new(
- data: {
- order_id: order_id,
- discounted_amount: 30,
- total_amount: 30
- }
- )
- )
-
- assert_equal 2, in_memory_broadcast.result.size
- [
- { :stream_id => order_id, :target_id => order_id, :target => "total_value", :content => "$30.00" },
- { :stream_id => order_id, :target_id => order_id, :target => "discounted_value", :content => "$30.00" }
- ].each do |expected_broadcast|
- assert_includes in_memory_broadcast.result, expected_broadcast
- end
- end
-
- def test_broadcast_update_discount
- event_store = Rails.configuration.event_store
-
- product_id = SecureRandom.uuid
- order_id = SecureRandom.uuid
- order_1_id = SecureRandom.uuid
- run_command(
- ProductCatalog::RegisterProduct.new(
- product_id: product_id
- )
- )
- run_command(Pricing::SetPrice.new(product_id: product_id, price: 20))
- event_store.publish(
- Ordering::ItemAddedToBasket.new(
- data: {
- order_id: order_id,
- product_id: product_id,
- quantity_before: 0
- }
- )
- )
- event_store.publish(
- Ordering::ItemAddedToBasket.new(
- data: {
- order_id: order_1_id,
- product_id: product_id,
- quantity_before: 0
- }
- )
- )
-
- event_store.publish(
- Pricing::PercentageDiscountSet.new(
- data: {
- order_id: order_1_id,
- amount: 30
- }
- )
- )
-
- in_memory_broadcast.result.clear
-
- event_store.publish(
- Pricing::PercentageDiscountSet.new(
- data: {
- order_id: order_id,
- amount: 30
- }
- )
- )
-
- assert_equal 3, in_memory_broadcast.result.size
- [
- { :stream_id => order_id, :target_id => order_id, :target => "percentage_discount", :content => 30 },
- ].each do |expected_broadcast|
- assert_includes in_memory_broadcast.result, expected_broadcast
- end
- end
-
- private
-
- def in_memory_broadcast
- Rails.configuration.broadcaster
- end
- end
-end
diff --git a/rails_application/test/orders/customer_registered_test.rb b/rails_application/test/orders/customer_registered_test.rb
deleted file mode 100644
index a1bd29aef..000000000
--- a/rails_application/test/orders/customer_registered_test.rb
+++ /dev/null
@@ -1,55 +0,0 @@
-require "test_helper"
-
-module Orders
- class CustomerRegisteredTest < InMemoryTestCase
- cover "Orders*"
-
- def setup
- super
- Customer.destroy_all
- end
-
- def test_customer_registered
- event_store = Rails.configuration.event_store
-
- customer_id = SecureRandom.uuid
-
- event_store.publish(Crm::CustomerRegistered.new(
- data: {
- customer_id: customer_id,
- name: "John Doe"
- }
- ))
-
- client = Customer.find_by(uid: customer_id)
- assert_not_nil(client)
- assert_equal("John Doe", client.name)
- assert_equal(customer_id, client.uid)
- end
-
- def test_customer_assigned_to_order
- event_store = Rails.configuration.event_store
-
- customer_id = SecureRandom.uuid
- order_id = SecureRandom.uuid
-
- event_store.publish(Crm::CustomerRegistered.new(
- data: {
- customer_id: customer_id,
- name: "John Doe"
- }
- ))
-
- event_store.publish(Crm::CustomerAssignedToOrder.new(
- data: {
- customer_id: customer_id,
- order_id: order_id
- }
- ))
-
- order = Order.find_by(uid: order_id)
- assert_equal "Draft", order.state
- end
-
- end
-end
\ No newline at end of file
diff --git a/rails_application/test/orders/discount_test.rb b/rails_application/test/orders/discount_test.rb
deleted file mode 100644
index 27482ee5f..000000000
--- a/rails_application/test/orders/discount_test.rb
+++ /dev/null
@@ -1,116 +0,0 @@
-require "test_helper"
-
-module Orders
- class DiscountTest < InMemoryTestCase
- cover "Orders*"
-
- def test_discount_set
- customer_id = SecureRandom.uuid
- product_id = SecureRandom.uuid
- order_id = SecureRandom.uuid
- customer_registered(customer_id)
- prepare_product(product_id)
- item_added_to_basket(order_id, product_id)
-
- set_percentage_discount(order_id)
-
- order = Order.find_by(uid: order_id)
- assert_equal 50, order.total_value
- assert_equal 45, order.discounted_value
- assert_equal 10, order.percentage_discount
- assert event_store.event_in_stream?(event_store.read.of_type([Pricing::PercentageDiscountSet]).last.event_id, "Orders$all")
- end
-
- def test_discount_changed
- customer_id = SecureRandom.uuid
- product_id = SecureRandom.uuid
- order_id = SecureRandom.uuid
- customer_registered(customer_id)
- prepare_product(product_id)
- item_added_to_basket(order_id, product_id)
- set_percentage_discount(order_id)
-
- change_percentage_discount(order_id)
-
- order = Order.find_by(uid: order_id)
- assert_equal 50, order.total_value
- assert_equal 49.5, order.discounted_value
- assert_equal 1, order.percentage_discount
- assert event_store.event_in_stream?(event_store.read.of_type([Pricing::PercentageDiscountChanged]).last.event_id, "Orders$all")
- end
-
- def test_reset_discount
- customer_id = SecureRandom.uuid
- product_id = SecureRandom.uuid
- order_id = SecureRandom.uuid
- customer_registered(customer_id)
- prepare_product(product_id)
- item_added_to_basket(order_id, product_id)
- set_percentage_discount(order_id)
-
- reset_percentage_discount(order_id)
-
- order = Order.find_by(uid: order_id)
- assert_equal(50, order.total_value)
- assert_equal(50, order.discounted_value)
- assert_nil(order.percentage_discount)
- assert event_store.event_in_stream?(event_store.read.of_type([Pricing::PercentageDiscountReset]).last.event_id, "Orders$all")
- end
-
- def test_newest_event_is_always_applied
- customer_id = SecureRandom.uuid
- product_id = SecureRandom.uuid
- order_id = SecureRandom.uuid
- customer_registered(customer_id)
- prepare_product(product_id)
- item_added_to_basket(order_id, product_id)
-
- event_store.publish(Pricing::PercentageDiscountSet.new(data: { order_id: order_id, amount: 30 }, metadata: { timestamp: Time.current }))
- event_store.publish(Pricing::PercentageDiscountSet.new(data: { order_id: order_id, amount: 20 }, metadata: { timestamp: 1.minute.ago }))
-
- assert_equal 30, Orders::Order.find_by(uid: order_id).percentage_discount
- end
-
- private
-
- def reset_percentage_discount(order_id)
- run_command(Pricing::ResetPercentageDiscount.new(order_id: order_id))
- end
-
- def set_percentage_discount(order_id)
- run_command(Pricing::SetPercentageDiscount.new(order_id: order_id, amount: 10))
- end
-
- def change_percentage_discount(order_id)
- run_command(Pricing::ChangePercentageDiscount.new(order_id: order_id, amount: 1))
- end
-
- def item_added_to_basket(order_id, product_id)
- event_store.publish(Ordering::ItemAddedToBasket.new(data: { product_id: product_id, order_id: order_id, quantity_before: 0 }))
- end
-
- def prepare_product(product_id)
- run_command(
- ProductCatalog::RegisterProduct.new(
- product_id: product_id,
- )
- )
- run_command(
- ProductCatalog::NameProduct.new(
- product_id: product_id,
- name: "test"
- )
- )
- run_command(Pricing::SetPrice.new(product_id: product_id, price: 50))
- end
-
- def customer_registered(customer_id)
- event_store.publish(Crm::CustomerRegistered.new(data: { customer_id: customer_id, name: "Arkency" }))
- end
-
- def event_store
- Rails.configuration.event_store
- end
- end
-end
-
diff --git a/rails_application/test/orders/item_added_to_basket_test.rb b/rails_application/test/orders/item_added_to_basket_test.rb
deleted file mode 100644
index 10540ae34..000000000
--- a/rails_application/test/orders/item_added_to_basket_test.rb
+++ /dev/null
@@ -1,186 +0,0 @@
-require "test_helper"
-
-module Orders
- class ItemAddedToBasketTest < InMemoryTestCase
- cover "Orders*"
-
- def setup
- super
- Order.destroy_all
- OrderLine.destroy_all
- end
-
- def test_add_new_item
- event_store = Rails.configuration.event_store
-
- product_id = SecureRandom.uuid
- run_command(
- ProductCatalog::RegisterProduct.new(
- product_id: product_id,
- )
- )
- run_command(
- ProductCatalog::NameProduct.new(
- product_id: product_id,
- name: "test"
- )
- )
- run_command(Pricing::SetPrice.new(product_id: product_id, price: 49))
-
- order_id = SecureRandom.uuid
-
- item_added_to_basket = Ordering::ItemAddedToBasket.new(
- data: {
- order_id: order_id,
- product_id: product_id,
- quantity_before: 0
- }
- )
- event_store.publish(item_added_to_basket)
-
- assert_equal(OrderLine.count, 1)
- order_line = OrderLine.find_by(order_uid: order_id)
- assert_equal(order_line.product_id, product_id)
- assert_equal(order_line.product_name, "test")
- assert_equal(order_line.quantity, 1)
- assert_equal(order_line.price, 49)
- assert_equal(order_line.value, 49)
- assert event_store.event_in_stream?(item_added_to_basket.event_id, "Orders$all")
-
- assert_equal(Order.count, 1)
- order = Order.find_by(uid: order_id)
- assert_equal(order.state, "Draft")
- assert_nil(order.customer)
- assert_nil(order.number)
- end
-
- def test_add_the_same_item_2nd_time
- event_store = Rails.configuration.event_store
-
- product_id = SecureRandom.uuid
- run_command(
- ProductCatalog::RegisterProduct.new(
- product_id: product_id,
- )
- )
- run_command(
- ProductCatalog::NameProduct.new(
- product_id: product_id,
- name: "test"
- )
- )
- run_command(Pricing::SetPrice.new(product_id: product_id, price: 49))
-
- order_id = SecureRandom.uuid
- event_store.publish(
- Ordering::ItemAddedToBasket.new(
- data: {
- order_id: order_id,
- product_id: product_id,
- quantity_before: 0
- }
- )
- )
-
- event_store.publish(
- Ordering::ItemAddedToBasket.new(
- data: {
- order_id: order_id,
- product_id: product_id,
- quantity_before: 1
- }
- )
- )
-
- assert_equal(OrderLine.count, 1)
- order_line = OrderLine.find_by(order_uid: order_id)
- assert_equal(order_line.product_id, product_id)
- assert_equal(order_line.product_name, "test")
- assert_equal(order_line.quantity, 2)
- assert_equal(order_line.price, 49)
- assert_equal(order_line.value, 98)
-
- assert_equal(Order.count, 1)
- order = Order.find_by(uid: order_id)
- assert_equal(order.state, "Draft")
- assert_nil(order.customer)
- assert_nil(order.number)
- end
-
- def test_add_another_item
- event_store = Rails.configuration.event_store
-
- product_id = SecureRandom.uuid
- run_command(
- ProductCatalog::RegisterProduct.new(
- product_id: product_id,
- )
- )
- run_command(
- ProductCatalog::NameProduct.new(
- product_id: product_id,
- name: "test"
- )
- )
- run_command(Pricing::SetPrice.new(product_id: product_id, price: 20))
-
- another_product_id = SecureRandom.uuid
- run_command(
- ProductCatalog::RegisterProduct.new(
- product_id: another_product_id,
- )
- )
- run_command(
- ProductCatalog::NameProduct.new(
- product_id: another_product_id,
- name: "2nd one"
- )
- )
- run_command(
- Pricing::SetPrice.new(product_id: another_product_id, price: 20)
- )
-
- order_id = SecureRandom.uuid
- event_store.publish(
- Ordering::ItemAddedToBasket.new(
- data: {
- order_id: order_id,
- product_id: product_id,
- quantity_before: 0
- }
- )
- )
-
- event_store.publish(
- Ordering::ItemAddedToBasket.new(
- data: {
- order_id: order_id,
- product_id: another_product_id,
- quantity_before: 0
- }
- )
- )
-
- order = Orders::Order.find_by(uid: order_id)
- assert_equal(order.order_lines.count, 2)
- order_lines = order.order_lines
- assert_equal(
- [product_id, another_product_id],
- order_lines.map(&:product_id)
- )
- assert_equal(order_lines[0].product_id, product_id)
- assert_equal(order_lines[0].product_name, "test")
- assert_equal(order_lines[0].quantity, 1)
-
- assert_equal(order_lines[1].product_id, another_product_id)
- assert_equal(order_lines[1].product_name, "2nd one")
- assert_equal(order_lines[1].quantity, 1)
-
- assert_equal(Order.count, 1)
- order = Order.find_by(uid: order_id)
- assert_equal(order.state, "Draft")
- assert_nil(order.customer)
- assert_nil(order.number)
- end
- end
-end
diff --git a/rails_application/test/orders/item_removed_from_basket_test.rb b/rails_application/test/orders/item_removed_from_basket_test.rb
deleted file mode 100644
index 3cea55968..000000000
--- a/rails_application/test/orders/item_removed_from_basket_test.rb
+++ /dev/null
@@ -1,185 +0,0 @@
-require "test_helper"
-
-module Orders
- class ItemRemovedFromBasketTest < InMemoryTestCase
- cover "Orders*"
-
- def setup
- super
- Order.destroy_all
- OrderLine.destroy_all
- Product.destroy_all
- end
-
- def test_remove_item_when_quantity_gt_1
- event_store = Rails.configuration.event_store
-
- product_id = SecureRandom.uuid
- run_command(
- ProductCatalog::RegisterProduct.new(
- product_id: product_id
- )
- )
- run_command(
- ProductCatalog::NameProduct.new(
- product_id: product_id,
- name: "something"
- )
- )
- run_command(Pricing::SetPrice.new(product_id: product_id, price: 20))
- customer_id = SecureRandom.uuid
- run_command(
- Crm::RegisterCustomer.new(customer_id: customer_id, name: "dummy")
- )
- order_id = SecureRandom.uuid
- event_store.publish(
- Ordering::ItemAddedToBasket.new(
- data: {
- order_id: order_id,
- product_id: product_id,
- quantity_before: 0
- }
- )
- )
- event_store.publish(
- Ordering::ItemAddedToBasket.new(
- data: {
- order_id: order_id,
- product_id: product_id,
- quantity_before: 1
- }
- )
- )
- item_removed_from_basket = Ordering::ItemRemovedFromBasket.new(
- data: {
- order_id: order_id,
- product_id: product_id
- }
- )
- event_store.publish(item_removed_from_basket)
-
- assert_equal(1, OrderLine.count)
- order_line = OrderLine.find_by(order_uid: order_id)
- assert_equal(order_line.product_id, product_id)
- assert_equal("something", order_line.product_name)
- assert_equal(1, order_line.quantity)
- assert event_store.event_in_stream?(item_removed_from_basket.event_id, "Orders$all")
- end
-
- def test_remove_item_when_quantity_eq_1
- event_store = Rails.configuration.event_store
-
- product_id = SecureRandom.uuid
- run_command(
- ProductCatalog::RegisterProduct.new(
- product_id: product_id
- )
- )
- run_command(Pricing::SetPrice.new(product_id: product_id, price: 20))
- customer_id = SecureRandom.uuid
- run_command(
- Crm::RegisterCustomer.new(customer_id: customer_id, name: "dummy")
- )
- order_id = SecureRandom.uuid
- event_store.publish(
- Ordering::ItemAddedToBasket.new(
- data: {
- order_id: order_id,
- product_id: product_id,
- quantity_before: 0
- }
- )
- )
- event_store.publish(
- Ordering::ItemRemovedFromBasket.new(
- data: {
- order_id: order_id,
- product_id: product_id
- }
- )
- )
-
- assert_equal(0, OrderLine.count)
- end
-
- def test_remove_item_when_there_is_another_item
- event_store = Rails.configuration.event_store
-
- product_id = SecureRandom.uuid
- run_command(
- ProductCatalog::RegisterProduct.new(
- product_id: product_id
- )
- )
- run_command(
- ProductCatalog::NameProduct.new(
- product_id: product_id,
- name: "test"
- )
- )
- run_command(Pricing::SetPrice.new(product_id: product_id, price: 20))
-
- another_product_id = SecureRandom.uuid
- run_command(
- ProductCatalog::RegisterProduct.new(
- product_id: another_product_id
- )
- )
- run_command(
- ProductCatalog::NameProduct.new(
- product_id: another_product_id,
- name: "test2"
- )
- )
- run_command(
- Pricing::SetPrice.new(product_id: another_product_id, price: 20)
- )
- customer_id = SecureRandom.uuid
- run_command(
- Crm::RegisterCustomer.new(customer_id: customer_id, name: "dummy")
- )
- order_id = SecureRandom.uuid
- event_store.publish(
- Ordering::ItemAddedToBasket.new(
- data: {
- order_id: order_id,
- product_id: product_id,
- quantity_before: 0
- }
- )
- )
- event_store.publish(
- Ordering::ItemAddedToBasket.new(
- data: {
- order_id: order_id,
- product_id: product_id,
- quantity_before: 1
- }
- )
- )
- event_store.publish(
- Ordering::ItemAddedToBasket.new(
- data: {
- order_id: order_id,
- product_id: another_product_id,
- quantity_before: 0
- }
- )
- )
- event_store.publish(
- Ordering::ItemRemovedFromBasket.new(
- data: {
- order_id: order_id,
- product_id: another_product_id
- }
- )
- )
-
- assert_equal(1, OrderLine.count,)
- order_lines = OrderLine.where(order_uid: order_id)
- assert_equal(product_id, order_lines[0].product_id)
- assert_equal("test", order_lines[0].product_name)
- assert_equal(2, order_lines[0].quantity)
- end
- end
-end
diff --git a/rails_application/test/orders/order_cancelled_test.rb b/rails_application/test/orders/order_cancelled_test.rb
deleted file mode 100644
index e075fd797..000000000
--- a/rails_application/test/orders/order_cancelled_test.rb
+++ /dev/null
@@ -1,44 +0,0 @@
-require "test_helper"
-
-module Orders
- class OrderCancelledTest < InMemoryTestCase
- cover "Orders*"
-
- def setup
- super
- Customer.destroy_all
- Order.destroy_all
- end
-
- def test_cancel_confirmed_order
- event_store = Rails.configuration.event_store
- customer_id = SecureRandom.uuid
- order_id = SecureRandom.uuid
- order_number = Ordering::FakeNumberGenerator::FAKE_NUMBER
-
- event_store.publish(Crm::CustomerRegistered.new(
- data: {
- customer_id: customer_id,
- name: "John Doe"
- }
- ))
-
- run_command(Ordering::SubmitOrder.new(order_id: order_id, order_number: order_number))
-
- order_cancelled = Fulfillment::OrderCancelled.new(
- data: {
- order_id: order_id
- }
- )
- event_store.publish(order_cancelled)
-
-
- orders = Order.all
- assert_not_empty(orders)
- assert_equal(1, orders.count)
- assert_equal(order_number, orders.first.number)
- assert_equal("Cancelled", orders.first.state)
- assert event_store.event_in_stream?(order_cancelled.event_id, "Orders$all")
- end
- end
-end
\ No newline at end of file
diff --git a/rails_application/test/orders/order_expired_test.rb b/rails_application/test/orders/order_expired_test.rb
deleted file mode 100644
index 59e5534bf..000000000
--- a/rails_application/test/orders/order_expired_test.rb
+++ /dev/null
@@ -1,58 +0,0 @@
-require "test_helper"
-
-module Orders
- class OrderExpiredTest < InMemoryTestCase
- cover "Orders*"
-
- def setup
- super
- Order.destroy_all
- end
-
- def test_expire_created_order
- event_store = Rails.configuration.event_store
-
- customer_id = SecureRandom.uuid
- run_command(
- Crm::RegisterCustomer.new(customer_id: customer_id, name: "dummy")
- )
- product_id = SecureRandom.uuid
- run_command(
- ProductCatalog::RegisterProduct.new(
- product_id: product_id
- )
- )
- run_command(Pricing::SetPrice.new(product_id: product_id, price: 39))
-
- order_id = SecureRandom.uuid
- order_number = Ordering::FakeNumberGenerator::FAKE_NUMBER
- event_store.publish(
- Ordering::ItemAddedToBasket.new(
- data: {
- order_id: order_id,
- product_id: product_id,
- quantity_before: 0
- }
- )
- )
- event_store.publish(
- Ordering::OrderPlaced.new(
- data: {
- order_id: order_id,
- order_number: order_number,
- customer_id: customer_id,
- order_lines: { product_id => 1 }
- }
- )
- )
-
- order_expired = Ordering::OrderExpired.new(data: { order_id: order_id })
- event_store.publish(order_expired)
-
- assert_equal(Order.count, 1)
- order = Order.find_by(uid: order_id)
- assert_equal(order.state, "Expired")
- assert event_store.event_in_stream?(order_expired.event_id, "Orders$all")
- end
- end
-end
diff --git a/rails_application/test/orders/order_line_test.rb b/rails_application/test/orders/order_line_test.rb
deleted file mode 100644
index b338b3e4b..000000000
--- a/rails_application/test/orders/order_line_test.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-require "test_helper"
-
-module Orders
- class OrderLineTest < InMemoryTestCase
- cover "Orders*"
-
- def test_calculates_value_correctly
- order_line = OrderLine.new(price: 10, quantity: 1)
- assert_equal 10, order_line.value
-
- order_line = OrderLine.new(price: 10, quantity: 0)
- assert_equal 0, order_line.value
-
- order_line = OrderLine.new(price: 10, quantity: 10)
- assert_equal 100, order_line.value
- end
- end
-end
\ No newline at end of file
diff --git a/rails_application/test/orders/order_paid_test.rb b/rails_application/test/orders/order_paid_test.rb
deleted file mode 100644
index c28553ad6..000000000
--- a/rails_application/test/orders/order_paid_test.rb
+++ /dev/null
@@ -1,41 +0,0 @@
-require "test_helper"
-
-module Orders
- class OrderConfirmedTest < InMemoryTestCase
- cover "Orders*"
-
- def setup
- super
- Customer.destroy_all
- Order.destroy_all
- end
-
- def test_order_confirmed
- event_store = Rails.configuration.event_store
- customer_id = SecureRandom.uuid
- order_id = SecureRandom.uuid
- order_number = Ordering::FakeNumberGenerator::FAKE_NUMBER
-
- run_command(Crm::RegisterCustomer.new(customer_id: customer_id, name: "John Doe"))
- run_command(Ordering::SubmitOrder.new(order_id: order_id, order_number: order_number))
- run_command(
- Crm::AssignCustomerToOrder.new(customer_id: customer_id, order_id: order_id)
- )
-
- event_store.publish(Pricing::OrderTotalValueCalculated.new(data: { order_id: order_id, discounted_amount: 0, total_amount: 10 }))
- order_confirmed = Fulfillment::OrderConfirmed.new(
- data: {
- order_id: order_id
- }
- )
- event_store.publish(order_confirmed)
-
- orders = Order.all
- assert_not_empty(orders)
- assert_equal(1, orders.count)
- assert_equal(order_number, orders.first.number)
- assert_equal("Paid", orders.first.state)
- assert event_store.event_in_stream?(order_confirmed.event_id, "Orders$all")
- end
- end
-end
\ No newline at end of file
diff --git a/rails_application/test/orders/order_placed_test.rb b/rails_application/test/orders/order_placed_test.rb
deleted file mode 100644
index 1a69cf23f..000000000
--- a/rails_application/test/orders/order_placed_test.rb
+++ /dev/null
@@ -1,71 +0,0 @@
-require "test_helper"
-
-module Orders
- class OrderPlacedTest < InMemoryTestCase
- include ActiveJob::TestHelper
- cover "Orders"
-
- def setup
- super
- Order.destroy_all
- OrderLine.destroy_all
- end
-
- def test_create_when_not_exists
- event_store = Rails.configuration.event_store
-
- product_id = SecureRandom.uuid
- run_command(
- ProductCatalog::RegisterProduct.new(
- product_id: product_id
- )
- )
- run_command(Pricing::SetPrice.new(product_id: product_id, price: 20))
- order_id = SecureRandom.uuid
- order_number = Ordering::FakeNumberGenerator::FAKE_NUMBER
-
- event_store.publish(
- Ordering::ItemAddedToBasket.new(
- data: {
- order_id: order_id,
- product_id: product_id,
- quantity_before: 0
- }
- )
- )
- order_placed = Ordering::OrderPlaced.new(
- data: {
- order_id: order_id,
- order_number: order_number,
- order_lines: { product_id => 1 }
- }
- )
- event_store.publish(order_placed)
-
- assert_equal(Order.count, 1)
- order = Order.find_by(uid: order_id)
- assert_equal(order.state, "Submitted")
- assert_equal(order.number, order_number)
-
- assert_enqueued_with(
- job: Turbo::Streams::ActionBroadcastJob,
- args: action_broadcast_args(order_id, 'Submitted')
- )
- assert event_store.event_in_stream?(order_placed.event_id, "Orders$all")
- end
-
- private
-
- def action_broadcast_args(order_uid, state)
- [
- "orders_order_#{order_uid}",
- {
- action: :update,
- target: "orders_order_#{order_uid}_state",
- targets: nil,
- html: state
- }
- ]
- end
- end
-end
diff --git a/rails_application/test/orders/product_price_changed_test.rb b/rails_application/test/orders/product_price_changed_test.rb
deleted file mode 100644
index b53f19cd0..000000000
--- a/rails_application/test/orders/product_price_changed_test.rb
+++ /dev/null
@@ -1,51 +0,0 @@
-require "test_helper"
-
-module Orders
- class ProductPriceChangedTest < InMemoryTestCase
- cover "Orders*"
-
- def test_reflects_change
- product_id = prepare_product
- unchanged_product_id = prepare_product
-
- run_command(Pricing::SetPrice.new(product_id: product_id, price: 100))
-
- assert_equal 100, Product.find_by_uid(product_id).price
- assert_equal 50, Product.find_by_uid(unchanged_product_id).price
- price_events = event_store.read.of_type([Pricing::PriceSet]).to_a.first
- assert event_store.event_in_stream?(price_events.event_id, "Orders$all")
- end
-
- def test_race_condition
- product_id = SecureRandom.uuid
- run_command(Pricing::SetPrice.new(product_id: product_id, price: 100))
- run_command(ProductCatalog::RegisterProduct.new(product_id: product_id))
-
- assert_equal 100, Product.find_by_uid(product_id).price
- end
-
- private
-
- def event_store
- Rails.configuration.event_store
- end
-
- def prepare_product
- product_id = SecureRandom.uuid
- run_command(
- ProductCatalog::RegisterProduct.new(
- product_id: product_id,
- )
- )
- run_command(
- ProductCatalog::NameProduct.new(
- product_id: product_id,
- name: "test"
- )
- )
- run_command(Pricing::SetPrice.new(product_id: product_id, price: 50))
-
- product_id
- end
- end
-end
diff --git a/rails_application/test/orders/register_product_test.rb b/rails_application/test/orders/register_product_test.rb
deleted file mode 100644
index cfee5b4b0..000000000
--- a/rails_application/test/orders/register_product_test.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-require "test_helper"
-
-module Orders
- class RegisterProductTest < InMemoryTestCase
- cover "Orders*"
-
- def test_register_product
- product_id = SecureRandom.uuid
- product_registered = ProductCatalog::ProductRegistered.new(data: { product_id: product_id })
- event_store.publish(product_registered)
- product = Product.find_by_uid(product_id)
- assert_equal(product_id, product.uid)
- product_named = ProductCatalog::ProductNamed.new(data: { product_id: product_id, name: "Async Remote" })
-
- event_store.publish(product_named)
-
- product = Product.find_by_uid(product_id)
- assert product
- assert_equal "Async Remote", product.name
- assert event_store.event_in_stream?(product_registered.event_id, "Orders$all")
- assert event_store.event_in_stream?(product_named.event_id, "Orders$all")
- end
-
- private
-
- def event_store
- Rails.configuration.event_store
- end
- end
-end
diff --git a/rails_application/test/orders/update_order_total_value_test.rb b/rails_application/test/orders/update_order_total_value_test.rb
deleted file mode 100644
index 50f432b64..000000000
--- a/rails_application/test/orders/update_order_total_value_test.rb
+++ /dev/null
@@ -1,65 +0,0 @@
-require "test_helper"
-
-module Orders
- class UpdateOrderTotalValueTest < InMemoryTestCase
- cover "Orders*"
-
- def test_order_created_has_draft_state
- customer_id = SecureRandom.uuid
- product_id = SecureRandom.uuid
- order_id = SecureRandom.uuid
- customer_registered(customer_id)
- prepare_product(product_id)
-
- event_store.publish(Pricing::OrderTotalValueCalculated.new(data: { order_id: order_id, discounted_amount: 0, total_amount: 10 }))
-
- order = Orders::Order.find_by(uid: order_id)
- assert_equal "Draft", order.state
- end
-
- def test_newest_event_is_always_applied
- customer_id = SecureRandom.uuid
- product_id = SecureRandom.uuid
- order_id = SecureRandom.uuid
- customer_registered(customer_id)
- prepare_product(product_id)
- item_added_to_basket(order_id, product_id)
-
- event_store.publish(Pricing::OrderTotalValueCalculated.new(data: { order_id: order_id, discounted_amount: 0, total_amount: 10 }, metadata: { timestamp: Time.current }))
- event_store.publish(Pricing::OrderTotalValueCalculated.new(data: { order_id: order_id, amount: 20, discounted_amount: 10, total_amount: 20 }, metadata: { timestamp: 1.minute.ago }))
-
- order = Orders::Order.find_by(uid: order_id)
- assert_equal 10, order.total_value
- assert_equal 0, order.discounted_value
- end
-
- private
-
- def item_added_to_basket(order_id, product_id)
- event_store.publish(Ordering::ItemAddedToBasket.new(data: { product_id: product_id, order_id: order_id, quantity_before: 0 }))
- end
-
- def prepare_product(product_id)
- run_command(
- ProductCatalog::RegisterProduct.new(
- product_id: product_id,
- )
- )
- run_command(
- ProductCatalog::NameProduct.new(
- product_id: product_id,
- name: "test"
- )
- )
- run_command(Pricing::SetPrice.new(product_id: product_id, price: 50))
- end
-
- def customer_registered(customer_id)
- event_store.publish(Crm::CustomerRegistered.new(data: { customer_id: customer_id, name: "Arkency" }))
- end
-
- def event_store
- Rails.configuration.event_store
- end
- end
-end
diff --git a/rails_application/test/problems/build_most_recent_products_in_unfinished_orders_test.rb b/rails_application/test/problems/build_most_recent_products_in_unfinished_orders_test.rb
new file mode 100644
index 000000000..1bf66ac82
--- /dev/null
+++ b/rails_application/test/problems/build_most_recent_products_in_unfinished_orders_test.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+require 'test_helper'
+
+class BuildMostRecentProductsInUnfinishedOrdersTest < InMemoryTestCase
+ def test_build
+ product_1_id = 1
+ product_2_id = 2
+ product_3_id = 3
+ event_store.publish(ProductCatalog::ProductCreated.new(data: { id: product_1_id, name: 'Product 1' }), stream_name: 'ProductCatalog::Product$1')
+ event_store.publish(ProductCatalog::ProductCreated.new(data: { id: product_2_id, name: 'Product 2' }), stream_name: 'ProductCatalog::Product$2')
+ event_store.publish(ProductCatalog::ProductCreated.new(data: { id: product_3_id, name: 'Product 3' }), stream_name: 'ProductCatalog::Product$3')
+
+ order_1_id = 1
+ order_2_id = 2
+
+ event_store.publish(Ordering::OrderCreated.new(data: { id: order_1_id }), stream_name: "Ordering::Order$#{order_1_id}")
+ event_store.publish(Ordering::ItemAdded.new(data: { order_id: order_1_id, product_id: product_1_id }), stream_name: "Ordering::Order$#{order_1_id}")
+ event_store.publish(Ordering::ItemAdded.new(data: { order_id: order_1_id, product_id: product_1_id }), stream_name: "Ordering::Order$#{order_1_id}")
+ event_store.publish(Ordering::ItemAdded.new(data: { order_id: order_1_id, product_id: product_1_id }), stream_name: "Ordering::Order$#{order_1_id}")
+ event_store.publish(Ordering::OrderSubmitted.new(data: { id: order_1_id }), stream_name: "Ordering::Order$#{order_1_id}")
+ event_store.publish(Ordering::OrderExpired.new(data: { id: order_1_id }), stream_name: "Ordering::Order$#{order_1_id}")
+
+ event_store.publish(Ordering::OrderCreated.new(data: { id: order_2_id }), stream_name: "Ordering::Order$#{order_1_id}")
+ event_store.publish(Ordering::ItemAdded.new(data: { order_id: order_2_id, product_id: product_1_id }), stream_name: "Ordering::Order$#{order_1_id}")
+ event_store.publish(Ordering::OrderPaid.new(data: { id: order_2_id }), stream_name: "Ordering::Order$#{order_2_id}")
+
+
+ MostRecentProductsInUnfinishedOrders.find_by(product_id: product_1_id).tap do |report|
+ assert_equal 1, report.number_of_unfinished_orders
+ assert_equal 3, report.number_of_items_in_unfinished_orders
+ assert_equal [order_1_id], report.order_ids
+ end
+ end
+
+ private
+
+ def event_store
+ Rails.configuration.event_store
+ end
+end
diff --git a/rails_application/test/problems/process_manager_test.rb b/rails_application/test/problems/process_manager_test.rb
new file mode 100644
index 000000000..3a2dfe1cb
--- /dev/null
+++ b/rails_application/test/problems/process_manager_test.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+require "test_helper"
+
+class ProcessManagerTest < InMemoryTestCase
+ def test_schedules_email_sending_only_when_process_is_finished
+ event_store = Rails.configuration.event_store
+ email_client = Rails.configuration.email_client
+ Rails.configuration.email_client = FakeEmailClient.new
+
+ event_store.publish(Ordering::OrderPaid.new(data: { id: "111" }))
+ event_store.publish(Invoicing::InvoiceGenerated.new(data: { order_id: "111" }))
+
+ assert_equal ["111"], Rails.configuration.email_client.sent_emails
+ ensure
+ Rails.configuration.email_client = email_client
+ end
+
+ class FakeEmailClient
+ def initialize
+ @sent_emails = []
+ end
+
+ def send_email(order_id)
+ @sent_emails << order_id
+ end
+
+ def sent_emails
+ @sent_emails
+ end
+ end
+end
\ No newline at end of file
diff --git a/rails_application/test/problems/saving_issues_when_two_admins_work_on_same_product_test.rb b/rails_application/test/problems/saving_issues_when_two_admins_work_on_same_product_test.rb
new file mode 100644
index 000000000..cff5b65dd
--- /dev/null
+++ b/rails_application/test/problems/saving_issues_when_two_admins_work_on_same_product_test.rb
@@ -0,0 +1,65 @@
+require_relative '../test_helper'
+
+class SavingIssuesWhenTwoAdminsWorkOnSameProductTest < RealRESIntegrationTestCase
+ def before_setup
+ post "/customers", params: { customer: { first_name: "Arkency", last_name: "Arkency", email: "lukasz@arkency.com" } }
+ post "/products", params: { product: { name: 'book', price: 10.00, vat_rate: 10, sku: "B00K" } }
+ super
+ end
+
+ def test_two_admins_cannot_work_on_same_product_stable
+ admin_works_on_product_details = the_product
+ admin_works_on_product_stock = the_product
+ # When first admin updates quantity
+ post "/products/#{admin_works_on_product_stock.id}/supplies", params: { quantity: 10 }
+ # When second admin updates name
+ patch "/products/#{admin_works_on_product_details.id}", params: { product: { name: 'bUk', version: admin_works_on_product_details.version } }
+ # Then the second admins change is not saved and they lose time
+ refute response.status == 409
+ end
+
+ def test_when_admins_works_on_product_they_are_blocked_by_customers_ordering
+ product = the_product
+ supply_product(product)
+ order = Order.create!(total: 0)
+ order_id = order.id
+ product_id = product.id
+ initial_version = product.version
+ latch = Concurrent::CountDownLatch.new
+ admin_result = nil
+
+ thread1 = Thread.new do
+ latch.wait
+ post "/orders/#{order_id}/add_item?product_id=#{product_id}"
+ end
+
+ thread2 = Thread.new do
+ latch.wait
+ patch "/products/#{product.id}", params: { product: { name: 'bUk', version: initial_version } }
+ admin_result = response.status
+ end
+
+ thread3 = Thread.new do
+ latch.wait
+ post "/orders/#{order_id}/add_item?product_id=#{product_id}"
+ end
+
+ threads = [thread1, thread2, thread3]
+ threads << Thread.new do
+ latch.count_down
+ end
+ threads.each(&:join)
+
+ refute admin_result == 409
+ end
+
+ private
+
+ def supply_product(product)
+ post "/products/#{product.id}/supplies", params: { quantity: 10 }
+ end
+
+ def the_product
+ Product.last
+ end
+end
\ No newline at end of file
diff --git a/rails_application/test/products/update_future_prices_calenar_test.rb b/rails_application/test/products/update_future_prices_calenar_test.rb
deleted file mode 100644
index b35142e02..000000000
--- a/rails_application/test/products/update_future_prices_calenar_test.rb
+++ /dev/null
@@ -1,43 +0,0 @@
-require "test_helper"
-
-module Products
- class UpdateFuturePricesCalendarTest < InMemoryTestCase
- cover "Products*"
-
- def test_add_future_price
- product_id = SecureRandom.uuid
- product_registered = ProductCatalog::ProductRegistered.new(data: { product_id: product_id })
- product_named = ProductCatalog::ProductNamed.new(data: { product_id: product_id, name: "Async Remote" })
- set_price = Pricing::PriceSet.new(data: { product_id: product_id, price: 10 })
- event_store.publish(product_registered)
- event_store.publish(product_named)
- event_store.publish(set_price)
-
- date_1 = 1.hour.from_now
- date_2 = 2.hours.from_now
- date_3 = 3.hours.from_now
-
- product = Product.find_by_id(product_id)
- assert_equal [], product.future_prices_calendar
-
- run_command(Pricing::SetFuturePrice.new(product_id: product_id, price: BigDecimal("12.01"), valid_since: date_3 ))
- run_command(Pricing::SetFuturePrice.new(product_id: product_id, price: BigDecimal("1.0"), valid_since: date_1 ))
- run_command(Pricing::SetFuturePrice.new(product_id: product_id, price: BigDecimal("2.0"), valid_since: date_2 ))
-
- product.reload
- assert_equal 4, product.current_prices_calendar.length
- assert_equal [
- BigDecimal("10.0"),
- BigDecimal("1.0"),
- BigDecimal("2.0"),
- BigDecimal("12.01")
- ], product.current_prices_calendar.map { |e| e[:price] }
- end
-
- private
-
- def event_store
- Rails.configuration.event_store
- end
- end
-end
diff --git a/rails_application/test/public_offer/product_price_changed_test.rb b/rails_application/test/public_offer/product_price_changed_test.rb
deleted file mode 100644
index 2b82214cb..000000000
--- a/rails_application/test/public_offer/product_price_changed_test.rb
+++ /dev/null
@@ -1,127 +0,0 @@
-require "test_helper"
-
-module PublicOffer
- class ProductPriceChangedTest < InMemoryTestCase
- cover "PublicOffer*"
-
- def test_reflects_change
- product_id = prepare_product
- unchanged_product_id = prepare_product
-
- set_price(product_id, 100)
-
- assert_equal 100, Product.find(product_id).price
- assert_equal 50, Product.find(unchanged_product_id).price
- end
-
- def test_registers_lowest_recent_price
- product_id = prepare_product
-
- set_price(product_id, 40)
-
- assert_equal 40, Product.find(product_id).lowest_recent_price
- end
-
- def test_keeps_lowest_recent_price
- product_id = prepare_product
-
- set_price(product_id, 100)
- set_price(product_id, 20)
- set_price(product_id, 50)
- set_price(product_id, 70)
-
- assert_equal 20, Product.find(product_id).lowest_recent_price
- end
-
- def test_takes_into_account_price_set_before_30_days
- product_id = prepare_product
-
- set_price(product_id, 100)
- set_past_price(product_id, 15, 31.days.ago.beginning_of_day)
- set_price(product_id, 50)
- set_price(product_id, 70)
-
- assert_equal 15, Product.find(product_id).lowest_recent_price
- end
-
- def test_ignores_prices_older_than_30_days
- freeze_time do
- product_id = prepare_product
-
- set_past_price(product_id, 20, 31.days.ago.beginning_of_day)
- set_past_price(product_id, 25, 30.days.ago.beginning_of_day - 1.second)
- set_past_price(product_id, 30, 30.days.ago.beginning_of_day)
- set_past_price(product_id, 32, 30.days.ago.beginning_of_day + 1.second)
- set_past_price(product_id, 35, 30.days.ago)
- set_past_price(product_id, 40, 29.days.ago.beginning_of_day)
-
- assert_equal 30, Product.find(product_id).lowest_recent_price
- end
- end
-
- def test_ignores_future_prices
- product_id = prepare_product
-
- set_past_price(product_id, 45, 2.days.ago)
- set_price(product_id, 35)
- set_future_price(product_id, 11, 2.days.from_now)
-
- assert_equal 35, Product.find(product_id).lowest_recent_price
- end
-
- def test_ignores_other_products
- product1_id = prepare_product
- product2_id = prepare_product
-
- set_price(product1_id, 35)
- set_price(product2_id, 25)
- set_price(product1_id, 30)
-
- assert_equal 30, Product.find(product1_id).lowest_recent_price
- end
-
- def test_takes_last_event_when_no_events_in_last_30_days
- product_id = SecureRandom.uuid
- run_command(
- ProductCatalog::RegisterProduct.new(
- product_id: product_id,
- )
- )
-
- set_past_price(product_id, 15, 45.days.ago)
- set_past_price(product_id, 45, 32.days.ago)
- set_future_price(product_id, 11, 2.days.from_now)
-
- assert_equal 45, Product.find(product_id).lowest_recent_price
- end
-
- private
-
- def prepare_product
- product_id = SecureRandom.uuid
- run_command(
- ProductCatalog::RegisterProduct.new(
- product_id: product_id,
- )
- )
- run_command(
- ProductCatalog::NameProduct.new(
- product_id: product_id,
- name: "test"
- )
- )
- run_command(Pricing::SetPrice.new(product_id: product_id, price: 50))
-
- product_id
- end
-
- def set_price(product_id, amount)
- run_command(Pricing::SetPrice.new(product_id: product_id, price: amount))
- end
-
- def set_future_price(product_id, amount, valid_since)
- run_command(Pricing::SetFuturePrice.new(product_id: product_id, price: amount, valid_since: valid_since))
- end
- alias set_past_price set_future_price
- end
-end
diff --git a/rails_application/test/read_model_handler_test.rb b/rails_application/test/read_model_handler_test.rb
deleted file mode 100644
index ae91ab73b..000000000
--- a/rails_application/test/read_model_handler_test.rb
+++ /dev/null
@@ -1,110 +0,0 @@
-require "test_helper"
-
-class ReadModelHandlerTest < InMemoryTestCase
- cover "ReadModelHandler*"
- cover "CreateRecord*"
- cover "CopyEventAttribute*"
-
- def test_create_record
- event = product_registered
- event_store.append(event)
- CreateRecord.new(event_store, PublicOffer::Product, :product_id).call(event)
- assert_equal 1, PublicOffer::Product.count
- end
-
- def test_dealing_with_at_least_once_delivery
- event = product_registered
- event_store.append(event)
- 2.times { CreateRecord.new(event_store, PublicOffer::Product, :product_id).call(event) }
- assert_equal 1, PublicOffer::Product.count
- end
-
- def test_copy
- event = product_named
- event_store.append(event)
- CopyEventAttribute.new(event_store, PublicOffer::Product, :product_id, :name, :name).call(event)
- assert_equal product_name, PublicOffer::Product.first.name
- end
-
- def test_copy_nested_attribute
- event = vat_rate_set
- event_store.append(event)
- CopyEventAttribute.new(event_store, Products::Product, :product_id, [:vat_rate, :code], :vat_rate_code).call(event)
- assert_equal available_vat_rate.code, Products::Product.first.vat_rate_code
- end
-
- def test_no_specific_order_expected
- event = product_registered
- event_store.append(event)
- PublicOffer::Product.create(id: product_id)
- CreateRecord.new(event_store, PublicOffer::Product, :product_id).call(event)
- assert_equal 1, PublicOffer::Product.count
- end
-
- def test_updating_with_newest_data
- event_store.append(first_event = product_named(product_name))
- event_store.append(second_event = product_named('New name'))
-
- handler = CopyEventAttribute.new(event_store, PublicOffer::Product, :product_id, :name, :name)
- handler.call(second_event)
- handler.call(first_event)
-
- assert_equal 'New name', PublicOffer::Product.first.name
- end
-
- def test_updating_with_newest_data_concurrently
- events = 3.times.map { |i| product_named("#{product_name} #{i}") }
- events += [product_named('New name')]
- events.each { |event| event_store.append(event) }
- handler = CopyEventAttribute.new(event_store, PublicOffer::Product, :product_id, :name, :name)
-
- assert_equal(5, ActiveRecord::Base.connection.pool.size)
-
- wait_for_it = true
- threads = events.reverse.map do |event|
- Thread.new do
- true while wait_for_it
- handler.call(event)
- end
- end
- wait_for_it = false
- threads.each(&:join)
- assert_equal 'New name', PublicOffer::Product.first.name
- ensure
- ActiveRecord::Base.connection_pool.disconnect!
- end
-
- private
-
- def read_model
- SingleTableReadModel.new(event_store, PublicOffer::Product, :product_id)
- end
-
- def product_id
- @product_id ||= SecureRandom.uuid
- end
-
- def product_registered
- ProductCatalog::ProductRegistered.new(data: { product_id: product_id })
- end
-
- def product_name
- 'Valid test name'
- end
-
- def product_named(name = product_name)
- ProductCatalog::ProductNamed.new(data: { product_id: product_id, name: name })
- end
-
- def vat_rate_set
- Taxes::VatRateSet.new(data: { product_id: product_id, vat_rate: available_vat_rate })
- end
-
- def available_vat_rate
- Taxes::Configuration.available_vat_rates.first
- end
-
- def event_store
- Rails.configuration.event_store
- end
-end
diff --git a/rails_application/test/test_helper.rb b/rails_application/test/test_helper.rb
index 7afc620ad..ce4135b42 100644
--- a/rails_application/test/test_helper.rb
+++ b/rails_application/test/test_helper.rb
@@ -50,6 +50,11 @@ def run_command(command)
end
class InMemoryRESIntegrationTestCase < ActionDispatch::IntegrationTest
+ include Infra::TestPlumbing.with(
+ event_store: -> { Infra::EventStore.in_memory_rails },
+ command_bus: -> { Arkency::CommandBus.new }
+ )
+
def setup
super
@@ -74,16 +79,16 @@ def before_teardown
result
end
- def register_customer(name)
- customer_id = SecureRandom.uuid
- post "/customers", params: { customer_id: customer_id, name: name }
- customer_id
+ def register_customer(name, email = "user@example.com")
+ post "/customers", params: { customer: { first_name: name, last_name: "", email: } }
+ Customer.find_by(email:).id
end
- def register_product(name, price, vat_rate)
- product_id = SecureRandom.uuid
- post "/products", params: { product_id: product_id, name: name, price: price, vat_rate: vat_rate }
- product_id
+ def register_product(name, price, vat_rate, sku = SecureRandom.uuid, stock_level: 10)
+ post "/products", params: { product: { name: name, price: price, vat_rate: vat_rate, sku: } }
+ product = Product.find_by(sku:)
+ post "/products/#{product.id}/supplies", params: { product_id: product.id, quantity: stock_level }
+ product.id
end
def supply_product(product_id, quantity)
@@ -95,7 +100,7 @@ def update_price(product_id, new_price)
params: {
"authenticity_token" => "[FILTERED]",
"product_id" => product_id,
- price: new_price,
+ product: { price: new_price },
}
end
@@ -111,7 +116,7 @@ def submit_order(customer_id, order_id)
"order_id" => order_id,
"customer_id" => customer_id,
"commit" => "Submit order"
- }
+ }
end
def visit_customers_index
@@ -134,6 +139,11 @@ def add_product_to_basket(order_id, product_id)
post "/orders/#{order_id}/add_item?product_id=#{product_id}"
end
+ def new_order
+ get "/orders/new"
+ Order.last
+ end
+
def run_command(command)
Rails.configuration.command_bus.call(command)
end
diff --git a/rails_application/test/update_product_catalog_test.rb b/rails_application/test/update_product_catalog_test.rb
new file mode 100644
index 000000000..c4fdf487e
--- /dev/null
+++ b/rails_application/test/update_product_catalog_test.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+require 'test_helper'
+
+class UpdateProductCatalogTest < InMemoryTestCase
+ def test_happy_path
+ product = Inventory::ProductCatalog.create!(stock_level: 0)
+
+ stock_level_increased = Inventory::StockLevelIncreased.new(data: { id: product.id, quantity: 10 })
+ stock_level_increased_second_time = Inventory::StockLevelIncreased.new(data: { id: product.id, quantity: 10 })
+ event_store.append(stock_level_increased, stream_name: "Inventory::Product$#{product.id}")
+ event_store.append(stock_level_increased_second_time, stream_name: "Inventory::Product$#{product.id}")
+
+ Inventory::UpdateProductCatalog.new.call(stock_level_increased)
+ Inventory::UpdateProductCatalog.new.call(stock_level_increased_second_time)
+
+ assert_equal 20, Inventory::ProductCatalog.find_by(product_id: product.id).stock_level
+ end
+
+ def test_life_is_brutal_and_full_of_traps
+ product = Inventory::ProductCatalog.create!(stock_level: 0)
+
+ stock_level_increased = Inventory::StockLevelIncreased.new(data: { id: product.id, quantity: 10 })
+ event_store.append(stock_level_increased, stream_name: "Inventory::Product$#{product.id}")
+
+ Inventory::UpdateProductCatalog.new.call(stock_level_increased)
+ Inventory::UpdateProductCatalog.new.call(stock_level_increased)
+
+ assert_equal 10, Inventory::ProductCatalog.find_by(product_id: product.id).stock_level
+ end
+
+ private
+
+ def event_store
+ Rails.configuration.event_store
+ end
+end
\ No newline at end of file