Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 29 additions & 1 deletion app/controllers/dashboard/orders_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
56 changes: 56 additions & 0 deletions app/services/order_creation_service.rb
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions app/views/dashboard/orders/new.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
<h2>注文情報</h2>
<%= form_with model: [:dashboard, @order], data: { turbo: false } do |form| %>
<%= form.hidden_field :reservation_id %>
<%= form.hidden_field :email %>

<div class="field">
<%= form.label :item_id, "購入商品" %>
Expand Down
96 changes: 96 additions & 0 deletions test/controllers/dashboard/orders_controller_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
Expand Down Expand Up @@ -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