diff --git a/app/controllers/dashboard/orders_controller.rb b/app/controllers/dashboard/orders_controller.rb
index 2c31bf5..8d7febc 100644
--- a/app/controllers/dashboard/orders_controller.rb
+++ b/app/controllers/dashboard/orders_controller.rb
@@ -16,6 +16,34 @@ def new
end
def create
- # Write Code Here
+ service = OrderCreationService.new(
+ order_params: order_params,
+ token: params[:order][:token]
+ )
+
+ begin
+ @order = service.call
+ redirect_to dashboard_orders_path, notice: "注文が作成されました"
+ rescue OrderCreationService::ReservationAlreadyCompletedError => e
+ flash[:alert] = e.message
+ redirect_to dashboard_orders_path
+ rescue OrderCreationService::InsufficientStockError => e
+ @order = Order.new(order_params)
+ @order.name = Item.find(params[:order][:item_id]).name
+ flash.now[:alert] = e.message
+ @items = Item.all.order(:name)
+ @users = User.all.order(:name)
+ render :new
+ rescue ActiveRecord::RecordInvalid
+ @items = Item.all.order(:name)
+ @users = User.all.order(:name)
+ render :new
+ end
+ end
+
+ private
+
+ def order_params
+ params.require(:order).permit(:reservation_id, :user_id, :item_id, :email)
end
end
diff --git a/app/services/order_creation_service.rb b/app/services/order_creation_service.rb
new file mode 100644
index 0000000..422ffc6
--- /dev/null
+++ b/app/services/order_creation_service.rb
@@ -0,0 +1,56 @@
+class OrderCreationService
+ def initialize(order_params:, token:)
+ @order_params = order_params
+ @token = token
+ end
+
+ def call
+ reservation = Reservation.find(@order_params[:reservation_id])
+ item = Item.find(@order_params[:item_id])
+
+ validate_preconditions!(reservation, item)
+
+ order = build_order(item)
+
+ Order.transaction do
+ order.save!
+
+ # 決済APIを実行
+ payment_result = PaymentApiClient.execute(token: @token, amount: item.price)
+
+ # Paymentレコードを作成
+ order.create_payment!(
+ payment_id: payment_result[:payment_id],
+ amount: payment_result[:amount]
+ )
+
+ reservation.update!(status: :completed)
+
+ # 在庫数を1減らす
+ item.decrement!(:stock)
+ end
+
+ order
+ end
+
+ private
+
+ def validate_preconditions!(reservation, item)
+ if reservation.completed?
+ raise ReservationAlreadyCompletedError, "この予約はすでに処理済みです。"
+ end
+
+ if item.stock < 1
+ raise InsufficientStockError, "在庫が不足しています。"
+ end
+ end
+
+ def build_order(item)
+ order = Order.new(@order_params)
+ order.name = item.name
+ order
+ end
+
+ class ReservationAlreadyCompletedError < StandardError; end
+ class InsufficientStockError < StandardError; end
+end
diff --git a/app/views/dashboard/orders/new.html.erb b/app/views/dashboard/orders/new.html.erb
index 4908e4f..a3a6d8d 100644
--- a/app/views/dashboard/orders/new.html.erb
+++ b/app/views/dashboard/orders/new.html.erb
@@ -25,6 +25,7 @@
注文情報
<%= form_with model: [:dashboard, @order], data: { turbo: false } do |form| %>
<%= form.hidden_field :reservation_id %>
+ <%= form.hidden_field :email %>
<%= form.label :item_id, "購入商品" %>
diff --git a/test/controllers/dashboard/orders_controller_test.rb b/test/controllers/dashboard/orders_controller_test.rb
index 8965865..4acd8e9 100644
--- a/test/controllers/dashboard/orders_controller_test.rb
+++ b/test/controllers/dashboard/orders_controller_test.rb
@@ -26,6 +26,7 @@ class Dashboard::OrdersControllerTest < ActionDispatch::IntegrationTest
reservation_id: reservation.id,
item_id: item.id,
user_id: user.id,
+ email: reservation.email,
token: token
}
}
@@ -53,4 +54,99 @@ class Dashboard::OrdersControllerTest < ActionDispatch::IntegrationTest
item.reload
assert_equal initial_stock - 1, item.stock
end
+
+ test "should not create order when reservation status is already completed" do
+ reservation = reservations(:one)
+ reservation.update!(status: "completed")
+ item = items(:one)
+ user = users(:one)
+ token = SecureRandom.alphanumeric(32)
+
+ assert_no_difference("Order.count") do
+ post dashboard_orders_path, params: {
+ order: {
+ reservation_id: reservation.id,
+ item_id: item.id,
+ user_id: user.id,
+ email: reservation.email,
+ name: reservation.name,
+ token: token
+ }
+ }
+ end
+
+ assert_redirected_to dashboard_orders_path
+ assert_equal "この予約はすでに処理済みです。", flash[:alert]
+ end
+
+ test "should update reservation status from pending to completed on successful order creation" do
+ reservation = reservations(:one)
+ reservation.update!(status: "pending")
+ item = items(:one)
+ user = users(:one)
+ token = SecureRandom.alphanumeric(32)
+
+ assert_equal "pending", reservation.status
+
+ post dashboard_orders_path, params: {
+ order: {
+ reservation_id: reservation.id,
+ item_id: item.id,
+ user_id: user.id,
+ email: reservation.email,
+ token: token
+ }
+ }
+
+ reservation.reload
+ assert_equal "completed", reservation.status
+ end
+
+ test "should decrease item stock by 1 on successful order creation" do
+ reservation = reservations(:one)
+ item = items(:one)
+ user = users(:one)
+ initial_stock = item.stock
+ token = SecureRandom.alphanumeric(32)
+
+ post dashboard_orders_path, params: {
+ order: {
+ reservation_id: reservation.id,
+ item_id: item.id,
+ user_id: user.id,
+ email: reservation.email,
+ token: token
+ }
+ }
+
+ item.reload
+ assert_equal initial_stock - 1, item.stock
+ end
+
+ test "should not create order when item stock is less than 1" do
+ reservation = reservations(:one)
+ item = items(:one)
+ item.update!(stock: 0)
+ user = users(:one)
+ token = SecureRandom.alphanumeric(32)
+
+ assert_no_difference("Order.count") do
+ post dashboard_orders_path, params: {
+ order: {
+ reservation_id: reservation.id,
+ item_id: item.id,
+ user_id: user.id,
+ email: reservation.email,
+ name: reservation.name,
+ token: token
+ }
+ }
+ end
+
+ assert_response :success
+ assert_equal "在庫が不足しています。", flash[:alert]
+
+ item.reload
+ assert_equal 0, item.stock
+ end
end