From fdede5bb99ae8921c4b5f055b9a8d4b4dacbd67a Mon Sep 17 00:00:00 2001 From: marlena-b Date: Wed, 28 Aug 2024 11:14:15 +0200 Subject: [PATCH] Display out of stock badge for products on order creation page --- .../client_orders/configuration.rb | 9 ++++ .../app/read_models/client_orders/product.rb | 5 --- .../update_product_availability.rb | 7 ++++ .../app/read_models/products/configuration.rb | 5 +++ .../app/views/client/orders/edit.html.erb | 6 +++ .../app/views/orders/edit.html.erb | 6 +++ ...0240826132237_add_available_to_products.rb | 5 +++ ..._add_available_to_client_order_products.rb | 5 +++ rails_application/db/schema.rb | 4 +- .../test/client_orders/product_test.rb | 21 ++++++++++ .../update_product_availability_test.rb | 41 +++++++++++++++++++ .../test/integration/client_orders_test.rb | 19 +++++++++ .../test/integration/orders_test.rb | 27 ++++++++++++ .../test/integration/supplies_test.rb | 5 +++ .../test/products/product_test.rb | 21 ++++++++++ 15 files changed, 180 insertions(+), 6 deletions(-) delete mode 100644 rails_application/app/read_models/client_orders/product.rb create mode 100644 rails_application/app/read_models/client_orders/update_product_availability.rb create mode 100644 rails_application/db/migrate/20240826132237_add_available_to_products.rb create mode 100644 rails_application/db/migrate/20240827090619_add_available_to_client_order_products.rb create mode 100644 rails_application/test/client_orders/product_test.rb create mode 100644 rails_application/test/client_orders/update_product_availability_test.rb create mode 100644 rails_application/test/products/product_test.rb diff --git a/rails_application/app/read_models/client_orders/configuration.rb b/rails_application/app/read_models/client_orders/configuration.rb index f2b162783..9cd8f903d 100644 --- a/rails_application/app/read_models/client_orders/configuration.rb +++ b/rails_application/app/read_models/client_orders/configuration.rb @@ -28,6 +28,14 @@ def value end end + class Product < ApplicationRecord + self.table_name = "client_order_products" + + def unavailable? + available && available <= 0 + end + end + class Configuration def call(event_store) event_store.subscribe(ExpireOrder, to: [Ordering::OrderExpired]) @@ -43,6 +51,7 @@ def call(event_store) event_store.subscribe(ChangeProductName, to: [ProductCatalog::ProductNamed]) event_store.subscribe(ChangeProductPrice, to: [Pricing::PriceSet]) event_store.subscribe(RegisterProduct, to: [ProductCatalog::ProductRegistered]) + event_store.subscribe(UpdateProductAvailability, to: [Inventory::AvailabilityChanged]) event_store.subscribe(UpdateDiscount, to: [Pricing::PercentageDiscountSet, Pricing::PercentageDiscountChanged]) event_store.subscribe(ResetDiscount, to: [Pricing::PercentageDiscountReset]) event_store.subscribe(UpdateOrderTotalValue, to: [Pricing::OrderTotalValueCalculated]) 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/update_product_availability.rb b/rails_application/app/read_models/client_orders/update_product_availability.rb new file mode 100644 index 000000000..4fdc5fd6d --- /dev/null +++ b/rails_application/app/read_models/client_orders/update_product_availability.rb @@ -0,0 +1,7 @@ +module ClientOrders + class UpdateProductAvailability + def call(event) + Product.find_by(uid: event.data.fetch(:product_id)).update(available: event.data.fetch(:available)) + end + end +end diff --git a/rails_application/app/read_models/products/configuration.rb b/rails_application/app/read_models/products/configuration.rb index 90ca49697..b3d4601e3 100644 --- a/rails_application/app/read_models/products/configuration.rb +++ b/rails_application/app/read_models/products/configuration.rb @@ -16,6 +16,10 @@ def future_prices_calendar current_prices_calendar.select { |entry| entry[:valid_since] > Time.current } end + def unavailable? + available && available <= 0 + end + private def last_price_before(time) @@ -49,6 +53,7 @@ def call @read_model.subscribe_copy(ProductCatalog::ProductNamed, :name) @read_model.subscribe_copy(Inventory::StockLevelChanged, :stock_level) @read_model.subscribe_copy(Taxes::VatRateSet, [:vat_rate, :code]) + @read_model.subscribe_copy(Inventory::AvailabilityChanged, :available) @event_store.subscribe(RefreshFuturePricesCalendar, to: [Pricing::PriceSet]) end end diff --git a/rails_application/app/views/client/orders/edit.html.erb b/rails_application/app/views/client/orders/edit.html.erb index 6c781b642..0751dc48a 100644 --- a/rails_application/app/views/client/orders/edit.html.erb +++ b/rails_application/app/views/client/orders/edit.html.erb @@ -2,6 +2,7 @@ Product + Quantity Price Value @@ -14,6 +15,11 @@ <% order_line = @order_lines&.find{|order_line| order_line.product_id == product.uid} %> <%= product.name %> + + <% if product.unavailable? %> + out of stock + <% end %> + "><%= order_line.try(&:product_quantity) || 0 %> <%= number_to_currency(product.price) %> "><%= number_to_currency(order_line.try(&:value)) %> diff --git a/rails_application/app/views/orders/edit.html.erb b/rails_application/app/views/orders/edit.html.erb index 70a419346..0352d6f44 100644 --- a/rails_application/app/views/orders/edit.html.erb +++ b/rails_application/app/views/orders/edit.html.erb @@ -30,6 +30,7 @@ Product + Quantity Price Value @@ -41,6 +42,11 @@ <% order_line = @order_lines.find{|order_line| order_line.product_id == product.id} %> <%= product.name %> + + <% if product.unavailable? %> + out of stock + <% end %> + "><%= order_line.try(&:quantity) || 0 %> <%= number_to_currency(product.price) %> "><%= number_to_currency(order_line.try(&:value)) %> diff --git a/rails_application/db/migrate/20240826132237_add_available_to_products.rb b/rails_application/db/migrate/20240826132237_add_available_to_products.rb new file mode 100644 index 000000000..0e83ebe22 --- /dev/null +++ b/rails_application/db/migrate/20240826132237_add_available_to_products.rb @@ -0,0 +1,5 @@ +class AddAvailableToProducts < ActiveRecord::Migration[7.2] + def change + add_column :products, :available, :integer + end +end diff --git a/rails_application/db/migrate/20240827090619_add_available_to_client_order_products.rb b/rails_application/db/migrate/20240827090619_add_available_to_client_order_products.rb new file mode 100644 index 000000000..66ce91e78 --- /dev/null +++ b/rails_application/db/migrate/20240827090619_add_available_to_client_order_products.rb @@ -0,0 +1,5 @@ +class AddAvailableToClientOrderProducts < ActiveRecord::Migration[7.2] + def change + add_column :client_order_products, :available, :integer + end +end diff --git a/rails_application/db/schema.rb b/rails_application/db/schema.rb index a8c932579..941de1682 100644 --- a/rails_application/db/schema.rb +++ b/rails_application/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.2].define(version: 2024_08_12_080357) do +ActiveRecord::Schema[7.2].define(version: 2024_08_27_090619) do # These are extensions that must be enabled in order to support this database enable_extension "pgcrypto" enable_extension "plpgsql" @@ -46,6 +46,7 @@ t.uuid "uid", null: false t.string "name" t.decimal "price", precision: 8, scale: 2 + t.integer "available" end create_table "client_orders", force: :cascade do |t| @@ -182,6 +183,7 @@ t.datetime "registered_at", precision: nil t.string "vat_rate_code" t.text "current_prices_calendar" + t.integer "available" end create_table "public_offer_products", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| diff --git a/rails_application/test/client_orders/product_test.rb b/rails_application/test/client_orders/product_test.rb new file mode 100644 index 000000000..448ddbb07 --- /dev/null +++ b/rails_application/test/client_orders/product_test.rb @@ -0,0 +1,21 @@ +require "test_helper" + +module ClientOrders + class ProductTest < InMemoryTestCase + cover "ClientOrders*" + + def test_unavailable + product = Product.new(available: nil) + refute product.unavailable? + + product = Product.new(available: 0) + assert product.unavailable? + + product = Product.new(available: 1) + refute product.unavailable? + + product = Product.new(available: -1) + assert product.unavailable? + end + end +end diff --git a/rails_application/test/client_orders/update_product_availability_test.rb b/rails_application/test/client_orders/update_product_availability_test.rb new file mode 100644 index 000000000..40993ebb2 --- /dev/null +++ b/rails_application/test/client_orders/update_product_availability_test.rb @@ -0,0 +1,41 @@ +require "test_helper" + +module ClientOrders + class UpdateProductAvailabilityTest < InMemoryTestCase + cover "ClientOrders*" + + def test_reflects_change + product_id = prepare_product + other_product_id = prepare_product + + supply_product(product_id, 5) + + assert_equal 5, Product.find_by_uid(product_id).available + assert_nil Product.find_by_uid(other_product_id).available + 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 supply_product(product_id, quantity) + run_command(Inventory::Supply.new(product_id: product_id, quantity: quantity)) + end + end +end diff --git a/rails_application/test/integration/client_orders_test.rb b/rails_application/test/integration/client_orders_test.rb index 226f2b5f6..5d2d54059 100644 --- a/rails_application/test/integration/client_orders_test.rb +++ b/rails_application/test/integration/client_orders_test.rb @@ -168,6 +168,25 @@ def test_empty_order_cannot_be_submitted assert_select "#alert", "You can't submit an empty order" end + def test_shows_out_of_stock_badge + shopify_id = register_customer("Shopify") + order_id = SecureRandom.uuid + async_remote_id = register_product("Async Remote", 39, 10) + + supply_product(async_remote_id, 1) + login(shopify_id) + get "/client_orders/new" + + assert_select "td span", text: "out of stock", count: 0 + + as_client_add_item_to_basket_for_order(async_remote_id, order_id) + as_client_submit_order_for_customer(order_id) + + get "/client_orders/new" + + assert_select "td span", "out of stock" + end + private def submit_order_for_customer(customer_id, order_id) diff --git a/rails_application/test/integration/orders_test.rb b/rails_application/test/integration/orders_test.rb index 327d75c96..c9b8f472f 100644 --- a/rails_application/test/integration/orders_test.rb +++ b/rails_application/test/integration/orders_test.rb @@ -219,6 +219,33 @@ def test_order_cannot_be_submitted_with_out_of_stock_product assert_equal "Order can not be submitted! Fearless Refactoring not available in requested quantity!", flash[:alert] end + def test_shows_out_of_stock_badge + shopify_id = register_customer("Shopify") + order_id = SecureRandom.uuid + async_remote_id = register_product("Async Remote", 39, 10) + + supply_product(async_remote_id, 1) + + get "/orders/new" + follow_redirect! + + assert_select "td span", text: "out of stock", count: 0 + + post "/orders/#{order_id}/add_item?product_id=#{async_remote_id}" + post "/orders", + params: { + "authenticity_token" => "[FILTERED]", + "order_id" => order_id, + "customer_id" => shopify_id, + "commit" => "Submit order" + } + + get "/orders/new" + follow_redirect! + + assert_select "td span", "out of stock" + end + private def assert_remove_buttons_visible(async_remote_id, fearless_id, order_id) diff --git a/rails_application/test/integration/supplies_test.rb b/rails_application/test/integration/supplies_test.rb index 22c0c0f87..5ade8ad75 100644 --- a/rails_application/test/integration/supplies_test.rb +++ b/rails_application/test/integration/supplies_test.rb @@ -1,6 +1,11 @@ require "test_helper" class SuppliesTest < InMemoryRESIntegrationTestCase + def setup + super + add_available_vat_rate(10) + end + def test_happy_path product_id = register_product("Async Remote", 100, 10) diff --git a/rails_application/test/products/product_test.rb b/rails_application/test/products/product_test.rb new file mode 100644 index 000000000..3ef589fea --- /dev/null +++ b/rails_application/test/products/product_test.rb @@ -0,0 +1,21 @@ +require "test_helper" + +module Products + class ProductTest < InMemoryTestCase + cover "Products*" + + def test_unavailable + product = Product.new(available: nil) + refute product.unavailable? + + product = Product.new(available: 0) + assert product.unavailable? + + product = Product.new(available: 1) + refute product.unavailable? + + product = Product.new(available: -1) + assert product.unavailable? + end + end +end