diff --git a/rails_application/app/controllers/refunds_controller.rb b/rails_application/app/controllers/refunds_controller.rb
new file mode 100644
index 00000000..6eb8a7f0
--- /dev/null
+++ b/rails_application/app/controllers/refunds_controller.rb
@@ -0,0 +1,20 @@
+class RefundsController < ApplicationController
+ def new
+ @order_id = params[:order_uid]
+ @order = Orders::Order.find_by_uid(params[:order_id])
+ @refund = Refunds::Refund.new
+ @order_lines = Orders::OrderLine.where(order_uid: params[:order_id])
+ end
+
+ def create
+
+ end
+
+ def add_item
+
+ end
+
+ def remove_item
+
+ end
+end
diff --git a/rails_application/app/read_models/refunds/configuration.rb b/rails_application/app/read_models/refunds/configuration.rb
new file mode 100644
index 00000000..271a8405
--- /dev/null
+++ b/rails_application/app/read_models/refunds/configuration.rb
@@ -0,0 +1,20 @@
+module Refunds
+ class Refund < ApplicationRecord
+ self.table_name = "refunds"
+
+ has_many :refund_items,
+ class_name: "Refunds::RefundItem",
+ foreign_key: :order_uid,
+ primary_key: :uid
+ end
+
+ class RefundItem < ApplicationRecord
+ self.table_name = "refund_items"
+ end
+
+ class Configuration
+ def call(event_store)
+ @event_store = event_store
+ end
+ end
+end
diff --git a/rails_application/app/views/orders/show.html.erb b/rails_application/app/views/orders/show.html.erb
index cf0ccd3b..979967ca 100644
--- a/rails_application/app/views/orders/show.html.erb
+++ b/rails_application/app/views/orders/show.html.erb
@@ -23,6 +23,10 @@
<%= 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") %>
<% end %>
+ <% if @order.state == "Submitted" %>
+ <%= link_to("Refund", new_order_refund_path(order_id: @order.uid), class: "mr-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-50 border-gray-300 text-gray-700 bg-white hover:bg-gray-50") %>
+ <% 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") %>
<% end %>
diff --git a/rails_application/app/views/refunds/new.html.erb b/rails_application/app/views/refunds/new.html.erb
new file mode 100644
index 00000000..3a2d5b42
--- /dev/null
+++ b/rails_application/app/views/refunds/new.html.erb
@@ -0,0 +1,43 @@
+<% content_for(:header) do %>
+ Refund for Order <%= @order.number %>
+<% end %>
+
+<% content_for(:actions) do %>
+ <%= secondary_action_button do %>
+ <%= link_to 'Back', orders_path %>
+ <% end %>
+
+ <%= primary_form_action_button do %>
+ Submit Refund
+ <% end %>
+<% end %>
+
+
+
+
+ Product |
+ Quantity |
+ Price |
+ Value |
+
+
+
+
+ <% @order_lines.each do |order_line| %>
+
+ <%= order_line.product_name %> |
+ 0 / <%= order_line.quantity %> |
+ <%= number_to_currency(order_line.price) %> |
+ <%= number_to_currency(order_line.value) %> |
+ <%= button_to "Add", "#", class: "hover:underline text-blue-500" %> |
+ <%= button_to("Remove", "#", class: "hover:underline text-blue-500") %> |
+
+ <% end %>
+
+
+
+ Total |
+ |
+
+
+
diff --git a/rails_application/config/routes.rb b/rails_application/config/routes.rb
index e52f6d35..8dd46f89 100644
--- a/rails_application/config/routes.rb
+++ b/rails_application/config/routes.rb
@@ -17,6 +17,12 @@
resource :shipping_address, only: [:edit, :update]
resource :billing_address, only: [:edit, :update]
resource :invoice, only: [:create]
+ resources :refunds, only: [:new, :create] do
+ member do
+ post :add_item
+ post :remove_item
+ end
+ end
end
resources :shipments, only: [:index, :show]
diff --git a/rails_application/db/migrate/20241209100544_create_refunds.rb b/rails_application/db/migrate/20241209100544_create_refunds.rb
new file mode 100644
index 00000000..5710d274
--- /dev/null
+++ b/rails_application/db/migrate/20241209100544_create_refunds.rb
@@ -0,0 +1,12 @@
+class CreateRefunds < ActiveRecord::Migration[7.2]
+ def change
+ create_table :refunds do |t|
+ t.uuid :uid
+ t.uuid :order_uid
+ t.string :status
+ t.decimal :total_value, precision: 8, scale: 2
+
+ t.timestamps
+ end
+ end
+end
diff --git a/rails_application/db/migrate/20241209102208_create_refund_items.rb b/rails_application/db/migrate/20241209102208_create_refund_items.rb
new file mode 100644
index 00000000..f75ce602
--- /dev/null
+++ b/rails_application/db/migrate/20241209102208_create_refund_items.rb
@@ -0,0 +1,12 @@
+class CreateRefundItems < ActiveRecord::Migration[7.2]
+ def change
+ create_table :refund_items do |t|
+ t.uuid :refund_uid
+ t.uuid :product_uid
+ t.integer :quantity
+ t.decimal :price, precision: 8, scale: 2
+
+ t.timestamps
+ end
+ end
+end
diff --git a/rails_application/db/schema.rb b/rails_application/db/schema.rb
index 939d0106..a80e1193 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_10_18_113912) do
+ActiveRecord::Schema[7.2].define(version: 2024_12_09_102208) do
# These are extensions that must be enabled in order to support this database
enable_extension "pgcrypto"
enable_extension "plpgsql"
@@ -194,6 +194,24 @@
t.decimal "lowest_recent_price", precision: 8, scale: 2
end
+ create_table "refund_items", force: :cascade do |t|
+ t.uuid "refund_uid"
+ t.uuid "product_uid"
+ t.integer "quantity"
+ t.decimal "price", precision: 8, scale: 2
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ end
+
+ create_table "refunds", force: :cascade do |t|
+ t.uuid "uid"
+ t.uuid "order_uid"
+ t.string "status"
+ t.decimal "total_value", precision: 8, scale: 2
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ end
+
create_table "shipment_items", force: :cascade do |t|
t.bigint "shipment_id", null: false
t.string "product_name", null: false
diff --git a/rails_application/lib/configuration.rb b/rails_application/lib/configuration.rb
index 512be070..60ed8722 100644
--- a/rails_application/lib/configuration.rb
+++ b/rails_application/lib/configuration.rb
@@ -17,6 +17,7 @@ def call(event_store, command_bus)
enable_availability_read_model(event_store)
enable_authentication_read_model(event_store)
enable_vat_rates_read_model(event_store)
+ enable_refunds_read_model(event_store)
Ecommerce::Configuration.new(
number_generator: Rails.configuration.number_generator,
@@ -81,4 +82,8 @@ def enable_authentication_read_model(event_store)
def enable_vat_rates_read_model(event_store)
VatRates::Configuration.new.call(event_store)
end
+
+ def enable_refunds_read_model(event_store)
+ Refunds::Configuration.new.call(event_store)
+ end
end
diff --git a/rails_application/test/integration/refunds_test.rb b/rails_application/test/integration/refunds_test.rb
new file mode 100644
index 00000000..1e2e17f5
--- /dev/null
+++ b/rails_application/test/integration/refunds_test.rb
@@ -0,0 +1,31 @@
+require "test_helper"
+
+class RefundsTest < InMemoryRESIntegrationTestCase
+ def setup
+ super
+ add_available_vat_rate(10)
+ end
+
+ def test_happy_path
+ shopify_id = register_customer("Shopify")
+ order_id = SecureRandom.uuid
+ async_remote_id = register_product("Async Remote", 39, 10)
+ fearless_id = register_product("Fearless Refactoring", 49, 10)
+
+ 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}"
+ submit_order(shopify_id, order_id)
+
+ show_order(order_id)
+
+ assert_select("a", "Refund")
+
+ get "/orders/#{order_id}/refunds/new"
+
+ assert_select("tr") do
+ assert_select("td", "Async Remote")
+ assert_select("td", "Fearless Refactoring")
+ end
+ end
+end
diff --git a/rails_application/test/test_helper.rb b/rails_application/test/test_helper.rb
index 1b30559f..fe4a61c7 100644
--- a/rails_application/test/test_helper.rb
+++ b/rails_application/test/test_helper.rb
@@ -123,10 +123,18 @@ def visit_client_orders
get "/client_orders"
end
+ def show_order(order_id)
+ get "/orders/#{order_id}"
+ end
+
def add_product_to_basket(order_id, product_id)
post "/orders/#{order_id}/add_item?product_id=#{product_id}"
end
+ def add_product_to_refund(order_id, refund_id, product_id)
+ post "/orders/#{order_id}/refunds/#{refund_id}/add_item?product_id=#{product_id}"
+ end
+
def run_command(command)
Rails.configuration.command_bus.call(command)
end