This is a continuation of the series "Creating an EC site with Rails 5 ⑨](https://qiita.com/GreenFingers_tk/items/3fd347dee907328d20e0), which creates an EC site where you can shop at a fictitious bakery. We will implement the Order model (order function), which is the main function and the most difficult part of this site.
The user's movement is as follows.
Put the product in the cart and press the "Proceed to order screen" button
↓
Enter order information(orders/new)screen
·Payment Method
·Addressee
And press the "Proceed to confirmation screen" button
↓
Confirm order information(orders/confirm)screen
Check the contents and press the "Confirm order" button
↓
orders/Show thanks screen (static page)
You can check the order history on the list and details screen.
The point is that you can select the delivery address to enter on the orders / new screen from "your address", "registered address", and "new address". When you select a new address and fill in the form on the new screen, it will be saved as Order data as well as new data in the Address model. Nesting forms and going through confirmation screens before saving that data are factors that make it difficult to implement this feature.
https://github.com/Sn16799/bakeryFUMIZUKI
Controller
app/controllers/orders_controller.rb
class OrdersController < ApplicationController
before_action :authenticate_customer!
before_action :set_customer
def index
@orders = @customer.orders
end
def create
if current_customer.cart_items.exists?
@order = Order.new(order_params)
@order.customer_id = current_customer.id
#Adjust arguments according to address radio button selection
@add = params[:order][:add].to_i
case @add
when 1
@order.post_code = @customer.post_code
@order.send_to_address = @customer.address
@order.addressee = full_name(@customer)
when 2
@order.post_code = params[:order][:post_code]
@order.send_to_address = params[:order][:send_to_address]
@order.addressee = params[:order][:addressee]
when 3
@order.post_code = params[:order][:post_code]
@order.send_to_address = params[:order][:send_to_address]
@order.addressee = params[:order][:addressee]
end
@order.save
# send_to_Address model search by address, create new if there is no corresponding data
if Address.find_by(address: @order.send_to_address).nil?
@address = Address.new
@address.post_code = @order.post_code
@address.address = @order.send_to_address
@address.addressee = @order.addressee
@address.customer_id = current_customer.id
@address.save
end
# cart_Order the contents of items_Newly registered in items
current_customer.cart_items.each do |cart_item|
order_item = @order.order_items.build
order_item.order_id = @order.id
order_item.product_id = cart_item.product_id
order_item.quantity = cart_item.quantity
order_item.order_price = cart_item.product.price
order_item.save
cart_item.destroy #order_After moving the information to item, cart_item is erased
end
render :thanks
else
redirect_to customer_top_path
flash[:danger] = 'The cart is empty.'
end
end
def show
@order = Order.find(params[:id])
if @order.customer_id != current_customer.id
redirect_back(fallback_location: root_path)
flash[:alert] = "Access failed."
end
end
def new
@order = Order.new
end
def confirm
@order = Order.new
@cart_items = current_customer.cart_items
@order.how_to_pay = params[:order][:how_to_pay]
#Adjust arguments according to address radio button selection
@add = params[:order][:add].to_i
case @add
when 1
@order.post_code = @customer.post_code
@order.send_to_address = @customer.address
@order.addressee = @customer.family_name + @customer.first_name
when 2
@sta = params[:order][:send_to_address].to_i
@send_to_address = Address.find(@sta)
@order.post_code = @send_to_address.post_code
@order.send_to_address = @send_to_address.address
@order.addressee = @send_to_address.addressee
when 3
@order.post_code = params[:order][:new_add][:post_code]
@order.send_to_address = params[:order][:new_add][:address]
@order.addressee = params[:order][:new_add][:addressee]
end
end
def thanks
end
private
def set_customer
@customer = current_customer
end
def order_params
params.require(:order).permit(
:created_at, :send_to_address, :addressee, :order_status, :how_to_pay, :post_code, :deliver_fee,
order_items_attributes: [:order_id, :product_id, :quantity, :order_price, :make_status]
)
end
end
It would be nice if the information sent by form_with could be saved as it is as usual, but form_with cannot insert the confirmation screen. Therefore, the confirm and create actions retrieve the parameters received from view in the form of params [: hoge].
Also, in create, within one action, "** Create new Order data " " Move product data from CartItem to OrderItem ** (Create new OrderItem and delete registered CartItem)" ** Only when there is no matching data in the Address model, you have to do the three things of "new registration **". Each operation is not complicated, but the total amount of code is quite large.
View new
html:app/views/orders/.html.erb
<div class="col-lg-10 offset-1 space">
<div class="row">
<div class="col-lg-4">
<h2>Enter order information</h2>
</div>
</div>
<%= form_with(model: @order, local: true, url: {action: 'confirm'}) do |f| %>
<!--Payment Method-->
<div class="row space">
<h3><strong><%= f.label :Payment Method%></strong></h3>
</div>
<div class="row">
<div class="col-lg-4 btn-group" data-toggle="buttons">
<label class="btn btn-outline-secondary active" style="width:50%">
<%= f.radio_button :how_to_pay, true, {checked: true} %>credit card
</label>
<label class="btn btn-outline-secondary" style="width:50%">
<%= f.radio_button :how_to_pay, false, {} %>Bank transfer
</label>
</div>
</div>
<!--Addressee-->
<div class="row space">
<h3><strong><%= f.label :Addressee%></strong></h3>
</div>
<!--Own address-->
<div class="row">
<p>
<label><%= f.radio_button :add, 1, checked: true, checked: "checked" %>Your own address</label><br>
<%= @customer.post_code %>
<%= @customer.address %>
<%= @customer.full_name %>
</p>
</div>
<!--Registered address-->
<div class="row space-sm">
<p>
<label><%= f.radio_button :add, 2, style: "display: inline-block" %>Select from registered address</label><br>
<%= f.collection_select :send_to_address, @customer.addresses, :id, :address %>
</p>
</div>
<!--New address-->
<div class="row space-sm">
<p><label><%= f.radio_button :add, 3 %>New destination</label></p>
</div>
<div class="row">
<div class="col-lg-12">
<%= f.fields_for :new_add do |na| %>
<div class="row">
<div class="col-lg-3">
<strong>Postal code(no hyphen)</strong>
</div>
<div class="col-lg-6">
<%= na.text_field :post_code, class: 'form-control' %>
</div>
</div>
<div class="row">
<div class="col-lg-3">
<strong>Street address</strong>
</div>
<div class="col-lg-6">
<%= na.text_field :address, class: 'form-control' %>
</div>
</div>
<div class="row">
<div class="col-lg-3">
<strong>address</strong>
</div>
<div class="col-lg-6">
<%= na.text_field :addressee, class: 'form-control' %>
</div>
</div>
<% end %>
</div>
</div>
<!--To here-->
<div class="row space">
<div class="col-lg-2 offset-lg-7">
<%= f.submit "Proceed to confirmation screen", class: "btn btn-danger"%>
</div>
</div>
<% end %>
</div>
confirm
html:app/views/orders/.html.erb
<div class="col-lg-10 offset-1 space">
<div class="row">
<h2>Confirm order information</h2>
</div>
<%= form_with(model: @order, local: true) do |f| %>
<div class="d-none d-lg-block space">
<div class="row">
<div class="col-lg-5"><h4>Product name</h4></div>
<div class="col-lg-2"><h4>Unit price (tax included)</h4></div>
<div class="col-lg-2"><h4>quantity</h4></div>
<div class="col-lg-2"><h4>subtotal</h4></div>
</div>
</div>
<% sum_all = 0 %>
<% @cart_items.each do |cart_item| %>
<div class="row space-sm">
<div class="col-lg-3">
<%= link_to product_path(cart_item.product) do %>
<%= attachment_image_tag(cart_item.product, :image, :fill, 100, 100, fallback: "no_img.jpg ") %>
<% end %>
</div>
<div class="col-lg-2">
<%= link_to product_path(cart_item.product) do %>
<%= cart_item.product.name %>
<% end %>
</div>
<div class="col-lg-2">
<%= price_include_tax(cart_item.product.price) %>
</div>
<div class="col-lg-2">
<%= cart_item.quantity %>
</div>
<div class="col-lg-2">
<%= sum_product = price_include_tax(cart_item.product.price).to_i * cart_item.quantity %>Circle
<% sum_all += sum_product %>
</div>
</div>
<% end %>
<div class="row space">
<div class="col-lg-12">
<div class="row">
<div class="col-lg-3">
<strong>Shipping</strong>
</div>
<div class="col-lg-3">
<%= @order.deliver_fee %>Circle
</div>
</div>
<div class="row">
<div class="col-lg-3">
<strong>Product total</strong>
</div>
<div class="col-lg-3">
<%= sum_all.to_i %>Circle
</div>
</div>
<div class="row">
<div class="col-lg-3">
<strong>Billed amount</strong>
</div>
<div class="col-lg-3">
<% billling_amount = sum_all + @order.deliver_fee.to_i %>
<%= billling_amount.to_i %>Circle
</div>
</div>
</div>
</div>
<div class="row space-sm">
<div class="col-lg-2">
<h3>Payment Method</h3>
</div>
<div class="col-lg-4">
<%= how_to_pay(@order.how_to_pay) %>
</div>
</div>
<div class="row space-sm">
<div class="col-lg-2">
<h3>Addressee</h3>
</div>
<div class="col-lg-4">
<%= @order.post_code %>
<%= @order.send_to_address %>
<%= @order.addressee %>
</div>
</div>
<%= f.hidden_field :customer_id, :value => current_customer.id %>
<%= f.hidden_field :post_code, :value => "#{@order.post_code}" %>
<%= f.hidden_field :send_to_address, :value => "#{@order.send_to_address}" %>
<%= f.hidden_field :addressee, :value => "#{@order.addressee}" %>
</div>
<div class="row space">
<div class="col-lg-2 offset-lg-5">
<%= f.submit "Confirm purchase", class: "btn btn-danger btn-lg" %>
</div>
</div>
<% end %>
</div>
thanks
html:app/views/orders/.html.erb
<div class="col-lg-10 offset-1 space">
<h2>Thank you for your purchase!</h2>
<h3><%= link_to 'Return to TOP', customer_top_path %></h3>
</div>
This page transitions after pressing the "Confirm purchase" button. Since it is a static page and does not have such a function, it is good to put only simple words as described above, or to display a photo with a slider etc.
index
html:app/views/orders/.html.erb
<div class="col-lg-10 offset-1 space">
<div class="row">
<h2>Order history list</h2>
</div>
<div class="d-none d-lg-block space">
<div class="row">
<div class="col-lg-2">Order date</div>
<div class="col-lg-3">Shipping address</div>
<div class="col-lg-2">Ordered products</div>
<div class="col-lg-2">payment</div>
<div class="col-lg-1">Status</div>
<div class="col-lg-2">Order details</div>
</div>
</div>
<% @orders.each do |order| %>
<div class="row space-sm">
<div class="col-lg-2">
<%= simple_time(order.created_at) %>
</div>
<div class="col-lg-3">
<div class="row">
<%= order.post_code + " " + order.send_to_address %>
</div>
<div class="row">
<%= order.addressee %>
</div>
</div>
<div class="col-lg-2">
<% sum_all = 0 %>
<% order.order_items.each do |order_item| %>
<%= order_item.product.name %><br>
<% sub_total = price_include_tax(order_item.order_price).to_i * order_item.quantity %>
<% sum_all += sub_total.to_i %>
<% end %>
</div>
<div class="col-lg-2">
<%= sum_all += order.deliver_fee.to_i %>Circle
</div>
<div class="col-lg-1">
<%= order_status(order) %>
</div>
<div class="col-lg-2">
<%= link_to 'indicate', order_path(order), class: "btn btn-sm btn-danger" %>
</div>
</div>
<% end %>
</div>
show
html:app/views/orders/.html.erb
<div class="col-lg-10 offset-1 space">
<div class="row">
<h2>Order history details</h2>
</div>
<div class="row">
<div class="col-lg-7">
<div class="row space">
<h3>Order information</h3>
</div>
<div class="row">
<div class="container">
<div class="row space-sm">
<div class="col-lg-3">
<strong>Order date</strong>
</div>
<div class="col-lg-9">
<%= simple_time(@order.created_at) %>
</div>
</div>
<div class="row space-sm">
<div class="col-lg-3">
<strong>Shipping address</strong>
</div>
<div class="col-lg-9">
<%= @order.send_to_address %>
</div>
</div>
<div class="row space-sm">
<div class="col-lg-3">
<strong>Payment Method</strong>
</div>
<div class="col-lg-9">
<%= how_to_pay(@order.how_to_pay) %>
</div>
</div>
<div class="row space-sm">
<div class="col-lg-3">
<strong>Status</strong>
</div>
<div class="col-lg-9">
<%= order_status(@order) %>
</div>
</div>
</div>
</div>
<div class="row space">
<h3>Order details</h3>
</div>
<div class="d-none d-lg-block">
<div class="row">
<div class="col-lg-4">
<strong>Product</strong>
</div>
<div class="col-lg-3">
<strong>Unit price (tax included)</strong>
</div>
<div class="col-lg-2">
<strong>Quantity</strong>
</div>
<div class="col-lg-2">
<strong>subtotal</strong>
</div>
</div>
</div>
<% sum_all = 0 %>
<% @order.order_items.each do |order_item| %>
<div class="row space-sm">
<div class="col-lg-4">
<%= order_item.product.name %>
</div>
<div class="col-lg-3">
<%= price_include_tax(order_item.order_price) %>
</div>
<div class="col-lg-2">
<%= order_item.quantity %>Pieces
</div>
<div class="col-lg-2">
<%= sub_total = price_include_tax(order_item.order_price).to_i * order_item.quantity %>Circle
<% sum_all += sub_total %>
</div>
</div>
<% end %>
</div>
<div class="col-lg-5">
<div class="row space">
<h3>Billing information</h3>
</div>
<div class="row">
<div class="container">
<div class="row space-sm">
<div class="col-lg-6">
<strong>Product total</strong>
</div>
<div class="col-6">
<%= sum_all %>Circle
</div>
</div>
<div class="row space-sm">
<div class="col-lg-6">
<strong>Delivery charge</strong>
</div>
<div class="col-lg-6">
<%= @order.deliver_fee %>Circle
</div>
</div>
<div class="row space-sm">
<div class="col-lg-6">
<strong>Billed amount</strong>
</div>
<div class="col-lg-6">
<%= sum_all + @order.deliver_fee.to_i %>Circle
</div>
</div>
</div>
</div>
</div>
</div>
</div>
In the above figure, I would like to display the total amount of products in the "Billing information" column, but if I build the screen in order from the top, it will be necessary to go through a loop for "Billing information" and "Order details" twice. It will be a hassle. So, thinking that it is a sloppy method, divide the screen vertically into two with col, first create columns for "order information" and "order details", and then calculate the total amount in the "order details" loop. It is stored in a variable and called in the "billing information" field. (In the "Billing Information" column, I forgot to set the default value of the shipping fee in the DB, so the total product and the billed amount are the same amount. Originally, the shipping fee of 800 yen is the billed amount for the total product. It is a specification to be added, and the view code also corresponds to it.)
There was only the central function of this EC site, and the amount of code was large and the content was complicated. In this implementation, while roughly following what I made in the school team implementation before, modifications such as newly defining helper and model methods to make the code easier to see and making the screen responsive Was added.
It is an additional note about the specification that address data is registered in Address at the same time as Order when a new address is selected in the create action. In the above description, the address column of the Address model is searched, and if there is no match, it is registered. For a more accurate search
unless Address.find_by(addressee: @order.addressee, address: @order.send_to_address).exists?
As such, you may want to make sure that both the address and address match. Although the code will be longer.
Anyway, this completes the functionality of the customer site. I'm happy.
Recommended Posts