From 435a8f144d4a0555759de903d12078543397ffa8 Mon Sep 17 00:00:00 2001 From: Okunichiyou Date: Wed, 26 Nov 2025 15:49:03 +0900 Subject: [PATCH 1/6] =?UTF-8?q?Order=E3=81=AE=E3=83=AC=E3=82=B3=E3=83=BC?= =?UTF-8?q?=E3=83=89=E3=82=92=E4=BD=9C=E6=88=90=E3=80=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/controllers/dashboard/orders_controller.rb | 16 +++++++++++++++- app/views/dashboard/orders/new.html.erb | 2 ++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/app/controllers/dashboard/orders_controller.rb b/app/controllers/dashboard/orders_controller.rb index 2c31bf5..e2fa1c3 100644 --- a/app/controllers/dashboard/orders_controller.rb +++ b/app/controllers/dashboard/orders_controller.rb @@ -16,6 +16,20 @@ def new end def create - # Write Code Here + @order = Order.new(order_params) + + if @order.save + redirect_to dashboard_orders_path, notice: '注文が作成されました。' + else + @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, :name) end end diff --git a/app/views/dashboard/orders/new.html.erb b/app/views/dashboard/orders/new.html.erb index 4908e4f..a51e42b 100644 --- a/app/views/dashboard/orders/new.html.erb +++ b/app/views/dashboard/orders/new.html.erb @@ -25,6 +25,8 @@

注文情報

