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