<%= form_with model: [:dashboard, @order], data: { turbo: false } do |form| %> <%= form.hidden_field :reservation_id %> + <%= form.hidden_field :email %> + <%= form.hidden_field :name %>
<%= form.label :item_id, "購入商品" %> From 39362b09a669e4c564d47c3f1157a3b228e8722d Mon Sep 17 00:00:00 2001 From: Okunichiyou Date: Wed, 26 Nov 2025 15:55:56 +0900 Subject: [PATCH 2/6] =?UTF-8?q?Payment=E3=83=AC=E3=82=B3=E3=83=BC=E3=83=89?= =?UTF-8?q?=E3=82=92=E4=BD=9C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/controllers/dashboard/orders_controller.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/app/controllers/dashboard/orders_controller.rb b/app/controllers/dashboard/orders_controller.rb index e2fa1c3..9e2489a 100644 --- a/app/controllers/dashboard/orders_controller.rb +++ b/app/controllers/dashboard/orders_controller.rb @@ -19,6 +19,17 @@ def create @order = Order.new(order_params) if @order.save + # 決済APIを実行 + token = params[:order][:token] + amount = @order.item.price + payment_result = PaymentApiClient.execute(token: token, amount: amount) + + # Paymentレコードを作成 + @order.create_payment!( + payment_id: payment_result[:payment_id], + amount: payment_result[:amount] + ) + redirect_to dashboard_orders_path, notice: '注文が作成されました。' else @items = Item.all.order(:name) From 0c53e9aaf9f75af94471756a9e31301689fb6766 Mon Sep 17 00:00:00 2001 From: Okunichiyou Date: Wed, 26 Nov 2025 16:11:36 +0900 Subject: [PATCH 3/6] =?UTF-8?q?completed=E3=81=AE=E6=99=82=E3=81=AF?= =?UTF-8?q?=E3=82=A2=E3=83=A9=E3=83=BC=E3=83=88=E8=BF=94=E3=81=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dashboard/orders_controller.rb | 9 +- app/views/dashboard/orders/new.html.erb | 1 - .../dashboard/orders_controller_test.rb | 96 +++++++++++++++++++ 3 files changed, 104 insertions(+), 2 deletions(-) diff --git a/app/controllers/dashboard/orders_controller.rb b/app/controllers/dashboard/orders_controller.rb index 9e2489a..4d674ac 100644 --- a/app/controllers/dashboard/orders_controller.rb +++ b/app/controllers/dashboard/orders_controller.rb @@ -18,6 +18,11 @@ def new def create @order = Order.new(order_params) + if @order.reservation.status == :completed + flash.now.alert = "すでに作成済み" + return render :new + end + if @order.save # 決済APIを実行 token = params[:order][:token] @@ -30,7 +35,9 @@ def create amount: payment_result[:amount] ) - redirect_to dashboard_orders_path, notice: '注文が作成されました。' + @order.reservation.update(status: :completed) + + redirect_to dashboard_orders_path, notice: "注文が作成されました" else @items = Item.all.order(:name) @users = User.all.order(:name) diff --git a/app/views/dashboard/orders/new.html.erb b/app/views/dashboard/orders/new.html.erb index a51e42b..a3a6d8d 100644 --- a/app/views/dashboard/orders/new.html.erb +++ b/app/views/dashboard/orders/new.html.erb @@ -26,7 +26,6 @@ <%= form_with model: [:dashboard, @order], data: { turbo: false } do |form| %> <%= form.hidden_field :reservation_id %> <%= form.hidden_field :email %> - <%= form.hidden_field :name %>
<%= 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 From a3aa94bb2a60ea84a33b289c2da871b05b95a88b Mon Sep 17 00:00:00 2001 From: Okunichiyou Date: Wed, 26 Nov 2025 16:17:54 +0900 Subject: [PATCH 4/6] =?UTF-8?q?=E3=82=B9=E3=83=88=E3=83=83=E3=82=AF?= =?UTF-8?q?=E3=82=92=E6=B8=9B=E3=82=89=E3=81=99=E5=87=A6=E7=90=86+?= =?UTF-8?q?=E3=83=90=E3=83=AA=E3=83=87=E3=83=BC=E3=82=B7=E3=83=A7=E3=83=B3?= =?UTF-8?q?=E5=88=A4=E5=AE=9A=E3=83=9F=E3=82=B9=E3=81=A3=E3=81=A6=E3=83=86?= =?UTF-8?q?=E3=82=B9=E3=83=88=E8=90=BD=E3=81=A1=E3=81=A6=E3=81=9F=E3=81=AE?= =?UTF-8?q?=E3=81=A7=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dashboard/orders_controller.rb | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/app/controllers/dashboard/orders_controller.rb b/app/controllers/dashboard/orders_controller.rb index 4d674ac..06a54e6 100644 --- a/app/controllers/dashboard/orders_controller.rb +++ b/app/controllers/dashboard/orders_controller.rb @@ -16,17 +16,30 @@ def new end def create - @order = Order.new(order_params) + reservation = Reservation.find(params[:order][:reservation_id]) + item = Item.find(params[:order][:item_id]) + + if reservation.completed? + flash[:alert] = "この予約はすでに処理済みです。" + return redirect_to dashboard_orders_path + end - if @order.reservation.status == :completed - flash.now.alert = "すでに作成済み" + if item.stock < 1 + @order = Order.new(order_params) + @order.name = item.name + flash.now[:alert] = "在庫が不足しています。" + @items = Item.all.order(:name) + @users = User.all.order(:name) return render :new end + @order = Order.new(order_params) + @order.name = item.name + if @order.save # 決済APIを実行 token = params[:order][:token] - amount = @order.item.price + amount = item.price payment_result = PaymentApiClient.execute(token: token, amount: amount) # Paymentレコードを作成 @@ -35,7 +48,10 @@ def create amount: payment_result[:amount] ) - @order.reservation.update(status: :completed) + reservation.update(status: :completed) + + # 在庫数を1減らす + item.decrement!(:stock) redirect_to dashboard_orders_path, notice: "注文が作成されました" else @@ -48,6 +64,6 @@ def create private def order_params - params.require(:order).permit(:reservation_id, :user_id, :item_id, :email, :name) + params.require(:order).permit(:reservation_id, :user_id, :item_id, :email) end end From 562ea6e6dda503a3c5c41fca9d0c8518b99162c0 Mon Sep 17 00:00:00 2001 From: Okunichiyou Date: Wed, 26 Nov 2025 16:21:17 +0900 Subject: [PATCH 5/6] =?UTF-8?q?Order=E4=BD=9C=E6=88=90=E3=81=8B=E3=82=89?= =?UTF-8?q?=E5=9C=A8=E5=BA=AB=E6=95=B0=E3=81=BE=E3=81=A7=E3=81=AF=EF=BC=91?= =?UTF-8?q?=E3=83=88=E3=83=A9=E3=83=B3=E3=82=B6=E3=82=AF=E3=82=B7=E3=83=A7?= =?UTF-8?q?=E3=83=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dashboard/orders_controller.rb | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/app/controllers/dashboard/orders_controller.rb b/app/controllers/dashboard/orders_controller.rb index 06a54e6..0ee6918 100644 --- a/app/controllers/dashboard/orders_controller.rb +++ b/app/controllers/dashboard/orders_controller.rb @@ -36,25 +36,29 @@ def create @order = Order.new(order_params) @order.name = item.name - if @order.save - # 決済APIを実行 - token = params[:order][:token] - amount = item.price - payment_result = PaymentApiClient.execute(token: token, amount: amount) + begin + Order.transaction do + @order.save! - # Paymentレコードを作成 - @order.create_payment!( - payment_id: payment_result[:payment_id], - amount: payment_result[:amount] - ) + # 決済APIを実行 + token = params[:order][:token] + amount = item.price + payment_result = PaymentApiClient.execute(token: token, amount: amount) - reservation.update(status: :completed) + # Paymentレコードを作成 + @order.create_payment!( + payment_id: payment_result[:payment_id], + amount: payment_result[:amount] + ) - # 在庫数を1減らす - item.decrement!(:stock) + reservation.update!(status: :completed) + + # 在庫数を1減らす + item.decrement!(:stock) + end redirect_to dashboard_orders_path, notice: "注文が作成されました" - else + rescue ActiveRecord::RecordInvalid @items = Item.all.order(:name) @users = User.all.order(:name) render :new From 0085a224d546a0a42d100de8377bb9cd15a3b016 Mon Sep 17 00:00:00 2001 From: Okunichiyou Date: Wed, 26 Nov 2025 16:24:06 +0900 Subject: [PATCH 6/6] =?UTF-8?q?=E3=83=88=E3=83=A9=E3=83=B3=E3=82=B6?= =?UTF-8?q?=E3=82=AF=E3=82=B7=E3=83=A7=E3=83=B3=E5=87=A6=E7=90=86=E3=82=92?= =?UTF-8?q?=E3=82=B5=E3=83=BC=E3=83=93=E3=82=B9=E3=81=AB=E7=A7=BB=E5=8B=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dashboard/orders_controller.rb | 52 +++++------------ app/services/order_creation_service.rb | 56 +++++++++++++++++++ 2 files changed, 70 insertions(+), 38 deletions(-) create mode 100644 app/services/order_creation_service.rb diff --git a/app/controllers/dashboard/orders_controller.rb b/app/controllers/dashboard/orders_controller.rb index 0ee6918..8d7febc 100644 --- a/app/controllers/dashboard/orders_controller.rb +++ b/app/controllers/dashboard/orders_controller.rb @@ -16,48 +16,24 @@ def new end def create - reservation = Reservation.find(params[:order][:reservation_id]) - item = Item.find(params[:order][:item_id]) - - if reservation.completed? - flash[:alert] = "この予約はすでに処理済みです。" - return redirect_to dashboard_orders_path - end + service = OrderCreationService.new( + order_params: order_params, + token: params[:order][:token] + ) - if item.stock < 1 + 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.name - flash.now[:alert] = "在庫が不足しています。" + @order.name = Item.find(params[:order][:item_id]).name + flash.now[:alert] = e.message @items = Item.all.order(:name) @users = User.all.order(:name) - return render :new - end - - @order = Order.new(order_params) - @order.name = item.name - - begin - Order.transaction do - @order.save! - - # 決済APIを実行 - token = params[:order][:token] - amount = item.price - payment_result = PaymentApiClient.execute(token: token, amount: amount) - - # Paymentレコードを作成 - @order.create_payment!( - payment_id: payment_result[:payment_id], - amount: payment_result[:amount] - ) - - reservation.update!(status: :completed) - - # 在庫数を1減らす - item.decrement!(:stock) - end - - redirect_to dashboard_orders_path, notice: "注文が作成されました" + render :new rescue ActiveRecord::RecordInvalid @items = Item.all.order(:name) @users = User.all.order(:name) 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